第一种方法:使用指向Mat数据部分的指针。
1 Mat& ScanImageAndReduceC(Mat& I, const uchar* const table) 2 { 3 // accept only char type matrices 4 CV_Assert(I.depth() != sizeof(uchar)); 5 6 int channels = I.channels(); 7 8 int nRows = I.rows; 9 int nCols = I.cols * channels; 10 11 if (I.isContinuous()) 12 { 13 nCols *= nRows; 14 nRows = 1; 15 } 16 17 int i,j; 18 uchar* p; 19 for( i = 0; i < nRows; ++i) 20 { 21 p = I.ptr(i); 22 for ( j = 0; j < nCols; ++j) 23 { 24 p[j] = table[p[j]]; 25 } 26 } 27 return I; 28 }
第11行使用isContinous函数,是为了保证图像的每一行之间是连续的,不存在某一行的行尾和下一行的开头的数据之间的内存单元存放其他数据。如果该函数返回true,则我们可以把图像当成1行、row*col列的数据格式进行遍历。
第21行使用ptr函数,它接受一个参数代表从0起始的行号。ptr的返回值默认为uchar*或者const uchar*(const版本的重载)。另外ptr有模板的实现,可以通过ptr
第二种方法:使用迭代器。
1 Mat& ScanImageAndReduceIterator(Mat& I, const uchar* const table) 2 { 3 // accept only char type matrices 4 CV_Assert(I.depth() != sizeof(uchar)); 5 6 const int channels = I.channels(); 7 switch(channels) 8 { 9 case 1: 10 { 11 MatIterator_<uchar> it, end; 12 for( it = I.begin<uchar>(), end = I.end<uchar>(); it != end; ++it) 13 *it = table[*it]; 14 break; 15 } 16 case 3: 17 { 18 MatIterator_<Vec3b> it, end; 19 for( it = I.begin<Vec3b>(), end = I.end<Vec3b>(); it != end; ++it) 20 { 21 (*it)[0] = table[(*it)[0]]; 22 (*it)[1] = table[(*it)[1]]; 23 (*it)[2] = table[(*it)[2]]; 24 } 25 } 26 } 27 28 return I; 29 }MatIterator_是Mat的迭代器,同样支持模板。在第12行和第19行的循环中,我们使用了Mat的begin和end函数,使迭代器分别指向Mat数据部分的开头和结尾。begin和end的实现如下:
1 template<typename _Tp> inline MatIterator_<_Tp> Mat::begin() 2 { 3 CV_DbgAssert( elemSize() == sizeof(_Tp) ); 4 return MatIterator_<_Tp>((Mat_<_Tp>*)this); 5 } 6 7 template<typename _Tp> inline MatIterator_<_Tp> Mat::end() 8 { 9 CV_DbgAssert( elemSize() == sizeof(_Tp) ); 10 MatIterator_<_Tp> it((Mat_<_Tp>*)this); 11 it += total(); 12 return it; 13 }Mat_类型可以方便地对数据进行操作,因为OpenCV的开发者对它的括号操作符进行了重载。我们看看Mat_类型对3通道图像的处理:
1 Mat_<Vec3b> img(240, 320, Vec3b(0, 255, 0)); 2 3 for (int i = 0; i < 100; i++) 4 img(i, i) = Vec3b(255, 255, 255); 5 // 对第三个通道(蓝色)单独操作 6 for (int i = 0; i < img.rows; i++) 7 for (int j = 0; j < img.cols; j++) 8 img(i, j)[2] ^= (uchar)(i ^ j);
第三种方法:使用at方法或者Mat_类型。
使用at方法的好处是可以随机访问你指定的数据。代码如下:
1 Mat& ScanImageAndReduceRandomAccess(Mat& I, const uchar* const table) 2 { 3 // accept only char type matrices 4 CV_Assert(I.depth() != sizeof(uchar)); 5 6 const int channels = I.channels(); 7 switch(channels) 8 { 9 case 1: 10 { 11 for( int i = 0; i < I.rows; ++i) 12 for( int j = 0; j < I.cols; ++j ) 13 I.at(i,j) = table[I.at (i,j)]; 14 break; 15 } 16 case 3: 17 { 18 Mat_ _I = I; 19 20 for( int i = 0; i < I.rows; ++i) 21 for( int j = 0; j < I.cols; ++j ) 22 { 23 _I(i,j)[0] = table[_I(i,j)[0]]; 24 _I(i,j)[1] = table[_I(i,j)[1]]; 25 _I(i,j)[2] = table[_I(i,j)[2]]; 26 } 27 I = _I; 28 break; 29 } 30 } 31 32 return I; 33 }
在第13行,我们使用了at
我们注意到,第18行使用了Mat_
1 template<typename _Tp> inline const _Tp& Mat_<_Tp>::operator ()(int i0, int i1) const
2 {
3 CV_DbgAssert( dims <= 2 && data &&
4 (unsigned)i0 < (unsigned)size.p[0] &&
5 (unsigned)i1 < (unsigned)size.p[1] &&
6 type() == DataType<_Tp>::type );
7 return ((const _Tp*)(data + step.p[0]*i0))[i1];
8 }