OpenCVでソーベルフィルタ処理を行うには、Sobel()関数を用います。
ソーベルフィルタは、画像のエッジを検出することで、位置決めや寸法計測などに用いられます。
ソーベルフィルタ処理の構文
Sobel( src, ddepth, dx, dy[, dst[, ksize[, scale[, delta[, borderType]]]]] ) -> dst
引数
src | 入力画像 |
ddepth | 出力画像のビット深度 |
dx | x方向に関する微分の次数 |
dy | y方向に関する微分の次数 |
ksize | カーネルのサイズ 1, 3, 5, 7 のいずれか |
scale | 出力する値の倍率 |
delta | 出力する値に加算する値 |
borderType | 外周処理の種類を指定 |
戻り値
dst | ソーベルフィルタ処理された画像データ |
サンプルプログラム
よくあるソーベルフィルタ処理のサンプルプログラムを以下に示します。
ソーベルフィルタはノイズの影響を受けやすい処理のため、最初にノイズ除去のため、メディアンフィルタ処理を施しています。
import cv2
src = cv2.imread("Text.bmp", cv2.IMREAD_UNCHANGED)
# ノイズ除去(メディアンフィルタ)
src_median = cv2.medianBlur(src, 5)
# ソーベルフィルタ
sobel_x = cv2.Sobel(src_median, cv2.CV_32F, 1, 0) # X方向
sobel_y = cv2.Sobel(src_median, cv2.CV_32F, 0, 1) # Y方向
# 立下りエッジ(白から黒へ変化する部分)がマイナスになるため絶対値を取る
# alphaの値は画像表示に合わせて倍率調整
sobel_x = cv2.convertScaleAbs(sobel_x, alpha = 0.5)
sobel_y = cv2.convertScaleAbs(sobel_y, alpha = 0.5)
# X方向とY方向を足し合わせる
sobel_xy = cv2.add(sobel_x, sobel_y)
# 二値化処理後の画像表示
cv2.imshow("Src Image", src)
cv2.imshow("SobelX", sobel_x)
cv2.imshow("SobelY", sobel_y)
cv2.imshow("SobelXY", sobel_xy)
cv2.waitKey()
実行結果
実際のSobelフィルタ処理の使い方
Sobleフィルタは最初にも書きましたが、被写体のエッジ部分を検出することで、位置決めや寸法計測に用います。
そのため、上記に示したようなエッジ部分を白く強調した画像は、ほとんど用いる事はありません。
さらに、上記のサンプルでは、Sobelフィルタ処理の絶対値を取得していますが、実際には、正、負の値が重要になります。
例えば、下図のようなバーコードの画像において、線の幅や位置を知りたいときは、黒い線の右側と左側を区別する必要があります。
画像を左側から右側に見た時、白から黒に変化する部分(立下りエッジ)では、X方向のソーベルフィルタ処理の値は負になります。
同様に黒から白に変化する部分(立上りエッジ)では、X方向のソーベルフィルタ処理の値は正になります。
上図の赤矢印の輝度値の一部分をグラフにすると、下図のようになります。
この輝度部分に関して、ソーベルフィルタ処理を行うと、
のようになり、ソーベルフィルタの値の正負を区別することで、立上りエッジと立下りエッジを認識することができるようになります。
この事を利用したサンプルを以下に示します。
import cv2
cv2.namedWindow("Image", cv2.WINDOW_NORMAL)
src = cv2.imread("image.jpg", cv2.IMREAD_GRAYSCALE) # 処理画像
src_disp = cv2.cvtColor(src, cv2.COLOR_GRAY2BGR) # 結果表示用カラー画像
# ソーベルフィルタ
sobel_x = cv2.Sobel(src, cv2.CV_32F, 1, 0) # X方向
# 中心付近の横1ライン分のデータ
sobel_value = sobel_x[112][:]
thre_high = 300 # 立上りエッジのしきい値
thre_low = -300 # 立下りエッジのしきい値
min = 0
max = 0
# 縦方向の中心付近のY座標
y = 112
for i in range(src.shape[1]):
if sobel_x[y, i] > thre_high:
if max < sobel_x[y, i]:
max = sobel_x[y, i]
max_x = i
else:
if max != 0:
# 立上りエッジの描画
cv2.line(src_disp, (max_x, 0), (max_x, src.shape[0] - 1), (255, 0, 0), 1)
max = 0
if sobel_x[y, i] < thre_low: if min > sobel_x[y, i]:
min = sobel_x[y, i]
min_x = i
else:
if min != 0:
# 立下りエッジの描画
cv2.line(src_disp, (min_x, 0), (min_x, src.shape[0] - 1), (0, 0, 255), 1)
min = 0
cv2.imshow("Image", src_disp)
cv2.waitKey()
実行結果
今回用いている画像は、ノイズが少ないため、ノイズ除去を行っていませんが、ノイズが多い画像の場合、エッジをぼかさずにノイズを除去するため、エッジ方向に沿ったノイズ除去フィルタを行います。
例えば、上図の画像では、幅1画素、高さ7画素のカーネルでガウシアンフィルタ処理などを行います。
参照ページ
https://docs.opencv.org/4.8.0/d4/d86/group__imgproc__filter.html#gacea54f142e81b6758cb6f375ce782c8d