本文为原创,若有错误欢迎批评指正!
一. IplImage结构体构成
IplImage比CvMat要复杂一些,结构体组成如下:
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]; /* 被OpenCV忽略 */
int dataOrder; /* 0 - 交叉存取颜色通道, 意思是在每一行的结构都是BGRBGRBGR...
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;
这里比较重要的参数是height、width、depth和nChannels。
二. 访问图像数据
与CvMat类似,都有三种方法。
首先是函数的方法:
/*函数的方法*/
IplImage* img=cvLoadImage(filename);
CvScalar s; /*sizeof(s) == img->nChannels*/
s=cvGet2D(img,i,j); /*get the (i,j) pixel value*/
cvSet2D(img,i,j,s); /*set the (i,j) pixel value*/
采用宏的方法:
/*宏*/
IplImage* img; //malloc memory by cvLoadImage or cvCreateImage
for(int row = 0; row < img->height; row++)
{
for (int col = 0; col < img->width; col++)
{
b = CV_IMAGE_ELEM(img, UCHAR, row, col * img->nChannels + 0);
g = CV_IMAGE_ELEM(img, UCHAR, row, col * img->nChannels + 1);
r = CV_IMAGE_ELEM(img, UCHAR, row, col * img->nChannels + 2);
}
}
接下来是最推崇的使用指针的方法。在这之前要弄清楚IplImage与Cvmat的一些差别,剩下的用法类似。IplImage在计算行地址的时候也要采用行长度widthStep而不是图像的宽度,与CvMat中step类似。IplImage图像的首地址记作imageData,与CvMat中data类似。IplImage访问时指针设置为(uchar*),而CvMat中data为联合类型,必须说明使用的指针类型。
/*指针访问*/
IplImage* img; //malloc memory by cvLoadImage or cvCreateImage
uchar b, g, r; // 3 channels
for(int row = 0; row < img->height; row++)
{
for (int col = 0; col < img->width; col++)
{
b = ((uchar *)(img->imageData + row * img->widthStep))[col * img->nChannels + 0];
g = ((uchar *)(img->imageData + row * img->widthStep))[col * img->nChannels + 1];
r = ((uchar *)(img->imageData + row * img->widthStep))[col * img->nChannels + 2];
}
}
三. 一个比较综合的应用
这里定义一个稍微复杂一点的应用,包括了对图像的常用操作,基本上足够用来实现其他的图像算法了。
首先读入一幅图像,若为3通道的则转化为一通道,然后将图像右下角1/4置为白色。
#include <opencv2\opencv.hpp>
int main()
{
IplImage *img = cvLoadImage("D:\\_Gonzalez\\ch02\\Fig0205(a)(cktboard_200dpi).tif");
//获取图像的参数
printf("width=%d\n", img->width);
printf("height=%d\n", img->height);
printf("channel=%d\n", img->nChannels);
if (img->nChannels == 3)
{
//新建一个与img一样大的一通道空图像
IplImage *dst = cvCreateImage(cvSize(img->width, img->height), img->depth, 1);
//将彩色图像转化为一通道灰度图
cvCvtColor(img, dst, CV_RGB2GRAY);
img = dst;
}
for (int y = img->height / 2; y < img->height; y++)
{
uchar* ptr = (uchar*)(img->imageData + y*img->widthStep);
for (int x = img->width / 2; x < img->width; x++)
{
ptr[x + 1] = 255;
ptr[x + 2] = 255;
ptr[x + 3] = 255;
}
}
cvNamedWindow("example", CV_WINDOW_AUTOSIZE);
cvShowImage("example", img);
cvWaitKey(0);
cvReleaseImage(&img);
cvDestroyWindow("example");
}
如果将要求改为将三通道的img右下角1/4置为白色,相应操作变成:
for (int y = img->height / 2; y < img->height; y++)
{
uchar* ptr = (uchar*)(img->imageData + y*img->widthStep);
for (int x = img->width / 2; x < img->width; x++)
{
ptr[x + 1] = 255;
ptr[x + 2] = 255;
ptr[x + 3] = 255;
/*也可以是
*(ptr+3*x+1) = 255;
*(ptr+3*x+2) = 255;
*(ptr+3*x+2) = 255;
*/
}
}