Pythonで画像に含まれる色のヒストグラムを調べる方法

2020年3月21日

今日は画像に含まれる色のヒストグラムを調べるプログラムを紹介します。

画像の色ヒストグラム

カラー画像はRGBの3つの色チャンネルを持ちます。

画像にどのような色が多く含まれるかを調べることで、画像のおおまかな特徴を知ることができます。

つまり、色のヒストグラムを調べれば画像の特徴や傾向が分かります。

ヒストグラム(histogram)とは、画像の中で、ある画素値(0~255)の画素が出現する数(度数:frequency)を数えて横軸に画素値、縦軸に度数を示してグラフにしたものです。

色ヒストグラムを作成する方法はいくつかあります。

画像の読み込み方によって若干プログラムが違うので、順に紹介します。

OpenCVで読み込む場合

以下のリストを見てください。

import cv2
from matplotlib import pyplot as plt

img = cv2.imread('./test_photo/hana/001_800px.jpg')

img_1 = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)

colors = ("r", "g", "b")

for i, channel in enumerate(colors):
    histgram = cv2.calcHist([img_1], [i], None, [256], [0, 256])
    plt.plot(histgram, color = channel)
    plt.xlim([0, 256])
plt.show()

おなじみですが、cv2.imread() で読み込むと色順番が「BGR」になります。

そこで、cv2.COLOR_BGR2RGB の部分で色順番を「BGR」から「RGB」に変更します。

次に、colors = ( ”r ”, ” g ”, ” b ” ) として、各色チャンネルに対してヒストグラムを計算するようにします。

enumerate() で インデックスと各channel の要素を同時に取得します。

例えば、i が 0 のとき channel はr、i が 1 のときchannel はg、i が 2 のときchannel はbとなります。

ヒストグラムを計算するのは for ループの中です。

cv2.calcHist() で計算します。

カッコの中身の意味を順に説明します。

第一引数 [img_1] は入力画像。

第二引数 [ i ] は色チャンネルのインデックス。例:0、1、2

第三引数 None はマスクの有無。今は気にしなくていいです。

第四引数 [256] はヒストグラムの大きさ。

第五引数 [0, 256] は範囲。

各色チャンネル( ”r ”, ” g ”, ” b ” )が取りうる値は256個あります。

x軸は256個の取りうるチャンネル値を表し、y軸は特定のチャンネル値が画像中に現れた回数(度数)を表します。

プログラムを実行すると以下となります。

左が入力画像。右が各色(RGB)のヒストグラムです。

ちなみに、RGB=(0, 0, 0)なら黒、RGB=(255, 255, 255)なら白です。

赤と緑(RとG)が重なっている箇所が黄色に見える部分です。

緑の度数が多いことから画像は全体的に緑色の傾向にあることが分かります。

matplotlibで読み込む場合

先ほどのリストと似ていますが、ちょっと違います。

以下のリストを見てください。

import cv2
from matplotlib import pyplot as plt

img_1 = plt.imread('./test_photo/hana/001_800px.jpg')

colors = ("r", "g", "b")

for i, channel in enumerate(colors):
    histgram = cv2.calcHist([img_1], [i], None, [256], [0, 256])
    plt.plot(histgram, color = channel)
    plt.xlim([0, 256])
plt.show()

OpenCVで読み込んだ場合と違う箇所は、plt.imread() の部分です。

実は、cv2.imread() で読み込むと色順番が「BGR」になるため、最終的にplt.show() で表示させ場合は「RGB」の色順番にしておかないといけません。

このため、先ほどは cv2.COLOR_BGR2RGB の部分で色順番を「BGR」から「RGB」に変更していました。

でも、最初から plt.imread() で読み込んでおくと色順番は「RGB」で読み込まれます。

なので、プログラムを一行減らすことができます。

しかも読み込んだ画像の型は ndarray 型となっているため、NumPy 得意のスライシング等の画像処理もやり易くて一石二鳥なんです。

プログラムの実行結果は同じなので省略します。

画像の明るさを変えるとヒスグラムはどうなる

もちろん明るい画素値が増えるので、予想としては255に近い画素が増えると思います。

本当にそうなるか検証してみます。

先ほどと同じ入力画像で明るさだけ別な画像処理ソフトで変えたものを準備します。

この画像の色ヒストグラムを調べると以下になります。

全体的に画素値が255の方向に近づいていることが分かります。

明るさを変える前のヒストグラムと比較してみます。

左が明るさを変える前のヒストグラム。右が明るさを変えた後のヒストグラムです。

一目瞭然ですね。

このように画像のおおまかな特徴を理解するのにとても役に立ちます。

まとめ

  • 色のヒストグラムを調べれば画像のおおまかな特徴が分かる。
  • cv2.imread() で画像を読み込むと色順番は「BGR」
  • plt.imread() で画像を読み込むと色順番は「RGB」
  • 最終的にplt.imshow() で表示させることを考えると plt.imread() で読み込んだ方がよい。