cvKMeans2接受Seq数据

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

你可能感兴趣的:(数据)