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做了 ,效果就看下面吧。