2013年01月31日

点広がり関数とフーリエ変換について

画像をぼかしたり、逆にぼけた画像を復元したりする場合に、点広がり関数を使うことがあります。
劣化画像g(x,y) = f(x,y)*h(x,y)
ここで、*は畳み込み演算を表します。

畳み込み演算は、積分というか掛けて足すというかそういう計算です。
上記の式で、h(x,y)が焦点ずれや手ブレを起こしたときの影響で、これに点広がり関数になります[1]。
一例として、ガウシアン関数([1]の(3.4))があります。

画像空間で畳み込み演算を行う場合、(本来は−∞〜+∞までの積分ですが)適当に範囲を決めて積和を計算します。
  for (int y=0; y<height; y++)
{
for (int x=0; x<width; x++)
{
g[y][x] = 0;
for (int t=-20; t<=20; t++)
{
for (int s=-20; s<=20; s++)
{
g[y][x] += f[y+t][x+s]*gaussian(s, t, sigma);
}
}
}
}
これをLenna画像に掛けると、次のような結果が得られます(σ=5.0)。

Lenna.png → gaussConv.png

4重ループになりますので、h(x,y)の範囲にもよりますが結構計算時間がかかります。

これをフーリエ変換を用いて周波数領域で計算すると、フーリエ変換の時間がかかりますが、畳み込み演算の部分は簡単になります。

G(u, v) = F(u, v)・H(u, v)

f(x, y)とh(x, y)をフーリエ変換して、F(u, v)とH(u, v)を計算して、それを単純に掛けたものがG(u,v)になるので、それを逆フーリエ変換すれば同じ結果になるはずです。
ここで注意点として、FもHも複素数ですので、単純に掛けるといっても複素数の掛け算になります。これで無駄に時間がかかってしまいました…。

    // 複素数の掛け算
for (int v=0; v<height; v++)
{
for (int u=0; u<width; u++)
{
G.ReF[v][u] = F.ReF[v][u]*H.ReF[v][u]-F.ImF[v][u]*H.ImF[v][u];
G.ImF[v][u] = F.ReF[v][u]*H.ImF[v][u]+F.ImF[v][u]*H.ReF[v][u];
}
}
ここで、Lenna画像のRGBそれぞれのパワースペクトルは以下のようになります。

Frgb(u, v)
FFT_R_Power.png FFT_G_Power.png FFT_B_Power.png
また、ガウシアン関数のパワースペクトルは以下のようになります。

h(x, y) → H(u, v)
h_gauss.png → FFT_H_Power.png
四隅に分かれているのは、離散フーリエ変換を左上を原点にしているからです。
この説明は[3]に書かれていました。

ガウシアン関数をフーリエ変換したものもガウシアン関数になりますので、H(u, v)もガウシアン関数になっています。

これでG(u, v)が計算できましたので、これを逆FFTすれば、畳み込みで計算した結果と同じ結果が得られることになります。

実行結果

gaussIFFT.png

これを利用してボケやブレのある画像から原画像を復元する手法があります。

F^(u, v)=G(u, v)/H(u, v)

上記の式ではノイズが乗った場合にひどいことになるので、それを解決する手法としてWienerフィルタなどが提案されているそうです。

GLSLでレンダリング→CUDAもしくはOpenCLでFFT→畳み込み処理→IFFTを試してみようかなと思ったのですが計算時間的にリアルタイムは厳しいんでしょうかね。

参考サイト
[1] http://pub.maruzen.co.jp/realize/search/sample/RLB220496.pdf
[2] http://www7a.biglobe.ne.jp/~fairytale/article/program/graphics.html#dft
[3] http://www.dm.u-tokai.ac.jp/~hama/lecture/2009/img/img_12.ppt

web拍手 by FC2
posted by シンドラー at 01:18 | Comment(0) | TrackBack(0) | Image Processing | このブログの読者になる | 更新情報をチェックする

2013年01月14日

OpenCLの使い方について その4

OpenCLとOpenGLの連携ができるようになりましたので、少し色々なレンダリングを試してみたいと思います。
今回は以前も少しやっていましたが、異方性反射です。

異方性反射を実現するためには、法線ベクトルだけではなく、接線、従接線ベクトルが必要になります。
この接線、従接線ベクトルの計算は、テクスチャのUV座標を使用する方法や、適当に指定した軸と法線ベクトルの外積を利用して計算する方法などがあるようです[1][2]。
今回は、上方向ベクトルと外積を使って適当に計算してみました。

後は異方性を考慮したレンダリング方法ということで、[3]を参考にAshikhminモデルを試してみました。
OpenCL側では、FBOでレンダリングした結果にガウスフィルタを掛けたりして最終的な画像を生成しているのですが、引数で渡したFBOの画像は必ず参照しないとエラーがでてしまうようです。
(文法的に間違っていないのにコンパイルエラーで-30が返ってきて無駄に詰まりました。)

実行結果

前回と同様にあぴミクさんを使用させていただいております。
[4]でいうところのnuとnvという2つのパラメータを変化させることで結果が変わります。
(見えにくいですがタイトルバーに数値を表示しています)
最初は10-10で広く浅く、100-100ぐらいでフォンシェーディングのような効果、10-2000や2000-10で縦方向もしくは横方向に伸びた光沢が得られています。



本当は部位毎に接線ベクトル方向やこのパラメータを変更した方がいいのだとは思います。

[1] http://marupeke296.com/DXPS_No12_CalcTangentVectorSpace.html
[2] http://lucille.sourceforge.net/blog/archives/000013.html
[3] http://asura.iaigiri.com/XNA_GS/xna19.html
[4] http://www.cs.utah.edu/~shirley/papers/jgtbrdf.pdf
web拍手 by FC2
posted by シンドラー at 03:00 | Comment(0) | TrackBack(0) | OpenCL | このブログの読者になる | 更新情報をチェックする

2013年01月07日

PMXファイルの表示

MMD用に素晴らしいミクさんがたくさん公開されていますね。
最近のミクさんはPMX形式になっているものが殆どですので、PMXファイルを読み込んで表示するようにしました。
PMXファイルについては、VPVP wikiに解説[1]がありますし、読込み可能なライブラリの紹介[2]もありますし、PMXエディタに仕様書も入っています。

自分で作る必要はないのですが、自分で作っていて困った点のメモです。
1.UTF16やUTF8がよくわからない
2.pngの透過色はOpenCVで昔は読み込んでもらえなかった

1.については、とりあえずUTF16だけに対応しました。
Windowsでは、charとかTCHARとかWCHAR色々あってよくわからない文字コードですが、TCHARはVisual Studioのプロパティ設定で振舞いが変わる汎用的な型で、WCHARの方がワイドバイト文字だそうです[3]。
PMXファイルのエンコード方式がUTF16の場合、マルチバイト文字のようですので、WCHARとして読み込んでwcstombs_s関数などでchar型に変換すればいいようです。

2.については、いつの間にか(2.3.1ぐらいから)pngのα値を読み込めるようになっていたようです。
注意点としては、画像読込関数の第2引数に-1を指定する必要があるみたいです[4]。

[1] http://www6.atwiki.jp/vpvpwiki/pages/284.html
[2] http://www6.atwiki.jp/vpvpwiki/pages/288.html
[3] http://note.phyllo.net/?eid=1106043
[4] http://opencv.jp/cookbook/opencv_img.html#png

実行結果

FBOに描画→OpenCLで3x3のガウシアンフィルタをかけた結果です。
ローカルメモリなどを使用していないので遅いですが、FPSは60弱ぐらいなのでまぁいいですかね。


Appearance Miku あぴミクさんを使用させていただいております。
以下ガウシアンフィルタのカーネル関数です。フィルタは普通はCPUから渡すと思いますが、面倒だったので直書きです。
const sampler_t s_linear = CLK_FILTER_LINEAR | CLK_NORMALIZED_COORDS_FALSE | CLK_ADDRESS_CLAMP_TO_EDGE;
__kernel void postprocess(__read_only image2d_t im_in, __write_only image2d_t im_out, int imgw, int imgh)
{
    const unsigned int x = get_global_id(0);
    const unsigned int y = get_global_id(1);
    const int index = y * imgw + x;

    const float k[9] = {1.0/16.0, 2.0/16.0, 1.0/16.0,
        2.0/16.0, 4.0/16.0, 2.0/16.0,
        1.0/16.0, 2.0/16.0, 1.0/16.0};

    if( x >= imgw || y >= imgh ) return;

    float4 output;
    output = k[0]*read_imagef(im_in, s_linear, (float2)(x-1, y-1));
    output += k[1]*read_imagef(im_in, s_linear, (float2)(x-1, y ));
    output += k[2]*read_imagef(im_in, s_linear, (float2)(x-1, y+1));
    output += k[3]*read_imagef(im_in, s_linear, (float2)(x , y-1));
    output += k[4]*read_imagef(im_in, s_linear, (float2)(x , y ));
    output += k[5]*read_imagef(im_in, s_linear, (float2)(x , y+1));
    output += k[6]*read_imagef(im_in, s_linear, (float2)(x+1, y-1));
    output += k[7]*read_imagef(im_in, s_linear, (float2)(x+1, y ));
    output += k[8]*read_imagef(im_in, s_linear, (float2)(x+1, y+1));

    write_imagef(im_out, (int2)(x, y), output);
}

web拍手 by FC2
posted by シンドラー at 21:16 | Comment(0) | TrackBack(0) | OpenCL | このブログの読者になる | 更新情報をチェックする

2013年01月04日

WebGLのテスト

明けましておめでとうございます。といってももう4日ですね。
冬休みということでWebGLを少し試してみました。

GLSLに慣れていれば、特に問題なさそうですね。ESなので多少違うのだとは思いますが。

実行結果


CanvasとWebGLをサポートしたブラウザ(Chrome?)を使用してください。



↑は画像ではなくWebGLの描画です。マウスイベント等何も設定していないので画像と変わりませんが。
Chromeでしか動作確認していませんので、それ以外だと表示されないと思います。
(getContext("webkit-3d")でのみ取得しています)

[2]で使われていたglMatrix-0.9.5.min.jsというものを使用させていただいております。
ほぼ[2]と一緒ですが一応以下のjsになっています。
DrawRects.js

参考文献
[1] 松田晃一著、“WebGL+HTML5 3DCGプログラミング入門”、株式会社カットシステム、2012
[2] Hack The WebGL (WebGL勉強会): WebGL lesson 1

web拍手 by FC2
posted by シンドラー at 19:35 | Comment(0) | TrackBack(0) | WebGL | このブログの読者になる | 更新情報をチェックする

広告


この広告は60日以上更新がないブログに表示がされております。

以下のいずれかの方法で非表示にすることが可能です。

・記事の投稿、編集をおこなう
・マイブログの【設定】 > 【広告設定】 より、「60日間更新が無い場合」 の 「広告を表示しない」にチェックを入れて保存する。