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

Add subjects crud #9

Merged
merged 17 commits into from
Sep 30, 2021
Merged
834 changes: 513 additions & 321 deletions README.md

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions compreface/client/__init__.py
Original file line number Diff line number Diff line change
@@ -20,3 +20,4 @@
from .recognize_face_from_image import RecognizeFaceFromImageClient
from .detect_face_from_image import DetectFaceFromImageClient
from .verify_face_from_image import VerifyFaceFromImageClient
from .subject_client import SubjectClient
81 changes: 81 additions & 0 deletions compreface/client/subject_client.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
"""
Copyright(c) 2021 the original author or authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
https: // www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
or implied. See the License for the specific language governing
permissions and limitations under the License.
"""
import json

import requests

from compreface.config.api_list import SUBJECTS_CRUD_API
from ..common import ClientRequest


class SubjectClient(ClientRequest):

def __init__(self, api_key: str, domain: str, port: str):
super().__init__()
self.client_url: str = SUBJECTS_CRUD_API
self.api_key: str = api_key
self.url: str = domain + ':' + port + self.client_url
self.headers = {'Content-Type': 'application/json', 'x-api-key': api_key}

"""
GET request for get all subjects.
:return: json with subjects from server.
"""

def get(self) -> dict:
url: str = self.url
result = requests.get(url, headers=self.headers)
return result.json()

"""
POST request for add subject without an image.
:param subject: fullname
:return: json with this subject from server.
"""

def post(self, subject: dict = '') -> dict:
url: str = self.url
result = requests.post(url, data=json.dumps(subject), headers=self.headers)
return result.json()

"""
PUT request to CompreFace server for rename existing subject.
:param subject: fullname
:return: json from server.
"""

def put(self, request: dict = '') -> dict:
url: str = self.url + '/' + request.get('api_endpoint')
result = requests.put(url, data=json.dumps(request), headers=self.headers)
return result.json()

"""
DELETE request to CompreFace server for delete subjects.
:param subject: fullname
:return: json from server.
"""

def delete(self, subject: str = '') -> dict:
url: str = self.url + '/' + subject if subject else self.url
result = requests.delete(url, headers=self.headers)
return result.json()
2 changes: 1 addition & 1 deletion compreface/collections/__init__.py
Original file line number Diff line number Diff line change
@@ -14,4 +14,4 @@
permissions and limitations under the License.
"""

from .face_collections import FaceCollection
from .face_collections import FaceCollection, Subjects
121 changes: 104 additions & 17 deletions compreface/collections/face_collections.py
Original file line number Diff line number Diff line change
@@ -17,10 +17,15 @@
from compreface.common.typed_dict import AllOptionsDict, ExpandedOptionsDict, DetProbOptionsDict, pass_dict
from ..use_cases import (
AddExampleOfSubject,
ListOfAllSavedSubjects,
AddSubject,
DeleteAllExamplesOfSubjectByName,
DeleteSubjectByName,
DeleteAllSubjects,
DeleteExampleById,
VerificationFaceFromImage
GetSubjects,
UpdateSubject,
VerificationFaceFromImage,
ListOfAllSavedSubjects
)


@@ -56,6 +61,13 @@ def __init__(self, api_key: str, domain: str, port: str, options: AllOptionsDict
api_key=api_key
)

def list(self) -> dict:
"""
Get list of collections
:return:
"""
return self.list_of_all_saved_subjects.execute()

def add(self, image_path: str, subject: str, options: DetProbOptionsDict = {}) -> dict:
"""
Add example to collection
@@ -70,12 +82,17 @@ def add(self, image_path: str, subject: str, options: DetProbOptionsDict = {}) -
)
return self.add_example.execute(request, pass_dict(options, DetProbOptionsDict) if options == {} else options)

def list(self) -> dict:
def delete(self, image_id: str) -> dict:
"""
Get list of collections
Delete example by Id
:param image_id:
:return:
"""
return self.list_of_all_saved_subjects.execute()
request = DeleteExampleById.Request(
api_key=self.api_key,
image_id=image_id
)
return self.delete_all_examples_by_id.execute(request)

def delete_all(self, subject: str) -> dict:
"""
@@ -89,18 +106,6 @@ def delete_all(self, subject: str) -> dict:
)
return self.delete_all_examples_of_subject_by_name.execute(request)

def delete(self, image_id: str) -> dict:
"""
Delete example by Id
:param image_id:
:return:
"""
request = DeleteExampleById.Request(
api_key=self.api_key,
image_id=image_id
)
return self.delete_all_examples_by_id.execute(request)

def verify(self, image_path: str, image_id: str, options: ExpandedOptionsDict = {}) -> dict:
"""
Compare image
@@ -114,3 +119,85 @@ def verify(self, image_path: str, image_id: str, options: ExpandedOptionsDict =
image_id=image_id
)
return self.verify_face_from_image.execute(request, pass_dict(options, ExpandedOptionsDict) if options == {} else options)


class Subjects:
def __init__(self, api_key: str, domain: str, port: str, options: AllOptionsDict = {}):
"""Init service with define API Key"""
self.available_services = []
self.api_key = api_key
self.options = options
self.add_subject: AddSubject = AddSubject(
domain=domain,
port=port,
api_key=api_key
)
self.update_subject: UpdateSubject = UpdateSubject(
domain=domain,
port=port,
api_key=api_key
)
self.delete_subject: DeleteSubjectByName = DeleteSubjectByName(
domain=domain,
port=port,
api_key=api_key
)
self.delete_all_subjects: DeleteAllSubjects = DeleteAllSubjects(
domain=domain,
port=port,
api_key=api_key
)
self.list_of_all_saved_subjects: GetSubjects = GetSubjects(
domain=domain,
port=port,
api_key=api_key
)

def list(self) -> dict:
"""
Get list of subjects
:return:
"""
return self.list_of_all_saved_subjects.execute()

def add(self, subject: str) -> dict:
"""
Add subject
:param subject:
:return:
"""
request = AddSubject.Request(
subject=subject
)
return self.add_subject.execute(request)

def update(self, subject: str, new_name: str) -> dict:
"""
Update subject by name
:param subject:
:param new_name:
:return:
"""
request = UpdateSubject.Request(
subject=new_name,
api_endpoint=subject
)
return self.update_subject.execute(request)

def delete(self, subject: str) -> dict:
"""
Delete subject by name
:param subject:
:return:
"""
request = DeleteSubjectByName.Request(
subject=subject
)
return self.delete_subject.execute(request)

def delete_all(self) -> dict:
"""
Delete all subjects
:return:
"""
return self.delete_all_subjects.execute()
6 changes: 4 additions & 2 deletions compreface/common/typed_dict.py
Original file line number Diff line number Diff line change
@@ -55,9 +55,11 @@ def check_fields_by_name(name: str, value: Any):
for row in values.split(','):
if row == ',':
pass
if row.find('age') == -1 and row.find('calculator') == -1 and row.find('gender') == -1 and row.find('landmarks') == -1:
if row.find('age') == -1 and row.find('calculator') == -1 and row.find('gender') == -1 \
and row.find('landmarks') == -1 and row.find('mask') == -1:
raise IncorrectFieldException(
"face_plugins must be only contains calculator,age,gender,landmarks. Incorrect value {}".format(row))
"face_plugins must be only contains calculator,age,gender,landmarks,mask. "
"Incorrect value {}".format(row))


def pass_dict(options: AllOptionsDict, type: DetProbOptionsDict or ExpandedOptionsDict):
1 change: 1 addition & 0 deletions compreface/config/api_list.py
Original file line number Diff line number Diff line change
@@ -19,6 +19,7 @@

RECOGNIZE_API: str = RECOGNITION_ROOT_API + '/recognize'
RECOGNIZE_CRUD_API: str = RECOGNITION_ROOT_API + '/faces'
SUBJECTS_CRUD_API: str = RECOGNITION_ROOT_API + '/subjects'

DETECTION_API: str = '/api/v1/detection/detect'

19 changes: 15 additions & 4 deletions compreface/service/recognition_service.py
Original file line number Diff line number Diff line change
@@ -18,10 +18,8 @@
from typing import List

from ..common import Service
from ..collections import FaceCollection
from ..use_cases import (
RecognizeFaceFromImage
)
from ..collections import FaceCollection, Subjects
from ..use_cases import RecognizeFaceFromImage


class RecognitionService(Service):
@@ -42,6 +40,12 @@ def __init__(self, api_key: str, domain: str, port: str, options: AllOptionsDict
api_key=api_key,
options=options
)
self.subjects: Subjects = Subjects(
domain=domain,
port=port,
api_key=api_key,
options=options
)

def get_available_functions(self) -> List[str]:
"""
@@ -69,3 +73,10 @@ def get_face_collection(self) -> FaceCollection:
:return:
"""
return self.face_collection

def get_subjects(self) -> Subjects:
"""
Get subjects
:return:
"""
return self.subjects
5 changes: 5 additions & 0 deletions compreface/use_cases/__init__.py
Original file line number Diff line number Diff line change
@@ -21,3 +21,8 @@
from .recognize_face_from_image import RecognizeFaceFromImage
from .verification_face_from_image import VerificationFaceFromImage
from .detect_face_from_image import DetectFaceFromImage
from .add_subject import AddSubject
from .get_subjects import GetSubjects
from .update_subject import UpdateSubject
from .delete_subject_by_name import DeleteSubjectByName
from .delete_all_subjects import DeleteAllSubjects
37 changes: 37 additions & 0 deletions compreface/use_cases/add_subject.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
"""
Copyright(c) 2021 the original author or authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
https: // www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
or implied. See the License for the specific language governing
permissions and limitations under the License.
"""

from dataclasses import dataclass, asdict

from compreface.client.subject_client import SubjectClient


class AddSubject:

@dataclass
class Request:
subject: str

def __init__(self, domain: str, port: str, api_key: str):
self.subject_client = SubjectClient(
api_key=api_key,
domain=domain,
port=port
)

def execute(self, request: Request) -> dict:
result: dict = self.subject_client.post(asdict(request))
return result
37 changes: 37 additions & 0 deletions compreface/use_cases/delete_all_subjects.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
"""
Copyright(c) 2021 the original author or authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
https: // www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
or implied. See the License for the specific language governing
permissions and limitations under the License.
"""

from dataclasses import dataclass

from compreface.client.subject_client import SubjectClient


class DeleteAllSubjects:

@dataclass
class Request:
pass

def __init__(self, domain: str, port: str, api_key: str):
self.add_subject = SubjectClient(
api_key=api_key,
domain=domain,
port=port
)

def execute(self) -> dict:
result: dict = self.add_subject.delete()
return result
37 changes: 37 additions & 0 deletions compreface/use_cases/delete_subject_by_name.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
"""
Copyright(c) 2021 the original author or authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
https: // www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
or implied. See the License for the specific language governing
permissions and limitations under the License.
"""

from dataclasses import dataclass

from compreface.client.subject_client import SubjectClient


class DeleteSubjectByName:

@dataclass
class Request:
subject: str

def __init__(self, domain: str, port: str, api_key: str):
self.subject_client = SubjectClient(
api_key=api_key,
domain=domain,
port=port
)

def execute(self, request: Request) -> dict:
result: dict = self.subject_client.delete(request.subject)
return result
37 changes: 37 additions & 0 deletions compreface/use_cases/get_subjects.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
"""
Copyright(c) 2021 the original author or authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
https: // www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
or implied. See the License for the specific language governing
permissions and limitations under the License.
"""

from dataclasses import dataclass

from compreface.client.subject_client import SubjectClient


class GetSubjects:

@dataclass
class Request:
pass

def __init__(self, domain: str, port: str, api_key: str):
self.subject_client = SubjectClient(
api_key=api_key,
domain=domain,
port=port
)

def execute(self) -> dict:
result: dict = self.subject_client.get()
return result
38 changes: 38 additions & 0 deletions compreface/use_cases/update_subject.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
"""
Copyright(c) 2021 the original author or authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
https: // www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
or implied. See the License for the specific language governing
permissions and limitations under the License.
"""

from dataclasses import dataclass, asdict

from compreface.client.subject_client import SubjectClient


class UpdateSubject:

@dataclass
class Request:
subject: str
api_endpoint: str

def __init__(self, domain: str, port: str, api_key: str):
self.subject_client = SubjectClient(
api_key=api_key,
domain=domain,
port=port
)

def execute(self, request: Request) -> dict:
result: dict = self.subject_client.put(asdict(request))
return result
4 changes: 2 additions & 2 deletions examples/add_example_of_a_subject.py
Original file line number Diff line number Diff line change
@@ -21,7 +21,7 @@

DOMAIN: str = 'http://localhost'
PORT: str = '8000'
RECOGNITION_API_KEY: str = 'b97fbc0a-518a-4b1d-a93a-581b1d3814cc'
RECOGNITION_API_KEY: str = '00000000-0000-0000-0000-000000000002'

compre_face: CompreFace = CompreFace(DOMAIN, PORT, {
"det_prob_threshold": 0.8
@@ -33,7 +33,7 @@
face_collection: FaceCollection = recognition.get_face_collection()

# Image from local path.
image: str = 'examples/common/jonathan-petit-unsplash.jpg'
image: str = 'common/jonathan-petit-unsplash.jpg'
subject: str = 'Jonathan Petit'

print(face_collection.add(image, subject))
33 changes: 33 additions & 0 deletions examples/add_subject.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
"""
Copyright(c) 2021 the original author or authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
https: // www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
or implied. See the License for the specific language governing
permissions and limitations under the License.
"""

from compreface import CompreFace
from compreface.service import RecognitionService
from compreface.collections import Subjects

DOMAIN: str = 'http://localhost'
PORT: str = '8000'
RECOGNITION_API_KEY: str = '00000000-0000-0000-0000-000000000002'

compre_face: CompreFace = CompreFace(DOMAIN, PORT)

recognition: RecognitionService = compre_face.init_face_recognition(RECOGNITION_API_KEY)

subjects: Subjects = recognition.get_subjects()

subject: str = 'Test Subject'

print(subjects.add(subject))
Binary file modified examples/common/jonathan-petit-unsplash.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion examples/delete_all_examples_of_subject.py
Original file line number Diff line number Diff line change
@@ -20,7 +20,7 @@

DOMAIN: str = 'http://localhost'
PORT: str = '8000'
RECOGNITION_API_KEY: str = 'b97fbc0a-518a-4b1d-a93a-581b1d3814cc'
RECOGNITION_API_KEY: str = '00000000-0000-0000-0000-000000000002'


compre_face: CompreFace = CompreFace(DOMAIN, PORT)
31 changes: 31 additions & 0 deletions examples/delete_all_subjects.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
"""
Copyright(c) 2021 the original author or authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
https: // www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
or implied. See the License for the specific language governing
permissions and limitations under the License.
"""

from compreface import CompreFace
from compreface.service import RecognitionService
from compreface.collections import Subjects

DOMAIN: str = 'http://localhost'
PORT: str = '8000'
RECOGNITION_API_KEY: str = '00000000-0000-0000-0000-000000000002'

compre_face: CompreFace = CompreFace(DOMAIN, PORT)

recognition: RecognitionService = compre_face.init_face_recognition(RECOGNITION_API_KEY)

subjects: Subjects = recognition.get_subjects()

print(subjects.delete_all())
2 changes: 1 addition & 1 deletion examples/delete_example_by_id.py
Original file line number Diff line number Diff line change
@@ -20,7 +20,7 @@

DOMAIN: str = 'http://localhost'
PORT: str = '8000'
RECOGNITION_API_KEY: str = 'b97fbc0a-518a-4b1d-a93a-581b1d3814cc'
RECOGNITION_API_KEY: str = '00000000-0000-0000-0000-000000000002'


compre_face: CompreFace = CompreFace(DOMAIN, PORT)
33 changes: 33 additions & 0 deletions examples/delete_subject_by_name.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
"""
Copyright(c) 2021 the original author or authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
https: // www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
or implied. See the License for the specific language governing
permissions and limitations under the License.
"""

from compreface import CompreFace
from compreface.service import RecognitionService
from compreface.collections import Subjects

DOMAIN: str = 'http://localhost'
PORT: str = '8000'
RECOGNITION_API_KEY: str = '00000000-0000-0000-0000-000000000002'

compre_face: CompreFace = CompreFace(DOMAIN, PORT)

recognition: RecognitionService = compre_face.init_face_recognition(RECOGNITION_API_KEY)

subjects: Subjects = recognition.get_subjects()

subject: str = 'Test Subject'

print(subjects.delete(subject))
4 changes: 2 additions & 2 deletions examples/detect_face_from_image.py
Original file line number Diff line number Diff line change
@@ -20,7 +20,7 @@

DOMAIN: str = 'http://localhost'
PORT: str = '8000'
DETECTION_API_KEY: str = '444dc40f-3168-43d5-8cca-108ab401ff5c'
DETECTION_API_KEY: str = 'f4bdcf1f-1ef9-442f-863d-7bcd170723db'

compre_face: CompreFace = CompreFace(DOMAIN, PORT, {
"limit": 0,
@@ -32,6 +32,6 @@
detection: DetectionService = compre_face.init_face_detection(
DETECTION_API_KEY)

image_path: str = 'examples/common/jonathan-petit-unsplash.jpg'
image_path: str = 'common/jonathan-petit-unsplash.jpg'

print(detection.detect(image_path))
8 changes: 4 additions & 4 deletions examples/get_list_of_all_subjects.py
Original file line number Diff line number Diff line change
@@ -16,18 +16,18 @@

from compreface import CompreFace
from compreface.service import RecognitionService
from compreface.collections import FaceCollection
from compreface.collections import Subjects

DOMAIN: str = 'http://localhost'
PORT: str = '8000'
RECOGNITION_API_KEY: str = 'b97fbc0a-518a-4b1d-a93a-581b1d3814cc'
RECOGNITION_API_KEY: str = '00000000-0000-0000-0000-000000000002'


compre_face: CompreFace = CompreFace(DOMAIN, PORT)

recognition: RecognitionService = compre_face.init_face_recognition(
RECOGNITION_API_KEY)

face_collection: FaceCollection = recognition.get_face_collection()
subjects: Subjects = recognition.get_subjects()

print(face_collection.list())
print(subjects.list())
4 changes: 2 additions & 2 deletions examples/recognize_face_from_image.py
Original file line number Diff line number Diff line change
@@ -19,7 +19,7 @@

DOMAIN: str = 'http://localhost'
PORT: str = '8000'
RECOGNITION_API_KEY: str = 'b97fbc0a-518a-4b1d-a93a-581b1d3814cc'
RECOGNITION_API_KEY: str = '00000000-0000-0000-0000-000000000002'


compre_face: CompreFace = CompreFace(DOMAIN, PORT, {
@@ -32,6 +32,6 @@
recognition: RecognitionService = compre_face.init_face_recognition(
RECOGNITION_API_KEY)

image_path: str = 'examples/common/jonathan-petit-unsplash.jpg'
image_path: str = 'common/jonathan-petit-unsplash.jpg'

print(recognition.recognize(image_path))
34 changes: 34 additions & 0 deletions examples/update_existing_subject.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
"""
Copyright(c) 2021 the original author or authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
https: // www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
or implied. See the License for the specific language governing
permissions and limitations under the License.
"""

from compreface import CompreFace
from compreface.service import RecognitionService
from compreface.collections import Subjects

DOMAIN: str = 'http://localhost'
PORT: str = '8000'
RECOGNITION_API_KEY: str = '00000000-0000-0000-0000-000000000002'

compre_face: CompreFace = CompreFace(DOMAIN, PORT)

recognition: RecognitionService = compre_face.init_face_recognition(RECOGNITION_API_KEY)

subjects: Subjects = recognition.get_subjects()

subject: str = 'Test Subject'
new_name: str = 'Updated Subject'

print(subjects.update(subject, new_name))
4 changes: 2 additions & 2 deletions examples/verification_face_from_image.py
Original file line number Diff line number Diff line change
@@ -20,7 +20,7 @@

DOMAIN: str = 'http://localhost'
PORT: str = '8000'
RECOGNITION_API_KEY: str = 'b97fbc0a-518a-4b1d-a93a-581b1d3814cc'
RECOGNITION_API_KEY: str = '00000000-0000-0000-0000-000000000002'

compre_face: CompreFace = CompreFace(DOMAIN, PORT, {
"limit": 0,
@@ -31,7 +31,7 @@
recognition: RecognitionService = compre_face.init_face_recognition(
RECOGNITION_API_KEY)

image_path: str = 'examples/common/jonathan-petit-unsplash.jpg'
image_path: str = 'common/jonathan-petit-unsplash.jpg'

face_collection: FaceCollection = recognition.get_face_collection()

5 changes: 2 additions & 3 deletions examples/verify_face_from_image.py
Original file line number Diff line number Diff line change
@@ -19,7 +19,7 @@

DOMAIN: str = 'http://localhost'
PORT: str = '8000'
VERIFICATION_API_KEY: str = '7aa288a2-9082-4fa2-a975-e175deba1ad2'
VERIFICATION_API_KEY: str = '5c765423-4192-4fe8-9c60-092f495a332a'


compre_face: CompreFace = CompreFace(DOMAIN, PORT, {
@@ -32,7 +32,6 @@
verify: VerificationService = compre_face.init_face_verification(
VERIFICATION_API_KEY)

image_path: str = 'examples/common/jonathan-petit-unsplash.jpg'

image_path: str = 'common/jonathan-petit-unsplash.jpg'

print(verify.verify(image_path, image_path))
8 changes: 4 additions & 4 deletions setup.py
Original file line number Diff line number Diff line change
@@ -23,15 +23,15 @@
setup(
name='compreface-sdk',
packages=find_packages(exclude=("tests",)),
version='0.1.0',
version='0.6.0',
license='Apache License 2.0',
description='CompreFace Python SDK makes face recognition into your application even easier.',
long_description=long_description,
long_description_content_type="text/markdown",
author='Artsiom Liubymov aliubymov@exadel.com, Artsiom Khadzkou akhadzkou@exadel.com',
author_email='aliubymov@exadel.com, akhadzkou@exadel.com',
author='Artsiom Liubymov aliubymov@exadel.com, Artsiom Khadzkou akhadzkou@exadel.com, Aliaksei Tauhen atauhen@exadel.com',
author_email='aliubymov@exadel.com, akhadzkou@exadel.com, atauhen@exadel.com',
url='https://exadel.com/solutions/compreface/',
download_url='https://github.com/exadel-inc/compreface-python-sdk/archive/refs/tags/0.1.0.tar.gz',
download_url='https://github.com/exadel-inc/compreface-python-sdk/archive/refs/tags/0.6.0.tar.gz',
keywords=[
"CompreFace",
"Face Recognition",
130 changes: 130 additions & 0 deletions tests/client/test_subject_crud_client.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@

"""
Copyright(c) 2021 the original author or authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
https: // www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
or implied. See the License for the specific language governing
permissions and limitations under the License.
"""
import json
import httpretty
import requests
from compreface.client import SubjectClient
from compreface.config.api_list import SUBJECTS_CRUD_API
from tests.client.const_config import DOMAIN, PORT, DETECTION_API_KEY
"""
Server configuration
"""
url: str = DOMAIN + ":" + PORT + SUBJECTS_CRUD_API


@httpretty.activate(verbose=True, allow_net_connect=False)
def test_get():
httpretty.register_uri(
httpretty.GET,
url,
headers={'x-api-key': DETECTION_API_KEY},
body='{"subjects": ["Subject", "Subject2"]}'
)
test_subject: SubjectClient = SubjectClient(DETECTION_API_KEY, DOMAIN, PORT)
response: dict = requests.get(url=url, headers={'x-api-key': DETECTION_API_KEY}).json()
test_response: dict = test_subject.get()
assert response == test_response


@httpretty.activate(verbose=True, allow_net_connect=False)
def test_post():
httpretty.register_uri(
httpretty.POST,
url,
headers={'x-api-key': DETECTION_API_KEY,
'Content-Type': 'application/json'},
body='{"subject": "Subject"}'
)

data = {'subject': 'Subject'}

response: dict = requests.post(
url=url, data=json.dumps(data), headers={'x-api-key': DETECTION_API_KEY,
'Content-Type': 'application/json'}).json()

test_subject: SubjectClient = SubjectClient(DETECTION_API_KEY, DOMAIN, PORT)
test_response: dict = test_subject.post({'subject': 'Subject'})
assert response == test_response


@httpretty.activate(verbose=True, allow_net_connect=False)
def test_post_incorrect_response():
httpretty.register_uri(
httpretty.POST,
url,
headers={'x-api-key': DETECTION_API_KEY,
'Content-Type': 'application/json'},
body='{"subject": "Subjectss"}'
)

data = {'subject': 'Subjectss'}

response: dict = requests.post(
url=url, data=json.dumps(data), headers={'x-api-key': DETECTION_API_KEY,
'Content-Type': 'application/json'}).json()

httpretty.register_uri(
httpretty.POST,
url,
headers={'x-api-key': DETECTION_API_KEY,
'Content-Type': 'application/json'},
body='{"subject": "Subject"}'
)

test_subject: SubjectClient = SubjectClient(DETECTION_API_KEY, DOMAIN, PORT)
test_response: dict = test_subject.post({'subject': 'Subject'})
assert response != test_response


@httpretty.activate(verbose=True, allow_net_connect=False)
def test_delete():
test_url = url + '/Subject'
httpretty.register_uri(
httpretty.DELETE,
test_url,
headers={'x-api-key': DETECTION_API_KEY,
'Content-Type': 'application/json'},
body='{"subject": "Subject"}'
)

response: dict = requests.delete(url=test_url,
headers={'x-api-key': DETECTION_API_KEY,
'Content-Type': 'application/json'}).json()

test_subject: SubjectClient = SubjectClient(DETECTION_API_KEY, DOMAIN, PORT)
test_response: dict = test_subject.delete("Subject")
assert response == test_response


@httpretty.activate(verbose=True, allow_net_connect=False)
def test_put():
test_url = url + '/Subject'
httpretty.register_uri(
httpretty.PUT,
test_url,
headers={'x-api-key': DETECTION_API_KEY,
'Content-Type': 'application/json'},
body='{"subject": "NewSubject"}'
)

data = {"subject": "NewSubject"}

response: dict = requests.put(url=test_url, data=json.dumps(data), headers={'x-api-key': DETECTION_API_KEY}).json()

test_subject: SubjectClient = SubjectClient(DETECTION_API_KEY, DOMAIN, PORT)
test_response: dict = test_subject.put({"subject": "NewSubject", "api_endpoint": "Subject"})
assert response == test_response
32 changes: 32 additions & 0 deletions webcam_demo/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
# Webcam demo

This is an example of how to use CompreFace face recognition with python sdk.

# Requirements

1. [Python](https://www.python.org/downloads/) (Version 3.7+)
2. [CompreFace](https://github.com/exadel-inc/CompreFace#getting-started-with-compreface)
3. [Compreface-python-sdk](https://github.com/exadel-inc/compreface-python-sdk)
4. [Opencv-python](https://pypi.org/project/opencv-python/)

# Face recognition demo

To run the demo, open `webcam_demo` folder and run:

```commandline
python compreface_webcam_recognition_demo.py --api-key your_api_key --host http://localhost --port 8000
```
* `--api-key` is your Face Recognition service API key. API key for this demo was created on step 5 of [How to Use CompreFace](https://github.com/exadel-inc/CompreFace/blob/master/docs/How-to-Use-CompreFace.md#how-to-use-compreface). Optional value. By default, the value is `00000000-0000-0000-0000-000000000002` - api key with celebrities demo.
* `--host` is the host where you deployed CompreFace. Optional value. By default, the value is `http://localhost`
* `--port` is the port of CompreFace instance. Optional value. By default, the value is `8000`

# Face detection demo

To run the demo, open `webcam_demo` folder and run:

```commandline
python compreface_webcam_detection_demo.py --api-key your_api_key --host http://localhost --port 8000
```
* `--api-key` is your Face Detection service API key. API key for this demo was created on step 5 of [How to Use CompreFace](https://github.com/exadel-inc/CompreFace/blob/master/docs/How-to-Use-CompreFace.md#how-to-use-compreface).
* `--host` is the host where you deployed CompreFace. Optional value. By default, the value is `http://localhost`
* `--port` is the port of CompreFace instance. Optional value. By default, the value is `8000`
115 changes: 115 additions & 0 deletions webcam_demo/compreface_webcam_detection_demo.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
"""
Copyright(c) 2021 the original author or authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
https: // www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
or implied. See the License for the specific language governing
permissions and limitations under the License.
"""

import argparse
import cv2
import time
from threading import Thread

from compreface import CompreFace
from compreface.service import DetectionService


def parseArguments():
parser = argparse.ArgumentParser()

parser.add_argument("--api-key", help="CompreFace detection service API key", type=str, required=True)
parser.add_argument("--host", help="CompreFace host", type=str, default='http://localhost')
parser.add_argument("--port", help="CompreFace port", type=str, default='8000')

args = parser.parse_args()

return args

class ThreadedCamera:
def __init__(self, api_key, host, port):
self.active = True
self.results = []
self.capture = cv2.VideoCapture(0)
self.capture.set(cv2.CAP_PROP_BUFFERSIZE, 2)

compre_face: CompreFace = CompreFace(host, port, {
"limit": 0,
"det_prob_threshold": 0.8,
"prediction_count": 1,
"face_plugins": "age,gender,mask",
"status": False
})

self.detection: DetectionService = compre_face.init_face_detection(api_key)

self.FPS = 1/30

# Start frame retrieval thread
self.thread = Thread(target=self.show_frame, args=())
self.thread.daemon = True
self.thread.start()

def show_frame(self):
print("Started")
while self.capture.isOpened():
(status, frame_raw) = self.capture.read()
self.frame = cv2.flip(frame_raw, 1)

if self.results:
results = self.results
for result in results:
box = result.get('box')
age = result.get('age')
gender = result.get('gender')
mask = result.get('mask')
if box:
cv2.rectangle(img=self.frame, pt1=(box['x_min'], box['y_min']),
pt2=(box['x_max'], box['y_max']), color=(0, 255, 0), thickness=1)
if age:
age = f"Age: {age['low']} - {age['high']}"
cv2.putText(self.frame, age, (box['x_max'], box['y_min'] + 15),
cv2.FONT_HERSHEY_SIMPLEX, 0.6, (0, 255, 0), 1)
if gender:
gender = f"Gender: {gender['value']}"
cv2.putText(self.frame, gender, (box['x_max'], box['y_min'] + 35),
cv2.FONT_HERSHEY_SIMPLEX, 0.6, (0, 255, 0), 1)
if mask:
mask = f"Mask: {mask['value']}"
cv2.putText(self.frame, mask, (box['x_max'], box['y_min'] + 55),
cv2.FONT_HERSHEY_SIMPLEX, 0.6, (0, 255, 0), 1)

cv2.imshow('CompreFace demo', self.frame)
time.sleep(self.FPS)

if cv2.waitKey(1) & 0xFF == 27:
self.capture.release()
cv2.destroyAllWindows()
self.active=False

def is_active(self):
return self.active

def update(self):
if not hasattr(self, 'frame'):
return

_, im_buf_arr = cv2.imencode(".jpg", self.frame)
byte_im = im_buf_arr.tobytes()
data = self.detection.detect(byte_im)
self.results = data.get('result')


if __name__ == '__main__':
args = parseArguments()
threaded_camera = ThreadedCamera(args.api_key, args.host, args.port)
while threaded_camera.is_active():
threaded_camera.update()
128 changes: 128 additions & 0 deletions webcam_demo/compreface_webcam_recognition_demo.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
"""
Copyright(c) 2021 the original author or authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
https: // www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
or implied. See the License for the specific language governing
permissions and limitations under the License.
"""

import cv2
import argparse
import time
from threading import Thread

from compreface import CompreFace
from compreface.service import RecognitionService

def parseArguments():
parser = argparse.ArgumentParser()

parser.add_argument("--api-key", help="CompreFace recognition service API key", type=str, default='00000000-0000-0000-0000-000000000002')
parser.add_argument("--host", help="CompreFace host", type=str, default='http://localhost')
parser.add_argument("--port", help="CompreFace port", type=str, default='8000')

args = parser.parse_args()

return args

class ThreadedCamera:
def __init__(self, api_key, host, port):
self.active = True
self.results = []
self.capture = cv2.VideoCapture(0)
self.capture.set(cv2.CAP_PROP_BUFFERSIZE, 2)

compre_face: CompreFace = CompreFace(host, port, {
"limit": 0,
"det_prob_threshold": 0.8,
"prediction_count": 1,
"face_plugins": "age,gender",
"status": False
})

self.recognition: RecognitionService = compre_face.init_face_recognition(api_key)

self.FPS = 1/30

# Start frame retrieval thread
self.thread = Thread(target=self.show_frame, args=())
self.thread.daemon = True
self.thread.start()

def show_frame(self):
print("Started")
while self.capture.isOpened():
(status, frame_raw) = self.capture.read()
self.frame = cv2.flip(frame_raw, 1)

if self.results:
results = self.results
for result in results:
box = result.get('box')
age = result.get('age')
gender = result.get('gender')
mask = result.get('mask')
subjects = result.get('subjects')
if box:
cv2.rectangle(img=self.frame, pt1=(box['x_min'], box['y_min']),
pt2=(box['x_max'], box['y_max']), color=(0, 255, 0), thickness=1)
if age:
age = f"Age: {age['low']} - {age['high']}"
cv2.putText(self.frame, age, (box['x_max'], box['y_min'] + 15),
cv2.FONT_HERSHEY_SIMPLEX, 0.6, (0, 255, 0), 1)
if gender:
gender = f"Gender: {gender['value']}"
cv2.putText(self.frame, gender, (box['x_max'], box['y_min'] + 35),
cv2.FONT_HERSHEY_SIMPLEX, 0.6, (0, 255, 0), 1)
if mask:
mask = f"Mask: {mask['value']}"
cv2.putText(self.frame, mask, (box['x_max'], box['y_min'] + 55),
cv2.FONT_HERSHEY_SIMPLEX, 0.6, (0, 255, 0), 1)

if subjects:
subjects = sorted(subjects, key=lambda k: k['similarity'], reverse=True)
subject = f"Subject: {subjects[0]['subject']}"
similarity = f"Similarity: {subjects[0]['similarity']}"
cv2.putText(self.frame, subject, (box['x_max'], box['y_min'] + 75),
cv2.FONT_HERSHEY_SIMPLEX, 0.6, (0, 255, 0), 1)
cv2.putText(self.frame, similarity, (box['x_max'], box['y_min'] + 95),
cv2.FONT_HERSHEY_SIMPLEX, 0.6, (0, 255, 0), 1)
else:
subject = f"No known faces"
cv2.putText(self.frame, subject, (box['x_max'], box['y_min'] + 75),
cv2.FONT_HERSHEY_SIMPLEX, 0.6, (0, 255, 0), 1)

cv2.imshow('CompreFace demo', self.frame)
time.sleep(self.FPS)

if cv2.waitKey(1) & 0xFF == 27:
self.capture.release()
cv2.destroyAllWindows()
self.active=False

def is_active(self):
return self.active

def update(self):
if not hasattr(self, 'frame'):
return

_, im_buf_arr = cv2.imencode(".jpg", self.frame)
byte_im = im_buf_arr.tobytes()
data = self.recognition.recognize(byte_im)
self.results = data.get('result')


if __name__ == '__main__':
args = parseArguments()
threaded_camera = ThreadedCamera(args.api_key, args.host, args.port)
while threaded_camera.is_active():
threaded_camera.update()