Ayataの

社会人カジュアルゲーマーのゲームブログです

【Pythonでエッジ処理】空間フィルタリングを適用したエッジ処理の自力実装

前回は空間フィルタリングの概要を勉強しました。

ayatalog.hatenablog.jp

今回は以前作成した簡単なエッジ処理のプログラムを空間フィルタリングを適用した形にし、縦と横のエッジ処理を実装していこうと思います。
OpenCVには便利な関数がありますが、今回も自力実装していきます。


1. フィルタの準備

まずはフィルタを準備します。以下のフィルタをnumpy配列で作成していきます。
f:id:Ayata:20210202231229p:plain:w400

#1次微分のフィルタ
#横方向
fil_late = np.array([[0, 0, 0],
                     [-1, 1, 0],
                     [0, 0, 0]])

#縦方向
fil_vert = np.array([[0, -1, 0],
                     [0, 1, 0],
                     [0, 0, 0]])

2. 空間フィルタリング演算部の作成

次に空間フィルタリングを適用するために、演算部を作っていきます。
空間フィルタリングの学習で出てきたように、入力画像I(x,y)中の注目画素(x,y)および近傍領域とフィルタW(i,j)の積和の演算は以下の式で表されます。
f:id:Ayata:20210208205450p:plain:w450 これはある注目画素(x,y)に対する演算であるため、(x,y)を走査しながらこの演算を行う必要があります。
このコードでは、i, jに関するfor文で上記の積和の演算を、x, yに関する2つのfor文で注目画素の走査を表しています。
また、問題となってくるのが注目画素の周辺領域のうち、入力画像からはみ出てしまう部分の処理です([-1, -1]や[500, 500]など(今回の入力画像サイズは500×500))。考えられる対処法としては2パターンあります。1つ目がはみ出す部分を0や255で補完する方法、2つ目がはみ出す部分をスルーして演算する方法です。今回は2つ目のはみ出す部分をスルーする方法を採用しました。
中身としては、x, yの二つのfor文for x in range(N, width - N, 1): for y in range(N, height - N, 1):の開始と終わりをNずらすことで実装しました。これにより今回の場合、開始が1から、終了が498となります。

#空間フィルタリングの演算部
N = 1
img_fil_x = np.zeros((height - N, width - N))
img_fil_y = np.zeros((height - N, width - N))

#横方向の演算
for x in range(N, width - N, 1):
    for y in range(N, height - N, 1):
        a = 0
        for i in range(-N, N + 1, 1):
            for j in range(-N, N + 1, 1):
                a = a + img_gray[y+j, x+i]*fil_x[j+N, i+N]
        img_fil_x[y-1, x-1] = abs(a)

得られたエッジ情報img_fil_x, img_fil_yを表示すると次のようになりました。 f:id:Ayata:20210217212349p:plain:w300
f:id:Ayata:20210217212353p:plain:w300

3. エッジ強度の計算

空間フィルタリングを用いて、縦と横の片方ずつのエッジが求められたため、エッジ強度を導入して統合します。注目画素(x, y)におけるエッジ強度は以下のように求められました。
f:id:Ayata:20210217205719p:plain:w350 縦方向のエッジI'y(x,y)はimg_fil_y[y, x]、横方向のエッジI'x(x,y)はimg_fil_x[y, x]に格納されているため、それぞれのエッジ情報を用いて式通りに計算していきます。出力はimg_edge_xyに格納しています。

#エッジ強度の計算
img_edge_xy = np.zeros((height - N, width - N))
for x in range(width - N):
    for y in range(height - N):
        img_edge_xy[x,y] = pow(pow(img_fil_x[y, x],2) + pow(img_fil_y[y, x],2), 0.5)

得られたimg_edge_xyを表示すると以下のようになりました。縦と横の両方のエッジ情報が画像に反映されています。 f:id:Ayata:20210217210217p:plain:w350

ソースコード全体

import matplotlib.pyplot as plt
import cv2 #OpenCVのインポート
import numpy as np

#画像パス
FILE_PATH = 'C:/Users/.../aaa.png'

#画像の読み込み
img = cv2.imread(FILE_PATH)

#画像サイズの取得
height, width, color = img.shape
print("width = " + str(height))
print("height = " + str(height))
print("color channel = " + str(color))

#グレースケールへの変換
img_gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

#1次微分のフィルタ
#横方向
fil_x = np.array([[0, 0, 0],
                  [-1, 1, 0],
                  [0, 0, 0]])

#縦方向
fil_y = np.array([[0, -1, 0],
                  [0, 1, 0],
                  [0, 0, 0]])

#空間フィルタリングの演算部
N = 1
img_fil_x = np.zeros((height - N, width - N))
img_fil_y = np.zeros((height - N, width - N))

#横方向の演算
for x in range(N, width - N, 1):
    for y in range(N, height - N, 1):
        a = 0
        for i in range(-N, N + 1, 1):
            for j in range(-N, N + 1, 1):
                a = a + img_gray[y+j, x+i]*fil_x[j+N, i+N]
        img_fil_x[y-1, x-1] = abs(a)

#縦方向の演算
for x in range(width - N):
    for y in range(height - N):
        a = 0
        for i in range(-N, N + 1, 1):
            for j in range(-N, N + 1, 1):
                a = a + img_gray[y+j, x+i]*fil_y[j+N, i+N]
        img_fil_y[y-1, x-1] = abs(a)

#エッジ強度の計算
img_edge_xy = np.zeros((height - N, width - N))
for x in range(width - N):
    for y in range(height - N):
        img_edge_xy[x,y] = pow(pow(img_fil_x[y, x],2) + pow(img_fil_y[y, x],2), 0.5)

#画像のプロット
cv2.namedWindow("img", cv2.WINDOW_AUTOSIZE)
cv2.namedWindow("img1", cv2.WINDOW_AUTOSIZE)
cv2.namedWindow("img2", cv2.WINDOW_AUTOSIZE)
cv2.namedWindow("img3", cv2.WINDOW_AUTOSIZE)
cv2.imshow("img",img)
cv2.imshow("img1",img_fil_x)
cv2.imshow("img2",img_fil_y)
cv2.imshow("img3",img_edge_xy)
cv2.waitKey(0)
cv2.destroyAllWindows()

#出力の保存
cv2.imwrite('img_fil_x.png',img_fil_x)
cv2.imwrite('img_fil_y.png',img_fil_y)
cv2.imwrite('img_edge_xy.png',img_edge_xy)

まとめ

今回は空間フィルタリングを用いて縦と横のエッジ処理を実装し、エッジ強度を導入して縦と横方向のエッジ情報を統合しました。
空間フィルタリングの実装方法がわかったため、次回はいろいろなフィルタを試してみたいと思います。また、OpenCVの関数を用いた実装方法についても学習していきたいと思います。