Ayataの

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

【Pythonでエッジ処理】単純なエッジ処理の自力実装

前回はエッジ処理について、ざっくりとした理論を学習しました。
ayatalog.hatenablog.jp 今回はOpenCV上の便利な関数は使わずに、自力で一次微分を用いた単純なエッジ処理を実装していこうと思います。最初に縦方向のエッジ、横方向のエッジをそれぞれ片方ずつ検出できるものを作成し、後で合体させていきます。


1. 画像の読み込み

画像の読み込みは以前、Python上のOpenCV環境の構築で行いました。

ayatalog.hatenablog.jp

上の記事と同様に、FILE_PTATHに読み込みたい画像のパスを代入して、それを元にimg = cv.imread(FILE_PATH)で画像を読み込みます。

2. 画像サイズの取得, 画像をグレースケールへ変換

読み込んだ画像はnumpy行列として格納されており、各画素の色情報はRGBの3チャンネルで表されています。このままだと画素ごとに比較がしずらいため、画像をグレースケールに変換します。また、ついでに画像のサイズの取得も行います。
画像サイズの取得は"img.shape"で行います。imgはcv.imreadで画像を読み込んだ行列です。img のshapeをheight, width, colorにそれぞれ格納します。
今回用意した画像は30×30ピクセルなので、height=30, width=30, color=30となります。
グレースケールへの変換は、今回は白黒の画像なので"cv2.cvtColor(img,cv2.COLOR_RGB2GRAY)"で一発変換してしまいます。グレースケール変換にもいろいろ方法があったり各色の重みを変えたりするみたいなのですが今回は置いときます。グレースケールに変換された画像は、高さ30, 横30, 輝度値1チャンネルの行列になります。輝度値は0-255の値を取ります。

#画像サイズの取得
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)

f:id:Ayata:20201212010912p:plain:w200

3. エッジ処理

さてメインのエッジ処理の部分です。今回はただ単純に画像を端から走査して、隣の画素と輝度値を比較して、しきい値を超えたらエッジと判定するものを作りました。
縦方向の走査のイメージはこんな感じ、横方向では比較対象が横の画素になります。
f:id:Ayata:20210122024141p:plain:w300

縦方向のエッジ検出のコードは以下です。今いる位置をa = img_gray[i, k], その隣(下)のピクセルb = img_gray[i+1, k]としました。外側にある2つのfor文で縦と横方向に現在位置が移動していきます。
横方向のピクセルを指定する場合はb = img_gray[i, k+1]とします。
aとbには輝度値が格納されているため、これらの差分をとることでエッジかの判断をします(if abs(int(a)-int(b))>100:の部分)。ここでint(a), int(b)とint型に変換しているのは、輝度値はunit8型(8ビット符号なし整数)であるため、計算結果がマイナスになるとエラーとなってしまうためです。100は適当に置きました。
差分が100より大きい場合、edgeに現在位置を格納していきます。edgeは始めは1行2列の行列ですが、edge = np.vstack([edge, [i, k]])でエッジとして検出した位置を追加していきます。
edge_indexを使っているのは、1回目だけedgeに直接値を代入し、それ以降にedgeに追加したいためです。また、あとでエッジ判定した回数として使用しています。

#一次微分によるエッジ検出
edge_index = 0
edge = np.array([0, 0])
for i in range(height-1):
    for k in range(width-1):
        a = img_gray[i, k]
        b = img_gray[i+1, k]
        if abs(int(a)-int(b))>100:
            if edge_index == 0:
                edge = np.array([i, k])
            else:
                edge = np.vstack([edge, [i, k]])
            edge_index += 1

4. 画像にエッジ情報の追加

得られたエッジがある位置(edge行列)を用いて、元画像にエッジ情報を反映していきます。
エッジがある位置は、edge行列の中身を見れば書いてあるので、img_edge[edge[i, 0], edge[i,1], :] = (255, 0 , 0)でエッジがある位置の色を赤(255, 0, 0)に置き換えていきます。for i in range(edge_index):でエッジ判定した回数文ループを繰り返します。

5. 画像のプロット

画像のプロットは上の画像にあるように"plt.omshow(img_edge)"と"plt.show()"でおこないます。以下の画像が表示されました。 <
 縦および横方向のエッジが赤く表示されています。30×30ピクセルなのでかなり微妙な仕上がりですが、一応エッジが検出できました。

f:id:Ayata:20210122030712p:plain:w200f:id:Ayata:20210122031112p:plain:w200

まとめ

今回は縦方向のみ、横方向のみのエッジを検出するものをそれぞれ作成しました。次回はこれらを合体させて、エッジ強度を導入し、エッジの方向も求めていきたいと思います。加えて、空間フィルタリングについても学習していきたいと思います。