using System;
using System.Drawing;
using System.Windows.Forms;
using System.Threading;
using System.Collections.Generic;
namespace K_means
{
public partial class Form1 : Form
{
const int pictureSize = 640 * 480;
const int k = 3; //质心的数量
Color[] arrayColor = new Color[pictureSize]; //数据集
int[,] resultArray = new int[pictureSize, 2]; //存储每个点的簇分配结果及与质心的距离
int[,] centArray = new int[k, 4]; //存储质心
int[,] tmpcentArray = new int[k, 4];
public Form1()
{
InitializeComponent();
pictureBox1.ImageLocation = "test.jpg";
}
private void button1_Click(object sender, EventArgs e)
{
Bitmap map = new Bitmap(pictureBox1.Image);
for (int j = 0; j < map.Height; j++)
{
for (int i = 0; i < map.Width; i++)
{
arrayColor[j * map.Width + i] = map.GetPixel(i, j);
}
}
new Thread(new ThreadStart(kmeans)).Start();
}
private void button1_Resize(object sender, EventArgs e)
{
this.Width = 800;
this.Height = 600;
}
private void button2_Click(object sender, EventArgs e)
{
Bitmap map = new Bitmap(pictureBox1.Image);
Bitmap newmap = map;
Color color = new Color();
Color newcolor = new Color();
Byte r, g, b, gray;
for (int j = 0; j < map.Height; j++)
{
for (int i = 0; i < map.Width; i++)
{
color = map.GetPixel(i, j);
r = color.R;
g = color.G;
b = color.B;
if (r + g + b != 0)
{
gray = (Byte)((r * 19595 + g * 38469 + b * 7472) >> 16);
newcolor = Color.FromArgb(gray, gray, gray);
newmap.SetPixel(i, j, newcolor);
}
}
}
pictureBox1.Image = newmap;
}
private int getDistance(int[,] a, Color b, int i)
{
return (int)Math.Sqrt(Math.Pow(a[i, 0] - b.R, 2) + Math.Pow(a[i, 1] - b.G, 2) + Math.Pow(a[i, 2] - b.B, 2) + Math.Pow(a[i, 3] - b.A, 2));
}
private void kmeans()
{
DateTime dt = DateTime.Now;
for (int i = 0; i < k; i++) //初始k个随机质心,颜色取值不看数据集了,直接0-255
{
tmpcentArray[i, 0] = centArray[i, 0] = new Random(BitConverter.ToInt32(Guid.NewGuid().ToByteArray(), 0)).Next(0, 256);
tmpcentArray[i, 1] = centArray[i, 1] = new Random(BitConverter.ToInt32(Guid.NewGuid().ToByteArray(), 0)).Next(0, 256);
tmpcentArray[i, 2] = centArray[i, 2] = new Random(BitConverter.ToInt32(Guid.NewGuid().ToByteArray(), 0)).Next(0, 256);
tmpcentArray[i, 3] = centArray[i, 3] = new Random(BitConverter.ToInt32(Guid.NewGuid().ToByteArray(), 0)).Next(0, 256);
}
while (true)
{
for (int i = 0; i < arrayColor.Length; i++) //遍历数据集中每个点并簇分配
{
int minDistance = getDistance(centArray, arrayColor[i], 0);
int count = 0;
for (int j = 1; j < k; j++)
{
int tmpDistance = getDistance(centArray, arrayColor[i], j);
if (minDistance > tmpDistance)
{
minDistance = tmpDistance;
count = j;
}
}
resultArray[i, 0] = count;
resultArray[i, 1] = minDistance;
}
for (int j = 0; j < 3; j++) //计算新的质心
{
int r = 0;
int g = 0;
int b = 0;
int a = 0;
int m = 0;
for (int i = 0; i < arrayColor.Length; i++)
{
if (resultArray[i, 0] == j)
{
r += arrayColor[i].R;
g += arrayColor[i].G;
b += arrayColor[i].B;
a += arrayColor[i].A;
m++;
}
}
if (m != 0)
{
centArray[j, 0] = r / m;
centArray[j, 1] = g / m;
centArray[j, 2] = b / m;
centArray[j, 3] = a / m;
}
this.Invoke(new MethodInvoker(() => { this.label1.Text = centArray[j, 0] + "\r\n" + centArray[j, 1] + "\r\n" + centArray[j, 2] + "\r\n" + centArray[j, 3] + "\r\n" + (DateTime.Now - dt).TotalSeconds; }));
}
bool isBreak = true;
for (int i = 0; i < k; i++) //检测质心是否还有变化
{
for (int j = 0; j < 4; j++)
{
if (tmpcentArray[i, j] != centArray[i, j])
{
isBreak = false;
break;
}
}
}
if (isBreak) //质心不变就可以退出了
{
break;
}
else
{
for (int i = 0; i < k; i++) //保存上一轮计算出的质心
{
for (int j = 0; j < 4; j++)
{
tmpcentArray[i, j] = centArray[i, j];
}
}
}
}
}
private void button3_Click(object sender, EventArgs e)
{
Bitmap map = new Bitmap(pictureBox1.Width, pictureBox1.Height);
for (int j = 0; j < map.Height; j++)
{
for (int i = 0; i < map.Width; i++)
{
if (resultArray[j * map.Width + i, 0] == 0)
{
map.SetPixel(i, j, Color.Red);
}
else if (resultArray[j * map.Width + i, 0] == 1)
{
map.SetPixel(i, j, Color.Green);
}
else if (resultArray[j * map.Width + i, 0] == 2)
{
map.SetPixel(i, j, Color.Yellow);
}
}
}
pictureBox1.Image = map;
}
}
}
代码随便写的,没啥优化,那个二分k-means有些细节还没弄清楚,只好先用一般的k-means做了 ,效果就看下面吧。