├── .github ├── ISSUE_TEMPLATE │ ├── bug_report.md │ └── feature_request.md └── workflows │ └── cla.yml ├── .gitignore ├── CLA.md ├── LICENSE ├── README.md ├── compreface ├── __init__.py ├── client │ ├── __init__.py │ ├── add_example_of_subject.py │ ├── delete_example_by_id.py │ ├── detect_face_from_image.py │ ├── recognize_face_from_image.py │ ├── subject_client.py │ ├── verification_face_from_image.py │ └── verify_face_from_image.py ├── collections │ ├── __init__.py │ └── face_collections.py ├── common │ ├── __init__.py │ ├── client.py │ ├── multipart_constructor.py │ ├── service.py │ └── typed_dict.py ├── config │ ├── __init__.py │ └── api_list.py ├── core │ ├── __init__.py │ └── model.py ├── exceptions │ ├── __init__.py │ └── field_exception.py ├── service │ ├── __init__.py │ ├── detection_service.py │ ├── recognition_service.py │ └── verification_service.py └── use_cases │ ├── __init__.py │ ├── add_example_of_subject.py │ ├── add_subject.py │ ├── delete_all_examples_of_subject_by_name.py │ ├── delete_all_subjects.py │ ├── delete_example_by_id.py │ ├── delete_subject_by_name.py │ ├── detect_face_from_image.py │ ├── get_subjects.py │ ├── list_of_all_saved_subjects.py │ ├── recognize_face_from_image.py │ ├── update_subject.py │ ├── verification_face_from_image.py │ └── verifiy_face_from_images.py ├── examples ├── add_example_of_a_subject.py ├── add_subject.py ├── common │ └── jonathan-petit-unsplash.jpg ├── delete_all_examples_of_subject.py ├── delete_all_subjects.py ├── delete_example_by_id.py ├── delete_subject_by_name.py ├── detect_face_from_image.py ├── get_list_of_all_subjects.py ├── recognize_face_from_image.py ├── update_existing_subject.py ├── verification_face_from_image.py └── verify_face_from_image.py ├── setup.cfg ├── setup.py ├── tests ├── __init__.py ├── client │ ├── __init__.py │ ├── const_config.py │ ├── test_add_example_of_subject_client.py │ ├── test_delete_example_by_id_client.py │ ├── test_detect_face_from_image.py │ ├── test_recognize_face_from_image.py │ ├── test_subject_crud_client.py │ ├── test_verification_face_from_image.py │ └── test_verify_face_from_image.py └── common │ └── jonathan-petit-unsplash.jpg ├── tox.ini └── webcam_demo ├── README.md ├── compreface_webcam_detection_demo.py └── compreface_webcam_recognition_demo.py /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **To Reproduce** 14 | Code example. 15 | 16 | **Expected behavior** 17 | A clear and concise description of what you expected to happen. 18 | 19 | **Logs** 20 | If applicable, add logs to help explain your problem. 21 | 22 | **Additional context** 23 | Add any other context about the problem here. 24 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem? Please describe.** 11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 12 | 13 | **Describe the solution you'd like** 14 | A clear and concise description of what you want to happen. 15 | 16 | **Describe alternatives you've considered** 17 | A clear and concise description of any alternative solutions or features you've considered. 18 | 19 | **Additional context** 20 | Add any other context or screenshots about the feature request here. 21 | -------------------------------------------------------------------------------- /.github/workflows/cla.yml: -------------------------------------------------------------------------------- 1 | name: "CLA Assistant" 2 | on: 3 | issue_comment: 4 | types: [created] 5 | pull_request_target: 6 | types: [opened,closed,synchronize] 7 | 8 | jobs: 9 | CLAssistant: 10 | runs-on: ubuntu-latest 11 | steps: 12 | - name: "CLA Assistant" 13 | if: (github.event.comment.body == 'recheckcla' || github.event.comment.body == 'I have read the CLA Document and I hereby sign the CLA') || github.event_name == 'pull_request_target' 14 | # Alpha Release 15 | uses: cla-assistant/github-action@v2.0.1-alpha 16 | env: 17 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 18 | PERSONAL_ACCESS_TOKEN : ${{ secrets.PERSONAL_ACCESS_TOKEN }} 19 | with: 20 | path-to-signatures: 'signatures/cla.json' 21 | path-to-cla-document: 'https://github.com/exadel-inc/compreface-python-sdk/blob/main/CLA.md' 22 | branch: 'CLA-signatures' 23 | allowlist: bot* 24 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Linux trash folder which might appear on any partition or disk 2 | .Trash-* 3 | 4 | ### Python ### 5 | # Byte-compiled / optimized / DLL files 6 | __pycache__/ 7 | *.py[cod] 8 | *$py.class 9 | 10 | # Distribution / packaging 11 | .Python 12 | build/ 13 | develop-eggs/ 14 | dist/ 15 | downloads/ 16 | eggs/ 17 | .eggs/ 18 | parts/ 19 | sdist/ 20 | var/ 21 | wheels/ 22 | pip-wheel-metadata/ 23 | share/python-wheels/ 24 | *.egg-info/ 25 | .installed.cfg 26 | *.egg 27 | MANIFEST 28 | 29 | # PyInstaller 30 | # Usually these files are written by a python script from a template 31 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 32 | *.manifest 33 | *.spec 34 | 35 | # Installer logs 36 | pip-log.txt 37 | pip-delete-this-directory.txt 38 | 39 | # Unit test / coverage reports 40 | htmlcov/ 41 | .tox/ 42 | .nox/ 43 | .coverage 44 | .coverage.* 45 | .cache 46 | nosetests.xml 47 | coverage.xml 48 | *.cover 49 | *.py,cover 50 | .hypothesis/ 51 | .pytest_cache/ 52 | pytestdebug.log 53 | 54 | # PyBuilder 55 | target/ 56 | 57 | # IPython 58 | profile_default/ 59 | ipython_config.py 60 | 61 | # Environments 62 | # .env 63 | .env/ 64 | .venv/ 65 | env/ 66 | venv/ 67 | ENV/ 68 | env.bak/ 69 | venv.bak/ 70 | pythonenv* 71 | 72 | # operating system-related files 73 | # file properties cache/storage on macOS 74 | *.DS_Store 75 | # thumbnail cache on Windows 76 | Thumbs.db 77 | 78 | # profiling data 79 | .prof 80 | 81 | 82 | ### VisualStudioCode ### 83 | .vscode/* 84 | !.vscode/settings.json 85 | !.vscode/tasks.json 86 | !.vscode/launch.json 87 | !.vscode/extensions.json 88 | *.code-workspace 89 | 90 | ### VisualStudioCode Patch ### 91 | # Ignore all local history of files 92 | .history 93 | .ionide 94 | 95 | # Vscode 96 | .vscode 97 | 98 | #Idea 99 | .idea 100 | .idea/* -------------------------------------------------------------------------------- /CLA.md: -------------------------------------------------------------------------------- 1 | # Contributor License Agreement 2 | 3 | Thank you for your interest in contributing to the **CompreFace Python SDK** (“Material”) by Exadel, Inc. ("We" or "Us"). The present 4 | Contributor License Agreement (“CLA”) is for your protection as a Contributor as well as the protection of Us; it does not change your rights to use your own Contributions for any other purpose. 5 | 6 | You must agree to the terms of this CLA before making a Contribution to the Material. This CLA covers any and all Contributions that You, now or in the future, submit to the Material. This CLA shall come into effect upon Your acceptance of its terms and conditions. 7 | 8 | ## 1. Definitions 9 | a. "You" means the individual Сopyright owner who Submits a Contribution to Us. 10 | 11 | b. "Contribution" means source code and any other copyrightable materials Submitted by you to Us, including any associated comments and documentation. 12 | 13 | c. "Copyright" means all rights protecting works of authorship, including copyright, moral and neighboring rights, as appropriate, for the full term of their existence. 14 | 15 | d. "Material" means the software or documentation made available by Us to third parties. After You Submit the Contribution, it may be included in the Material. 16 | 17 | e. "Submit" means any act by which a Contribution is transferred to Us by You by means of tangible or intangible media, including but not limited to electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, Us, but excluding any transfer that is conspicuously marked or otherwise designated in writing by You as "Not a Contribution." 18 | 19 | ## 2. Grant of Copyright License 20 | 21 | Subject to the terms and conditions of this CLA, You hereby grant to Us and to recipients of Material distributed by Us a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare derivative works of, publicly display, publicly perform, sublicense, and distribute Your Contributions and such derivative works. 22 | 23 | ## 3. Grant of Patent License 24 | 25 | Subject to the terms and conditions of this CLA, You hereby grant to Us and to recipients of Material distributed by Us a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Material, where such license applies only to those patent claims licensable by You that are necessarily infringed by Your Contribution(s) alone or by a combination of Your Contribution(s) with the Material to which such Contribution(s) was submitted. If any entity institutes patent litigation against You or any other entity (including a cross-claim or counterclaim in a lawsuit) alleging that your Contribution, or the Material to which you have contributed, constitutes a direct or contributory patent infringement, then any patent licenses granted to that entity under this CLA for that Contribution or Material shall terminate as of the date such litigation is filed. 26 | 27 | ## 4. Other rights reserved 28 | 29 | Each party reserves all rights not expressly granted in this CLA. No additional licenses or rights whatsoever (including, without limitation, any implied licenses) are granted by implication, exhaustion, estoppel or otherwise. 30 | 31 | ## 5. Originality of Contributions 32 | 33 | You represent that you are legally entitled to grant the above licenses. If your employer(s) has rights to intellectual property that you create that includes your Contributions, you represent that you have received permission to make Contributions on behalf of that employer or that your employer has waived such rights for your Contributions to Us. You represent that Your Contribution submissions include complete details of any third-party license or other restriction (including, but not limited to, related patents and trademarks) of which you are personally aware and which are associated with any part of Your Contributions. 34 | 35 | ## 6. Notice to Us 36 | 37 | You agree to notify Us of any facts or circumstances of which you become aware that would make the representations in this CLA inaccurate in any respect. 38 | 39 | ## 7. Disclaimer 40 | 41 | You are not expected to provide support for Your Contributions, except to the extent You desire to provide support. You may provide support for free, for a fee, or not at all. Unless required by applicable law or agreed to in writing, You provide Your Contributions on "as is" basis. More particularly, all express or implied warranties including, without limitation, any implied warranty of satisfactory quality, fitness for a particular purpose, and non-infringement are expressly disclaimed by You to Us and by Us to You. To the extent that any such warranties cannot be disclaimed, such warranty is limited in duration and extent to the minimum period and extent permitted by applicable law. 42 | 43 | ## 8. Consequential Damage Waiver 44 | 45 | To the maximum extent permitted by applicable law, in no event will You or We be liable for any loss of profits, loss of anticipated savings, loss of data, indirect, special, incidental, consequential and exemplary damages arising out of this CLA regardless of the legal or equitable theory (contract, tort or otherwise) upon which the claim is based. 46 | 47 | 48 | ## 9. Information About Submissions 49 | 50 | You agree that this Material and Contributions to it are public and that a record of the Contribution (including all personal information you submit with it) is maintained indefinitely and may be redistributed consistent with this Material, compliance with the open source license(s) involved, and maintenance of authorship attribution. 51 | 52 | ## 11. Miscellaneous 53 | 54 | This CLA is the entire agreement between the parties and supersedes any and all prior agreements, understandings or communications, written or oral, between the parties relating to the subject matter hereof. You acknowledge that We are not obligated to use your Contribution as part of the Material distributed by Us and may make the decision to include any Contribution as We believe is appropriate. 55 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # CompreFace Python SDK 2 | 3 | CompreFace Python SDK makes face recognition into your application even easier. 4 | 5 | # Table of content 6 | - [Requirements](#requirements) 7 | - [Installation](#installation) 8 | - [Usage](#usage) 9 | - [Initialization](#initialization) 10 | - [Adding faces into a face collection](#adding-faces-into-a-face-collection) 11 | - [Recognition](#recognition) 12 | - [Webcam demo](#webcam-demo) 13 | - [Reference](#reference) 14 | - [CompreFace Global Object](#compreFace-global-object) 15 | - [Methods](#methods) 16 | - [Options structure](#options-structure) 17 | - [Face Recognition Service](#face-recognition-service) 18 | - [Recognize Faces from a Given Image](#recognize-faces-from-a-given-image) 19 | - [Get Face Collection](#get-face-collection) 20 | - [Add an Example of a Subject](#add-an-example-of-a-subject) 21 | - [List of All Saved Examples of the Subject](#list-of-all-saved-examples-of-the-subject) 22 | - [Delete All Examples of the Subject by Name](#delete-all-examples-of-the-subject-by-name) 23 | - [Delete an Example of the Subject by ID](#delete-an-example-of-the-subject-by-id) 24 | - [Verify Faces from a Given Image](#verify-faces-from-a-given-image) 25 | - [Get Subjects](#get-subjects) 26 | - [Add a Subject](#add-a-subject) 27 | - [List Subjects](#list-subjects) 28 | - [Rename a Subject](#rename-a-subject) 29 | - [Delete a Subject](#delete-a-subject) 30 | - [Delete All Subjects](#delete-all-subjects) 31 | - [Face Detection Service](#face-detection-service) 32 | - [Detect](#detect) 33 | - [Face Verification Service](#face-verification-service) 34 | - [Verify](#verify) 35 | - [Contributing](#contributing) 36 | - [Report Bugs](#report-bugs) 37 | - [Submit Feedback](#submit-feedback) 38 | - [License info](#license-info) 39 | 40 | # Requirements 41 | 42 | Before using our SDK make sure you have installed CompreFace and Python on your machine. 43 | 44 | 1. [CompreFace](https://github.com/exadel-inc/CompreFace#getting-started-with-compreface) 45 | 2. [Python](https://www.python.org/downloads/) (Version 3.7+) 46 | 47 | ## CompreFace compatibility matrix 48 | 49 | | CompreFace Python SDK version | CompreFace 0.5.x | CompreFace 0.6.x | 50 | | ------------------------------| ---------------- | ---------------- | 51 | | 0.1.0 | ✔ | :yellow_circle: | 52 | | 0.6.x | :yellow_circle: | ✔ | 53 | 54 | Explanation: 55 | 56 | * ✔ SDK supports all functionality from CompreFace. 57 | * :yellow_circle: SDK works with this CompreFace version. 58 | In case if CompreFace version is newer - SDK won't support new features of CompreFace. In case if CompreFace version is older - new SDK features will fail. 59 | * ✘ There are major backward compatibility issues. It is not recommended to use these versions together 60 | 61 | 62 | # Installation 63 | 64 | It can be installed through pip: 65 | 66 | ```shell 67 | pip install compreface-sdk 68 | ``` 69 | 70 | # Usage 71 | 72 | All these examples you can find in repository inside [examples](/examples) folder. 73 | 74 | ## Initialization 75 | 76 | To start using Python SDK you need to import `CompreFace` object from 'compreface-sdk' dependency. 77 | 78 | Then you need to init it with `url` and `port`. By default, if you run CompreFace on your local machine, it's `http://localhost` and `8000` respectively. 79 | You can pass optional `options` object when call method to set default parameters, see reference for [more information](#options-structure). 80 | 81 | After you initialized `CompreFace` object you need to init the service object with the `api key` of your face service. You can use this service object to recognize faces. 82 | 83 | However, before recognizing you need first to add faces into the face collection. To do this, get the face collection object from the service object. 84 | 85 | ```python 86 | from compreface import CompreFace 87 | from compreface.service import RecognitionService 88 | from compreface.collections import FaceCollection 89 | from compreface.collections.face_collections import Subjects 90 | 91 | DOMAIN: str = 'http://localhost' 92 | PORT: str = '8000' 93 | API_KEY: str = 'your_face_recognition_key' 94 | 95 | compre_face: CompreFace = CompreFace(DOMAIN, PORT) 96 | 97 | recognition: RecognitionService = compre_face.init_face_recognition(API_KEY) 98 | 99 | face_collection: FaceCollection = recognition.get_face_collection() 100 | 101 | subjects: Subjects = recognition.get_subjects() 102 | ``` 103 | 104 | ## Adding faces into a face collection 105 | 106 | Here is example that shows how to add an image to your face collection from your file system: 107 | 108 | ```python 109 | image_path: str = 'examples/common/jonathan-petit-unsplash.jpg' 110 | subject: str = 'Jonathan Petit' 111 | 112 | face_collection.add(image_path=image_path, subject=subject) 113 | ``` 114 | 115 | ## Recognition 116 | 117 | This code snippet shows how to recognize unknown face. 118 | 119 | ```python 120 | image_path: str = 'examples/common/jonathan-petit-unsplash.jpg' 121 | 122 | recognition.recognize(image_path=image_path) 123 | ``` 124 | 125 | ## Webcam demo 126 | Webcam demo shows how to use CompreFace Recognition and Detection services using Python SDK. 127 | In both cases, age, gender and mask plugins are applied. 128 | 129 | Follow this [link](/webcam_demo) to see the instructions. 130 | 131 | # Reference 132 | 133 | ## CompreFace Global Object 134 | 135 | Global CompreFace Object is used for initializing connection to CompreFace and setting default values for options. 136 | Default values will be used in every service method if applicable. 137 | If the option’s value is set in the global object and passed as a function argument then the function argument value will be used. 138 | 139 | **Constructor:** 140 | 141 | ```CompreFace(domain, port, options)``` 142 | 143 | | Argument | Type | Required | Notes | 144 | | ---------| ------ | -------- | ----------------------------------------- | 145 | | url | string | required | URL with protocol where CompreFace is located. E.g. `http://localhost` | 146 | | port | string | required | CompreFace port. E.g. `8000` | 147 | | options | object | optional | Default values for face recognition services. See more [here](#options-structure). `AllOptionsDict` object can be used in this method | 148 | 149 | Possible options: 150 | 151 | | Option | Type | Notes | 152 | | --------------------| ------ | ----------------------------------------- | 153 | | det_prob_threshold | float | minimum required confidence that a recognized face is actually a face. Value is between 0.0 and 1.0 | 154 | | limit | integer | maximum number of faces on the image to be recognized. It recognizes the biggest faces first. Value of 0 represents no limit. Default value: 0 | 155 | | prediction_count | integer | maximum number of subject predictions per face. It returns the most similar subjects. Default value: 1 | 156 | | face_plugins | string | comma-separated slugs of face plugins. If empty, no additional information is returned. [Learn more](https://github.com/exadel-inc/CompreFace/tree/master/docs/Face-services-and-plugins.md) | 157 | | status | boolean | if true includes system information like execution_time and plugin_version fields. Default value is false | 158 | 159 | Example: 160 | 161 | ```python 162 | from compreface import CompreFace 163 | 164 | DOMAIN: str = 'http://localhost' 165 | PORT: str = '8000' 166 | 167 | compre_face: CompreFace = CompreFace(domain=DOMAIN, port=PORT, options={ 168 | "limit": 0, 169 | "det_prob_threshold": 0.8, 170 | "prediction_count": 1, 171 | "face_plugins": "calculator,age,gender,landmarks", 172 | "status": "true" 173 | }) 174 | ``` 175 | 176 | ### Methods 177 | 178 | 1. ```CompreFace.init_face_recognition(api_key)``` 179 | 180 | Inits face recognition service object. 181 | 182 | | Argument | Type | Required | Notes | 183 | | ---------| ------ | -------- | ----------------------------------------- | 184 | | api_key | string | required | Face Recognition Api Key in UUID format | 185 | 186 | Example: 187 | 188 | ```python 189 | from compreface.service import RecognitionService 190 | 191 | API_KEY: str = 'your_face_recognition_key' 192 | 193 | recognition: RecognitionService = compre_face.init_face_recognition(API_KEY) 194 | ``` 195 | 196 | 2. ```CompreFace.init_face_detection(api_key)``` 197 | 198 | Inits face detection service object. 199 | 200 | | Argument | Type | Required | Notes | 201 | | ---------| ------ | -------- | ----------------------------------------- | 202 | | api_key | string | required | Face Detection Api Key in UUID format | 203 | 204 | Example: 205 | 206 | ```python 207 | from compreface.service import DetectionService 208 | 209 | DETECTION_API_KEY: str = 'your_face_detection_key' 210 | 211 | detection: DetectionService = compre_face.init_face_detection(DETECTION_API_KEY) 212 | ``` 213 | 214 | 3. ```CompreFace.init_face_verification(api_key)``` 215 | 216 | Inits face verification service object. 217 | 218 | | Argument | Type | Required | Notes | 219 | | ---------| ------ | -------- | ----------------------------------------- | 220 | | api_key | string | required | Face Verification Api Key in UUID format | 221 | 222 | Example: 223 | 224 | ```python 225 | from compreface.service import VerificationService 226 | 227 | VERIFICATION_API_KEY: str = 'your_face_verification_key' 228 | 229 | verify: VerificationService = compre_face.init_face_verification(VERIFICATION_API_KEY) 230 | ``` 231 | 232 | ## Options structure 233 | 234 | Options is optional field in every request that contains an image. 235 | If the option’s value is set in the global object and passed as a function argument then the function argument value will be used. 236 | 237 | ```python 238 | 239 | class DetProbOptionsDict(TypedDict): 240 | det_prob_threshold: float 241 | 242 | 243 | class ExpandedOptionsDict(DetProbOptionsDict): 244 | limit: int 245 | status: bool 246 | face_plugins: str 247 | 248 | 249 | class AllOptionsDict(ExpandedOptionsDict): 250 | prediction_count: int 251 | 252 | ``` 253 | | Option | Type | Notes | 254 | | --------------------| ------ | ----------------------------------------- | 255 | | det_prob_threshold | float | minimum required confidence that a recognized face is actually a face. Value is between 0.0 and 1.0 | 256 | | limit | integer | maximum number of faces on the image to be recognized. It recognizes the biggest faces first. Value of 0 represents no limit. Default value: 0 | 257 | | prediction_count | integer | maximum number of subject predictions per face. It returns the most similar subjects. Default value: 1 | 258 | | face_plugins | string | comma-separated slugs of face plugins. If empty, no additional information is returned. [Learn more](https://github.com/exadel-inc/CompreFace/tree/master/docs/Face-services-and-plugins.md) | 259 | | status | boolean | if true includes system information like execution_time and plugin_version fields. Default value is false | 260 | 261 | Example of face recognition with object: 262 | 263 | ```python 264 | recognition.recognize(image_path=image_path, options={ 265 | "limit": 0, 266 | "det_prob_threshold": 0.8, 267 | "prediction_count": 1, 268 | "face_plugins": "calculator,age,gender,landmarks", 269 | "status": "true" 270 | }) 271 | ``` 272 | 273 | ## Face Recognition Service 274 | 275 | Face recognition service is used for face identification. 276 | This means that you first need to upload known faces to face collection and then recognize unknown faces among them. 277 | When you upload an unknown face, the service returns the most similar faces to it. 278 | Also, face recognition service supports verify endpoint to check if this person from face collection is the correct one. 279 | For more information, see [CompreFace page](https://github.com/exadel-inc/CompreFace). 280 | 281 | ### Recognize Faces from a Given Image 282 | 283 | *[Example](examples/recognize_face_from_image.py)* 284 | 285 | Recognizes all faces from the image. 286 | The first argument is the image location, it can be an url, local path or bytes. 287 | 288 | ```python 289 | recognition.recognize(image_path, options) 290 | ``` 291 | 292 | | Argument | Type | Required | Notes | 293 | | ------------------ | ------- | -------- | ---------------------------------------------------------------------------------------------------------------------------------------------- | 294 | | image_path | image | required | Image can pass from url, local path or bytes. Max size is 5Mb | 295 | | options | object | optional | `AllOptionsDict` object can be used in this method. See more [here](#options-structure). | 296 | 297 | Response: 298 | 299 | ```json 300 | { 301 | "result" : [ { 302 | "age" : { 303 | "probability": 0.9308982491493225, 304 | "high": 32, 305 | "low": 25 306 | }, 307 | "gender" : { 308 | "probability": 0.9898611307144165, 309 | "value": "female" 310 | }, 311 | "mask" : { 312 | "probability": 0.9999470710754395, 313 | "value": "without_mask" 314 | }, 315 | "embedding" : [ 9.424854069948196E-4, "...", -0.011415496468544006 ], 316 | "box" : { 317 | "probability" : 1.0, 318 | "x_max" : 1420, 319 | "y_max" : 1368, 320 | "x_min" : 548, 321 | "y_min" : 295 322 | }, 323 | "landmarks" : [ [ 814, 713 ], [ 1104, 829 ], [ 832, 937 ], [ 704, 1030 ], [ 1017, 1133 ] ], 324 | "subjects" : [ { 325 | "similarity" : 0.97858, 326 | "subject" : "subject1" 327 | } ], 328 | "execution_time" : { 329 | "age" : 28.0, 330 | "gender" : 26.0, 331 | "detector" : 117.0, 332 | "calculator" : 45.0, 333 | "mask": 36.0 334 | } 335 | } ], 336 | "plugins_versions" : { 337 | "age" : "agegender.AgeDetector", 338 | "gender" : "agegender.GenderDetector", 339 | "detector" : "facenet.FaceDetector", 340 | "calculator" : "facenet.Calculator", 341 | "mask": "facemask.MaskDetector" 342 | } 343 | } 344 | ``` 345 | 346 | | Element | Type | Description | 347 | | -------------------------- | ------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------- | 348 | | age | object | detected age range. Return only if [age plugin](https://github.com/exadel-inc/CompreFace/blob/master/docs/Face-services-and-plugins.md) is enabled | 349 | | gender | object | detected gender. Return only if [gender plugin](https://github.com/exadel-inc/CompreFace/blob/master/docs/Face-services-and-plugins.md) is enabled | 350 | | mask | object | detected mask. Return only if [face mask plugin](https://github.com/exadel-inc/CompreFace/blob/master/docs/Face-services-and-plugins.md) is enabled. | 351 | | embedding | array | face embeddings. Return only if [calculator plugin](https://github.com/exadel-inc/CompreFace/blob/master/docs/Face-services-and-plugins.md) is enabled | 352 | | box | object | list of parameters of the bounding box for this face | 353 | | probability | float | probability that a found face is actually a face | 354 | | x_max, y_max, x_min, y_min | integer | coordinates of the frame containing the face | 355 | | landmarks | array | list of the coordinates of the frame containing the face-landmarks.| 356 | | subjects | list | list of similar subjects with size of order by similarity | 357 | | similarity | float | similarity that on that image predicted person | 358 | | subject | string | name of the subject in Face Collection | 359 | | execution_time | object | execution time of all plugins | 360 | | plugins_versions | object | contains information about plugin versions | 361 | 362 | ### Get Face Collection 363 | 364 | ```python 365 | recognition.get_face_collection() 366 | ``` 367 | 368 | Returns Face collection object 369 | 370 | Face collection could be used to manage known faces, e.g. add, list, or delete them. 371 | 372 | Face recognition is performed for the saved known faces in face collection, so before using the `recognize` method you need to save at least one face into the face collection. 373 | 374 | More information about face collection and managing examples [here](https://github.com/exadel-inc/CompreFace/blob/master/docs/Rest-API-description.md#managing-subject-examples) 375 | 376 | **Methods:** 377 | 378 | #### Add an Example of a Subject 379 | 380 | *[Example](examples/add_example_of_a_subject.py)* 381 | 382 | This creates an example of the subject by saving images. You can add as many images as you want to train the system. Image should 383 | contain only one face. 384 | 385 | ```python 386 | face_collection.add(image_path, subject, options) 387 | ``` 388 | 389 | | Argument | Type | Required | Notes | 390 | | ------------------ | ------ | -------- | ---------------------------------------------------------------------------------------------------- | 391 | | image_path | image | required | Image can pass from url, local path or bytes. Max size is 5Mb | 392 | | subject | string | required | is the name you assign to the image you save | 393 | | options | object | optional | `DetProbOptionsDict` object can be used in this method. See more [here](#options-structure). | 394 | 395 | Response: 396 | 397 | ```json 398 | { 399 | "image_id": "6b135f5b-a365-4522-b1f1-4c9ac2dd0728", 400 | "subject": "subject1" 401 | } 402 | ``` 403 | 404 | | Element | Type | Description | 405 | | -------- | ------ | -------------------------- | 406 | | image_id | UUID | UUID of uploaded image | 407 | | subject | string | Subject of the saved image | 408 | 409 | #### List of All Saved Examples of the Subject 410 | 411 | To retrieve a list of subjects saved in a Face Collection: 412 | 413 | ```python 414 | face_collection.list() 415 | ``` 416 | 417 | Response: 418 | 419 | ``` 420 | { 421 | "faces": [ 422 | { 423 | "image_id": , 424 | "subject": 425 | }, 426 | ... 427 | ] 428 | } 429 | ``` 430 | 431 | | Element | Type | Description | 432 | | -------- | ------ | ----------------------------------------------------------------- | 433 | | image_id | UUID | UUID of the face | 434 | | subject | string | of the person, whose picture was saved for this api key | 435 | 436 | #### Delete All Examples of the Subject by Name 437 | 438 | *[Example](examples/delete_all_examples_of_subject.py)* 439 | 440 | To delete all image examples of the : 441 | 442 | ```python 443 | face_collection.delete_all(subject) 444 | ``` 445 | 446 | | Argument | Type | Required | Notes | 447 | | --------- | ------ | -------- | ------------------------------------------------------------------------------------------------------------------------------------ | 448 | | subject | string | optional | is the name you assign to the image you save. If this parameter is absent, all faces in Face Collection will be removed | 449 | 450 | Response: 451 | 452 | ``` 453 | { 454 | "deleted": 455 | } 456 | ``` 457 | 458 | | Element | Type | Description | 459 | | -------- | ------- | ------------------------ | 460 | | deleted | integer | Number of deleted faces | 461 | 462 | #### Delete an Example of the Subject by ID 463 | 464 | *[Example](examples/delete_example_by_id.py)* 465 | 466 | To delete an image by ID: 467 | 468 | ```python 469 | face_collection.delete(image_id) 470 | ``` 471 | | Argument | Type | Required | Notes | 472 | | --------- | ------ | -------- | ------------------------------------------------------------ 473 | | image_id | UUID | required | UUID of the removing face | 474 | 475 | Response: 476 | 477 | ``` 478 | { 479 | "image_id": , 480 | "subject": 481 | } 482 | ``` 483 | 484 | | Element | Type | Description | 485 | | -------- | ------ | ----------------------------------------------------------------- | 486 | | image_id | UUID | UUID of the removed face | 487 | | subject | string | of the person, whose picture was saved for this api key | 488 | 489 | #### Verify Faces from a Given Image 490 | 491 | *[Example](examples/verification_face_from_image.py)* 492 | 493 | ```python 494 | face_collection.verify(image_path, image_id, options) 495 | ``` 496 | 497 | Compares similarities of given image with image from your face collection. 498 | 499 | 500 | | Argument | Type | Required | Notes | 501 | | ------------------ | ------- | -------- | ----------------------------------------------------------------------------------------------------------------------------------------------------- | 502 | | image_path | image | required | Image can pass from url, local path or bytes. Max size is 5Mb | 503 | | image_id | UUID | required | UUID of the verifying face | 504 | | options | string | Object | `ExpandedOptionsDict` object can be used in this method. See more [here](#options-structure). | 505 | 506 | Response: 507 | 508 | ```json 509 | { 510 | "result" : [ { 511 | "age" : { 512 | "probability": 0.9308982491493225, 513 | "high": 32, 514 | "low": 25 515 | }, 516 | "gender" : { 517 | "probability": 0.9898611307144165, 518 | "value": "female" 519 | }, 520 | "mask" : { 521 | "probability": 0.9999470710754395, 522 | "value": "without_mask" 523 | }, 524 | "embedding" : [ 9.424854069948196E-4, "...", -0.011415496468544006 ], 525 | "box" : { 526 | "probability" : 1.0, 527 | "x_max" : 1420, 528 | "y_max" : 1368, 529 | "x_min" : 548, 530 | "y_min" : 295 531 | }, 532 | "landmarks" : [ [ 814, 713 ], [ 1104, 829 ], [ 832, 937 ], [ 704, 1030 ], [ 1017, 1133 ] ], 533 | "subjects" : [ { 534 | "similarity" : 0.97858, 535 | "subject" : "subject1" 536 | } ], 537 | "execution_time" : { 538 | "age" : 28.0, 539 | "gender" : 26.0, 540 | "detector" : 117.0, 541 | "calculator" : 45.0, 542 | "mask": 36.0 543 | } 544 | } ], 545 | "plugins_versions" : { 546 | "age" : "agegender.AgeDetector", 547 | "gender" : "agegender.GenderDetector", 548 | "detector" : "facenet.FaceDetector", 549 | "calculator" : "facenet.Calculator", 550 | "mask": "facemask.MaskDetector" 551 | } 552 | } 553 | ``` 554 | 555 | | Element | Type | Description | 556 | | ------------------------------ | ------- | ------------------------------------------------------------ | 557 | | age | object | detected age range. Return only if [age plugin](https://github.com/exadel-inc/CompreFace/tree/master/docs/Face-services-and-plugins.md#face-plugins) is enabled | 558 | | gender | object | detected gender. Return only if [gender plugin](https://github.com/exadel-inc/CompreFace/tree/master/docs/Face-services-and-plugins.md#face-plugins) is enabled | 559 | | mask | object | detected mask. Return only if [face mask plugin](https://github.com/exadel-inc/CompreFace/blob/master/docs/Face-services-and-plugins.md) is enabled. | 560 | | embedding | array | face embeddings. Return only if [calculator plugin](https://github.com/exadel-inc/CompreFace/tree/master/docs/Face-services-and-plugins.md#face-plugins) is enabled | 561 | | box | object | list of parameters of the bounding box for this face | 562 | | probability | float | probability that a found face is actually a face | 563 | | x_max, y_max, x_min, y_min | integer | coordinates of the frame containing the face | 564 | | landmarks | array | list of the coordinates of the frame containing the face-landmarks. Return only if [landmarks plugin](https://github.com/exadel-inc/CompreFace/tree/master/docs/Face-services-and-plugins.md#face-plugins) is enabled | 565 | | similarity | float | similarity that on that image predicted person | 566 | | execution_time | object | execution time of all plugins | 567 | | plugins_versions | object | contains information about plugin versions | 568 | 569 | ### Get Subjects 570 | 571 | ```python 572 | recognition.get_subjects() 573 | ``` 574 | 575 | Returns subjects object 576 | 577 | Subjects object allows working with subjects directly (not via subject examples). 578 | 579 | More information about subjects [here](https://github.com/exadel-inc/CompreFace/blob/master/docs/Rest-API-description.md#managing-subjects) 580 | 581 | **Methods:** 582 | 583 | #### Add a Subject 584 | 585 | *[Example](examples/add_subject.py)* 586 | 587 | Create a new subject in Face Collection. 588 | ```python 589 | subjects.add(subject) 590 | ``` 591 | 592 | | Argument | Type | Required | Notes | 593 | | ------------------ | ------ | -------- | ------------------------------------------------------------------------| 594 | | subject | string | required | is the name of the subject. It can be any string | 595 | 596 | Response: 597 | 598 | ```json 599 | { 600 | "subject": "subject1" 601 | } 602 | ``` 603 | 604 | | Element | Type | Description | 605 | | -------- | ------ | -------------------------- | 606 | | subject | string | is the name of the subject | 607 | 608 | #### List Subjects 609 | 610 | *[Example](examples/get_list_of_all_subjects.py)* 611 | 612 | Returns all subject related to Face Collection. 613 | ```python 614 | subjects.list() 615 | ``` 616 | 617 | Response: 618 | 619 | ```json 620 | { 621 | "subjects": [ 622 | "", 623 | "" 624 | ] 625 | } 626 | ``` 627 | 628 | | Element | Type | Description | 629 | | -------- | ------ | -------------------------- | 630 | | subjects | array | the list of subjects in Face Collection | 631 | 632 | #### Rename a Subject 633 | 634 | *[Example](examples/update_existing_subject.py)* 635 | 636 | Rename existing subject. If a new subject name already exists, subjects are merged - all faces from the old subject name are reassigned to the subject with the new name, old subject removed. 637 | 638 | ```python 639 | subjects.rename(subject, new_name) 640 | ``` 641 | 642 | | Argument | Type | Required | Notes | 643 | | ------------------ | ------ | -------- | ------------------------------------------------------------------------| 644 | | subject | string | required | is the name of the subject that will be updated | 645 | | new_name | string | required | is the name of the subject. It can be any string | 646 | 647 | Response: 648 | 649 | ```json 650 | { 651 | "updated": "true|false" 652 | } 653 | ``` 654 | 655 | | Element | Type | Description | 656 | | -------- | ------ | -------------------------- | 657 | | updated | boolean | failed or success | 658 | 659 | #### Delete a Subject 660 | 661 | *[Example](examples/delete_subject_by_name.py)* 662 | 663 | Delete existing subject and all saved faces. 664 | ```python 665 | subjects.delete(subject) 666 | ``` 667 | 668 | | Argument | Type | Required | Notes | 669 | | ------------------ | ------ | -------- | ------------------------------------------------------------------------| 670 | | subject | string | required | is the name of the subject. | 671 | 672 | Response: 673 | 674 | ```json 675 | { 676 | "subject": "subject1" 677 | } 678 | ``` 679 | 680 | | Element | Type | Description | 681 | | -------- | ------ | -------------------------- | 682 | | subject | string | is the name of the subject | 683 | 684 | #### Delete All Subjects 685 | 686 | *[Example](examples/delete_all_subjects.py)* 687 | 688 | Delete all existing subjects and all saved faces. 689 | ```python 690 | subjects.delete_all() 691 | ``` 692 | 693 | Response: 694 | 695 | ```json 696 | { 697 | "deleted": "" 698 | } 699 | ``` 700 | 701 | | Element | Type | Description | 702 | | -------- | ------ | -------------------------- | 703 | | deleted | integer | number of deleted subjects | 704 | 705 | 706 | ## Face Detection Service 707 | 708 | Face detection service is used for detecting faces in the image. 709 | 710 | **Methods:** 711 | 712 | ### Detect 713 | 714 | *[Example](examples/detect_face_from_image.py)* 715 | 716 | ```python 717 | detection.detect(image_path, options) 718 | ``` 719 | 720 | Finds all faces on the image. 721 | 722 | | Argument | Type | Required | Notes | 723 | | ----------------- | ------- | -------- | ---------------------------------------------------------------------------------------------------------------------------------------------- | 724 | | image_path | image | required | image where to detect faces. Image can pass from url, local path or bytes. Max size is 5Mb | 725 | | options | string | Object | `ExpandedOptionsDict` object can be used in this method. See more [here](#options-structure). | 726 | 727 | Response: 728 | 729 | ```json 730 | { 731 | "result" : [ { 732 | "age" : { 733 | "probability": 0.9308982491493225, 734 | "high": 32, 735 | "low": 25 736 | }, 737 | "gender" : { 738 | "probability": 0.9898611307144165, 739 | "value": "female" 740 | }, 741 | "mask" : { 742 | "probability": 0.9999470710754395, 743 | "value": "without_mask" 744 | }, 745 | "embedding" : [ -0.03027934394776821, "...", -0.05117142200469971 ], 746 | "box" : { 747 | "probability" : 0.9987509250640869, 748 | "x_max" : 376, 749 | "y_max" : 479, 750 | "x_min" : 68, 751 | "y_min" : 77 752 | }, 753 | "landmarks" : [ [ 156, 245 ], [ 277, 253 ], [ 202, 311 ], [ 148, 358 ], [ 274, 365 ] ], 754 | "execution_time" : { 755 | "age" : 30.0, 756 | "gender" : 26.0, 757 | "detector" : 130.0, 758 | "calculator" : 49.0, 759 | "mask": 36.0 760 | } 761 | } ], 762 | "plugins_versions" : { 763 | "age" : "agegender.AgeDetector", 764 | "gender" : "agegender.GenderDetector", 765 | "detector" : "facenet.FaceDetector", 766 | "calculator" : "facenet.Calculator", 767 | "mask": "facemask.MaskDetector" 768 | } 769 | } 770 | ``` 771 | 772 | | Element | Type | Description | 773 | | ------------------------------ | ------- | ------------------------------------------------------------ | 774 | | age | object | detected age range. Return only if [age plugin](https://github.com/exadel-inc/CompreFace/tree/master/docs/Face-services-and-plugins.md#face-plugins) is enabled | 775 | | gender | object | detected gender. Return only if [gender plugin](https://github.com/exadel-inc/CompreFace/tree/master/docs/Face-services-and-plugins.md#face-plugins) is enabled | 776 | | mask | object | detected mask. Return only if [face mask plugin](https://github.com/exadel-inc/CompreFace/blob/master/docs/Face-services-and-plugins.md) is enabled. | 777 | | embedding | array | face embeddings. Return only if [calculator plugin](https://github.com/exadel-inc/CompreFace/tree/master/docs/Face-services-and-plugins.md#face-plugins) is enabled | 778 | | box | object | list of parameters of the bounding box for this face (on processedImage) | 779 | | probability | float | probability that a found face is actually a face (on processedImage) | 780 | | x_max, y_max, x_min, y_min | integer | coordinates of the frame containing the face (on processedImage) | 781 | | landmarks | array | list of the coordinates of the frame containing the face-landmarks. Return only if [landmarks plugin](https://github.com/exadel-inc/CompreFace/tree/master/docs/Face-services-and-plugins.md#face-plugins) is enabled | 782 | | execution_time | object | execution time of all plugins | 783 | | plugins_versions | object | contains information about plugin versions | 784 | 785 | 786 | ## Face Verification Service 787 | 788 | *[Example](examples/verify_face_from_image.py)* 789 | 790 | Face verification service is used for comparing two images. 791 | A source image should contain only one face which will be compared to all faces on the target image. 792 | 793 | **Methods:** 794 | 795 | ### Verify 796 | 797 | ```python 798 | verify.verify(source_image_path, target_image_path, options) 799 | ``` 800 | 801 | Compares two images provided in arguments. Source image should contain only one face, it will be compared to all faces in the target image. 802 | 803 | | Argument | Type | Required | Notes | 804 | | ------------------ | ------- | -------- | ----------------------------------------------------------------------------------------------------------------------------------------------------- | 805 | | image_id | UUID | required | UUID of the verifying face | 806 | | source_image_path | image | required | file to be verified. Image can pass from url, local path or bytes. Max size is 5Mb | 807 | | target_image_path | image | required | reference file to check the source file. Image can pass from url, local path or bytes. Max size is 5Mb | 808 | | options | string | Object | `ExpandedOptionsDict` object can be used in this method. See more [here](#options-structure). | 809 | 810 | Response: 811 | 812 | ```json 813 | { 814 | "result" : [{ 815 | "source_image_face" : { 816 | "age" : { 817 | "probability": 0.9308982491493225, 818 | "high": 32, 819 | "low": 25 820 | }, 821 | "gender" : { 822 | "probability": 0.9898611307144165, 823 | "value": "female" 824 | }, 825 | "mask" : { 826 | "probability": 0.9999470710754395, 827 | "value": "without_mask" 828 | }, 829 | "embedding" : [ -0.0010271212086081505, "...", -0.008746841922402382 ], 830 | "box" : { 831 | "probability" : 0.9997453093528748, 832 | "x_max" : 205, 833 | "y_max" : 167, 834 | "x_min" : 48, 835 | "y_min" : 0 836 | }, 837 | "landmarks" : [ [ 92, 44 ], [ 130, 68 ], [ 71, 76 ], [ 60, 104 ], [ 95, 125 ] ], 838 | "execution_time" : { 839 | "age" : 85.0, 840 | "gender" : 51.0, 841 | "detector" : 67.0, 842 | "calculator" : 116.0, 843 | "mask": 36.0 844 | } 845 | }, 846 | "face_matches": [ 847 | { 848 | "age" : { 849 | "probability": 0.9308982491493225, 850 | "high": 32, 851 | "low": 25 852 | }, 853 | "gender" : { 854 | "probability": 0.9898611307144165, 855 | "value": "female" 856 | }, 857 | "mask" : { 858 | "probability": 0.9999470710754395, 859 | "value": "without_mask" 860 | }, 861 | "embedding" : [ -0.049007344990968704, "...", -0.01753818802535534 ], 862 | "box" : { 863 | "probability" : 0.99975, 864 | "x_max" : 308, 865 | "y_max" : 180, 866 | "x_min" : 235, 867 | "y_min" : 98 868 | }, 869 | "landmarks" : [ [ 260, 129 ], [ 273, 127 ], [ 258, 136 ], [ 257, 150 ], [ 269, 148 ] ], 870 | "similarity" : 0.97858, 871 | "execution_time" : { 872 | "age" : 59.0, 873 | "gender" : 30.0, 874 | "detector" : 177.0, 875 | "calculator" : 70.0, 876 | "mask": 36.0 877 | } 878 | }], 879 | "plugins_versions" : { 880 | "age" : "agegender.AgeDetector", 881 | "gender" : "agegender.GenderDetector", 882 | "detector" : "facenet.FaceDetector", 883 | "calculator" : "facenet.Calculator", 884 | "mask": "facemask.MaskDetector" 885 | } 886 | }] 887 | } 888 | ``` 889 | 890 | | Element | Type | Description | 891 | | ------------------------------ | ------- | ------------------------------------------------------------ | 892 | | source_image_face | object | additional info about source image face | 893 | | face_matches | array | result of face verification | 894 | | age | object | detected age range. Return only if [age plugin](https://github.com/exadel-inc/CompreFace/tree/master/docs/Face-services-and-plugins.md#face-plugins) is enabled | 895 | | gender | object | detected gender. Return only if [gender plugin](https://github.com/exadel-inc/CompreFace/tree/master/docs/Face-services-and-plugins.md#face-plugins) is enabled | 896 | | mask | object | detected mask. Return only if [face mask plugin](https://github.com/exadel-inc/CompreFace/blob/master/docs/Face-services-and-plugins.md) is enabled. | 897 | | embedding | array | face embeddings. Return only if [calculator plugin](https://github.com/exadel-inc/CompreFace/tree/master/docs/Face-services-and-plugins.md#face-plugins) is enabled | 898 | | box | object | list of parameters of the bounding box for this face | 899 | | probability | float | probability that a found face is actually a face | 900 | | x_max, y_max, x_min, y_min | integer | coordinates of the frame containing the face | 901 | | landmarks | array | list of the coordinates of the frame containing the face-landmarks. Return only if [landmarks plugin](https://github.com/exadel-inc/CompreFace/tree/master/docs/Face-services-and-plugins.md#face-plugins) is enabled | 902 | | similarity | float | similarity between this face and the face on the source image | 903 | | execution_time | object | execution time of all plugins | 904 | | plugins_versions | object | contains information about plugin versions | 905 | 906 | # Contributing 907 | 908 | Contributions are what make the open source community such an amazing place to be learn, inspire, and create. Any contributions you make are greatly appreciated. 909 | 910 | 1. Fork the Project 911 | 2. Create your Feature Branch (`git checkout -b feature/AmazingFeature`) 912 | 3. Commit your Changes (`git commit -m 'Add some AmazingFeature'`) 913 | 4. Push to the Branch (`git push origin feature/AmazingFeature`) 914 | 5. Open a Pull Request 915 | 916 | After creating your first contributing pull request, you will receive a request to sign our Contributor License Agreement by commenting your pull request with a special message. 917 | 918 | ## Report Bugs 919 | 920 | Please report any bugs [here](https://github.com/exadel-inc/compreface-python-sdk/issues). 921 | 922 | If you are reporting a bug, please specify: 923 | 924 | - Your operating system name and version 925 | - Any details about your local setup that might be helpful in troubleshooting 926 | - Detailed steps to reproduce the bug 927 | 928 | ## Submit Feedback 929 | 930 | The best way to send us feedback is to file an issue at https://github.com/exadel-inc/compreface-python-sdk/issues. 931 | 932 | If you are proposing a feature, please: 933 | 934 | - Explain in detail how it should work. 935 | - Keep the scope as narrow as possible to make it easier to implement. 936 | 937 | # License info 938 | 939 | CompreFace Python SDK is open-source facial recognition SDK released under the [Apache 2.0 license](https://www.apache.org/licenses/LICENSE-2.0.html). 940 | -------------------------------------------------------------------------------- /compreface/__init__.py: -------------------------------------------------------------------------------- 1 | """ 2 | Copyright(c) 2021 the original author or authors 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | https: // www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 13 | or implied. See the License for the specific language governing 14 | permissions and limitations under the License. 15 | """ 16 | 17 | from .core import CompreFace 18 | -------------------------------------------------------------------------------- /compreface/client/__init__.py: -------------------------------------------------------------------------------- 1 | """ 2 | Copyright(c) 2021 the original author or authors 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | https: // www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 13 | or implied. See the License for the specific language governing 14 | permissions and limitations under the License. 15 | """ 16 | 17 | from .verification_face_from_image import VerificationFaceFromImageClient 18 | from .add_example_of_subject import AddExampleOfSubjectClient 19 | from .delete_example_by_id import DeleteExampleByIdClient 20 | from .recognize_face_from_image import RecognizeFaceFromImageClient 21 | from .detect_face_from_image import DetectFaceFromImageClient 22 | from .verify_face_from_image import VerifyFaceFromImageClient 23 | from .subject_client import SubjectClient 24 | -------------------------------------------------------------------------------- /compreface/client/add_example_of_subject.py: -------------------------------------------------------------------------------- 1 | """ 2 | Copyright(c) 2021 the original author or authors 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | https: // www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 13 | or implied. See the License for the specific language governing 14 | permissions and limitations under the License. 15 | """ 16 | 17 | import requests 18 | 19 | from compreface.common.multipart_constructor import multipart_constructor 20 | from compreface.common.typed_dict import DetProbOptionsDict, check_fields_by_name 21 | from compreface.config.api_list import RECOGNIZE_CRUD_API 22 | from ..common import ClientRequest 23 | 24 | 25 | class AddExampleOfSubjectClient(ClientRequest): 26 | 27 | def __init__(self, api_key: str, domain: str, port: str): 28 | super().__init__() 29 | self.client_url: str = RECOGNIZE_CRUD_API 30 | self.api_key: str = api_key 31 | self.url: str = domain + ':' + port + self.client_url 32 | 33 | """ 34 | GET request for get all subjects. 35 | 36 | :return: json with subjects from server. 37 | """ 38 | 39 | def get(self) -> dict: 40 | url: str = self.url 41 | result = requests.get(url, headers={'x-api-key': self.api_key}) 42 | return result.json() 43 | 44 | """ 45 | POST request for add subject from his face in image. 46 | 47 | :param image_path: path to image in file system. 48 | :param subject: fullname 49 | :param options: dictionary with options for server. 50 | 51 | :return: json with this subject from server. 52 | """ 53 | 54 | def post(self, image: str = '' or bytes, subject: str = '', options: DetProbOptionsDict = {}) -> dict: 55 | url: str = self.url + '?subject=' + subject 56 | # Validation loop and adding fields to the url. 57 | for key in options.keys(): 58 | # Checks fields with necessary rules. 59 | # key - key field by options. 60 | check_fields_by_name(key, options[key]) 61 | url += '&' + key + "=" + str(options[key]) 62 | m = multipart_constructor(image) 63 | result = requests.post(url, data=m, headers={'Content-Type': m.content_type, 64 | 'x-api-key': self.api_key}) 65 | return result.json() 66 | 67 | def put(self): 68 | pass 69 | 70 | """ 71 | Delete request to CompreFace server. 72 | 73 | :param subject: fullname 74 | 75 | :return: json from server. 76 | """ 77 | 78 | def delete(self, subject: str = ''): 79 | url: str = self.url + '?subject=' + subject 80 | result = requests.delete(url, headers={'x-api-key': self.api_key}) 81 | return result.json() 82 | -------------------------------------------------------------------------------- /compreface/client/delete_example_by_id.py: -------------------------------------------------------------------------------- 1 | """ 2 | Copyright(c) 2021 the original author or authors 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | https: // www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 13 | or implied. See the License for the specific language governing 14 | permissions and limitations under the License. 15 | """ 16 | 17 | import requests 18 | 19 | from compreface.config.api_list import RECOGNIZE_CRUD_API 20 | from ..common import ClientRequest 21 | 22 | 23 | class DeleteExampleByIdClient(ClientRequest): 24 | 25 | """ 26 | Delete example by id from image_id. 27 | """ 28 | 29 | def __init__(self, api_key: str, domain: str, port: str): 30 | super().__init__() 31 | self.client_url: str = RECOGNIZE_CRUD_API 32 | self.api_key: str = api_key 33 | self.url: str = domain + ':' + port + self.client_url 34 | 35 | def get(self): 36 | pass 37 | 38 | def post(self): 39 | pass 40 | 41 | def put(self): 42 | pass 43 | 44 | """ 45 | DELETE request to CompreFace server. Delete example by id from image_id. 46 | 47 | :param image_id: 48 | 49 | :return: json from server. 50 | """ 51 | 52 | def delete(self, image_id: str = ''): 53 | url: str = self.url + '/' + image_id 54 | result = requests.delete(url, headers={'x-api-key': self.api_key}) 55 | return result.json() 56 | -------------------------------------------------------------------------------- /compreface/client/detect_face_from_image.py: -------------------------------------------------------------------------------- 1 | """ 2 | Copyright(c) 2021 the original author or authors 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | https: // www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 13 | or implied. See the License for the specific language governing 14 | permissions and limitations under the License. 15 | """ 16 | 17 | import requests 18 | from compreface.common.multipart_constructor import multipart_constructor 19 | from compreface.common.typed_dict import ExpandedOptionsDict, check_fields_by_name 20 | from ..common import ClientRequest 21 | from compreface.config.api_list import DETECTION_API 22 | 23 | 24 | class DetectFaceFromImageClient(ClientRequest): 25 | """ 26 | Detection faces in image. It uses image path for encode and send to CompreFace server. 27 | """ 28 | 29 | def __init__(self, api_key: str, domain: str, port: str): 30 | super().__init__() 31 | self.client_url: str = DETECTION_API 32 | self.api_key: str = api_key 33 | self.url: str = domain + ':' + port + self.client_url 34 | 35 | def get(self): 36 | pass 37 | 38 | """ 39 | POST request for detection faces in image. 40 | 41 | :param image_path: Path to image in file system. 42 | :param options: dictionary with options for server. 43 | 44 | :return: json from server. 45 | """ 46 | 47 | def post(self, image: str = '' or bytes, options: ExpandedOptionsDict = {}): 48 | url: str = self.url + '?' 49 | 50 | # Validation loop and adding fields to the url. 51 | for key in options.keys(): 52 | # Checks fields with necessary rules. 53 | # key - key field by options. 54 | check_fields_by_name(key, options[key]) 55 | url += '&' + key + "=" + str(options[key]) 56 | 57 | # Encoding image from path and encode in multipart for sending to the server. 58 | m = multipart_constructor(image) 59 | 60 | # Sending encode image for detection faces. 61 | result = requests.post(url, data=m, headers={'Content-Type': m.content_type, 62 | 'x-api-key': self.api_key}) 63 | return result.json() 64 | 65 | def put(self): 66 | pass 67 | 68 | def delete(self): 69 | pass 70 | -------------------------------------------------------------------------------- /compreface/client/recognize_face_from_image.py: -------------------------------------------------------------------------------- 1 | """ 2 | Copyright(c) 2021 the original author or authors 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | https: // www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 13 | or implied. See the License for the specific language governing 14 | permissions and limitations under the License. 15 | """ 16 | import requests 17 | 18 | from compreface.common.multipart_constructor import multipart_constructor 19 | from compreface.common.typed_dict import AllOptionsDict, check_fields_by_name 20 | from compreface.config.api_list import RECOGNIZE_API 21 | from ..common import ClientRequest 22 | 23 | 24 | class RecognizeFaceFromImageClient(ClientRequest): 25 | """ 26 | Recognize faces in image. It uses image path for encode and send to CompreFace server. 27 | """ 28 | 29 | def __init__(self, api_key: str, domain: str, port: str): 30 | super().__init__() 31 | self.client_url: str = RECOGNIZE_API 32 | self.api_key: str = api_key 33 | self.url: str = domain + ':' + port + self.client_url 34 | 35 | def get(self): 36 | pass 37 | 38 | """ 39 | POST request for recognize faces in image. 40 | 41 | :param image_path: Path to image in file system. 42 | :param options: dictionary with options for server. 43 | 44 | :return: json from server. 45 | """ 46 | 47 | def post(self, image: str = '' or bytes, options: AllOptionsDict = {}): 48 | url: str = self.url + "?" 49 | 50 | # Validation loop and adding fields to the url. 51 | for key in options.keys(): 52 | # Checks fields with necessary rules. 53 | # key - key field by options. 54 | check_fields_by_name(key, options[key]) 55 | url += '&' + key + "=" + str(options[key]) 56 | 57 | # Encoding image from path and encode in multipart for sending to the server. 58 | m = multipart_constructor(image) 59 | 60 | # Sending encode image for recognize faces. 61 | result = requests.post(url, data=m, headers={'Content-Type': m.content_type, 62 | 'x-api-key': self.api_key}) 63 | return result.json() 64 | 65 | def put(self): 66 | pass 67 | 68 | def delete(self): 69 | pass 70 | -------------------------------------------------------------------------------- /compreface/client/subject_client.py: -------------------------------------------------------------------------------- 1 | """ 2 | Copyright(c) 2021 the original author or authors 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | https: // www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 13 | or implied. See the License for the specific language governing 14 | permissions and limitations under the License. 15 | """ 16 | import json 17 | 18 | import requests 19 | 20 | from compreface.config.api_list import SUBJECTS_CRUD_API 21 | from ..common import ClientRequest 22 | 23 | 24 | class SubjectClient(ClientRequest): 25 | 26 | def __init__(self, api_key: str, domain: str, port: str): 27 | super().__init__() 28 | self.client_url: str = SUBJECTS_CRUD_API 29 | self.api_key: str = api_key 30 | self.url: str = domain + ':' + port + self.client_url 31 | self.headers = {'Content-Type': 'application/json', 'x-api-key': api_key} 32 | 33 | """ 34 | GET request for get all subjects. 35 | 36 | :return: json with subjects from server. 37 | """ 38 | 39 | def get(self) -> dict: 40 | url: str = self.url 41 | result = requests.get(url, headers=self.headers) 42 | return result.json() 43 | 44 | """ 45 | POST request for add subject without an image. 46 | 47 | :param subject: fullname 48 | 49 | :return: json with this subject from server. 50 | """ 51 | 52 | def post(self, subject: dict = '') -> dict: 53 | url: str = self.url 54 | result = requests.post(url, data=json.dumps(subject), headers=self.headers) 55 | return result.json() 56 | 57 | """ 58 | PUT request to CompreFace server for rename existing subject. 59 | 60 | :param subject: fullname 61 | 62 | :return: json from server. 63 | """ 64 | 65 | def put(self, request: dict = '') -> dict: 66 | url: str = self.url + '/' + request.get('api_endpoint') 67 | result = requests.put(url, data=json.dumps(request), headers=self.headers) 68 | return result.json() 69 | 70 | """ 71 | DELETE request to CompreFace server for delete subjects. 72 | 73 | :param subject: fullname 74 | 75 | :return: json from server. 76 | """ 77 | 78 | def delete(self, subject: str = '') -> dict: 79 | url: str = self.url + '/' + subject if subject else self.url 80 | result = requests.delete(url, headers=self.headers) 81 | return result.json() 82 | -------------------------------------------------------------------------------- /compreface/client/verification_face_from_image.py: -------------------------------------------------------------------------------- 1 | """ 2 | Copyright(c) 2021 the original author or authors 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | https: // www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 13 | or implied. See the License for the specific language governing 14 | permissions and limitations under the License. 15 | """ 16 | import requests 17 | 18 | from compreface.common.multipart_constructor import multipart_constructor 19 | from compreface.common.typed_dict import ExpandedOptionsDict, check_fields_by_name 20 | from compreface.config.api_list import RECOGNIZE_CRUD_API 21 | from ..common import ClientRequest 22 | 23 | 24 | class VerificationFaceFromImageClient(ClientRequest): 25 | """ 26 | Compare face in image. It uses image path for encode and send to CompreFace 27 | server with validation by image id. 28 | """ 29 | 30 | def __init__(self, api_key: str, domain: str, port: str): 31 | super().__init__() 32 | self.client_url: str = RECOGNIZE_CRUD_API 33 | self.api_key: str = api_key 34 | self.url: str = domain + ':' + port + self.client_url 35 | 36 | def get(self): 37 | pass 38 | 39 | """ 40 | POST request for compare face in image using image id. 41 | 42 | :param image_path: Path to image in file system. 43 | :param image_id: subject id from previously added image. 44 | :param options: dictionary with options for server. 45 | 46 | :return: json from server. 47 | """ 48 | 49 | def post(self, 50 | image: str = '' or bytes, 51 | image_id: str = '', 52 | options: ExpandedOptionsDict = {}) -> dict: 53 | 54 | url: str = self.url + '/' + image_id + '/verify?' 55 | 56 | # Validation loop and adding fields to the url. 57 | for key in options.keys(): 58 | # Checks fields with necessary rules. 59 | # key - key field by options. 60 | check_fields_by_name(key, options[key]) 61 | url += '&' + key + "=" + str(options[key]) 62 | 63 | # Encoding image from path and encode in multipart for sending to the server. 64 | m = multipart_constructor(image) 65 | 66 | # Sending encode image for verify face. 67 | result = requests.post(url, data=m, headers={'Content-Type': m.content_type, 68 | 'x-api-key': self.api_key}) 69 | return result.json() 70 | 71 | def put(self): 72 | pass 73 | 74 | def delete(self): 75 | pass 76 | -------------------------------------------------------------------------------- /compreface/client/verify_face_from_image.py: -------------------------------------------------------------------------------- 1 | """ 2 | Copyright(c) 2021 the original author or authors 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | https: // www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 13 | or implied. See the License for the specific language governing 14 | permissions and limitations under the License. 15 | """ 16 | 17 | from compreface.common.multipart_constructor import multipart_constructor_with_two_images 18 | import requests 19 | from compreface.config.api_list import VERIFICATION_API 20 | from compreface.common.typed_dict import ExpandedOptionsDict, check_fields_by_name 21 | from compreface.common.client import ClientRequest 22 | 23 | 24 | class VerifyFaceFromImageClient(ClientRequest): 25 | """ 26 | Verify face in image. It uses source and target images for encode and send to CompreFace 27 | server with validation by image id. 28 | """ 29 | 30 | def __init__(self, api_key: str, domain: str, port: str): 31 | super().__init__() 32 | self.client_url: str = VERIFICATION_API 33 | self.api_key: str = api_key 34 | self.url: str = domain + ':' + port + self.client_url 35 | 36 | def get(self): 37 | pass 38 | 39 | """ 40 | POST request for verify face in image using source and target images. 41 | 42 | :param source_image: Path to source image in file system. 43 | :param target_image: Path to target image in file system. 44 | :param image_id: subject id from previously added image. 45 | :param options: dictionary with options for server. 46 | 47 | :return: json from server. 48 | """ 49 | 50 | def post(self, 51 | source_image: str = '' or bytes, 52 | target_image: str = '' or bytes, 53 | options: ExpandedOptionsDict = {}) -> dict: 54 | 55 | url: str = self.url + '/verify?' 56 | # Validation loop and adding fields to the url. 57 | for key in options.keys(): 58 | # Checks fields with necessary rules. 59 | # key - key field by options. 60 | check_fields_by_name(key, options[key]) 61 | url += '&' + key + "=" + str(options[key]) 62 | 63 | # Encoding image from path and encode in multipart for sending to the server. 64 | m = multipart_constructor_with_two_images(source_image, target_image) 65 | 66 | # Sending encode image for verify face. 67 | result = requests.post(url, data=m, headers={'Content-Type': m.content_type, 68 | 'x-api-key': self.api_key}) 69 | return result.json() 70 | 71 | def put(self): 72 | pass 73 | 74 | def delete(self): 75 | pass 76 | -------------------------------------------------------------------------------- /compreface/collections/__init__.py: -------------------------------------------------------------------------------- 1 | """ 2 | Copyright(c) 2021 the original author or authors 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | https: // www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 13 | or implied. See the License for the specific language governing 14 | permissions and limitations under the License. 15 | """ 16 | 17 | from .face_collections import FaceCollection, Subjects 18 | -------------------------------------------------------------------------------- /compreface/collections/face_collections.py: -------------------------------------------------------------------------------- 1 | """ 2 | Copyright(c) 2021 the original author or authors 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | https: // www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 13 | or implied. See the License for the specific language governing 14 | permissions and limitations under the License. 15 | """ 16 | 17 | from compreface.common.typed_dict import AllOptionsDict, ExpandedOptionsDict, DetProbOptionsDict, pass_dict 18 | from ..use_cases import ( 19 | AddExampleOfSubject, 20 | AddSubject, 21 | DeleteAllExamplesOfSubjectByName, 22 | DeleteSubjectByName, 23 | DeleteAllSubjects, 24 | DeleteExampleById, 25 | GetSubjects, 26 | UpdateSubject, 27 | VerificationFaceFromImage, 28 | ListOfAllSavedSubjects 29 | ) 30 | 31 | 32 | class FaceCollection: 33 | def __init__(self, api_key: str, domain: str, port: str, options: AllOptionsDict = {}): 34 | """Init service with define API Key""" 35 | self.available_services = [] 36 | self.api_key = api_key 37 | self.options = options 38 | self.add_example: AddExampleOfSubject = AddExampleOfSubject( 39 | domain=domain, 40 | port=port, 41 | api_key=api_key 42 | ) 43 | self.list_of_all_saved_subjects: ListOfAllSavedSubjects = ListOfAllSavedSubjects( 44 | domain=domain, 45 | port=port, 46 | api_key=api_key 47 | ) 48 | self.delete_all_examples_of_subject_by_name: DeleteAllExamplesOfSubjectByName = DeleteAllExamplesOfSubjectByName( 49 | domain=domain, 50 | port=port, 51 | api_key=api_key 52 | ) 53 | self.delete_all_examples_by_id: DeleteExampleById = DeleteExampleById( 54 | domain=domain, 55 | port=port, 56 | api_key=api_key 57 | ) 58 | self.verify_face_from_image: VerificationFaceFromImage = VerificationFaceFromImage( 59 | domain=domain, 60 | port=port, 61 | api_key=api_key 62 | ) 63 | 64 | def list(self) -> dict: 65 | """ 66 | Get list of collections 67 | :return: 68 | """ 69 | return self.list_of_all_saved_subjects.execute() 70 | 71 | def add(self, image_path: str, subject: str, options: DetProbOptionsDict = {}) -> dict: 72 | """ 73 | Add example to collection 74 | :param image_path: 75 | :param subject: 76 | :return: 77 | """ 78 | request = AddExampleOfSubject.Request( 79 | api_key=self.api_key, 80 | image_path=image_path, 81 | subject=subject 82 | ) 83 | return self.add_example.execute(request, pass_dict(options, DetProbOptionsDict) if options == {} else options) 84 | 85 | def delete(self, image_id: str) -> dict: 86 | """ 87 | Delete example by Id 88 | :param image_id: 89 | :return: 90 | """ 91 | request = DeleteExampleById.Request( 92 | api_key=self.api_key, 93 | image_id=image_id 94 | ) 95 | return self.delete_all_examples_by_id.execute(request) 96 | 97 | def delete_all(self, subject: str) -> dict: 98 | """ 99 | Delete all examples of subject 100 | :param subject: 101 | :return: 102 | """ 103 | request = DeleteAllExamplesOfSubjectByName.Request( 104 | api_key=self.api_key, 105 | subject=subject 106 | ) 107 | return self.delete_all_examples_of_subject_by_name.execute(request) 108 | 109 | def verify(self, image_path: str, image_id: str, options: ExpandedOptionsDict = {}) -> dict: 110 | """ 111 | Compare image 112 | :param image_path: 113 | :param image_id: 114 | :return: 115 | """ 116 | request = VerificationFaceFromImage.Request( 117 | api_key=self.api_key, 118 | image_path=image_path, 119 | image_id=image_id 120 | ) 121 | return self.verify_face_from_image.execute(request, pass_dict(options, ExpandedOptionsDict) if options == {} else options) 122 | 123 | 124 | class Subjects: 125 | def __init__(self, api_key: str, domain: str, port: str, options: AllOptionsDict = {}): 126 | """Init service with define API Key""" 127 | self.available_services = [] 128 | self.api_key = api_key 129 | self.options = options 130 | self.add_subject: AddSubject = AddSubject( 131 | domain=domain, 132 | port=port, 133 | api_key=api_key 134 | ) 135 | self.update_subject: UpdateSubject = UpdateSubject( 136 | domain=domain, 137 | port=port, 138 | api_key=api_key 139 | ) 140 | self.delete_subject: DeleteSubjectByName = DeleteSubjectByName( 141 | domain=domain, 142 | port=port, 143 | api_key=api_key 144 | ) 145 | self.delete_all_subjects: DeleteAllSubjects = DeleteAllSubjects( 146 | domain=domain, 147 | port=port, 148 | api_key=api_key 149 | ) 150 | self.list_of_all_saved_subjects: GetSubjects = GetSubjects( 151 | domain=domain, 152 | port=port, 153 | api_key=api_key 154 | ) 155 | 156 | def list(self) -> dict: 157 | """ 158 | Get list of subjects 159 | :return: 160 | """ 161 | return self.list_of_all_saved_subjects.execute() 162 | 163 | def add(self, subject: str) -> dict: 164 | """ 165 | Add subject 166 | :param subject: 167 | :return: 168 | """ 169 | request = AddSubject.Request( 170 | subject=subject 171 | ) 172 | return self.add_subject.execute(request) 173 | 174 | def update(self, subject: str, new_name: str) -> dict: 175 | """ 176 | Update subject by name 177 | :param subject: 178 | :param new_name: 179 | :return: 180 | """ 181 | request = UpdateSubject.Request( 182 | subject=new_name, 183 | api_endpoint=subject 184 | ) 185 | return self.update_subject.execute(request) 186 | 187 | def delete(self, subject: str) -> dict: 188 | """ 189 | Delete subject by name 190 | :param subject: 191 | :return: 192 | """ 193 | request = DeleteSubjectByName.Request( 194 | subject=subject 195 | ) 196 | return self.delete_subject.execute(request) 197 | 198 | def delete_all(self) -> dict: 199 | """ 200 | Delete all subjects 201 | :return: 202 | """ 203 | return self.delete_all_subjects.execute() 204 | -------------------------------------------------------------------------------- /compreface/common/__init__.py: -------------------------------------------------------------------------------- 1 | """ 2 | Copyright(c) 2021 the original author or authors 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | https: // www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 13 | or implied. See the License for the specific language governing 14 | permissions and limitations under the License. 15 | """ 16 | 17 | from .client import ClientRequest 18 | from .service import Service 19 | -------------------------------------------------------------------------------- /compreface/common/client.py: -------------------------------------------------------------------------------- 1 | """ 2 | Copyright(c) 2021 the original author or authors 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | https: // www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 13 | or implied. See the License for the specific language governing 14 | permissions and limitations under the License. 15 | """ 16 | 17 | from abc import ABC, abstractmethod 18 | 19 | 20 | class ClientRequest(ABC): 21 | """The best class of all requests""" 22 | 23 | @abstractmethod 24 | def __init__(self): 25 | pass 26 | 27 | @abstractmethod 28 | def get(self): 29 | pass 30 | 31 | @abstractmethod 32 | def post(self): 33 | pass 34 | 35 | @abstractmethod 36 | def put(self): 37 | pass 38 | 39 | @abstractmethod 40 | def delete(self): 41 | pass 42 | -------------------------------------------------------------------------------- /compreface/common/multipart_constructor.py: -------------------------------------------------------------------------------- 1 | import os 2 | import requests 3 | 4 | from requests_toolbelt.multipart.encoder import MultipartEncoder 5 | 6 | 7 | def get_file(image: str = '' or bytes): 8 | if not os.path.isfile(image): 9 | if type(image) != bytes: 10 | response = requests.get(image) 11 | file = response.content 12 | else: 13 | file = image 14 | file = ('image.jpg', file) 15 | else: 16 | name_img: str = os.path.basename(image) 17 | file = (name_img, open(image, 'rb')) 18 | return file 19 | 20 | 21 | def multipart_constructor(image: str = '' or bytes): 22 | 23 | # Encoding image from path and encode in multipart for sending to the server. 24 | return MultipartEncoder( 25 | fields={'file': get_file(image)} 26 | ) 27 | 28 | 29 | def multipart_constructor_with_two_images(source_image: str = '' or bytes, target_image: str = '' or bytes): 30 | return MultipartEncoder( 31 | fields={'source_image': get_file( 32 | source_image), 'target_image': get_file(target_image)} 33 | ) 34 | -------------------------------------------------------------------------------- /compreface/common/service.py: -------------------------------------------------------------------------------- 1 | """ 2 | Copyright(c) 2021 the original author or authors 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | https: // www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 13 | or implied. See the License for the specific language governing 14 | permissions and limitations under the License. 15 | """ 16 | 17 | from abc import ABC, abstractmethod 18 | from compreface.common.typed_dict import AllOptionsDict 19 | 20 | 21 | class Service(ABC): 22 | """The best class of all services""" 23 | 24 | @abstractmethod 25 | def __init__(self, api_key: str, options: AllOptionsDict): 26 | self._api_key = api_key 27 | self._options = options 28 | 29 | @property 30 | def api_key(self): 31 | return self._api_key 32 | 33 | @property 34 | def options(self): 35 | return self._options 36 | 37 | @abstractmethod 38 | def get_available_functions(self): 39 | pass 40 | -------------------------------------------------------------------------------- /compreface/common/typed_dict.py: -------------------------------------------------------------------------------- 1 | """ 2 | Copyright(c) 2021 the original author or authors 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | https: // www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 13 | or implied. See the License for the specific language governing 14 | permissions and limitations under the License. 15 | """ 16 | 17 | from compreface.exceptions import IncorrectFieldException 18 | from typing import Any, TypedDict 19 | 20 | 21 | class DetProbOptionsDict(TypedDict): 22 | det_prob_threshold: float 23 | 24 | 25 | class ExpandedOptionsDict(DetProbOptionsDict): 26 | limit: int 27 | status: bool 28 | face_plugins: str 29 | 30 | 31 | class AllOptionsDict(ExpandedOptionsDict): 32 | prediction_count: int 33 | 34 | 35 | """ 36 | Checks fields with necessary rules. 37 | :param name: key from dictionary. 38 | :param value: value from dictionary. 39 | 40 | raise exception when value break necessary rules. 41 | """ 42 | 43 | 44 | def check_fields_by_name(name: str, value: Any): 45 | if name == 'limit' or name == "prediction_count": 46 | if value < 0: 47 | raise IncorrectFieldException( 48 | '{} must be greater or equal zero.'.format(name)) 49 | if name == 'det_prob_threshold': 50 | if value < 0.0 or value > 1.0: 51 | raise IncorrectFieldException( 52 | 'det_prob_threshold must be between 0.0 and 1.0. Received value {}'.format(value)) 53 | if name == "face_plugins": 54 | values = value.strip() 55 | for row in values.split(','): 56 | if row == ',': 57 | pass 58 | if row.find('age') == -1 and row.find('calculator') == -1 and row.find('gender') == -1 \ 59 | and row.find('landmarks') == -1 and row.find('mask') == -1: 60 | raise IncorrectFieldException( 61 | "face_plugins must be only contains calculator,age,gender,landmarks,mask. " 62 | "Incorrect value {}".format(row)) 63 | 64 | 65 | def pass_dict(options: AllOptionsDict, type: DetProbOptionsDict or ExpandedOptionsDict): 66 | converted_options: ExpandedOptionsDict or DetProbOptionsDict = {} 67 | for key in type.__annotations__.keys(): 68 | value = options.get(key) 69 | if value != None: 70 | converted_options[key] = value 71 | return converted_options 72 | -------------------------------------------------------------------------------- /compreface/config/__init__.py: -------------------------------------------------------------------------------- 1 | """ 2 | Copyright(c) 2021 the original author or authors 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | https: // www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 13 | or implied. See the License for the specific language governing 14 | permissions and limitations under the License. 15 | """ 16 | -------------------------------------------------------------------------------- /compreface/config/api_list.py: -------------------------------------------------------------------------------- 1 | """ 2 | Copyright(c) 2021 the original author or authors 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | https: // www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 13 | or implied. See the License for the specific language governing 14 | permissions and limitations under the License. 15 | """ 16 | 17 | 18 | RECOGNITION_ROOT_API: str = '/api/v1/recognition' 19 | 20 | RECOGNIZE_API: str = RECOGNITION_ROOT_API + '/recognize' 21 | RECOGNIZE_CRUD_API: str = RECOGNITION_ROOT_API + '/faces' 22 | SUBJECTS_CRUD_API: str = RECOGNITION_ROOT_API + '/subjects' 23 | 24 | DETECTION_API: str = '/api/v1/detection/detect' 25 | 26 | VERIFICATION_API: str = '/api/v1/verification' 27 | -------------------------------------------------------------------------------- /compreface/core/__init__.py: -------------------------------------------------------------------------------- 1 | """ 2 | Copyright(c) 2021 the original author or authors 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | https: // www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 13 | or implied. See the License for the specific language governing 14 | permissions and limitations under the License. 15 | """ 16 | from .model import CompreFace 17 | -------------------------------------------------------------------------------- /compreface/core/model.py: -------------------------------------------------------------------------------- 1 | """ 2 | Copyright(c) 2021 the original author or authors 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | https: // www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 13 | or implied. See the License for the specific language governing 14 | permissions and limitations under the License. 15 | """ 16 | 17 | from compreface.common.typed_dict import AllOptionsDict 18 | from typing import Optional 19 | from ..service import ( 20 | RecognitionService, 21 | VerificationService, 22 | DetectionService 23 | ) 24 | 25 | 26 | class CompreFace(object): 27 | """ 28 | Main class 29 | """ 30 | 31 | def __init__(self, domain: str, port: str, options: AllOptionsDict = {}): 32 | self._domain: str = domain 33 | self._port: str = port 34 | self._options: AllOptionsDict = options 35 | self.recognition: Optional[RecognitionService] = None 36 | self.verification: Optional[VerificationService] = None 37 | self.detection: Optional[DetectionService] = None 38 | 39 | @property 40 | def domain(self): 41 | return self._domain 42 | 43 | @domain.setter 44 | def domain(self, domain: str): 45 | self._domain = domain 46 | 47 | @property 48 | def port(self): 49 | return self._port 50 | 51 | @port.setter 52 | def port(self, port: str): 53 | self._port = port 54 | 55 | @property 56 | def options(self): 57 | return self._options 58 | 59 | @options.setter 60 | def options(self, options: AllOptionsDict): 61 | self._options = options 62 | 63 | def init_face_recognition(self, api_key: str) -> RecognitionService: 64 | """ 65 | Init Face Recognition Service 66 | :param api_key: 67 | :return: 68 | """ 69 | self.recognition = RecognitionService(api_key=api_key, 70 | domain=self.domain, 71 | port=self.port, 72 | options=self.options) 73 | return self.recognition 74 | 75 | def init_face_verification(self, api_key: str) -> VerificationService: 76 | """ 77 | Init Face Verification Service 78 | :param api_key: 79 | :return: 80 | """ 81 | self.verification = VerificationService(api_key=api_key, 82 | domain=self.domain, 83 | port=self.port, 84 | options=self.options) 85 | return self.verification 86 | 87 | def init_face_detection(self, api_key: str) -> DetectionService: 88 | """ 89 | Init Face Detection Service 90 | :param api_key: 91 | :return: 92 | """ 93 | self.detection = DetectionService(api_key=api_key, 94 | domain=self.domain, 95 | port=self.port, 96 | options=self.options) 97 | return self.detection 98 | -------------------------------------------------------------------------------- /compreface/exceptions/__init__.py: -------------------------------------------------------------------------------- 1 | """ 2 | Copyright(c) 2021 the original author or authors 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | https: // www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 13 | or implied. See the License for the specific language governing 14 | permissions and limitations under the License. 15 | """ 16 | 17 | from .field_exception import IncorrectFieldException 18 | -------------------------------------------------------------------------------- /compreface/exceptions/field_exception.py: -------------------------------------------------------------------------------- 1 | """ 2 | Copyright(c) 2021 the original author or authors 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | https: // www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 13 | or implied. See the License for the specific language governing 14 | permissions and limitations under the License. 15 | """ 16 | 17 | 18 | class IncorrectFieldException(BaseException): 19 | pass 20 | -------------------------------------------------------------------------------- /compreface/service/__init__.py: -------------------------------------------------------------------------------- 1 | """ 2 | Copyright(c) 2021 the original author or authors 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | https: // www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 13 | or implied. See the License for the specific language governing 14 | permissions and limitations under the License. 15 | """ 16 | 17 | from .detection_service import DetectionService 18 | from .verification_service import VerificationService 19 | from .recognition_service import RecognitionService 20 | -------------------------------------------------------------------------------- /compreface/service/detection_service.py: -------------------------------------------------------------------------------- 1 | """ 2 | Copyright(c) 2021 the original author or authors 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | https: // www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 13 | or implied. See the License for the specific language governing 14 | permissions and limitations under the License. 15 | """ 16 | 17 | from compreface.common.typed_dict import AllOptionsDict, ExpandedOptionsDict, pass_dict 18 | from compreface.use_cases.detect_face_from_image import DetectFaceFromImage 19 | from typing import List 20 | 21 | from ..common import Service 22 | 23 | 24 | class DetectionService(Service): 25 | """Detection service""" 26 | 27 | def __init__(self, api_key: str, domain: str, port: str, options: AllOptionsDict = {}): 28 | """Init service with define API Key""" 29 | super().__init__(api_key, options) 30 | self.available_services = [] 31 | self.detect_face_from_image: DetectFaceFromImage = DetectFaceFromImage( 32 | domain=domain, 33 | port=port, 34 | api_key=api_key 35 | ) 36 | 37 | def get_available_functions(self) -> List[str]: 38 | """ 39 | Get List of available functions in service 40 | :return: 41 | """ 42 | return self.available_services 43 | 44 | def detect(self, image_path: str, options: ExpandedOptionsDict = {}) -> dict: 45 | """ 46 | Detect face in image 47 | :param image_path: 48 | :param options: 49 | :return: 50 | """ 51 | request = DetectFaceFromImage.Request( 52 | api_key=self.api_key, 53 | image_path=image_path 54 | ) 55 | return self.detect_face_from_image.execute(request, pass_dict( 56 | self.options, ExpandedOptionsDict) if options == {} else options) 57 | -------------------------------------------------------------------------------- /compreface/service/recognition_service.py: -------------------------------------------------------------------------------- 1 | """ 2 | Copyright(c) 2021 the original author or authors 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | https: // www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 13 | or implied. See the License for the specific language governing 14 | permissions and limitations under the License. 15 | """ 16 | 17 | from compreface.common.typed_dict import AllOptionsDict 18 | from typing import List 19 | 20 | from ..common import Service 21 | from ..collections import FaceCollection, Subjects 22 | from ..use_cases import RecognizeFaceFromImage 23 | 24 | 25 | class RecognitionService(Service): 26 | """Recognition service""" 27 | 28 | def __init__(self, api_key: str, domain: str, port: str, options: AllOptionsDict = {}): 29 | """Init service with define API Key""" 30 | super().__init__(api_key, options) 31 | self.available_services = [] 32 | self.recognize_face_from_images: RecognizeFaceFromImage = RecognizeFaceFromImage( 33 | domain=domain, 34 | port=port, 35 | api_key=api_key 36 | ) 37 | self.face_collection: FaceCollection = FaceCollection( 38 | domain=domain, 39 | port=port, 40 | api_key=api_key, 41 | options=options 42 | ) 43 | self.subjects: Subjects = Subjects( 44 | domain=domain, 45 | port=port, 46 | api_key=api_key, 47 | options=options 48 | ) 49 | 50 | def get_available_functions(self) -> List[str]: 51 | """ 52 | Get List of available functions in service 53 | :return: 54 | """ 55 | return self.available_services 56 | 57 | def recognize(self, image_path: str, options: AllOptionsDict = {}) -> dict: 58 | """ 59 | Recognize image 60 | :param image_path: 61 | :param options: 62 | :return: 63 | """ 64 | request = RecognizeFaceFromImage.Request( 65 | api_key=self.api_key, 66 | image_path=image_path 67 | ) 68 | return self.recognize_face_from_images.execute(request, self.options if options == {} else options) 69 | 70 | def get_face_collection(self) -> FaceCollection: 71 | """ 72 | Get face collection 73 | :return: 74 | """ 75 | return self.face_collection 76 | 77 | def get_subjects(self) -> Subjects: 78 | """ 79 | Get subjects 80 | :return: 81 | """ 82 | return self.subjects 83 | -------------------------------------------------------------------------------- /compreface/service/verification_service.py: -------------------------------------------------------------------------------- 1 | """ 2 | Copyright(c) 2021 the original author or authors 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | https: // www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 13 | or implied. See the License for the specific language governing 14 | permissions and limitations under the License. 15 | """ 16 | 17 | from compreface.use_cases.verifiy_face_from_images import VerifyFaceFromImage 18 | from compreface.common.typed_dict import AllOptionsDict, ExpandedOptionsDict, pass_dict 19 | from compreface.client.verify_face_from_image import VerifyFaceFromImageClient 20 | from typing import List 21 | 22 | from ..common import Service 23 | 24 | 25 | class VerificationService(Service): 26 | """Verification service""" 27 | 28 | def __init__(self, api_key: str, domain: str, port: str, options: AllOptionsDict = {}): 29 | """Init service with define API Key""" 30 | super().__init__(api_key, options) 31 | self.available_services = [] 32 | self.verify_face_from_image: VerifyFaceFromImageClient = VerifyFaceFromImageClient( 33 | domain=domain, 34 | port=port, 35 | api_key=api_key 36 | ) 37 | 38 | def get_available_functions(self) -> List[str]: 39 | """ 40 | Get List of available functions in service 41 | :return: 42 | """ 43 | return self.available_services 44 | 45 | def verify(self, source_image_path: str, target_image_path: str, options: ExpandedOptionsDict = {}) -> dict: 46 | """ 47 | Verify face in images 48 | :param source_image_path: 49 | :param target_image_path: 50 | :param options: 51 | :return: 52 | """ 53 | request = VerifyFaceFromImage.Request(api_key=self.api_key, 54 | source_image_path=source_image_path, 55 | target_image_path=target_image_path) 56 | return self.verify_face_from_image.post( 57 | source_image=request.source_image_path, 58 | target_image=request.target_image_path, 59 | options=pass_dict( 60 | self.options, ExpandedOptionsDict) if options == {} else options 61 | ) 62 | -------------------------------------------------------------------------------- /compreface/use_cases/__init__.py: -------------------------------------------------------------------------------- 1 | """ 2 | Copyright(c) 2021 the original author or authors 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | https: // www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 13 | or implied. See the License for the specific language governing 14 | permissions and limitations under the License. 15 | """ 16 | 17 | from .add_example_of_subject import AddExampleOfSubject 18 | from .delete_all_examples_of_subject_by_name import DeleteAllExamplesOfSubjectByName 19 | from .delete_example_by_id import DeleteExampleById 20 | from .list_of_all_saved_subjects import ListOfAllSavedSubjects 21 | from .recognize_face_from_image import RecognizeFaceFromImage 22 | from .verification_face_from_image import VerificationFaceFromImage 23 | from .detect_face_from_image import DetectFaceFromImage 24 | from .add_subject import AddSubject 25 | from .get_subjects import GetSubjects 26 | from .update_subject import UpdateSubject 27 | from .delete_subject_by_name import DeleteSubjectByName 28 | from .delete_all_subjects import DeleteAllSubjects 29 | -------------------------------------------------------------------------------- /compreface/use_cases/add_example_of_subject.py: -------------------------------------------------------------------------------- 1 | """ 2 | Copyright(c) 2021 the original author or authors 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | https: // www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 13 | or implied. See the License for the specific language governing 14 | permissions and limitations under the License. 15 | """ 16 | 17 | from compreface.common.typed_dict import DetProbOptionsDict 18 | from dataclasses import dataclass 19 | from ..client import AddExampleOfSubjectClient 20 | 21 | 22 | class AddExampleOfSubject: 23 | 24 | @dataclass 25 | class Request: 26 | api_key: str 27 | image_path: str 28 | subject: str 29 | 30 | def __init__(self, domain: str, port: str, api_key: str): 31 | self.add_example_of_subject = AddExampleOfSubjectClient( 32 | api_key=api_key, 33 | domain=domain, 34 | port=port 35 | ) 36 | 37 | def execute(self, request: Request, options: DetProbOptionsDict = {}) -> dict: 38 | result: dict = self.add_example_of_subject.post( 39 | request.image_path, request.subject, options) 40 | return result 41 | -------------------------------------------------------------------------------- /compreface/use_cases/add_subject.py: -------------------------------------------------------------------------------- 1 | """ 2 | Copyright(c) 2021 the original author or authors 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | https: // www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 13 | or implied. See the License for the specific language governing 14 | permissions and limitations under the License. 15 | """ 16 | 17 | from dataclasses import dataclass, asdict 18 | 19 | from compreface.client.subject_client import SubjectClient 20 | 21 | 22 | class AddSubject: 23 | 24 | @dataclass 25 | class Request: 26 | subject: str 27 | 28 | def __init__(self, domain: str, port: str, api_key: str): 29 | self.subject_client = SubjectClient( 30 | api_key=api_key, 31 | domain=domain, 32 | port=port 33 | ) 34 | 35 | def execute(self, request: Request) -> dict: 36 | result: dict = self.subject_client.post(asdict(request)) 37 | return result 38 | -------------------------------------------------------------------------------- /compreface/use_cases/delete_all_examples_of_subject_by_name.py: -------------------------------------------------------------------------------- 1 | """ 2 | Copyright(c) 2021 the original author or authors 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | https: // www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 13 | or implied. See the License for the specific language governing 14 | permissions and limitations under the License. 15 | """ 16 | 17 | from dataclasses import dataclass 18 | from ..client import AddExampleOfSubjectClient 19 | 20 | 21 | class DeleteAllExamplesOfSubjectByName: 22 | 23 | @dataclass 24 | class Request: 25 | api_key: str 26 | subject: str 27 | 28 | def __init__(self, domain: str, port: str, api_key: str): 29 | self.add_example_of_subject = AddExampleOfSubjectClient( 30 | api_key=api_key, 31 | domain=domain, 32 | port=port 33 | ) 34 | 35 | def execute(self, request: Request) -> dict: 36 | result: dict = self.add_example_of_subject.delete(request.subject) 37 | return result 38 | -------------------------------------------------------------------------------- /compreface/use_cases/delete_all_subjects.py: -------------------------------------------------------------------------------- 1 | """ 2 | Copyright(c) 2021 the original author or authors 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | https: // www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 13 | or implied. See the License for the specific language governing 14 | permissions and limitations under the License. 15 | """ 16 | 17 | from dataclasses import dataclass 18 | 19 | from compreface.client.subject_client import SubjectClient 20 | 21 | 22 | class DeleteAllSubjects: 23 | 24 | @dataclass 25 | class Request: 26 | pass 27 | 28 | def __init__(self, domain: str, port: str, api_key: str): 29 | self.add_subject = SubjectClient( 30 | api_key=api_key, 31 | domain=domain, 32 | port=port 33 | ) 34 | 35 | def execute(self) -> dict: 36 | result: dict = self.add_subject.delete() 37 | return result 38 | -------------------------------------------------------------------------------- /compreface/use_cases/delete_example_by_id.py: -------------------------------------------------------------------------------- 1 | """ 2 | Copyright(c) 2021 the original author or authors 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | https: // www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 13 | or implied. See the License for the specific language governing 14 | permissions and limitations under the License. 15 | """ 16 | 17 | from dataclasses import dataclass 18 | from ..client import DeleteExampleByIdClient 19 | 20 | 21 | class DeleteExampleById: 22 | 23 | @dataclass 24 | class Request: 25 | api_key: str 26 | image_id: str 27 | 28 | def __init__(self, domain: str, port: str, api_key: str): 29 | self.delete_example_by_id = DeleteExampleByIdClient( 30 | api_key=api_key, 31 | domain=domain, 32 | port=port 33 | ) 34 | 35 | def execute(self, request: Request): 36 | result: dict = self.delete_example_by_id.delete(request.image_id) 37 | return result 38 | -------------------------------------------------------------------------------- /compreface/use_cases/delete_subject_by_name.py: -------------------------------------------------------------------------------- 1 | """ 2 | Copyright(c) 2021 the original author or authors 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | https: // www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 13 | or implied. See the License for the specific language governing 14 | permissions and limitations under the License. 15 | """ 16 | 17 | from dataclasses import dataclass 18 | 19 | from compreface.client.subject_client import SubjectClient 20 | 21 | 22 | class DeleteSubjectByName: 23 | 24 | @dataclass 25 | class Request: 26 | subject: str 27 | 28 | def __init__(self, domain: str, port: str, api_key: str): 29 | self.subject_client = SubjectClient( 30 | api_key=api_key, 31 | domain=domain, 32 | port=port 33 | ) 34 | 35 | def execute(self, request: Request) -> dict: 36 | result: dict = self.subject_client.delete(request.subject) 37 | return result 38 | -------------------------------------------------------------------------------- /compreface/use_cases/detect_face_from_image.py: -------------------------------------------------------------------------------- 1 | """ 2 | Copyright(c) 2021 the original author or authors 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | https: // www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 13 | or implied. See the License for the specific language governing 14 | permissions and limitations under the License. 15 | """ 16 | 17 | from compreface.common.typed_dict import ExpandedOptionsDict 18 | from dataclasses import dataclass 19 | from ..client import DetectFaceFromImageClient 20 | 21 | 22 | class DetectFaceFromImage: 23 | 24 | @dataclass 25 | class Request: 26 | api_key: str 27 | image_path: str 28 | 29 | def __init__(self, domain: str, port: str, api_key: str): 30 | self.detect_face_from_image = DetectFaceFromImageClient( 31 | api_key=api_key, 32 | domain=domain, 33 | port=port 34 | ) 35 | 36 | def execute(self, request: Request, options: ExpandedOptionsDict = {}) -> dict: 37 | result: dict = self.detect_face_from_image.post( 38 | request.image_path, options) 39 | return result 40 | -------------------------------------------------------------------------------- /compreface/use_cases/get_subjects.py: -------------------------------------------------------------------------------- 1 | """ 2 | Copyright(c) 2021 the original author or authors 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | https: // www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 13 | or implied. See the License for the specific language governing 14 | permissions and limitations under the License. 15 | """ 16 | 17 | from dataclasses import dataclass 18 | 19 | from compreface.client.subject_client import SubjectClient 20 | 21 | 22 | class GetSubjects: 23 | 24 | @dataclass 25 | class Request: 26 | pass 27 | 28 | def __init__(self, domain: str, port: str, api_key: str): 29 | self.subject_client = SubjectClient( 30 | api_key=api_key, 31 | domain=domain, 32 | port=port 33 | ) 34 | 35 | def execute(self) -> dict: 36 | result: dict = self.subject_client.get() 37 | return result 38 | -------------------------------------------------------------------------------- /compreface/use_cases/list_of_all_saved_subjects.py: -------------------------------------------------------------------------------- 1 | """ 2 | Copyright(c) 2021 the original author or authors 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | https: // www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 13 | or implied. See the License for the specific language governing 14 | permissions and limitations under the License. 15 | """ 16 | 17 | from dataclasses import dataclass 18 | from ..client import AddExampleOfSubjectClient 19 | 20 | 21 | class ListOfAllSavedSubjects: 22 | 23 | @dataclass 24 | class Request: 25 | pass 26 | 27 | def __init__(self, domain: str, port: str, api_key: str): 28 | self.add_example_of_subject = AddExampleOfSubjectClient( 29 | api_key=api_key, 30 | domain=domain, 31 | port=port 32 | ) 33 | 34 | def execute(self) -> dict: 35 | result: dict = self.add_example_of_subject.get() 36 | return result 37 | -------------------------------------------------------------------------------- /compreface/use_cases/recognize_face_from_image.py: -------------------------------------------------------------------------------- 1 | """ 2 | Copyright(c) 2021 the original author or authors 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | https: // www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 13 | or implied. See the License for the specific language governing 14 | permissions and limitations under the License. 15 | """ 16 | 17 | from compreface.common.typed_dict import AllOptionsDict 18 | from dataclasses import dataclass 19 | from ..client import RecognizeFaceFromImageClient 20 | 21 | 22 | class RecognizeFaceFromImage: 23 | 24 | @dataclass 25 | class Request: 26 | api_key: str 27 | image_path: str 28 | 29 | def __init__(self, domain: str, port: str, api_key: str): 30 | self.recognize_face_from_image = RecognizeFaceFromImageClient( 31 | api_key=api_key, 32 | domain=domain, 33 | port=port 34 | ) 35 | 36 | def execute(self, request: Request, options: AllOptionsDict = {}) -> dict: 37 | result: dict = self.recognize_face_from_image.post( 38 | request.image_path, options) 39 | return result 40 | -------------------------------------------------------------------------------- /compreface/use_cases/update_subject.py: -------------------------------------------------------------------------------- 1 | """ 2 | Copyright(c) 2021 the original author or authors 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | https: // www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 13 | or implied. See the License for the specific language governing 14 | permissions and limitations under the License. 15 | """ 16 | 17 | from dataclasses import dataclass, asdict 18 | 19 | from compreface.client.subject_client import SubjectClient 20 | 21 | 22 | class UpdateSubject: 23 | 24 | @dataclass 25 | class Request: 26 | subject: str 27 | api_endpoint: str 28 | 29 | def __init__(self, domain: str, port: str, api_key: str): 30 | self.subject_client = SubjectClient( 31 | api_key=api_key, 32 | domain=domain, 33 | port=port 34 | ) 35 | 36 | def execute(self, request: Request) -> dict: 37 | result: dict = self.subject_client.put(asdict(request)) 38 | return result 39 | -------------------------------------------------------------------------------- /compreface/use_cases/verification_face_from_image.py: -------------------------------------------------------------------------------- 1 | """ 2 | Copyright(c) 2021 the original author or authors 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | https: // www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 13 | or implied. See the License for the specific language governing 14 | permissions and limitations under the License. 15 | """ 16 | 17 | from compreface.common.typed_dict import ExpandedOptionsDict 18 | from dataclasses import dataclass 19 | from ..client import VerificationFaceFromImageClient 20 | 21 | 22 | class VerificationFaceFromImage: 23 | 24 | @dataclass 25 | class Request: 26 | api_key: str 27 | image_path: str 28 | image_id: str 29 | 30 | def __init__(self, domain: str, port: str, api_key: str): 31 | self.verify_face_from_image = VerificationFaceFromImageClient( 32 | api_key=api_key, 33 | domain=domain, 34 | port=port 35 | ) 36 | 37 | def execute(self, request: Request, options: ExpandedOptionsDict = {}): 38 | result: dict = self.verify_face_from_image.post(request.image_path, 39 | request.image_id, options) 40 | return result 41 | -------------------------------------------------------------------------------- /compreface/use_cases/verifiy_face_from_images.py: -------------------------------------------------------------------------------- 1 | """ 2 | Copyright(c) 2021 the original author or authors 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | https: // www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 13 | or implied. See the License for the specific language governing 14 | permissions and limitations under the License. 15 | """ 16 | 17 | from compreface.client.verify_face_from_image import VerifyFaceFromImageClient 18 | from compreface.common.typed_dict import ExpandedOptionsDict 19 | from dataclasses import dataclass 20 | 21 | 22 | class VerifyFaceFromImage: 23 | 24 | @dataclass 25 | class Request: 26 | api_key: str 27 | source_image_path: str 28 | target_image_path: str 29 | 30 | def __init__(self, domain: str, port: str, api_key: str): 31 | self.verify_face_from_image = VerifyFaceFromImageClient( 32 | api_key=api_key, 33 | domain=domain, 34 | port=port 35 | ) 36 | 37 | def execute(self, request: Request, options: ExpandedOptionsDict = {}): 38 | result: dict = self.verify_face_from_image.post(request.source_image_path, 39 | request.target_image_path, 40 | options) 41 | return result 42 | -------------------------------------------------------------------------------- /examples/add_example_of_a_subject.py: -------------------------------------------------------------------------------- 1 | """ 2 | Copyright(c) 2021 the original author or authors 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | https: // www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 13 | or implied. See the License for the specific language governing 14 | permissions and limitations under the License. 15 | """ 16 | 17 | from compreface.common.typed_dict import ExpandedOptionsDict 18 | from compreface import CompreFace 19 | from compreface.service import RecognitionService 20 | from compreface.collections import FaceCollection 21 | 22 | DOMAIN: str = 'http://localhost' 23 | PORT: str = '8000' 24 | RECOGNITION_API_KEY: str = '00000000-0000-0000-0000-000000000002' 25 | 26 | compre_face: CompreFace = CompreFace(DOMAIN, PORT, { 27 | "det_prob_threshold": 0.8 28 | }) 29 | 30 | recognition: RecognitionService = compre_face.init_face_recognition( 31 | RECOGNITION_API_KEY) 32 | 33 | face_collection: FaceCollection = recognition.get_face_collection() 34 | 35 | # Image from local path. 36 | image: str = 'common/jonathan-petit-unsplash.jpg' 37 | subject: str = 'Jonathan Petit' 38 | 39 | print(face_collection.add(image, subject)) 40 | -------------------------------------------------------------------------------- /examples/add_subject.py: -------------------------------------------------------------------------------- 1 | """ 2 | Copyright(c) 2021 the original author or authors 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | https: // www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 13 | or implied. See the License for the specific language governing 14 | permissions and limitations under the License. 15 | """ 16 | 17 | from compreface import CompreFace 18 | from compreface.service import RecognitionService 19 | from compreface.collections import Subjects 20 | 21 | DOMAIN: str = 'http://localhost' 22 | PORT: str = '8000' 23 | RECOGNITION_API_KEY: str = '00000000-0000-0000-0000-000000000002' 24 | 25 | compre_face: CompreFace = CompreFace(DOMAIN, PORT) 26 | 27 | recognition: RecognitionService = compre_face.init_face_recognition(RECOGNITION_API_KEY) 28 | 29 | subjects: Subjects = recognition.get_subjects() 30 | 31 | subject: str = 'Test Subject' 32 | 33 | print(subjects.add(subject)) 34 | -------------------------------------------------------------------------------- /examples/common/jonathan-petit-unsplash.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/exadel-inc/compreface-python-sdk/9f6e2c6fdd7b477697c83ed2210aab898d8b0ecf/examples/common/jonathan-petit-unsplash.jpg -------------------------------------------------------------------------------- /examples/delete_all_examples_of_subject.py: -------------------------------------------------------------------------------- 1 | """ 2 | Copyright(c) 2021 the original author or authors 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | https: // www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 13 | or implied. See the License for the specific language governing 14 | permissions and limitations under the License. 15 | """ 16 | 17 | from compreface import CompreFace 18 | from compreface.service import RecognitionService 19 | from compreface.collections import FaceCollection 20 | 21 | DOMAIN: str = 'http://localhost' 22 | PORT: str = '8000' 23 | RECOGNITION_API_KEY: str = '00000000-0000-0000-0000-000000000002' 24 | 25 | 26 | compre_face: CompreFace = CompreFace(DOMAIN, PORT) 27 | 28 | recognition: RecognitionService = compre_face.init_face_recognition( 29 | RECOGNITION_API_KEY) 30 | subject: str = 'Jonathan Petit' 31 | 32 | face_collection: FaceCollection = recognition.get_face_collection() 33 | 34 | print(face_collection.delete_all(subject)) 35 | -------------------------------------------------------------------------------- /examples/delete_all_subjects.py: -------------------------------------------------------------------------------- 1 | """ 2 | Copyright(c) 2021 the original author or authors 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | https: // www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 13 | or implied. See the License for the specific language governing 14 | permissions and limitations under the License. 15 | """ 16 | 17 | from compreface import CompreFace 18 | from compreface.service import RecognitionService 19 | from compreface.collections import Subjects 20 | 21 | DOMAIN: str = 'http://localhost' 22 | PORT: str = '8000' 23 | RECOGNITION_API_KEY: str = '00000000-0000-0000-0000-000000000002' 24 | 25 | compre_face: CompreFace = CompreFace(DOMAIN, PORT) 26 | 27 | recognition: RecognitionService = compre_face.init_face_recognition(RECOGNITION_API_KEY) 28 | 29 | subjects: Subjects = recognition.get_subjects() 30 | 31 | print(subjects.delete_all()) 32 | -------------------------------------------------------------------------------- /examples/delete_example_by_id.py: -------------------------------------------------------------------------------- 1 | """ 2 | Copyright(c) 2021 the original author or authors 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | https: // www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 13 | or implied. See the License for the specific language governing 14 | permissions and limitations under the License. 15 | """ 16 | 17 | from compreface import CompreFace 18 | from compreface.service import RecognitionService 19 | from compreface.collections import FaceCollection 20 | 21 | DOMAIN: str = 'http://localhost' 22 | PORT: str = '8000' 23 | RECOGNITION_API_KEY: str = '00000000-0000-0000-0000-000000000002' 24 | 25 | 26 | compre_face: CompreFace = CompreFace(DOMAIN, PORT) 27 | 28 | recognition: RecognitionService = compre_face.init_face_recognition( 29 | RECOGNITION_API_KEY) 30 | 31 | face_collection: FaceCollection = recognition.get_face_collection() 32 | 33 | faces: list = face_collection.list().get('faces') 34 | 35 | if(len(faces) != 0): 36 | last_face: dict = faces[len(faces) - 1] 37 | print(face_collection.delete(last_face.get('image_id'))) 38 | else: 39 | print('No subject found') 40 | -------------------------------------------------------------------------------- /examples/delete_subject_by_name.py: -------------------------------------------------------------------------------- 1 | """ 2 | Copyright(c) 2021 the original author or authors 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | https: // www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 13 | or implied. See the License for the specific language governing 14 | permissions and limitations under the License. 15 | """ 16 | 17 | from compreface import CompreFace 18 | from compreface.service import RecognitionService 19 | from compreface.collections import Subjects 20 | 21 | DOMAIN: str = 'http://localhost' 22 | PORT: str = '8000' 23 | RECOGNITION_API_KEY: str = '00000000-0000-0000-0000-000000000002' 24 | 25 | compre_face: CompreFace = CompreFace(DOMAIN, PORT) 26 | 27 | recognition: RecognitionService = compre_face.init_face_recognition(RECOGNITION_API_KEY) 28 | 29 | subjects: Subjects = recognition.get_subjects() 30 | 31 | subject: str = 'Test Subject' 32 | 33 | print(subjects.delete(subject)) 34 | -------------------------------------------------------------------------------- /examples/detect_face_from_image.py: -------------------------------------------------------------------------------- 1 | """ 2 | Copyright(c) 2021 the original author or authors 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | https: // www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 13 | or implied. See the License for the specific language governing 14 | permissions and limitations under the License. 15 | """ 16 | 17 | from compreface import CompreFace 18 | from compreface.service import DetectionService 19 | 20 | 21 | DOMAIN: str = 'http://localhost' 22 | PORT: str = '8000' 23 | DETECTION_API_KEY: str = 'f4bdcf1f-1ef9-442f-863d-7bcd170723db' 24 | 25 | compre_face: CompreFace = CompreFace(DOMAIN, PORT, { 26 | "limit": 0, 27 | "det_prob_threshold": 0.8, 28 | "face_plugins": "age,gender", 29 | "status": "true" 30 | }) 31 | 32 | detection: DetectionService = compre_face.init_face_detection( 33 | DETECTION_API_KEY) 34 | 35 | image_path: str = 'common/jonathan-petit-unsplash.jpg' 36 | 37 | print(detection.detect(image_path)) 38 | -------------------------------------------------------------------------------- /examples/get_list_of_all_subjects.py: -------------------------------------------------------------------------------- 1 | """ 2 | Copyright(c) 2021 the original author or authors 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | https: // www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 13 | or implied. See the License for the specific language governing 14 | permissions and limitations under the License. 15 | """ 16 | 17 | from compreface import CompreFace 18 | from compreface.service import RecognitionService 19 | from compreface.collections import Subjects 20 | 21 | DOMAIN: str = 'http://localhost' 22 | PORT: str = '8000' 23 | RECOGNITION_API_KEY: str = '00000000-0000-0000-0000-000000000002' 24 | 25 | 26 | compre_face: CompreFace = CompreFace(DOMAIN, PORT) 27 | 28 | recognition: RecognitionService = compre_face.init_face_recognition( 29 | RECOGNITION_API_KEY) 30 | 31 | subjects: Subjects = recognition.get_subjects() 32 | 33 | print(subjects.list()) 34 | -------------------------------------------------------------------------------- /examples/recognize_face_from_image.py: -------------------------------------------------------------------------------- 1 | """ 2 | Copyright(c) 2021 the original author or authors 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | https: // www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 13 | or implied. See the License for the specific language governing 14 | permissions and limitations under the License. 15 | """ 16 | 17 | from compreface import CompreFace 18 | from compreface.service import RecognitionService 19 | 20 | DOMAIN: str = 'http://localhost' 21 | PORT: str = '8000' 22 | RECOGNITION_API_KEY: str = '00000000-0000-0000-0000-000000000002' 23 | 24 | 25 | compre_face: CompreFace = CompreFace(DOMAIN, PORT, { 26 | "limit": 0, 27 | "det_prob_threshold": 0.8, 28 | "prediction_count": 1, 29 | "status": "true" 30 | }) 31 | 32 | recognition: RecognitionService = compre_face.init_face_recognition( 33 | RECOGNITION_API_KEY) 34 | 35 | image_path: str = 'common/jonathan-petit-unsplash.jpg' 36 | 37 | print(recognition.recognize(image_path)) 38 | -------------------------------------------------------------------------------- /examples/update_existing_subject.py: -------------------------------------------------------------------------------- 1 | """ 2 | Copyright(c) 2021 the original author or authors 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | https: // www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 13 | or implied. See the License for the specific language governing 14 | permissions and limitations under the License. 15 | """ 16 | 17 | from compreface import CompreFace 18 | from compreface.service import RecognitionService 19 | from compreface.collections import Subjects 20 | 21 | DOMAIN: str = 'http://localhost' 22 | PORT: str = '8000' 23 | RECOGNITION_API_KEY: str = '00000000-0000-0000-0000-000000000002' 24 | 25 | compre_face: CompreFace = CompreFace(DOMAIN, PORT) 26 | 27 | recognition: RecognitionService = compre_face.init_face_recognition(RECOGNITION_API_KEY) 28 | 29 | subjects: Subjects = recognition.get_subjects() 30 | 31 | subject: str = 'Test Subject' 32 | new_name: str = 'Updated Subject' 33 | 34 | print(subjects.update(subject, new_name)) 35 | -------------------------------------------------------------------------------- /examples/verification_face_from_image.py: -------------------------------------------------------------------------------- 1 | """ 2 | Copyright(c) 2021 the original author or authors 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | https: // www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 13 | or implied. See the License for the specific language governing 14 | permissions and limitations under the License. 15 | """ 16 | 17 | from compreface.collections.face_collections import FaceCollection 18 | from compreface import CompreFace 19 | from compreface.service import RecognitionService 20 | 21 | DOMAIN: str = 'http://localhost' 22 | PORT: str = '8000' 23 | RECOGNITION_API_KEY: str = '00000000-0000-0000-0000-000000000002' 24 | 25 | compre_face: CompreFace = CompreFace(DOMAIN, PORT, { 26 | "limit": 0, 27 | "det_prob_threshold": 0.8, 28 | "status": "true" 29 | }) 30 | 31 | recognition: RecognitionService = compre_face.init_face_recognition( 32 | RECOGNITION_API_KEY) 33 | 34 | image_path: str = 'common/jonathan-petit-unsplash.jpg' 35 | 36 | face_collection: FaceCollection = recognition.get_face_collection() 37 | 38 | print(face_collection.list()) 39 | 40 | face: dict = next(item for item in face_collection.list().get('faces') if item['subject'] == 41 | 'Jonathan Petit') 42 | 43 | image_id = face.get('image_id') 44 | 45 | print(face_collection.verify(image_path, image_id)) 46 | -------------------------------------------------------------------------------- /examples/verify_face_from_image.py: -------------------------------------------------------------------------------- 1 | """ 2 | Copyright(c) 2021 the original author or authors 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | https: // www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 13 | or implied. See the License for the specific language governing 14 | permissions and limitations under the License. 15 | """ 16 | 17 | from compreface import CompreFace 18 | from compreface.service import VerificationService 19 | 20 | DOMAIN: str = 'http://localhost' 21 | PORT: str = '8000' 22 | VERIFICATION_API_KEY: str = '5c765423-4192-4fe8-9c60-092f495a332a' 23 | 24 | 25 | compre_face: CompreFace = CompreFace(DOMAIN, PORT, { 26 | "limit": 0, 27 | "det_prob_threshold": 0.8, 28 | "face_plugins": "age,gender", 29 | "status": "true" 30 | }) 31 | 32 | verify: VerificationService = compre_face.init_face_verification( 33 | VERIFICATION_API_KEY) 34 | 35 | image_path: str = 'common/jonathan-petit-unsplash.jpg' 36 | 37 | print(verify.verify(image_path, image_path)) 38 | -------------------------------------------------------------------------------- /setup.cfg: -------------------------------------------------------------------------------- 1 | [bdist_wheel] 2 | universal = 1 3 | 4 | [metadata] 5 | description-file = README.md -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | """ 2 | Copyright(c) 2021 the original author or authors 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | https: // www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 13 | or implied. See the License for the specific language governing 14 | permissions and limitations under the License. 15 | """ 16 | 17 | 18 | from setuptools import find_packages, setup 19 | 20 | with open("README.md", "r") as fh: 21 | long_description = fh.read() 22 | 23 | setup( 24 | name='compreface-sdk', 25 | packages=find_packages(exclude=("tests",)), 26 | version='0.6.0', 27 | license='Apache License 2.0', 28 | description='CompreFace Python SDK makes face recognition into your application even easier.', 29 | long_description=long_description, 30 | long_description_content_type="text/markdown", 31 | author='Artsiom Liubymov aliubymov@exadel.com, Artsiom Khadzkou akhadzkou@exadel.com, Aliaksei Tauhen atauhen@exadel.com', 32 | author_email='aliubymov@exadel.com, akhadzkou@exadel.com, atauhen@exadel.com', 33 | url='https://exadel.com/solutions/compreface/', 34 | download_url='https://github.com/exadel-inc/compreface-python-sdk/archive/refs/tags/0.6.0.tar.gz', 35 | keywords=[ 36 | "CompreFace", 37 | "Face Recognition", 38 | "Face Detection", 39 | "Face Verification", 40 | "Face Identification", 41 | "Computer Vision", 42 | "SDK" 43 | ], 44 | install_requires=[ 45 | 'requests-toolbelt==0.9.1' 46 | ], 47 | classifiers=[ 48 | 'Development Status :: 5 - Production/Stable', 49 | 'Intended Audience :: Developers', 50 | 'Intended Audience :: Science/Research', 51 | 'Intended Audience :: Information Technology', 52 | 'Topic :: Software Development', 53 | 'Topic :: Software Development :: Build Tools', 54 | 'Topic :: Software Development :: Libraries', 55 | 'Topic :: Scientific/Engineering :: Image Recognition', 56 | 'Topic :: Scientific/Engineering :: Artificial Intelligence', 57 | 'License :: OSI Approved :: Apache Software License', 58 | 'Programming Language :: Python :: 3', 59 | 'Programming Language :: Python :: 3.7' 60 | ], 61 | ) 62 | -------------------------------------------------------------------------------- /tests/__init__.py: -------------------------------------------------------------------------------- 1 | """ 2 | Copyright(c) 2021 the original author or authors 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | https: // www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 13 | or implied. See the License for the specific language governing 14 | permissions and limitations under the License. 15 | """ 16 | -------------------------------------------------------------------------------- /tests/client/__init__.py: -------------------------------------------------------------------------------- 1 | 2 | """ 3 | Copyright(c) 2021 the original author or authors 4 | 5 | Licensed under the Apache License, Version 2.0 (the "License"); 6 | you may not use this file except in compliance with the License. 7 | You may obtain a copy of the License at 8 | 9 | https: // www.apache.org/licenses/LICENSE-2.0 10 | 11 | Unless required by applicable law or agreed to in writing, software 12 | distributed under the License is distributed on an "AS IS" BASIS, 13 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 14 | or implied. See the License for the specific language governing 15 | permissions and limitations under the License. 16 | """ 17 | -------------------------------------------------------------------------------- /tests/client/const_config.py: -------------------------------------------------------------------------------- 1 | """ 2 | Copyright(c) 2021 the original author or authors 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | https: // www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 13 | or implied. See the License for the specific language governing 14 | permissions and limitations under the License. 15 | """ 16 | 17 | from compreface.config.api_list import DETECTION_API 18 | 19 | 20 | DOMAIN: str = 'http://localhost' 21 | PORT: str = '8000' 22 | RECOGNIZE_API_KEY: str = '9916f5d1-216f-4049-9e06-51c140bfa898' 23 | FILE_PATH: str = "tests/common/jonathan-petit-unsplash.jpg" 24 | IMAGE_ID: str = "image-id" 25 | DETECTION_API_KEY: str = 'a482a613-3118-4554-a295-153bd6e8ac65' 26 | VERIFICATION_API_KEY: str = '3c6171a4-e115-41f0-afda-4032bda4bfe9' 27 | -------------------------------------------------------------------------------- /tests/client/test_add_example_of_subject_client.py: -------------------------------------------------------------------------------- 1 | 2 | """ 3 | Copyright(c) 2021 the original author or authors 4 | 5 | Licensed under the Apache License, Version 2.0 (the "License"); 6 | you may not use this file except in compliance with the License. 7 | You may obtain a copy of the License at 8 | 9 | https: // www.apache.org/licenses/LICENSE-2.0 10 | 11 | Unless required by applicable law or agreed to in writing, software 12 | distributed under the License is distributed on an "AS IS" BASIS, 13 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 14 | or implied. See the License for the specific language governing 15 | permissions and limitations under the License. 16 | """ 17 | 18 | import pytest 19 | import os 20 | import httpretty 21 | import requests 22 | from requests_toolbelt.multipart.encoder import MultipartEncoder 23 | from compreface.client import AddExampleOfSubjectClient 24 | from compreface.config.api_list import RECOGNIZE_CRUD_API 25 | from tests.client.const_config import DOMAIN, PORT, RECOGNIZE_API_KEY, FILE_PATH 26 | """ 27 | Server configuration 28 | """ 29 | url: str = DOMAIN + ":" + PORT + RECOGNIZE_CRUD_API 30 | 31 | 32 | @httpretty.activate(verbose=True, allow_net_connect=False) 33 | def test_get(): 34 | httpretty.register_uri( 35 | httpretty.GET, 36 | url, 37 | headers={'x-api-key': RECOGNIZE_API_KEY}, 38 | body='{"faces": [{"image_id": "image_id_subject", "subject": "Subject"}]}' 39 | ) 40 | test_subject: AddExampleOfSubjectClient = AddExampleOfSubjectClient( 41 | RECOGNIZE_API_KEY, DOMAIN, PORT) 42 | response: dict = requests.get( 43 | url=url, headers={'x-api-key': RECOGNIZE_API_KEY}).json() 44 | test_response: dict = test_subject.get() 45 | assert response == test_response 46 | 47 | 48 | @httpretty.activate(verbose=True, allow_net_connect=False) 49 | def test_delete(): 50 | httpretty.register_uri( 51 | httpretty.DELETE, 52 | url + '?subject=Subject', 53 | headers={'x-api-key': RECOGNIZE_API_KEY}, 54 | body='{"faces": [{"image_id": "image_id_subject", "subject": "Subject"}]}' 55 | ) 56 | response: dict = requests.delete( 57 | url=url, headers={'x-api-key': RECOGNIZE_API_KEY}).json() 58 | 59 | test_subject: AddExampleOfSubjectClient = AddExampleOfSubjectClient( 60 | RECOGNIZE_API_KEY, DOMAIN, PORT) 61 | test_response: dict = test_subject.delete("Subject") 62 | assert response == test_response 63 | 64 | 65 | @httpretty.activate(verbose=True, allow_net_connect=False) 66 | def test_post(): 67 | 68 | httpretty.register_uri( 69 | httpretty.POST, 70 | url, 71 | headers={'x-api-key': RECOGNIZE_API_KEY, 72 | 'Content-Type': 'multipart/form-data'}, 73 | body='{"image_id": "image_id_subject", "subject": "Subject"}' 74 | ) 75 | 76 | name_img: str = os.path.basename(FILE_PATH) 77 | m: MultipartEncoder = MultipartEncoder( 78 | fields={'file': (name_img, open(FILE_PATH, 'rb'))} 79 | ) 80 | response: dict = requests.post( 81 | url=url, data=m, headers={'x-api-key': RECOGNIZE_API_KEY, 82 | 'Content-Type': 'multipart/form-data'}).json() 83 | 84 | test_subject: AddExampleOfSubjectClient = AddExampleOfSubjectClient( 85 | RECOGNIZE_API_KEY, DOMAIN, PORT) 86 | test_response: dict = test_subject.post( 87 | FILE_PATH, "Subject") 88 | assert response == test_response 89 | 90 | 91 | @httpretty.activate(verbose=True, allow_net_connect=False) 92 | def test_post_incorrect_response(): 93 | httpretty.register_uri( 94 | httpretty.POST, 95 | url, 96 | headers={'x-api-key': RECOGNIZE_API_KEY, 97 | 'Content-Type': 'multipart/form-data'}, 98 | body='{"image_id": "image_id_subject", "subject": "Subject"}' 99 | ) 100 | 101 | name_img: str = os.path.basename(FILE_PATH) 102 | m: MultipartEncoder = MultipartEncoder( 103 | fields={'file': (name_img, open(FILE_PATH, 'rb'))} 104 | ) 105 | response: dict = requests.post( 106 | url=url, data=m, headers={'x-api-key': RECOGNIZE_API_KEY, 107 | 'Content-Type': 'multipart/form-data'}).json() 108 | 109 | httpretty.register_uri( 110 | httpretty.POST, 111 | url, 112 | headers={'x-api-key': RECOGNIZE_API_KEY, 113 | 'Content-Type': 'multipart/form-data'}, 114 | body='{"image_id": "image_id_subjectssss", "subject": "Subjectss"}' 115 | ) 116 | test_subject: AddExampleOfSubjectClient = AddExampleOfSubjectClient( 117 | RECOGNIZE_API_KEY, DOMAIN, PORT) 118 | test_response: dict = test_subject.post( 119 | FILE_PATH, "Subject") 120 | assert response != test_response 121 | 122 | 123 | @httpretty.activate(verbose=True, allow_net_connect=False) 124 | def test_get_with_empty_list(): 125 | httpretty.register_uri( 126 | httpretty.GET, 127 | url, 128 | headers={'x-api-key': RECOGNIZE_API_KEY}, 129 | body='{"faces": [{"image_id": "image_id_subject", "subject": "Subject"}]}' 130 | ) 131 | test_subject: AddExampleOfSubjectClient = AddExampleOfSubjectClient( 132 | RECOGNIZE_API_KEY, DOMAIN, PORT) 133 | response: dict = requests.get( 134 | url=url, headers={'x-api-key': RECOGNIZE_API_KEY}).json() 135 | httpretty.register_uri( 136 | httpretty.GET, 137 | url, 138 | headers={'x-api-key': RECOGNIZE_API_KEY}, 139 | body='{"faces": []}' 140 | ) 141 | test_response: dict = test_subject.get() 142 | assert response != test_response 143 | 144 | 145 | @httpretty.activate(verbose=True, allow_net_connect=False) 146 | def test_delete_incorrect_response(): 147 | httpretty.register_uri( 148 | httpretty.DELETE, 149 | url + '?subject=Subject', 150 | headers={'x-api-key': RECOGNIZE_API_KEY}, 151 | body='{"faces": [{"image_id": "image_id_subject", "subject": "Subject"}]}' 152 | ) 153 | response: dict = requests.delete( 154 | url=url, headers={'x-api-key': RECOGNIZE_API_KEY}).json() 155 | 156 | httpretty.register_uri( 157 | httpretty.DELETE, 158 | url + '?subject=Subject', 159 | headers={'x-api-key': RECOGNIZE_API_KEY}, 160 | body='{"faces": []}' 161 | ) 162 | test_subject: AddExampleOfSubjectClient = AddExampleOfSubjectClient( 163 | RECOGNIZE_API_KEY, DOMAIN, PORT) 164 | test_response: dict = test_subject.delete("Subject") 165 | assert response != test_response 166 | -------------------------------------------------------------------------------- /tests/client/test_delete_example_by_id_client.py: -------------------------------------------------------------------------------- 1 | """ 2 | Copyright(c) 2021 the original author or authors 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | https: // www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 13 | or implied. See the License for the specific language governing 14 | permissions and limitations under the License. 15 | """ 16 | 17 | import pytest 18 | import httpretty 19 | import requests 20 | from compreface.client.delete_example_by_id import DeleteExampleByIdClient 21 | from compreface.config.api_list import RECOGNIZE_CRUD_API 22 | from tests.client.const_config import DOMAIN, PORT, RECOGNIZE_API_KEY, IMAGE_ID 23 | 24 | url: str = DOMAIN + ":" + PORT + RECOGNIZE_CRUD_API + '/' + IMAGE_ID 25 | 26 | 27 | @httpretty.activate(verbose=True, allow_net_connect=False) 28 | def test_delete(): 29 | httpretty.register_uri( 30 | httpretty.DELETE, 31 | url, 32 | headers={'x-api-key': RECOGNIZE_API_KEY}, 33 | body='{"image_id": "image_id", "subject": "Donatello"}' 34 | ) 35 | response: dict = requests.delete( 36 | url=url, headers={'x-api-key': RECOGNIZE_API_KEY}).json() 37 | 38 | test_subject: DeleteExampleByIdClient = DeleteExampleByIdClient( 39 | RECOGNIZE_API_KEY, DOMAIN, PORT) 40 | test_response: dict = test_subject.delete(IMAGE_ID) 41 | assert response == test_response 42 | 43 | 44 | @httpretty.activate(verbose=True, allow_net_connect=False) 45 | def test_delete_other_response(): 46 | httpretty.register_uri( 47 | httpretty.DELETE, 48 | url, 49 | headers={'x-api-key': RECOGNIZE_API_KEY}, 50 | body='{"image_id": "image_id", "subject": "Donatello"}' 51 | ) 52 | response: dict = requests.delete( 53 | url=url, headers={'x-api-key': RECOGNIZE_API_KEY}).json() 54 | 55 | test_subject: DeleteExampleByIdClient = DeleteExampleByIdClient( 56 | RECOGNIZE_API_KEY, DOMAIN, PORT) 57 | httpretty.register_uri( 58 | httpretty.DELETE, 59 | url, 60 | headers={'x-api-key': RECOGNIZE_API_KEY}, 61 | body='{}' 62 | ) 63 | test_response: dict = test_subject.delete(IMAGE_ID) 64 | assert response != test_response 65 | -------------------------------------------------------------------------------- /tests/client/test_detect_face_from_image.py: -------------------------------------------------------------------------------- 1 | """ 2 | Copyright(c) 2021 the original author or authors 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | https: // www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 13 | or implied. See the License for the specific language governing 14 | permissions and limitations under the License. 15 | """ 16 | 17 | import os 18 | import httpretty 19 | import requests 20 | from compreface.config.api_list import DETECTION_API 21 | from requests_toolbelt.multipart.encoder import MultipartEncoder 22 | from compreface.client.detect_face_from_image import DetectFaceFromImageClient 23 | from tests.client.const_config import DOMAIN, PORT, DETECTION_API_KEY, FILE_PATH, IMAGE_ID 24 | 25 | url: str = DOMAIN + ":" + PORT + DETECTION_API 26 | 27 | 28 | @httpretty.activate(verbose=True, allow_net_connect=False) 29 | def test_post(): 30 | httpretty.register_uri( 31 | httpretty.POST, 32 | url, 33 | headers={'x-api-key': DETECTION_API_KEY, 34 | 'Content-Type': 'multipart/form-data'}, 35 | body='{"result" : [{"age" : [ 25, 32 ], "gender" : "male"}]}' 36 | ) 37 | 38 | name_img: str = os.path.basename(FILE_PATH) 39 | m: MultipartEncoder = MultipartEncoder( 40 | fields={'file': (name_img, open(FILE_PATH, 'rb'))} 41 | ) 42 | response: dict = requests.post( 43 | url=url, data=m, headers={'x-api-key': DETECTION_API_KEY}).json() 44 | test_subject: DetectFaceFromImageClient = DetectFaceFromImageClient( 45 | DETECTION_API_KEY, DOMAIN, PORT) 46 | test_response: dict = test_subject.post(FILE_PATH) 47 | assert response == test_response 48 | 49 | 50 | @httpretty.activate(verbose=True, allow_net_connect=False) 51 | def test_post_other_response(): 52 | httpretty.register_uri( 53 | httpretty.POST, 54 | url, 55 | headers={'x-api-key': DETECTION_API_KEY, 56 | 'Content-Type': 'multipart/form-data'}, 57 | body='{"result" : [{"age" : [ 25, 32 ], "gender" : "male"}]}' 58 | ) 59 | 60 | name_img: str = os.path.basename(FILE_PATH) 61 | m: MultipartEncoder = MultipartEncoder( 62 | fields={'file': (name_img, open(FILE_PATH, 'rb'))} 63 | ) 64 | response: dict = requests.post( 65 | url=url, data=m, headers={'x-api-key': DETECTION_API_KEY}).json() 66 | test_subject: DetectFaceFromImageClient = DetectFaceFromImageClient( 67 | DETECTION_API_KEY, DOMAIN, PORT) 68 | httpretty.register_uri( 69 | httpretty.POST, 70 | url, 71 | headers={'x-api-key': DETECTION_API_KEY, 72 | 'Content-Type': 'multipart/form-data'}, 73 | body='{"result" : [{"age" : [ 21, 32 ], "gender" : "female"}]}' 74 | ) 75 | test_response: dict = test_subject.post(FILE_PATH) 76 | assert response != test_response 77 | -------------------------------------------------------------------------------- /tests/client/test_recognize_face_from_image.py: -------------------------------------------------------------------------------- 1 | """ 2 | Copyright(c) 2021 the original author or authors 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | https: // www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 13 | or implied. See the License for the specific language governing 14 | permissions and limitations under the License. 15 | """ 16 | 17 | import os 18 | import pytest 19 | import httpretty 20 | import requests 21 | from requests_toolbelt.multipart.encoder import MultipartEncoder 22 | from compreface.client.recognize_face_from_image import RecognizeFaceFromImageClient 23 | from compreface.config.api_list import RECOGNIZE_API 24 | from tests.client.const_config import DOMAIN, PORT, RECOGNIZE_API_KEY, FILE_PATH 25 | """ 26 | Server configuration 27 | """ 28 | url: str = DOMAIN + ":" + PORT + RECOGNIZE_API 29 | 30 | 31 | @httpretty.activate(verbose=True, allow_net_connect=False) 32 | def test_recognize(): 33 | httpretty.register_uri( 34 | httpretty.POST, 35 | url, 36 | headers={'x-api-key': RECOGNIZE_API_KEY, 37 | 'Content-Type': 'multipart/form-data'}, 38 | body='{"result" : [{"age" : [ 25, 32 ], "gender" : "male"}]}' 39 | ) 40 | name_img: str = os.path.basename(FILE_PATH) 41 | m: MultipartEncoder = MultipartEncoder( 42 | fields={'file': (name_img, open(FILE_PATH, 'rb'))} 43 | ) 44 | response: dict = requests.post( 45 | url=url, data=m, headers={'x-api-key': RECOGNIZE_API_KEY, 'Content-Type': 'multipart/form-data'}).json() 46 | 47 | test_subject: RecognizeFaceFromImageClient = RecognizeFaceFromImageClient( 48 | RECOGNIZE_API_KEY, DOMAIN, PORT) 49 | test_response: dict = test_subject.post(FILE_PATH) 50 | assert response == test_response 51 | 52 | 53 | @httpretty.activate(verbose=True, allow_net_connect=False) 54 | def test_recognize_other_response(): 55 | httpretty.register_uri( 56 | httpretty.POST, 57 | url, 58 | headers={'x-api-key': RECOGNIZE_API_KEY, 59 | 'Content-Type': 'multipart/form-data'}, 60 | body='{"result" : [{"age" : [ 25, 32 ], "gender" : "male"}]}' 61 | ) 62 | name_img: str = os.path.basename(FILE_PATH) 63 | m: MultipartEncoder = MultipartEncoder( 64 | fields={'file': (name_img, open(FILE_PATH, 'rb'))} 65 | ) 66 | response: dict = requests.post( 67 | url=url, data=m, headers={'x-api-key': RECOGNIZE_API_KEY, 'Content-Type': 'multipart/form-data'}).json() 68 | httpretty.register_uri( 69 | httpretty.POST, 70 | url, 71 | headers={'x-api-key': RECOGNIZE_API_KEY, 72 | 'Content-Type': 'multipart/form-data'}, 73 | body='{"result" : [{"age" : [ 26, 31 ], "gender" : "female"}]}' 74 | ) 75 | test_subject: RecognizeFaceFromImageClient = RecognizeFaceFromImageClient( 76 | RECOGNIZE_API_KEY, DOMAIN, PORT) 77 | test_response: dict = test_subject.post(FILE_PATH) 78 | assert response != test_response 79 | -------------------------------------------------------------------------------- /tests/client/test_subject_crud_client.py: -------------------------------------------------------------------------------- 1 | 2 | """ 3 | Copyright(c) 2021 the original author or authors 4 | 5 | Licensed under the Apache License, Version 2.0 (the "License"); 6 | you may not use this file except in compliance with the License. 7 | You may obtain a copy of the License at 8 | 9 | https: // www.apache.org/licenses/LICENSE-2.0 10 | 11 | Unless required by applicable law or agreed to in writing, software 12 | distributed under the License is distributed on an "AS IS" BASIS, 13 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 14 | or implied. See the License for the specific language governing 15 | permissions and limitations under the License. 16 | """ 17 | import json 18 | import httpretty 19 | import requests 20 | from compreface.client import SubjectClient 21 | from compreface.config.api_list import SUBJECTS_CRUD_API 22 | from tests.client.const_config import DOMAIN, PORT, DETECTION_API_KEY 23 | """ 24 | Server configuration 25 | """ 26 | url: str = DOMAIN + ":" + PORT + SUBJECTS_CRUD_API 27 | 28 | 29 | @httpretty.activate(verbose=True, allow_net_connect=False) 30 | def test_get(): 31 | httpretty.register_uri( 32 | httpretty.GET, 33 | url, 34 | headers={'x-api-key': DETECTION_API_KEY}, 35 | body='{"subjects": ["Subject", "Subject2"]}' 36 | ) 37 | test_subject: SubjectClient = SubjectClient(DETECTION_API_KEY, DOMAIN, PORT) 38 | response: dict = requests.get(url=url, headers={'x-api-key': DETECTION_API_KEY}).json() 39 | test_response: dict = test_subject.get() 40 | assert response == test_response 41 | 42 | 43 | @httpretty.activate(verbose=True, allow_net_connect=False) 44 | def test_post(): 45 | httpretty.register_uri( 46 | httpretty.POST, 47 | url, 48 | headers={'x-api-key': DETECTION_API_KEY, 49 | 'Content-Type': 'application/json'}, 50 | body='{"subject": "Subject"}' 51 | ) 52 | 53 | data = {'subject': 'Subject'} 54 | 55 | response: dict = requests.post( 56 | url=url, data=json.dumps(data), headers={'x-api-key': DETECTION_API_KEY, 57 | 'Content-Type': 'application/json'}).json() 58 | 59 | test_subject: SubjectClient = SubjectClient(DETECTION_API_KEY, DOMAIN, PORT) 60 | test_response: dict = test_subject.post({'subject': 'Subject'}) 61 | assert response == test_response 62 | 63 | 64 | @httpretty.activate(verbose=True, allow_net_connect=False) 65 | def test_post_incorrect_response(): 66 | httpretty.register_uri( 67 | httpretty.POST, 68 | url, 69 | headers={'x-api-key': DETECTION_API_KEY, 70 | 'Content-Type': 'application/json'}, 71 | body='{"subject": "Subjectss"}' 72 | ) 73 | 74 | data = {'subject': 'Subjectss'} 75 | 76 | response: dict = requests.post( 77 | url=url, data=json.dumps(data), headers={'x-api-key': DETECTION_API_KEY, 78 | 'Content-Type': 'application/json'}).json() 79 | 80 | httpretty.register_uri( 81 | httpretty.POST, 82 | url, 83 | headers={'x-api-key': DETECTION_API_KEY, 84 | 'Content-Type': 'application/json'}, 85 | body='{"subject": "Subject"}' 86 | ) 87 | 88 | test_subject: SubjectClient = SubjectClient(DETECTION_API_KEY, DOMAIN, PORT) 89 | test_response: dict = test_subject.post({'subject': 'Subject'}) 90 | assert response != test_response 91 | 92 | 93 | @httpretty.activate(verbose=True, allow_net_connect=False) 94 | def test_delete(): 95 | test_url = url + '/Subject' 96 | httpretty.register_uri( 97 | httpretty.DELETE, 98 | test_url, 99 | headers={'x-api-key': DETECTION_API_KEY, 100 | 'Content-Type': 'application/json'}, 101 | body='{"subject": "Subject"}' 102 | ) 103 | 104 | response: dict = requests.delete(url=test_url, 105 | headers={'x-api-key': DETECTION_API_KEY, 106 | 'Content-Type': 'application/json'}).json() 107 | 108 | test_subject: SubjectClient = SubjectClient(DETECTION_API_KEY, DOMAIN, PORT) 109 | test_response: dict = test_subject.delete("Subject") 110 | assert response == test_response 111 | 112 | 113 | @httpretty.activate(verbose=True, allow_net_connect=False) 114 | def test_put(): 115 | test_url = url + '/Subject' 116 | httpretty.register_uri( 117 | httpretty.PUT, 118 | test_url, 119 | headers={'x-api-key': DETECTION_API_KEY, 120 | 'Content-Type': 'application/json'}, 121 | body='{"subject": "NewSubject"}' 122 | ) 123 | 124 | data = {"subject": "NewSubject"} 125 | 126 | response: dict = requests.put(url=test_url, data=json.dumps(data), headers={'x-api-key': DETECTION_API_KEY}).json() 127 | 128 | test_subject: SubjectClient = SubjectClient(DETECTION_API_KEY, DOMAIN, PORT) 129 | test_response: dict = test_subject.put({"subject": "NewSubject", "api_endpoint": "Subject"}) 130 | assert response == test_response 131 | -------------------------------------------------------------------------------- /tests/client/test_verification_face_from_image.py: -------------------------------------------------------------------------------- 1 | """ 2 | Copyright(c) 2021 the original author or authors 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | https: // www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 13 | or implied. See the License for the specific language governing 14 | permissions and limitations under the License. 15 | """ 16 | 17 | import os 18 | import httpretty 19 | import requests 20 | from requests_toolbelt.multipart.encoder import MultipartEncoder 21 | from compreface.config.api_list import RECOGNIZE_CRUD_API 22 | from compreface.client import VerificationFaceFromImageClient 23 | from tests.client.const_config import DOMAIN, PORT, RECOGNIZE_API_KEY, FILE_PATH, IMAGE_ID 24 | 25 | url: str = DOMAIN + ":" + PORT + RECOGNIZE_CRUD_API + '/' + IMAGE_ID + '/verify' 26 | 27 | 28 | @httpretty.activate(verbose=True, allow_net_connect=False) 29 | def test_post(): 30 | httpretty.register_uri( 31 | httpretty.POST, 32 | url, 33 | headers={'x-api-key': RECOGNIZE_API_KEY, 34 | 'Content-Type': 'multipart/form-data'}, 35 | body='{"result" : [{"age" : [ 25, 32 ], "gender" : "male"}]}' 36 | ) 37 | 38 | name_img: str = os.path.basename(FILE_PATH) 39 | m: MultipartEncoder = MultipartEncoder( 40 | fields={'file': (name_img, open(FILE_PATH, 'rb'))} 41 | ) 42 | response: dict = requests.post( 43 | url=url, data=m, headers={'x-api-key': RECOGNIZE_API_KEY}).json() 44 | 45 | test_subject: VerificationFaceFromImageClient = VerificationFaceFromImageClient( 46 | RECOGNIZE_API_KEY, DOMAIN, PORT) 47 | test_response: dict = test_subject.post(FILE_PATH, IMAGE_ID) 48 | assert response == test_response 49 | 50 | 51 | @httpretty.activate(verbose=True, allow_net_connect=False) 52 | def test_post_other_response(): 53 | httpretty.register_uri( 54 | httpretty.POST, 55 | url, 56 | headers={'x-api-key': RECOGNIZE_API_KEY, 57 | 'Content-Type': 'multipart/form-data'}, 58 | body='{"result" : [{"age" : [ 25, 32 ], "gender" : "male"}]}' 59 | ) 60 | 61 | name_img: str = os.path.basename(FILE_PATH) 62 | m: MultipartEncoder = MultipartEncoder( 63 | fields={'file': (name_img, open(FILE_PATH, 'rb'))} 64 | ) 65 | response: dict = requests.post( 66 | url=url, data=m, headers={'x-api-key': RECOGNIZE_API_KEY}).json() 67 | 68 | httpretty.register_uri( 69 | httpretty.POST, 70 | url, 71 | headers={'x-api-key': RECOGNIZE_API_KEY, 72 | 'Content-Type': 'multipart/form-data'}, 73 | body='{"result" : [{"age" : [ 26, 31 ], "gender" : "female"}]}' 74 | ) 75 | test_subject: VerificationFaceFromImageClient = VerificationFaceFromImageClient( 76 | RECOGNIZE_API_KEY, DOMAIN, PORT) 77 | test_response: dict = test_subject.post(FILE_PATH, IMAGE_ID) 78 | assert response != test_response 79 | -------------------------------------------------------------------------------- /tests/client/test_verify_face_from_image.py: -------------------------------------------------------------------------------- 1 | """ 2 | Copyright(c) 2021 the original author or authors 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | https: // www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 13 | or implied. See the License for the specific language governing 14 | permissions and limitations under the License. 15 | """ 16 | 17 | import pytest 18 | import os 19 | import httpretty 20 | import requests 21 | from requests_toolbelt.multipart.encoder import MultipartEncoder 22 | from compreface.client import VerifyFaceFromImageClient 23 | from compreface.config.api_list import VERIFICATION_API 24 | from tests.client.const_config import DOMAIN, PORT, VERIFICATION_API_KEY, FILE_PATH 25 | """ 26 | Server configuration 27 | """ 28 | url: str = DOMAIN + ":" + PORT + VERIFICATION_API + '/verify' 29 | 30 | 31 | @httpretty.activate(verbose=True, allow_net_connect=False) 32 | def test_post(): 33 | httpretty.register_uri( 34 | httpretty.POST, 35 | url, 36 | headers={'x-api-key': VERIFICATION_API_KEY, 37 | 'Content-Type': 'multipart/form-data'}, 38 | body='{"result" : [{"age" : [ 25, 32 ], "gender" : "male"}]}' 39 | ) 40 | 41 | name_img: str = os.path.basename(FILE_PATH) 42 | m: MultipartEncoder = MultipartEncoder( 43 | fields={'source_image': (name_img, open( 44 | FILE_PATH, 'rb')), 'target_image': (name_img, open( 45 | FILE_PATH, 'rb'))} 46 | ) 47 | response: dict = requests.post( 48 | url=url, data=m, headers={'x-api-key': VERIFICATION_API_KEY, 'Content-Type': m.content_type}).json() 49 | test_subject: VerifyFaceFromImageClient = VerifyFaceFromImageClient( 50 | VERIFICATION_API_KEY, DOMAIN, PORT) 51 | test_response: dict = test_subject.post(FILE_PATH, FILE_PATH) 52 | assert response == test_response 53 | 54 | 55 | @httpretty.activate(verbose=True, allow_net_connect=False) 56 | def test_post_other_response(): 57 | httpretty.register_uri( 58 | httpretty.POST, 59 | url, 60 | headers={'x-api-key': VERIFICATION_API_KEY, 61 | 'Content-Type': 'multipart/form-data'}, 62 | body='{"result" : [{"age" : [ 25, 32 ], "gender" : "male"}]}' 63 | ) 64 | 65 | name_img: str = os.path.basename(FILE_PATH) 66 | m: MultipartEncoder = MultipartEncoder( 67 | fields={'source_image': (name_img, open( 68 | FILE_PATH, 'rb')), 'target_image': (name_img, open( 69 | FILE_PATH, 'rb'))} 70 | ) 71 | response: dict = requests.post( 72 | url=url, data=m, headers={'x-api-key': VERIFICATION_API_KEY, 'Content-Type': m.content_type}).json() 73 | test_subject: VerifyFaceFromImageClient = VerifyFaceFromImageClient( 74 | VERIFICATION_API_KEY, DOMAIN, PORT) 75 | httpretty.register_uri( 76 | httpretty.POST, 77 | url, 78 | headers={'x-api-key': VERIFICATION_API_KEY, 79 | 'Content-Type': 'multipart/form-data'}, 80 | body='{"result" : [{"age" : [ 21, 32 ], "gender" : "female"}]}' 81 | ) 82 | test_response: dict = test_subject.post(FILE_PATH, FILE_PATH) 83 | assert response != test_response 84 | -------------------------------------------------------------------------------- /tests/common/jonathan-petit-unsplash.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/exadel-inc/compreface-python-sdk/9f6e2c6fdd7b477697c83ed2210aab898d8b0ecf/tests/common/jonathan-petit-unsplash.jpg -------------------------------------------------------------------------------- /tox.ini: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/exadel-inc/compreface-python-sdk/9f6e2c6fdd7b477697c83ed2210aab898d8b0ecf/tox.ini -------------------------------------------------------------------------------- /webcam_demo/README.md: -------------------------------------------------------------------------------- 1 | # Webcam demo 2 | 3 | This is an example of how to use CompreFace face recognition with python sdk. 4 | 5 | # Requirements 6 | 7 | 1. [Python](https://www.python.org/downloads/) (Version 3.7+) 8 | 2. [CompreFace](https://github.com/exadel-inc/CompreFace#getting-started-with-compreface) 9 | 3. [Compreface-python-sdk](https://github.com/exadel-inc/compreface-python-sdk) 10 | 4. [Opencv-python](https://pypi.org/project/opencv-python/) 11 | 12 | # Face recognition demo 13 | 14 | To run the demo, open `webcam_demo` folder and run: 15 | 16 | ```commandline 17 | python compreface_webcam_recognition_demo.py --api-key your_api_key --host http://localhost --port 8000 18 | ``` 19 | * `--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. 20 | * `--host` is the host where you deployed CompreFace. Optional value. By default, the value is `http://localhost` 21 | * `--port` is the port of CompreFace instance. Optional value. By default, the value is `8000` 22 | 23 | # Face detection demo 24 | 25 | To run the demo, open `webcam_demo` folder and run: 26 | 27 | ```commandline 28 | python compreface_webcam_detection_demo.py --api-key your_api_key --host http://localhost --port 8000 29 | ``` 30 | * `--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). 31 | * `--host` is the host where you deployed CompreFace. Optional value. By default, the value is `http://localhost` 32 | * `--port` is the port of CompreFace instance. Optional value. By default, the value is `8000` -------------------------------------------------------------------------------- /webcam_demo/compreface_webcam_detection_demo.py: -------------------------------------------------------------------------------- 1 | """ 2 | Copyright(c) 2021 the original author or authors 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | https: // www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 13 | or implied. See the License for the specific language governing 14 | permissions and limitations under the License. 15 | """ 16 | 17 | import argparse 18 | import cv2 19 | import time 20 | from threading import Thread 21 | 22 | from compreface import CompreFace 23 | from compreface.service import DetectionService 24 | 25 | 26 | def parseArguments(): 27 | parser = argparse.ArgumentParser() 28 | 29 | parser.add_argument("--api-key", help="CompreFace detection service API key", type=str, required=True) 30 | parser.add_argument("--host", help="CompreFace host", type=str, default='http://localhost') 31 | parser.add_argument("--port", help="CompreFace port", type=str, default='8000') 32 | 33 | args = parser.parse_args() 34 | 35 | return args 36 | 37 | class ThreadedCamera: 38 | def __init__(self, api_key, host, port): 39 | self.active = True 40 | self.results = [] 41 | self.capture = cv2.VideoCapture(0) 42 | self.capture.set(cv2.CAP_PROP_BUFFERSIZE, 2) 43 | 44 | compre_face: CompreFace = CompreFace(host, port, { 45 | "limit": 0, 46 | "det_prob_threshold": 0.8, 47 | "prediction_count": 1, 48 | "face_plugins": "age,gender,mask", 49 | "status": False 50 | }) 51 | 52 | self.detection: DetectionService = compre_face.init_face_detection(api_key) 53 | 54 | self.FPS = 1/30 55 | 56 | # Start frame retrieval thread 57 | self.thread = Thread(target=self.show_frame, args=()) 58 | self.thread.daemon = True 59 | self.thread.start() 60 | 61 | def show_frame(self): 62 | print("Started") 63 | while self.capture.isOpened(): 64 | (status, frame_raw) = self.capture.read() 65 | self.frame = cv2.flip(frame_raw, 1) 66 | 67 | if self.results: 68 | results = self.results 69 | for result in results: 70 | box = result.get('box') 71 | age = result.get('age') 72 | gender = result.get('gender') 73 | mask = result.get('mask') 74 | if box: 75 | cv2.rectangle(img=self.frame, pt1=(box['x_min'], box['y_min']), 76 | pt2=(box['x_max'], box['y_max']), color=(0, 255, 0), thickness=1) 77 | if age: 78 | age = f"Age: {age['low']} - {age['high']}" 79 | cv2.putText(self.frame, age, (box['x_max'], box['y_min'] + 15), 80 | cv2.FONT_HERSHEY_SIMPLEX, 0.6, (0, 255, 0), 1) 81 | if gender: 82 | gender = f"Gender: {gender['value']}" 83 | cv2.putText(self.frame, gender, (box['x_max'], box['y_min'] + 35), 84 | cv2.FONT_HERSHEY_SIMPLEX, 0.6, (0, 255, 0), 1) 85 | if mask: 86 | mask = f"Mask: {mask['value']}" 87 | cv2.putText(self.frame, mask, (box['x_max'], box['y_min'] + 55), 88 | cv2.FONT_HERSHEY_SIMPLEX, 0.6, (0, 255, 0), 1) 89 | 90 | cv2.imshow('CompreFace demo', self.frame) 91 | time.sleep(self.FPS) 92 | 93 | if cv2.waitKey(1) & 0xFF == 27: 94 | self.capture.release() 95 | cv2.destroyAllWindows() 96 | self.active=False 97 | 98 | def is_active(self): 99 | return self.active 100 | 101 | def update(self): 102 | if not hasattr(self, 'frame'): 103 | return 104 | 105 | _, im_buf_arr = cv2.imencode(".jpg", self.frame) 106 | byte_im = im_buf_arr.tobytes() 107 | data = self.detection.detect(byte_im) 108 | self.results = data.get('result') 109 | 110 | 111 | if __name__ == '__main__': 112 | args = parseArguments() 113 | threaded_camera = ThreadedCamera(args.api_key, args.host, args.port) 114 | while threaded_camera.is_active(): 115 | threaded_camera.update() -------------------------------------------------------------------------------- /webcam_demo/compreface_webcam_recognition_demo.py: -------------------------------------------------------------------------------- 1 | """ 2 | Copyright(c) 2021 the original author or authors 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | https: // www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 13 | or implied. See the License for the specific language governing 14 | permissions and limitations under the License. 15 | """ 16 | 17 | import cv2 18 | import argparse 19 | import time 20 | from threading import Thread 21 | 22 | from compreface import CompreFace 23 | from compreface.service import RecognitionService 24 | 25 | def parseArguments(): 26 | parser = argparse.ArgumentParser() 27 | 28 | parser.add_argument("--api-key", help="CompreFace recognition service API key", type=str, default='00000000-0000-0000-0000-000000000002') 29 | parser.add_argument("--host", help="CompreFace host", type=str, default='http://localhost') 30 | parser.add_argument("--port", help="CompreFace port", type=str, default='8000') 31 | 32 | args = parser.parse_args() 33 | 34 | return args 35 | 36 | class ThreadedCamera: 37 | def __init__(self, api_key, host, port): 38 | self.active = True 39 | self.results = [] 40 | self.capture = cv2.VideoCapture(0) 41 | self.capture.set(cv2.CAP_PROP_BUFFERSIZE, 2) 42 | 43 | compre_face: CompreFace = CompreFace(host, port, { 44 | "limit": 0, 45 | "det_prob_threshold": 0.8, 46 | "prediction_count": 1, 47 | "face_plugins": "age,gender", 48 | "status": False 49 | }) 50 | 51 | self.recognition: RecognitionService = compre_face.init_face_recognition(api_key) 52 | 53 | self.FPS = 1/30 54 | 55 | # Start frame retrieval thread 56 | self.thread = Thread(target=self.show_frame, args=()) 57 | self.thread.daemon = True 58 | self.thread.start() 59 | 60 | def show_frame(self): 61 | print("Started") 62 | while self.capture.isOpened(): 63 | (status, frame_raw) = self.capture.read() 64 | self.frame = cv2.flip(frame_raw, 1) 65 | 66 | if self.results: 67 | results = self.results 68 | for result in results: 69 | box = result.get('box') 70 | age = result.get('age') 71 | gender = result.get('gender') 72 | mask = result.get('mask') 73 | subjects = result.get('subjects') 74 | if box: 75 | cv2.rectangle(img=self.frame, pt1=(box['x_min'], box['y_min']), 76 | pt2=(box['x_max'], box['y_max']), color=(0, 255, 0), thickness=1) 77 | if age: 78 | age = f"Age: {age['low']} - {age['high']}" 79 | cv2.putText(self.frame, age, (box['x_max'], box['y_min'] + 15), 80 | cv2.FONT_HERSHEY_SIMPLEX, 0.6, (0, 255, 0), 1) 81 | if gender: 82 | gender = f"Gender: {gender['value']}" 83 | cv2.putText(self.frame, gender, (box['x_max'], box['y_min'] + 35), 84 | cv2.FONT_HERSHEY_SIMPLEX, 0.6, (0, 255, 0), 1) 85 | if mask: 86 | mask = f"Mask: {mask['value']}" 87 | cv2.putText(self.frame, mask, (box['x_max'], box['y_min'] + 55), 88 | cv2.FONT_HERSHEY_SIMPLEX, 0.6, (0, 255, 0), 1) 89 | 90 | if subjects: 91 | subjects = sorted(subjects, key=lambda k: k['similarity'], reverse=True) 92 | subject = f"Subject: {subjects[0]['subject']}" 93 | similarity = f"Similarity: {subjects[0]['similarity']}" 94 | cv2.putText(self.frame, subject, (box['x_max'], box['y_min'] + 75), 95 | cv2.FONT_HERSHEY_SIMPLEX, 0.6, (0, 255, 0), 1) 96 | cv2.putText(self.frame, similarity, (box['x_max'], box['y_min'] + 95), 97 | cv2.FONT_HERSHEY_SIMPLEX, 0.6, (0, 255, 0), 1) 98 | else: 99 | subject = f"No known faces" 100 | cv2.putText(self.frame, subject, (box['x_max'], box['y_min'] + 75), 101 | cv2.FONT_HERSHEY_SIMPLEX, 0.6, (0, 255, 0), 1) 102 | 103 | cv2.imshow('CompreFace demo', self.frame) 104 | time.sleep(self.FPS) 105 | 106 | if cv2.waitKey(1) & 0xFF == 27: 107 | self.capture.release() 108 | cv2.destroyAllWindows() 109 | self.active=False 110 | 111 | def is_active(self): 112 | return self.active 113 | 114 | def update(self): 115 | if not hasattr(self, 'frame'): 116 | return 117 | 118 | _, im_buf_arr = cv2.imencode(".jpg", self.frame) 119 | byte_im = im_buf_arr.tobytes() 120 | data = self.recognition.recognize(byte_im) 121 | self.results = data.get('result') 122 | 123 | 124 | if __name__ == '__main__': 125 | args = parseArguments() 126 | threaded_camera = ThreadedCamera(args.api_key, args.host, args.port) 127 | while threaded_camera.is_active(): 128 | threaded_camera.update() --------------------------------------------------------------------------------