【Python/OpenCV】高速フーリエ変換+ローパスフィルタでノイズ除去

Python版OpenCVとNumPyを用いてフーリエ変換とローパスフィルタを実装し、画像から輪郭を取り出す方法をソースコード付きで解説します。

ローパスフィルタ

ローパスフィルタとは、その名の通り、信号の低周波数成分のみを通過させます。
画像データの場合は、画素値の変化が小さい部分が低周波成分となります。(ノイズは高周波成分)
つまり、画像処理においてはローパスフィルタでノイズ除去などに利用できます。原理の詳細は以下ページで解説しています。

【画像処理】フーリエ変換の原理・実装例
この記事では、画像処理におけるフーリエ変換について解説します。

今回は、Python+OpenCVを用いて画像のノイズ除去を行うためのローパスフィルタを作成します。
処理手順は以下のようになります。

①高速フーリエ変換で画像データを空間領域から空間周波数領域に変換します。
②零周波数成分を中心に移動します。(空間周波数領域の中心ほど低周波数成分)
③ハイパスフィルタで高周波成分のみを取り出します。(空間周波数領域の中心のデータは0に置換)
③高速逆フーリエ変換でデータを空間領域に戻します。

サンプルコード

サンプルプログラムのソースコードです。


実行結果

サンプルプログラムの実行結果です。

■入力画像(左)と出力画像(右)

一部分を拡大すると以下のとおりです。ノイズが低減(平滑化)されていることがわかります。

■入力画像(左)と出力画像(右)

サンプルコードの詳細解説

上記サンプルコードの詳細解説を行います。

インポートと初期設定

# -*- coding: utf-8 -*-
import numpy as np
import cv2
  • numpycv2(OpenCV)ライブラリをインポートします。
  • # -*- coding: utf-8 -*-は、ファイルのエンコーディングを指定しています。

ローパスフィルタ関数

def lowpass_filter(src, param = 0.5):
    # 高速フーリエ変換(2次元)
    src = np.fft.fft2(src)

    # 画像サイズ
    height, width = src.shape

    # 画像の中心座標
    cy, cx =  int(height/2), int(width/2)

    # フィルタのサイズ(矩形の高さと幅)
    rh, rw = int(param*cy), int(param*cx)

    # 第1象限と第3象限、第1象限と第4象限を入れ替え
    fsrc =  np.fft.fftshift(src)

    # 入力画像と同じサイズで値0の配列を生成
    fdst = np.zeros(src.shape, dtype=complex)

    # 中心部分の値だけ代入(中心部分以外は0のまま)
    fdst[cy-rh:cy+rh, cx-rw:cx+rw] = fsrc[cy-rh:cy+rh, cx-rw:cx+rw]

    # 第1象限と第3象限、第1象限と第4象限を入れ替え(元に戻す)
    fdst =  np.fft.fftshift(fdst)

    # 高速逆フーリエ変換
    dst = np.fft.ifft2(fdst)

    # 実部の値のみを取り出し、符号なし整数型に変換して返す
    return  np.uint8(dst.real)
  1. 高速フーリエ変換: np.fft.fft2(src)で画像を周波数領域に変換します。
  2. 画像サイズと中心座標の取得: 画像の高さと幅を取得し、その中心座標を計算します。
  3. フィルタサイズの計算: フィルタのサイズを指定されたパラメータに基づいて計算します。
  4. フーリエ変換のシフト: np.fft.fftshift(src)でフーリエ変換結果をシフトし、低周波成分を中心に移動します。
  5. フィルタの適用: 中心部分の値を保持し、他の部分をゼロにします。
  6. 逆シフトと逆フーリエ変換: フィルタ適用後のデータを元に戻し、逆フーリエ変換を行います。
  7. 実部の取り出し: 結果の実部を取り出し、符号なし整数型に変換して返します。

メイン関数

def main():

    # フィルタのサイズ(倍率)、小さいほどフィルタの影響が強くなる
    param = 0.3

    # 入力画像を読み込み
    img = cv2.imread("C:/github/sample/python/opencv/fft/sample.jpg")

    # RGB画像をRed, Green, Blueの1チャンネル画像に分割
    img_blue, img_green, img_red = cv2.split(img)

    # ローパスフィルタ処理
    himg_blue = lowpass_filter(img_blue, param)
    himg_green = lowpass_filter(img_green, param)
    himg_red = lowpass_filter(img_red, param)

    # RGB画像に戻す
    himg = cv2.merge((himg_blue, himg_green, himg_red))

    # 処理結果を出力
    cv2.imwrite("C:/github/sample/python/opencv/fft/lowpass_filter.jpg", himg)

if __name__ == "__main__":
    main()
  1. フィルタのサイズ設定: param = 0.3でフィルタの影響範囲を設定します。
  2. 画像の読み込み: cv2.imreadで画像を読み込みます。
  3. RGBチャンネルの分割: cv2.splitで画像をRGBの各チャンネルに分割します。
  4. ローパスフィルタの適用: 各チャンネルに対してローパスフィルタを適用します。
  5. RGB画像の再構成: cv2.mergeでフィルタ処理後の各チャンネルを再度結合します。
  6. 結果の保存: cv2.imwriteで処理結果を保存します。

おすすめ記事

【Python版OpenCV超入門】使い方とサンプルコードを解説
Python版OpenCVで画像処理プログラミングを行う方法を入門者向けにソースコード付きで解説するページです。
【画像処理入門】アルゴリズム&プログラミング
画像処理における基本的なアルゴリズムとその実装例(プログラム)についてまとめました。

コメント