转https://www.cnblogs.com/codingmengmeng/p/6559724.html
opencv2.1版本之前使用的IplImage *数据结构来表示图像,2.1之后的版本使用图像容器垫来存储.IplImage结构体如下所示。
1 typedef struct _IplImage
2 {
3 int nSize; /* IplImage大小 */
4 int ID; /* 版本 (=0)*/
5 int nChannels; /* 大多数OPENCV函数支持1,2,3 或 4 个通道 */
6 int alphaChannel; /* 被OpenCV忽略 */
7 int depth; /* 像素的位深度: IPL_DEPTH_8U, IPL_DEPTH_8S, IPL_DEPTH_16U,
8 IPL_DEPTH_16S, IPL_DEPTH_32S, IPL_DEPTH_32F and IPL_DEPTH_64F 可支持 */
9 char colorModel[4]; /* 被OpenCV忽略 */
10 char channelSeq[4]; /* 同上 */
11 int dataOrder; /* 0 - 交叉存取颜色通道, 1 - 分开的颜色通道.
12 cvCreateImage只能创建交叉存取图像 */
13 int origin; /* 0 - 顶—左结构,
14 1 - 底—左结构 (Windows bitmaps 风格) */
15 int align; /* 图像行排列 (4 or 8). OpenCV 忽略它,使用 widthStep 代替 */
16 int width; /* 图像宽像素数 */
17 int height; /* 图像高像素数*/
18 struct _IplROI *roi;/* 图像感兴趣区域. 当该值非空只对该区域进行处理 */
19 struct _IplImage *maskROI; /* 在 OpenCV中必须置NULL */
20 void *imageId; /* 同上*/
21 struct _IplTileInfo *tileInfo; /*同上*/
22 int imageSize; /* 图像数据大小(在交叉存取格式下imageSize=image->height*image->widthStep),单位字节*/
23 char *imageData; /* 指向排列的图像数据 */
24 int widthStep; /* 排列的图像行大小,以字节为单位 */
25 int BorderMode[4]; /* 边际结束模式, 被OpenCV忽略 */
26 int BorderConst[4]; /* 同上 */
27 char *imageDataOrigin; /* 指针指向一个不同的图像数据结构(不是必须排列的),是为了纠正图像内存分配准备的 */
28 }
29 IplImage;
1,使用指针遍历图像像素
(1)单通道字节型图像像素访问
1 /*
2 @author:CodingMengmeng
3 @theme:Read the image pixel values
4 @time:2017-3-16 11:27:31
5 @blog:http://www.cnblogs.com/codingmengmeng/
6 */
7 #include <cv.h>
8 #include <highgui.h>
9 using namespace std;
10 using namespace cv;
11 int main(void)
12 {
13 IplImage* imgSrc = cvLoadImage("./inputData\\shuke1.jpg",0);
14 uchar* pixel = new uchar;
15 for (int i = 0; i < imgSrc->height; i++)
16 {
17 for (int j = 0; j < imgSrc->width; j++)
18 {
19 pixel = (uchar*)(imgSrc->imageData + i*imgSrc->widthStep+j);
20 cout << "pixel=" <<(*pixel)+0<< endl;//+0隐式转换为整型,否则会打印出字符
21 }
22 }
23 delete pixel;
24 return 0;
25 }
输出结果是0〜255灰度级的灰度值。
其中(uchar *)(imgSrc-> imageData + i * imgSrc-> widthStep + j)的具体含义:
(a)imgSrc-> imageData指向图像第一行的首地址,i是指当前像素点所在的行,widthStep是指图像每行所占的字节数;所以imgSrc-> imageData + i * imgSrc-> widthStep表示该像素点所在行的首地址; j表示当前像素点所在列,所以imgSrc-> imageData + i * imgSrc-> widthStep + j即表示该像素点的地址。而因为IplImage-> ImageData的默认类型是char类型,所以再对图像像素值进行操作时,要使用强制类型转换为unsigned char,再对其进行处理。否则,图像像素值中,会有负值出现。
(b)中widthStep表示存储一行像素需要的字节数
一个m * n个的单通道字节型图像,其为imageData排列如下:
因为OpenCV的分配的内存是按4字节对齐的,所以widthStep必须是4的倍数,如果8U图像宽度为3,那么widthStep是4,加一个字节补齐。这个图像的一行需要4个字节,只使用前3个,最后一个空在那儿不用。也就是一个宽3高3的图像的的imageData数据大小为4×3 = 12字节。
(2)三通道字节型图像像素访问
多通道字节型图像的为imageData排列如下:
其中的(Bi,BJ)(GI,GJ)(RI,RJ)表示图像(I,J)处BGR分量的值。
1 /*
2 //@author:CodingMengmeng
3 //@theme:Read the image pixel values
4 //@time:2017-3-16 11:59:17
5 //@blog:http://www.cnblogs.com/codingmengmeng/
6 */
7 #include <cv.h>
8 #include <highgui.h>
9 using namespace std;
10 using namespace cv;
11 int main(void)
12 {
13 IplImage* imgSrc = cvLoadImage("./inputData\\shuke1.jpg");
14 uchar* b_pixel = new uchar;
15 uchar* g_pixel = new uchar;
16 uchar* r_pixel = new uchar;
17 for (int i = 0; i < imgSrc->height; i++)
18 {
19 for (int j = 0; j < imgSrc->width; j++)
20 {
21 b_pixel = (uchar*)(imgSrc->imageData + i*imgSrc->widthStep + (j*imgSrc->nChannels + 0));
22 g_pixel = (uchar*)(imgSrc->imageData + i*imgSrc->widthStep + (j*imgSrc->nChannels + 1));
23 r_pixel=(uchar*)(imgSrc->imageData + i*imgSrc->widthStep + (j*imgSrc->nChannels + 2));
24 cout << "b_pixel=" << *b_pixel+0 << endl;
25 cout << "g_pixel=" << *g_pixel+0 << endl;
26 cout << "r_pixel=" << *r_pixel+0 << endl;
27 cout << "/********************************************/" << endl;
28 }
29 }
30 delete b_pixel;
31 delete g_pixel;
32 delete r_pixel;
33 return 0;
34 }
运行结果:
2,使用cvGet2D()函数访问
cvGet * d系列函数可以用来返回特定位置的数组元素(一般使用cvGet2D),原型如下:
1 CvScalar cvGet1D( const CvArr* arr, int idx0 );
2 CvScalar cvGet2D( const CvArr* arr, int idx0, int idx1 );
3 CvScalar cvGet3D( const CvArr* arr, int idx0, int idx1, int idx2 );
4 CvScalar cvGetND( const CvArr* arr, int* idx );
(1)单通道图像像素访问
1 /*
2 @author:CodingMengmeng
3 @theme:Read the image pixel values
4 @time:2017-3-16 15:12:57
5 @blog:http://www.cnblogs.com/codingmengmeng/
6 */
7 #include <cv.h>
8 #include <highgui.h>
9 using namespace std;
10 using namespace cv;
11 int main(void)
12 {
13 IplImage* imgSrc = cvLoadImage("./inputData\\shuke1.jpg",0);
14 CvScalar pixel_v;
15 /*
16 CvScalar是一个可以用来存放4个double数值的数组
17 一般用来存放像素值(不一定是灰度值哦)的,最多可以存放4个通道的
18 如何赋值:
19 a) 存放单通道图像中像素:cvScalar(255);
20 b) 存放三通道图像中像素:cvScalar(255,255,255);
21 c)只使用第一个通道,val[0]=val0;等同于cvScalar(val0,0,0,0);
22 */
23 for (int i = 0; i < imgSrc->height; i++)
24 {
25 for (int j = 0; j < imgSrc->width; j++)
26 {
27 pixel_v = cvGet2D(imgSrc, i, j);
28 cout << "pixel=" << pixel_v.val[0] << endl;
29 }
30 }
31 return 0;
32 }
注意:
对于图像中的某一像素点P(x,y),在我们正常的坐标系中,x代表其横坐标,y代表其纵坐标;而在opencv的函数cvGet2D()的函数原型是:CvScalar cvGet2D (const CvArr * arr,int idx0,int idx1); 函数返回的是一个CvScalar容器,其参数中也有两个方向的坐标,但跟我们平常习惯的坐标不一样的是,idx0代表是的行,即高度,对应于我们平常坐标系的y,idx1代表的是列,即宽度,对应于我们平常坐标系的X使用时请千万别弄反,否则容易出现溢出引发异常。
(2)多通道字节型/浮点型图像像素访问
1 /*
2 @author:CodingMengmeng
3 @theme:Read the image pixel values
4 @time:2017-3-16 15:18:29
5 @blog:http://www.cnblogs.com/codingmengmeng/
6 */
7 #include <cv.h>
8 #include <highgui.h>
9 using namespace std;
10 using namespace cv;
11 int main(void)
12 {
13 IplImage* imgSrc = cvLoadImage("./inputData\\shuke1.jpg");
14 CvScalar pixel_v;
15 /*
16 CvScalar是一个可以用来存放4个double数值的数组
17 一般用来存放像素值(不一定是灰度值哦)的,最多可以存放4个通道的
18 如何赋值:
19 a) 存放单通道图像中像素:cvScalar(255);
20 b) 存放三通道图像中像素:cvScalar(255,255,255);
21 c)只使用第一个通道,val[0]=val0;等同于cvScalar(val0,0,0,0);
22 */
23 for (int i = 0; i < imgSrc->height; i++)
24 {
25 for (int j = 0; j < imgSrc->width; j++)
26 {
27 pixel_v = cvGet2D(imgSrc, i, j);
28 cout << "b_pixel=" << pixel_v.val[0] << endl;//B分量
29 cout << "g_pixel=" << pixel_v.val[1] << endl;//G分量
30 cout << "r_pixel=" << pixel_v.val[2] << endl;//R分量
31 cout << "/********************************************/" << endl;
32 }
33 }
34 return 0;
35 }
运行结果: