Archive
以前実装した、K-Meansを画像に適用してみます。

以前は、2次元空間(x,y)内でクラスタリングを行っていましたが、
画像では、RGBをそれぞれxyzの3次元空間と考えて、 3次元空間(R,G,B)で行います。

ちなみに、カラー画像はRGB空間上にプロットすると、
以下のようにある程度の傾向があります。
マンドリルの画像をプロットしたものです。
RGB_Graph.png

ソース

ソースは以下になります。


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_MeansImg
{
    public partial class Form1 : Form
    {
        public static Bitmap bit_map1 = new Bitmap("Mandrill.bmp");
        public static int CLUSTER_SIZE = 7; //分類するクラスターの初期数

        Color[] centroid = new Color[CLUSTER_SIZE];
        Color[] buffer = new Color[CLUSTER_SIZE];
        public static int[,] map = new int[bit_map1.Width, bit_map1.Height];

        public Form1()
        {
            InitializeComponent();

            pictureBox1.Height = bit_map1.Height;
            pictureBox1.Width = bit_map1.Width;

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

        private void pictureBox1_Paint(object sender, PaintEventArgs e)
        {
            pictureBox1.Image = bit_map1;
        }

        // 初期クラスターの生成
        private void randinit()
        {
            int seed = Environment.TickCount;
            for (int i = 0; i < CLUSTER_SIZE; i++)
            {
                Random rnd = new Random(seed++);
                centroid[i] = Color.FromArgb(rnd.Next(255), rnd.Next(255), rnd.Next(255));
            }
        }

        // 重心が停止しているか
        private bool ClusterCheck()
        {
            int cnt = 0;
            bool ret = false;

            for (int i = 0; i < CLUSTER_SIZE; i++)
            {
                if (centroid[i].ToArgb() == buffer[i].ToArgb())
                {
                    System.Diagnostics.Debug.WriteLine("Cluster Check {0}", i);
                    cnt = cnt + 1;
                }
            }
            System.Diagnostics.Debug.WriteLine("cnt={0} CLUSTER_SIZE={1}", cnt, CLUSTER_SIZE);
            if (cnt == CLUSTER_SIZE)
            {
                ret = true;
            }

            return ret;
        }

        // 2点間の距離を計算
        private double ColorDistance(Point a, Color b)
        {
            double dR = bit_map1.GetPixel(a.X, a.Y).R - (int)b.R;
            double dG = bit_map1.GetPixel(a.X, a.Y).G - (int)b.G;
            double dB = bit_map1.GetPixel(a.X, a.Y).B - (int)b.B;

            return Math.Sqrt(dR * dR + dG * dG + dB * dB);
        }

        private void button1_Click(object sender, EventArgs e)
        {
            bit_map1 = new Bitmap("Mandrill.bmp");

            // コンボボックスで選択した値を取得する
            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 centroid, CLUSTER_SIZE);
                Array.Resize(ref buffer, CLUSTER_SIZE);
            }

            // 初期化処理
            randinit();

            // 重心が移動しなくなるまで
            while (ClusterCheck() == false)
            {
                // 画像内を走査
                for (int i = 0; i < bit_map1.Width; i++)
                {
                    for (int j = 0; j < bit_map1.Height; j++)
                    {
                        double temp = 2000;
                        double dist = 2000;
                        Point img = new Point(i, j);
                        int place = 0;

                        // 各点がどのクラスターに属するか判定
                        for (int k = 0; k < CLUSTER_SIZE; k++)
                        {
                            // 距離を計算
                            temp = ColorDistance(img, centroid[k]);
                            if (dist > temp)
                            {
                                dist = temp;
                                place = k;
                            }
                        }
                        map[i, j] = place;
                    }
                }

                double[] sum_R = new double[CLUSTER_SIZE];
                double[] sum_G = new double[CLUSTER_SIZE];
                double[] sum_B = new double[CLUSTER_SIZE];
                int[] num = new int[CLUSTER_SIZE];
                int cntdebug = 0;
                // 重心を計算
                for (int i = 0; i < bit_map1.Width; i++)
                {
                    for (int j = 0; j < bit_map1.Height; j++)
                    {
                        sum_R[map[i, j]] += bit_map1.GetPixel(i, j).R;
                        sum_G[map[i, j]] += bit_map1.GetPixel(i, j).G;
                        sum_B[map[i, j]] += bit_map1.GetPixel(i, j).B;
                        num[map[i, j]] = num[map[i, j]] + 1;
                        cntdebug = cntdebug + 1;
                    }
                }

                for (int k = 0; k < CLUSTER_SIZE; k++)
                {
                    // 前の重心位置を記憶しておく
                    buffer[k] = Color.FromArgb(centroid[k].R, centroid[k].G, centroid[k].B);

                    if (num[k] == 0)
                    {
                        continue;
                    }

                    // 重心位置の更新
                    centroid[k] = Color.FromArgb((int)sum_R[k] / num[k], (int)sum_G[k] / num[k], (int)sum_B[k] / num[k]);
                    System.Diagnostics.Debug.WriteLine("clsuter:{0} R:{1} G:{2} B:{3}", k, centroid[k].R, centroid[k].G, centroid[k].B);
                }
            }

            //画像内を走査
            for (int i = 0; i < bit_map1.Width; i++)
            {
                for (int j = 0; j < bit_map1.Height; j++)
                {
                    // 最近傍のクラスターの色に設定
                    bit_map1.SetPixel(i, j, centroid[map[i, j]]);
                }
            }
            // 表示を更新
            pictureBox1.Image = bit_map1;
        }
    }
}


結果

結果は以下のようになります。
クラスター数が増えるほど色数がもちろん増えます。

クラスター数:5
cluster5.jpg

クラスター数:10
cluster10.jpg

クラスター数:15
cluster15.jpg



RGBでのクラスターは、 それぞれの色でのヒストグラムをクラスター数分だけただ分割しているだけなので、 領域分割はされていません。

(x,y)の領域も含めて行う場合はRGBxyの5次元空間で行うとよいのかもしれません。
それはまた次の回に実装・試してみたいと思います。


FC2カウンター
プロフィール

詠み人知らず

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

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

カレンダー
08 | 2012/09 | 10
- - - - - - 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 - - - - - -
最新記事
タグクラウドとサーチ

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