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

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

画像処理では、画像から特定の色の部分だけを抽出したい場面があります。例えば、物体検知の前処理として特定の色のオブジェクトだけを残したり、不要な背景色を除去したりするケースです。

この記事では、色空間(RGB/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で抽出する

青色を抽出する

BGRの範囲を指定し、cv2.inRange()で2値化した後、cv2.bitwise_and()でマスク処理を行います。

# 青を抽出
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()

処理の流れ:

  1. 抽出したい色の範囲をminBGRmaxBGRで定義する
  2. cv2.inRange()で、指定範囲内のピクセルを白(255)、範囲外を黒(0)に2値化する
  3. cv2.bitwise_and()で2値化マスクを使って元画像から該当部分のみを抽出する
2値化マスク
青色抽出結果

赤色を抽出する

赤色の場合も同様の手順で抽出します。

# 赤を抽出
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])

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()
赤色抽出結果
赤色マスク

ポイント: BGRの値に負の値を指定していますが、これは赤とオレンジを明確に区別するためです。BGRの有効範囲は0〜255のため、負の値は無視されます。閾値の範囲(±40)を維持しつつ、0〜10付近の狭い範囲を指定するテクニックです。

HSVで抽出する

HSVで色を抽出するには、まず画像を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)
HSV変換後

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

HSVの色相(H)は通常0〜360°で表現されますが、OpenCVでは0〜179の範囲で指定します。

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()
HSV青色抽出結果
HSV青色マスク

赤色を抽出する

HSVで赤色を抽出する場合は注意が必要です。色相の値を見ると、赤色は0付近と179付近の2箇所に分かれています。そのため、2つの範囲のマスクを作成し、それらを統合する必要があります。

# 赤を抽出(色相が0付近)
low_hsv_min = np.array([0, 200, 50])
low_hsv_max = np.array([1, 255, 255])
maskHSV_low = cv2.inRange(resized_img_HSV, low_hsv_min, low_hsv_max)

# 赤を抽出(色相が179付近)
high_hsv_min = np.array([178, 200, 0])
high_hsv_max = np.array([179, 255, 255])
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赤色抽出結果
HSV赤色マスク

maskHSV_low | maskHSV_highで2つのマスクのOR(和)を取ることで、色相が0付近と179付近の両方の赤色を統合して抽出しています。

まとめ

今回は、OpenCVで画像から特定の色を抽出する方法を紹介しました。

  • cv2.inRange()で色の範囲を指定して2値化マスクを作成する
  • cv2.bitwise_and()でマスクを使って元画像から色を抽出する
  • HSVでの赤色抽出は色相が2箇所に分かれるため、マスクの統合が必要
  • 色空間を活用した色の抽出は、画像解析における前処理として広く活用される