Archive
画像の形を変換する?
射影変換をやってみることに。。。


まずは、どんなものか感じをつかむために、こちらのサイトの画像をC#で書き直してみました。
OpenCV@Chihara-Lab. 射影変換

計算を自前でやろうとしたのですが、逆行列の計算が中々うまくいかず結局、OpenCvをC#で使用するOpenCvSharpというラッパーを使用しました(;´Д`)
OpenCVをC#やVB.NETなどの.NET Frameworkの言語から利用するためのクロスプラットフォームで動作するラッパーです。とのこと


ソースコード

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using OpenCvSharp;
using OpenCvSharp.CPlusPlus; 

namespace HomographyOpenCV
{
    class Program
    {
        static void Main(string[] args)
        {
            // 元画像の頂点の座標
            double[] a = new double[]{
                                93, 90,
                                20, 422,
                                501, 468,
                                408, 43
            };

            // 変換先の頂点の座標
            double[] b = new double[]{
                                0, 0,
                                0, 511,
                                511, 511,
                                511, 0
            };
            CvMat Mata = new CvMat(4, 2, MatrixType.F64C1, a);
            CvMat Matb = new CvMat(4, 2, MatrixType.F64C1, b);

            IplImage src = Cv.LoadImage("1.jpg", LoadMode.Color);
            IplImage dst = src.Clone();

            //homography
            CvMat h = new CvMat(3, 3, MatrixType.F64C1);

            Cv.FindHomography(Mata, Matb, h);

            for(int i=0 ; i<3 ;i++)
            {
                for(int j=0; j<3; j++)
                {
                    System.Diagnostics.Debug.WriteLine("h[{0},{1}] = {2}", i,j,h[i,j]);
                }
            }

            Cv.WarpPerspective(src, dst, h);

            using (new CvWindow("src", src))
            using (new CvWindow("dst", dst))
            {
                Cv.WaitKey();
            }

            src.Dispose();
            dst.Dispose();
        }
    }
}


結果

プログラムの結果はこんな感じになりました。
元画像
src.png

変換画像
dst.png

きちんと変換できてるようです。

いわゆる、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

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 - - - - - -
最新記事
タグクラウドとサーチ

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