C均值算法(K-means)在opencv中实现图像分割(抠图)

学习了模式识别中的K均值算法,之后自己琢磨着实现了图像分割(受到刘波老师ppt上的启发),经过一下午的时间,终于实现了图像分割。后来才发想opencv中自带kmeans算法,而且直接调用就行。

下面就是介绍和自己写的源代码。

分类:

监督学习:给定已知类别的学习样本,设计分类器。
非监督学习:给定未知(未知类别及类别数)样本,设计分类器。
两大类非监督学习:
基于概率密度函数估计的直接方法
基于样本间相似性(similarity)度量的间接聚类方法。

而K均值就是属于无监督学习中间接聚类方法中的动态聚类

C-均值算法(k-means, k-均值)
      对样本集KN={xi}尚不知每个样本的类别,但可假设所有样本可分为c类,各类样本在特征空间依类聚集,且近似球形分布;
      用一代表点(prototype)来表示一个聚类,如类内均值mi来代表聚类Ki;
     聚类准则:误差平方和J

《C均值算法(K-means)在opencv中实现图像分割(抠图)》

流程:

Step 1. 初始化:随机选择k个样本点,并将其视为各聚
            类的初始中心m1,…,mk ;
Step 2. 按照最小距离法则逐个将样本x划分到以聚类中        
            心m1,…,mk为代表的k个类C1,…,Ck中;
Step 3. 计算聚类准则函数J,重新计算k个类的聚类中心
            m1,…,mk ;
Step 4. 重复Step 2和 3直到聚类中心m1,…,mk无改变
             或目标函数J不减小。

自己写的算法(完全自己实现)

/***************
name:testfun.cpp
介绍:这是利用模式识别里的动态聚类法(C均值法)来实现图像分割
是自己根据模式识别书上的思想弄个出来的
designed by Wupeng 
created 2015-12-19
基于k均值(无监督)
********************/
#include<opencv2/core/core.hpp>
#include<opencv2/highgui/highgui.hpp>
#include<opencv2/imgproc/imgproc.hpp>
#include<iostream>
#include<math.h>
using namespace std;
using namespace cv;
int findmin(double *dis,int n); //找到最近的一类
void segmentation(const Mat,Mat&,int n=2);
int main()
{
system(“color 5E”);
Mat src,dst;
//src = imread(“tree.jpg”);
src = imread(“dog.jpg”);
//src = imread(“hair.png”);
segmentation(src,dst,2);//这数字是收到切换分成几类(默认两类)
namedWindow(“dst image”);
namedWindow(“src image”);
imshow(“dst image”,dst);
imshow(“src image”,src);
waitKey(0);
return 0;
}
int findmin(double *dis,int n)
{
double min;
int temp = 0;
min = dis[0];
for(int i = 1;i<n;i++)
if(dis[i]<min)
{
min = dis[i];
temp = i;
}
return temp;
}
void segmentation(const Mat src,Mat &dst,int n)
{
//dst = Mat::zeros(src.size(),src.type());
dst = src.clone();
int **m = new int*[2*n];     //新旧共2n个均值点
int *nk = new int[n];         //每一类的个数
int *x = new int[n];           //算新旧均值点是否已经到达终止条件
double *dis = new double[n];   //待分的点到几个均值点的距离
int **sum = new int*[n];        //求新的一类中所有点的和   三维
for(int i = 0;i<2*n;i++)      
m[i] = new int[3];
for(int i = 0;i<n;i++)
sum[i] = new int[3];
for(int i = 0;i<n;i++)          //随机生成的n个均值点
{
for(int k = 0;k<3;k++)
m[i][k] = src.at<Vec3b>(rand()/src.cols,rand()/src.rows)[k];  //随机生成初始点
}
for(int i = n;i<2*n;i++)
{
for(int k = 0;k<3;k++)
m[i][k] = 0;
}
for(int i = 0;i<n;i++)      //新旧均值点的差距
{
x[i] = abs(m[i+n][0]-m[i][0])+abs(m[i+n][1]-m[i][1])+abs(m[i+n][2]-m[i][2]);
}
int mink;
int comp=0;     //新旧均值点是否满足停止条件
int times = 0;
for(int i = 0;i<n;i++)
if(x[i]>0)
comp++;
while(comp)
{
for(int i = 0;i<n;i++)
{
nk[i] = 0;
for(int k = 0;k<3;k++)
sum[i][k] = 0;
}
for(int i = 0;i<src.rows;i++)
for(int j = 0;j<src.cols;j++)
{
for(int k = 0;k<n;k++)
{
dis[k] = sqrt((src.at<Vec3b>(i,j)[0]-m[k][0])*(src.at<Vec3b>(i,j)[0]-m[k][0])+  //计算每个像素点到n个均值点的距离
(src.at<Vec3b>(i,j)[1]-m[k][1])*(src.at<Vec3b>(i,j)[1]-m[k][1])+
(src.at<Vec3b>(i,j)[2]-m[k][2])*(src.at<Vec3b>(i,j)[2]-m[k][2]));
}
mink = findmin(dis,n);              //找到距离最近的均值点
nk[mink]++;                          //该类个数自增
sum[mink][0] += src.at<Vec3b>(i,j)[0];//求和
sum[mink][1] += src.at<Vec3b>(i,j)[1];
sum[mink][2] += src.at<Vec3b>(i,j)[2];
}
for(int i = 0;i<n;i++)
for(int k = 0;k<3;k++)
{
m[i+n][k] = sum[i][k]/nk[i];   //新的均值点
}
for(int i = 0;i<n;i++)
{
x[i] = abs(m[i+n][0]-m[i][0])+abs(m[i+n][1]-m[i][1])+abs(m[i+n][2]-m[i][2]);
}
for(int i = 0;i<n;i++)
{
for(int k = 0;k<3;k++)
m[i][k] = m[i+n][k];        //将新的均值点替换旧的均值点
}
comp = 0;
for(int i = 0;i<n;i++)
if(x[i]>0)
comp++;
times++;
}
cout<<“times is:”<<times<<endl;
/****************
最后一次分类结束后,在分类不会变化了,此时将每个类中用不同颜色标记
*****************/
for(int i = 0;i<src.rows;i++)
for(int j = 0;j<src.cols;j++)
{
for(int k = 0;k<n;k++)
{
dis[k] = sqrt((src.at<Vec3b>(i,j)[0]-m[k][0])*(src.at<Vec3b>(i,j)[0]-m[k][0])+  //计算距离
(src.at<Vec3b>(i,j)[1]-m[k][1])*(src.at<Vec3b>(i,j)[1]-m[k][1])+
(src.at<Vec3b>(i,j)[2]-m[k][2])*(src.at<Vec3b>(i,j)[2]-m[k][2]));
}
mink = findmin(dis,n);
if(mink == 0)
{
dst.at<Vec3b>(i,j)[0] = 255;
dst.at<Vec3b>(i,j)[1] = 0;
dst.at<Vec3b>(i,j)[2] = 0;
}
else if(mink == 1)
{
dst.at<Vec3b>(i,j)[0] = 0;
dst.at<Vec3b>(i,j)[1] = 255;
dst.at<Vec3b>(i,j)[2] = 0;
}
else if(mink==2)
{
dst.at<Vec3b>(i,j)[0] = 0;
dst.at<Vec3b>(i,j)[1] = 0;
dst.at<Vec3b>(i,j)[2] = 255;
}
else if(mink==3)
{
dst.at<Vec3b>(i,j)[0] = 0;
dst.at<Vec3b>(i,j)[1] = 0;
dst.at<Vec3b>(i,j)[2] = 0;
}
else if(mink==4)
{
dst.at<Vec3b>(i,j)[0] = 255;
dst.at<Vec3b>(i,j)[1] = 255;
dst.at<Vec3b>(i,j)[2] = 255;
}
else
{
dst.at<Vec3b>(i,j)[0] = 256/n*mink;
dst.at<Vec3b>(i,j)[1] = 2;
dst.at<Vec3b>(i,j)[2] = 255/n*mink;
}

}
delete[] nk;
delete[] x;
delete[] dis;
for(int i = 0;i<2*n;i++)
delete[] m[i];
delete[] m;
for(int i = 0;i<n;i++)
delete[] sum[i];
delete[] sum;
}

效果如下:

《C均值算法(K-means)在opencv中实现图像分割(抠图)》

下面这个也是实现图。

    原文作者:聚类算法
    原文地址: https://blog.csdn.net/windows_peng/article/details/50599779
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞