OPENCV之Kmeans圖像分割

1.kmeans簡介
雖然 k-means 並不能保證總是能得到全局最優解,但是對於這樣的問題,像 k-means 這種複雜度的算法,這樣的結果已經是很不錯的了。
下面我們來總結一下 k-means 算法的具體步驟:
[1] 選定 K 箇中心 \mu_k 的初值。這個過程通常是針對具體的問題有一些啓發式的選取方法,或者大多數情況下采用隨機選取的辦法。因爲前面說過 k-means 並不能保證全局最優,而是否能收斂到全局最優解其實和初值的選取有很大的關係,所以有時候我們會多次選取初值跑 k-means ,並取其中最好的一次結果。
[2] 將每個數據點歸類到離它最近的那個中心點所代表的 cluster 中。
[3] 用公式 \mu_k = \frac{1}{N_k}\sum_{j\in\text{cluster}_k}x_j 計算出每個 cluster 的新的中心點。
[4] 重複第二步,一直到迭代了最大的步數或者前後的 J 的值相差小於一個閾值爲止。

OPENCV中提供了kmeans具體的實現,這裏就不介紹了。直接上代碼。

Mat labels;
    Mat center;
    Mat p = Mat::zeros(pDoc->m_MatImage.cols*pDoc->m_MatImage.rows, 3, CV_32F);    //初始化全0矩陣
    for (int i = 0; i<pDoc->m_MatImage.cols*pDoc->m_MatImage.rows; i++)
    {
        p.at<float>(i, 0) = (i / pDoc->m_MatImage.cols) / pDoc->m_MatImage.rows;        // p.at<uchar>(y,x) 相當於 p->Imagedata[y *p->widthstep + x], p是8位uchar
        p.at<float>(i, 1) = (i%pDoc->m_MatImage.cols) / pDoc->m_MatImage.cols;        // p.at<float>(y,x) 相當於 p->Imagedata[y *p->widthstep + x], p是32位float
        p.at<float>(i, 2) = pDoc->m_MatImage.at<uchar>(i);                          //灰度值
    }
    vector<int> cenValue;//聚類中心的值,可能以後不需要
    vector<cv::Mat> maskMat;//掩碼矩陣
    kmeans(p, 4, labels, TermCriteria(CV_TERMCRIT_EPS + CV_TERMCRIT_ITER, 100, 1.0), 1, KMEANS_PP_CENTERS, center);//)
    for (int i = 0; i <center.rows; i++)//列是分爲了幾類
    {
        Mat wp(pDoc->m_MatImage.rows,pDoc->m_MatImage.cols,CV_8U,255);
        maskMat.push_back(wp);//初始化掩碼矩陣
    }
    for (int i = 0; i<pDoc->m_MatImage.cols*pDoc->m_MatImage.rows; i++)
    {
        int intensity = labels.at<int>(i);//分爲4類,生成4個msak矩陣
        if (intensity == 0)
        {
        pDoc->m_MatImage.at<uchar>(i) = 0;
            maskMat.at(0).at<uchar>(i) = 0;
        }
        else if (intensity == 1)
        {
        pDoc->m_MatImage.at<uchar>(i) = 100;
            maskMat.at(1).at<uchar>(i) = 0;
        }
        else if (intensity == 2)
        {
        pDoc->m_MatImage.at<uchar>(i) = 200;
            maskMat.at(2).at<uchar>(i) = 0;
        }
        else
        {
        pDoc->m_MatImage.at<uchar>(i) = 255;
            maskMat.at(3).at<uchar>(i) =0;
        }
    }

沒法修改圖片了,第一副是原圖,第二幅是聚類爲4類後用不同的灰度表示,第三幅是4個掩碼圖,與原圖做與就可以得到要分割的部分了。
這裏寫圖片描述
原圖
這裏寫圖片描述
聚類結果圖
這裏寫圖片描述
四個掩碼圖

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