Skip to content

Commit

Permalink
Various refactoring to extract data from AI module + update setup met…
Browse files Browse the repository at this point in the history
…adata
  • Loading branch information
davidfischer-ch committed Oct 3, 2018
1 parent f850f9d commit 643edc8
Show file tree
Hide file tree
Showing 7 changed files with 42 additions and 17 deletions.
1 change: 1 addition & 0 deletions AUTHORS
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,6 @@ Main Developers
Contributors
============

* Abdulhadi Mohamed @AbdulTheProgrammer
* Dimitri Racordon @kyouko-taiga
* Guillaume Martres @smarter
2 changes: 1 addition & 1 deletion README.rst
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
=========
pytoolbox
Pytoolbox
=========

.. image:: https://badge.fury.io/py/pytoolbox.png
Expand Down
24 changes: 16 additions & 8 deletions pytoolbox/ai/vision/face/detect/dlib.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,12 @@

# Original: https://github.com/krasserm/face-recognition/blob/master/align.py

import bz2, os, tempfile
import bz2, tempfile

import cv2, dlib, numpy as np

from ... import utils

__all__ = ['DlibFaceDetector']

TEMPLATE = np.float32([
Expand Down Expand Up @@ -78,8 +80,11 @@ class DlibFaceDetector(object):
"""

# Downloaded from http://dlib.net/files/
DEFAULT_PREDICTOR = os.path.join(
os.path.dirname(__file__), 'data', 'shape_predictor_68_face_landmarks.dat.bz2')
DEFAULT_PREDICTOR = (
'https://s3-eu-west-1.amazonaws.com/pytoolbox/ai/vision/face/detect/'
'shape_predictor_68_face_landmarks.dat.bz2')

PREDICTORS_DOWNLOAD_DIRECTORY = tempfile.gettempdir()

#: Landmark indices.
INNER_EYES_AND_BOTTOM_LIP = [39, 42, 57]
Expand All @@ -92,7 +97,9 @@ def __init__(self, predictor=None):
:param predictor: The path to dlib's landmarks file.
:type predictor: str
"""
predictor = predictor or self.DEFAULT_PREDICTOR
if predictor is None:
predictor = self.DEFAULT_PREDICTOR
predictor = utils.load_to_file(predictor)
if predictor.endswith('.bz2'):
decompressor = bz2.BZ2Decompressor()
with open(predictor, 'rb') as f, tempfile.NamedTemporaryFile('wb') as g:
Expand All @@ -102,7 +109,7 @@ def __init__(self, predictor=None):
self.predictor = dlib.shape_predictor(predictor)
self.detector = dlib.get_frontal_face_detector()

def align(self, image, box, dimension=96, landmark_indices=OUTER_EYES_AND_NOSE, landmarks=None):
def align(self, image, box, dimension=96, landmark_indices=None, landmarks=None):
"""
Transform and align a face in an image.
Expand All @@ -120,6 +127,8 @@ def align(self, image, box, dimension=96, landmark_indices=OUTER_EYES_AND_NOSE,
:return: The aligned RGB image. Shape: (dimension, dimension, 3)
:rtype: numpy.ndarray
"""
if landmark_indices is None:
landmark_indices = self.OUTER_EYES_AND_NOSE
if landmarks is None:
landmarks = self.find_landmarks(image, box)

Expand All @@ -132,12 +141,11 @@ def align(self, image, box, dimension=96, landmark_indices=OUTER_EYES_AND_NOSE,

return cv2.warpAffine(image, H, (dimension, dimension))

def extract_all_faces(self, image, dimension=96, landmark_indices=OUTER_EYES_AND_NOSE):
def extract_all_faces(self, image, dimension=96, landmark_indices=None):
for box in self.get_all_faces_bounding_boxes(image):
yield box, self.align(image, box, dimension, landmark_indices)

def extract_largest_face(self, image, dimension=96, landmark_indices=OUTER_EYES_AND_NOSE,
skip_multi=False):
def extract_largest_face(self, image, dimension=96, landmark_indices=None, skip_multi=False):
box = self.get_largest_face_bounding_box(image, skip_multi=skip_multi)
return box, (self.align(image, box, dimension, landmark_indices) if box else None)

Expand Down
14 changes: 8 additions & 6 deletions pytoolbox/ai/vision/face/recognize/nn4_small2.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,6 @@
# Code taken from https://github.com/iwantooxxoox/Keras-OpenFace (with minor modifications)
# --------------------------------------------------------------------------------------------------

import os

import tensorflow as tf
from keras import backend as K
from keras.layers import Conv2D, ZeroPadding2D, Activation, Input, concatenate
Expand All @@ -12,7 +10,11 @@
from keras.layers.pooling import MaxPooling2D, AveragePooling2D
from keras.models import Model

WEIGHTS_FILENAME = os.path.join(os.path.dirname(__file__), 'data', 'nn4.small2.v1.h5')
from ... import utils

DEFAULT_WEIGHTS = (
'https://s3-eu-west-1.amazonaws.com/pytoolbox/ai/vision/face/recognize/'
'nn4.small2.v1.h5')

E = 0.00001 # Epsilon

Expand Down Expand Up @@ -324,8 +326,8 @@ def create_model():
return Model(inputs=[inputs], outputs=outputs)


def load_model(weights_filename=WEIGHTS_FILENAME):
def load_model(weights=DEFAULT_WEIGHTS):
model = create_model()
if weights_filename:
model.load_weights(weights_filename)
if weights:
model.load_weights(utils.load_to_file(weights))
return model
12 changes: 12 additions & 0 deletions pytoolbox/ai/vision/utils.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,23 @@
import os, tempfile

import cv2, numpy as np

from pytoolbox.network.http import download_ext


def load_image(path):
"""Reverse channels because OpenCV loads images in BGR mode."""
return cv2.imread(path, 1)[..., ::-1]


def load_to_file(uri):
if uri.startswith('http'):
path = os.path.join(tempfile.gettempdir(), os.path.basename(uri))
download_ext(uri, path, force=False)
return path
return uri


def normalize_rgb(image):
"""Scale integer RGB values [0,255] to float32 [0.0,1.0]."""
return (image / 255).astype(np.float32)
5 changes: 3 additions & 2 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@
# -*- encoding: utf-8 -*-

# **************************************************************************************************
# PYTOOLBOX - TOOLBOX FOR PYTHON SCRIPTS
# PYTOOLBOX - TOOLBOX FOR PYTHON SCRIPTS
#
# Main Developer : David Fischer (david.fischer.ch@gmail.com)
# Copyright : Copyright (c) 2012-2015 David Fischer. All rights reserved.
# Copyright : Copyright (c) 2012-2018 David Fischer. All rights reserved.
#
# **************************************************************************************************
#
Expand Down Expand Up @@ -193,6 +193,7 @@ def run(self):
# https://pypi.python.org/pypi?%3Aaction=list_classifiers
'Development Status :: 5 - Production/Stable',
'Intended Audience :: Developers',
'Framework :: Django',
'Framework :: Flask',
'License :: OSI Approved :: European Union Public Licence 1.1 (EUPL 1.1)',
'Natural Language :: English',
Expand Down
1 change: 1 addition & 0 deletions tests/pytoolbox_runtests.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,5 +45,6 @@ def main():
print('Run the tests with nose')
return runtests(__file__, cover_packages=['pytoolbox'], packages=['pytoolbox', 'tests'])


if __name__ == '__main__':
main()

0 comments on commit 643edc8

Please sign in to comment.