目次

apple_s

画像の2値化とは

画像の2値化とは画像を白(255)か黒(0)の2値の画像に変換する処理です。主にグレースケール画像に対して行われる処理で閾値より大きいところを白、閾値以下ところを黒といった具合に2値化を行います。

OpenCVで2値化

cv2.thresholdを使う

ret, result = cv2.threshold(img,thresh,max_val,thresholdType)

retは2値化が成功したかどうかのフラグです。resultは2値化された画像を示しています。imgは入力画像、threshは閾値、max_valは2値化するときの白の値です。thresholdTypeは2値化する方法です。thresholdTypeにcv2.THRESH_BINARYを指定すると閾値以上がmax_valになります。

cv2.thresholdをGUIで確認する。

threading

しきい値をトラックバーで選択して確認するスクリプトです。

# -*- coding: utf-8 -*-
import cv2

thresh = 0
max_val = 255
thresholdType = cv2.THRESH_BINARY

#トラックバーで、しきい値を変更
def changethresh(pos):
    global thresh
    thresh = pos

#画像をグレースケールで読み込む
img = cv2.imread("img.jpg", 0)
#ウィンドウの名前を設定
cv2.namedWindow("img")
cv2.namedWindow("thresh")

#トラックバーのコールバック関数の設定
cv2.createTrackbar("trackbar", "thresh", 0, 255, changethresh)

while(1):
    cv2.imshow("img", img)
    _, thresh_img = cv2.threshold(img, thresh, max_val, thresholdType)
    cv2.imshow("thresh", thresh_img)
    k = cv2.waitKey(1)

    #Escキーを押すと終了
    if k == 27:
        break

2値化+BGR分解で前景抽出

sun-flower

このひまわりの画像から前景を抽出しましょう。この場合は青色を抜き出して2値化を行うと簡単に前景と背景を分けることができます。thresholdTypeをcv2.THRESH_BINARY_INVに設定しています。こうすることで青が少ない場所が白色(255)になります。"s"を押すと背景が透明化された"result.png"が生成されます。

thresh_sunflower
# -*- coding: utf-8 -*-
import cv2
import numpy as np

thresh = 0
max_val = 255
thresholdType = cv2.THRESH_BINARY_INV

#トラックバーで、しきい値を変更
def changethresh(pos):
    global thresh
    thresh = pos

#画像を読み込む
img = cv2.imread("sun-flower.jpg")
#BGRで分解
blue_img, green_img, red_img = cv2.split(img)

#ウィンドウの名前を設定
cv2.namedWindow("img")
cv2.namedWindow("thresh")

#トラックバーのコールバック関数の設定
cv2.createTrackbar("trackbar", "thresh", 0, 255, changethresh)

while(1):
    cv2.imshow("img", img)
    _, thresh_img = cv2.threshold(blue_img, thresh, max_val, thresholdType)
    cv2.imshow("thresh", thresh_img)
    
    k = cv2.waitKey(1)
    #Escキーを押すと終了
    if k == 27:
        break
    #sを押すと結果を保存
    if k == ord("s"):
        result = cv2.merge(bgr + [thresh_img])
        cv2.imwrite("result.png", result)
        break

HSV分解

RGB分解よりHSV分解の方が上手くいく?

前の例は前景が黄色(Bがほとんど0)、背景が(Bがほとんど255)という 前景抽出しやすい状況でした。そこで、もっと一般的な画像に応用できそうな方法を考えてみます。ペイントツールを使って色々な方法で画像を編集していたら、HSV分解が使えそうなので試してみます。

HSVとは?

色はRBG(赤青緑)で一般的に表現されますが、HSVで表現することもできます。 HSVとは色相(Hue)、彩度(Saturation)、明度(Value)を表しています。 HSVによる色の表現のほうがRBGに比べて人の直感的な色の認識に近いらしいです。

OpenCVでHSV分解

apple-256261_640

このコードは画像を色相(H)、彩度(S)、明度(V)に分解して、 それぞれをウィンドに表示します。上記の画像を"apple.jpg"として保存して、このコードを実行すると、りんごの画像がHSV分解されます。

# -*- coding: utf-8 -*-
import cv2

#画像を表示するウィンドの用意
cv2.namedWindow("img")
cv2.namedWindow("h")
cv2.namedWindow("s")
cv2.namedWindow("v")

#画像の読み込み
img = cv2.imread("apple.jpg")
#BGRをHSVに変換
hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
#HSVに分解
h_img, s_img, v_img = cv2.split(hsv)

#元画像とHSV分解した画像をそれぞれ表示
cv2.imshow("img", img)
cv2.imshow("h", h_img)
cv2.imshow("s", s_img)
cv2.imshow("v", v_img)

cv2.waitKey(-1)

HSV分解の結果

HSVの結果は左から色相(H)、彩度(S)、明度(V)です。彩度を使うと前景が抽出できそうです。

apple_h apple_s apple_v

2値化+HSVで前景抽出

サンプルコード

python thresh_hsv.py 画像のパス
で実行すると前の例と同じように前景抽出ができます。
#thresh_hsv.py
# -*- coding: utf-8 -*-
import cv2
import sys

thresh = 0
max_val = 255
thresholdType = cv2.THRESH_BINARY

#トラックバーで、しきい値を変更
def changethresh(pos):
    global thresh
    thresh = pos

filename = sys.argv[1]
#画像を読み込む
img = cv2.imread(filename)
#BGRをHSVに変換
hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
#HSVに分解
h_img, s_img, v_img = cv2.split(hsv)

#ウィンドウの名前を設定
cv2.namedWindow("img")
cv2.namedWindow("thresh")

#トラックバーのコールバック関数の設定
cv2.createTrackbar("trackbar", "thresh", 0, 255, changethresh)

while(1):
    cv2.imshow("img", img)
    _, thresh_img = cv2.threshold(s_img, thresh, max_val, thresholdType)
    cv2.imshow("thresh", thresh_img)
    
    k = cv2.waitKey(1)
    #Escキーを押すと終了
    if k == 27:
        break
    #sを押すと結果を保存
    if k == ord("s"):
        result = cv2.merge(cv2.split(img) + [thresh_img])
        cv2.imwrite(filename[:filename.rfind(".")] + "_result.png", result)
        break

上手くいった例

上のスクリプトを使って、前景抽出してみました。

apple-256261_640 apple_result
jeans-564089_640jeans-564089_640_result
sun-flowersun-flower_result

上手くいかなかった画像

次の画像は全く上手くいきませんでした。

birdleather-shoes-2530462_640

自動で閾値設定(大津の二値化)

OpenCVで大津の2値化

cv2.thresholdのthresholdTypeを閾値のタイプ+ cv2.THRESH_OTSUとすることで 閾値を自動設定してくれます。

大津の2値化を使って前景抽出

# -*- coding: utf-8 -*-
import cv2
import sys

filename = sys.argv[1]
#画像を読み込む
img = cv2.imread(filename)
#BGRをHSVに変換
hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
#HSVに分解
h_img, s_img, v_img = cv2.split(hsv)

#cv2.THRESH_OTSUをフラグに足すと閾値を自動決定してくれます。
_, thresh_img = cv2.threshold(s_img, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)

result = cv2.merge(cv2.split(img) + [thresh_img])
cv2.imwrite(filename[:filename.rfind(".")] + "_result_otsu.png", result)

結果

先ほどの例で私が見て閾値を選んだ結果と大体一致してます。

apple-256261_640 apple_result_otsu
jeans-564089_640 jeans-564089_640_result_otsu
sun-flower sun-flower_result_otsu