[opencv]フーリエ変換(さまざまなフィルターの周波数特性を分析します。ハイパスフィルターはエッジ検出操作であり、高周波のぼやけた画像を削除します)

4_11_Fourier変換-OpenCV中国語の公式ドキュメント

OpenCVを使用して画像のフーリエ変換を見つける-Numpyで利用可能なFFT関数を利用する-フーリエ変換のいくつかのアプリケーション-次の関数が表示されます:cv.dft()、cv.idft()など。

 プレフィード

  • フーリエ変換は、さまざまなフィルターの周波数特性を分析するために使用されます。
  • 画像の場合、** 2D離散フーリエ変換**(DFT)を使用して周波数領域を見つけます
  • DFTの計算には、高速フーリエ変換(FFT)と呼ばれる高速アルゴリズムが使用されます。
  • より直感的には、正弦波信号の場合、振幅が短時間で非常に速く変化する場合、それは高周波信号であると言えます。変化が遅い場合は、低周波信号です。
  • 同じアイデアを画像に拡張することもできます。画像の振幅はどこで大幅に変化しますか?エッジポイントまたはノイズで。したがって、エッジやノイズは画像の高周波成分であると言えます。振幅があまり変化しない場合、それは低周波数成分です。
  • ハイパスフィルタリングはエッジ検出操作です

  • DFTの逆変換を実行します前のセクションでHPFを作成しましたが、今回は画像から高周波コンテンツを削除する方法、つまりLPFを画像に適用する方法を説明します。実際に画像がぼやけますこれを行うには、最初に低周波数部分に高い値(1)を作成します。つまり、低周波数コンテンツをフィルタリングし、高周波数領域で0をフィルタリングします。

  • 個人的な理解:opencvを学習すると、NumpyとOpenCVで同じ機能を実装できることがよくわかります。通常、2つの方法にはパフォーマンスの違いがあります。プロジェクトの規模を考慮すると、この問題に注意を払う必要があります。 。
  • ラプラシアンはハイパスフィルターです

コンテンツ

 仮説

Numpyのフーリエ変換

OpenCVでのフーリエ変換

DFTのパフォーマンスの最適化

ラプラシアンがハイパスフィルターであるのはなぜですか?


 仮説

  • フーリエ変換は、さまざまなフィルターの周波数特性を分析するために使用されます。
  • 画像の場合、** 2D離散フーリエ変換**(DFT)を使用して周波数領域を見つけます
  • DFTの計算には、高速フーリエ変換(FFT)と呼ばれる高速アルゴリズムが使用されます。

正弦波信号の場合、fは信号の周波数であると言えます。その周波数領域をとると、fのスパイクがわかります。信号をサンプリングして離散信号を形成すると、同じ周波数領域が得られますが、範囲は[-π、π]または[0,2π](NポイントDFTの場合は[0、N] [0、N])です。定期的です。画像は、両方向にサンプリングされた信号と考えることができます。したがって、フーリエ変換はX方向とY方向の両方で実行され、画像の周波数表現を取得できます。 

  • より直感的には、正弦波信号の場合、振幅が短時間で非常に速く変化する場合、それは高周波信号であると言えます。変化が遅い場合は、低周波信号です。
  • 同じアイデアを画像に拡張することもできます。画像の振幅はどこで大幅に変化しますか?エッジポイントまたはノイズで。したがって、エッジやノイズは画像の高周波成分であると言えます。振幅があまり変化しない場合、それは低周波数成分です。

次に、フーリエ変換を見つける方法を見ていきます。

Numpyのフーリエ変換

まず、Numpyを使用してフーリエ変換を見つける方法を説明します。Numpyにはこれを行うためのFFTパッケージがあります。np.fft.fft2()は周波数変換を提供し、複雑な配列になります。

  1. 最初のパラメータは、グレースケール画像である入力画像です。
  2. 2番目のパラメーターはオプションであり、出力配列のサイズを決定します。入力画像のサイズよりも大きい場合は、FFTを計算する前に、入力画像にゼロを埋め込みます。入力画像よりも小さい場合、入力画像はトリミングされます。引数が渡されない場合、出力配列は入力と同じサイズになります。

これで、結果が得られると、ゼロ周波数成分(DC成分)が左上隅に表示されます。中央に配置する場合は、結果N2N2を両方向にシフトする必要があります。関数**np.fft.fftshift**()を渡すだけで実行できます。(分析が簡単です)。周波数変換が見つかると、振幅スペクトルを見つけることができます。

import cv2 as cv
import numpy as np
from matplotlib import pyplot as plt
img = cv.imread('messi5.jpg',0)
f = np.fft.fft2(img)
fshift = np.fft.fftshift(f)
magnitude_spectrum = 20*np.log(np.abs(fshift))
plt.subplot(121),plt.imshow(img, cmap = 'gray')
plt.title('Input Image'), plt.xticks([]), plt.yticks([])
plt.subplot(122),plt.imshow(magnitude_spectrum, cmap = 'gray')
plt.title('Magnitude Spectrum'), plt.xticks([]), plt.yticks([])
plt.show()
Result look like below:

結果は次のようになります。

ほら、中央に白い領域が多く見えます。これは、低周波数のコンテンツが多いことを示しています。

これで、周波数変換が見つかりました。これで、ハイパスフィルタリングや画像の再構築など、周波数領域でいくつかの操作を実行できます。つまり、逆DFTを見つけます。これを行うには、サイズ60x60の長方形のウィンドウで低周波数をマスクするだけです。次に、** np.fft.ifftshift **()を使用して逆シフトを適用し、DC成分が再び左上隅に表示されるようにします。次に、** np.ifft2 **()関数を使用して逆FFTを見つけます。繰り返しますが、結果は複素数になります。あなたはその絶対値を取ることができます。

rows, cols = img.shape
crow,ccol = rows//2 , cols//2
fshift[crow-30:crow+31, ccol-30:ccol+31] = 0
f_ishift = np.fft.ifftshift(fshift)
img_back = np.fft.ifft2(f_ishift)
img_back = np.real(img_back)
plt.subplot(131),plt.imshow(img, cmap = 'gray')
plt.title('Input Image'), plt.xticks([]), plt.yticks([])
plt.subplot(132),plt.imshow(img_back, cmap = 'gray')
plt.title('Image after HPF'), plt.xticks([]), plt.yticks([])
plt.subplot(133),plt.imshow(img_back)
plt.title('Result in JET'), plt.xticks([]), plt.yticks([])
plt.show()

結果は次のようになります。

ハイパスフィルタリングはエッジ検出操作であることがわかりますこれは、「画像グラデーション」の章で見たものです。これは、ほとんどの画像データがスペクトルの低周波数領域に存在することも示しています。とにかく、NumpyでDFT、IDFTなどを見つける方法を見てきました。それでは、OpenCVでそれを行う方法を見てみましょう。

  • 結果、特に最後のJETカラー画像をよく見ると、いくつかのアーティファクト(赤い矢印でマークされたインスタンス)が表示されます。そこには、 **リンギング**と呼ばれる波形の構造が示されています
  • これは、マスクに使用する長方形のウィンドウが原因です。このマスクは正弦波に変換されるため、この問題が発生します。
  • したがって、長方形のウィンドウはフィルタリングに使用されません。より良いオプションはガウスウィンドウです。

OpenCVでのフーリエ変換

OpenCVは、このための** cv.dft **()および** cv.idft **()関数を提供します。前の結果と同じ結果を返しますが、2つのチャネルがあります。最初のチャネルは結果の実数部であり、2番目のチャネルは結果の虚数部です。入力画像は最初にに変換する必要がありnp.float32ます。方法を見てみましょう。

import numpy as np
import cv2 as cv
from matplotlib import pyplot as plt
img = cv.imread('messi5.jpg',0)
dft = cv.dft(np.float32(img),flags = cv.DFT_COMPLEX_OUTPUT)
dft_shift = np.fft.fftshift(dft)
magnitude_spectrum = 20*np.log(cv.magnitude(dft_shift[:,:,0],dft_shift[:,:,1]))
plt.subplot(121),plt.imshow(img, cmap = 'gray')
plt.title('Input Image'), plt.xticks([]), plt.yticks([])
plt.subplot(122),plt.imshow(magnitude_spectrum, cmap = 'gray')
plt.title('Magnitude Spectrum'), plt.xticks([]), plt.yticks([])
plt.show()

** cv.cartToPolar  **()を使用して、1回のショットで振幅と位相の両方を返すこともできます。

次に、DFTの逆変換を行います。前のセクションでHPFを作成しましたが、今回は画像から高周波コンテンツを削除する方法、つまりLPFを画像に適用する方法を説明します。

実際に画像がぼやけますこれを行うには、最初に低周波数部分に高い値(1)を作成します。つまり、低周波数コンテンツをフィルタリングし、高周波数領域で0をフィルタリングします。

rows, cols = img.shape
crow,ccol = rows/2 , cols/2
# 首先创建一个掩码,中心正方形为1,其余全为零
mask = np.zeros((rows,cols,2),np.uint8)
mask[crow-30:crow+30, ccol-30:ccol+30] = 1
# 应用掩码和逆DFT
fshift = dft_shift*mask
f_ishift = np.fft.ifftshift(fshift)
img_back = cv.idft(f_ishift)
img_back = cv.magnitude(img_back[:,:,0],img_back[:,:,1])
plt.subplot(121),plt.imshow(img, cmap = 'gray')
plt.title('Input Image'), plt.xticks([]), plt.yticks([])
plt.subplot(122),plt.imshow(img_back, cmap = 'gray')
plt.title('Magnitude Spectrum'), plt.xticks([]), plt.yticks([])
plt.show()

結果を確認してください。 

 一般に、OpenCV関数** cv.dft **()および** cv.idft **()はNumpy関数よりも高速であることに注意してください。ただし、Numpy関数の方が使いやすいです。パフォーマンスの問題の詳細については、以下のセクションを参照してください。

DFTのパフォーマンスの最適化

一部の配列サイズでは、DFTの計算パフォーマンスが向上します。配列サイズが2の累乗の場合に最速。サイズ2、3、および5の製品の配列も非常に効率的に処理できます。したがって、コードのパフォーマンスが心配な場合は、DFTを見つける前に、配列のサイズを任意の最適なサイズに変更できます(ゼロで埋めることにより)。OpenCVの場合、手動でゼロを埋める必要があります。ただし、Numpyでは、FFT計算の新しいサイズを指定すると、自動的にゼロが埋め込まれます。

では、最適なサイズを見つける方法は?OpenCVは、このための関数cv.getOptimalDFTSize()を提供します。** cv.dft **()と** np.fft.fft2 **()の両方で機能します。IPythonマジックコマンドtimeitを使用してパフォーマンスを確認してみましょう。

In [16]: img = cv.imread('messi5.jpg',0)
In [17]: rows,cols = img.shape
In [18]: print("{} {}".format(rows,cols))
342 548
In [19]: nrows = cv.getOptimalDFTSize(rows)
In [20]: ncols = cv.getOptimalDFTSize(cols)
In [21]: print("{} {}".format(nrows,ncols))
360 576

を参照してください。(342,548)サイズをに変更してください(360,576)それでは、(OpenCVの場合)ゼロを埋めて、DFT計算のパフォーマンスを調べましょう。これを行うには、新しいゼロ配列を作成してデータをその配列にコピーするか、** cv.copyMakeBorder **()を使用します。

nimg = np.zeros((nrows,ncols))
nimg[:rows,:cols] = img

また:

right = ncols - cols
bottom = nrows - rows
bordertype = cv.BORDER_CONSTANT #只是为了避免PDF文件中的行中断
nimg = cv.copyMakeBorder(img,0,bottom,0,right,bordertype, value = 0)

ここで、Numpy関数のDFTパフォーマンス比較を計算します。

In [22]: %timeit fft1 = np.fft.fft2(img)
10 loops, best of 3: 40.9 ms per loop
In [23]: %timeit fft2 = np.fft.fft2(img,[nrows,ncols])
100 loops, best of 3: 10.4 ms per loop

それは4倍のスピードアップを示しています。次に、OpenCV関数を使用してみます。

In [24]: %timeit dft1= cv.dft(np.float32(img),flags=cv.DFT_COMPLEX_OUTPUT)
100 loops, best of 3: 13.5 ms per loop
In [27]: %timeit dft2= cv.dft(np.float32(nimg),flags=cv.DFT_COMPLEX_OUTPUT)
100 loops, best of 3: 3.11 ms per loop

また、4倍のスピードアップを示しています。また、OpenCV関数はNumpy関数よりも約3倍高速であることがわかります。逆FFTもテストできますが、これは演習として残しておきます。

ラプラシアンがハイパスフィルターであるのはなぜですか?

同様の質問がフォーラムで行われました。問題は、なぜラプラス変換がハイパスフィルターなのか、なぜSobelがHPFなのかなどです。最初の答えはフーリエ変換についてです。より大きなFFTの場合、ラプラス変換のみが必要です。次のコードを分析します。

import cv2 as cv
import numpy as np
from matplotlib import pyplot as plt
# 没有缩放参数的简单均值滤波器
mean_filter = np.ones((3,3))
# 创建高斯滤波器
x = cv.getGaussianKernel(5,10)
gaussian = x*x.T
# 不同的边缘检测滤波器
# x方向上的scharr
scharr = np.array([[-3, 0, 3],
                   [-10,0,10],
                   [-3, 0, 3]])
# x方向上的sobel
sobel_x= np.array([[-1, 0, 1],
                   [-2, 0, 2],
                   [-1, 0, 1]])
# y方向上的sobel
sobel_y= np.array([[-1,-2,-1],
                   [0, 0, 0],
                   [1, 2, 1]])
# 拉普拉斯变换
laplacian=np.array([[0, 1, 0],
                    [1,-4, 1],
                    [0, 1, 0]])
filters = [mean_filter, gaussian, laplacian, sobel_x, sobel_y, scharr]
filter_name = ['mean_filter', 'gaussian','laplacian', 'sobel_x', \
                'sobel_y', 'scharr_x']
fft_filters = [np.fft.fft2(x) for x in filters]
fft_shift = [np.fft.fftshift(y) for y in fft_filters]
mag_spectrum = [np.log(np.abs(z)+1) for z in fft_shift]
for i in xrange(6):
    plt.subplot(2,3,i+1),plt.imshow(mag_spectrum[i],cmap = 'gray')
    plt.title(filter_name[i]), plt.xticks([]), plt.yticks([])
plt.show()

結果を確認してください。

画像から、各コアがブロックする周波数領域と通過が許可されている領域を確認できます。この情報から、各コアがHPFまたはLPFである理由がわかります。

おすすめ

転載: blog.csdn.net/dujuancao11/article/details/122450807