upload
Browse files- .gitignore +108 -0
- Dockerfile +14 -0
- Info.txt +4 -0
- LICENSE +21 -0
- Medium/face_makeup.png +0 -0
- Medium/face_makeup_m.png +0 -0
- Medium/face_rec_HTML.jpg +0 -0
- Medium/face_rec_resp_web.jpg +0 -0
- Medium/face_rec_response.jpg +0 -0
- Medium/form_example.jpg +0 -0
- app.py +156 -0
- demo_client.py +40 -0
- face_makeup.png +0 -0
- face_util.py +96 -0
- find_facial_features_in_picture_w_api.py +47 -0
- flask_server.py +156 -0
- gists/demo_client_part1.py +13 -0
- gists/demo_client_part2.py +13 -0
- gists/demo_client_part2_v1.py +12 -0
- gists/demo_client_v1.py +22 -0
- gists/demo_client_v3.py +39 -0
- gists/face_util_part1.py +14 -0
- gists/face_util_part2.py +15 -0
- gists/face_util_part3.py +14 -0
- gists/face_util_part4.py +14 -0
- gists/flask_server_v1.py +30 -0
- gists/flask_server_v1_part1.py +19 -0
- gists/flask_server_v1_part2.py +9 -0
- gists/flask_server_v2.py +51 -0
- gists/flask_server_v2_part1.py +17 -0
- gists/flask_server_v2_part2.py +22 -0
- gists/flask_server_v3.py +149 -0
- gists/form-data_example.txt +12 -0
- gists/form-data_file.txt +9 -0
- requirements.txt +7 -0
- sample_images/biden.jpg +0 -0
- sample_images/obama.jpg +0 -0
- sample_images/obama2.jpg +0 -0
- sample_images/turnbull.jpg +0 -0
- sample_outputs.txt +13 -0
- ssl_keys/gen_ssl_keys.py +2 -0
- ssl_keys/key.crt +18 -0
- ssl_keys/key.key +28 -0
- wsgi.py +4 -0
.gitignore
ADDED
|
@@ -0,0 +1,108 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# app specific files
|
| 2 |
+
peter*.jpg
|
| 3 |
+
# received_files/
|
| 4 |
+
|
| 5 |
+
# Byte-compiled / optimized / DLL files
|
| 6 |
+
__pycache__/
|
| 7 |
+
*.py[cod]
|
| 8 |
+
*$py.class
|
| 9 |
+
|
| 10 |
+
# C extensions
|
| 11 |
+
*.so
|
| 12 |
+
|
| 13 |
+
# Distribution / packaging
|
| 14 |
+
.Python
|
| 15 |
+
build/
|
| 16 |
+
develop-eggs/
|
| 17 |
+
dist/
|
| 18 |
+
downloads/
|
| 19 |
+
eggs/
|
| 20 |
+
.eggs/
|
| 21 |
+
lib/
|
| 22 |
+
lib64/
|
| 23 |
+
parts/
|
| 24 |
+
sdist/
|
| 25 |
+
var/
|
| 26 |
+
wheels/
|
| 27 |
+
*.egg-info/
|
| 28 |
+
.installed.cfg
|
| 29 |
+
*.egg
|
| 30 |
+
MANIFEST
|
| 31 |
+
|
| 32 |
+
# PyInstaller
|
| 33 |
+
# Usually these files are written by a python script from a template
|
| 34 |
+
# before PyInstaller builds the exe, so as to inject date/other infos into it.
|
| 35 |
+
*.manifest
|
| 36 |
+
*.spec
|
| 37 |
+
|
| 38 |
+
# Installer logs
|
| 39 |
+
pip-log.txt
|
| 40 |
+
pip-delete-this-directory.txt
|
| 41 |
+
|
| 42 |
+
# Unit test / coverage reports
|
| 43 |
+
htmlcov/
|
| 44 |
+
.tox/
|
| 45 |
+
.coverage
|
| 46 |
+
.coverage.*
|
| 47 |
+
.cache
|
| 48 |
+
nosetests.xml
|
| 49 |
+
coverage.xml
|
| 50 |
+
*.cover
|
| 51 |
+
.hypothesis/
|
| 52 |
+
.pytest_cache/
|
| 53 |
+
|
| 54 |
+
# Translations
|
| 55 |
+
*.mo
|
| 56 |
+
*.pot
|
| 57 |
+
|
| 58 |
+
# Django stuff:
|
| 59 |
+
*.log
|
| 60 |
+
local_settings.py
|
| 61 |
+
db.sqlite3
|
| 62 |
+
|
| 63 |
+
# Flask stuff:
|
| 64 |
+
instance/
|
| 65 |
+
.webassets-cache
|
| 66 |
+
|
| 67 |
+
# Scrapy stuff:
|
| 68 |
+
.scrapy
|
| 69 |
+
|
| 70 |
+
# Sphinx documentation
|
| 71 |
+
docs/_build/
|
| 72 |
+
|
| 73 |
+
# PyBuilder
|
| 74 |
+
target/
|
| 75 |
+
|
| 76 |
+
# Jupyter Notebook
|
| 77 |
+
.ipynb_checkpoints
|
| 78 |
+
|
| 79 |
+
# pyenv
|
| 80 |
+
.python-version
|
| 81 |
+
|
| 82 |
+
# celery beat schedule file
|
| 83 |
+
celerybeat-schedule
|
| 84 |
+
|
| 85 |
+
# SageMath parsed files
|
| 86 |
+
*.sage.py
|
| 87 |
+
|
| 88 |
+
# Environments
|
| 89 |
+
.env
|
| 90 |
+
.venv
|
| 91 |
+
env/
|
| 92 |
+
venv/
|
| 93 |
+
ENV/
|
| 94 |
+
env.bak/
|
| 95 |
+
venv.bak/
|
| 96 |
+
|
| 97 |
+
# Spyder project settings
|
| 98 |
+
.spyderproject
|
| 99 |
+
.spyproject
|
| 100 |
+
|
| 101 |
+
# Rope project settings
|
| 102 |
+
.ropeproject
|
| 103 |
+
|
| 104 |
+
# mkdocs documentation
|
| 105 |
+
/site
|
| 106 |
+
|
| 107 |
+
# mypy
|
| 108 |
+
.mypy_cache/
|
Dockerfile
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# read the doc: https://huggingface.co/docs/hub/spaces-sdks-docker
|
| 2 |
+
# you will also find guides on how best to write your Dockerfile
|
| 3 |
+
|
| 4 |
+
FROM python:3.9
|
| 5 |
+
|
| 6 |
+
WORKDIR /code
|
| 7 |
+
|
| 8 |
+
COPY ./requirements.txt /code/requirements.txt
|
| 9 |
+
|
| 10 |
+
RUN pip install --no-cache-dir --upgrade -r /code/requirements.txt
|
| 11 |
+
|
| 12 |
+
COPY . .
|
| 13 |
+
|
| 14 |
+
CMD ["uvicorn", "-b", "0.0.0.0:7860", "flask_server:app"]
|
Info.txt
ADDED
|
@@ -0,0 +1,4 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
Peter2.jpg file size: 89KB
|
| 2 |
+
Post form data content length: 91162
|
| 3 |
+
Post base64 data content length: 121396
|
| 4 |
+
(121396 - 91162) / 91162 = 33%
|
LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
MIT License
|
| 2 |
+
|
| 3 |
+
Copyright (c) 2019 Peter Jiping Xie
|
| 4 |
+
|
| 5 |
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
| 6 |
+
of this software and associated documentation files (the "Software"), to deal
|
| 7 |
+
in the Software without restriction, including without limitation the rights
|
| 8 |
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
| 9 |
+
copies of the Software, and to permit persons to whom the Software is
|
| 10 |
+
furnished to do so, subject to the following conditions:
|
| 11 |
+
|
| 12 |
+
The above copyright notice and this permission notice shall be included in all
|
| 13 |
+
copies or substantial portions of the Software.
|
| 14 |
+
|
| 15 |
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
| 16 |
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
| 17 |
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
| 18 |
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
| 19 |
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
| 20 |
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
| 21 |
+
SOFTWARE.
|
Medium/face_makeup.png
ADDED
|
Medium/face_makeup_m.png
ADDED
|
Medium/face_rec_HTML.jpg
ADDED
|
Medium/face_rec_resp_web.jpg
ADDED
|
Medium/face_rec_response.jpg
ADDED
|
Medium/form_example.jpg
ADDED
|
app.py
ADDED
|
@@ -0,0 +1,156 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from flask import Flask, request, redirect, jsonify
|
| 2 |
+
from werkzeug.utils import secure_filename
|
| 3 |
+
import os
|
| 4 |
+
import json
|
| 5 |
+
from face_util import compare_faces, face_rec, find_facial_features, find_face_locations
|
| 6 |
+
import re
|
| 7 |
+
import base64
|
| 8 |
+
|
| 9 |
+
app = Flask(__name__)
|
| 10 |
+
|
| 11 |
+
UPLOAD_FOLDER = 'received_files'
|
| 12 |
+
ALLOWED_EXTENSIONS = ['png', 'jpg', 'jpeg']
|
| 13 |
+
|
| 14 |
+
def allowed_file(filename):
|
| 15 |
+
return '.' in filename and \
|
| 16 |
+
filename.rsplit('.', 1)[1].lower() in ALLOWED_EXTENSIONS
|
| 17 |
+
|
| 18 |
+
def print_request(request):
|
| 19 |
+
# Print request url
|
| 20 |
+
print(request.url)
|
| 21 |
+
# print relative headers
|
| 22 |
+
print('content-type: "%s"' % request.headers.get('content-type'))
|
| 23 |
+
print('content-length: %s' % request.headers.get('content-length'))
|
| 24 |
+
# print body content
|
| 25 |
+
if request.is_json:
|
| 26 |
+
json_data = request.get_json(cache=True)
|
| 27 |
+
# replace image_data with '<image base64 data>'
|
| 28 |
+
if json_data.get('image_data', None) is not None:
|
| 29 |
+
json_data['image_data'] = '<image base64 data>'
|
| 30 |
+
else:
|
| 31 |
+
print('request image_data is None.')
|
| 32 |
+
print(json.dumps(json_data,indent=4))
|
| 33 |
+
else: # form data
|
| 34 |
+
body_data=request.get_data()
|
| 35 |
+
# replace image raw data with string '<image raw data>'
|
| 36 |
+
body_sub_image_data=re.sub(b'(\r\n\r\n)(.*?)(\r\n--)',br'\1<image raw data>\3', body_data,flags=re.DOTALL)
|
| 37 |
+
print(body_sub_image_data.decode('utf-8'))
|
| 38 |
+
# print(body_data[0:500] + b'...' + body_data[-500:]) # raw binary
|
| 39 |
+
|
| 40 |
+
@app.route('/face_rec', methods=['POST', 'GET'])
|
| 41 |
+
def face_recognition():
|
| 42 |
+
if request.method == 'POST':
|
| 43 |
+
# Print request url, headers and content
|
| 44 |
+
print_request(request)
|
| 45 |
+
|
| 46 |
+
# JSON data format
|
| 47 |
+
if request.is_json:
|
| 48 |
+
""" Sample data
|
| 49 |
+
{'file_format':'jpg', 'image_data': <base64 ascii string>}
|
| 50 |
+
"""
|
| 51 |
+
# print('Request is a JSON format.')
|
| 52 |
+
json_data = request.get_json(cache=False)
|
| 53 |
+
file_format = json_data.get('file_format', None)
|
| 54 |
+
image_data = json_data.get('image_data', None)
|
| 55 |
+
if file_format not in ALLOWED_EXTENSIONS or image_data is None:
|
| 56 |
+
return '{"error":"Invalid JSON."}'
|
| 57 |
+
|
| 58 |
+
file = os.path.join(UPLOAD_FOLDER, 'image.' + file_format)
|
| 59 |
+
with open(file,'wb') as f:
|
| 60 |
+
# Note: Convert ascii string to binary string first, e.g. 'abc' to b'abc', before decode as base64 string.
|
| 61 |
+
f.write(base64.b64decode(image_data.encode('ascii')))
|
| 62 |
+
|
| 63 |
+
# form data format
|
| 64 |
+
else:
|
| 65 |
+
# check if the post request has the file part
|
| 66 |
+
if 'file' not in request.files:
|
| 67 |
+
print('No file part')
|
| 68 |
+
return redirect(request.url)
|
| 69 |
+
file = request.files.get('file')
|
| 70 |
+
# if user does not select file, browser also submit an empty part without filename
|
| 71 |
+
if file.filename == '':
|
| 72 |
+
print('No selected file')
|
| 73 |
+
return redirect(request.url)
|
| 74 |
+
|
| 75 |
+
if not allowed_file(file.filename):
|
| 76 |
+
return '{"error":"Invalid image file format."}'
|
| 77 |
+
|
| 78 |
+
# Process image file
|
| 79 |
+
# Note file could be a filename or a file object.
|
| 80 |
+
name = face_rec(file)
|
| 81 |
+
resp_data = {'name': name }
|
| 82 |
+
|
| 83 |
+
# get parameters from url if any.
|
| 84 |
+
# facial_features parameter:
|
| 85 |
+
param_features = request.args.get('facial_features', '')
|
| 86 |
+
if param_features.lower() == 'true':
|
| 87 |
+
facial_features = find_facial_features(file)
|
| 88 |
+
# append facial_features to resp_data
|
| 89 |
+
resp_data.update({'facial_features': facial_features})
|
| 90 |
+
|
| 91 |
+
# face_locations parameter:
|
| 92 |
+
param_locations = request.args.get('face_locations', '')
|
| 93 |
+
if param_locations.lower() == 'true':
|
| 94 |
+
face_locations = find_face_locations(file)
|
| 95 |
+
resp_data.update({'face_locations': face_locations})
|
| 96 |
+
|
| 97 |
+
return json.dumps(resp_data)
|
| 98 |
+
|
| 99 |
+
return '''
|
| 100 |
+
<!doctype html>
|
| 101 |
+
<title>Face Recognition</title>
|
| 102 |
+
<h1>Upload an image</h1>
|
| 103 |
+
<form method=post enctype=multipart/form-data>
|
| 104 |
+
<input type=file name=file>
|
| 105 |
+
<input type=submit value=Upload>
|
| 106 |
+
</form>
|
| 107 |
+
'''
|
| 108 |
+
|
| 109 |
+
@app.route('/face_match', methods=['POST', 'GET'])
|
| 110 |
+
def face_match():
|
| 111 |
+
if request.method == 'POST':
|
| 112 |
+
# check if the post request has the file part
|
| 113 |
+
if ('file1' not in request.files) or ('file2' not in request.files):
|
| 114 |
+
print('No file part')
|
| 115 |
+
return redirect(request.url)
|
| 116 |
+
|
| 117 |
+
file1 = request.files.get('file1')
|
| 118 |
+
file2 = request.files.get('file2')
|
| 119 |
+
# if user does not select file, browser also submit an empty part without filename
|
| 120 |
+
if file1.filename == '' or file2.filename == '':
|
| 121 |
+
print('No selected file')
|
| 122 |
+
return redirect(request.url)
|
| 123 |
+
|
| 124 |
+
if allowed_file(file1.filename) and allowed_file(file2.filename):
|
| 125 |
+
file1.save( os.path.join(UPLOAD_FOLDER, secure_filename(file1.filename)) )
|
| 126 |
+
file2.save( os.path.join(UPLOAD_FOLDER, secure_filename(file2.filename)) )
|
| 127 |
+
ret = compare_faces(file1, file2)
|
| 128 |
+
resp_data = {
|
| 129 |
+
"success": ret[0],
|
| 130 |
+
"message": ret[1],
|
| 131 |
+
"data": {
|
| 132 |
+
"match": bool(ret[2] < 0.5),
|
| 133 |
+
"error_procentage": ret[2],
|
| 134 |
+
},
|
| 135 |
+
} # convert ret (numpy._bool) to bool for json.dumps
|
| 136 |
+
return jsonify(resp_data)
|
| 137 |
+
|
| 138 |
+
# Return a demo page for GET request
|
| 139 |
+
return '''
|
| 140 |
+
<!doctype html>
|
| 141 |
+
<title>Face Match</title>
|
| 142 |
+
<h1>Upload two images</h1>
|
| 143 |
+
<form method=post enctype=multipart/form-data>
|
| 144 |
+
<input type=file name=file1>
|
| 145 |
+
<input type=file name=file2>
|
| 146 |
+
<input type=submit value=Upload>
|
| 147 |
+
</form>
|
| 148 |
+
'''
|
| 149 |
+
|
| 150 |
+
@app.route('/')
|
| 151 |
+
def hello_world():
|
| 152 |
+
return 'Hello, World!'
|
| 153 |
+
|
| 154 |
+
# Run in HTTP
|
| 155 |
+
# When debug = True, code is reloaded on the fly while saved
|
| 156 |
+
app.run(host='0.0.0.0', port='7860', debug=True)
|
demo_client.py
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import requests
|
| 2 |
+
import json
|
| 3 |
+
import base64
|
| 4 |
+
# import ipdb
|
| 5 |
+
|
| 6 |
+
def test_face_match():
|
| 7 |
+
url = 'http://127.0.0.1:5001/face_match'
|
| 8 |
+
# open file in binary mode
|
| 9 |
+
files = {'file1': open('sample_images/obama.jpg', 'rb'),
|
| 10 |
+
'file2': open('sample_images/obama2.jpg', 'rb')}
|
| 11 |
+
resp = requests.post(url, files=files)
|
| 12 |
+
print( 'face_match response:\n', json.dumps(resp.json()) )
|
| 13 |
+
|
| 14 |
+
def test_face_rec():
|
| 15 |
+
url = 'http://127.0.0.1:5001/face_rec'
|
| 16 |
+
# open file in binary mode
|
| 17 |
+
files = {'file': open('sample_images/obama2.jpg', 'rb')}
|
| 18 |
+
params = {'facial_features': 'true', 'face_locations':'true'}
|
| 19 |
+
resp = requests.post(url, files = files, params = params)
|
| 20 |
+
print( 'face_rec response:\n', json.dumps(resp.json()) )
|
| 21 |
+
|
| 22 |
+
def test_face_rec_json():
|
| 23 |
+
url = 'http://127.0.0.1:5001/face_rec'
|
| 24 |
+
# encode image as base64 text string.
|
| 25 |
+
# Note: Must convert output bytes string from b64encode to an ascii string to form a JSON, e.g. b'abc' to 'abc'.
|
| 26 |
+
with open('sample_images/obama2.jpg', 'rb') as f:
|
| 27 |
+
image_data = base64.b64encode(f.read()).decode('ascii')
|
| 28 |
+
|
| 29 |
+
data = {'file_format':'jpg', 'image_data': image_data}
|
| 30 |
+
params = {'facial_features': 'true', 'face_locations':'true'}
|
| 31 |
+
resp = requests.post(url, json = data, params = params)
|
| 32 |
+
print( 'face_rec response:\n', json.dumps(resp.json()) )
|
| 33 |
+
|
| 34 |
+
def main():
|
| 35 |
+
test_face_match()
|
| 36 |
+
test_face_rec()
|
| 37 |
+
test_face_rec_json()
|
| 38 |
+
|
| 39 |
+
if __name__ == '__main__':
|
| 40 |
+
main()
|
face_makeup.png
ADDED
|
face_util.py
ADDED
|
@@ -0,0 +1,96 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import face_recognition as fr
|
| 2 |
+
import logging
|
| 3 |
+
import logging.config
|
| 4 |
+
|
| 5 |
+
def compare_faces(file1, file2):
|
| 6 |
+
"""
|
| 7 |
+
Compare two images and return True / False for matching.
|
| 8 |
+
"""
|
| 9 |
+
# Load the jpg files into numpy arrays
|
| 10 |
+
image1 = fr.load_image_file(file1)
|
| 11 |
+
image2 = fr.load_image_file(file2)
|
| 12 |
+
|
| 13 |
+
# Get the face encodings for each face in each image file
|
| 14 |
+
# Assume there is only 1 face in each image, so get 1st face of an image.
|
| 15 |
+
encoding1 = fr.face_encodings(image1)
|
| 16 |
+
encoding2 = fr.face_encodings(image2)
|
| 17 |
+
|
| 18 |
+
# print(len(encoding1))
|
| 19 |
+
# print(len(encoding2))
|
| 20 |
+
|
| 21 |
+
# Defind response format
|
| 22 |
+
# [0] = status success or error
|
| 23 |
+
# [1] = message
|
| 24 |
+
# [2] = data error procentage
|
| 25 |
+
|
| 26 |
+
res = [bool(1), '', 100]
|
| 27 |
+
|
| 28 |
+
# RESPONSE
|
| 29 |
+
|
| 30 |
+
# asd
|
| 31 |
+
if len(encoding1) > 0:
|
| 32 |
+
image1_encoding = encoding1[0]
|
| 33 |
+
else:
|
| 34 |
+
res[0] = bool(0)
|
| 35 |
+
res[1] = 'Wajah tidak ditemukan. (E001)'
|
| 36 |
+
return res
|
| 37 |
+
|
| 38 |
+
if len(encoding2) > 0:
|
| 39 |
+
image2_encoding = encoding2[0]
|
| 40 |
+
else:
|
| 41 |
+
res[0] = bool(0)
|
| 42 |
+
res[1] = 'Wajah tidak ditemukan. (E002)'
|
| 43 |
+
return res
|
| 44 |
+
|
| 45 |
+
# results is an array of True/False telling if the unknown face matched anyone in the known_faces array
|
| 46 |
+
results = fr.face_distance([image1_encoding], image2_encoding)
|
| 47 |
+
# print(results[0])
|
| 48 |
+
# for i, face_distance in enumerate(results):
|
| 49 |
+
# print("The test image has a distance of {:.2} from known image #{}".format(face_distance, i))
|
| 50 |
+
# print("- With a normal cutoff of 0.6, would the test image match the known image? {}".format(face_distance < 0.6))
|
| 51 |
+
# print("- With a very strict cutoff of 0.5, would the test image match the known image? {}".format(face_distance < 0.5))
|
| 52 |
+
# print()
|
| 53 |
+
res[0] = bool(1)
|
| 54 |
+
res[1] = 'Berhasil mendeteksi wajah'
|
| 55 |
+
res[2] = results[0]
|
| 56 |
+
return res
|
| 57 |
+
|
| 58 |
+
# Each face is tuple of (Name,sample image)
|
| 59 |
+
known_faces = [('Obama','sample_images/obama.jpg'),
|
| 60 |
+
('Peter','sample_images/peter.jpg'),
|
| 61 |
+
]
|
| 62 |
+
|
| 63 |
+
def face_rec(file):
|
| 64 |
+
"""
|
| 65 |
+
Return name for a known face, otherwise return 'Unknown'.
|
| 66 |
+
"""
|
| 67 |
+
for name, known_file in known_faces:
|
| 68 |
+
if compare_faces(known_file,file):
|
| 69 |
+
return name
|
| 70 |
+
return 'Unknown'
|
| 71 |
+
|
| 72 |
+
def find_facial_features(file):
|
| 73 |
+
# Load the jpg file into a numpy array
|
| 74 |
+
image = fr.load_image_file(file)
|
| 75 |
+
|
| 76 |
+
# Find all facial features in all the faces in the image
|
| 77 |
+
face_landmarks_list = fr.face_landmarks(image)
|
| 78 |
+
|
| 79 |
+
# return facial features if there is only 1 face in the image
|
| 80 |
+
if len(face_landmarks_list) != 1:
|
| 81 |
+
return {}
|
| 82 |
+
else:
|
| 83 |
+
return face_landmarks_list[0]
|
| 84 |
+
|
| 85 |
+
def find_face_locations(file):
|
| 86 |
+
# Load the jpg file into a numpy array
|
| 87 |
+
image = fr.load_image_file(file)
|
| 88 |
+
|
| 89 |
+
# Find all face locations for the faces in the image
|
| 90 |
+
face_locations = fr.face_locations(image)
|
| 91 |
+
|
| 92 |
+
# return facial features if there is only 1 face in the image
|
| 93 |
+
if len(face_locations) != 1:
|
| 94 |
+
return []
|
| 95 |
+
else:
|
| 96 |
+
return face_locations[0]
|
find_facial_features_in_picture_w_api.py
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from PIL import Image, ImageDraw
|
| 2 |
+
import requests
|
| 3 |
+
import json
|
| 4 |
+
|
| 5 |
+
image_file = 'sample_images/obama.jpg'
|
| 6 |
+
|
| 7 |
+
# call face recognition REST API
|
| 8 |
+
url = 'http://127.0.0.1:5001/face_rec'
|
| 9 |
+
files = {'file': open(image_file, 'rb')}
|
| 10 |
+
params = {'facial_features': 'true', 'face_locations':'true'}
|
| 11 |
+
resp = requests.post(url, files = files, params = params)
|
| 12 |
+
|
| 13 |
+
# get facial features
|
| 14 |
+
resp_dict = resp.json() # convert to dict
|
| 15 |
+
facial_features = resp_dict['facial_features']
|
| 16 |
+
face_locations = resp_dict['face_locations']
|
| 17 |
+
|
| 18 |
+
pil_image = Image.open(image_file)
|
| 19 |
+
d = ImageDraw.Draw(pil_image)
|
| 20 |
+
|
| 21 |
+
facial_feature_names = [
|
| 22 |
+
'chin',
|
| 23 |
+
'left_eyebrow',
|
| 24 |
+
'right_eyebrow',
|
| 25 |
+
'nose_bridge',
|
| 26 |
+
'nose_tip',
|
| 27 |
+
'left_eye',
|
| 28 |
+
'right_eye',
|
| 29 |
+
'top_lip',
|
| 30 |
+
'bottom_lip'
|
| 31 |
+
]
|
| 32 |
+
|
| 33 |
+
def convert_list(list_of_list):
|
| 34 |
+
list_of_tuple = [tuple(i) for i in list_of_list]
|
| 35 |
+
return list_of_tuple
|
| 36 |
+
|
| 37 |
+
# Let's trace out each facial feature in the image with a line!
|
| 38 |
+
for feature_name in facial_feature_names:
|
| 39 |
+
d.line( convert_list(facial_features[feature_name]), width=5)
|
| 40 |
+
|
| 41 |
+
# draw rectangle for face location:
|
| 42 |
+
top, right, bottom, left = face_locations
|
| 43 |
+
d.rectangle([left,top,right,bottom],outline='red')
|
| 44 |
+
|
| 45 |
+
# Display drawed image
|
| 46 |
+
pil_image.show()
|
| 47 |
+
pil_image.save('face_makeup.png')
|
flask_server.py
ADDED
|
@@ -0,0 +1,156 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from flask import Flask, request, redirect, jsonify
|
| 2 |
+
from werkzeug.utils import secure_filename
|
| 3 |
+
import os
|
| 4 |
+
import json
|
| 5 |
+
from face_util import compare_faces, face_rec, find_facial_features, find_face_locations
|
| 6 |
+
import re
|
| 7 |
+
import base64
|
| 8 |
+
|
| 9 |
+
app = Flask(__name__)
|
| 10 |
+
|
| 11 |
+
UPLOAD_FOLDER = 'received_files'
|
| 12 |
+
ALLOWED_EXTENSIONS = ['png', 'jpg', 'jpeg']
|
| 13 |
+
|
| 14 |
+
def allowed_file(filename):
|
| 15 |
+
return '.' in filename and \
|
| 16 |
+
filename.rsplit('.', 1)[1].lower() in ALLOWED_EXTENSIONS
|
| 17 |
+
|
| 18 |
+
def print_request(request):
|
| 19 |
+
# Print request url
|
| 20 |
+
print(request.url)
|
| 21 |
+
# print relative headers
|
| 22 |
+
print('content-type: "%s"' % request.headers.get('content-type'))
|
| 23 |
+
print('content-length: %s' % request.headers.get('content-length'))
|
| 24 |
+
# print body content
|
| 25 |
+
if request.is_json:
|
| 26 |
+
json_data = request.get_json(cache=True)
|
| 27 |
+
# replace image_data with '<image base64 data>'
|
| 28 |
+
if json_data.get('image_data', None) is not None:
|
| 29 |
+
json_data['image_data'] = '<image base64 data>'
|
| 30 |
+
else:
|
| 31 |
+
print('request image_data is None.')
|
| 32 |
+
print(json.dumps(json_data,indent=4))
|
| 33 |
+
else: # form data
|
| 34 |
+
body_data=request.get_data()
|
| 35 |
+
# replace image raw data with string '<image raw data>'
|
| 36 |
+
body_sub_image_data=re.sub(b'(\r\n\r\n)(.*?)(\r\n--)',br'\1<image raw data>\3', body_data,flags=re.DOTALL)
|
| 37 |
+
print(body_sub_image_data.decode('utf-8'))
|
| 38 |
+
# print(body_data[0:500] + b'...' + body_data[-500:]) # raw binary
|
| 39 |
+
|
| 40 |
+
@app.route('/face_rec', methods=['POST', 'GET'])
|
| 41 |
+
def face_recognition():
|
| 42 |
+
if request.method == 'POST':
|
| 43 |
+
# Print request url, headers and content
|
| 44 |
+
print_request(request)
|
| 45 |
+
|
| 46 |
+
# JSON data format
|
| 47 |
+
if request.is_json:
|
| 48 |
+
""" Sample data
|
| 49 |
+
{'file_format':'jpg', 'image_data': <base64 ascii string>}
|
| 50 |
+
"""
|
| 51 |
+
# print('Request is a JSON format.')
|
| 52 |
+
json_data = request.get_json(cache=False)
|
| 53 |
+
file_format = json_data.get('file_format', None)
|
| 54 |
+
image_data = json_data.get('image_data', None)
|
| 55 |
+
if file_format not in ALLOWED_EXTENSIONS or image_data is None:
|
| 56 |
+
return '{"error":"Invalid JSON."}'
|
| 57 |
+
|
| 58 |
+
file = os.path.join(UPLOAD_FOLDER, 'image.' + file_format)
|
| 59 |
+
with open(file,'wb') as f:
|
| 60 |
+
# Note: Convert ascii string to binary string first, e.g. 'abc' to b'abc', before decode as base64 string.
|
| 61 |
+
f.write(base64.b64decode(image_data.encode('ascii')))
|
| 62 |
+
|
| 63 |
+
# form data format
|
| 64 |
+
else:
|
| 65 |
+
# check if the post request has the file part
|
| 66 |
+
if 'file' not in request.files:
|
| 67 |
+
print('No file part')
|
| 68 |
+
return redirect(request.url)
|
| 69 |
+
file = request.files.get('file')
|
| 70 |
+
# if user does not select file, browser also submit an empty part without filename
|
| 71 |
+
if file.filename == '':
|
| 72 |
+
print('No selected file')
|
| 73 |
+
return redirect(request.url)
|
| 74 |
+
|
| 75 |
+
if not allowed_file(file.filename):
|
| 76 |
+
return '{"error":"Invalid image file format."}'
|
| 77 |
+
|
| 78 |
+
# Process image file
|
| 79 |
+
# Note file could be a filename or a file object.
|
| 80 |
+
name = face_rec(file)
|
| 81 |
+
resp_data = {'name': name }
|
| 82 |
+
|
| 83 |
+
# get parameters from url if any.
|
| 84 |
+
# facial_features parameter:
|
| 85 |
+
param_features = request.args.get('facial_features', '')
|
| 86 |
+
if param_features.lower() == 'true':
|
| 87 |
+
facial_features = find_facial_features(file)
|
| 88 |
+
# append facial_features to resp_data
|
| 89 |
+
resp_data.update({'facial_features': facial_features})
|
| 90 |
+
|
| 91 |
+
# face_locations parameter:
|
| 92 |
+
param_locations = request.args.get('face_locations', '')
|
| 93 |
+
if param_locations.lower() == 'true':
|
| 94 |
+
face_locations = find_face_locations(file)
|
| 95 |
+
resp_data.update({'face_locations': face_locations})
|
| 96 |
+
|
| 97 |
+
return json.dumps(resp_data)
|
| 98 |
+
|
| 99 |
+
return '''
|
| 100 |
+
<!doctype html>
|
| 101 |
+
<title>Face Recognition</title>
|
| 102 |
+
<h1>Upload an image</h1>
|
| 103 |
+
<form method=post enctype=multipart/form-data>
|
| 104 |
+
<input type=file name=file>
|
| 105 |
+
<input type=submit value=Upload>
|
| 106 |
+
</form>
|
| 107 |
+
'''
|
| 108 |
+
|
| 109 |
+
@app.route('/face_match', methods=['POST', 'GET'])
|
| 110 |
+
def face_match():
|
| 111 |
+
if request.method == 'POST':
|
| 112 |
+
# check if the post request has the file part
|
| 113 |
+
if ('file1' not in request.files) or ('file2' not in request.files):
|
| 114 |
+
print('No file part')
|
| 115 |
+
return redirect(request.url)
|
| 116 |
+
|
| 117 |
+
file1 = request.files.get('file1')
|
| 118 |
+
file2 = request.files.get('file2')
|
| 119 |
+
# if user does not select file, browser also submit an empty part without filename
|
| 120 |
+
if file1.filename == '' or file2.filename == '':
|
| 121 |
+
print('No selected file')
|
| 122 |
+
return redirect(request.url)
|
| 123 |
+
|
| 124 |
+
if allowed_file(file1.filename) and allowed_file(file2.filename):
|
| 125 |
+
file1.save( os.path.join(UPLOAD_FOLDER, secure_filename(file1.filename)) )
|
| 126 |
+
file2.save( os.path.join(UPLOAD_FOLDER, secure_filename(file2.filename)) )
|
| 127 |
+
ret = compare_faces(file1, file2)
|
| 128 |
+
resp_data = {
|
| 129 |
+
"success": ret[0],
|
| 130 |
+
"message": ret[1],
|
| 131 |
+
"data": {
|
| 132 |
+
"match": bool(ret[2] < 0.5),
|
| 133 |
+
"error_procentage": ret[2],
|
| 134 |
+
},
|
| 135 |
+
} # convert ret (numpy._bool) to bool for json.dumps
|
| 136 |
+
return jsonify(resp_data)
|
| 137 |
+
|
| 138 |
+
# Return a demo page for GET request
|
| 139 |
+
return '''
|
| 140 |
+
<!doctype html>
|
| 141 |
+
<title>Face Match</title>
|
| 142 |
+
<h1>Upload two images</h1>
|
| 143 |
+
<form method=post enctype=multipart/form-data>
|
| 144 |
+
<input type=file name=file1>
|
| 145 |
+
<input type=file name=file2>
|
| 146 |
+
<input type=submit value=Upload>
|
| 147 |
+
</form>
|
| 148 |
+
'''
|
| 149 |
+
|
| 150 |
+
@app.route('/')
|
| 151 |
+
def hello_world():
|
| 152 |
+
return 'Hello, World!'
|
| 153 |
+
|
| 154 |
+
# Run in HTTP
|
| 155 |
+
# When debug = True, code is reloaded on the fly while saved
|
| 156 |
+
app.run(host='0.0.0.0', port='80', debug=True)
|
gists/demo_client_part1.py
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import requests
|
| 2 |
+
import json
|
| 3 |
+
|
| 4 |
+
def test_face_match():
|
| 5 |
+
url = 'http://127.0.0.1:5001/face_match'
|
| 6 |
+
# open file in binary mode
|
| 7 |
+
files = {'file1': open('sample_images/peter.jpg', 'rb'),
|
| 8 |
+
'file2': open('sample_images/peter2.jpg', 'rb')}
|
| 9 |
+
resp = requests.post(url, files=files)
|
| 10 |
+
print( 'face_match response:\n', json.dumps(resp.json()) )
|
| 11 |
+
|
| 12 |
+
if __name__ == '__main__':
|
| 13 |
+
test_face_match()
|
gists/demo_client_part2.py
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import requests
|
| 2 |
+
import json
|
| 3 |
+
|
| 4 |
+
def test_face_rec():
|
| 5 |
+
url = 'http://127.0.0.1:5001/face_rec'
|
| 6 |
+
# open file in binary mode
|
| 7 |
+
files = {'file': open('sample_images/peter2.jpg', 'rb')}
|
| 8 |
+
params = {'facial_features': 'true', 'face_locations':'true'}
|
| 9 |
+
resp = requests.post(url, files = files, params = params)
|
| 10 |
+
print( 'face_rec response:\n', json.dumps(resp.json()) )
|
| 11 |
+
|
| 12 |
+
if __name__ == '__main__':
|
| 13 |
+
test_face_rec()
|
gists/demo_client_part2_v1.py
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import requests
|
| 2 |
+
import json
|
| 3 |
+
|
| 4 |
+
def test_face_rec():
|
| 5 |
+
url = 'http://127.0.0.1:5001/face_rec'
|
| 6 |
+
# open file in binary mode
|
| 7 |
+
files = {'file': open('sample_images/peter2.jpg', 'rb')}
|
| 8 |
+
resp = requests.post(url, files = files)
|
| 9 |
+
print( 'face_rec response:\n', json.dumps(resp.json()) )
|
| 10 |
+
|
| 11 |
+
if __name__ == '__main__':
|
| 12 |
+
test_face_rec()
|
gists/demo_client_v1.py
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import requests
|
| 2 |
+
import json
|
| 3 |
+
|
| 4 |
+
def test_face_match():
|
| 5 |
+
url = 'http://127.0.0.1:5001/face_match'
|
| 6 |
+
# open file in binary mode
|
| 7 |
+
files = {'file1': open('sample_images/peter.jpg', 'rb'),
|
| 8 |
+
'file2': open('sample_images/peter2.jpg', 'rb')}
|
| 9 |
+
resp = requests.post(url, files=files)
|
| 10 |
+
print( 'face_match response:\n', json.dumps(resp.json()) )
|
| 11 |
+
|
| 12 |
+
def test_face_rec():
|
| 13 |
+
url = 'http://127.0.0.1:5001/face_rec'
|
| 14 |
+
# open file in binary mode
|
| 15 |
+
files = {'file': open('sample_images/peter2.jpg', 'rb')}
|
| 16 |
+
params = {'facial_features': 'true', 'face_locations':'true'}
|
| 17 |
+
resp = requests.post(url, files = files, params = params)
|
| 18 |
+
print( 'face_rec response:\n', json.dumps(resp.json()) )
|
| 19 |
+
|
| 20 |
+
if __name__ == '__main__':
|
| 21 |
+
test_face_match()
|
| 22 |
+
# test_face_rec()
|
gists/demo_client_v3.py
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import requests
|
| 2 |
+
import json
|
| 3 |
+
import base64
|
| 4 |
+
|
| 5 |
+
def test_face_match():
|
| 6 |
+
url = 'http://127.0.0.1:5001/face_match'
|
| 7 |
+
# open file in binary mode
|
| 8 |
+
files = {'file1': open('sample_images/obama.jpg', 'rb'),
|
| 9 |
+
'file2': open('sample_images/obama2.jpg', 'rb')}
|
| 10 |
+
resp = requests.post(url, files=files)
|
| 11 |
+
print( 'face_match response:\n', json.dumps(resp.json()) )
|
| 12 |
+
|
| 13 |
+
def test_face_rec():
|
| 14 |
+
url = 'http://127.0.0.1:5001/face_rec'
|
| 15 |
+
# open file in binary mode
|
| 16 |
+
files = {'file': open('sample_images/obama2.jpg', 'rb')}
|
| 17 |
+
params = {'facial_features': 'true', 'face_locations':'true'}
|
| 18 |
+
resp = requests.post(url, files = files, params = params)
|
| 19 |
+
print( 'face_rec response:\n', json.dumps(resp.json()) )
|
| 20 |
+
|
| 21 |
+
def test_face_rec_json():
|
| 22 |
+
url = 'http://127.0.0.1:5001/face_rec'
|
| 23 |
+
# encode image as base64 text string.
|
| 24 |
+
# Note: Must convert output bytes string from b64encode to an ascii string to form a JSON, e.g. b'abc' to 'abc'.
|
| 25 |
+
with open('sample_images/obama2.jpg', 'rb') as f:
|
| 26 |
+
image_data = base64.b64encode(f.read()).decode('ascii')
|
| 27 |
+
|
| 28 |
+
data = {'file_format':'jpg', 'image_data': image_data}
|
| 29 |
+
params = {'facial_features': 'true', 'face_locations':'true'}
|
| 30 |
+
resp = requests.post(url, json = data, params = params)
|
| 31 |
+
print( 'face_rec response:\n', json.dumps(resp.json()) )
|
| 32 |
+
|
| 33 |
+
def main():
|
| 34 |
+
test_face_match()
|
| 35 |
+
test_face_rec()
|
| 36 |
+
test_face_rec_json()
|
| 37 |
+
|
| 38 |
+
if __name__ == '__main__':
|
| 39 |
+
main()
|
gists/face_util_part1.py
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import face_recognition as fr
|
| 2 |
+
|
| 3 |
+
def compare_faces(file1, file2):
|
| 4 |
+
# Load the jpg files into numpy arrays
|
| 5 |
+
image1 = fr.load_image_file(file1)
|
| 6 |
+
image2 = fr.load_image_file(file2)
|
| 7 |
+
|
| 8 |
+
# Get the face encodings for 1st face in each image file
|
| 9 |
+
image1_encoding = fr.face_encodings(image1)[0]
|
| 10 |
+
image2_encoding = fr.face_encodings(image2)[0]
|
| 11 |
+
|
| 12 |
+
# Compare faces and return True / False
|
| 13 |
+
results = fr.compare_faces([image1_encoding], image2_encoding)
|
| 14 |
+
return results[0]
|
gists/face_util_part2.py
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import face_recognition as fr
|
| 2 |
+
|
| 3 |
+
# Each face is tuple of (Name,sample image)
|
| 4 |
+
known_faces = [('Obama','sample_images/obama.jpg'),
|
| 5 |
+
('Peter','sample_images/peter.jpg'),
|
| 6 |
+
]
|
| 7 |
+
|
| 8 |
+
def face_rec(file):
|
| 9 |
+
"""
|
| 10 |
+
Return name for a known face, otherwise return 'Uknown'.
|
| 11 |
+
"""
|
| 12 |
+
for name, known_file in known_faces:
|
| 13 |
+
if compare_faces(known_file,file):
|
| 14 |
+
return name
|
| 15 |
+
return 'Unknown'
|
gists/face_util_part3.py
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import face_recognition as fr
|
| 2 |
+
|
| 3 |
+
def find_face_locations(file):
|
| 4 |
+
# Load the jpg file into a numpy array
|
| 5 |
+
image = fr.load_image_file(file)
|
| 6 |
+
|
| 7 |
+
# Find all face locations for the faces in the image
|
| 8 |
+
face_locations = fr.face_locations(image)
|
| 9 |
+
|
| 10 |
+
# return facial features if there is only 1 face in the image
|
| 11 |
+
if len(face_locations) != 1:
|
| 12 |
+
return []
|
| 13 |
+
else:
|
| 14 |
+
return face_locations[0]
|
gists/face_util_part4.py
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import face_recognition as fr
|
| 2 |
+
|
| 3 |
+
def find_facial_features(file):
|
| 4 |
+
# Load the jpg file into a numpy array
|
| 5 |
+
image = fr.load_image_file(file)
|
| 6 |
+
|
| 7 |
+
# Find all facial features in all the faces in the image
|
| 8 |
+
face_landmarks_list = fr.face_landmarks(image)
|
| 9 |
+
|
| 10 |
+
# return facial features if there is only 1 face in the image
|
| 11 |
+
if len(face_landmarks_list) != 1:
|
| 12 |
+
return {}
|
| 13 |
+
else:
|
| 14 |
+
return face_landmarks_list[0]
|
gists/flask_server_v1.py
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from flask import Flask, request
|
| 2 |
+
import os
|
| 3 |
+
import json
|
| 4 |
+
from face_util import compare_faces, face_rec
|
| 5 |
+
|
| 6 |
+
app = Flask(__name__)
|
| 7 |
+
|
| 8 |
+
@app.route('/face_match', methods=['POST'])
|
| 9 |
+
def face_match():
|
| 10 |
+
if request.method == 'POST':
|
| 11 |
+
# check if the post request has the file part
|
| 12 |
+
if ('file1' in request.files) and ('file2' in request.files):
|
| 13 |
+
file1 = request.files.get('file1')
|
| 14 |
+
file2 = request.files.get('file2')
|
| 15 |
+
ret = compare_faces(file1, file2)
|
| 16 |
+
resp_data = {"match": bool(ret)} # convert numpy._bool to bool for json.dumps
|
| 17 |
+
return json.dumps(resp_data)
|
| 18 |
+
|
| 19 |
+
@app.route('/face_rec', methods=['POST'])
|
| 20 |
+
def face_recognition():
|
| 21 |
+
if request.method == 'POST':
|
| 22 |
+
# check if the post request has the file part
|
| 23 |
+
if 'file' in request.files:
|
| 24 |
+
file = request.files.get('file')
|
| 25 |
+
name = face_rec(file)
|
| 26 |
+
resp_data = {'name': name }
|
| 27 |
+
return json.dumps(resp_data)
|
| 28 |
+
|
| 29 |
+
# When debug = True, code is reloaded on the fly while saved
|
| 30 |
+
app.run(host='0.0.0.0', port='5001', debug=True)
|
gists/flask_server_v1_part1.py
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from flask import Flask, request
|
| 2 |
+
import json
|
| 3 |
+
from face_util import compare_faces, face_rec
|
| 4 |
+
|
| 5 |
+
app = Flask(__name__)
|
| 6 |
+
|
| 7 |
+
@app.route('/face_match', methods=['POST'])
|
| 8 |
+
def face_match():
|
| 9 |
+
if request.method == 'POST':
|
| 10 |
+
# check if the post request has the file part
|
| 11 |
+
if ('file1' in request.files) and ('file2' in request.files):
|
| 12 |
+
file1 = request.files.get('file1')
|
| 13 |
+
file2 = request.files.get('file2')
|
| 14 |
+
ret = compare_faces(file1, file2)
|
| 15 |
+
resp_data = {"match": bool(ret)} # convert numpy._bool to bool for json.dumps
|
| 16 |
+
return json.dumps(resp_data)
|
| 17 |
+
|
| 18 |
+
# When debug = True, code is reloaded on the fly while saved
|
| 19 |
+
app.run(host='0.0.0.0', port='5001', debug=True)
|
gists/flask_server_v1_part2.py
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
@app.route('/face_rec', methods=['POST'])
|
| 2 |
+
def face_recognition():
|
| 3 |
+
if request.method == 'POST':
|
| 4 |
+
# check if the post request has the file part
|
| 5 |
+
if 'file' in request.files:
|
| 6 |
+
file = request.files.get('file')
|
| 7 |
+
name = face_rec(file)
|
| 8 |
+
resp_data = {'name': name }
|
| 9 |
+
return json.dumps(resp_data)
|
gists/flask_server_v2.py
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from flask import Flask, request
|
| 2 |
+
import re, json
|
| 3 |
+
from face_util import compare_faces, face_rec
|
| 4 |
+
|
| 5 |
+
app = Flask(__name__)
|
| 6 |
+
|
| 7 |
+
@app.route('/face_match', methods=['POST'])
|
| 8 |
+
def face_match():
|
| 9 |
+
if request.method == 'POST':
|
| 10 |
+
# check if the post request has the file part
|
| 11 |
+
if ('file1' in request.files) and ('file2' in request.files):
|
| 12 |
+
file1 = request.files.get('file1')
|
| 13 |
+
file2 = request.files.get('file2')
|
| 14 |
+
ret = compare_faces(file1, file2)
|
| 15 |
+
resp_data = {"match": bool(ret)} # convert numpy._bool to bool for json.dumps
|
| 16 |
+
return json.dumps(resp_data)
|
| 17 |
+
|
| 18 |
+
def print_request(request):
|
| 19 |
+
# Print request url
|
| 20 |
+
print(request.url)
|
| 21 |
+
# print relative headers
|
| 22 |
+
print('content-type: "%s"' % request.headers.get('content-type'))
|
| 23 |
+
print('content-length: %s' % request.headers.get('content-length'))
|
| 24 |
+
# print body content
|
| 25 |
+
body_bytes = request.get_data()
|
| 26 |
+
# replace image raw data with string '<image raw data>'
|
| 27 |
+
body_sub = re.sub(b'(\r\n\r\n)(.*?)(\r\n--)',br'\1<image raw data>\3', body_bytes,flags=re.DOTALL)
|
| 28 |
+
print(body_sub.decode('utf-8'))
|
| 29 |
+
|
| 30 |
+
@app.route('/face_rec', methods=['POST', 'GET'])
|
| 31 |
+
def face_recognition():
|
| 32 |
+
if request.method == 'POST':
|
| 33 |
+
print_request(request)
|
| 34 |
+
# check if the post request has the file part
|
| 35 |
+
if 'file' in request.files:
|
| 36 |
+
file = request.files.get('file')
|
| 37 |
+
name = face_rec(file)
|
| 38 |
+
resp_data = {'name': name }
|
| 39 |
+
return json.dumps(resp_data)
|
| 40 |
+
|
| 41 |
+
return '''
|
| 42 |
+
<!doctype html>
|
| 43 |
+
<title>Face Recognition</title>
|
| 44 |
+
<h1>Upload an image</h1>
|
| 45 |
+
<form method=post enctype=multipart/form-data>
|
| 46 |
+
<input type=file name=file>
|
| 47 |
+
<input type=submit value=Upload>
|
| 48 |
+
</form>
|
| 49 |
+
'''
|
| 50 |
+
|
| 51 |
+
app.run(host='0.0.0.0', port='5001', debug=True)
|
gists/flask_server_v2_part1.py
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from flask import Flask, request
|
| 2 |
+
import re, json
|
| 3 |
+
from face_util import compare_faces, face_rec
|
| 4 |
+
|
| 5 |
+
app = Flask(__name__)
|
| 6 |
+
|
| 7 |
+
def print_request(request):
|
| 8 |
+
# Print request url
|
| 9 |
+
print(request.url)
|
| 10 |
+
# print relative headers
|
| 11 |
+
print('content-type: "%s"' % request.headers.get('content-type'))
|
| 12 |
+
print('content-length: %s' % request.headers.get('content-length'))
|
| 13 |
+
# print body content
|
| 14 |
+
body_bytes = request.get_data()
|
| 15 |
+
# replace image raw data with string '<image raw data>'
|
| 16 |
+
body_sub = re.sub(b'(\r\n\r\n)(.*?)(\r\n--)',br'\1<image raw data>\3', body_bytes,flags=re.DOTALL)
|
| 17 |
+
print(body_sub.decode('utf-8'))
|
gists/flask_server_v2_part2.py
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
@app.route('/face_rec', methods=['POST', 'GET'])
|
| 2 |
+
def face_recognition():
|
| 3 |
+
if request.method == 'POST':
|
| 4 |
+
print_request(request)
|
| 5 |
+
# check if the post request has the file part
|
| 6 |
+
if 'file' in request.files:
|
| 7 |
+
file = request.files.get('file')
|
| 8 |
+
name = face_rec(file)
|
| 9 |
+
resp_data = {'name': name }
|
| 10 |
+
return json.dumps(resp_data)
|
| 11 |
+
|
| 12 |
+
return '''
|
| 13 |
+
<!doctype html>
|
| 14 |
+
<title>Face Recognition</title>
|
| 15 |
+
<h1>Upload an image</h1>
|
| 16 |
+
<form method=post enctype=multipart/form-data>
|
| 17 |
+
<input type=file name=file>
|
| 18 |
+
<input type=submit value=Upload>
|
| 19 |
+
</form>
|
| 20 |
+
'''
|
| 21 |
+
|
| 22 |
+
app.run(host='0.0.0.0', port='5001', debug=True)
|
gists/flask_server_v3.py
ADDED
|
@@ -0,0 +1,149 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from flask import Flask, request, redirect
|
| 2 |
+
from werkzeug.utils import secure_filename
|
| 3 |
+
import os
|
| 4 |
+
import json
|
| 5 |
+
from face_util import compare_faces, face_rec, find_facial_features, find_face_locations
|
| 6 |
+
import re
|
| 7 |
+
import base64
|
| 8 |
+
|
| 9 |
+
app = Flask(__name__)
|
| 10 |
+
|
| 11 |
+
UPLOAD_FOLDER = 'received_files'
|
| 12 |
+
ALLOWED_EXTENSIONS = ['png', 'jpg', 'jpeg']
|
| 13 |
+
|
| 14 |
+
def allowed_file(filename):
|
| 15 |
+
return '.' in filename and \
|
| 16 |
+
filename.rsplit('.', 1)[1].lower() in ALLOWED_EXTENSIONS
|
| 17 |
+
|
| 18 |
+
def print_request(request):
|
| 19 |
+
# Print request url
|
| 20 |
+
print(request.url)
|
| 21 |
+
# print relative headers
|
| 22 |
+
print('content-type: "%s"' % request.headers.get('content-type'))
|
| 23 |
+
print('content-length: %s' % request.headers.get('content-length'))
|
| 24 |
+
# print body content
|
| 25 |
+
if request.is_json:
|
| 26 |
+
json_data = request.get_json(cache=True)
|
| 27 |
+
# replace image_data with '<image base64 data>'
|
| 28 |
+
if json_data.get('image_data', None) is not None:
|
| 29 |
+
json_data['image_data'] = '<image base64 data>'
|
| 30 |
+
else:
|
| 31 |
+
print('request image_data is None.')
|
| 32 |
+
print(json.dumps(json_data,indent=4))
|
| 33 |
+
else: # form data
|
| 34 |
+
body_data=request.get_data()
|
| 35 |
+
# replace image raw data with string '<image raw data>'
|
| 36 |
+
body_sub_image_data=re.sub(b'(\r\n\r\n)(.*?)(\r\n--)',br'\1<image raw data>\3', body_data,flags=re.DOTALL)
|
| 37 |
+
print(body_sub_image_data.decode('utf-8'))
|
| 38 |
+
# print(body_data[0:500] + b'...' + body_data[-500:]) # raw binary
|
| 39 |
+
|
| 40 |
+
@app.route('/face_rec', methods=['POST', 'GET'])
|
| 41 |
+
def face_recognition():
|
| 42 |
+
if request.method == 'POST':
|
| 43 |
+
# Print request url, headers and content
|
| 44 |
+
print_request(request)
|
| 45 |
+
|
| 46 |
+
# JSON data format
|
| 47 |
+
if request.is_json:
|
| 48 |
+
""" Sample data
|
| 49 |
+
{'file_format':'jpg', 'image_data': <base64 ascii string>}
|
| 50 |
+
"""
|
| 51 |
+
# print('Request is a JSON format.')
|
| 52 |
+
json_data = request.get_json(cache=False)
|
| 53 |
+
file_format = json_data.get('file_format', None)
|
| 54 |
+
image_data = json_data.get('image_data', None)
|
| 55 |
+
if file_format not in ALLOWED_EXTENSIONS or image_data is None:
|
| 56 |
+
return '{"error":"Invalid JSON."}'
|
| 57 |
+
|
| 58 |
+
file = os.path.join(UPLOAD_FOLDER, 'image.' + file_format)
|
| 59 |
+
with open(file,'wb') as f:
|
| 60 |
+
# Note: Convert ascii string to bytes string first, e.g. 'abc' to b'abc', before decode as base64 string.
|
| 61 |
+
f.write(base64.b64decode(image_data.encode('ascii')))
|
| 62 |
+
|
| 63 |
+
# form data format
|
| 64 |
+
else:
|
| 65 |
+
# check if the post request has the file part
|
| 66 |
+
if 'file' not in request.files:
|
| 67 |
+
print('No file part')
|
| 68 |
+
return redirect(request.url)
|
| 69 |
+
file = request.files.get('file')
|
| 70 |
+
# if user does not select file, browser also submit an empty part without filename
|
| 71 |
+
if file.filename == '':
|
| 72 |
+
print('No selected file')
|
| 73 |
+
return redirect(request.url)
|
| 74 |
+
|
| 75 |
+
if not allowed_file(file.filename):
|
| 76 |
+
return '{"error":"Invalid image file format."}'
|
| 77 |
+
|
| 78 |
+
# Process image file
|
| 79 |
+
# Note file could be a filename or a file object.
|
| 80 |
+
name = face_rec(file)
|
| 81 |
+
resp_data = {'name': name }
|
| 82 |
+
|
| 83 |
+
# get parameters from url if any.
|
| 84 |
+
# facial_features parameter:
|
| 85 |
+
param_features = request.args.get('facial_features', '')
|
| 86 |
+
if param_features.lower() == 'true':
|
| 87 |
+
facial_features = find_facial_features(file)
|
| 88 |
+
# append facial_features to resp_data
|
| 89 |
+
resp_data.update({'facial_features': facial_features})
|
| 90 |
+
|
| 91 |
+
# face_locations parameter:
|
| 92 |
+
param_locations = request.args.get('face_locations', '')
|
| 93 |
+
if param_locations.lower() == 'true':
|
| 94 |
+
face_locations = find_face_locations(file)
|
| 95 |
+
resp_data.update({'face_locations': face_locations})
|
| 96 |
+
|
| 97 |
+
return json.dumps(resp_data)
|
| 98 |
+
|
| 99 |
+
return '''
|
| 100 |
+
<!doctype html>
|
| 101 |
+
<title>Face Recognition</title>
|
| 102 |
+
<h1>Upload an image</h1>
|
| 103 |
+
<form method=post enctype=multipart/form-data>
|
| 104 |
+
<input type=file name=file>
|
| 105 |
+
<input type=submit value=Upload>
|
| 106 |
+
</form>
|
| 107 |
+
'''
|
| 108 |
+
|
| 109 |
+
@app.route('/face_match', methods=['POST', 'GET'])
|
| 110 |
+
def face_match():
|
| 111 |
+
if request.method == 'POST':
|
| 112 |
+
# check if the post request has the file part
|
| 113 |
+
if ('file1' not in request.files) or ('file2' not in request.files):
|
| 114 |
+
print('No file part')
|
| 115 |
+
return redirect(request.url)
|
| 116 |
+
|
| 117 |
+
file1 = request.files.get('file1')
|
| 118 |
+
file2 = request.files.get('file2')
|
| 119 |
+
# if user does not select file, browser also submit an empty part without filename
|
| 120 |
+
if file1.filename == '' or file2.filename == '':
|
| 121 |
+
print('No selected file')
|
| 122 |
+
return redirect(request.url)
|
| 123 |
+
|
| 124 |
+
if allowed_file(file1.filename) and allowed_file(file2.filename):
|
| 125 |
+
#file1.save( os.path.join(UPLOAD_FOLDER, secure_filename(file1.filename)) )
|
| 126 |
+
#file2.save( os.path.join(UPLOAD_FOLDER, secure_filename(file2.filename)) )
|
| 127 |
+
ret = compare_faces(file1, file2)
|
| 128 |
+
resp_data = {"match": bool(ret)} # convert ret (numpy._bool) to bool for json.dumps
|
| 129 |
+
return json.dumps(resp_data)
|
| 130 |
+
|
| 131 |
+
# Return a demo page for GET request
|
| 132 |
+
return '''
|
| 133 |
+
<!doctype html>
|
| 134 |
+
<title>Face Match</title>
|
| 135 |
+
<h1>Upload two images</h1>
|
| 136 |
+
<form method=post enctype=multipart/form-data>
|
| 137 |
+
<input type=file name=file1>
|
| 138 |
+
<input type=file name=file2>
|
| 139 |
+
<input type=submit value=Upload>
|
| 140 |
+
</form>
|
| 141 |
+
'''
|
| 142 |
+
|
| 143 |
+
@app.route('/')
|
| 144 |
+
def hello_world():
|
| 145 |
+
return 'Hello, World!'
|
| 146 |
+
|
| 147 |
+
# Run in HTTP
|
| 148 |
+
# When debug = True, code is reloaded on the fly while saved
|
| 149 |
+
app.run(host='0.0.0.0', port='5001', debug=True)
|
gists/form-data_example.txt
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
content-type:"multipart/form-data; boundary=--------------------------706175916610648661144841"
|
| 2 |
+
content-length:278
|
| 3 |
+
|
| 4 |
+
----------------------------706175916610648661144841
|
| 5 |
+
Content-Disposition: form-data; name="firstname"
|
| 6 |
+
|
| 7 |
+
Mickey
|
| 8 |
+
----------------------------706175916610648661144841
|
| 9 |
+
Content-Disposition: form-data; name="lastname"
|
| 10 |
+
|
| 11 |
+
Mouse
|
| 12 |
+
----------------------------706175916610648661144841--
|
gists/form-data_file.txt
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
http://127.0.0.1:5001/face_rec
|
| 2 |
+
content-type: "multipart/form-data; boundary=---------------------------151482349718403643091396029930"
|
| 3 |
+
content-length: 91238
|
| 4 |
+
-----------------------------151482349718403643091396029930
|
| 5 |
+
Content-Disposition: form-data; name="file"; filename="peter2.jpg"
|
| 6 |
+
Content-Type: image/jpeg
|
| 7 |
+
|
| 8 |
+
<image raw data>
|
| 9 |
+
-----------------------------151482349718403643091396029930--
|
requirements.txt
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
cmake
|
| 2 |
+
Werkzeug
|
| 3 |
+
requests
|
| 4 |
+
Flask
|
| 5 |
+
gunicorn
|
| 6 |
+
face_recognition
|
| 7 |
+
Pillow
|
sample_images/biden.jpg
ADDED
|
sample_images/obama.jpg
ADDED
|
sample_images/obama2.jpg
ADDED
|
sample_images/turnbull.jpg
ADDED
|
sample_outputs.txt
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
face_rec response:
|
| 2 |
+
{"name": "Peter", "face_locations": [206, 526, 527, 205], "facial_features": {"left_eye": [[285, 285], [301, 278], [320, 279], [335, 289], [318, 292], [300, 292]], "nose_bridge": [[376, 277], [377, 304], [377, 328], [378, 354]], "top_lip": [[329, 433], [348, 420], [366, 411], [379, 416], [393, 412], [410, 420], [425, 432], [417, 432], [393, 429], [379, 430], [366, 429], [337, 434]], "left_eyebrow": [[255, 259], [275, 243], [301, 236], [328, 241], [350, 249]], "bottom_lip": [[425, 432], [410, 442], [395, 449], [380, 452], [366, 451], [349, 447], [329, 433], [337, 434], [366, 430], [379, 432], [393, 429], [417, 432]], "nose_tip": [[349, 378], [363, 381], [378, 384], [392, 381], [405, 378]], "right_eyebrow": [[399, 246], [421, 238], [444, 237], [466, 244], [482, 259]], "right_eye": [[411, 290], [428, 280], [446, 281], [458, 289], [446, 295], [429, 294]], "chin": [[228, 301], [229, 338], [237, 377], [246, 414], [261, 445], [282, 475], [311, 498], [344, 515], [381, 518], [415, 512], [441, 493], [462, 468], [477, 440], [488, 408]]}}
|
| 3 |
+
|
| 4 |
+
|
| 5 |
+
http://127.0.0.1:5001/face_rec
|
| 6 |
+
content-type: "multipart/form-data; boundary=---------------------------151482349718403643091396029930"
|
| 7 |
+
content-length: 91238
|
| 8 |
+
-----------------------------151482349718403643091396029930
|
| 9 |
+
Content-Disposition: form-data; name="file"; filename="peter2.jpg"
|
| 10 |
+
Content-Type: image/jpeg
|
| 11 |
+
|
| 12 |
+
<image raw data>
|
| 13 |
+
-----------------------------151482349718403643091396029930--
|
ssl_keys/gen_ssl_keys.py
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from werkzeug.serving import make_ssl_devcert
|
| 2 |
+
make_ssl_devcert('key', host='localhost')
|
ssl_keys/key.crt
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
-----BEGIN CERTIFICATE-----
|
| 2 |
+
MIIC+TCCAeECCBojnT0ZPNwAMA0GCSqGSIb3DQEBCwUAMD8xITAfBgNVBAMMGCou
|
| 3 |
+
bG9jYWxob3N0L0NOPWxvY2FsaG9zdDEaMBgGA1UECgwRRHVtbXkgQ2VydGlmaWNh
|
| 4 |
+
dGUwHhcNMTkwMzIyMDY1MjEyWhcNMjAwMzIxMDY1MjEyWjA/MSEwHwYDVQQDDBgq
|
| 5 |
+
LmxvY2FsaG9zdC9DTj1sb2NhbGhvc3QxGjAYBgNVBAoMEUR1bW15IENlcnRpZmlj
|
| 6 |
+
YXRlMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA5JD+A+MK51iKFKG8
|
| 7 |
+
T+5rWOYWs3i8Z3L/YqjAe4yxS7vWPRmTKgf5B1YIKM8+wSsyX+2sH6iQQtOR8qmr
|
| 8 |
+
4FjY94mGH365GPBVLgvExoGJyoVtdha3c5mkiRufhVxIou8Pss3kaKkRBrlevOaE
|
| 9 |
+
b9f65abeZmazxYTYP/mX201/Z/cLNRP5EaIBL+Ixpxsu19uP1Xhua/p6LF86W6AS
|
| 10 |
+
FhThTKR5/XtCKWxB7RIn2pPsipP51iragAsP1GQ3kX96dZZ9ovkc/Vf4hZdFucgS
|
| 11 |
+
2OucuOJMHG5F80UuNnXlUjVvUTvbcX/0/vCEG2zYvr/qI2km56Qmgei89r7UNc9m
|
| 12 |
+
TD5sgQIDAQABMA0GCSqGSIb3DQEBCwUAA4IBAQDJFD8LplIAdUAbTAoCuuUt+oyl
|
| 13 |
+
zBExrEotUO7xTUqLYCP73dEVb/63Rzoc7CsfZU+Wk3G5d0gTGstr4FThke4tDpE+
|
| 14 |
+
ODyiYNubYb/R55Q25JzeShKptvMcNQyh9G2MCGnCMO3Y//yO37YrUjttv39L4LCO
|
| 15 |
+
tU8EgOr1DsMnKSNUIS0+HEXaRry7QJv2J4jpXufx+UsScAq9yxniPox6SeeyzEqX
|
| 16 |
+
wJwi/uSUi8dxt++JZQOoQ08VVUQzy2cbZO5zDv5KmmQ2EgdQGdfFYly7wT9UIku6
|
| 17 |
+
QocprekLImbV88dOqTf+76V7n7zx02nw+GiP/F8XcW7McZU3NUmx6/kupyjc
|
| 18 |
+
-----END CERTIFICATE-----
|
ssl_keys/key.key
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
-----BEGIN PRIVATE KEY-----
|
| 2 |
+
MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDkkP4D4wrnWIoU
|
| 3 |
+
obxP7mtY5hazeLxncv9iqMB7jLFLu9Y9GZMqB/kHVggozz7BKzJf7awfqJBC05Hy
|
| 4 |
+
qavgWNj3iYYffrkY8FUuC8TGgYnKhW12FrdzmaSJG5+FXEii7w+yzeRoqREGuV68
|
| 5 |
+
5oRv1/rlpt5mZrPFhNg/+ZfbTX9n9ws1E/kRogEv4jGnGy7X24/VeG5r+nosXzpb
|
| 6 |
+
oBIWFOFMpHn9e0IpbEHtEifak+yKk/nWKtqACw/UZDeRf3p1ln2i+Rz9V/iFl0W5
|
| 7 |
+
yBLY65y44kwcbkXzRS42deVSNW9RO9txf/T+8IQbbNi+v+ojaSbnpCaB6Lz2vtQ1
|
| 8 |
+
z2ZMPmyBAgMBAAECggEBAM9A8jRQEbkJPdvLdFf+VvR7XqZKmnwreIvbfP4K61FC
|
| 9 |
+
99bbc+gu5o7SYf+vPLYoFzuI5gSm8njGI5coZyO9LK/40deJLwoAExz3quxc1bcA
|
| 10 |
+
Get0WdDGxr1UgOeKcIrdvxxhOfX1J+0y9UbQt9I6w9St5QhxpB08gmIwTpSaxyZa
|
| 11 |
+
03d/a/Y/7rzaSFpTbIw+ydc+oyczQ9q9yTFa0WTvQAbsrnLvq4YIjQID9bhzhO7z
|
| 12 |
+
6yLjXZXJrjoXxmlgSMrTyr9m21df564jnEfqRSL2dMRawScM3iaGPsFpMQGEd6HT
|
| 13 |
+
ihKBKciHsHhnnozdXmPaV7VUZ1GGvjmruCz2EiZAGokCgYEA/Tycey0zWX/GosTu
|
| 14 |
+
lZ1muZrNxuBJHc0/IRDIZd2t8k7UGv09Yc6sICQ/GHgv495u7bOuw9MOnZQaPPB3
|
| 15 |
+
1fj0vGJJcQIbZwAMcneuYIDimGh9jYP445J4EMXv+kOLkCCy91L7xxgjp2twoJ+l
|
| 16 |
+
PonKFAsDf63Hr0jtqknGr7Dbe18CgYEA5w93je16r96YCI13DVpa01NU22eVFoYL
|
| 17 |
+
V/4wItfXrw/RXVb53JLN53HaUwZefyDicnyGnXA9VN0fIXSNZI/O2gJcjWlkbdON
|
| 18 |
+
arDYTqDf5R8a1zYrfYmhLYA0OvuMJrDEQQxvZZhbJaDHLS+e9bmMWEwxSBcOWYWO
|
| 19 |
+
TCqtmwJjBB8CgYBKIaEpSd0bWRVb9sxlDfPRZ6jPjD67M6dEVbZkY5WVNlBPKkz6
|
| 20 |
+
wdaPfizeS+ixCx9sBEqn1rQTmxRnPHnIMJ3sO9WF+HjvGQuLws9QPsqWlRgUAkss
|
| 21 |
+
y2bMejU7DOThJ0Fct3UvHjjpvMD2tGVaPlniriqzCLtI689vJzO7oMlPfwKBgDdt
|
| 22 |
+
PWedZQXVJrE+hiV83Da/UabEBT5+7y+veQqSrll/OODkIVHpIV14sT+jUn26fLer
|
| 23 |
+
6XrQ1tWJqZMT62sJGDyxvlPpyT6TNeQ3AdxyuOK0gHLJussFfdV1YJIZj3gkhR95
|
| 24 |
+
hStgzdSL6duMBdy6ItF3jYbWiQmugQECZ9y8SNZPAoGBAMIG5YmMcnRmJLoW1y9h
|
| 25 |
+
RWtJ0HP1hKZpLeWzkjEoRJe6PmVj+W/zmXWynjyLE47QiFsgm0EGCkGMvbBtWgbY
|
| 26 |
+
yM+fjKIPeYGS/zHgK+4TgN6HHM9HxGL76NSH5yzBP0lpxDpiWdEOmYt7ahwiF1ga
|
| 27 |
+
KUZzUGXJ6p9vr4nIIwz3Bd96
|
| 28 |
+
-----END PRIVATE KEY-----
|
wsgi.py
ADDED
|
@@ -0,0 +1,4 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from app import app
|
| 2 |
+
|
| 3 |
+
if __name__ == "__main__":
|
| 4 |
+
app.run()
|