OpenCV 5.0.0
Open Source Computer Vision
読み込み中...
検索中...
見つかりません
🤖 AIによる機械翻訳(非公式) — これは OpenCV 5.0.0 公式リファレンス(英語)を AI (Claude) で自動翻訳したものです。訳に誤りを含む場合があります。正確な情報は 公式英語版(原文) を参照してください。
離散フーリエ変換

前のチュートリアル: 画像のコントラストと明るさを変える!
次のチュートリアル: XML / YAML / JSON ファイルによるファイル入出力

原著者Bernát Gábor
互換性OpenCV >= 3.0

目的

次の問いに対する答えを探す:

  • フーリエ変換とは何か、なぜ使うのか?
  • OpenCV ではどのように行うのか?
  • 次のような関数の使い方: copyMakeBorder() , merge() , dft() , getOptimalDFTSize() , log() , normalize()

ソースコード

以下は dft() の使用例である:

解説

フーリエ変換は画像を正弦成分と余弦成分に分解する。言い換えると、画像を空間領域から周波数領域に変換する。その考え方は、任意の関数は無限個の正弦関数と余弦関数の和によって厳密に近似できるというものである。フーリエ変換はそれを実現する方法の一つである。数学的に、2次元画像のフーリエ変換は次のとおりである:

\[F(k,l) = \displaystyle\sum\limits_{i=0}^{N-1}\sum\limits_{j=0}^{N-1} f(i,j)e^{-i2\pi(\frac{ki}{N}+\frac{lj}{N})}\]

\[e^{ix} = \cos{x} + i\sin {x}\]

ここで f は空間領域における画像の値、F は周波数領域における値である。変換の結果は複素数になる。これを表示するには、実部 (real) 画像と 虚部 (complex) 画像で表す方法と、振幅 (magnitude) 画像と 位相 (phase) 画像で表す方法がある。ただし画像処理アルゴリズム全般において重要なのは 振幅 (magnitude) 画像だけであり、これは画像の幾何学的構造について必要な情報をすべて含んでいる。とはいえ、これらの形式で画像に何らかの修正を加え、その後に逆変換する必要がある場合は、両方を保持しておく必要がある。

このサンプルでは、フーリエ変換の 振幅 (magnitude) 画像を計算して表示する方法を示す。デジタル画像の場合、画像は離散的である。これは、与えられた領域内の値を取りうることを意味する。例えば、基本的なグレースケール画像では値は通常0から255の間である。したがってフーリエ変換も離散型である必要があり、その結果が離散フーリエ変換 (DFT) となる。画像の構造を幾何学的観点から判定したいときには、これを使うとよい。以下に従うべき手順を示す(グレースケール入力画像 I の場合):

画像を最適なサイズに拡張する

DFTの性能は画像サイズに依存する。2、3、5の倍数となるサイズで最速になる傾向がある。したがって、最大の性能を得るためには、こうした性質を持つサイズになるよう画像に境界値をパディングするのが一般によい考えである。getOptimalDFTSize() はこの最適なサイズを返し、copyMakeBorder() 関数を使って画像の境界を拡張できる(追加されたピクセルはゼロで初期化される):

複素数値と実数値の両方の場所を確保する

フーリエ変換の結果は複素数である。これは、各画像値に対して結果が2つの画像値(成分ごとに1つ)になることを意味する。さらに、周波数領域の値の範囲は空間領域に比べてはるかに大きい。したがって、これらは通常、少なくとも float 形式で格納する。そのため、入力画像をこの型に変換し、複素数値を保持するためにもう1つチャンネルを追加して拡張する:

離散フーリエ変換を行う

インプレース計算(入力と出力が同じ)も可能である:

実数値と複素数値を大きさ(magnitude)に変換する

複素数は実部 (Re) と複素部(虚部 - Im) を持つ。DFTの結果は複素数である。DFTの振幅は次のとおりである:

\[M = \sqrt[2]{ {Re(DFT(I))}^2 + {Im(DFT(I))}^2}\]

OpenCVのコードに翻訳すると:

対数スケールに切り替える

フーリエ係数のダイナミックレンジは大きすぎて画面に表示できないことがわかる。このままでは観察できないような、小さい値と大きく変化する値が混在している。そのため、大きい値はすべて白い点として表れ、小さい値は黒として表れる。可視化のためにグレースケール値を使うには、線形スケールを対数スケールに変換すればよい:

\[M_1 = \log{(1 + M)}\]

OpenCVのコードに翻訳すると:

切り出しと並べ替え

最初のステップで画像を拡張したことを思い出してほしい。ここで、新たに追加した値を破棄する時が来た。可視化のために、結果の象限を並べ替えて、原点(ゼロ, ゼロ)が画像の中心に対応するようにしてもよい。

正規化

これも可視化のために行う。これで振幅が得られたが、これはまだ画像の表示範囲である0から1の外にある。cv::normalize() 関数を使って、値をこの範囲に正規化する。

結果

応用例として、画像に存在する幾何学的な向きを判定することが考えられる。例えば、テキストが水平かどうかを調べてみよう。テキストを見ると、テキストの行が水平な線のようなものを形成し、文字が垂直な線のようなものを形成していることに気づくだろう。テキスト断片のこれら2つの主要な構成要素は、フーリエ変換においても見ることができる。テキストに関するこの水平な画像とこの回転した画像を使ってみよう。

水平なテキストの場合:

回転したテキストの場合:

周波数領域で最も影響力のある成分(振幅画像上で最も明るい点)が、画像中のオブジェクトの幾何学的な回転に従っていることがわかる。ここからオフセットを計算し、画像を回転させて最終的な位置ずれを補正できる。