OpenCV(Python)で手のひらの面積を算出する

# OpenCV
```
pip install opencv-python
```

# 手順の概要
**１．方眼の上で手のひらの画像を撮る**
**２．方眼の部分だけを残しトリミング**
**３．OpenCVで読み込んでグレースケール・二値化（python）**
**４．二値化した画像で手のひらの輪郭を探索（python）**
**５．「手のひらのピクセル数」と「全体のピクセル数」の比により面積を算出（python）**

# １．２．画像取得とトリミング
取得した画像
![S__13139991.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/601935/1d26dd7a-cd7d-eef3-1786-28548411f58c.jpeg)
トリミング後
![S__13139993.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/601935/abc39679-13fc-e001-4d27-ce6dae875038.jpeg)


# ３．OpenCVでグレースケール・二値化
```python
img = cv2.imread("hand.jpg")  # 画像データの取得

gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)  # 白黒（グレースケール）化
threshold = 160  # 二値化の閾値
ret, thresh = cv2.threshold(gray, threshold, 255, cv2.THRESH_BINARY) #  閾値での二値化
```
二値化後の画像
![hand_gray.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/601935/83db3dbd-6cd9-9ec6-3e85-00897c8bbb70.jpeg)

# ４．５．手のひらの輪郭を探索・面積を算出
```python
contours, hierarchy = cv2.findContours(thresh, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)  # 輪郭を取得
contours = list(filter(lambda x: cv2.contourArea(x) > 20000, contours))  # 小さい輪郭を除く

height, width = img.shape[0], img.shape[1]  # 縦・横のピクセル数を取得
rectangle_area = height * width  # 総ピクセル数
rate = cv2.contourArea(contours[0]) / rectangle_area  # 手のピクセル数が全体のピクセル数に占める割合
bg_area = 19*28  # 青色の背景の面積[cm^2]
hand_S = round(bg_area*rate, 3)  # 手の面積[cm^2]
```
```math
手のひらの面積S=\frac{手のひらのピクセル数}{画像全体のピクセル数}・方眼の面積[\rm{cm^2}]
```
![S__13139994.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/601935/1a36f1e3-69f6-f33f-cadd-93e749bbf2ee.jpeg)


# 結果
私（男性）の手のひらの面積は$150.4\rm{cm^2}$と算出できた。
[1]によると、手のひらの面積の平均値は、男性$140\rm{cm^2}$、女性$120\rm{cm^2}$とのことだったので、妥当な結果だと言える。

# コード全体
```python
import cv2

#############################
# 画像取り込み
img = cv2.imread("hand7.jpg")  # 画像データの取得
height, width = img.shape[0], img.shape[1]  # 縦・横のピクセル数を取得
scale = 1  # 表示倍率
height2, width2 = img.shape[0]*scale, img.shape[1]*scale  # 表示用の縦・横のピクセル数
rectangle_area = height2 * width2  # 総ピクセル数
img = cv2.resize(img, (int(width2), int(height2)))  # 指定した倍率にリサイズ

#############################
#グレースケール・二値化
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)  # 白黒（グレースケール）化
threshold = 160  # 二値化の閾値
ret, thresh = cv2.threshold(gray, threshold, 255, cv2.THRESH_BINARY) #  閾値での二値化
# ret, thresh = cv2.threshold(img, 0, 255, cv2.THRESH_OTSU)  # 自動二値化

#############################
#輪郭の探索・面積の算出
contours, hierarchy = cv2.findContours(thresh, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)  # 輪郭を取得
contours = list(filter(lambda x: cv2.contourArea(x) > 20000, contours))  # 小さい輪郭を除く
rate = cv2.contourArea(contours[0]) / rectangle_area  # 手のピクセル数が全体のピクセル数に占める割合
bg_area = 19*28  # 青色の背景の面積[cm^2]
hand_S = round(bg_area*rate, 3)  # 手の面積[cm^2]
print(str(hand_S)+"[cm^2]")
cv2.putText(img, str(hand_S)+"cm^2", org=(int(height2/2), int(width2/2)),  # 画像内に面積表示
            fontFace=cv2.FONT_HERSHEY_SIMPLEX,
            fontScale=5.0,
            color=(0, 0, 0),
            thickness=5,
            lineType=cv2.LINE_4)

cv2.drawContours(img, contours, -1, color=(0, 0, 255), thickness=5)  # 輪郭の描画

#############################
# 画像保存
cv2.imwrite("hand_result.jpg", img)
cv2.imwrite("hand_gray.jpg", thresh)

#############################
# 画像表示
cv2.imshow("img", img)
cv2.imshow("img2", thresh)
cv2.waitKey (0)
cv2.destroyAllwindows()

#############################
```

# 参考文献
[1] 服部恒明; 大槻文夫. 成人の手部平面積の非対称性について. 体育学研究, 1974, 19.3: 133-136.