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 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章