Archive
POCの角度検出のために、log-polar変換を勉強していたのですが、
その前に、画像を極座標変換に挑戦してみます。



基本は高校か大学の時にならった変換を使います。


ですが、画像を回転させるときにやったようにこのままでは変換後の画像が穴だらけになるので、
以下のように、rとθからx,yを逆算します。




原点を画像の中心にとるとrは最大でも画像のHeightかWidthの大きい方ですし、
θは0~2πまでしかとらないため、プログラム上のループで回す限度はわかります。
※刻み幅はどれくらいがいいのかという問題はあると思いますが。。。



実際に計算した結果です。
極座標変換で使用されているサ○ダーのキャップを変換してみます。


【元画像:例その1】



【極座標変換:例その1】
cap2.jpg








【元画像:例その2】
circle.jpg


【極座標変換:例その2】
circle2.jpg




どちらの変換後の画像も、円が直線になっておらず若干波打った状態になっています。。。
この辺は今後の課題にしておきます。。。

小数点の精度での画像回転するソフトがほしかったため、自分で実装することにしました。

参考にしたのは、以下のサイトです。高速化などについてソース付きで説明されているので非常に参考になりました。
C言語による画像回転処理について


今回、画像において回転前後の座標を以下としています。
  • 回転前:(x,y)
  • 回転後:(X,Y)


そこで、角度θ分回転を行う座標変換は以下のように行います。(時計回りを正とした場合)

eqn1.png



結果は以下のようになります。

pat1.png



虫食い状態になります。
これは、画像が整数の座標しかとらないため、回転前の座標がすべて回転後の座標に対応しているわけではないためです。

これを回避するため、回転後の座標を基準にして考えます。
すなわち、回転後の座標に対応する座標は回転前ではどの座標に対応するか以下の数式のように逆算して行います。


eqn2.png



そして、実装を行った結果が以下のようになります。


pat2.png



これでもまだ回転後の画像がギザギザになっています、
バイリニア、ニアレストネイバーなどを用いれば、より自然な画像になると思います。

近いうちにまた実装してみようと思います。。。

IplImage()やCv.CreateImage()で使うEnum等がいまいち覚えられなくて、
その都度調べているという状態がもったいないのでまとめてみました。

とりあえず、今回はLoadModeとBitDepthについて簡単にまとめてみました。

LoadMode

IplImage()の第2引数等で使用するLoadModeについてまとめたのが以下の表です。
実際に定義されているソースはhttp://opencvsharp.googlecode.com/svn/trunk/2.4/OpenCvSharp/Src/Enum/LoadMode.csになります。



OpenCV
OpenCVSharp
備考
CV_LOAD_IMAGE_UNCHANGED
LoadMode.Unchanged
-1
CV_LOAD_IMAGE_GRAYSCALE
LoadMode.GrayScale
0
CV_LOAD_IMAGE_COLOR
LoadMode.Color
1
CV_LOAD_IMAGE_ANYDEPTH
LoadMode.AnyDepth
2
CV_LOAD_IMAGE_ANYCOLOR
LoadMode.AnyColor
4



BitDepth

Cv.CreateImage()等の第2引数で使用する、BitDepthについてOpenCVとOpenCVSharpの差異についてまとめたものが以下です。
実際に定義されているソースがhttp://opencvsharp.googlecode.com/svn/trunk/2.4/OpenCvSharp/Src/Enum/BitDepth.csになります。



OpenCV
OpenCVSharp
備考
IPL_DEPTH_1U
BitDepth.U1
2値画像
IPL_DEPTH_8U
BitDepth.U8
通常のグレースケール画像
IPL_DEPTH_16U
BitDepth.U16
単精度画像
IPL_DEPTH_32F
BitDepth.F32
倍精度画像
IPL_DEPTH_8S BitDepth.S8
符号付きグレースケール画像
IPL_DEPTH_16S
BitDepth.S16 符号付き単精度画像
IPL_DEPTH_32S
BitDepth.S32 符号付き倍精度画像


自分のメモ程度ですが、今後必要になればまた作成しようと思います。

※数年ぶりにtableのhtmlを書いたらすっかり忘れています・・・汗
以前、Octaveで求めていた位相画像をC#とOpenCVSharpにて実装してみたいと思います。



ソース

以下がソースコードになります。
Form1_Paint()内にてフーリエ変換などを行っていますが、 途中まではOpenCVのサンプルをまねしています。

離散変換 - http://opencv.jp/sample/discrete_transforms.html

また、
DFT - http://opencv.jp/opencv-1.0.0/document/opencvref_cxcore_discrete.htmlにて記載されているように離散フーリエ変換であるcvDFT(Cv.DFT)の変換・逆変換は3つ目の引数であるflagsにより決まります。

つまり、以下のようになります。
CV_DXT_FORWARD(DFTFlag.Forward)
→フーリエ変換
CV_DXT_INVERSE(DFTFlag.Inverse)
→フーリエ逆変換


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;

namespace PhaseImage
{
    public partial class Form1 : Form
    {
        Bitmap bitmap;
        String filename = "Lenna.bmp";

        public Form1()
        {
            InitializeComponent();
            bitmap = new Bitmap(filename);

            //左側のpictureBoxの設定
            pictureBox1.Height = bitmap.Height;
            pictureBox1.Width = bitmap.Width;
            pictureBox1.Left = 0;
            pictureBox1.Top = 0;
            pictureBox1.Image = bitmap;

            //右側のpictureBoxの設定
            pictureBox2.Height = bitmap.Height;
            pictureBox2.Width = bitmap.Width;
            pictureBox2.Left = pictureBox1.Width;
            pictureBox2.Top = 0;

            this.ClientSize = new Size(pictureBox1.Width + pictureBox2.Width, pictureBox1.Height);
        }

        private void Form1_Paint(object sender, PaintEventArgs e)
        {
            IplImage img = new IplImage(filename, LoadMode.GrayScale);
            IplImage phase;
            using (IplImage reimg = Cv.CreateImage(img.Size, BitDepth.F64, 1))
            using (IplImage imimg = Cv.CreateImage(img.Size, BitDepth.F64, 1))
            using (IplImage cmpimg = Cv.CreateImage(img.Size, BitDepth.F64, 2))
            {
                // (1)入力画像を実数配列にコピーし,虚数配列とマージして複素数平面を構成
                Cv.Scale(img, reimg, 1.0, 0.0);
                Cv.Zero(imimg);
                Cv.Merge(reimg, imimg, null, null, cmpimg);

                // (2)DFT用の最適サイズを計算し,そのサイズで行列を確保する
                int dft_M = Cv.GetOptimalDFTSize(img.Height - 1);
                int dft_N = Cv.GetOptimalDFTSize(img.Width - 1);

                using (CvMat dft_A = Cv.CreateMat(dft_M, dft_N, MatrixType.F64C2))
                using (IplImage image_Re = Cv.CreateImage(new CvSize(dft_N, dft_M), BitDepth.F64, 1))
                using (IplImage image_Im = Cv.CreateImage(new CvSize(dft_N, dft_M), BitDepth.F64, 1))
                using (IplImage image_Re_Norm = Cv.CreateImage(new CvSize(dft_N, dft_M), BitDepth.F64, 1))
                using (IplImage image_Im_Norm = Cv.CreateImage(new CvSize(dft_N, dft_M), BitDepth.F64, 1))
                {
                    // (3)複素数平面をdft_Aにコピーし,残りの行列右側部分を0で埋める
                    CvMat tmp;
                    Cv.GetSubRect(dft_A, out tmp, new CvRect(0, 0, img.Width, img.Height));
                    Cv.Copy(cmpimg, tmp, null);
                    if (dft_A.Cols > img.Width)
                    {
                        Cv.GetSubRect(dft_A, out tmp, new CvRect(img.Width, 0, dft_A.Cols - img.Width, img.Height));
                        Cv.Zero(tmp);
                    }

                    // (4)離散フーリエ変換を行い,その結果を実数部分と虚数部分に分解
                    Cv.DFT(dft_A, dft_A, DFTFlag.Forward, cmpimg.Height);
                    Cv.Split(dft_A, image_Re, image_Im, null, null);

                    // 正規化用に複素数の値を保持しておく
                    Cv.Split(dft_A, image_Re_Norm, image_Im_Norm, null, null);

                    // (5)スペクトルの振幅を計算 Mag = sqrt(Re^2 + Im^2)
                    Cv.Pow(image_Re, image_Re, 2.0);
                    Cv.Pow(image_Im, image_Im, 2.0);
                    Cv.Add(image_Re, image_Im, image_Re, null);
                    Cv.Pow(image_Re, image_Re, 0.5);

                    // 正規化する
                    for (int j = 0; j < image_Re.Height; j++)
                    {
                        for (int i = 0; i < image_Re.Width; i++)
                        {
                            image_Re_Norm[j, i] = image_Re_Norm[j, i] / image_Re[j, i];
                            image_Im_Norm[j, i] = image_Im_Norm[j, i] / image_Re[j, i];
                        }
                    }
                    // 正規化した複素数平面をdft_Aにコピーする
                    Cv.Merge(image_Re_Norm, image_Im_Norm, null, null, dft_A);
                    Cv.DFT(dft_A, dft_A, DFTFlag.Inverse, cmpimg.Height);
                    
                    // 逆変換した画像を表示用の変数に格納
                    Cv.Split(dft_A, image_Re, image_Im, null, null);
                    phase = image_Re.Clone();
                }
            }

            //描画
            pictureBox1.Image = bitmap;
            pictureBox2.Image = phase.ToBitmap();
        }
    }
}

結果

左が元画像、右が位相画像です。





どうやらうまくいっているようです(;´Д`)=3

ExcelVBAで画像処理の場合のように、
有効な範囲を取得しその範囲で処理を行いたい場合の方法のメモ

'シートで使用されている最大行数を取得
LastRowIndex = Cells(1, 1).End(xlDown).Row

'シートで使用されている最大列数を取得
LastColumnIndex = Cells(1, 1).End(xlToRight).Column

'カウンター
i = 0
j = 0

For i = 1 To LastRowIndex
    For j = 1 To LastColumnIndex
        'ここに処理を書く
    Next
Next


Excelのセル「A1」上でctrlキーを押しながら→や↓を押したときと同じ挙動を行っているようです。

ただ、問題点としては不連続な領域ではこの手法を扱えないというところです。

PythonをWindowsでプログラミングするために、eclipse+Pydevをインストールしたのですが、


下のように改行マークが不思議なことに・・・(汗

文字コードまわりで失敗しているか疑っていたのですが違ったようです。

code.png





調べたところ、
「一般」→「エディター」→「テキスト・エディター」の 「空白文字の表示」のチェックを外せばよいとのこと

設定





適用して確認したところ、表示が消えました。


code2.png


※変なマークと言ってしまったけど、eclipseでは標準だったのですね(;´Д`)
 失礼いたしました。。。
だいぶ更新があいたのですが、以前やったK-Meansを適用した画像をプロットしてみようと思い立ったので。

まず、元画像のグラフですが





もちろんまんべんなく色んな座標の画像を含んでいます。

以下に各クラスター数での画像とグラフを列挙していきます。

クラスター数:5



cluster5.jpg

pl5.png

クラスター数:10



cluster10.jpg

pl10.png

クラスター数:15



cluster15.jpg

pl15.png



思った通り、散らばっていた画素がクラスターの重心の画素値として置き換えられていることを確認。

おそらくヒストグラムを計測しても同じ結果となると思います。 クラスター数10と15の時だけ、クラスターの数が一つ足りない・・・
どこかにバグがあるかもしれないです・・・(汗

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

詠み人知らず

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

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

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

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