Archive
上記の広告は1ヶ月以上更新のないブログに表示されています。
新しい記事を書く事で広告が消せます。
以前行った。射影変換lでは変換したい座標をあらかじめ配列に格納して実行していましたが、
Pictureboxでクリックした座標を取得して変換を行います。

C#とOpenCVSharpを使って行います。

ソース

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

pictureBox1_MouseDownの引数「MouseEventArgs e」にてpictureboxをクリックした座標を取得できます。

プログラムとしては、4点pictureboxをクリックして、その後buttunを押すと、クリックした座標を使用して射影変換をします。
特にガードなど細かいことはしていませんのでご了承を・・・m(_ _)m
button1_Click、pictureBox1_MouseDownの部分が処理のメイン部分です。



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;
using OpenCvSharp;
using OpenCvSharp.CPlusPlus;

namespace HomographyImage
{
    public partial class Form1 : Form
    {
        // 表示するBitmap  
        private Bitmap bmp = null;
        // 描画用Graphicsオブジェクト  
        private Graphics g = null;
        // クリック位置の描画用座標
        private Point[] point = new Point[4];
        private int clickcnt = 0;

        public Form1()
        {
            InitializeComponent();
        }

        private void UpdateForm(Bitmap bmp)
        {
            int margin = 50;
            int maxsize = 800;
            int width = maxsize;
            int height = maxsize;

            if (maxsize > bmp.Width)
            {
                width = bmp.Width;
            }
            if (maxsize > bmp.Height)
            {
                height = bmp.Height;
            }

            // Formのサイズを画像に応じて変更
            this.Width = pictureBox1.Location.X + width + margin;
            this.Height = pictureBox1.Location.Y + height + margin;

            pictureBox1.Height = height;
            pictureBox1.Width = width;

            // 画像を表示
            pictureBox1.Image = bmp;
        }


        private void 開くOToolStripMenuItem_Click(object sender, EventArgs e)
        {
            //「ファイルの種類」を設定
            openFileDialog1.Filter = "画像ファイル(BMP,JPEG,GIF,PNG)|*.bmp;*.jpg;*gif;*.png|すべてのファイル(*.*)|*.*";
            if (openFileDialog1.ShowDialog() == DialogResult.OK)
            {
                bmp = new Bitmap(openFileDialog1.FileName);

                // 表示を更新
                UpdateForm(bmp);
            }
        }

        private void 保存SToolStripMenuItem_Click(object sender, EventArgs e)
        {
            // ファイルのフィルタを設定する
            saveFileDialog1.Filter = "Jpeg Image|*.jpg|Bitmap Image|*.bmp|Gif Image|*.gif|PNG Image|*.png";

            saveFileDialog1.Title = "画像を保存";

            // 初期表示するファイル名を設定する
            saveFileDialog1.FileName = "";

            if (saveFileDialog1.ShowDialog() == DialogResult.OK)
            {
                Console.WriteLine(saveFileDialog1.FileName);
                string extension = System.IO.Path.GetExtension(saveFileDialog1.FileName);
                switch (extension.ToUpper())
                {
                    case ".JPEG":
                    case ".JPG":
                        pictureBox1.Image.Save(saveFileDialog1.FileName, System.Drawing.Imaging.ImageFormat.Jpeg);
                        break;
                    case ".BMP":
                        pictureBox1.Image.Save(saveFileDialog1.FileName, System.Drawing.Imaging.ImageFormat.Bmp);
                        break;
                    case ".GIF":
                        pictureBox1.Image.Save(saveFileDialog1.FileName, System.Drawing.Imaging.ImageFormat.Gif);
                        break;
                    case ".PNG":
                        pictureBox1.Image.Save(saveFileDialog1.FileName, System.Drawing.Imaging.ImageFormat.Png);
                        break;
                    default:
                        break;
                }
            }
        }

        private void 終了CToolStripMenuItem_Click(object sender, EventArgs e)
        {
            Environment.Exit(0);
        }

        private void pictureBox1_MouseDown(object sender, MouseEventArgs e)
        {
            if (bmp == null)
            {
                return;
            }

            // クリックした座標を取得
            point[clickcnt].X = e.X;
            point[clickcnt].Y = e.Y;

            //System.Diagnostics.Debug.WriteLine("({0},{1}) {2}", point[clickcnt].X, point[clickcnt].Y, clickcnt);

            // クリックした位置に点を描画
            g = pictureBox1.CreateGraphics();
            g.FillEllipse(Brushes.Aqua, point[clickcnt].X, point[clickcnt].Y, 10, 10);

            // 点の間でLineを引く
            if (clickcnt != 0)
            {
                Pen p = new Pen(Color.Aqua, 3);
                g.DrawLine(p, point[clickcnt], point[clickcnt - 1]);
                p.Dispose();
            }
            if (clickcnt == point.Length-1)
            {
                Pen p = new Pen(Color.Aqua, 3);
                g.DrawLine(p, point[clickcnt], point[(clickcnt + 1) % point.Length]);
                p.Dispose();
            }

            g.Dispose();

            clickcnt = (clickcnt + 1) % point.Length;
        }

        private void button1_Click(object sender, EventArgs e)
        {
            // クリックで取得した座標
            double[] a = new double[]{
                                point[0].X,point[0].Y,
                                point[1].X,point[1].Y,
                                point[2].X,point[2].Y,
                                point[3].X,point[3].Y
                                };

            // 変換先の頂点の座標
            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 = BitmapConverter.ToIplImage(bmp);
            IplImage dst = Cv.CreateImage(Cv.Size(512, 512), src.Depth, src.NChannels);

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

            Cv.FindHomography(Mata, Matb, homography);
            Cv.WarpPerspective(src, dst, homography, Interpolation.Cubic);

            // pictureboxを更新
            UpdateForm(BitmapConverter.ToBitmap(dst));
        }

    }
}



結果

結果は以下のようになります。
クリックしていくたびに点が描画され領域が表示されます。


1_20121124221003.png


2_20121124221002.png


3_20121124221001.png


4_20121124221000.png


5.png


スポンサーサイト
前回、前々回とバイリニア、バイキュービック補間を行っていましたが、
今回はLanczos(読めない)で行います。


参考にさせていただいたサイトは以下になります。理論的なところはこちらの方が詳しいです。
画像の拡大「Lanczos法」


結果

Lancoz2

Lancoz2.png


Lancoz3

Lancoz3.png


バイキュービック

Bicubic.png


どうやらうまくいっているようです。
前回のバイリニアに続き、バイキュービックを適用した結果です。

参考にさせていただいたサイトは以下です。m(_ _)mアリガタヤ
画素の補間(Nearest neighbor,Bilinear,Bicubic)



すごく簡単に言ってしまうと、
バイリニアは周囲4点から画素の値を推測していたのを、
バイキュービックは周囲16点から画素の値を推測しています。

結果

ニアレストネイバー

near.png


バイリニア

bilinear.png


バイキュービック

Bicubic.png


バイリニアでは、若干ぼけていた画像がバイキュービックでははっきり見えるようになっています。
しかし、計算する周囲の画素の数が増加したため、やはり処理が重くなっています。


どうやって高速化するかは自分のプログラム上の課題ですね(汗



詠み人知らずの備忘録 - 画像を回転させる
にて実装したプログラムは何も補間をかけずに回転させたため、ギザギザな画像となっていました。

これは変換前の座標を算出した際に、必ずしも整数値をとる座標とはならないため最も近い点の画素値を持ってきていたためと考えられます。

I(x',y')を求める際にαなどを以下のように仮定します。(図がキタナイですが・・・)
bilinear_graph.png


その時のI(x',y')は下記の式のように求めることができます。
eqn_20121109023054.png


結果

ニアレストネイバー





バイリニア補間

bilinear.png




バイリニアの方がギザギザが軽減しているためきれいに見えます。
今度はバイキュービックに挑戦してみようと思います。
以前行った、
位相限定相関(POC)で求めたピーク値を探し出せば、移動量が求められるのですが、

画像は整数の値しかとらない格子なので、サブピクセルのレベルで移動していた場合詳細は求められません。

スズメレンダラー・クマ将棋の開発日記さんの記事ではSinc関数に当てはめて求める方法が記載してあります。

ちなみに、Sinc関数とは以下のような関数です


調べてみたところ、最急降下法などで関数のフィッティングを行い厳密な値を求めていきます。



・・・ですが、

私のような凡人にはちょいと難しかったので、もっと簡単な方法を探してました。
x方向だけで考えると移動量dは、




と表すことができます。ここで、
・R(0)はピーク値(位相限定相関画像の値)
・R(-1)はピーク値の一つ前の座標の相関値
・R(1)はピーク値の一つ先の座標の相関値

です。

このパラボラフィッティングを使えば、Sinc関数よりは精度は落ちますが楽にサブピクセル精度で移動量を算出できます。


また、等角直線フィッティングもあるそうですが、
1次関数でのフィッティングであるため、パラボラフィッティングの2次関数の方が精度はよいと考えられます。


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

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

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

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

カテゴリ
最新コメント
最新トラックバック
月別アーカイブ
上記広告は1ヶ月以上更新のないブログに表示されています。新しい記事を書くことで広告を消せます。