ITや趣味など気軽に投稿しています。

【Python】OpenCVで画像から特定の色を抽出する

前回は色空間について学んだな

BGRとか、HSVとかですね。

今回は、色空間を利用して、画像から特定の色の部分を抽出する方法を紹介する。

お願いします。

事前準備

今回は、色抽出がわかりやすいようにカラフルな画像を使ってみる。

ColorImage.jpg
import numpy as np
import cv2

#画像の読込
img = cv2.imread('input/colorImage.jpg')

#画像のサイズを小さくする(前処理)
height = img.shape[0]
width = img.shape[1]
resized_img = cv2.resize(img,(round(width/4), round(height/4)))

RGBで抽出する

まずはRGBで色を抽出してみよう。

いきましょう。

青色を抽出する

画像から青色部分を抽出する。

#青を抽出
bgr = [210,150,40]
thresh = 40

#色の閾値
minBGR = np.array([bgr[0] - thresh, bgr[1] - thresh, bgr[2] - thresh])
maxBGR = np.array([bgr[0] + thresh, bgr[1] + thresh, bgr[2] + thresh])

#画像の2値化
maskBGR = cv2.inRange(resized_img,minBGR,maxBGR)
#画像のマスク(合成)
resultBGR = cv2.bitwise_and(resized_img, resized_img, mask = maskBGR)

cv2.imshow("Result BGR", resultBGR)
cv2.imshow("Result mask", maskBGR)

cv2.waitKey(0)
cv2.destroyAllWindows()

抽出する色はminBGR~maxBGRの範囲で指定する。最初に指定しているbgr±threshの範囲がそれにあたる。

cv2.inRange()ではオリジナルの画像を、指定したBGRの範囲で2値化している。

2値化ということは、白黒にしているということです?

その通り。この場合、青色の部分が白くなる。

cv2.inRange()で2値化された画像

最後に、cv2.bitwise_and()でこの2値化した情報を基にオリジナル画像をマスクする。

具体的には引数maskに2値化した情報(maskBGR)を渡している。

cv2.bitwise_and(resized_img, resized_img, mask = maskBGR)
blue

おぉ、青色だけ見事に抜き取れてますね。

赤色を抽出する

赤色を抽出する方法も同様だが、コードと結果だけ示しておこう。

#赤を抽出
bgr = [-30,-30,150]
thresh = 40

#色の閾値
minBGR = np.array([bgr[0] - thresh, bgr[1] - thresh, bgr[2] - thresh])
maxBGR = np.array([bgr[0] + thresh, bgr[1] + thresh, bgr[2] + thresh])

#画像の2値化
maskBGR = cv2.inRange(resized_img,minBGR,maxBGR)
#画像のマスク(合成)
resultBGR = cv2.bitwise_and(resized_img, resized_img, mask = maskBGR)

cv2.imshow("Result BGR", resultBGR)
cv2.imshow("Result mask", maskBGR)

cv2.waitKey(0)
cv2.destroyAllWindows()
resultBGR = cv2.bitwise_and(resized_img, resized_img, mask = maskBGR)
maskBGR = cv2.inRange(resized_img,minBGR,maxBGR)

あれ、BGRって0から255までの範囲で指定するんじゃなかったですっけ?マイナス?

確かにBGRでマイナスの範囲は存在しない。今回は赤とオレンジを明確に分けるために0~10の範囲を指定したかったんだ。

マイナス範囲は無視されるから、レンジを±40に保つために敢えて-30として、0~10の範囲を指定している。

とりあえず、マイナス値は無視されることを利用してる、、んですよね。

HSVで抽出する

前提条件で画像をRGBで読み込んでいるから、まずHSVに変換する必要がある。

import numpy as np
import cv2

img = cv2.imread('input/colorImage.jpg')

#画像のサイズを小さくする(前処理)
height = img.shape[0]
width = img.shape[1]
resized_img = cv2.resize(img,(round(width/4), round(height/4)))

# HSVに変換
resized_img_HSV = cv2.cvtColor(resized_img, cv2.COLOR_BGR2HSV)
cv2.imshow("HSV", resized_img_HSV)

HSVに変換すると以下のような色合いになる。

HSV変換後

うわ、眩しい、、

確かに、直感的には理解しづらい色合いにはなっているな。

OpenCVで色相(H)を扱う際の注意点

HSVの色相(H)は通常0~360°の範囲で表現されることが多いが、OpenCVでは0~179の範囲で指定する。

OpenCVにおけるHSVの色相
OpenCVにおける色相

紛らわしいですね、間違えないようにしないと、、

青色を抽出する

では、HSVを使って青色を抽出してみよう。

#青を抽出
hsv_min = np.array([100,45,70])
hsv_max = np.array([105,255,255])

#画像の2値化
maskHSV = cv2.inRange(resized_img_HSV,hsv_min,hsv_max)
#画像のマスク(合成)
resultHSV = cv2.bitwise_and(resized_img, resized_img, mask = maskHSV)

cv2.imshow("Result HSV", resultHSV)
cv2.imshow("Result mask", maskHSV)

cv2.waitKey(0)
cv2.destroyAllWindows()
resultHSV = cv2.bitwise_and(resized_img, resized_img, mask = maskHSV)
maskHSV = cv2.inRange(resized_img_HSV,hsv_min,hsv_max)

手順は、BGRの時と同じですね。

赤色を抽出する

今度は赤色を抽出するが、OpenCVにおける色相から気づきはないかね

気づきって、特に、、全色揃ってますし。

では、赤色はどの位置にある?

、、あ、0付近と180付近の2か所ありますね。

その通り。なので、赤色を抽出する場合は2か所を足し合わせる必要がある。

#赤を抽出
low_hsv_min = np.array([0, 200,50])
low_hsv_max = np.array([1, 255, 255])

#画像の2値化(Hueが0近辺)
maskHSV_low = cv2.inRange(resized_img_HSV,low_hsv_min,low_hsv_max)

high_hsv_min = np.array([178, 200,0])
high_hsv_max = np.array([179, 255, 255])

#画像の2値化(Hueが179近辺)
maskHSV_high = cv2.inRange(resized_img_HSV,high_hsv_min,high_hsv_max)

#2つの領域を統合
hsv_mask = maskHSV_low | maskHSV_high

#画像のマスク(合成)
resultHSV = cv2.bitwise_and(resized_img, resized_img, mask = hsv_mask)

cv2.imshow("Result HSV", resultHSV)
cv2.imshow("Result mask", hsv_mask)

cv2.waitKey(0)
cv2.destroyAllWindows()

hsv_mask = maskHSV_low | maskHSV_highで2か所の和を取ってるわけですね。

色空間を使って画像から色を抽出してみたが、画像解析では不要な情報を落とすためにもつかわれるので、こういったことができるということを覚えておいてほしい。

ほいさー。

次回は少しトピックが変わって画像のフィルタリングについて扱う。画像解析の前処理で使われる技法を紹介していく。では今回はここまで。

ありがとうございました~~♪