簡單來說這就是一個分群集的演算法。
詳細說明可以參考:
https://jason-chen-1992.weebly.com/home/-k-means-clustering
https://edisonx.pixnet.net/blog/post/84122954
https://dotblogs.com.tw/dragon229/2013/02/04/89919
程式步驟(假設分3群集):
- 隨機產生3個群集中心
- 計算這些點離群集中心的距離,並分配這些點給較近的群集中心
- 重新計算群中心距離
- 循環2-3步驟,直至群集中心不變或者變動較小為止。
代碼如下:
#include<opencv2/opencv.hpp>
#include<iostream>
#include <cstdlib> /* 亂數相關函數 */
#include <ctime> /* 時間相關函數 */
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
using namespace cv;
using namespace std;
#define K_GROUP 4
Mat srcImage, grayImage;
int k_cnt = 0;
int max_x, min_x;
void K_Manes(Point *kp, int k_group);
int main()
{
char k_first_Flag = 0;
Point K_center[K_GROUP];
srcImage = imread("C:\\Users\\User\\Downloads\\Image\\1cm.bmp");
cvtColor(srcImage, grayImage, CV_RGB2GRAY);
//get point range
for (int x = 0; x < grayImage.rows; x++)
{
for (int y = 0; y < grayImage.cols; y++)
{
if (grayImage.ptr<uchar>(x)[y] != 0)
{
if (k_first_Flag == 0)
{
k_first_Flag = 1;
min_x = x;
}
max_x = x;
k_cnt++;//get total point
}
}
}
printf("min_x=%d\n", min_x);
printf("max_x=%d\n", max_x);
//get rand point
int lx, ly;
srand(time(NULL));
for (int i = 0; i < K_GROUP; i++)
{
lx = rand() % (max_x - min_x + 1) + min_x;
printf("lx=%d\n", lx);
ly = (rand() % grayImage.cols) + 1;
K_center[i].x = lx;
K_center[i].y = ly;
printf("ly=%d\n", ly);
}
//k_manes calculate
K_Manes(K_center, K_GROUP);
imshow("show",grayImage);
waitKey();
return 0;
}
void K_Manes(Point *kp, int k_group)
{
Point p2;
int d[K_GROUP];
Point **data;
unsigned long **dataD;
unsigned long *loc_cnt;
long loc_g = 0;
//char rand_flag[K_GROUP];
int lx, ly;
int t = 100, stop_cnt = 0;
Point last_p[K_GROUP];
data = new Point*[K_GROUP];
dataD = new unsigned long*[K_GROUP];
loc_cnt = new unsigned long[K_GROUP];
srand(time(NULL));
for (int i = 0; i < K_GROUP; i++){
data[i] = new Point[k_cnt];
dataD[i] = new unsigned long[k_cnt];
//rand_flag[i] = 0;
last_p[i].x = 0;
last_p[i].y = 0;
}
for (int i = 0; i < K_GROUP; i++)
{
for (int j = 0; j < k_cnt; j++){
data[i][j].x = 0;
data[i][j].y = 0;
dataD[i][j] = 0;
}
}
//show point
Mat show = Mat::zeros(grayImage.rows, grayImage.cols, grayImage.type());
for (int x = 0; x < grayImage.rows; x++)
{
for (int y = 0; y < grayImage.cols; y++)
{
for (int i = 0; i < K_GROUP; i++)
if (x == kp[i].x && y == kp[i].y){
show.ptr<uchar>(x)[y] = 255;
for (int j = 0; j < 10; j++){
if (x - j > 0 && x + j < grayImage.rows && y + j < grayImage.cols && y - j>0){
show.ptr<uchar>(x - j)[y] = 255;
show.ptr<uchar>(x + j)[y] = 255;
show.ptr<uchar>(x)[y - j] = 255;
show.ptr<uchar>(x)[y + j] = 255;
}
}
}
}
}
imshow("show point", show);
while (t--)
{
//clear loc_cnt
for (int i = 0; i < K_GROUP; i++)
{
loc_cnt[i] = 0;
for (int j = 0; j < k_cnt; j++)
{
data[i][j].x = 0;
data[i][j].y = 0;
dataD[i][j] = 0;
}
}
int samll_d= 0;
//find group
for (int x = 0; x < grayImage.rows; x++)
{
for (int y = 0; y < grayImage.cols; y++)
{
if (grayImage.ptr<uchar>(x)[y] != 0)
{
p2.x = x;
p2.y = y;
//find distance
loc_g = 0;
samll_d = d[0];
for (int i = 0; i < K_GROUP; i++)
{
d[i] = sqrt(pow(p2.x - kp[i].x, 2) + pow(p2.y - kp[i].y, 2));
if (samll_d > d[i]){
loc_g = i;
samll_d = d[i];
}
}
//save data
data[loc_g][loc_cnt[loc_g]].x = p2.x;
data[loc_g][loc_cnt[loc_g]].y = p2.y;
dataD[loc_g][loc_cnt[loc_g]] = samll_d;
loc_cnt[loc_g]++;
data[loc_g][loc_cnt[loc_g]].x = 0;
data[loc_g][loc_cnt[loc_g]].y = 0;
dataD[loc_g][loc_cnt[loc_g]] = 0;
}
}
}
//count max point
int s = loc_cnt[0];
int sl = 0;
for (int i = 0; i < K_GROUP; i++){
if (s < loc_cnt[i]){
s = loc_cnt[i];
sl = i;
}
}
//draw center point
char p = 5;
Mat show2 = Mat::zeros(show.rows, show.cols, show.type());
for (int x = 0; x < show.rows; x++)
{
for (int y = 0; y < show.cols; y++)
{
for (int i = 0; i < K_GROUP; i++)
{
if (x == kp[i].x && y == kp[i].y){
show2.ptr<uchar>(x)[y] = 255;
i == sl ? p = 10 : p = 5;
for (int j = 0; j < p; j++){
if (x - j > 0 && x + j < grayImage.rows && y + j < grayImage.cols && y - j>0){
show2.ptr<uchar>(x - j)[y] = 255;
show2.ptr<uchar>(x + j)[y] = 255;
show2.ptr<uchar>(x)[y - j] = 255;
show2.ptr<uchar>(x)[y + j] = 255;
}
}
}
}
}
}
//return loop
Mat show3 = Mat::zeros(show2.rows, show2.cols, show2.type());
for (int i = 0; i < K_GROUP; i++)
{
if (stop_cnt > 3 || t == 0)
{
printf("t=%d\n", 100 - t);
for (int x = 0; x < grayImage.rows; x++)
{
for (int y = 0; y < grayImage.cols; y++)
{
if (grayImage.ptr<uchar>(x)[y] != 0)
{
show2.ptr<uchar>(x)[y] = 255;
}
}
}
for (int x = 0; x < K_GROUP; x++){
printf("loc_cnt[%d]=%ld\n", x, loc_cnt[x]);
printf("kp[%d].x=%d kp[%d].y=%d \n", x, kp[x].x, x, kp[x].y);
}
imshow("show2 point", show2);
return;
}
if (last_p[i].x == kp[i].x && last_p[i].y == kp[i].y && loc_cnt[i]>0)
stop_cnt++;
else if (sqrt(pow(last_p[i].x - kp[i].x, 2) + pow(last_p[i].y - kp[i].y, 2))<5)
stop_cnt++;
}
//calcuate K-group center again
Point max_loc[K_GROUP], min_loc[K_GROUP];
unsigned long long max_p, min_p;
unsigned long long new_point_X[K_GROUP];
unsigned long long new_point_Y[K_GROUP];
for (int i = 0; i < K_GROUP; i++)
{
new_point_X[i] = 0;
new_point_Y[i] = 0;
max_p = dataD[i][0];
min_p = dataD[i][0];
max_loc[i].x = data[i][0].x;
min_loc[i].y = data[i][0].y;
for (int j = 0; j < loc_cnt[i]; j++)
{
if (data[i][j].x != 0 && data[i][j].y != 0)
{
if (loc_cnt[i] > 0){
/*average point*/
new_point_X[i] += data[i][j].x;
new_point_Y[i] += data[i][j].y;
}
}
}
}
//update group center
for (int i = 0; i < K_GROUP; i++)
{
if (loc_cnt[i] > 0){
/*average point*/
new_point_X[i] /= loc_cnt[i];
new_point_Y[i] /= loc_cnt[i];
last_p[i].x = kp[i].x;
last_p[i].y = kp[i].y;
kp[i].x = new_point_X[i];
kp[i].y = new_point_Y[i];
}
}
}
}
此程式碼將圖片(灰階)載入後,取白灰點做處理並分群集。