LoginSignup
6
3

More than 3 years have passed since last update.

肌の色を認識しよう!

Last updated at Posted at 2020-12-02

 理論

この部分の内容は、この論文からまとめて翻訳されています。

 色空間

色空間は、色情報を3つまたは4つの異なる色成分として表す数学モデルです。皮膚の検出には、さまざまな色空間を利用できます。 それらは、RGBベースの色空間(RGB、正規化されたRGB)、色相ベースの色空間(HSI、HSV、およびHSL)、輝度ベースの色空間(YCBCr、YIQ、およびYUV)です。

RGB(Red-Green-Blue:赤ー緑ー青)

RGB色空間は広く使用されており、通常、デジタル画像を保存および表現するためのデフォルトの色空間です。 原色である赤、緑、青の3つのコンポーネントで構成されています。 3つの基本色を混合することにより、任意の色を得ることができます。

これは、赤、緑、青の値の線形結合によって色が取得される付加的な色空間です。
3つのチャネルは、表面に当たる光の量によって相関しています。

image.png

HSV(Hue Saturation Value:色相ー彩度ー明度)

色相(H)が0から1.0まで変化すると、対応する色は赤から黄色、緑、シアン、青、マゼンタを経て赤に戻ります。 彩度(S)が0から1.0まで変化すると、対応する色(色相)は不飽和(灰色の陰影)から完全に飽和(白成分なし)まで変化します。 値(V)または明るさが0から1.0まで変化すると、対応する色がますます明るくなります。

image.png

YCbCr(Luminance, Chrominance)

YCbCrは、エンコードされた非線形RGB信号です。 色は、RGB値の加重和として構築された輝度(非線形RGBから計算された輝度)で表されます。
このフォーマットでは、輝度情報は単一の成分(Y)として保存され、クロミナンス情報は2つの色差成分(CbとCr)として保存されます。この表現により、冗長な色情報を簡単に取り除くことができるため、JPEG、MPEG1、MPEG2、MPEG4などの画像およびビデオ圧縮規格で使用されます。

image.png

人間の肌色

著者が提案した皮膚検出アルゴリズム

image.png

image.png

以上のアルゴリズムについていくつかまだわからなくて、実装も難しそうなので、このもっと簡単な方法を使ってみます
https://github.com/CHEREF-Mehdi/SkinDetection

    HSV: 0<=H<=17 and 15<=S<=170 and 0<=V<=255
            and
    YCrCb: 0<=Y<=255 and 135<=Cr<=180 and 85<=Cb<=135

 実装

目的

test.jpg
以上の画像から肌の色で指先を認識することです。

環境

Windows 10
OpenCV 3.4.2
Python 3.7.9

コード

HSVフィルター

img=cv2.imread("a.jpg")
img_YCrCb = cv2.cvtColor(img, cv2.COLOR_BGR2YCrCb)
HSV_mask = cv2.inRange(img_HSV, (0, 15, 0), (17,170,255)) 
HSV_mask = cv2.morphologyEx(HSV_mask, cv2.MORPH_OPEN, np.ones((3,3), np.uint8))
cv2.imshow("HSV.jpg", HSV_mask)

HSVフィルターの結果
1_HSV.jpg

YCrCbフィルター

img=cv2.imread("a.jpg")
img_YCrCb = cv2.cvtColor(img, cv2.COLOR_BGR2YCrCb)
YCrCb_mask = cv2.inRange(img_YCrCb, (0, 135, 85), (255,180,135)) 
YCrCb_mask = cv2.morphologyEx(YCrCb_mask, cv2.MORPH_OPEN, np.ones((3,3), np.uint8))
cv2.imshow("YCbCr.jpg",YCrCb_mask)

YCrCbフィルターの結果
2_YCbCr.jpg

見えるようにYCrCbフィルターの方はよく肌の色をにんしきできます。

OpenCVの輪郭(リンク


def apply_contours(contour_img):
    ret, thresh = cv2.threshold(contour_img, 127, 255, 0)
    contours, hierarchy = cv2.findContours(thresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)[-2:]
    # 一番大きい輪郭を選ぶ
    cnt = max(contours, key = cv2.contourArea)
    # 輪郭の中最上の点を選ぶ
    topmost = tuple(cnt[cnt[:,:,1].argmin()][0])    
    contour_img = cv2.drawContours(img, [cnt], 0, (0,255,0), 3)
    contour_img = cv2.circle(contour_img , topmost, radius=5, color=(0, 0, 255), thickness=-1)
    return contour_img

輪郭とYCrCbフィルターの結果:
2_YCbCr.jpg

最後のコード

import cv2
import numpy as np


img=cv2.imread("WIN_20201203_01_38_27_Pro.jpg")

img_YCrCb = cv2.cvtColor(img, cv2.COLOR_BGR2YCrCb)
YCrCb_mask = cv2.inRange(img_YCrCb, (0, 135, 85), (255,180,135)) 
YCrCb_mask = cv2.morphologyEx(YCrCb_mask, cv2.MORPH_OPEN, np.ones((3,3), np.uint8))


def apply_contours(contour_img):
    ret, thresh = cv2.threshold(contour_img, 127, 255, 0)
    contours, hierarchy = cv2.findContours(thresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)[-2:]
    # 一番大きい輪郭を選ぶ
    cnt = max(contours, key = cv2.contourArea)
    # 輪郭の中最上の点を選ぶ
    topmost = tuple(cnt[cnt[:,:,1].argmin()][0])    
    contour_img = cv2.drawContours(img, [cnt], 0, (0,255,0), 3)
    contour_img = cv2.circle(contour_img , topmost, radius=8, color=(0, 0, 255), thickness=-1)
    return contour_img

YCrCb_result = apply_contours(YCrCb_mask)

#show results
# cv2.imshow("Show image",YCrCb_result)
cv2.imwrite("YCbCr_result.jpg",YCrCb_result)
cv2.waitKey(0)
cv2.destroyAllWindows() 

Happy coding!

6
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
6
3