画像の勾配¶
目的¶
このチュートリアルでは:
- 画像の勾配,エッジなどを検出する方法を学びます.
- 以下の関数について学びます : cv2.Sobel(), cv2.Scharr(), cv2.Laplacian() など
理論¶
OpenCVはSobel, Scharr, Laplacianという3種類の勾配検出フィルタ(もしくはハイパスフィルタ)を提供しています.それぞれ見ていきましょう.
1. Sobel と Scharr 微分¶
Sobel演算子はGaussianによる平滑化と微分演算子を組み合わせた演算子であり,ノイズに対する耐性があります.勾配を計算する方向はそれぞれ引数 yorder
と xorder
で指定します.第3引数( yorder
)を1とするとx方向の勾配,第4引数( xorder
)を1とするとy方向の勾配を計算します.また,第5引数( ksize
)によって勾配を計算するカーネルのサイズを指定できます.もしもksize = -1 とすると,3x3のSobelフィルタより良いと言われている3x3のScharrフィルタを使います.詳細については各カーネルに関するドキュメントを参照してください.
2. Laplacian微分¶
以下の式に基づいて画像のLaplacian(2次微分)を計算します ここで各微分はSobelフィルタを使って計算されます. ksize = 1
と指定すると以下のカーネルを使います.:
コード(実装)¶
以下に上述した全演算子の結果を見せるためのコードを示します.全てのカーネルを5x5のサイズにしています.出力画像のdepthは -1 を指定して, np.uint8 型としています.
import cv2
import numpy as np
from matplotlib import pyplot as plt
img = cv2.imread('dave.jpg',0)
laplacian = cv2.Laplacian(img,cv2.CV_64F)
sobelx = cv2.Sobel(img,cv2.CV_64F,1,0,ksize=5)
sobely = cv2.Sobel(img,cv2.CV_64F,0,1,ksize=5)
plt.subplot(2,2,1),plt.imshow(img,cmap = 'gray')
plt.title('Original'), plt.xticks([]), plt.yticks([])
plt.subplot(2,2,2),plt.imshow(laplacian,cmap = 'gray')
plt.title('Laplacian'), plt.xticks([]), plt.yticks([])
plt.subplot(2,2,3),plt.imshow(sobelx,cmap = 'gray')
plt.title('Sobel X'), plt.xticks([]), plt.yticks([])
plt.subplot(2,2,4),plt.imshow(sobely,cmap = 'gray')
plt.title('Sobel Y'), plt.xticks([]), plt.yticks([])
plt.show()
結果:
重要な点!¶
上記のコードでは出力画像の型を cv2.CV_8U もしくは np.uint8 としていますが,実はちょっとした問題があります.黒から白への変化(画素値の低い値から高い値への変化)は正方向の傾きとして計算されますが,白から黒への変化(画素値の高い値から低い値への変化)は負の傾きとして計算されます.そのため,勾配を np.uint8 へ変換すると,負の値は全て0になってしまいます.一言で言えば,負の勾配を失ってしまいます.
正負両方のエッジを検出したいのであれば,画素値の型を cv2.CV_16S
や cv2.CV_64F
といったより高次のものに変更してください.以下のコードは横方向のSobelフィルタを例に,画素値の型の違いが結果に及ぼす影響を示します.
import cv2
import numpy as np
from matplotlib import pyplot as plt
img = cv2.imread('box.png',0)
# Output dtype = cv2.CV_8U
sobelx8u = cv2.Sobel(img,cv2.CV_8U,1,0,ksize=5)
# Output dtype = cv2.CV_64F. Then take its absolute and convert to cv2.CV_8U
sobelx64f = cv2.Sobel(img,cv2.CV_64F,1,0,ksize=5)
abs_sobel64f = np.absolute(sobelx64f)
sobel_8u = np.uint8(abs_sobel64f)
plt.subplot(1,3,1),plt.imshow(img,cmap = 'gray')
plt.title('Original'), plt.xticks([]), plt.yticks([])
plt.subplot(1,3,2),plt.imshow(sobelx8u,cmap = 'gray')
plt.title('Sobel CV_8U'), plt.xticks([]), plt.yticks([])
plt.subplot(1,3,3),plt.imshow(sobel_8u,cmap = 'gray')
plt.title('Sobel abs(CV_64F)'), plt.xticks([]), plt.yticks([])
plt.show()
以下の結果を比較してください: