5
7

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

OpenCV(C++)チュートリアル:離散フーリエ変換

Last updated at Posted at 2018-12-21

公式チュートリアルのDiscrete Fourier Transformの要約。
筆者の環境はdebian9。

##やること
入力画像を離散フーリエ変換して周波数画像を出力する
###何が嬉しいのか
入力画像の幾何的な構造についての情報が得られる。今回の例では文字列が傾いているとかがわかりやすくなる。
###理論
フーリエ変換は画像をサイン・コサインの要素に分解する。つまり空間ドメインから周波数ドメインへ変換する。フーリエ変換のアイデアは、いかなる関数も無限のsin,cos関数の和で近似できるというもの。
画像のフーリエ変換は次式で表される。

F(k,l)= \sum_{i=0}^{N−1}\sum_{j=0}^{N−1}f(i,j)e^{−i2π(\frac{ki}{N}+\frac{lj}{N})}
e^{ix}=cosx+isinx

$f$は空間ドメインの画素値、$F$はその周波数ドメイン。結果は複素数になる。実数部、虚数部、絶対値、位相それぞれで画像を作れるが、ほしい情報をもっているのは絶対値(magnitude)画像のみ。

ピクセルの値は離散値なので離散フーリエ変換(Discrete Fourier Transform (DFT))を使う。グレースケール画像で行う。

##コード

dft.cpp
#include "opencv2/core.hpp"
#include "opencv2/imgproc.hpp"
#include "opencv2/imgcodecs.hpp"
#include "opencv2/highgui.hpp"
#include <iostream>
using namespace cv;
using namespace std;
static void help(void)
{
    cout << endl
        <<  "This program demonstrated the use of the discrete Fourier transform (DFT). " << endl
        <<  "The dft of an image is taken and it's power spectrum is displayed."          << endl
        <<  "Usage:"                                                                      << endl
        <<  "./discrete_fourier_transform [image_name -- default ../data/lena.jpg]"       << endl;
}
int main(int argc, char ** argv)
{
    help();
    const char* filename = argc >=2 ? argv[1] : "../data/lena.jpg";
    Mat I = imread(filename, IMREAD_GRAYSCALE);
    if( I.empty()){
        cout << "Error opening image" << endl;
        return -1;
    }
    Mat padded;
    // 入力画像をDFTに最適な大きさに広げる。画像サイズが2,3,5の倍数のときに高速になる
    int m = getOptimalDFTSize( I.rows );
    int n = getOptimalDFTSize( I.cols ); 
    // 入力画像を中央に置き、周囲は0で埋める
    copyMakeBorder(I, padded, 0, m - I.rows, 0, n - I.cols, BORDER_CONSTANT, Scalar::all(0));
    //dtfの結果は複素数(1要素につき2つ値がある)であり、また値の表現範囲が広い。
    //そのためfloatと複素数を保持するもので2枚作る
    Mat planes[] = {Mat_<float>(padded), Mat::zeros(padded.size(), CV_32F)};
    Mat complexI;
    // 2枚の画像を、2チャネルを持った1枚にする
    merge(planes, 2, complexI);         
    // complexIにdftを適用し、complexIに結果を戻す
    dft(complexI, complexI);            
    
    // 絶対値を計算し、logスケールにする
    // => log(1 + sqrt(Re(DFT(I))^2 + Im(DFT(I))^2))
    split(complexI, planes);                   // planes[0] = Re(DFT(I), planes[1] = Im(DFT(I))
    magnitude(planes[0], planes[1], planes[0]);// planes[0] = magnitude
    Mat magI = planes[0];
    magI += Scalar::all(1);
    // 結果の値は大きすぎるものと小さすぎるものが混じっているので、logを適用して抑制する                  
    log(magI, magI);
    // 行・列が奇数の場合用。クロップする
    magI = magI(Rect(0, 0, magI.cols & -2, magI.rows & -2));
    // 画像の中央に原点が来るように、象限を入れ替える(元のままだと画像の中心部分が4つ角の方向を向いているらしい?)
    int cx = magI.cols/2;
    int cy = magI.rows/2;
    Mat q0(magI, Rect(0, 0, cx, cy));   // 左上(第二象限)
    Mat q1(magI, Rect(cx, 0, cx, cy));  // 右上(第一象限)
    Mat q2(magI, Rect(0, cy, cx, cy));  // 左下(第三象限)
    Mat q3(magI, Rect(cx, cy, cx, cy)); // 右下(大四象限)
    Mat tmp;                           
    // 入れ替え(左上と右下)
    q0.copyTo(tmp);
    q3.copyTo(q0);
    tmp.copyTo(q3);
    q1.copyTo(tmp);                    
    // 右上と左下
    q2.copyTo(q1);
    tmp.copyTo(q2);
    // 見ることができる値(float[0,1])に変換
    normalize(magI, magI, 0, 1, NORM_MINMAX); 
    
    //表示     
    imshow("Input Image"       , I   );
    imshow("spectrum magnitude", magI);
    waitKey();
    return 0;
}

###コンパイル

g++ dft.cpp -I/usr/local/include/opencv2 -I/usr/local/include/opencv -L/usr/local/lib -lopencv_core -lopencv_imgcodecs -lopencv_highgui

##結果
次のような感じになる

imageTextN.png
入力
水平画像
結果


imageTextR.png
入力
回転画像
結果

5
7
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
7

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?