C均值算法(K-means)在opencv中實現圖像分割(摳圖)

學習了模式識別中的K均值算法,之後自己琢磨着實現了圖像分割(受到劉波老師ppt上的啓發),經過一下午的時間,終於實現了圖像分割。後來才發想opencv中自帶kmeans算法,而且直接調用就行。

下面就是介紹和自己寫的源代碼。

分類:

監督學習:給定已知類別的學習樣本,設計分類器。
非監督學習:給定未知(未知類別及類別數)樣本,設計分類器。
兩大類非監督學習:
基於概率密度函數估計的直接方法
基於樣本間相似性(similarity)度量的間接聚類方法。

而K均值就是屬於無監督學習中間接聚類方法中的動態聚類

C-均值算法(k-means, k-均值)
      對樣本集KN={xi}尚不知每個樣本的類別,但可假設所有樣本可分爲c類,各類樣本在特徵空間依類聚集,且近似球形分佈;
      用一代表點(prototype)來表示一個聚類,如類內均值mi來代表聚類Ki;
     聚類準則:誤差平方和J



流程:

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;
}

效果如下:


下面這個也是實現圖。

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