(4)对于所有的c个聚类中心,如果利用(2)(3)的迭代法更新后,值保持不变,则迭代结束,否则继续迭代。
用opencv实现的代码如下:
#include "stdafx.h" #include <opencv2/opencv.hpp> #include "highgui.h" #include <math.h> typedef unsigned long uint32; typedef unsigned int uint16; typedef unsigned char uint8; IplImage *src_gray1, *src_gray2, *src_gray3; IplImage* src_img; uint32 dim=0; CvMat *Label; uint16 mt[1000][2]; void AllocateImage(IplImage* I) //给图像分配大小 { CvSize sz = cvGetSize(I); src_gray1 = cvCreateImage( sz, IPL_DEPTH_8U, 1); //原图的三个通道 src_gray2 = cvCreateImage( sz, IPL_DEPTH_8U, 1); src_gray3 = cvCreateImage( sz, IPL_DEPTH_8U, 1); } void DeallocateImage() { cvReleaseImage(&src_img); cvReleaseImage(&src_gray1); cvReleaseImage(&src_gray2); cvReleaseImage(&src_gray3); } void AllocateMatrix(uint32 num) //分配矩阵大小 { Label = cvCreateMat( num, 3, CV_16UC1 ); } void GenerateMatrix() //初始化矩阵,矩阵前两列是非零点的坐标,第三列是标签值,0表示未被分类 { int i; for(i=0; i<Label->rows; i++) { cvSetReal2D(Label, i, 0, mt[i][0]); cvSetReal2D(Label, i, 1, mt[i][1]); cvSetReal2D(Label, i, 2, 0); } } int myabs(int x) //取绝对值 { if(x<0) x=-x; return x; } void myKmeans(CvMat* L, CvPoint* pt) //kmeans算法 { uint32 PCondition; double px,py; double MinDis,Dis; int i,j; double num[4]; double XValue[4]; double YValue[4]; int FLabel; CvPoint Last_Kpt[4]={cvPoint(1,1), cvPoint(1,1), cvPoint(1,1), cvPoint(1,1)}; PCondition = myabs(pt[0].x-Last_Kpt[0].x) + myabs(pt[0].y-Last_Kpt[0].y) + myabs(pt[1].x-Last_Kpt[1].x) + myabs(pt[1].y-Last_Kpt[1].y) + myabs(pt[2].x-Last_Kpt[2].x) + myabs(pt[2].y-Last_Kpt[2].y) + myabs(pt[3].x-Last_Kpt[3].x) + myabs(pt[3].y-Last_Kpt[3].y); while( PCondition>2 ) { Last_Kpt[0] = pt[0]; Last_Kpt[1] = pt[1]; Last_Kpt[2] = pt[2]; Last_Kpt[3] = pt[3]; //对每个点贴上标签 for(i=0; i<Label->rows; i++) { MinDis = 10000; px = cvGetReal2D(L,i,0); py = cvGetReal2D(L,i,1); for(j=0;j<4;j++) { Dis = sqrt( (px-pt[j].x)*(px-pt[j].x) + (py-pt[j].y)*(py-pt[j].y) ); if(Dis<MinDis) { MinDis = Dis; cvSetReal2D(L,i,2,j+1); } } } //变量清零 for(i=0;i<4;i++) { XValue[i] = 0; YValue[i] = 0; num[i] = 0; } //重新计算中心 for(i=0; i<Label->rows; i++) { FLabel = cvGetReal2D(L, i, 2); px = cvGetReal2D(L, i, 0); py = cvGetReal2D(L, i, 1); switch(FLabel) { case 1: XValue[0] += px; YValue[0] += py; num[0]++; break; case 2: XValue[1] += px; YValue[1] += py; num[1]++; break; case 3: XValue[2] += px; YValue[2] += py; num[2]++; break; case 4: XValue[3] += px; YValue[3] += py; num[3]++; break; default: break; } } for(i=0;i<4;i++) { pt[i].x = XValue[i]/num[i]; pt[i].y = YValue[i]/num[i]; } PCondition = myabs(pt[0].x-Last_Kpt[0].x) + myabs(pt[0].y-Last_Kpt[0].y) //计算终止条件 + myabs(pt[1].x-Last_Kpt[1].x) + myabs(pt[1].y-Last_Kpt[1].y) + myabs(pt[2].x-Last_Kpt[2].x) + myabs(pt[2].y-Last_Kpt[2].y) + myabs(pt[3].x-Last_Kpt[3].x) + myabs(pt[3].y-Last_Kpt[3].y); } } uint32 CalFeatureNum(IplImage* I) //计算特征点数目,保存坐标 { int i,j; uint32 num = 0; for ( i=0; i<I->height; i++ ) { uint8* ptr = (uint8*)( I->imageData + i*I->widthStep ); for ( j=0; j<I->width; j++ ) { uint8 Pixel = ptr[j]; if( Pixel == 255 ) { mt[num][0] = j; mt[num][1] = i; num++; } } } return num; } void CalRadius(CvMat* L, CvPoint* pt, double* MaxDis) //计算点群最大半径 { int i,FLabel; double px,py; double dis; for(i=0; i<Label->rows; i++) { FLabel = cvGetReal2D(L, i, 2); px = cvGetReal2D(L, i, 0); py = cvGetReal2D(L, i, 1); switch(FLabel) { case 1: dis = sqrt( (px-pt[0].x)*(px-pt[0].x) + (py-pt[0].y)*(py-pt[0].y) ); if(dis>MaxDis[0]) MaxDis[0] =dis; break; case 2: dis = sqrt( (px-pt[1].x)*(px-pt[1].x) + (py-pt[1].y)*(py-pt[1].y) ); if(dis>MaxDis[1]) MaxDis[1] =dis; break; case 3: dis = sqrt( (px-pt[2].x)*(px-pt[2].x) + (py-pt[2].y)*(py-pt[2].y) ); if(dis>MaxDis[2]) MaxDis[2] =dis; break; case 4: dis = sqrt( (px-pt[3].x)*(px-pt[3].x) + (py-pt[3].y)*(py-pt[3].y) ); if(dis>MaxDis[3]) MaxDis[3] =dis; break; default: break; } } } int _tmain(int argc, _TCHAR* argv[]) { CvPoint Kpt[4]={cvPoint(170,96), cvPoint(511,96), cvPoint(170,288), cvPoint(511,288)}; //初始种子点 double Kmeans_Radius[4] = {0,0,0,0}; //初始半径 src_img = cvLoadImage("kmeans_24bit.bmp"); AllocateImage(src_img); cvSplit( src_img, src_gray1, src_gray2, src_gray3, 0); //将两幅三通道图像分解为3幅单通道图像 dim = CalFeatureNum(src_gray1); //计算特征点的个数,并保存特征点的坐标 AllocateMatrix(dim); //给矩阵分配大小 GenerateMatrix(); //生成用于Kmeans计算的矩阵 myKmeans(Label, Kpt); //Kmeans算法 CalRadius(Label, Kpt, Kmeans_Radius); //计算每个点群的最大半径 cvCircle(src_img,Kpt[0],Kmeans_Radius[0],cvScalar(0,0,255),1,8,0); //将每个点群标出来 cvCircle(src_img,Kpt[1],Kmeans_Radius[1],cvScalar(0,0,255),1,8,0); cvCircle(src_img,Kpt[2],Kmeans_Radius[2],cvScalar(0,0,255),1,8,0); cvCircle(src_img,Kpt[3],Kmeans_Radius[3],cvScalar(0,0,255),1,8,0); cvNamedWindow("my picture",CV_WINDOW_AUTOSIZE); cvShowImage("my picture",src_img); cvWaitKey(0); DeallocateImage(); cvDestroyWindow("my picture"); return 0; }