之四 二分k-均值算法 C#实现图像分割

时间:2021-10-22 23:04:29
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做了 ,效果就看下面吧。

之四 二分k-均值算法 C#实现图像分割之四 二分k-均值算法 C#实现图像分割