Xiangism

从一个无知角落里开始,蹒跚学步,一个未知到另一个未知,在跌跌撞撞中越走越快,越走越远,最后宇宙也为之开源。对于探索者来说,最后他们的思想总是变得和自己的足迹一样伟大。
  博客园  :: 首页  :: 联系 :: 管理

cvKMeans2接受Seq数据

Posted on 2012-10-26 12:31  Xiangism  阅读(1665)  评论(0编辑  收藏  举报

最近,开始学习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 ? &centers : 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释放。