├── .github └── workflows │ ├── build-and-test.yml │ └── cla.yml ├── .gitignore ├── .whitesource ├── CLA.md ├── ComprefaceNetSdk.sln ├── Exadel.Compreface.AcceptenceTests ├── CompreFaceClientTests │ ├── CompreFaceClientTests.cs │ ├── CompreFaceClientWithConfigTests.cs │ └── TestService.cs ├── ConfigurationTest │ └── ComprefaceConfigurationTest.cs ├── Exadel.Compreface.AcceptenceTests.csproj ├── Resources │ └── Images │ │ ├── pexels-14344696.jpg │ │ ├── pexels-jonathan-yakubu.jpg │ │ └── pexels.jpg ├── Services │ ├── DetectionServiceTest.cs │ ├── RecognitionServiceTests │ │ ├── FaceCollectionTest │ │ │ ├── FaceCollectionTest.cs │ │ │ └── FaceCollectionTestBeforeAfter.cs │ │ ├── RecognizeFaceFromImageTest.cs │ │ └── SubjectTests.cs │ └── VerificationServiceTest.cs ├── UrlConstConfig.cs ├── Usings.cs └── appsettings.json ├── Exadel.Compreface.UnitTests ├── ApiClientTest │ ├── ApiClientTests.FlurlHttpException.cs │ ├── ApiClientTests.FlurlHttpTimeoutException.cs │ ├── ApiClientTests.HttpVerbs.cs │ └── ApiClientTests.cs ├── Exadel.Compreface.UnitTests.csproj ├── Helpers │ ├── GetRandomStringHelper.cs │ └── SetupAndVerifyTests.cs ├── Services │ ├── DetectionServiceTest.cs │ ├── FaceCollectionTests.cs │ ├── RecognizeFaceFromImageTests.cs │ ├── SubjectTests.cs │ └── VerificationTests.cs └── Usings.cs ├── Exadel.Compreface ├── Clients │ ├── ApiClient │ │ ├── ApiClient.cs │ │ └── IApiClient.cs │ ├── CompreFaceClient │ │ ├── CompreFaceClient.cs │ │ └── ICompreFaceClient.cs │ └── Config │ │ └── ConfigInitializer.cs ├── Configuration │ ├── ComprefaceConfiguration.cs │ └── IComprefaceConfiguration.cs ├── DTOs │ ├── FaceCollectionDTOs │ │ ├── AddSubjectExample │ │ │ ├── AddBase64SubjectExampleRequest.cs │ │ │ ├── AddSubjectExampleRequestByBytes.cs │ │ │ ├── AddSubjectExampleRequestByFilePath.cs │ │ │ ├── AddSubjectExampleRequestByFileUrl.cs │ │ │ └── AddSubjectExampleResponse.cs │ │ ├── DeleteAllSubjectExamples │ │ │ ├── DeleteAllExamplesRequest.cs │ │ │ └── DeleteAllExamplesResponse.cs │ │ ├── DeleteImageById │ │ │ ├── DeleteImageByIdRequest.cs │ │ │ └── DeleteImageByIdResponse.cs │ │ ├── DeleteMultipleExamples │ │ │ ├── DeleteMultipleExamplesRequest.cs │ │ │ └── DeleteMultipleExamplesResponse.cs │ │ ├── DownloadImageById │ │ │ └── DownloadImageByIdDirectlyRequest.cs │ │ ├── DownloadImageByIdFromSubject │ │ │ └── DownloadImageByIdFromSubjectRequest.cs │ │ └── ListAllSubjectExamples │ │ │ ├── ListAllSubjectExamplesRequest.cs │ │ │ └── ListAllSubjectExamplesResponse.cs │ ├── FaceDetectionDTOs │ │ └── FaceDetection │ │ │ ├── FaceDetectionBase64Request.cs │ │ │ ├── FaceDetectionRequestByBytes.cs │ │ │ ├── FaceDetectionRequestByFilePath.cs │ │ │ ├── FaceDetectionRequestByFileUrl.cs │ │ │ └── FaceDetectionResponse.cs │ ├── FaceVerificationDTOs │ │ └── FaceVerification │ │ │ ├── FaceVerificationRequestByBytes.cs │ │ │ ├── FaceVerificationRequestByFilePath.cs │ │ │ ├── FaceVerificationRequestByFileUrl.cs │ │ │ ├── FaceVerificationResponse.cs │ │ │ └── FaceVerificationWithBase64Request.cs │ ├── HelperDTOs │ │ ├── Age.cs │ │ ├── BaseDTOs │ │ │ ├── BaseExampleRequest.cs │ │ │ ├── BaseFaceRequest.cs │ │ │ └── BaseResult.cs │ │ ├── Box.cs │ │ ├── ExecutionTime.cs │ │ ├── Face.cs │ │ ├── Gender.cs │ │ ├── Mask.cs │ │ ├── PluginVersions.cs │ │ └── SimilarSubject.cs │ ├── RecognizeFaceFromImageDTOs │ │ ├── BaseRequests │ │ │ ├── BaseRecognizeFaceFromImageRequest.cs │ │ │ └── BaseVerifyFacesFromImageRequest.cs │ │ ├── RecognizeFaceFromImage │ │ │ ├── RecognizeFaceFromImageRequestByBytes.cs │ │ │ ├── RecognizeFaceFromImageRequestByFilePath.cs │ │ │ ├── RecognizeFaceFromImageRequestByFileUrl.cs │ │ │ ├── RecognizeFaceFromImageResponse.cs │ │ │ └── RecognizeFacesFromImageWithBase64Request.cs │ │ └── VerifyFacesFromImage │ │ │ ├── VerifyFacesFromImageByFilePathRequest.cs │ │ │ ├── VerifyFacesFromImageByFileUrlRequest.cs │ │ │ ├── VerifyFacesFromImageResponse.cs │ │ │ ├── VerifyFacesFromImageWithBase64Request.cs │ │ │ └── VerifyFacesFromImageWithBytesRequest.cs │ └── SubjectDTOs │ │ ├── AddSubject │ │ ├── AddSubjectRequest.cs │ │ └── AddSubjectResponse.cs │ │ ├── DeleteAllSubjects │ │ └── DeleteAllSubjectsResponse.cs │ │ ├── DeleteSubject │ │ ├── DeleteSubjectRequest.cs │ │ └── DeleteSubjectResponse.cs │ │ ├── GetSubjectList │ │ └── GetAllSubjectResponse.cs │ │ └── RenameSubject │ │ ├── RenameSubjectRequest.cs │ │ └── RenameSubjectResponse.cs ├── Exadel.Compreface.csproj ├── Exceptions │ ├── ServiceException.cs │ ├── ServiceTimeoutException.cs │ └── TypeNotBelongCompreFaceException.cs ├── Helpers │ ├── ConvertUrlToBase64StringHelpers.cs │ ├── FileHelpers.cs │ ├── SnakeCaseToCamelCaseNamingPolicy.cs │ ├── StringExtensions.cs │ └── SystemJsonSerializer.cs ├── Services │ ├── Attributes │ │ └── CompreFaceServiceAttribute.cs │ ├── DetectionService.cs │ ├── Interfaces │ │ ├── IDetectionService.cs │ │ ├── IFaceCollection.cs │ │ ├── IRecognizeFaceFromImage.cs │ │ ├── ISubject.cs │ │ └── IVerificationService.cs │ ├── RecognitionService │ │ ├── FaceCollection.cs │ │ ├── RecognitionService.cs │ │ ├── RecognizeFaceFromImage.cs │ │ └── Subject.cs │ └── VerificationService.cs └── images │ └── compreface_icon.png ├── Example ├── Example.csproj ├── ExampleConst.cs ├── Program.cs └── appsettings.json ├── LICENSE ├── README.md ├── RecognitionExampleApp ├── .gitignore ├── App.axaml ├── App.axaml.cs ├── Assets │ ├── compreface-icon.ico │ ├── haarcascade_frontalface_alt2.xml │ └── question_mark.png ├── CreateSubjectHelpWindow.axaml ├── CreateSubjectHelpWindow.axaml.cs ├── DoneWindow.axaml ├── DoneWindow.axaml.cs ├── MainWindow.axaml ├── MainWindow.axaml.cs ├── ProcessingWindow.axaml ├── ProcessingWindow.axaml.cs ├── Program.cs ├── README.md ├── RecognitionExampleApp.csproj ├── ResultsWindow.axaml ├── ResultsWindow.axaml.cs ├── Roots.xml └── app.manifest └── signatures └── cla.json /.github/workflows/build-and-test.yml: -------------------------------------------------------------------------------- 1 | # This workflow will build a .NET project 2 | # For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-net 3 | 4 | name: build and test 5 | 6 | on: 7 | pull_request: 8 | branches: [ "add-upload-from-remote-server" ] 9 | paths: 10 | - '**.cs' 11 | - '**.csproj' 12 | env: 13 | DOTNET_VERSION: '7.0' # The .NET SDK version to use 14 | 15 | jobs: 16 | build-and-test: 17 | 18 | name: build-and-test 19 | runs-on: ubuntu-latest 20 | 21 | steps: 22 | - uses: actions/checkout@v3 23 | - name: Setup .NET 24 | uses: actions/setup-dotnet@v3 25 | with: 26 | dotnet-version: 7.0.x 27 | 28 | - name: Restore dependencies 29 | run: dotnet restore 30 | - name: Build 31 | run: dotnet build --configuration Release --no-restore 32 | 33 | - name: Show contents 34 | run: pwd 35 | 36 | - name: Show pictures 37 | run: ls Exadel.Compreface.AcceptenceTests/Resources/Images/ 38 | 39 | - name: Test 40 | run: dotnet test --no-restore --verbosity normal 41 | -------------------------------------------------------------------------------- /.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-net-sdk/blob/develop/CLA.md' 22 | branch: 'CLA-signatures' 23 | allowlist: bot* 24 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | ## 4 | ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore 5 | 6 | # User-specific files 7 | *.rsuser 8 | *.suo 9 | *.user 10 | *.userosscache 11 | *.sln.docstates 12 | 13 | # User-specific files (MonoDevelop/Xamarin Studio) 14 | *.userprefs 15 | 16 | # Mono auto generated files 17 | mono_crash.* 18 | 19 | # Build results 20 | [Dd]ebug/ 21 | [Dd]ebugPublic/ 22 | [Rr]elease/ 23 | [Rr]eleases/ 24 | x64/ 25 | x86/ 26 | [Aa][Rr][Mm]/ 27 | [Aa][Rr][Mm]64/ 28 | bld/ 29 | [Bb]in/ 30 | [Oo]bj/ 31 | [Ll]og/ 32 | [Ll]ogs/ 33 | 34 | # Visual Studio 2015/2017 cache/options directory 35 | .vs/ 36 | # Uncomment if you have tasks that create the project's static files in wwwroot 37 | #wwwroot/ 38 | 39 | # Visual Studio 2017 auto generated files 40 | Generated\ Files/ 41 | 42 | # MSTest test Results 43 | [Tt]est[Rr]esult*/ 44 | [Bb]uild[Ll]og.* 45 | 46 | # NUnit 47 | *.VisualState.xml 48 | TestResult.xml 49 | nunit-*.xml 50 | 51 | # Build Results of an ATL Project 52 | [Dd]ebugPS/ 53 | [Rr]eleasePS/ 54 | dlldata.c 55 | 56 | # Benchmark Results 57 | BenchmarkDotNet.Artifacts/ 58 | 59 | # .NET Core 60 | project.lock.json 61 | project.fragment.lock.json 62 | artifacts/ 63 | 64 | # StyleCop 65 | StyleCopReport.xml 66 | 67 | # Files built by Visual Studio 68 | *_i.c 69 | *_p.c 70 | *_h.h 71 | *.ilk 72 | *.meta 73 | *.obj 74 | *.iobj 75 | *.pch 76 | *.pdb 77 | *.ipdb 78 | *.pgc 79 | *.pgd 80 | *.rsp 81 | *.sbr 82 | *.tlb 83 | *.tli 84 | *.tlh 85 | *.tmp 86 | *.tmp_proj 87 | *_wpftmp.csproj 88 | *.log 89 | *.vspscc 90 | *.vssscc 91 | .builds 92 | *.pidb 93 | *.svclog 94 | *.scc 95 | 96 | # Chutzpah Test files 97 | _Chutzpah* 98 | 99 | # Visual C++ cache files 100 | ipch/ 101 | *.aps 102 | *.ncb 103 | *.opendb 104 | *.opensdf 105 | *.sdf 106 | *.cachefile 107 | *.VC.db 108 | *.VC.VC.opendb 109 | 110 | # Visual Studio profiler 111 | *.psess 112 | *.vsp 113 | *.vspx 114 | *.sap 115 | 116 | # Visual Studio Trace Files 117 | *.e2e 118 | 119 | # TFS 2012 Local Workspace 120 | $tf/ 121 | 122 | # Guidance Automation Toolkit 123 | *.gpState 124 | 125 | # ReSharper is a .NET coding add-in 126 | _ReSharper*/ 127 | *.[Rr]e[Ss]harper 128 | *.DotSettings.user 129 | 130 | # TeamCity is a build add-in 131 | _TeamCity* 132 | 133 | # DotCover is a Code Coverage Tool 134 | *.dotCover 135 | 136 | # AxoCover is a Code Coverage Tool 137 | .axoCover/* 138 | !.axoCover/settings.json 139 | 140 | # Visual Studio code coverage results 141 | *.coverage 142 | *.coveragexml 143 | 144 | # NCrunch 145 | _NCrunch_* 146 | .*crunch*.local.xml 147 | nCrunchTemp_* 148 | 149 | # MightyMoose 150 | *.mm.* 151 | AutoTest.Net/ 152 | 153 | # Web workbench (sass) 154 | .sass-cache/ 155 | 156 | # Installshield output folder 157 | [Ee]xpress/ 158 | 159 | # DocProject is a documentation generator add-in 160 | DocProject/buildhelp/ 161 | DocProject/Help/*.HxT 162 | DocProject/Help/*.HxC 163 | DocProject/Help/*.hhc 164 | DocProject/Help/*.hhk 165 | DocProject/Help/*.hhp 166 | DocProject/Help/Html2 167 | DocProject/Help/html 168 | 169 | # Click-Once directory 170 | publish/ 171 | 172 | # Publish Web Output 173 | *.[Pp]ublish.xml 174 | *.azurePubxml 175 | # Note: Comment the next line if you want to checkin your web deploy settings, 176 | # but database connection strings (with potential passwords) will be unencrypted 177 | *.pubxml 178 | *.publishproj 179 | 180 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 181 | # checkin your Azure Web App publish settings, but sensitive information contained 182 | # in these scripts will be unencrypted 183 | PublishScripts/ 184 | 185 | # NuGet Packages 186 | *.nupkg 187 | # NuGet Symbol Packages 188 | *.snupkg 189 | # The packages folder can be ignored because of Package Restore 190 | **/[Pp]ackages/* 191 | # except build/, which is used as an MSBuild target. 192 | !**/[Pp]ackages/build/ 193 | # Uncomment if necessary however generally it will be regenerated when needed 194 | #!**/[Pp]ackages/repositories.config 195 | # NuGet v3's project.json files produces more ignorable files 196 | *.nuget.props 197 | *.nuget.targets 198 | 199 | # Microsoft Azure Build Output 200 | csx/ 201 | *.build.csdef 202 | 203 | # Microsoft Azure Emulator 204 | ecf/ 205 | rcf/ 206 | 207 | # Windows Store app package directories and files 208 | AppPackages/ 209 | BundleArtifacts/ 210 | Package.StoreAssociation.xml 211 | _pkginfo.txt 212 | *.appx 213 | *.appxbundle 214 | *.appxupload 215 | 216 | # Visual Studio cache files 217 | # files ending in .cache can be ignored 218 | *.[Cc]ache 219 | # but keep track of directories ending in .cache 220 | !?*.[Cc]ache/ 221 | 222 | # Others 223 | ClientBin/ 224 | ~$* 225 | *~ 226 | *.dbmdl 227 | *.dbproj.schemaview 228 | *.jfm 229 | *.pfx 230 | *.publishsettings 231 | orleans.codegen.cs 232 | 233 | # Including strong name files can present a security risk 234 | # (https://github.com/github/gitignore/pull/2483#issue-259490424) 235 | #*.snk 236 | 237 | # Since there are multiple workflows, uncomment next line to ignore bower_components 238 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 239 | #bower_components/ 240 | 241 | # RIA/Silverlight projects 242 | Generated_Code/ 243 | 244 | # Backup & report files from converting an old project file 245 | # to a newer Visual Studio version. Backup files are not needed, 246 | # because we have git ;-) 247 | _UpgradeReport_Files/ 248 | Backup*/ 249 | UpgradeLog*.XML 250 | UpgradeLog*.htm 251 | ServiceFabricBackup/ 252 | *.rptproj.bak 253 | 254 | # SQL Server files 255 | *.mdf 256 | *.ldf 257 | *.ndf 258 | 259 | # Business Intelligence projects 260 | *.rdl.data 261 | *.bim.layout 262 | *.bim_*.settings 263 | *.rptproj.rsuser 264 | *- [Bb]ackup.rdl 265 | *- [Bb]ackup ([0-9]).rdl 266 | *- [Bb]ackup ([0-9][0-9]).rdl 267 | 268 | # Microsoft Fakes 269 | FakesAssemblies/ 270 | 271 | # GhostDoc plugin setting file 272 | *.GhostDoc.xml 273 | 274 | # Node.js Tools for Visual Studio 275 | .ntvs_analysis.dat 276 | node_modules/ 277 | 278 | # Visual Studio 6 build log 279 | *.plg 280 | 281 | # Visual Studio 6 workspace options file 282 | *.opt 283 | 284 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.) 285 | *.vbw 286 | 287 | # Visual Studio LightSwitch build output 288 | **/*.HTMLClient/GeneratedArtifacts 289 | **/*.DesktopClient/GeneratedArtifacts 290 | **/*.DesktopClient/ModelManifest.xml 291 | **/*.Server/GeneratedArtifacts 292 | **/*.Server/ModelManifest.xml 293 | _Pvt_Extensions 294 | 295 | # Paket dependency manager 296 | .paket/paket.exe 297 | paket-files/ 298 | 299 | # FAKE - F# Make 300 | .fake/ 301 | 302 | # CodeRush personal settings 303 | .cr/personal 304 | 305 | # Python Tools for Visual Studio (PTVS) 306 | __pycache__/ 307 | *.pyc 308 | 309 | # Cake - Uncomment if you are using it 310 | # tools/** 311 | # !tools/packages.config 312 | 313 | # Tabs Studio 314 | *.tss 315 | 316 | # Telerik's JustMock configuration file 317 | *.jmconfig 318 | 319 | # BizTalk build output 320 | *.btp.cs 321 | *.btm.cs 322 | *.odx.cs 323 | *.xsd.cs 324 | 325 | # OpenCover UI analysis results 326 | OpenCover/ 327 | 328 | # Azure Stream Analytics local run output 329 | ASALocalRun/ 330 | 331 | # MSBuild Binary and Structured Log 332 | *.binlog 333 | 334 | # NVidia Nsight GPU debugger configuration file 335 | *.nvuser 336 | 337 | # MFractors (Xamarin productivity tool) working folder 338 | .mfractor/ 339 | 340 | # Local History for Visual Studio 341 | .localhistory/ 342 | 343 | # BeatPulse healthcheck temp database 344 | healthchecksdb 345 | 346 | # Backup folder for Package Reference Convert tool in Visual Studio 2017 347 | MigrationBackup/ 348 | 349 | # Ionide (cross platform F# VS Code tools) working folder 350 | .ionide/ 351 | 352 | # rider idea files 353 | .idea/ -------------------------------------------------------------------------------- /.whitesource: -------------------------------------------------------------------------------- 1 | { 2 | "scanSettings": { 3 | "baseBranches": [] 4 | }, 5 | "checkRunSettings": { 6 | "vulnerableCheckRunConclusionLevel": "failure", 7 | "displayMode": "diff", 8 | "useMendCheckNames": true 9 | }, 10 | "issueSettings": { 11 | "minSeverityLevel": "LOW", 12 | "issueType": "DEPENDENCY" 13 | } 14 | } -------------------------------------------------------------------------------- /CLA.md: -------------------------------------------------------------------------------- 1 | # Contributor License Agreement 2 | 3 | Thank you for your interest in contributing to the **CompreFace** (“Material”) by Exadel, Inc. ("We" or "Us"). The present 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. 4 | 5 | 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. 6 | 7 | ## 1. Definitions 8 | a. "You" means the individual Сopyright owner who Submits a Contribution to Us. 9 | 10 | b. "Contribution" means source code and any other copyrightable materials Submitted by you to Us, including any associated comments and documentation. 11 | 12 | c. "Copyright" means all rights protecting works of authorship, including copyright, moral and neighboring rights, as appropriate, for the full term of their existence. 13 | 14 | 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. 15 | 16 | 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." 17 | 18 | ## 2. Grant of Copyright License 19 | 20 | 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. 21 | 22 | ## 3. Grant of Patent License 23 | 24 | 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. 25 | 26 | ## 4. Other rights reserved 27 | 28 | 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. 29 | 30 | ## 5. Originality of Contributions 31 | 32 | 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. 33 | 34 | ## 6. Notice to Us 35 | 36 | 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. 37 | 38 | ## 7. Disclaimer 39 | 40 | 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. 41 | 42 | ## 8. Consequential Damage Waiver 43 | 44 | 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. 45 | 46 | 47 | ## 9. Information About Submissions 48 | 49 | 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. 50 | 51 | ## 11. Miscellaneous 52 | 53 | 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. 54 | -------------------------------------------------------------------------------- /ComprefaceNetSdk.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 17 4 | VisualStudioVersion = 17.4.33205.214 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Exadel.Compreface", "Exadel.Compreface\Exadel.Compreface.csproj", "{F056624D-D598-4D8A-82C3-D64A7E395B3E}" 7 | EndProject 8 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Exadel.Compreface.UnitTests", "Exadel.Compreface.UnitTests\Exadel.Compreface.UnitTests.csproj", "{BA070E01-7A81-40D5-A470-740DACBF3B01}" 9 | EndProject 10 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Exadel.Compreface.AcceptenceTests", "Exadel.Compreface.AcceptenceTests\Exadel.Compreface.AcceptenceTests.csproj", "{84E10A9B-1C7A-4C63-916C-31A2A0C784DB}" 11 | EndProject 12 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Example", "Example\Example.csproj", "{84797413-7869-42E3-A017-06F1D02ACBE5}" 13 | EndProject 14 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "RecognitionExampleApp", "RecognitionExampleApp\RecognitionExampleApp.csproj", "{655124E0-C7C4-48D6-85C2-05CA45F8E804}" 15 | EndProject 16 | Global 17 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 18 | Debug|Any CPU = Debug|Any CPU 19 | Release|Any CPU = Release|Any CPU 20 | EndGlobalSection 21 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 22 | {F056624D-D598-4D8A-82C3-D64A7E395B3E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 23 | {F056624D-D598-4D8A-82C3-D64A7E395B3E}.Debug|Any CPU.Build.0 = Debug|Any CPU 24 | {F056624D-D598-4D8A-82C3-D64A7E395B3E}.Release|Any CPU.ActiveCfg = Release|Any CPU 25 | {F056624D-D598-4D8A-82C3-D64A7E395B3E}.Release|Any CPU.Build.0 = Release|Any CPU 26 | {BA070E01-7A81-40D5-A470-740DACBF3B01}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 27 | {BA070E01-7A81-40D5-A470-740DACBF3B01}.Debug|Any CPU.Build.0 = Debug|Any CPU 28 | {BA070E01-7A81-40D5-A470-740DACBF3B01}.Release|Any CPU.ActiveCfg = Release|Any CPU 29 | {BA070E01-7A81-40D5-A470-740DACBF3B01}.Release|Any CPU.Build.0 = Release|Any CPU 30 | {84E10A9B-1C7A-4C63-916C-31A2A0C784DB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 31 | {84E10A9B-1C7A-4C63-916C-31A2A0C784DB}.Debug|Any CPU.Build.0 = Debug|Any CPU 32 | {84E10A9B-1C7A-4C63-916C-31A2A0C784DB}.Release|Any CPU.ActiveCfg = Release|Any CPU 33 | {84E10A9B-1C7A-4C63-916C-31A2A0C784DB}.Release|Any CPU.Build.0 = Release|Any CPU 34 | {84797413-7869-42E3-A017-06F1D02ACBE5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 35 | {84797413-7869-42E3-A017-06F1D02ACBE5}.Debug|Any CPU.Build.0 = Debug|Any CPU 36 | {84797413-7869-42E3-A017-06F1D02ACBE5}.Release|Any CPU.ActiveCfg = Release|Any CPU 37 | {84797413-7869-42E3-A017-06F1D02ACBE5}.Release|Any CPU.Build.0 = Release|Any CPU 38 | {655124E0-C7C4-48D6-85C2-05CA45F8E804}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 39 | {655124E0-C7C4-48D6-85C2-05CA45F8E804}.Debug|Any CPU.Build.0 = Debug|Any CPU 40 | {655124E0-C7C4-48D6-85C2-05CA45F8E804}.Release|Any CPU.ActiveCfg = Release|Any CPU 41 | {655124E0-C7C4-48D6-85C2-05CA45F8E804}.Release|Any CPU.Build.0 = Release|Any CPU 42 | EndGlobalSection 43 | GlobalSection(SolutionProperties) = preSolution 44 | HideSolutionNode = FALSE 45 | EndGlobalSection 46 | GlobalSection(ExtensibilityGlobals) = postSolution 47 | SolutionGuid = {4F554D1E-BD8B-4DB5-9474-64B2BE1D2D8C} 48 | EndGlobalSection 49 | EndGlobal 50 | -------------------------------------------------------------------------------- /Exadel.Compreface.AcceptenceTests/CompreFaceClientTests/CompreFaceClientTests.cs: -------------------------------------------------------------------------------- 1 | using Exadel.Compreface.Clients.CompreFaceClient; 2 | using static Exadel.Compreface.AcceptenceTests.UrlConstConfig; 3 | using Exadel.Compreface.Services; 4 | using Exadel.Compreface.Services.RecognitionService; 5 | using Exadel.Compreface.Exceptions; 6 | 7 | namespace Exadel.Compreface.AcceptenceTests.CompreFaceClientTests 8 | { 9 | public class CompreFaceClientTests 10 | { 11 | private readonly ICompreFaceClient _compreFaceClient; 12 | 13 | public CompreFaceClientTests() 14 | { 15 | _compreFaceClient = new CompreFaceClient(DOMAIN, PORT); 16 | } 17 | 18 | [Fact] 19 | public void CompreFaceClient_SetFaceDetectionService_ReturnsProperService() 20 | { 21 | //Act 22 | var service = _compreFaceClient.GetCompreFaceService(API_KEY_DETECTION_SERVICE); 23 | 24 | //Assert 25 | Assert.IsType(service); 26 | } 27 | 28 | [Fact] 29 | public void CompreFaceClient_SetFaceVerificationService_ReturnsProperService() 30 | { 31 | //Act 32 | var service = _compreFaceClient.GetCompreFaceService(API_KEY_VERIFICATION_SERVICE); 33 | 34 | //Assert 35 | Assert.IsType(service); 36 | } 37 | 38 | [Fact] 39 | public void CompreFaceClient_SetRecognitionService_ReturnsProperService() 40 | { 41 | //Act 42 | var service = _compreFaceClient.GetCompreFaceService(API_KEY_RECOGNITION_SERVICE); 43 | 44 | //Assert 45 | Assert.IsType(service); 46 | } 47 | 48 | [Fact] 49 | public void CompreFaceClient_SetRecognitionService_IsBelongsToDefiniteAttribute() 50 | { 51 | //Arrange 52 | var testApiKey = Guid.NewGuid().ToString(); 53 | 54 | //Act 55 | var func = () => _compreFaceClient.GetCompreFaceService(testApiKey); 56 | 57 | //Assert 58 | Assert.Throws(func); 59 | } 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /Exadel.Compreface.AcceptenceTests/CompreFaceClientTests/CompreFaceClientWithConfigTests.cs: -------------------------------------------------------------------------------- 1 | using Exadel.Compreface.Clients.CompreFaceClient; 2 | using Exadel.Compreface.Exceptions; 3 | using Exadel.Compreface.Services.RecognitionService; 4 | using Exadel.Compreface.Services; 5 | using Microsoft.Extensions.Configuration; 6 | using Microsoft.Extensions.Hosting; 7 | using Microsoft.Extensions.DependencyInjection; 8 | 9 | namespace Exadel.Compreface.AcceptenceTests.CompreFaceClientTests 10 | { 11 | public class CompreFaceClientWithConfigTests 12 | { 13 | private readonly ICompreFaceClient _compreFaceClient; 14 | private readonly IConfiguration _configuration; 15 | 16 | public CompreFaceClientWithConfigTests() 17 | { 18 | var host = Host.CreateDefaultBuilder().Build(); 19 | var serviceProvider = host.Services; 20 | 21 | _configuration = serviceProvider.GetService()!; 22 | 23 | _compreFaceClient = new CompreFaceClient(_configuration, "Domain", "Port"); 24 | } 25 | 26 | [Fact] 27 | public void CompreFaceClientWithConfig_SetFaceDetectionService_ReturnsProperService() 28 | { 29 | //Act 30 | var service = _compreFaceClient.GetCompreFaceService(_configuration, "FaceDetectionApiKey"); 31 | 32 | //Assert 33 | Assert.IsType(service); 34 | } 35 | 36 | [Fact] 37 | public void CompreFaceClientWithConfig_SetFaceVerificationService_ReturnsProperService() 38 | { 39 | //Act 40 | var service = _compreFaceClient.GetCompreFaceService(_configuration, "FaceVerificationApiKey"); 41 | 42 | //Assert 43 | Assert.IsType(service); 44 | } 45 | 46 | [Fact] 47 | public void CompreFaceClientWithConfig_SetRecognitionService_ReturnsProperService() 48 | { 49 | //Act 50 | var service = _compreFaceClient.GetCompreFaceService(_configuration, "FaceRecognitionApiKey"); 51 | 52 | //Assert 53 | Assert.IsType(service); 54 | } 55 | 56 | [Fact] 57 | public void CompreFaceClientWithConfig_SetRecognitionService_IsBelongsToDefiniteAttribute() 58 | { 59 | //Arrange 60 | var testApiKey = Guid.NewGuid().ToString(); 61 | 62 | //Act 63 | var func = () => _compreFaceClient.GetCompreFaceService(testApiKey); 64 | 65 | //Assert 66 | Assert.Throws(func); 67 | } 68 | 69 | [Fact] 70 | public void CompreFaceClientWithConfig_TakesNullForDOMAINSection_ThrowsArgumentNullException() 71 | { 72 | // Act 73 | var func = () => new CompreFaceClient(_configuration, null, "Port").GetCompreFaceService(_configuration, "FaceDetectionApiKey"); 74 | 75 | // Assert 76 | Assert.Throws(func); 77 | } 78 | 79 | [Fact] 80 | public void CompreFaceClientWithConfig_TakesNullForPORTSection_ThrowsArgumentNullException() 81 | { 82 | // Act 83 | var func = () => new CompreFaceClient(_configuration, "Domain", null).GetCompreFaceService(_configuration, "FaceDetectionApiKey"); 84 | 85 | // Assert 86 | Assert.Throws(func); 87 | } 88 | 89 | [Fact] 90 | public void CompreFaceClientWithConfig_TakesNullForAPIKEYSection_ThrowsArgumentNullException() 91 | { 92 | // Act 93 | var func = () => new CompreFaceClient(_configuration, "Domain", "Port").GetCompreFaceService(_configuration, null!); 94 | 95 | // Assert 96 | Assert.Throws(func); 97 | } 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /Exadel.Compreface.AcceptenceTests/CompreFaceClientTests/TestService.cs: -------------------------------------------------------------------------------- 1 | namespace Exadel.Compreface.AcceptenceTests.CompreFaceClientTests 2 | { 3 | internal class TestService 4 | { 5 | public TestService() {} 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /Exadel.Compreface.AcceptenceTests/ConfigurationTest/ComprefaceConfigurationTest.cs: -------------------------------------------------------------------------------- 1 | using Exadel.Compreface.Clients.CompreFaceClient; 2 | using Exadel.Compreface.Services; 3 | using static Exadel.Compreface.AcceptenceTests.UrlConstConfig; 4 | 5 | namespace Exadel.Compreface.AcceptenceTests.ConfigurationTest 6 | { 7 | public class ComprefaceConfigurationTest 8 | { 9 | [Fact] 10 | public void ConfigConstructor_TakesNullForPORTProperty_ThrowsArgumentNullException() 11 | { 12 | // Act 13 | var func = () => new CompreFaceClient(DOMAIN, null); 14 | 15 | // Assert 16 | Assert.Throws(func); 17 | } 18 | 19 | [Fact] 20 | public void ConfigConstructor_TakesNullForDOMAINProperty_ThrowsArgumentNullException() 21 | { 22 | // Act 23 | var func = () => new CompreFaceClient(null, PORT); 24 | 25 | // Assert 26 | Assert.Throws(func); 27 | } 28 | 29 | [Fact] 30 | public void ConfigConstructor_TakesNullForAPIKEYProperty_ThrowsArgumentNullException() 31 | { 32 | // Act 33 | var func = () => new CompreFaceClient(DOMAIN, PORT).GetCompreFaceService(null!); 34 | 35 | // Assert 36 | Assert.Throws(func); 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /Exadel.Compreface.AcceptenceTests/Exadel.Compreface.AcceptenceTests.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net6.0;net7.0 5 | enable 6 | enable 7 | 8 | false 9 | 10 | 11 | 12 | 13 | 14 | 15 | runtime; build; native; contentfiles; analyzers; buildtransitive 16 | all 17 | 18 | 19 | runtime; build; native; contentfiles; analyzers; buildtransitive 20 | all 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | PreserveNewest 31 | 32 | 33 | 34 | 35 | -------------------------------------------------------------------------------- /Exadel.Compreface.AcceptenceTests/Resources/Images/pexels-14344696.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/exadel-inc/compreface-net-sdk/5e2275353ef59688e9d628d5060ee7d8604f9912/Exadel.Compreface.AcceptenceTests/Resources/Images/pexels-14344696.jpg -------------------------------------------------------------------------------- /Exadel.Compreface.AcceptenceTests/Resources/Images/pexels-jonathan-yakubu.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/exadel-inc/compreface-net-sdk/5e2275353ef59688e9d628d5060ee7d8604f9912/Exadel.Compreface.AcceptenceTests/Resources/Images/pexels-jonathan-yakubu.jpg -------------------------------------------------------------------------------- /Exadel.Compreface.AcceptenceTests/Resources/Images/pexels.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/exadel-inc/compreface-net-sdk/5e2275353ef59688e9d628d5060ee7d8604f9912/Exadel.Compreface.AcceptenceTests/Resources/Images/pexels.jpg -------------------------------------------------------------------------------- /Exadel.Compreface.AcceptenceTests/Services/DetectionServiceTest.cs: -------------------------------------------------------------------------------- 1 | using Exadel.Compreface.Clients.CompreFaceClient; 2 | using Exadel.Compreface.DTOs.FaceDetectionDTOs.FaceDetection; 3 | using Exadel.Compreface.Exceptions; 4 | using Exadel.Compreface.Services; 5 | using Exadel.Compreface.Services.Interfaces; 6 | using static Exadel.Compreface.AcceptenceTests.UrlConstConfig; 7 | 8 | namespace Exadel.Compreface.AcceptenceTests.Services 9 | { 10 | public class DetectionServiceTest 11 | { 12 | private readonly IDetectionService _faceDetectionService; 13 | 14 | private readonly FaceDetectionRequestByFilePath _faceDetectionRequest; 15 | private readonly FaceDetectionBase64Request _faceDetectionBase64Request; 16 | private readonly FaceDetectionRequestByFileUrl _faceDetectionFromURIRequest; 17 | 18 | 19 | public DetectionServiceTest() 20 | { 21 | var client = new CompreFaceClient(DOMAIN, PORT); 22 | var detProbThreshold = 0.85m; 23 | var status = true; 24 | var limit = 0; 25 | var facePlugins = new List() 26 | { 27 | "landmarks", 28 | "gender", 29 | "age", 30 | "detector", 31 | "calculator" 32 | }; 33 | 34 | _faceDetectionService = client.GetCompreFaceService(API_KEY_DETECTION_SERVICE); 35 | _faceDetectionRequest = new FaceDetectionRequestByFilePath 36 | { 37 | FilePath = FILE_PATH, 38 | DetProbThreshold = detProbThreshold, 39 | FacePlugins = facePlugins, 40 | Status = status, 41 | Limit = limit 42 | }; 43 | 44 | _faceDetectionFromURIRequest = new FaceDetectionRequestByFileUrl 45 | { 46 | FileUrl = FILE_URL, 47 | DetProbThreshold = detProbThreshold, 48 | FacePlugins = facePlugins, 49 | Status = status, 50 | Limit = limit 51 | }; 52 | 53 | _faceDetectionBase64Request = new FaceDetectionBase64Request() 54 | { 55 | File = IMAGE_BASE64_STRING, 56 | DetProbThreshold = detProbThreshold, 57 | FacePlugins = facePlugins, 58 | Status = status, 59 | Limit = limit 60 | }; 61 | } 62 | 63 | [Fact] 64 | public async Task DetectAsync_TakesRequestModel_ReturnsProperResponseModel() 65 | { 66 | // Act 67 | var response = await _faceDetectionService.DetectAsync(_faceDetectionRequest); 68 | 69 | // Assert 70 | Assert.IsType(response); 71 | } 72 | 73 | [Fact] 74 | public async Task DetectAsync_TakesRequestModel_ReturnsNotNull() 75 | { 76 | // Act 77 | var response = await _faceDetectionService.DetectAsync(_faceDetectionRequest); 78 | 79 | // Assert 80 | Assert.NotNull(response); 81 | } 82 | 83 | [Fact] 84 | public async Task DetectAsync_TakesNullRequest_ThrowsException() 85 | { 86 | // Act 87 | var func = async () => await _faceDetectionService.DetectAsync((FaceDetectionRequestByFilePath)null!); 88 | 89 | // Assert 90 | await Assert.ThrowsAsync(func); 91 | } 92 | 93 | [Fact] 94 | public async Task DetectAsync_TakesNullRequest_ThrowsServiceException() 95 | { 96 | // Act 97 | var detectRequest = new FaceDetectionRequestByFilePath() 98 | { 99 | FilePath = PATH_OF_WRONG_FILE, 100 | DetProbThreshold = 0.81m, 101 | FacePlugins = new List() 102 | { 103 | "landmarks", 104 | "gender", 105 | "age", 106 | "detector", 107 | "calculator" 108 | }, 109 | Status = true, 110 | Limit = 0 111 | }; 112 | 113 | // Act 114 | var func = async () => await _faceDetectionService.DetectAsync(detectRequest); 115 | 116 | // Assert 117 | await Assert.ThrowsAsync(func); 118 | } 119 | 120 | [Fact] 121 | public async Task DetectBase64Async_TakesRequestModel_ReturnsProperResponseModel() 122 | { 123 | // Act 124 | var response = await _faceDetectionService.DetectAsync(_faceDetectionBase64Request); 125 | 126 | // Assert 127 | Assert.IsType(response); 128 | } 129 | 130 | [Fact] 131 | public async Task DetectBase64Async_TakesRequestModel_ReturnsNotNull() 132 | { 133 | // Act 134 | var response = await _faceDetectionService.DetectAsync(_faceDetectionBase64Request); 135 | 136 | // Assert 137 | Assert.NotNull(response); 138 | } 139 | 140 | [Fact] 141 | public async Task DetectBase64Async_TakesNullRequest_ThrowsException() 142 | { 143 | // Act 144 | var func = async () => await _faceDetectionService.DetectAsync((FaceDetectionBase64Request)null!); 145 | 146 | // Assert 147 | await Assert.ThrowsAsync(func); 148 | } 149 | 150 | [Fact] 151 | public async Task DetectBase64Async_TakesNullRequest_ThrowsServiceException() 152 | { 153 | // Act 154 | var detectRequest = new FaceDetectionBase64Request() 155 | { 156 | File = WRONG_BASE64_IMAGE, 157 | DetProbThreshold = 0.81m, 158 | FacePlugins = new List() 159 | { 160 | "landmarks", 161 | "gender", 162 | "age", 163 | "detector", 164 | "calculator" 165 | }, 166 | Status = true, 167 | Limit = 0 168 | }; 169 | 170 | // Act 171 | var func = async () => await _faceDetectionService.DetectAsync(detectRequest); 172 | 173 | // Assert 174 | await Assert.ThrowsAsync(func); 175 | } 176 | 177 | [Fact] 178 | public async Task DetectFromURIAsync_TakesRequestModel_ReturnsProperResponseModel() 179 | { 180 | // Act 181 | var response = await _faceDetectionService.DetectAsync(_faceDetectionFromURIRequest); 182 | 183 | // Assert 184 | Assert.IsType(response); 185 | } 186 | 187 | [Fact] 188 | public async Task DetectFromURIAsync_TakesRequestModel_ReturnsNotNull() 189 | { 190 | // Act 191 | var response = await _faceDetectionService.DetectAsync(_faceDetectionFromURIRequest); 192 | 193 | // Assert 194 | Assert.NotNull(response); 195 | } 196 | 197 | [Fact] 198 | public async Task DetectFromURIAsync_TakesNullRequest_ThrowsException() 199 | { 200 | // Act 201 | var func = async () => await _faceDetectionService.DetectAsync((FaceDetectionRequestByFileUrl)null!); 202 | 203 | // Assert 204 | await Assert.ThrowsAsync(func); 205 | } 206 | 207 | [Fact] 208 | public async Task DetectFromURIAsync_TakesNullRequest_ThrowsServiceException() 209 | { 210 | // Act 211 | var detectRequest = new FaceDetectionRequestByFileUrl() 212 | { 213 | FileUrl = WRONG_FILE_URL, 214 | DetProbThreshold = 0.81m, 215 | FacePlugins = new List() 216 | { 217 | "landmarks", 218 | "gender", 219 | "age", 220 | "detector", 221 | "calculator" 222 | }, 223 | Status = true, 224 | Limit = 0 225 | }; 226 | 227 | // Act 228 | var func = async () => await _faceDetectionService.DetectAsync(detectRequest); 229 | 230 | // Assert 231 | await Assert.ThrowsAsync(func); 232 | } 233 | } 234 | } 235 | -------------------------------------------------------------------------------- /Exadel.Compreface.AcceptenceTests/Services/RecognitionServiceTests/FaceCollectionTest/FaceCollectionTestBeforeAfter.cs: -------------------------------------------------------------------------------- 1 | using Exadel.Compreface.Clients.CompreFaceClient; 2 | using Exadel.Compreface.DTOs.FaceCollectionDTOs.DeleteAllSubjectExamples; 3 | using Exadel.Compreface.DTOs.SubjectDTOs.AddSubject; 4 | using Exadel.Compreface.DTOs.SubjectDTOs.DeleteSubject; 5 | using Exadel.Compreface.Services.RecognitionService; 6 | using System.Reflection; 7 | using Xunit.Sdk; 8 | using static Exadel.Compreface.AcceptenceTests.UrlConstConfig; 9 | 10 | namespace Exadel.Compreface.AcceptenceTests.Services.RecognitionServiceTests 11 | { 12 | [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false, Inherited = true)] 13 | public class FaceCollectionTestBeforeAfter : BeforeAfterTestAttribute 14 | { 15 | private readonly ICompreFaceClient _client; 16 | private readonly RecognitionService _recognitionService; 17 | 18 | public FaceCollectionTestBeforeAfter() 19 | { 20 | _client = new CompreFaceClient(DOMAIN, PORT); 21 | _recognitionService = _client.GetCompreFaceService(API_KEY_RECOGNITION_SERVICE); 22 | } 23 | 24 | public async override void Before(MethodInfo methodUnderTest) 25 | { 26 | await _recognitionService.FaceCollection.DeleteAllAsync(new DeleteAllExamplesRequest() { Subject = TEST_SUBJECT_EXAMPLE_NAME }); 27 | 28 | await _recognitionService.Subject.AddAsync( 29 | new AddSubjectRequest() { Subject = TEST_SUBJECT_EXAMPLE_NAME }); 30 | } 31 | 32 | public async override void After(MethodInfo methodUnderTest) 33 | { 34 | await _recognitionService.Subject.DeleteAsync(new DeleteSubjectRequest() { ActualSubject = TEST_SUBJECT_EXAMPLE_NAME }); 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /Exadel.Compreface.AcceptenceTests/Services/RecognitionServiceTests/SubjectTests.cs: -------------------------------------------------------------------------------- 1 | using Exadel.Compreface.Clients.CompreFaceClient; 2 | using Exadel.Compreface.DTOs.SubjectDTOs.AddSubject; 3 | using Exadel.Compreface.DTOs.SubjectDTOs.DeleteAllSubjects; 4 | using Exadel.Compreface.DTOs.SubjectDTOs.DeleteSubject; 5 | using Exadel.Compreface.DTOs.SubjectDTOs.GetSubjectList; 6 | using Exadel.Compreface.DTOs.SubjectDTOs.RenameSubject; 7 | using Exadel.Compreface.Exceptions; 8 | using Exadel.Compreface.Services.Interfaces; 9 | using Exadel.Compreface.Services.RecognitionService; 10 | using static Exadel.Compreface.AcceptenceTests.UrlConstConfig; 11 | 12 | namespace Exadel.Compreface.AcceptenceTests.Services.RecognitionServiceTests 13 | { 14 | public class SubjectTests 15 | { 16 | private readonly ISubject _subjectService; 17 | 18 | private readonly AddSubjectRequest _addSubjectRequest; 19 | private readonly RenameSubjectRequest _renameSubjectRequest; 20 | private readonly DeleteSubjectRequest _deleteSubjectRequest; 21 | private readonly DeleteSubjectRequest _renamedSubjectDeleteRequest; 22 | 23 | public SubjectTests() 24 | { 25 | var client = new CompreFaceClient(DOMAIN, PORT); 26 | var subjectName = TEST_SUBJECT_NAME; 27 | var renamedSubjectName = RENAMED_SUBJECT_NAME; 28 | 29 | _subjectService = client.GetCompreFaceService(API_KEY_RECOGNITION_SERVICE).Subject; 30 | 31 | _addSubjectRequest = new AddSubjectRequest 32 | { 33 | Subject = subjectName 34 | }; 35 | 36 | _renameSubjectRequest = new RenameSubjectRequest 37 | { 38 | CurrentSubject = subjectName, 39 | Subject = renamedSubjectName 40 | }; 41 | 42 | _deleteSubjectRequest = new DeleteSubjectRequest 43 | { 44 | ActualSubject = subjectName 45 | }; 46 | 47 | _renamedSubjectDeleteRequest = new DeleteSubjectRequest 48 | { 49 | ActualSubject = renamedSubjectName 50 | }; 51 | } 52 | 53 | [Fact] 54 | public async Task GetAllAsync_Executes_ReturnsProperResponseModel() 55 | { 56 | // Act 57 | var response = await _subjectService.ListAsync(); 58 | 59 | // Assert 60 | Assert.IsType(response); 61 | } 62 | 63 | [Fact] 64 | public async Task GetAllAsync_Executes_ReturnsNotNull() 65 | { 66 | // Act 67 | var response = await _subjectService.ListAsync(); 68 | 69 | // Assert 70 | Assert.NotNull(response); 71 | } 72 | 73 | [Fact] 74 | public async Task AddAsync_TakesRequestModel_ReturnsProperResponseModel() 75 | { 76 | // Act 77 | var response = await _subjectService.AddAsync(_addSubjectRequest); 78 | 79 | // Assert 80 | Assert.IsType(response); 81 | 82 | // Clear 83 | await _subjectService.DeleteAsync(new DeleteSubjectRequest { ActualSubject = _addSubjectRequest.Subject }); 84 | } 85 | 86 | [Fact] 87 | public async Task AddAsync_TakesRequestModel_ReturnsNotNull() 88 | { 89 | // Act 90 | var response = await _subjectService.AddAsync(_addSubjectRequest); 91 | 92 | // Assert 93 | Assert.NotNull(response); 94 | await _subjectService.DeleteAsync(new DeleteSubjectRequest { ActualSubject = _addSubjectRequest.Subject }); 95 | } 96 | 97 | [Fact] 98 | public async Task AddAsync_TakesNullRequestModel_ThrowsNullReferenceException() 99 | { 100 | // Act 101 | var func = async () => await _subjectService.AddAsync(null!); 102 | 103 | // Assert 104 | await Assert.ThrowsAsync(func); 105 | } 106 | 107 | [Fact] 108 | public async Task AddAsync_TakesNullRequestModel_ThrowsServiceException() 109 | { 110 | //Arrange 111 | var addSubjectRequest = new AddSubjectRequest 112 | { 113 | Subject = TEST_SUBJECT_NAME 114 | }; 115 | await _subjectService.AddAsync(addSubjectRequest); 116 | 117 | // Act 118 | var func = async () => await _subjectService.AddAsync(null!); 119 | 120 | // Assert 121 | await Assert.ThrowsAsync(func); 122 | 123 | // Clear 124 | await _subjectService.DeleteAsync(new DeleteSubjectRequest { ActualSubject = addSubjectRequest.Subject }); 125 | } 126 | 127 | [Fact] 128 | public async Task RenameAsync_TakesRequestModel_ReturnsProperResponseModel() 129 | { 130 | // Arrange 131 | await _subjectService.AddAsync(_addSubjectRequest); 132 | 133 | // Act 134 | var response = await _subjectService.RenameAsync(_renameSubjectRequest); 135 | await _subjectService.DeleteAsync(_renamedSubjectDeleteRequest); 136 | 137 | // Assert 138 | Assert.IsType(response); 139 | } 140 | 141 | [Fact] 142 | public async Task RenameAsync_TakesRequestModel_ReturnsNotNull() 143 | { 144 | // Arrange 145 | await _subjectService.AddAsync(_addSubjectRequest); 146 | 147 | // Act 148 | var response = await _subjectService.RenameAsync(_renameSubjectRequest); 149 | await _subjectService.DeleteAsync(_renamedSubjectDeleteRequest); 150 | 151 | // Assert 152 | Assert.NotNull(response); 153 | } 154 | 155 | [Fact] 156 | public async Task RenameAsync_TakesNullRequestModel_ThrowsNullReferenceException() 157 | { 158 | // Act 159 | var func = async () => await _subjectService.RenameAsync(null!); 160 | 161 | // Assert 162 | await Assert.ThrowsAsync(func); 163 | } 164 | 165 | [Fact] 166 | public async Task RenameAsync_TakesNullRequestModel_ThrowsServiceException() 167 | { 168 | //Arrange 169 | var addSubjectRequest = new AddSubjectRequest 170 | { 171 | Subject = TEST_SUBJECT_NAME 172 | }; 173 | await _subjectService.AddAsync(addSubjectRequest); 174 | 175 | var renameSubjectRequest = new RenameSubjectRequest 176 | { 177 | CurrentSubject = addSubjectRequest.Subject, 178 | Subject = "" 179 | }; 180 | 181 | // Act 182 | var func = async () => await _subjectService.RenameAsync(renameSubjectRequest); 183 | 184 | // Assert 185 | await Assert.ThrowsAsync(func); 186 | 187 | // Clear 188 | await _subjectService.DeleteAsync(new DeleteSubjectRequest { ActualSubject = addSubjectRequest.Subject }); 189 | } 190 | 191 | [Fact] 192 | public async Task DeleteAsync_TakesRequestModel_ReturnsProperResponseModel() 193 | { 194 | // Arrange 195 | await _subjectService.AddAsync(_addSubjectRequest); 196 | 197 | // Act 198 | var response = await _subjectService.DeleteAsync(_deleteSubjectRequest); 199 | 200 | // Assert 201 | Assert.IsType(response); 202 | } 203 | 204 | [Fact] 205 | public async Task DeleteAsync_TakesRequestModel_ReturnsNotNull() 206 | { 207 | // Arrange 208 | await _subjectService.AddAsync(_addSubjectRequest); 209 | 210 | // Act 211 | var response = await _subjectService.DeleteAsync(_deleteSubjectRequest); 212 | 213 | // Assert 214 | Assert.NotNull(response); 215 | } 216 | 217 | [Fact] 218 | public async Task DeleteAsync_TakesNullRequestModel_ThrowsNullReferenceException() 219 | { 220 | // Act 221 | var func = async () => await _subjectService.DeleteAsync(null!); 222 | 223 | // Assert 224 | await Assert.ThrowsAsync(func); 225 | } 226 | 227 | [Fact] 228 | public async Task DeleteAsync_TakesNullRequestModel_ThrowsServiceException() 229 | { 230 | // Act 231 | var func = async () => await _subjectService.DeleteAsync(_deleteSubjectRequest); 232 | 233 | // Assert 234 | await Assert.ThrowsAsync(func); 235 | } 236 | 237 | [Fact] 238 | public async Task DeleteAllAsync_TakesRequestModel_ReturnsProperResponseModel() 239 | { 240 | // Act 241 | var response = await _subjectService.DeleteAllAsync(); 242 | 243 | // Assert 244 | Assert.IsType(response); 245 | } 246 | 247 | [Fact] 248 | public async Task DeleteAllAsync_TakesRequestModel_ReturnsNotNull() 249 | { 250 | // Act 251 | var response = await _subjectService.DeleteAllAsync(); 252 | 253 | // Assert 254 | Assert.NotNull(response); 255 | } 256 | 257 | [Fact] 258 | public async Task DeleteAllAsync_TakesNullRequestModel_ThrowsServiceException() 259 | { 260 | //Arrange 261 | var client = new CompreFaceClient(DOMAIN, PORT); 262 | 263 | var subjectService = client.GetCompreFaceService(API_KEY_DETECTION_SERVICE).Subject; 264 | 265 | // Act 266 | var func = async () => await subjectService.DeleteAllAsync(); 267 | 268 | // Assert 269 | await Assert.ThrowsAsync(func); 270 | } 271 | } 272 | } 273 | -------------------------------------------------------------------------------- /Exadel.Compreface.AcceptenceTests/Services/VerificationServiceTest.cs: -------------------------------------------------------------------------------- 1 | using Exadel.Compreface.Clients.CompreFaceClient; 2 | using Exadel.Compreface.DTOs.FaceVerificationDTOs.FaceVerification; 3 | using Exadel.Compreface.Exceptions; 4 | using Exadel.Compreface.Services; 5 | using Exadel.Compreface.Services.Interfaces; 6 | using static Exadel.Compreface.AcceptenceTests.UrlConstConfig; 7 | 8 | namespace Exadel.Compreface.AcceptenceTests.Services 9 | { 10 | public class VerificationServiceTest 11 | { 12 | private readonly IVerificationService _faceVerificationService; 13 | 14 | private readonly FaceVerificationRequestByFilePath _faceVerificationRequest; 15 | private readonly FaceVerificationWithBase64Request _faceVerificationBase64Request; 16 | private readonly FaceVerificationRequestByFileUrl _faceVerificationFromURLRequest; 17 | 18 | public VerificationServiceTest() 19 | { 20 | var client = new CompreFaceClient(DOMAIN, PORT); 21 | var detProbThreshold = 0.85m; 22 | var status = true; 23 | var limit = 0; 24 | var facePlugins = new List() 25 | { 26 | "landmarks", 27 | "gender", 28 | "age", 29 | "detector", 30 | "calculator" 31 | }; 32 | 33 | _faceVerificationService = client.GetCompreFaceService(API_KEY_VERIFICATION_SERVICE); 34 | _faceVerificationRequest = new FaceVerificationRequestByFilePath 35 | { 36 | SourceImageFilePath = FILE_PATH, 37 | TargetImageFilePath = FILE_PATH, 38 | DetProbThreshold = detProbThreshold, 39 | FacePlugins = facePlugins, 40 | Status = status, 41 | Limit = limit 42 | }; 43 | 44 | _faceVerificationFromURLRequest = new FaceVerificationRequestByFileUrl 45 | { 46 | SourceImageFileUrl = FILE_URL, 47 | TargetImageFileUrl = FILE_URL, 48 | DetProbThreshold = detProbThreshold, 49 | FacePlugins = facePlugins, 50 | Status = status, 51 | Limit = limit 52 | }; 53 | 54 | _faceVerificationBase64Request = new FaceVerificationWithBase64Request() 55 | { 56 | SourceImageWithBase64 = IMAGE_BASE64_STRING, 57 | TargetImageWithBase64 = IMAGE_BASE64_STRING, 58 | DetProbThreshold = detProbThreshold, 59 | FacePlugins = facePlugins, 60 | Status = status, 61 | Limit = limit 62 | }; 63 | } 64 | 65 | [Fact] 66 | public async Task VerifyAsync_TakesRequestModel_ReturnsProperResponseModel() 67 | { 68 | // Act 69 | var response = await _faceVerificationService.VerifyAsync(_faceVerificationRequest); 70 | 71 | // Assert 72 | Assert.IsType(response); 73 | } 74 | 75 | [Fact] 76 | public async Task VerifyAsync_TakesRequestModel_ReturnsNotNull() 77 | { 78 | // Act 79 | var response = await _faceVerificationService.VerifyAsync(_faceVerificationRequest); 80 | 81 | // Assert 82 | Assert.NotNull(response); 83 | } 84 | 85 | [Fact] 86 | public async Task VerifyAsync_TakesNullRequest_ThrowsException() 87 | { 88 | // Act 89 | var func = async () => await _faceVerificationService.VerifyAsync((FaceVerificationRequestByFilePath)null!); 90 | 91 | // Assert 92 | await Assert.ThrowsAsync(func); 93 | } 94 | 95 | [Fact] 96 | public async Task VerifyAsync_TakesNullRequest_ThrowsServiceException() 97 | { 98 | //Arrange 99 | var request = new FaceVerificationRequestByFilePath 100 | { 101 | SourceImageFilePath = TWO_FACES_IMAGE_PATH, 102 | TargetImageFilePath = FILE_PATH, 103 | DetProbThreshold = 0.81m, 104 | FacePlugins = new List() 105 | { 106 | "landmarks", 107 | "gender", 108 | "age", 109 | "detector", 110 | "calculator" 111 | }, 112 | Status = true, 113 | Limit = 0 114 | }; 115 | 116 | // Act 117 | var func = async () => await _faceVerificationService.VerifyAsync(request); 118 | 119 | // Assert 120 | await Assert.ThrowsAsync(func); 121 | } 122 | 123 | [Fact] 124 | public async Task VerifyBase64Async_TakesRequestModel_ReturnsProperResponseModel() 125 | { 126 | // Act 127 | var response = await _faceVerificationService.VerifyAsync(_faceVerificationBase64Request); 128 | 129 | // Assert 130 | Assert.IsType(response); 131 | } 132 | 133 | [Fact] 134 | public async Task VerifyBase64Async_TakesRequestModel_ReturnsNotNull() 135 | { 136 | // Act 137 | var response = await _faceVerificationService.VerifyAsync(_faceVerificationBase64Request); 138 | 139 | // Assert 140 | Assert.NotNull(response); 141 | } 142 | 143 | [Fact] 144 | public async Task VerifyBase64Async_TakesNullRequest_ThrowsException() 145 | { 146 | // Act 147 | var func = async () => await _faceVerificationService.VerifyAsync((FaceVerificationWithBase64Request)null!); 148 | 149 | // Assert 150 | await Assert.ThrowsAsync(func); 151 | } 152 | 153 | [Fact] 154 | public async Task VerifyBase64Async_TakesNullRequest_ThrowsServiceException() 155 | { 156 | //Arrange 157 | var request = new FaceVerificationWithBase64Request() 158 | { 159 | SourceImageWithBase64 = TWO_FACES_IMAGE_BASE64, 160 | TargetImageWithBase64 = IMAGE_BASE64_STRING, 161 | DetProbThreshold = 0.81m, 162 | FacePlugins = new List() 163 | { 164 | "landmarks", 165 | "gender", 166 | "age", 167 | "detector", 168 | "calculator" 169 | }, 170 | Status = true, 171 | Limit = 0 172 | }; 173 | 174 | // Act 175 | var func = async () => await _faceVerificationService.VerifyAsync(request); 176 | 177 | // Assert 178 | await Assert.ThrowsAsync(func); 179 | } 180 | 181 | [Fact] 182 | public async Task VerifyFromURLAsync_TakesRequestModel_ReturnsProperResponseModel() 183 | { 184 | // Act 185 | var response = await _faceVerificationService.VerifyAsync(_faceVerificationFromURLRequest); 186 | 187 | // Assert 188 | Assert.IsType(response); 189 | } 190 | 191 | [Fact] 192 | public async Task VerifyFromURLAsync_TakesRequestModel_ReturnsNotNull() 193 | { 194 | // Act 195 | var response = await _faceVerificationService.VerifyAsync(_faceVerificationFromURLRequest); 196 | 197 | // Assert 198 | Assert.NotNull(response); 199 | } 200 | 201 | [Fact] 202 | public async Task VerifyFromURLAsync_TakesNullRequest_ThrowsException() 203 | { 204 | // Act 205 | var func = async () => await _faceVerificationService.VerifyAsync((FaceVerificationRequestByFileUrl)null!); 206 | 207 | // Assert 208 | await Assert.ThrowsAsync(func); 209 | } 210 | 211 | [Fact] 212 | public async Task VerifyFromURLAsync_TakesNullRequest_ThrowsServiceException() 213 | { 214 | //Arrange 215 | var request = new FaceVerificationRequestByFileUrl() 216 | { 217 | SourceImageFileUrl = FILE_URL, 218 | TargetImageFileUrl = WRONG_FILE_URL, 219 | DetProbThreshold = 0.81m, 220 | FacePlugins = new List() 221 | { 222 | "landmarks", 223 | "gender", 224 | "age", 225 | "detector", 226 | "calculator" 227 | }, 228 | Status = true, 229 | Limit = 0 230 | }; 231 | 232 | // Act 233 | var func = async () => await _faceVerificationService.VerifyAsync(request); 234 | 235 | // Assert 236 | await Assert.ThrowsAsync(func); 237 | } 238 | } 239 | } 240 | -------------------------------------------------------------------------------- /Exadel.Compreface.AcceptenceTests/Usings.cs: -------------------------------------------------------------------------------- 1 | global using Xunit; -------------------------------------------------------------------------------- /Exadel.Compreface.AcceptenceTests/appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "Domain": "http://localhost", 3 | "Port": "8000", 4 | "FaceDetectionApiKey": "00000000-0000-0000-0000-000000000003", 5 | "FaceVerificationApiKey": "00000000-0000-0000-0000-000000000004", 6 | "FaceRecognitionApiKey": "00000000-0000-0000-0000-000000000002" 7 | } -------------------------------------------------------------------------------- /Exadel.Compreface.UnitTests/ApiClientTest/ApiClientTests.FlurlHttpException.cs: -------------------------------------------------------------------------------- 1 | using Exadel.Compreface.DTOs.FaceCollectionDTOs.AddSubjectExample; 2 | using Exadel.Compreface.DTOs.SubjectDTOs.GetSubjectList; 3 | using Exadel.Compreface.Exceptions; 4 | using Flurl.Http; 5 | using Moq; 6 | using static Exadel.Compreface.UnitTests.Helpers.GetRandomStringHelper; 7 | 8 | 9 | namespace Exadel.Compreface.UnitTests.ApiClientTest; 10 | 11 | public partial class ApiClientTests 12 | { 13 | [Fact] 14 | public async Task GetJsonAsync_ShouldHandleFlurlHttpExceptionAndThrowServiceException() 15 | { 16 | _httpTest.RespondWith(ExceptionMessage, BadResponseStatusCode); 17 | 18 | var httpException = It.IsAny(); 19 | _httpTest.SimulateException(httpException); 20 | 21 | var responseCall = _apiClient.GetJsonAsync(requestUrl: RequestUrl); 22 | 23 | await Assert.ThrowsAsync(async () => await responseCall); 24 | } 25 | 26 | [Fact] 27 | public async Task PostJsonAsync_ShouldHandleFlurlHttpExceptionAndThrowServiceException() 28 | { 29 | var httpException = It.IsAny(); 30 | 31 | _httpTest.RespondWith(ExceptionMessage, BadResponseStatusCode); 32 | _httpTest.SimulateException(httpException); 33 | 34 | var responseCall = _apiClient.PostJsonAsync( 35 | requestUrl: RequestUrl, 36 | body: It.IsAny()); 37 | 38 | await Assert.ThrowsAsync(async () => await responseCall); 39 | } 40 | 41 | [Fact] 42 | public async Task PutJsonAsync_ShouldHandleFlurlHttpExceptionAndThrowServiceException() 43 | { 44 | var httpException = It.IsAny(); 45 | 46 | _httpTest.RespondWith(ExceptionMessage, BadResponseStatusCode); 47 | _httpTest.SimulateException(httpException); 48 | 49 | var responseCall = _apiClient.PutJsonAsync( 50 | requestUrl: RequestUrl, 51 | body: It.IsAny()); 52 | 53 | await Assert.ThrowsAsync(async () => await responseCall); 54 | } 55 | 56 | [Fact] 57 | public async Task DeleteJsonAsync_ShouldHandleFlurlHttpExceptionAndThrowServiceException() 58 | { 59 | var httpException = It.IsAny(); 60 | 61 | _httpTest.RespondWith(ExceptionMessage, BadResponseStatusCode); 62 | _httpTest.SimulateException(httpException); 63 | 64 | var responseCall = _apiClient.DeleteJsonAsync(requestUrl: RequestUrl); 65 | 66 | await Assert.ThrowsAsync(async () => await responseCall); 67 | } 68 | 69 | [Fact] 70 | public async Task PostMultipartAsync_ShouldHandleFlurlHttpExceptionAndThrowServiceException() 71 | { 72 | var httpException = It.IsAny(); 73 | 74 | _httpTest.RespondWith(ExceptionMessage, BadResponseStatusCode); 75 | _httpTest.SimulateException(httpException); 76 | 77 | var randomstring = GetRandomString(); 78 | 79 | var responseCall = _apiClient.PostMultipartAsync( 80 | requestUrl: RequestUrl, 81 | mp => mp.AddFile(randomstring, fileName: randomstring, path: randomstring)); 82 | 83 | await Assert.ThrowsAsync(async () => await responseCall); 84 | } 85 | 86 | [Fact] 87 | public async Task GetBytesFromRemoteAsync_ShouldHandleFlurlHttpExceptionAndThrowServiceException() 88 | { 89 | var httpException = It.IsAny(); 90 | 91 | _httpTest.RespondWith(ExceptionMessage, BadResponseStatusCode); 92 | _httpTest.SimulateException(httpException); 93 | 94 | var responseCall = _apiClient.GetBytesFromRemoteAsync(requestUrl: RequestUrl); 95 | 96 | await Assert.ThrowsAsync(async () => await responseCall); 97 | } 98 | } -------------------------------------------------------------------------------- /Exadel.Compreface.UnitTests/ApiClientTest/ApiClientTests.FlurlHttpTimeoutException.cs: -------------------------------------------------------------------------------- 1 | using Exadel.Compreface.DTOs.FaceCollectionDTOs.AddSubjectExample; 2 | using Exadel.Compreface.DTOs.SubjectDTOs.GetSubjectList; 3 | using Exadel.Compreface.Exceptions; 4 | using Flurl.Http; 5 | using Moq; 6 | using static Exadel.Compreface.UnitTests.Helpers.GetRandomStringHelper; 7 | 8 | 9 | namespace Exadel.Compreface.UnitTests.ApiClientTest; 10 | 11 | public partial class ApiClientTests 12 | { 13 | [Fact] 14 | public async Task GetJsonAsync_ShouldHandleFlurlHttpTimeoutExceptionAndThrowServiceException() 15 | { 16 | var httpException = It.IsAny(); 17 | 18 | _httpTest 19 | .SimulateTimeout() 20 | .SimulateException(httpException); 21 | 22 | var responseCall = _apiClient.GetJsonAsync(requestUrl: RequestUrl); 23 | 24 | await Assert.ThrowsAsync(async () => await responseCall); 25 | } 26 | 27 | [Fact] 28 | public async Task PostJsonAsync_ShouldHandleFlurlHttpTimeoutExceptionAndThrowServiceException() 29 | { 30 | var httpException = It.IsAny(); 31 | 32 | _httpTest 33 | .SimulateTimeout() 34 | .SimulateException(httpException); 35 | 36 | var responseCall = _apiClient.PostJsonAsync(requestUrl: RequestUrl, body: It.IsAny()); 37 | 38 | await Assert.ThrowsAsync(async () => await responseCall); 39 | } 40 | 41 | [Fact] 42 | public async Task PutJsonAsync_ShouldHandleFlurlHttpTimeoutExceptionAndThrowServiceException() 43 | { 44 | var httpException = It.IsAny(); 45 | 46 | _httpTest 47 | .SimulateTimeout() 48 | .SimulateException(httpException); 49 | 50 | var responseCall = _apiClient.PutJsonAsync( 51 | requestUrl: RequestUrl, 52 | body: It.IsAny()); 53 | 54 | await Assert.ThrowsAsync(async () => await responseCall); 55 | } 56 | 57 | [Fact] 58 | public async Task DeleteJsonAsync_ShouldHandleFlurlHttpTimeoutExceptionAndThrowServiceException() 59 | { 60 | var httpException = It.IsAny(); 61 | 62 | _httpTest 63 | .SimulateTimeout() 64 | .SimulateException(httpException); 65 | 66 | var responseCall = _apiClient.DeleteJsonAsync(requestUrl: RequestUrl); 67 | 68 | await Assert.ThrowsAsync(async () => await responseCall); 69 | } 70 | 71 | [Fact] 72 | public async Task PostMultipartAsync_ShouldHandleFlurlHttpTimeoutExceptionAndThrowServiceException() 73 | { 74 | var httpException = It.IsAny(); 75 | 76 | _httpTest 77 | .SimulateTimeout() 78 | .SimulateException(httpException); 79 | 80 | var randomstring = GetRandomString(); 81 | 82 | var responseCall = _apiClient.PostMultipartAsync( 83 | requestUrl: RequestUrl, 84 | mp => mp.AddFile(randomstring, fileName: randomstring, path: randomstring)); 85 | 86 | await Assert.ThrowsAsync(async () => await responseCall); 87 | } 88 | 89 | [Fact] 90 | public async Task GetBytesFromRemoteAsync_ShouldHandleFlurlHttpTimeoutExceptionAndThrowServiceException() 91 | { 92 | var httpException = It.IsAny(); 93 | 94 | _httpTest 95 | .SimulateTimeout() 96 | .SimulateException(httpException); 97 | 98 | var responseCall = _apiClient.GetBytesFromRemoteAsync(requestUrl: RequestUrl); 99 | 100 | await Assert.ThrowsAsync(async () => await responseCall); 101 | } 102 | } -------------------------------------------------------------------------------- /Exadel.Compreface.UnitTests/ApiClientTest/ApiClientTests.HttpVerbs.cs: -------------------------------------------------------------------------------- 1 | using Exadel.Compreface.DTOs.FaceCollectionDTOs.AddSubjectExample; 2 | using Exadel.Compreface.DTOs.SubjectDTOs.GetSubjectList; 3 | using Moq; 4 | using static Exadel.Compreface.UnitTests.Helpers.GetRandomStringHelper; 5 | 6 | 7 | namespace Exadel.Compreface.UnitTests.ApiClientTest; 8 | 9 | public partial class ApiClientTests 10 | { 11 | [Fact] 12 | public async Task GetJsonAsync_ShouldUseGETHttpMethod() 13 | { 14 | // simulating api calls here and constructing fake response 15 | _httpTest.RespondWith(SuccessMessage, SuccessResponseStatusCode); 16 | 17 | await _apiClient.GetJsonAsync(RequestUrl); 18 | 19 | _httpTest 20 | .ShouldHaveCalled(RequestUrl) 21 | .WithVerb(HttpMethod.Get); 22 | } 23 | 24 | [Fact] 25 | public async Task PostJsonAsync_ShouldUsePOSTHttpMethod() 26 | { 27 | // simulating api calls here and constructing fake response 28 | _httpTest.RespondWith(SuccessMessage, SuccessResponseStatusCode); 29 | 30 | await _apiClient.PostJsonAsync( 31 | requestUrl: RequestUrl, 32 | body: It.IsAny()); 33 | 34 | _httpTest 35 | .ShouldHaveCalled(RequestUrl) 36 | .WithVerb(HttpMethod.Post); 37 | } 38 | 39 | [Fact] 40 | public async Task PutJsonAsync_ShouldUsePUTHttpMethod() 41 | { 42 | // simulating api calls here and constructing fake response 43 | _httpTest.RespondWith(SuccessMessage, SuccessResponseStatusCode); 44 | 45 | await _apiClient.PutJsonAsync( 46 | requestUrl: RequestUrl, 47 | body: It.IsAny()); 48 | 49 | _httpTest 50 | .ShouldHaveCalled(RequestUrl) 51 | .WithVerb(HttpMethod.Put); 52 | } 53 | 54 | [Fact] 55 | public async Task DeleteJsonAsync_ShoulUseDELETEHttpMethod() 56 | { 57 | // simulating api calls here and constructing fake response 58 | _httpTest.RespondWith(SuccessMessage, SuccessResponseStatusCode); 59 | 60 | await _apiClient.DeleteJsonAsync(requestUrl: RequestUrl); 61 | 62 | _httpTest 63 | .ShouldHaveCalled(RequestUrl) 64 | .WithVerb(HttpMethod.Delete); 65 | } 66 | 67 | [Fact] 68 | public async Task PostMultipartAsync_ShouldUsePOSTHttpMethod() 69 | { 70 | var randomstring = GetRandomString(); 71 | 72 | // simulating api calls here and constructing fake response 73 | _httpTest.RespondWith(SuccessMessage, SuccessResponseStatusCode); 74 | 75 | await _apiClient.PostMultipartAsync(requestUrl: RequestUrl, mp => mp.AddFile(randomstring, fileName: randomstring, path: randomstring)); 76 | 77 | _httpTest 78 | .ShouldHaveCalled(RequestUrl) 79 | .WithVerb(HttpMethod.Post) 80 | .WithContentType("multipart/form-data"); 81 | } 82 | 83 | [Fact] 84 | public async Task GetBytesFromRemoteAsync_ShouldUseGETHttpMethod() 85 | { 86 | // simulating api calls here and constructing fake response 87 | _httpTest.RespondWith(SuccessMessage, SuccessResponseStatusCode); 88 | 89 | await _apiClient.GetBytesFromRemoteAsync( 90 | requestUrl: RequestUrl); 91 | 92 | _httpTest 93 | .ShouldHaveCalled(RequestUrl) 94 | .WithVerb(HttpMethod.Get); 95 | } 96 | } -------------------------------------------------------------------------------- /Exadel.Compreface.UnitTests/ApiClientTest/ApiClientTests.cs: -------------------------------------------------------------------------------- 1 | using Exadel.Compreface.Clients.ApiClient; 2 | using Exadel.Compreface.Configuration; 3 | using Flurl.Http.Testing; 4 | using static Exadel.Compreface.UnitTests.Helpers.GetRandomStringHelper; 5 | 6 | namespace Exadel.Compreface.UnitTests.ApiClientTest; 7 | 8 | public partial class ApiClientTests : IDisposable 9 | { 10 | private readonly HttpTest _httpTest; 11 | private readonly IApiClient _apiClient; 12 | 13 | public ApiClientTests() 14 | { 15 | var apiKey = GetRandomString(); 16 | var domain = GetRandomString(); 17 | var port = GetRandomString(); 18 | 19 | _httpTest = new HttpTest(); 20 | _apiClient = new ApiClient(new ComprefaceConfiguration(apiKey, domain, port)); 21 | 22 | } 23 | 24 | private const string RequestUrl = "http://site.com"; 25 | private const string ExceptionMessage = "{\"message\":\"Something bad happened!!!\",\"code\":20}"; 26 | private const string SuccessMessage = "{\"message\":\"Everything is good so far!!!\"}"; 27 | public const int BadResponseStatusCode = 400; 28 | public const int SuccessResponseStatusCode = 200; 29 | 30 | public void Dispose() 31 | { 32 | _httpTest.Dispose(); 33 | } 34 | } -------------------------------------------------------------------------------- /Exadel.Compreface.UnitTests/Exadel.Compreface.UnitTests.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | net6.0;net7.0 5 | enable 6 | enable 7 | 8 | false 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | runtime; build; native; contentfiles; analyzers; buildtransitive 18 | all 19 | 20 | 21 | runtime; build; native; contentfiles; analyzers; buildtransitive 22 | all 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /Exadel.Compreface.UnitTests/Helpers/GetRandomStringHelper.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | using Tynamix.ObjectFiller; 7 | 8 | namespace Exadel.Compreface.UnitTests.Helpers 9 | { 10 | public static class GetRandomStringHelper 11 | { 12 | public static string GetRandomString() 13 | { 14 | return new Filler().Create(); 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /Exadel.Compreface.UnitTests/Helpers/SetupAndVerifyTests.cs: -------------------------------------------------------------------------------- 1 | using Exadel.Compreface.Clients.ApiClient; 2 | using Flurl; 3 | using Flurl.Http.Content; 4 | using Moq; 5 | 6 | namespace Exadel.Compreface.UnitTests.Helpers 7 | { 8 | public abstract class SetupAndVerifyTests 9 | { 10 | public Mock ApiClientMock { get; set; } 11 | 12 | public SetupAndVerifyTests() 13 | { 14 | ApiClientMock = new Mock(); 15 | } 16 | 17 | public void SetupGetBytesFromRemote() 18 | { 19 | ApiClientMock.Setup(service => 20 | service.GetBytesFromRemoteAsync( 21 | It.IsAny(), 22 | It.IsAny(), 23 | It.IsAny())) 24 | .ReturnsAsync(Array.Empty()); 25 | } 26 | 27 | public void VerifyGetBytesFromRemote() 28 | { 29 | ApiClientMock.Verify(service => 30 | service.GetBytesFromRemoteAsync( 31 | It.IsAny(), 32 | It.IsAny(), 33 | It.IsAny()), Times.Once); 34 | } 35 | 36 | public void SetupGetJson() where TResponse : new() 37 | { 38 | ApiClientMock.Setup(service => 39 | service.GetJsonAsync( 40 | It.IsAny(), 41 | It.IsAny(), 42 | It.IsAny())) 43 | .ReturnsAsync(new TResponse()); 44 | } 45 | 46 | public void SetupGetJson() where TResponse : new() 47 | { 48 | ApiClientMock.Setup(service => 49 | service.GetJsonAsync( 50 | It.IsAny(), 51 | It.IsAny(), 52 | It.IsAny())) 53 | .ReturnsAsync(new TResponse()); 54 | } 55 | 56 | public void VerifyGetJson() 57 | { 58 | ApiClientMock.Verify(service => 59 | service.GetJsonAsync( 60 | It.IsAny(), 61 | It.IsAny(), 62 | It.IsAny()), Times.Once); 63 | } 64 | 65 | public void VerifyGetJson() 66 | { 67 | ApiClientMock.Verify(service => 68 | service.GetJsonAsync( 69 | It.IsAny(), 70 | It.IsAny(), 71 | It.IsAny()), Times.Once); 72 | } 73 | 74 | public void SetupPostMultipart() where TResponse : new() 75 | { 76 | ApiClientMock.Setup(service => 77 | service.PostMultipartAsync( 78 | It.IsAny(), 79 | It.IsAny>(), 80 | It.IsAny(), 81 | It.IsAny())) 82 | .ReturnsAsync(new TResponse()); 83 | } 84 | 85 | public void VerifyPostMultipart() 86 | { 87 | ApiClientMock.Verify(service => 88 | service.PostMultipartAsync( 89 | It.IsAny(), 90 | It.IsAny>(), 91 | It.IsAny(), 92 | It.IsAny()), Times.Once); 93 | } 94 | 95 | public void SetupPostJson() where TResponse : class, new() 96 | { 97 | 98 | ApiClientMock.Setup(service => 99 | service.PostJsonAsync( 100 | It.IsAny(), 101 | It.IsAny(), 102 | It.IsAny(), 103 | It.IsAny())) 104 | .ReturnsAsync(new TResponse()); 105 | } 106 | 107 | public void VerifyPostJson() where TResponse : class, new() 108 | { 109 | ApiClientMock.Verify(service => 110 | service.PostJsonAsync( 111 | It.IsAny(), 112 | It.IsAny(), 113 | It.IsAny(), 114 | It.IsAny()), Times.Once); 115 | } 116 | 117 | public void SetupPostJson() where TResponse : class, new() 118 | { 119 | if (typeof(TUrl).IsEquivalentTo(typeof(Url))) 120 | { 121 | ApiClientMock.Setup(service => 122 | service.PostJsonAsync( 123 | It.IsAny(), 124 | It.IsAny(), 125 | It.IsAny(), 126 | It.IsAny())) 127 | .ReturnsAsync(new TResponse()); 128 | } 129 | else if (typeof(TUrl).IsEquivalentTo(typeof(string))) 130 | { 131 | ApiClientMock.Setup(service => 132 | service.PostJsonAsync( 133 | It.IsAny(), 134 | It.IsAny(), 135 | It.IsAny(), 136 | It.IsAny())) 137 | .ReturnsAsync(new TResponse()); 138 | } 139 | else 140 | { 141 | throw new Exception("Only string and Url types are possible"); 142 | } 143 | } 144 | 145 | public void VerifyPostJson() where TResponse : class, new() 146 | { 147 | if (typeof(TUrl).IsEquivalentTo(typeof(Url))) 148 | { 149 | ApiClientMock.Verify(service => 150 | service.PostJsonAsync( 151 | It.IsAny(), 152 | It.IsAny(), 153 | It.IsAny(), 154 | It.IsAny()), Times.Once); 155 | } 156 | else if (typeof(TUrl).IsEquivalentTo(typeof(string))) 157 | { 158 | ApiClientMock.Verify(service => 159 | service.PostJsonAsync( 160 | It.IsAny(), 161 | It.IsAny(), 162 | It.IsAny(), 163 | It.IsAny()), Times.Once); 164 | } 165 | else 166 | { 167 | throw new Exception("Only string and Url types are possible"); 168 | } 169 | } 170 | 171 | public void SetupPutJson() where TResponse : class, new() 172 | { 173 | ApiClientMock.Setup(service => 174 | service.PutJsonAsync( 175 | It.IsAny(), 176 | It.IsAny(), 177 | It.IsAny(), 178 | It.IsAny())) 179 | .ReturnsAsync(new TResponse()); 180 | } 181 | 182 | public void VerifyPutJson() where TResponse : class 183 | { 184 | ApiClientMock.Verify(service => 185 | service.PutJsonAsync( 186 | It.IsAny(), 187 | It.IsAny(), 188 | It.IsAny(), 189 | It.IsAny()), Times.Once); 190 | } 191 | 192 | public void SetupDeleteJson() where TResponse : class, new() 193 | { 194 | ApiClientMock.Setup(service => 195 | service.DeleteJsonAsync( 196 | It.IsAny(), 197 | It.IsAny(), 198 | It.IsAny())) 199 | .ReturnsAsync(new TResponse()); 200 | } 201 | 202 | public void SetupDeleteJson() where TResponse : class, new() 203 | { 204 | ApiClientMock.Setup(service => 205 | service.DeleteJsonAsync( 206 | It.IsAny(), 207 | It.IsAny(), 208 | It.IsAny())) 209 | .ReturnsAsync(new TResponse()); 210 | } 211 | 212 | public void VerifyDeleteJson() where TResponse : class 213 | { 214 | ApiClientMock.Verify(service => 215 | service.DeleteJsonAsync( 216 | It.IsAny(), 217 | It.IsAny(), 218 | It.IsAny()), Times.Once); 219 | } 220 | 221 | public void VerifyDeleteJson() where TResponse : class 222 | { 223 | ApiClientMock.Verify(service => 224 | service.DeleteJsonAsync( 225 | It.IsAny(), 226 | It.IsAny(), 227 | It.IsAny()), Times.Once); 228 | } 229 | 230 | public void SetupGetBytes() 231 | { 232 | ApiClientMock.Setup(service => 233 | service.GetBytesAsync( 234 | It.IsAny(), 235 | It.IsAny(), 236 | It.IsAny())) 237 | .ReturnsAsync(new byte[] { }); 238 | } 239 | 240 | public void VerifySetupGetBytes() 241 | { 242 | ApiClientMock.Verify(service => 243 | service.GetBytesAsync( 244 | It.IsAny(), 245 | It.IsAny(), 246 | It.IsAny()), Times.Once); 247 | } 248 | 249 | public void VerifySetupGetBytes2Times() 250 | { 251 | ApiClientMock.Verify(service => 252 | service.GetBytesAsync( 253 | It.IsAny(), 254 | It.IsAny(), 255 | It.IsAny()), Times.Exactly(2)); 256 | } 257 | } 258 | } -------------------------------------------------------------------------------- /Exadel.Compreface.UnitTests/Services/DetectionServiceTest.cs: -------------------------------------------------------------------------------- 1 | using Exadel.Compreface.DTOs.FaceDetectionDTOs.FaceDetection; 2 | using Exadel.Compreface.Services; 3 | using Flurl; 4 | using static Exadel.Compreface.UnitTests.Helpers.GetRandomStringHelper; 5 | using Exadel.Compreface.Configuration; 6 | using Exadel.Compreface.UnitTests.Helpers; 7 | 8 | namespace Exadel.Compreface.UnitTests.Services 9 | { 10 | public class DetectionServiceTest : SetupAndVerifyTests 11 | { 12 | private readonly IComprefaceConfiguration _comprefaceConfiguration; 13 | 14 | private readonly DetectionService _faceDetectionService; 15 | 16 | public DetectionServiceTest() 17 | { 18 | var apiKey = GetRandomString(); 19 | var domain = GetRandomString(); 20 | var port = GetRandomString(); 21 | 22 | _comprefaceConfiguration = new ComprefaceConfiguration(apiKey, domain, port); 23 | 24 | _faceDetectionService = new DetectionService(_comprefaceConfiguration, ApiClientMock.Object); 25 | } 26 | 27 | [Fact] 28 | public async Task DetectAsync_TakesRequestModel_ReturnsProperResponseModel() 29 | { 30 | // Arrange 31 | var request = new FaceDetectionRequestByFilePath() 32 | { 33 | FacePlugins = new List() 34 | }; 35 | 36 | SetupPostMultipart(); 37 | 38 | // Act 39 | var response = await _faceDetectionService.DetectAsync(request); 40 | 41 | // Assert 42 | Assert.IsType(response); 43 | Assert.NotNull(response); 44 | 45 | VerifyPostMultipart(); 46 | base.ApiClientMock.VerifyNoOtherCalls(); 47 | } 48 | 49 | [Fact] 50 | public async Task DetectAsync_TakesRequestModelUsingUrl_ReturnsProperResponseModel() 51 | { 52 | // Arrange 53 | var request = new FaceDetectionRequestByFileUrl() 54 | { 55 | FacePlugins = new List() 56 | }; 57 | 58 | SetupPostJson(); 59 | SetupGetBytes(); 60 | 61 | // Act 62 | var response = await _faceDetectionService.DetectAsync(request); 63 | 64 | // Assert 65 | Assert.IsType(response); 66 | Assert.NotNull(response); 67 | 68 | VerifyPostJson(); 69 | VerifySetupGetBytes(); 70 | base.ApiClientMock.VerifyNoOtherCalls(); 71 | } 72 | 73 | [Fact] 74 | public async Task DetectAsync_TakesRequestModelUsingImageInBytes_ReturnsProperResponseModel() 75 | { 76 | // Arrange 77 | var request = new FaceDetectionRequestByBytes() 78 | { 79 | FacePlugins = new List(), 80 | ImageInBytes = new byte[] {}, 81 | }; 82 | 83 | SetupPostJson(); 84 | 85 | // Act 86 | var response = await _faceDetectionService.DetectAsync(request); 87 | 88 | // Assert 89 | Assert.IsType(response); 90 | Assert.NotNull(response); 91 | 92 | VerifyPostJson(); 93 | base.ApiClientMock.VerifyNoOtherCalls(); 94 | } 95 | 96 | [Fact] 97 | public async Task DetectAsync_TakesNullRequestModel_ThrowsNullReferenceException() 98 | { 99 | // Arrange 100 | SetupPostMultipart(); 101 | 102 | // Act 103 | var func = async () => await _faceDetectionService.DetectAsync((FaceDetectionRequestByFilePath)null!); 104 | 105 | // Assert 106 | await Assert.ThrowsAsync(func); 107 | } 108 | 109 | [Fact] 110 | public async Task DetectAsync_TakesNullRequestModelUsingUrl_ThrowsNullReferenceException() 111 | { 112 | // Arrange 113 | SetupPostJson(); 114 | SetupGetBytes(); 115 | 116 | // Act 117 | var func = async () => await _faceDetectionService.DetectAsync((FaceDetectionRequestByFileUrl)null!); 118 | 119 | // Assert 120 | await Assert.ThrowsAsync(func); 121 | } 122 | 123 | [Fact] 124 | public async Task DetectAsync_TakesNullRequestModelUsingImageInBytes_ThrowsNullReferenceException() 125 | { 126 | // Arrange 127 | SetupPostJson(); 128 | SetupGetBytes(); 129 | 130 | // Act 131 | var func = async () => await _faceDetectionService.DetectAsync((FaceDetectionRequestByBytes)null!); 132 | 133 | // Assert 134 | await Assert.ThrowsAsync(func); 135 | } 136 | 137 | [Fact] 138 | public async Task FaceDetectionBase64Async_TakesRequestModel_ReturnsProperResponseModel() 139 | { 140 | // Arrange 141 | var request = new FaceDetectionBase64Request() 142 | { 143 | FacePlugins = new List() 144 | }; 145 | 146 | SetupPostJson(); 147 | 148 | // Act 149 | var response = await _faceDetectionService.DetectAsync(request); 150 | 151 | // Assert 152 | Assert.IsType(response); 153 | Assert.NotNull(response); 154 | 155 | VerifyPostJson(); 156 | base.ApiClientMock.VerifyNoOtherCalls(); 157 | } 158 | 159 | [Fact] 160 | public async Task DetectBase64Async_TakesNullRequestModel_ThrowsNullReferenceException() 161 | { 162 | // Arrange 163 | SetupPostJson(); 164 | 165 | // Act 166 | var func = async () => await _faceDetectionService.DetectAsync((FaceDetectionBase64Request)null!); 167 | 168 | // Assert 169 | await Assert.ThrowsAsync(func); 170 | } 171 | } 172 | } -------------------------------------------------------------------------------- /Exadel.Compreface.UnitTests/Services/SubjectTests.cs: -------------------------------------------------------------------------------- 1 | using Exadel.Compreface.Configuration; 2 | using Exadel.Compreface.DTOs.SubjectDTOs.AddSubject; 3 | using Exadel.Compreface.DTOs.SubjectDTOs.DeleteAllSubjects; 4 | using Exadel.Compreface.DTOs.SubjectDTOs.DeleteSubject; 5 | using Exadel.Compreface.DTOs.SubjectDTOs.GetSubjectList; 6 | using Exadel.Compreface.DTOs.SubjectDTOs.RenameSubject; 7 | using static Exadel.Compreface.UnitTests.Helpers.GetRandomStringHelper; 8 | using Exadel.Compreface.Services.RecognitionService; 9 | using Exadel.Compreface.UnitTests.Helpers; 10 | 11 | namespace Exadel.Compreface.UnitTests.Services 12 | { 13 | public class SubjectTests : SetupAndVerifyTests 14 | { 15 | private readonly IComprefaceConfiguration _comprefaceConfiguration; 16 | 17 | private readonly Subject _subject; 18 | 19 | public SubjectTests() 20 | { 21 | var apiKey = GetRandomString(); 22 | var domain = GetRandomString(); 23 | var port = GetRandomString(); 24 | 25 | _comprefaceConfiguration = new ComprefaceConfiguration(apiKey, domain, port); 26 | 27 | _subject = new Subject(_comprefaceConfiguration, ApiClientMock.Object); 28 | } 29 | 30 | [Fact] 31 | public async Task GetAllAsync_Executes_ReturnsProperResponseModel() 32 | { 33 | // Arrange 34 | SetupGetJson(); 35 | 36 | // Act 37 | var response = await _subject.ListAsync(); 38 | 39 | // Assert 40 | Assert.IsType(response); 41 | Assert.NotNull(response); 42 | 43 | VerifyGetJson(); 44 | ApiClientMock.VerifyNoOtherCalls(); 45 | } 46 | 47 | [Fact] 48 | public async Task AddAsync_TakesRequestModel_ReturnsProperResponseModel() 49 | { 50 | // Arrange 51 | var request = new AddSubjectRequest(); 52 | 53 | SetupPostJson(); 54 | 55 | // Act 56 | var response = await _subject.AddAsync(request); 57 | 58 | // Assert 59 | Assert.IsType(response); 60 | Assert.NotNull(response); 61 | 62 | VerifyPostJson(); 63 | ApiClientMock.VerifyNoOtherCalls(); 64 | } 65 | 66 | [Fact] 67 | public async Task RenameAsync_TakesRequestModel_ReturnsProperResponseModel() 68 | { 69 | // Arrange 70 | var request = new RenameSubjectRequest(); 71 | 72 | SetupPutJson(); 73 | 74 | // Act 75 | var response = await _subject.RenameAsync(request); 76 | 77 | // Assert 78 | Assert.IsType(response); 79 | Assert.NotNull(response); 80 | 81 | VerifyPutJson(); 82 | ApiClientMock.VerifyNoOtherCalls(); 83 | } 84 | 85 | [Fact] 86 | public async Task RenameAsync_TakesNullRequestModel_ThrowsNullReferenceException() 87 | { 88 | // Arrange 89 | SetupPutJson(); 90 | 91 | // Act 92 | var func = async () => await _subject.RenameAsync(null!); 93 | 94 | // Assert 95 | await Assert.ThrowsAsync(func); 96 | } 97 | 98 | [Fact] 99 | public async Task DeleteAsync_TakesRequestModel_ReturnsProperResponseModel() 100 | { 101 | // Arrange 102 | var request = new DeleteSubjectRequest(); 103 | 104 | SetupDeleteJson(); 105 | 106 | // Act 107 | var response = await _subject.DeleteAsync(request); 108 | 109 | // Assert 110 | Assert.IsType(response); 111 | Assert.NotNull(response); 112 | 113 | VerifyDeleteJson(); 114 | ApiClientMock.VerifyNoOtherCalls(); 115 | } 116 | 117 | [Fact] 118 | public async Task DeleteAsync_TakesNullRequestModel_ThrowsNullReferenceException() 119 | { 120 | // Arrange 121 | SetupDeleteJson(); 122 | 123 | // Act 124 | var func = async () => await _subject.DeleteAsync(null!); 125 | 126 | // Assert 127 | await Assert.ThrowsAsync(func); 128 | } 129 | 130 | [Fact] 131 | public async Task DeleteAllAsync_TakesRequestModel_ReturnsProperResponseModel() 132 | { 133 | // Arrange 134 | SetupDeleteJson(); 135 | 136 | // Act 137 | var response = await _subject.DeleteAllAsync(); 138 | 139 | // Assert 140 | Assert.IsType(response); 141 | Assert.NotNull(response); 142 | 143 | VerifyDeleteJson(); 144 | ApiClientMock.VerifyNoOtherCalls(); 145 | } 146 | } 147 | } -------------------------------------------------------------------------------- /Exadel.Compreface.UnitTests/Services/VerificationTests.cs: -------------------------------------------------------------------------------- 1 | using Exadel.Compreface.Configuration; 2 | using Exadel.Compreface.DTOs.FaceVerificationDTOs.FaceVerification; 3 | using Exadel.Compreface.Services; 4 | using Exadel.Compreface.UnitTests.Helpers; 5 | using Flurl; 6 | using static Exadel.Compreface.UnitTests.Helpers.GetRandomStringHelper; 7 | 8 | namespace Exadel.Compreface.UnitTests.Services; 9 | 10 | public class VerificationTests : SetupAndVerifyTests 11 | { 12 | private readonly IComprefaceConfiguration _comprefaceConfiguration; 13 | 14 | private readonly VerificationService _faceVerificationService; 15 | 16 | public VerificationTests() 17 | { 18 | var apiKey = GetRandomString(); 19 | var domain = GetRandomString(); 20 | var port = GetRandomString(); 21 | 22 | _comprefaceConfiguration = new ComprefaceConfiguration(apiKey, domain, port); 23 | 24 | _faceVerificationService = new VerificationService(_comprefaceConfiguration, ApiClientMock.Object); 25 | } 26 | 27 | [Fact] 28 | public async Task VerifyAsync_TakesRequestModel_ReturnsProperResponseModel() 29 | { 30 | // Arrange 31 | var request = new FaceVerificationRequestByFilePath() 32 | { 33 | FacePlugins = new List() 34 | }; 35 | var t = new Result(); 36 | List results = new List { t }; 37 | var result = new FaceVerificationResponse() { Result = results }; 38 | 39 | SetupPostMultipart(); 40 | 41 | // Act 42 | var response = await _faceVerificationService.VerifyAsync(request); 43 | 44 | // Assert 45 | Assert.IsType(response); 46 | Assert.NotNull(response); 47 | 48 | VerifyPostMultipart(); 49 | ApiClientMock.VerifyNoOtherCalls(); 50 | } 51 | 52 | [Fact] 53 | public async Task VerifyAsync_TakesRequestModelUsingUrl_ReturnsProperResponseModel() 54 | { 55 | // Arrange 56 | var request = new FaceVerificationRequestByFileUrl() 57 | { 58 | FacePlugins = new List() 59 | }; 60 | 61 | SetupPostJson(); 62 | SetupGetBytes(); 63 | 64 | // Act 65 | var response = await _faceVerificationService.VerifyAsync(request); 66 | 67 | // Assert 68 | Assert.IsType(response); 69 | Assert.NotNull(response); 70 | 71 | VerifyPostJson(); 72 | VerifySetupGetBytes2Times(); 73 | ApiClientMock.VerifyNoOtherCalls(); 74 | } 75 | 76 | [Fact] 77 | public async Task VerifyAsync_TakesRequestModelUsingImageInBytes_ReturnsProperResponseModel() 78 | { 79 | // Arrange 80 | var request = new FaceVerificationRequestByBytes() 81 | { 82 | FacePlugins = new List(), 83 | SourceImageInBytes= new byte[] {}, 84 | TargetImageInBytes= new byte[] {} 85 | }; 86 | 87 | SetupPostJson(); 88 | 89 | // Act 90 | var response = await _faceVerificationService.VerifyAsync(request); 91 | 92 | // Assert 93 | Assert.IsType(response); 94 | Assert.NotNull(response); 95 | 96 | VerifyPostJson(); 97 | ApiClientMock.VerifyNoOtherCalls(); 98 | } 99 | 100 | [Fact] 101 | public async Task VerifyAsync_TakesNullRequestModel_ThrowsNullReferenceException() 102 | { 103 | // Arrange 104 | SetupPostMultipart(); 105 | 106 | // Act 107 | var responseCall = async () => await _faceVerificationService.VerifyAsync((FaceVerificationRequestByFilePath)null!); 108 | 109 | // Assert 110 | await Assert.ThrowsAsync(responseCall); 111 | } 112 | 113 | [Fact] 114 | public async Task VerifyAsync_TakesNullRequestModelUsingImageInBytes_ThrowsNullReferenceException() 115 | { 116 | // Arrange 117 | SetupPostMultipart(); 118 | 119 | // Act 120 | var responseCall = async () => await _faceVerificationService.VerifyAsync((FaceVerificationRequestByBytes)null!); 121 | 122 | // Assert 123 | await Assert.ThrowsAsync(responseCall); 124 | } 125 | 126 | [Fact] 127 | public async Task VerifyAsync_TakesNullRequestModelUsingUrl_ThrowsNullReferenceException() 128 | { 129 | // Arrange 130 | SetupPostJson(); 131 | 132 | // Act 133 | var responseCall = async () => await _faceVerificationService.VerifyAsync((FaceVerificationRequestByFileUrl)null!); 134 | 135 | // Assert 136 | await Assert.ThrowsAsync(responseCall); 137 | } 138 | 139 | [Fact] 140 | public async Task VerifyBase64ImageAsync_TakesRequestModel_ReturnsProperResponseModel() 141 | { 142 | // Arrange 143 | var request = new FaceVerificationWithBase64Request() 144 | { 145 | FacePlugins = new List() 146 | }; 147 | 148 | SetupPostJson(); 149 | 150 | // Act 151 | var response = await _faceVerificationService.VerifyAsync(request); 152 | 153 | // Assert 154 | Assert.IsType(response); 155 | Assert.NotNull(response); 156 | 157 | VerifyPostJson(); 158 | ApiClientMock.VerifyNoOtherCalls(); 159 | } 160 | 161 | [Fact] 162 | public async Task VerifyBase64Async_TakesNullRequestModel_ThrowsNullReferenceException() 163 | { 164 | // Arrange 165 | SetupPostJson(); 166 | 167 | // Act 168 | var responseCall = async () => await _faceVerificationService.VerifyAsync((FaceVerificationWithBase64Request)null!); 169 | 170 | // Assert 171 | await Assert.ThrowsAsync(responseCall); 172 | } 173 | } -------------------------------------------------------------------------------- /Exadel.Compreface.UnitTests/Usings.cs: -------------------------------------------------------------------------------- 1 | global using Xunit; -------------------------------------------------------------------------------- /Exadel.Compreface/Clients/ApiClient/IApiClient.cs: -------------------------------------------------------------------------------- 1 | using Flurl; 2 | using Flurl.Http.Content; 3 | 4 | namespace Exadel.Compreface.Clients.ApiClient 5 | { 6 | public interface IApiClient 7 | { 8 | Task DeleteJsonAsync(string requestUrl, HttpCompletionOption completionOption = HttpCompletionOption.ResponseContentRead, CancellationToken cancellationToken = default); 9 | 10 | Task DeleteJsonAsync(Url requestUrl, HttpCompletionOption completionOption = HttpCompletionOption.ResponseContentRead, CancellationToken cancellationToken = default); 11 | 12 | Task GetBytesFromRemoteAsync(string requestUrl, HttpCompletionOption completionOption = HttpCompletionOption.ResponseContentRead, CancellationToken cancellationToken = default); 13 | 14 | Task GetBytesFromRemoteAsync(Url requestUrl, HttpCompletionOption completionOption = HttpCompletionOption.ResponseContentRead, CancellationToken cancellationToken = default); 15 | 16 | Task GetJsonAsync(string requestUrl, HttpCompletionOption completionOption = HttpCompletionOption.ResponseContentRead, CancellationToken cancellationToken = default); 17 | 18 | Task GetJsonAsync(Url requestUrl, HttpCompletionOption completionOption = HttpCompletionOption.ResponseContentRead, CancellationToken cancellationToken = default); 19 | 20 | Task PostJsonAsync(string requestUrl, object body, HttpCompletionOption completionOption = HttpCompletionOption.ResponseContentRead, CancellationToken cancellationToken = default) where TResponse : class; 21 | 22 | Task PostJsonAsync(Url requestUrl, object body, HttpCompletionOption completionOption = HttpCompletionOption.ResponseContentRead, CancellationToken cancellationToken = default) where TResponse : class; 23 | 24 | Task PostMultipartAsync(string requestUrl, Action buildContent, HttpCompletionOption completionOption = HttpCompletionOption.ResponseContentRead, CancellationToken cancellationToken = default); 25 | 26 | Task PostMultipartAsync(Url requestUrl, Action buildContent, HttpCompletionOption completionOption = HttpCompletionOption.ResponseContentRead, CancellationToken cancellationToken = default); 27 | 28 | Task PutJsonAsync(string requestUrl, object body, HttpCompletionOption completionOption = HttpCompletionOption.ResponseContentRead, CancellationToken cancellationToken = default); 29 | 30 | Task PutJsonAsync(Url requestUrl, object body, HttpCompletionOption completionOption = HttpCompletionOption.ResponseContentRead, CancellationToken cancellationToken = default); 31 | 32 | Task GetBytesAsync(string url, HttpCompletionOption completionOption = HttpCompletionOption.ResponseContentRead, CancellationToken cancellationToken = default); 33 | } 34 | } -------------------------------------------------------------------------------- /Exadel.Compreface/Clients/CompreFaceClient/CompreFaceClient.cs: -------------------------------------------------------------------------------- 1 | using Exadel.Compreface.Configuration; 2 | using Exadel.Compreface.Exceptions; 3 | using Exadel.Compreface.Services.Attributes; 4 | using Microsoft.Extensions.Configuration; 5 | using System.Reflection; 6 | 7 | namespace Exadel.Compreface.Clients.CompreFaceClient; 8 | 9 | /// 10 | /// Global CompreFace provider. 11 | /// 12 | public class CompreFaceClient : ICompreFaceClient 13 | { 14 | private readonly string _domain; 15 | private readonly string _port; 16 | 17 | private readonly Dictionary _services = new(); 18 | 19 | /// 20 | /// Constructor for string parameters. 21 | /// 22 | /// Domain with protocol where CompreFace is located. 23 | /// CompreFace port. 24 | /// Is throwed if one of the parameters is null. 25 | public CompreFaceClient(string? domain, string? port) 26 | { 27 | _domain = domain ?? throw new ArgumentNullException($"{nameof(domain)} cannot be null!"); 28 | _port = port ?? throw new ArgumentNullException($"{nameof(port)} cannot be null!"); 29 | } 30 | 31 | /// 32 | /// Constructor allows to setup CompreFaceClient from appsettings.json. 33 | /// 34 | /// IConfiguration object. 35 | /// Name of the section for domain parameter in an appsetting.json file. 36 | /// Name of the section for port parameter in an appsetting.json file. 37 | /// Is throwed if one of the sections in appseting.json doesn't have a value. 38 | public CompreFaceClient(IConfiguration configuration, string? domainSection, string? portSection) 39 | { 40 | _domain = configuration.GetSection(domainSection).Value ?? throw new ArgumentNullException($"{nameof(domainSection)} cannot be null!"); 41 | _port = configuration.GetSection(portSection).Value ?? throw new ArgumentNullException($"{nameof(portSection)} cannot be null!"); 42 | } 43 | 44 | /// 45 | /// Creates instance of the service. 46 | /// 47 | /// Type of the service. 48 | /// Api key string from CompreFace. 49 | /// Is throwed if T doesn't belong to CompreFace services. 50 | /// var faceVerificationService = client.GetCompreFaceService("00000000-0000-0000-0000-000000000004") 51 | /// Service instance. 52 | public T GetCompreFaceService(string apiKey) where T : class 53 | { 54 | var compreFaceService = GetService(apiKey, typeof(T)); 55 | 56 | return (T)compreFaceService; 57 | } 58 | 59 | /// 60 | /// Creates instance of the service with api key from appsettings.json file. 61 | /// 62 | /// 63 | /// IConfiguration object. 64 | /// Name of the section for api key parameter in an appsetting.json file. 65 | /// Is throwed if T doesn't belong to CompreFace services. 66 | /// Is throwed if api key section in appseting.json doesn't have a value. 67 | /// Service instance. 68 | public T GetCompreFaceService(IConfiguration configuration, string apiKeySection) where T : class 69 | { 70 | 71 | var apiKey = configuration.GetSection(apiKeySection).Value ?? throw new ArgumentNullException($"{nameof(apiKeySection)} cannot be null!"); 72 | var compreFaceService = GetService(apiKey, typeof(T)); 73 | 74 | return (T)compreFaceService; 75 | } 76 | 77 | private object GetService(string apiKey, Type type) 78 | { 79 | try 80 | { 81 | var key = new ServiceDictionaryKey(apiKey, type); 82 | var baseService = _services.GetValueOrDefault(key); 83 | 84 | if (baseService == null) 85 | { 86 | var config = new ComprefaceConfiguration(apiKey, _domain, _port); 87 | var apiClient = new ApiClient.ApiClient(config); 88 | baseService = ReturnServiceIfTypeIsValid(type, config, apiClient); 89 | 90 | _services.Add(key, baseService!); 91 | } 92 | 93 | return baseService; 94 | } 95 | catch (Exception) 96 | { 97 | throw; 98 | } 99 | } 100 | 101 | private object ReturnServiceIfTypeIsValid(Type type, ComprefaceConfiguration config, ApiClient.ApiClient apiClient) 102 | { 103 | object baseService = null; 104 | 105 | if (type.GetCustomAttribute(typeof(CompreFaceService)) != null) 106 | baseService = Activator.CreateInstance(type, config, apiClient); 107 | 108 | if (baseService == null) 109 | throw new TypeNotBelongCompreFaceException("Type doesn't belong to CompreFace services. Class should be covered by CompreFaceService attribute."); 110 | 111 | return baseService; 112 | } 113 | 114 | private class ServiceDictionaryKey 115 | { 116 | public string ApiKey { get; set; } 117 | 118 | public Type Type { get; set; } 119 | 120 | public ServiceDictionaryKey(string apiKey, Type type) 121 | { 122 | ApiKey = apiKey; 123 | Type = type; 124 | } 125 | } 126 | } -------------------------------------------------------------------------------- /Exadel.Compreface/Clients/CompreFaceClient/ICompreFaceClient.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Extensions.Configuration; 2 | 3 | namespace Exadel.Compreface.Clients.CompreFaceClient 4 | { 5 | public interface ICompreFaceClient 6 | { 7 | public T GetCompreFaceService(string apiKey) where T : class; 8 | 9 | public T GetCompreFaceService(IConfiguration configuration, string apiKeySection) where T : class; 10 | } 11 | } -------------------------------------------------------------------------------- /Exadel.Compreface/Clients/Config/ConfigInitializer.cs: -------------------------------------------------------------------------------- 1 | using Exadel.Compreface.Helpers; 2 | using Flurl.Http; 3 | using System.Text.Json; 4 | 5 | namespace Exadel.Compreface.Clients.Config 6 | { 7 | /// 8 | /// Configures all the needed external configs for services clients/> 9 | /// 10 | public static class ConfigInitializer 11 | { 12 | /// 13 | /// Creates the instance of instance and binds it to Flurl's built-in JsonSerializer 14 | /// 15 | public static void InitializeSnakeCaseJsonConfigs() 16 | { 17 | var jsonOptions = new JsonSerializerOptions() 18 | { 19 | PropertyNamingPolicy = SnakeCaseToCamelCaseNamingPolicy.Policy, 20 | PropertyNameCaseInsensitive = true, 21 | }; 22 | 23 | FlurlHttp.GlobalSettings.JsonSerializer = new SystemJsonSerializer(jsonOptions); 24 | } 25 | } 26 | } -------------------------------------------------------------------------------- /Exadel.Compreface/Configuration/ComprefaceConfiguration.cs: -------------------------------------------------------------------------------- 1 | namespace Exadel.Compreface.Configuration; 2 | 3 | /// 4 | /// Setups main parameters for the CompreFace API. 5 | /// 6 | public class ComprefaceConfiguration : IComprefaceConfiguration 7 | { 8 | public string Domain { get; set; } 9 | 10 | public string Port { get; set; } 11 | 12 | public string ApiKey { get; set; } 13 | 14 | /// 15 | /// 16 | /// 17 | /// Api key of a service from CompreFace. 18 | /// Domain with protocol where CompreFace is located. 19 | /// CompreFace port. 20 | /// Is throwed if one of the parameters is null. 21 | public ComprefaceConfiguration(string apiKey, string domain, string port) 22 | { 23 | Domain = domain ?? throw new ArgumentNullException($"{nameof(domain)} cannot be null!"); 24 | Port = port ?? throw new ArgumentNullException($"{nameof(port)} cannot be null!"); 25 | ApiKey = apiKey ?? throw new ArgumentNullException($"{nameof(apiKey)} cannot be null!"); 26 | } 27 | } -------------------------------------------------------------------------------- /Exadel.Compreface/Configuration/IComprefaceConfiguration.cs: -------------------------------------------------------------------------------- 1 | namespace Exadel.Compreface.Configuration; 2 | 3 | public interface IComprefaceConfiguration 4 | { 5 | public string Domain { get; } 6 | 7 | public string Port { get; } 8 | 9 | public string ApiKey { get; } 10 | } -------------------------------------------------------------------------------- /Exadel.Compreface/DTOs/FaceCollectionDTOs/AddSubjectExample/AddBase64SubjectExampleRequest.cs: -------------------------------------------------------------------------------- 1 | using Exadel.Compreface.DTOs.HelperDTOs.BaseDTOs; 2 | 3 | namespace Exadel.Compreface.DTOs.FaceCollectionDTOs.AddSubjectExample 4 | { 5 | /// 6 | /// DTO helps to create an example of the subject by saving images. 7 | /// 8 | public class AddBase64SubjectExampleRequest : BaseExampleRequest 9 | { 10 | /// 11 | /// Image as base64 string. 12 | /// 13 | public string File { get; set; } 14 | } 15 | } -------------------------------------------------------------------------------- /Exadel.Compreface/DTOs/FaceCollectionDTOs/AddSubjectExample/AddSubjectExampleRequestByBytes.cs: -------------------------------------------------------------------------------- 1 | using Exadel.Compreface.DTOs.HelperDTOs.BaseDTOs; 2 | 3 | namespace Exadel.Compreface.DTOs.FaceCollectionDTOs.AddSubjectExample 4 | { 5 | /// 6 | /// DTO helps to create an example of the subject by saving images. 7 | /// 8 | public class AddSubjectExampleRequestByBytes : BaseExampleRequest 9 | { 10 | /// 11 | /// Image as byte array. 12 | /// 13 | public byte[] ImageInBytes { get; set; } 14 | } 15 | } -------------------------------------------------------------------------------- /Exadel.Compreface/DTOs/FaceCollectionDTOs/AddSubjectExample/AddSubjectExampleRequestByFilePath.cs: -------------------------------------------------------------------------------- 1 | using Exadel.Compreface.DTOs.HelperDTOs.BaseDTOs; 2 | 3 | namespace Exadel.Compreface.DTOs.FaceCollectionDTOs.AddSubjectExample; 4 | 5 | /// 6 | /// DTO helps to create an example of the subject by saving images. 7 | /// 8 | public class AddSubjectExampleRequestByFilePath : BaseExampleRequest 9 | { 10 | /// 11 | /// Full file path. 12 | /// 13 | public string FilePath { get; set; } 14 | } -------------------------------------------------------------------------------- /Exadel.Compreface/DTOs/FaceCollectionDTOs/AddSubjectExample/AddSubjectExampleRequestByFileUrl.cs: -------------------------------------------------------------------------------- 1 | using Exadel.Compreface.DTOs.HelperDTOs.BaseDTOs; 2 | 3 | namespace Exadel.Compreface.DTOs.FaceCollectionDTOs.AddSubjectExample 4 | { 5 | /// 6 | /// DTO helps to create an example of the subject by saving images. 7 | /// 8 | public class AddSubjectExampleRequestByFileUrl : BaseExampleRequest 9 | { 10 | /// 11 | /// Url of image. 12 | /// 13 | public string FileUrl { get; set; } 14 | } 15 | } -------------------------------------------------------------------------------- /Exadel.Compreface/DTOs/FaceCollectionDTOs/AddSubjectExample/AddSubjectExampleResponse.cs: -------------------------------------------------------------------------------- 1 | namespace Exadel.Compreface.DTOs.FaceCollectionDTOs.AddSubjectExample; 2 | 3 | public class AddSubjectExampleResponse 4 | { 5 | public Guid ImageId { get; set; } 6 | 7 | public string Subject { get; set; } 8 | } -------------------------------------------------------------------------------- /Exadel.Compreface/DTOs/FaceCollectionDTOs/DeleteAllSubjectExamples/DeleteAllExamplesRequest.cs: -------------------------------------------------------------------------------- 1 | namespace Exadel.Compreface.DTOs.FaceCollectionDTOs.DeleteAllSubjectExamples 2 | { 3 | /// 4 | /// DTO helps to delete all examples in a specified subject. 5 | /// 6 | public class DeleteAllExamplesRequest 7 | { 8 | /// 9 | /// Name of the subject. 10 | /// 11 | public string Subject { get; set; } 12 | } 13 | } -------------------------------------------------------------------------------- /Exadel.Compreface/DTOs/FaceCollectionDTOs/DeleteAllSubjectExamples/DeleteAllExamplesResponse.cs: -------------------------------------------------------------------------------- 1 | namespace Exadel.Compreface.DTOs.FaceCollectionDTOs.DeleteAllSubjectExamples 2 | { 3 | public class DeleteAllExamplesResponse 4 | { 5 | /// 6 | /// Amount of a deleted examples. 7 | /// 8 | public int? Deleted{ get; set; } 9 | } 10 | } -------------------------------------------------------------------------------- /Exadel.Compreface/DTOs/FaceCollectionDTOs/DeleteImageById/DeleteImageByIdRequest.cs: -------------------------------------------------------------------------------- 1 | namespace Exadel.Compreface.DTOs.FaceCollectionDTOs.DeleteImageById 2 | { 3 | public class DeleteImageByIdRequest 4 | { 5 | public Guid ImageId { get; set; } 6 | } 7 | } -------------------------------------------------------------------------------- /Exadel.Compreface/DTOs/FaceCollectionDTOs/DeleteImageById/DeleteImageByIdResponse.cs: -------------------------------------------------------------------------------- 1 | namespace Exadel.Compreface.DTOs.FaceCollectionDTOs.DeleteImageById 2 | { 3 | public class DeleteImageByIdResponse 4 | { 5 | public Guid ImageId { get; set; } 6 | 7 | public string Subject { get; set; } 8 | } 9 | } -------------------------------------------------------------------------------- /Exadel.Compreface/DTOs/FaceCollectionDTOs/DeleteMultipleExamples/DeleteMultipleExamplesRequest.cs: -------------------------------------------------------------------------------- 1 | namespace Exadel.Compreface.DTOs.FaceCollectionDTOs.DeleteMultipleExamples 2 | { 3 | public class DeleteMultipleExampleRequest 4 | { 5 | public IList ImageIdList { get; set; } 6 | } 7 | } -------------------------------------------------------------------------------- /Exadel.Compreface/DTOs/FaceCollectionDTOs/DeleteMultipleExamples/DeleteMultipleExamplesResponse.cs: -------------------------------------------------------------------------------- 1 | using Exadel.Compreface.DTOs.HelperDTOs; 2 | 3 | namespace Exadel.Compreface.DTOs.FaceCollectionDTOs.DeleteMultipleExamples 4 | { 5 | public class DeleteMultipleExamplesResponse 6 | { 7 | public IList Faces { get; set; } 8 | } 9 | } -------------------------------------------------------------------------------- /Exadel.Compreface/DTOs/FaceCollectionDTOs/DownloadImageById/DownloadImageByIdDirectlyRequest.cs: -------------------------------------------------------------------------------- 1 | namespace Exadel.Compreface.DTOs.FaceCollectionDTOs.DownloadImageById 2 | { 3 | /// 4 | /// DTO for direct example downloading from recognition service. 5 | /// 6 | public class DownloadImageByIdDirectlyRequest 7 | { 8 | public Guid ImageId { get; set; } 9 | 10 | public Guid RecognitionApiKey { get; set; } 11 | } 12 | } -------------------------------------------------------------------------------- /Exadel.Compreface/DTOs/FaceCollectionDTOs/DownloadImageByIdFromSubject/DownloadImageByIdFromSubjectRequest.cs: -------------------------------------------------------------------------------- 1 | namespace Exadel.Compreface.DTOs.FaceCollectionDTOs.DownloadImageByIdFromSubject 2 | { 3 | /// 4 | /// DTO for downloading of example from subject. 5 | /// 6 | public class DownloadImageByIdFromSubjectRequest 7 | { 8 | public Guid ImageId { get; set; } 9 | } 10 | } -------------------------------------------------------------------------------- /Exadel.Compreface/DTOs/FaceCollectionDTOs/ListAllSubjectExamples/ListAllSubjectExamplesRequest.cs: -------------------------------------------------------------------------------- 1 | namespace Exadel.Compreface.DTOs.FaceCollectionDTOs.ListAllExampleSubject; 2 | 3 | /// 4 | /// DTO helps to retrieve a list of subjects saved in a Face Collection. 5 | /// 6 | public class ListAllSubjectExamplesRequest 7 | { 8 | /// 9 | /// Page number of examples to return. Can be used for pagination. Default value is 0. 10 | /// 11 | public int? Page { get; set; } 12 | 13 | /// 14 | /// Faces on page (page size). Can be used for pagination. Default value is 20. 15 | /// 16 | public int? Size { get; set; } 17 | 18 | /// 19 | /// What subject examples endpoint should return. If empty, return examples for all subjects. 20 | /// 21 | public string Subject { get; set; } 22 | } -------------------------------------------------------------------------------- /Exadel.Compreface/DTOs/FaceCollectionDTOs/ListAllSubjectExamples/ListAllSubjectExamplesResponse.cs: -------------------------------------------------------------------------------- 1 | using Exadel.Compreface.DTOs.HelperDTOs; 2 | 3 | namespace Exadel.Compreface.DTOs.FaceCollectionDTOs.ListAllExampleSubject; 4 | 5 | public class ListAllSubjectExamplesResponse 6 | { 7 | public IList Faces { get; set; } 8 | 9 | public int PageNumber { get; set; } 10 | 11 | public int PageSize { get; set; } 12 | 13 | public int TotalPages { get; set; } 14 | 15 | public int TotalElements { get; set; } 16 | } -------------------------------------------------------------------------------- /Exadel.Compreface/DTOs/FaceDetectionDTOs/FaceDetection/FaceDetectionBase64Request.cs: -------------------------------------------------------------------------------- 1 | using Exadel.Compreface.DTOs.HelperDTOs.BaseDTOs; 2 | 3 | namespace Exadel.Compreface.DTOs.FaceDetectionDTOs.FaceDetection 4 | { 5 | public class FaceDetectionBase64Request : BaseFaceRequest 6 | { 7 | public string File { get; set; } 8 | } 9 | } -------------------------------------------------------------------------------- /Exadel.Compreface/DTOs/FaceDetectionDTOs/FaceDetection/FaceDetectionRequestByBytes.cs: -------------------------------------------------------------------------------- 1 | using Exadel.Compreface.DTOs.HelperDTOs.BaseDTOs; 2 | 3 | namespace Exadel.Compreface.DTOs.FaceDetectionDTOs.FaceDetection 4 | { 5 | public class FaceDetectionRequestByBytes : BaseFaceRequest 6 | { 7 | public byte[] ImageInBytes { get; set; } 8 | } 9 | } -------------------------------------------------------------------------------- /Exadel.Compreface/DTOs/FaceDetectionDTOs/FaceDetection/FaceDetectionRequestByFilePath.cs: -------------------------------------------------------------------------------- 1 | using Exadel.Compreface.DTOs.HelperDTOs.BaseDTOs; 2 | 3 | namespace Exadel.Compreface.DTOs.FaceDetectionDTOs.FaceDetection 4 | { 5 | public class FaceDetectionRequestByFilePath : BaseFaceRequest 6 | { 7 | public string FilePath { get; set; } 8 | } 9 | } -------------------------------------------------------------------------------- /Exadel.Compreface/DTOs/FaceDetectionDTOs/FaceDetection/FaceDetectionRequestByFileUrl.cs: -------------------------------------------------------------------------------- 1 | using Exadel.Compreface.DTOs.HelperDTOs.BaseDTOs; 2 | 3 | namespace Exadel.Compreface.DTOs.FaceDetectionDTOs.FaceDetection 4 | { 5 | public class FaceDetectionRequestByFileUrl : BaseFaceRequest 6 | { 7 | public string FileUrl { get; set; } 8 | } 9 | } -------------------------------------------------------------------------------- /Exadel.Compreface/DTOs/FaceDetectionDTOs/FaceDetection/FaceDetectionResponse.cs: -------------------------------------------------------------------------------- 1 | using Exadel.Compreface.DTOs.HelperDTOs; 2 | using Exadel.Compreface.DTOs.HelperDTOs.BaseDTOs; 3 | 4 | namespace Exadel.Compreface.DTOs.FaceDetectionDTOs.FaceDetection 5 | { 6 | public class FaceDetectionResponse 7 | { 8 | public IList Result { get; set; } 9 | 10 | public PluginVersions PluginsVersions { get; set; } 11 | } 12 | } -------------------------------------------------------------------------------- /Exadel.Compreface/DTOs/FaceVerificationDTOs/FaceVerification/FaceVerificationRequestByBytes.cs: -------------------------------------------------------------------------------- 1 | using Exadel.Compreface.DTOs.HelperDTOs.BaseDTOs; 2 | 3 | namespace Exadel.Compreface.DTOs.FaceVerificationDTOs.FaceVerification 4 | { 5 | public class FaceVerificationRequestByBytes : BaseFaceRequest 6 | { 7 | public byte[] SourceImageInBytes { get; set; } 8 | 9 | public byte[] TargetImageInBytes { get; set; } 10 | } 11 | } -------------------------------------------------------------------------------- /Exadel.Compreface/DTOs/FaceVerificationDTOs/FaceVerification/FaceVerificationRequestByFilePath.cs: -------------------------------------------------------------------------------- 1 | using Exadel.Compreface.DTOs.HelperDTOs.BaseDTOs; 2 | 3 | namespace Exadel.Compreface.DTOs.FaceVerificationDTOs.FaceVerification; 4 | 5 | public class FaceVerificationRequestByFilePath : BaseFaceRequest 6 | { 7 | public string SourceImageFilePath { get; set; } 8 | 9 | public string TargetImageFilePath { get; set; } 10 | } -------------------------------------------------------------------------------- /Exadel.Compreface/DTOs/FaceVerificationDTOs/FaceVerification/FaceVerificationRequestByFileUrl.cs: -------------------------------------------------------------------------------- 1 | using Exadel.Compreface.DTOs.HelperDTOs.BaseDTOs; 2 | 3 | namespace Exadel.Compreface.DTOs.FaceVerificationDTOs.FaceVerification 4 | { 5 | public class FaceVerificationRequestByFileUrl : BaseFaceRequest 6 | { 7 | public string SourceImageFileUrl { get; set; } 8 | 9 | public string TargetImageFileUrl { get; set; } 10 | } 11 | } -------------------------------------------------------------------------------- /Exadel.Compreface/DTOs/FaceVerificationDTOs/FaceVerification/FaceVerificationResponse.cs: -------------------------------------------------------------------------------- 1 | using Exadel.Compreface.DTOs.HelperDTOs; 2 | using Exadel.Compreface.DTOs.HelperDTOs.BaseDTOs; 3 | 4 | namespace Exadel.Compreface.DTOs.FaceVerificationDTOs.FaceVerification; 5 | 6 | public class FaceVerificationResponse 7 | { 8 | public IList Result { get; set; } 9 | } 10 | 11 | public class Result 12 | { 13 | public SourceImageFace SourceImageFace { get; set; } 14 | 15 | public IList FaceMatches { get; set; } 16 | 17 | public PluginVersions PluginsVersions { get; set; } 18 | } 19 | 20 | public class SourceImageFace : BaseResult 21 | { } 22 | 23 | public class FaceMatches : BaseResult 24 | { 25 | public decimal Similarity { get; set; } 26 | } -------------------------------------------------------------------------------- /Exadel.Compreface/DTOs/FaceVerificationDTOs/FaceVerification/FaceVerificationWithBase64Request.cs: -------------------------------------------------------------------------------- 1 | using Exadel.Compreface.DTOs.HelperDTOs.BaseDTOs; 2 | 3 | namespace Exadel.Compreface.DTOs.FaceVerificationDTOs.FaceVerification; 4 | 5 | public class FaceVerificationWithBase64Request : BaseFaceRequest 6 | { 7 | public string SourceImageWithBase64 { get; set; } 8 | 9 | public string TargetImageWithBase64 { get; set; } 10 | } -------------------------------------------------------------------------------- /Exadel.Compreface/DTOs/HelperDTOs/Age.cs: -------------------------------------------------------------------------------- 1 | namespace Exadel.Compreface.DTOs.HelperDTOs; 2 | 3 | public class Age 4 | { 5 | public decimal Probability { get; set; } 6 | 7 | public int High { get; set; } 8 | 9 | public int Low { get; set; } 10 | } -------------------------------------------------------------------------------- /Exadel.Compreface/DTOs/HelperDTOs/BaseDTOs/BaseExampleRequest.cs: -------------------------------------------------------------------------------- 1 | namespace Exadel.Compreface.DTOs.HelperDTOs.BaseDTOs 2 | { 3 | /// 4 | /// Base class with several common properties. 5 | /// 6 | public class BaseExampleRequest 7 | { 8 | /// 9 | /// Name of the subject. 10 | /// 11 | public string Subject { get; set; } 12 | 13 | /// 14 | /// Optional property: Minimum required confidence that a recognized face is actually a face. Value is between 0.0 and 1.0. 15 | /// 16 | public decimal? DetProbThreShold { get; set; } 17 | } 18 | } -------------------------------------------------------------------------------- /Exadel.Compreface/DTOs/HelperDTOs/BaseDTOs/BaseFaceRequest.cs: -------------------------------------------------------------------------------- 1 | namespace Exadel.Compreface.DTOs.HelperDTOs.BaseDTOs; 2 | 3 | /// 4 | /// Base class with several common properties. 5 | /// 6 | public class BaseFaceRequest 7 | { 8 | /// 9 | /// Optional property: 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. 10 | /// 11 | public int? Limit { get; set; } 12 | 13 | /// 14 | /// Optional property: minimum required confidence that a recognized face is actually a face. Value is between 0.0 and 1.0. 15 | /// 16 | public decimal DetProbThreshold { get; set; } 17 | 18 | /// 19 | /// Optional property: comma-separated slugs of face plugins. If empty, no additional information is returned. 20 | /// 21 | public IList FacePlugins { get; set; } = new List(); 22 | 23 | /// 24 | /// Optional property: if true includes system information like execution_time and plugin_version fields. Default value is false 25 | /// 26 | public bool Status { get; set; } 27 | } -------------------------------------------------------------------------------- /Exadel.Compreface/DTOs/HelperDTOs/BaseDTOs/BaseResult.cs: -------------------------------------------------------------------------------- 1 | namespace Exadel.Compreface.DTOs.HelperDTOs.BaseDTOs; 2 | 3 | public class BaseResult 4 | { 5 | public Age Age { get; set; } 6 | 7 | public Gender Gender { get; set; } 8 | 9 | public Mask Mask { get; set; } 10 | 11 | public Box Box { get; set; } 12 | 13 | public IList> Landmarks { get; set; } 14 | 15 | public ExecutionTime ExecutionTime { get; set; } 16 | 17 | public IList Embedding { get; set; } 18 | } -------------------------------------------------------------------------------- /Exadel.Compreface/DTOs/HelperDTOs/Box.cs: -------------------------------------------------------------------------------- 1 | namespace Exadel.Compreface.DTOs.HelperDTOs; 2 | 3 | public class Box 4 | { 5 | public decimal Probability { get; set; } 6 | 7 | public int XMax { get; set; } 8 | 9 | public int YMax { get; set; } 10 | 11 | public int XMin { get; set; } 12 | 13 | public int YMin { get; set; } 14 | } -------------------------------------------------------------------------------- /Exadel.Compreface/DTOs/HelperDTOs/ExecutionTime.cs: -------------------------------------------------------------------------------- 1 | namespace Exadel.Compreface.DTOs.HelperDTOs; 2 | 3 | public class ExecutionTime 4 | { 5 | public decimal Age { get; set; } 6 | 7 | public decimal Gender { get; set; } 8 | 9 | public decimal Detector { get; set; } 10 | 11 | public decimal Calculator { get; set; } 12 | 13 | public decimal Mask { get; set; } 14 | } -------------------------------------------------------------------------------- /Exadel.Compreface/DTOs/HelperDTOs/Face.cs: -------------------------------------------------------------------------------- 1 | namespace Exadel.Compreface.DTOs.HelperDTOs; 2 | 3 | public class Face 4 | { 5 | public Guid ImageId { get; set; } 6 | 7 | public string Subject{ get; set; } 8 | } -------------------------------------------------------------------------------- /Exadel.Compreface/DTOs/HelperDTOs/Gender.cs: -------------------------------------------------------------------------------- 1 | namespace Exadel.Compreface.DTOs.HelperDTOs; 2 | 3 | public class Gender 4 | { 5 | public decimal Probability { get; set; } 6 | 7 | public string Value { get; set; } 8 | } -------------------------------------------------------------------------------- /Exadel.Compreface/DTOs/HelperDTOs/Mask.cs: -------------------------------------------------------------------------------- 1 | namespace Exadel.Compreface.DTOs.HelperDTOs; 2 | 3 | public class Mask 4 | { 5 | public decimal Probability { get; set; } 6 | 7 | public string Value { get; set; } 8 | } -------------------------------------------------------------------------------- /Exadel.Compreface/DTOs/HelperDTOs/PluginVersions.cs: -------------------------------------------------------------------------------- 1 | namespace Exadel.Compreface.DTOs.HelperDTOs; 2 | 3 | public class PluginVersions 4 | { 5 | public string Age { get; set; } 6 | 7 | public string Gender { get; set; } 8 | 9 | public string Detector { get; set; } 10 | 11 | public string Calculator { get; set; } 12 | 13 | public string Mask { get; set; } 14 | } -------------------------------------------------------------------------------- /Exadel.Compreface/DTOs/HelperDTOs/SimilarSubject.cs: -------------------------------------------------------------------------------- 1 | namespace Exadel.Compreface.DTOs.HelperDTOs; 2 | 3 | public class SimilarSubject 4 | { 5 | public decimal Similarity { get; set; } 6 | 7 | public string Subject { get; set; } 8 | } -------------------------------------------------------------------------------- /Exadel.Compreface/DTOs/RecognizeFaceFromImageDTOs/BaseRequests/BaseRecognizeFaceFromImageRequest.cs: -------------------------------------------------------------------------------- 1 | using Exadel.Compreface.DTOs.HelperDTOs.BaseDTOs; 2 | 3 | namespace Exadel.Compreface.DTOs.RecognitionDTOs.BaseRequests; 4 | 5 | /// 6 | /// Base class with several common properties. 7 | /// 8 | public class BaseRecognizeFaceFromImageRequest : BaseFaceRequest 9 | { 10 | /// 11 | /// Optional property: maximum number of subject predictions per face. It returns the most similar subjects. Default value: 1 12 | /// 13 | public int? PredictionCount { get; set; } 14 | } -------------------------------------------------------------------------------- /Exadel.Compreface/DTOs/RecognizeFaceFromImageDTOs/BaseRequests/BaseVerifyFacesFromImageRequest.cs: -------------------------------------------------------------------------------- 1 | using Exadel.Compreface.DTOs.HelperDTOs.BaseDTOs; 2 | 3 | namespace Exadel.Compreface.DTOs.RecognitionDTOs.BaseRequests; 4 | 5 | public class BaseVerifyFacesFromImageRequest : BaseFaceRequest 6 | { 7 | public Guid ImageId { get; set; } 8 | } -------------------------------------------------------------------------------- /Exadel.Compreface/DTOs/RecognizeFaceFromImageDTOs/RecognizeFaceFromImage/RecognizeFaceFromImageRequestByBytes.cs: -------------------------------------------------------------------------------- 1 | using Exadel.Compreface.DTOs.RecognitionDTOs.BaseRequests; 2 | 3 | namespace Exadel.Compreface.DTOs.RecognizeFaceFromImageDTOs.RecognizeFaceFromImage 4 | { 5 | public class RecognizeFaceFromImageRequestByBytes : BaseRecognizeFaceFromImageRequest 6 | { 7 | public byte[] ImageInBytes { get; set; } 8 | } 9 | } -------------------------------------------------------------------------------- /Exadel.Compreface/DTOs/RecognizeFaceFromImageDTOs/RecognizeFaceFromImage/RecognizeFaceFromImageRequestByFilePath.cs: -------------------------------------------------------------------------------- 1 | using Exadel.Compreface.DTOs.RecognitionDTOs.BaseRequests; 2 | 3 | namespace Exadel.Compreface.DTOs.RecognitionDTOs.RecognizeFaceFromImage; 4 | 5 | public class RecognizeFaceFromImageRequestByFilePath : BaseRecognizeFaceFromImageRequest 6 | { 7 | public string FilePath { get; set; } 8 | } -------------------------------------------------------------------------------- /Exadel.Compreface/DTOs/RecognizeFaceFromImageDTOs/RecognizeFaceFromImage/RecognizeFaceFromImageRequestByFileUrl.cs: -------------------------------------------------------------------------------- 1 | using Exadel.Compreface.DTOs.RecognitionDTOs.BaseRequests; 2 | 3 | namespace Exadel.Compreface.DTOs.RecognitionDTOs.RecognizeFaceFromImage 4 | { 5 | public class RecognizeFaceFromImageRequestByFileUrl : BaseRecognizeFaceFromImageRequest 6 | { 7 | public string FileUrl { get; set; } 8 | } 9 | } -------------------------------------------------------------------------------- /Exadel.Compreface/DTOs/RecognizeFaceFromImageDTOs/RecognizeFaceFromImage/RecognizeFaceFromImageResponse.cs: -------------------------------------------------------------------------------- 1 | using Exadel.Compreface.DTOs.HelperDTOs; 2 | using Exadel.Compreface.DTOs.HelperDTOs.BaseDTOs; 3 | 4 | namespace Exadel.Compreface.DTOs.RecognitionDTOs.RecognizeFaceFromImage; 5 | 6 | public class RecognizeFaceFromImageResponse 7 | { 8 | public IList Result { get; set; } 9 | 10 | public PluginVersions PluginsVersions { get; set; } 11 | } 12 | 13 | public class Result : BaseResult 14 | { 15 | public IList Subjects { get; set; } 16 | } -------------------------------------------------------------------------------- /Exadel.Compreface/DTOs/RecognizeFaceFromImageDTOs/RecognizeFaceFromImage/RecognizeFacesFromImageWithBase64Request.cs: -------------------------------------------------------------------------------- 1 | using Exadel.Compreface.DTOs.RecognitionDTOs.BaseRequests; 2 | 3 | namespace Exadel.Compreface.DTOs.RecognizeFaceFromImageDTOs.RecognizeFaceFromImage; 4 | 5 | public class RecognizeFacesFromImageWithBase64Request : BaseRecognizeFaceFromImageRequest 6 | { 7 | public string FileBase64Value { get; set; } 8 | } -------------------------------------------------------------------------------- /Exadel.Compreface/DTOs/RecognizeFaceFromImageDTOs/VerifyFacesFromImage/VerifyFacesFromImageByFilePathRequest.cs: -------------------------------------------------------------------------------- 1 | using Exadel.Compreface.DTOs.RecognitionDTOs.BaseRequests; 2 | 3 | namespace Exadel.Compreface.DTOs.RecognitionDTOs.VerifyFacesFromImage; 4 | 5 | public class VerifyFacesFromImageByFilePathRequest : BaseVerifyFacesFromImageRequest 6 | { 7 | public string FilePath { get; set; } 8 | } -------------------------------------------------------------------------------- /Exadel.Compreface/DTOs/RecognizeFaceFromImageDTOs/VerifyFacesFromImage/VerifyFacesFromImageByFileUrlRequest.cs: -------------------------------------------------------------------------------- 1 | using Exadel.Compreface.DTOs.RecognitionDTOs.BaseRequests; 2 | 3 | namespace Exadel.Compreface.DTOs.RecognizeFaceFromImageDTOs.VerifyFacesFromImage 4 | { 5 | public class VerifyFacesFromImageByFileUrlRequest : BaseVerifyFacesFromImageRequest 6 | { 7 | public string FileUrl { get; set; } 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /Exadel.Compreface/DTOs/RecognizeFaceFromImageDTOs/VerifyFacesFromImage/VerifyFacesFromImageResponse.cs: -------------------------------------------------------------------------------- 1 | using Exadel.Compreface.DTOs.HelperDTOs; 2 | using Exadel.Compreface.DTOs.HelperDTOs.BaseDTOs; 3 | 4 | namespace Exadel.Compreface.DTOs.RecognitionDTOs.VerifyFacesFromImage; 5 | 6 | public class VerifyFacesFromImageResponse 7 | { 8 | public IList Result { get; set; } 9 | 10 | public PluginVersions PluginsVersions { get; set; } 11 | } 12 | 13 | public class Result : BaseResult 14 | { 15 | public string Subject { get; set; } 16 | 17 | public decimal Similarity { get; set; } 18 | } -------------------------------------------------------------------------------- /Exadel.Compreface/DTOs/RecognizeFaceFromImageDTOs/VerifyFacesFromImage/VerifyFacesFromImageWithBase64Request.cs: -------------------------------------------------------------------------------- 1 | using Exadel.Compreface.DTOs.RecognitionDTOs.BaseRequests; 2 | 3 | namespace Exadel.Compreface.DTOs.RecognizeFaceFromImageDTOs.VerifyFacesFromImage; 4 | 5 | public class VerifyFacesFromImageWithBase64Request : BaseVerifyFacesFromImageRequest 6 | { 7 | public string FileBase64Value { get; set; } 8 | } -------------------------------------------------------------------------------- /Exadel.Compreface/DTOs/RecognizeFaceFromImageDTOs/VerifyFacesFromImage/VerifyFacesFromImageWithBytesRequest.cs: -------------------------------------------------------------------------------- 1 | using Exadel.Compreface.DTOs.RecognitionDTOs.BaseRequests; 2 | 3 | namespace Exadel.Compreface.DTOs.RecognizeFaceFromImageDTOs.VerifyFacesFromImage 4 | { 5 | public class VerifyFacesFromImageWithBytesRequest : BaseVerifyFacesFromImageRequest 6 | { 7 | public byte[] ImageInBytes { get; set; } 8 | } 9 | } -------------------------------------------------------------------------------- /Exadel.Compreface/DTOs/SubjectDTOs/AddSubject/AddSubjectRequest.cs: -------------------------------------------------------------------------------- 1 | namespace Exadel.Compreface.DTOs.SubjectDTOs.AddSubject; 2 | 3 | public class AddSubjectRequest 4 | { 5 | public string Subject { get; set; } 6 | } -------------------------------------------------------------------------------- /Exadel.Compreface/DTOs/SubjectDTOs/AddSubject/AddSubjectResponse.cs: -------------------------------------------------------------------------------- 1 | namespace Exadel.Compreface.DTOs.SubjectDTOs.AddSubject; 2 | 3 | public class AddSubjectResponse 4 | { 5 | public string Subject { get; set; } 6 | } -------------------------------------------------------------------------------- /Exadel.Compreface/DTOs/SubjectDTOs/DeleteAllSubjects/DeleteAllSubjectsResponse.cs: -------------------------------------------------------------------------------- 1 | namespace Exadel.Compreface.DTOs.SubjectDTOs.DeleteAllSubjects; 2 | 3 | public class DeleteAllSubjectsResponse 4 | { 5 | public int Deleted { get; set; } 6 | } -------------------------------------------------------------------------------- /Exadel.Compreface/DTOs/SubjectDTOs/DeleteSubject/DeleteSubjectRequest.cs: -------------------------------------------------------------------------------- 1 | namespace Exadel.Compreface.DTOs.SubjectDTOs.DeleteSubject; 2 | 3 | public class DeleteSubjectRequest 4 | { 5 | public string ActualSubject { get; set; } 6 | } -------------------------------------------------------------------------------- /Exadel.Compreface/DTOs/SubjectDTOs/DeleteSubject/DeleteSubjectResponse.cs: -------------------------------------------------------------------------------- 1 | namespace Exadel.Compreface.DTOs.SubjectDTOs.DeleteSubject; 2 | 3 | public class DeleteSubjectResponse 4 | { 5 | public string Subject { get; set; } 6 | } -------------------------------------------------------------------------------- /Exadel.Compreface/DTOs/SubjectDTOs/GetSubjectList/GetAllSubjectResponse.cs: -------------------------------------------------------------------------------- 1 | namespace Exadel.Compreface.DTOs.SubjectDTOs.GetSubjectList; 2 | 3 | public class GetAllSubjectResponse 4 | { 5 | public IList Subjects { get; set; } 6 | } -------------------------------------------------------------------------------- /Exadel.Compreface/DTOs/SubjectDTOs/RenameSubject/RenameSubjectRequest.cs: -------------------------------------------------------------------------------- 1 | namespace Exadel.Compreface.DTOs.SubjectDTOs.RenameSubject; 2 | 3 | public class RenameSubjectRequest 4 | { 5 | public string CurrentSubject { get; set; } 6 | 7 | public string Subject { get; set; } 8 | } -------------------------------------------------------------------------------- /Exadel.Compreface/DTOs/SubjectDTOs/RenameSubject/RenameSubjectResponse.cs: -------------------------------------------------------------------------------- 1 | namespace Exadel.Compreface.DTOs.SubjectDTOs.RenameSubject; 2 | 3 | public class RenameSubjectResponse 4 | { 5 | public bool Updated { get; set; } 6 | } -------------------------------------------------------------------------------- /Exadel.Compreface/Exadel.Compreface.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | net6.0;net7.0 5 | enable 6 | enable 7 | True 8 | CompreFace.NET.Sdk 9 | CompreFace NET SDK makes face recognition into your application even easier. 10 | Exadel 11 | Exadel 12 | .Net SDK for CompreFace - free and open-source face recognition system from Exadel 13 | https://exadel.com/solutions/compreface/ 14 | https://github.com/exadel-inc/compreface-net-sdk 15 | compreface 16 | en 17 | 1.0.2 18 | True 19 | compreface_icon.png 20 | 21 | 22 | 23 | embedded 24 | 25 | 26 | 27 | embedded 28 | 29 | 30 | 31 | embedded 32 | 33 | 34 | 35 | embedded 36 | 37 | 38 | 39 | 40 | True 41 | \ 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | PreserveNewest 55 | 56 | 57 | 58 | 59 | -------------------------------------------------------------------------------- /Exadel.Compreface/Exceptions/ServiceException.cs: -------------------------------------------------------------------------------- 1 | namespace Exadel.Compreface.Exceptions; 2 | 3 | public class ServiceException : Exception 4 | { 5 | public ServiceException() 6 | { } 7 | 8 | public ServiceException(string message) 9 | : base(message) 10 | { } 11 | 12 | public ServiceException(string message, Exception inner) 13 | : base(message, inner) 14 | { } 15 | } -------------------------------------------------------------------------------- /Exadel.Compreface/Exceptions/ServiceTimeoutException.cs: -------------------------------------------------------------------------------- 1 | namespace Exadel.Compreface.Exceptions; 2 | 3 | public class ServiceTimeoutException : Exception 4 | { 5 | public ServiceTimeoutException() 6 | { } 7 | 8 | public ServiceTimeoutException(string message) 9 | : base(message) 10 | { } 11 | 12 | public ServiceTimeoutException(string message, Exception inner) 13 | : base(message, inner) 14 | { } 15 | } -------------------------------------------------------------------------------- /Exadel.Compreface/Exceptions/TypeNotBelongCompreFaceException.cs: -------------------------------------------------------------------------------- 1 | namespace Exadel.Compreface.Exceptions 2 | { 3 | public class TypeNotBelongCompreFaceException : Exception 4 | { 5 | public TypeNotBelongCompreFaceException() 6 | { } 7 | 8 | public TypeNotBelongCompreFaceException(string message) 9 | : base(message) 10 | { } 11 | 12 | public TypeNotBelongCompreFaceException(string message, Exception inner) 13 | : base(message, inner) 14 | { } 15 | } 16 | } -------------------------------------------------------------------------------- /Exadel.Compreface/Helpers/ConvertUrlToBase64StringHelpers.cs: -------------------------------------------------------------------------------- 1 | using Exadel.Compreface.Clients.ApiClient; 2 | 3 | namespace Exadel.Compreface.Helpers 4 | { 5 | public static class ConvertUrlToBase64StringHelpers 6 | { 7 | public static async Task ConvertUrlAsync(IApiClient apiClient, string url) 8 | { 9 | var fileSourceImageStream = await apiClient.GetBytesAsync(url); 10 | return Convert.ToBase64String(fileSourceImageStream); 11 | } 12 | } 13 | } -------------------------------------------------------------------------------- /Exadel.Compreface/Helpers/FileHelpers.cs: -------------------------------------------------------------------------------- 1 | namespace Exadel.Compreface.Helpers; 2 | 3 | public class FileHelpers 4 | { 5 | public static string GenerateFileName(string filePath) 6 | { 7 | if (!File.Exists(filePath)) 8 | { 9 | throw new FileNotFoundException(message: $"File does not exist in path : {filePath}!"); 10 | } 11 | 12 | var fileExtension = Path.GetExtension(filePath); 13 | 14 | var generatedFileName = $"{Guid.NewGuid()}{fileExtension}"; 15 | 16 | return generatedFileName; 17 | } 18 | } -------------------------------------------------------------------------------- /Exadel.Compreface/Helpers/SnakeCaseToCamelCaseNamingPolicy.cs: -------------------------------------------------------------------------------- 1 | using System.Text.Json; 2 | 3 | namespace Exadel.Compreface.Helpers; 4 | 5 | public class SnakeCaseToCamelCaseNamingPolicy : JsonNamingPolicy 6 | { 7 | public static SnakeCaseToCamelCaseNamingPolicy Policy { get; } = new SnakeCaseToCamelCaseNamingPolicy(); 8 | public override string ConvertName(string name) 9 | { 10 | return name.ToSnakeCase(); 11 | } 12 | } -------------------------------------------------------------------------------- /Exadel.Compreface/Helpers/StringExtensions.cs: -------------------------------------------------------------------------------- 1 | namespace Exadel.Compreface.Helpers; 2 | 3 | public static class StringExtensions 4 | { 5 | public static string ToSnakeCase(this string str) 6 | { 7 | return string.Concat(str.Select((x, i) => i > 0 && char.IsUpper(x) ? "_" + x.ToString() : x.ToString())).ToLower(); 8 | } 9 | } -------------------------------------------------------------------------------- /Exadel.Compreface/Helpers/SystemJsonSerializer.cs: -------------------------------------------------------------------------------- 1 | using System.Text.Json; 2 | using Flurl.Http.Configuration; 3 | 4 | namespace Exadel.Compreface.Helpers; 5 | 6 | public class SystemJsonSerializer : ISerializer 7 | { 8 | private readonly JsonSerializerOptions? _options; 9 | 10 | public SystemJsonSerializer(JsonSerializerOptions? options = null) 11 | { 12 | _options = options; 13 | } 14 | 15 | public T Deserialize(string s) 16 | { 17 | return JsonSerializer.Deserialize(s, _options)!; 18 | } 19 | 20 | public T Deserialize(Stream stream) 21 | { 22 | using var reader = new StreamReader(stream); 23 | return Deserialize(reader.ReadToEnd()); 24 | } 25 | 26 | public string Serialize(object obj) 27 | { 28 | return JsonSerializer.Serialize(obj, _options); 29 | } 30 | } -------------------------------------------------------------------------------- /Exadel.Compreface/Services/Attributes/CompreFaceServiceAttribute.cs: -------------------------------------------------------------------------------- 1 | using System.Runtime.CompilerServices; 2 | 3 | [assembly: InternalsVisibleTo("Exadel.Compreface.AcceptenceTests")] 4 | [assembly: InternalsVisibleTo("Exadel.Compreface.UnitTests")] 5 | namespace Exadel.Compreface.Services.Attributes 6 | { 7 | /// 8 | /// Tag for CompreFace services to open ability call them from client. 9 | /// 10 | [AttributeUsage(AttributeTargets.Class, AllowMultiple = true)] 11 | internal class CompreFaceService : Attribute 12 | { 13 | } 14 | } -------------------------------------------------------------------------------- /Exadel.Compreface/Services/DetectionService.cs: -------------------------------------------------------------------------------- 1 | using Exadel.Compreface.Clients.ApiClient; 2 | using Exadel.Compreface.Configuration; 3 | using Exadel.Compreface.DTOs.FaceDetectionDTOs.FaceDetection; 4 | using Exadel.Compreface.DTOs.HelperDTOs.BaseDTOs; 5 | using Exadel.Compreface.Helpers; 6 | using Exadel.Compreface.Services.Attributes; 7 | using Exadel.Compreface.Services.Interfaces; 8 | using Flurl; 9 | using Flurl.Http; 10 | 11 | namespace Exadel.Compreface.Services 12 | { 13 | [CompreFaceService] 14 | public class DetectionService : IDetectionService 15 | { 16 | private readonly IComprefaceConfiguration _configuration; 17 | private readonly IApiClient _apiClient; 18 | 19 | public DetectionService(IComprefaceConfiguration configuration, IApiClient apiClient) 20 | { 21 | _configuration = configuration; 22 | _apiClient = apiClient; 23 | } 24 | 25 | public async Task DetectAsync(FaceDetectionRequestByFilePath faceDetectionRequest) 26 | { 27 | var requestUrlWithQueryParameters = GetRequestUrl(faceDetectionRequest); 28 | 29 | var response = await 30 | _apiClient.PostMultipartAsync( 31 | requestUrl: requestUrlWithQueryParameters, 32 | buildContent: mp => 33 | mp.AddFile("file", fileName: FileHelpers.GenerateFileName(faceDetectionRequest.FilePath), path: faceDetectionRequest.FilePath)); 34 | 35 | return response; 36 | } 37 | 38 | public async Task DetectAsync(FaceDetectionRequestByFileUrl faceDetectionRequest) 39 | { 40 | var requestUrlWithQueryParameters = GetRequestUrl(faceDetectionRequest); 41 | 42 | var fileInBase64String = await ConvertUrlToBase64StringHelpers.ConvertUrlAsync(_apiClient, faceDetectionRequest.FileUrl); 43 | 44 | var response = await _apiClient.PostJsonAsync(requestUrlWithQueryParameters, body: new { file = fileInBase64String }); 45 | 46 | return response; 47 | } 48 | 49 | public async Task DetectAsync(FaceDetectionRequestByBytes faceDetectionRequest) 50 | { 51 | var requestUrlWithQueryParameters = GetRequestUrl(faceDetectionRequest); 52 | 53 | var fileInBase64String = Convert.ToBase64String(faceDetectionRequest.ImageInBytes); 54 | 55 | var response = await _apiClient.PostJsonAsync(requestUrlWithQueryParameters, body: new { file = fileInBase64String }); 56 | 57 | return response; 58 | } 59 | 60 | public async Task DetectAsync(FaceDetectionBase64Request faceDetectionRequest) 61 | { 62 | var requestUrlWithQueryParameters = GetRequestUrl(faceDetectionRequest); 63 | 64 | var response = await 65 | _apiClient.PostJsonAsync( 66 | requestUrl: requestUrlWithQueryParameters, 67 | body: new { file = faceDetectionRequest.File }); 68 | 69 | return response; 70 | } 71 | 72 | private Url GetRequestUrl(BaseFaceRequest baseFaceRequest) 73 | { 74 | var requestUrl = $"{_configuration.Domain}:{_configuration.Port}/api/v1/detection/detect"; 75 | 76 | return requestUrl 77 | .SetQueryParams(new 78 | { 79 | limit = baseFaceRequest.Limit, 80 | det_prob_threshold = baseFaceRequest.DetProbThreshold, 81 | face_plugins = string.Join(",", baseFaceRequest.FacePlugins), 82 | status = baseFaceRequest.Status, 83 | }); 84 | } 85 | } 86 | } -------------------------------------------------------------------------------- /Exadel.Compreface/Services/Interfaces/IDetectionService.cs: -------------------------------------------------------------------------------- 1 | using Exadel.Compreface.DTOs.FaceDetectionDTOs.FaceDetection; 2 | 3 | namespace Exadel.Compreface.Services.Interfaces 4 | { 5 | public interface IDetectionService 6 | { 7 | Task DetectAsync(FaceDetectionRequestByFilePath faceDetectionRequest); 8 | 9 | Task DetectAsync(FaceDetectionRequestByFileUrl faceDetectionRequest); 10 | 11 | Task DetectAsync(FaceDetectionBase64Request faceDetectionRequest); 12 | 13 | Task DetectAsync(FaceDetectionRequestByBytes faceDetectionRequest); 14 | } 15 | } -------------------------------------------------------------------------------- /Exadel.Compreface/Services/Interfaces/IFaceCollection.cs: -------------------------------------------------------------------------------- 1 | using Exadel.Compreface.DTOs.FaceCollectionDTOs.AddSubjectExample; 2 | using Exadel.Compreface.DTOs.FaceCollectionDTOs.DeleteAllSubjectExamples; 3 | using Exadel.Compreface.DTOs.FaceCollectionDTOs.DeleteImageById; 4 | using Exadel.Compreface.DTOs.FaceCollectionDTOs.DeleteMultipleExamples; 5 | using Exadel.Compreface.DTOs.FaceCollectionDTOs.DownloadImageById; 6 | using Exadel.Compreface.DTOs.FaceCollectionDTOs.DownloadImageByIdFromSubject; 7 | using Exadel.Compreface.DTOs.FaceCollectionDTOs.ListAllExampleSubject; 8 | 9 | namespace Exadel.Compreface.Services.Interfaces 10 | { 11 | public interface IFaceCollection 12 | { 13 | Task AddAsync(AddSubjectExampleRequestByFilePath request); 14 | 15 | Task AddAsync(AddSubjectExampleRequestByFileUrl request); 16 | 17 | Task AddAsync(AddSubjectExampleRequestByBytes request); 18 | 19 | Task AddAsync(AddBase64SubjectExampleRequest request); 20 | 21 | Task ListAsync(ListAllSubjectExamplesRequest request); 22 | 23 | Task DeleteAllAsync(DeleteAllExamplesRequest request); 24 | 25 | Task DeleteAsync(DeleteImageByIdRequest request); 26 | 27 | Task DeleteAsync(DeleteMultipleExampleRequest deleteMultipleExamplesRequest); 28 | 29 | Task DownloadAsync(DownloadImageByIdDirectlyRequest downloadImageByIdRequest); 30 | 31 | Task DownloadAsync(DownloadImageByIdFromSubjectRequest downloadImageBySubjectIdRequest); 32 | } 33 | } -------------------------------------------------------------------------------- /Exadel.Compreface/Services/Interfaces/IRecognizeFaceFromImage.cs: -------------------------------------------------------------------------------- 1 | using Exadel.Compreface.DTOs.RecognitionDTOs.RecognizeFaceFromImage; 2 | using Exadel.Compreface.DTOs.RecognitionDTOs.VerifyFacesFromImage; 3 | using Exadel.Compreface.DTOs.RecognizeFaceFromImageDTOs.RecognizeFaceFromImage; 4 | using Exadel.Compreface.DTOs.RecognizeFaceFromImageDTOs.VerifyFacesFromImage; 5 | 6 | namespace Exadel.Compreface.Services.Interfaces 7 | { 8 | public interface IRecognizeFaceFromImage 9 | { 10 | Task RecognizeAsync(RecognizeFaceFromImageRequestByFilePath request); 11 | 12 | Task RecognizeAsync(RecognizeFaceFromImageRequestByFileUrl request); 13 | 14 | Task RecognizeAsync(RecognizeFaceFromImageRequestByBytes request); 15 | 16 | Task RecognizeAsync(RecognizeFacesFromImageWithBase64Request request); 17 | 18 | Task VerifyAsync(VerifyFacesFromImageByFilePathRequest request); 19 | 20 | Task VerifyAsync(VerifyFacesFromImageByFileUrlRequest request); 21 | 22 | Task VerifyAsync(VerifyFacesFromImageWithBase64Request request); 23 | 24 | Task VerifyAsync(VerifyFacesFromImageWithBytesRequest request); 25 | } 26 | } -------------------------------------------------------------------------------- /Exadel.Compreface/Services/Interfaces/ISubject.cs: -------------------------------------------------------------------------------- 1 | using Exadel.Compreface.DTOs.SubjectDTOs.AddSubject; 2 | using Exadel.Compreface.DTOs.SubjectDTOs.DeleteAllSubjects; 3 | using Exadel.Compreface.DTOs.SubjectDTOs.DeleteSubject; 4 | using Exadel.Compreface.DTOs.SubjectDTOs.GetSubjectList; 5 | using Exadel.Compreface.DTOs.SubjectDTOs.RenameSubject; 6 | 7 | namespace Exadel.Compreface.Services.Interfaces 8 | { 9 | public interface ISubject 10 | { 11 | Task ListAsync(); 12 | 13 | Task AddAsync(AddSubjectRequest request); 14 | 15 | Task RenameAsync(RenameSubjectRequest request); 16 | 17 | Task DeleteAsync(DeleteSubjectRequest request); 18 | 19 | Task DeleteAllAsync(); 20 | } 21 | } -------------------------------------------------------------------------------- /Exadel.Compreface/Services/Interfaces/IVerificationService.cs: -------------------------------------------------------------------------------- 1 | using Exadel.Compreface.DTOs.FaceVerificationDTOs.FaceVerification; 2 | 3 | namespace Exadel.Compreface.Services.Interfaces 4 | { 5 | public interface IVerificationService 6 | { 7 | Task VerifyAsync(FaceVerificationRequestByFilePath request); 8 | 9 | Task VerifyAsync(FaceVerificationRequestByFileUrl request); 10 | 11 | Task VerifyAsync(FaceVerificationRequestByBytes request); 12 | 13 | Task VerifyAsync(FaceVerificationWithBase64Request request); 14 | } 15 | } -------------------------------------------------------------------------------- /Exadel.Compreface/Services/RecognitionService/FaceCollection.cs: -------------------------------------------------------------------------------- 1 | using Exadel.Compreface.Clients.ApiClient; 2 | using Exadel.Compreface.Configuration; 3 | using Exadel.Compreface.DTOs.FaceCollectionDTOs.AddSubjectExample; 4 | using Exadel.Compreface.DTOs.FaceCollectionDTOs.DeleteAllSubjectExamples; 5 | using Exadel.Compreface.DTOs.FaceCollectionDTOs.DeleteImageById; 6 | using Exadel.Compreface.DTOs.FaceCollectionDTOs.DeleteMultipleExamples; 7 | using Exadel.Compreface.DTOs.FaceCollectionDTOs.DownloadImageById; 8 | using Exadel.Compreface.DTOs.FaceCollectionDTOs.DownloadImageByIdFromSubject; 9 | using Exadel.Compreface.DTOs.FaceCollectionDTOs.ListAllExampleSubject; 10 | using Exadel.Compreface.DTOs.HelperDTOs; 11 | using Exadel.Compreface.DTOs.HelperDTOs.BaseDTOs; 12 | using Exadel.Compreface.Helpers; 13 | using Exadel.Compreface.Services.Interfaces; 14 | using Flurl; 15 | using Flurl.Http; 16 | 17 | namespace Exadel.Compreface.Services.RecognitionService 18 | { 19 | public class FaceCollection : IFaceCollection 20 | { 21 | private readonly IComprefaceConfiguration _configuration; 22 | private readonly IApiClient _apiClient; 23 | 24 | public FaceCollection(IComprefaceConfiguration configuration, IApiClient apiClient) 25 | { 26 | _configuration = configuration; 27 | _apiClient = apiClient; 28 | } 29 | 30 | public async Task AddAsync(AddSubjectExampleRequestByFilePath request) 31 | { 32 | var requestUrlWithQueryParameters = GetRequestUrl(request); 33 | 34 | var response = await _apiClient.PostMultipartAsync( 35 | requestUrl: requestUrlWithQueryParameters, 36 | buildContent: mp => 37 | mp.AddFile("file", fileName: FileHelpers.GenerateFileName(request.FilePath), path: request.FilePath)); 38 | 39 | return response; 40 | } 41 | 42 | public async Task AddAsync(AddSubjectExampleRequestByFileUrl request) 43 | { 44 | var requestUrlWithQueryParameters = GetRequestUrl(request); 45 | 46 | var fileInBase64String = await ConvertUrlToBase64StringHelpers.ConvertUrlAsync(_apiClient, request.FileUrl); 47 | 48 | var response = await _apiClient.PostJsonAsync(requestUrlWithQueryParameters, body: new { file = fileInBase64String }); 49 | 50 | return response; 51 | } 52 | 53 | public async Task AddAsync(AddSubjectExampleRequestByBytes request) 54 | { 55 | var requestUrlWithQueryParameters = GetRequestUrl(request); 56 | 57 | var fileInBase64String = Convert.ToBase64String(request.ImageInBytes); 58 | 59 | var response = await _apiClient.PostJsonAsync(requestUrlWithQueryParameters, body: new { file = fileInBase64String }); 60 | 61 | return response; 62 | } 63 | 64 | public async Task AddAsync(AddBase64SubjectExampleRequest request) 65 | { 66 | var requestUrlWithQueryParameters = GetRequestUrl(request); 67 | 68 | var response = await _apiClient.PostJsonAsync(requestUrlWithQueryParameters, new { file = request.File }); 69 | 70 | return response; 71 | } 72 | 73 | public async Task ListAsync(ListAllSubjectExamplesRequest request) 74 | { 75 | var requestUrl = $"{_configuration.Domain}:{_configuration.Port}/api/v1/recognition/faces"; 76 | var requestUrlWithQueryParameters = requestUrl 77 | .SetQueryParams(new 78 | { 79 | page = request.Page, 80 | size = request.Size, 81 | subject = request.Subject, 82 | }); 83 | 84 | var response = await _apiClient.GetJsonAsync(requestUrlWithQueryParameters); 85 | 86 | return response; 87 | } 88 | 89 | public async Task DeleteAllAsync(DeleteAllExamplesRequest request) 90 | { 91 | var requestUrl = $"{_configuration.Domain}:{_configuration.Port}/api/v1/recognition/faces"; 92 | var requestUrlWithQueryParameters = requestUrl 93 | .SetQueryParam("subject", request.Subject); 94 | 95 | var response = 96 | await _apiClient.DeleteJsonAsync(requestUrlWithQueryParameters); 97 | 98 | return response; 99 | } 100 | 101 | public async Task DeleteAsync(DeleteImageByIdRequest request) 102 | { 103 | var requestUrl = $"{_configuration.Domain}:{_configuration.Port}/api/v1/recognition/faces"; 104 | var requestUrlWithQueryParameters = requestUrl 105 | .AppendPathSegment(request.ImageId.ToString()); 106 | 107 | var response = await 108 | _apiClient.DeleteJsonAsync(requestUrlWithQueryParameters); 109 | 110 | return response; 111 | } 112 | 113 | public async Task DeleteAsync(DeleteMultipleExampleRequest deleteMultipleExamplesRequest) 114 | { 115 | var requestUrl = $"{_configuration.Domain}:{_configuration.Port}/api/v1/recognition/faces"; 116 | var requestUrlWithQueryParameters = requestUrl 117 | .AppendPathSegment("delete"); 118 | 119 | var response = await _apiClient.PostJsonAsync>(requestUrlWithQueryParameters, deleteMultipleExamplesRequest.ImageIdList); 120 | 121 | return new DeleteMultipleExamplesResponse() { Faces = response }; 122 | } 123 | 124 | public async Task DownloadAsync(DownloadImageByIdDirectlyRequest downloadImageByIdRequest) 125 | { 126 | var requestUrl = $"{_configuration.Domain}:{_configuration.Port}/api/v1/static"; 127 | var requestUrlWithQueryParameters = requestUrl 128 | .AppendPathSegments( 129 | downloadImageByIdRequest.RecognitionApiKey.ToString(), 130 | "/images/", 131 | downloadImageByIdRequest.ImageId.ToString()); 132 | 133 | var response = await _apiClient.GetBytesFromRemoteAsync(requestUrlWithQueryParameters); 134 | 135 | return response; 136 | } 137 | 138 | public async Task DownloadAsync(DownloadImageByIdFromSubjectRequest downloadImageBySubjectIdRequest) 139 | { 140 | var requestUrl = $"{_configuration.Domain}:{_configuration.Port}/api/v1/recognition/faces"; 141 | var requestUrlWithQueryParameters = requestUrl 142 | .AppendPathSegments(downloadImageBySubjectIdRequest.ImageId.ToString(), "/img"); 143 | 144 | var response = await _apiClient.GetBytesFromRemoteAsync(requestUrlWithQueryParameters); 145 | 146 | return response; 147 | } 148 | 149 | private Url GetRequestUrl(BaseExampleRequest request) 150 | { 151 | var requestUrl = $"{_configuration.Domain}:{_configuration.Port}/api/v1/recognition/faces"; 152 | 153 | return requestUrl 154 | .SetQueryParams(new 155 | { 156 | subject = request.Subject, 157 | det_prob_threshold = request.DetProbThreShold, 158 | }); 159 | } 160 | } 161 | } -------------------------------------------------------------------------------- /Exadel.Compreface/Services/RecognitionService/RecognitionService.cs: -------------------------------------------------------------------------------- 1 | using Exadel.Compreface.Clients.ApiClient; 2 | using Exadel.Compreface.Configuration; 3 | using Exadel.Compreface.Services.Attributes; 4 | using Exadel.Compreface.Services.Interfaces; 5 | 6 | namespace Exadel.Compreface.Services.RecognitionService; 7 | 8 | [CompreFaceService] 9 | public class RecognitionService 10 | { 11 | public IFaceCollection FaceCollection { get; set; } 12 | 13 | public ISubject Subject { get; set; } 14 | 15 | public IRecognizeFaceFromImage RecognizeFaceFromImage { get; set; } 16 | 17 | public RecognitionService(IComprefaceConfiguration configuration, IApiClient apiClient) 18 | { 19 | FaceCollection = new FaceCollection(configuration, apiClient); 20 | Subject = new Subject(configuration, apiClient); 21 | RecognizeFaceFromImage = new RecognizeFaceFromImage(configuration, apiClient); 22 | } 23 | } -------------------------------------------------------------------------------- /Exadel.Compreface/Services/RecognitionService/RecognizeFaceFromImage.cs: -------------------------------------------------------------------------------- 1 | using Exadel.Compreface.Clients.ApiClient; 2 | using Exadel.Compreface.Configuration; 3 | using Exadel.Compreface.DTOs.RecognitionDTOs.BaseRequests; 4 | using Exadel.Compreface.DTOs.RecognitionDTOs.RecognizeFaceFromImage; 5 | using Exadel.Compreface.DTOs.RecognitionDTOs.VerifyFacesFromImage; 6 | using Exadel.Compreface.DTOs.RecognizeFaceFromImageDTOs.RecognizeFaceFromImage; 7 | using Exadel.Compreface.DTOs.RecognizeFaceFromImageDTOs.VerifyFacesFromImage; 8 | using Exadel.Compreface.Helpers; 9 | using Exadel.Compreface.Services.Interfaces; 10 | using Flurl; 11 | using Flurl.Http; 12 | 13 | namespace Exadel.Compreface.Services.RecognitionService 14 | { 15 | public class RecognizeFaceFromImage : IRecognizeFaceFromImage 16 | { 17 | private readonly IComprefaceConfiguration _configuration; 18 | private readonly IApiClient _apiClient; 19 | 20 | public RecognizeFaceFromImage(IComprefaceConfiguration configuration, IApiClient apiClient) 21 | { 22 | _configuration = configuration; 23 | _apiClient = apiClient; 24 | } 25 | 26 | public async Task RecognizeAsync(RecognizeFaceFromImageRequestByFilePath request) 27 | { 28 | var requestUrlWithQueryParameters = GetRequestUrl(request); 29 | 30 | var response = await 31 | _apiClient.PostMultipartAsync( 32 | requestUrl: requestUrlWithQueryParameters, 33 | buildContent: mp => 34 | mp.AddFile("file", fileName: FileHelpers.GenerateFileName(request.FilePath), path: request.FilePath)); 35 | 36 | return response; 37 | } 38 | 39 | public async Task RecognizeAsync(RecognizeFaceFromImageRequestByFileUrl request) 40 | { 41 | var requestUrlWithQueryParameters = GetRequestUrl(request); 42 | 43 | var fileInBase64String = await ConvertUrlToBase64StringHelpers.ConvertUrlAsync(_apiClient, request.FileUrl); 44 | 45 | var response = await _apiClient.PostJsonAsync(requestUrlWithQueryParameters, body: new { file = fileInBase64String }); 46 | 47 | return response; 48 | } 49 | 50 | public async Task RecognizeAsync(RecognizeFaceFromImageRequestByBytes request) 51 | { 52 | var requestUrlWithQueryParameters = GetRequestUrl(request); 53 | 54 | var fileInBase64String = Convert.ToBase64String(request.ImageInBytes); 55 | 56 | var response = await _apiClient.PostJsonAsync(requestUrlWithQueryParameters, body: new { file = fileInBase64String }); 57 | 58 | return response; 59 | } 60 | 61 | public async Task RecognizeAsync(RecognizeFacesFromImageWithBase64Request request) 62 | { 63 | var requestUrlWithQueryParameters = GetRequestUrl(request); 64 | 65 | var response = await 66 | _apiClient.PostJsonAsync( 67 | requestUrl: requestUrlWithQueryParameters, 68 | body: new { file = request.FileBase64Value }); 69 | 70 | return response; 71 | } 72 | 73 | public async Task VerifyAsync(VerifyFacesFromImageByFilePathRequest request) 74 | { 75 | var requestUrlWithQueryParameters = GetRequestUrl(request); 76 | 77 | var response = await 78 | _apiClient.PostMultipartAsync( 79 | requestUrl: requestUrlWithQueryParameters, 80 | buildContent: mp => 81 | mp.AddFile("file", fileName: FileHelpers.GenerateFileName(request.FilePath), path: request.FilePath)); 82 | 83 | return response; 84 | } 85 | 86 | public async Task VerifyAsync(VerifyFacesFromImageWithBytesRequest request) 87 | { 88 | var requestUrlWithQueryParameters = GetRequestUrl(request); 89 | 90 | var response = await 91 | _apiClient.PostJsonAsync( 92 | requestUrl: requestUrlWithQueryParameters, 93 | body: new { file = request.ImageInBytes }); 94 | 95 | return response; 96 | } 97 | 98 | public async Task VerifyAsync(VerifyFacesFromImageWithBase64Request request) 99 | { 100 | var requestUrlWithQueryParameters = GetRequestUrl(request); 101 | 102 | var response = await 103 | _apiClient.PostJsonAsync( 104 | requestUrl: requestUrlWithQueryParameters, 105 | body: new { file = request.FileBase64Value }); 106 | 107 | return response; 108 | } 109 | 110 | private Url GetRequestUrl(BaseRecognizeFaceFromImageRequest request) 111 | { 112 | var requestUrl = $"{_configuration.Domain}:{_configuration.Port}/api/v1/recognition/recognize"; 113 | var requestUrlWithQueryParameters = requestUrl 114 | .SetQueryParams(new 115 | { 116 | limit = request.Limit, 117 | prediction_count = request.PredictionCount, 118 | det_prob_threshold = request.DetProbThreshold, 119 | face_plugins = string.Join(",", request.FacePlugins), 120 | status = request.Status, 121 | }); 122 | return requestUrlWithQueryParameters; 123 | } 124 | 125 | private Url GetRequestUrl(BaseVerifyFacesFromImageRequest request) 126 | { 127 | var requestUrl = $"{_configuration.Domain}:{_configuration.Port}/api/v1/recognition/faces/{request.ImageId}/verify"; 128 | var requestUrlWithQueryParameters = requestUrl 129 | .SetQueryParams(new 130 | { 131 | limit = request.Limit, 132 | det_prob_threshold = request.DetProbThreshold, 133 | face_plugins = string.Join(",", request.FacePlugins), 134 | status = request.Status, 135 | }); 136 | return requestUrlWithQueryParameters; 137 | } 138 | 139 | public async Task VerifyAsync(VerifyFacesFromImageByFileUrlRequest request) 140 | { 141 | var requestUrl = $"{_configuration.Domain}:{_configuration.Port}/api/v1/recognition/faces/{request.ImageId}/verify"; 142 | var requestUrlWithQueryParameters = requestUrl 143 | .SetQueryParams(new 144 | { 145 | limit = request.Limit, 146 | det_prob_threshold = request.DetProbThreshold, 147 | face_plugins = string.Join(",", request.FacePlugins), 148 | status = request.Status, 149 | }); 150 | 151 | var fileInBase64String = await ConvertUrlToBase64StringHelpers.ConvertUrlAsync(_apiClient, request.FileUrl); 152 | 153 | var response = await 154 | _apiClient.PostJsonAsync( 155 | requestUrl: requestUrlWithQueryParameters, 156 | body: new { file = fileInBase64String }); 157 | 158 | return response; 159 | } 160 | } 161 | } -------------------------------------------------------------------------------- /Exadel.Compreface/Services/RecognitionService/Subject.cs: -------------------------------------------------------------------------------- 1 | using Exadel.Compreface.Clients.ApiClient; 2 | using Exadel.Compreface.Configuration; 3 | using Exadel.Compreface.DTOs.SubjectDTOs.AddSubject; 4 | using Exadel.Compreface.DTOs.SubjectDTOs.DeleteAllSubjects; 5 | using Exadel.Compreface.DTOs.SubjectDTOs.DeleteSubject; 6 | using Exadel.Compreface.DTOs.SubjectDTOs.GetSubjectList; 7 | using Exadel.Compreface.DTOs.SubjectDTOs.RenameSubject; 8 | using Exadel.Compreface.Services.Interfaces; 9 | 10 | namespace Exadel.Compreface.Services.RecognitionService 11 | { 12 | public class Subject : ISubject 13 | { 14 | private readonly IComprefaceConfiguration _configuration; 15 | private readonly IApiClient _apiClient; 16 | 17 | public Subject(IComprefaceConfiguration configuration, IApiClient apiClient) 18 | { 19 | _configuration = configuration; 20 | _apiClient = apiClient; 21 | } 22 | 23 | public async Task ListAsync() 24 | { 25 | var requestUrl = $"{_configuration.Domain}:{_configuration.Port}/api/v1/recognition/subjects/"; 26 | 27 | var response = await _apiClient.GetJsonAsync(requestUrl); 28 | 29 | return response; 30 | } 31 | 32 | public async Task AddAsync(AddSubjectRequest request) 33 | { 34 | var requestUrl = $"{_configuration.Domain}:{_configuration.Port}/api/v1/recognition/subjects"; 35 | 36 | var response = await _apiClient.PostJsonAsync(requestUrl, request); 37 | 38 | return response; 39 | } 40 | 41 | public async Task RenameAsync(RenameSubjectRequest request) 42 | { 43 | var requestUrl = $"{_configuration.Domain}:{_configuration.Port}/api/v1/recognition/subjects/{request.CurrentSubject}"; 44 | 45 | var response = await _apiClient.PutJsonAsync(requestUrl, body: request.Subject); 46 | 47 | return response; 48 | } 49 | 50 | public async Task DeleteAsync(DeleteSubjectRequest request) 51 | { 52 | var requestUrl = $"{_configuration.Domain}:{_configuration.Port}/api/v1/recognition/subjects/{request.ActualSubject}"; 53 | 54 | var response = await _apiClient.DeleteJsonAsync(requestUrl); 55 | 56 | return response; 57 | } 58 | 59 | public async Task DeleteAllAsync() 60 | { 61 | var requestUrl = $"{_configuration.Domain}:{_configuration.Port}/api/v1/recognition/subjects"; 62 | 63 | var response = await _apiClient.DeleteJsonAsync(requestUrl); 64 | 65 | return response; 66 | } 67 | } 68 | } -------------------------------------------------------------------------------- /Exadel.Compreface/Services/VerificationService.cs: -------------------------------------------------------------------------------- 1 | using Exadel.Compreface.Clients.ApiClient; 2 | using Exadel.Compreface.Configuration; 3 | using Exadel.Compreface.DTOs.FaceVerificationDTOs.FaceVerification; 4 | using Exadel.Compreface.DTOs.HelperDTOs.BaseDTOs; 5 | using Exadel.Compreface.Helpers; 6 | using Exadel.Compreface.Services.Attributes; 7 | using Exadel.Compreface.Services.Interfaces; 8 | using Flurl; 9 | using Flurl.Http; 10 | 11 | namespace Exadel.Compreface.Services; 12 | 13 | [CompreFaceService] 14 | public class VerificationService : IVerificationService 15 | { 16 | private readonly IComprefaceConfiguration _configuration; 17 | private readonly IApiClient _apiClient; 18 | 19 | public VerificationService(IComprefaceConfiguration configuration, IApiClient apiClient) 20 | { 21 | _configuration = configuration; 22 | _apiClient = apiClient; 23 | } 24 | 25 | public async Task VerifyAsync(FaceVerificationRequestByFilePath request) 26 | { 27 | var requestUrlWithQueryParameters = GetRequestUrl(request); 28 | 29 | var response = await 30 | _apiClient.PostMultipartAsync( 31 | requestUrl: requestUrlWithQueryParameters, 32 | buildContent: mp => 33 | { 34 | mp.AddFile(name: "source_image", fileName: FileHelpers.GenerateFileName(request.SourceImageFilePath), 35 | path: request.SourceImageFilePath); 36 | mp.AddFile(name: "target_image", fileName: FileHelpers.GenerateFileName(request.TargetImageFilePath), 37 | path: request.TargetImageFilePath); 38 | } 39 | ); 40 | 41 | return response; 42 | } 43 | 44 | public async Task VerifyAsync(FaceVerificationRequestByFileUrl request) 45 | { 46 | var requestUrlWithQueryParameters = GetRequestUrl(request); 47 | 48 | var fileSourceImagInBase64String = await ConvertUrlToBase64StringHelpers.ConvertUrlAsync(_apiClient, request.SourceImageFileUrl); 49 | var fileTargetImagegInBase64Strin = await ConvertUrlToBase64StringHelpers.ConvertUrlAsync(_apiClient, request.TargetImageFileUrl); 50 | 51 | var response = await _apiClient.PostJsonAsync(requestUrlWithQueryParameters, 52 | body: new 53 | { 54 | source_image = fileSourceImagInBase64String, 55 | target_image = fileTargetImagegInBase64Strin 56 | }); 57 | 58 | return response; 59 | } 60 | 61 | public async Task VerifyAsync(FaceVerificationRequestByBytes request) 62 | { 63 | var requestUrlWithQueryParameters = GetRequestUrl(request); 64 | 65 | var fileSourceImagInBase64String = Convert.ToBase64String(request.SourceImageInBytes); 66 | var fileTargetImagegInBase64Strin = Convert.ToBase64String(request.TargetImageInBytes); 67 | 68 | var response = await _apiClient.PostJsonAsync(requestUrlWithQueryParameters, 69 | body: new 70 | { 71 | source_image = fileSourceImagInBase64String, 72 | target_image = fileTargetImagegInBase64Strin 73 | }); 74 | 75 | return response; 76 | } 77 | 78 | public async Task VerifyAsync(FaceVerificationWithBase64Request request) 79 | { 80 | var requestUrlWithQueryParameters = GetRequestUrl(request); 81 | 82 | var response = await _apiClient.PostJsonAsync(requestUrl: requestUrlWithQueryParameters, 83 | body: new 84 | { 85 | source_image = request.SourceImageWithBase64, 86 | target_image = request.TargetImageWithBase64, 87 | }); 88 | 89 | return response; 90 | } 91 | 92 | private Url GetRequestUrl(BaseFaceRequest baseFaceRequest) 93 | { 94 | var requestUrl = $"{_configuration.Domain}:{_configuration.Port}/api/v1/verification/verify"; 95 | 96 | return requestUrl 97 | .SetQueryParams(new 98 | { 99 | limit = baseFaceRequest.Limit, 100 | det_prob_threshold = baseFaceRequest.DetProbThreshold, 101 | face_plugins = string.Join(",", baseFaceRequest.FacePlugins), 102 | status = baseFaceRequest.Status, 103 | }); 104 | } 105 | } -------------------------------------------------------------------------------- /Exadel.Compreface/images/compreface_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/exadel-inc/compreface-net-sdk/5e2275353ef59688e9d628d5060ee7d8604f9912/Exadel.Compreface/images/compreface_icon.png -------------------------------------------------------------------------------- /Example/Example.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Exe 5 | net6.0;net7.0 6 | enable 7 | enable 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | PreserveNewest 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /Example/appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "Domain": "http://localhost", 3 | "Port": "8000", 4 | "FaceDetectionApiKey": "00000000-0000-0000-0000-000000000003", 5 | "FaceVerificationApiKey": "00000000-0000-0000-0000-000000000004", 6 | "FaceRecognitionApiKey": "00000000-0000-0000-0000-000000000002" 7 | } -------------------------------------------------------------------------------- /RecognitionExampleApp/.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | ## 4 | ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore 5 | 6 | # User-specific files 7 | *.rsuser 8 | *.suo 9 | *.user 10 | *.userosscache 11 | *.sln.docstates 12 | 13 | # User-specific files (MonoDevelop/Xamarin Studio) 14 | *.userprefs 15 | 16 | # Mono auto generated files 17 | mono_crash.* 18 | 19 | # Build results 20 | [Dd]ebug/ 21 | [Dd]ebugPublic/ 22 | [Rr]elease/ 23 | [Rr]eleases/ 24 | x64/ 25 | x86/ 26 | [Ww][Ii][Nn]32/ 27 | [Aa][Rr][Mm]/ 28 | [Aa][Rr][Mm]64/ 29 | bld/ 30 | [Bb]in/ 31 | [Oo]bj/ 32 | [Ll]og/ 33 | [Ll]ogs/ 34 | 35 | # Visual Studio 2015/2017 cache/options directory 36 | .vs/ 37 | # Uncomment if you have tasks that create the project's static files in wwwroot 38 | #wwwroot/ 39 | 40 | # Visual Studio 2017 auto generated files 41 | Generated\ Files/ 42 | 43 | # MSTest test Results 44 | [Tt]est[Rr]esult*/ 45 | [Bb]uild[Ll]og.* 46 | 47 | # NUnit 48 | *.VisualState.xml 49 | TestResult.xml 50 | nunit-*.xml 51 | 52 | # Build Results of an ATL Project 53 | [Dd]ebugPS/ 54 | [Rr]eleasePS/ 55 | dlldata.c 56 | 57 | # Benchmark Results 58 | BenchmarkDotNet.Artifacts/ 59 | 60 | # .NET Core 61 | project.lock.json 62 | project.fragment.lock.json 63 | artifacts/ 64 | 65 | # Tye 66 | .tye/ 67 | 68 | # ASP.NET Scaffolding 69 | ScaffoldingReadMe.txt 70 | 71 | # StyleCop 72 | StyleCopReport.xml 73 | 74 | # Files built by Visual Studio 75 | *_i.c 76 | *_p.c 77 | *_h.h 78 | *.ilk 79 | *.meta 80 | *.obj 81 | *.iobj 82 | *.pch 83 | *.pdb 84 | *.ipdb 85 | *.pgc 86 | *.pgd 87 | *.rsp 88 | *.sbr 89 | *.tlb 90 | *.tli 91 | *.tlh 92 | *.tmp 93 | *.tmp_proj 94 | *_wpftmp.csproj 95 | *.log 96 | *.vspscc 97 | *.vssscc 98 | .builds 99 | *.pidb 100 | *.svclog 101 | *.scc 102 | 103 | # Chutzpah Test files 104 | _Chutzpah* 105 | 106 | # Visual C++ cache files 107 | ipch/ 108 | *.aps 109 | *.ncb 110 | *.opendb 111 | *.opensdf 112 | *.sdf 113 | *.cachefile 114 | *.VC.db 115 | *.VC.VC.opendb 116 | 117 | # Visual Studio profiler 118 | *.psess 119 | *.vsp 120 | *.vspx 121 | *.sap 122 | 123 | # Visual Studio Trace Files 124 | *.e2e 125 | 126 | # TFS 2012 Local Workspace 127 | $tf/ 128 | 129 | # Guidance Automation Toolkit 130 | *.gpState 131 | 132 | # ReSharper is a .NET coding add-in 133 | _ReSharper*/ 134 | *.[Rr]e[Ss]harper 135 | *.DotSettings.user 136 | 137 | # TeamCity is a build add-in 138 | _TeamCity* 139 | 140 | # DotCover is a Code Coverage Tool 141 | *.dotCover 142 | 143 | # AxoCover is a Code Coverage Tool 144 | .axoCover/* 145 | !.axoCover/settings.json 146 | 147 | # Coverlet is a free, cross platform Code Coverage Tool 148 | coverage*.json 149 | coverage*.xml 150 | coverage*.info 151 | 152 | # Visual Studio code coverage results 153 | *.coverage 154 | *.coveragexml 155 | 156 | # NCrunch 157 | _NCrunch_* 158 | .*crunch*.local.xml 159 | nCrunchTemp_* 160 | 161 | # MightyMoose 162 | *.mm.* 163 | AutoTest.Net/ 164 | 165 | # Web workbench (sass) 166 | .sass-cache/ 167 | 168 | # Installshield output folder 169 | [Ee]xpress/ 170 | 171 | # DocProject is a documentation generator add-in 172 | DocProject/buildhelp/ 173 | DocProject/Help/*.HxT 174 | DocProject/Help/*.HxC 175 | DocProject/Help/*.hhc 176 | DocProject/Help/*.hhk 177 | DocProject/Help/*.hhp 178 | DocProject/Help/Html2 179 | DocProject/Help/html 180 | 181 | # Click-Once directory 182 | publish/ 183 | 184 | # Publish Web Output 185 | *.[Pp]ublish.xml 186 | *.azurePubxml 187 | # Note: Comment the next line if you want to checkin your web deploy settings, 188 | # but database connection strings (with potential passwords) will be unencrypted 189 | *.pubxml 190 | *.publishproj 191 | 192 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 193 | # checkin your Azure Web App publish settings, but sensitive information contained 194 | # in these scripts will be unencrypted 195 | PublishScripts/ 196 | 197 | # NuGet Packages 198 | *.nupkg 199 | # NuGet Symbol Packages 200 | *.snupkg 201 | # The packages folder can be ignored because of Package Restore 202 | **/[Pp]ackages/* 203 | # except build/, which is used as an MSBuild target. 204 | !**/[Pp]ackages/build/ 205 | # Uncomment if necessary however generally it will be regenerated when needed 206 | #!**/[Pp]ackages/repositories.config 207 | # NuGet v3's project.json files produces more ignorable files 208 | *.nuget.props 209 | *.nuget.targets 210 | 211 | # Microsoft Azure Build Output 212 | csx/ 213 | *.build.csdef 214 | 215 | # Microsoft Azure Emulator 216 | ecf/ 217 | rcf/ 218 | 219 | # Windows Store app package directories and files 220 | AppPackages/ 221 | BundleArtifacts/ 222 | Package.StoreAssociation.xml 223 | _pkginfo.txt 224 | *.appx 225 | *.appxbundle 226 | *.appxupload 227 | 228 | # Visual Studio cache files 229 | # files ending in .cache can be ignored 230 | *.[Cc]ache 231 | # but keep track of directories ending in .cache 232 | !?*.[Cc]ache/ 233 | 234 | # Others 235 | ClientBin/ 236 | ~$* 237 | *~ 238 | *.dbmdl 239 | *.dbproj.schemaview 240 | *.jfm 241 | *.pfx 242 | *.publishsettings 243 | orleans.codegen.cs 244 | 245 | # Including strong name files can present a security risk 246 | # (https://github.com/github/gitignore/pull/2483#issue-259490424) 247 | #*.snk 248 | 249 | # Since there are multiple workflows, uncomment next line to ignore bower_components 250 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 251 | #bower_components/ 252 | 253 | # RIA/Silverlight projects 254 | Generated_Code/ 255 | 256 | # Backup & report files from converting an old project file 257 | # to a newer Visual Studio version. Backup files are not needed, 258 | # because we have git ;-) 259 | _UpgradeReport_Files/ 260 | Backup*/ 261 | UpgradeLog*.XML 262 | UpgradeLog*.htm 263 | ServiceFabricBackup/ 264 | *.rptproj.bak 265 | 266 | # SQL Server files 267 | *.mdf 268 | *.ldf 269 | *.ndf 270 | 271 | # Business Intelligence projects 272 | *.rdl.data 273 | *.bim.layout 274 | *.bim_*.settings 275 | *.rptproj.rsuser 276 | *- [Bb]ackup.rdl 277 | *- [Bb]ackup ([0-9]).rdl 278 | *- [Bb]ackup ([0-9][0-9]).rdl 279 | 280 | # Microsoft Fakes 281 | FakesAssemblies/ 282 | 283 | # GhostDoc plugin setting file 284 | *.GhostDoc.xml 285 | 286 | # Node.js Tools for Visual Studio 287 | .ntvs_analysis.dat 288 | node_modules/ 289 | 290 | # Visual Studio 6 build log 291 | *.plg 292 | 293 | # Visual Studio 6 workspace options file 294 | *.opt 295 | 296 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.) 297 | *.vbw 298 | 299 | # Visual Studio LightSwitch build output 300 | **/*.HTMLClient/GeneratedArtifacts 301 | **/*.DesktopClient/GeneratedArtifacts 302 | **/*.DesktopClient/ModelManifest.xml 303 | **/*.Server/GeneratedArtifacts 304 | **/*.Server/ModelManifest.xml 305 | _Pvt_Extensions 306 | 307 | # Paket dependency manager 308 | .paket/paket.exe 309 | paket-files/ 310 | 311 | # FAKE - F# Make 312 | .fake/ 313 | 314 | # CodeRush personal settings 315 | .cr/personal 316 | 317 | # Python Tools for Visual Studio (PTVS) 318 | __pycache__/ 319 | *.pyc 320 | 321 | # Cake - Uncomment if you are using it 322 | # tools/** 323 | # !tools/packages.config 324 | 325 | # Tabs Studio 326 | *.tss 327 | 328 | # Telerik's JustMock configuration file 329 | *.jmconfig 330 | 331 | # BizTalk build output 332 | *.btp.cs 333 | *.btm.cs 334 | *.odx.cs 335 | *.xsd.cs 336 | 337 | # OpenCover UI analysis results 338 | OpenCover/ 339 | 340 | # Azure Stream Analytics local run output 341 | ASALocalRun/ 342 | 343 | # MSBuild Binary and Structured Log 344 | *.binlog 345 | 346 | # NVidia Nsight GPU debugger configuration file 347 | *.nvuser 348 | 349 | # MFractors (Xamarin productivity tool) working folder 350 | .mfractor/ 351 | 352 | # Local History for Visual Studio 353 | .localhistory/ 354 | 355 | # BeatPulse healthcheck temp database 356 | healthchecksdb 357 | 358 | # Backup folder for Package Reference Convert tool in Visual Studio 2017 359 | MigrationBackup/ 360 | 361 | # Ionide (cross platform F# VS Code tools) working folder 362 | .ionide/ 363 | 364 | # Fody - auto-generated XML schema 365 | FodyWeavers.xsd 366 | 367 | ## 368 | ## Visual studio for Mac 369 | ## 370 | 371 | 372 | # globs 373 | Makefile.in 374 | *.userprefs 375 | *.usertasks 376 | config.make 377 | config.status 378 | aclocal.m4 379 | install-sh 380 | autom4te.cache/ 381 | *.tar.gz 382 | tarballs/ 383 | test-results/ 384 | 385 | # Mac bundle stuff 386 | *.dmg 387 | *.app 388 | 389 | # content below from: https://github.com/github/gitignore/blob/master/Global/macOS.gitignore 390 | # General 391 | .DS_Store 392 | .AppleDouble 393 | .LSOverride 394 | 395 | # Icon must end with two \r 396 | Icon 397 | 398 | 399 | # Thumbnails 400 | ._* 401 | 402 | # Files that might appear in the root of a volume 403 | .DocumentRevisions-V100 404 | .fseventsd 405 | .Spotlight-V100 406 | .TemporaryItems 407 | .Trashes 408 | .VolumeIcon.icns 409 | .com.apple.timemachine.donotpresent 410 | 411 | # Directories potentially created on remote AFP share 412 | .AppleDB 413 | .AppleDesktop 414 | Network Trash Folder 415 | Temporary Items 416 | .apdisk 417 | 418 | # content below from: https://github.com/github/gitignore/blob/master/Global/Windows.gitignore 419 | # Windows thumbnail cache files 420 | Thumbs.db 421 | ehthumbs.db 422 | ehthumbs_vista.db 423 | 424 | # Dump file 425 | *.stackdump 426 | 427 | # Folder config file 428 | [Dd]esktop.ini 429 | 430 | # Recycle Bin used on file shares 431 | $RECYCLE.BIN/ 432 | 433 | # Windows Installer files 434 | *.cab 435 | *.msi 436 | *.msix 437 | *.msm 438 | *.msp 439 | 440 | # Windows shortcuts 441 | *.lnk 442 | 443 | # JetBrains Rider 444 | .idea/ 445 | *.sln.iml 446 | 447 | ## 448 | ## Visual Studio Code 449 | ## 450 | .vscode/* 451 | !.vscode/settings.json 452 | !.vscode/tasks.json 453 | !.vscode/launch.json 454 | !.vscode/extensions.json 455 | -------------------------------------------------------------------------------- /RecognitionExampleApp/App.axaml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /RecognitionExampleApp/App.axaml.cs: -------------------------------------------------------------------------------- 1 | using Avalonia; 2 | using Avalonia.Controls.ApplicationLifetimes; 3 | using Avalonia.Markup.Xaml; 4 | 5 | namespace RecognitionExampleApp 6 | { 7 | public partial class App : Application 8 | { 9 | public override void Initialize() 10 | { 11 | AvaloniaXamlLoader.Load(this); 12 | } 13 | 14 | public override void OnFrameworkInitializationCompleted() 15 | { 16 | if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop) 17 | { 18 | desktop.MainWindow = new MainWindow(); 19 | } 20 | 21 | base.OnFrameworkInitializationCompleted(); 22 | } 23 | } 24 | } -------------------------------------------------------------------------------- /RecognitionExampleApp/Assets/compreface-icon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/exadel-inc/compreface-net-sdk/5e2275353ef59688e9d628d5060ee7d8604f9912/RecognitionExampleApp/Assets/compreface-icon.ico -------------------------------------------------------------------------------- /RecognitionExampleApp/Assets/question_mark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/exadel-inc/compreface-net-sdk/5e2275353ef59688e9d628d5060ee7d8604f9912/RecognitionExampleApp/Assets/question_mark.png -------------------------------------------------------------------------------- /RecognitionExampleApp/CreateSubjectHelpWindow.axaml: -------------------------------------------------------------------------------- 1 | 10 | 11 | 12 | 13 | 1. Install and run CompreFace from Docker on your local machine. 14 | 2. In your browser sign up for the system and login into the account you've just created or use the one you already have. After that, the system redirects you to the main page. 15 | 3. Create an application (left section) using the "Create" link at the bottom of the page. An application is where you can create and manage your Face Collections. 16 | 4. To recognize subjects among the known subjects, you need to create a Face Recognition Service. After creating a new Face Service, you can see it in the Services List with an appropriate name and API key. 17 | 5. Copy Api Key and paste it here to ApiKey field. 18 | 6. Choose folder with photos. 19 | 7. Click "Create object" button for process starting. 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /RecognitionExampleApp/CreateSubjectHelpWindow.axaml.cs: -------------------------------------------------------------------------------- 1 | using Avalonia.Controls; 2 | 3 | namespace RecognitionExampleApp 4 | { 5 | public partial class CreateSubjectHelpWindow : Window 6 | { 7 | public CreateSubjectHelpWindow() 8 | { 9 | InitializeComponent(); 10 | } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /RecognitionExampleApp/DoneWindow.axaml: -------------------------------------------------------------------------------- 1 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /RecognitionExampleApp/DoneWindow.axaml.cs: -------------------------------------------------------------------------------- 1 | using Avalonia.Controls; 2 | using Avalonia.Interactivity; 3 | 4 | namespace RecognitionExampleApp 5 | { 6 | public partial class DoneWindow : Window 7 | { 8 | public DoneWindow() 9 | { 10 | InitializeComponent(); 11 | } 12 | 13 | private void OnCloseClick(object sender, RoutedEventArgs e) 14 | { 15 | Close(); 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /RecognitionExampleApp/MainWindow.axaml: -------------------------------------------------------------------------------- 1 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 24 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 83 | 84 | 85 | 86 | 87 | 88 | Use this option to clear all previous created subjects 89 | in application according to the chosen Api Key. 90 | 91 | 92 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | -------------------------------------------------------------------------------- /RecognitionExampleApp/MainWindow.axaml.cs: -------------------------------------------------------------------------------- 1 | using Avalonia.Interactivity; 2 | using Exadel.Compreface.Clients.CompreFaceClient; 3 | using Exadel.Compreface.DTOs.FaceCollectionDTOs.AddSubjectExample; 4 | using Exadel.Compreface.DTOs.SubjectDTOs.AddSubject; 5 | using Exadel.Compreface.Services.RecognitionService; 6 | using Exadel.Compreface.DTOs.RecognitionDTOs.RecognizeFaceFromImage; 7 | using System.IO; 8 | using Avalonia.Controls; 9 | using System; 10 | using System.Threading.Tasks; 11 | using Emgu.CV.Structure; 12 | using Emgu.CV; 13 | 14 | namespace RecognitionExampleApp 15 | { 16 | public partial class MainWindow : Window 17 | { 18 | private ICompreFaceClient compreFaceClient; 19 | private RecognitionService recognitionService; 20 | private string[] imagePathList; 21 | 22 | private string recognizeImagePath; 23 | 24 | private string domain; 25 | private string port; 26 | private string apiKey; 27 | 28 | public MainWindow() 29 | { 30 | InitializeComponent(); 31 | } 32 | 33 | private async void OnBrowseClick(object sender, RoutedEventArgs e) 34 | { 35 | var dialogWindow = new OpenFolderDialog(); 36 | var inputFolder = await dialogWindow.ShowAsync(new Window()); 37 | if (inputFolder != null) 38 | { 39 | folderPath.Text = inputFolder; 40 | imagePathList = Directory.GetFiles(inputFolder); 41 | } 42 | } 43 | 44 | private void OnCreateSubjectHelpClick(object sender, RoutedEventArgs e) 45 | { 46 | new CreateSubjectHelpWindow().Show(); 47 | } 48 | 49 | private async void OnClearClick(object sender, RoutedEventArgs e) 50 | { 51 | var processingWindow = new ProcessingWindow(); 52 | processingWindow.Show(); 53 | 54 | ConfigureRecognitionService(); 55 | await recognitionService.Subject.DeleteAllAsync(); 56 | 57 | processingWindow.Close(); 58 | new DoneWindow().Show(); 59 | } 60 | 61 | private async void OnChooseClick(object sender, RoutedEventArgs e) 62 | { 63 | var dialogWindow = new OpenFileDialog(); 64 | var inputFile = await dialogWindow.ShowAsync(new Avalonia.Controls.Window()); 65 | if (inputFile != null) 66 | { 67 | imagePath.Text = inputFile[0]; 68 | recognizeImagePath = inputFile[0]; 69 | 70 | FileStream file = null!; 71 | 72 | if (File.Exists(recognizeImagePath)) 73 | { 74 | file = File.OpenRead(recognizeImagePath); 75 | } 76 | 77 | using (var imageStream = file) 78 | { 79 | uploadedImage.Source = Avalonia.Media.Imaging.Bitmap.DecodeToHeight(imageStream, 500); 80 | } 81 | } 82 | } 83 | 84 | private async void OnProcessClick(object sender, RoutedEventArgs e) 85 | { 86 | var recognizeRequest = new RecognizeFaceFromImageRequestByFilePath() 87 | { 88 | FilePath = recognizeImagePath, 89 | Limit = 1, 90 | PredictionCount = 100, 91 | Status = false, 92 | }; 93 | 94 | var recognitionResponse = await recognitionService.RecognizeFaceFromImage.RecognizeAsync(recognizeRequest); 95 | 96 | var similarityValue = (decimal)similarity.Value; 97 | 98 | var resultsWindow = new ResultsWindow(recognitionResponse, similarityValue, imagePathList); 99 | 100 | resultsWindow.Show(); 101 | } 102 | 103 | private async void OnCreateClick(object sender, RoutedEventArgs e) 104 | { 105 | var processingWindow = new ProcessingWindow(); 106 | processingWindow.Show(); 107 | 108 | ConfigureRecognitionService(); 109 | 110 | foreach (var imagePath in imagePathList) 111 | { 112 | await SaveCropedImageToSubject(imagePath); 113 | } 114 | 115 | processingWindow.Close(); 116 | new DoneWindow().Show(); 117 | } 118 | 119 | private void ConfigureRecognitionService() 120 | { 121 | domain = domainTextBox.Text; 122 | port = portTextBox.Text; 123 | apiKey = apiKeyTextBox.Text; 124 | 125 | compreFaceClient = new CompreFaceClient(domain, port); 126 | recognitionService = compreFaceClient.GetCompreFaceService(apiKey); 127 | } 128 | 129 | private async Task SaveCropedImageToSubject(string imagePath) 130 | { 131 | var imageName = Path.GetFileName(imagePath); 132 | var subject = await recognitionService.Subject.AddAsync(new AddSubjectRequest() { Subject = imageName }); 133 | 134 | Image image = new Image(imagePath); 135 | 136 | var face_quality_plugin_path = GetFacePluginPath(); 137 | CascadeClassifier faceDetector = new CascadeClassifier(face_quality_plugin_path); 138 | 139 | var faces = faceDetector.DetectMultiScale(image, 1.05, 10); 140 | 141 | foreach (var face in faces) 142 | { 143 | var faceImage = image.Copy(face).ToJpegData(); 144 | 145 | var imageBase64 = Convert.ToBase64String(faceImage); 146 | 147 | await recognitionService.FaceCollection.AddAsync(new AddBase64SubjectExampleRequest() { File = imageBase64, Subject = subject.Subject }); 148 | } 149 | } 150 | 151 | private string GetFacePluginPath() 152 | { 153 | var rootPath = Directory.GetCurrentDirectory(); 154 | var debugPath = Directory.GetParent(rootPath)?.ToString(); 155 | var binPath = Directory.GetParent(debugPath!)?.ToString(); 156 | var projectPath = Directory.GetParent(binPath!)?.ToString(); 157 | 158 | return Path.Combine(projectPath!, "Assets", "haarcascade_frontalface_alt2.xml"); 159 | } 160 | } 161 | } -------------------------------------------------------------------------------- /RecognitionExampleApp/ProcessingWindow.axaml: -------------------------------------------------------------------------------- 1 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /RecognitionExampleApp/ProcessingWindow.axaml.cs: -------------------------------------------------------------------------------- 1 | using Avalonia.Controls; 2 | 3 | namespace RecognitionExampleApp 4 | { 5 | public partial class ProcessingWindow : Window 6 | { 7 | public ProcessingWindow() 8 | { 9 | InitializeComponent(); 10 | } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /RecognitionExampleApp/Program.cs: -------------------------------------------------------------------------------- 1 | using Avalonia; 2 | using System; 3 | 4 | namespace RecognitionExampleApp 5 | { 6 | internal class Program 7 | { 8 | // Initialization code. Don't use any Avalonia, third-party APIs or any 9 | // SynchronizationContext-reliant code before AppMain is called: things aren't initialized 10 | // yet and stuff might break. 11 | [STAThread] 12 | public static void Main(string[] args) => BuildAvaloniaApp() 13 | .StartWithClassicDesktopLifetime(args); 14 | 15 | // Avalonia configuration, don't remove; also used by visual designer. 16 | public static AppBuilder BuildAvaloniaApp() 17 | => AppBuilder.Configure() 18 | .UsePlatformDetect() 19 | .LogToTrace(); 20 | } 21 | } -------------------------------------------------------------------------------- /RecognitionExampleApp/README.md: -------------------------------------------------------------------------------- 1 | # Recognition demo. 2 | This cross-platform desktop app is a simple example how to use CompreFace SDK for .NET. App is cross-platform and can be used with Linux, Mac and Windows. 3 | Before using our SDK make sure you have installed CompreFace, .NET and AvaloniaUI framework on your machine. 4 | 5 | # Description 6 | Application provide an ability to recognize face from a list of photos in a folder. 7 | 8 | # How demo app works. 9 | 1. Expand CompreFace on your local machine. 10 | 2. Create an application in your CompreFace account. 11 | 3. Create recognition service in this new application and copy its apikey. 12 | 4. Start the RecognitionExampleApp project from your IDE. 13 | 5. Fill the Domain, Port(by default: http://localhost:8000) and ApiKey(which was copied) fields in main window of application. 14 | 6. Then choose the folder with photos and click "Create subject" button. 15 | 7. Move to the "Step 2" and choose the photo with one person for recognition. Choose similarity threshold and click "Process" button. 16 | 17 | App will crop the faces from photos in a chosen folder and will save them as a subjects with names of a photo in your created service in CompreFace account. And then the window with results will be shown. Raise the similarity threshhold if you need more accurate result. 18 | 19 | ## Optional step. 20 | "Clear service" button clear your recognition service from previous created subjects by provided apikey. Please, to avoid exception use this button if you try to create subjects repeatedly from the same folder with photos. 21 | -------------------------------------------------------------------------------- /RecognitionExampleApp/RecognitionExampleApp.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | WinExe 4 | net6.0 5 | enable 6 | true 7 | app.manifest 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /RecognitionExampleApp/ResultsWindow.axaml: -------------------------------------------------------------------------------- 1 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /RecognitionExampleApp/ResultsWindow.axaml.cs: -------------------------------------------------------------------------------- 1 | using Avalonia; 2 | using Avalonia.Controls; 3 | using Avalonia.Media.Imaging; 4 | using Exadel.Compreface.DTOs.RecognitionDTOs.RecognizeFaceFromImage; 5 | using System; 6 | using System.IO; 7 | using System.Linq; 8 | 9 | namespace RecognitionExampleApp 10 | { 11 | public partial class ResultsWindow : Window 12 | { 13 | //http://localhost 14 | public ResultsWindow() 15 | { 16 | InitializeComponent(); 17 | } 18 | 19 | public ResultsWindow(RecognizeFaceFromImageResponse recognizeResponse, decimal similarityValue, string[] imagePathList) : this() 20 | { 21 | ShowImages(recognizeResponse, similarityValue, imagePathList); 22 | } 23 | 24 | private void ShowImages(RecognizeFaceFromImageResponse recognizeResponse, decimal similarityValue, string[] imagePathList) 25 | { 26 | foreach (var result in recognizeResponse.Result) 27 | { 28 | var subjects = result.Subjects.Where(x => x.Similarity >= similarityValue).ToList(); 29 | var subjectCounter = subjects.Count(); 30 | var counter = 0; 31 | decimal rowCount = Math.Ceiling((decimal)subjects.Count / 5m); 32 | for (int i = 0; i < rowCount; i++) 33 | { 34 | ImagesGrid.RowDefinitions.Add(new RowDefinition() { Height = new GridLength(300)}); 35 | 36 | for (int k = 0; k < 5; k++) 37 | { 38 | if (subjectCounter == 0) 39 | break; 40 | 41 | Image image = new Image() 42 | { 43 | VerticalAlignment = Avalonia.Layout.VerticalAlignment.Center, 44 | HorizontalAlignment = Avalonia.Layout.HorizontalAlignment.Center, 45 | [Grid.ColumnProperty] = k, 46 | [Grid.RowProperty] = i, 47 | Height = 250, 48 | Width = 250, 49 | Margin = Thickness.Parse("0, 10, 0, 10") 50 | }; 51 | 52 | Label label = new Label() 53 | { 54 | VerticalAlignment = Avalonia.Layout.VerticalAlignment.Bottom, 55 | HorizontalAlignment = Avalonia.Layout.HorizontalAlignment.Center, 56 | [Grid.ColumnProperty] = k, 57 | [Grid.RowProperty] = i, 58 | Margin = Thickness.Parse("0, 5, 5, 5") 59 | }; 60 | 61 | label.Content = subjects[counter].Subject; 62 | 63 | var filePath = imagePathList.FirstOrDefault(x => Path.GetFileName(x) == subjects[counter].Subject); 64 | 65 | AddImageToGrid(image, filePath); 66 | 67 | ImagesGrid.Children.Add(image); 68 | ImagesGrid.Children.Add(label); 69 | 70 | counter++; 71 | subjectCounter--; 72 | } 73 | } 74 | } 75 | } 76 | 77 | private Image AddImageToGrid(Image image, string filePath) 78 | { 79 | FileStream file = null; 80 | if (File.Exists(filePath)) 81 | { 82 | file = File.OpenRead(filePath); 83 | } 84 | 85 | using (var imageStream = file) 86 | { 87 | image.Source = Bitmap.DecodeToHeight(imageStream, 500); 88 | } 89 | 90 | return image; 91 | } 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /RecognitionExampleApp/Roots.xml: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /RecognitionExampleApp/app.manifest: -------------------------------------------------------------------------------- 1 |  2 | 3 | 6 | 7 | 8 | 9 | 10 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /signatures/cla.json: -------------------------------------------------------------------------------- 1 | { 2 | "signedContributors": [ 3 | { 4 | "name": "pospielov", 5 | "id": 3736126, 6 | "comment_id": 1378786038, 7 | "created_at": "2023-01-11T13:53:28Z", 8 | "repoId": 577248319, 9 | "pullRequestNo": 15 10 | } 11 | ] 12 | } --------------------------------------------------------------------------------