转载请注明出处:http://blog.youkuaiyun.com/xiaowei_cqu/article/details/7557063
!!此篇是基于IplImage* (C接口或者说2.1之前版本的接口,新的Mat的访问方式请参考博文:
IplImage是OpenCV中CxCore部分基础的数据结构,用来表示图像,其中Ipl是Intel Image Processing Library的简写。以下是IplImage的结构分析(来自OpenCV中文网站:http://www.opencv.org.cn/index.php/Cxcore%E5%9F%BA%E7%A1%80%E7%BB%93%E6%9E%84#IplImage)
- typedef struct _IplImage
- {
- int nSize; /* IplImage大小 */
- int ID; /* 版本 (=0)*/
- int nChannels; /* 大多数OPENCV函数支持1,2,3 或 4 个通道 */
- int alphaChannel; /* 被OpenCV忽略 */
- int depth; /* 像素的位深度: IPL_DEPTH_8U, IPL_DEPTH_8S, IPL_DEPTH_16U,
- IPL_DEPTH_16S, IPL_DEPTH_32S, IPL_DEPTH_32F and IPL_DEPTH_64F 可支持 */
- char colorModel[4]; /* 被OpenCV忽略 */
- char channelSeq[4]; /* 同上 */
- int dataOrder; /* 0 - 交叉存取颜色通道, 1 - 分开的颜色通道.
- cvCreateImage只能创建交叉存取图像 */
- int origin; /* 0 - 顶—左结构,
- 1 - 底—左结构 (Windows bitmaps 风格) */
- int align; /* 图像行排列 (4 or 8). OpenCV 忽略它,使用 widthStep 代替 */
- int width; /* 图像宽像素数 */
- int height; /* 图像高像素数*/
- struct _IplROI *roi;/* 图像感兴趣区域. 当该值非空只对该区域进行处理 */
- struct _IplImage *maskROI; /* 在 OpenCV中必须置NULL */
- void *imageId; /* 同上*/
- struct _IplTileInfo *tileInfo; /*同上*/
- int imageSize; /* 图像数据大小(在交叉存取格式下imageSize=image->height*image->widthStep),单位字节*/
- char *imageData; /* 指向排列的图像数据 */
- int widthStep; /* 排列的图像行大小,以字节为单位 */
- int BorderMode[4]; /* 边际结束模式, 被OpenCV忽略 */
- int BorderConst[4]; /* 同上 */
- char *imageDataOrigin; /* 指针指向一个不同的图像数据结构(不是必须排列的),是为了纠正图像内存分配准备的 */
- }
- IplImage;
直接访问:
对我们来说比较重要的两个元素是:char *imageData以及widthStep。imageData存放图像像素数据,而widStep类似CvMat中的step,表示以字节为单位的行数据长度。
一个m*n的单通道字节型图像,其imageData排列如下:
如果我们要遍历图像中的元素,只需:
- IplImage* img=cvCreateImage(cvSize(640,480),IPL_DEPTH_8U,1);
- uchar* tmp;
- for(int i=0;i<img->height;i++)
- for(int j=0;j<img->width;j++)
- *tmp=((uchar *)(img->imageData + i*img->widthStep))[j];
这种直接访问的方法速度快,但容易出错,我们可以通过定义指针来访问。即:
- IplImage* img=cvCreateImage(cvSize(640,480),IPL_DEPTH_8U,1);
- ucha* data=(uchar *)img->imageData;
- int step = img->widthStep/sizeof(uchar);
- uchar* tmp;
- for(int i=0;i<img->height;i++)
- for(int j=0;j<img->width;j++)
- *tmp=data[i*step+j];
而多通道(三通道)字节图像中,imageData排列如下:
其中(Bi,Bj)(Gi,Gj)(Ri,Rj)表示图像(i,j)处BGR分量的值。使用指针的遍历方法如下:
- IplImage* img=cvCreateImage(cvSize(640,480),IPL_DEPTH_8U,1);
- IplImage* img=cvCreateImage(cvSize(640,480),IPL_DEPTH_8U,3);
- uchar* data=(uchar *)img->imageData;
- int step = img->widthStep/sizeof(uchar);
- int channels = img->nChannels;
- uchar *b,*g,*r;
- for(int i=0;i<img->height;i++)
- for(int j=0;j<img->width;j++){
- *b=data[i*step+j*chanels+0];
- *g=data[i*step+j*chanels+1];
- *r=data[i*step+j*chanels+2];
- }
*如果要修改某像素值,则直接赋值。
使用cvGet2D()函数访问:
cvGet*D系列函数可以用来返回特定位置的数组元素(一般使用cvGet2D),原型如下:
- CvScalar cvGet1D( const CvArr* arr, int idx0 );
- CvScalar cvGet2D( const CvArr* arr, int idx0, int idx1 );
- CvScalar cvGet3D( const CvArr* arr, int idx0, int idx1, int idx2 );
- CvScalar cvGetND( const CvArr* arr, int* idx );
因此,单通道图像像素访问方式如下:
- IplImage* img=cvCreateImage(cvSize(640,480),IPL_DEPTH_8U,1);
- double tmp;
- for(int i=0;i<img->height;i++)
- for(int j=0;j<img->width;j++)
- tmp=cvGet2D(img,i,j).val[0];
- IplImage* img=cvCreateImage(cvSize(640,480),IPL_DEPTH_32F,3);
- double tmpb,tmpg,bmpr;
- for(int i=0;i<img->height;i++)
- for(int j=0;j<img->width;j++){
- tmpb=cvGet2D(img,i,j).val[0];
- tmpg=cvGet2D(img,i,j).val[1];
- tmpr=cvGet2D(img,i,j).val[2];
- }
如果是修改元素的值,可用cvSet*D(一般是cvSet2D)函数:
- void cvSet1D( CvArr* arr, int idx0, CvScalar value );
- void cvSet2D( CvArr* arr, int idx0, int idx1, CvScalar value );
- void cvSet3D( CvArr* arr, int idx0, int idx1, int idx2, CvScalar value );
- void cvSetND( CvArr* arr, int* idx, CvScalar value );
版权声明:本文为博主原创文章,未经博主允许不得转载。
-
顶
- 56
-
踩
- 0
-
猜你在找
21楼 qq_26313129 2015-03-04 14:59发表 [回复]-
-
楼主,能加qq2419918945吗,请教一点问题^_^
20楼 OneDay咖主 2014-10-08 10:14发表 [回复]-
-
学习了
19楼 jiaodachenghao 2014-09-05 11:15发表 [回复]-
-
uchar *b,*g,*r;
for(int i=0;i<img->height;i++)
for(int j=0;j<img->width;j++){
*b=data[i*step+j*chanels+0];
*g=data[i*step+j*chanels+1];
*r=data[i*step+j*chanels+2];
}
楼主这种赋值的方法,我用MFC试了,会出错:首先涉及到b,g,r没有初始化的问题。给他们初始化后会出现内存的错误,不知道是不是我初始化的问题。
后来用二维数组赋值才实现。
18楼 Elly111 2014-07-25 17:17发表 [回复]-
-
IplImage* src = cvLoadImage("image/006.jpg",0); //二值图像
int width = src->width , height = src->height ;
int imgsize = width*height;
//将图像像素值读取到二维素组中
CvScalar s;
// 二维数组的动态生成
int **pix;
pix = new int *[height];
for(int i = 0;i < height;i++)
{pix[i] = new int[width]; }
for(int i = 0;i < height;i++)
for(int j=0; j<width;j++)
{s=cvGet2D(src,i,j);
pix[i][j]=(int)s.val[0]; }
// 将src的像素值通过二维素组传递给新图像seeding
IplImage* seeding = cvCreateImage(cvGetSize(src), 8, 1);
for(int i=0;i<height;i++)
for(int j=0;j<width;j++)
{ seeding->imageData [i*width+j] = (unsigned char) pix[i][j]; }
// 将新的图像像素值读到二维素组中
int **pix1;
pix1 = new int *[height];
for(int i = 0;i < height;i++)
{ pix1[i] = new int[width]; }
for(int i = 0;i < height;i++)
for(int j=0; j<width;j++)
{ s=cvGet2D(seeding,i,j);
pix1[i][j]=(int)s.val[0]; }
问题:二维素组pix与pix1中对应的位置的值不相同,为什么?
(pix与pix1中的值与其对应的图像是匹配的)
Re: algorithm_678 2014-10-24 15:49发表 [回复]-
-
回复u011981018:我也想问同样的问题,cvGet2d与指针直接访问得到的元素值不一样,但是最后显示的图像时一样的,为什么呢?
17楼 wang3857287 2014-07-24 16:42发表 [回复]-
-
博主,你很厉害,很崇拜你。偶像
16楼 猪皮冻 2014-07-09 14:55发表 [回复]-
-
楼主,第一种方法遍历,只能到图像的列的三分之一,图像还是灰度的,楼主遇到这种情况没
Re: 猪皮冻 2014-07-09 15:51发表 [回复]-
-
回复mghhz816210:我想明白了,读入的时候通道数不对
15楼 XiaoShuoYiMei 2014-07-02 21:05发表 [回复]-
-
LZ,又来叨扰了。。。我想把一个cvMat矩阵转化成一幅图片,结果运行出来是灰色的,不知道怎么改,还望指教,不胜感激!!
#include<stdio.h>
#include<cv.h>
#include<highgui.h>
const int m_Height=1024;
const int m_Width=1280;
void main()
{
CvMat* m_MatLUT1=NULL;
if (m_MatLUT1 != NULL)
{
cvReleaseMat(&m_MatLUT1);
m_MatLUT1 = NULL;
}
m_MatLUT1 = cvCreateMat(m_Height,m_Width,CV_32FC1);
for (int i=0;i<m_Height;i++)
{
int* p = m_MatLUT1->data.i+i*(m_MatLUT1->step/sizeof(int));
for (int j=0;j<m_Width;j++)
{
//为每个元素赋值
p[j] =255;
}
}
IplImage* Img = cvCreateImage(cvGetSize(m_MatLUT1),8,1);
cvGetImage(m_MatLUT1,Img);
cvNamedWindow("m_MatLUT1",CV_WINDOW_AUTOSIZE);
cvShowImage("m_MatLUT1",Img);
while(1);
}
Re: xiaowei_cqu 2014-07-07 13:14发表 [回复]-
-
回复XiaoShuoYiMei:你的数据是单通道的,彩色图像需要三通道的
Re: liyuanbhu 2014-07-08 12:45发表 [回复]-
-
回复xiaowei_cqu:单通道数据也可以形成彩色图的,不过需要自定义一个colormap。
14楼 加点水 2014-04-19 13:20发表 [回复]-
-
这么好的博客!!!!这么好的博主,我怎能不赞啊!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
13楼 future_Vin 2013-08-22 12:46发表 [回复]-
-
*tmp=((uchar *)(img->imageData + i*img->widthStep))[j];
有错误,我改成了 *tmp=&((uchar *)(img->imageData + i*img->widthStep))[j];
Re: 拨浪鼓儿 2013-10-03 22:37发表 [回复]-
-
回复chwlfg:改成&肯定是错误的,不能将uchar*转换成unsigned char型,楼主的是正确的,只是我没给tmp指针分配内存。
Re: 拨浪鼓儿 2013-10-03 22:29发表 [回复]-
-
回复chwlfg:改了也是不对啊,我在linux下运行,不管哪一种都有错误。
Re: future_Vin 2013-10-05 11:39发表 [回复]-
-
回复rkshuai:是我错了,tmp=&((uchar *)(img->imageData + i*img->widthStep))[j];语法才正确。 你说的对,要给tmp先分配空间,不过这样不就是赋值给新的空间了么。 我觉得楼主说的通过指针遍历应该是通过指针指向内存空间,然后直接访问吧.
Re: 拨浪鼓儿 2013-10-10 11:04发表 [回复]-
-
回复chwlfg:我也弄错了,如果直接按楼主那么写在windows下运行是没问题的,因为有内存保护机制,但是在linux就会出现段错误,因为tmp只是定义了一个空指针,指向一个不确定的地址。给它开辟一块空间也就分配了一个指定的动态内存,因此是可以的。更准确的方法还是像您说的那样把地址赋过去。
Re: future_Vin 2013-10-05 11:28发表 [回复]-
-
回复rkshuai:我的是win7下VS2012运行成功啊
12楼 sword5212012 2013-05-15 12:00发表 [回复]-
-
请教一下,我读入一幅图像,为什么pImg->nsize不是width和height的乘积?谢谢,还有一个问题,运行上面代码*tmp=data[i*step+j]; 出现错误未初始化局部变量“tmp”?希望可以帮忙解答一下!
Re: 拨浪鼓儿 2013-10-03 22:42发表 [回复]-
-
回复sword5212012:是widthStep和height的乘积,tmp是个指针,需要初始化的,可以写成tmp=(uchar*)malloc(sizeof(uchar)*img->width*img->height);
11楼 zhang1991992 2013-05-14 20:53发表 [回复]-
-
您好!请问Lab色彩空间的也是这样访问吗?谢谢!
10楼 fminsearch 2012-11-06 15:44发表 [回复]-
-
lz,有个地方请教一下,cvGet2D(image, index0, index1) 用这个函数来遍历图像时,如果图像不是方图(width=height),就会报错,寻址越界了,这个是函数的问题吗?没有弄明白它怎么寻址的
Re: xiaowei_cqu 2012-11-06 17:13发表 [回复]-
-
回复fminsearch:不是方图也没关系,可能你width和height写反了
9楼 hahaha 2012-10-27 13:42发表 [回复]-
-
uchar* tmp;
for(int i=0;i<img->height;i++)
for(int j=0;j<img->width;j++)
*tmp=data[i*step+j];
这个代码是有问题的,tmp这个指针指向哪里呢?
另外,遍历像素我一般这么写(自己定义了一个ImageWarp类
封装了opencv的图像类,GetHeight()等都是封装的inline函数)
for (int r = 0; r < GetHeight(); r++)
{
uchar * pLine = GetData() + GetWidthStep() * r;
for (int c = 0; c < GetWidth(); c++)
{
for (int ch = 0; ch < GetChannel(); ch ++)
{
//access pLine[c * GetChannel() + ch]
}
}
}
Re: xiaowei_cqu 2012-10-28 22:08发表 [回复]-
-
回复wutongthucs:tmp指的图像数据。
多谢你的代码~
8楼 cz0987 2012-09-05 22:03发表 [回复]-
-
谢谢你的帮忙!
7楼 cz0987 2012-09-03 22:55发表 [回复]-
-
您好,请教个问题:我在opencv2.1.0和VS2008结合的环境下运行第三段代码时
(b=data[i*step+j*chanels+0];
g=data[i*step+j*chanels+1];
r=data[i*step+j*chanels+2];
)出现错误:error C2440: “=”: 无法从“uchar”转换为“uchar *”,请问如何解决?我想修改像素值为0的话,这样对么(*b=0)?
运行第二段代码(tmp=data[i*step+j];
)也出现同样的错误:无法从“uchar”转换为“uchar *”。谢谢!
Re: xiaowei_cqu 2012-09-03 23:05发表 [回复]-
-
回复cz0987:嗯,我写错了,很抱歉!
Re: cz0987 2012-09-04 21:57发表 [回复]-
-
回复xiaowei_cqu:对于这个问题,你能给说说该怎么修改么?
Re: xiaowei_cqu 2012-09-04 22:00发表 [回复]-
-
回复cz0987: *b=data[i*step+j*chanels+0]; 我在上面改出来了
6楼 zhangleiyiran 2012-08-06 17:37发表 [回复]-
-
请教个问题,就是如果读取完图像RGB后,想将RGB值以普通数据存取起来,怎么就会出现程序泄露?
Re: xiaowei_cqu 2012-08-06 18:31发表 [回复]-
-
回复zhangleiyiran:有地方没释放吧
Re: zhangleiyiran 2012-08-07 09:59发表 [回复]-
-
回复xiaowei_cqu:好像是数据类型不匹配啊
5楼 liucheng17 2012-07-31 09:36发表 [回复]-
-
1,2,3,4段遍历的程序中i,j的顺序反了,正常的应该是这样的
for(int i=0;i<src->height;i++)
for(int j=0;j<src->width;j++)
或者在下面的定位中将i,j调换下位置~
Re: xiaowei_cqu 2012-07-31 09:44发表 [回复]-
-
回复liucheng17:非常感谢~
Re: liucheng17 2012-07-31 10:17发表 [回复]-
-
回复xiaowei_cqu:第三段代码的循环中少了data.....
Re: xiaowei_cqu 2012-07-31 10:29发表 [回复]-
-
回复liucheng17:现在还是比较习惯c++接口的
Re: liucheng17 2012-07-31 10:43发表 [回复]-
-
回复xiaowei_cqu:在看learning opencv,里面貌似都是c接口的,也刚学Qt,比lz晚了好几步啊
Re: xiaowei_cqu 2012-07-31 10:48发表 [回复]-
-
回复liucheng17:感觉learning opencv有点旧了,看源码里的文档吧
4楼 ai625146307 2012-07-10 18:38发表 [回复]-
-
请教个问题,如果我要获取某点的像素坐标,遍历循环的i,j就是吧?还有如果想知道遍历的点是不是特定的颜色(比如红黄蓝绿紫)应该怎么办的?直接用像素值来做比较吗?谢谢啦
Re: xiaowei_cqu 2012-07-10 21:23发表 [回复]-
-
回复ai625146307:嗯,作比较,if...else或switch...case之类的
Re: ai625146307 2012-07-12 20:14发表 [回复]-
-
回复xiaowei_cqu:谢谢了,非常感谢。再打扰下,像素识别哪种格式的图片更好?相机拍摄下来的可以直接用不。还有(255/0/0) 红与(128/0/0) 深红,程序能区分开来不?
谢啦
Re: xiaowei_cqu 2012-07-12 22:14发表 [回复]-
-
回复ai625146307:图像格式我没有仔细研究过;相机格式一般是jpg可以用;红和深红可以区分
3楼 henreash 2012-06-27 11:42发表 [回复]-
-
WidthStep需要字节对齐,很多时候WidthStep不完全等于width*depth*channel,不足32位(CPU位数)的倍数要补齐.因此在遍历图像中的像素时必须使用WidthStep
Re: xiaowei_cqu 2012-06-27 12:14发表 [回复]-
-
回复henreash:哦,原来这样
2楼 passball 2012-06-18 15:17发表 [回复]-
-
用图像数据指针访问的时候,遍历循环的i,j反了吧...
Re: xiaowei_cqu 2012-07-31 09:44发表 [回复]-
-
回复passball:确实反了,谢谢~
1楼 full_notepad 2012-05-28 22:22发表 [回复] [引用] [举报]-
-
图画的很形象
Re: xiaowei_cqu 2012-05-29 10:00发表 [回复] [引用] [举报]-
-
回复full_notepad:谢谢
Re: zhangleiyiran 2012-08-06 17:40发表 [回复] [引用] [举报]-
-
回复xiaowei_cqu:int height,width,step,channels;
uchar *data;
int i,j;
float moban_RGB[3000][3];
float MobanLab[i][0];
// get the image data
height = Image1->height;
width = Image1->width;
step = Image1->widthStep;
channels = Image1->nChannels;
data = (uchar *)Image1->imageData;
for(i=0;i<height;i++)
{
for(j=0;j<width;j++)
{
moban_RGB[i][0]=data[i*step+j*channels+2]; //存取图片R值
moban_RGB[i][1]=data[i*step+j*channels+1]; //存取图片G值
moban_RGB[i][2]=data[i*step+j*channels+0]; //存取图片B值
}
}
这样会出现类型不匹配,如果我不直接赋值,而采取cvmGet效率我觉得就会慢。