【Python・OpenCV】色相の範囲による閾値処理(cv2.inRange)

※当サイトではアフィリエイト広告を利用しています

Python プログラミング 画像処理

【Python・OpenCV】色相の範囲による閾値処理・色抽出(cv2.inRange)

2023-12-31

はじめに

OpenCVには閾値処理を行う関数が用意されていますが、いずれもグレースケール画像を対象としたもので、色に対して閾値処理を行うものではありません。
本記事では、色相の範囲を指定して閾値処理を行う方法を解説します。
また、適用するアプリケーションやグレースケールによる閾値処理との使い分けについても考察しました。

グレースケール画像を対象とした閾値処理については、次の記事で解説しています。

色相の範囲を指定した閾値処理とは

この方法は、特定の色相の範囲を抽出する必要がある場面に適しています。以下は、この手法が有用なアプリケーションの一例です:

  1. 物体検出:
    特定の色相で物体を検出する際に利用されます。例えば、果物や植物が特定の色相の範囲にある場合、それらを目立たせて抽出するのに役立ちます。
  2. 画像の前処理:
    画像処理パイプラインの一環として、特定の色相を持つ対象を他の要素から分離するために使用されることがあります。例えば、顔の検出やトラッキングの前処理として応用できます。
  3. セグメンテーション:
    画像を異なる領域に分割するセグメンテーションタスクにおいて、特定の色相の領域を単独で抽出するのに役立ちます。
  4. 特定の色の強調:
    特定の色相の領域を強調して表示したり、他の領域を抑制することで、画像内の特定の要素を強調するのに使用できます。

この方法は、特定のカラーフィルタリングや領域抽出が必要な場面で広く活用されます。実際の利用は、具体的な要件に合わせて調整を行って下さい。

色相について

色相(Hue)は、色の主観的な属性であり、色の種類を示します。色相は、光の波長の種類や波形ではなく、人間の視覚システムが感知する方法に基づいています。色相は、虹のようなスペクトルの中で異なる色を識別するのに使用されます。

主にHSL(Hue, Saturation, Lightness)またはHSV(Hue, Saturation, Value)の色空間で表現され、Hueは0°から360°の範囲で表されます。この範囲を360°で循環させることで、赤が0°、緑が120°、青が240°など、全ての基本的な色が表現できます。

具体的な例を挙げると、赤、青、緑、黄色などは異なる色相に属します。色相は、色の視覚的な種類を区別するのに役立ち、例えば画像処理やデザインなどの分野で頻繁に使用されます。

HSV色空間については次の記事で解説しています。

合わせてどうぞ

色相の範囲による閾値処理

色相の範囲を指定して閾値処理を行う際、一般的にはHSV色空間を使用します。
以下にその手順を簡単に説明します。

  1. 画像をHSV色空間に変換する。
  2. 色相の範囲を指定して、その範囲内のピクセルを白(255)に、範囲外のピクセルを黒(0)にするマスクを生成する。
  3. 元の画像とマスクを結合し、指定した色相の領域だけを強調する。

色相の範囲による閾値処理グレースケール閾値処理との使い分け

  1. 対象の情報:
    • 色相ベースの閾値処理は、特定の色相の領域を抽出します。例えば、青い空や赤い車など。
      一方、グレースケールの閾値処理は、画像の明るさに基づいて対象を抽出します。
  2. 応用:
    • 色相ベースの処理は、特定の色の物体や領域を目立たせたり、抽出したりするのに適しています。
      一方で、グレースケールの閾値処理はエッジ検出や物体の輪郭抽出に使われることがあります。
  3. 複雑性:
    • 色相ベースの処理は、RGBからHSVへの変換と色相範囲指定が必要です。
      これに対して、グレースケールの閾値処理は単純で、画像を単一のチャンネルに変換してから閾値を適用するだけです。
  4. 環境への頑健性:
    • グレースケールでの閾値処理は光の変動に対して比較的頑健で、照明条件が変わっても機能する傾向があります。
      色相ベースの処理は環境の色温度の変化に影響を受けやすい場合があります。

cv2.inRange関数で閾値処理を行う

色相の範囲による閾値処理はOpenCVのcv2.inRange関数を使用します。

cv2.inRange(入力画像, lowerb, upperb[, dst])

引数

名称説明
入力画像(必須)入力画像。通常は3チャンネル(カラー画像)の場合が多いですが、単一のチャンネル(グレースケール画像)も可能です。
lowerb(必須)下限の範囲。これは(low_H, low_S, low_V)の形式で指定され、HSV色空間ならば色相(Hue)、彩度(Saturation)、明度(Value)の各要素の下限値です。
upperb(必須)上限の範囲。これは(high_H, high_S, high_V)の形式で指定され、HSV色空間ならば色相(Hue)、彩度(Saturation)、明度(Value)の各要素の上限値です。
dst(オプション)出力画像。指定された場合、結果がこの変数に格納されます。指定されない場合は、新しい配列が作成されて結果が格納され戻り値として取得できます。

戻り値

cv2.inRange関数の戻り値は、指定された範囲内にあるピクセルが白(255)に、範囲外のピクセルが黒(0)に設定された2値のマスクです。このマスクは、入力画像と同じサイズの配列として返されます。

サンプルコード

入力画像として、この画像を使用しました。

引用元
オレンジの半分が乗った青いプレート

サンプルコードを下に示します。
本サンプルコードではお皿に乗ったオレンジの部分を閾値処理で取り出します。

import cv2
import matplotlib.pyplot as plt

# 画像の読み込み
image = cv2.imread("image.jpg")

# BGRからHSVに変換
hsv_image = cv2.cvtColor(image, cv2.COLOR_BGR2HSV)

# BGRのチャンネル並びをRGBの並びに変更(matplotlibで結果を表示するため)
rgb_image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB) 

# 色相の範囲を指定
lower_hue = 14  # 下限
upper_hue = 90  # 上限

# 色相に基づいて閾値処理を行う
mask = cv2.inRange(hsv_image, (lower_hue, 50, 50), (upper_hue, 255, 255))

# 元画像とマスクを結合して結果を得る
result = cv2.bitwise_and(rgb_image, rgb_image, mask=mask)

# 入力画像と閾値処理画像を表示
plt.rcParams["figure.figsize"] = [9,4]                                  # ウィンドウサイズを設定
title = "cv2.inRange: codevace.com"
plt.figure(title)                                                       # ウィンドウタイトルを設定
plt.subplots_adjust(left=0.05, right=0.95, bottom=0.03, top=0.95)       # 余白を設定
plt.subplot(121)                                                        # 1行2列の1番目の領域にプロットを設定
plt.imshow(rgb_image)                                                   # 入力画像を表示
plt.title('Original Image')                                             # 画像タイトル設定
plt.axis("off")                                                         # 軸目盛、軸ラベルを消す
plt.subplot(122)                                                        # 1行2列の2番目の領域にプロットを設定
plt.imshow(result)                                                      # 結果を表示
plt.title("Thresholded Image: lower_hue=" + str(lower_hue) + ", upper_hue=" + str(upper_hue))              # 画像タイトル設定
plt.axis("off")                                                         # 軸目盛、軸ラベルを消す
plt.show()

結果は以下となります。

似ている色の背景の木目も含まれてしまいましたが、指定した色の範囲で閾値処理ができていることが確認できました。

サンプルコードの14行目、15行目で色の範囲を指定しいますが、この範囲を青系の色の範囲に下記の様に指定するとお皿の部分を取り出すことができます。

lower_hue = 70   # 下限
upper_hue = 120  # 上限

HSVの値について

OpenCVにおいてHSV色空間の色相(H)は0〜179の範囲の整数で指定することができます。
閾値として設定したい色が、色相のどの値か知るために下のカラーバーが参考になります。

このカラーバーを作成するコードは以下になります。

import cv2
import numpy as np
import matplotlib.pyplot as plt

def create_hsv_colorbar(width, height):
    hsv_colorbar = np.zeros((height, width, 3), dtype=np.uint8)

    for i in range(width):
        hue = int((i / width) * 180)  # OpenCVではHueのレンジは'0'から'179'
        hsv_color = np.array([[[hue, 255, 255]]], dtype=np.uint8)
        rgb_color = cv2.cvtColor(hsv_color, cv2.COLOR_HSV2BGR)[0, 0]
        hsv_colorbar[:, i] = rgb_color

    return hsv_colorbar

def main():
    # 画像のサイズ
    width, height = 540, 100

    # カラーバー画像を作成
    hsv_colorbar = create_hsv_colorbar(width, height)

    # BGRのチャンネル並びをRGBの並びに変更(matplotlibで結果を表示するため)
    rgb_image = cv2.cvtColor(hsv_colorbar, cv2.COLOR_BGR2RGB) 

    # 入力画像と閾値処理画像を表示
    plt.rcParams["figure.figsize"] = [8,2.5]                                # ウィンドウサイズを設定
    title = "Hue(HSV) Colorbar: codevace.com"
    plt.figure(title)                                                       # ウィンドウタイトルを設定
    plt.subplots_adjust(left=0.05, right=0.95, bottom=0.10, top=0.90)       # 余白を設定
    plt.subplot(111)                                                        # 1行1列の1番目の領域にプロットを設定
    plt.imshow(rgb_image)                                                   # 入力画像を表示
    plt.xticks([0, 90, 180, 270, 360, 450, 540], ['0', '29', '59', '89', '119', '149', '179']) # X軸のラベル
    plt.tick_params(labelbottom=True, labelleft=False, labelright=False, labeltop=False) # X軸のみラベルを表示する
    plt.tick_params(bottom=True, left=False, right=False, top=False)        # X軸のみ目盛を表示する
    plt.xlabel("Hue")
    plt.show()

if __name__ == "__main__":
    main()
(広告) OpenCV関連書籍をAmazonで探す

おわりに

色相の範囲を指定して閾値処理を行う方法を紹介しました。
特定の色相の領域を抽出するための閾値処理は、視覚的な情報の抽出や領域分割など幅広いアプリケーションで応用できます。
グレースケールでの閾値処理との目的に応じた使い分けの参考になれば嬉しいです。

ご質問や取り上げて欲しい内容などがありましたら、コメントをお願いします。
最後までご覧いただきありがとうございました。

参考リンク

■(広告)OpenCVの参考書としてどうぞ!■

-Python, プログラミング, 画像処理
-