Pythonで画像から特定の色のみ抽出〜OpenCVでやってみた〜

Pythonで画像から特定の色のみ抽出〜OpenCVでやってみた〜

画像処理を勉強中なのでその内容のアウトプットです。

対象の画像から 3原色の内、赤色(赤系統)のみを抽出した画像にする 処理です。

OpenCVを使用した赤色のみ抽出

コメント多めなのは自分の勉強を兼ねているからです。

Code

import cv2 #OpenCVライブラリ
import matplotlib.pyplot as plt #グラフ化用ライブラリ
%matplotlib inline
IMG_PATH = './data/smarties.png'
#画像表示
def show_img(img, vmin=0, vmax=255, title=None):
    if img.ndim == 3:
        #カラー(ndim=次元数=3 -> RGBの3色)        
        #CV2のRGB3色に変換
        rgb_img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
        #変換後画像をplotへ設定
        plt.imshow(rgb_img, vmin=vmin, vmax=vmax)
    elif img.ndim == 2:
        #グレースケール(ndim=次元数=2 -> 白黒の2色)
        #変換後画像をplotへ設定
        plt.imshow(img, cmap='gray', vmin=vmin, vmax=vmax)
    #座標軸非表示
    plt.axis('off')
    #タイトル設定
    plt.title(title)
    #グラフ表示
    plt.show()

#画像表示(比較)
def show_img_compare(src, dst, vmin=0, vmax=255):
    # 元画像表示
    plt.subplot(121)
    show_img(src, vmin, vmax, title='Before')
        
    # 返還後画像表示
    plt.subplot(122)
    show_img(dst, vmin, vmax, title='After')    
bgr_img = cv2.imread(IMG_PATH)
show_img(bgr_img)

#3色に分離(チャンネルを分ける)
b, g, r = cv2.split(bgr_img)
#blueのみチャンネル
#show_img(b)

#greenのみチャンネル
show_img(g)

#redのみチャンネル
show_img(r)

#blueをマスク(黒にする)
_, mask_b_img = cv2.threshold(b, 200, 255, cv2.THRESH_BINARY_INV)
#show_img_compare(b, mask_b_img)

#元画像と合成して青を除外(白の座標のみ元画像を復元)
except_b_img = cv2.bitwise_and(bgr_img, bgr_img, mask=mask_b_img)
#show_img_compare(mask_b_img,except_b_img)

#redをマスクする(白にする)
_, mask_r_img = cv2.threshold(r, 150, 255, cv2.THRESH_BINARY)
#show_img_compare(r, mask_r_img)

#元画像と合成してredのみにする(白の座標のみ元画像を復元)
only_r_img = cv2.bitwise_and(except_b_img, except_b_img, mask= mask_r_img)
#show_img_compare(bgr_img, only_r_img)

#境界線が粗いのでフィルタをかける(境界を曖昧にする)
recure_img = cv2.medianBlur(only_r_img, 5)
#show_img(only_r_img)
show_img(recure_img)

RESULT

元画像

実行後画像

やってみて

対象物の境界線の粗さが目立つ

フィルターの方法がマズイんだと思います。フィルターについては厳密には画像にある境界線を暈しているだけなので元画像と比較すると正確さが損なわれていると思います。

ここで載せている画像を人の目がみても、その差はあまりわからないかもですね。ちなみにフィルター前は↓

パッと見ではわからないでしょ?これを数値化するとわかりやすいかもしれませんね。

チャンネル毎のマスク処理

blueのマスク処理で白色にする→元画像と合成→blueを除外

の流れですが、除外しなくても良い座標についても除外されている気がします。その結果が対象物の境界線の粗さに繋がっているのかも。。。

この辺りは閾値の設定次第かと思われます。ひたすら調べていくと丁度良い数値があるのでしょうが、今回はなしです。

提供されているライブラリにその辺りを調整してくれるモノがあるかもなので、もし見つけたらその機会に実行してみます。

Pythonカテゴリの最新記事