标签:
1 指针算术
在一个彩色图像中,图像数据缓冲区中的前三个字节对应图像左上角像素的三个通道值,接下来的三个字节对应第一行的第二个像素,以此类推。一个宽为W、高为H的图像需要一个大小由W X H X3个uchar构成的内存块。但是,出于效率考虑,每行会填补一些额外的像素。这是因为,如果行的长度是4或8的倍数,一些多媒体处理芯片(如Intel的MMX架构)可以更高效的处理图像。这些额外的像素不会被显示或者保存,填补的值将被忽略。
opencv将填补后一行的长度指定为关键字。如果图像没有对行进行填补,那么图像的有效宽度就等于图像的真实宽度。成员变量cols代表图像的宽度(图像的列数),rows代表图像的高度,step代表以字节为单位的图像的有效宽度。即使你的图像的元素类型不是uchar,step仍然代表着行的字节数。像素的大小可以由elemSize函数得到:对于一个三通道的short型矩阵(CV_16SC3),elemSize返回6。图像的通道数可以channels方法得到。total函数返回矩阵的像素个数。
1 #include "highgui.h" 2 #include "cxcore.h" 3 #include "cv.h" 4 #include "cmath" 5 void colorReduce(cv::Mat &, int ); 6 7 int main() 8 { 9 cv::Mat tp1 = cv::imread("1.bmp"); 10 cv::Mat tp2 = tp1.clone();//深拷贝 11 colorReduce(tp2, 64); 12 cv::imshow("1",tp1); 13 cv::imshow("2",tp2); 14 imwrite("2.jpg",tp1);//保存图片 15 std::cout << "all pix_count = " << tp1.total() << std::endl;//获得图像像素个数 16 std::cout << "channels = " << tp1.channels() << std::endl;//获得图像通道数 17 std::cout << "pix_size = " << tp1.elemSize() << std::endl;//获得每个像素占内存大小 18 cv::waitKey(0); 19 return 0; 20 } 21 22 23 24 void colorReduce(cv::Mat &tp, int div) 25 { 26 int nl = tp.rows; //行数 27 int nc = tp.cols * tp.channels(); //每行的元素个数 28 for (int j = 0; j < nl; j++) 29 { 30 uchar * data = tp.ptr<uchar>(j);//获取第j行的首地址 31 for(int i = 0; i < nc; i++) 32 { 33 data[i] = data[i] / div * div + div / 2; 34 } 35 } 36 37 }
效果如下:
all pix_count = 405504
channels = 3
pix_size = 3
请按任意键继续. . .
(1)为了简化指针运算,cv::Mat提供了ptr函数可以得到图像任意行的首地址。ptr函数是一个模板函数,它返回第j行的首地址:
uchar * data = image.ptr<uchar>(j);
(2) cv::Mat tp2 = tp1.clone();复制一幅图片。
cv::Mat tp2;
tp2.create(tp1.rows, tp1.cols, tp1.type());
create函数创建的图像内存都是连续的,create函数不会对图像的行进行填补。分配的内存大小为total()* elemSize()。
2 高效遍历连续图像
考虑到效率,图像有可能会在行尾扩大若干个像素。但是,当不对行进行填补时,图像可以被视为一个长为WxH的一维数组。我们可以通过cv::Mat 的一个成员函数isContinuous 来判断这幅图像是否对行进行了填补。如果返回值为真的话,说明这幅图像没有对行进行填补。在一些图像处理算法中,我们可以利用图像的连续性,把整个处理过程使用一个循环完成。
3 底层指针运算
在类cv::Mat中,图像数据以unsigned char 形式保存在一块内存中。这块内存的首地址可以通过data成员变量得到。data是一个unsigned char 型的指针,所以循环可以以如下方式开始:
uchar * data = image.data;
从当前行到下一行可以通过对指针加上行宽完成:
data += image.step;
step代表图像的行宽(包括填补对象)。通常而言,可以通过如下方式获得第j行、第i列像素的地址:
data = image.data + j * image.step + i * image.elemSize();
4 使用迭代器遍历图像
在面向对象的编程中,遍历数据集合通常是通过迭代器来完成的。迭代器是一种特殊的类,它专门用来遍历集合中的各个元素,同时隐藏了在给定的集合上元素迭代的具体实现方式。这种信息隐蔽原则的使用使得遍历集合更加容易。另外,不管数据类型是什么,我们都可以使用类似的方式遍历集合。标准模板库(STL)为每个容器类型都提供了迭代器。OpenCV同样为cv::Mat提供了与STL迭代器兼容的迭代器。(略)
标签:
原文地址:http://www.cnblogs.com/tianpeng-blog/p/4560909.html