Created
March 19, 2020 17:15
-
-
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"
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# 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