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个掩码图,与原图做与就可以得到要分割的部分了。
这里写图片描述
原图
这里写图片描述
聚类结果图
这里写图片描述
四个掩码图

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