OpenCV-操作像素(访问像素值)

为构建计算机视觉应用程序,我们需要学会访问图像的内容,有时也要修改或者创建图像。本章将讲讲如何操作图像的元素(即像素)。
图像本质上就是由数组组成的矩阵。OpenCV使用了cv:Mat结构来操作图像。矩阵中的每一个元素表示一个像素。对灰度图像而言,像素是8位无符号数(数据类型为unsigned char),0表示黑色,255表示白色。

#include <iostream>
#include <random>
#include <opencv2\core\core.hpp>
#include <opencv2\imgproc\imgproc.hpp>
#include <opencv2\highgui\highgui.hpp>

using namespace std;
using namespace cv;

void salt(cv::Mat image, int n)
{
	std::default_random_engine generator;
	std::uniform_int_distribution<int>
		randomRow(0, image.rows - 1);
	std::uniform_int_distribution<int>
		randomCol(0, image.cols - 1);
	int i, j;
	for (int k = 0; k < n; k++)
	{
		// 随机生成图形位置
		i = randomCol(generator);
		j = randomRow(generator);
		if (image.type() == CV_8UC1)
		{ 
			// 灰度图像
		// 单通道8 位图像
			image.at<uchar>(j, i) = 255;
		}
		else if (image.type() == CV_8UC3) { // 彩色图像
	 // 3 通道图像
			image.at<cv::Vec3b>(j, i)[0] = 255;
			image.at<cv::Vec3b>(j, i)[1] = 255;
			image.at<cv::Vec3b>(j, i)[2] = 255;
		}
	}
}
int main()
{
	// 打开图像
	cv::Mat image = cv::imread("puppy.JPEG", 1);
	// 调用函数以添加噪声
	salt(image, 3000);
	// 显示结果
	cv::namedWindow("Image");
	cv::imshow("Image", image);

	cv::waitKey();
	return 0;
}

在这里插入图片描述
cv::Mat 类包含多种方法,可用来访问图像的各种属性:利用公共成员变量cols 和rows可得到图像的列数和行数;利用cv::Mat 的at(int y,int x)方法可以访问元素,其中x 是列号,y 是行号。at 方法被实现成一个模板方法。在调用at 方法时,必须指定图像元素的类型,例如:
image.at(j,i)= 255;
有一点需要特别注意,必须保证指定的类型与矩阵内的类型是一致的。at 方法不会进行任何类型转换。
彩色图像的每个像素对应三个部分:红色通道、绿色通道和蓝色通道,因此包含彩色图像的cv::Mat 类会返回一个向量,向量中包含三个8 位的数值。OpenCV 为这样的短向量定义了一种类型,即cv::Vec3b。这个向量包含三个无符号字符(unsigned character)类型的数据。因此,访问彩色像素中元素的方法如下所示:
image.atcv::Vec3b(j,i)[channel]= value;
channel 索引用来指明三个颜色通道中的一个。OpenCV 存储通道数据的次序是蓝色、绿色和红色(因此蓝色是通道0)。你也可以直接使用短向量,方法如下所示:
image.atcv::Vec3b(j, i) = cv::Vec3b(255, 255, 255);
还有类似的向量类型用来表示二元素向量和四元素向量(cv::Vec2b 和cv::Vec4b)。此外还有针对其他元素类型的向量。例如,表示二元素浮点数类型的向量就是把类型名称的最后一个字母换成f,即cv::Vec2f。对于短整型,最后的字母换成s;对于整型,最后的字母换成i;对于双精度浮点数向量,最后的字母换成d。所有这些类型都用cv::Vec<T,N>模板类定义,其中T 是类型,N 是向量元素的数量。
最后一个提示,你也许会觉得奇怪,为什么这些修改图像的函数在使用图像作为参数时,都
采用了值传递的方式?之所以这样做,是因为它们在复制图像时仍共享了同一块图像数据。因此
在需要修改图像内容时,图像参数没必要采用引用传递的方式。顺便说一下,编译器做代码优化
时,用值传递参数的方法通常比较容易实现。

扩展
cv::Mat 类的定义采用了C++模板,因此它的通用性很强。
cv::Mat_模板类
因为每次调用都必须在模板参数中指明返回类型,所以cv::Mat 类的at 方法有时会显得冗长。如果已经知道矩阵的类型,就可以使用cv::Mat_类(cv::Mat 类的模板子类)。cv::Mat_类定义了一些新的方法,但没有定义新的数据属性,因此这两个类的指针或引用可以直接互相转换。新方法中有一个operator(),可用来直接访问矩阵的元素。因此可以这样写代码(其中image是一个对应uchar 矩阵的cv::Mat 变量):
// 用Mat 模板操作图像
cv::Mat_ img(image);
img(50,100)= 0; // 访问第50 行、第100 列处那个值
在创建cv::Mat_变量时,我们就定义了它的元素类型,因此在编译时就已经知道了operator()的返回类型。使用操作符operator()和使用at 方法产生的结果是完全相同的,只是前者的代码更简短。

  • 0
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值