查看Mat对象的数据的三种方法

来源:互联网 发布:尔雅通识网络课程 编辑:程序博客网 时间:2024/05/03 17:29

第一种方法:使用指向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<uchar>(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<type>实现type*和const type*的返回值。这些返回值就是返回指向指定行号的指针。使用ptr方法返回的指针进行遍历,遍历的是图像的每一个字节(或者我们指定的type长度),而非像素(注意第9行ncols的定义)。

第二种方法:使用迭代器。

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<uchar>(i,j) = table[I.at<uchar>(i,j)];14             break;15         }16     case 3:17         {18          Mat_<Vec3b> _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<uchar>(i, j),该方法返回第i行第j列的数据的引用。at方法还支持我们传入cv::Point类型的参数,例如at<uchar>(cv::Point2f(16, 18))。

我们注意到,第18行使用了Mat_<Vec3b>,我们上面也说了,它可以看做是支持模板和随机访问的Mat类的变形。Mat_重载了括号运算符以支持随机访问,其代码实现如下:

1 template<typename _Tp> inline const _Tp& Mat_<_Tp>::operator ()(int i0, int i1) const2 {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 }

0 0