Post

影像處理小白(五):利用 HSV 色域找到膚色區域

本系列將紀錄影像處理小白從 0 開始學習 Python x OpenCV 的過程。
透過選修課一次次的作業把影像處理的基礎知識建立起來。

這次作業終於進入 RGB 圖像的環節,目標是標示出影像中的膚色區域。

沒有跟上進度的可以參照: 影像處理小白(四):利用頻域圖像消除週期性雜訊
或是 影像處理分類

功課要求

偵測輸入照片中的皮膚區域並將其標示出:

輸入的圖片 1 Python OpenCV find skin region

成果

圖像處理 程式完成後的執行結果,膚色區域以紅色標記

開發環境

OSEditorLanguageOpenCV
Windows 10Visual Studio CodePython 3.9.16OpenCV 4.5.4

實作

本次程式碼

使用的 libraries 如下:

1
2
3
import cv2
import matplotlib.pyplot as plt
import numpy as np

1/ 利用迴圈讀入三張圖片

建立一個儲存三張圖片路徑的 list ,使用迴圈搭配 cv2.imread(filename) 讀入圖片並顯示。
顯示圖片中有一點要注意, plt 使用的彩色圖片是 RGB ,而 OpenCV 讀入的圖片是以 BGR 編碼,所以必須透過 cv2.cvtColor(original_img, cv2.COLOR_BGR2RGB) 來轉換要顯示的圖片,不然就會出現三名藍色皮膚的人。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
images = ['img1.jpg', 'img2.jpg', 'img3.jpg']

image_count = 0
for image in images:

    print("Now processing: ", image)

    # read image
    original_img = cv2.imread(image)
    # show original image
    plt.subplot(3, 3, image_count * 3 + 1)
    plt.imshow(cv2.cvtColor(original_img, cv2.COLOR_BGR2RGB))
    plt.title("Original Image")
    plt.axis("off")

2/ 轉換圖片色域並設定膚色範圍

RGB 色域容易受到光線等因素影響,導致難以判斷顏色是否為膚色,使用 cv2.cvtColor(src, code) 轉換至 HSV 色域後就能把色相、飽和度、明度分開看。

1
2
3
4
# convert to HSV color space
hsv_img = cv2.cvtColor(original_img, cv2.COLOR_BGR2HSV)
# convert to YCrCb color space
ycbcr_img = cv2.cvtColor(original_img, cv2.COLOR_BGR2YCrCb)

創建 array 來儲存膚色的範圍,這是我採用的範圍:

  • H: 0 ~ 17
  • S: 50 ~ 170
  • V: 0 ~ 255
  • Y: 0 ~ 255
  • Cr: 135 ~ 180
  • Cb: 85 ~ 135

調整過後我仍舊無法避免有些非皮膚(頭髮、陰影等)處還是會被判別為膚色 QQ

1
2
3
4
5
 # create skin upper and lower bounds
lower_skin = np.array([0, 50, 0], dtype=np.uint8)
upper_skin = np.array([17, 170, 255], dtype=np.uint8)
lower_skin_ycbcr = np.array((0, 135, 85), dtype=np.uint8)
upper_skin_ycbcr = np.array((255, 180, 135), dtype=np.uint8)

3/ 提取膚色區域

使用 cv2.inRange(src, lowerb, upperb) 提取膚色區域,獲得一張和原尺寸相同大小的二值化 mask ,膚色區域為白色,其他地方為黑色,就如結果圖中第二個 column 顯示的那樣。

1
2
3
4
5
6
7
8
9
# find skin color in the image
skin_mask_hsv = cv2.inRange(hsv_img, lower_skin, upper_skin)
skin_mask_ycbcr = cv2.inRange(ycbcr_img, lower_skin_ycbcr, upper_skin_ycbcr)
skin_mask = cv2.bitwise_and(skin_mask_hsv, skin_mask_ycbcr)
# show skin mask
plt.subplot(3, 3, image_count * 3 + 2)
plt.imshow(skin_mask, cmap="gray")
plt.title("Skin Mask")
plt.axis("off")

4/ 將原圖膚色區域標示為紅色

最後在 skin_mask 不為 0 的像素,更改原圖的顏色為紅色。

1
2
3
4
5
6
7
8
# make skin region red
skin_region = original_img.copy()
skin_region[skin_mask != 0] = [0, 0, 255]
# show skin region
plt.subplot(3, 3, image_count * 3 + 3)
plt.imshow(cv2.cvtColor(skin_region, cv2.COLOR_BGR2RGB))
plt.title("Skin Region")
plt.axis("off")

別忘記迴圈的最後要 += 1 和程式結束前要顯示圖片。

1
2
3
  image_count += 1

plt.show()

總結

將圖片轉換至 HSV 色域後,可以減少光線明暗造成的影響。

參考資料

This post is licensed under CC BY 4.0 by the author.