Python
OpenCV

画像から輪郭を抽出して重ね合わせる

画像から輪郭を抽出して重ね合わせる方法についてメモ
※opencv-python 3.4にて動作確認済み

サンプル画像

昔非常にお世話になったこの画像に今回もお世話になります。

入力画像 a.jpg
グレースケール b.jpg
dilate⇒diff c.jpg
二値化 d.jpg
重ね合わせ e.jpg

ソースコード

import cv2

def edgeDetect(color):

    # カーネルサイズの設定
    kernel5 = np.ones((5, 5), np.uint8)
    # [1] 画像のグレースケール化とノイズ除去
    gray = cv2.fastNlMeansDenoising(
        cv2.cvtColor(color, cv2.COLOR_BGR2GRAY), h=20)
    # [2] dilate -> diff で線抽出
    dilation = cv2.dilate(gray, kernel5, iterations=1)
    diff = cv2.subtract(dilation, gray)
    diff_inv = 255 - diff
    # [3] 線を単色 -> 2値に
    _, edge = cv2.threshold(diff_inv, 225, 255, cv2.THRESH_BINARY)
    # [4] 線画とモノクロ2値を重ねる
    edge = cv2.cvtColor(edge, cv2.COLOR_GRAY2BGR)
    img = cv2.bitwise_and(color, edge)

    return img

各工程の解説

[1] 画像のグレースケール化とノイズ除去

輪郭抽出するためにまず画像をグレースケール化する。ついでにcv2.fastNlMeansDenoising()を使用してノイズを除去することでゴミを輪郭として残さないようにする。ノイズの除去量はhを増やせば増えるが、やりすぎに注意。

fastNlMeansDenoisingの詳細
https://docs.opencv.org/3.0-beta/modules/photo/doc/denoising.html#fastnlmeansdenoising

[2] dilate -> diff で線抽出

cv2.dilate()cv2.subtract()を使うと輪郭がいい感じに抽出できる。最後に255 - diffで白黒反転している。

[3] 線を単色 -> 2値に

cv2.threshold()で抽出した線を二値化する。

thresholdの詳細
https://docs.opencv.org/3.0-beta/modules/imgproc/doc/miscellaneous_transformations.html#threshold

[4] 線画とモノクロ2値を重ねる

最後に抽出した輪郭をカラー画像に重ね合わせる。ただし、輪郭はグレースケール画像なのでcv2.cvtColor()を忘れないようにする。

以上