いわゆる、K-MeansをC#で実装してみました。 PictureBoxに点を貼りつけて実装しています。

停止条件である重心の位置が更新されなくなったら分類停止っていうのは実装していません。


ソース

実装したものは以下のようになります。

例外などエラー時の処理は考慮していないのであしからずm(_ _)m
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;

namespace K_Means
{
    public partial class Form1 : Form
    {
        struct Cluster
        {
            public Point Point;    // 座標
            public int Pattern;    // 色のパターン
        }

        public static int POINT_SIZE = 70;  //分類する点の数
        public static int CLUSTER_SIZE = 5; //分類するクラスターの数

        Cluster[] kernel = new Cluster[POINT_SIZE];   //分類する点
        Cluster[] cluster = new Cluster[CLUSTER_SIZE];//分類するクラスター
        Color[] myColor = new Color[] {               //色
            Color.Red,
            Color.Blue, 
            Color.Brown,
            Color.Cyan,
            Color.Yellow,
            Color.DarkGray,
            Color.DarkSlateBlue,
            Color.DarkGoldenrod,
            Color.DarkGreen,
            Color.DarkRed
        };

        public Form1()
        {
            InitializeComponent();

            // コンボボックスの初期位置
            comboBox1.SelectedIndex = 4;

            pictureBox1.Image = new Bitmap(pictureBox1.Width, pictureBox1.Height);
        }

        private void button1_Click(object sender, EventArgs e)
        {
            pictureBox1.Image = new Bitmap(pictureBox1.Width, pictureBox1.Height);
            Graphics g = Graphics.FromImage(pictureBox1.Image);
            if (comboBox1.SelectedIndex != -1)
            {
                // 洗濯されている要素を取得
                int index = comboBox1.SelectedIndex;
                string Text = comboBox1.Items[index].ToString();
                Console.WriteLine(int.Parse(Text));

                // クラスターの配列数を変更
                CLUSTER_SIZE = int.Parse(Text);
                Array.Resize(ref cluster, CLUSTER_SIZE);
            }

            // 任意の点を生成
            int seed = Environment.TickCount;
            for (int i = 0; i < POINT_SIZE; i++)
            {
                Random rnd = new Random(seed++);
                kernel[i].Point.X = rnd.Next(pictureBox1.Width);
                kernel[i].Point.Y = rnd.Next(pictureBox1.Height);
                kernel[i].Pattern = 0;
                
                // 描画
                g.FillEllipse(Brushes.Black, kernel[i].Point.X, kernel[i].Point.Y, 10, 10);
            }

            // clusterの初期位置を決定
            for (int i = 0; i < CLUSTER_SIZE; i++)
            {
                Random rnd = new Random(seed++);
                cluster[i].Point.X = rnd.Next(pictureBox1.Width);
                cluster[i].Point.Y = rnd.Next(pictureBox1.Height);
                cluster[i].Pattern = i;
            }
            // 描画
            DrawCluster(g, cluster, CLUSTER_SIZE, 20);
            //DrawCluster(g, kernel, POINT_SIZE, 10);

            g.Dispose();
            pictureBox1.Refresh();
        }

        private void button2_Click(object sender, EventArgs e)
        {
            pictureBox1.Image = new Bitmap(pictureBox1.Width, pictureBox1.Height);
            Graphics g = Graphics.FromImage(pictureBox1.Image);

            // 各点がどのクラスターに属するか判定
            for (int i = 0; i < POINT_SIZE; i++)
            {
                double temp = 2000;
                double dist = 2000;
                int pat = 0;

                for (int j = 0; j < CLUSTER_SIZE; j++)
                {
                    // 距離を計算
                    temp = Distance(cluster[j], kernel[i]);
                    if (dist > temp)
                    {
                        dist = temp;
                        pat = j;
                    }
                }
                kernel[i].Pattern = pat;
            }
            // クラスターの重心位置を計算し移動させる
            for (int j = 0; j < CLUSTER_SIZE; j++)
            {
                double sum_x = 0;
                double sum_y = 0;
                int num = 0;
                for (int i = 0; i < POINT_SIZE; i++)
                {
                    if (cluster[j].Pattern == kernel[i].Pattern)
                    {
                        sum_x += kernel[i].Point.X;
                        sum_y += kernel[i].Point.Y;
                        num = num + 1;
                    }
                }
                //System.Diagnostics.Debug.WriteLine("sum_x:{0},sum_y:{1}, Pattern{2}", sum_x, sum_y, j);
                cluster[j].Point.X = (int)sum_x / num;
                cluster[j].Point.Y = (int)sum_y / num;
            }
            // 描画
            DrawCluster(g, cluster, CLUSTER_SIZE, 20);
            DrawCluster(g, kernel, POINT_SIZE, 10);

            g.Dispose();
            pictureBox1.Refresh();
        }

        // 各座標にPatternにて指定されたカラーで描画
        private void DrawCluster(Graphics g, Cluster[] p, int Num_Point, int radius)
        {
            for (int i = 0; i < Num_Point; i++)
            {
                Brush brush = new SolidBrush(myColor[p[i].Pattern]);
                g.FillEllipse(brush, p[i].Point.X, p[i].Point.Y, radius, radius);
            }
        }

        // 2点間の距離を計算
        private double Distance(Cluster a, Cluster b)
        {
            double dx = a.Point.X - b.Point.X;
            double dy = a.Point.Y - b.Point.Y;

            return Math.Sqrt(dx * dx + dy * dy);
        }
    }
}




結果

コンボボックスで分類できる数を変更できるようにしています。


【初期位置】
kmeans1.png

【5個分類時】
kmeans2.png

【9個分類時】
kmeans3.png

【3個分類時】
kmeans4.png

Related Entries
Comments
Post a comment
Contributor
Comment
Trackbacks URL
URL
Permalink
Trackbacks
FC2カウンター
プロフィール

詠み人知らず

Author:詠み人知らず
プログラム好きな名もなき凡人がお送りしています。(得意とは言っていない
最近の興味はPython、C#、Matlab(Octave)、画像処理、AR(拡張現実)、統計などなど・・・

気分で思いついたことを書くため話題に一貫性がないかもしれません。

カレンダー
03 | 2017/04 | 05
- - - - - - 1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28 29
30 - - - - - -
最新記事
タグクラウドとサーチ

カテゴリ
最新コメント
最新トラックバック
月別アーカイブ