1. 程式人生 > >OpenCV學習 kmeans實現影象分割

OpenCV學習 kmeans實現影象分割

using namespace std;
using namespace cv;
int main()
{
	const int MAX_CLUSTERS = 5;
	Vec3b colorTab[] =
	{
		Vec3b(0, 0, 255),
		Vec3b(0, 255, 0),
		Vec3b(255, 100, 100),
		Vec3b(255, 0, 255),
		Vec3b(0, 255, 255)
	};
	Mat data, labels;
	Mat pic = imread("C:/Users/dell/Desktop/1.jpg");
	for (int i = 0; i < pic.rows; i++)
	for (int j = 0; j < pic.cols; j++)
	{
		Vec3b point = pic.at<Vec3b>(i, j);
		Mat tmp = (Mat_<float>(1, 3) << point[0], point[1], point[2]);
		data.push_back(tmp);
	}

	//根據瀏覽圖片,確定k=3
	kmeans(data, 4, labels, TermCriteria(TermCriteria::EPS + TermCriteria::COUNT, 10, 1.0),
		3, KMEANS_RANDOM_CENTERS);

	int n = 0;
	//顯示聚類結果,不同的類別用不同的顏色顯示
	for (int i = 0; i < pic.rows; i++)
	for (int j = 0; j < pic.cols; j++)
	{
		int clusterIdx = labels.at<int>(n);
		pic.at<Vec3b>(i, j) = colorTab[clusterIdx];
		n++;
	}
	imshow("pic", pic);
	waitKey(0);

	return 0;
}

基本演算法步驟:
初始化:從 n個數據物件任意選擇 k 個物件作為初始聚類中心;
迭代:
    1. 根據每個聚類物件的均值(中心物件),計算每個物件與這些中心物件的距離;並根據最小距離重新對相應物件進行分類;
    2. 由新的分類資料,重新計算每個(有變化)聚類的均值(中心物件);
    3. 計算標準測度函式,當滿足一定條件,如函式收斂時,則演算法終止;如果條件不滿足則迴圈執行。如opencv中,每次迭代,最大的聚類中心位移max_center_shift < criteria.epsilon小於精度要求時,就結束迭代。以及迭代次數超過設定的最大值時,也結束 iter >= criteria.maxCount。
結束。計算方差,及labels每個物件的分類結果,返回。
演算法的時間複雜度上界為O(n*k*t), 其中t是迭代次數。屬於非監督學習方法。
OpenCV中增加了引數:
attempts:使用不同的初始化條件,進行分類的次數
flag: KMEANS_RANDOM_CENTERS, KMEANS_PP_CENTERS, KMEANS_USE_INITIAL_LABELS
        kmeans演算法的2個缺點:第一是必須人為指定所聚的類的個數k;第二是如果使用歐式距離來衡量相似度的話,可能會得到錯誤的結果,因為沒有考慮到屬性的重要性和相關性。為了減少這種錯誤,在使用kmeans距離時,一定要使樣本的每一維資料歸一化,不然的話由於樣本的屬性範圍不同會導致錯誤的結果。