一、Mat中图像像素的访问方式
1.ptr操作和指针-高效的方式
这种方式基于.ptr的操作,也是比较推荐的遍历图像的方式。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 | /** @Method 1: the efficient method accept grayscale image and RGB image */ int ScanImageEfficiet(Mat & image) { // channels of the image int iChannels = image.channels(); // rows(height) of the image int iRows = image.rows; // cols(width) of the image int iCols = image.cols * iChannels; // check if the image data is stored continuous if (image.isContinuous()) { iCols *= iRows; iRows = 1; } uchar* p; for ( int i = 0; i < iRows; i++) { // get the pointer to the ith row p = image.ptr<uchar>(i); // operates on each pixel for ( int j = 0; j < iCols; j++) { // assigns new value p[j] = table[p[j]]; } } return 0; } |
这里获取一个指向每一行的指针,然后遍历这一行所有的数据。当图像数据是连续存储的时候,只需要取一次指针,然后就可以遍历整个图像数据。
2.迭代器-比较安全的方式
相较于高效的方式需要自己来计算需要遍历的数据量,以及当图像的行与行之间数据不连续的时候需要跳过一些间隙。迭代器(iterator)方式提供了一个更安全的访问图像像素的方式。你只需要做的就是声明两个MatIterator_变量,一个指向图像开始,一个指向图像结束,然后迭代。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 | /** @Method 2: the iterator(safe) method accept grayscale image and RGB image */ int ScanImageIterator(Mat & image) { // channels of the image int iChannels = image.channels(); switch (iChannels) { case 1: { MatIterator_<uchar> it, end; for (it = image.begin<uchar>(), end = image.end<uchar>(); it != end; it++) { *it = table[*it]; } break ; } case 3: { MatIterator_<Vec3b> it, end; for (it = image.begin<Vec3b>(), end = image.end<Vec3b>(); it != end; it++) { (*it)[0] = table[(*it)[0]]; (*it)[1] = table[(*it)[1]]; (*it)[2] = table[(*it)[2]]; } break ; } } return 0; } |
彩色图像的话,由于是三个通道的向量,OpenCV提供了Vec3b的数据类型来存储。
3.动态地址计算-更适合随机访问的方式
这种方式不推荐用来遍历图像,一般用在要随机访问很少量的图像数据的时候。基本用法就是指定行列号,返回该位置的像素值。不过需要你事先知道返回的数据类型是uchar还是Vec3b或者其他的。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 | /** @Method 3: random access method accept grayscale image and RGB image */ int ScanImageRandomAccess(Mat & image) { // channels of the image int iChannels = image.channels(); // rows(height) of the image int iRows = image.rows; // cols(width) of the image int iCols = image.cols; switch (iChannels) { // grayscale case 1: { for ( int i = 0; i < iRows; i++) { for ( int j = 0; j < iCols; j++) { image.at<uchar>(i, j) = table[image.at<uchar>(i, j)]; } } break ; } // RGB case 3: { Mat_<Vec3b> _image = image; for ( int i = 0; i < iRows; i++) { for ( int j = 0; j < iCols; j++) { _image(i, j)[0] = table[_image(i, j)[0]]; _image(i, j)[1] = table[_image(i, j)[1]]; _image(i, j)[2] = table[_image(i, j)[2]]; } } image = _image; break ; } } return 0; } |
4.查找表-一颗赛艇的方式
OpenCV大概也考虑到了有很多这种需要改变单个像素值的场合(比如基于单个像素值的亮度变换,gamma矫正等),因此在core模块提供了一个更加高效很一颗赛艇的LUT()函数来进行这种操作而且不需要遍历整个图像。
首先建个映射查找表:
1 2 3 4 5 6 7 | // build a Mat type of the lookup table Mat lookupTable(1, 256, CV_8U); uchar* p = lookupTable.data; for ( int i = 0; i < 256; i++) { p[i] = table[i]; } |
然后调用LUT()函数:
1 2 | // call the function LUT(image, lookupTable, matout); |
image是输入图像,matout是输出图像。
二、Mat图像像素的输出方式
1.最简单直接输出方式
(1)直接通过cout<<mat<<endl;语句将所需输出的Mat矩阵输出到窗口;
(2)将mat中数据输出到xml文件中
FileStorage fs(“./mat.xml”,FileStorage::WRITE);
fs<<“mat”<<mat;
fs.release();
2.通过ptr操作和指针输出Mat图像像素
通过指针遍历mat中图像像素地址,依次输出
uchar * pdata=mat.ptr<uchar>(i);
for (int i=0;i<mat.rows;i++)
{
for (int j=0;j<mat.cols*mat.channels();j++)
{
cout<<padata[j]<<endl;
}
}
注意:由于mat中存储的像素数据是uchar或vec3d格式,直接通过cout<<padata[j]<<endl输出的话,将其数据对应的ASCII码进行输出,而有的数据ASCII码是隐藏的不对窗口显示,所以需要将数据类型进行转换再输出即可,如下:
int img_pixel=(int)padata[j];//像素中数据为0-255,所以使用int类型即可
cout<<padata[j]<<endl;
3.其余输出方式
其余输出方式都是通过指针将mat中像素数据读取并转化再输出即可,只是获取指针方式不同而已。