Warning: Undefined variable $position in /home/pystyles/pystyle.info/public_html/wp/wp-content/themes/lionblog/functions.php on line 4897

OpenCV – cv2.compareHist で画像のヒストグラムを比較する方法

OpenCV – cv2.compareHist で画像のヒストグラムを比較する方法

概要

OpenCV の cv2.compareHist を使用してヒストグラムの類似度を計算する方法について解説します。

Advertisement

cv2.compareHist

retval = cv2.compareHist(H1, H2, method)
引数
名前 デフォルト値
H1 ndarray
ヒストグラム
H2 ndarray
ヒストグラム
method HistCompMethods
ヒストグラムの比較手法
返り値
名前 説明
retval 計算結果

サンプルコード

以下の2枚のグレースケール画像のヒストグラム計算し、cv2.compareHist() で計算します。

sample1.jpg

sample2.jpg

ヒストグラムを計算する

In [1]:
import cv2
import numpy as np
from IPython.display import Image, display
from matplotlib import pyplot as plt

def imshow(img):
    """ndarray 配列をインラインで Notebook 上に表示する。
    """
    # リサイズする。
    h, w = img.shape[:2]
    height = round(h * (300 / w))
    img = cv2.resize(img, dsize=(300, height))
    # エンコードする。
    ret, encoded = cv2.imencode(".jpg", img)
    display(Image(encoded))


# 画像をグレースケール形式で読み込む。
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, 255, cv2.NORM_MINMAX)
    # (n_bins, 1) -> (n_bins,)
    hist = hist.squeeze(axis=-1)

    return hist


hist1 = calc_hist(img1)
hist2 = calc_hist(img2)
Advertisement

ヒストグラムを描画する

In [2]:
def plot_hist(hist, title=None):
    fig, ax = plt.subplots()
    if title:
        ax.set_title(title)
    ax.set_xticks([0, 255])
    ax.set_xlim([0, 255])
    ax.set_xlabel("Pixel Value")

    bins = np.arange(257)
    centers = (bins[:-1] + bins[1:]) / 2
    widths = np.diff(bins)
    ax.bar(centers, hist, width=widths, color="k")
    plt.show()


# 描画する。
plot_hist(hist1, "sample1.jpg")
plot_hist(hist2, "sample2.jpg")

ヒストグラムを比較する。

cv2.HISTCMP_CORREL を指定した場合、計算される値はヒストグラムが近いほど $1$、遠いほど $-1$ に近くなります。

In [3]:
score = cv2.compareHist(hist1, hist2, cv2.HISTCMP_CORREL)
print(score)
-0.2177525026462249

cv2.compareHist を利用して類似画像を検索する

ディレクトリ内の5000枚の画像から cv2.compareHist() を使って類似画像を探してみます。 ヒストグラムの計算は、画像の色空間を BGR から HSV 色空間に変換し、Hue (色相) のヒストグラムの比較により、ヒストグラムが似た画像を探します。

  1. ディレクトリ内の画像を読み込む
  2. BGR から HSV 色空間に変換する。
  3. cv2.calcHist() でヒストグラムを比較する。
In [4]:
from pathlib import Path

img_paths = sorted(str(x) for x in Path("/data/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]$ あたりは緑色を表すので、そこが盛り上がったヒストグラムにします。

Hue

In [5]:
# 検索対象のヒストグラムを作成する。
target_hist = np.zeros(256, dtype=np.float32)
target_hist[50:70] = 256

target_hist = cv2.normalize(target_hist, target_hist, 0, 255, cv2.NORM_MINMAX)
plot_hist(target_hist)

検索対象のヒストグラムと各画像のヒストグラムの類似度を計算します。 cv2.HISTCMP_CORREL の場合、類似度が高いヒストグラムほど1に近い値になるので、値が大きい順にソートし、類似度が高い上位5枚を抽出します。

In [6]:
# 各画像のヒストグラムとの類似度を計算する。
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])

ヒストグラムの類似度が高い画像を抽出した結果、緑色の画像が抽出されました。