【OpenCV】透過画像も使えるヒストグラム生成関数を作ってみた!

公開:2020/12/16
更新:2020/12/30
4 min読了の目安(約3700字TECH技術記事

はじめに

OpenCVを用いて透過画像のヒストグラムを生成しようと思ったときに詰まってしまったのですが,どれだけ調べても記事を見つけることができなかったため,自分で書いてみることにしました!💐
Python自体初心者なのでご指摘があれば,気軽にコメントをしていただけると嬉しいです🔰

使用環境

  • Python 3.8.3
  • OpenCV 4.4.0

今回使用する画像

今回は,いらすとやさんの「AIに支配される人達のイラスト」をお借りします.
背景は元から透過されています.

ai_pet_family.png

透過画像を読み込む

  • 透過されているpng画像をOpenCVでアルファチャンネル込みで読み込むには,cv2.imread()の第2引数にcv2.IMREAD_UNCHANGEDまたは-1を指定します.
  • ここで読み込まれた画像はBGRAなので,混乱しないためにRGBAに変換しておきます(混乱しないかたはそのままで大丈夫です).
read_rgba.py
import cv2

img_bgra = cv2.imread('ai_pet_family.png', -1)
img_rgba = cv2.cvtColor(img_bgra, cv2.COLOR_BGRA2RGBA)

はじめに起きた失敗

OpenCVにはcalcHistという便利そうなものがある…!ということで @tatsuya11bbs さんの「OpenCVでRGBとHSVのヒストグラムを取得する方法」を参考にヒストグラムを生成してみました.

import cv2
import matplotlib.pyplot as plt

img_bgra = cv2.imread('ai_pet_family', -1)
img_rgba = cv2.cvtColor(img_bgra, cv2.COLOR_BGRA2RGBA)

img_r, img_g, img_b = img_rgba[:,:,0], img_rgba[:,:,1], img_rgba[:,:,2]

r = cv2.calcHist([img_r], [0], None, [256], [0, 256])
g = cv2.calcHist([img_g], [0], None, [256], [0, 256])
b = cv2.calcHist([img_b], [0], None, [256], [0, 256])

plt.plot(r, color = 'red', label = 'red')
plt.plot(g, color = 'green', label = 'green')
plt.plot(b, color = 'blue', label = 'blue')
plt.legend()
plt.show()

生成されたヒストグラムは次の通りでした.

…あれ?真っ黒すぎない?😿

RGB画像として保存してみる

もしかして透過部分もヒストグラムが生成されているのでは…?と思い,念のためアルファチャンネルを含まないRGB画像として読み込み,表示してみます.先ほどのread_rgba.pyからcv2.imread()の第2引数を取り除くだけでBGR画像として読み込むことができます.

read_rgb.py
import cv2

img_bgr = cv2.imread('ai_pet_family')
img_rgb = cv2.cvtColor(img_bgr, cv2.COLOR_BGR2RGB)

plt.imshow(img_rgb)
plt.show()

表示された画像は…

やはり真っ黒でした…calcHistでは透過部分もヒストグラムが生成されてしまうようです😣💧

解決策

やっとここからが本題です!わたしの検索力ではcalcHistを用いて透過部分を除いたヒストグラムを生成する方法に辿りつくことはできませんでした…🥺
今回は固定長NumPy配列とarray.item()を用いた,自作ヒストグラム生成関数をご紹介します!🌷

import cv2
import numpy as np
import matplotlib.pyplot as plt

# read_rgba.pyと同様に画像をアルファチャンネル込みのRGBA画像として読み込む
img_bgra = cv2.imread('ai_pet_family.png', -1)
img_rgba = cv2.cvtColor(img_bgra, cv2.COLOR_BGRA2RGBA)

# グラフ生成時にx軸として使用する長さ256のNumPy配列をcnt[i] = iで初期化する
cnt = np.arange(256)

# RGBの各色の値をカウントする長さ256のNumPy配列を0で初期化する
cnt_red = np.zeros(256)
cnt_green = np.zeros(256)
cnt_blue = np.zeros(256)

# 画像の縦横を得る
height = img_rgba.shape[0]
width = img_rgba.shape[1]

for i in range(height):
    for j in range(width):
	# アルファチャンネルが0(透明)であればスキップする
        if img_rgba.item(i, j, 3) == 0:
            continue
        else:
	    # 不透明であればRGBの各色の値をカウントしていく
            cnt_red[img_rgba.item(i, j, 0)] += 1
            cnt_blue[img_rgba.item(i, j, 1)] += 1
            cnt_green[img_rgba.item(i, j, 2)] += 1

plt.plot(cnt, cnt_red, color = 'red', label = 'red')
plt.plot(cnt, cnt_green, color = 'green', label = 'green')
plt.plot(cnt, cnt_blue, color = 'blue', label = 'blue')
plt.legend()
plt.show()

array.item()では,第1引数に画素の行,第2引数に画素の列,第3引数に色成分の値の配列を指定します.今回はRGBA画像なので0R1G2B3Aをそれぞれ得ることができます.
生成されたヒストグラムは次の通りです.

欲しかったヒストグラムを得ることができました!✌️
表色系を変更すれば,XYZやグレースケールのヒストグラムを生成することもできます🌱

まとめ

  • calcHistを普通に使用するだけでは透過画像のヒストグラム生成はできない
  • 固定長NumPy配列とarray.item()でヒストグラム生成関数は自作できる

参考サイト