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

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

# 使用環境
- Python 3.8.3
- OpenCV 4.4.0

# 今回使用する画像
今回は，いらすとやさんの「[AIに支配される人達のイラスト](https://www.irasutoya.com/2016/08/ai.html)」(`ai_pet_family.png`)をお借りします．
背景は元から透過されています．

<div align="center">
<img width=500px src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1092916/232474b5-bd53-6253-9da5-2aa27a5af7f7.png" title="ai_pet_family.png">
</div>


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

```python: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のヒストグラムを取得する方法](https://qiita.com/tatsuya11bbs/items/ff4a07d73e14385b6922)」を参考にヒストグラムを生成してみました．

```python
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()
```

生成されたヒストグラムは次の通りでした．
<div align="center">
<img width=500px src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1092916/5c19de74-a6f1-7e1e-6a02-334b3259cde7.png" title="rgba.png">
</div>
…あれ？真っ黒すぎない？😿

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

```python:read_rgb.py
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()
```

表示された画像は…
<div align="center">
<img width=500px src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1092916/5f12f8fb-5f6b-3360-e44b-b4b736c121c8.png" title="rgb.png">
</div>
やはり真っ黒でした…`calcHist`では透過部分もヒストグラムが生成されてしまうようです😣💧

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

```python
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`をそれぞれ得ることができます．
生成されたヒストグラムは次の通りです．
<div align="center">
<img width=500px src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1092916/00032615-4d6e-733b-91f4-346013aabb7d.png" title="histogram.png">
</div>
欲しかったヒストグラムを得ることができました！✌️
表色系を変更すれば，XYZやグレースケールのヒストグラムを生成することもできます🌱

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

# 参考サイト
- [OpenCVでRGBとHSVのヒストグラムを取得する方法](https://qiita.com/tatsuya11bbs/items/ff4a07d73e14385b6922)
- [NumPy配列の基礎](http://www.kamishima.net/mlmpyja/nbayes1/ndarray.html)