OpenCVを用いた画像の画素ごとの補色を用いた色変換【Python】

# 目次
[はじめに](#はじめに)
[実行環境](#実行環境)
[画像の画素値へのアクセス方法](#画像の画素値へのアクセス方法)
[補色の計算方法](#補色の計算方法)
[画像上に文字を出力する方法](#画像上に文字を出力する方法)
[複数の画像を横並びに表示する方法](#複数の画像を横並びに表示する方法)
[ソースコード](#ソースコード)
[結果](#結果)
[まとめ](#まとめ)

# はじめに
今回は、[前回のTwitter(X)のアンケート](https://twitter.com/0ca00118726208m/status/1692153768840986941)でトップだった<font color="red">**動画像処理**</font>についての記事です。
そのジャンルの中から、<font color="red"><ins>**オリジナル画像**</ins></font>・<font color="red"><ins>**画像の画素値ごとの補色を用いて変換した画像**</ins></font>・<font color="red"><ins>**ネガポジ反転した画像**</ins></font>**を並べて表示**させ、**<font color="red"><ins>ネガポジ反転</ins></font>と<font color="red"><ins>補色を用いた画像変換</ins></font>の<ins>画素値に関するヒストグラム</ins>を表示**させるプログラムを作成したのでそれについて紹介したいと思います。

--------

<details><summary>マイページについて</summary>

:::note info
主に<ins>**Pythonについての記事**</ins>を投稿してます!!
:::

[<font color="blue"><ins>**マイページはこちら**</ins></font>](https://qiita.com/blueman "マイページ")

</details>

<details><summary>X(Twitter)について</summary>

:::note info
<ins>**Qiitaの新着記事の通知**</ins>や<ins>**おススメの理工学書の紹介**</ins>をツイートしてます!!
:::
[<font color="blue"><ins>**X(Twitter)はこちら**</ins></font>](https://twitter.com/0ca00118726208m "X(Twitter)")

</details>

<details><summary>Zennについて</summary>

:::note info
主に<ins>**自分の書いたQiitaの記事の転載**</ins>と<ins>**Pythonを使っていて遭遇したエラーの対処法のような記事**</ins>を投稿していく予定です!!
:::
[<font color="blue"><ins>**Zennはこちら**</ins></font>](https://zenn.dev/blueman "Zenn")

</details>

# 実行環境
実行環境は次の通りです。

<details><summary>実行環境</summary>

- **環境**
    - windows10
    - Python 3.10.5

- **ライブラリ**
    - OpenCV 4.8.0
    - matplotlib 3.6.1

</details>

**処理内容**と**ライブラリ**の対応を分かりやすく表にすると次の通りです。
<details><summary>説明</summary>

| <font color="red">処理内容</font> | <font color="red">モジュール・ライブラリ</font> |
|:-:|:-:|
|**画像の読み込み<br>画像のネガポジ反転<br>画素値へのアクセス<br>画像への文字列の出力<br>画像の表示**|***OpenCV***|
|**ヒストグラムの表示**|***matplotlib***|

</details>

画像はこちらを使います。

<details><summary>使用した画像</summary>

![使用した画像](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3198998/1a3af0ac-143b-c46d-26dc-1666d2d11b1f.jpeg "使用した画像")

</details>

画像の引用元はこちらです

http://www.ess.ic.kanagawa-it.ac.jp/app_images_j.html

# 画像の画素値へのアクセス方法
[前回の記事](https://qiita.com/blueman/items/8b9b8a719d59c6f32979#%E7%94%BB%E5%83%8F%E3%81%AE%E7%94%BB%E7%B4%A0%E5%80%A4%E3%81%B8%E3%81%AE%E3%82%A2%E3%82%AF%E3%82%BB%E3%82%B9%E6%96%B9%E6%B3%95)を参考にして画像の画素値へアクセスしました。

# 補色の計算方法
こちらのソース

https://appakumaturi.hatenablog.com/entry/20120121/1327143125

によると、
> **補色はRGB値の中での<font color="red"><ins>最大値</ins></font>と<font color="red"><ins>最小値</ins></font>を足し合わせ<font color="red"><ins>その値をRGBのそれぞれの値から引くことで補色のRGB値が算出</ins></font>される**

そうです。
この方法を用いて、補色を計算しました。

:::note

この方法だと **`OpenCV`のRGB値の範囲**が<ins>**0~255**</ins>であり<font color="red">**整数型(int)に型変換しないと和が255以上の値だと255を超えると0に初期化**</font>されてしまいます。
この問題を解決するために、<font color="red">**最大値と最小値を整数型(`int`)に型変換して和を出しました。**</font>

:::

# 画像上に文字を出力する方法
こちらのソース

https://shikaku-mafia.com/opencv-puttext/

を見ると、
> **`cv2.putText`を用いることで画像上に文字を出力できる**

そうです。

この方法を使って画像上に文字を出力しました。

# 複数の画像を横並びに表示する方法
こちらのソース

https://note.nkmk.me/python-opencv-hconcat-vconcat-np-tile/

を見ると、

>**`cv2.hconcat`を用いることで複数の画像を横並びに表示できる**

そうです。

この方法を使って複数の画像を横並びに表示させました。

# ソースコード
下にソースコードを示します。おそらく[実行環境](#実行環境)で示した環境では動くはず。

<details><summary>ソースコード</summary>

```python:color_trans(comp).py
import cv2                                               #OpenCVをインポート
import matplotlib.pyplot as plt                          #matplotlibをpltという名前でインポート

img_original = cv2.imread('Airplane.jpg')                #画像を読み込み(元の画像表示用)
img_trans = cv2.imread('Airplane.jpg')                   #画像を読み込み(画像変換用)
img_BitwiseNot = cv2.bitwise_not(img_original)           #画像をネガポジ反転

img_trans_blue_val = []                                  #変換後の青(blue)の画素値を格納するためのリストの宣言
img_trans_green_val = []                                 #変換後の緑(green)の画素値を格納するためのリストの宣言
img_trans_red_val = []                                   #変換後の赤(red)の画素値を格納するためのリストの宣言

img_BitwiseNot_blue_val = []                             #ネガポジ反転後の青(blue)の画素値を格納するためのリストの宣言
img_BitwiseNot_green_val = []                            #ネガポジ反転後の緑(green)の画素値を格納するためのリストの宣言
img_BitwiseNot_red_val = []                              #ネガポジ反転後の赤(red)の画素値を格納するためのリストの宣言

for i in range(img_BitwiseNot.shape[0]):                        #画像のy座標iを画像の高さの分img_BitwiseNotの配列の第１引数に入力
	for j in range(img_BitwiseNot.shape[1]):                    #画像のx座標jを画像の幅の分img_BitwiseNotの配列の第2引数に入力
		img_BitwiseNot_blue_val.append(img_BitwiseNot[i,j,0])   #ネガポジ反転後の青(blue)の画素値をimg_BitwiseNot_blue_valというリストに追加
		img_BitwiseNot_green_val.append(img_BitwiseNot[i,j,1])  #ネガポジ反転後の緑(green)の画素値をimg_BitwiseNot_green_valというリストに追加
		img_BitwiseNot_red_val.append(img_BitwiseNot[i,j,2])    #ネガポジ反転後の赤(red)の画素値をimg_BitwiseNot_red_valというリストに追加

for i in range(img_trans.shape[0]):                      #画像のy座標iを画像の高さの分img_transの配列の第１引数に入力
	for j in range(img_trans.shape[1]):                  #画像のx座標jを画像の幅の分img_transの配列の第２引数に入力
		Max = int(max(img_trans[i,j]))                   #BGRのリストの最大値の取得(型変換をしないと255まで行くと0になってしまう)
		Min = int(min(img_trans[i,j]))                   #BGRのリストの最小値の取得(型変換をしないと255まで行くと0になってしまう)
		total = Max+Min                                  #BGR値のリストの最大値とBGR値の最小値の和の計算
		img_trans[i,j,0] = total-img_trans[i,j,0]        #和から青(B)の値を引いてその値を新しい青(B)の値にする
		img_trans[i,j,1] = total-img_trans[i,j,1]        #和から緑(G)の値を引いてその値を新しい緑(G)の値にする
		img_trans[i,j,2] = total-img_trans[i,j,2]        #和から赤(R)の値を引いてその値を新しい赤(R)の値にする
		
		img_trans_blue_val.append(img_trans[i,j,0])      #新しい青(B)の値をimg_trans_blue_valというリストに追加
		img_trans_green_val.append(img_trans[i,j,1])     #新しい緑(G)の値をimg_trans_green_valというリストに追加
		img_trans_red_val.append(img_trans[i,j,2])       #新しい赤(R)の値をimg_trans_red_valというリストに追加

#img_originalの画像に「Original」という文字を描画
img_original = cv2.putText(img_original,text='Original',org=(0,25),fontFace=cv2.FONT_HERSHEY_SIMPLEX,fontScale=1.0,color=(0,0,0),thickness=2,lineType=cv2.LINE_AA)
#img_transの画像に「Trans」という文字を描画
img_trans = cv2.putText(img_trans,text='Trans',org=(0,25),fontFace=cv2.FONT_HERSHEY_SIMPLEX,fontScale=1.0,color=(0,0,0),thickness=2,lineType=cv2.LINE_AA)
#img_BitwiseNotの画像に「Bitwise Not」という文字を描画
img_BitwiseNot = cv2.putText(img_BitwiseNot,text='Bitwise Not',org=(0,25),fontFace=cv2.FONT_HERSHEY_SIMPLEX,fontScale=1.0,color=(255,255,255),thickness=2,lineType=cv2.LINE_AA)

img_result = cv2.hconcat([img_original,img_trans,img_BitwiseNot])       #元の画像と変換後の画像を横に並べる
cv2.imshow("result",img_result)                                         #横に並べた画像を表示
cv2.waitKey()                                                           #window closeボタンが押されるまで待機

plt.hist(img_trans_blue_val,ec='black',label="Trans")                        #変換後の画素値に関するヒストグラムの設定(青(blue)について)
plt.hist(img_BitwiseNot_blue_val,ec='black',alpha=0.5,label="Bitwise Not")   #ネガポジ反転の画素値に関するヒストグラムの設定(青(blue)について)
plt.title('Blue')                                                            #タイトルの表示
plt.legend(loc='center',bbox_to_anchor=(0.8,1.08))                           #凡例の表示(グラフ外)
plt.show()                                                                   #ヒストグラムの表示

plt.hist(img_trans_green_val,ec='black',label="Trans")                       #変換後の画素値に関するヒストグラムの設定(緑(green)について)
plt.hist(img_BitwiseNot_green_val,ec='black',alpha=0.5,label="Bitwise Not")  #ネガポジ反転の画素値に関するヒストグラムの設定(緑(green)について)
plt.title('Green')                                                           #タイトルの表示
plt.legend(loc='center',bbox_to_anchor=(0.8,1.08))                           #凡例の表示(グラフ外)
plt.show()                                                                   #ヒストグラムの表示

plt.hist(img_trans_red_val,ec='black',label="Trans")                         #変換後の画素値に関するヒストグラムの設定(赤(red)について)
plt.hist(img_BitwiseNot_red_val,ec='black',alpha=0.5,label="Bitwise Not")    #ネガポジ反転の画素値に関するヒストグラムの設定(赤(red)について)
plt.title('Red')                                                             #タイトルの表示
plt.legend(loc='center',bbox_to_anchor=(0.8,1.08))                           #凡例の表示(グラフ外)
plt.show()                                                                   #ヒストグラムを表示
```

</details>

# 結果
まず、<ins>出力画像</ins>を示します。
<details><summary>出力画像</summary>

![result.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3198998/b492e743-6a3c-0aa1-db07-15e6ca2010fd.jpeg "出力画像")

:::note
Bitwise Notの文字は**文字を見えやすくするために白色に**しました。
:::

</details>

次に、<ins>ヒストグラム</ins>を示します。
<details><summary>青(B)のヒストグラム</summary>

![comp_hist_blue.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3198998/60c1dccf-10bc-0962-c9b8-e241ad80d82c.png "青(B)のヒストグラム")

</details>

<details><summary>緑(G)のヒストグラム</summary>

![comp_hist_green.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3198998/bd2e924a-3bcd-7163-9ce5-f1184d8993dc.png "緑(G)のヒストグラム")

</details>

<details><summary>赤(R)のヒストグラム</summary>

![comp_hist_red.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3198998/43d472a0-81ac-83f6-16fe-1c78477840ad.png "赤(R)のヒストグラム")

</details>

:::note
- <font color="red">**`Trans`**</font>が<font color="red">**補色による色変換**</font>のヒストグラム
- <font color="red">**`Bitwise Not`**</font>が<font color="red">**ネガポジ反転**</font>のヒストグラム

です。
:::

# 特徴
特徴はこんな感じです。
<details><summary>画像について</summary>

画像を見ると、
- <font color="red"><ins>**オリジナル画像で**</ins><font color="black">**赤系統の色**</font></font>だと<font color="red"><ins>**補色による色変換とネガポジ反転での色変換で**</ins><font color="black">**同系色**</font>

になるみたいです。

</details>

<details><summary>ヒストグラムについて</summary>

ヒストグラムを見ると、
- **赤(R)のヒストグラム**では<font color="red">**`Trans`<ins>の最頻値の頻度**</ins></font>の方が<font color="red">**`Bitwise Not`<ins>の最頻値の頻度**</ins></font>よりも多くなっている
- **すべてのヒストグラム**で<font color="red">**`Trans`<ins>の最頻値**</font>の方が<font color="red">**`Bitwise Not`<ins>の最頻値**</ins></font>よりも大きくなっている

ことが分かります。

</details>

# まとめ

今回は、[**前回のTwitter(X)のアンケート**](https://twitter.com/0ca00118726208m/status/1692153768840986941)でトップだった<font color="red"><ins>**動画像処理**</ins></font>から<font color="red"><ins>**画像の画素ごとの**</ins><font color="black">**補色**</font><ins>**で色変換を行いその結果と**</ins>[**ネガポジ反転**](https://pystyle.info/opencv-tone-transform/#outline__4)<ins>**から得られた結果とオリジナル画像を並べて表示させ補色による色変換とネガポジ反転の**</ins><font color="black">**画素値によるヒストグラム**</font><ins>**を表示させる**</ins></font>プログラムを作成しました。
この記事が実際に役に立つかどうかは分かりませんが、誰かの役に立ってくれると嬉しいです。
記事を執筆する余力があれば、<ins>次回も記事を投稿する予定</ins>です。
次回の予定としては、[**今回のTwitter(X)のアンケート**](https://twitter.com/0ca00118726208m/status/1695006241549058293)でトップだった<font color="red"><ins>**動画像処理**</ins></font>から<ins>**webカメラから得られた映像に**</ins>**`γ変換`**<ins>**をかけて明るくした<font color="red">リアルタイム</font>の動画を**</ins>**`Tkinter`**<ins>**を用いて表示させ**</ins>**`γ`**<ins>**の値を**</ins>**`Tkinter`**<ins>**の**</ins>**`tkinter.Scale`**<ins>**で調整させる**</ins>プログラムを作成できたのでそれに関する記事を投稿予定です!!