記事のディレクトリ
OpenCV-Python:OpenCVでのIV画像処理
23画像変換
23.1フーリエ変換
目標
このセクションでは、以下について学習します
。•OpenCVを
使用して画像をフーリエ変換する•NumpyでFFT(高速フーリエ変換)関数を
使用する
•フーリエ変換のいくつかの使用法•学習する関数は次のとおりです。cv2.dft()、cv2 .idft()およびその他の
原理
フーリエ変換は、さまざまなフィルターの周波数特性を分析するためによく使用されます。2D離散フーリエ変換(DFT)を使用して、画像の周波数領域特性を分析できます。DFTを実装する高速アルゴリズムは、高速フーリエ変換(FFT)と呼ばれます。フーリエ変換に関する詳細な知識は、画像処理または信号処理に関する本に記載されています。このセクションのその他のリソースセクションを確認してください。
正弦波信号の場合:x(t)= Asin(2πft)、その周波数はfです。この信号を周波数領域表現に変換すると、周波数fにピークが見られます。信号がサンプリングによって生成された離散信号で構成されている場合、前のスペクトログラムが連続であり、現在は離散であることを除いて、同様のスペクトログラムが得られます。画像は、2方向に収集された信号と考えることができます。したがって、画像に対してX方向とY方向のフーリエ変換を同時に実行することにより、この画像の周波数領域表現(スペクトログラム)を取得します。
より直感的に言うと、正弦波信号の場合、振幅の変化が非常に速い場合は高周波信号であり、変化が非常に遅い場合は低周波信号と呼ばれます。このアイデアを画像に適用できます。画像の振幅が大きく変化しますか?境界点またはノイズ。したがって、境界とノイズは画像内の高周波成分であると言います(ここでの高周波は、発生回数ではなく、非常に速い変化を指していることに注意してください)。このような大きな振幅変化がない場合、それを低周波成分と呼びます。
ここで、フーリエ変換を実行する方法を確認します。
23.1.1Numpyでのフーリエ変換
まず、Numpyを使用してフーリエ変換を実行する方法を見てみましょう。NumpyのFFTパッケージは、高速フーリエ変換の実装に役立ちます。関数np.fft.fft2()は信号に対して周波数変換を実行でき、出力結果は複素数配列になります。この関数の最初のパラメーターは入力画像であり、グレースケール形式である必要があります。2番目のパラメーターはオプションであり、出力配列のサイズを決定します。出力配列のサイズは、入力画像のサイズと同じです。出力結果が入力画像よりも大きい場合、FFTの前に入力画像を0で埋める必要があります。出力結果が入力画像よりも小さい場合、入力画像はカットされます。
これで結果が得られました。周波数が0の部分(DC成分)が出力画像の左上隅にあります。それ(DC成分)を出力画像の中央に配置する場合は、結果を-N / 2で両方向に変換する必要もあります。関数np.fft.fftshift()は、このステップを実行するのに役立ちます。(これにより、分析が容易になります)。
周波数変換を実行した後、振幅スペクトルを作成できます。
import cv2
import numpy as np
from matplotlib import pyplot as plt
img = cv2.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()
結果は次のとおりです。
出力の中央部分がより白く(明るく)なっていることがわかります。これは、低周波成分が多いことを意味します。
周波数領域変換を実行できるようになったので、ハイパスフィルタリングや画像の再構成(DFTの逆変換)など、周波数領域の画像に対していくつかの操作を実行できます。たとえば、60x60の長方形のウィンドウを使用して画像をマスクし、低周波成分を除去できます。次に、関数np.fft.ifftshift()を使用して逆シフト操作を実行します。これでDCコンポーネントが左上隅に戻り、関数np.ifft2()を使用して左側で逆FFT変換を実行します。また、複雑な数値をたくさん取得します。それらの絶対値を取得できます。
rows, cols = img.shape
crow,ccol = rows/2 , cols/2
fshift[crow-30:crow+30, ccol-30:ccol+30] = 0
f_ishift = np.fft.ifftshift(fshift)
img_back = np.fft.ifft2(f_ishift)
img_back = np.abs(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カラー画像を注意深く観察すると、不自然なものがいくつか見られます(赤い矢印でマークした領域など)。リンギング効果と呼ばれるいくつかの縞模様の構造がある上の写真を見てください。これは、長方形のウィンドウをマスクとして使用しているためです。この問題は、マスクが正弦波形状に変換されるときに発生します。したがって、通常、長方形のウィンドウフィルタリングは適用しません。最良の選択はガウスウィンドウです。
23.1.2OpenCVでのフーリエ変換
OpenCVの対応する関数は、cv2.dft()とcv2.idft()です。結果は前の出力と同じですが、デュアルチャネルです。最初のチャネルは結果の実数部であり、2番目のチャネルは結果の虚数部です。入力画像は、最初にnp.float32形式に変換する必要があります。それを行う方法を見てみましょう。
import numpy as np
import cv2
from matplotlib import pyplot as plt
img = cv2.imread('messi5.jpg',0)
dft = cv2.dft(np.float32(img),flags = cv2.DFT_COMPLEX_OUTPUT)
dft_shift = np.fft.fftshift(dft)
magnitude_spectrum = 20*np.log(cv2.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()
注:関数cv2.cartToPolar()を使用できます。この関数は、振幅と位相を同時に返します。
それでは、逆DFTを実行しましょう。前のパートではHPF(ハイパスフィルター)を実装しましたが、次にLPF(ローパスフィルター)を実行して高周波部分を削除します。実際、それは画像をぼかすことです。まず、マスクを作成し、低周波領域に対応する場所を1に設定し、高周波領域に対応する場所を0に設定する必要があります。
rows, cols = img.shape
crow,ccol = rows/2 , cols/2
# create a mask first, center square is 1, remaining all zeros
mask = np.zeros((rows,cols,2),np.uint8)
mask[crow-30:crow+30, ccol-30:ccol+30] = 1
# apply mask and inverse DFT
fshift = dft_shift*mask
f_ishift = np.fft.ifftshift(fshift)
img_back = cv2.idft(f_ishift)
img_back = cv2.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の関数cv2.dft()およびcv2.idft()は、Numpyよりも高速です。ただし、Numpy関数の方が使いやすいです。パフォーマンスの説明については、次の章を参照してください。
23.1.3DFTパフォーマンスの最適化
配列のサイズが特定の値の場合、DFTのパフォーマンスは向上します。DFTは、配列のサイズが2の指数である場合に最も効率的です。アレイのサイズが2、3、または5の倍数の場合も、効率が高くなります。したがって、コードの効率を向上させたい場合は、入力画像のサイズを変更できます(0が埋め込まれます)。OpenCVの場合、手動で0を入力する必要があります。ただし、Numpyでは、FFT演算のサイズを指定するだけで、自動的に0が入力されます。
最適なサイズをどのように決定しますか?OpenCVは関数cv2.getOptimalDFTSize()を提供します。
cv2.dft()とnp.fft.fft2()の両方で同時に使用できます。IPythonのマジックコマンド%timeitを使用してテストしてみましょう。
In [16]: img = cv2.imread('messi5.jpg',0)
In [17]: rows,cols = img.shape
In [18]: print rows,cols
342 548
In [19]: nrows = cv2.getOptimalDFTSize(rows)
In [20]: ncols = cv2.getOptimalDFTSize(cols)
In [21]: print nrows, ncols
360 576
ご覧のとおり、配列のサイズが(342,548)から(360,576)に変更されました。ここで0を入力し、パフォーマンスが向上したかどうかを確認します。大きな0配列を作成してデータをコピーするか、関数cv2.copyMakeBoder()を使用できます。
nimg = np.zeros((nrows,ncols))
nimg[:rows,:cols] = img
または:
right = ncols - cols
bottom = nrows - rows
#just to avoid line breakup in PDF file
bordertype = cv2.BORDER_CONSTANT
nimg = cv2.copyMakeBorder(img,0,bottom,0,right,bordertype, value = 0)
次に、Numpyのパフォーマンスを見てみましょう。
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= cv2.dft(np.float32(img),flags=cv2.DFT_COMPLEX_OUTPUT)
100 loops, best of 3: 13.5 ms per loop
In [27]: %timeit dft2= cv2.dft(np.float32(nimg),flags=cv2.DFT_COMPLEX_OUTPUT)
100 loops, best of 3: 3.11 ms per loop
また、4倍に増加し、OpenCVはNumpyの3倍高速であることがわかります。
逆FFTのパフォーマンスをテストすることもできます。
23.1.4ラプラシアン演算子がハイパスフィルターであるのはなぜですか?
フォーラムでも同様の問題が発生しました。ラプラシアン演算子がハイパスフィルターであるのはなぜですか?
SobelがHPFなのはなぜですか?などなど。最初の質問に対する答えは、フーリエ変換の形で与えられます。異なる演算子で一緒にフーリエ変換を実行し、それらを分析してみましょう。
import cv2
import numpy as np
from matplotlib import pyplot as plt
# simple averaging filter without scaling parameter
mean_filter = np.ones((3,3))
# creating a guassian filter
x = cv2.getGaussianKernel(5,10)
#x.T 为矩阵转置
gaussian = x*x.T
# different edge detecting filters
# scharr in x-direction
scharr = np.array([[-3, 0, 3],
[-10,0,10],
[-3, 0, 3]])
# sobel in x direction
sobel_x= np.array([[-1, 0, 1],
[-2, 0, 2],
[-1, 0, 1]])
# sobel in y direction
sobel_y= np.array([[-1,-2,-1],
[0, 0, 0],
[1, 2, 1]])
# laplacian
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であることがわかります。
その他のリソース
- スティーブン・レハールによるフーリエ理論の直感的な説明
- フーリエ変換HIPR
- 画像の場合、周波数領域は何を意味しますか?
詳細については、公式アカウントに注意してください。