kmeans算法的原理參考:http://www.cnblogs.com/mikewolf2002/p/3368118.html
下面學習一下opencv中kmeans函數的使用。
首先我們通過OpenCV中的隨機數產生器RNG,生成一些均勻分布的隨機點,這些點的位置對應一副圖像中的像素位置,然后使用kmeans算法對這些隨機點進行分類,並計算出分類簇的中心點。
隨機產生的簇的數量是2到5之間的值,采樣點的數量范圍是1-1000,一維矩陣centers存放kmeans算法結束后,各個簇的中心位置。
//簇的數量
int k, clusterCount = rng.uniform(2, MAX_CLUSTERS+1);
//采樣點的數量
int i, sampleCount = rng.uniform(1, 1001);
Mat points(sampleCount, 1, CV_32FC2), labels;
clusterCount = MIN(clusterCount, sampleCount);
//中心點矩陣
Mat centers(clusterCount, 1, points.type());
printf("clusterCount=%d, sampleCount=%d\n", clusterCount, sampleCount);
//產生多高斯部分的隨機采樣點
for( k = 0; k < clusterCount; k++ )
{
Point center;
center.x = rng.uniform(0, img.cols);
center.y = rng.uniform(0, img.rows);
Mat pointChunk = points.rowRange(k*sampleCount/clusterCount,
k == clusterCount - 1 ? sampleCount :
(k+1)*sampleCount/clusterCount);
printf("rows start=%d rows end=%d\n", k*sampleCount/clusterCount, k == clusterCount - 1 ? sampleCount :
(k+1)*sampleCount/clusterCount);
注意rng.fill函數,會以center點為中心,產生高斯分布的隨機點(位置點),並把位置點保存在矩陣pointChunk中。
//第三個參數中心,第四個參數偏移
rng.fill(pointChunk, CV_RAND_NORMAL, Scalar(center.x, center.y), Scalar(img.cols*0.05, img.rows*0.05));
}
//打亂points中值,第二個參數表示隨機交換元素的數量的縮放因子,總的交換次數dst.rows*dst.cols*iterFactor,第三個參數是個隨機發生器,決定選那兩個元素交換。
randShuffle(points, 1, &rng);
kmeans函數中points為輸入矩陣,其中存儲的是采樣點,labels也是一個一維矩陣,它的size和points一樣,里面存儲的是每個采樣點執行kmeans算法后屬於屬於那一個簇,值為0到clusterCount-1,centers中存放的是kmeans算法結束后每個簇的中心位置。
flags(第7個參數)為KMEANS_PP_CENTERS 表示使用 kmeans++ center initialization by Arthur and Vassilvitskii [Arthur2007]算法決定簇的初始中心,否則就是采用隨機值的方法決定初始中心。
如果flags是CV_KMEANS_USE_INITIAL_LABELS,則需要初始化labels,就是初始指定點的分類。
最后我們在圖像中畫出每個位置點對應的像素,中心位置用藍色的圓圈表示。
//labels中放的是執行kmeans算法后sample中簇的索引
kmeans(points, clusterCount, labels,
TermCriteria( CV_TERMCRIT_EPS+CV_TERMCRIT_ITER, 10, 1.0),
3, KMEANS_PP_CENTERS, centers);
img = Scalar::all(0);
for( i = 0; i < sampleCount; i++ )
{
int clusterIdx = labels.at<int>(i);
Point ipt = points.at<Point2f>(i);
circle( img, ipt, 2, colorTab[clusterIdx], CV_FILLED, CV_AA );
}
cout<<"Center: \n"<<centers<<endl;
//用藍色畫出每個聚類的中心
//有bug,不讓我直接用centers.at<Point2f>(i);,會異常
for( i = 0; i < clusterCount; i++ )
{
Point ipt = Point(centers.at<float>(i*2), centers.at<float>(i*2+1));
circle( img, ipt, 5, Scalar(255,0,0),CV_FILLED, CV_AA );
}
imshow("clusters", img);
下面圖像是5個簇的kmeans聚類結果。
源代碼參考工程:FirstOpenCV15