Skip to content

Commit

Permalink
Merge pull request oarriaga#16 from ekholabs/master
Browse files Browse the repository at this point in the history
Dockerize the project and add a RESTful service to process images
  • Loading branch information
oarriaga authored Nov 30, 2017
2 parents f4e8817 + 6d9e711 commit 5bc94fd
Show file tree
Hide file tree
Showing 5 changed files with 153 additions and 2 deletions.
17 changes: 17 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
FROM debian:latest

RUN apt-get -y update && apt-get install -y git python3-pip python3-dev python3-tk vim procps curl

#Face classificarion dependencies & web application
RUN pip3 install numpy scipy scikit-learn pillow tensorflow pandas h5py opencv-python==3.2.0.8 keras statistics pyyaml pyparsing cycler matplotlib Flask

ADD . /ekholabs/face-classifier

WORKDIR ekholabs/face-classifier

ENV PYTHONPATH=$PYTHONPATH:src
ENV FACE_CLASSIFIER_PORT=8084
EXPOSE $FACE_CLASSIFIER_PORT

ENTRYPOINT ["python3"]
CMD ["src/web/faces.py"]
12 changes: 10 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,16 @@ Real-time face detection and emotion/gender classification using fer2013/IMDB da

For more information please consult the [publication](https://github.com/oarriaga/face_classification/blob/master/report.pdf)

Emotion/gender examples:
# Running with Docker

With a few steps one can get its own face classification and detection running. Follow the commands below:

* ```docker pull ekholabs/face-classifier```
* ```docker run -d -p 8084:8084 --name=face-classifier ekholabs/face-classifier```
* ```curl -v -F image=@[path_to_image] http://localhost:8084/classifyImage > image.png```

# Emotion/gender examples:

![alt tag](images/demo_results.png)

Guided back-prop
Expand All @@ -19,7 +28,6 @@ Real-time demo:
[B-IT-BOTS](https://mas-group.inf.h-brs.de/?page_id=622) robotics team :)
![alt tag](images/robocup_team.png)


## Instructions

### To train previous/new models for emotion classification:
Expand Down
1 change: 1 addition & 0 deletions src/web/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@

94 changes: 94 additions & 0 deletions src/web/emotion_gender_processor.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
import os
import sys
import logging

import cv2
from keras.models import load_model
import numpy as np

from utils.datasets import get_labels
from utils.inference import detect_faces
from utils.inference import draw_text
from utils.inference import draw_bounding_box
from utils.inference import apply_offsets
from utils.inference import load_detection_model
from utils.inference import load_image
from utils.preprocessor import preprocess_input

def process_image(image):

try:
# parameters for loading data and images
detection_model_path = './trained_models/detection_models/haarcascade_frontalface_default.xml'
emotion_model_path = './trained_models/emotion_models/fer2013_mini_XCEPTION.102-0.66.hdf5'
gender_model_path = './trained_models/gender_models/simple_CNN.81-0.96.hdf5'
emotion_labels = get_labels('fer2013')
gender_labels = get_labels('imdb')
font = cv2.FONT_HERSHEY_SIMPLEX

# hyper-parameters for bounding boxes shape
gender_offsets = (30, 60)
gender_offsets = (10, 10)
emotion_offsets = (20, 40)
emotion_offsets = (0, 0)

# loading models
face_detection = load_detection_model(detection_model_path)
emotion_classifier = load_model(emotion_model_path, compile=False)
gender_classifier = load_model(gender_model_path, compile=False)

# getting input model shapes for inference
emotion_target_size = emotion_classifier.input_shape[1:3]
gender_target_size = gender_classifier.input_shape[1:3]

# loading images
image_array = np.fromstring(image, np.uint8)
unchanged_image = cv2.imdecode(image_array, cv2.IMREAD_UNCHANGED)

rgb_image = cv2.cvtColor(unchanged_image, cv2.COLOR_BGR2RGB)
gray_image = cv2.cvtColor(unchanged_image, cv2.COLOR_BGR2GRAY)

faces = detect_faces(face_detection, gray_image)
for face_coordinates in faces:
x1, x2, y1, y2 = apply_offsets(face_coordinates, gender_offsets)
rgb_face = rgb_image[y1:y2, x1:x2]

x1, x2, y1, y2 = apply_offsets(face_coordinates, emotion_offsets)
gray_face = gray_image[y1:y2, x1:x2]

try:
rgb_face = cv2.resize(rgb_face, (gender_target_size))
gray_face = cv2.resize(gray_face, (emotion_target_size))
except:
continue

rgb_face = preprocess_input(rgb_face, False)
rgb_face = np.expand_dims(rgb_face, 0)
gender_prediction = gender_classifier.predict(rgb_face)
gender_label_arg = np.argmax(gender_prediction)
gender_text = gender_labels[gender_label_arg]

gray_face = preprocess_input(gray_face, True)
gray_face = np.expand_dims(gray_face, 0)
gray_face = np.expand_dims(gray_face, -1)
emotion_label_arg = np.argmax(emotion_classifier.predict(gray_face))
emotion_text = emotion_labels[emotion_label_arg]

if gender_text == gender_labels[0]:
color = (0, 0, 255)
else:
color = (255, 0, 0)

draw_bounding_box(face_coordinates, rgb_image, color)
draw_text(face_coordinates, rgb_image, gender_text, color, 0, -20, 1, 2)
draw_text(face_coordinates, rgb_image, emotion_text, color, 0, -50, 1, 2)
except Exception as err:
logging.error('Error in emotion gender processor: "{0}"'.format(err))

bgr_image = cv2.cvtColor(rgb_image, cv2.COLOR_RGB2BGR)

dirname = 'result'
if not os.path.exists(dirname):
os.mkdir(dirname)

cv2.imwrite(os.path.join(dirname, 'predicted_image.png'), bgr_image)
31 changes: 31 additions & 0 deletions src/web/faces.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
from flask import Flask, jsonify, make_response, request, abort, redirect, send_file
import logging

import emotion_gender_processor as eg_processor

app = Flask(__name__)

@app.route('/')
def index():
return redirect("https://ekholabs.ai", code=302)

@app.route('/classifyImage', methods=['POST'])
def upload():
try:
image = request.files['image'].read()
eg_processor.process_image(image)
return send_file('/ekholabs/face-classifier/result/predicted_image.png', mimetype='image/png')
except Exception as err:
logging.error('An error has occurred whilst processing the file: "{0}"'.format(err))
abort(400)

@app.errorhandler(400)
def bad_request(erro):
return make_response(jsonify({'error': 'We cannot process the file sent in the request.'}), 400)

@app.errorhandler(404)
def not_found(error):
return make_response(jsonify({'error': 'Resource no found.'}), 404)

if __name__ == '__main__':
app.run(debug=True, host='0.0.0.0', port=8084)

0 comments on commit 5bc94fd

Please sign in to comment.