-
-
Save ncouture/7957f6a0f222bd61823b54e6bb73ed38 to your computer and use it in GitHub Desktop.
Simple Python app show you easy way to identify particular face among the other faces
This file contains hidden or 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
{ | |
"cells": [ | |
{ | |
"cell_type": "code", | |
"execution_count": null, | |
"metadata": { | |
"collapsed": false | |
}, | |
"outputs": [], | |
"source": [ | |
"import numpy as np\n", | |
"import httplib2\n", | |
"import os\n", | |
"\n", | |
"# matplot\n", | |
"from matplotlib import pyplot as plt\n", | |
"\n", | |
"from face_tools import *\n", | |
"\n", | |
"%matplotlib inline" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": null, | |
"metadata": { | |
"collapsed": false | |
}, | |
"outputs": [], | |
"source": [ | |
"os.environ[\"GOOGLE_APPLICATION_CREDENTIALS\"] = '{}/google_cloud_vision.json'.format(os.getcwd())\n", | |
"\n", | |
"group = '{}/../../test/g7.jpg'.format(os.getcwd())\n", | |
"single = '{}/../../test/s7.jpg'.format(os.getcwd())\n", | |
"# group = '/Users/alex/Downloads/g.jpg'\n", | |
"# single = '/Users/alex/Downloads/s.png'" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": null, | |
"metadata": { | |
"collapsed": false | |
}, | |
"outputs": [], | |
"source": [ | |
"# plot group image\n", | |
"fig = plt.figure(figsize=(15,15))\n", | |
"plt.subplot(121)\n", | |
"plt.imshow(Image.open(group)) \n", | |
"plt.axis(\"off\")\n", | |
"\n", | |
"# plot single image\n", | |
"plt.subplot(122)\n", | |
"plt.imshow(Image.open(single)) \n", | |
"plt.axis(\"off\")" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": null, | |
"metadata": { | |
"collapsed": false | |
}, | |
"outputs": [], | |
"source": [ | |
"result = dict(group=dict(image_file=group, original_image=None, labeled_image=None, faces=None, total_faces=None, faces_matrix=None),\n", | |
" single=dict(image_file=single, original_image=None, labeled_image=None, faces=None, total_faces=None, faces_matrix=None))\n", | |
"\n", | |
"for item in result:\n", | |
" with open(result[item]['image_file'], 'rb') as image:\n", | |
" result[item]['original_image'] = Image.open(result[item]['image_file'])\n", | |
" result[item]['faces'], result[item]['total_faces'] = detect_face(image)\n", | |
" \n", | |
" print('{}: found {} face{}'.format(item, result[item]['total_faces'], '' if result[item]['total_faces'] == 1 else 's'))\n", | |
"\n", | |
" # Reset the file pointer, so we can read the file again\n", | |
" image.seek(0)\n", | |
" \n", | |
" result[item]['labeled_image'] = highlight_faces(image, result[item]['faces'])" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": null, | |
"metadata": { | |
"collapsed": false | |
}, | |
"outputs": [], | |
"source": [ | |
"# plot group image\n", | |
"fig = plt.figure(figsize=(15,15))\n", | |
"plt.subplot(121)\n", | |
"plt.imshow(result['group']['labeled_image']) \n", | |
"plt.axis(\"off\")\n", | |
"\n", | |
"# plot single image\n", | |
"plt.subplot(122)\n", | |
"plt.imshow(result['single']['labeled_image']) \n", | |
"plt.axis(\"off\")" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"Show detected faces\n", | |
"==" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": null, | |
"metadata": { | |
"collapsed": false | |
}, | |
"outputs": [], | |
"source": [ | |
"for item in result:\n", | |
" \n", | |
" fig = plt.figure(figsize=(15,2))\n", | |
"\n", | |
" for i, face in enumerate(result[item]['faces']):\n", | |
" pos = i+1\n", | |
" plt.subplot(1, result[item]['total_faces'], pos)\n", | |
" vertices = face['fdBoundingPoly']['vertices']\n", | |
" box = (vertices[0]['x'], vertices[1]['y'], vertices[1]['x'], vertices[2]['y'],) \n", | |
" cropped_face = result[item]['original_image'].crop(box)\n", | |
" plt.imshow(cropped_face) \n", | |
" plt.axis(\"off\")" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"Create matrix\n", | |
"==" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": null, | |
"metadata": { | |
"collapsed": false | |
}, | |
"outputs": [], | |
"source": [ | |
"'''Transform faces to matrices.'''\n", | |
"\n", | |
"for item in result:\n", | |
" result[item]['faces_matrix'] = np.zeros(shape=(result[item]['total_faces'], FACE_RESOLUTION ** 2))\n", | |
" \n", | |
" fig = plt.figure(figsize=(15,15))\n", | |
"\n", | |
" for i in range(result[item]['total_faces']):\n", | |
" vertices = result[item]['faces'][i]['fdBoundingPoly']['vertices']\n", | |
" \n", | |
" box = (vertices[0]['x'], vertices[1]['y'], vertices[1]['x'], vertices[2]['y'],)\n", | |
" rotated_box = (vertices[0]['x']-50, vertices[1]['y']-50, vertices[1]['x']+50, vertices[2]['y']+50,)\n", | |
" original_face = result[item]['original_image'].crop(rotated_box)\n", | |
" \n", | |
" pos = i*3+1\n", | |
" plt.subplot(result[item]['total_faces'], 3, pos)\n", | |
" plt.imshow(original_face) \n", | |
" plt.axis(\"off\")\n", | |
" \n", | |
" rotated_face = original_face.rotate(result[item]['faces'][i]['rollAngle'])\n", | |
" \n", | |
" cropped_face = rotated_face.crop((50, 50, box[2]-box[0]+50, box[3]-box[1]+50,))\n", | |
"\n", | |
" pos = i*3+2\n", | |
" plt.subplot(result[item]['total_faces'], 3, pos)\n", | |
" plt.imshow(cropped_face) \n", | |
" plt.axis(\"off\")\n", | |
" \n", | |
" scaled_face = cropped_face.resize(([FACE_RESOLUTION, FACE_RESOLUTION]))\n", | |
" \n", | |
" pos = i*3+3\n", | |
" plt.subplot(result[item]['total_faces'], 3, pos)\n", | |
" plt.imshow(scaled_face) \n", | |
" plt.axis(\"off\")\n", | |
" \n", | |
" converted_face = scaled_face.convert('L')\n", | |
" \n", | |
"# lbp_features = calc_lbp_features(converted_face)\n", | |
" \n", | |
" result[item]['faces_matrix'][i] = np.asmatrix(converted_face).A1\n", | |
"# result[item]['faces_matrix'][i] = np.asmatrix(lbp_features).A1 " | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"Calc disimilarity matrix\n", | |
"==" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": null, | |
"metadata": { | |
"collapsed": false | |
}, | |
"outputs": [], | |
"source": [ | |
"predicted = np.argmin(np.linalg.norm(result['group']['faces_matrix']-result['single']['faces_matrix'], axis=1))" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"Show result\n", | |
"==" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": null, | |
"metadata": { | |
"collapsed": false | |
}, | |
"outputs": [], | |
"source": [ | |
"draw = ImageDraw.Draw(result['group']['original_image'])\n", | |
"box = [(v.get('x', 0.0), v.get('y', 0.0)) for v in result['group']['faces'][predicted]['fdBoundingPoly']['vertices']]\n", | |
"print(box)\n", | |
"draw.line(box + [box[0]], width=5, fill='#00ff00')\n", | |
"del draw\n", | |
" \n", | |
" \n", | |
"# plot group image\n", | |
"fig = plt.figure(figsize=(15,15))\n", | |
"plt.subplot(111)\n", | |
"plt.imshow(result['group']['original_image']) \n", | |
"plt.axis(\"off\")" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"Link to gist\n", | |
"==\n", | |
"\n", | |
"https://gist.github.com/onidzelskyi/2878f78d499da7d6a5c4e0b2b2cd8111\n", | |
"\n", | |
"Requirements\n", | |
"==\n", | |
"\n", | |
"```bash\n", | |
"sudo apt-get install python-pip libtiff5-dev libjpeg8-dev zlib1g-dev libfreetype6-dev liblcms2-dev libwebp-dev tcl8.6-dev tk8.6-dev python-tk python-matplotlib\n", | |
"\n", | |
"sudo pip install google-api-python-client==1.5.0 Pillow==3.1.1 numpy\n", | |
"```" | |
] | |
} | |
], | |
"metadata": { | |
"kernelspec": { | |
"display_name": "Python 3", | |
"language": "python", | |
"name": "python3" | |
}, | |
"language_info": { | |
"codemirror_mode": { | |
"name": "ipython", | |
"version": 3 | |
}, | |
"file_extension": ".py", | |
"mimetype": "text/x-python", | |
"name": "python", | |
"nbconvert_exporter": "python", | |
"pygments_lexer": "ipython3", | |
"version": "3.5.2" | |
} | |
}, | |
"nbformat": 4, | |
"nbformat_minor": 0 | |
} |
This file contains hidden or 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
import base64 | |
from googleapiclient import discovery | |
from oauth2client.client import GoogleCredentials | |
from PIL import Image, ImageDraw | |
# Google' variables and environments | |
DISCOVERY_URL='https://{api}.googleapis.com/$discovery/rest?version={apiVersion}' | |
# variables | |
FACE_RESOLUTION = 100 # Face' size 100 by 100 pixels | |
def get_vision_service(): | |
credentials = GoogleCredentials.get_application_default() | |
return discovery.build('vision', 'v1', credentials=credentials, | |
discoveryServiceUrl=DISCOVERY_URL) | |
def detect_face(face_file, max_results=10): | |
"""Uses the Vision API to detect faces in the given file. | |
Args: | |
face_file: A file-like object containing an image with faces. | |
Returns: | |
An array of dicts with information about the faces in the picture. | |
""" | |
image_content = face_file.read() | |
batch_request = [{ | |
'image': { | |
'content': base64.b64encode(image_content).decode('UTF-8') | |
}, | |
'features': [{ | |
'type': 'FACE_DETECTION', | |
'maxResults': max_results, | |
}] | |
}] | |
service = get_vision_service() | |
request = service.images().annotate(body={ | |
'requests': batch_request, | |
}) | |
response = request.execute() | |
if not response['responses'][0]: | |
raise ValueError('No faces were detected in {}.'.format(face_file)) | |
return response['responses'][0]['faceAnnotations'], len(response['responses'][0]['faceAnnotations']) | |
def highlight_faces(image, faces, output_filename=None): | |
"""Draws a polygon around the faces, then saves to output_filename. | |
Args: | |
image: a file containing the image with the faces. | |
faces: a list of faces found in the file. This should be in the format | |
returned by the Vision API. | |
output_filename: the name of the image file to be created, where the faces | |
have polygons drawn around them. | |
""" | |
im = Image.open(image) | |
draw = ImageDraw.Draw(im) | |
for face in faces: | |
box = [(v.get('x', 0.0), v.get('y', 0.0)) for v in face['fdBoundingPoly']['vertices']] | |
draw.line(box + [box[0]], width=15, fill='#00ff00') | |
del draw | |
if output_filename: | |
im.save(output_filename) | |
return im | |
def extract_faces(image, faces): | |
"""Extract faces from image. | |
Args: | |
image: a file containing the image with the faces. | |
faces: a list of faces found in the file. This should be in the format | |
returned by the Vision API. | |
Return array of faces | |
""" | |
arr = np.zeros() | |
I = Image.open(image) | |
for face in faces: | |
box = [(v.get('x', 0.0), v.get('y', 0.0)) for v in face['fdBoundingPoly']['vertices']] | |
Arr.line(box + [box[0]], width=5, fill='#00ff00') | |
return arr | |
def calc_lbp_features(img): | |
from skimage.feature import local_binary_pattern | |
radius = 2 | |
n_points = 8 * radius | |
METHOD = 'uniform' | |
return local_binary_pattern(img, n_points, radius, METHOD) |
This file contains hidden or 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
{ | |
"type": "service_account", | |
"project_id": "amiable-hour-128508", | |
"private_key_id": "b45e3f6b9e403fdd0ac1ab8f836ba28763f7739a", | |
"private_key": "-----BEGIN PRIVATE KEY-----\nMIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCLw1Mv6RxL0eUU\n20uLcJXmVdu6MD+Vo0xb/N+3A12sRlfi6Jqe0ncxiY+vpQrujxgB0tuRuOLKKEue\nSACZ9pHPwcpLJnZeBIlBfAVQdHYH3FD+0HEPKWydwpPRAgL1iy684CUtXZ/n9fsv\neCugh5ziRNC9CbUHz+19jJ4H0vw9tLO8zNOL2ZJGL7BribX6F2MOQT+BkDbQlJlT\nlNU+7mU0I/i5s9uQqppo7bR7D7x8s5pyssB9ZzH1x+uKHqCAVPH+0JHLz7KXUd9j\nhtztuga2+LYjd3KTlDC/9XxvyJX4oBC6RHR6Wc1qC6fkfiyj/8WKbyzI4czw3Jt4\nryFwufHpAgMBAAECggEADIJ9Laql17t/z7AZQO/g2u8QbcfnyRMGwK0PsSfh+IuL\nTkoXnOOlu49ehxA0vEmekns3IAH2erXEnvQ+6YkCYCAuZS+aJKwvROc+6oaiEe4M\naeJqXDsO98f0xx5w+V1t34YIjdj1eSOxSfxtmz7IMhCKxoDZ7UsVLMmrU3P1E+i4\nbxzYwyaTtT3RbPBVDHHCpIx/M22Aw10j+29PjD2f/a4SIQdnwsSOzGwY1PNLICkM\nhAWaQW8f4dmFG3RJgkNphG8m2oGChZEy1SL7rWVi+OoNUQncjU2eb2EtcM5uIxaH\noMPVhyGxynbPLQQDRmoGxxC2Sfl7YvZHXIjm3835IQKBgQDdJ4obPC9VXz1E2ccu\njSKv1A4LvCSo0//+fPQJu2VhPQhtE97BmoEjthW5hqS6j+I2to3BmKMJtufKd6CO\nunecxXikc+4Ex0C+68gdabGifmcn3/GeKJ1BY+O39lt16fVb5iYHkJu8Nw+AVioZ\naq7wCCMNU7LW1BjLQ4RQJ1n6NQKBgQChyMntHgeuHZtIct9VNEYsthd1D9dtesuf\nFxLUnFwOCHlh90mW2wDPErpsjRHjfL1I+s7sQdYq0lJtfUP8A/g5vJZdhDSNeJ80\nnYvqAX+w142H2DNz62/rkBtVMI0HjUjrJjAhi8Xh4pJL34lkYMxJdcA8qt9M2hh2\nnsSMTXCvZQKBgE1vQcElgqFO1qIb6s0W59VO15guSVu/1zuhPsWFkwrIFwIyWeXK\nFnuNju/GADm+lYN4ygvGSRfmriRLXLF9AtKmpgZW/Tk5FyeCYTBDSMpPngjtName\nKOmzcxF/tiiEQb/qxtgRPyZY1RmnkdgSbaxn/PX2n4DvBLtvKY3zKWMdAoGBAIyF\nFPZSXgNajdR4igi8lbMiGe/AabIatR+ib+WhRceejqeLnSxdXi99qdS9FuuLHfF0\nfyPv7zBUa+nToqgTXpfveH8TZH34ZffSW4NN9/XG05RJMi+eMCDfkw4EVIpZqhss\nmSOZ/q87Fyy1pOU9ulKX5vgKxlc/G350Wm0umhCNAoGAQkNlZFKGV00iRQzLHtps\n4djapGJM8gcO14ZUZK8mkkb53IWMeTbI4wDwoh7qbZaSzUErINeyZW5regribwiU\n3eU8AECdqrV5a9mcf+ta4kMTmSnk+cg8r2h7BNpR2y8Ux2N2w5ZhhxVjByxfkPTL\n85yY46iU0goNXBdoCq8zWcU=\n-----END PRIVATE KEY-----\n", | |
"client_email": "[email protected]", | |
"client_id": "106183748320275873602", | |
"auth_uri": "https://accounts.google.com/o/oauth2/auth", | |
"token_uri": "https://accounts.google.com/o/oauth2/token", | |
"auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs", | |
"client_x509_cert_url": "https://www.googleapis.com/robot/v1/metadata/x509/996116364663-compute%40developer.gserviceaccount.com" | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment