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

OpenCV で画像のヒストグラムを作成する方法

OpenCV で画像のヒストグラムを作成する方法

概要

画像の画素値のヒストグラムを作成することで、その画像の特性を理解し、2値化などの処理に役立てることができます。
この記事では、OpenCV の calcHist() で画像のヒストグラムを作成する方法を紹介します。

Advertisement

画像のヒストグラム

画像のヒストグラムとは、横軸に画素値、縦軸に頻度をとったヒストグラムです。 カラー画像の場合はチャンネルごとにヒストグラムを作成します。

cv2.calcHist()

OpenCV の calcHist() を使用すると、画像から指定したチャンネルのヒストグラムを計算できます。

hist = cv2.calcHist(images, channels, mask, histSize, ranges[, hist[, accumulate]])
  • 引数
    • images: 画像の一覧。
    • channels: ヒストグラムを計算するチャンネルの一覧。
    • mask: マスクの一覧。
    • histSize: 各チャンネルのビン数
    • ranges: 各チャンネルのヒストグラムの範囲。この範囲外の画素値は集計対象外となる。
  • 返り値
    • hist: ヒストグラム

channelshistSizeranges の指定方法は少々複雑なので、詳しく見ていきます。

1次元ヒストグラム

1次元ヒストグラムの場合、以下のように指定します。

hist = cv2.calcHist([img], [ch], None, histSize=[bins], ranges=[l, u])
  • ch は1次元ヒストグラムを計算するチャンネルを指定します。
  • bins はビンの数を指定します。
  • l, u はヒストグラムを作成する範囲 $[l, u)$ を指定します。

例えば、bins=[15], ranges=[0, 256] とした場合、$[0, 256]$ を15等分したビンが作成されます。

sample.jpg

Advertisement

グレースケール画像のヒストグラム

グレースケール画像のヒストグラムする場合、channels=[0], histSize=[ビンの数], ranges=[下限, 上限] と指定します。

In [1]:
import cv2
import matplotlib.pyplot as plt
import numpy as np


# 画像をグレースケール形式で読み込む。
img = cv2.imread("sample.jpg", cv2.IMREAD_GRAYSCALE)

# 1次元ヒストグラムを作成する。
n_bins = 100  # ビンの数
hist_range = [0, 256]  # 集計範囲

hist = cv2.calcHist(
    [img], channels=[0], mask=None, histSize=[n_bins], ranges=hist_range
)
hist = hist.squeeze(axis=-1)  # (n_bins, 1) -> (n_bins,)

# 描画する。
def plot_hist(bins, hist, color):
    centers = (bins[:-1] + bins[1:]) / 2
    widths = np.diff(bins)
    ax.bar(centers, hist, width=widths, color=color)


bins = np.linspace(*hist_range, n_bins + 1)

fig, ax = plt.subplots()
ax.set_xticks([0, 256])
ax.set_xlim([0, 256])
ax.set_xlabel("Pixel Value")
plot_hist(bins, hist, color="k")
plt.show()

カラー画像のヒストグラム

カラー画像のヒストグラムする場合、channels=[チャンネル], histSize=[ビンの数], ranges=[下限, 上限] と指定します。
例えば、BGR 画像の場合、0が blue、1が greeen、2が red になります。

In [2]:
import cv2
import matplotlib.pyplot as plt
import numpy as np


# 画像を読み込む。
img = cv2.imread("sample.jpg")

# ヒストグラムを作成する。
n_bins = 256  # ビンの数
hist_range = [0, 256]  # 集計範囲

hists = []
channels = {0: "blue", 1: "green", 2: "red"}
for ch in channels:
    hist = cv2.calcHist(
        [img], channels=[ch], mask=None, histSize=[n_bins], ranges=hist_range
    )
    hist = hist.squeeze(axis=-1)
    hists.append(hist)


# 描画する。
def plot_hist(bins, hist, color):
    centers = (bins[:-1] + bins[1:]) / 2
    widths = np.diff(bins)
    ax.bar(centers, hist, width=widths, color=color)


bins = np.linspace(*hist_range, n_bins + 1)

fig, ax = plt.subplots()
ax.set_xticks([0, 256])
ax.set_xlim([0, 256])
ax.set_xlabel("Pixel Value")
for hist, color in zip(hists, channels.values()):
    plot_hist(bins, hist, color=color)
plt.show()

2次元ヒストグラム

2次元ヒストグラムの場合、以下のように指定します。

hist = cv2.calcHist(
    [img], [ch1, ch2], None, histSize=[bins1, bins2], ranges=[l1, u1, l2, u2])
  • ch1, ch2 は1次元ヒストグラムを計算するチャンネルを指定します。
  • bins1 はチャンネル ch1 のビンの数、bins2 はチャンネル ch2 のビンの数を指定します。
  • l1, u1 はチャンネル ch1 のヒストグラムを作成する範囲 $l1, u1$、l2, u2 はチャンネル ch2 のヒストグラムを作成する範囲 $[l2, u2)$ を指定します。

例えば、bins=[15, 17], ranges=[0, 256, 0, 256] とした場合、$x$ 軸は [0, 256] を15等分、$y$ 軸は [0, 256] を17等分したビンが作成されます。

Advertisement

HSV 画像の2次元ヒストグラムを作成する

In [3]:
import cv2
import matplotlib.pyplot as plt
import numpy as np


# 画像を読み込む。
img = cv2.imread("sample.jpg")

# HSV に変換する。
hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)

# 2次元ヒストグラムを作成する。
hist_range1 = [0, 256]
hist_range2 = [0, 256]
n_bins1, n_bins2 = 20, 20

hists = []
channel_pairs = [[0, 1], [1, 2], [0, 2]]
for pair in channel_pairs:
    hist = cv2.calcHist(
        [hsv], channels=pair, mask=None, histSize=[20, 20], ranges=[0, 256, 0, 256]
    )
    hists.append(hist)

# 描画する。
ch_names = {0: "Hue", 1: "Saturation", 2: "Brightness"}


fig = plt.figure(figsize=(10, 10 / 3))
for i, (hist, ch) in enumerate(zip(hists, channels), 1):
    xlabel, ylabel = ch_names[pair[0]], ch_names[pair[1]]

    ax = fig.add_subplot(1, 3, i)
    fig.subplots_adjust(wspace=0.3)
    ax.imshow(hist, cmap="jet")

    # 2Dヒストグラムを描画する。
    ax.set_title(f"{xlabel} and {ylabel}")
    ax.set_xlabel(xlabel)
    ax.set_ylabel(ylabel)

plt.show()

参考文献