【Python/OpenCV】赤・緑・青色の検出(HSV色空間)

PythonとOpenCVを用いて画像をHSV色空間に変換し、赤・緑・青色の領域を色検出する方法をソースコード付きで解説します。

HSV色空間とは

HSV色空間とは、「色相(Hue)」「彩度(Saturation)」「明度(Value)」の3つの組み合わせで色を表現する手法です。

説明
色相(H) 色合い。(赤っぽい、青っぽいといった色のおおまかな違いのことで、赤なら0度、黄色なら 60度といったように角度で色合いが決まります)
彩度(S) 色の鮮やかさ。(色相が同じ場合でも、彩度が高ければ鮮やかに見え、低ければグレーに見える。彩度がゼロの場合は無彩色[黒、グレー、白])
明度(V) 色の明るさ。(高いほど明るい色になる)

HSV色空間は人間が色を知覚する方法と類似しているため、RGB色空間よりも人がイメージした通りの色を作りやすいという特徴があります。この特徴から、画像処理でも色検出やカラートラッキングをおこなう場合などにHSV色空間が利用されています。

解説動画

本ページの内容は以下動画でも解説しています。併せてご活用ください。

RGB色空間からHSV色空間へ変換する計算式

(R、G、B)の値が0.0(最小)から1.0(最大)の範囲にあるとします。
そして、各画素のR、G、Bの3つの画素値のうち、最大値をMAX、最小値をMINとします。

H(色相)の計算

最小値MINがB(青)の画素値であった場合、H(色相)は以下の式で計算できます。
$$ H=60\times \frac{G-R}{MAX-MIN}+60 $$

最小値MINがR(赤)の画素値であった場合、H(色相)は以下の式で計算できます。

$$ H=60\times \frac{B-G}{MAX-MIN}+180 $$

最小値MINがG(緑)の画素値であった場合、H(色相)は以下の式で計算できます。

$$ H=60\times \frac{R-B}{MAX-MIN}+360 $$

最大値MAXと最小値MINが等しい場合、定義はありません。

$$ H=undefined $$

S(色彩)の計算

HSV色空間が円錐モデルのとき、S(色彩)は以下の式で計算できます。

$$ S = MAX-MIN $$

円柱モデルのとき、S(色彩)は以下の式で計算できます。

$$ S=\frac{MAX-MIN}{MAX} $$

V(明度)の計算

V(明度)は以下の式で計算できます。

$$ V=MAX $$

【画像処理】HSV色空間の原理・特徴・計算式
HSV色空間の原理や特徴、計算式についてまとめました。

サンプルコード①RGBからHSVに変換

Python版OpenCVでは、cv2.cvtColorメソッドでRGBからHSVに変換できます。


【実行結果】
左が入力画像(input.png)、右が出力画像(output1.png)

【ポイント】

hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)

imgは入力画像(RGB)のデータが格納されたNumPy配列、hsvはHSV色空間に変換されたあとのデータが格納されたNumPy配列です。
HSV色空間に変換された画像データの色相(H)は0~179、彩度(S)は0~255、明度(V)は0~255の範囲の値をとります。

サンプルコード② 画像中から赤、緑、青色の部分を検出

HSV色空間における、赤色、緑色、青色の色相(H)のおおよその範囲は以下のとおりです。

値の範囲 値の範囲(OpenCVの場合)
赤色の色相(H) 0~60, 300~360[度] 0~30, 150~179
緑色の色相(H) 60~189[度] 30~90
青色の色相(H) 180~300[度] 90~150

彩度(S)と明度(V)は対象に応じて適宜調整しますが、今回は以下の値域で赤、緑、青を検出します、

値の範囲 値の範囲(OpenCVの場合)
彩度(S) 25~100[%] 64~255
明度(V) 00~100[%] 0~255
彩度(S)は下限をさげるほど薄い色も検出します。明度(V)はは下限をさげるほど暗い部分も検出します。

ポイント解説

cv2.inRange(src, lowerb, upperb)

cv2.inRangeは、画像内の特定の範囲の色を検出するために使用しています。
指定した範囲内のピクセルを白(255)、範囲外のピクセルを黒(0)にする2値画像(マスク画像)を生成します。

引数 説明
src 入力画像(通常はHSV色空間に変換された画像)。
lowerb 検出する色の下限値(配列)。
upperb 検出する色の上限値(配列)。
マスク画像とは、画像の一部を選択して処理を行うための二値画像です。マスク画像では、処理したい部分を白(255)、処理したくない部分を黒(0)で表現します。これにより、特定の領域だけを操作することができます。
cv2.bitwise_and(src1, src2, dst=None, mask=None)

cv2.bitwise_andは、画像の特定の領域をマスクを使って抽出するために使用しています。ビット単位のAND演算を行い、指定されたマスクに基づいて画像の一部を選択します。

引数 説明
src1 最初の入力画像。
src2 2番目の入力画像(通常はsrc1と同じ画像を指定します)。
dst 出力画像(省略可能)。
mask マスク画像。マスク画像は、処理したい部分を白(255)、処理したくない部分を黒(0)で表現します。

実行結果

サンプルプログラムの実行結果です。

■入力画像(input.png)

■赤色の検出結果
左がマスク画像(red_mask.png)、右がマスキング処理した画像(red_masked_img.png)

■緑色の検出結果
左がマスク画像(green_mask.png)、右がマスキング処理した画像(green_masked_img.png)

■青色の検出結果
左がマスク画像(blue_mask.png)、右がマスキング処理した画像(blue_masked_img.png)

【補足】彩度と明度の調整

対象とする色以外も抽出してしまった場合は、S(彩度)やV(明度)を調整することで改善できます。
例えば、先程の赤色検出の例だと赤色の眼鏡以外にも、茶色系の髪や衣服も検出してしまっています。Sは彩度(色の鮮やかさ)を示す値で、下限をさげるほど薄い色も検出してしまうため、茶色も検出してしまいます。
また、Sは明るさを示す値で、これも下限を下げるほど黒っぽい色も検出しやすくなります。
よって、これらの下限を次のように引き上げてみます。

    # 赤色のHSVの値域1
    hsv_min = np.array([0,200,50])
    hsv_max = np.array([30,255,255])
    mask1 = cv2.inRange(hsv, hsv_min, hsv_max)

    # 赤色のHSVの値域2
    hsv_min = np.array([150, 200, 50])
    hsv_max = np.array([179,255,255])
    mask2 = cv2.inRange(hsv, hsv_min, hsv_max)

すると、以下のように赤色の眼鏡と輪郭のみが検出され、その他の部分は大幅にカットできます。
このように、対象物の色の濃さ等も考慮して適宜SやVの値を調整することで、色検出の精度を改善できます。

■赤色の検出結果
左がマスク画像(red_mask.png)、右がマスキング処理した画像(red_masked_img.png)

【応用例】色追跡(カラートラッキング)

色検出の応用例として、カメラで物体の動きを追跡したいときに、追跡対象に色を塗る、もしくはマーカーを塗ってそれを色検出するというものがあります。
これを使えば、振り子の運動をカメラで計測したりできます。

■赤色の振り子を追跡している様子

■グラフ化した結果

詳細は下記事で紹介していますので、興味のある方はご参照ください。

【Python/OpenCV】カラートラッキングで振り子の運動を測定
Python+OpenCVのカラートラッキングで振り子の運動を測定する方法をソースコード付きで解説します。

関連ページ

【Python版OpenCV超入門】使い方とサンプルコードを解説
Python版OpenCVで画像処理プログラミングを行う方法を入門者向けにソースコード付きで解説するページです。

コメント