在OpenCV里边,widthStep必须是4的倍数,从而实现字节对齐,有利于提高运算速度。如果8U单通道图像宽度为3,那么widthStep是 4,加一个字节补齐。这个图像的一行需要4个字节,只使用前3个,最后一个空着。也就是一个宽3高3的图像的imageData数据大小为4*3=12字 节。
空着的那个像素并不是无效的,它仍然可以被操作:
IplImage* image = cvCreateImage(cvSize(15, 15), 8, 1);
memcpy(image->imageData, data, 15*15);
在cvCreateImage的时候,OpenCV为实现字节对齐,使得每行数据实际有16个字节(多出一个),在使用memcpy的过程 中,这些多出的字节就把对应的数据给“吃”了,因为这些数据在cvShowImage的时候并不会显示出来,这样,第二行就少一个字节,第三行少两个字 节,……,这样会造成整个图像偏向左下角!
此时,应该这样Copy:
for(int i = 0; i<15; i++)...{
memcpy(image->imageData + image->widthStep*i, data + 15*i, 15);
}
IplImage和单字节char*之间相互转换的正确、简洁的方法:
已知 IplImage* image 和 char* data
从 IplImage 到 char* :
data = image->imageData //对齐的图像数据
或
data = image->imageDataOrigin //未对齐的原始图像数据
从 char* 到 IplImage :
image = cvCreateImageHeader(cvSize(width,height), depth, channels);
cvSetData(image, data, step);
step指定IplImage图像每行占的字节数。需要注意是,在释放空间时不能直接使用cvReleaseImage,而需cvReleaseImageHeader,然后再delete data。
cvSetData code from opencv2.3.1(now opencv2.4.3 is out . As usual, the packages are available at SourceForge (http://sourceforge.net/projects/opencvlibrary), or alternatively you can download the source code from https://github.com/Itseez/opencv/tree/2.4):
// Assigns external data to array
CV_IMPL void
cvSetData( CvArr* arr, void* data, int step )
{
int pix_size, min_step;
if( CV_IS_MAT_HDR(arr) || CV_IS_MATND_HDR(arr) )
cvReleaseData( arr );
if( CV_IS_MAT_HDR( arr ))
{
CvMat* mat = (CvMat*)arr;
int type = CV_MAT_TYPE(mat->type);
pix_size = CV_ELEM_SIZE(type);
min_step = mat->cols*pix_size;
if( step != CV_AUTOSTEP && step != 0 )
{
if( step < min_step && data != 0 )
CV_Error( CV_BadStep, "" );
mat->step = step;
}
else
mat->step = min_step;
mat->data.ptr = (uchar*)data;
mat->type = CV_MAT_MAGIC_VAL | type |
(mat->rows == 1 || mat->step == min_step ? CV_MAT_CONT_FLAG : 0);
icvCheckHuge( mat );
}
else if( CV_IS_IMAGE_HDR( arr ))
{
IplImage* img = (IplImage*)arr;
pix_size = ((img->depth & 255) >> 3)*img->nChannels;
min_step = img->width*pix_size;
if( step != CV_AUTOSTEP && img->height > 1 )
{
if( step < min_step && data != 0 )
CV_Error( CV_BadStep, "" );
img->widthStep = step;
}
else
{
img->widthStep = min_step;
}
img->imageSize = img->widthStep * img->height;
img->imageData = img->imageDataOrigin = (char*)data;
if( (((int)(size_t)data | step) & 7) == 0 &&
cvAlign(img->width * pix_size, 8) == step )
img->align = 8;
else
img->align = 4;
}
else if( CV_IS_MATND_HDR( arr ))
{
CvMatND* mat = (CvMatND*)arr;
int i;
int64 cur_step;
if( step != CV_AUTOSTEP )
CV_Error( CV_BadStep,
"For multidimensional array only CV_AUTOSTEP is allowed here" );
mat->data.ptr = (uchar*)data;
cur_step = CV_ELEM_SIZE(mat->type);
for( i = mat->dims - 1; i >= 0; i-- )
{
if( cur_step > INT_MAX )
CV_Error( CV_StsOutOfRange, "The array is too big" );
mat->dim[i].step = (int)cur_step;
cur_step *= mat->dim[i].size;
}
}
else
CV_Error( CV_StsBadArg, "unrecognized or unsupported array type" );
}