Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge branch 'master' of https://github.com/baseapp/DeepSight-Face-Re…
- Loading branch information
Showing
11 changed files
with
250 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
examples/face_comparison/facedb.json | ||
examples/face_comparison/__pycache__/utils.cpython-36.pyc |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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.
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.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 |