目次
概要
OpenCV の cv2.compareHist()
を使用してヒストグラムの類似度を計算する方法について解説します。
cv2.compareHist
retval = cv2.compareHist(H1, H2, method)
引数
名前 | 型 | デフォルト値 |
---|---|---|
H1 | ndarray | |
ヒストグラム | ||
H2 | ndarray | |
ヒストグラム | ||
method | HistCompMethods | |
ヒストグラムの比較手法 |
返り値
名前 | 説明 | ||
---|---|---|---|
retval | 計算結果 |
サンプルコード
以下の 2 枚のグレースケール画像のヒストグラム計算し、cv2.compareHist()
で比較します。
ヒストグラムを計算する
In [1]:
import cv2
import numpy as np
from IPython.display import Image, display
def imshow(img):
"""ndarray 配列をインラインで Notebook 上に表示する。"""
ret, encoded = cv2.imencode(".jpg", img)
display(Image(encoded))
2 枚の画像のヒストグラムをそれぞれ計算します。
In [2]:
import matplotlib.pyplot as plt
# 画像をグレースケール形式で読み込む。
img1 = cv2.imread("sample1.jpg", cv2.IMREAD_GRAYSCALE)
img2 = cv2.imread("sample2.jpg", cv2.IMREAD_GRAYSCALE)
def calc_hist(img):
# ヒストグラムを計算する。
hist = cv2.calcHist([img], channels=[0], mask=None, histSize=[256], ranges=[0, 256])
# ヒストグラムを正規化する。
hist = cv2.normalize(hist, hist, 0, 1, cv2.NORM_MINMAX)
# (bins_num, 1) -> (bins_num,)
hist = hist.squeeze(axis=-1)
return hist
hist1 = calc_hist(img1)
hist2 = calc_hist(img2)
ヒストグラムを描画する
In [3]:
def plot_hist(hist, title=None):
fig, ax = plt.subplots()
ax.set_xticks([0, 255])
ax.set_xlim([0, 255])
ax.set_xlabel("Pixel Value")
if title:
ax.set_title(title)
bins = np.linspace(0, 255, 256)
ax.plot(bins, hist, color="k")
plt.show()
# 描画する。
plot_hist(hist1, "sample1.jpg")
plot_hist(hist2, "sample2.jpg")
ヒストグラムを比較する
cv2.HISTCMP_CORREL
を指定した場合、計算される値はヒストグラムが近いほど $1$、遠いほど $-1$ に近くなります。
In [4]:
score = cv2.compareHist(hist1, hist2, cv2.HISTCMP_CORREL)
print(score)
-0.2177525030677163
cv2.compareHist を利用して類似画像を検索する
ディレクトリ内の大量の画像から cv2.compareHist()
を使って類似画像を探します。ヒストグラムの計算は、画像の色空間を BGR から HSV 色空間に変換し、Hue (色相) のヒストグラムの比較によって行います。これにより、色相が似た画像を探します。
- ディレクトリ内の画像を読み込む
- BGR から HSV 色空間に変換する
cv2.calcHist()
でヒストグラムを計算するcv2.compareHist()
でヒストグラムを比較する
In [5]:
from pathlib import Path
img_paths = sorted(str(x) for x in Path("/data/dataset/samples").glob("*.jpg"))
def calc_hue_hist(img):
# HSV 形式に変換する。
hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
# 成分ごとに分離する。
h, s, v = cv2.split(hsv)
# Hue 成分のヒストグラムを計算する。
hist = calc_hist(h)
return hist
# 各画像の Hue 成分のヒストグラムを計算する。
hists = []
for path in img_paths:
# 画像を読み込む。
img = cv2.imread(str(path))
# 画像の Hue 成分のヒストグラムを取得する
hist = calc_hue_hist(img)
hists.append(hist)
hists = np.array(hists)
検索対象のヒストグラムを人工的に作成します。Hue が $[50, 70]$ あたりは緑色を表すので、そこが盛り上がったヒストグラムにします。
In [6]:
# 検索対象のヒストグラムを作成する。
target_hist = np.zeros(256, dtype=np.float32)
target_hist[50:70] = 1
target_hist = cv2.normalize(target_hist, target_hist, 0, 1, cv2.NORM_MINMAX)
plot_hist(target_hist)
検索対象のヒストグラムと各画像のヒストグラムの類似度を計算します。cv2.HISTCMP_CORREL
を使用する場合、類似度が高いヒストグラムほど 1 に近い値になります。そのため、値が大きい順にソートし、類似度が高い上位 5 枚を抽出します。
In [7]:
# 各画像のヒストグラムとの類似度を計算する。
dists = []
for hist in hists:
dist = cv2.compareHist(hist, target_hist, cv2.HISTCMP_CORREL)
dists.append(dist)
dists = np.array(dists)
sorted_indices = dists.argsort()[::-1]
# 上位5枚を取り出す。
for i in sorted_indices[:5]:
img = cv2.imread(img_paths[i])
imshow(img)
plot_hist(hists[i])
ヒストグラムの類似度が高い画像を抽出した結果、緑色の画像が抽出されました。
コメント