以下内容摘自OpenCV2 计算机视觉编程手册
大多数图像处理中,我们需要遍历图像的所有像素,接下来介绍遍历循环的方式(以三通道的彩色图像为例)
存储方式
在一个彩色图像中,图像数据缓冲区的前三个字节对应图像左上角像素的三个通道值,接下来三个字节对应第一行的第二个像素块,以此类推,出于效率考虑,有些图像每行会填补一些额外像素方便一些多媒体芯片处理数据,如果没有填补像素,则图像的有效宽度等于真实宽度。
本节用到的相关参数
成员变量:
- rows 图像的高度(像素的行数)
- cols 图像的宽度(像素的列数)
- step 图像的有效宽度(一行像素包含的字节数)
成员函数:
- elemSize():返回像素的大小(字节数)
- channels():返回图像的通道数,比如RGB图像的通道数为3
- total():返回矩阵的像素个数
遍历图像的方法
(1)指针遍历图像
ptr()模板函数获取图像任意行的地址
代码如下:
int row=image.rows;
int col=image.cols*image.channels();
for(int i=0;i<row;++i){
uchar* data=image.ptr<uchar>(i); //获得第i行的首地址
for(int j=0;j<cols;j++){
//在这里处理每一个像素data[j]或*data
}
}
如果图像没有进行填补时,那么每一行的像素在内存中都是连续的,则可以仅使用一个循环就完成遍历。
代码如下:
if(image.isContinuous()) //判断图像是否有填补
{
image.reshape(1,1);
}
int row=image.rows;
int col=image.cols*image.channels();
for(int i=0;i<row;++i){
uchar* data=image.ptr<uchar>(i); //获得第i行的首地址
for(int j=0;j<cols;j++){
//在这里处理每一个像素data[j]或*data
}
}
reshape函数在不改变内存的情况下改变矩阵的维度,参数分别为新的通道数和行数,列数会根据设置值自适应。
还有一种不推荐使用的方式代码如下:
int row=image.rows;
int col=image.cols*image.channels();
for(int i=0;i<row;++i){
for(int j=0;j<cols;j++){
uchar *data=image.data+i*image.step+j*image.elemSize();
//在这里处理每一个像素*data
}
}
(2)使用迭代器遍历图像
OpenCv为cv::Mat提供了与STL迭代器兼容的迭代器:
cv::Mat_< xxx >::iterator 或 cv::Mat_< xxx >::const_iterator
代码如下:
cv::Mat_<cv::Vec3b>::iterator it=image.begin<cv::Vec3b>();
cv::Mat_<cv::Vec3b>::iterator itend=image.end<cv::Vec3b>();
for(;it!=itend;it++){
//在这里处理每一个像素的三个通道分别是(*it)[0] (*it)[1] (*it)[2]
}
//如果想从任意位置开始,则迭代器初始化为image.begin<cv::Vec3b>()+x x为要跳过的像素个数
说明:使用指针遍历的运行速度更快,使用迭代器遍历代码更加清晰简洁,各有好处,根据具体项目进行取舍即可。