mirror of
https://github.com/serengil/deepface.git
synced 2025-06-07 20:15:21 +00:00
Merge pull request #1004 from serengil/feat-task-0402-api-bug-and-api-tests
Feat task 0402 api bug and api tests
This commit is contained in:
commit
889d033d12
@ -19,11 +19,8 @@ RUN apt-get install ffmpeg libsm6 libxext6 -y
|
|||||||
# -----------------------------------
|
# -----------------------------------
|
||||||
# Copy required files from repo into image
|
# Copy required files from repo into image
|
||||||
COPY ./deepface /app/deepface
|
COPY ./deepface /app/deepface
|
||||||
COPY ./api/src/app.py /app/
|
|
||||||
COPY ./api/src/api.py /app/
|
|
||||||
COPY ./api/src/routes.py /app/
|
|
||||||
COPY ./api/src/service.py /app/
|
|
||||||
COPY ./requirements.txt /app/
|
COPY ./requirements.txt /app/
|
||||||
|
COPY ./package_info.json /app/package_info.json
|
||||||
COPY ./setup.py /app/
|
COPY ./setup.py /app/
|
||||||
COPY ./README.md /app/
|
COPY ./README.md /app/
|
||||||
|
|
||||||
@ -50,5 +47,6 @@ ENV PYTHONUNBUFFERED=1
|
|||||||
|
|
||||||
# -----------------------------------
|
# -----------------------------------
|
||||||
# run the app (re-configure port if necessary)
|
# run the app (re-configure port if necessary)
|
||||||
|
WORKDIR /app/deepface/api/src
|
||||||
EXPOSE 5000
|
EXPOSE 5000
|
||||||
CMD ["gunicorn", "--workers=1", "--timeout=3600", "--bind=0.0.0.0:5000", "app:create_app()"]
|
CMD ["gunicorn", "--workers=1", "--timeout=3600", "--bind=0.0.0.0:5000", "app:create_app()"]
|
||||||
|
@ -280,7 +280,7 @@ user
|
|||||||
|
|
||||||
**API** - [`Demo`](https://youtu.be/HeKCQ6U9XmI)
|
**API** - [`Demo`](https://youtu.be/HeKCQ6U9XmI)
|
||||||
|
|
||||||
DeepFace serves an API as well. You can clone [`/api`](https://github.com/serengil/deepface/tree/master/api) folder and run the api via gunicorn server. This will get a rest service up. In this way, you can call deepface from an external system such as mobile app or web.
|
DeepFace serves an API as well. You can clone deepface source code and run the api via gunicorn server with the following command. This will get a rest service up. In this way, you can call deepface from an external system such as mobile app or web.
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
cd scripts
|
cd scripts
|
||||||
|
@ -1,42 +0,0 @@
|
|||||||
from deepface import DeepFace
|
|
||||||
|
|
||||||
|
|
||||||
def represent(img_path, model_name, detector_backend, enforce_detection, align):
|
|
||||||
result = {}
|
|
||||||
embedding_objs = DeepFace.represent(
|
|
||||||
img_path=img_path,
|
|
||||||
model_name=model_name,
|
|
||||||
detector_backend=detector_backend,
|
|
||||||
enforce_detection=enforce_detection,
|
|
||||||
align=align,
|
|
||||||
)
|
|
||||||
result["results"] = embedding_objs
|
|
||||||
return result
|
|
||||||
|
|
||||||
|
|
||||||
def verify(
|
|
||||||
img1_path, img2_path, model_name, detector_backend, distance_metric, enforce_detection, align
|
|
||||||
):
|
|
||||||
obj = DeepFace.verify(
|
|
||||||
img1_path=img1_path,
|
|
||||||
img2_path=img2_path,
|
|
||||||
model_name=model_name,
|
|
||||||
detector_backend=detector_backend,
|
|
||||||
distance_metric=distance_metric,
|
|
||||||
align=align,
|
|
||||||
enforce_detection=enforce_detection,
|
|
||||||
)
|
|
||||||
return obj
|
|
||||||
|
|
||||||
|
|
||||||
def analyze(img_path, actions, detector_backend, enforce_detection, align):
|
|
||||||
result = {}
|
|
||||||
demographies = DeepFace.analyze(
|
|
||||||
img_path=img_path,
|
|
||||||
actions=actions,
|
|
||||||
detector_backend=detector_backend,
|
|
||||||
enforce_detection=enforce_detection,
|
|
||||||
align=align,
|
|
||||||
)
|
|
||||||
result["results"] = demographies
|
|
||||||
return result
|
|
0
deepface/api/__init__.py
Normal file
0
deepface/api/__init__.py
Normal file
0
deepface/api/src/__init__.py
Normal file
0
deepface/api/src/__init__.py
Normal file
@ -1,7 +1,6 @@
|
|||||||
# 3rd parth dependencies
|
# 3rd parth dependencies
|
||||||
from flask import Flask
|
from flask import Flask
|
||||||
from routes import blueprint
|
from deepface.api.src.modules.core.routes import blueprint
|
||||||
|
|
||||||
|
|
||||||
def create_app():
|
def create_app():
|
||||||
app = Flask(__name__)
|
app = Flask(__name__)
|
0
deepface/api/src/modules/__init__.py
Normal file
0
deepface/api/src/modules/__init__.py
Normal file
0
deepface/api/src/modules/core/__init__.py
Normal file
0
deepface/api/src/modules/core/__init__.py
Normal file
@ -1,5 +1,8 @@
|
|||||||
from flask import Blueprint, request
|
from flask import Blueprint, request
|
||||||
import service
|
from deepface.api.src.modules.core import service
|
||||||
|
from deepface.commons.logger import Logger
|
||||||
|
|
||||||
|
logger = Logger(module="api/src/routes.py")
|
||||||
|
|
||||||
blueprint = Blueprint("routes", __name__)
|
blueprint = Blueprint("routes", __name__)
|
||||||
|
|
||||||
@ -16,7 +19,7 @@ def represent():
|
|||||||
if input_args is None:
|
if input_args is None:
|
||||||
return {"message": "empty input set passed"}
|
return {"message": "empty input set passed"}
|
||||||
|
|
||||||
img_path = input_args.get("img")
|
img_path = input_args.get("img") or input_args.get("img_path")
|
||||||
if img_path is None:
|
if img_path is None:
|
||||||
return {"message": "you must pass img_path input"}
|
return {"message": "you must pass img_path input"}
|
||||||
|
|
||||||
@ -33,6 +36,8 @@ def represent():
|
|||||||
align=align,
|
align=align,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
logger.debug(obj)
|
||||||
|
|
||||||
return obj
|
return obj
|
||||||
|
|
||||||
|
|
||||||
@ -43,8 +48,8 @@ def verify():
|
|||||||
if input_args is None:
|
if input_args is None:
|
||||||
return {"message": "empty input set passed"}
|
return {"message": "empty input set passed"}
|
||||||
|
|
||||||
img1_path = input_args.get("img1_path")
|
img1_path = input_args.get("img1") or input_args.get("img1_path")
|
||||||
img2_path = input_args.get("img2_path")
|
img2_path = input_args.get("img2") or input_args.get("img2_path")
|
||||||
|
|
||||||
if img1_path is None:
|
if img1_path is None:
|
||||||
return {"message": "you must pass img1_path input"}
|
return {"message": "you must pass img1_path input"}
|
||||||
@ -68,7 +73,7 @@ def verify():
|
|||||||
enforce_detection=enforce_detection,
|
enforce_detection=enforce_detection,
|
||||||
)
|
)
|
||||||
|
|
||||||
verification["verified"] = str(verification["verified"])
|
logger.debug(verification)
|
||||||
|
|
||||||
return verification
|
return verification
|
||||||
|
|
||||||
@ -80,7 +85,7 @@ def analyze():
|
|||||||
if input_args is None:
|
if input_args is None:
|
||||||
return {"message": "empty input set passed"}
|
return {"message": "empty input set passed"}
|
||||||
|
|
||||||
img_path = input_args.get("img_path")
|
img_path = input_args.get("img") or input_args.get("img_path")
|
||||||
if img_path is None:
|
if img_path is None:
|
||||||
return {"message": "you must pass img_path input"}
|
return {"message": "you must pass img_path input"}
|
||||||
|
|
||||||
@ -97,4 +102,6 @@ def analyze():
|
|||||||
align=align,
|
align=align,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
logger.debug(demographies)
|
||||||
|
|
||||||
return demographies
|
return demographies
|
54
deepface/api/src/modules/core/service.py
Normal file
54
deepface/api/src/modules/core/service.py
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
from deepface import DeepFace
|
||||||
|
|
||||||
|
# pylint: disable=broad-except
|
||||||
|
|
||||||
|
|
||||||
|
def represent(img_path, model_name, detector_backend, enforce_detection, align):
|
||||||
|
try:
|
||||||
|
result = {}
|
||||||
|
embedding_objs = DeepFace.represent(
|
||||||
|
img_path=img_path,
|
||||||
|
model_name=model_name,
|
||||||
|
detector_backend=detector_backend,
|
||||||
|
enforce_detection=enforce_detection,
|
||||||
|
align=align,
|
||||||
|
)
|
||||||
|
result["results"] = embedding_objs
|
||||||
|
return result
|
||||||
|
except Exception as err:
|
||||||
|
return {"error": f"Exception while representing: {str(err)}"}, 400
|
||||||
|
|
||||||
|
|
||||||
|
def verify(
|
||||||
|
img1_path, img2_path, model_name, detector_backend, distance_metric, enforce_detection, align
|
||||||
|
):
|
||||||
|
try:
|
||||||
|
obj = DeepFace.verify(
|
||||||
|
img1_path=img1_path,
|
||||||
|
img2_path=img2_path,
|
||||||
|
model_name=model_name,
|
||||||
|
detector_backend=detector_backend,
|
||||||
|
distance_metric=distance_metric,
|
||||||
|
align=align,
|
||||||
|
enforce_detection=enforce_detection,
|
||||||
|
)
|
||||||
|
return obj
|
||||||
|
except Exception as err:
|
||||||
|
return {"error": f"Exception while verifying: {str(err)}"}, 400
|
||||||
|
|
||||||
|
|
||||||
|
def analyze(img_path, actions, detector_backend, enforce_detection, align):
|
||||||
|
try:
|
||||||
|
result = {}
|
||||||
|
demographies = DeepFace.analyze(
|
||||||
|
img_path=img_path,
|
||||||
|
actions=actions,
|
||||||
|
detector_backend=detector_backend,
|
||||||
|
enforce_detection=enforce_detection,
|
||||||
|
align=align,
|
||||||
|
silent=True,
|
||||||
|
)
|
||||||
|
result["results"] = demographies
|
||||||
|
return result
|
||||||
|
except Exception as err:
|
||||||
|
return {"error": f"Exception while analyzing: {str(err)}"}, 400
|
@ -167,10 +167,10 @@ def extract_faces(
|
|||||||
{
|
{
|
||||||
"face": img_pixels[:, :, ::-1] if human_readable is True else img_pixels,
|
"face": img_pixels[:, :, ::-1] if human_readable is True else img_pixels,
|
||||||
"facial_area": {
|
"facial_area": {
|
||||||
"x": current_region.x,
|
"x": int(current_region.x),
|
||||||
"y": current_region.y,
|
"y": int(current_region.y),
|
||||||
"w": current_region.w,
|
"w": int(current_region.w),
|
||||||
"h": current_region.h,
|
"h": int(current_region.h),
|
||||||
},
|
},
|
||||||
"confidence": confidence,
|
"confidence": confidence,
|
||||||
}
|
}
|
||||||
|
@ -1,3 +1,3 @@
|
|||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
cd ../api/src
|
cd ../deepface/api/src
|
||||||
gunicorn --workers=1 --timeout=3600 --bind=0.0.0.0:5000 "app:create_app()"
|
gunicorn --workers=1 --timeout=3600 --bind=0.0.0.0:5000 "app:create_app()"
|
123
tests/test_api.py
Normal file
123
tests/test_api.py
Normal file
@ -0,0 +1,123 @@
|
|||||||
|
import unittest
|
||||||
|
from deepface.commons.logger import Logger
|
||||||
|
from deepface.api.src.app import create_app
|
||||||
|
|
||||||
|
|
||||||
|
logger = Logger("tests/test_api.py")
|
||||||
|
|
||||||
|
|
||||||
|
class TestVerifyEndpoint(unittest.TestCase):
|
||||||
|
def setUp(self):
|
||||||
|
app = create_app()
|
||||||
|
app.config["DEBUG"] = True
|
||||||
|
app.config["TESTING"] = True
|
||||||
|
self.app = app.test_client()
|
||||||
|
|
||||||
|
def test_tp_verify(self):
|
||||||
|
data = {
|
||||||
|
"img1_path": "dataset/img1.jpg",
|
||||||
|
"img2_path": "dataset/img2.jpg",
|
||||||
|
}
|
||||||
|
response = self.app.post("/verify", json=data)
|
||||||
|
assert response.status_code == 200
|
||||||
|
result = response.json
|
||||||
|
logger.debug(result)
|
||||||
|
|
||||||
|
assert result.get("verified") is not None
|
||||||
|
assert result.get("model") is not None
|
||||||
|
assert result.get("similarity_metric") is not None
|
||||||
|
assert result.get("detector_backend") is not None
|
||||||
|
assert result.get("distance") is not None
|
||||||
|
assert result.get("threshold") is not None
|
||||||
|
assert result.get("facial_areas") is not None
|
||||||
|
|
||||||
|
assert result.get("verified") is True
|
||||||
|
|
||||||
|
logger.info("✅ true-positive verification api test is done")
|
||||||
|
|
||||||
|
def test_tn_verify(self):
|
||||||
|
data = {
|
||||||
|
"img1_path": "dataset/img1.jpg",
|
||||||
|
"img2_path": "dataset/img2.jpg",
|
||||||
|
}
|
||||||
|
response = self.app.post("/verify", json=data)
|
||||||
|
assert response.status_code == 200
|
||||||
|
result = response.json
|
||||||
|
logger.debug(result)
|
||||||
|
|
||||||
|
assert result.get("verified") is not None
|
||||||
|
assert result.get("model") is not None
|
||||||
|
assert result.get("similarity_metric") is not None
|
||||||
|
assert result.get("detector_backend") is not None
|
||||||
|
assert result.get("distance") is not None
|
||||||
|
assert result.get("threshold") is not None
|
||||||
|
assert result.get("facial_areas") is not None
|
||||||
|
|
||||||
|
assert result.get("verified") is True
|
||||||
|
|
||||||
|
logger.info("✅ true-negative verification api test is done")
|
||||||
|
|
||||||
|
def test_represent(self):
|
||||||
|
data = {
|
||||||
|
"img": "dataset/img1.jpg",
|
||||||
|
}
|
||||||
|
response = self.app.post("/represent", json=data)
|
||||||
|
assert response.status_code == 200
|
||||||
|
result = response.json
|
||||||
|
logger.debug(result)
|
||||||
|
assert result.get("results") is not None
|
||||||
|
assert isinstance(result["results"], list) is True
|
||||||
|
assert len(result["results"]) > 0
|
||||||
|
for i in result["results"]:
|
||||||
|
assert i.get("embedding") is not None
|
||||||
|
assert isinstance(i.get("embedding"), list) is True
|
||||||
|
assert len(i.get("embedding")) == 4096
|
||||||
|
assert i.get("face_confidence") is not None
|
||||||
|
assert i.get("facial_area") is not None
|
||||||
|
|
||||||
|
logger.info("✅ representation api test is done")
|
||||||
|
|
||||||
|
def test_analyze(self):
|
||||||
|
data = {
|
||||||
|
"img": "dataset/img1.jpg",
|
||||||
|
}
|
||||||
|
response = self.app.post("/analyze", json=data)
|
||||||
|
assert response.status_code == 200
|
||||||
|
result = response.json
|
||||||
|
logger.debug(result)
|
||||||
|
assert result.get("results") is not None
|
||||||
|
assert isinstance(result["results"], list) is True
|
||||||
|
assert len(result["results"]) > 0
|
||||||
|
for i in result["results"]:
|
||||||
|
assert i.get("age") is not None
|
||||||
|
assert isinstance(i.get("age"), (int, float))
|
||||||
|
assert i.get("dominant_gender") is not None
|
||||||
|
assert i.get("dominant_gender") in ["Man", "Woman"]
|
||||||
|
assert i.get("dominant_emotion") is not None
|
||||||
|
assert i.get("dominant_race") is not None
|
||||||
|
|
||||||
|
logger.info("✅ analyze api test is done")
|
||||||
|
|
||||||
|
def test_invalid_verify(self):
|
||||||
|
data = {
|
||||||
|
"img1_path": "dataset/invalid_1.jpg",
|
||||||
|
"img2_path": "dataset/invalid_2.jpg",
|
||||||
|
}
|
||||||
|
response = self.app.post("/verify", json=data)
|
||||||
|
assert response.status_code == 400
|
||||||
|
logger.info("✅ invalid verification request api test is done")
|
||||||
|
|
||||||
|
def test_invalid_represent(self):
|
||||||
|
data = {
|
||||||
|
"img": "dataset/invalid_1.jpg",
|
||||||
|
}
|
||||||
|
response = self.app.post("/represent", json=data)
|
||||||
|
assert response.status_code == 400
|
||||||
|
logger.info("✅ invalid represent request api test is done")
|
||||||
|
|
||||||
|
def test_invalid_analyze(self):
|
||||||
|
data = {
|
||||||
|
"img": "dataset/invalid.jpg",
|
||||||
|
}
|
||||||
|
response = self.app.post("/analyze", json=data)
|
||||||
|
assert response.status_code == 400
|
Loading…
x
Reference in New Issue
Block a user