Commit b8f533e0 authored by WillBrennan's avatar WillBrennan Committed by GitHub

Merge pull request #5 from WillBrennan/develop

Develop
parents bb5c2af0 b529b76a
[style]
based_on_style = pep8
column_limit = 120
spaces_before_comment = 4
split_before_logical_operator = true
\ No newline at end of file
...@@ -2,7 +2,6 @@ ...@@ -2,7 +2,6 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
__author__ = 'Will Brennan' __author__ = 'Will Brennan'
# Built-in Modules # Built-in Modules
import os import os
import argparse import argparse
...@@ -25,7 +24,7 @@ def find_images(path, recursive=False, ignore=True): ...@@ -25,7 +24,7 @@ def find_images(path, recursive=False, ignore=True):
assert isinstance(recursive, bool), 'FileIO - get_images: recursive must be a boolean variable' assert isinstance(recursive, bool), 'FileIO - get_images: recursive must be a boolean variable'
ext, result = ['png', 'jpg', 'jpeg'], [] ext, result = ['png', 'jpg', 'jpeg'], []
for path_a in os.listdir(path): for path_a in os.listdir(path):
path_a = path + '/' +path_a path_a = path + '/' + path_a
if os.path.isdir(path_a) and recursive: if os.path.isdir(path_a) and recursive:
for path_b in find_images(path_a): for path_b in find_images(path_a):
yield path_b yield path_b
...@@ -36,6 +35,7 @@ def find_images(path, recursive=False, ignore=True): ...@@ -36,6 +35,7 @@ def find_images(path, recursive=False, ignore=True):
else: else:
raise ValueError('error! path is not a valid path or directory') raise ValueError('error! path is not a valid path or directory')
if __name__ == '__main__': if __name__ == '__main__':
parser = argparse.ArgumentParser(description=__doc__) parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument('image_paths', type=str, nargs='+', help="paths to one or more images or image directories") parser.add_argument('image_paths', type=str, nargs='+', help="paths to one or more images or image directories")
...@@ -46,19 +46,13 @@ if __name__ == '__main__': ...@@ -46,19 +46,13 @@ if __name__ == '__main__':
parser.add_argument('-t', '--thresh', dest='thresh', default=0.5, type=float, help='threshold for skin mask') parser.add_argument('-t', '--thresh', dest='thresh', default=0.5, type=float, help='threshold for skin mask')
args = parser.parse_args() args = parser.parse_args()
logger = logging.getLogger('main') if args.debug:
if not args.quite: logging.basicConfig(level=logging.DEBUG)
if args.debug: else:
level = logging.DEBUG logging.basicConfig(level=logging.INFO)
else: logger = logging.getLogger("main")
level = logging.INFO
ch = logging.StreamHandler()
ch.setLevel(level=level)
formatter = logging.Formatter('%(asctime)s - %(funcName)s - %(levelname)s - %(message)s')
ch.setFormatter(formatter)
logger.addHandler(ch)
detector = SkinDetector(args) detector = SkinDetector(thresh=args.thresh, debug=args.debug)
for image_arg in args.image_paths: for image_arg in args.image_paths:
for image_path in find_images(image_arg): for image_path in find_images(image_arg):
...@@ -71,4 +65,4 @@ if __name__ == '__main__': ...@@ -71,4 +65,4 @@ if __name__ == '__main__':
scripts.display('img_col', img_col) scripts.display('img_col', img_col)
scripts.display('img_msk', img_msk) scripts.display('img_msk', img_msk)
scripts.display('img_skn', cv2.bitwise_and(img_col, img_col, mask=img_msk)) scripts.display('img_skn', cv2.bitwise_and(img_col, img_col, mask=img_msk))
cv2.waitKey(0) cv2.waitKey(0)
\ No newline at end of file
...@@ -2,7 +2,6 @@ ...@@ -2,7 +2,6 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
__author__ = 'Will Brennan' __author__ = 'Will Brennan'
# Built-in Modules # Built-in Modules
import time import time
import argparse import argparse
...@@ -11,14 +10,15 @@ import logging ...@@ -11,14 +10,15 @@ import logging
import cv2 import cv2
import numpy import numpy
# Custom Modules # Custom Modules
import scripts
logger = logging.getLogger('main') logger = logging.getLogger('main')
class SkinDetector(object): class SkinDetector(object):
def __init__(self, args): def __init__(self, thresh=0.5, debug=False):
assert isinstance(args, argparse.Namespace), 'args must be of type argparse.Namespace' self.debug = debug
self.args = args self.thresh = thresh
self.mask = None self.mask = None
logger.debug('SkinDetector initialised') logger.debug('SkinDetector initialised')
...@@ -39,7 +39,7 @@ class SkinDetector(object): ...@@ -39,7 +39,7 @@ class SkinDetector(object):
upper_thresh = numpy.array([120, 150, 255], dtype=numpy.uint8) upper_thresh = numpy.array([120, 150, 255], dtype=numpy.uint8)
img_hsv = cv2.cvtColor(img, cv2.COLOR_RGB2HSV) img_hsv = cv2.cvtColor(img, cv2.COLOR_RGB2HSV)
msk_hsv = cv2.inRange(img_hsv, lower_thresh, upper_thresh) msk_hsv = cv2.inRange(img_hsv, lower_thresh, upper_thresh)
if self.args.debug: if self.debug:
scripts.display('input', img) scripts.display('input', img)
scripts.display('mask_hsv', msk_hsv) scripts.display('mask_hsv', msk_hsv)
self.add_mask(msk_hsv) self.add_mask(msk_hsv)
...@@ -49,13 +49,13 @@ class SkinDetector(object): ...@@ -49,13 +49,13 @@ class SkinDetector(object):
lower_thresh = numpy.array([45, 52, 108], dtype=numpy.uint8) lower_thresh = numpy.array([45, 52, 108], dtype=numpy.uint8)
upper_thresh = numpy.array([255, 255, 255], dtype=numpy.uint8) upper_thresh = numpy.array([255, 255, 255], dtype=numpy.uint8)
mask_a = cv2.inRange(img, lower_thresh, upper_thresh) mask_a = cv2.inRange(img, lower_thresh, upper_thresh)
mask_b = 255*((img[:, :, 2]-img[:, :, 1])/20) mask_b = 255 * ((img[:, :, 2] - img[:, :, 1]) / 20)
logger.debug('mask_b unique: {0}'.format(numpy.unique(mask_b))) logger.debug('mask_b unique: {0}'.format(numpy.unique(mask_b)))
mask_c = 255*((numpy.max(img, axis=2)-numpy.min(img, axis=2))/20) mask_c = 255 * ((numpy.max(img, axis=2) - numpy.min(img, axis=2)) / 20)
logger.debug('mask_d unique: {0}'.format(numpy.unique(mask_c))) logger.debug('mask_d unique: {0}'.format(numpy.unique(mask_c)))
msk_rgb = cv2.bitwise_and(mask_a, mask_b) msk_rgb = cv2.bitwise_and(mask_a, mask_b)
msk_rgb = cv2.bitwise_and(mask_c, msk_rgb) msk_rgb = cv2.bitwise_and(mask_c, msk_rgb)
if self.args.debug: if self.debug:
scripts.display('input', img) scripts.display('input', img)
scripts.display('mask_rgb', msk_rgb) scripts.display('mask_rgb', msk_rgb)
self.add_mask(msk_rgb) self.add_mask(msk_rgb)
...@@ -66,30 +66,30 @@ class SkinDetector(object): ...@@ -66,30 +66,30 @@ class SkinDetector(object):
upper_thresh = numpy.array([230, 120, 180], dtype=numpy.uint8) upper_thresh = numpy.array([230, 120, 180], dtype=numpy.uint8)
img_ycrcb = cv2.cvtColor(img, cv2.COLOR_RGB2YCR_CB) img_ycrcb = cv2.cvtColor(img, cv2.COLOR_RGB2YCR_CB)
msk_ycrcb = cv2.inRange(img_ycrcb, lower_thresh, upper_thresh) msk_ycrcb = cv2.inRange(img_ycrcb, lower_thresh, upper_thresh)
if self.args.debug: if self.debug:
scripts.display('input', img) scripts.display('input', img)
scripts.display('mask_ycrcb', msk_ycrcb) scripts.display('mask_ycrcb', msk_ycrcb)
self.add_mask(msk_ycrcb) self.add_mask(msk_ycrcb)
def grab_cut_mask(self, img_col, mask): def grab_cut_mask(self, img_col, mask):
kernel = numpy.ones((50, 50), numpy.float32)/(50*50) kernel = numpy.ones((50, 50), numpy.float32) / (50 * 50)
dst = cv2.filter2D(mask, -1, kernel) dst = cv2.filter2D(mask, -1, kernel)
dst[dst != 0] = 255 dst[dst != 0] = 255
free = numpy.array(cv2.bitwise_not(dst), dtype=numpy.uint8) free = numpy.array(cv2.bitwise_not(dst), dtype=numpy.uint8)
if self.args.debug: if self.debug:
scripts.display('not skin', free) scripts.display('not skin', free)
scripts.display('grabcut input', mask) scripts.display('grabcut input', mask)
grab_mask = numpy.zeros(mask.shape, dtype=numpy.uint8) grab_mask = numpy.zeros(mask.shape, dtype=numpy.uint8)
grab_mask[:, :] = 2 grab_mask[:, :] = 2
grab_mask[mask == 255] = 1 grab_mask[mask == 255] = 1
grab_mask[free == 255] = 0 grab_mask[free == 255] = 0
print numpy.unique(grab_mask)
if numpy.unique(grab_mask).tolist() == [0, 1]: if numpy.unique(grab_mask).tolist() == [0, 1]:
logger.debug('conducting grabcut') logger.debug('conducting grabcut')
bgdModel = numpy.zeros((1, 65), numpy.float64) bgdModel = numpy.zeros((1, 65), numpy.float64)
fgdModel = numpy.zeros((1, 65), numpy.float64) fgdModel = numpy.zeros((1, 65), numpy.float64)
if img_col.size != 0: if img_col.size != 0:
mask, bgdModel, fgdModel = cv2.grabCut(img_col, grab_mask, None, bgdModel, fgdModel, 5, cv2.GC_INIT_WITH_MASK) 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('uint8') mask = numpy.where((mask == 2) | (mask == 0), 0, 1).astype('uint8')
else: else:
logger.warning('img_col is empty') logger.warning('img_col is empty')
...@@ -118,12 +118,12 @@ class SkinDetector(object): ...@@ -118,12 +118,12 @@ class SkinDetector(object):
self.get_mask_rgb(img) self.get_mask_rgb(img)
self.get_mask_ycrcb(img) self.get_mask_ycrcb(img)
logger.debug('Thresholding sum of masks') logger.debug('Thresholding sum of masks')
self.threshold(self.args.thresh) self.threshold(self.thresh)
if self.args.debug: if self.debug:
scripts.display('skin_mask', self.mask) scripts.display('skin_mask', self.mask)
scripts.display('input_img', img) scripts.display('input_img', img)
dt = round(time.time()-dt, 2) dt = round(time.time() - dt, 2)
hz = round(1/dt, 2) hz = round(1 / dt, 2)
logger.debug('Conducted processing in {0}s ({1}Hz)'.format(dt, hz)) logger.debug('Conducted processing in {0}s ({1}Hz)'.format(dt, hz))
self.mask = self.closing(self.mask) self.mask = self.closing(self.mask)
self.mask = self.grab_cut_mask(img, self.mask) self.mask = self.grab_cut_mask(img, self.mask)
...@@ -143,11 +143,12 @@ class SkinDetector(object): ...@@ -143,11 +143,12 @@ class SkinDetector(object):
def threshold(self, threshold): def threshold(self, threshold):
assert isinstance(threshold, float), 'threshold must be a float (current type - {0})'.format(type(threshold)) assert isinstance(threshold, float), 'threshold must be a float (current type - {0})'.format(type(threshold))
assert 0 <= threshold <= 1, 'threshold must be between 0 & 1 (current value - {0})'.format(threshold) assert 0 <= threshold <= 1, 'threshold must be between 0 & 1 (current value - {0})'.format(threshold)
assert self.n_mask > 0, 'Number of masks must be greater than 0 [n_mask ({0}) = {1}]'.format(type(self.n_mask), self.n_mask) assert self.n_mask > 0, 'Number of masks must be greater than 0 [n_mask ({0}) = {1}]'.format(
logger.debug('Threshold Value - {0}%'.format(int(100*threshold))) type(self.n_mask), self.n_mask)
logger.debug('Threshold Value - {0}%'.format(int(100 * threshold)))
logger.debug('Number of Masks - {0}'.format(self.n_mask)) logger.debug('Number of Masks - {0}'.format(self.n_mask))
self.mask /= self.n_mask self.mask /= self.n_mask
self.mask[self.mask < threshold] = 0 self.mask[self.mask < threshold] = 0
self.mask[self.mask >= threshold] = 255 self.mask[self.mask >= threshold] = 255
logger.debug('{0}% of the image is skin'.format(int((100.0/255.0)*numpy.sum(self.mask)/(self.mask.size)))) logger.debug('{0}% of the image is skin'.format(int((100.0 / 255.0) * numpy.sum(self.mask) / (self.mask.size))))
return self.mask return self.mask
\ No newline at end of file
#!/usr/bin/env python
# -*- coding: utf-8 -*-
__author__ = 'willbrennan'
from SkinDetector import SkinDetector
__all__ = ["SkinDetector"]
\ No newline at end of file
...@@ -2,7 +2,6 @@ ...@@ -2,7 +2,6 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
__author__ = 'Will Brennan' __author__ = 'Will Brennan'
# Built-in Modules # Built-in Modules
import argparse import argparse
import logging import logging
...@@ -12,35 +11,31 @@ import cv2 ...@@ -12,35 +11,31 @@ import cv2
import scripts import scripts
from SkinDetector import SkinDetector from SkinDetector import SkinDetector
if __name__ == '__main__': if __name__ == '__main__':
parser = argparse.ArgumentParser(description=__doc__) parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument('image_paths', type=str, nargs='+', help="paths to one or more images or image directories")
parser.add_argument('-b', '--debug', dest='debug', action='store_true', help='enable debug logging') parser.add_argument('-b', '--debug', dest='debug', action='store_true', help='enable debug logging')
parser.add_argument('-q', '--quite', dest='quite', action='store_true', help='disable all logging')
parser.add_argument('-t', '--thresh', dest='thresh', default=0.5, type=float, help='threshold for skin mask') parser.add_argument('-t', '--thresh', dest='thresh', default=0.5, type=float, help='threshold for skin mask')
args = parser.parse_args() args = parser.parse_args()
logger = logging.getLogger('main') if args.debug:
if not args.quite: logging.basicConfig(level=logging.DEBUG)
if args.debug: else:
level = logging.DEBUG logging.basicConfig(level=logging.INFO)
else: logger = logging.getLogger("main")
level = logging.INFO
ch = logging.StreamHandler()
ch.setLevel(level=level)
formatter = logging.Formatter('%(asctime)s - %(funcName)s - %(levelname)s - %(message)s')
ch.setFormatter(formatter)
logger.addHandler(ch)
detector = SkinDetector(args) detector = SkinDetector(thresh=args.thresh, debug=args.debug)
cam = cv2.VideoCapture(0) cam = cv2.VideoCapture(0)
logging.info("press any key to exit")
while True: while True:
ret, img_col = cam.read() ret, img_col = cam.read()
img_msk = detector.process(img_col) img_msk = detector.process(img_col)
if not args.display:
scripts.display('img_col', img_col) scripts.display('img_col', img_col)
scripts.display('img_msk', img_msk) scripts.display('img_msk', img_msk)
scripts.display('img_skn', cv2.bitwise_and(img_col, img_col, mask=img_msk)) scripts.display('img_skn', cv2.bitwise_and(img_col, img_col, mask=img_msk))
cv2.waitKey(5)
\ No newline at end of file waitkey = cv2.waitKey(5)
if waitkey != -1:
break
...@@ -2,17 +2,18 @@ ...@@ -2,17 +2,18 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
__author__ = 'Will Brennan' __author__ = 'Will Brennan'
# Built-in Modules # Built-in Modules
# Standard Modules # Standard Modules
import cv2 import cv2
import numpy import numpy
# Custom Modules # Custom Modules
def display(title, img, max_size=200000): def display(title, img, max_size=200000):
assert isinstance(img, numpy.ndarray), 'img must be a numpy array' assert isinstance(img, numpy.ndarray), 'img must be a numpy array'
assert isinstance(title, str), 'title must be a string' assert isinstance(title, str), 'title must be a string'
scale = numpy.sqrt(min(1.0, float(max_size)/(img.shape[0]*img.shape[1]))) scale = numpy.sqrt(min(1.0, float(max_size) / (img.shape[0] * img.shape[1])))
shape = (int(scale*img.shape[1]), int(scale*img.shape[0])) shape = (int(scale * img.shape[1]), int(scale * img.shape[0]))
img = cv2.resize(img, shape) img = cv2.resize(img, shape)
cv2.imshow(title, img) cv2.imshow(title, img)
\ No newline at end of file
...@@ -2,11 +2,9 @@ ...@@ -2,11 +2,9 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
__author__ = 'Will Brennan' __author__ = 'Will Brennan'
# Built-in Modules # Built-in Modules
# Standard Modules # Standard Modules
# Custom Modules # Custom Modules
""" """
Will automatically ensure that all build prerequisites are available Will automatically ensure that all build prerequisites are available
via ez_setup. via ez_setup.
...@@ -25,5 +23,4 @@ setup( ...@@ -25,5 +23,4 @@ setup(
author='Will Brennan', author='Will Brennan',
author_email='william.brennan@skytales.com', author_email='william.brennan@skytales.com',
license='GPL', license='GPL',
install_requires=["numpy"], install_requires=["numpy"], )
)
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment