重新学习2.0以上OPENCV(基于MAT类型)2
2017-01-03 15:53
288 查看
图像中像素点的遍历:
1.用模板函数
Mat类提供了一个at的方法用于取得图像上的点,它是一个模板函数,可以取到任何类型的图像上的点。下面我们通过一个图像处理中的实际来说明它的用法。
在实际应用中,我们很多时候需要对图像降色彩,因为256*256*256实在太多了,在图像颜色聚类或彩色直方图时,我们需要用一些代表性的颜色代替丰富的色彩空间,我们的思路是将每个通道的256种颜色用64种代替,即将原来256种颜色划分64个颜色段,每个颜色段取中间的颜色值作为代表色。
1.
2.
更简单一些的方法:OpenCV定义了一个Mat的模板子类为Mat_,它重载了operator()让我们可以更方便的取图像上的点:
2.用指针来遍历像素点
上面的例程中可以看到,我们实际喜欢把原图传进函数内,但是在函数内我们对原图像进行了修改,而将原图作为一个结果输出,很多时候我们需要保留原图,这样我们需要一个原图的副本。
值得说明的是:程序中将三通道的数据转换为1通道,在建立在每一行数据元素之间在内存里是连续存储的,每个像素三通道像素按顺序存储。也就是一幅图像数据最开始的三个值,是最左上角的那像素的三个通道的值。
但是这种用法不能用在行与行之间,因为图像在OpenCV里的存储机制问题,行与行之间可能有空白单元。这些空白单元对图像来说是没有意思的,只是为了在某些架构上能够更有效率,比如intel MMX可以更有效的处理那种个数是4或8倍数的行。一般来说图像行与行之间往往存储是不连续的,但是有些图像可以是连续的,Mat提供了一个检测图像是否连续的函数isContinuous()。当图像连通时,我们就可以把图像完全展开,看成是一行。
用指针除了用上面的方法外,还可以用指针来索引固定位置的像素:
Image.step返回图像一行像素元素的个数(包括空白元素),Image.elemSize()返回一个图像像素的大小。
3.迭代器来遍历图像
下面的方法可以让我们来为图像中的像素声明一个迭代器:
如果迭代器指向一个const图像,则可以用下面的声明:
下面我们用迭代器来简化上面的colorReduce程序:
4.图像的领域操作:
很多时候,我们对图像处理时,要考虑它的邻域,比如3*3是我们常用的,这在图像滤波、去噪中最为常见,下面我们介绍如果在一次图像遍历过程中进行邻域的运算。
下面我们进行一个简单的滤波操作,拉普拉斯算子:[-1 –1 -1;-1 8 –1;-1 –1 -1]。(能使图像变的尖锐)
原图像
先cvtColor函数转换成灰度图,在用拉普拉斯算子2进行边缘提取的结果
补充:当形参不是引用的时候发生错误。
1.用模板函数
at<typename>(i,j)
Mat类提供了一个at的方法用于取得图像上的点,它是一个模板函数,可以取到任何类型的图像上的点。下面我们通过一个图像处理中的实际来说明它的用法。
在实际应用中,我们很多时候需要对图像降色彩,因为256*256*256实在太多了,在图像颜色聚类或彩色直方图时,我们需要用一些代表性的颜色代替丰富的色彩空间,我们的思路是将每个通道的256种颜色用64种代替,即将原来256种颜色划分64个颜色段,每个颜色段取中间的颜色值作为代表色。
void colorReduce(Mat& Image,int div) { for(int i=0;i<Image.rows;i++) { for(int j=0;j<Image.cols;j++) { Image.at<Vec3b>(i,j)[0]=Image.at<Vec3b>(i,j)[0]/div*div+div/2; Image.at<Vec3b>(i,j)[1]=Image.at<Vec3b>(i,j)[1]/div*div+div/2; Image.at<Vec3b>(i,j)[2]=Image.at<Vec3b>(i,j)[2]/div*div+div/2; } } }
1.
Image.at<uchar>(i,j):取出灰度图像中i行j列的点。
2.
Image.at<Vec3b>(i,j)[k]:取出彩色图像中i行j列第k通道的颜色点。其中uchar,Vec3b都是图像像素值的类型,不要对Vec3b这种类型感觉害怕,其实在core里它是通过
typedef Vec<T,N>来定义的,N代表元素的个数,T代表类型。
更简单一些的方法:OpenCV定义了一个Mat的模板子类为Mat_,它重载了operator()让我们可以更方便的取图像上的点:
Mat_<uchar> im=Image;
im(i,j)=im(i,j)/div*div+div/2;
2.用指针来遍历像素点
上面的例程中可以看到,我们实际喜欢把原图传进函数内,但是在函数内我们对原图像进行了修改,而将原图作为一个结果输出,很多时候我们需要保留原图,这样我们需要一个原图的副本。
void colorReduce(const Mat& Image,Mat& outImage,int div) { // 创建与原图像等尺寸的图像 outImage.create(Image.size(),Image.type()); int nr=Image.rows; // 将3通道转换为1通道 int nr=Image.cols*Image.channels(); for(int k=0;k<nr;k++) { // 每一行图像的指针 const uchar* inData=Image.ptr<uchar>(k); uchar* outData=outImage.ptr<uchar>(k); for(int i=0;i<nr;i++) { outData[i]=inData[i]/div*div+div/2; //*outData++=*inData++/div*div+div/2; } } }
值得说明的是:程序中将三通道的数据转换为1通道,在建立在每一行数据元素之间在内存里是连续存储的,每个像素三通道像素按顺序存储。也就是一幅图像数据最开始的三个值,是最左上角的那像素的三个通道的值。
但是这种用法不能用在行与行之间,因为图像在OpenCV里的存储机制问题,行与行之间可能有空白单元。这些空白单元对图像来说是没有意思的,只是为了在某些架构上能够更有效率,比如intel MMX可以更有效的处理那种个数是4或8倍数的行。一般来说图像行与行之间往往存储是不连续的,但是有些图像可以是连续的,Mat提供了一个检测图像是否连续的函数isContinuous()。当图像连通时,我们就可以把图像完全展开,看成是一行。
if(Image.isContinuous()&&outImage.isContinuous()) { nr=1; nc=nc*Image.rows*Image.channels(); } for(int i=0;i<nr;i++) { const uchar* inData=Image.ptr<uchar>(i); uchar* outData=outImage.ptr<uchar>(i); for(int j=0;j<nc;j++) { *outData++=*inData++/div*div+div/2; } }
用指针除了用上面的方法外,还可以用指针来索引固定位置的像素:
Image.step返回图像一行像素元素的个数(包括空白元素),Image.elemSize()返回一个图像像素的大小。
&Image.at<uchar>(i,j)=Image.data+i*Image.step+j*Image.elemSize();
3.迭代器来遍历图像
下面的方法可以让我们来为图像中的像素声明一个迭代器:
MatIterator_<Vec3b> it; Mat_<Vec3b>::iterator it;
如果迭代器指向一个const图像,则可以用下面的声明:
MatConstIterator<Vec3b> it; 或者 Mat_<Vec3b>::const_iterator it;
下面我们用迭代器来简化上面的colorReduce程序:
void colorReduce(const Mat& Image,Mat& outImage,int div) { outImage.create(Image.size(),Image.type()); MatConstIterator_<Vec3b> it_in=Image.begin<Vec3b>(); MatConstIterator_<Vec3b> itend_in=Image.end<Vec3b>(); MatIterator_<Vec3b> it_out=outImage.begin<Vec3b>(); MatIterator_<Vec3b> itend_out=outImage.end<Vec3b>(); while(it_in!=itend_in) { (*it_out)[0]=(*it_in)[0]/div*div+div/2; (*it_out)[1]=(*it_in)[1]/div*div+div/2; (*it_out)[2]=(*it_in)[2]/div*div+div/2; it_in++; it_out++; } }
4.图像的领域操作:
很多时候,我们对图像处理时,要考虑它的邻域,比如3*3是我们常用的,这在图像滤波、去噪中最为常见,下面我们介绍如果在一次图像遍历过程中进行邻域的运算。
下面我们进行一个简单的滤波操作,拉普拉斯算子:[-1 –1 -1;-1 8 –1;-1 –1 -1]。(能使图像变的尖锐)
void Laplacian(const Mat& Image,Mat& newImage) { newImage.create(Image.size(),Image.type()); int nr=Image.rows; int nc=Image.cols; for(int i=1;i<nr-1;i++) { const uchar* line1=Image.ptr<uchar>(i-1); const uchar* line2=Image.ptr<uchar>(i); const uchar* line3=Image.ptr<uchar>(i+1); uchar* outData=newImage.ptr<uchar>(i); for(int j=1;j<nc-1;j++) { outData[j]=saturate_cast<uchar>(8*line2[j]-(line1[j-1]+line1[j]+line1[j+1]+line2[j-1]+line2[j+1]+line3[j-1]+line3[j]+line3[j+1])); //staturate_cast<typename>是一个类型转换函数,程序里是为了确保运算结果还在uchar范围内 } } newImage.row(0).setTo(Scalar(0)); newImage.row(newImage.rows-1).setTo(Scalar(0)); newImage.col(0).setTo(Scalar(0)); newImage.col(newImage.cols-1).setTo(Scalar(0)); }
原图像
先cvtColor函数转换成灰度图,在用拉普拉斯算子2进行边缘提取的结果
补充:当形参不是引用的时候发生错误。
相关文章推荐
- 重新学习2.0以上OPENCV(基于MAT类型)1
- 重新学习2.0以上OPENCV(基于MAT类型)3
- OpenCV2.0以后:Mat类型和CvMat类型的互相转换(附:OpenCV矩阵合并)
- 【OpenCV学习笔记】【类型转换】一(IplImage和cv::Mat的类型相互转换)
- Opencv2.X以上Mat类型与IplImage*的转换
- C++读写txt文件方式以及基于opencv的Mat数据类型读写txt文件
- Opencv2.X以上Mat类型与IplImage*的转换
- C++读写txt文件方式以及基于opencv的Mat数据类型读写txt文件类型
- REST服务学习笔记一、基于JAX-RS 2.0 REST服务几种类型
- OpenCV2.3.1学习笔记(1):用vec类型变量构造Mat
- Opencv2.X以上Mat类型与IplImage*的转换
- [OpenCV学习笔记][Mat数据类型和操作]
- OpenCV中Mat 类型学习笔记
- 学习openCV之Mat类型访问每个像素
- 【OpenCV学习笔记】之四:二值图像细化方法/骨架提取----基于2.0 Mat接口
- 【OpenCV学习笔记】之二:Mat使用的潜规则----对数据类型敏感
- OpenCV学习笔记(五)——2.0以上版本和以下版本的数据兼容
- opencv学习之Mat数据类型
- 基于 Android NDK 的学习之旅-----数据传输一(基本数据类型和数组传输)(附源码)
- OpenCV中Mat与IplImage和CvMat数据类型间的相互转换