利用k-means算法对灰度图像分割

本文主要利用k-means来对灰度图像进行分割。首先对k-means进行简单的介绍,然后直接上代码。那么什么是k-means算法?K-means算法是硬聚类算法,是典型的基于原型的目标函数聚类方法的代表,它是数据点到原型的某种距离作为优化的目标函数,利用函数求极值的方法得到迭代运算的调整规则。K-means算法以欧式距离作为相似度测度,它是求对应某一初始聚类中心向量V最优分类,使得评价指标J最小。算法采用误差平方和准则函数作为聚类准则函数。这是百度百科的介绍,说了这么多,其实作者也不知道百度百科在说啥,那就来看看基本的算法步骤吧。基本的算法步骤如下: 
1. 从N个样本随机选取K个文本作为质心; 
2. 对剩余每个样本测量其到每个质心的距离,并把它归到最近的质心类 
3. 重新计算已经得到的各个类的质心; 
4. 迭代2、3步骤直至新的质心与原质心相等或小于指定的阈值,算法结束。 
这下算法就比较清晰,下面直接贴代码,我的代码主要定义了point.h头文件、kmeans.h头文件和kmeans.cpp源文件。下面是point.h文件的代码。

#ifndef POINT_H
#define POINT_H
//point结构主要用来存储图像中节点的横坐标,纵坐标以及灰度值
struct point
{
    int row;
    int col;
    double pixVal;
    point(int row, int col, double pixVal) :row(row),col(col),pixVal(pixVal) {}
};
#endif

接下来,是keams.h头文件:

#ifndef KMEANS_H
#define KMEANS_H

#include<opencv2\opencv.hpp>
#include<random>
#include<time.h>
#include<stdio.h>
#include<stdlib.h>
#include<list>
#include<iostream>
#include<math.h>
#include"point.h"

using namespace cv;
using namespace std;

class Kmeans{
private:
    //存储所有点
    vector<point> points;
    //存储簇的中心点
    vector<point> centers;
    //存储每个点到相应的簇
    vector<point>* clusters;
    //向量的维数
    int dimension;
    //簇的个数
    int k;
public:
    //构造函数
    Kmeans(vector<point> points, vector<point> centers, int k, int dimension)
    {
        this->points = points;
        this->centers = centers;
        this->dimension = dimension;
        this->k = k;
        clusters = new vector<point>[k];
    }

    //析构函数
    ~Kmeans()
    {
        delete clusters;
    }

    //获取簇
    vector<point>* getClusters()
    {
        return this->clusters;
    }

    //计算两个向量之间的欧式距离
    double getDistanceBetweenTwoPoints(const point& point1, const point& point2)
    {
        double sum = 0;
        //double tmp;
        //for (int i = 0; i < dimension; i++)
        //{
        //tmp = pow(point1.pixVal - point2.pixVal,2);
        //sum += tmp;
        //}
        sum = pow(point1.pixVal - point2.pixVal, 2);
        return sqrt(sum);
    }

    //计算每个点到离它最近的簇中心点,结果保存到vector中
    vector<int> getClosetClusterCenterLabel()
    {
        double min;
        int label;
        vector<int> labels;
        for (int i = 0; i < points.size(); i++)
        {
            label = 0;
            min = getDistanceBetweenTwoPoints(points[i], centers[0]);
            for (int j = 1; j < centers.size(); j++)
            {
                double tmp = getDistanceBetweenTwoPoints(points[i], centers[j]);
                if (tmp < min)
                {
                    min = tmp;
                    label = j;
                }
            }
            labels.push_back(label);
        }
        return labels;
    }

    //将每个点放入它离的最近的中心点对应的簇中
    void computeClusters(const vector<int>& labels)
    {
        for (int i = 0; i < k; i++)
        {
            clusters[i].clear();
        }
        for (int i = 0; i < labels.size(); i++)
        {
            int label = labels[i];
            clusters[label].push_back(points[i]);
        }
    }

    //重新计算所有簇的中心点的灰度值
    void computeCenters()
    {
        centers.clear();
        for (int i = 0; i < k; i++)
        {
            double sum = 0;
            for (int j = 0; j < clusters[i].size(); j++)
            {
                sum += clusters[i][j].pixVal;
            }
            double meanVal = sum / clusters[i].size();
            point cp(-1, -1, meanVal);
            centers.push_back(cp);
        }
    }

    //确定新的中心点后重新计算一次cost
    double computeCost()
    {
        double sum = 0;
        for (int i = 0; i < k; i++)
        {
            vector<point> tmpVec=clusters[i];
            for (int j = 0; j < tmpVec.size(); j++)
            {
                sum += getDistanceBetweenTwoPoints(tmpVec[j], centers[i]);
            }
        }
        return sum / points.size();
    }

    //迭代执行k-means算法的步骤
    void kmeans()
    {
        double oldCost, newCost;
        vector<int> labels=getClosetClusterCenterLabel();
        computeClusters(labels);
        newCost = computeCost();

        computeCenters();
        labels = getClosetClusterCenterLabel();
        computeClusters(labels);
        oldCost = newCost;
        newCost = computeCost();

        while (oldCost != newCost)
        {
            oldCost = newCost;
            computeCenters();
            labels = getClosetClusterCenterLabel();
            computeClusters(labels);
            newCost = computeCost();
        }
        cout <<"Final Cost: "<< newCost << endl;
    }
};
#endif

接下来,是测试的kmeans.cpp文件:

#include "kmeans.h"
//图片的存放位置
const String imageFolder = "F:\\";
//簇的个数(即k的大小,根据自己需要调整)
const int numOfCluster =4;
//最大像素值
const int MAX_PIX_VALUE = 255;
//存放所有点
vector<point> points;
//存放所有簇中心
vector<point> centers;
//存放所有点颜色特征(i,j)->i*rows+j
vector<double> pixVec;

//读取图像
Mat readImage(String imageName)
{
    String imageLoc = imageFolder + imageName;
    Mat image=imread(imageLoc);
    return image;
}

//初始化k-means聚类中心
void initializeCenters(const Mat& img)
{
    srand((unsigned)time(NULL));
    for (int i = 0; i < numOfCluster; i++)
    {
        int randomX = rand() % img.rows;
        int randomY = rand() % img.cols;
        uchar pixVal = img.at<uchar>(randomX, randomY);
        point cp(randomX, randomY, (double)pixVal);
        centers.push_back(cp);
    }
}

//将图像中的所有点装入points中
void initializePoints(const Mat& img)
{
    for (int i = 0; i < img.rows; i++)
    {
        const uchar* data = img.ptr<uchar>(i);
        for (int j = 0; j < img.cols; j++)
        {
            uchar pixVal = data[j];
            point p(i,j, (double)pixVal);
            points.push_back(p);
        }
    }
}

int main()
{
    String imageName = "lena.jpg";
    Mat img = readImage(imageName);
    cvtColor(img, img, CV_RGB2GRAY);//转化为灰度图像
    namedWindow(imageName,WINDOW_NORMAL);
    imshow(imageName, img);
    waitKey(0);
    int rows = img.rows;
    int cols = img.cols;
    initializeCenters(img);
    initializePoints(img);
    Kmeans* km=new Kmeans(points, centers, numOfCluster, 1);
    cout << "---------------k-means start-------------" << endl;
    km->kmeans();
    cout << "---------------k-means end---------------" <<endl;
    vector<point>* clusters = km->getClusters();
    Mat res(img.rows,img.cols,img.type());
    double div = MAX_PIX_VALUE / numOfCluster;
    for (int i = 0; i < numOfCluster; i++)
    {
        vector<point> tmpVec = clusters[i];
        for (int j = 0; j < tmpVec.size(); j++)
        {
            res.at<uchar>(tmpVec[j].row, tmpVec[j].col) = i*div;
        }
    }
    namedWindow("kmeansResult",WINDOW_NORMAL);
    imshow("kmeansResult", res);
    waitKey(0);
    imwrite("./segment_lena.jpg", res);
    system("pause");
}
--------------------- 
作者:Rainbowzhouwen 
来源:CSDN 
原文:https://blog.csdn.net/u014612806/article/details/65442061 
版权声明:本文为博主原创文章,转载请附上博文链接!

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值