モーダルを閉じる工作HardwareHub ロゴ画像

工作HardwareHubは、ロボット工作や電子工作に関する情報やモノが行き交うコミュニティサイトです。さらに詳しく

利用規約プライバシーポリシー に同意したうえでログインしてください。

離散フーリエ変換による周波数に応じた画像のフィルタ (OpenCV3 C++)

モーダルを閉じる

ステッカーを選択してください

お支払い手続きへ
モーダルを閉じる

お支払い内容をご確認ください

購入商品
」ステッカーの表示権
メッセージ
料金
(税込)
決済方法
GooglePayマーク
決済プラットフォーム
確認事項

利用規約をご確認のうえお支払いください

※カード情報はGoogleアカウント内に保存されます。本サイトやStripeには保存されません

※記事の執筆者は購入者のユーザー名を知ることができます

※購入後のキャンセルはできません

作成日作成日
2020/01/14
最終更新最終更新
2024/06/29
記事区分記事区分
一般公開

周期関数は三角関数の無限級数和で展開できることが知られており、フーリエ級数展開とよばれます。更に、連続した非周期関数にも適用できるようにフーリエ級数展開の考え方を拡張することができ、フーリエ変換とよばれます。これを離散化した離散フーリエ変換 (DFT: Discrete Fourier Transform) を用いると、デジタル信号の周波数解析を行うことができます。画像データをデジタル信号であると考えると、離散フーリエ変換を適用できます。

画像 f(i,j)f(i,j) を離散フーリエ変換すると以下のようになります。

F(k,l)=m=0N1n=0N1f(m,n)e2πiN(km+ln)F(k,l) = \sum_{m=0}^{N-1} \sum_{n=0}^{N-1} f(m,n)\,\mathrm{e}^{-\frac{2\pi i}{N} (k m + l n)}

離散フーリエ変換には逆変換が存在しており、もとの画像データは、変換結果 F(k,l)F(k,l) から構成できます。離散フーリエ変換には、計算を高速に行うためのアルゴリズムが存在しており、高速フーリエ変換 (FFT: Fast Fourier Transform) とよばれます。関連ページ

ローパスフィルタによる画像圧縮

離散フーリエ変換の応用の一つに画像圧縮が存在します。人間の目は高周波の情報を認識しづらい傾向にあることが知られており、高周波に相当するデータを除いて画像データを圧縮することができます。この考え方は JPEG 圧縮で利用されています。F(k,l)F(k,l) で逆フーリエ変換を行う際に、高周波に相当する k,lk,l の値を 0 にすることで、低周波の情報のみを用いてもと画像を構成することができます。

フィルタなし

ローパスフィルタ (k > 100 かつ l > 100 の周波数を除去)

ハイパスフィルタ (k < 100 かつ l < 100 の周波数を除去)

#include <opencv2/opencv.hpp>

#include <iostream>

int main() {

    // グレースケールで画像を読み込みます。
    cv::Mat I = cv::imread("aaa.png",  cv::IMREAD_GRAYSCALE);

    // FFT アルゴリズムの計算に適したサイズに変換します。増加したサイズ部分は 0 で埋めます。
    cv::Mat padded;
    int m = cv::getOptimalDFTSize( I.rows );
    int n = cv::getOptimalDFTSize( I.cols );
    cv::copyMakeBorder(I, padded, 0, m - I.rows, 0, n - I.cols, cv::BORDER_CONSTANT, cv::Scalar::all(0));

    // DFT 変換結果は非常に大きな値になり得るため、浮動小数点型に変換しておきます。
    // DFT 変換結果は複素数です。虚部を格納するために、画像データを 2 チャンネルの変数に格納しておきます。
    cv::Mat planes[] = {cv::Mat_<float>(padded), cv::Mat::zeros(padded.size(), CV_32F)};
    cv::Mat complexI;
    cv::merge(planes, 2, complexI);

    // 離散フーリエ変換を実行します。
    cv::dft(complexI, complexI);

    std::cout << complexI.size() << std::endl; //=> [200 x 200]

    // フィルタをかけてみます。
    for(int i = 0; i < 200; ++i) {
        for(int j = 0; j < 200; ++j) {
            // if(i > 100 && j > 100) {
            if(i < 100 && j < 100) {
                complexI.at<cv::Vec2f>(i, j)[0] = 0;
                complexI.at<cv::Vec2f>(i, j)[1] = 0;
            }
        }
    }

    // 逆離散フーリエ変換
    cv::Mat I2;
    cv::dft(complexI, I2, cv::DFT_INVERSE | cv::DFT_REAL_OUTPUT);

    std::cout << I2.size() << std::endl; //=> [200 x 200]
    std::cout << I2.channels() << std::endl; //=> 1

    // 描画するために [0, 1] の範囲に変換します。
    cv::normalize(I2, I2, 0, 1, CV_MINMAX);

    cv::imshow("Original", I);
    cv::imshow("DFT/IDFT", I2);
    cv::waitKey(0);
    return 0;
}
Likeボタン(off)0
詳細設定を開く/閉じる

記事の執筆者にステッカーを贈る

有益な情報に対するお礼として、またはコメント欄における質問への返答に対するお礼として、 記事の読者は、執筆者に有料のステッカーを贈ることができます。

>>さらに詳しくステッカーを贈る
ステッカーを贈る コンセプト画像

Feedbacks

Feedbacks コンセプト画像

    ログインするとコメントを投稿できます。

    ログインする

    関連記事