问题场景:

我需要从一幅图像中截出一小块,把它转成一维向量。开始是这样做的


IplImage *result;
CvSize size;
size.width=rectInImage.width;
size.height=rectInImage.height;
result=cvCreateImage( size, orgImage->depth, orgImage->nChannels );
//从图像中提取子图像
cvSetImageROI(orgImage,rectInImage);
cvCopy(orgImage,result);
cvResetImageROI(orgImage);
//把CvMat转成IplImage
cvGetMat(result, matRect, NULL, 0 );
//转成一维向量
CvMat vecHead,*vec;
vec=cvReshape( matRect, &vecHead, 0, 1 )



但该程序运行到cvReshape时 出错,提示:


Image step is wrong(The matrix is not continuous,thus its number of rows can not be changed)



在google上搜了一下,也有人出过类似的错,地址如下:


​ http://www.opencv.org.cn/forum/viewtopic.php?f=1&t=4699&start=0&st=0&sk=t&sd=a ​


按上面的帖子中的一种方法改了程序,程序通过了,即把我程序的cvGetMat句换成下面的



matRect->data.ptr=(uchar*)result->imageData;



但仍担心会出错,因为怕这种给指针直接赋值的方法不保险,如果result被撤消会影响到matRect的值。于是采用跟踪的方法仔细研究了一下cvGetMat和cvReshape.


//cvGetMat是从不确定数组返回矩阵头。
CvMat* cvGetMat( const CvArr* arr, CvMat* header, int* coi=NULL, int allowND=0 );



矩阵header只是根据arr生成一个矩阵头,而其数据指向arr的数据。

I

plImage类型的数据,其widthStep比同样大小的CvMat数据的step大,对于无符号整型数据,IplImage->widthStep=CvMat->step+1. 当arr是IplImage类型时,cvGetMat所得到的header->widthStep=arr->step,也就是说比用cvCreateMat生成的矩阵的step值大,这样接下来再使用cvReshpae函数时就会提示“Image step is wrong”


cvReshape函数也是只改变矩阵的头,数据部分不变。


常用方式(见openCV的帮助)

1.
CvMat row_header, *row;
row = cvReshape( mat, &row_header, 0, 1 );
//运行后row=&row_header,row->data.ptr=mat->data.ptr
//cvReshape的第一个参数可以是IplImage,但是所产生的CvMat在调用其他函数时是否会再出现“Image step is wrong”的错就不好说了(因为没时间,所以没试)。
//我的程序最后改为下述代码实现。
2.
cvSetImageROI(srcImg,blockRect);//blockRect为CvRect类型
cvCopy(srcImg,block);//srcImg为IplImage类型,block为CvMat类型
cvResetImageROI(srcImg);
CvMat vecHead,*vec;
vec=cvReshape( block, &vecHead, 0, 1 );//vec是得到的一维向量




总结:

1 同样大小的IplImage和CvMat,IplImage->widthStep不等于CvMat->step;


2 cvGetMat和cvReshape都只生成一个新的矩阵头,而数据都指向原来的地址,所以是两个矩阵共有一组数据,这一点在使用中要注意,原来的数据撤消是否会影响后生成的矩阵的使用。


3 cvGetMat得到的矩阵的step,等于原来IplImage的widthStep,再调用cvReshape时会出错。


4 cvReshape是按行形成向量,如果想按列形成向量,就先调用cvTranspose对矩阵进行转置,再调用cvReshape.


5 用cvCopy可以在IplImage和CvMat之间转换,比cvGetMat好,但其数据必须是同样的type和size.


6 如果是不同的type之间转换,可以用cvScale.