图像容器Mat:访问图像像素
1、关于Mat:
基本上讲,Mat是一个类,由两部分组成:矩阵头;
............................................ 一个指向像素值矩阵的指针。
使用Mat进行图像操作时,使每个Mat对象创建自己的矩阵头,但共享同一个矩阵。这通过使用矩阵指针指向同一地址实现。
例如:在拷贝图像时,只拷贝矩阵头和矩阵指针,而不拷贝矩阵。
Mat A, C; // 只创建信息头部分
A = imread(argv[1], CV_LOAD_IMAGE_COLOR); // 这里为矩阵开辟内存
Mat B(A); // 使用拷贝构造函数
C = A; // 赋值运算符

2、Mat公共属性:
Mat的成员函数含义以下几个公共属性;
Mat信息头:
cv::Mat::Mat(
int rows,
int cols,
int type
)
cv::Mat::Mat(
int ndims,
const int *sizes,
int type
)
cv::Mat::Mat(
const std::vector< int > & sizes,
int type
)
cv::Mat::Mat(
Size size,
int type,
void * data,
size_t step = AUTO_STEP
)

3、常见成员函数:
// make a 7x7 complex matrix filled with 1+3j.
Mat M(7,7,CV_32FC2,Scalar(1,3));
// and now turn M to a 100x60 15-channel 8-bit matrix.
// The old content will be deallocated
M.create(100,60,CV_8UC(15));
IplImage*转Mat
IplImage* img = cvLoadImage("greatwave.png", 1);Mat mtx(img); // convert IplImage* -> Mat
具体成员函数如下图所示:




4、Mat类型存储格式:
存储像素值需要指定颜色空间和数据类型。
对灰度图像,一般存放<uchar>数据类型;
对RGB彩色图像,一般存放<Vec3b>数据类型;
单通道灰度图数据存放格式:如图所示
多通道彩色图像数据存放格式:如图所示
通常,图像的数据都是连续存放在内存中的。
使用isContinuous()函数来判断图像十足是否为连续的。


5、访问图像像素:
方法一:“NaNr<>”和内存访问操作符“[]”
Mat最直接访问方法是通过NaNr<>函数得到一行的指针,并使用[]操作符访问某一列的像素值。
//访问图像像素
//在每行定义一个指针,然后在内存上直接连续访问;
Mat& mImageReduce_C(Mat &mImg, int mGreyThresh)
{
//只接受字符类型的图像
CV_Assert(mImg.depth()!=sizeof(uchar));
int mChannels=mImg.channels();
int mRows=mImg.rows;
int mCols=mImg.cols*mChannels;//根据图像存储结构
if (mImg.isContinuous())//一般情况下isContinuous()为true;
{
mCols *= mRows;
mRows =1;//图像内存存储形式
}
uchar *mData;
for (int i=0;i<mRows;++i)
{
mData = mImgNaNr<uchar>(i);
for (int j=0;j<mCols;j++)
{
if (mData[j]<mGreyThresh)
{
mData[j]=0;
}
else
continue;
}
}
return mImg;
}


6、方法二:迭代器,较安全方法;
OpenCV中,迭代器会访问每一列然后自动跳到下一行,不管内存是否isContinous。
【注】:三通道图像定义<Vec3b>格式的迭代器。
这种方式虽然安全,速度较慢。
Mat& mImageReduce_Interator(Mat &mImg, int mGreyThresh)
{
CV_Assert(mImg.depth()!=sizeof(uchar));
const int mChannels = mImg.channels();
switch(mChannels)
{
case 1:
{
MatIterator_<uchar> it,end;
for (it=mImg.begin<uchar>(),end=mImg.end<uchar>();it!=end; it++)
{
if ((*it)<mGreyThresh)
{
(*it)=0;
}
else
continue;
}
break;
}
case 3:
{
MatIterator_<Vec3b> it,end;
for (it=mImg.begin<Vec3b>(),end=mImg.end<Vec3b>();it!=end;++it)
{
for (int i=0;i<3;i++)
{
if ((*it)[i]<mGreyThresh)
{
(*it)[i]=0;
}
else
continue;
}
}
}
}
return mImg;
}



7、方法三:动态地址访问
功能:访问任意的某一行某一列;更适用于随机访问,在连续扫描点时不推荐。
Mat& mImageReduce_Random(Mat &mImg, int mGrayThresh)
{
CV_Assert(mImg.depth() != sizeof(uchar));
const int mChannels = mImg.channels();
switch(mChannels)
{
case 1:
{
for (int i=0;i<mImg.rows;i++)
for (int j=0;j<mImg.cols;j++)
if (mImg.at<uchar>(i,j)<mGrayThresh)
mImg.at<uchar>(i,j)=0;
}
break;
case 3:
{
Mat_<Vec3b> _mImg =mImg;
for (int i=0;i<mImg.rows;i++)
for (int j=0;j<mImg.cols;j++)
for (int k=0;k<3;k++)
{
if (_mImg(i,j)[k]<mGrayThresh)
_mImg(i,j)[k]=0;
}
mImg=_mImg;
break;
}
}
return mImg;
}


