【OpenCV】透過画像も使えるヒストグラム生成関数を作ってみた!
はじめに
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
に変換しておきます(混乱しないかたはそのままで大丈夫です).
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.png', -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
画像として読み込むことができます.
import cv2
import matplotlib.pyplot as plt
img_bgr = cv2.imread('ai_pet_family.png')
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
画像なので0
でR
,1
でG
,2
でB
,3
でA
をそれぞれ得ることができます.
生成されたヒストグラムは次の通りです.
欲しかったヒストグラムを得ることができました!✌️
表色系を変更すれば,XYZやグレースケールのヒストグラムを生成することもできます🌱
まとめ
-
calcHist
を普通に使用するだけでは透過画像のヒストグラム生成はできない - 固定長
NumPy
配列とarray.item()
でヒストグラム生成関数は自作できる
Discussion