上記の広告は1ヶ月以上更新のないブログに表示されています。
新しい記事を書く事で広告が消せます。
Log-Polar変換なのですが、霊長類の網膜のモデルらしいです。
中心部は解像度が高いのですが、周辺部は低いという特性らしいです。

以下のような式で変換をします。(極座標変換でのrにlogをとったものです。)
logpolar.png


ですが、今まで行った回転のときのように、どの変換先の座標は元画像のどの位置を参照するかと考えます。
すなわち以下の逆変換を使います。
logpolar2.png



ソース


OpenCVのやりかたを真似したため、一部のみ抜粋です。
bmpが入力画像、lpcが出力画像です。
また、Mはformから入力された値を使用しました。256x256だと40くらいがよさそうです。
BiLinearInterpolationは以前の補間のさいに使用したものです。

画像を回転させる際の画素を補間するコード | 詠み人知らずの備忘録


Bitmap lpc = new Bitmap(bmp.Width, bmp.Height);

int cx = lpc.Width / 2;
int cy = lpc.Height / 2;

double M = Convert.ToDouble(textBox1.Text);

for (int i = 0; i < lpc.Width; i++)
{
    for (int j = 0; j < lpc.Height; j++)
    {
        double r = Math.Exp((double)i / (double)M);
        double x = r * Math.Cos(2 * Math.PI * j / lpc.Height) + cx;
        double y = r * Math.Sin(2 * Math.PI * j / lpc.Height) + cy;
        
        if (0 < x && x < bmp.Width - 1 && 0 < y && y < bmp.Height - 1)
        {
            Color bmpCol = BiLinearInterpolation(x, y, bmp);
            lpc.SetPixel(i, j, bmpCol);
        }

    }
}



結果

変換した結果、(x,y)と(rho,phi)の関係がどのような対応になるのかは、下記のサイトがイメージがつきやすいと思います。
Log polar transform - Rhea



※クリックで拡大します。

【元画像】
Lenna

【変換画像】
Lenna_log.png

【元画像】
Lenna_90.png

【変換画像】
Lenna_log90.png



画像の回転が、変換後の画像の下への移動としてあらわされています。
この考え方を、位相限定相関法(POC)と供に使用すると、角度のズレを判別できるためより便利な手法となります。(RIPOCという手法らしいです。)

位相限定相関法(POC) - スズメレンダラー・クマ将棋の開発日記

最近IronPythonに興味をもちまして。
調べたことのメモを


・利点
  1. .Netが使用できる
  2. numpyなどのライブラリが使用できる
  3. Visual Studio(expressじゃないほう)で開発ができる

Pythonはeclipseでも開発できますが

IronPythonのインストールからVisual Studio(expressじゃないほう)で使用できるようになるまでの準備はこちらで
Visual Studio 2010のインストール


VSで生成されるプログラムは以下のようになりました。
実行すれば何もないWindowが表示されます。

import clr
clr.AddReference('System.Drawing')
clr.AddReference('System.Windows.Forms')

from System.Drawing import *
from System.Windows.Forms import *

class MyForm(Form):
    def __init__(self):
        # Create child controls and initialize form
        pass


Application.EnableVisualStyles()
Application.SetCompatibleTextRenderingDefault(False)

form = MyForm()
Application.Run(form)



ButtonやLabelをGUI上でペタペタ貼ってUIを作成する機能があれば楽なのですが。。。


IronPython用(?)のnumpyをインストールしたい場合は下記のURLの
3.) ironpkg
4.) scipy
を実施すれば問題なしです
https://www.enthought.com/repo/.iron/

以前、Matlab(Octave)で実装した。
位相限定相関法(POC)




位相画像をOpenCVSharpで実装した
位相画像をC#とOpenCVSharpで実装

の続きです。

ソース

ソースはクラスもきちんと作れていないし、汚いソースなので紙面の都合上、肝の部分だけ抜粋します。

あとバグがありそうですので・・・見つけたらオシエテクダサイ。

// DFT用の最適サイズを計算し,そのサイズを返却する。
public static int GetOptimalDFTSize(int size)
{
    return Cv.GetOptimalDFTSize(size);
}

public static CvMat dft(IplImage img)
{
    IplImage reimg = Cv.CreateImage(img.Size, BitDepth.F64, 1);
    IplImage imimg = Cv.CreateImage(img.Size, BitDepth.F64, 1);
    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 = GetOptimalDFTSize(img.Height);
    int dft_N = GetOptimalDFTSize(img.Width);

    CvMat dft_A = Cv.CreateMat(dft_M, dft_N, MatrixType.F64C2);

    // (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);

    return dft_A;
}

public static IplImage idft(CvMat dft)
{
    // DFT用の最適サイズを計算し,そのサイズで行列を確保する
    int dft_M = GetOptimalDFTSize(dft.Height);
    int dft_N = GetOptimalDFTSize(dft.Width);
    IplImage img_Re = Cv.CreateImage(new CvSize(dft_N, dft_M), BitDepth.F64, 1);
    IplImage img_Im = Cv.CreateImage(new CvSize(dft_N, dft_M), BitDepth.F64, 1);

    // 逆変換
    Cv.DFT(dft, dft, DFTFlag.Inverse, dft_N);

    // 逆変換した画像を表示用の変数に格納
    Cv.Split(dft, img_Re, img_Im, null, null);

    return img_Re;
}

public static CvMat Conj(CvMat dft)
{
    int dft_M = Image.GetOptimalDFTSize(dft.Height);
    int dft_N = Image.GetOptimalDFTSize(dft.Width);

    using (CvMat srcRe = Cv.CreateMat(dft_M, dft_N, MatrixType.F64C1))
    using (CvMat srcIm = Cv.CreateMat(dft_M, dft_N, MatrixType.F64C1))
    {
        // 実部と虚部に分解
        Cv.Split(dft, srcRe, srcIm, null, null);
        for (int i = 0; i < dft_M; i++)
        {
            for (int j = 0; j < dft_N; j++)
            {
                srcIm[i, j] = -1 * srcIm[i, j];
            }
        }
        // 実部・虚部で計算した値をマージする
        Cv.Merge(srcRe, srcIm, null, null, dft);
    }
    return dft;
}

public static IplImage PhaseOnlyCorrelation(IplImage img1, IplImage img2)
{
    int dft_M = Image.GetOptimalDFTSize(img1.Height);
    int dft_N = Image.GetOptimalDFTSize(img1.Width);

    IplImage img = Cv.CreateImage(new CvSize(dft_M, dft_N), BitDepth.F64, 1);
    CvMat dft1 = Cv.CreateMat(dft_M, dft_N, MatrixType.F64C2);
    CvMat dft2 = Cv.CreateMat(dft_M, dft_N, MatrixType.F64C2);
    CvMat result = Cv.CreateMat(dft_M, dft_N, MatrixType.F64C2);

    // フーリエ変換
    dft1 = Image.dft(img1);
    // フーリエ変換後に複素共役にする
    dft2 = Image.Conj(Image.dft(img2));

    using (IplImage srcRe1 = Cv.CreateImage(new CvSize(dft_N, dft_M), BitDepth.F64, 1))
    using (IplImage srcIm1 = Cv.CreateImage(new CvSize(dft_N, dft_M), BitDepth.F64, 1))
    using (IplImage srcRe2 = Cv.CreateImage(new CvSize(dft_N, dft_M), BitDepth.F64, 1))
    using (IplImage srcIm2 = Cv.CreateImage(new CvSize(dft_N, dft_M), BitDepth.F64, 1))
    using (IplImage dstRe = Cv.CreateImage(new CvSize(dft_N, dft_M), BitDepth.F64, 1))
    using (IplImage dstIm = Cv.CreateImage(new CvSize(dft_N, dft_M), BitDepth.F64, 1))
    {
        // フーリエ変換した画像を実部と虚部に分解
        Cv.Split(dft1, srcRe1, srcIm1, null, null);
        Cv.Split(dft2, srcRe2, srcIm2, null, null);

        for (int i = 0; i < dft_M; i++)
        {
            for (int j = 0; j < dft_N; j++)
            {
                // 画像をかけあわせる F×G*
                dstRe[i, j] = srcRe1[i, j] * srcRe2[i, j] - srcIm1[i, j] * srcIm2[i, j];
                dstIm[i, j] = srcRe1[i, j] * srcIm2[i, j] + srcRe2[i, j] * srcIm1[i, j];

                // 絶対値を計算しその値で割る |F×G*|
                double spectrum = Math.Sqrt(dstRe[i, j] * dstRe[i, j] + dstIm[i, j] * dstIm[i, j]);
                dstRe[i, j] = dstRe[i, j] / spectrum;
                dstIm[i, j] = dstIm[i, j] / spectrum;
            }
        }
        // 計算結果を統合する
        Cv.Merge(dstRe, dstIm, null, null, result);
    }

    // 逆フーリエ変換
    img = idft(result);

    // データを解放
    dft1.Dispose();
    dft2.Dispose();
    result.Dispose();

    return img;
}



しかし、OpenCVって複素数をサポートしていないのかなぁ・・・
チャンネル毎に行うのはちょっと面倒で計算ミスが起こりそう・・・


結果

今回も画像はnashruddin.com - Phase Correlation in OpenCVさんから拝借したもので、行いました。


結果は以下のようになるようです。

相関値:0.6344
移動距離:(6,15)



1.jpg 2.jpg

POCを行ったグラフ
左上に白い点がありますが、そこがピーク値です。
POC1.png


ちなみに、私の計算結果は・・・
相関値:0.5904
移動距離:(6,15)



・・・あれ?ちょっと相関値が違う・・・
移動させた距離はあっているのだけどなぁ・・・


しばらく放置してしまいました。新年一つ目の内容は画像の拡大・縮小にしました。



画像を拡大・縮小させる場合は、回転させる場合と同様に考えます。
つまり、outputの画像の座標から必要なinputの画像の座標を求めて画素値を取得します。

コードは以下のように考えました。
Scaleが取得した拡大率になります。
for (int i = 0; i < bitmap.Width; i++)
{
    for (int j = 0; j < bitmap.Height; j++)
    {
        double x = i / Scale;
        double y = j / Scale;

        if (chk[flag](x, y, bmp))
        {
            Color bmpCol = func[flag](x, y, bmp);
            bitmap.SetPixel(i, j, bmpCol);
        }
    }
}


前回同様処理の肝の部分だけ書きました。
delegateを用いてfunc内で使用する補間用のメソッドを切り替えています。 詳細なコードは、
画像を回転させる際の画素を補間するコード
で記載した補間方法を使用しています。

拡大(1.3倍)

【ニアレストネイバー】
Near13.png


【バイリニア】
bilinear13.png


【バイキュービック】
bicubic13.png


【Lanczos2】
Lan2_13.png


バイリニアは若干ぼやけているように見えます。
下二つの違いがあるのですかねぇ・・・

縮小(0.75倍)

【ニアレストネイバー】
Near075.png


【バイリニア】
bilinear075.png


【バイキュービック】
bicubic075.png


【Lanczos2】
Lan2_075.png


ギザギザが目立つようです・・・
縮小の場合は低域通過フィルターで高域をカットしないとダメなようです。

以前、連続で記載していた補間についてですが、画像しか載せていなかったのでまとめてコードを記載いたしました。
バグはあると思いますが、ご了承願います。m(_ _)m


基本構造は以下のようになります。
ループ内の(x,y)は出力先の画像の画素値を求めるために、入力の画像のどの位置の画像から取得するかを求めています。

// 角度をラジアンへ変更
double radian = ConvertRadian(degree);

// 回転に使用するsin,cosは定数値
double COS = Math.Cos(radian);
double SIN = Math.Sin(radian);

// 出力先の画像の高さ・幅を計算
int width = (int)(Math.Abs(bmp.Width * COS) + Math.Abs(bmp.Height * SIN) + 0.5);
int height = (int)(Math.Abs(bmp.Width * SIN) + Math.Abs(bmp.Height * COS) + 0.5);

// 出力先の画像を生成
Bitmap bit = new Bitmap(width, height);
FillBlack(bit);

// 中心座標
int cx = bmp.Width / 2;
int cy = bmp.Height / 2;

int cx2 = bit.Width / 2;
int cy2 = bit.Height / 2;

for (int i = 0; i < bit.Width; i++)
{
    for (int j = 0; j < bit.Height; j++)
    {
        double x = (i - cx2) * COS + (j - cy2) * SIN + cx;
        double y = -(i - cx2) * SIN + (j - cy2) * COS + cy;
        
        if (chk(x, y, bmp))
        {
            Color bmpCol = func(x, y, bmp);
            bit.SetPixel(i, j, bmpCol);
        } 
}


chk関数は入力の画像内か判断するだけですので省略します
funcの中を以下に記述していきます。

バイリニア補間

http://authorunknown408.blog.fc2.com/blog-entry-22.html

private static Color BiLinearInterpolation(double x, double y, Bitmap bmp)
{
    int x1 = (int)Math.Floor(x);
    int y1 = (int)Math.Floor(y);

    double R = 0;
    double G = 0;
    double B = 0;

    double[] Wx = new double[2];
    double[] Wy = new double[2];

    Wx[0] = 1 - (x - x1) ;
    Wx[1] = x - x1;
    Wy[0] = 1 - (y - y1);
    Wy[1] = y - y1;

    for (int i = 0; i < Wx.Length; i++)
    {
        for (int j = 0; j < Wy.Length; j++)
        {
            R += ((double)bmp.GetPixel(x1 + i, y1 + j).R * Wx[i] * Wy[j]);
            G += ((double)bmp.GetPixel(x1 + i, y1 + j).G * Wx[i] * Wy[j]);
            B += ((double)bmp.GetPixel(x1 + i, y1 + j).B * Wx[i] * Wy[j]);

        }
    }
    
    return Color.FromArgb((int)R, (int)G, (int)B);
}


バイキュービック補間

http://authorunknown408.blog.fc2.com/blog-entry-23.html

private static Color BiCubicInterpolation(double x, double y, Bitmap bmp)
{
    double[] dx = new double[4];
    double[] dy = new double[4];
    double[] Wx = new double[4];
    double[] Wy = new double[4];
    
    int x1 = (int)Math.Floor(x);
    int y1 = (int)Math.Floor(y);
    
    double R = 0;
    double G = 0;
    double B = 0;

    dx[0] = (x1 - x) - 1;
    dx[1] = (x1 - x);
    dx[2] = (Math.Ceiling(x) - x);
    dx[3] = dx[2] + 1;

    dy[0] = (y1 - y) - 1;
    dy[1] = (y1 - y);
    dy[2] = (Math.Ceiling(y) - y);
    dy[3] = dy[2] + 1;

    for (int i = 0; i < dx.Length; i++)
    {
        Wx[i] = BiCubicWeight(dx[i]);
        Wy[i] = BiCubicWeight(dy[i]);
    }

    double temp = 0;
    for (int i = 0; i < Wx.Length; i++)
    {
        for (int j = 0; j < Wy.Length; j++)
        {
            R += ((double)bmp.GetPixel(x1 - 2 + i, y1 - 2 + j).R * Wx[i] * Wy[j]);
            G += ((double)bmp.GetPixel(x1 - 2 + i, y1 - 2 + j).G * Wx[i] * Wy[j]);
            B += ((double)bmp.GetPixel(x1 - 2 + i, y1 - 2 + j).B * Wx[i] * Wy[j]);

            temp += Wx[i] * Wy[j];
        }
    }
    
    R = ColorCheck(R / temp);
    G = ColorCheck(G / temp);
    B = ColorCheck(B / temp);
    
    return Color.FromArgb((int)R, (int)G, (int)B);
}

private static double BiCubicWeight(double x)
{
    double a = -1;
    double h = 0;
    double t = Math.Abs(x);

    if (0 <= t && t <= 1)
    {
        h = (a + 2) * Math.Pow(t, 3) - (a + 3) * Math.Pow(t, 2) + 1;
    }
    else if (1 <= t && t <= 2)
    {
        h = a * Math.Pow(t, 3) - 5 * a * Math.Pow(t, 2) + 8 * a * t - 4 * a;
    }
    else
    {
        h = 0;
    }
    return h;
}


Lanczos補間

http://authorunknown408.blog.fc2.com/blog-entry-24.html

public static Color Lanczos2Interpolation(double x, double y, Bitmap bmp)
{
    double[] dx = new double[4];
    double[] dy = new double[4];
    double[] Wx = new double[4];
    double[] Wy = new double[4];

    int x1 = (int)Math.Floor(x);
    int y1 = (int)Math.Floor(y);

    double R = 0;
    double G = 0;
    double B = 0;

    dx[0] = (x1 - x) - 1;
    dx[1] = (x1 - x);
    dx[2] = (Math.Ceiling(x) - x);
    dx[3] = dx[2] + 1;

    dy[0] = (y1 - y) - 1;
    dy[1] = (y1 - y);
    dy[2] = (Math.Ceiling(y) - y);
    dy[3] = dy[2] + 1;

    for (int i = 0; i < dx.Length; i++)
    {
        Wx[i] = LanczosWeight(dx[i], 2);
        Wy[i] = LanczosWeight(dy[i], 2);
    }

    double temp = 0;
    for (int i = 0; i < Wx.Length; i++)
    {
        for (int j = 0; j < Wy.Length; j++)
        {
            R += ((double)bmp.GetPixel(x1 - 2 + i, y1 - 2 + j).R * Wx[i] * Wy[j]);
            G += ((double)bmp.GetPixel(x1 - 2 + i, y1 - 2 + j).G * Wx[i] * Wy[j]);
            B += ((double)bmp.GetPixel(x1 - 2 + i, y1 - 2 + j).B * Wx[i] * Wy[j]);

            temp += Wx[i] * Wy[j];
        }
    }
    
    R = ColorCheck(R / temp);
    G = ColorCheck(G / temp);
    B = ColorCheck(B / temp);


    return Color.FromArgb((int)R, (int)G, (int)B);
}
public static Color Lanczos3Interpolation(double x, double y, Bitmap bmp)
{
    double[] dx = new double[6];
    double[] dy = new double[6];
    double[] Wx = new double[6];
    double[] Wy = new double[6];

    int x1 = (int)Math.Floor(x);
    int y1 = (int)Math.Floor(y);

    double R = 0;
    double G = 0;
    double B = 0;

    dx[0] = (x1 - x) - 2;
    dx[1] = (x1 - x) - 1;
    dx[2] = (x1 - x);
    dx[3] = (Math.Ceiling(x) - x);
    dx[4] = dx[3] + 1;
    dx[5] = dx[4] + 1;

    dy[0] = (y1 - y) - 2;
    dy[1] = (y1 - y) - 1;
    dy[2] = (y1 - y);
    dy[3] = (Math.Ceiling(y) - y);
    dy[4] = dy[3] + 1;
    dy[5] = dy[4] + 1;

    for (int i = 0; i < dx.Length; i++)
    {
        Wx[i] = LanczosWeight(dx[i], 3);
        Wy[i] = LanczosWeight(dy[i], 3);
    }

    double temp = 0;
    for (int i = 0; i < Wx.Length; i++)
    {
        for (int j = 0; j < Wy.Length; j++)
        {
            R += ((double)bmp.GetPixel(x1 - 2 + i, y1 - 2 + j).R * Wx[i] * Wy[j]);
            G += ((double)bmp.GetPixel(x1 - 2 + i, y1 - 2 + j).G * Wx[i] * Wy[j]);
            B += ((double)bmp.GetPixel(x1 - 2 + i, y1 - 2 + j).B * Wx[i] * Wy[j]);

            temp += Wx[i] * Wy[j];
        }
    }

    R = ColorCheck(R / temp);
    G = ColorCheck(G / temp);
    B = ColorCheck(B / temp);

    return Color.FromArgb((int)R, (int)G, (int)B);
}

private static double Sinc(double x)
{
    double X = Math.PI * x;
    if (X == 0)
    {
        return 1;
    }
    else
    {
        return Math.Sin(X) / X;
    }
}

private static double ColorCheck(double x)
{
    if(x > 255.0)
    {
        x = 255;
    }
    else if (x < 0)
    {
        x = 0.0;
    }
    else
    {
        // do nothing
    }

    return x;
}

private static double LanczosWeight(double x, int n)
{            
    double t = Math.Abs(x);
    double h = 0;

    if (t <= n)
    {
        h = Sinc(t) * Sinc(t / n);
    }
    else
    {
        h = 0;
    }
    return h;
}




ちょっとわかりずらいかもしれませんが、残しておきます。

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

詠み人知らず

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

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

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

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