阅读本节内容,建议先阅读Lecture 2 opencv2系列之初识Mat,了解图像像素数据在Mat
中的存储方式。
《The OpenCV Tutorials》给出3种遍历Mat中图像像素矩阵的方法,分别是指针遍历(ptr)、迭代器(MatIterator)、
at函数,前两者亦分别称作高效方法、安全方法。其中最为高效的是指针方法。
本文只介绍指针方法。
以下代码实现对图像像素数据的遍历,我们打印图像的红色分量。
代码
#include <iostream>
#include "opencv2/core/core.hpp"
#include "opencv2/highgui/highgui.hpp"
using namespace std;
using namespace cv;
int main(int argc, const char *argv[])
{
if (argc != 2)
{
cout<<"Number of Parameters is Wrong!"<<endl;
return -1;
}
Mat src = imread(argv[1],IMREAD_COLOR);
int channels = src.channels();
int nRows = src.rows;
//图像数据列需要考虑通道数的影响;
int nCols = src.cols * channels;
if (src.isContinuous())//连续存储的数据,按一行处理
{
nCols *= nRows;
nRows = 1;
}
int i,j;
uchar* p;
for( i = 0; i < nRows; ++i)
{
p = src.ptr<uchar>(i);
for ( j = 2; j < nCols; j+=3)//注意通道顺序为BGR,红色为第三个数据;
{
cout<<int(p[j])<<" ";
}
cout<<endl;
}
return 0;
}
释义
- 如果只是单纯打印图像数据的话,可以直接用
cout<<src
来实现,OpenCV2重定义了<<
运算符,实现
了矩阵的格式化输出。
详细内容参见The OpenCV Tutorials Release 2.4.9.0,p.148。 - 图像坐标系和像素矩阵。
图像左上角作为原点,按行和列展开。故图像左上角像素对应矩阵的0行0列数据,这里列不计通道。 - OpenCV默认使用BGR的通道顺序。
- 行数据的填补。
宽W高H的真彩色图像,像素数据存储需要WxHx3个uchar
构成的内存块,但是出于效率考虑,每行可能
会填补一些额外像素,亦即存储像素数据的宽度不一定是W,往往填补为4或8的倍数,因为这样一些多
媒体处理芯片可以更高效的处理图像。
图像的宽高分别由cols
、rows
给出,行像素个数由cols*channels()
给出,实际行的字节数由step[0]
给出。对于有额外填补的行数据,cols!= step[0]/elemSize()
,反之,cols==step[0]/elemSize()
。
我们可以用成员函数isContinuous()
判断图像是否对行进行了填补,对于没有进行填补的图像,我们视其
像素数据为一个长为WxHxchannels()
的一维数组,加快循环速度。 - 行首地址。
Mat的成员函数ptr(int j)
返回第j
行的首地址。 - 如果仔细阅读过Lecture 2 opencv2系列之初识Mat,对于遍历
Mat
,自然想到用data+step+elemSize()
来
实现,这也是基于指针的方法。
核心循环代码为:
int i,j;
uchar *p;
for (i = 0; i < nRows; i++)
{
p = src.<uchar>data + i * src.step[0];
for (j = 2; j < nCols; j += 3)
{
cout<<int(p[j*src.elemSize1()])<<" ";//本例中,src.elemSize1()=1;故可简写为p[j]。
}
}
本质上是因为src.ptr(i) == src.data + i * src.step[0]
。
在《OpenCV2 计算机视觉编程手册》一书中,Laganiere不建议用这种方法,原因是:容易出错,并且不适用于带“感兴趣
区域”的图像。
参考文献
- The OpenCV Reference Manual Release 2.4.9.0。
- The OpenCV Tutorials Release 2.4.9.0。
- 加拿大, Laganiere, R., & 张静. OpenCV2 计算机视觉编程手册.
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。