Skip to content

Instantly share code, notes, and snippets.

@makew0rld
Created March 19, 2020 17:15
Show Gist options
  • Save makew0rld/3f627a994310bf498431fe6a649b7599 to your computer and use it in GitHub Desktop.
Save makew0rld/3f627a994310bf498431fe6a649b7599 to your computer and use it in GitHub Desktop.
OpenCV Python skin detection algorithm from the paper "RGB-H-CbCr Skin Colour Model for Human Face Detection"
# Algorithm from: http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.718.1964&rep=rep1&type=pdf
# Archived version: https://archive.org/download/10.1.1.718.1964/10.1.1.718.1964.pdf
# Very similar (a copy?) of https://arxiv.org/pdf/1708.02694.pdf
import cv2
import numpy as np
def np_multi_and(*args):
result = args[0]
for arr in args[1:]:
# Chain all the ANDs
result = np.logical_and(result, arr)
return result
def np_multi_or(*args):
result = args[0]
for arr in args[1:]:
# Chain all the ORs
result = np.logical_or(result, arr)
return result
def np_multi_max(*args):
result = args[0]
for arr in args[1:]:
# Chain all the maxes
result = np.maximum(result, arr)
return result
def np_multi_min(*args):
result = args[0]
for arr in args[1:]:
# Chain all the mins
result = np.minimum(result, arr)
return result
frame = whatever # Could be a frame a video source, or just a still image
# BGR
# Extract channels
frame_b = frame[:, :, 0]
frame_g = frame[:, :, 1]
frame_r = frame[:, :, 2]
# Calculate masks - AND all the conditions
# mask1: uniform daylight
bgr_mask1 = np_multi_and(frame_r > 95, frame_g > 40, frame_b > 20,
np_multi_max(frame_r, frame_g, frame_b) - np_multi_min(frame_r, frame_g, frame_b) > 15,
abs(frame_r - frame_g) > 15, frame_r > frame_g, frame_r > frame_b
)
# mask2: flashlight or lateral daylight
bgr_mask2 = np_multi_and(frame_r > 220, frame_g > 210, frame_b > 170,
abs(frame_r - frame_g) <= 15, frame_r > frame_b, frame_g > frame_b
)
bgr_mask = np.logical_or(bgr_mask1, bgr_mask2)
# Display
cv2.imshow("BGR Mask", bgr_mask.astype("uint8") * 255)
# [Y]CrCb
# Extract channels
YCrCb_frame = cv2.cvtColor(frame, cv2.COLOR_BGR2YCrCb)
YCrCb_frame_Cr = YCrCb_frame[:, :, 1]
YCrCb_frame_Cb = YCrCb_frame[:, :, 2]
# Calculate masks
YCrCb_mask = np_multi_and(YCrCb_frame_Cr <= 1.5862 * YCrCb_frame_Cb + 20,
YCrCb_frame_Cr >= 0.3448 * YCrCb_frame_Cb + 76.2069,
YCrCb_frame_Cr >= -4.5652 * YCrCb_frame_Cb + 234.5652,
YCrCb_frame_Cr <= -1.15 * YCrCb_frame_Cb + 301.75,
YCrCb_frame_Cr <= -2.2857 * YCrCb_frame_Cb + 432.85
)
# Display
cv2.imshow("YCrCb Mask", YCrCb_mask.astype("uint8") * 255)
# H[SV]
# Extract channels
hsv_frame = cv2.cvtColor(frame, cv2.COLOR_BGR2HSV)
hsv_frame_h = hsv_frame[:, :, 0]
# Calc mask
hsv_mask = np.logical_or(hsv_frame_h < 25, hsv_frame_h > 230)
# Display
cv2.imshow("HSV Mask", hsv_mask.astype("uint8") * 255)
# Display the final mask
skin_mask = np_multi_and(bgr_mask, YCrCb_mask, hsv_mask)
cv2.imshow("Combined Mask", skin_mask.astype("uint8") * 255)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment