OpenCV】访问图像中每个像素的值

17 篇文章 0 订阅

目录(?)[+]

转载请注明出处:http://blog.csdn.net/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

[cpp]  view plain copy
  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;  

直接访问:

对我们来说比较重要的两个元素是:char *imageData以及widthStep。imageData存放图像像素数据,而widStep类似CvMat中的step,表示以字节为单位的行数据长度。

一个m*n的单通道字节型图像,其imageData排列如下:


如果我们要遍历图像中的元素,只需:

[cpp]  view plain copy
  1. IplImage* img=cvCreateImage(cvSize(640,480),IPL_DEPTH_8U,1);  
  2. uchar* tmp;  
  3. for(int i=0;i<img->height;i++)  
  4.     for(int j=0;j<img->width;j++)  
  5.         *tmp=((uchar *)(img->imageData + i*img->widthStep))[j];  

这种直接访问的方法速度快,但容易出错,我们可以通过定义指针来访问。即:

[cpp]  view plain copy
  1. IplImage* img=cvCreateImage(cvSize(640,480),IPL_DEPTH_8U,1);  
  2. ucha* data=(uchar *)img->imageData;  
  3. int step = img->widthStep/sizeof(uchar);  
  4. uchar* tmp;  
  5. for(int i=0;i<img->height;i++)  
  6.     for(int j=0;j<img->width;j++)  
  7.         *tmp=data[i*step+j];  

而多通道(三通道)字节图像中,imageData排列如下:

其中(Bi,Bj)(Gi,Gj)(Ri,Rj)表示图像(i,j)处BGR分量的值。使用指针的遍历方法如下:

[cpp]  view plain copy
  1. IplImage* img=cvCreateImage(cvSize(640,480),IPL_DEPTH_8U,1);  
  2. IplImage* img=cvCreateImage(cvSize(640,480),IPL_DEPTH_8U,3);  
  3. uchar* data=(uchar *)img->imageData;  
  4. int step = img->widthStep/sizeof(uchar);  
  5. int channels = img->nChannels;  
  6. uchar *b,*g,*r;  
  7. for(int i=0;i<img->height;i++)  
  8.      for(int j=0;j<img->width;j++){  
  9.            *b=data[i*step+j*chanels+0];  
  10.            *g=data[i*step+j*chanels+1];  
  11.            *r=data[i*step+j*chanels+2];  
  12.       }  

*如果要修改某像素值,则直接赋值。

使用cvGet2D()函数访问:

cvGet*D系列函数可以用来返回特定位置的数组元素(一般使用cvGet2D),原型如下:
[cpp]  view plain copy
  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 );  
idx0,idx1,idx2分别用来指示元素数组下标,即cvGet2D返回(idx0,idx1)处元素的值。
因此,单通道图像像素访问方式如下:
[cpp]  view plain copy
  1. IplImage* img=cvCreateImage(cvSize(640,480),IPL_DEPTH_8U,1);  
  2. double tmp;  
  3. for(int i=0;i<img->height;i++)  
  4.     for(int j=0;j<img->width;j++)  
  5.         tmp=cvGet2D(img,i,j).val[0];  
多通道字节型/浮点型图像:
[cpp]  view plain copy
  1. IplImage* img=cvCreateImage(cvSize(640,480),IPL_DEPTH_32F,3);  
  2. double tmpb,tmpg,bmpr;  
  3. for(int i=0;i<img->height;i++)  
  4.     for(int j=0;j<img->width;j++){  
  5.         tmpb=cvGet2D(img,i,j).val[0];  
  6.         tmpg=cvGet2D(img,i,j).val[1];  
  7.         tmpr=cvGet2D(img,i,j).val[2];  
  8.     }  
如果是修改元素的值,可用cvSet*D(一般是cvSet2D)函数:
[cpp]  view plain copy
  1. void cvSet1D( CvArr* arr, int idx0, CvScalar value );  
  2. void cvSet2D( CvArr* arr, int idx0, int idx1, CvScalar value );  
  3. void cvSet3D( CvArr* arr, int idx0, int idx1, int idx2, CvScalar value );  
  4. void cvSetND( CvArr* arr, int* idx, CvScalar value );  
这种方法对于任何图像的访问方式是一样的,比较简单,但效率较低,不推荐使用。


版权声明:本文为博主原创文章,未经博主允许不得转载。

56
0
主题推荐
color c语言 opencv
猜你在找
Windows Server 2012 DHCP Server 管理
Windows CE车载应用的实现与相关技术点
数据结构(C版)
SCOM 2012 SP1 管理
Qt基础与Qt on Android入门
查看评论
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效率我觉得就会慢。
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值