OpenCV2で佐々木希の顔パーツの面積比率（%）を計算した

###やったこと

__[前回の記事](https://qiita.com/electronics_diy721/items/7201a2166668a55a9fff)__の続きです。

__（ 前回の記事 ）__

https://qiita.com/electronics_diy721/items/7201a2166668a55a9fff

今回は、画面を構成する各パーツが、画像ファイル全体の中で何パーセントの広さ（面積）を占める領域なのかを、算出しました。

例として、この記事では、被写体の人物（佐々木希）の顔の各部位が、画像全体の中でどれくらいの広さ（面積）を占めて映っているのかを算出しました。

より実用的な使い道としては、テレビ番組のあるコマの中で、特定の芸人が画面の面積の何%を占めて（ズームアップされている。遠目に引いている、など）いるかを定量的に計測することで、その芸人が、その番組の中でどれくらいの扱いを受けているのか（どれくらい露出しているか）を推し量る、などの調査用途が考えられる。

ある画像や動画の特定シーンにおけるある人物の扱いの大きさ（小ささ）を可視化するのに、役立つのではないだろうか。

あとは、今回行ったように、「画面における、顔全体（顔のすべての検出部位）の合計面積比率」を分母にとり、分子に、「画面に占める瞳の面積比率」をとって割り算することで、「顔全体に占める瞳の大きさの割合い」を知ることもできる。「あの人、目デカっ」を定量的に裏付ける資料など。。。小顔の芸能人の顔の大きさを、隣に立つ別の芸人の顔の大きさと比べることも、（カメラと２人の被写体との距離が等しいならば）可能なはずである。

誰かが撮影した写真のデジタル画像を解析するのではなく、ドローンやロボットに搭載されたカメラから撮影した画像を分析するのであれば、機体に距離測距レーザーを設置しておけば、各被写体とカメラとの距離を記録に残すことができるため、撮影した画像内の被写体の大きさの情報をもとに、画面内の各部位（パーツ）の実寸サイズ（センチとかメートルとか）を計算することもできそうだ。

###今回、得られた結果

- All_detected_parts_black_and_white_nozomi_1.jpg.png

```bash:Terminal
% open All_detected_parts_black_and_white_nozomi_1.jpg.png
```

![](https://user-images.githubusercontent.com/87643752/129410857-54d681ce-d36d-4c0d-ab2e-6880cbea4159.png)

- All_detected_parts_nozomi_1.jpg.png

```bash:Terminal
% open All_detected_parts_nozomi_1.jpg.png
```

![](https://user-images.githubusercontent.com/87643752/129410872-b8b94091-acd2-468e-9895-e271843105af.png)

- result_black_and_white_nozomi_1.jpg275.png

```bash:Terminal
% open result_black_and_white_nozomi_1.jpg275.png
```

![](https://user-images.githubusercontent.com/87643752/129412315-91c52eae-027d-4238-883b-40429532d5c6.png)

- result_nozomi_1.jpg275.png

```bash:Terminal
% open result_nozomi_1.jpg275.png
```

![](https://user-images.githubusercontent.com/87643752/129412395-2ad43915-3bbc-481e-8aae-289e087fab8a.png)

- result_black_and_white_nozomi_1.jpg173.png

![](https://user-images.githubusercontent.com/87643752/129412335-71f28756-58ce-4ce1-b0eb-0d1853ce24df.png)

- result_nozomi_1.jpg173.png

![](https://user-images.githubusercontent.com/87643752/129412415-26443514-3e90-4030-8348-d7889eb6ceba.png)

- result_black_and_white_nozomi_1.jpg344.png

![](https://user-images.githubusercontent.com/87643752/129412359-70484c15-bbb1-40c4-b2d5-34365dd55a20.png)

- result_nozomi_1.jpg344.png

![](https://user-images.githubusercontent.com/87643752/129412437-cb97ad72-2d1c-417e-bfa6-af02e8a6e3c7.png)

###参考にしたサイト

- [OpenCV 入門 (11) - 輪郭抽出](https://note.com/npaka/n/naae77d6af87d)

> __12. 輪郭によるマスク生成__
>
>輪郭によるマスク生成を行うには、黒画像を用意し、輪郭をthickness=-1のdrawContours()で白を描画します。
>例の輪郭でマスク生成すると、次のようになります。
>
>```Python3:
># 輪郭によるマスク生成
>contour_mask = np.zeros_like(img_gray)
>cv2.drawContours(contour_mask, contours, -1, color=255, thickness=-1)
>```
>
>__13. 白黒割合の確認__
>
>白黒割合を確認するには、countNonZero()を使います。
>例の輪郭でマスクを生成した後、白黒割合を確認すると、次のようになります。
>
>```Python3:
># 白黒割合の確認
>image_size = contour_mask.size
>white_pixels = cv2.countNonZero(contour_mask)
>black_pixels = contour_mask.size-white_pixels
>print('white :', int((white_pixels/image_size)*100), '%')
>print('black :', int((black_pixels/image_size)*100), '%')
>
>white : 13 %
>black : 86 %
>```


###作成したスクリプト・ファイル

```Python3:object_area_calc_2.py
import cv2

from typing import Tuple, Optional
import os
import cv2
import numpy as np
import cv2
import argparse

# 動画ファイル名をコマンドライン引数から受け取る
parser = argparse.ArgumentParser(description='')    #
parser.add_argument('--file_name')
args = parser.parse_args()

img_file = args.file_name

im = cv2.imread(img_file)
im_gray = cv2.cvtColor(im, cv2.COLOR_BGR2GRAY)
retval, im_bw = cv2.threshold(im_gray, 0, 255, cv2.THRESH_BINARY_INV + cv2.THRESH_OTSU)

# 輪郭の検出
contours, hierarchy = cv2.findContours(im_bw, cv2.RETR_LIST, cv2.CHAIN_APPROX_SIMPLE)

# 輪郭を１つずつ書き込んで出力
# 何も出力されないか、かろうじて視認できる点しか書き込まれないものは、画像に出力しない
# 面積が100ピクセル以上のものみ、青色で輪郭を１件ずつ個別のファイルに出力。
# 検出されたすべての面積は、その輪郭のいかんによらず、detected_all_the_contours_resultにすべてまとめて出力する。
for i in range(len(contours)):
    im_con = im.copy()
    im_con = cv2.drawContours(im_con, contours, i, (255, 191, 0), 10) # dep skyblue RGB(0, 191, 255)
    #検出したパーツが、画面全体に占める面積比を求める
    #この画像ファイルを真っ黒にしたnumpy.array行列オブジェクトを作成
    im_con2 = im.copy()
    img_gray = cv2.cvtColor(im_con2, cv2.COLOR_BGR2GRAY)
    this_image_file_size_all_element_zero_image = np.zeros_like(img_gray)
    #https://code-graffiti.com/opencv-contour-detection-in-python/
    #第5引数で輪郭線の太さを指定。-1を指定すると塗りつぶしになる。色はスカラー0で黒（(0,0,0)と同義）、255で白（(255,255,255)と同義）
    cv2.drawContours(this_image_file_size_all_element_zero_image, contours, i, color=255, thickness=-1)
    # 白黒割合の確認
    image_size = this_image_file_size_all_element_zero_image.size #画面全体の面積
    white_pixels = cv2.countNonZero(this_image_file_size_all_element_zero_image)
    #すぐ上のdarwCounterでパーツ領域だけ白で塗りつぶされている。NonZeroの値が、その白い画素部分
    black_pixels = int(this_image_file_size_all_element_zero_image.size)-int(white_pixels)
    #cv2.countZeroメソッドがないので、全体から白領域を引いて黒領域の面積を得る
    area_percentage = (white_pixels/image_size)*100
    # 小数点第2位まで出力
    area_percentage_str = format(area_percentage, '.2f')

    message0 =  "ID : {0}  ,  Area {1}%".format(i, area_percentage_str)
    message1 =  "Area {0} of All Area {1}".format(cv2.contourArea(contours[i]), image_size)
    
    print(message0)
    print(message1+"\n")

    if cv2.contourArea(contours[i]) >= 100:
        cv2.putText(im_con, str(message0),  (0, 50), cv2.FONT_HERSHEY_TRIPLEX, 1, (0, 0, 255), 1, cv2.LINE_AA)
        cv2.putText(im_con, str(message1),  (0, 80), cv2.FONT_HERSHEY_TRIPLEX, 1, (0, 0, 255), 1, cv2.LINE_AA)
        cv2.imwrite('result_' + img_file + str(i) + '.png', im_con)
        
        cv2.putText(this_image_file_size_all_element_zero_image, str(message0),  (0, 50), cv2.FONT_HERSHEY_TRIPLEX, 1, 255, 1, cv2.LINE_AA)
        cv2.putText(this_image_file_size_all_element_zero_image, str(message1),  (0, 80), cv2.FONT_HERSHEY_TRIPLEX, 1, 255, 1, cv2.LINE_AA)
        cv2.imwrite('result_black_and_white_' + img_file + str(i) + '.png', this_image_file_size_all_element_zero_image)
    else:
        pass
        

# 検出されたすべての輪郭を画像に書き込んでファイル出力
im_new = cv2.imread(img_file) #im_conには、ループ処理の最終回でcv2.putTextで書き込んだ内容が残っている。新しいイメージオブジェクトを取得。
all_counours_engraved_img = cv2.drawContours(im_new, contours, -1, (0, 250, 154), 3)
#cv2.imwrite('detected_all_the_contours_result' + img_file + '.png', all_counours_engraved_img)

#この画像ファイルを真っ黒にしたnumpy.array行列オブジェクトを作成
im_con3 = im_new.copy()
img_gray2 = cv2.cvtColor(im_con3, cv2.COLOR_BGR2GRAY)
all_element_zero_image = np.zeros_like(img_gray2)
#第5引数で輪郭線の太さを指定。-1を指定すると塗りつぶしになる。色はスカラー0で黒（(0,0,0)と同義）、255で白（(255,255,255)と同義）
cv2.drawContours(all_element_zero_image, contours, -1, color=255, thickness=-1)
# 白黒割合の確認
image_size = this_image_file_size_all_element_zero_image.size #画面全体の面積
white_pixels2 = cv2.countNonZero(all_element_zero_image)
#すぐ上のdarwCounterでパーツ領域だけ白で塗りつぶされている。NonZeroの値が、その白い画素部分
black_pixels = int(all_element_zero_image.size)-int(white_pixels2)
#cv2.countZeroメソッドがないので、全体から白領域を引いて黒領域の面積を得る
all_area_percentage = (white_pixels2/image_size)*100
# 小数点第2位まで出力
all_area_percentage_str = format(all_area_percentage, '.2f')

message2 =  "ID : NA  ,  Area {0}%".format(all_area_percentage_str)
message3 =  "Area {0} of All Area {1}".format(white_pixels2, image_size)

print(message2)
print(message3+"\n")

# 原画像（カラー色）に検出されたすべてのパーツの輪郭と面積比率を記入した画像
cv2.putText(im_con3, str(message2),  (0, 50), cv2.FONT_HERSHEY_TRIPLEX, 1, (0, 0, 255), 1, cv2.LINE_AA)
cv2.putText(im_con3, str(message3),  (0, 80), cv2.FONT_HERSHEY_TRIPLEX, 1, (0, 0, 255), 1, cv2.LINE_AA)

cv2.imwrite('All_detected_parts_' + img_file + '.png', im_con3)
# 上記を白黒にした画像
cv2.putText(all_element_zero_image, str(message2),  (0, 50), cv2.FONT_HERSHEY_TRIPLEX, 1, 255, 1, cv2.LINE_AA)
cv2.putText(all_element_zero_image, str(message3),  (0, 80), cv2.FONT_HERSHEY_TRIPLEX, 1, 255, 1, cv2.LINE_AA)
cv2.imwrite('All_detected_parts_black_and_white_' + img_file  + '.png', all_element_zero_image)



```

###実行結果


```bash:Terminal
% python3 object_area_calc_2.py --file_name nozomi_1.jpg
ID : 0  ,  Area 0.00%
Area 0.0 of All Area 362240


ID : 1  ,  Area 0.00%
Area 0.0 of All Area 362240


ID : 2  ,  Area 0.00%
Area 0.0 of All Area 362240


ID : 3  ,  Area 0.00%
Area 5.5 of All Area 362240


ID : 4  ,  Area 0.00%
Area 5.5 of All Area 362240


ID : 5  ,  Area 0.00%
Area 2.0 of All Area 362240


ID : 6  ,  Area 0.00%
Area 4.0 of All Area 362240


ID : 7  ,  Area 0.00%
Area 5.5 of All Area 362240


ID : 8  ,  Area 0.00%

（ 省略 ）

ID : 341  ,  Area 0.00%
Area 4.0 of All Area 362240


ID : 342  ,  Area 0.00%
Area 2.0 of All Area 362240


ID : 343  ,  Area 0.00%
Area 0.0 of All Area 362240


ID : 344  ,  Area 21.56%
Area 76917.5 of All Area 362240


ID : NA  ,  Area 26.30%
Area 95254 of All Area 362240

%
```

```bash:Terminal
% ls *.png
All_detected_parts_black_and_white_nozomi_1.jpg.png	result_black_and_white_nozomi_1.jpg260.png		result_nozomi_1.jpg173.png
All_detected_parts_nozomi_1.jpg.png			result_black_and_white_nozomi_1.jpg275.png		result_nozomi_1.jpg176.png
result_black_and_white_nozomi_1.jpg106.png		result_black_and_white_nozomi_1.jpg276.png		result_nozomi_1.jpg220.png
result_black_and_white_nozomi_1.jpg133.png		result_black_and_white_nozomi_1.jpg344.png		result_nozomi_1.jpg232.png
result_black_and_white_nozomi_1.jpg171.png		result_black_and_white_nozomi_1.jpg85.png		result_nozomi_1.jpg260.png
result_black_and_white_nozomi_1.jpg172.png		result_black_and_white_nozomi_1.jpg93.png		result_nozomi_1.jpg275.png
result_black_and_white_nozomi_1.jpg173.png		result_nozomi_1.jpg106.png				result_nozomi_1.jpg276.png
result_black_and_white_nozomi_1.jpg176.png		result_nozomi_1.jpg133.png				result_nozomi_1.jpg344.png
result_black_and_white_nozomi_1.jpg220.png		result_nozomi_1.jpg171.png				result_nozomi_1.jpg85.png
result_black_and_white_nozomi_1.jpg232.png		result_nozomi_1.jpg172.png				result_nozomi_1.jpg93.png
```