Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
  • Loading branch information
azmathmoosa committed Jul 31, 2018
2 parents 73230eb + 640ebfb commit ccd6670
Show file tree
Hide file tree
Showing 11 changed files with 250 additions and 0 deletions.
2 changes: 2 additions & 0 deletions .gitignore
@@ -0,0 +1,2 @@
examples/face_comparison/facedb.json
examples/face_comparison/__pycache__/utils.cpython-36.pyc
39 changes: 39 additions & 0 deletions examples/face_comparison/README.md
@@ -0,0 +1,39 @@
This example demonstrates performing face recognition using deepsight SDK.

![face_comparison](https://github.com/baseapp/DeepSight-Face-Recognition-SDK/blob/master/examples/face_comparison/usage.gif)


### Running

* To run this example, install the following dependencies

```sh
pip install requests
pip install opencv-python
pip install scipy
```
* Next, start `Deepsight Face SDK` and let it run.
* In the folder `facedb/` add images of people you want to add to db
* Each person should have a separate folder
* Next, run the appliation with `python face_comparison.py --scan ./facedb/` to scan the folder and update the db
* Then, run the application with `python face_comparison.py <image_path>` to compare a new face with the db

### Usage

* The application accepts arguments as follows

```sh
usage: face_comparison.py [-h] [--scan] path [path ...]

Face Comparison Application

positional arguments:
path path to image (or folder in scan mode)

optional arguments:
-h, --help show this help message and exit
--scan scan folder and create facedb
```

* `scan` arg is used to scan a folder for new faces and save as a db.
* `path` arg points to the scanning folder path in scan mode or image path in comparison mode.
145 changes: 145 additions & 0 deletions examples/face_comparison/face_comparison.py
@@ -0,0 +1,145 @@
"""
* Author: Azmath Moosa
* Created: 4th June 2018
* Description: Performs face recognition and compares against a db
* Dependencies: Deepsight Face, Python 3 packages:- requests, argparse, opencv-python, scipy, numpy
"""

import cv2
import requests
import numpy as np
import json
import argparse
import logging
import datetime, time
from scipy import spatial
import os
import glob
import utils

face_api = "http://127.0.0.1:5000/inferImage?returnFaceId=true&detector=mmod&returnFaceLandmarks=true"

# init logger
logger = logging.getLogger('FaceComparison')
logger.setLevel(logging.DEBUG)
# create console handler with a higher log level
ch = logging.StreamHandler()
ch.setLevel(logging.DEBUG)
# create formatter and add it to the handlers
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
ch.setFormatter(formatter)
# add the handlers to the logger
logger.addHandler(ch)


# parse arguments
parser = argparse.ArgumentParser(description='Face Comparison Application')
parser.add_argument('path', metavar='path', type=str, nargs='+',
help='path to image (or folder in scan mode)')
parser.add_argument('--scan', action='store_true',
help='scan folder and create facedb')
args = parser.parse_args()

# initialize database
db = {"names":[],"embeddings":[]}
dbtree = ""
try:
db = json.loads(open('facedb.json').read())
# kdtree for similarity search
dbtree = spatial.KDTree(db["embeddings"])
except:
pass


def get_face_embedding_crop(imgpath):
frame = cv2.imread(imgpath)

# encode as bmp (optional)
r, imgbuf = cv2.imencode(".bmp", frame)
image = {'pic':bytearray(imgbuf)}

# post request
r = requests.post(face_api, files=image)
result = r.json()

if len(result) == 2:
faces = result[:-1]
diag = result[-1]['diagnostics']
#print(diag)

for face in faces:
rect, embedding = [face[i] for i in ['faceRectangle','faceEmbeddings']]
x,y,w,h, confidence = [rect[i] for i in ['left', 'top', 'width', 'height', 'confidence']]

if confidence < 0.8:
continue

crop = frame[y:y+h, x:x+w]

return embedding, crop
else:
RuntimeError("no faces or more than 1 face in %s"%imgpath)

def save_db():
with open("facedb.json","w") as f:
f.write(json.dumps(db))

# search for a face in the db
def identify_face(embedding):
if dbtree != "":
dist, idx = dbtree.query(embedding)
name = db["names"][idx]
if dist > 0.6:
name = "unknown"
else:
name = "unknown"

return name

if __name__ == "__main__":

if args.scan:
db = {"names":[],"embeddings":[]}
path = args.path[0]
dirs = glob.glob(os.path.join(path, "*/"))
for d in dirs:
name = os.path.basename(os.path.dirname(d))
imgs = glob.glob(os.path.join(d, "*.jpg"))
imgs.extend(glob.glob(os.path.join(d,"*.png")))

crops = []
for img in imgs:
try:
emb, crop = get_face_embedding_crop(img)
db["names"].append(name)
db["embeddings"].append(emb)
crops.append(crop)
except Exception as e:
logger.error("ERROR:%s"%e)

# convert image list into a montage of 256x256 images tiled in a 5x5 montage
montages = utils.build_montages(crops, (150, 150), (2, 3))
# iterate through montages and display
for montage in montages:
cv2.destroyAllWindows()
cv2.imshow(name, montage)
cv2.waitKey(1000)

logger.info("Saved %d faces"%len(db["names"]))

save_db()
exit(0)
else:

for path in args.path:
# start the camera
emb, crop = get_face_embedding_crop(path)
name = identify_face(emb)

logger.info("Face in %s similar to %s"%(path, name))
cv2.imshow("Input", crop)
cv2.waitKey(4000)

print("Exiting")
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added examples/face_comparison/facedb/Rock/2.jpg
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added examples/face_comparison/facedb/Rock/rock.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added examples/face_comparison/usage.gif
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
64 changes: 64 additions & 0 deletions examples/face_comparison/utils.py
@@ -0,0 +1,64 @@
import numpy as np
import cv2

def build_montages(image_list, image_shape, montage_shape):
"""
---------------------------------------------------------------------------------------------
author: Kyle Hounslow
---------------------------------------------------------------------------------------------
Converts a list of single images into a list of 'montage' images of specified rows and columns.
A new montage image is started once rows and columns of montage image is filled.
Empty space of incomplete montage images are filled with black pixels
---------------------------------------------------------------------------------------------
:param image_list: python list of input images
:param image_shape: tuple, size each image will be resized to for display (width, height)
:param montage_shape: tuple, shape of image montage (width, height)
:return: list of montage images in numpy array format
---------------------------------------------------------------------------------------------
example usage:
# load single image
img = cv2.imread('lena.jpg')
# duplicate image 25 times
num_imgs = 25
img_list = []
for i in xrange(num_imgs):
img_list.append(img)
# convert image list into a montage of 256x256 images tiled in a 5x5 montage
montages = make_montages_of_images(img_list, (256, 256), (5, 5))
# iterate through montages and display
for montage in montages:
cv2.imshow('montage image', montage)
cv2.waitKey(0)
----------------------------------------------------------------------------------------------
"""
if len(image_shape) != 2:
raise Exception('image shape must be list or tuple of length 2 (rows, cols)')
if len(montage_shape) != 2:
raise Exception('montage shape must be list or tuple of length 2 (rows, cols)')
image_montages = []
# start with black canvas to draw images onto
montage_image = np.zeros(shape=(image_shape[1] * (montage_shape[1]), image_shape[0] * montage_shape[0], 3),
dtype=np.uint8)
cursor_pos = [0, 0]
start_new_img = False
for img in image_list:
if type(img).__module__ != np.__name__:
raise Exception('input of type {} is not a valid numpy array'.format(type(img)))
start_new_img = False
img = cv2.resize(img, image_shape)
# draw image to black canvas
montage_image[cursor_pos[1]:cursor_pos[1] + image_shape[1], cursor_pos[0]:cursor_pos[0] + image_shape[0]] = img
cursor_pos[0] += image_shape[0] # increment cursor x position
if cursor_pos[0] >= montage_shape[0] * image_shape[0]:
cursor_pos[1] += image_shape[1] # increment cursor y position
cursor_pos[0] = 0
if cursor_pos[1] >= montage_shape[1] * image_shape[1]:
cursor_pos = [0, 0]
image_montages.append(montage_image)
# reset black canvas
montage_image = np.zeros(shape=(image_shape[1] * (montage_shape[1]), image_shape[0] * montage_shape[0], 3),
dtype=np.uint8)
start_new_img = True
if start_new_img is False:
image_montages.append(montage_image) # add unfinished montage
return image_montages

0 comments on commit ccd6670

Please sign in to comment.