はじめに
色相(Hue)の平均値を算出するため、色相環・ベクトルととらえることで、平均計算を実現する
色相の平均値を算出する方法は以下
- Hueの画素をベクトル化する
- 全画素のベクトル平均を算出する
- 平均ベクトルを色相の平均値として返す
色相の平均値を算出するためのコードはこちら
import numpy as np
def calculate_average_hue(hue_image: np.ndarray) -> float:
if (0 > np.min(hue_image)) or (np.max(hue_image) > 179):
raise ValueError("入力するHue画像は0~179を入力してください")
# OpencvのHueの範囲は0~180であるため、0~360に拡張
hue_image_0_to_360 = hue_image * 2
# X, Y方向にそれぞれベクトル化
hue_vec_x = np.cos(np.deg2rad(hue_image_0_to_360))
hue_vec_y = np.sin(np.deg2rad(hue_image_0_to_360))
# X, Y方向それぞれベクトルを平均化
hue_vec_x_ave = np.average(hue_vec_x)
hue_vec_y_ave = np.average(hue_vec_y)
# ベクトルのarctanを算出
average_hue_vec_arctan_deg = np.rad2deg(
np.arctan2(hue_vec_y_ave, hue_vec_x_ave))
# np.arctan2の出力範囲は-180deg~180degであるため、0~360degに変換
if average_hue_vec_arctan_deg < 0:
average_hue_vec_arctan_deg += 360
# OpencvのHueの範囲である0~180に合わせる
average_hue_vec_arctan2_deg_0_to_180 = average_hue_vec_arctan_deg / 2.
return average_hue_vec_arctan2_deg_0_to_180
【設計】色相(Hue)の平均値を算出する考え方
色相を「環」として考える
色相(Hue)は下記のように「色相環」として捉えることができる。
この性質を活用し、色相の平均値算出を行う。
参照元:Thresholding Operations using inRange
色相をベクトル演算として解く
色相をベクトルとして定義することで、色相環上の計算として求めることができるようになる。
OpenCVのHueの範囲に注意する
OpenCVのHue成分は、0~179の範囲で表現されう。
そのため、実装上は0~360 [deg] の範囲で演算するように前処理・後処理を行う必要がある。
Note For HSV, Hue range is [0,179], Saturation range is [0,255] and Value range is [0,255]. Different softwares use different scales. So if you are comparing OpenCV values with them, you need to normalize these ranges.
Changing Colorspaces
【実装】色相(Hue)の平均値をPythonで算出する
下記コードによって、色相の平均値を算出することができる
import numpy as np
def calculate_average_hue(hue_image: np.ndarray) -> float:
if (0 > np.min(hue_image)) or (np.max(hue_image) > 179):
raise ValueError("入力するHue画像は0~179を入力してください")
# OpencvのHueの範囲は0~180であるため、0~360に拡張
hue_image_0_to_360 = hue_image * 2
# X, Y方向にそれぞれベクトル化
hue_vec_x = np.cos(np.deg2rad(hue_image_0_to_360))
hue_vec_y = np.sin(np.deg2rad(hue_image_0_to_360))
# X, Y方向それぞれベクトルを平均化
hue_vec_x_ave = np.average(hue_vec_x)
hue_vec_y_ave = np.average(hue_vec_y)
# ベクトルのarctanを算出
average_hue_vec_arctan_deg = np.rad2deg(
np.arctan2(hue_vec_y_ave, hue_vec_x_ave))
# np.arctan2の出力範囲は-180deg~180degであるため、0~360degに変換
if average_hue_vec_arctan_deg < 0:
average_hue_vec_arctan_deg += 360
# OpencvのHueの範囲である0~180に合わせる
average_hue_vec_arctan2_deg_0_to_180 = average_hue_vec_arctan_deg / 2.
return average_hue_vec_arctan2_deg_0_to_180
動作確認
単純な「1x2」の配列での動作確認
>>> print("[0, 0]の色相平均:", calculate_average_hue(np.array([0, 0])))
[0, 30]の色相平均: 0.0
>>> print("[0, 30]の色相平均:", calculate_average_hue(np.array([0, 30])))
[0, 30]の色相平均: 14.999999999999998
>>> print("[0, 90]の色相平均:", calculate_average_hue(np.array([0, 90])))
[0, 90]の色相平均: 45.0
>>> print("[0, 91]の色相平均:", calculate_average_hue(np.array([0, 91])))
[0, 91]の色相平均: 135.49999999999997
>>> print("[0, 100]の色相平均:", calculate_average_hue(np.array([0, 100])))
[0, 100]の色相平均: 140.0
>>> print("[0, 170]の色相平均:", calculate_average_hue(np.array([0, 170])))
[0, 170]の色相平均: 175.0
>>> print("[0, 179]の色相平均:", calculate_average_hue(np.array([0, 179])))
[0, 179]の色相平均: 179.5
>>> print("[90, 90]の色相平均:", calculate_average_hue(np.array([90, 90])))
[90, 90]の色相平均: 90.0
>>> print("[90, 91]の色相平均:", calculate_average_hue(np.array([90, 91])))
[90, 91]の色相平均: 90.5
>>> print("[90, 170]の色相平均:", calculate_average_hue(np.array([90, 170])))
[90, 170]の色相平均: 130.0
>>> print("[90, 179]の色相平均:", calculate_average_hue(np.array([90, 179])))
[90, 179]の色相平均: 134.50000000000003
>>> print("[90, 1]の色相平均:", calculate_average_hue(np.array([90, 1])))
[90, 1]の色相平均: 45.49999999999997
lennaでの動作確認
>>> import cv2
>>> # lennaの画像は`http://www.lenna.org/len_std.jpg`より取得
>>> bgr_image = cv2.imread("len_std.jpg", cv2.IMREAD_UNCHANGED)
>>> hue_image = cv2.split(cv2.cvtColor(bgr_image, cv2.COLOR_BGR2HSV))[0]
>>> calculate_average_hue(hue_image)
24.453125
本日紹介したコードのスニペットは以下に公開している