Pythonで画像処理〜基本とウォーリー探し〜
こんにちは。久々の更新となってしまいました・・・
下書きだけ書いて大量に溜めている状態なので、徐々に更新していきたいと思います。
さて、今期の授業でPythonの画像処理を扱うので、自分のメモ的に書いていきたいと思います。
環境
jupyter notebook 5.7.0
python 3.7
numpy 1.15.2
scipy 1.1.0
matplotlib 3.0.0
openCVは使わない方針(みたい)です。
画像の読み込みと表示
jupyter notebook では、最初に
%matplotlib inline
と書いておかないと画像が表示されないようです。
import matplotlib.pyplot as plt #画像の読み込み img = matplotlib.pyplot.imread('image.png') #画像の表示 plt.figure(1) plt.imshow(img, interpolation='none') #グレースケールで表示 img_gray = img[:, :, 0] plt.figure(2) plt.imshow(img_gray, cmap='gray', interpolation='none') plt.colorbar()
こんな感じで表示されます。
ガウシアンフィルタ
ガウシアンフィルタとは、平滑化(ぼかし)を行うフィルタのようです。
原理としては、ある画像(行列)に対してあるフィルタ(行列)でたたみ込み演算を行うことでその結果(行列)が新たな画像として出てくる
といった感じです。
scipyには関数が実装されているのですが、関数を使わない方法も書きました。
import scipy.ndimage as nd #実装されている関数 img_smoothed = nd.filters.gaussian_filter(img_gray,1) #関数を使わない方法 #重み(カーネル)?の計算(式に代入しただけ・9*9の行列の場合です) sigma = 1.0 kernel_gauss = np.full((9,9),0.0) a = np.arange(-4.0, 5.0) nums = np.arange(0, 9) for i in nums: for j in nums: a[j] = math.exp(-((i-4)**2 + (j-4)**2) / (2 * sigma**2)) / (2 * math.pi * sigma**2) kernel_gauss[i] = a #たたみ込みの計算 beach_smoothed = nd.filters.convolve(img_gray, kernel_gauss)
たたみ込みを計算する関数が
nd.filters.convolve() nd.convolve()
の2つあるのですが、違いがあまりわかりませんでした・・・。
参考リンク
http://www.clg.niigata-u.ac.jp/~lee/jyugyou/img_processing/medical_image_processing_03_press.pdf
docs.scipy.org
scipy.ndimage.filters.convolve — SciPy v0.16.1 Reference Guide
scipy.ndimage.convolve — SciPy v1.3.1 Reference Guide
NumPyのarange, linspaceの使い方(連番や等差数列を生成) | note.nkmk.me
【画像処理】ガウシアンフィルタの原理・特徴・計算式 | 技術雑記
https://algorithm.joho.info/programming/python/scipy-gaussian-filter/
ラプラシアンフィルタ
ラプラシアンフィルタは、画像の輪郭を取り出すフィルタだそうです。
#実装されている関数 img_edges = nd.filters.laplace(img_gray) #関数を使わない方法 #カーネルは決まっているので与えるだけ kernel_laplace = kernel_laplace = np.array([np.ones(9), np.ones(9), np.ones(9), [1, 1, 1, -8, -8, -8, 1, 1, 1], [1, 1, 1, -8, -8, -8, 1, 1, 1], [1, 1, 1, -8, -8, -8, 1, 1, 1], np.ones(9), np.ones(9), np.ones(9)]) #たたみ込みの計算 img_edges = nd.convolve(img_smoothed, kernel_laplace)
画像の中から特定の部分を探す
いわゆるウォーリーを探せ的な事をする時に使います。
相関係数を求めることで画像のどこが探したい画像に一番似ているのかがわかります。
#相関係数を求める correlation = nd.correlate(img_gray, wally_edges) #その中の最大値を抜き出す index = correlation.argmax() #最大値が画像のどの部分なのかを求める sh = np.unravel_index(index, correlation.shape) #求めた場所が画像の中心なので幅と高さを指定して画像を表示 cs = wally.shape img_cropped = beach[sh[0]-int(cs[0]/2):sh[0]+int(cs[0]/2), sh[1]-int(cs[1]/2):sh[1]+int(cs[1]/2)] plt.figure(3) plt.subplot(121) plt.imshow(wally, interpolation='none') plt.subplot(122) plt.imshow(img_cropped, interpolation='none')
たたみ込み演算の実装
最後に、たたみ込み演算を行うコードも実装したのでメモしておきます。
愚直に計算するバージョン
#カーネルを与える kernel = np.diag(np.full(5, 0.2)) #画像とカーネルの行列の大きさ h, w = img.shape kh, kw = kernel.shape #端から計算していく for y in range(h): for x in range(w): # initialize result for pixel at y, x val = 0. # loop over all pixels in kernel for j in range(kh): for i in range(kw): imageY = y - 2 + j imageX = x - 2 + i if(imageY < 0 or imageY >= h): imageY = 0 if(imageX < 0 or imageX >= w): imageX = 0 val += img[imageY, imageX] * kernel[j, i] result_explicit[y, x] = val plt.figure(4) plt.subplot(121) plt.imshow(img, cmap='gray', interpolation='none') plt.subplot(122) plt.imshow(result_explicit, cmap='gray', interpolation='none')
フーリエ変換を用いるバージョン
#カーネルを与える kernel = np.diag(np.full(5, 0.2)) #元画像のフーリエ変換 img_ft = np.fft.fft2(img) #カーネルが元画像と同じサイズの行列になるようにゼロパディングを行う? kernel_pad = np.zeros_like(img, dtype=float) kernel_pad[h//2-kh//2:h//2+kh//2+1, w//2-kw//2:w//2+kw//2+1] = kernel kernel_pad = np.fft.ifftshift(kernel_pad) #カーネルのフーリエ変換 kernel_ft = np.fft.fft2(kernel_pad) #元画像とカーネルを掛けて逆フーリエ変換して画像出力 #実部を取り出すために.realを用いている result_fourier = np.fft.ifft2(img_ft * kernel_ft).real