为了编写计算机视觉应用,我们必须会存取图像的内容,如修改或者创建图像,这些过程都需要对图像的基本元素进行操作,即所谓的像素。
1【存取像素值】
为了存取矩阵元素,我们需要在代码中指定元素所在的行和列,程序会返回相应的元素。如果是单通道的,返回值是单个数值;如果是多通道的,返回值是一组向量(Vector)。
我们先通过一个简单的例子演示如何直接存取像素值。在图像随机添加一些椒盐噪点,随机将一些像素设置为白色或者黑色。在传输过程中,如果部分像素值丢失,那么这种噪点就会出现。首先写一个简单的函数
void salt(cv::Mat&image, int n)
{
for(int i = 0; i < n; i++)
{
int k = qrand() % image.cols;
int j = qrand() % image.rows;
if(image.channels() == 1)
{
image.at
}
else if(image.channels() == 3)
{
image.at
image.at
image.at
}
}
}
函数功能很简单,就不介绍了。
测试程序如下
cv::Mat image = cv::imread("timg.jpg");
cv::namedWindow("Test1");
cv::imshow("Test1", image);
salt(image, 30000);
cv::namedWindow("Test2");
cv::imshow("Test2", image);
效果如下所示
2【通过指针遍历图像】
通过一个颜色缩减函数来说明指针遍历图像的方法。主要函数如下
void colorReduce(const cv::Mat& image, cv::Mat& result, int div = 64)
{
result.create(image.rows, image.cols, image.type());
int nl = image.rows;
int nc = image.cols * image.channels();
if (image.isContinuous())
{
nc = nc * nl;
nl = 1;
}
for(int i = 0; i < nl; i++)
{
const uchar* data_in = image.ptr
uchar* data_out = result.ptr
for (int j = 0; j < nc; j++)
{
data_out[j] = data_in[j]/div * div + div / 2;
}
}
}
运行效果如下所示
成员变量cols代表图像的列数(宽度),rows代表图像的行数(高度),step代表以字节为单位的图像的有效宽度。即使你的图像的元素类型不是uchar,step仍然代表着行的字节数。像素的大小可以油elemSize函数得到:对于一个三通道的short行矩阵(CV_16SC3),elemSize返回6。图像的通道数可以由channels方法得到:对于灰度图像来说为1,对于彩色图像来说为3。total函数返回矩阵的像素个数。一般出于效率考虑,每行会填补一些额外像素,这是因为,如果行的长度为4或者8的倍数,对于一些处理器来说,处理的更高效。如果需要根据新的尺寸和数据类型对一个矩阵进行重新分配,我们可以调用create成员函数。。而且,如果新指定的尺寸和数据类型与原有的一样,create函数会直接返回,不会对本矩阵做任何改动。图像分配内存的大小为total()*elemSize()。
3【使用迭代器遍历图像】
opencv为cv::Mat提供了与STL迭代器兼容的迭代器。可以通过cv::Mat Iteerator_的实例来得到。这是一个模板类。主要函数程序如下:
void colorIteratorReduce( cv::Mat& image, cv::Mat& result, int div = 64)
{
result.create(image.rows, image.cols, image.type());
cv::Mat_
cv::Mat_
cv::Mat_
for(; it != itend; it++)
{
(*rit)[0] = (*it)[0]/div * div + div /2 ;
(*rit)[1] = (*it)[1] / div * div + div / 2;
(*rit)[2] = (*it)[2] / div * div + div / 2;
rit++;
}
}
运行效果和上一个效果一样。image.begin
二【遍历图像和邻域操作】
在图像处理中,通过当前位置的相邻像素计算新的像素位置是很常见的操作,当邻域包含图像的前几行和下几行是,就需要同时扫描图像的若干行。本例子对图像进行锐化,它是基于拉普拉斯算子(在后面章节会介绍)。本例子是减去前后左右的像素来锐化。主要代码如下
void sharpen(const cv::Mat& image, cv::Mat&result)
{
result.create(image.rows, image.cols, image.type());
int nl = image.rows - 1 ;
int cl = (image.cols - 1) * image.channels() ;
std::cout<<"muyi"<
for(int j = 1; j < nl ; j++)
{
const uchar* previous = image.ptr
const uchar* current = image.ptr
const uchar* next = image.ptr
uchar* outPut = result.ptr
for(int i = 1; i < cl; i++)
{
*outPut++ = cv::saturate_cast
}
}
result.row(0).setTo(cv::Scalar(0,0,0));
result.row(nl).setTo(cv::Scalar(0,0,0));
result.col(0).setTo(cv::Scalar(0,0,0));
result.col(image.cols - 1).setTo(cv::Scalar(0,0,0));
}
运行效果如下
cv::saturate_cast被用来对计算结果进行截断。使用setTo函数来设置矩阵的值,这个函数会将矩阵的所有元素设为指定的值。cv::Scalar(a,b,c)来指定像素三个通道的目标值。