1. 程式人生 > >opencv——GMM影象分割

opencv——GMM影象分割

GMM即高斯混合模型,GMM加上貝葉斯就能對影象進行分割。

在說高斯混合模型之前,得先認識單高斯模型,即高斯分佈(正態分佈),由圖可知,以某個點為例,它的高斯分佈含義:離該點越近其權重越大影響越大,越遠其權重越小影響越小,中心點的大小要受到周圍點的影響。比如  5 _ 10 _ _ 6,以10為中心點的高斯分佈,_代表距離,因為5離10更近,權值更大,設為0.8,則5變成5*0.8=4。因為6離10更遠,權值更小,設為0.4,則6變成6*0.4=2.4。

高斯分佈
高斯分佈

對於GMM高斯混合模型:圖中是一個直方圖,我用曲線去大概模擬走勢,可以看到為凹凸曲線,仔細想想,是不是像一個個單高斯分佈組合起來的?GMM高斯混合模型就是一個個單高斯分佈組合的。可以用來擬合直方圖。

直方圖

 

混合高斯模型去擬合直方圖

 

影象處理知識:對於影象明顯的類別,在其直方圖上可以明顯看出,若直方圖上存在兩個波峰,則影象分為兩類。

貝葉斯:貝葉斯就是一個概率估計演算法,就是估計一個東西最有可能屬於哪類。

 影象分割:對於上圖,由直方圖可以認為影象分為兩類,即A和B類。現在要判斷每個畫素點屬於哪一類。以K畫素點為例,我們能從經驗認為,它有很大概率屬於A類。 如何對每個畫素點都能自動分類呢?用貝葉斯演算法,關於貝葉斯演算法,可以自查資料,這樣就把影象每個畫素點都找到屬於自己的類別。

以上都是自己對於GMM演算法的個人理解。

// GMM——影象分割.cpp: 定義控制檯應用程式的入口點。
//

#include<opencv2\opencv.hpp>
using namespace cv;
using namespace ml;

int main(int arc, char** argv) {
	Mat src = imread("C:/Users/zhang/Desktop/1.png");
	imshow("input", src);

	int width = src.cols;
	int height = src.rows;
	int dims = src.channels();
	int pointsCount = width * height;//總共資料點的個數

	Mat points(pointsCount, dims, CV_64FC1);//輸入資料,與原影象有著相同通道 
	Mat labels;//輸出資料,為各個資料點最終的分類索引
	int k = 3;//分別個數
	Scalar color[] = { //每個分類的顏色
		Scalar(0,0,255), 
		Scalar(0,255,0), 
		Scalar(255,0,0)
	};

	//將影象轉換為資料點
	int index = 0;
	for (int i = 0; i < height; i++) {
		for (int j = 0; j < width; j++) {
			//把RGB影象的三個通道的各個畫素點值分別賦給points的三個通道
			index = i * width + j;
			points.at<double>(index, 0) = src.at<Vec3b>(i, j)[0];
			points.at<double>(index, 1) = src.at<Vec3b>(i, j)[1];
			points.at<double>(index, 2) = src.at<Vec3b>(i, j)[2];
		}
	}
	//GMM分割(基於高斯混合模型的期望最大值)
	TermCriteria criteria = TermCriteria(TermCriteria::COUNT + TermCriteria::EPS, 100, 0.1);
	//10代表最大迴圈數目,1.0代表閾值
	Ptr<EM> em = EM::create();
	em->setClustersNumber(k);//分類個數
	em->setCovarianceMatrixType(EM::COV_MAT_SPHERICAL);//協方差矩陣型別
	em->setTermCriteria(criteria);//停止條件
	em->trainEM(points, noArray(), labels, noArray());
	//第一個:表示輸入的資料集合,可以一維或者多維資料,型別是Mat型別,
	//第二個:可選項,輸出一個矩陣,裡面包含每個樣本的似然對數值,如果不需要則為noArray()
	//第三個:labels表示計算之後各個資料點的最終的分類索引,是一個INT型別的Mat物件,型別和長寬與原影象一致
	//第四個://可選項,輸出一個矩陣,裡面包含每個隱性變數的後驗概率,如果不需要則為noArray()

	//將資料點轉換為影象並顯示
	Mat result = Mat::zeros(src.size(), CV_8UC3);
	for (int i = 0; i < height; i++) {
		for (int j = 0; j < width; j++) {
			index = i * width + j;
			int label = labels.at<int>(index, 0);////每個畫素點的標籤
			//把每個畫素點對應的標籤所對應的顏色賦給新影象
			result.at<Vec3b>(i, j)[0] = color[label][0];
			result.at<Vec3b>(i, j)[1] = color[label][1];
			result.at<Vec3b>(i, j)[2] = color[label][2];
		}
	}
	imshow("output", result);
	waitKey(0);
	return 0;
}

結果:分為3類

 

 

GMM進階:如何確定應該分為幾類?如何進行好壞評估?可以看看下面資料

https://blog.csdn.net/wangxiaopeng0329/article/details/53542606

https://blog.csdn.net/xiligey1/article/details/82457271