Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat task 0402 api bug and api tests #1004

Merged
merged 10 commits into from
Feb 4, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
6 changes: 2 additions & 4 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,8 @@ RUN apt-get install ffmpeg libsm6 libxext6 -y
# -----------------------------------
# Copy required files from repo into image
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 ./package_info.json /app/package_info.json
COPY ./setup.py /app/
COPY ./README.md /app/

Expand All @@ -50,5 +47,6 @@ ENV PYTHONUNBUFFERED=1

# -----------------------------------
# run the app (re-configure port if necessary)
WORKDIR /app/deepface/api/src
EXPOSE 5000
CMD ["gunicorn", "--workers=1", "--timeout=3600", "--bind=0.0.0.0:5000", "app:create_app()"]
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -280,7 +280,7 @@ user

**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
cd scripts
Expand Down
42 changes: 0 additions & 42 deletions api/src/service.py

This file was deleted.

Empty file added deepface/api/__init__.py
Empty file.
Empty file added deepface/api/src/__init__.py
Empty file.
File renamed without changes.
3 changes: 1 addition & 2 deletions api/src/app.py → deepface/api/src/app.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
# 3rd parth dependencies
from flask import Flask
from routes import blueprint

from deepface.api.src.modules.core.routes import blueprint

def create_app():
app = Flask(__name__)
Expand Down
Empty file.
Empty file.
19 changes: 13 additions & 6 deletions api/src/routes.py → deepface/api/src/modules/core/routes.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
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__)

Expand All @@ -16,7 +19,7 @@ def represent():
if input_args is None:
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:
return {"message": "you must pass img_path input"}

Expand All @@ -33,6 +36,8 @@ def represent():
align=align,
)

logger.debug(obj)

return obj


Expand All @@ -43,8 +48,8 @@ def verify():
if input_args is None:
return {"message": "empty input set passed"}

img1_path = input_args.get("img1_path")
img2_path = input_args.get("img2_path")
img1_path = input_args.get("img1") or input_args.get("img1_path")
img2_path = input_args.get("img2") or input_args.get("img2_path")

if img1_path is None:
return {"message": "you must pass img1_path input"}
Expand All @@ -68,7 +73,7 @@ def verify():
enforce_detection=enforce_detection,
)

verification["verified"] = str(verification["verified"])
logger.debug(verification)

return verification

Expand All @@ -80,7 +85,7 @@ def analyze():
if input_args is None:
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:
return {"message": "you must pass img_path input"}

Expand All @@ -97,4 +102,6 @@ def analyze():
align=align,
)

logger.debug(demographies)

return demographies
54 changes: 54 additions & 0 deletions deepface/api/src/modules/core/service.py
Original file line number Diff line number Diff line change
@@ -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
8 changes: 4 additions & 4 deletions deepface/modules/detection.py
Original file line number Diff line number Diff line change
Expand Up @@ -167,10 +167,10 @@ def extract_faces(
{
"face": img_pixels[:, :, ::-1] if human_readable is True else img_pixels,
"facial_area": {
"x": current_region.x,
"y": current_region.y,
"w": current_region.w,
"h": current_region.h,
"x": int(current_region.x),
"y": int(current_region.y),
"w": int(current_region.w),
"h": int(current_region.h),
},
"confidence": confidence,
}
Expand Down
2 changes: 1 addition & 1 deletion scripts/service.sh
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
#!/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()"
123 changes: 123 additions & 0 deletions tests/test_api.py
Original file line number Diff line number Diff line change
@@ -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