最近,开始学习openCV,准备用openCV来加强自己对图像处理和算法的理解与应用。
下面是自己最近学习cvKMeans2时的一点经验——
在《opencv基础》与《学习opencv》中介绍cvKMeans2时,都只说samples输入样例的浮点矩阵,每个样例一行。而在实际运用时,很多情况都是在运行时才知道样本的大小,并且会随时改变,那只有求助于CvSeq。研究cvKMeans的源码,如下:
1 CV_IMPL int 2 cvKMeans2( const CvArr* _samples, int cluster_count, CvArr* _labels, 3 CvTermCriteria termcrit, int attempts, CvRNG*, 4 int flags, CvArr* _centers, double* _compactness ) 5 { 6 cv::Mat data = cv::cvarrToMat(_samples), labels = cv::cvarrToMat(_labels), centers; 7 if( _centers ) 8 centers = cv::cvarrToMat(_centers); 9 CV_Assert( labels.isContinuous() && labels.type() == CV_32S && 10 (labels.cols == 1 || labels.rows == 1) && 11 labels.cols + labels.rows - 1 == data.rows ); 12 double compactness = cv::kmeans(data, cluster_count, labels, termcrit, attempts, 13 flags, _centers ? ¢ers : 0 ); 14 if( _compactness ) 15 *_compactness = compactness; 16 return 1; 17 }
第6行中,用cv::cvarrToMat将_sample和_labels都转化成Mat,而cvarrToMat的源码如下:
1 static inline Mat cvarrToMat(const CvArr* arr, bool copyData=false, 2 bool allowND=true, int coiMode=0) 3 { 4 if( CV_IS_MAT(arr) ) 5 return Mat((const CvMat*)arr, copyData ); 6 else if( CV_IS_IMAGE(arr) ) 7 { 8 const IplImage* iplimg = (const IplImage*)arr; 9 if( coiMode == 0 && iplimg->roi && iplimg->roi->coi > 0 ) 10 CV_Error(CV_BadCOI, "COI is not supported by the function"); 11 return Mat(iplimg, copyData); 12 } 13 else if( CV_IS_SEQ(arr) ) 14 { 15 CvSeq* seq = (CvSeq*)arr; 16 CV_Assert(seq->total > 0 && CV_ELEM_SIZE(seq->flags) == seq->elem_size); 17 if(!copyData && seq->first->next == seq->first) 18 return Mat(seq->total, 1, CV_MAT_TYPE(seq->flags), seq->first->data); 19 Mat buf(seq->total, 1, CV_MAT_TYPE(seq->flags)); 20 cvCvtSeqToArray(seq, buf.data, CV_WHOLE_SEQ); 21 return buf; 22 } 23 else 24 { 25 CvMat hdr, *cvmat = cvGetMat( arr, &hdr, 0, allowND ? 1 : 0 ); 26 if( cvmat ) 27 return Mat(cvmat, copyData); 28 } 29 return Mat(); 30 }
其中,有对arr进行判断的,判断其是否为Mat,Image或者是Seq。我们先不管其是如何判断的,有这个判断在这,就说明cvKMeans2可以支持Seq数据,于是自己开始尝试写代码。
以下是自己的代码:
1 void test2() //有缺陷,运行到23行会出错。因为clusters中没有元素,故没有位置存放结果 2 { 3 int width=500,height=500; 4 int sample_count=3; 5 int cluster_count=2; 6 7 CvScalar color_tab[2]; 8 color_tab[0] = CV_RGB(255,0,0); 9 color_tab[1] = CV_RGB(0,255,0); 10 11 CvMemStorage *memPoints=cvCreateMemStorage(0); 12 CvMemStorage *memClu=cvCreateMemStorage(0); 13 14 CvSeq *points=cvCreateSeq(CV_32SC2,sizeof(CvSeq),sizeof(CvPoint),memPoints); 15 CvSeq *clusters=cvCreateSeq(CV_32SC1,sizeof(CvSeq),sizeof(int),memClu); 16 CvSeqWriter writer; 17 cvStartAppendToSeq(points,&writer); 18 CV_WRITE_SEQ_ELEM(cvPoint(10,20),writer); 19 CV_WRITE_SEQ_ELEM(cvPoint(50,100),writer); 20 CV_WRITE_SEQ_ELEM(cvPoint(100,50),writer); 21 cvEndWriteSeq(&writer); 22 23 cvKMeans2( points, cluster_count, clusters, 24 cvTermCriteria( CV_TERMCRIT_EPS+CV_TERMCRIT_ITER, 25 10, 0.1)); 26 IplImage *img=cvCreateImage(cvSize(width,height),8,3); 27 cvZero( img ); 28 CvSeqReader readerPoints; 29 CvSeqReader readerClu; 30 cvStartReadSeq(points,&readerPoints,0); 31 cvStartReadSeq(clusters,&readerClu,0); 32 33 for (int i=0;i<sample_count;++i) 34 { 35 CvPoint2D32f point; 36 int cl; 37 CV_READ_SEQ_ELEM(point,readerPoints); 38 CV_READ_SEQ_ELEM(cl,readerClu); 39 cvCircle(img,cvPointFrom32f(point),2,color_tab[cl],CV_FILLED); 40 } 41 42 cvShowImage( "clusters", img ); 43 cvWaitKey(0); 44 }
运行,发现提示如下的错误:
OpenCV Error: Assertion failed (seq->total > 0 && CV_ELEM_SIZE(seq->flags) == se
q->elem_size) in unknown function, file c:\user\vp\ocv\opencv\include\opencv\cxm
at.hpp, line 225
打开源码,发现是cvarrToMat执行转化时,检测参数出错了。于是先检查了points,确定其没有问题;再看clusters,于是明白了,cvKMeans2不会为我们分配内存,clusters应该在函数调用前就用值进行填充,使其与points有一样的大小。于是有一如下的代码
1 void test2()//有缺陷,points应该为浮点型的坐标 2 { 3 int width=500,height=500; 4 int sample_count=3; 5 int cluster_count=2; 6 7 CvScalar color_tab[2]; 8 color_tab[0] = CV_RGB(255,0,0); 9 color_tab[1] = CV_RGB(0,255,0); 10 11 CvMemStorage *memPoints=cvCreateMemStorage(0); 12 CvMemStorage *memClu=cvCreateMemStorage(0); 13 14 CvSeq *points=cvCreateSeq(CV_32SC2,sizeof(CvSeq),sizeof(CvPoint),memPoints); 15 CvSeq *clusters=cvCreateSeq(CV_32SC1,sizeof(CvSeq),sizeof(int),memClu); 16 CvSeqWriter writer; 17 cvStartAppendToSeq(points,&writer); 18 CV_WRITE_SEQ_ELEM(cvPoint(10,20),writer); 19 CV_WRITE_SEQ_ELEM(cvPoint(50,100),writer); 20 CV_WRITE_SEQ_ELEM(cvPoint(100,50),writer); 21 cvEndWriteSeq(&writer); 22 23 int i=0; 24 cvStartAppendToSeq(clusters,&writer); //这里是新添加的,这里的i可以是随意值,只要添加一个占位即可 25 CV_WRITE_SEQ_ELEM(i,writer); 26 CV_WRITE_SEQ_ELEM(i,writer); 27 CV_WRITE_SEQ_ELEM(i,writer); 28 cvEndWriteSeq(&writer); 29 30 cvKMeans2( points, cluster_count, clusters, 31 cvTermCriteria( CV_TERMCRIT_EPS+CV_TERMCRIT_ITER, 32 10, 0.1)); 33 IplImage *img=cvCreateImage(cvSize(width,height),8,3); 34 cvZero( img ); 35 CvSeqReader readerPoints; 36 CvSeqReader readerClu; 37 cvStartReadSeq(points,&readerPoints,0); 38 cvStartReadSeq(clusters,&readerClu,0); 39 40 for (int i=0;i<sample_count;++i) 41 { 42 CvPoint2D32f point; 43 int cl; 44 CV_READ_SEQ_ELEM(point,readerPoints); 45 CV_READ_SEQ_ELEM(cl,readerClu); 46 cvCircle(img,cvPointFrom32f(point),2,color_tab[cl],CV_FILLED); 47 } 48 49 cvShowImage( "clusters", img ); 50 cvWaitKey(0); 51 }
运行,又提示如下错误
OpenCV Error: Assertion failed (type == CV_32F && K > 0) in unknown function, fi
le ..\..\..\..\ocv\opencv\src\cxcore\cxmatrix.cpp, line 860
这次是在cv::kmeans函数中,points的类型不为CV_32F?? 哦~~ cvKMenas2只指定浮点数。故将points的构建代码改成如下所示:
CvSeq *points=cvCreateSeq(CV_32FC2,sizeof(CvSeq),sizeof(CvPoint2D32f),memPoints);
故最后的代码如下:
1 void test2() 2 { 3 int width=500,height=500; 4 int sample_count=3; 5 int cluster_count=2; 6 7 CvScalar color_tab[2]; 8 color_tab[0] = CV_RGB(255,0,0); 9 color_tab[1] = CV_RGB(0,255,0); 10 11 CvMemStorage *memPoints=cvCreateMemStorage(0); 12 CvMemStorage *memClu=cvCreateMemStorage(0); 13 14 CvSeq *points=cvCreateSeq(CV_32FC2,sizeof(CvSeq),sizeof(CvPoint2D32f),memPoints); 15 CvSeq *clusters=cvCreateSeq(CV_32SC1,sizeof(CvSeq),sizeof(int),memClu); 16 CvSeqWriter writer; 17 cvStartAppendToSeq(points,&writer); 18 CV_WRITE_SEQ_ELEM(cvPoint2D32f(10,20),writer); 19 CV_WRITE_SEQ_ELEM(cvPoint2D32f(50,100),writer); 20 CV_WRITE_SEQ_ELEM(cvPoint2D32f(100,50),writer); 21 cvEndWriteSeq(&writer); 22 23 int i=0; 24 cvStartAppendToSeq(clusters,&writer); 25 CV_WRITE_SEQ_ELEM(i,writer); 26 CV_WRITE_SEQ_ELEM(i,writer); 27 CV_WRITE_SEQ_ELEM(i,writer); 28 cvEndWriteSeq(&writer); 29 30 cvKMeans2( points, cluster_count, clusters, 31 cvTermCriteria( CV_TERMCRIT_EPS+CV_TERMCRIT_ITER, 32 10, 0.1)); 33 IplImage *img=cvCreateImage(cvSize(width,height),8,3); 34 cvZero( img ); 35 CvSeqReader readerPoints; 36 CvSeqReader readerClu; 37 cvStartReadSeq(points,&readerPoints,0); 38 cvStartReadSeq(clusters,&readerClu,0); 39 40 for (int i=0;i<sample_count;++i) 41 { 42 CvPoint2D32f point; 43 int cl; 44 CV_READ_SEQ_ELEM(point,readerPoints); 45 CV_READ_SEQ_ELEM(cl,readerClu); 46 cvCircle(img,cvPointFrom32f(point),2,color_tab[cl],CV_FILLED); 47 } 48 49 cvShowImage( "clusters", img ); 50 cvWaitKey(0); 51 }
上面的代码有内存泄漏,cvCreateImage出来的图像,应该用cvReleaseImage释放
cvCreateMemStorage出来的内存,应该用ReleaseMenStorage释放。