#!/usr/bin/env python # -*- coding: utf-8 -*- __author__ = 'Will Brennan' import logging import cv2 import numpy from . import scripts logger = logging.getLogger('main') def get_hsv_mask(img, debug=False): assert isinstance(img, numpy.ndarray), 'image must be a numpy array' assert img.ndim == 3, 'skin detection can only work on color images' logger.debug('getting hsv mask') lower_thresh = numpy.array([0, 50, 0], dtype=numpy.uint8) upper_thresh = numpy.array([120, 150, 255], dtype=numpy.uint8) img_hsv = cv2.cvtColor(img, cv2.COLOR_RGB2HSV) msk_hsv = cv2.inRange(img_hsv, lower_thresh, upper_thresh) msk_hsv[msk_hsv < 128] = 0 msk_hsv[msk_hsv >= 128] = 1 if debug: scripts.display('input', img) scripts.display('mask_hsv', msk_hsv) return msk_hsv.astype(float) def get_rgb_mask(img, debug=False): assert isinstance(img, numpy.ndarray), 'image must be a numpy array' assert img.ndim == 3, 'skin detection can only work on color images' logger.debug('getting rgb mask') lower_thresh = numpy.array([45, 52, 108], dtype=numpy.uint8) upper_thresh = numpy.array([255, 255, 255], dtype=numpy.uint8) mask_a = cv2.inRange(img, lower_thresh, upper_thresh) mask_b = 255 * ((img[:, :, 2] - img[:, :, 1]) / 20) mask_c = 255 * ((numpy.max(img, axis=2) - numpy.min(img, axis=2)) / 20) # msk_rgb = cv2.bitwise_and(mask_c, cv2.bitwise_and(mask_a, mask_b)) mask_d = numpy.bitwise_and(numpy.uint64(mask_a), numpy.uint64(mask_b)) msk_rgb = numpy.bitwise_and(numpy.uint64(mask_c), numpy.uint64(mask_d)) msk_rgb[msk_rgb < 128] = 0 msk_rgb[msk_rgb >= 128] = 1 if debug: scripts.display('input', img) scripts.display('mask_rgb', msk_rgb) return msk_rgb.astype(float) def get_ycrcb_mask(img, debug=False): assert isinstance(img, numpy.ndarray), 'image must be a numpy array' assert img.ndim == 3, 'skin detection can only work on color images' logger.debug('getting ycrcb mask') lower_thresh = numpy.array([90, 100, 130], dtype=numpy.uint8) upper_thresh = numpy.array([230, 120, 180], dtype=numpy.uint8) img_ycrcb = cv2.cvtColor(img, cv2.COLOR_RGB2YCR_CB) msk_ycrcb = cv2.inRange(img_ycrcb, lower_thresh, upper_thresh) msk_ycrcb[msk_ycrcb < 128] = 0 msk_ycrcb[msk_ycrcb >= 128] = 1 if debug: scripts.display('input', img) scripts.display('mask_ycrcb', msk_ycrcb) return msk_ycrcb.astype(float) def grab_cut_mask(img_col, mask, debug=False): assert isinstance(img_col, numpy.ndarray), 'image must be a numpy array' assert isinstance(mask, numpy.ndarray), 'mask must be a numpy array' assert img_col.ndim == 3, 'skin detection can only work on color images' assert mask.ndim == 2, 'mask must be 2D' kernel = numpy.ones((50, 50), numpy.float32) / (50 * 50) dst = cv2.filter2D(mask, -1, kernel) dst[dst != 0] = 255 free = numpy.array(cv2.bitwise_not(dst), dtype=numpy.uint8) if debug: scripts.display('not skin', free) scripts.display('grabcut input', mask) grab_mask = numpy.zeros(mask.shape, dtype=numpy.uint8) grab_mask[:, :] = 2 grab_mask[mask == 255] = 1 grab_mask[free == 255] = 0 if numpy.unique(grab_mask).tolist() == [0, 1]: logger.debug('conducting grabcut') bgdModel = numpy.zeros((1, 65), numpy.float64) fgdModel = numpy.zeros((1, 65), numpy.float64) if img_col.size != 0: mask, bgdModel, fgdModel = cv2.grabCut(img_col, grab_mask, None, bgdModel, fgdModel, 5, cv2.GC_INIT_WITH_MASK) mask = numpy.where((mask == 2) | (mask == 0), 0, 1).astype(numpy.uint8) else: logger.warning('img_col is empty') return mask def closing(mask): assert isinstance(mask, numpy.ndarray), 'mask must be a numpy array' assert mask.ndim == 2, 'mask must be a greyscale image' logger.debug("closing mask of shape {0}".format(mask.shape)) kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (5, 5)) mask = cv2.morphologyEx(mask, cv2.MORPH_CLOSE, kernel) kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (3, 3)) mask = cv2.morphologyEx(mask, cv2.MORPH_OPEN, kernel, iterations=2) return mask def process(img, thresh=0.5, debug=False): assert isinstance(img, numpy.ndarray), 'image must be a numpy array' assert img.ndim == 3, 'skin detection can only work on color images' logger.debug("processing image of shape {0}".format(img.shape)) mask_hsv = get_hsv_mask(img, debug=debug) mask_rgb = get_rgb_mask(img, debug=debug) mask_ycrcb = get_ycrcb_mask(img, debug=debug) n_masks = 3.0 mask = (mask_hsv + mask_rgb + mask_ycrcb) / n_masks mask[mask < thresh] = 0.0 mask[mask >= thresh] = 255.0 logger.debug('{0}% of the image is skin'.format(int((100.0 / 255.0) * numpy.sum(mask) / mask.size))) mask = mask.astype(numpy.uint8) mask = closing(mask) mask = grab_cut_mask(img, mask, debug=debug) return mask