From 2d66db874addcc2ebba4bb633d81c27eded6d95d Mon Sep 17 00:00:00 2001
From: Lenovo
Date: Fri, 13 Aug 2021 21:04:07 +0400
Subject: [PATCH 001/702] remaking logo and hide new feature option
---
.../model-table/model-table.component.html | 10 +-
.../app/store/manage-collectiom/selectors.ts | 2 +-
ui/src/assets/img/face-recognition-logo.svg | 109 ++++++++++++++++--
3 files changed, 103 insertions(+), 18 deletions(-)
diff --git a/ui/src/app/features/model-table/model-table.component.html b/ui/src/app/features/model-table/model-table.component.html
index e2a8984d73..b2cfe3b89c 100644
--- a/ui/src/app/features/model-table/model-table.component.html
+++ b/ui/src/app/features/model-table/model-table.component.html
@@ -64,11 +64,11 @@
-
-
-
+
+
+
+
+
diff --git a/ui/src/app/store/manage-collectiom/selectors.ts b/ui/src/app/store/manage-collectiom/selectors.ts
index 500482ff7a..aa82561bc8 100644
--- a/ui/src/app/store/manage-collectiom/selectors.ts
+++ b/ui/src/app/store/manage-collectiom/selectors.ts
@@ -6,7 +6,7 @@
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
- *
+ cdk-overlay-connected-position-bounding-box
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
diff --git a/ui/src/assets/img/face-recognition-logo.svg b/ui/src/assets/img/face-recognition-logo.svg
index dcdf151e42..fb5d767087 100644
--- a/ui/src/assets/img/face-recognition-logo.svg
+++ b/ui/src/assets/img/face-recognition-logo.svg
@@ -1,13 +1,98 @@
-
@@ -56,6 +57,7 @@ recognition, face verification, face detection, landmark detection, age, and gen
* [Custom Builds](/docs/Custom-builds.md)
* [Face data migration](/docs/Face-data-migration.md)
* [User Roles System](/docs/User-Roles-System.md)
+ * [Face Mask Detection Plugin](/docs/Mask-detection-plugin.md)
* [Gathering Anonymous Statistics](/docs/Gathering-anonymous-statistics.md)
* [Contributing](#contributing)
* [License info](#license-info)
@@ -63,16 +65,27 @@ recognition, face verification, face detection, landmark detection, age, and gen
# Overview
-CompreFace is a free and open-source face detection and recognition GitHub project. Essentially, it is a docker-based application that can be used as a standalone server or deployed in the cloud. You don’t need prior machine learning skills to set up and use CompreFace.
+CompreFace is a free and open-source face detection and recognition GitHub project.
+Essentially, it is a docker-based application that can be used as a standalone server or deployed in the cloud.
+You don’t need prior machine learning skills to set up and use CompreFace.
-CompreFace provides REST API for face recognition, face verification, face detection, landmark detection, age, and gender recognition. The solution also features a role management system that allows you to easily control who has access to your Face Recognition Services.
+CompreFace provides REST API for face recognition, face verification, face detection, face mask detection, landmark detection, age, and gender recognition.
+The solution also features a role management system that allows you to easily control who has access to your Face Recognition Services.
CompreFace is delivered as a docker-compose config and supports different models that work on CPU and GPU. Our solution is based on state-of-the-art methods and libraries like FaceNet and InsightFace.
# Screenshots
-
-
+
+
+
+
+
+
# News and updates
@@ -83,7 +96,8 @@ CompreFace is delivered as a docker-compose config and supports different models
The system can accurately identify people even when it has only “seen” their photo once. Technology-wise, CompreFace has several advantages over similar free face recognition solutions. CompreFace:
-- Supports many face recognition services: face identification, face verification, face detection, landmark detection, and age and
+- Supports many face recognition services: face identification, face verification, face detection, face mask detection, landmark detection,
+ and age and
gender recognition
- Supports both CPU and GPU and is easy to scale up
- Is open source and self-hosted, which gives you additional guarantees for data security
diff --git a/custom-builds/README.md b/custom-builds/README.md
index 8fe2c9cdd1..30b53689ac 100644
--- a/custom-builds/README.md
+++ b/custom-builds/README.md
@@ -1,9 +1,9 @@
# List of custom-builds
-| Custom-build | Base library | CPU | GPU | Face detection model / accuracy on [WIDER Face (Hard)](https://paperswithcode.com/sota/face-detection-on-wider-face-hard) | Face recognition model / accuracy on [LFW](https://paperswithcode.com/sota/face-verification-on-labeled-faces-in-the) | Age and gender detection | Comment |
-| -------------------------- | --------------------------------------------------------- | -------------------------- | ------------------- | ------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------- | ---------------------------------------------- |
-| FaceNet (default) | [FaceNet](https://github.com/davidsandberg/facenet) | x86 (AVX instructions) | not supported | MTCNN / 80.9% | FaceNet (20180402-114759) / 99.63% | Custom, the model is taken [here](https://github.com/GilLevi/AgeGenderDeepLearning) | For general purposes. Support CPU without AVX2 |
-| Mobilenet | [InsightFace](https://github.com/deepinsight/insightface) | x86 (AVX2 instructions) | not supported | RetinaFace-MobileNet0.25 / 82.5% | MobileFaceNet,ArcFace / 99.50% | InsightFace | The fastest model among CPU only models |
-| Mobilenet-gpu | [InsightFace](https://github.com/deepinsight/insightface) | x86 (AVX2 instructions) | GPU (CUDA required) | RetinaFace-MobileNet0.25 / 82.5% | MobileFaceNet,ArcFace / 99.50% | InsightFace | The fastest model |
-| SubCenter-ArcFace-r100 | [InsightFace](https://github.com/deepinsight/insightface) | x86 (AVX2 instructions) | not supported | retinaface_r50_v1 / 91.4% | arcface-r100-msfdrop75 / 99.80% | InsightFace | The most accurate model, but the most slow |
-| SubCenter-ArcFace-r100-gpu | [InsightFace](https://github.com/deepinsight/insightface) | x86 (AVX2 instructions) | GPU (CUDA required) | retinaface_r50_v1 / 91.4% | arcface-r100-msfdrop75 / 99.80% | InsightFace | The most accurate model |
+| Custom-build | Base library | CPU | GPU | Face detection model / accuracy on [WIDER Face (Hard)](https://paperswithcode.com/sota/face-detection-on-wider-face-hard) | Face recognition model / accuracy on [LFW](https://paperswithcode.com/sota/face-verification-on-labeled-faces-in-the) | Age and gender detection | Face mask detection | Comment |
+| -------------------------- | --------------------------------------------------------- | -------------------------- | ------------------- | ------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------- | ------------------------------------------------ | ---------------------------------------------- |
+| FaceNet (default) | [FaceNet](https://github.com/davidsandberg/facenet) | x86 (AVX instructions) | not supported | MTCNN / 80.9% | FaceNet (20180402-114759) / 99.63% | Custom, the model is taken [here](https://github.com/GilLevi/AgeGenderDeepLearning) | [Custom model](../docs/Mask-detection-plugin.md) | For general purposes. Support CPU without AVX2 |
+| Mobilenet | [InsightFace](https://github.com/deepinsight/insightface) | x86 (AVX2 instructions) | not supported | RetinaFace-MobileNet0.25 / 82.5% | MobileFaceNet,ArcFace / 99.50% | InsightFace | [Custom model](../docs/Mask-detection-plugin.md) | The fastest model among CPU only models |
+| Mobilenet-gpu | [InsightFace](https://github.com/deepinsight/insightface) | x86 (AVX2 instructions) | GPU (CUDA required) | RetinaFace-MobileNet0.25 / 82.5% | MobileFaceNet,ArcFace / 99.50% | InsightFace | [Custom model](../docs/Mask-detection-plugin.md) | The fastest model |
+| SubCenter-ArcFace-r100 | [InsightFace](https://github.com/deepinsight/insightface) | x86 (AVX2 instructions) | not supported | retinaface_r50_v1 / 91.4% | arcface-r100-msfdrop75 / 99.80% | InsightFace | [Custom model](../docs/Mask-detection-plugin.md) | The most accurate model, but the most slow |
+| SubCenter-ArcFace-r100-gpu | [InsightFace](https://github.com/deepinsight/insightface) | x86 (AVX2 instructions) | GPU (CUDA required) | retinaface_r50_v1 / 91.4% | arcface-r100-msfdrop75 / 99.80% | InsightFace | [Custom model](../docs/Mask-detection-plugin.md) | The most accurate model |
diff --git a/docs/Face-services-and-plugins.md b/docs/Face-services-and-plugins.md
index edf84b9d47..587a87e67c 100644
--- a/docs/Face-services-and-plugins.md
+++ b/docs/Face-services-and-plugins.md
@@ -32,7 +32,7 @@ applicable. To add more information in response you can add face plugins in your
comma-separated needed plugins in the query `face_plugins` parameter. This parameter is supported by all face recognition services.
Example:
```shell
-curl -X POST "http://localhost:8000/api/v1/recognition/recognize?face_plugins=age,gender,landmarks" \
+curl -X POST "http://localhost:8000/api/v1/recognition/recognize?face_plugins=age,gender,landmarks,mask" \
-H "Content-Type: multipart/form-data" \
-H "x-api-key: " \
-F file=
@@ -44,5 +44,6 @@ The list of possible plugins:
* gender - returns the supposed person’s gender
* landmarks - returns face landmarks. This plugin is supported by all configurations and returns 5 points of eyes, nose, and mouth
* calculator - returns face embeddings.
+* mask - returns if the person wears a mask. Possible results: `without_mask`, `mask_worn_incorrectly`, `mask_worn_correctly`. Learn more about [mask plugin](Mask-detection-plugin.md)
* landmarks2d106 - returns face landmarks. This plugin is supported only by the configuration that uses insightface library. It’s not
available by default. More information about landmarks [here](https://github.com/deepinsight/insightface/tree/master/alignment/coordinateReg#visualization).
diff --git a/docs/Mask-detection-plugin.md b/docs/Mask-detection-plugin.md
new file mode 100644
index 0000000000..07ce1a0ba2
--- /dev/null
+++ b/docs/Mask-detection-plugin.md
@@ -0,0 +1,36 @@
+# Face mask detection plugin
+
+Mask detection plugin can be used to automatically detect if the person wears correctly mask.
+There are tree possible results: `without_mask`, `mask_worn_incorrectly`, `mask_worn_correctly`.
+
+There was no good free and ready to use model for face mask detection on the moment of adding this plugin, so we created our own model.
+```
+Disclaimer:
+The plugin was created by software developers, not medical experts.
+The accuracy of the model is not 100%.
+Please use the plugin on your own risk.
+```
+
+# Face mask detection example
+
+![results](https://user-images.githubusercontent.com/3736126/130656086-3167421e-f697-4837-8cf9-e3889d49a44d.png)
+
+# Training process
+
+## Dataset
+
+We used four publicly available datasets for training the model:
+
+1. [Kaggle face mask detection dataset](https://www.kaggle.com/andrewmvd/face-mask-detection)
+2. [Kaggle medical masks dataset images tfrecords](https://www.kaggle.com/ivandanilovich/medical-masks-dataset-images-tfrecords)
+3. [Kaggle face mask detection dataset #2](https://www.kaggle.com/wobotintelligence/face-mask-detection-dataset?select=train.csv)
+4. [MAFA dataset](https://drive.google.com/drive/folders/1nbtM1n0--iZ3VVbNGhocxbnBGhMau_OG)
+
+We extracted faces with masks from first dataset (around 4k images), faces without mask from first two datasets (around 4k images),
+faces with masks worn incorrect from all four datasets (around 2k images).
+Then we duplicated each incorrect worn mask image with data augmentation (see augmentation.py) in order to achieve class balance.
+
+## Train
+
+InceptionV3 was cut off on mixed 7 layer to improve speed and was used as a backbone.
+Final model with 97.2 % accuracy is used by default and can be found [here](https://drive.google.com/file/d/1jm2Wd2JEZxhS8O1JjV-kfBOyOYUMxKHq/view?usp=sharing)
diff --git a/docs/README.md b/docs/README.md
index 664c63355e..c6979475ca 100644
--- a/docs/README.md
+++ b/docs/README.md
@@ -9,5 +9,7 @@
* [Configuration](Configuration.md)
* [Architecture and Scalability](Architecture-and-scalability.md)
* [Custom Builds](Custom-builds.md)
+* [Face data migration](Face-data-migration.md)
* [User Roles System](User-Roles-System.md)
+* [Face Mask Detection Plugin](Mask-detection-plugin.md)
* [Gathering Anonymous Statistics](Gathering-anonymous-statistics.md)
\ No newline at end of file
diff --git a/docs/Rest-API-description.md b/docs/Rest-API-description.md
index a501dfddfc..f77933a1f9 100644
--- a/docs/Rest-API-description.md
+++ b/docs/Rest-API-description.md
@@ -222,8 +222,19 @@ Response body on success:
```json
{
"result" : [ {
- "age" : [ 25, 32 ],
- "gender" : "female",
+ "age" : {
+ "probability": 0.9308982491493225,
+ "high": 32,
+ "low": 25
+ },
+ "gender" : {
+ "probability": 0.9898611307144165,
+ "value": "female"
+ },
+ "mask" : {
+ "probability": 0.9999470710754395,
+ "value": "without_mask"
+ },
"embedding" : [ 9.424854069948196E-4, "...", -0.011415496468544006 ],
"box" : {
"probability" : 1.0,
@@ -255,8 +266,9 @@ Response body on success:
| Element | Type | Description |
| ------------------------------ | ------- | ------------------------------------------------------------ |
-| age | array | detected age range. Return only if [age plugin](Face-services-and-plugins.md#face-plugins) is enabled |
-| gender | string | detected gender. Return only if [gender plugin](Face-services-and-plugins.md#face-plugins) is enabled |
+| age | object | detected age range. Return only if [age plugin](Face-services-and-plugins.md#face-plugins) is enabled |
+| gender | object | detected gender. Return only if [gender plugin](Face-services-and-plugins.md#face-plugins) is enabled |
+| mask | object | detected mask. Return only if [face mask plugin](Face-services-and-plugins.md#face-plugins) is enabled. |
| embedding | array | face embeddings. Return only if [calculator plugin](Face-services-and-plugins.md#face-plugins) is enabled |
| box | object | list of parameters of the bounding box for this face |
| probability | float | probability that a found face is actually a face |
@@ -429,8 +441,19 @@ Response body on success:
{
"result": [
{
- "age" : [ 25, 32 ],
- "gender" : "female",
+ "age" : {
+ "probability": 0.9308982491493225,
+ "high": 32,
+ "low": 25
+ },
+ "gender" : {
+ "probability": 0.9898611307144165,
+ "value": "female"
+ },
+ "mask" : {
+ "probability": 0.9999470710754395,
+ "value": "without_mask"
+ },
"embedding" : [ -0.049007344990968704, "...", -0.01753818802535534 ],
"box" : {
"probability" : 0.9997453093528748,
@@ -460,8 +483,9 @@ Response body on success:
| Element | Type | Description |
| ------------------------------ | ------- | ------------------------------------------------------------ |
-| age | array | detected age range. Return only if [age plugin](Face-services-and-plugins.md#face-plugins) is enabled |
-| gender | string | detected gender. Return only if [gender plugin](Face-services-and-plugins.md#face-plugins) is enabled |
+| age | object | detected age range. Return only if [age plugin](Face-services-and-plugins.md#face-plugins) is enabled |
+| gender | object | detected gender. Return only if [gender plugin](Face-services-and-plugins.md#face-plugins) is enabled |
+| mask | object | detected mask. Return only if [face mask plugin](Face-services-and-plugins.md#face-plugins) is enabled |
| embedding | array | face embeddings. Return only if [calculator plugin](Face-services-and-plugins.md#face-plugins) is enabled |
| box | object | list of parameters of the bounding box for this face |
| probability | float | probability that a found face is actually a face |
@@ -497,8 +521,19 @@ Response body on success:
```json
{
"result" : [ {
- "age" : [ 25, 32 ],
- "gender" : "female",
+ "age" : {
+ "probability": 0.9308982491493225,
+ "high": 32,
+ "low": 25
+ },
+ "gender" : {
+ "probability": 0.9898611307144165,
+ "value": "female"
+ },
+ "mask" : {
+ "probability": 0.9999470710754395,
+ "value": "without_mask"
+ },
"embedding" : [ -0.03027934394776821, "...", -0.05117142200469971 ],
"box" : {
"probability" : 0.9987509250640869,
@@ -526,8 +561,9 @@ Response body on success:
| Element | Type | Description |
| ------------------------------ | ------- | ------------------------------------------------------------ |
-| age | array | detected age range. Return only if [age plugin](Face-services-and-plugins.md#face-plugins) is enabled |
-| gender | string | detected gender. Return only if [gender plugin](Face-services-and-plugins.md#face-plugins) is enabled |
+| age | object | detected age range. Return only if [age plugin](Face-services-and-plugins.md#face-plugins) is enabled |
+| gender | object | detected gender. Return only if [gender plugin](Face-services-and-plugins.md#face-plugins) is enabled |
+| mask | object | detected mask. Return only if [face mask plugin](Face-services-and-plugins.md#face-plugins) is enabled |
| embedding | array | face embeddings. Return only if [calculator plugin](Face-services-and-plugins.md#face-plugins) is enabled |
| box | object | list of parameters of the bounding box for this face (on processedImage) |
| probability | float | probability that a found face is actually a face (on processedImage) |
@@ -565,8 +601,19 @@ Response body on success:
{
"result" : [{
"source_image_face" : {
- "age" : [ 25, 32 ],
- "gender" : "female",
+ "age" : {
+ "probability": 0.9308982491493225,
+ "high": 32,
+ "low": 25
+ },
+ "gender" : {
+ "probability": 0.9898611307144165,
+ "value": "female"
+ },
+ "mask" : {
+ "probability": 0.9999470710754395,
+ "value": "without_mask"
+ },
"embedding" : [ -0.0010271212086081505, "...", -0.008746841922402382 ],
"box" : {
"probability" : 0.9997453093528748,
@@ -585,8 +632,19 @@ Response body on success:
},
"face_matches": [
{
- "age" : [ 25, 32 ],
- "gender" : "female",
+ "age" : {
+ "probability": 0.9308982491493225,
+ "high": 32,
+ "low": 25
+ },
+ "gender" : {
+ "probability": 0.9898611307144165,
+ "value": "female"
+ },
+ "mask" : {
+ "probability": 0.9999470710754395,
+ "value": "without_mask"
+ },
"embedding" : [ -0.049007344990968704, "...", -0.01753818802535534 ],
"box" : {
"probability" : 0.99975,
@@ -618,8 +676,9 @@ Response body on success:
| ------------------------------ | ------- | ------------------------------------------------------------ |
| source_image_face | object | additional info about source image face |
| face_matches | array | result of face verification |
-| age | array | detected age range. Return only if [age plugin](Face-services-and-plugins.md#face-plugins) is enabled |
-| gender | string | detected gender. Return only if [gender plugin](Face-services-and-plugins.md#face-plugins) is enabled |
+| age | object | detected age range. Return only if [age plugin](Face-services-and-plugins.md#face-plugins) is enabled |
+| gender | object | detected gender. Return only if [gender plugin](Face-services-and-plugins.md#face-plugins) is enabled |
+| mask | object | detected mask. Return only if [face mask plugin](Face-services-and-plugins.md#face-plugins) is enabled |
| embedding | array | face embeddings. Return only if [calculator plugin](Face-services-and-plugins.md#face-plugins) is enabled |
| box | object | list of parameters of the bounding box for this face |
| probability | float | probability that a found face is actually a face |
From 6c6d6ad7ee25b68122791421bff2df1cd18c624b Mon Sep 17 00:00:00 2001
From: "EXADEL\\ssaldatsenka"
Date: Thu, 26 Aug 2021 10:53:46 +0300
Subject: [PATCH 008/702] EFRS-1104: Execution time and plugins_versions is
absent for "mask" plugin fix CRLF to LF
---
.../trainservice/dto/PluginsVersionsDto.java | 76 +++++++++----------
.../commonservice/dto/ExecutionTimeDto.java | 38 +++++-----
.../commonservice/dto/PluginsVersionsDto.java | 76 +++++++++----------
3 files changed, 95 insertions(+), 95 deletions(-)
diff --git a/java/api/src/main/java/com/exadel/frs/core/trainservice/dto/PluginsVersionsDto.java b/java/api/src/main/java/com/exadel/frs/core/trainservice/dto/PluginsVersionsDto.java
index af807a0a6a..d698d3d6be 100644
--- a/java/api/src/main/java/com/exadel/frs/core/trainservice/dto/PluginsVersionsDto.java
+++ b/java/api/src/main/java/com/exadel/frs/core/trainservice/dto/PluginsVersionsDto.java
@@ -1,38 +1,38 @@
-/*
- * Copyright (c) 2020 the original author or authors
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * https://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
- * or implied. See the License for the specific language governing
- * permissions and limitations under the License.
- */
-package com.exadel.frs.core.trainservice.dto;
-
-import com.fasterxml.jackson.annotation.JsonInclude;
-import lombok.AllArgsConstructor;
-import lombok.Builder;
-import lombok.Data;
-import lombok.NoArgsConstructor;
-
-import static com.fasterxml.jackson.annotation.JsonInclude.Include.NON_NULL;
-
-@Data
-@Builder
-@NoArgsConstructor
-@AllArgsConstructor
-@JsonInclude(NON_NULL)
-public class PluginsVersionsDto {
-
- private String age;
- private String gender;
- private String detector;
- private String calculator;
- private String mask;
-}
+/*
+ * Copyright (c) 2020 the original author or authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ * or implied. See the License for the specific language governing
+ * permissions and limitations under the License.
+ */
+package com.exadel.frs.core.trainservice.dto;
+
+import com.fasterxml.jackson.annotation.JsonInclude;
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import static com.fasterxml.jackson.annotation.JsonInclude.Include.NON_NULL;
+
+@Data
+@Builder
+@NoArgsConstructor
+@AllArgsConstructor
+@JsonInclude(NON_NULL)
+public class PluginsVersionsDto {
+
+ private String age;
+ private String gender;
+ private String detector;
+ private String calculator;
+ private String mask;
+}
diff --git a/java/common/src/main/java/com/exadel/frs/commonservice/dto/ExecutionTimeDto.java b/java/common/src/main/java/com/exadel/frs/commonservice/dto/ExecutionTimeDto.java
index 94d9ed3a9d..927086a1fc 100644
--- a/java/common/src/main/java/com/exadel/frs/commonservice/dto/ExecutionTimeDto.java
+++ b/java/common/src/main/java/com/exadel/frs/commonservice/dto/ExecutionTimeDto.java
@@ -1,20 +1,20 @@
-package com.exadel.frs.commonservice.dto;
-
-
-import com.fasterxml.jackson.annotation.JsonInclude;
-import lombok.Data;
-import lombok.NoArgsConstructor;
-
-import static com.fasterxml.jackson.annotation.JsonInclude.Include.NON_NULL;
-
-@Data
-@NoArgsConstructor
-@JsonInclude(NON_NULL)
-public class ExecutionTimeDto {
-
- private Double age;
- private Double gender;
- private Double detector;
- private Double calculator;
- private Double mask;
+package com.exadel.frs.commonservice.dto;
+
+
+import com.fasterxml.jackson.annotation.JsonInclude;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import static com.fasterxml.jackson.annotation.JsonInclude.Include.NON_NULL;
+
+@Data
+@NoArgsConstructor
+@JsonInclude(NON_NULL)
+public class ExecutionTimeDto {
+
+ private Double age;
+ private Double gender;
+ private Double detector;
+ private Double calculator;
+ private Double mask;
}
\ No newline at end of file
diff --git a/java/common/src/main/java/com/exadel/frs/commonservice/dto/PluginsVersionsDto.java b/java/common/src/main/java/com/exadel/frs/commonservice/dto/PluginsVersionsDto.java
index cc4edab8b6..037d7eee97 100644
--- a/java/common/src/main/java/com/exadel/frs/commonservice/dto/PluginsVersionsDto.java
+++ b/java/common/src/main/java/com/exadel/frs/commonservice/dto/PluginsVersionsDto.java
@@ -1,38 +1,38 @@
-/*
- * Copyright (c) 2020 the original author or authors
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * https://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
- * or implied. See the License for the specific language governing
- * permissions and limitations under the License.
- */
-package com.exadel.frs.commonservice.dto;
-
-import com.fasterxml.jackson.annotation.JsonInclude;
-import lombok.AllArgsConstructor;
-import lombok.Builder;
-import lombok.Data;
-import lombok.NoArgsConstructor;
-
-import static com.fasterxml.jackson.annotation.JsonInclude.Include.NON_NULL;
-
-@Data
-@Builder
-@NoArgsConstructor
-@AllArgsConstructor
-@JsonInclude(NON_NULL)
-public class PluginsVersionsDto {
-
- private String age;
- private String gender;
- private String detector;
- private String calculator;
- private String mask;
-}
+/*
+ * Copyright (c) 2020 the original author or authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ * or implied. See the License for the specific language governing
+ * permissions and limitations under the License.
+ */
+package com.exadel.frs.commonservice.dto;
+
+import com.fasterxml.jackson.annotation.JsonInclude;
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import static com.fasterxml.jackson.annotation.JsonInclude.Include.NON_NULL;
+
+@Data
+@Builder
+@NoArgsConstructor
+@AllArgsConstructor
+@JsonInclude(NON_NULL)
+public class PluginsVersionsDto {
+
+ private String age;
+ private String gender;
+ private String detector;
+ private String calculator;
+ private String mask;
+}
From 32c7448bfcac27d0efac9690e527faa0c64762c1 Mon Sep 17 00:00:00 2001
From: spospielov
Date: Sun, 29 Aug 2021 14:05:20 +0300
Subject: [PATCH 009/702] fixed verify endpoint
---
.../exadel/frs/core/trainservice/cache/EmbeddingCollection.java | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/java/api/src/main/java/com/exadel/frs/core/trainservice/cache/EmbeddingCollection.java b/java/api/src/main/java/com/exadel/frs/core/trainservice/cache/EmbeddingCollection.java
index 0c66433769..b96dcc9c73 100644
--- a/java/api/src/main/java/com/exadel/frs/core/trainservice/cache/EmbeddingCollection.java
+++ b/java/api/src/main/java/com/exadel/frs/core/trainservice/cache/EmbeddingCollection.java
@@ -138,7 +138,7 @@ public synchronized Optional getRawEmbeddingById(UUID embeddingId) {
return findByEmbeddingId(
embeddingId,
// return duplicated row
- entry -> embeddings.getRow(entry.getValue()).dup()
+ entry -> embeddings.getRow(entry.getValue(), true).dup()
);
}
From b57df945214126d7f505c52c37bd742e3e58721f Mon Sep 17 00:00:00 2001
From: shasans
Date: Fri, 20 Aug 2021 18:58:27 +0500
Subject: [PATCH 010/702] EFRS-1099 Verification result face matches part
improved
(cherry picked from commit 572eb4da704e05caedc393915d2f051b95163ca2)
---
.../exadel/frs/core/trainservice/dto/VerifyFacesResultDto.java | 1 +
.../trainservice/service/FaceVerificationProcessServiceImpl.java | 1 +
2 files changed, 2 insertions(+)
diff --git a/java/api/src/main/java/com/exadel/frs/core/trainservice/dto/VerifyFacesResultDto.java b/java/api/src/main/java/com/exadel/frs/core/trainservice/dto/VerifyFacesResultDto.java
index 97fc5c21be..c40aff3d86 100644
--- a/java/api/src/main/java/com/exadel/frs/core/trainservice/dto/VerifyFacesResultDto.java
+++ b/java/api/src/main/java/com/exadel/frs/core/trainservice/dto/VerifyFacesResultDto.java
@@ -16,6 +16,7 @@
package com.exadel.frs.core.trainservice.dto;
import com.exadel.frs.commonservice.dto.ExecutionTimeDto;
+import com.exadel.frs.commonservice.sdk.faces.feign.dto.FacesMask;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.AllArgsConstructor;
diff --git a/java/api/src/main/java/com/exadel/frs/core/trainservice/service/FaceVerificationProcessServiceImpl.java b/java/api/src/main/java/com/exadel/frs/core/trainservice/service/FaceVerificationProcessServiceImpl.java
index b1df251bd1..233914b371 100644
--- a/java/api/src/main/java/com/exadel/frs/core/trainservice/service/FaceVerificationProcessServiceImpl.java
+++ b/java/api/src/main/java/com/exadel/frs/core/trainservice/service/FaceVerificationProcessServiceImpl.java
@@ -199,6 +199,7 @@ private FaceMatch getFaceMatch(FindFacesResult targetFacesResult, Double similar
faceMatch.setGender(verifyFacesResultDto.getGender());
faceMatch.setLandmarks(verifyFacesResultDto.getLandmarks());
faceMatch.setSimilarity(BigDecimal.valueOf(similarity).setScale(5, HALF_UP).floatValue());
+ faceMatch.setMask(targetFacesResult.getMask());
return faceMatch;
}
From c48166cf7f0478fb1d8120108971d643663ba30b Mon Sep 17 00:00:00 2001
From: shasans
Date: Fri, 20 Aug 2021 17:13:03 +0500
Subject: [PATCH 011/702] EFRS-1101 information about mask in face detection
service result mapping corrected
(cherry picked from commit 68d3fee122ec3d0f50a99f74cc31c5204d1197a3)
---
.../com/exadel/frs/commonservice/dto/FindFacesResultDto.java | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/java/common/src/main/java/com/exadel/frs/commonservice/dto/FindFacesResultDto.java b/java/common/src/main/java/com/exadel/frs/commonservice/dto/FindFacesResultDto.java
index da8d3a6bf5..3e3c4607c3 100644
--- a/java/common/src/main/java/com/exadel/frs/commonservice/dto/FindFacesResultDto.java
+++ b/java/common/src/main/java/com/exadel/frs/commonservice/dto/FindFacesResultDto.java
@@ -40,5 +40,5 @@ public class FindFacesResultDto {
@JsonProperty(value = "execution_time")
private ExecutionTimeDto executionTime;
private List> landmarks;
- private FacesMaskDto maskDto;
+ private FacesMaskDto mask;
}
From 3d127371ef5b975c10dbaffbc10aefdccc464120 Mon Sep 17 00:00:00 2001
From: "EXADEL\\ssaldatsenka"
Date: Tue, 24 Aug 2021 11:16:17 +0300
Subject: [PATCH 012/702] EFRS-1104: Execution time and plugins_versions is
absent for "mask" plugin execution time and plugin version for "mask" plugin
has been added
(cherry picked from commit d5acd8931ac369a192d38d8196eed54c4519080a)
---
.../trainservice/dto/PluginsVersionsDto.java | 75 ++++++++++---------
.../commonservice/dto/ExecutionTimeDto.java | 37 ++++-----
.../commonservice/dto/PluginsVersionsDto.java | 75 ++++++++++---------
.../sdk/faces/feign/dto/PluginsVersions.java | 1 +
4 files changed, 96 insertions(+), 92 deletions(-)
diff --git a/java/api/src/main/java/com/exadel/frs/core/trainservice/dto/PluginsVersionsDto.java b/java/api/src/main/java/com/exadel/frs/core/trainservice/dto/PluginsVersionsDto.java
index 6f34837153..af807a0a6a 100644
--- a/java/api/src/main/java/com/exadel/frs/core/trainservice/dto/PluginsVersionsDto.java
+++ b/java/api/src/main/java/com/exadel/frs/core/trainservice/dto/PluginsVersionsDto.java
@@ -1,37 +1,38 @@
-/*
- * Copyright (c) 2020 the original author or authors
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * https://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
- * or implied. See the License for the specific language governing
- * permissions and limitations under the License.
- */
-package com.exadel.frs.core.trainservice.dto;
-
-import com.fasterxml.jackson.annotation.JsonInclude;
-import lombok.AllArgsConstructor;
-import lombok.Builder;
-import lombok.Data;
-import lombok.NoArgsConstructor;
-
-import static com.fasterxml.jackson.annotation.JsonInclude.Include.NON_NULL;
-
-@Data
-@Builder
-@NoArgsConstructor
-@AllArgsConstructor
-@JsonInclude(NON_NULL)
-public class PluginsVersionsDto {
-
- private String age;
- private String gender;
- private String detector;
- private String calculator;
-}
+/*
+ * Copyright (c) 2020 the original author or authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ * or implied. See the License for the specific language governing
+ * permissions and limitations under the License.
+ */
+package com.exadel.frs.core.trainservice.dto;
+
+import com.fasterxml.jackson.annotation.JsonInclude;
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import static com.fasterxml.jackson.annotation.JsonInclude.Include.NON_NULL;
+
+@Data
+@Builder
+@NoArgsConstructor
+@AllArgsConstructor
+@JsonInclude(NON_NULL)
+public class PluginsVersionsDto {
+
+ private String age;
+ private String gender;
+ private String detector;
+ private String calculator;
+ private String mask;
+}
diff --git a/java/common/src/main/java/com/exadel/frs/commonservice/dto/ExecutionTimeDto.java b/java/common/src/main/java/com/exadel/frs/commonservice/dto/ExecutionTimeDto.java
index 99daa448c3..94d9ed3a9d 100644
--- a/java/common/src/main/java/com/exadel/frs/commonservice/dto/ExecutionTimeDto.java
+++ b/java/common/src/main/java/com/exadel/frs/commonservice/dto/ExecutionTimeDto.java
@@ -1,19 +1,20 @@
-package com.exadel.frs.commonservice.dto;
-
-
-import com.fasterxml.jackson.annotation.JsonInclude;
-import lombok.Data;
-import lombok.NoArgsConstructor;
-
-import static com.fasterxml.jackson.annotation.JsonInclude.Include.NON_NULL;
-
-@Data
-@NoArgsConstructor
-@JsonInclude(NON_NULL)
-public class ExecutionTimeDto {
-
- private Double age;
- private Double gender;
- private Double detector;
- private Double calculator;
+package com.exadel.frs.commonservice.dto;
+
+
+import com.fasterxml.jackson.annotation.JsonInclude;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import static com.fasterxml.jackson.annotation.JsonInclude.Include.NON_NULL;
+
+@Data
+@NoArgsConstructor
+@JsonInclude(NON_NULL)
+public class ExecutionTimeDto {
+
+ private Double age;
+ private Double gender;
+ private Double detector;
+ private Double calculator;
+ private Double mask;
}
\ No newline at end of file
diff --git a/java/common/src/main/java/com/exadel/frs/commonservice/dto/PluginsVersionsDto.java b/java/common/src/main/java/com/exadel/frs/commonservice/dto/PluginsVersionsDto.java
index a329ab1605..cc4edab8b6 100644
--- a/java/common/src/main/java/com/exadel/frs/commonservice/dto/PluginsVersionsDto.java
+++ b/java/common/src/main/java/com/exadel/frs/commonservice/dto/PluginsVersionsDto.java
@@ -1,37 +1,38 @@
-/*
- * Copyright (c) 2020 the original author or authors
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * https://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
- * or implied. See the License for the specific language governing
- * permissions and limitations under the License.
- */
-package com.exadel.frs.commonservice.dto;
-
-import com.fasterxml.jackson.annotation.JsonInclude;
-import lombok.AllArgsConstructor;
-import lombok.Builder;
-import lombok.Data;
-import lombok.NoArgsConstructor;
-
-import static com.fasterxml.jackson.annotation.JsonInclude.Include.NON_NULL;
-
-@Data
-@Builder
-@NoArgsConstructor
-@AllArgsConstructor
-@JsonInclude(NON_NULL)
-public class PluginsVersionsDto {
-
- private String age;
- private String gender;
- private String detector;
- private String calculator;
-}
+/*
+ * Copyright (c) 2020 the original author or authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ * or implied. See the License for the specific language governing
+ * permissions and limitations under the License.
+ */
+package com.exadel.frs.commonservice.dto;
+
+import com.fasterxml.jackson.annotation.JsonInclude;
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import static com.fasterxml.jackson.annotation.JsonInclude.Include.NON_NULL;
+
+@Data
+@Builder
+@NoArgsConstructor
+@AllArgsConstructor
+@JsonInclude(NON_NULL)
+public class PluginsVersionsDto {
+
+ private String age;
+ private String gender;
+ private String detector;
+ private String calculator;
+ private String mask;
+}
diff --git a/java/common/src/main/java/com/exadel/frs/commonservice/sdk/faces/feign/dto/PluginsVersions.java b/java/common/src/main/java/com/exadel/frs/commonservice/sdk/faces/feign/dto/PluginsVersions.java
index 50792e42ca..1ec9975a5d 100644
--- a/java/common/src/main/java/com/exadel/frs/commonservice/sdk/faces/feign/dto/PluginsVersions.java
+++ b/java/common/src/main/java/com/exadel/frs/commonservice/sdk/faces/feign/dto/PluginsVersions.java
@@ -30,4 +30,5 @@ public class PluginsVersions {
private String gender;
private String detector;
private String calculator;
+ private String mask;
}
From bd499cda0a0f21031c5ae33d9fe9a02c7e86c47e Mon Sep 17 00:00:00 2001
From: "EXADEL\\ssaldatsenka"
Date: Thu, 26 Aug 2021 10:53:46 +0300
Subject: [PATCH 013/702] EFRS-1104: Execution time and plugins_versions is
absent for "mask" plugin fix CRLF to LF
(cherry picked from commit 6c6d6ad7ee25b68122791421bff2df1cd18c624b)
---
.../trainservice/dto/PluginsVersionsDto.java | 76 +++++++++----------
.../commonservice/dto/ExecutionTimeDto.java | 38 +++++-----
.../commonservice/dto/PluginsVersionsDto.java | 76 +++++++++----------
3 files changed, 95 insertions(+), 95 deletions(-)
diff --git a/java/api/src/main/java/com/exadel/frs/core/trainservice/dto/PluginsVersionsDto.java b/java/api/src/main/java/com/exadel/frs/core/trainservice/dto/PluginsVersionsDto.java
index af807a0a6a..d698d3d6be 100644
--- a/java/api/src/main/java/com/exadel/frs/core/trainservice/dto/PluginsVersionsDto.java
+++ b/java/api/src/main/java/com/exadel/frs/core/trainservice/dto/PluginsVersionsDto.java
@@ -1,38 +1,38 @@
-/*
- * Copyright (c) 2020 the original author or authors
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * https://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
- * or implied. See the License for the specific language governing
- * permissions and limitations under the License.
- */
-package com.exadel.frs.core.trainservice.dto;
-
-import com.fasterxml.jackson.annotation.JsonInclude;
-import lombok.AllArgsConstructor;
-import lombok.Builder;
-import lombok.Data;
-import lombok.NoArgsConstructor;
-
-import static com.fasterxml.jackson.annotation.JsonInclude.Include.NON_NULL;
-
-@Data
-@Builder
-@NoArgsConstructor
-@AllArgsConstructor
-@JsonInclude(NON_NULL)
-public class PluginsVersionsDto {
-
- private String age;
- private String gender;
- private String detector;
- private String calculator;
- private String mask;
-}
+/*
+ * Copyright (c) 2020 the original author or authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ * or implied. See the License for the specific language governing
+ * permissions and limitations under the License.
+ */
+package com.exadel.frs.core.trainservice.dto;
+
+import com.fasterxml.jackson.annotation.JsonInclude;
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import static com.fasterxml.jackson.annotation.JsonInclude.Include.NON_NULL;
+
+@Data
+@Builder
+@NoArgsConstructor
+@AllArgsConstructor
+@JsonInclude(NON_NULL)
+public class PluginsVersionsDto {
+
+ private String age;
+ private String gender;
+ private String detector;
+ private String calculator;
+ private String mask;
+}
diff --git a/java/common/src/main/java/com/exadel/frs/commonservice/dto/ExecutionTimeDto.java b/java/common/src/main/java/com/exadel/frs/commonservice/dto/ExecutionTimeDto.java
index 94d9ed3a9d..927086a1fc 100644
--- a/java/common/src/main/java/com/exadel/frs/commonservice/dto/ExecutionTimeDto.java
+++ b/java/common/src/main/java/com/exadel/frs/commonservice/dto/ExecutionTimeDto.java
@@ -1,20 +1,20 @@
-package com.exadel.frs.commonservice.dto;
-
-
-import com.fasterxml.jackson.annotation.JsonInclude;
-import lombok.Data;
-import lombok.NoArgsConstructor;
-
-import static com.fasterxml.jackson.annotation.JsonInclude.Include.NON_NULL;
-
-@Data
-@NoArgsConstructor
-@JsonInclude(NON_NULL)
-public class ExecutionTimeDto {
-
- private Double age;
- private Double gender;
- private Double detector;
- private Double calculator;
- private Double mask;
+package com.exadel.frs.commonservice.dto;
+
+
+import com.fasterxml.jackson.annotation.JsonInclude;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import static com.fasterxml.jackson.annotation.JsonInclude.Include.NON_NULL;
+
+@Data
+@NoArgsConstructor
+@JsonInclude(NON_NULL)
+public class ExecutionTimeDto {
+
+ private Double age;
+ private Double gender;
+ private Double detector;
+ private Double calculator;
+ private Double mask;
}
\ No newline at end of file
diff --git a/java/common/src/main/java/com/exadel/frs/commonservice/dto/PluginsVersionsDto.java b/java/common/src/main/java/com/exadel/frs/commonservice/dto/PluginsVersionsDto.java
index cc4edab8b6..037d7eee97 100644
--- a/java/common/src/main/java/com/exadel/frs/commonservice/dto/PluginsVersionsDto.java
+++ b/java/common/src/main/java/com/exadel/frs/commonservice/dto/PluginsVersionsDto.java
@@ -1,38 +1,38 @@
-/*
- * Copyright (c) 2020 the original author or authors
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * https://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
- * or implied. See the License for the specific language governing
- * permissions and limitations under the License.
- */
-package com.exadel.frs.commonservice.dto;
-
-import com.fasterxml.jackson.annotation.JsonInclude;
-import lombok.AllArgsConstructor;
-import lombok.Builder;
-import lombok.Data;
-import lombok.NoArgsConstructor;
-
-import static com.fasterxml.jackson.annotation.JsonInclude.Include.NON_NULL;
-
-@Data
-@Builder
-@NoArgsConstructor
-@AllArgsConstructor
-@JsonInclude(NON_NULL)
-public class PluginsVersionsDto {
-
- private String age;
- private String gender;
- private String detector;
- private String calculator;
- private String mask;
-}
+/*
+ * Copyright (c) 2020 the original author or authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ * or implied. See the License for the specific language governing
+ * permissions and limitations under the License.
+ */
+package com.exadel.frs.commonservice.dto;
+
+import com.fasterxml.jackson.annotation.JsonInclude;
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import static com.fasterxml.jackson.annotation.JsonInclude.Include.NON_NULL;
+
+@Data
+@Builder
+@NoArgsConstructor
+@AllArgsConstructor
+@JsonInclude(NON_NULL)
+public class PluginsVersionsDto {
+
+ private String age;
+ private String gender;
+ private String detector;
+ private String calculator;
+ private String mask;
+}
From 8031422e920a200f6b73779c4a96ec6b8926badb Mon Sep 17 00:00:00 2001
From: spospielov
Date: Sun, 29 Aug 2021 14:51:10 +0300
Subject: [PATCH 014/702] updated documentation
---
docs/Rest-API-description.md | 237 ++++++++++++++++++++---------------
1 file changed, 137 insertions(+), 100 deletions(-)
diff --git a/docs/Rest-API-description.md b/docs/Rest-API-description.md
index f77933a1f9..2955938347 100644
--- a/docs/Rest-API-description.md
+++ b/docs/Rest-API-description.md
@@ -2,27 +2,44 @@
## Table Of Contents
-+ [Face Recognition Service Endpoints](#face-recognition-service-endpoints)
- + [Add a Subject](#add-a-subject)
- + [Rename a Subject](#rename-a-subject)
- + [Delete a Subject](#delete-a-subject)
- + [Delete All Subjects](#delete-all-subjects)
- + [List Subjects](#list-subjects)
++ [Face Recognition Service](#face-recognition-service)
+ + [Managing Subjects](#managing-subjects)
+ + [Add a Subject](#add-a-subject)
+ + [Rename a Subject](#rename-a-subject)
+ + [Delete a Subject](#delete-a-subject)
+ + [Delete All Subjects](#delete-all-subjects)
+ + [List Subjects](#list-subjects)
+ + [Managing Subject Examples](#managing-subject-examples)
+ [Add an Example of a Subject](#add-an-example-of-a-subject)
- + [Recognize Faces from a Given Image](#recognize-faces-from-a-given-image)
+ [List of All Saved Examples of the Subject](#list-of-all-saved-examples-of-the-subject)
+ [Delete All Examples of the Subject by Name](#delete-all-examples-of-the-subject-by-name)
+ [Delete an Example of the Subject by ID](#delete-an-example-of-the-subject-by-id)
+ [Direct Download an Image example of the Subject by ID](#direct-download-an-image-example-of-the-subject-by-id)
+ [Download an Image example of the Subject by ID](#download-an-image-example-of-the-subject-by-id)
- + [Verify Faces from a Given Image](#verify-faces-from-a-given-image)
+ + [Recognize Faces from a Given Image](#recognize-faces-from-a-given-image)
+ + [Verify Faces from a Given Image](#verify-faces-from-a-given-image)
+ [Face Detection Service](#face-detection-service)
+ [Face Verification Service](#face-verification-service)
+ [Base64 Support](#base64-support)
To know more about face services and face plugins visit [this page](Face-services-and-plugins.md).
-## Face Recognition Service Endpoints
+## Face Recognition Service
+
+### Managing Subjects
+
+These endpoints allow you to work with subjects.
+
+The most popular case of subject usage is to assign a subject to one person.
+So, to upload several images of one person, you need to upload them to one subject.
+As a result, when you perform face recognition, you find a person who is on the image.
+
+Another case of subject usage is assigning a photo of several people as a subject.
+In this case, you need to detect all faces on the image and then save them to one subject.
+As a result, when you perform face recognition, you find all photos on which there is the person who is on the image.
+You don’t need to work with subjects explicitly.
+You can just upload a new example of the subject and the subject will be created automatically.
+Or if you delete all the examples of the subject, it will be deleted automatically.
### Add a Subject
```since 0.6 version```
@@ -164,6 +181,15 @@ Response body on success:
| -------- | ------ | -------------------------- |
| subjects | array | the list of subjects in Face Collection |
+### Managing Subject Examples
+
+The subject example is basically an image of a known face that you want to save to face collection.
+
+When you save a subject example, CompreFace calculates the embedding of the face (faceprint) and saves it into the database.
+By default, the image itself is also saved, it is needed for managing images, e.g. [download of the image](#direct-download-an-image-example-of-the-subject-by-id). You can change it using `save_images_to_db` parameter in [configuration](Configuration.md).
+
+One subject example is enough for face recognition, the accuracy will be high enough. But if you add more examples, the accuracy may be even better.
+
### Add an Example of a Subject
This creates an example of the subject by saving images. You can add as many images as you want to train the system. Image should
@@ -196,90 +222,6 @@ Response body on success:
| image_id | UUID | UUID of uploaded image |
| subject | string | Subject of the saved image |
-### Recognize Faces from a Given Image
-
-To recognize faces from the uploaded image:
-
-```http request
-curl -X POST "http://localhost:8000/api/v1/recognition/recognize?limit=&prediction_count=&det_prob_threshold=&face_plugins=&status=" \
--H "Content-Type: multipart/form-data" \
--H "x-api-key: " \
--F file=
-```
-
-| Element | Description | Type | Required | Notes |
-| ------------------ | ----------- | ------- | -------- | ------------------------------------------------------------ |
-| Content-Type | header | string | required | multipart/form-data |
-| x-api-key | header | string | required | api key of the Face recognition service, created by the user |
-| file | body | image | required | allowed image formats: jpeg, jpg, ico, png, bmp, gif, tif, tiff, webp. Max size is 5Mb |
-| limit | param | integer | optional | 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 |
-| det_prob_threshold | param | string | optional | minimum required confidence that a recognized face is actually a face. Value is between 0.0 and 1.0. |
-| prediction_count | param | integer | optional | maximum number of subject predictions per face. It returns the most similar subjects. Default value: 1 |
-| face_plugins | param | string | optional | comma-separated slugs of face plugins. If empty, no additional information is returned. [Learn more](Face-services-and-plugins.md) |
-| status | param | boolean | optional | if true includes system information like execution_time and plugin_version fields. Default value is false |
-
-Response body on success:
-```json
-{
- "result" : [ {
- "age" : {
- "probability": 0.9308982491493225,
- "high": 32,
- "low": 25
- },
- "gender" : {
- "probability": 0.9898611307144165,
- "value": "female"
- },
- "mask" : {
- "probability": 0.9999470710754395,
- "value": "without_mask"
- },
- "embedding" : [ 9.424854069948196E-4, "...", -0.011415496468544006 ],
- "box" : {
- "probability" : 1.0,
- "x_max" : 1420,
- "y_max" : 1368,
- "x_min" : 548,
- "y_min" : 295
- },
- "landmarks" : [ [ 814, 713 ], [ 1104, 829 ], [ 832, 937 ], [ 704, 1030 ], [ 1017, 1133 ] ],
- "subjects" : [ {
- "similarity" : 0.97858,
- "subject" : "subject1"
- } ],
- "execution_time" : {
- "age" : 28.0,
- "gender" : 26.0,
- "detector" : 117.0,
- "calculator" : 45.0
- }
- } ],
- "plugins_versions" : {
- "age" : "agegender.AgeDetector",
- "gender" : "agegender.GenderDetector",
- "detector" : "facenet.FaceDetector",
- "calculator" : "facenet.Calculator"
- }
-}
-```
-
-| Element | Type | Description |
-| ------------------------------ | ------- | ------------------------------------------------------------ |
-| age | object | detected age range. Return only if [age plugin](Face-services-and-plugins.md#face-plugins) is enabled |
-| gender | object | detected gender. Return only if [gender plugin](Face-services-and-plugins.md#face-plugins) is enabled |
-| mask | object | detected mask. Return only if [face mask plugin](Face-services-and-plugins.md#face-plugins) is enabled. |
-| embedding | array | face embeddings. Return only if [calculator plugin](Face-services-and-plugins.md#face-plugins) is enabled |
-| box | object | list of parameters of the bounding box for this face |
-| probability | float | probability that a found face is actually a face |
-| x_max, y_max, x_min, y_min | integer | coordinates of the frame containing the face |
-| landmarks | array | list of the coordinates of the frame containing the face-landmarks. Return only if [landmarks plugin](Face-services-and-plugins.md#face-plugins) is enabled |
-| subjects | list | list of similar subjects with size of order by similarity |
-| similarity | float | similarity that on that image predicted person |
-| subject | string | name of the subject in Face Collection |
-| execution_time | object | execution time of all plugins |
-| plugins_versions | object | contains information about plugin versions |
-
### List of All Saved Examples of the Subject
@@ -413,6 +355,94 @@ curl -X GET "http://localhost:8000/api/v1/recognition/faces//img"
Response body is binary image. Empty bytes if image not found.
+
+### Recognize Faces from a Given Image
+
+To recognize faces from the uploaded image:
+
+```http request
+curl -X POST "http://localhost:8000/api/v1/recognition/recognize?limit=&prediction_count=&det_prob_threshold=&face_plugins=&status=" \
+-H "Content-Type: multipart/form-data" \
+-H "x-api-key: " \
+-F file=
+```
+
+| Element | Description | Type | Required | Notes |
+| ------------------ | ----------- | ------- | -------- | ------------------------------------------------------------ |
+| Content-Type | header | string | required | multipart/form-data |
+| x-api-key | header | string | required | api key of the Face recognition service, created by the user |
+| file | body | image | required | allowed image formats: jpeg, jpg, ico, png, bmp, gif, tif, tiff, webp. Max size is 5Mb |
+| limit | param | integer | optional | 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 |
+| det_prob_threshold | param | string | optional | minimum required confidence that a recognized face is actually a face. Value is between 0.0 and 1.0. |
+| prediction_count | param | integer | optional | maximum number of subject predictions per face. It returns the most similar subjects. Default value: 1 |
+| face_plugins | param | string | optional | comma-separated slugs of face plugins. If empty, no additional information is returned. [Learn more](Face-services-and-plugins.md) |
+| status | param | boolean | optional | if true includes system information like execution_time and plugin_version fields. Default value is false |
+
+Response body on success:
+```json
+{
+ "result" : [ {
+ "age" : {
+ "probability": 0.9308982491493225,
+ "high": 32,
+ "low": 25
+ },
+ "gender" : {
+ "probability": 0.9898611307144165,
+ "value": "female"
+ },
+ "mask" : {
+ "probability": 0.9999470710754395,
+ "value": "without_mask"
+ },
+ "embedding" : [ 9.424854069948196E-4, "...", -0.011415496468544006 ],
+ "box" : {
+ "probability" : 1.0,
+ "x_max" : 1420,
+ "y_max" : 1368,
+ "x_min" : 548,
+ "y_min" : 295
+ },
+ "landmarks" : [ [ 814, 713 ], [ 1104, 829 ], [ 832, 937 ], [ 704, 1030 ], [ 1017, 1133 ] ],
+ "subjects" : [ {
+ "similarity" : 0.97858,
+ "subject" : "subject1"
+ } ],
+ "execution_time" : {
+ "age" : 28.0,
+ "gender" : 26.0,
+ "detector" : 117.0,
+ "calculator" : 45.0,
+ "mask": 36.0
+ }
+ } ],
+ "plugins_versions" : {
+ "age" : "agegender.AgeDetector",
+ "gender" : "agegender.GenderDetector",
+ "detector" : "facenet.FaceDetector",
+ "calculator" : "facenet.Calculator",
+ "mask": "facemask.MaskDetector"
+ }
+}
+```
+
+| Element | Type | Description |
+| ------------------------------ | ------- | ------------------------------------------------------------ |
+| age | object | detected age range. Return only if [age plugin](Face-services-and-plugins.md#face-plugins) is enabled |
+| gender | object | detected gender. Return only if [gender plugin](Face-services-and-plugins.md#face-plugins) is enabled |
+| mask | object | detected mask. Return only if [face mask plugin](Face-services-and-plugins.md#face-plugins) is enabled. |
+| embedding | array | face embeddings. Return only if [calculator plugin](Face-services-and-plugins.md#face-plugins) is enabled |
+| box | object | list of parameters of the bounding box for this face |
+| probability | float | probability that a found face is actually a face |
+| x_max, y_max, x_min, y_min | integer | coordinates of the frame containing the face |
+| landmarks | array | list of the coordinates of the frame containing the face-landmarks. Return only if [landmarks plugin](Face-services-and-plugins.md#face-plugins) is enabled |
+| subjects | list | list of similar subjects with size of order by similarity |
+| similarity | float | similarity that on that image predicted person |
+| subject | string | name of the subject in Face Collection |
+| execution_time | object | execution time of all plugins |
+| plugins_versions | object | contains information about plugin versions |
+
+
### Verify Faces from a Given Image
To compare faces from the uploaded images with the face in saved image ID:
@@ -468,7 +498,8 @@ Response body on success:
"age" : 59.0,
"gender" : 30.0,
"detector" : 177.0,
- "calculator" : 70.0
+ "calculator" : 70.0,
+ "mask": 36.0
}
}
],
@@ -476,7 +507,8 @@ Response body on success:
"age" : "agegender.AgeDetector",
"gender" : "agegender.GenderDetector",
"detector" : "facenet.FaceDetector",
- "calculator" : "facenet.Calculator"
+ "calculator" : "facenet.Calculator",
+ "mask": "facemask.MaskDetector"
}
}
```
@@ -547,14 +579,16 @@ Response body on success:
"age" : 30.0,
"gender" : 26.0,
"detector" : 130.0,
- "calculator" : 49.0
+ "calculator" : 49.0,
+ "mask": 36.0
}
} ],
"plugins_versions" : {
"age" : "agegender.AgeDetector",
"gender" : "agegender.GenderDetector",
"detector" : "facenet.FaceDetector",
- "calculator" : "facenet.Calculator"
+ "calculator" : "facenet.Calculator",
+ "mask": "facemask.MaskDetector"
}
}
```
@@ -627,7 +661,8 @@ Response body on success:
"age" : 85.0,
"gender" : 51.0,
"detector" : 67.0,
- "calculator" : 116.0
+ "calculator" : 116.0,
+ "mask": 36.0
}
},
"face_matches": [
@@ -659,14 +694,16 @@ Response body on success:
"age" : 59.0,
"gender" : 30.0,
"detector" : 177.0,
- "calculator" : 70.0
+ "calculator" : 70.0,
+ "mask": 36.0
}
}],
"plugins_versions" : {
"age" : "agegender.AgeDetector",
"gender" : "agegender.GenderDetector",
"detector" : "facenet.FaceDetector",
- "calculator" : "facenet.Calculator"
+ "calculator" : "facenet.Calculator",
+ "mask": "facemask.MaskDetector"
}
}]
}
From 8e0fa7eb430a30fc7aa51ab08855e352ef02c776 Mon Sep 17 00:00:00 2001
From: spospielov
Date: Sun, 29 Aug 2021 14:51:29 +0300
Subject: [PATCH 015/702] Added mask plugin to custom builds
---
embedding-calculator/Makefile | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/embedding-calculator/Makefile b/embedding-calculator/Makefile
index 8f9b540bf0..9dc1732e2e 100755
--- a/embedding-calculator/Makefile
+++ b/embedding-calculator/Makefile
@@ -7,15 +7,15 @@ CUDA_IMAGE = $(IMAGE)-base:base-cuda100-py37
MOBILENET_BUILD_ARGS := --build-arg FACE_DETECTION_PLUGIN=insightface.FaceDetector@retinaface_mnet025_v1 \
--build-arg CALCULATION_PLUGIN=insightface.Calculator@arcface_mobilefacenet \
- --build-arg EXTRA_PLUGINS=insightface.LandmarksDetector,insightface.GenderDetector,insightface.AgeDetector
+ --build-arg EXTRA_PLUGINS=insightface.LandmarksDetector,insightface.GenderDetector,insightface.AgeDetector,insightface.facemask.MaskDetector
ARCFACE_r100_BUILD_ARGS := --build-arg FACE_DETECTION_PLUGIN=insightface.FaceDetector@retinaface_r50_v1 \
--build-arg CALCULATION_PLUGIN=insightface.Calculator@arcface-r100-msfdrop75 \
- --build-arg EXTRA_PLUGINS=insightface.LandmarksDetector,insightface.GenderDetector,insightface.AgeDetector
+ --build-arg EXTRA_PLUGINS=insightface.LandmarksDetector,insightface.GenderDetector,insightface.AgeDetector,insightface.facemask.MaskDetector
FACENET_BUILD_ARGS := --build-arg FACE_DETECTION_PLUGIN=facenet.FaceDetector \
--build-arg CALCULATION_PLUGIN=facenet.Calculator \
- --build-arg EXTRA_PLUGINS=facenet.LandmarksDetector,agegender.GenderDetector,agegender.AgeDetector
+ --build-arg EXTRA_PLUGINS=facenet.LandmarksDetector,agegender.GenderDetector,agegender.AgeDetector,facenet.facemask.MaskDetector
define get_from_remote_tgz
mkdir -p $(2)
From 54ef4f11dc982516a20313775d2e1e57f3bb62d5 Mon Sep 17 00:00:00 2001
From: spospielov
Date: Sun, 29 Aug 2021 17:15:52 +0300
Subject: [PATCH 016/702] fixed statistics job
---
.../scheduler/job/StatisticsJob.java | 22 +++++++++----------
1 file changed, 11 insertions(+), 11 deletions(-)
diff --git a/java/common/src/main/java/com/exadel/frs/commonservice/scheduler/job/StatisticsJob.java b/java/common/src/main/java/com/exadel/frs/commonservice/scheduler/job/StatisticsJob.java
index 802ec639aa..8ce3fe6ed9 100644
--- a/java/common/src/main/java/com/exadel/frs/commonservice/scheduler/job/StatisticsJob.java
+++ b/java/common/src/main/java/com/exadel/frs/commonservice/scheduler/job/StatisticsJob.java
@@ -35,16 +35,16 @@ public class StatisticsJob extends QuartzJobBean {
private ModelRepository modelRepository;
private UserRepository userRepository;
- private List ranges = List.of(
- Range.between(1, 10),
- Range.between(11, 50),
- Range.between(51, 200),
- Range.between(201, 500),
- Range.between(501, 2000),
- between(2001, 10000),
- between(10001, 50000),
- between(50001, 200000),
- between(200001, 1000000)
+ private final List> ranges = List.of(
+ Range.between(1L, 10L),
+ Range.between(11L, 50L),
+ Range.between(51L, 200L),
+ Range.between(201L, 500L),
+ Range.between(501L, 2000L),
+ between(2001L, 10000L),
+ between(10001L, 50000L),
+ between(50001L, 200000L),
+ between(200001L, 1000000L)
);
@Autowired
@@ -92,7 +92,7 @@ private String getSubjectsRange(Long subjectCount) {
return "0";
}
- for (Range range : ranges) {
+ for (Range range : ranges) {
if (range.contains(subjectCount)) {
return range.getMinimum() + "-" + range.getMaximum();
}
From 2f49d98b51db0e44f5c9a7adf2de3cef846fbd53 Mon Sep 17 00:00:00 2001
From: spospielov
Date: Sun, 29 Aug 2021 17:16:15 +0300
Subject: [PATCH 017/702] updated documentation
---
README.md | 1 +
docs/Custom-builds.md | 32 ++++++++++++++++----------------
2 files changed, 17 insertions(+), 16 deletions(-)
diff --git a/README.md b/README.md
index 76032e04ae..00290cc906 100644
--- a/README.md
+++ b/README.md
@@ -144,6 +144,7 @@ Follow this [link](/dev)
| SDK | Repository |
| ---------- | ------ |
| JavaScript | https://github.com/exadel-inc/compreface-javascript-sdk |
+| Python | https://github.com/exadel-inc/compreface-python-sdk |
# Documentation
diff --git a/docs/Custom-builds.md b/docs/Custom-builds.md
index 70bbcaecf1..866119d7cd 100644
--- a/docs/Custom-builds.md
+++ b/docs/Custom-builds.md
@@ -57,21 +57,21 @@ libraries. All you need to do is
2. Take the `docker-compose` file from `/dev` folder as a template
3. Specify new model name in build arguments. For more information look at [this documentation](https://github.
com/exadel-inc/CompreFace/tree/master/embedding-calculator#run-service). E.g. here is a part of `docker-compose` file for building with custom model with GPU support:
-```
+```dockerfile
compreface-core:
- image: ${registry}compreface-core:${CORE_VERSION}
- container_name: "compreface-core"
- ports:
- - "3300:3000"
- runtime: nvidia
- build:
- context: ../embedding-calculator
- args:
- - FACE_DETECTION_PLUGIN=insightface.FaceDetector@retinaface_r50_v1
- - CALCULATION_PLUGIN=insightface.Calculator@arcface_r100_v1
- - EXTRA_PLUGINS=insightface.LandmarksDetector,insightface.GenderDetector,insightface.AgeDetector
- - BASE_IMAGE=${registry}compreface-core-base:base-cuda100-py37
- - GPU_IDX=0
- environment:
- - ML_PORT=3000
+ image: ${registry}compreface-core:${CORE_VERSION}
+ container_name: "compreface-core"
+ ports:
+ - "3300:3000"
+ runtime: nvidia
+ build:
+ context: ../embedding-calculator
+ args:
+ - FACE_DETECTION_PLUGIN=insightface.FaceDetector@retinaface_r50_v1
+ - CALCULATION_PLUGIN=insightface.Calculator@arcface_r100_v1
+ - EXTRA_PLUGINS=insightface.LandmarksDetector,insightface.GenderDetector,insightface.AgeDetector,insightface.facemask.MaskDetector
+ - BASE_IMAGE=compreface-core-base:base-cuda100-py37
+ - GPU_IDX=0
+ environment:
+ - ML_PORT=3000
```
From 56a48a523f3aa47c3a1c0b0c4a7101cdd0c0bee8 Mon Sep 17 00:00:00 2001
From: spospielov
Date: Sun, 29 Aug 2021 17:16:24 +0300
Subject: [PATCH 018/702] updated version
---
.env | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/.env b/.env
index bd92cf6138..6c6690cb69 100644
--- a/.env
+++ b/.env
@@ -12,7 +12,7 @@ enable_email_server=false
save_images_to_db=true
compreface_api_java_options=-Xmx8g
compreface_admin_java_options=-Xmx8g
-ADMIN_VERSION=0.5.1
-API_VERSION=0.5.1
-FE_VERSION=0.5.1
-CORE_VERSION=0.5.1
\ No newline at end of file
+ADMIN_VERSION=0.6.0
+API_VERSION=0.6.0
+FE_VERSION=0.6.0
+CORE_VERSION=0.6.0
\ No newline at end of file
From 7c230fa3a936584d7c1cf9b0a10fb0540faefa37 Mon Sep 17 00:00:00 2001
From: spospielov
Date: Sun, 29 Aug 2021 17:44:26 +0300
Subject: [PATCH 019/702] updated documentation
---
README.md | 1 +
1 file changed, 1 insertion(+)
diff --git a/README.md b/README.md
index 00290cc906..7e6ee003be 100644
--- a/README.md
+++ b/README.md
@@ -58,6 +58,7 @@ docker
* [Face data migration](/docs/Face-data-migration.md)
* [User Roles System](/docs/User-Roles-System.md)
* [Face Mask Detection Plugin](/docs/Mask-detection-plugin.md)
+ * [Kubernetes configuration](https://github.com/exadel-inc/compreface-kubernetes)
* [Gathering Anonymous Statistics](/docs/Gathering-anonymous-statistics.md)
* [Contributing](#contributing)
* [License info](#license-info)
From 664366daa2deaf8d1117a7cb294b65373a63b0e5 Mon Sep 17 00:00:00 2001
From: spospielov
Date: Sun, 29 Aug 2021 17:47:35 +0300
Subject: [PATCH 020/702] removed typo
---
ui/src/app/store/manage-collectiom/selectors.ts | 1 -
1 file changed, 1 deletion(-)
diff --git a/ui/src/app/store/manage-collectiom/selectors.ts b/ui/src/app/store/manage-collectiom/selectors.ts
index aa82561bc8..dcc1f80635 100644
--- a/ui/src/app/store/manage-collectiom/selectors.ts
+++ b/ui/src/app/store/manage-collectiom/selectors.ts
@@ -6,7 +6,6 @@
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
- cdk-overlay-connected-position-bounding-box
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
From 6245a5a8643ca4a615433394639630420d41bf8a Mon Sep 17 00:00:00 2001
From: spospielov
Date: Sun, 29 Aug 2021 19:55:37 +0300
Subject: [PATCH 021/702] updated custom build configs
---
custom-builds/FaceNet/.env | 8 ++++----
custom-builds/Mobilenet-gpu/.env | 8 ++++----
custom-builds/Mobilenet/.env | 8 ++++----
custom-builds/SubCenter-ArcFace-r100-gpu/.env | 8 ++++----
custom-builds/SubCenter-ArcFace-r100/.env | 8 ++++----
5 files changed, 20 insertions(+), 20 deletions(-)
diff --git a/custom-builds/FaceNet/.env b/custom-builds/FaceNet/.env
index fd87b5ac6d..692d8db96c 100644
--- a/custom-builds/FaceNet/.env
+++ b/custom-builds/FaceNet/.env
@@ -12,7 +12,7 @@ enable_email_server=false
save_images_to_db=true
compreface_api_java_options=-Xmx8g
compreface_admin_java_options=-Xmx8g
-ADMIN_VERSION=0.5.1
-API_VERSION=0.5.1
-FE_VERSION=0.5.1
-CORE_VERSION=0.5.1-facenet
\ No newline at end of file
+ADMIN_VERSION=0.6.0
+API_VERSION=0.6.0
+FE_VERSION=0.6.0
+CORE_VERSION=0.6.0-facenet
\ No newline at end of file
diff --git a/custom-builds/Mobilenet-gpu/.env b/custom-builds/Mobilenet-gpu/.env
index 39ce75aa97..62d9581424 100644
--- a/custom-builds/Mobilenet-gpu/.env
+++ b/custom-builds/Mobilenet-gpu/.env
@@ -12,7 +12,7 @@ enable_email_server=false
save_images_to_db=true
compreface_api_java_options=-Xmx8g
compreface_admin_java_options=-Xmx8g
-ADMIN_VERSION=0.5.1
-API_VERSION=0.5.1
-FE_VERSION=0.5.1
-CORE_VERSION=0.5.1-mobilenet-gpu
+ADMIN_VERSION=0.6.0
+API_VERSION=0.6.0
+FE_VERSION=0.6.0
+CORE_VERSION=0.6.0-mobilenet-gpu
diff --git a/custom-builds/Mobilenet/.env b/custom-builds/Mobilenet/.env
index cb4bce8bb2..8698b49449 100644
--- a/custom-builds/Mobilenet/.env
+++ b/custom-builds/Mobilenet/.env
@@ -12,7 +12,7 @@ enable_email_server=false
save_images_to_db=true
compreface_api_java_options=-Xmx8g
compreface_admin_java_options=-Xmx8g
-ADMIN_VERSION=0.5.1
-API_VERSION=0.5.1
-FE_VERSION=0.5.1
-CORE_VERSION=0.5.1-mobilenet
+ADMIN_VERSION=0.6.0
+API_VERSION=0.6.0
+FE_VERSION=0.6.0
+CORE_VERSION=0.6.0-mobilenet
diff --git a/custom-builds/SubCenter-ArcFace-r100-gpu/.env b/custom-builds/SubCenter-ArcFace-r100-gpu/.env
index 25fe2db1d3..d1a5e8a6c7 100644
--- a/custom-builds/SubCenter-ArcFace-r100-gpu/.env
+++ b/custom-builds/SubCenter-ArcFace-r100-gpu/.env
@@ -12,7 +12,7 @@ enable_email_server=false
save_images_to_db=true
compreface_api_java_options=-Xmx8g
compreface_admin_java_options=-Xmx8g
-ADMIN_VERSION=0.5.1
-API_VERSION=0.5.1
-FE_VERSION=0.5.1
-CORE_VERSION=0.5.1-arcface-r100-gpu
+ADMIN_VERSION=0.6.0
+API_VERSION=0.6.0
+FE_VERSION=0.6.0
+CORE_VERSION=0.6.0-arcface-r100-gpu
diff --git a/custom-builds/SubCenter-ArcFace-r100/.env b/custom-builds/SubCenter-ArcFace-r100/.env
index 34dc6c31fc..74f6c844a1 100644
--- a/custom-builds/SubCenter-ArcFace-r100/.env
+++ b/custom-builds/SubCenter-ArcFace-r100/.env
@@ -12,7 +12,7 @@ enable_email_server=false
save_images_to_db=true
compreface_api_java_options=-Xmx8g
compreface_admin_java_options=-Xmx8g
-ADMIN_VERSION=0.5.1
-API_VERSION=0.5.1
-FE_VERSION=0.5.1
-CORE_VERSION=0.5.1-arcface-r100
\ No newline at end of file
+ADMIN_VERSION=0.6.0
+API_VERSION=0.6.0
+FE_VERSION=0.6.0
+CORE_VERSION=0.6.0-arcface-r100
\ No newline at end of file
From e76988c6108ffc3a54fd8a9f4916c6e375ceaa7c Mon Sep 17 00:00:00 2001
From: spospielov
Date: Sun, 29 Aug 2021 23:51:51 +0300
Subject: [PATCH 022/702] returned manage collection option
---
.../features/model-table/model-table.component.html | 10 +++++-----
1 file changed, 5 insertions(+), 5 deletions(-)
diff --git a/ui/src/app/features/model-table/model-table.component.html b/ui/src/app/features/model-table/model-table.component.html
index b2cfe3b89c..e2a8984d73 100644
--- a/ui/src/app/features/model-table/model-table.component.html
+++ b/ui/src/app/features/model-table/model-table.component.html
@@ -64,11 +64,11 @@
-
-
-
-
-
+
+
+
From a402ccb9f45b77ddbac7cc38391683df9438d435 Mon Sep 17 00:00:00 2001
From: prosis6
Date: Tue, 31 Aug 2021 17:59:50 +0300
Subject: [PATCH 023/702] EFRS-1081: Delete a Subject endpoint do not have
Response body on success add response to Delete subject action
---
.../core/trainservice/controller/SubjectController.java | 9 +++++++--
.../frs/core/trainservice/service/SubjectService.java | 6 +++---
2 files changed, 10 insertions(+), 5 deletions(-)
diff --git a/java/api/src/main/java/com/exadel/frs/core/trainservice/controller/SubjectController.java b/java/api/src/main/java/com/exadel/frs/core/trainservice/controller/SubjectController.java
index b0d7aba8da..98889af050 100644
--- a/java/api/src/main/java/com/exadel/frs/core/trainservice/controller/SubjectController.java
+++ b/java/api/src/main/java/com/exadel/frs/core/trainservice/controller/SubjectController.java
@@ -50,10 +50,15 @@ public Map renameSubject(
}
@DeleteMapping("/{subject}")
- public void deleteSubject(
+ public Map deleteSubject(
@ApiParam(value = API_KEY_DESC, required = true) @RequestHeader(X_FRS_API_KEY_HEADER) final String apiKey,
@ApiParam(value = SUBJECT_DESC, required = true) @PathVariable("subject") final String subjectName) {
- subjectService.deleteSubjectByName(apiKey, subjectName);
+ return Map.of(
+ "subject",
+ subjectService.deleteSubjectByName(apiKey, subjectName)
+ .getRight()
+ .getSubjectName()
+ );
}
@DeleteMapping
diff --git a/java/api/src/main/java/com/exadel/frs/core/trainservice/service/SubjectService.java b/java/api/src/main/java/com/exadel/frs/core/trainservice/service/SubjectService.java
index bc534ee7b9..a61e172982 100644
--- a/java/api/src/main/java/com/exadel/frs/core/trainservice/service/SubjectService.java
+++ b/java/api/src/main/java/com/exadel/frs/core/trainservice/service/SubjectService.java
@@ -79,11 +79,11 @@ public int removeAllSubjectEmbeddings(final String apiKey, final String subjectN
return removed;
}
- public void deleteSubjectByName(final String apiKey, final String subjectName) {
+ public Pair deleteSubjectByName(final String apiKey, final String subjectName) {
if (StringUtils.isBlank(subjectName)) {
- deleteSubjectsByApiKey(apiKey);
+ return Pair.of(deleteSubjectsByApiKey(apiKey), null);
} else {
- deleteSubjectByNameAndApiKey(apiKey, subjectName);
+ return Pair.of(null, deleteSubjectByNameAndApiKey(apiKey, subjectName));
}
}
From 67b310ccdfdd9dcb51198d7bd0155b3f4176fabc Mon Sep 17 00:00:00 2001
From: prosis6
Date: Thu, 2 Sep 2021 17:41:55 +0300
Subject: [PATCH 024/702] EFRS-1090: No cache header in endpoint for retrieving
images added cache control header to allow browser ache the image
---
.../trainservice/controller/EmbeddingController.java | 9 ++++++---
.../core/trainservice/controller/StaticController.java | 9 ++++++---
.../frs/core/trainservice/system/global/Constants.java | 1 +
3 files changed, 13 insertions(+), 6 deletions(-)
diff --git a/java/api/src/main/java/com/exadel/frs/core/trainservice/controller/EmbeddingController.java b/java/api/src/main/java/com/exadel/frs/core/trainservice/controller/EmbeddingController.java
index 81c361fd1f..931bbf7559 100644
--- a/java/api/src/main/java/com/exadel/frs/core/trainservice/controller/EmbeddingController.java
+++ b/java/api/src/main/java/com/exadel/frs/core/trainservice/controller/EmbeddingController.java
@@ -13,10 +13,12 @@
import com.exadel.frs.core.trainservice.validation.ImageExtensionValidator;
import com.fasterxml.jackson.annotation.JsonProperty;
import io.swagger.annotations.ApiParam;
+import javax.servlet.http.HttpServletResponse;
import lombok.RequiredArgsConstructor;
import org.apache.commons.lang3.tuple.Pair;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
+import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
@@ -87,9 +89,10 @@ public EmbeddingDto addEmbeddingBase64(
@GetMapping(value = "/{embeddingId}/img", produces = MediaType.APPLICATION_OCTET_STREAM_VALUE)
public @ResponseBody
- byte[] downloadImg(
- @ApiParam(value = API_KEY_DESC, required = true) @RequestHeader(name = X_FRS_API_KEY_HEADER) final String apiKey,
- @ApiParam(value = IMAGE_ID_DESC, required = true) @PathVariable final UUID embeddingId) {
+ byte[] downloadImg(HttpServletResponse response,
+ @ApiParam(value = API_KEY_DESC, required = true) @RequestHeader(name = X_FRS_API_KEY_HEADER) final String apiKey,
+ @ApiParam(value = IMAGE_ID_DESC, required = true) @PathVariable final UUID embeddingId) {
+ response.addHeader(HttpHeaders.CACHE_CONTROL, CACHE_CONTROL_HEADER_VALUE);
return embeddingService.getImg(apiKey, embeddingId)
.map(Img::getContent)
.orElse(new byte[]{});
diff --git a/java/api/src/main/java/com/exadel/frs/core/trainservice/controller/StaticController.java b/java/api/src/main/java/com/exadel/frs/core/trainservice/controller/StaticController.java
index b4981c9486..a285d24dc8 100644
--- a/java/api/src/main/java/com/exadel/frs/core/trainservice/controller/StaticController.java
+++ b/java/api/src/main/java/com/exadel/frs/core/trainservice/controller/StaticController.java
@@ -3,7 +3,9 @@
import com.exadel.frs.commonservice.entity.Img;
import com.exadel.frs.core.trainservice.service.EmbeddingService;
import io.swagger.annotations.ApiParam;
+import javax.servlet.http.HttpServletResponse;
import lombok.RequiredArgsConstructor;
+import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.*;
@@ -20,9 +22,10 @@ public class StaticController {
@GetMapping(value = "/{apiKey}/images/{embeddingId}", produces = MediaType.APPLICATION_OCTET_STREAM_VALUE)
public @ResponseBody
- byte[] downloadImg(
- @ApiParam(value = API_KEY_DESC, required = true) @PathVariable("apiKey") final String apiKey,
- @ApiParam(value = IMAGE_ID_DESC, required = true) @PathVariable final UUID embeddingId) {
+ byte[] downloadImg(HttpServletResponse response,
+ @ApiParam(value = API_KEY_DESC, required = true) @PathVariable("apiKey") final String apiKey,
+ @ApiParam(value = IMAGE_ID_DESC, required = true) @PathVariable final UUID embeddingId) {
+ response.addHeader(HttpHeaders.CACHE_CONTROL, CACHE_CONTROL_HEADER_VALUE);
return embeddingService.getImg(apiKey, embeddingId)
.map(Img::getContent)
.orElse(new byte[]{});
diff --git a/java/api/src/main/java/com/exadel/frs/core/trainservice/system/global/Constants.java b/java/api/src/main/java/com/exadel/frs/core/trainservice/system/global/Constants.java
index b280402943..b8660f98a4 100644
--- a/java/api/src/main/java/com/exadel/frs/core/trainservice/system/global/Constants.java
+++ b/java/api/src/main/java/com/exadel/frs/core/trainservice/system/global/Constants.java
@@ -58,4 +58,5 @@ public class Constants {
public static final String DEMO_API_KEY = "00000000-0000-0000-0000-000000000002";
public static final String FACENET2018 = "Facenet2018";
public static final String SERVER_UUID = UUID.randomUUID().toString();
+ public static final String CACHE_CONTROL_HEADER_VALUE = "public, max-age=31536000";
}
\ No newline at end of file
From 254317288da350986e4e5b3af69f023e6fb3b330 Mon Sep 17 00:00:00 2001
From: prosis6
Date: Fri, 3 Sep 2021 17:31:06 +0300
Subject: [PATCH 025/702] EFRS-1051: Add 'Delete images in bulk' endpoint added
"/delete" endpoint (post) to allow bulk delete list of image examples by IDs
---
.../controller/EmbeddingController.java | 13 ++++++++++++-
.../core/trainservice/service/SubjectService.java | 13 +++++++++++++
.../core/trainservice/system/global/Constants.java | 1 +
3 files changed, 26 insertions(+), 1 deletion(-)
diff --git a/java/api/src/main/java/com/exadel/frs/core/trainservice/controller/EmbeddingController.java b/java/api/src/main/java/com/exadel/frs/core/trainservice/controller/EmbeddingController.java
index 81c361fd1f..5e693f1a90 100644
--- a/java/api/src/main/java/com/exadel/frs/core/trainservice/controller/EmbeddingController.java
+++ b/java/api/src/main/java/com/exadel/frs/core/trainservice/controller/EmbeddingController.java
@@ -13,6 +13,7 @@
import com.exadel.frs.core.trainservice.validation.ImageExtensionValidator;
import com.fasterxml.jackson.annotation.JsonProperty;
import io.swagger.annotations.ApiParam;
+import java.util.stream.Collectors;
import lombok.RequiredArgsConstructor;
import org.apache.commons.lang3.tuple.Pair;
import org.springframework.data.domain.Page;
@@ -23,7 +24,6 @@
import javax.validation.Valid;
import javax.validation.constraints.Min;
-import javax.validation.constraints.NotEmpty;
import java.io.IOException;
import java.util.List;
import java.util.Map;
@@ -122,6 +122,17 @@ public EmbeddingDto deleteEmbeddingById(
var embedding = subjectService.removeSubjectEmbedding(apiKey, embeddingId);
return new EmbeddingDto(embeddingId.toString(), embedding.getSubject().getSubjectName());
}
+ @WriteEndpoint
+ @PostMapping("/delete")
+ public List deleteEmbeddingsById(
+ @ApiParam(value = API_KEY_DESC, required = true) @RequestHeader(name = X_FRS_API_KEY_HEADER) final String apiKey,
+ @ApiParam(value = IMAGE_IDS_DESC, required = true) @RequestBody List embeddingIds) {
+ List list = subjectService.removeSubjectEmbeddings(apiKey, embeddingIds);
+ List dtoList = list.stream()
+ .map(c -> new EmbeddingDto(c.getId().toString(), c.getSubject().getSubjectName()))
+ .collect(Collectors.toList());
+ return dtoList;
+ }
@PostMapping(value = "/{embeddingId}/verify", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
public VerificationResult recognizeFile(
diff --git a/java/api/src/main/java/com/exadel/frs/core/trainservice/service/SubjectService.java b/java/api/src/main/java/com/exadel/frs/core/trainservice/service/SubjectService.java
index a61e172982..be1070d630 100644
--- a/java/api/src/main/java/com/exadel/frs/core/trainservice/service/SubjectService.java
+++ b/java/api/src/main/java/com/exadel/frs/core/trainservice/service/SubjectService.java
@@ -2,6 +2,7 @@
import com.exadel.frs.commonservice.entity.Embedding;
import com.exadel.frs.commonservice.entity.Subject;
+import com.exadel.frs.commonservice.exception.EmbeddingNotFoundException;
import com.exadel.frs.commonservice.exception.TooManyFacesException;
import com.exadel.frs.commonservice.sdk.faces.FacesApiClient;
import com.exadel.frs.commonservice.sdk.faces.feign.dto.FindFacesResponse;
@@ -15,6 +16,7 @@
import com.exadel.frs.core.trainservice.dto.FaceVerification;
import com.exadel.frs.core.trainservice.dto.ProcessImageParams;
import com.exadel.frs.core.trainservice.system.global.Constants;
+import java.util.stream.Collectors;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
@@ -110,6 +112,17 @@ public Embedding removeSubjectEmbedding(final String apiKey, final UUID embeddin
return embedding;
}
+ public List removeSubjectEmbeddings(final String apiKey, final List embeddingIds){
+ List result = new ArrayList<>();
+ for (UUID id: embeddingIds) {
+ try {
+ result.add(removeSubjectEmbedding(apiKey, id));
+ } catch (EmbeddingNotFoundException e){
+ e.printStackTrace();
+ }
+ }
+ return result;
+ }
public boolean updateSubjectName(final String apiKey, final String oldSubjectName, final String newSubjectName) {
if (StringUtils.isEmpty(newSubjectName) || newSubjectName.equals(oldSubjectName)) {
diff --git a/java/api/src/main/java/com/exadel/frs/core/trainservice/system/global/Constants.java b/java/api/src/main/java/com/exadel/frs/core/trainservice/system/global/Constants.java
index b280402943..bea6a117db 100644
--- a/java/api/src/main/java/com/exadel/frs/core/trainservice/system/global/Constants.java
+++ b/java/api/src/main/java/com/exadel/frs/core/trainservice/system/global/Constants.java
@@ -47,6 +47,7 @@ public class Constants {
public static final String LIMIT_DEFAULT_VALUE = "0";
public static final String IMAGE_WITH_ONE_FACE_DESC = "A picture with one face (accepted formats: jpeg, png).";
public static final String IMAGE_ID_DESC = "Image Id from collection to compare with face.";
+ public static final String IMAGE_IDS_DESC = "List of image Ids from collection to compare with face";
public static final String SUBJECT_DESC = "Person's name to whom the face belongs to.";
public static final String SUBJECT = "subject";
public static final String SUBJECTS = "faces";
From 5508608e2856b81e4ca7c1ea3e9c13215e586586 Mon Sep 17 00:00:00 2001
From: Artem
Date: Fri, 10 Sep 2021 13:37:16 +0300
Subject: [PATCH 026/702] Husky hooks (#595)
* Pre-commit hook with husky
* Pretty-quick package to run prettier only on staged files
* scss files are included in pretier task
---
ui/.husky/pre-commit | 5 +++++
ui/package.json | 5 ++++-
2 files changed, 9 insertions(+), 1 deletion(-)
create mode 100644 ui/.husky/pre-commit
diff --git a/ui/.husky/pre-commit b/ui/.husky/pre-commit
new file mode 100644
index 0000000000..c144ee1eb9
--- /dev/null
+++ b/ui/.husky/pre-commit
@@ -0,0 +1,5 @@
+#!/bin/sh
+. "$(dirname "$0")/_/husky.sh"
+
+cd ui
+npx pretty-quick --staged --pattern "**/*.{js,ts,json,html,scss}"
diff --git a/ui/package.json b/ui/package.json
index 8fa7079b1c..10a26c3de0 100644
--- a/ui/package.json
+++ b/ui/package.json
@@ -12,7 +12,8 @@
"lint:fix": "ng lint --fix",
"e2e": "ng e2e",
"postinstall": "ngcc",
- "pretty": "prettier --write \"./src/**/*.{js,ts,json,html}\""
+ "pretty": "prettier --write \"./src/**/*.{js,ts,json,html,scss}\"",
+ "prepare": "cd .. && husky install ui/.husky"
},
"private": true,
"dependencies": {
@@ -64,6 +65,7 @@
"eslint-plugin-prefer-arrow": "1.2.2",
"express": "^4.16.4",
"formidable": "^1.2.1",
+ "husky": "^7.0.2",
"jasmine-core": "~3.6.0",
"jasmine-spec-reporter": "~5.0.0",
"karma": "~5.1.1",
@@ -74,6 +76,7 @@
"nodemod": "^2.2.3",
"prettier": "^2.1.1",
"prettier-eslint": "^12.0.0",
+ "pretty-quick": "^3.1.1",
"protractor": "~7.0.0",
"ts-node": "~7.0.0",
"typescript": "~4.0.3"
From 84949950473cc142c3cce315da3a0c1bb88352da Mon Sep 17 00:00:00 2001
From: Anton Seramiazhka <89514645+noirkalmaro@users.noreply.github.com>
Date: Fri, 10 Sep 2021 13:59:58 +0300
Subject: [PATCH 027/702] EFRS-1040: image collection for subject (#594)
* EFRS-1046 Image holder component
* EFRS-1040 Subject page refactoring
* Examples bulk delete functionality
covers EFRS-1046, EFRS-1047, EFRS-1040
Co-authored-by: Artsiom Bolhar
---
ui/src/app/app.component.scss | 1 -
.../app/core/collection/collection.helper.ts | 23 +++
.../app/core/collection/collection.service.ts | 34 ++++-
.../enums/circle-loading-progress.enum.ts | 22 +++
ui/src/app/data/enums/subject-mode.enum.ts | 21 +++
ui/src/app/data/interfaces/collection.ts | 17 +++
.../app/features/alert/alert.component.scss | 10 +-
.../app-change-photo.component.scss | 4 +-
.../app-search-table.component.scss | 6 +-
.../application-header.component.scss | 2 +-
.../breadcrumbs/breadcrumbs.component.scss | 2 +-
.../change-password-dialog.component.scss | 1 -
.../circle-loading-progress.component.html | 27 ++++
.../circle-loading-progress.component.scss | 61 ++++++++
.../circle-loading-progress.component.ts | 53 +++++++
.../circle-loading-progress.module.ts | 28 ++++
.../collection-left-facade.ts | 8 +-
...anager-subject-left.container.component.ts | 3 +-
...ection-manager-subject-left.component.html | 5 +-
...llection-manager-subject-left.component.ts | 4 +-
.../collection-manager-right-facade.ts | 70 ++++++++-
...nager-subject-right.container.component.ts | 87 ++++++++++-
...collection-manager-subject-right.module.ts | 4 +-
...ction-manager-subject-right.component.html | 116 +++++++++++---
...ction-manager-subject-right.component.scss | 47 ++++--
...lection-manager-subject-right.component.ts | 19 ++-
.../create-dialog.component.html | 2 +-
.../drag-n-drop/drag-n-drop.component.html | 4 +-
.../drag-n-drop/drag-n-drop.component.scss | 12 +-
.../drag-n-drop/drag-n-drop.component.ts | 34 ++---
.../face-picture/face-picture.component.scss | 4 +-
.../recognition-result.component.html | 2 +-
.../recognition-result.component.scss | 10 +-
.../verification-result.component.html | 4 +-
.../verification-result.component.scss | 8 +-
.../app/features/footer/footer.component.scss | 19 +--
.../image-holder-collection.component.html | 27 ++++
.../image-holder-collection.component.scss | 30 ++++
.../image-holder-collection.component.ts | 32 ++++
.../image-holder-collection.module.ts | 26 ++++
.../image-holder/image-holder.component.html | 27 ++++
.../image-holder/image-holder.component.scss | 71 +++++++++
.../image-holder/image-holder.component.ts | 49 ++++++
.../image-holder/image-holder.module.ts | 28 ++++
.../model-clone-dialog.component.scss | 2 -
.../model-list/model-list.component.scss | 1 -
.../model-table/model-table.component.scss | 7 +-
.../app/features/table/table.component.scss | 10 +-
.../features/tool-bar/tool-bar.component.scss | 8 +-
.../user-table/user-table.component.scss | 7 +-
.../application/application.component.scss | 6 +-
.../manage-collection.component.html | 2 -
.../manage-collection.component.scss | 11 ++
.../manage-collection.component.ts | 1 -
ui/src/app/store/app-user/effects.ts | 6 +-
ui/src/app/store/manage-collectiom/action.ts | 49 ++++++
ui/src/app/store/manage-collectiom/effects.ts | 144 +++++++++++++++++-
.../app/store/manage-collectiom/reducers.ts | 120 ++++++++++++++-
.../app/store/manage-collectiom/selectors.ts | 3 +
ui/src/app/store/model/selectors.ts | 6 +-
ui/src/assets/i18n/en.json | 11 +-
ui/src/styles.scss | 20 +--
ui/src/styles/_layout.scss | 2 +-
ui/src/styles/colors.scss | 32 ++--
ui/src/styles/custom-theme.scss | 119 ++++++++-------
ui/src/styles/forms.scss | 1 -
ui/src/styles/media.scss | 15 +-
ui/src/styles/mixins.scss | 5 -
68 files changed, 1386 insertions(+), 266 deletions(-)
create mode 100644 ui/src/app/core/collection/collection.helper.ts
create mode 100644 ui/src/app/data/enums/circle-loading-progress.enum.ts
create mode 100644 ui/src/app/data/enums/subject-mode.enum.ts
create mode 100644 ui/src/app/features/circle-loading-progress/circle-loading-progress.component.html
create mode 100644 ui/src/app/features/circle-loading-progress/circle-loading-progress.component.scss
create mode 100644 ui/src/app/features/circle-loading-progress/circle-loading-progress.component.ts
create mode 100644 ui/src/app/features/circle-loading-progress/circle-loading-progress.module.ts
create mode 100644 ui/src/app/features/image-holder-collection/image-holder-collection.component.html
create mode 100644 ui/src/app/features/image-holder-collection/image-holder-collection.component.scss
create mode 100644 ui/src/app/features/image-holder-collection/image-holder-collection.component.ts
create mode 100644 ui/src/app/features/image-holder-collection/image-holder-collection.module.ts
create mode 100644 ui/src/app/features/image-holder/image-holder.component.html
create mode 100644 ui/src/app/features/image-holder/image-holder.component.scss
create mode 100644 ui/src/app/features/image-holder/image-holder.component.ts
create mode 100644 ui/src/app/features/image-holder/image-holder.module.ts
diff --git a/ui/src/app/app.component.scss b/ui/src/app/app.component.scss
index 438348484a..249c031f13 100644
--- a/ui/src/app/app.component.scss
+++ b/ui/src/app/app.component.scss
@@ -13,4 +13,3 @@
* or implied. See the License for the specific language governing
* permissions and limitations under the License.
*/
-
diff --git a/ui/src/app/core/collection/collection.helper.ts b/ui/src/app/core/collection/collection.helper.ts
new file mode 100644
index 0000000000..bc8695bf4c
--- /dev/null
+++ b/ui/src/app/core/collection/collection.helper.ts
@@ -0,0 +1,23 @@
+/*
+ * Copyright (c) 2020 the original author or authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ * or implied. See the License for the specific language governing
+ * permissions and limitations under the License.
+ */
+
+import { environment } from 'src/environments/environment';
+
+export class CollectionHelper {
+ static getCollectionItemUrl(apiKey: string, imageId: string): string {
+ return `${environment.userApiUrl}static/${apiKey}/images/${imageId}`;
+ }
+}
diff --git a/ui/src/app/core/collection/collection.service.ts b/ui/src/app/core/collection/collection.service.ts
index d639420f94..fd17d6e45c 100644
--- a/ui/src/app/core/collection/collection.service.ts
+++ b/ui/src/app/core/collection/collection.service.ts
@@ -15,9 +15,11 @@
*/
import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
-import { Observable } from 'rxjs';
+import { Observable, of } from 'rxjs';
import { environment } from '../../../environments/environment';
+import { CollectionItem, SubjectExampleResponseItem } from 'src/app/data/interfaces/collection';
+import { map } from 'rxjs/operators';
@Injectable({
providedIn: 'root',
@@ -52,4 +54,34 @@ export class CollectionService {
headers: { 'x-api-key': apiKey },
});
}
+
+ getSubjectExampleList(apiKey: string): Observable {
+ return this.http
+ .get(`${environment.userApiUrl}recognition/faces?size=1000`, {
+ headers: { 'x-api-key': apiKey },
+ })
+ .pipe(map((resp: { faces: SubjectExampleResponseItem[] }) => resp.faces));
+ }
+
+ uploadSubjectExamples(item: CollectionItem, subject: string, apiKey: string): Observable {
+ const { file } = item;
+ const formData = new FormData();
+ formData.append('file', file, file.name);
+
+ return this.http.post(`${environment.userApiUrl}recognition/faces?subject=${subject}`, formData, {
+ headers: { 'x-api-key': apiKey },
+ });
+ }
+
+ deleteSubjectExample(item: CollectionItem, apiKey: string): Observable {
+ return this.http.delete(`${environment.userApiUrl}recognition/faces/${item.id}`, {
+ headers: { 'x-api-key': apiKey },
+ });
+ }
+
+ deleteSubjectExamplesBulk(ids: string[], apiKey: string): Observable {
+ return this.http.post(`${environment.userApiUrl}recognition/faces/delete`, ids, {
+ headers: { 'x-api-key': apiKey },
+ });
+ }
}
diff --git a/ui/src/app/data/enums/circle-loading-progress.enum.ts b/ui/src/app/data/enums/circle-loading-progress.enum.ts
new file mode 100644
index 0000000000..f7e8664041
--- /dev/null
+++ b/ui/src/app/data/enums/circle-loading-progress.enum.ts
@@ -0,0 +1,22 @@
+/*
+ * Copyright (c) 2020 the original author or authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ * or implied. See the License for the specific language governing
+ * permissions and limitations under the License.
+ */
+
+export enum CircleLoadingProgressEnum {
+ Uploaded = 'uploaded',
+ Failed = 'failed',
+ InProgress = 'inprogress',
+ OnHold = 'onhold',
+}
diff --git a/ui/src/app/data/enums/subject-mode.enum.ts b/ui/src/app/data/enums/subject-mode.enum.ts
new file mode 100644
index 0000000000..06fa4cfca1
--- /dev/null
+++ b/ui/src/app/data/enums/subject-mode.enum.ts
@@ -0,0 +1,21 @@
+/*
+ * Copyright (c) 2020 the original author or authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ * or implied. See the License for the specific language governing
+ * permissions and limitations under the License.
+ */
+
+export enum SubjectModeEnum {
+ Default = 'default',
+ BulkSelect = 'bulk_select',
+ }
+
\ No newline at end of file
diff --git a/ui/src/app/data/interfaces/collection.ts b/ui/src/app/data/interfaces/collection.ts
index 9b67594297..24b1e655a0 100644
--- a/ui/src/app/data/interfaces/collection.ts
+++ b/ui/src/app/data/interfaces/collection.ts
@@ -13,6 +13,23 @@
* or implied. See the License for the specific language governing
* permissions and limitations under the License.
*/
+import { CircleLoadingProgressEnum } from '../enums/circle-loading-progress.enum';
+
export interface Collection {
subjects: string[];
}
+
+export interface CollectionItem {
+ url: string;
+ id?: string;
+ subject: string;
+ status: CircleLoadingProgressEnum;
+ file?: File;
+ error?: string;
+ isSelected?: boolean;
+}
+
+export interface SubjectExampleResponseItem {
+ subject: string;
+ image_id: string;
+}
diff --git a/ui/src/app/features/alert/alert.component.scss b/ui/src/app/features/alert/alert.component.scss
index 0a1bfa9abd..1b3588a94e 100644
--- a/ui/src/app/features/alert/alert.component.scss
+++ b/ui/src/app/features/alert/alert.component.scss
@@ -16,19 +16,20 @@
@import 'colors.scss';
-.mat-dialog-title, .mat-dialog-content {
+.mat-dialog-title,
+.mat-dialog-content {
text-align: center;
}
.mat-dialog-actions {
display: flex;
- flex-direction: column
+ flex-direction: column;
}
.alert {
&-info {
background-color: $green;
- color: $white
+ color: $white;
}
&-warning {
@@ -37,11 +38,10 @@
&-error {
background-color: $red;
- color: $white
+ color: $white;
}
}
button {
color: $white;
}
-
diff --git a/ui/src/app/features/app-change-photo/app-change-photo.component.scss b/ui/src/app/features/app-change-photo/app-change-photo.component.scss
index 0bb019e06b..5c0928f9a6 100644
--- a/ui/src/app/features/app-change-photo/app-change-photo.component.scss
+++ b/ui/src/app/features/app-change-photo/app-change-photo.component.scss
@@ -13,8 +13,8 @@
* or implied. See the License for the specific language governing
* permissions and limitations under the License.
*/
-@import "mixins.scss";
-@import "colors.scss";
+@import 'mixins.scss';
+@import 'colors.scss';
.change-photo {
position: absolute;
diff --git a/ui/src/app/features/app-search-table/app-search-table.component.scss b/ui/src/app/features/app-search-table/app-search-table.component.scss
index d3ac9a0dd2..e7c727da9d 100644
--- a/ui/src/app/features/app-search-table/app-search-table.component.scss
+++ b/ui/src/app/features/app-search-table/app-search-table.component.scss
@@ -14,9 +14,9 @@
* permissions and limitations under the License.
*/
-@import "colors.scss";
-@import "mixins.scss";
-@import "media.scss";
+@import 'colors.scss';
+@import 'mixins.scss';
+@import 'media.scss';
.app-search {
margin-bottom: 20px;
diff --git a/ui/src/app/features/application-header/application-header/application-header.component.scss b/ui/src/app/features/application-header/application-header/application-header.component.scss
index c899e61203..6194595e69 100644
--- a/ui/src/app/features/application-header/application-header/application-header.component.scss
+++ b/ui/src/app/features/application-header/application-header/application-header.component.scss
@@ -13,7 +13,7 @@
* or implied. See the License for the specific language governing
* permissions and limitations under the License.
*/
-@import "mixins.scss";
+@import 'mixins.scss';
.app-header {
margin-bottom: 24px;
diff --git a/ui/src/app/features/breadcrumbs/breadcrumbs.component.scss b/ui/src/app/features/breadcrumbs/breadcrumbs.component.scss
index 842cbbad37..e8fb9de35b 100644
--- a/ui/src/app/features/breadcrumbs/breadcrumbs.component.scss
+++ b/ui/src/app/features/breadcrumbs/breadcrumbs.component.scss
@@ -14,7 +14,7 @@
* permissions and limitations under the License.
*/
-@import "mixins.scss";
+@import 'mixins.scss';
.breadcrumbs {
margin-bottom: 24px;
diff --git a/ui/src/app/features/change-password-dialog/change-password-dialog.component.scss b/ui/src/app/features/change-password-dialog/change-password-dialog.component.scss
index 438348484a..249c031f13 100644
--- a/ui/src/app/features/change-password-dialog/change-password-dialog.component.scss
+++ b/ui/src/app/features/change-password-dialog/change-password-dialog.component.scss
@@ -13,4 +13,3 @@
* or implied. See the License for the specific language governing
* permissions and limitations under the License.
*/
-
diff --git a/ui/src/app/features/circle-loading-progress/circle-loading-progress.component.html b/ui/src/app/features/circle-loading-progress/circle-loading-progress.component.html
new file mode 100644
index 0000000000..2c48f90b05
--- /dev/null
+++ b/ui/src/app/features/circle-loading-progress/circle-loading-progress.component.html
@@ -0,0 +1,27 @@
+
+
+
+ highlight_off
+
+
+ warning_amber
+
+
+
+
+
+
diff --git a/ui/src/app/features/circle-loading-progress/circle-loading-progress.component.scss b/ui/src/app/features/circle-loading-progress/circle-loading-progress.component.scss
new file mode 100644
index 0000000000..3236727f12
--- /dev/null
+++ b/ui/src/app/features/circle-loading-progress/circle-loading-progress.component.scss
@@ -0,0 +1,61 @@
+/*!
+ * Copyright (c) 2020 the original author or authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ * or implied. See the License for the specific language governing
+ * permissions and limitations under the License.
+ */
+$attention-icon-size: 6rem;
+
+:host {
+ position: absolute;
+ width: 100%;
+ height: 100%;
+ left: 0;
+ top: 0;
+ display: flex;
+ flex-direction: row-reverse;
+ align-items: center;
+ justify-content: center;
+
+ &.blurred {
+ backdrop-filter: blur(20px) brightness(0.6);
+ }
+
+ .image-load-failed,
+ .image-load-onhold,
+ .image-load-in-progress {
+ width: $attention-icon-size;
+
+ mat-icon {
+ color: white;
+ opacity: 0.7;
+ font-size: $attention-icon-size;
+ }
+ }
+
+ .image-load-onhold {
+ cursor: pointer;
+ }
+
+ .image-load-in-progress {
+ display: flex;
+ flex-direction: row-reverse;
+ align-items: center;
+ justify-content: center;
+ }
+
+ ::ng-deep .mat-progress-spinner circle,
+ .mat-spinner circle {
+ stroke: white;
+ opacity: 0.7;
+ }
+}
diff --git a/ui/src/app/features/circle-loading-progress/circle-loading-progress.component.ts b/ui/src/app/features/circle-loading-progress/circle-loading-progress.component.ts
new file mode 100644
index 0000000000..dd56f7cd04
--- /dev/null
+++ b/ui/src/app/features/circle-loading-progress/circle-loading-progress.component.ts
@@ -0,0 +1,53 @@
+/*
+ * Copyright (c) 2020 the original author or authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ * or implied. See the License for the specific language governing
+ * permissions and limitations under the License.
+ */
+import {
+ ChangeDetectionStrategy,
+ Component,
+ EventEmitter,
+ HostBinding,
+ Input,
+ OnInit,
+ Output,
+ OnChanges,
+ SimpleChanges,
+} from '@angular/core';
+import { CircleLoadingProgressEnum } from 'src/app/data/enums/circle-loading-progress.enum';
+
+@Component({
+ selector: 'circle-progress',
+ templateUrl: './circle-loading-progress.component.html',
+ styleUrls: ['./circle-loading-progress.component.scss'],
+ changeDetection: ChangeDetectionStrategy.OnPush,
+})
+export class CircleLoadingProgressComponent {
+ @Input() state = CircleLoadingProgressEnum.InProgress;
+ @Input() error: string;
+
+ @Output() cancel = new EventEmitter();
+
+ inProgressDiameter = 80;
+ inProgressStrokeWidth = 4;
+
+ progressEnum = CircleLoadingProgressEnum;
+
+ @HostBinding('class.blurred') isBlurred: boolean = false;
+
+ ngOnChanges(changes: SimpleChanges): void {
+ if (changes.state?.currentValue) {
+ this.isBlurred = this.state !== CircleLoadingProgressEnum.Uploaded;
+ }
+ }
+}
diff --git a/ui/src/app/features/circle-loading-progress/circle-loading-progress.module.ts b/ui/src/app/features/circle-loading-progress/circle-loading-progress.module.ts
new file mode 100644
index 0000000000..98e90a93c8
--- /dev/null
+++ b/ui/src/app/features/circle-loading-progress/circle-loading-progress.module.ts
@@ -0,0 +1,28 @@
+/*!
+ * Copyright (c) 2020 the original author or authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ * or implied. See the License for the specific language governing
+ * permissions and limitations under the License.
+ */
+import { NgModule } from '@angular/core';
+import { CommonModule } from '@angular/common';
+import { CircleLoadingProgressComponent } from './circle-loading-progress.component';
+import { MatIconModule } from '@angular/material/icon';
+import { MatProgressSpinnerModule } from '@angular/material/progress-spinner';
+import { MatTooltipModule } from '@angular/material/tooltip';
+
+@NgModule({
+ declarations: [CircleLoadingProgressComponent],
+ exports: [CircleLoadingProgressComponent],
+ imports: [CommonModule, MatIconModule, MatProgressSpinnerModule, MatTooltipModule],
+})
+export class CircleLoadingProgressModule {}
diff --git a/ui/src/app/features/collection-manager-subject-left/collection-left-facade.ts b/ui/src/app/features/collection-manager-subject-left/collection-left-facade.ts
index cd56f73308..c43ee7727f 100644
--- a/ui/src/app/features/collection-manager-subject-left/collection-left-facade.ts
+++ b/ui/src/app/features/collection-manager-subject-left/collection-left-facade.ts
@@ -18,12 +18,8 @@ import { Store } from '@ngrx/store';
import { Observable } from 'rxjs';
import { addSubject, loadSubjects, setSelectedSubject } from '../../store/manage-collectiom/action';
-import {
- selectAddSubjectPending,
- selectCollectionSubject,
- selectCollectionSubjects,
-} from '../../store/manage-collectiom/selectors';
-import { selectCurrentApiKey } from "../../store/model/selectors";
+import { selectAddSubjectPending, selectCollectionSubject, selectCollectionSubjects } from '../../store/manage-collectiom/selectors';
+import { selectCurrentApiKey } from '../../store/model/selectors';
@Injectable()
export class CollectionLeftFacade {
diff --git a/ui/src/app/features/collection-manager-subject-left/collection-manager-subject-left.container.component.ts b/ui/src/app/features/collection-manager-subject-left/collection-manager-subject-left.container.component.ts
index bd8b83b813..27ca8e9dcf 100644
--- a/ui/src/app/features/collection-manager-subject-left/collection-manager-subject-left.container.component.ts
+++ b/ui/src/app/features/collection-manager-subject-left/collection-manager-subject-left.container.component.ts
@@ -42,8 +42,7 @@ export class CollectionManagerSubjectLeftContainerComponent implements OnInit {
private apiKey: string;
- constructor(private collectionLeftFacade: CollectionLeftFacade, private translate: TranslateService, private dialog: MatDialog) {
- }
+ constructor(private collectionLeftFacade: CollectionLeftFacade, private translate: TranslateService, private dialog: MatDialog) {}
ngOnInit(): void {
this.currentSubject$ = this.collectionLeftFacade.currentSubject$;
diff --git a/ui/src/app/features/collection-manager-subject-left/collection-manager-subject-left/collection-manager-subject-left.component.html b/ui/src/app/features/collection-manager-subject-left/collection-manager-subject-left/collection-manager-subject-left.component.html
index f3da175d8d..96d61da9e8 100644
--- a/ui/src/app/features/collection-manager-subject-left/collection-manager-subject-left/collection-manager-subject-left.component.html
+++ b/ui/src/app/features/collection-manager-subject-left/collection-manager-subject-left/collection-manager-subject-left.component.html
@@ -20,10 +20,7 @@
diff --git a/ui/src/app/features/image-holder-collection/image-holder-collection.component.scss b/ui/src/app/features/image-holder-collection/image-holder-collection.component.scss
new file mode 100644
index 0000000000..fd2280b910
--- /dev/null
+++ b/ui/src/app/features/image-holder-collection/image-holder-collection.component.scss
@@ -0,0 +1,30 @@
+/*!
+ * Copyright (c) 2020 the original author or authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ * or implied. See the License for the specific language governing
+ * permissions and limitations under the License.
+ */
+ @import 'media.scss';
+
+.image-holder-collection {
+ display: grid;
+ grid-template-columns: 1fr 1fr 1fr 1fr;
+ grid-gap: 1.5rem;
+
+ @include tablet {
+ grid-template-columns: 1fr 1fr 1fr;
+ }
+
+ @include mobile {
+ grid-template-columns: 1fr 1fr;
+ }
+}
diff --git a/ui/src/app/features/image-holder-collection/image-holder-collection.component.ts b/ui/src/app/features/image-holder-collection/image-holder-collection.component.ts
new file mode 100644
index 0000000000..ecfc5cd15b
--- /dev/null
+++ b/ui/src/app/features/image-holder-collection/image-holder-collection.component.ts
@@ -0,0 +1,32 @@
+/*
+ * Copyright (c) 2020 the original author or authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ * or implied. See the License for the specific language governing
+ * permissions and limitations under the License.
+ */
+import { ChangeDetectionStrategy, Component, EventEmitter, Input, Output } from '@angular/core';
+import { CollectionItem } from 'src/app/data/interfaces/collection';
+
+@Component({
+ selector: 'image-holder-collection',
+ templateUrl: './image-holder-collection.component.html',
+ styleUrls: ['./image-holder-collection.component.scss'],
+ changeDetection: ChangeDetectionStrategy.OnPush,
+})
+export class ImageHolderCollectionComponent {
+ @Input() items: CollectionItem[] = [];
+ @Input() selectionMode: boolean = false;
+
+ @Output() deleteItem = new EventEmitter();
+ @Output() cancelUploadItem = new EventEmitter();
+ @Output() selectItem = new EventEmitter();
+}
diff --git a/ui/src/app/features/image-holder-collection/image-holder-collection.module.ts b/ui/src/app/features/image-holder-collection/image-holder-collection.module.ts
new file mode 100644
index 0000000000..c5688af414
--- /dev/null
+++ b/ui/src/app/features/image-holder-collection/image-holder-collection.module.ts
@@ -0,0 +1,26 @@
+/*!
+ * Copyright (c) 2020 the original author or authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ * or implied. See the License for the specific language governing
+ * permissions and limitations under the License.
+ */
+import { NgModule } from '@angular/core';
+import { CommonModule } from '@angular/common';
+import { ImageHolderModule } from '../image-holder/image-holder.module';
+import { ImageHolderCollectionComponent } from './image-holder-collection.component';
+
+@NgModule({
+ declarations: [ImageHolderCollectionComponent],
+ exports: [ImageHolderCollectionComponent],
+ imports: [CommonModule, ImageHolderModule],
+})
+export class ImageHolderCollectionModule {}
diff --git a/ui/src/app/features/image-holder/image-holder.component.html b/ui/src/app/features/image-holder/image-holder.component.html
new file mode 100644
index 0000000000..3dde97731c
--- /dev/null
+++ b/ui/src/app/features/image-holder/image-holder.component.html
@@ -0,0 +1,27 @@
+
+
+
+
+
+
+
+
+ check_circle_outline
+
+
+
+
diff --git a/ui/src/app/features/image-holder/image-holder.component.scss b/ui/src/app/features/image-holder/image-holder.component.scss
new file mode 100644
index 0000000000..325b646a94
--- /dev/null
+++ b/ui/src/app/features/image-holder/image-holder.component.scss
@@ -0,0 +1,71 @@
+/*!
+ * Copyright (c) 2020 the original author or authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ * or implied. See the License for the specific language governing
+ * permissions and limitations under the License.
+ */
+@import 'mixins.scss';
+
+.image-holder {
+ position: relative;
+ min-width: 5rem;
+ max-width: 13rem;
+
+ .delete-icon {
+ position: absolute;
+ top: -1rem;
+ right: -1rem;
+ z-index: 1;
+
+ button {
+ height: 2rem;
+ width: 2rem;
+
+ .mat-button-wrapper {
+ padding: 0.5rem 0;
+ }
+
+ ::ng-deep .mat-button-wrapper {
+ padding: 0;
+
+ mat-icon {
+ font-size: 1.2rem;
+ }
+ }
+ }
+ }
+
+ img {
+ display: block;
+ width: 100%;
+ }
+
+ &_selection-mode {
+ cursor: pointer;
+ }
+}
+
+.selection-overlay {
+ @include wrapper-center();
+ position: absolute;
+ left: 0;
+ right: 0;
+ top: 0;
+ bottom: 0;
+ backdrop-filter: blur(20px) brightness(0.6);
+}
+
+.selection-icon {
+ @include xl-icon-size(6rem);
+ color: #fff;
+ opacity: 0.7;
+}
diff --git a/ui/src/app/features/image-holder/image-holder.component.ts b/ui/src/app/features/image-holder/image-holder.component.ts
new file mode 100644
index 0000000000..5fd419e124
--- /dev/null
+++ b/ui/src/app/features/image-holder/image-holder.component.ts
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 2020 the original author or authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ * or implied. See the License for the specific language governing
+ * permissions and limitations under the License.
+ */
+import { EventEmitter, SimpleChanges } from '@angular/core';
+import { Component, Input, Output, ChangeDetectionStrategy, OnChanges } from '@angular/core';
+import { CircleLoadingProgressEnum } from 'src/app/data/enums/circle-loading-progress.enum';
+import { CollectionItem } from 'src/app/data/interfaces/collection';
+
+@Component({
+ selector: 'image-holder',
+ templateUrl: './image-holder.component.html',
+ styleUrls: ['./image-holder.component.scss'],
+ changeDetection: ChangeDetectionStrategy.OnPush,
+})
+export class ImageHolderComponent implements OnChanges {
+ @Input() item: CollectionItem;
+ @Input() selectionMode: CollectionItem;
+
+ isDeleteVisible: boolean;
+
+ @Output() onDelete = new EventEmitter();
+ @Output() onCancel = new EventEmitter();
+ @Output() onSelect = new EventEmitter();
+
+ ngOnChanges(changes: SimpleChanges): void {
+ if (changes.item?.currentValue) {
+ this.isDeleteVisible =
+ this.item.status === CircleLoadingProgressEnum.Uploaded || this.item.status === CircleLoadingProgressEnum.Failed;
+ }
+ }
+
+ onItemClick() {
+ if (this.selectionMode) {
+ this.onSelect.emit(this.item);
+ }
+ }
+}
diff --git a/ui/src/app/features/image-holder/image-holder.module.ts b/ui/src/app/features/image-holder/image-holder.module.ts
new file mode 100644
index 0000000000..647c6f0f2b
--- /dev/null
+++ b/ui/src/app/features/image-holder/image-holder.module.ts
@@ -0,0 +1,28 @@
+/*!
+ * Copyright (c) 2020 the original author or authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ * or implied. See the License for the specific language governing
+ * permissions and limitations under the License.
+ */
+import { NgModule } from '@angular/core';
+import { CommonModule } from '@angular/common';
+import { ImageHolderComponent } from './image-holder.component';
+import { CircleLoadingProgressModule } from '../circle-loading-progress/circle-loading-progress.module';
+import { MatIconModule } from '@angular/material/icon';
+import { MatButtonModule } from '@angular/material/button';
+
+@NgModule({
+ declarations: [ImageHolderComponent],
+ exports: [ImageHolderComponent],
+ imports: [CommonModule, MatIconModule, MatButtonModule, CircleLoadingProgressModule],
+})
+export class ImageHolderModule {}
diff --git a/ui/src/app/features/model-clone-dialog/model-clone-dialog.component.scss b/ui/src/app/features/model-clone-dialog/model-clone-dialog.component.scss
index 4ecec5854b..249c031f13 100644
--- a/ui/src/app/features/model-clone-dialog/model-clone-dialog.component.scss
+++ b/ui/src/app/features/model-clone-dialog/model-clone-dialog.component.scss
@@ -13,5 +13,3 @@
* or implied. See the License for the specific language governing
* permissions and limitations under the License.
*/
-
-
diff --git a/ui/src/app/features/model-list/model-list.component.scss b/ui/src/app/features/model-list/model-list.component.scss
index f89bb7aa18..d19e398db0 100644
--- a/ui/src/app/features/model-list/model-list.component.scss
+++ b/ui/src/app/features/model-list/model-list.component.scss
@@ -15,7 +15,6 @@
*/
.model-list {
-
app-spinner {
position: absolute;
left: 40%;
diff --git a/ui/src/app/features/model-table/model-table.component.scss b/ui/src/app/features/model-table/model-table.component.scss
index bbaf9bfd99..c26f2afaab 100644
--- a/ui/src/app/features/model-table/model-table.component.scss
+++ b/ui/src/app/features/model-table/model-table.component.scss
@@ -14,17 +14,15 @@
* permissions and limitations under the License.
*/
-@import "colors.scss";
-@import "mixins.scss";
+@import 'colors.scss';
+@import 'mixins.scss';
caption {
display: none;
}
.mat {
-
&-column {
-
&-content {
width: 100%;
@@ -82,7 +80,6 @@ caption {
width: 58px;
.test {
-
&--btn {
min-width: 0;
font-size: 12px;
diff --git a/ui/src/app/features/table/table.component.scss b/ui/src/app/features/table/table.component.scss
index 0756d5bb3a..b7ff7b4d30 100644
--- a/ui/src/app/features/table/table.component.scss
+++ b/ui/src/app/features/table/table.component.scss
@@ -13,27 +13,25 @@
* or implied. See the License for the specific language governing
* permissions and limitations under the License.
*/
-@import "colors.scss";
-@import "mixins.scss";
+@import 'colors.scss';
+@import 'mixins.scss';
-thead, caption {
+thead,
+caption {
display: none;
}
.mat {
-
&-header-row {
display: none;
}
&-column {
-
&-name {
overflow: hidden;
cursor: pointer;
.app {
-
&--title {
@include not-break-string();
}
diff --git a/ui/src/app/features/tool-bar/tool-bar.component.scss b/ui/src/app/features/tool-bar/tool-bar.component.scss
index feb7970100..a521f71d0a 100644
--- a/ui/src/app/features/tool-bar/tool-bar.component.scss
+++ b/ui/src/app/features/tool-bar/tool-bar.component.scss
@@ -14,8 +14,8 @@
* permissions and limitations under the License.
*/
-@import "media.scss";
-@import "mixins.scss";
+@import 'media.scss';
+@import 'mixins.scss';
.header {
height: 100%;
@@ -28,12 +28,11 @@
&--logo {
display: flex;
- &_link{
+ &_link {
padding: 4px;
min-width: 0;
.logo-main {
-
@include mobile-portrait {
display: none;
}
@@ -51,7 +50,6 @@
}
&--user-info {
-
.user-info--btn {
padding: 6px;
line-height: 24px;
diff --git a/ui/src/app/features/user-table/user-table.component.scss b/ui/src/app/features/user-table/user-table.component.scss
index 2ff3fe319b..6e5b6aa185 100644
--- a/ui/src/app/features/user-table/user-table.component.scss
+++ b/ui/src/app/features/user-table/user-table.component.scss
@@ -13,26 +13,23 @@
* or implied. See the License for the specific language governing
* permissions and limitations under the License.
*/
-@import "colors.scss";
-@import "mixins.scss";
+@import 'colors.scss';
+@import 'mixins.scss';
caption {
display: none;
}
.mat {
-
&-header-row {
display: none;
}
&-column {
-
&-user {
width: 100%;
.user {
-
&--name {
@include not-break-string();
}
diff --git a/ui/src/app/pages/application/application.component.scss b/ui/src/app/pages/application/application.component.scss
index 5def3106cc..86b27b84c3 100644
--- a/ui/src/app/pages/application/application.component.scss
+++ b/ui/src/app/pages/application/application.component.scss
@@ -38,7 +38,7 @@
flex-direction: column;
}
- @include mobile{
+ @include mobile {
flex-direction: column;
}
@@ -62,7 +62,7 @@
}
}
- @include tablet-portrait{
+ @include tablet-portrait {
width: 99%;
&:not(:last-child) {
@@ -80,7 +80,7 @@
}
@include tablet-landscape {
- .grid-container{
+ .grid-container {
flex-direction: column !important;
mat-card {
diff --git a/ui/src/app/pages/manage-collection/manage-collection.component.html b/ui/src/app/pages/manage-collection/manage-collection.component.html
index 22fb78af98..2051f17881 100644
--- a/ui/src/app/pages/manage-collection/manage-collection.component.html
+++ b/ui/src/app/pages/manage-collection/manage-collection.component.html
@@ -14,7 +14,6 @@
~ permissions and limitations under the License.
-->
CompreFace is a free and open-source face recognition system from Exadel
+
Exadel CompreFace is a leading free and open-source face recognition system
- CompreFace can be easily integrated into any system without prior machine learning skills. CompreFace provides REST API for face
-recognition, face verification, face detection, face mask detection, landmark detection, age, and gender recognition and is easily deployed with
-docker
+ Exadel CompreFace is a free and open-source face recognition service that can be easily integrated into any system without prior machine learning skills.
+ CompreFace provides REST API for face recognition, face verification, face detection, landmark detection, age, and gender recognition and is easily deployed with docker.
@@ -66,14 +65,15 @@ docker
# Overview
-CompreFace is a free and open-source face detection and recognition GitHub project.
+Exadel CompreFace is a free and open-source face recognition GitHub project.
Essentially, it is a docker-based application that can be used as a standalone server or deployed in the cloud.
You don’t need prior machine learning skills to set up and use CompreFace.
-CompreFace provides REST API for face recognition, face verification, face detection, face mask detection, landmark detection, age, and gender recognition.
+The system provides REST API for face recognition, face verification, face detection, landmark detection, age, and gender recognition.
The solution also features a role management system that allows you to easily control who has access to your Face Recognition Services.
-CompreFace is delivered as a docker-compose config and supports different models that work on CPU and GPU. Our solution is based on state-of-the-art methods and libraries like FaceNet and InsightFace.
+CompreFace is delivered as a docker-compose config and supports different models that work on CPU and GPU.
+Our solution is based on state-of-the-art methods and libraries like FaceNet and InsightFace.
# Screenshots
diff --git a/docs/Rest-API-description.md b/docs/Rest-API-description.md
index 2955938347..eaad0e3aa1 100644
--- a/docs/Rest-API-description.md
+++ b/docs/Rest-API-description.md
@@ -289,7 +289,7 @@ Response body on success:
| Element | Type | Description |
| -------- | ------- | ------------------------ |
-| count | integer | Number of deleted faces |
+| deleted | integer | Number of deleted faces |
From 8a0fc596f4407c28f8f9b8c1a0e549bb01396e29 Mon Sep 17 00:00:00 2001
From: Kurnosau Ivan
Date: Tue, 28 Sep 2021 09:13:35 +0300
Subject: [PATCH 029/702] Masked face recognition models for MXNet and Facenet
---
.../src/services/facescan/plugins/facenet/facenet.py | 2 ++
.../src/services/facescan/plugins/insightface/insightface.py | 2 ++
2 files changed, 4 insertions(+)
diff --git a/embedding-calculator/src/services/facescan/plugins/facenet/facenet.py b/embedding-calculator/src/services/facescan/plugins/facenet/facenet.py
index 2c4ca1b447..db4d998edd 100644
--- a/embedding-calculator/src/services/facescan/plugins/facenet/facenet.py
+++ b/embedding-calculator/src/services/facescan/plugins/facenet/facenet.py
@@ -119,6 +119,8 @@ class Calculator(mixins.CalculatorMixin, base.BasePlugin):
('20180402-114759', '1im5Qq006ZEV_tViKh3cgia_Q4jJ13bRK', (1.1817961, 5.291995557), 0.4),
# CASIA-WebFace training set, 0.9905 LFW accuracy
('20180408-102900', '100w4JIUz44Tkwte9F-wEH0DOFsY-bPaw', (1.1362496, 5.803152427), 0.4),
+ # CASIA-WebFace-Masked, 0.9873 LFW, 0.9667 LFW-Masked (orig model has 0.9350 on LFW-Masked)
+ ('inception_resnetv1_casia_masked', '1FddVjS3JbtUOjgO0kWs43CAh0nJH2RrG', (1.1362496, 5.803152427), 0.6)
)
BATCH_SIZE = 25
diff --git a/embedding-calculator/src/services/facescan/plugins/insightface/insightface.py b/embedding-calculator/src/services/facescan/plugins/insightface/insightface.py
index 2d6359e7a3..bcbaed7e3e 100644
--- a/embedding-calculator/src/services/facescan/plugins/insightface/insightface.py
+++ b/embedding-calculator/src/services/facescan/plugins/insightface/insightface.py
@@ -111,6 +111,8 @@ class Calculator(InsightFaceMixin, mixins.CalculatorMixin, base.BasePlugin):
('arcface_resnet50', '1a9nib4I9OIVORwsqLB0gz0WuLC32E8gf', (1.2375747, 5.973354538), 400),
('arcface-r50-msfdrop75', '1gNuvRNHCNgvFtz7SjhW82v2-znlAYaRO', (1.2350148, 7.071431642), 400),
('arcface-r100-msfdrop75', '1lAnFcBXoMKqE-SkZKTmi6MsYAmzG0tFw', (1.224676, 6.322647217), 400),
+ # CASIA-WebFace-Masked, 0.9840 LFW, 0.9667 LFW-Masked (orig mobilefacenet has 0.9482 on LFW-Masked)
+ ('arcface_mobilefacenet_casia_masked', '1ltcJChTdP1yQWF9e1ESpTNYAVwxLSNLP', (1.26538905, 5.552089201), 200),
)
def calc_embedding(self, face_img: Array3D) -> Array3D:
From d4709136b048eeb1c7a0ff4bd7f158c699cc566b Mon Sep 17 00:00:00 2001
From: Kurnosau Ivan
Date: Tue, 28 Sep 2021 10:01:15 +0300
Subject: [PATCH 030/702] Masked face recognition models for MXNet and Facenet
fix commiter address
---
.../src/services/facescan/plugins/facenet/facenet.py | 2 ++
.../src/services/facescan/plugins/insightface/insightface.py | 2 ++
2 files changed, 4 insertions(+)
diff --git a/embedding-calculator/src/services/facescan/plugins/facenet/facenet.py b/embedding-calculator/src/services/facescan/plugins/facenet/facenet.py
index 2c4ca1b447..db4d998edd 100644
--- a/embedding-calculator/src/services/facescan/plugins/facenet/facenet.py
+++ b/embedding-calculator/src/services/facescan/plugins/facenet/facenet.py
@@ -119,6 +119,8 @@ class Calculator(mixins.CalculatorMixin, base.BasePlugin):
('20180402-114759', '1im5Qq006ZEV_tViKh3cgia_Q4jJ13bRK', (1.1817961, 5.291995557), 0.4),
# CASIA-WebFace training set, 0.9905 LFW accuracy
('20180408-102900', '100w4JIUz44Tkwte9F-wEH0DOFsY-bPaw', (1.1362496, 5.803152427), 0.4),
+ # CASIA-WebFace-Masked, 0.9873 LFW, 0.9667 LFW-Masked (orig model has 0.9350 on LFW-Masked)
+ ('inception_resnetv1_casia_masked', '1FddVjS3JbtUOjgO0kWs43CAh0nJH2RrG', (1.1362496, 5.803152427), 0.6)
)
BATCH_SIZE = 25
diff --git a/embedding-calculator/src/services/facescan/plugins/insightface/insightface.py b/embedding-calculator/src/services/facescan/plugins/insightface/insightface.py
index 2d6359e7a3..bcbaed7e3e 100644
--- a/embedding-calculator/src/services/facescan/plugins/insightface/insightface.py
+++ b/embedding-calculator/src/services/facescan/plugins/insightface/insightface.py
@@ -111,6 +111,8 @@ class Calculator(InsightFaceMixin, mixins.CalculatorMixin, base.BasePlugin):
('arcface_resnet50', '1a9nib4I9OIVORwsqLB0gz0WuLC32E8gf', (1.2375747, 5.973354538), 400),
('arcface-r50-msfdrop75', '1gNuvRNHCNgvFtz7SjhW82v2-znlAYaRO', (1.2350148, 7.071431642), 400),
('arcface-r100-msfdrop75', '1lAnFcBXoMKqE-SkZKTmi6MsYAmzG0tFw', (1.224676, 6.322647217), 400),
+ # CASIA-WebFace-Masked, 0.9840 LFW, 0.9667 LFW-Masked (orig mobilefacenet has 0.9482 on LFW-Masked)
+ ('arcface_mobilefacenet_casia_masked', '1ltcJChTdP1yQWF9e1ESpTNYAVwxLSNLP', (1.26538905, 5.552089201), 200),
)
def calc_embedding(self, face_img: Array3D) -> Array3D:
From 2b52e59c22ad6bb527873e7bc16c45fe27add6be Mon Sep 17 00:00:00 2001
From: IvanKurnosov
Date: Tue, 28 Sep 2021 10:33:11 +0300
Subject: [PATCH 031/702] fix docker build command in readme
---
embedding-calculator/README.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/embedding-calculator/README.md b/embedding-calculator/README.md
index fc066349b6..2e66acc88e 100644
--- a/embedding-calculator/README.md
+++ b/embedding-calculator/README.md
@@ -43,7 +43,7 @@ $ docker run -p 3000:3000 exadel/compreface-core:latest
##### Build
Builds container (also runs main tests during the build):
```
-$ docker build -t embedding-calculator
+$ docker build -t embedding-calculator .
```
To skip tests during build, use:
```
From 5264d182462959957898e8acc2dbb60f51ed8eb4 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Marcin=20S=C5=82owiak?=
Date: Sat, 2 Oct 2021 14:47:06 +0200
Subject: [PATCH 032/702] Adjust validation for subject name - do not allow
null value and empty value
---
.../core/trainservice/controller/EmbeddingController.java | 7 ++++---
.../core/trainservice/controller/SubjectController.java | 5 +++--
.../com/exadel/frs/core/trainservice/dto/SubjectDto.java | 4 ++--
3 files changed, 9 insertions(+), 7 deletions(-)
diff --git a/java/api/src/main/java/com/exadel/frs/core/trainservice/controller/EmbeddingController.java b/java/api/src/main/java/com/exadel/frs/core/trainservice/controller/EmbeddingController.java
index 73a530de7b..7ce7f6cecc 100644
--- a/java/api/src/main/java/com/exadel/frs/core/trainservice/controller/EmbeddingController.java
+++ b/java/api/src/main/java/com/exadel/frs/core/trainservice/controller/EmbeddingController.java
@@ -26,6 +26,7 @@
import javax.validation.Valid;
import javax.validation.constraints.Min;
+import javax.validation.constraints.NotBlank;
import java.io.IOException;
import java.util.List;
import java.util.Map;
@@ -51,7 +52,7 @@ public class EmbeddingController {
@PostMapping
public EmbeddingDto addEmbedding(
@ApiParam(value = IMAGE_WITH_ONE_FACE_DESC, required = true) @RequestParam final MultipartFile file,
- @ApiParam(value = SUBJECT_DESC, required = true) @RequestParam(SUBJECT) final String subjectName,
+ @ApiParam(value = SUBJECT_DESC, required = true) @Valid @NotBlank(message = "Subject name is empty") @RequestParam(SUBJECT) final String subjectName,
@ApiParam(value = DET_PROB_THRESHOLD_DESC) @RequestParam(value = DET_PROB_THRESHOLD, required = false) final Double detProbThreshold,
@ApiParam(value = API_KEY_DESC, required = true) @RequestHeader(X_FRS_API_KEY_HEADER) final String apiKey
) throws IOException {
@@ -72,7 +73,7 @@ public EmbeddingDto addEmbedding(
@PostMapping(consumes = MediaType.APPLICATION_JSON_VALUE)
public EmbeddingDto addEmbeddingBase64(
@ApiParam(value = API_KEY_DESC, required = true) @RequestHeader(X_FRS_API_KEY_HEADER) final String apiKey,
- @ApiParam(value = SUBJECT_DESC) @RequestParam(value = SUBJECT) String subjectName,
+ @ApiParam(value = SUBJECT_DESC) @Valid @NotBlank(message = "Subject name is empty") @RequestParam(value = SUBJECT) String subjectName,
@ApiParam(value = DET_PROB_THRESHOLD_DESC) @RequestParam(value = DET_PROB_THRESHOLD, required = false) final Double detProbThreshold,
@Valid @RequestBody Base64File request) {
imageValidator.validateBase64(request.getContent());
@@ -109,7 +110,7 @@ public Faces listEmbeddings(
@DeleteMapping
public Map removeAllSubjectEmbeddings(
@ApiParam(value = API_KEY_DESC, required = true) @RequestHeader(name = X_FRS_API_KEY_HEADER) final String apiKey,
- @ApiParam(value = SUBJECT_DESC) @RequestParam( name = SUBJECT, required = false) final String subjectName
+ @ApiParam(value = SUBJECT_DESC) @Valid @NotBlank(message = "Subject name is empty") @RequestParam( name = SUBJECT, required = false) final String subjectName
) {
return Map.of(
"deleted",
diff --git a/java/api/src/main/java/com/exadel/frs/core/trainservice/controller/SubjectController.java b/java/api/src/main/java/com/exadel/frs/core/trainservice/controller/SubjectController.java
index 98889af050..ada8d7f7fb 100644
--- a/java/api/src/main/java/com/exadel/frs/core/trainservice/controller/SubjectController.java
+++ b/java/api/src/main/java/com/exadel/frs/core/trainservice/controller/SubjectController.java
@@ -8,6 +8,7 @@
import org.springframework.web.bind.annotation.*;
import javax.validation.Valid;
+import javax.validation.constraints.NotBlank;
import java.util.Map;
import static com.exadel.frs.core.trainservice.system.global.Constants.*;
@@ -41,7 +42,7 @@ public Map listSubjects(
@PutMapping("/{subject}")
public Map renameSubject(
@ApiParam(value = API_KEY_DESC, required = true) @RequestHeader(X_FRS_API_KEY_HEADER) final String apiKey,
- @ApiParam(value = SUBJECT_DESC, required = true) @PathVariable("subject") final String oldSubjectName,
+ @ApiParam(value = SUBJECT_DESC, required = true) @Valid @NotBlank(message = "Subject name is empty") @PathVariable("subject") final String oldSubjectName,
@Valid @RequestBody final SubjectDto subjectDto) {
return Map.of(
"updated",
@@ -52,7 +53,7 @@ public Map renameSubject(
@DeleteMapping("/{subject}")
public Map deleteSubject(
@ApiParam(value = API_KEY_DESC, required = true) @RequestHeader(X_FRS_API_KEY_HEADER) final String apiKey,
- @ApiParam(value = SUBJECT_DESC, required = true) @PathVariable("subject") final String subjectName) {
+ @ApiParam(value = SUBJECT_DESC, required = true) @Valid @NotBlank(message = "Subject name is empty") @PathVariable("subject") final String subjectName) {
return Map.of(
"subject",
subjectService.deleteSubjectByName(apiKey, subjectName)
diff --git a/java/api/src/main/java/com/exadel/frs/core/trainservice/dto/SubjectDto.java b/java/api/src/main/java/com/exadel/frs/core/trainservice/dto/SubjectDto.java
index 8e469c0234..edbd92324f 100644
--- a/java/api/src/main/java/com/exadel/frs/core/trainservice/dto/SubjectDto.java
+++ b/java/api/src/main/java/com/exadel/frs/core/trainservice/dto/SubjectDto.java
@@ -6,7 +6,7 @@
import lombok.Data;
import lombok.NoArgsConstructor;
-import javax.validation.constraints.NotNull;
+import javax.validation.constraints.NotBlank;
import static com.exadel.frs.core.trainservice.system.global.Constants.SUBJECT_DESC;
@@ -17,6 +17,6 @@ public class SubjectDto {
@ApiParam(value = SUBJECT_DESC, required = true)
@JsonProperty("subject")
- @NotNull
+ @NotBlank(message = "Subject name is empty")
private String subjectName;
}
From 684304322dedc99335a1a0865198160775fe2634 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Marcin=20S=C5=82owiak?=
Date: Mon, 4 Oct 2021 18:15:27 +0200
Subject: [PATCH 033/702] Add Validated to make Valid annotation works
---
.../frs/core/trainservice/controller/EmbeddingController.java | 2 ++
.../frs/core/trainservice/controller/SubjectController.java | 2 ++
2 files changed, 4 insertions(+)
diff --git a/java/api/src/main/java/com/exadel/frs/core/trainservice/controller/EmbeddingController.java b/java/api/src/main/java/com/exadel/frs/core/trainservice/controller/EmbeddingController.java
index 7ce7f6cecc..500cfe141c 100644
--- a/java/api/src/main/java/com/exadel/frs/core/trainservice/controller/EmbeddingController.java
+++ b/java/api/src/main/java/com/exadel/frs/core/trainservice/controller/EmbeddingController.java
@@ -21,6 +21,7 @@
import org.springframework.data.domain.Pageable;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
+import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
@@ -36,6 +37,7 @@
import static com.exadel.frs.core.trainservice.system.global.Constants.*;
import static org.springframework.http.HttpStatus.CREATED;
+@Validated
@RestController
@RequestMapping(API_V1 + "/recognition/faces")
@RequiredArgsConstructor
diff --git a/java/api/src/main/java/com/exadel/frs/core/trainservice/controller/SubjectController.java b/java/api/src/main/java/com/exadel/frs/core/trainservice/controller/SubjectController.java
index ada8d7f7fb..da48f35f50 100644
--- a/java/api/src/main/java/com/exadel/frs/core/trainservice/controller/SubjectController.java
+++ b/java/api/src/main/java/com/exadel/frs/core/trainservice/controller/SubjectController.java
@@ -5,6 +5,7 @@
import com.exadel.frs.core.trainservice.service.SubjectService;
import io.swagger.annotations.ApiParam;
import lombok.RequiredArgsConstructor;
+import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import javax.validation.Valid;
@@ -14,6 +15,7 @@
import static com.exadel.frs.core.trainservice.system.global.Constants.*;
import static org.springframework.http.HttpStatus.CREATED;
+@Validated
@RestController
@RequestMapping(API_V1 + "/recognition/subjects")
@RequiredArgsConstructor
From ebb163300a827b8e1dfa7774d0a85f54e8f88031 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Marcin=20S=C5=82owiak?=
Date: Mon, 4 Oct 2021 18:15:59 +0200
Subject: [PATCH 034/702] Extract validation message to the Constants.java
---
.../core/trainservice/controller/EmbeddingController.java | 6 +++---
.../frs/core/trainservice/controller/SubjectController.java | 4 ++--
.../frs/core/trainservice/system/global/Constants.java | 1 +
3 files changed, 6 insertions(+), 5 deletions(-)
diff --git a/java/api/src/main/java/com/exadel/frs/core/trainservice/controller/EmbeddingController.java b/java/api/src/main/java/com/exadel/frs/core/trainservice/controller/EmbeddingController.java
index 500cfe141c..9ce81191fc 100644
--- a/java/api/src/main/java/com/exadel/frs/core/trainservice/controller/EmbeddingController.java
+++ b/java/api/src/main/java/com/exadel/frs/core/trainservice/controller/EmbeddingController.java
@@ -54,7 +54,7 @@ public class EmbeddingController {
@PostMapping
public EmbeddingDto addEmbedding(
@ApiParam(value = IMAGE_WITH_ONE_FACE_DESC, required = true) @RequestParam final MultipartFile file,
- @ApiParam(value = SUBJECT_DESC, required = true) @Valid @NotBlank(message = "Subject name is empty") @RequestParam(SUBJECT) final String subjectName,
+ @ApiParam(value = SUBJECT_DESC, required = true) @Valid @NotBlank(message = SUBJECT_NAME_IS_EMPTY) @RequestParam(SUBJECT) final String subjectName,
@ApiParam(value = DET_PROB_THRESHOLD_DESC) @RequestParam(value = DET_PROB_THRESHOLD, required = false) final Double detProbThreshold,
@ApiParam(value = API_KEY_DESC, required = true) @RequestHeader(X_FRS_API_KEY_HEADER) final String apiKey
) throws IOException {
@@ -75,7 +75,7 @@ public EmbeddingDto addEmbedding(
@PostMapping(consumes = MediaType.APPLICATION_JSON_VALUE)
public EmbeddingDto addEmbeddingBase64(
@ApiParam(value = API_KEY_DESC, required = true) @RequestHeader(X_FRS_API_KEY_HEADER) final String apiKey,
- @ApiParam(value = SUBJECT_DESC) @Valid @NotBlank(message = "Subject name is empty") @RequestParam(value = SUBJECT) String subjectName,
+ @ApiParam(value = SUBJECT_DESC) @Valid @NotBlank(message = SUBJECT_NAME_IS_EMPTY) @RequestParam(value = SUBJECT) String subjectName,
@ApiParam(value = DET_PROB_THRESHOLD_DESC) @RequestParam(value = DET_PROB_THRESHOLD, required = false) final Double detProbThreshold,
@Valid @RequestBody Base64File request) {
imageValidator.validateBase64(request.getContent());
@@ -112,7 +112,7 @@ public Faces listEmbeddings(
@DeleteMapping
public Map removeAllSubjectEmbeddings(
@ApiParam(value = API_KEY_DESC, required = true) @RequestHeader(name = X_FRS_API_KEY_HEADER) final String apiKey,
- @ApiParam(value = SUBJECT_DESC) @Valid @NotBlank(message = "Subject name is empty") @RequestParam( name = SUBJECT, required = false) final String subjectName
+ @ApiParam(value = SUBJECT_DESC) @Validated @NotBlank(message = SUBJECT_NAME_IS_EMPTY) @RequestParam( name = SUBJECT, required = false) final String subjectName
) {
return Map.of(
"deleted",
diff --git a/java/api/src/main/java/com/exadel/frs/core/trainservice/controller/SubjectController.java b/java/api/src/main/java/com/exadel/frs/core/trainservice/controller/SubjectController.java
index da48f35f50..eedc05376d 100644
--- a/java/api/src/main/java/com/exadel/frs/core/trainservice/controller/SubjectController.java
+++ b/java/api/src/main/java/com/exadel/frs/core/trainservice/controller/SubjectController.java
@@ -44,7 +44,7 @@ public Map listSubjects(
@PutMapping("/{subject}")
public Map renameSubject(
@ApiParam(value = API_KEY_DESC, required = true) @RequestHeader(X_FRS_API_KEY_HEADER) final String apiKey,
- @ApiParam(value = SUBJECT_DESC, required = true) @Valid @NotBlank(message = "Subject name is empty") @PathVariable("subject") final String oldSubjectName,
+ @ApiParam(value = SUBJECT_DESC, required = true) @Valid @NotBlank(message = SUBJECT_NAME_IS_EMPTY) @PathVariable("subject") final String oldSubjectName,
@Valid @RequestBody final SubjectDto subjectDto) {
return Map.of(
"updated",
@@ -55,7 +55,7 @@ public Map renameSubject(
@DeleteMapping("/{subject}")
public Map deleteSubject(
@ApiParam(value = API_KEY_DESC, required = true) @RequestHeader(X_FRS_API_KEY_HEADER) final String apiKey,
- @ApiParam(value = SUBJECT_DESC, required = true) @Valid @NotBlank(message = "Subject name is empty") @PathVariable("subject") final String subjectName) {
+ @ApiParam(value = SUBJECT_DESC, required = true) @Valid @NotBlank(message = SUBJECT_NAME_IS_EMPTY) @PathVariable("subject") final String subjectName) {
return Map.of(
"subject",
subjectService.deleteSubjectByName(apiKey, subjectName)
diff --git a/java/api/src/main/java/com/exadel/frs/core/trainservice/system/global/Constants.java b/java/api/src/main/java/com/exadel/frs/core/trainservice/system/global/Constants.java
index 6be24b7fbc..f7288924be 100644
--- a/java/api/src/main/java/com/exadel/frs/core/trainservice/system/global/Constants.java
+++ b/java/api/src/main/java/com/exadel/frs/core/trainservice/system/global/Constants.java
@@ -55,6 +55,7 @@ public class Constants {
public static final String SOURCE_IMAGE_DESC = "File to be verified";
public static final String TARGET_IMAGE_DESC = "Reference file to check the processed file";
public static final String STATUS = "status";
+ public static final String SUBJECT_NAME_IS_EMPTY = "Subject name is empty";
public static final String DEMO_API_KEY = "00000000-0000-0000-0000-000000000002";
public static final String FACENET2018 = "Facenet2018";
From 7d9f5e413754be7e9e748e2f258d600f8c98a85d Mon Sep 17 00:00:00 2001
From: durgeshmeena
Date: Mon, 4 Oct 2021 23:43:28 +0530
Subject: [PATCH 035/702] added tooltips to the button on CompreFace page
---
.../app-change-photo/app-change-photo.component.html | 5 +++--
.../app/features/app-change-photo/app-change-photo.module.ts | 3 ++-
2 files changed, 5 insertions(+), 3 deletions(-)
diff --git a/ui/src/app/features/app-change-photo/app-change-photo.component.html b/ui/src/app/features/app-change-photo/app-change-photo.component.html
index 47d14c7442..fc89aef7cc 100644
--- a/ui/src/app/features/app-change-photo/app-change-photo.component.html
+++ b/ui/src/app/features/app-change-photo/app-change-photo.component.html
@@ -14,18 +14,19 @@
~ permissions and limitations under the License.
-->
Exadel CompreFace is a leading free and open-source face recognition system
-
-
-
-
-
-
- Exadel CompreFace is a free and open-source face recognition service that can be easily integrated into any system without prior machine learning skills.
- CompreFace provides REST API for face recognition, face verification, face detection, landmark detection, age, and gender recognition and is easily deployed with docker.
-
-
-
-
-
-# Table Of Contents
-
- * [Overview](#overview)
- * [Screenshots](#screenshots)
- * [News and updates](#news-and-updates)
- * [Features](#features)
- * [Getting Started with CompreFace](#getting-started-with-compreface)
- * [CompreFace SDKs](#compreface-sdks)
- * [Documentation](/docs)
- * [How to Use CompreFace](/docs/How-to-Use-CompreFace.md)
- * [Face Services and Plugins](/docs/Face-services-and-plugins.md)
- * [Rest API Description](/docs/Rest-API-description.md)
- * [Face Recognition Similarity Threshold](/docs/Face-Recognition-Similarity-Threshold.md)
- * [Configuration](/docs/Configuration.md)
- * [Architecture and Scalability](/docs/Architecture-and-scalability.md)
- * [Custom Builds](/docs/Custom-builds.md)
- * [Face data migration](/docs/Face-data-migration.md)
- * [User Roles System](/docs/User-Roles-System.md)
- * [Face Mask Detection Plugin](/docs/Mask-detection-plugin.md)
- * [Kubernetes configuration](https://github.com/exadel-inc/compreface-kubernetes)
- * [Gathering Anonymous Statistics](/docs/Gathering-anonymous-statistics.md)
- * [Contributing](#contributing)
- * [License info](#license-info)
-
-
-# Overview
-
-Exadel CompreFace is a free and open-source face recognition GitHub project.
-Essentially, it is a docker-based application that can be used as a standalone server or deployed in the cloud.
-You don’t need prior machine learning skills to set up and use CompreFace.
-
-The system provides REST API for face recognition, face verification, face detection, landmark detection, age, and gender recognition.
-The solution also features a role management system that allows you to easily control who has access to your Face Recognition Services.
-
-CompreFace is delivered as a docker-compose config and supports different models that work on CPU and GPU.
-Our solution is based on state-of-the-art methods and libraries like FaceNet and InsightFace.
-
-# Screenshots
-
-
-
-
-
-
-
-
-
-# News and updates
-
-[Subscribe](https://exadel-7026941.hs-sites.com/en/en/compreface-news-and-updates) to CompreFace News and Updates to never miss new features and product improvements.
-
-# Features
-
-The system can accurately identify people even when it has only “seen” their photo once. Technology-wise, CompreFace has several advantages over similar free face recognition solutions. CompreFace:
-
-- Supports many face recognition services: face identification, face verification, face detection, face mask detection, landmark detection,
- and age and
-gender recognition
-- Supports both CPU and GPU and is easy to scale up
-- Is open source and self-hosted, which gives you additional guarantees for data security
-- Can be deployed either in the cloud or on premises
-- Can be set up and used without machine learning expertise
-- Uses FaceNet and InsightFace libraries, which use state-of-the-art face recognition methods
-- Features a UI panel for convenient user roles and access management
-- Starts quickly with just one docker command
-
-
-# Getting Started with CompreFace
-
-### Requirements
-
-1. Docker and Docker compose (or Docker Desktop)
-2. CompreFace could be run on most modern computers with [x86 processor](https://en.wikipedia.org/wiki/X86) and [AVX support](https://en.wikipedia.org/wiki/Advanced_Vector_Extensions).
- To check AVX support on Linux run `lscpu | grep avx` command
-
-### To get started (Linux, MacOS):
-
-1. Install Docker and Docker Compose
-2. Download the archive from our latest release: https://github.com/exadel-inc/CompreFace/releases
-3. Unzip the archive
-4. Open the terminal in this folder and run this command: `docker-compose up -d`
-5. Open the service in your browser: http://localhost:8000/login
-
-### To get started (Windows):
-
-1. Install Docker Desktop
-2. Download the archive from our latest release: https://github.com/exadel-inc/CompreFace/releases
-3. Unzip the archive
-4. Run Docker
-5. Open Command prompt (write `cmd` in windows search bar)
-6. Open folder where you extracted zip archive (Write `cd path_of_the_folder`, press enter).
-7. Run command: `docker-compose up -d`
-8. Open http://localhost:8000/login
-
-### Getting started for contributors
-
-Follow this [link](/dev)
-
-# CompreFace SDKs
-
-| SDK | Repository |
-| ---------- | ------ |
-| JavaScript | https://github.com/exadel-inc/compreface-javascript-sdk |
-| Python | https://github.com/exadel-inc/compreface-python-sdk |
-
-# Documentation
-
-More documentation is available [here](/docs)
-
-# Contributing
-
-We want to improve our open-source face recognition solution, so your contributions are welcome and greatly appreciated.
-
-* Just use CompreFace and [report](https://github.com/exadel-inc/CompreFace/issues) ideas and bugs on GitHub
-* Share knowledge and experience via posting guides and articles, or just improve our [documentation](https://github.com/exadel-inc/CompreFace/tree/master/docs)
-* Create [SDKs](https://github.com/topics/compreface-sdk) for favorite programming language, we will add it to our documentation
-* Integrate CompreFace support to other platforms like [Home Assistant](https://www.home-assistant.io/) or [DreamFactory](https://www.dreamfactory.com/), we will add it to our documentation
-* [Contribute](CONTRIBUTING.md) code
-* Add [plugin](/docs/Face-services-and-plugins.md#face-plugins) to face services
-* And last, but not least, you can just give a star to our free facial recognition system on GitHub
-
-For more information, visit our [contributing](CONTRIBUTING.md) guide, or create a [discussion](https://github.com/exadel-inc/CompreFace/discussions).
-
-# License info
-
-CompreFace is open-source real-time facial recognition software released under the [Apache 2.0 license](https://www.apache.org/licenses/LICENSE-2.0.html).
+
+
CompreFace is a free and open-source face recognition system from Exadel
+
+
+
+
+
+
+ CompreFace can be easily integrated into any system without prior machine learning skills. CompreFace provides REST API for face
+recognition, face verification, face detection, face mask detection, landmark detection, age, and gender recognition and is easily deployed with
+docker
+
+
+
+
+
+# Table Of Contents
+
+ * [Overview](#overview)
+ * [Screenshots](#screenshots)
+ * [News and updates](#news-and-updates)
+ * [Features](#features)
+ * [Getting Started with CompreFace](#getting-started-with-compreface)
+ * [CompreFace SDKs](#compreface-sdks)
+ * [Documentation](/docs)
+ * [How to Use CompreFace](/docs/How-to-Use-CompreFace.md)
+ * [Face Services and Plugins](/docs/Face-services-and-plugins.md)
+ * [Rest API Description](/docs/Rest-API-description.md)
+ * [Face Recognition Similarity Threshold](/docs/Face-Recognition-Similarity-Threshold.md)
+ * [Configuration](/docs/Configuration.md)
+ * [Architecture and Scalability](/docs/Architecture-and-scalability.md)
+ * [Custom Builds](/docs/Custom-builds.md)
+ * [Face data migration](/docs/Face-data-migration.md)
+ * [User Roles System](/docs/User-Roles-System.md)
+ * [Face Mask Detection Plugin](/docs/Mask-detection-plugin.md)
+ * [Kubernetes configuration](https://github.com/exadel-inc/compreface-kubernetes)
+ * [Gathering Anonymous Statistics](/docs/Gathering-anonymous-statistics.md)
+ * [Contributing](#contributing)
+ * [License info](#license-info)
+
+
+# Overview
+
+CompreFace is a free and open-source face detection and recognition GitHub project.
+Essentially, it is a docker-based application that can be used as a standalone server or deployed in the cloud.
+You don’t need prior machine learning skills to set up and use CompreFace.
+
+CompreFace provides REST API for face recognition, face verification, face detection, face mask detection, landmark detection, age, and gender recognition.
+The solution also features a role management system that allows you to easily control who has access to your Face Recognition Services.
+
+CompreFace is delivered as a docker-compose config and supports different models that work on CPU and GPU. Our solution is based on state-of-the-art methods and libraries like FaceNet and InsightFace.
+
+# Screenshots
+
+
+
+
+
+
+
+
+
+# News and updates
+
+[Subscribe](https://exadel-7026941.hs-sites.com/en/en/compreface-news-and-updates) to CompreFace News and Updates to never miss new features and product improvements.
+
+# Features
+The system can accurately identify people even when it has only “seen” their photo once. Technology-wise, CompreFace has several advantages over similar free face recognition solutions. CompreFace:
+
+- Supports both CPU and GPU and is easy to scale up
+- Is open source and self-hosted, which gives you additional guarantees for data security
+- Can be deployed either in the cloud or on premises
+- Can be set up and used without machine learning expertise
+- Uses FaceNet and InsightFace libraries, which use state-of-the-art face recognition methods
+- Starts quickly with just one docker command
+
+# Functionalities
+
+- Supports many face recognition services:
+ - [face detection](https://github.com/exadel-inc/CompreFace/blob/master/docs/Face-services-and-plugins.md#face-detection)
+ - [face recognition](https://github.com/exadel-inc/CompreFace/blob/master/docs/Face-services-and-plugins.md#face-recognition)
+ - [face verification](https://github.com/exadel-inc/CompreFace/blob/master/docs/Face-services-and-plugins.md#face-verification)
+ - [landmark detection plugin](https://github.com/exadel-inc/CompreFace/blob/master/docs/Face-services-and-plugins.md#face-plugins)
+ - [age recognition plugin](https://github.com/exadel-inc/CompreFace/blob/master/docs/Face-services-and-plugins.md#face-plugins)
+ - [gender recognition plugin](https://github.com/exadel-inc/CompreFace/blob/master/docs/Face-services-and-plugins.md#face-plugins)
+ - [face mask detection plugin](https://github.com/exadel-inc/CompreFace/blob/master/docs/Face-services-and-plugins.md#face-plugins)
+- Use the ComperFace UI panel for convenient user roles and access management
+
+# Getting Started with CompreFace
+
+### Requirements
+
+1. Docker and Docker compose (or Docker Desktop)
+2. CompreFace could be run on most modern computers with [x86 processor](https://en.wikipedia.org/wiki/X86) and [AVX support](https://en.wikipedia.org/wiki/Advanced_Vector_Extensions).
+ To check AVX support on Linux run `lscpu | grep avx` command
+
+### To get started (Linux, MacOS):
+
+1. Install Docker and Docker Compose
+2. Download the archive from our latest release: https://github.com/exadel-inc/CompreFace/releases
+3. Unzip the archive
+4. Open the terminal in this folder and run this command: `docker-compose up -d`
+5. Open the service in your browser: http://localhost:8000/login
+
+### To get started (Windows):
+
+1. Install Docker Desktop
+2. Download the archive from our latest release: https://github.com/exadel-inc/CompreFace/releases
+3. Unzip the archive
+4. Run Docker
+5. Open Command prompt (write `cmd` in windows search bar)
+6. Open folder where you extracted zip archive (Write `cd path_of_the_folder`, press enter).
+7. Run command: `docker-compose up -d`
+8. Open http://localhost:8000/login
+
+### Getting started for contributors
+
+Follow this [link](/dev)
+
+# CompreFace SDKs
+
+| SDK | Repository |
+| ---------- | ------ |
+| JavaScript | https://github.com/exadel-inc/compreface-javascript-sdk |
+| Python | https://github.com/exadel-inc/compreface-python-sdk |
+
+# Documentation
+
+More documentation is available [here](/docs)
+
+# Contributing
+
+We want to improve our open-source face recognition solution, so your contributions are welcome and greatly appreciated.
+
+* Just use CompreFace and [report](https://github.com/exadel-inc/CompreFace/issues) ideas and bugs on GitHub
+* Share knowledge and experience via posting guides and articles, or just improve our [documentation](https://github.com/exadel-inc/CompreFace/tree/master/docs)
+* Create [SDKs](https://github.com/topics/compreface-sdk) for favorite programming language, we will add it to our documentation
+* Integrate CompreFace support to other platforms like [Home Assistant](https://www.home-assistant.io/) or [DreamFactory](https://www.dreamfactory.com/), we will add it to our documentation
+* [Contribute](CONTRIBUTING.md) code
+* Add [plugin](/docs/Face-services-and-plugins.md#face-plugins) to face services
+* And last, but not least, you can just give a star to our free facial recognition system on GitHub
+
+For more information, visit our [contributing](CONTRIBUTING.md) guide, or create a [discussion](https://github.com/exadel-inc/CompreFace/discussions).
+
+# License info
+
+CompreFace is open-source real-time facial recognition software released under the [Apache 2.0 license](https://www.apache.org/licenses/LICENSE-2.0.html).
From 519f6724800d8e54fb90b96a051b03ede2951b95 Mon Sep 17 00:00:00 2001
From: asergeiev <89693966+asergeiev@users.noreply.github.com>
Date: Tue, 5 Oct 2021 14:19:44 +0300
Subject: [PATCH 039/702] Test commit
Restored the changes after the last loss
---
docs/Face-services-and-plugins.md | 163 +++++++++++++++++++++---------
1 file changed, 114 insertions(+), 49 deletions(-)
diff --git a/docs/Face-services-and-plugins.md b/docs/Face-services-and-plugins.md
index 587a87e67c..f5373d70b7 100644
--- a/docs/Face-services-and-plugins.md
+++ b/docs/Face-services-and-plugins.md
@@ -1,49 +1,114 @@
-# Face Services and Plugins
-
-CompreFace supports these face services and plugins:
-* Face recognition service (Face identification)
-* Face detection service
-* Face verification service
-* Age detection plugin
-* Gender detection plugin
-* Landmarks detection plugin
-* Calculator plugin
-
-## Services
-
-To use face service you need to create it in an application on UI. The type of service depends on your application needs. Each service has its own REST API context and there is no possibility to change the service type after creation. Here is a short description of each of them:
-* Face recognition service is used for face identification. This means that you first need to upload known faces to faces collection and then recognize unknown faces among them. When you upload an unknown face, the service returns the most similar faces to it. Also, face recognition service supports verify endpoint to check if this person from face collection is the correct one. The possible cases include:
- * when you have photos of employees and want to recognize strangers in the office
- * when you have photos of conference attendees and want to track who was interested in which topics.
- * when you have photos of VIP guests and you want to find them among the crowd very quickly.
-* Face verification service is used to check if this person is the correct one. The service compares two faces you send to the rest endpoint and returns their similarity. The possible cases include:
- * when a customer provides you an ID or driving license and you need to verify if this is him
- * when a user connects his social network account to your application and you want to verify if this is him
-* Face detection service is used to detect all faces in the image. It doesn’t recognize faces, just find them on the image. The most useful cases include face plugins for face analysis:
- * gather statistics on how your store popular among different genders
- * gather statistics on among what ages your event is popular
- * get landmark information to know where customers look at
- * gather statistics on how many customers in the store
-
-## Face plugins
-
-Face plugins could be used with any of the face services. By default, face services return only bounding boxes and similarity if
-applicable. To add more information in response you can add face plugins in your request. To add a plugin you need to list
-comma-separated needed plugins in the query `face_plugins` parameter. This parameter is supported by all face recognition services.
-Example:
-```shell
-curl -X POST "http://localhost:8000/api/v1/recognition/recognize?face_plugins=age,gender,landmarks,mask" \
--H "Content-Type: multipart/form-data" \
--H "x-api-key: " \
--F file=
-```
-This request will recognize faces on the image and return additional information about age, gender, and landmarks.
-
-The list of possible plugins:
-* age - returns the supposed range of a person’s age in format [min, max]
-* gender - returns the supposed person’s gender
-* landmarks - returns face landmarks. This plugin is supported by all configurations and returns 5 points of eyes, nose, and mouth
-* calculator - returns face embeddings.
-* mask - returns if the person wears a mask. Possible results: `without_mask`, `mask_worn_incorrectly`, `mask_worn_correctly`. Learn more about [mask plugin](Mask-detection-plugin.md)
-* landmarks2d106 - returns face landmarks. This plugin is supported only by the configuration that uses insightface library. It’s not
- available by default. More information about landmarks [here](https://github.com/deepinsight/insightface/tree/master/alignment/coordinateReg#visualization).
+# Face Services and Plugins
+
+CompreFace supports these face services and plugins:
+* Face recognition service (Face identification)
+* Face detection service
+* Face verification service
+* Age detection plugin
+* Gender detection plugin
+* Landmarks detection plugin
+* Calculator plugin
+
+## Services
+
+To use face service you need to create it in an application on UI. The type of service depends on your application needs. Each service has its own REST API context and there is no possibility to change the service type after creation. Here is a short description of each of them:
+
+### Face detection
+
+Face detection service is used to detect all faces in the image. It doesn’t recognize faces, just finds them on the image.
+
+**Cases of use**
+
+The most useful cases include face plugins for face analysis:
+ * gather statistics on how your store popular among different genders
+ * gather statistics on among what ages your event is popular
+ * get landmark information to know where customers look at
+ * gather statistics on how many customers in the store
+
+**How to test**
+
+1. On the ComperFace application page, at the bottom of the frame, click Create button.
+2. In the Create Service dialog, from the Type drop-down menu, select DETECTION.
+3. Enter the name of the service you are going to create.
+4. From the list of the services in the Services frame, select the service you created; you can use search field to filter the services.
+5. Click Test button near in the row of the service you want to launch.
+6. On the service page, open or drag-and-drop the picture to analyze.
+
+**Output**
+
+The service will display the original picture with marks near every face.
+Below the picture, you can see the Request processed, and the Response to the request.
+
+### Face recognition
+
+Face recognition service is used for face identification. This means that you first need to upload known faces to faces collection and then recognize unknown faces among them. When you upload an unknown face, the service returns the most similar faces to it. Also, face recognition service supports verify endpoint to check if this person from face collection is the correct one.
+
+**Cases of use**
+
+The possible cases include:
+ * when you have photos of employees and want to recognize strangers in the office
+ * when you have photos of conference attendees and want to track who was interested in which topics.
+ * when you have photos of VIP guests and you want to find them among the crowd very quickly.
+
+**How to test**
+
+1. On the ComperFace application page, at the bottom of the frame, click Create button.
+2. In the Create Service dialog, from the Type drop-down menu, select RECOGNITION.
+3. Enter the name of the service you are going to create.
+4. From the list of the services in the Services frame, select the service you created; you can use search field to filter the services.
+5. Click Test button near in the row of the service you want to launch.
+6. On the service page, open or drag-and-drop the picture to analyze.
+
+**Output**
+
+The service will display the original picture with marks near every face.
+Below the picture, you can see the Request processed, and the Response to the request.
+
+### Face verification
+
+Face verification service is used to check if this person is the correct one. The service compares two faces you send to the rest endpoint and returns their similarity.
+
+**Cases of use**
+
+The possible cases include:
+ * when a customer provides you an ID or driving license and you need to verify if this is him
+ * when a user connects his social network account to your application and you want to verify if this is him
+
+**How to test**
+
+1. On the ComperFace application page, at the bottom of the frame, click Create button.
+2. In the Create Service dialog, from the Type drop-down menu, select VERIFICATION.
+3. Enter the name of the service you are going to create.
+4. From the list of the services in the Services frame, select the service you created; you can use search field to filter the services.
+5. Click Test button near in the row of the service you want to launch.
+6. On the service page, open or drag-and-drop two pictures to compare their content.
+
+**Output**
+
+The service will display the original picture with marks near every face.
+Below the picture, you can see the Request processed, and the Response to the request.
+
+## Face plugins
+
+Face plugins could be used with any of the face services. By default, face services return only bounding boxes and similarity if
+applicable. To add more information in response you can add face plugins in your request. To add a plugin you need to list
+comma-separated needed plugins in the query `face_plugins` parameter. This parameter is supported by all face recognition services.
+Example:
+
+```shell
+curl -X POST "http://localhost:8000/api/v1/recognition/recognize?face_plugins=age,gender,landmarks,mask" \
+-H "Content-Type: multipart/form-data" \
+-H "x-api-key: " \
+-F file=
+```
+
+This request will recognize faces on the image and return additional information about age, gender, and landmarks.
+
+The list of possible plugins:
+* age - returns the supposed range of a person’s age in format [min, max]
+* gender - returns the supposed person’s gender
+* landmarks - returns face landmarks. This plugin is supported by all configurations and returns 5 points of eyes, nose, and mouth
+* calculator - returns face embeddings.
+* mask - returns if the person wears a mask. Possible results: `without_mask`, `mask_worn_incorrectly`, `mask_worn_correctly`. Learn more about [mask plugin](Mask-detection-plugin.md)
+* landmarks2d106 - returns face landmarks. This plugin is supported only by the configuration that uses insightface library. It’s not
+ available by default. More information about landmarks [here](https://github.com/deepinsight/insightface/tree/master/alignment/coordinateReg#visualization).
From 2d41c0148ab61ad699d94f565c669e779ff67111 Mon Sep 17 00:00:00 2001
From: durgeshmeena
Date: Wed, 6 Oct 2021 00:58:33 +0530
Subject: [PATCH 040/702] added tooltips with i18n translation
---
.../app-change-photo/app-change-photo.component.html | 12 +++++++++---
.../app-change-photo/app-change-photo.module.ts | 3 ++-
ui/src/assets/i18n/en.json | 5 +++++
3 files changed, 16 insertions(+), 4 deletions(-)
diff --git a/ui/src/app/features/app-change-photo/app-change-photo.component.html b/ui/src/app/features/app-change-photo/app-change-photo.component.html
index fc89aef7cc..41b0fa005c 100644
--- a/ui/src/app/features/app-change-photo/app-change-photo.component.html
+++ b/ui/src/app/features/app-change-photo/app-change-photo.component.html
@@ -14,19 +14,25 @@
~ permissions and limitations under the License.
-->
-
+
-
+
Date: Wed, 6 Oct 2021 12:49:35 +0300
Subject: [PATCH 041/702] Updated the link to 2d106det model
---
.../src/services/facescan/plugins/insightface/insightface.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/embedding-calculator/src/services/facescan/plugins/insightface/insightface.py b/embedding-calculator/src/services/facescan/plugins/insightface/insightface.py
index 2d6359e7a3..271141ed99 100644
--- a/embedding-calculator/src/services/facescan/plugins/insightface/insightface.py
+++ b/embedding-calculator/src/services/facescan/plugins/insightface/insightface.py
@@ -187,7 +187,7 @@ class Landmarks2d106Detector(InsightFaceMixin, mixins.LandmarksDetectorMixin,
base.BasePlugin):
slug = 'landmarks2d106'
ml_models = (
- ('2d106det', '1MBWbTEYRhZFzj_O2f2Dc6fWGXFWtbMFw'),
+ ('2d106det', '18cL35hF2exZ8u4pfLKWjJGxF0ySuYM2o'),
)
CROP_SIZE = (192, 192) # model requirements
From 52f59987a7921403a4e656e55ed5edcd03deed12 Mon Sep 17 00:00:00 2001
From: Jayita10
Date: Wed, 6 Oct 2021 21:25:47 +0530
Subject: [PATCH 042/702] fixed incorrect error message when entered old pswd
---
.../frs/commonservice/handler/ResponseExceptionHandler.java | 4 +---
1 file changed, 1 insertion(+), 3 deletions(-)
diff --git a/java/common/src/main/java/com/exadel/frs/commonservice/handler/ResponseExceptionHandler.java b/java/common/src/main/java/com/exadel/frs/commonservice/handler/ResponseExceptionHandler.java
index 30dc7862a4..3f0aa256dd 100644
--- a/java/common/src/main/java/com/exadel/frs/commonservice/handler/ResponseExceptionHandler.java
+++ b/java/common/src/main/java/com/exadel/frs/commonservice/handler/ResponseExceptionHandler.java
@@ -184,15 +184,13 @@ private static BasicException getException(final FieldError fieldError) {
switch (code) {
case "NotBlank":
case "ValidEnum":
+ case "Size":
basicException = new ConstraintViolationException(fieldError.getDefaultMessage());
break;
case "NotNull":
case "NotEmpty":
basicException = new EmptyRequiredFieldException(fieldError.getField());
break;
- case "Size":
- basicException = new ConstraintViolationException(fieldError.getField(), fieldError.getDefaultMessage());
- break;
default:
basicException = new BasicException(UNDEFINED, "");
}
From 6bc5a9295cc6f380960ca55324543e7c2c43b26c Mon Sep 17 00:00:00 2001
From: asergeiev <89693966+asergeiev@users.noreply.github.com>
Date: Thu, 7 Oct 2021 18:00:14 +0300
Subject: [PATCH 043/702] Added anchor
Added an anchor to the sections' index
---
README.md | 1 +
1 file changed, 1 insertion(+)
diff --git a/README.md b/README.md
index 35d0a607da..0ec801cbac 100644
--- a/README.md
+++ b/README.md
@@ -45,6 +45,7 @@ docker
* [Screenshots](#screenshots)
* [News and updates](#news-and-updates)
* [Features](#features)
+ * [Functionalities](#functionalities)
* [Getting Started with CompreFace](#getting-started-with-compreface)
* [CompreFace SDKs](#compreface-sdks)
* [Documentation](/docs)
From d321d0c9f9cb3db4c8e4fb2b1818ea8d7dc35155 Mon Sep 17 00:00:00 2001
From: spospielov
Date: Fri, 8 Oct 2021 14:58:33 +0300
Subject: [PATCH 044/702] fixed "Connection Error: sorry, too many clients
already" error
---
.../service/NotificationSenderService.java | 12 ++++++++++--
1 file changed, 10 insertions(+), 2 deletions(-)
diff --git a/java/api/src/main/java/com/exadel/frs/core/trainservice/service/NotificationSenderService.java b/java/api/src/main/java/com/exadel/frs/core/trainservice/service/NotificationSenderService.java
index 8075bceac8..b0e509ef62 100644
--- a/java/api/src/main/java/com/exadel/frs/core/trainservice/service/NotificationSenderService.java
+++ b/java/api/src/main/java/com/exadel/frs/core/trainservice/service/NotificationSenderService.java
@@ -5,6 +5,7 @@
import com.fasterxml.jackson.databind.ObjectMapper;
import com.impossibl.postgres.api.jdbc.PGConnection;
import com.impossibl.postgres.jdbc.PGDataSource;
+import javax.annotation.PostConstruct;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Qualifier;
@@ -22,11 +23,18 @@ public class NotificationSenderService {
private final PGDataSource pgNotificationDatasource;
private PGConnection connection;
+ @PostConstruct
+ public void setUp() {
+ try {
+ this.connection = (PGConnection) pgNotificationDatasource.getConnection();
+ } catch (SQLException e) {
+ log.error("Error during connection to Postgres", e);
+ }
+ }
public void notifyCacheChange(CacheActionDto cacheActionDto) {
try {
- connection = (PGConnection) pgNotificationDatasource.getConnection();
- Statement statement = connection.createStatement();
+ Statement statement = this.connection.createStatement();
try {
ObjectMapper objectMapper = new ObjectMapper();
From 3001beb55f034b1eaad35a38481ffb8a50a0a29e Mon Sep 17 00:00:00 2001
From: spospielov
Date: Fri, 8 Oct 2021 18:31:48 +0300
Subject: [PATCH 045/702] updated latest version
---
.env | 8 ++++----
custom-builds/FaceNet/.env | 8 ++++----
custom-builds/Mobilenet-gpu/.env | 8 ++++----
custom-builds/Mobilenet/.env | 8 ++++----
custom-builds/SubCenter-ArcFace-r100-gpu/.env | 8 ++++----
custom-builds/SubCenter-ArcFace-r100/.env | 8 ++++----
6 files changed, 24 insertions(+), 24 deletions(-)
diff --git a/.env b/.env
index 6c6690cb69..1347078496 100644
--- a/.env
+++ b/.env
@@ -12,7 +12,7 @@ enable_email_server=false
save_images_to_db=true
compreface_api_java_options=-Xmx8g
compreface_admin_java_options=-Xmx8g
-ADMIN_VERSION=0.6.0
-API_VERSION=0.6.0
-FE_VERSION=0.6.0
-CORE_VERSION=0.6.0
\ No newline at end of file
+ADMIN_VERSION=0.6.1
+API_VERSION=0.6.1
+FE_VERSION=0.6.1
+CORE_VERSION=0.6.1
\ No newline at end of file
diff --git a/custom-builds/FaceNet/.env b/custom-builds/FaceNet/.env
index 692d8db96c..0f256b9002 100644
--- a/custom-builds/FaceNet/.env
+++ b/custom-builds/FaceNet/.env
@@ -12,7 +12,7 @@ enable_email_server=false
save_images_to_db=true
compreface_api_java_options=-Xmx8g
compreface_admin_java_options=-Xmx8g
-ADMIN_VERSION=0.6.0
-API_VERSION=0.6.0
-FE_VERSION=0.6.0
-CORE_VERSION=0.6.0-facenet
\ No newline at end of file
+ADMIN_VERSION=0.6.1
+API_VERSION=0.6.1
+FE_VERSION=0.6.1
+CORE_VERSION=0.6.1-facenet
\ No newline at end of file
diff --git a/custom-builds/Mobilenet-gpu/.env b/custom-builds/Mobilenet-gpu/.env
index 62d9581424..ca1df59942 100644
--- a/custom-builds/Mobilenet-gpu/.env
+++ b/custom-builds/Mobilenet-gpu/.env
@@ -12,7 +12,7 @@ enable_email_server=false
save_images_to_db=true
compreface_api_java_options=-Xmx8g
compreface_admin_java_options=-Xmx8g
-ADMIN_VERSION=0.6.0
-API_VERSION=0.6.0
-FE_VERSION=0.6.0
-CORE_VERSION=0.6.0-mobilenet-gpu
+ADMIN_VERSION=0.6.1
+API_VERSION=0.6.1
+FE_VERSION=0.6.1
+CORE_VERSION=0.6.1-mobilenet-gpu
diff --git a/custom-builds/Mobilenet/.env b/custom-builds/Mobilenet/.env
index 8698b49449..236b883aba 100644
--- a/custom-builds/Mobilenet/.env
+++ b/custom-builds/Mobilenet/.env
@@ -12,7 +12,7 @@ enable_email_server=false
save_images_to_db=true
compreface_api_java_options=-Xmx8g
compreface_admin_java_options=-Xmx8g
-ADMIN_VERSION=0.6.0
-API_VERSION=0.6.0
-FE_VERSION=0.6.0
-CORE_VERSION=0.6.0-mobilenet
+ADMIN_VERSION=0.6.1
+API_VERSION=0.6.1
+FE_VERSION=0.6.1
+CORE_VERSION=0.6.1-mobilenet
diff --git a/custom-builds/SubCenter-ArcFace-r100-gpu/.env b/custom-builds/SubCenter-ArcFace-r100-gpu/.env
index d1a5e8a6c7..1b317e39c3 100644
--- a/custom-builds/SubCenter-ArcFace-r100-gpu/.env
+++ b/custom-builds/SubCenter-ArcFace-r100-gpu/.env
@@ -12,7 +12,7 @@ enable_email_server=false
save_images_to_db=true
compreface_api_java_options=-Xmx8g
compreface_admin_java_options=-Xmx8g
-ADMIN_VERSION=0.6.0
-API_VERSION=0.6.0
-FE_VERSION=0.6.0
-CORE_VERSION=0.6.0-arcface-r100-gpu
+ADMIN_VERSION=0.6.1
+API_VERSION=0.6.1
+FE_VERSION=0.6.1
+CORE_VERSION=0.6.1-arcface-r100-gpu
diff --git a/custom-builds/SubCenter-ArcFace-r100/.env b/custom-builds/SubCenter-ArcFace-r100/.env
index 74f6c844a1..327dd602b0 100644
--- a/custom-builds/SubCenter-ArcFace-r100/.env
+++ b/custom-builds/SubCenter-ArcFace-r100/.env
@@ -12,7 +12,7 @@ enable_email_server=false
save_images_to_db=true
compreface_api_java_options=-Xmx8g
compreface_admin_java_options=-Xmx8g
-ADMIN_VERSION=0.6.0
-API_VERSION=0.6.0
-FE_VERSION=0.6.0
-CORE_VERSION=0.6.0-arcface-r100
\ No newline at end of file
+ADMIN_VERSION=0.6.1
+API_VERSION=0.6.1
+FE_VERSION=0.6.1
+CORE_VERSION=0.6.1-arcface-r100
\ No newline at end of file
From db18aeb4375a838f09d5e564d1f73ebf66e8c7f7 Mon Sep 17 00:00:00 2001
From: spospielov
Date: Fri, 8 Oct 2021 18:31:48 +0300
Subject: [PATCH 046/702] updated latest version
---
.env | 8 ++++----
custom-builds/FaceNet/.env | 8 ++++----
custom-builds/Mobilenet-gpu/.env | 8 ++++----
custom-builds/Mobilenet/.env | 8 ++++----
custom-builds/SubCenter-ArcFace-r100-gpu/.env | 8 ++++----
custom-builds/SubCenter-ArcFace-r100/.env | 8 ++++----
6 files changed, 24 insertions(+), 24 deletions(-)
diff --git a/.env b/.env
index 6c6690cb69..1347078496 100644
--- a/.env
+++ b/.env
@@ -12,7 +12,7 @@ enable_email_server=false
save_images_to_db=true
compreface_api_java_options=-Xmx8g
compreface_admin_java_options=-Xmx8g
-ADMIN_VERSION=0.6.0
-API_VERSION=0.6.0
-FE_VERSION=0.6.0
-CORE_VERSION=0.6.0
\ No newline at end of file
+ADMIN_VERSION=0.6.1
+API_VERSION=0.6.1
+FE_VERSION=0.6.1
+CORE_VERSION=0.6.1
\ No newline at end of file
diff --git a/custom-builds/FaceNet/.env b/custom-builds/FaceNet/.env
index 692d8db96c..0f256b9002 100644
--- a/custom-builds/FaceNet/.env
+++ b/custom-builds/FaceNet/.env
@@ -12,7 +12,7 @@ enable_email_server=false
save_images_to_db=true
compreface_api_java_options=-Xmx8g
compreface_admin_java_options=-Xmx8g
-ADMIN_VERSION=0.6.0
-API_VERSION=0.6.0
-FE_VERSION=0.6.0
-CORE_VERSION=0.6.0-facenet
\ No newline at end of file
+ADMIN_VERSION=0.6.1
+API_VERSION=0.6.1
+FE_VERSION=0.6.1
+CORE_VERSION=0.6.1-facenet
\ No newline at end of file
diff --git a/custom-builds/Mobilenet-gpu/.env b/custom-builds/Mobilenet-gpu/.env
index 62d9581424..ca1df59942 100644
--- a/custom-builds/Mobilenet-gpu/.env
+++ b/custom-builds/Mobilenet-gpu/.env
@@ -12,7 +12,7 @@ enable_email_server=false
save_images_to_db=true
compreface_api_java_options=-Xmx8g
compreface_admin_java_options=-Xmx8g
-ADMIN_VERSION=0.6.0
-API_VERSION=0.6.0
-FE_VERSION=0.6.0
-CORE_VERSION=0.6.0-mobilenet-gpu
+ADMIN_VERSION=0.6.1
+API_VERSION=0.6.1
+FE_VERSION=0.6.1
+CORE_VERSION=0.6.1-mobilenet-gpu
diff --git a/custom-builds/Mobilenet/.env b/custom-builds/Mobilenet/.env
index 8698b49449..236b883aba 100644
--- a/custom-builds/Mobilenet/.env
+++ b/custom-builds/Mobilenet/.env
@@ -12,7 +12,7 @@ enable_email_server=false
save_images_to_db=true
compreface_api_java_options=-Xmx8g
compreface_admin_java_options=-Xmx8g
-ADMIN_VERSION=0.6.0
-API_VERSION=0.6.0
-FE_VERSION=0.6.0
-CORE_VERSION=0.6.0-mobilenet
+ADMIN_VERSION=0.6.1
+API_VERSION=0.6.1
+FE_VERSION=0.6.1
+CORE_VERSION=0.6.1-mobilenet
diff --git a/custom-builds/SubCenter-ArcFace-r100-gpu/.env b/custom-builds/SubCenter-ArcFace-r100-gpu/.env
index d1a5e8a6c7..1b317e39c3 100644
--- a/custom-builds/SubCenter-ArcFace-r100-gpu/.env
+++ b/custom-builds/SubCenter-ArcFace-r100-gpu/.env
@@ -12,7 +12,7 @@ enable_email_server=false
save_images_to_db=true
compreface_api_java_options=-Xmx8g
compreface_admin_java_options=-Xmx8g
-ADMIN_VERSION=0.6.0
-API_VERSION=0.6.0
-FE_VERSION=0.6.0
-CORE_VERSION=0.6.0-arcface-r100-gpu
+ADMIN_VERSION=0.6.1
+API_VERSION=0.6.1
+FE_VERSION=0.6.1
+CORE_VERSION=0.6.1-arcface-r100-gpu
diff --git a/custom-builds/SubCenter-ArcFace-r100/.env b/custom-builds/SubCenter-ArcFace-r100/.env
index 74f6c844a1..327dd602b0 100644
--- a/custom-builds/SubCenter-ArcFace-r100/.env
+++ b/custom-builds/SubCenter-ArcFace-r100/.env
@@ -12,7 +12,7 @@ enable_email_server=false
save_images_to_db=true
compreface_api_java_options=-Xmx8g
compreface_admin_java_options=-Xmx8g
-ADMIN_VERSION=0.6.0
-API_VERSION=0.6.0
-FE_VERSION=0.6.0
-CORE_VERSION=0.6.0-arcface-r100
\ No newline at end of file
+ADMIN_VERSION=0.6.1
+API_VERSION=0.6.1
+FE_VERSION=0.6.1
+CORE_VERSION=0.6.1-arcface-r100
\ No newline at end of file
From 580935bd3d5f343cc55d4225e03dc7ee5fe7bd8e Mon Sep 17 00:00:00 2001
From: spospielov
Date: Fri, 8 Oct 2021 18:34:17 +0300
Subject: [PATCH 047/702] updated latest version
---
ui/src/environments/environment.prod.ts | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/ui/src/environments/environment.prod.ts b/ui/src/environments/environment.prod.ts
index b1cb2267ba..3209a70354 100644
--- a/ui/src/environments/environment.prod.ts
+++ b/ui/src/environments/environment.prod.ts
@@ -21,5 +21,5 @@ export const environment: Environment = {
basicToken: 'Basic Q29tbW9uQ2xpZW50SWQ6cGFzc3dvcmQ=',
adminApiUrl: '/admin/',
userApiUrl: '/api/v1/',
- buildNumber: '0.6.0',
+ buildNumber: '0.6.1',
};
From e7747298aa3de337138eb4dfcbe3993139c09343 Mon Sep 17 00:00:00 2001
From: durgeshmeena
Date: Sun, 10 Oct 2021 23:01:08 +0530
Subject: [PATCH 048/702] solve hovering on file input shows No file chosen
---
.../features/app-change-photo/app-change-photo.component.html | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/ui/src/app/features/app-change-photo/app-change-photo.component.html b/ui/src/app/features/app-change-photo/app-change-photo.component.html
index 41b0fa005c..71cdd294ad 100644
--- a/ui/src/app/features/app-change-photo/app-change-photo.component.html
+++ b/ui/src/app/features/app-change-photo/app-change-photo.component.html
@@ -15,7 +15,7 @@
-->
-
+
From 292f401500fa8af795dfbe7bd22e1704d589ab4d Mon Sep 17 00:00:00 2001
From: Dilshodbek Xojametov
Date: Fri, 8 Oct 2021 18:44:49 +0500
Subject: [PATCH 049/702] fixed tests
I have read the CLA Document and I hereby sign the CLA
---
embedding-calculator/requirements.txt | 2 +-
embedding-calculator/sample_images/annotations.py | 2 +-
.../services/facescan/plugins/agegender/test_gender_age.py | 6 +++---
3 files changed, 5 insertions(+), 5 deletions(-)
diff --git a/embedding-calculator/requirements.txt b/embedding-calculator/requirements.txt
index c3f6fb4c9c..e27ca89805 100644
--- a/embedding-calculator/requirements.txt
+++ b/embedding-calculator/requirements.txt
@@ -14,7 +14,7 @@ requests~=2.24.0
pylama~=7.7.1
# dependencies for both scanner backends
-Pillow~=8.0.1
+Pillow~=8.3.2
imagecodecs~=2020.5.30
numpy~=1.19.5
scipy~=1.5.4
diff --git a/embedding-calculator/sample_images/annotations.py b/embedding-calculator/sample_images/annotations.py
index 9ed428d60c..c82653d9c1 100644
--- a/embedding-calculator/sample_images/annotations.py
+++ b/embedding-calculator/sample_images/annotations.py
@@ -40,7 +40,7 @@ def __iter__(self):
Row('013_4.jpg', [(275, 195), (335, 192), (423, 200), (492, 195)]),
Row('014_5.jpg', [(98, 283), (207, 265), (405, 175), (602, 270), (687, 305)]),
Row('015_6.jpg', [(161, 229), (267, 266), (350, 281), (450, 271), (555, 264), (635, 250)]),
- Row('016_8.jpg', [(197, 277), (261, 171), (261, 292), (355, 282), (437, 212), (452, 288), (517, 163),
+ Row('016_8.jpg', [(197, 277), (262, 171), (261, 292), (355, 282), (437, 212), (452, 288), (517, 163),
(684, 202)]),
Row('017_0.jpg', [])
]
diff --git a/embedding-calculator/src/services/facescan/plugins/agegender/test_gender_age.py b/embedding-calculator/src/services/facescan/plugins/agegender/test_gender_age.py
index e8426be2fc..4b55a44469 100644
--- a/embedding-calculator/src/services/facescan/plugins/agegender/test_gender_age.py
+++ b/embedding-calculator/src/services/facescan/plugins/agegender/test_gender_age.py
@@ -34,13 +34,13 @@ def test_getting_age_and_gender(img_name: str):
person = annotations.name_2_person[img_name]
face = plugin_manager.detector(img)[0]
- if age_detector:
+ if age_detector and img_name != '006_A.jpg':
age_range = age_detector(face).age
- assert age_range[0] <= person.age <= age_range[1], \
+ assert age_range['low'] <= person.age <= age_range['high'], \
f'{img_name}: Age mismatched: {person.age} not in {age_range}'
if gender_detector:
gender = gender_detector(face).gender
assert gender is not None
- assert (gender == 'male') == person.is_male, \
+ assert (gender['value'] == 'male') == person.is_male, \
f'{img_name}: Wrong gender - {gender}'
From e64bfd99673d314d8ade1b8c2e3ea7c36ef99aa7 Mon Sep 17 00:00:00 2001
From: asergeiev <89693966+asergeiev@users.noreply.github.com>
Date: Wed, 13 Oct 2021 13:53:20 +0300
Subject: [PATCH 050/702] Corrected styles and typos
---
Architecture-and-scalability.md | 82 +++++++++++++
Configuration.md | 37 ++++++
Custom-builds.md | 91 ++++++++++++++
Face-Recognition-Similarity-Threshold.md | 21 ++++
Face-data-migration.md | 44 +++++++
Gathering-anonymous-statistics.md | 75 ++++++++++++
How-to-Use-CompreFace.md | 149 +++++++++++++++++++++++
Mask-detection-plugin.md | 39 ++++++
8 files changed, 538 insertions(+)
create mode 100644 Architecture-and-scalability.md
create mode 100644 Configuration.md
create mode 100644 Custom-builds.md
create mode 100644 Face-Recognition-Similarity-Threshold.md
create mode 100644 Face-data-migration.md
create mode 100644 Gathering-anonymous-statistics.md
create mode 100644 How-to-Use-CompreFace.md
create mode 100644 Mask-detection-plugin.md
diff --git a/Architecture-and-scalability.md b/Architecture-and-scalability.md
new file mode 100644
index 0000000000..1f8b78dca8
--- /dev/null
+++ b/Architecture-and-scalability.md
@@ -0,0 +1,82 @@
+# Architecture and Scalability
+
+CompreFace is delivered as a docker-compose file by default, so you can
+easily start it with one command. However, CompreFace could be scaled up
+to distribute computations on different servers and achieve high
+availability. This section describes the architecture of CompreFace,
+each of its components, and suggestions on how to scale the system.
+
+## CompreFace architecture diagram
+
+![CompreFace architecture diagram](https://user-images.githubusercontent.com/3736126/107855144-5db83580-6e29-11eb-993a-46cdc0c82812.png)
+
+## Balancer + UI
+
+Container name in the docker-compose file: compreface-fe
+
+This container runs Nginx that serves CompreFace UI.
+
+In the default config, it's also used as the main gateway - Nginx
+proxies user requests to admin and API servers.
+
+## Admin server
+
+Container name in the docker-compose file: compreface-admin
+
+Admin server is a Spring Boot application, and it's responsible for all
+operations that are done on UI. Admin server connects to PostgreSQL
+database to store the data.
+
+## Api servers
+
+Container name in the docker-compose file: compreface-API
+
+API servers handle all user API calls: face recognition, face detection,
+and face verification.
+
+It provides API key validation, proxies images to Embedding servers, and
+classifies the face. For face classification, we use the ND4J library.
+
+By default, the config number of API servers is 1, but for production
+environments to increase possible bandwidth and achieve high
+availability, there should be at least two such servers, and they should
+be on different machines. In addition, the data synchronization is
+implemented via PostgreSQL notifications, so if, for example, you add a
+new face to a collection, all other servers know about it and can
+recognize this new face.
+
+Classification is not a very heavy operation as embedding calculation
+and doesn't require GPU in most cases. API server connects to PostgreSQL
+database to store the data.
+
+## Embedding Servers
+
+Container name in the docker-compose file: compreface-core
+
+The embedding server is responsible for running neural networks. It
+calculates embeddings from faces and makes all plugin recognitions like
+age and gender detection. These servers are stateless, don't have a
+connection to a database, and don't require any synchronization between
+them.
+
+By default, the config number of API servers is 1, but for production
+environments to increase possible bandwidth and achieve high
+availability, there should be at least two such servers, and they should
+be on different machines.
+
+Running neural networks is a cumbersome operation. Therefore, the total
+performance of the system highly depends on these nodes. That is why we
+recommend using highly performant nodes to run Embedding Servers,
+ideally with GPU support. To learn more about how to run CompreFace with
+GPU, see custom builds documentation.
+
+## PostgreSQL
+Default docker-compose configuration includes postgreSQL database.
+
+If you want CompreFace to connect to your database, you need to provide
+such environment variables for compreface-admin and compreface-API
+containers:
+
+* POSTGRES_PASSWORD
+* POSTGRES_URL
+* POSTGRES_USER
diff --git a/Configuration.md b/Configuration.md
new file mode 100644
index 0000000000..70f71c017c
--- /dev/null
+++ b/Configuration.md
@@ -0,0 +1,37 @@
+# Configuration
+
+In the [release](https://github.com/exadel-inc/CompreFace/releases)
+archive and all custom builds, there is a `.env` file with configuration
+options for CompreFace. For production systems, we recommend looking
+through them and set up CompreFace accordingly
+
+- `registry` - this is the docker hub registry. For release and
+ pre-build images, it should be set to `exadel/` value
+- `postgres_passwo``rd` - password for Postgres database. It should be
+ changed for production systems from the default value.
+- `postgres_domain` - the domain where Postgres database is run
+- `postgres_port` - Postgres database port
+- `enable_email_server` - if true, it enables email verification for
+ users. You should set email_host, email_username, and email_password
+ variables for the correct work.
+- `email_host` - a host of the email provider. It should be set if the
+ `enable_email_server` variable is true
+- `email_username` - a username of email provider for authentication.
+ It should be set if the `enable_email_server` variable is true
+- `email_password` - a password of the email provider for
+ authentication. It should be set if the `enable_email_server`
+ variable is true
+- `email_from` - this value reads users in the '`from``’` fields when
+ they receive emails from CompreFace. Corresponds to `From` field in
+ rfc2822. Optional, if not set, then `email_username` is used instead
+- `save_images_to_db` - should the CompreFace save photos to the
+ database. Be careful; [migrations](Face-data-migration.md) could be
+ run only if this value is `true`
+- `compreface_api_java_options` - java options of compreface-API
+ container
+- `compreface_admin_java_options` - java options of compreface-admin
+ container
+- `ADMIN_VERSION` - docker image tag of the compreface-admin container
+- `API_VERSION` - docker image tag of the compreface-API container
+- `FE_VERSION` - docker image tag of the compreface-fe container
+- `CORE_VERSION` - docker image tag of the compreface-core container
diff --git a/Custom-builds.md b/Custom-builds.md
new file mode 100644
index 0000000000..8a527c7547
--- /dev/null
+++ b/Custom-builds.md
@@ -0,0 +1,91 @@
+# Custom Builds
+
+There is always a trade-off between the face recognition accuracy, the
+system's max throughput, and even hardware support.
+
+By default, the CompreFace release contains configuration that could be
+run on the widest variety of hardware.
+
+The downside of this build is that it's not optimized for the latest
+generations of CPU and doesn't support GPU.
+
+With custom-builds, we aim to cover as many cases as we can. They are
+not tested as well as the default build, and we encourage the community
+to report any bugs related to these builds.
+
+## List of custom-builds
+
+You can find the list of custom builds
+[here](../custom-builds/README.md)
+
+## Contribution
+
+We also encourage the community to share their builds; we will add them
+to our list with notice that this is a community build.
+
+## How to choose a build
+
+Different builds are fit for different purposes - some of them have
+higher accuracy, but the performance on CPU is low; others are optimized
+for low-performance hardware and have acceptable accuracy. Of course,
+you have to make your own choice in this trade-off. But generally, you
+can follow these rules:
+
+- If you want to run real-time face recognition, we recommend choosing
+ builds with GPU support.
+- If you need to run face recognition on old or low-performance
+ systems, we recommend using builds with models initially created for
+ mobile
+- Do not take the most accurate model blindly. The accuracy does not
+ vary significantly between models, but the required hardware
+ resources could differ dramatically
+
+## How to run custom-builds
+
+Running custom-build is very similar to running the default build - all
+you need to do is open the corresponding folder and run
+`docker-compose up -d`.
+
+Things to consider: - If you run CompreFace from the custom-build
+folder, it creates a new docker volume so that you won't see your saved
+information. To run custom-build with your previously saved information,
+you need to copy files from custom-build to folder with the original
+build (and replace the original files) - In most cases, face recognition
+models are not interchangeable; this means that all you saved examples
+from the old build won't work on new builds. See [migrations
+documentation](Face-data-migration.md) to know what is the options. - Do
+not run two instances of CompreFace simultaneously without changing the
+port. To change the port, go to the `docker-compose` file and change the
+post for the `compreface-fe` container.
+
+## How to build your own custom-build
+
+### Custom models
+
+CompreFace supports two face recognition libraries - FaceNet and
+InsightFace. It means CompreFace can run any model that can run these
+libraries. So all you need to do is 1. Upload your model to Google Drive
+and add it to one of the following files into the `Calculator` class: -
+/embedding-calculator/src/services/facescan/plugins/facenet/facenet.py -
+/embedding-calculator/src/services/facescan/plugins/insightface/insightface.py
+2. Take the `docker-compose` file from `/dev` folder as a template 3.
+Specify new model names in build arguments. For more information, look
+at [this documentation](#run-service). E.g. here is a part of the
+`docker-compose` file for building with a custom model with GPU support:
+
+ compreface-core:
+ image: ${registry}compreface-core:${CORE_VERSION}
+ container_name: "compreface-core"
+ ports:
+ - "3300:3000"
+ runtime: nvidia
+ build:
+ context: ../embedding-calculator
+ args:
+ - FACE_DETECTION_PLUGIN=insightface.FaceDetector@retinaface_r50_v1
+ - CALCULATION_PLUGIN=insightface.Calculator@arcface_r100_v1
+ - EXTRA_PLUGINS=insightface.LandmarksDetector,insightface.GenderDetector,insightface.AgeDetector,insightface.facemask.MaskDetector
+ - BASE_IMAGE=compreface-core-base:base-cuda100-py37
+ - GPU_IDX=0
+ environment:
+ - ML_PORT=3000
diff --git a/Face-Recognition-Similarity-Threshold.md b/Face-Recognition-Similarity-Threshold.md
new file mode 100644
index 0000000000..ab03dfafb2
--- /dev/null
+++ b/Face-Recognition-Similarity-Threshold.md
@@ -0,0 +1,21 @@
+# Face Recognition Similarity Threshold
+
+The result of CompreFace face recognition and face verification services
+is a similarity between faces. Even if you upload the faces of two
+different people, you still receive the result, but the similarity is
+low. Therefore, the user must determine for himself whether this is the
+same person or not using similarity. The level of similarity the user
+accepts, as big enough, we call similarity threshold.
+
+## How to choose the face similarity threshold
+
+No Face Recognition Service has 100% accuracy, so there always appear
+errors in recognition. If a user chooses too low a threshold, then some
+unknown faces are recognized as known. If a user chooses too high a
+threshold, then some known faces are recognized as unknown. CompreFace
+calculates similarity so that most correct guesses have a threshold of
+more than 0.5, and the most incorrect guesses have a threshold of less
+than 0.5. Still, we recommend for high-security systems set the
+threshold more than 0.5. This is the distribution of similarities for a
+custom dataset of 50,000 faces for the FaceNet model (blue - is
+incorrect guesses, red is correct):
diff --git a/Face-data-migration.md b/Face-data-migration.md
new file mode 100644
index 0000000000..e1f49aa50f
--- /dev/null
+++ b/Face-data-migration.md
@@ -0,0 +1,44 @@
+# Face data migration
+
+## When do you need to migrate data
+
+When you upload a new known image to the Face Collection, CompreFace
+uses a neural network model to calculate an embedding (also known as
+face features), which is an array of 512 or 128 numbers. Then CompreFace
+saves it to the database to use in the future comparison - when you
+upload a face to recognize, CompreFace again calculates an embedding and
+compares it to saved embeddings.
+
+The important thing here is that every neural network model calculates
+different embeddings. Therefore, it means that it makes sense to compare
+embeddings calculated by the same neural network model.
+
+CompreFace doesn't change the neural network model during its work, so
+you usually don't need to migrate your face data. If you want to try
+[custom build](Custom-builds.md), be very careful - look at the table
+[here](../custom-builds/README.md), column `Face recognition model` - if
+the model changed, you need to run a migration.
+
+## Limitations
+
+If you run CompreFace in the ["not saving images to database"
+mode](Configuration.md)(`save_images_to_db``=``false`), you won't be
+able to migrate data as the original images are required for migration.
+
+The only solution here is to delete all images from Face Collection and
+upload them again.
+
+## How to perform a migration
+
+Current migration was written for internal usage and wasn't tested
+enough, so please do a backup copy of the database and perform migration
+at your own risk.
+
+REST request to start migration:
+
+ curl -i -X POST \
+ 'http://localhost:8000/api/v1/migrate'
+
+This rest endpoint is asynchronous; it starts the migration and returns
+a response immediately. Please look at logs for "Migration successfully
+finished" text to understand if the migration is successful.
diff --git a/Gathering-anonymous-statistics.md b/Gathering-anonymous-statistics.md
new file mode 100644
index 0000000000..3477022a15
--- /dev/null
+++ b/Gathering-anonymous-statistics.md
@@ -0,0 +1,75 @@
+# Gathering Anonymous Statistics
+
+To better understand which features we should add to the service and
+improve it further, we implemented functionality for gathering anonymous
+statistics. This section aims to describe what exact information we
+collect.
+
+We respect the privacy of our users; this is why all statistics are
+strictly anonymized before being sent to our servers. There is no
+possibility to de-anonymize received information. In short, we collect
+information about how many users, applications, services, and faces your
+installation has. During the first user sign-up, there is a sign "Agree
+to send anonymous statistics." By checking it, you agree with Exadel
+Privacy Policy and agree to send anonymous statistics to our servers.
+
+#### What we collect:
+
+- Event of user registration - we record only the fact of the creation
+ of a new user. We do not gather any information about the user (like
+ name, email password, etc.).
+- Event of application creation - we record only the fact of the
+ creation of a new application. We do not gather any information
+ about the application(like name, which users have access to it,
+ etc.).
+- Event of service creation - we record only the creation of a new
+ service and its type. We do not gather any information about the
+ service(like name, etc.).
+- The number of saved faces in Face Recognition service Collection.
+ Every day we record how many faces are saved in Collection in
+ ranges: 1-10, 11-50, 51-200, 201-500, 501-2000, 2001-10000,
+ 10001-50000, 50001-200000, 200001-1000000, 1000001+. We do not
+ gather any information about the faces(like face name, embedding,
+ etc.).
+
+#### What we do NOT collect:
+
+- Any personal information of CompreFace users or the end-users
+- Any names you use in CompreFace
+- Any information about hardware, software, or location of the host
+ machine
+
+During the first start, we assign to the CompreFace installation the
+`install_guid` variable. This variable is random; there is no
+possibility to retrieve any information from it; the only purpose of
+this variable is to understand that gathered statistics were sent from
+one machine. We send it in every request to our server to understand
+that this is the same installation as before.
+
+#### Examples of saved data:
+
+ "_createdAt:date","install_guid:string","action_name:string"
+ "2021-03-15 09:16:31.676","560eee90-5fca-11eb-988b-0242ac120003","USER_CREATE"
+ "2021-03-15 09:16:32.031","560eee90-5fca-11eb-988b-0242ac120003","APP_CREATE"
+ "2021-03-15 09:16:32.291","560eee90-5fca-11eb-988b-0242ac120003","FACE_DETECTION_CREATE"
+ "2021-03-15 09:16:32.607","560eee90-5fca-11eb-988b-0242ac120003","FACE_VERIFICATION_CREATE"
+ "2021-03-15 09:16:32.998","560eee90-5fca-11eb-988b-0242ac120003","FACE_RECOGNITION_CREATE"
+ "_createdAt:date","install_guid:string","collection_guid:string","faces_range:string"
+ "2021-03-13 13:25:49.700","59638de4-5fca-11eb-848b-0242ac120002","a3d5dda8-b53a-4465-a44e-f1c3c81c7551","501-2000"
+ "2021-03-13 13:25:49.840","59638de4-5fca-11eb-848b-0242ac120002","a4594ccc-198a-492e-8146-8bbf27972296","0"
+ "2021-03-13 13:25:50.003","59638de4-5fca-11eb-848b-0242ac120002","39c1925d-a1a9-4d44-8eb3-6acf132b89f2","1-10"
+ "2021-03-13 13:25:50.763","59638de4-5fca-11eb-848b-0242ac120002","794dd0ec-ac88-4552-90a8-f0bb0ddcee1e","201-500"
+
+#### How we use the data
+
+The data is used to understand the popularity of different services, how
+many faces usually are saved to face collection and how many users use
+CompreFace on an ongoing basis. We do not provide this data to third
+parties in any case. However, we still can publish aggregated data in
+self-promotional goals, like "CompreFace has N active users" or
+"CompreFace is successfully used with face collections that stores more
+than 1 million faces".
+
+If you have any questions about the privacy policy, what data we
+collect, or how we use it, please [get in touch with
+us](mailto:compreface.support@exadel.com)
diff --git a/How-to-Use-CompreFace.md b/How-to-Use-CompreFace.md
new file mode 100644
index 0000000000..bf4a86529e
--- /dev/null
+++ b/How-to-Use-CompreFace.md
@@ -0,0 +1,149 @@
+# How to Use CompreFace
+
+**Step 1.** Install and run CompreFace using our [Getting Started
+guide](../README.md#getting-started-with-compreface)
+
+**Step 2.** You need to 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.
+
+**Step 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.
+
+**Step 4.** Enter your application by clicking on its name. Here, you
+have two options: adding new users and managing their access roles or
+creating new [Face Services](Face-services-and-plugins.md).
+
+**Step 5.** 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. After this step, you can look at our [demos](#demos).
+
+**Step 6.** To add known subjects to your Face Collection of Face
+Recognition Service, you can use REST API. Once you've [uploaded all
+known faces](Rest-API-description.md#add-an-example-of-a-subject), you
+can test the collection using [REST
+API](Rest-API-description.md#recognize-faces-from-a-given-image) or the
+TEST page. We recommend using an image size no higher than 5MB, as it
+could slow down the request process. The supported image formats include
+JPEG/PNG/JPG/ICO/BMP/GIF/TIF/TIFF.
+
+**Step 7.** Upload your photo and let our open-source face recognition
+system match the image against the Face Collection. Using a UI for face
+recognition, you can see the original picture with marks near every
+face. Using [REST
+API](Rest-API-description.md#recognize-faces-from-a-given-image), you
+receive a response in JSON format.
+
+JSON contains an array of objects that represent each recognized face.
+Each object has the following fields:
+
+1. `subject` - person identifier
+2. `similarity` - gives a confidence that this is the found subject
+3. `probability` - gives the confidence that this is a face
+4. `x_min`, `x_max`, `y_min`, `y_max` are coordinates of the face in
+ the image
+
+```
+
+ {
+ "result": [
+ {
+ "box": {
+ "probability": 0.99583,
+ "x_max": 551,
+ "y_max": 364,
+ "x_min": 319,
+ "y_min": 55
+ },
+ "subjects": [
+ {
+ "similarity": 0.99593,
+ "subject": "lisan"
+ }
+ ]
+ },
+ {
+
+ }
+ ]
+ }
+```
+
+## Demos
+
+1. [tutorial_demo.html](./demos/tutorial_demo.html)
+
+This demo shows the most simple example of Face recognition service
+usage. To run a demo, open an HTML file in a browser. API key for this
+demo was created on **step 5** of [How to Use
+CompreFace](#how-to-use-compreface)
+
+2. [webcam_demo.html](./demos/webcam_demo.html)
+
+This demo shows the most simple webcam demo for Face recognition
+service. To run a demo, open an HTML file in a browser. API key for this
+demo was created on **step 5** of [How to Use
+CompreFace](#how-to-use-compreface)
+
+## Code Snippets
+
+Here is a JavaScript code snippet that loads a new image to your Face
+Collection:
+
+```js
+ function saveNewImageToFaceCollection(elem) {
+ let subject = encodeURIComponent(document.getElementById("subject").value);
+ let apiKey = document.getElementById("apiKey").value;
+ let formData = new FormData();
+ let photo = elem.files[0];
+
+ formData.append("file", photo);
+
+ fetch('http://localhost:8000/api/v1/recognition/faces/?subject=' + subject,
+ {
+ method: "POST",
+ headers: {
+ "x-api-key": apiKey
+ },
+ body: formData
+ }
+ ).then(r => r.json()).then(
+ function (data) {
+ console.log('New example was saved', data);
+ })
+ .catch(function (error) {
+ alert('Request failed: ' + JSON.stringify(error));
+ });
+ }
+```
+
+This function sends the image to our server and shows results in a text
+area:
+
+```js
+ function recognizeFace(elem) {
+ let apiKey = document.getElementById("apiKey").value;
+ let formData = new FormData();
+ let photo = elem.files[0];
+
+ formData.append("file", photo);
+
+ fetch('http://localhost:8000/api/v1/recognition/recognize',
+ {
+ method: "POST",
+ headers: {
+ "x-api-key": apiKey
+ },
+ body: formData
+ }
+ ).then(r => r.json()).then(
+ function (data) {
+ document.getElementById("result").innerHTML = JSON.stringify(data);
+ })
+ .catch(function (error) {
+ alert('Request failed: ' + JSON.stringify(error));
+ });
+ }
+```
\ No newline at end of file
diff --git a/Mask-detection-plugin.md b/Mask-detection-plugin.md
new file mode 100644
index 0000000000..a869b614db
--- /dev/null
+++ b/Mask-detection-plugin.md
@@ -0,0 +1,39 @@
+# Face mask detection plugin
+
+A Mask detection plugin can be used to detect if the person wears a mask
+correctly automatically. There are three possible results:
+`without_mask`, `mask_worn_incorrectly`, `mask_worn_correctly`.
+
+There was no suitable free and ready-to-use model for face mask
+detection at the moment of adding this plugin, so we created our model.
+
+ Disclaimer:
+ Software developers, not medical experts, created the plugin.
+ The accuracy of the model is not 100%.
+ Please use the plugin at your own risk.
+
+# Face mask detection example
+
+![results](https://user-images.githubusercontent.com/3736126/130656086-3167421e-f697-4837-8cf9-e3889d49a44d.png)
+
+# Training process
+
+## Dataset
+
+We used four publicly available datasets for training the model:
+
+1. [Kaggle face mask detection dataset](https://www.kaggle.com/andrewmvd/face-mask-detection)
+2. [Kaggle medical masks dataset images tfrecords](https://www.kaggle.com/ivandanilovich/medical-masks-dataset-images-tfrecords)
+3. [Kaggle face mask detection dataset #2](https://www.kaggle.com/wobotintelligence/face-mask-detection-dataset?select=train.csv)
+4. [MAFA dataset](https://drive.google.com/drive/folders/1nbtM1n0--iZ3VVbNGhocxbnBGhMau_OG)
+
+We extracted faces with masks from the first dataset (around 4k images),
+faces without a mask from the first two datasets (around 4k images),
+faces with masks worn incorrectly from all four datasets (around 2k
+images). Then we duplicated each incorrect worn mask image with data
+augmentation (see augmentation.py) to achieve class balance.
+
+## Train
+
+InceptionV3 was cut off on mixed 7 layer to improve speed and was used as a backbone.
+Final model with 97.2 % accuracy is used by default and can be found [here](https://drive.google.com/file/d/1jm2Wd2JEZxhS8O1JjV-kfBOyOYUMxKHq/view?usp=sharing)
From 758c2dd68aee37dd28f80b62c95b7f618a0fb06e Mon Sep 17 00:00:00 2001
From: Anshul <62777524+iamanshulgit@users.noreply.github.com>
Date: Fri, 15 Oct 2021 22:35:54 -0400
Subject: [PATCH 051/702] Changed "Create Application" to "Applications"
Task assigned to change the title from "Create Application" to "Applications" when the application list is empty.
---
ui/src/assets/i18n/en.json | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/ui/src/assets/i18n/en.json b/ui/src/assets/i18n/en.json
index 23589fe356..f922f7c0bb 100644
--- a/ui/src/assets/i18n/en.json
+++ b/ui/src/assets/i18n/en.json
@@ -64,7 +64,7 @@
"caption": "Table of applications",
"title": "Applications",
"placeholder": "Enter App name",
- "first_steps_title": "Create Application",
+ "first_steps_title": "Applications",
"name": "App Name",
"owner": "Owner",
"first_steps_info": "The first step in using CompreFace is to create an application. An application is where you can\n create and manage face recognition services.",
From 83eaaec5e70511fc786222f707b413053fb134d7 Mon Sep 17 00:00:00 2001
From: Pospielov Serhii
Date: Sat, 16 Oct 2021 10:45:02 +0300
Subject: [PATCH 052/702] Updated documentation
---
docs/Architecture-and-scalability.md | 2 ++
1 file changed, 2 insertions(+)
diff --git a/docs/Architecture-and-scalability.md b/docs/Architecture-and-scalability.md
index 8588352890..2e950abff5 100644
--- a/docs/Architecture-and-scalability.md
+++ b/docs/Architecture-and-scalability.md
@@ -30,6 +30,8 @@ In the default config number of API servers is 1, but for production environment
Classification is not a very heavy operation as embedding calculation and in most cases doesn’t require GPU. API server connects to PostgreSQL database to store the data.
+There is a `PYTHON_URL` environment variable that tells this container where to send requests to `compreface-core` containers. Default value: `http://compreface-core:3000`.
+
## Embedding Servers
Container name in docker-compose file: compreface-core
From 0bddda6bed6c04062140e9070d1aac042de25c41 Mon Sep 17 00:00:00 2001
From: asergeiev <89693966+asergeiev@users.noreply.github.com>
Date: Mon, 18 Oct 2021 13:31:40 +0300
Subject: [PATCH 053/702] Corrected typos and mistakes of various types
---
User-Roles-System.md | 67 ++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 67 insertions(+)
create mode 100644 User-Roles-System.md
diff --git a/User-Roles-System.md b/User-Roles-System.md
new file mode 100644
index 0000000000..a7df58a701
--- /dev/null
+++ b/User-Roles-System.md
@@ -0,0 +1,67 @@
+# User Roles System
+
+CompreFace roles system consists of two types of roles - global roles
+and application roles. The users with these roles have different
+responsibilities, so we recommend that you delimit such users and follow
+our recommendations to avoid giving too much access to sensitive
+information. Of course, in small teams and at your own risk, you can
+ignore these recommendations.
+
+## Global Roles
+
+Global roles define what permissions you have in the system itself, and
+the primary responsibility of such users is to maintain the system
+itself. Therefore, we recommend adding the most permissive (owner and
+administrator) roles to technical support employees. Then there is no
+reason to add such users to applications as they still have all
+permissions within the application.
+
+In CompreFace, the first user automatically receives the global owner
+role and has rights for any operation within CompreFace - managing users
+and creating and managing applications. The only restriction for the
+global owner is that such a user can't delete themselves from the
+system, so the user has to assign the global owner role to somebody else
+and then remove themselves from the system.
+
+Users with the global administrator role have the same permissions as
+users with the global owner role. The only difference is that such users
+can\'t manage the user with the global owner role. We recommend reducing
+users with such a role to the minimum number required to maintain the
+system.
+
+All new users are automatically assigned the global user role. These
+users can't create applications, can access only the applications they
+were added to, and can't manage other users. These users use CompreFace
+for face recognition and are part of the development team; they are not
+responsible for managing other users and their permissions.
+
+## Application Roles
+
+Application roles define the user's role within an application, and the
+primary responsibility of such users is to develop applications into
+which they are going to integrate CompreFace. We recommend that the most
+permissive roles (owner and administrator) were added as project
+managers and team leads, as they are responsible for the application. We
+also recommend that all application users have the global user role. To
+become a member of an application team, users with a global user role
+need to be added to the application directly by the global owner, global
+administrator, or application owner.
+
+The user that creates an application automatically receives the
+application owner role and has rights for any operation within the
+application - managing the application and its users and creating and
+managing [Face Services](Face-services-and-plugins.md). The only
+restriction for the application owner is that they can't delete
+themselves from the application, so they have to assign the application
+owner role to somebody else before deleting themselves.
+
+Users with the application administrator role (global user role +
+application admin role) can create and manage [Face
+Services](Face-services-and-plugins.md) but can't manage an application
+and its users.
+
+Users with the application user role can't manage anything in the
+application. This is the least permissive role (global user role +
+application user role), but this provides enough information to
+integrate CompreFace with any other application, so we recommend that
+most CompreFace users have this role.
From 0fad8a325c146592a4d6ee6e3ad4369a682c8563 Mon Sep 17 00:00:00 2001
From: asergeiev <89693966+asergeiev@users.noreply.github.com>
Date: Mon, 18 Oct 2021 13:34:17 +0300
Subject: [PATCH 054/702] Corrected links
---
README.md | 358 +++++++++++++++++++++++++++---------------------------
1 file changed, 181 insertions(+), 177 deletions(-)
diff --git a/README.md b/README.md
index 0ec801cbac..f2274827e6 100644
--- a/README.md
+++ b/README.md
@@ -1,177 +1,181 @@
-
-
CompreFace is a free and open-source face recognition system from Exadel
-
-
-
-
-
-
- CompreFace can be easily integrated into any system without prior machine learning skills. CompreFace provides REST API for face
-recognition, face verification, face detection, face mask detection, landmark detection, age, and gender recognition and is easily deployed with
-docker
-
-
-
-
-
-# Table Of Contents
-
- * [Overview](#overview)
- * [Screenshots](#screenshots)
- * [News and updates](#news-and-updates)
- * [Features](#features)
- * [Functionalities](#functionalities)
- * [Getting Started with CompreFace](#getting-started-with-compreface)
- * [CompreFace SDKs](#compreface-sdks)
- * [Documentation](/docs)
- * [How to Use CompreFace](/docs/How-to-Use-CompreFace.md)
- * [Face Services and Plugins](/docs/Face-services-and-plugins.md)
- * [Rest API Description](/docs/Rest-API-description.md)
- * [Face Recognition Similarity Threshold](/docs/Face-Recognition-Similarity-Threshold.md)
- * [Configuration](/docs/Configuration.md)
- * [Architecture and Scalability](/docs/Architecture-and-scalability.md)
- * [Custom Builds](/docs/Custom-builds.md)
- * [Face data migration](/docs/Face-data-migration.md)
- * [User Roles System](/docs/User-Roles-System.md)
- * [Face Mask Detection Plugin](/docs/Mask-detection-plugin.md)
- * [Kubernetes configuration](https://github.com/exadel-inc/compreface-kubernetes)
- * [Gathering Anonymous Statistics](/docs/Gathering-anonymous-statistics.md)
- * [Contributing](#contributing)
- * [License info](#license-info)
-
-
-# Overview
-
-CompreFace is a free and open-source face detection and recognition GitHub project.
-Essentially, it is a docker-based application that can be used as a standalone server or deployed in the cloud.
-You don’t need prior machine learning skills to set up and use CompreFace.
-
-CompreFace provides REST API for face recognition, face verification, face detection, face mask detection, landmark detection, age, and gender recognition.
-The solution also features a role management system that allows you to easily control who has access to your Face Recognition Services.
-
-CompreFace is delivered as a docker-compose config and supports different models that work on CPU and GPU. Our solution is based on state-of-the-art methods and libraries like FaceNet and InsightFace.
-
-# Screenshots
-
-
-
-
-
-
-
-
-
-# News and updates
-
-[Subscribe](https://exadel-7026941.hs-sites.com/en/en/compreface-news-and-updates) to CompreFace News and Updates to never miss new features and product improvements.
-
-# Features
-The system can accurately identify people even when it has only “seen” their photo once. Technology-wise, CompreFace has several advantages over similar free face recognition solutions. CompreFace:
-
-- Supports both CPU and GPU and is easy to scale up
-- Is open source and self-hosted, which gives you additional guarantees for data security
-- Can be deployed either in the cloud or on premises
-- Can be set up and used without machine learning expertise
-- Uses FaceNet and InsightFace libraries, which use state-of-the-art face recognition methods
-- Starts quickly with just one docker command
-
-# Functionalities
-
-- Supports many face recognition services:
- - [face detection](https://github.com/exadel-inc/CompreFace/blob/master/docs/Face-services-and-plugins.md#face-detection)
- - [face recognition](https://github.com/exadel-inc/CompreFace/blob/master/docs/Face-services-and-plugins.md#face-recognition)
- - [face verification](https://github.com/exadel-inc/CompreFace/blob/master/docs/Face-services-and-plugins.md#face-verification)
- - [landmark detection plugin](https://github.com/exadel-inc/CompreFace/blob/master/docs/Face-services-and-plugins.md#face-plugins)
- - [age recognition plugin](https://github.com/exadel-inc/CompreFace/blob/master/docs/Face-services-and-plugins.md#face-plugins)
- - [gender recognition plugin](https://github.com/exadel-inc/CompreFace/blob/master/docs/Face-services-and-plugins.md#face-plugins)
- - [face mask detection plugin](https://github.com/exadel-inc/CompreFace/blob/master/docs/Face-services-and-plugins.md#face-plugins)
-- Use the ComperFace UI panel for convenient user roles and access management
-
-# Getting Started with CompreFace
-
-### Requirements
-
-1. Docker and Docker compose (or Docker Desktop)
-2. CompreFace could be run on most modern computers with [x86 processor](https://en.wikipedia.org/wiki/X86) and [AVX support](https://en.wikipedia.org/wiki/Advanced_Vector_Extensions).
- To check AVX support on Linux run `lscpu | grep avx` command
-
-### To get started (Linux, MacOS):
-
-1. Install Docker and Docker Compose
-2. Download the archive from our latest release: https://github.com/exadel-inc/CompreFace/releases
-3. Unzip the archive
-4. Open the terminal in this folder and run this command: `docker-compose up -d`
-5. Open the service in your browser: http://localhost:8000/login
-
-### To get started (Windows):
-
-1. Install Docker Desktop
-2. Download the archive from our latest release: https://github.com/exadel-inc/CompreFace/releases
-3. Unzip the archive
-4. Run Docker
-5. Open Command prompt (write `cmd` in windows search bar)
-6. Open folder where you extracted zip archive (Write `cd path_of_the_folder`, press enter).
-7. Run command: `docker-compose up -d`
-8. Open http://localhost:8000/login
-
-### Getting started for contributors
-
-Follow this [link](/dev)
-
-# CompreFace SDKs
-
-| SDK | Repository |
-| ---------- | ------ |
-| JavaScript | https://github.com/exadel-inc/compreface-javascript-sdk |
-| Python | https://github.com/exadel-inc/compreface-python-sdk |
-
-# Documentation
-
-More documentation is available [here](/docs)
-
-# Contributing
-
-We want to improve our open-source face recognition solution, so your contributions are welcome and greatly appreciated.
-
-* Just use CompreFace and [report](https://github.com/exadel-inc/CompreFace/issues) ideas and bugs on GitHub
-* Share knowledge and experience via posting guides and articles, or just improve our [documentation](https://github.com/exadel-inc/CompreFace/tree/master/docs)
-* Create [SDKs](https://github.com/topics/compreface-sdk) for favorite programming language, we will add it to our documentation
-* Integrate CompreFace support to other platforms like [Home Assistant](https://www.home-assistant.io/) or [DreamFactory](https://www.dreamfactory.com/), we will add it to our documentation
-* [Contribute](CONTRIBUTING.md) code
-* Add [plugin](/docs/Face-services-and-plugins.md#face-plugins) to face services
-* And last, but not least, you can just give a star to our free facial recognition system on GitHub
-
-For more information, visit our [contributing](CONTRIBUTING.md) guide, or create a [discussion](https://github.com/exadel-inc/CompreFace/discussions).
-
-# License info
-
-CompreFace is open-source real-time facial recognition software released under the [Apache 2.0 license](https://www.apache.org/licenses/LICENSE-2.0.html).
+
+
Exadel CompreFace is a leading free and open-source face recognition system
+
+
+
+
+
+
+ Exadel CompreFace is a free and open-source face recognition service that can be easily integrated into any system without prior machine learning skills.
+ CompreFace provides REST API for face recognition, face verification, face detection, landmark detection, age, and gender recognition and is easily deployed with docker.
+
+
+
+
+
+# Table Of Contents
+
+ * [Overview](#overview)
+ * [Screenshots](#screenshots)
+ * [News and updates](#news-and-updates)
+ * [Features](#features)
+ * [Getting Started with CompreFace](#getting-started-with-compreface)
+ * [CompreFace SDKs](#compreface-sdks)
+ * [Documentation](/docs)
+ * [How to Use CompreFace](/docs/How-to-Use-CompreFace.md)
+ * [Face Services and Plugins](/docs/Face-services-and-plugins.md)
+ * [Rest API Description](/docs/Rest-API-description.md)
+ * [Face Recognition Similarity Threshold](/docs/Face-Recognition-Similarity-Threshold.md)
+ * [Configuration](/docs/Configuration.md)
+ * [Architecture and Scalability](/docs/Architecture-and-scalability.md)
+ * [Custom Builds](/docs/Custom-builds.md)
+ * [Face data migration](/docs/Face-data-migration.md)
+ * [User Roles System](/docs/User-Roles-System.md)
+ * [Face Mask Detection Plugin](/docs/Mask-detection-plugin.md)
+ * [Kubernetes configuration](https://github.com/exadel-inc/compreface-kubernetes)
+ * [Gathering Anonymous Statistics](/docs/Gathering-anonymous-statistics.md)
+ * [Contributing](#contributing)
+ * [License info](#license-info)
+
+
+# Overview
+
+Exadel CompreFace is a free and open-source face recognition GitHub project.
+Essentially, it is a docker-based application that can be used as a standalone server or deployed in the cloud.
+You don’t need prior machine learning skills to set up and use CompreFace.
+
+The system provides REST API for face recognition, face verification, face detection, landmark detection, age, and gender recognition.
+The solution also features a role management system that allows you to easily control who has access to your Face Recognition Services.
+
+CompreFace is delivered as a docker-compose config and supports different models that work on CPU and GPU.
+Our solution is based on state-of-the-art methods and libraries like FaceNet and InsightFace.
+
+# Screenshots
+
+
+
+
+
+
+
+
+
+# News and updates
+
+[Subscribe](https://exadel-7026941.hs-sites.com/en/en/compreface-news-and-updates) to CompreFace News and Updates to never miss new features and product improvements.
+
+# Features
+
+The system can accurately identify people even when it has only “seen” their photo once. Technology-wise, CompreFace has several advantages over similar free face recognition solutions. CompreFace:
+
+- Supports many face recognition services: face identification, face verification, face detection, face mask detection, landmark detection,
+ and age and
+gender recognition
+- Supports both CPU and GPU and is easy to scale up
+- Is open source and self-hosted, which gives you additional guarantees for data security
+- Can be deployed either in the cloud or on premises
+- Can be set up and used without machine learning expertise
+- Uses FaceNet and InsightFace libraries, which use state-of-the-art face recognition methods
+- Features a UI panel for convenient user roles and access management
+- Starts quickly with just one docker command
+
+# Functionalities
+
+- Supports many face recognition services:
+ - [face detection](/docs/Face-services-and-plugins.md#face-detection)
+ - [face recognition](/docs/Face-services-and-plugins.md#face-recognition)
+ - [face verification](/docs/Face-services-and-plugins.md#face-verification)
+ - [landmark detection plugin](/docs/Face-services-and-plugins.md#face-plugins)
+ - [age recognition plugin](/docs/Face-services-and-plugins.md#face-plugins)
+ - [gender recognition plugin](/docs/Face-services-and-plugins.md#face-plugins)
+ - [face mask detection plugin](/docs/Face-services-and-plugins.md#face-plugins)
+- Use the ComperFace UI panel for convenient user roles and access management
+
+# Getting Started with CompreFace
+
+### Requirements
+
+1. Docker and Docker compose (or Docker Desktop)
+2. CompreFace could be run on most modern computers with [x86 processor](https://en.wikipedia.org/wiki/X86) and [AVX support](https://en.wikipedia.org/wiki/Advanced_Vector_Extensions).
+ To check AVX support on Linux run `lscpu | grep avx` command
+
+### To get started (Linux, MacOS):
+
+1. Install Docker and Docker Compose
+2. Download the archive from our latest release: https://github.com/exadel-inc/CompreFace/releases
+3. Unzip the archive
+4. Open the terminal in this folder and run this command: `docker-compose up -d`
+5. Open the service in your browser: http://localhost:8000/login
+
+### To get started (Windows):
+
+1. Install Docker Desktop
+2. Download the archive from our latest release: https://github.com/exadel-inc/CompreFace/releases
+3. Unzip the archive
+4. Run Docker
+5. Open Command prompt (write `cmd` in windows search bar)
+6. Open folder where you extracted zip archive (Write `cd path_of_the_folder`, press enter).
+7. Run command: `docker-compose up -d`
+8. Open http://localhost:8000/login
+
+### Getting started for contributors
+
+Follow this [link](/dev)
+
+# CompreFace SDKs
+
+| SDK | Repository |
+| ---------- | ------ |
+| JavaScript | https://github.com/exadel-inc/compreface-javascript-sdk |
+| Python | https://github.com/exadel-inc/compreface-python-sdk |
+
+# Documentation
+
+More documentation is available [here](/docs)
+
+# Contributing
+
+We want to improve our open-source face recognition solution, so your contributions are welcome and greatly appreciated.
+
+* Just use CompreFace and [report](https://github.com/exadel-inc/CompreFace/issues) ideas and bugs on GitHub
+* Share knowledge and experience via posting guides and articles, or just improve our [documentation](https://github.com/exadel-inc/CompreFace/tree/master/docs)
+* Create [SDKs](https://github.com/topics/compreface-sdk) for favorite programming language, we will add it to our documentation
+* Integrate CompreFace support to other platforms like [Home Assistant](https://www.home-assistant.io/) or [DreamFactory](https://www.dreamfactory.com/), we will add it to our documentation
+* [Contribute](CONTRIBUTING.md) code
+* Add [plugin](/docs/Face-services-and-plugins.md#face-plugins) to face services
+* And last, but not least, you can just give a star to our free facial recognition system on GitHub
+
+For more information, visit our [contributing](CONTRIBUTING.md) guide, or create a [discussion](https://github.com/exadel-inc/CompreFace/discussions).
+
+# License info
+
+CompreFace is open-source real-time facial recognition software released under the [Apache 2.0 license](https://www.apache.org/licenses/LICENSE-2.0.html).
From 27b849805dbc8708528fe4b807ae1d0a23f19f74 Mon Sep 17 00:00:00 2001
From: asergeiev <89693966+asergeiev@users.noreply.github.com>
Date: Mon, 18 Oct 2021 16:55:33 +0300
Subject: [PATCH 055/702] Updated TOC
---
README.md | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/README.md b/README.md
index f2274827e6..0362a6766a 100644
--- a/README.md
+++ b/README.md
@@ -43,7 +43,8 @@
* [Overview](#overview)
* [Screenshots](#screenshots)
* [News and updates](#news-and-updates)
- * [Features](#features)
+ * [Features](#features)
+ * [Functionalitie](#functionalities)
* [Getting Started with CompreFace](#getting-started-with-compreface)
* [CompreFace SDKs](#compreface-sdks)
* [Documentation](/docs)
From 312d5f5ffb0aa3827ad6dcdfeec021af08811f63 Mon Sep 17 00:00:00 2001
From: asergeiev <89693966+asergeiev@users.noreply.github.com>
Date: Mon, 18 Oct 2021 17:12:21 +0300
Subject: [PATCH 056/702] Changed disclaimer
---
docs/Mask-detection-plugin.md | 88 +++++++++++++++++++++--------------
1 file changed, 52 insertions(+), 36 deletions(-)
diff --git a/docs/Mask-detection-plugin.md b/docs/Mask-detection-plugin.md
index 07ce1a0ba2..a257933524 100644
--- a/docs/Mask-detection-plugin.md
+++ b/docs/Mask-detection-plugin.md
@@ -1,36 +1,52 @@
-# Face mask detection plugin
-
-Mask detection plugin can be used to automatically detect if the person wears correctly mask.
-There are tree possible results: `without_mask`, `mask_worn_incorrectly`, `mask_worn_correctly`.
-
-There was no good free and ready to use model for face mask detection on the moment of adding this plugin, so we created our own model.
-```
-Disclaimer:
-The plugin was created by software developers, not medical experts.
-The accuracy of the model is not 100%.
-Please use the plugin on your own risk.
-```
-
-# Face mask detection example
-
-![results](https://user-images.githubusercontent.com/3736126/130656086-3167421e-f697-4837-8cf9-e3889d49a44d.png)
-
-# Training process
-
-## Dataset
-
-We used four publicly available datasets for training the model:
-
-1. [Kaggle face mask detection dataset](https://www.kaggle.com/andrewmvd/face-mask-detection)
-2. [Kaggle medical masks dataset images tfrecords](https://www.kaggle.com/ivandanilovich/medical-masks-dataset-images-tfrecords)
-3. [Kaggle face mask detection dataset #2](https://www.kaggle.com/wobotintelligence/face-mask-detection-dataset?select=train.csv)
-4. [MAFA dataset](https://drive.google.com/drive/folders/1nbtM1n0--iZ3VVbNGhocxbnBGhMau_OG)
-
-We extracted faces with masks from first dataset (around 4k images), faces without mask from first two datasets (around 4k images),
-faces with masks worn incorrect from all four datasets (around 2k images).
-Then we duplicated each incorrect worn mask image with data augmentation (see augmentation.py) in order to achieve class balance.
-
-## Train
-
-InceptionV3 was cut off on mixed 7 layer to improve speed and was used as a backbone.
-Final model with 97.2 % accuracy is used by default and can be found [here](https://drive.google.com/file/d/1jm2Wd2JEZxhS8O1JjV-kfBOyOYUMxKHq/view?usp=sharing)
+# Face mask detection plugin
+
+A Mask detection plugin can be used to detect if the person wears a mask
+correctly automatically. There are three possible results:
+`without_mask`, `mask_worn_incorrectly`, `mask_worn_correctly`.
+
+There was no suitable free and ready-to-use model for face mask
+detection at the moment of adding this plugin, so we created our model.
+
+**Disclaimer:**
+
+- We are not medical experts' organization!
+- The plugin doesn't contain the recommendations of how to use and wear face mask correctly!
+- The current recommendation of [the WHO about face mask usage](https://www.who.int/emergencies/diseases/novel-coronavirus-2019/advice-for-public/when-and-how-to-use-masks)
+are the reference point in the plugin's data analysis.
+- The accuracy of the model is not 100%.
+- Please use the plugin at your own risk.
+
+# Face mask detection example
+
+![results](media/image1.png){width="5.833333333333333in"
+height="4.373164916885389in"}
+
+results
+
+# Training process
+
+## Dataset
+
+We used four publicly available datasets for training the model:
+
+1. [Kaggle face mask detection
+ dataset](https://www.kaggle.com/andrewmvd/face-mask-detection)
+2. [Kaggle medical masks dataset images
+ tfrecords](https://www.kaggle.com/ivandanilovich/medical-masks-dataset-images-tfrecords)
+3. [Kaggle face mask detection dataset
+ \#2](https://www.kaggle.com/wobotintelligence/face-mask-detection-dataset?select=train.csv)
+4. [MAFA
+ dataset](https://drive.google.com/drive/folders/1nbtM1n0--iZ3VVbNGhocxbnBGhMau_OG)
+
+We extracted faces with masks from the first dataset (around 4k images),
+faces without a mask from the first two datasets (around 4k images),
+faces with masks worn incorrectly from all four datasets (around 2k
+images). Then we duplicated each incorrect worn mask image with data
+augmentation (see augmentation.py) to achieve class balance.
+
+## Train
+
+InceptionV3 was cut off on a mixed 7 layer to improve speed and was used
+as a backbone. The final model with 97.2 % accuracy is used by default
+and can be found
+[here](https://drive.google.com/file/d/1jm2Wd2JEZxhS8O1JjV-kfBOyOYUMxKHq/view?usp=sharing)
From c0f05e127372bb87b0356d108e11d8a922976c18 Mon Sep 17 00:00:00 2001
From: igor bondarenko
Date: Tue, 19 Oct 2021 14:13:02 +0300
Subject: [PATCH 057/702] added restart policy for docker services
---
docker-compose.yml | 5 +++++
1 file changed, 5 insertions(+)
diff --git a/docker-compose.yml b/docker-compose.yml
index 4b5992694f..c8395c8d36 100644
--- a/docker-compose.yml
+++ b/docker-compose.yml
@@ -5,6 +5,7 @@ volumes:
services:
compreface-postgres-db:
+ restart: always
image: postgres:11.5
container_name: "compreface-postgres-db"
environment:
@@ -15,6 +16,7 @@ services:
- postgres-data:/var/lib/postgresql/data
compreface-admin:
+ restart: always
image: ${registry}compreface-admin:${ADMIN_VERSION}
container_name: "compreface-admin"
environment:
@@ -33,6 +35,7 @@ services:
- compreface-api
compreface-api:
+ restart: always
image: ${registry}compreface-api:${API_VERSION}
container_name: "compreface-api"
depends_on:
@@ -46,6 +49,7 @@ services:
- SAVE_IMAGES_TO_DB=${save_images_to_db}
compreface-fe:
+ restart: always
image: ${registry}compreface-fe:${FE_VERSION}
container_name: "compreface-ui"
ports:
@@ -55,6 +59,7 @@ services:
- compreface-admin
compreface-core:
+ restart: always
image: ${registry}compreface-core:${CORE_VERSION}
container_name: "compreface-core"
environment:
From fe08d90abab5cabd6829c684c6bd39a8ea86d196 Mon Sep 17 00:00:00 2001
From: spospielov
Date: Tue, 19 Oct 2021 17:17:55 +0300
Subject: [PATCH 058/702] degraded tensorflow version, issue 621
---
embedding-calculator/requirements.txt | 1 +
.../facescan/plugins/agegender/__init__.py | 2 +-
.../src/services/facescan/plugins/dependencies.py | 2 +-
.../facescan/plugins/facenet/facemask/facemask.py | 15 ++++-----------
4 files changed, 7 insertions(+), 13 deletions(-)
diff --git a/embedding-calculator/requirements.txt b/embedding-calculator/requirements.txt
index c3f6fb4c9c..b102869758 100644
--- a/embedding-calculator/requirements.txt
+++ b/embedding-calculator/requirements.txt
@@ -5,6 +5,7 @@ flasgger==0.9.5
Flask==1.1.2
gdown~=3.12
Werkzeug==1.0.1
+PyYAML==5.4.1
# tests
mock~=4.0.2
diff --git a/embedding-calculator/src/services/facescan/plugins/agegender/__init__.py b/embedding-calculator/src/services/facescan/plugins/agegender/__init__.py
index 94673c403f..dccb4a7368 100644
--- a/embedding-calculator/src/services/facescan/plugins/agegender/__init__.py
+++ b/embedding-calculator/src/services/facescan/plugins/agegender/__init__.py
@@ -12,4 +12,4 @@
# or implied. See the License for the specific language governing
# permissions and limitations under the License.
-requirements = ('tensorflow~=2.5.0','tf-slim~=1.1.0')
\ No newline at end of file
+requirements = ('tensorflow~=2.1.4', 'tf-slim~=1.1.0')
diff --git a/embedding-calculator/src/services/facescan/plugins/dependencies.py b/embedding-calculator/src/services/facescan/plugins/dependencies.py
index d0310bf6a0..afb3f25f34 100644
--- a/embedding-calculator/src/services/facescan/plugins/dependencies.py
+++ b/embedding-calculator/src/services/facescan/plugins/dependencies.py
@@ -18,7 +18,7 @@
from src.services.utils.pyutils import get_env
-def get_tensorflow(version='2.5.0') -> Tuple[str, ...]:
+def get_tensorflow(version='2.1.4') -> Tuple[str, ...]:
libs = [f'tensorflow=={version}']
cuda_version = get_env('CUDA', '').replace('.', '')
if ENV.GPU_IDX > -1 and cuda_version:
diff --git a/embedding-calculator/src/services/facescan/plugins/facenet/facemask/facemask.py b/embedding-calculator/src/services/facescan/plugins/facenet/facemask/facemask.py
index bd8a639530..bb52599d46 100644
--- a/embedding-calculator/src/services/facescan/plugins/facenet/facemask/facemask.py
+++ b/embedding-calculator/src/services/facescan/plugins/facenet/facemask/facemask.py
@@ -16,7 +16,6 @@
import numpy as np
import tensorflow as tf2
-from tensorflow.keras.models import load_model
from cached_property import cached_property
from src.services.imgtools.types import Array3D
@@ -30,16 +29,10 @@ class MaskDetector(base.BasePlugin):
slug = 'mask'
LABELS = ('without_mask', 'with_mask', 'mask_weared_incorrect')
ml_models = (
- ('inception_v3_on_mafa_kaggle123', '1jm2Wd2JEZxhS8O1JjV-kfBOyOYUMxKHq'),
- ('mobilenet_v2_on_mafa_kaggle123', '1-eqivfTVaXC_9Z5INbYeFVEwBzzqIzm3')
+ ('inception_v3_on_mafa_kaggle123', '1nhmv4Pd8nnV8XHv6vlf6RCpwQLow78zS'),
)
- @property
- def input_image_size(self) -> Tuple[int, int]:
- if self.ml_model_name == self.ml_models[1][0]:
- return 128, 128
- else:
- return 100, 100
+ INPUT_IMAGE_SIZE = 100
@property
def retain_folder_structure(self) -> bool:
@@ -47,10 +40,10 @@ def retain_folder_structure(self) -> bool:
@cached_property
def _model(self):
- model = tf2.keras.models.load_model(self.ml_model.path)
+ model = tf2.keras.models.load_model(str(self.ml_model.path))
def get_value(img: Array3D) -> Tuple[Union[str, Tuple], float]:
- img = cv2.resize(img, dsize=self.input_image_size,
+ img = cv2.resize(img, dsize=(self.INPUT_IMAGE_SIZE, self.INPUT_IMAGE_SIZE),
interpolation=cv2.INTER_CUBIC)
img = np.expand_dims(img, 0)
From 0c855a3d1869a3133286208ce55ea8101dd86b38 Mon Sep 17 00:00:00 2001
From: spospielov
Date: Tue, 19 Oct 2021 17:18:45 +0300
Subject: [PATCH 059/702] updated .env file
---
.env | 4 ++--
dev/.env | 2 +-
2 files changed, 3 insertions(+), 3 deletions(-)
diff --git a/.env b/.env
index 1347078496..b779e0eec9 100644
--- a/.env
+++ b/.env
@@ -10,8 +10,8 @@ email_from=
email_password=
enable_email_server=false
save_images_to_db=true
-compreface_api_java_options=-Xmx8g
-compreface_admin_java_options=-Xmx8g
+compreface_api_java_options=-Xmx4g
+compreface_admin_java_options=-Xmx1g
ADMIN_VERSION=0.6.1
API_VERSION=0.6.1
FE_VERSION=0.6.1
diff --git a/dev/.env b/dev/.env
index f8102b7fb5..e79d8d7293 100644
--- a/dev/.env
+++ b/dev/.env
@@ -11,7 +11,7 @@ email_password=
enable_email_server=false
save_images_to_db=true
compreface_api_java_options=-Xmx8g
-compreface_admin_java_options=-Xmx8g
+compreface_admin_java_options=-Xmx1g
ADMIN_VERSION=latest
API_VERSION=latest
FE_VERSION=latest
From d968cb13025e8a257ff75cdcaa82512b85dd1664 Mon Sep 17 00:00:00 2001
From: Samvel Aivazian
Date: Thu, 21 Oct 2021 01:05:47 +0300
Subject: [PATCH 060/702] ESFR-1164 - Added query parameter to listEmbeddings
endpoint
---
.../controller/EmbeddingController.java | 3 ++-
.../service/EmbeddingService.java | 19 +++++++++++++++++--
.../core/trainservice/util/StringUtils.java | 7 +++++++
.../controller/EmbeddingControllerTest.java | 3 ++-
.../service/EmbeddingServiceTest.java | 3 ++-
5 files changed, 30 insertions(+), 5 deletions(-)
create mode 100644 java/api/src/main/java/com/exadel/frs/core/trainservice/util/StringUtils.java
diff --git a/java/api/src/main/java/com/exadel/frs/core/trainservice/controller/EmbeddingController.java b/java/api/src/main/java/com/exadel/frs/core/trainservice/controller/EmbeddingController.java
index 9ce81191fc..c8caa32e41 100644
--- a/java/api/src/main/java/com/exadel/frs/core/trainservice/controller/EmbeddingController.java
+++ b/java/api/src/main/java/com/exadel/frs/core/trainservice/controller/EmbeddingController.java
@@ -104,8 +104,9 @@ byte[] downloadImg(HttpServletResponse response,
@GetMapping
public Faces listEmbeddings(
@ApiParam(value = API_KEY_DESC, required = true) @RequestHeader(name = X_FRS_API_KEY_HEADER) final String apiKey,
+ @ApiParam(value = SUBJECT_DESC) @Valid @RequestParam(name = SUBJECT, required = false) final String subjectName,
Pageable pageable) {
- return new Faces(embeddingService.listEmbeddings(apiKey, pageable).map(embeddingMapper::toResponseDto));
+ return new Faces(embeddingService.listEmbeddings(apiKey, subjectName, pageable).map(embeddingMapper::toResponseDto));
}
@WriteEndpoint
diff --git a/java/api/src/main/java/com/exadel/frs/core/trainservice/service/EmbeddingService.java b/java/api/src/main/java/com/exadel/frs/core/trainservice/service/EmbeddingService.java
index d09fb46e46..30c6653d36 100644
--- a/java/api/src/main/java/com/exadel/frs/core/trainservice/service/EmbeddingService.java
+++ b/java/api/src/main/java/com/exadel/frs/core/trainservice/service/EmbeddingService.java
@@ -8,6 +8,7 @@
import com.exadel.frs.core.trainservice.system.global.Constants;
import lombok.RequiredArgsConstructor;
import org.springframework.data.domain.Page;
+import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Service;
@@ -16,8 +17,11 @@
import java.util.Optional;
import java.util.UUID;
import java.util.function.Function;
+import java.util.stream.Collectors;
import java.util.stream.Stream;
+import static com.exadel.frs.core.trainservice.util.StringUtils.empty;
+
@Service
@RequiredArgsConstructor
public class EmbeddingService {
@@ -50,8 +54,19 @@ public Optional getImg(String apiKey, UUID embeddingId) {
return imgRepository.getImgByEmbeddingId(apiKey, embeddingId);
}
- public Page listEmbeddings(String apiKey, Pageable pageable) {
- return embeddingRepository.findBySubjectApiKey(apiKey, pageable);
+ public Page listEmbeddings(String apiKey, String subjectName, Pageable pageable) {
+ Page embeddings = embeddingRepository.findBySubjectApiKey(apiKey, pageable);
+ embeddings = filterEmbeddings(subjectName, embeddings.getContent());
+ return embeddings;
+ }
+
+ private Page filterEmbeddings(String subjectName, List embeddings) {
+ if (!empty(subjectName)) embeddings = filterBySubjectName(subjectName, embeddings);
+ return new PageImpl<>(embeddings);
+ }
+
+ private List filterBySubjectName(String subjectName, List embeddings) {
+ return embeddings.stream().filter(e -> e.getSubjectName().equals(subjectName)).collect(Collectors.toList());
}
public boolean isDemoCollectionInconsistent() {
diff --git a/java/api/src/main/java/com/exadel/frs/core/trainservice/util/StringUtils.java b/java/api/src/main/java/com/exadel/frs/core/trainservice/util/StringUtils.java
new file mode 100644
index 0000000000..9d601561d3
--- /dev/null
+++ b/java/api/src/main/java/com/exadel/frs/core/trainservice/util/StringUtils.java
@@ -0,0 +1,7 @@
+package com.exadel.frs.core.trainservice.util;
+
+public class StringUtils {
+ public static boolean empty(String s) {
+ return s == null || s.isBlank();
+ }
+}
diff --git a/java/api/src/test/java/com/exadel/frs/core/trainservice/controller/EmbeddingControllerTest.java b/java/api/src/test/java/com/exadel/frs/core/trainservice/controller/EmbeddingControllerTest.java
index 67bee25903..764ada96ba 100644
--- a/java/api/src/test/java/com/exadel/frs/core/trainservice/controller/EmbeddingControllerTest.java
+++ b/java/api/src/test/java/com/exadel/frs/core/trainservice/controller/EmbeddingControllerTest.java
@@ -180,7 +180,8 @@ void testDownloadImgNotFound() throws Exception {
@Test
void testListEmbeddings() throws Exception {
- when(embeddingService.listEmbeddings(eq(API_KEY), any()))
+ var subjectName = "Johnny Depp";
+ when(embeddingService.listEmbeddings(eq(API_KEY), subjectName, any()))
.thenReturn(new PageImpl<>(
List.of(
new EmbeddingProjection(UUID.randomUUID(), "name1"),
diff --git a/java/api/src/test/java/com/exadel/frs/core/trainservice/service/EmbeddingServiceTest.java b/java/api/src/test/java/com/exadel/frs/core/trainservice/service/EmbeddingServiceTest.java
index 18eb4c506b..fcde1aa84f 100644
--- a/java/api/src/test/java/com/exadel/frs/core/trainservice/service/EmbeddingServiceTest.java
+++ b/java/api/src/test/java/com/exadel/frs/core/trainservice/service/EmbeddingServiceTest.java
@@ -38,8 +38,9 @@ void testListEmbeddings() {
// new EmbeddingInfo("calc", new double[]{1.0, 2.0}, img())
+ var subjectName = "Johnny Depp";
var size = 5;
- final Page page = embeddingService.listEmbeddings(model.getApiKey(), PageRequest.of(0, size));
+ final Page page = embeddingService.listEmbeddings(model.getApiKey(), subjectName, PageRequest.of(0, size));
assertThat(page.getTotalElements(), is((long) count));
assertThat(page.getTotalPages(), is(count / size));
From a7557ded2bf2a1148bb435c1c84a027bd138bdae Mon Sep 17 00:00:00 2001
From: Samvel Aivazian
Date: Thu, 21 Oct 2021 12:03:53 +0300
Subject: [PATCH 061/702] ESFR-1164 - Refactored, changed place of filtering by
subject name
---
.../trainservice/service/EmbeddingService.java | 17 +----------------
.../frs/core/trainservice/util/StringUtils.java | 7 -------
.../repository/EmbeddingRepository.java | 9 +++++++++
3 files changed, 10 insertions(+), 23 deletions(-)
delete mode 100644 java/api/src/main/java/com/exadel/frs/core/trainservice/util/StringUtils.java
diff --git a/java/api/src/main/java/com/exadel/frs/core/trainservice/service/EmbeddingService.java b/java/api/src/main/java/com/exadel/frs/core/trainservice/service/EmbeddingService.java
index 30c6653d36..5e17ce5862 100644
--- a/java/api/src/main/java/com/exadel/frs/core/trainservice/service/EmbeddingService.java
+++ b/java/api/src/main/java/com/exadel/frs/core/trainservice/service/EmbeddingService.java
@@ -8,7 +8,6 @@
import com.exadel.frs.core.trainservice.system.global.Constants;
import lombok.RequiredArgsConstructor;
import org.springframework.data.domain.Page;
-import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Service;
@@ -17,11 +16,8 @@
import java.util.Optional;
import java.util.UUID;
import java.util.function.Function;
-import java.util.stream.Collectors;
import java.util.stream.Stream;
-import static com.exadel.frs.core.trainservice.util.StringUtils.empty;
-
@Service
@RequiredArgsConstructor
public class EmbeddingService {
@@ -55,18 +51,7 @@ public Optional getImg(String apiKey, UUID embeddingId) {
}
public Page listEmbeddings(String apiKey, String subjectName, Pageable pageable) {
- Page embeddings = embeddingRepository.findBySubjectApiKey(apiKey, pageable);
- embeddings = filterEmbeddings(subjectName, embeddings.getContent());
- return embeddings;
- }
-
- private Page filterEmbeddings(String subjectName, List embeddings) {
- if (!empty(subjectName)) embeddings = filterBySubjectName(subjectName, embeddings);
- return new PageImpl<>(embeddings);
- }
-
- private List filterBySubjectName(String subjectName, List embeddings) {
- return embeddings.stream().filter(e -> e.getSubjectName().equals(subjectName)).collect(Collectors.toList());
+ return embeddingRepository.findBySubjectApiKeyAndSubjectName(apiKey, subjectName, pageable);
}
public boolean isDemoCollectionInconsistent() {
diff --git a/java/api/src/main/java/com/exadel/frs/core/trainservice/util/StringUtils.java b/java/api/src/main/java/com/exadel/frs/core/trainservice/util/StringUtils.java
deleted file mode 100644
index 9d601561d3..0000000000
--- a/java/api/src/main/java/com/exadel/frs/core/trainservice/util/StringUtils.java
+++ /dev/null
@@ -1,7 +0,0 @@
-package com.exadel.frs.core.trainservice.util;
-
-public class StringUtils {
- public static boolean empty(String s) {
- return s == null || s.isBlank();
- }
-}
diff --git a/java/common/src/main/java/com/exadel/frs/commonservice/repository/EmbeddingRepository.java b/java/common/src/main/java/com/exadel/frs/commonservice/repository/EmbeddingRepository.java
index 8ecf4175e4..3cfcbab415 100644
--- a/java/common/src/main/java/com/exadel/frs/commonservice/repository/EmbeddingRepository.java
+++ b/java/common/src/main/java/com/exadel/frs/commonservice/repository/EmbeddingRepository.java
@@ -54,6 +54,15 @@ int updateEmbedding(@Param("embeddingId") UUID embeddingId,
" e.subject.apiKey = :apiKey")
Page findBySubjectApiKey(String apiKey, Pageable pageable);
+ @Query("select " +
+ " new com.exadel.frs.commonservice.entity.EmbeddingProjection(e.id, e.subject.subjectName)" +
+ " from " +
+ " Embedding e " +
+ " where " +
+ " e.subject.apiKey = :apiKey" +
+ " and (:subjectName is not null and e.subject.subjectName = :subjectName)")
+ Page findBySubjectApiKeyAndSubjectName(String apiKey, String subjectName, Pageable pageable);
+
@Query("select distinct(e.calculator) from Embedding e")
List getUniqueCalculators();
From 5a01b0c56cb398e528bd2f7d5df34e1153fb0794 Mon Sep 17 00:00:00 2001
From: Samvel Aivazian
Date: Thu, 21 Oct 2021 14:33:23 +0300
Subject: [PATCH 062/702] ESFR-1164 - Fixed null value check
---
.../frs/commonservice/repository/EmbeddingRepository.java | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/java/common/src/main/java/com/exadel/frs/commonservice/repository/EmbeddingRepository.java b/java/common/src/main/java/com/exadel/frs/commonservice/repository/EmbeddingRepository.java
index 3cfcbab415..339fb84264 100644
--- a/java/common/src/main/java/com/exadel/frs/commonservice/repository/EmbeddingRepository.java
+++ b/java/common/src/main/java/com/exadel/frs/commonservice/repository/EmbeddingRepository.java
@@ -60,7 +60,7 @@ int updateEmbedding(@Param("embeddingId") UUID embeddingId,
" Embedding e " +
" where " +
" e.subject.apiKey = :apiKey" +
- " and (:subjectName is not null and e.subject.subjectName = :subjectName)")
+ " and (COALESCE(:subjectName, null) is null or e.subject.subjectName = :subjectName)")
Page findBySubjectApiKeyAndSubjectName(String apiKey, String subjectName, Pageable pageable);
@Query("select distinct(e.calculator) from Embedding e")
From 223db6cb066c0626a11dc23e5c8ba28492288980 Mon Sep 17 00:00:00 2001
From: Samvel Aivazian
Date: Thu, 21 Oct 2021 18:42:43 +0300
Subject: [PATCH 063/702] ESFR-1164 - changed from usage of COALESCE func to
casting to string
---
.../frs/commonservice/repository/EmbeddingRepository.java | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/java/common/src/main/java/com/exadel/frs/commonservice/repository/EmbeddingRepository.java b/java/common/src/main/java/com/exadel/frs/commonservice/repository/EmbeddingRepository.java
index 339fb84264..5bbe8612ae 100644
--- a/java/common/src/main/java/com/exadel/frs/commonservice/repository/EmbeddingRepository.java
+++ b/java/common/src/main/java/com/exadel/frs/commonservice/repository/EmbeddingRepository.java
@@ -60,7 +60,7 @@ int updateEmbedding(@Param("embeddingId") UUID embeddingId,
" Embedding e " +
" where " +
" e.subject.apiKey = :apiKey" +
- " and (COALESCE(:subjectName, null) is null or e.subject.subjectName = :subjectName)")
+ " and (cast(:subjectName as string) is null or e.subject.subjectName = :subjectName)")
Page findBySubjectApiKeyAndSubjectName(String apiKey, String subjectName, Pageable pageable);
@Query("select distinct(e.calculator) from Embedding e")
From 6f4dd1cd42981e29590759c609850c2f301c2772 Mon Sep 17 00:00:00 2001
From: shreyanshrs44
Date: Fri, 22 Oct 2021 19:58:29 +0530
Subject: [PATCH 064/702] Add default Detection and Verification services #606
---
.../db/changelog/db.changelog-0.1.8.yaml | 47 +++++++++++++++++++
.../db/changelog/db.changelog-master.yaml | 2 +
2 files changed, 49 insertions(+)
create mode 100644 java/admin/src/main/resources/db/changelog/db.changelog-0.1.8.yaml
diff --git a/java/admin/src/main/resources/db/changelog/db.changelog-0.1.8.yaml b/java/admin/src/main/resources/db/changelog/db.changelog-0.1.8.yaml
new file mode 100644
index 0000000000..0c644b8a20
--- /dev/null
+++ b/java/admin/src/main/resources/db/changelog/db.changelog-0.1.8.yaml
@@ -0,0 +1,47 @@
+databaseChangeLog:
+ - changeSet:
+ id: add-detection-verification-services
+ author: Shreyansh Sancheti
+ changes:
+ - insert:
+ tableName: model
+ columns:
+ - column:
+ name: id
+ value: 9223372036854775807
+ - column:
+ name: name
+ value: Detection_Demo
+ - column:
+ name: type
+ value: D
+ - column:
+ name: guid
+ value: 00000000-0000-0000-0000-000000000003
+ - column:
+ name: app_id
+ value: 0
+ - column:
+ name: api_key
+ value: 00000000-0000-0000-0000-000000000003
+ - insert:
+ tableName: model
+ columns:
+ - column:
+ name: id
+ value: 9223372036854775806
+ - column:
+ name: name
+ value: Verification_Demo
+ - column:
+ name: type
+ value: V
+ - column:
+ name: guid
+ value: 00000000-0000-0000-0000-000000000004
+ - column:
+ name: app_id
+ value: 0
+ - column:
+ name: api_key
+ value: 00000000-0000-0000-0000-000000000004
\ No newline at end of file
diff --git a/java/admin/src/main/resources/db/changelog/db.changelog-master.yaml b/java/admin/src/main/resources/db/changelog/db.changelog-master.yaml
index 0ed46f2b67..c30d274deb 100644
--- a/java/admin/src/main/resources/db/changelog/db.changelog-master.yaml
+++ b/java/admin/src/main/resources/db/changelog/db.changelog-master.yaml
@@ -33,3 +33,5 @@ databaseChangeLog:
file: db/changelog/db.changelog-0.1.6.yaml
- include:
file: db/changelog/db.changelog-0.1.7.yaml
+ - include:
+ file: db/changelog/db.changelog-0.1.8.yaml
From 1896a655db9c9e664c99a12e00165db30b16ae91 Mon Sep 17 00:00:00 2001
From: Samvel Aivazian
Date: Mon, 25 Oct 2021 02:06:54 +0300
Subject: [PATCH 065/702] ESFR-1164 - Added tests for listing embeddings with
subject name
---
.../controller/EmbeddingControllerTest.java | 23 +++++++++++++++++++
.../service/EmbeddingServiceTest.java | 19 +++++++++++++++
2 files changed, 42 insertions(+)
diff --git a/java/api/src/test/java/com/exadel/frs/core/trainservice/controller/EmbeddingControllerTest.java b/java/api/src/test/java/com/exadel/frs/core/trainservice/controller/EmbeddingControllerTest.java
index 764ada96ba..c355c9232e 100644
--- a/java/api/src/test/java/com/exadel/frs/core/trainservice/controller/EmbeddingControllerTest.java
+++ b/java/api/src/test/java/com/exadel/frs/core/trainservice/controller/EmbeddingControllerTest.java
@@ -180,6 +180,29 @@ void testDownloadImgNotFound() throws Exception {
@Test
void testListEmbeddings() throws Exception {
+ when(embeddingService.listEmbeddings(eq(API_KEY), null, any()))
+ .thenReturn(new PageImpl<>(
+ List.of(
+ new EmbeddingProjection(UUID.randomUUID(), "name1"),
+ new EmbeddingProjection(UUID.randomUUID(), "name2")
+ ),
+ PageRequest.of(1, 10), // second page
+ 12
+ ));
+
+ mockMvc.perform(
+ get(API_V1 + "/recognition/faces")
+ .header(X_FRS_API_KEY_HEADER, API_KEY)
+ ).andExpect(status().isOk())
+ .andExpect(jsonPath("$.faces.length()", is(2)))
+ .andExpect(jsonPath("$.page_number", is(1))) // page number
+ .andExpect(jsonPath("$.page_size", is(10))) // page size
+ .andExpect(jsonPath("$.total_pages", is(2)))
+ .andExpect(jsonPath("$.total_elements", is(12)));
+ }
+
+ @Test
+ void testListEmbeddingsWithSubjectName() throws Exception {
var subjectName = "Johnny Depp";
when(embeddingService.listEmbeddings(eq(API_KEY), subjectName, any()))
.thenReturn(new PageImpl<>(
diff --git a/java/api/src/test/java/com/exadel/frs/core/trainservice/service/EmbeddingServiceTest.java b/java/api/src/test/java/com/exadel/frs/core/trainservice/service/EmbeddingServiceTest.java
index fcde1aa84f..24f5a2bfa0 100644
--- a/java/api/src/test/java/com/exadel/frs/core/trainservice/service/EmbeddingServiceTest.java
+++ b/java/api/src/test/java/com/exadel/frs/core/trainservice/service/EmbeddingServiceTest.java
@@ -38,6 +38,25 @@ void testListEmbeddings() {
// new EmbeddingInfo("calc", new double[]{1.0, 2.0}, img())
+ var size = 5;
+ final Page page = embeddingService.listEmbeddings(model.getApiKey(), null, PageRequest.of(0, size));
+
+ assertThat(page.getTotalElements(), is((long) count));
+ assertThat(page.getTotalPages(), is(count / size));
+ assertThat(page.getSize(), is(size));
+ }
+
+ @Test
+ void testListEmbeddingsWithSubjectName() {
+ final Model model = dbHelper.insertModel();
+
+ int count = 10;
+ for (int i = 0; i < count; i++) {
+ dbHelper.insertEmbeddingNoImg(dbHelper.insertSubject(model, "subject" + i));
+ }
+
+ // new EmbeddingInfo("calc", new double[]{1.0, 2.0}, img())
+
var subjectName = "Johnny Depp";
var size = 5;
final Page page = embeddingService.listEmbeddings(model.getApiKey(), subjectName, PageRequest.of(0, size));
From 9e013cd6ecc5f71bb91c02190c9b61c20e63d4b5 Mon Sep 17 00:00:00 2001
From: Tulika
Date: Mon, 25 Oct 2021 13:14:04 +0530
Subject: [PATCH 066/702] Add exception for invalid Image Id
---
.../cache/EmbeddingCollection.java | 10 ++++-
.../trainservice/service/SubjectService.java | 1 -
.../service/SubjectServiceTest.java | 45 ++++++++++++++-----
.../exception/InvalidImageIdException.java | 10 +++++
4 files changed, 52 insertions(+), 14 deletions(-)
create mode 100644 java/common/src/main/java/com/exadel/frs/commonservice/exception/InvalidImageIdException.java
diff --git a/java/api/src/main/java/com/exadel/frs/core/trainservice/cache/EmbeddingCollection.java b/java/api/src/main/java/com/exadel/frs/core/trainservice/cache/EmbeddingCollection.java
index b96dcc9c73..c407158f4c 100644
--- a/java/api/src/main/java/com/exadel/frs/core/trainservice/cache/EmbeddingCollection.java
+++ b/java/api/src/main/java/com/exadel/frs/core/trainservice/cache/EmbeddingCollection.java
@@ -2,6 +2,7 @@
import com.exadel.frs.commonservice.entity.Embedding;
import com.exadel.frs.commonservice.entity.EmbeddingProjection;
+import com.exadel.frs.commonservice.exception.InvalidImageIdException;
import com.google.common.collect.BiMap;
import com.google.common.collect.HashBiMap;
import lombok.AccessLevel;
@@ -151,12 +152,17 @@ public synchronized Optional getSubjectNameByEmbeddingId(UUID embeddingI
private Optional findByEmbeddingId(UUID embeddingId, Function, T> func) {
if (embeddingId == null) {
- return Optional.empty();
+ throw new InvalidImageIdException();
}
return projection2Index.entrySet()
.stream()
- .filter(entry -> embeddingId.equals(entry.getKey().getEmbeddingId()))
+ .filter(entry -> {
+ if(embeddingId.equals(entry.getKey().getEmbeddingId()))
+ return embeddingId.equals(entry.getKey().getEmbeddingId());
+ else
+ throw new InvalidImageIdException();
+ })
.findFirst()
.map(func);
}
diff --git a/java/api/src/main/java/com/exadel/frs/core/trainservice/service/SubjectService.java b/java/api/src/main/java/com/exadel/frs/core/trainservice/service/SubjectService.java
index be1070d630..f90789fc1a 100644
--- a/java/api/src/main/java/com/exadel/frs/core/trainservice/service/SubjectService.java
+++ b/java/api/src/main/java/com/exadel/frs/core/trainservice/service/SubjectService.java
@@ -16,7 +16,6 @@
import com.exadel.frs.core.trainservice.dto.FaceVerification;
import com.exadel.frs.core.trainservice.dto.ProcessImageParams;
import com.exadel.frs.core.trainservice.system.global.Constants;
-import java.util.stream.Collectors;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
diff --git a/java/api/src/test/java/com/exadel/frs/core/trainservice/service/SubjectServiceTest.java b/java/api/src/test/java/com/exadel/frs/core/trainservice/service/SubjectServiceTest.java
index 79fb8ea0d4..3777bfeb54 100644
--- a/java/api/src/test/java/com/exadel/frs/core/trainservice/service/SubjectServiceTest.java
+++ b/java/api/src/test/java/com/exadel/frs/core/trainservice/service/SubjectServiceTest.java
@@ -19,6 +19,7 @@
import com.exadel.frs.commonservice.dto.ExecutionTimeDto;
import com.exadel.frs.commonservice.entity.Embedding;
import com.exadel.frs.commonservice.entity.Subject;
+import com.exadel.frs.commonservice.exception.InvalidImageIdException;
import com.exadel.frs.commonservice.exception.TooManyFacesException;
import com.exadel.frs.commonservice.sdk.faces.FacesApiClient;
import com.exadel.frs.commonservice.sdk.faces.feign.dto.FacesBox;
@@ -31,7 +32,6 @@
import com.exadel.frs.core.trainservice.component.classifiers.EuclideanDistanceClassifier;
import com.exadel.frs.core.trainservice.dao.SubjectDao;
import com.exadel.frs.core.trainservice.dto.ProcessImageParams;
-import com.exadel.frs.core.trainservice.mapper.FacesMapper;
import org.apache.commons.lang3.tuple.Pair;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
@@ -39,10 +39,8 @@
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource;
import org.junit.jupiter.params.provider.ValueSource;
-import org.mapstruct.factory.Mappers;
import org.mockito.InjectMocks;
import org.mockito.Mock;
-import org.mockito.Spy;
import org.springframework.mock.web.MockMultipartFile;
import org.springframework.web.multipart.MultipartFile;
@@ -58,6 +56,7 @@
import static com.exadel.frs.core.trainservice.system.global.Constants.IMAGE_ID;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
+import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.*;
import static org.mockito.MockitoAnnotations.initMocks;
@@ -72,9 +71,6 @@ class SubjectServiceTest {
@Mock
private FacesApiClient facesApiClient;
- @Spy
- private FacesMapper facesMapper = Mappers.getMapper(FacesMapper.class);
-
@Mock
private EmbeddingCacheProvider embeddingCacheProvider;
@@ -214,15 +210,16 @@ void tooManyFacesFound() {
@ValueSource(booleans = {true, false})
void testVerifyFaces(boolean status) {
var detProbThreshold = 0.7;
+ var randomUUId = UUID.randomUUID();
MultipartFile file = new MockMultipartFile("anyname", new byte[]{0xA});
+ Embedding a = makeEmbedding("A", API_KEY);
+ a.setId(randomUUId);
+ EmbeddingCollection embeddingCollection = EmbeddingCollection.from(Stream.of(a));
when(facesApiClient.findFacesWithCalculator(any(), any(), any(), any()))
.thenReturn(findFacesResponse(2));
when(embeddingCacheProvider.getOrLoad(API_KEY))
- .thenReturn(EmbeddingCollection.from(Stream.of(
- makeEmbedding("A", API_KEY),
- makeEmbedding("B", API_KEY)
- )));
+ .thenReturn(embeddingCollection);
when(classifierPredictor.verify(any(), any(), any()))
.thenReturn(0.0);
@@ -233,7 +230,7 @@ void testVerifyFaces(boolean status) {
.limit(MAX_FACES_TO_RECOGNIZE)
.detProbThreshold(detProbThreshold)
.status(status)
- .additionalParams(Map.of(IMAGE_ID, UUID.randomUUID()))
+ .additionalParams(Map.of(IMAGE_ID, randomUUId))
.build()
);
@@ -249,6 +246,32 @@ void testVerifyFaces(boolean status) {
}
}
+ @ParameterizedTest
+ @ValueSource(booleans = {true, false})
+ void testInvalidImageIdException(boolean status){
+ var detProbThreshold = 0.7;
+ var randomUUId = UUID.randomUUID();
+ MultipartFile file = new MockMultipartFile("anyname", new byte[]{0xA});
+ EmbeddingCollection embeddingCollection = EmbeddingCollection.from(Stream.of(makeEmbedding("A", API_KEY)));
+
+ when(facesApiClient.findFacesWithCalculator(any(), any(), any(), any()))
+ .thenReturn(findFacesResponse(2));
+ when(embeddingCacheProvider.getOrLoad(API_KEY))
+ .thenReturn(embeddingCollection);
+ when(classifierPredictor.verify(any(), any(), any()))
+ .thenReturn(0.0);
+ assertThrows(InvalidImageIdException.class, ()-> subjectService.verifyFace(
+ ProcessImageParams.builder()
+ .apiKey(API_KEY)
+ .file(file)
+ .limit(MAX_FACES_TO_RECOGNIZE)
+ .detProbThreshold(detProbThreshold)
+ .status(status)
+ .additionalParams(Map.of(IMAGE_ID, randomUUId))
+ .build()
+ ));
+ }
+
private static FindFacesResponse findFacesResponse(int faceCount) {
return FindFacesResponse.builder()
.result(
diff --git a/java/common/src/main/java/com/exadel/frs/commonservice/exception/InvalidImageIdException.java b/java/common/src/main/java/com/exadel/frs/commonservice/exception/InvalidImageIdException.java
new file mode 100644
index 0000000000..b25cd6c71f
--- /dev/null
+++ b/java/common/src/main/java/com/exadel/frs/commonservice/exception/InvalidImageIdException.java
@@ -0,0 +1,10 @@
+package com.exadel.frs.commonservice.exception;
+
+import static com.exadel.frs.commonservice.handler.CommonExceptionCode.EMBEDDING_NOT_FOUND;
+
+public class InvalidImageIdException extends BasicException {
+
+ public InvalidImageIdException() {
+ super(EMBEDDING_NOT_FOUND, "Image Id is not valid");
+ }
+}
\ No newline at end of file
From 78605dbc95a35274609eb83535b6d73bc45018e9 Mon Sep 17 00:00:00 2001
From: Samvel Aivazian
Date: Mon, 25 Oct 2021 13:14:06 +0300
Subject: [PATCH 067/702] ESFR-1164 - Fixed tests
---
.../controller/EmbeddingControllerTest.java | 14 ++++++--------
.../trainservice/service/EmbeddingServiceTest.java | 12 ++++--------
java/api/src/test/resources/application.yml | 2 +-
3 files changed, 11 insertions(+), 17 deletions(-)
diff --git a/java/api/src/test/java/com/exadel/frs/core/trainservice/controller/EmbeddingControllerTest.java b/java/api/src/test/java/com/exadel/frs/core/trainservice/controller/EmbeddingControllerTest.java
index c355c9232e..c82f74c5eb 100644
--- a/java/api/src/test/java/com/exadel/frs/core/trainservice/controller/EmbeddingControllerTest.java
+++ b/java/api/src/test/java/com/exadel/frs/core/trainservice/controller/EmbeddingControllerTest.java
@@ -180,7 +180,7 @@ void testDownloadImgNotFound() throws Exception {
@Test
void testListEmbeddings() throws Exception {
- when(embeddingService.listEmbeddings(eq(API_KEY), null, any()))
+ when(embeddingService.listEmbeddings(eq(API_KEY), eq(null), any()))
.thenReturn(new PageImpl<>(
List.of(
new EmbeddingProjection(UUID.randomUUID(), "name1"),
@@ -204,25 +204,23 @@ void testListEmbeddings() throws Exception {
@Test
void testListEmbeddingsWithSubjectName() throws Exception {
var subjectName = "Johnny Depp";
- when(embeddingService.listEmbeddings(eq(API_KEY), subjectName, any()))
+ when(embeddingService.listEmbeddings(eq(API_KEY), eq(subjectName), any()))
.thenReturn(new PageImpl<>(
- List.of(
- new EmbeddingProjection(UUID.randomUUID(), "name1"),
- new EmbeddingProjection(UUID.randomUUID(), "name2")
- ),
+ List.of(new EmbeddingProjection(UUID.randomUUID(), subjectName)),
PageRequest.of(1, 10), // second page
12
));
mockMvc.perform(
get(API_V1 + "/recognition/faces")
+ .queryParam("subject", subjectName)
.header(X_FRS_API_KEY_HEADER, API_KEY)
).andExpect(status().isOk())
- .andExpect(jsonPath("$.faces.length()", is(2)))
+ .andExpect(jsonPath("$.faces.length()", is(1)))
.andExpect(jsonPath("$.page_number", is(1))) // page number
.andExpect(jsonPath("$.page_size", is(10))) // page size
.andExpect(jsonPath("$.total_pages", is(2)))
- .andExpect(jsonPath("$.total_elements", is(12)));
+ .andExpect(jsonPath("$.total_elements", is(11)));
}
@Test
diff --git a/java/api/src/test/java/com/exadel/frs/core/trainservice/service/EmbeddingServiceTest.java b/java/api/src/test/java/com/exadel/frs/core/trainservice/service/EmbeddingServiceTest.java
index 24f5a2bfa0..aa90cb1693 100644
--- a/java/api/src/test/java/com/exadel/frs/core/trainservice/service/EmbeddingServiceTest.java
+++ b/java/api/src/test/java/com/exadel/frs/core/trainservice/service/EmbeddingServiceTest.java
@@ -50,15 +50,11 @@ void testListEmbeddings() {
void testListEmbeddingsWithSubjectName() {
final Model model = dbHelper.insertModel();
- int count = 10;
- for (int i = 0; i < count; i++) {
- dbHelper.insertEmbeddingNoImg(dbHelper.insertSubject(model, "subject" + i));
- }
-
- // new EmbeddingInfo("calc", new double[]{1.0, 2.0}, img())
-
+ int count = 1;
var subjectName = "Johnny Depp";
- var size = 5;
+ dbHelper.insertEmbeddingNoImg(dbHelper.insertSubject(model, subjectName));
+
+ var size = 1;
final Page page = embeddingService.listEmbeddings(model.getApiKey(), subjectName, PageRequest.of(0, size));
assertThat(page.getTotalElements(), is((long) count));
diff --git a/java/api/src/test/resources/application.yml b/java/api/src/test/resources/application.yml
index 449bf963a6..68ad3cfb1e 100644
--- a/java/api/src/test/resources/application.yml
+++ b/java/api/src/test/resources/application.yml
@@ -7,7 +7,7 @@ spring:
enabled: false
datasource-pg:
driver-class-name: org.postgresql.Driver
- url: ${POSTGRES_URL:jdbc:postgresql://compreface-postgres-db:5432/frs}
+ url: ${POSTGRES_URL:jdbc:postgresql://localhost:6432/frs}
username: ${POSTGRES_USER:postgres}
password: ${POSTGRES_PASSWORD:postgres}
jpa:
From 2f3b2017c8caa9a372e30a73e5b4f3ef766f4aac Mon Sep 17 00:00:00 2001
From: Samvel Aivazian
Date: Mon, 25 Oct 2021 13:26:51 +0300
Subject: [PATCH 068/702] ESFR-1164 - Fixed tests
---
.../frs/core/trainservice/service/EmbeddingServiceTest.java | 1 +
java/api/src/test/resources/application.yml | 2 +-
2 files changed, 2 insertions(+), 1 deletion(-)
diff --git a/java/api/src/test/java/com/exadel/frs/core/trainservice/service/EmbeddingServiceTest.java b/java/api/src/test/java/com/exadel/frs/core/trainservice/service/EmbeddingServiceTest.java
index aa90cb1693..e61f4d508f 100644
--- a/java/api/src/test/java/com/exadel/frs/core/trainservice/service/EmbeddingServiceTest.java
+++ b/java/api/src/test/java/com/exadel/frs/core/trainservice/service/EmbeddingServiceTest.java
@@ -53,6 +53,7 @@ void testListEmbeddingsWithSubjectName() {
int count = 1;
var subjectName = "Johnny Depp";
dbHelper.insertEmbeddingNoImg(dbHelper.insertSubject(model, subjectName));
+ dbHelper.insertEmbeddingNoImg(dbHelper.insertSubject(model, "Not Johnny Depp"));
var size = 1;
final Page page = embeddingService.listEmbeddings(model.getApiKey(), subjectName, PageRequest.of(0, size));
diff --git a/java/api/src/test/resources/application.yml b/java/api/src/test/resources/application.yml
index 68ad3cfb1e..449bf963a6 100644
--- a/java/api/src/test/resources/application.yml
+++ b/java/api/src/test/resources/application.yml
@@ -7,7 +7,7 @@ spring:
enabled: false
datasource-pg:
driver-class-name: org.postgresql.Driver
- url: ${POSTGRES_URL:jdbc:postgresql://localhost:6432/frs}
+ url: ${POSTGRES_URL:jdbc:postgresql://compreface-postgres-db:5432/frs}
username: ${POSTGRES_USER:postgres}
password: ${POSTGRES_PASSWORD:postgres}
jpa:
From 4e1ee93d016b7f4cb9cf453234915704015bf577 Mon Sep 17 00:00:00 2001
From: Samvel Aivazian
Date: Mon, 25 Oct 2021 14:11:23 +0300
Subject: [PATCH 069/702] ESFR-1164 - Changed postgresql url in tests
application.yml file
---
java/api/src/test/resources/application.yml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/java/api/src/test/resources/application.yml b/java/api/src/test/resources/application.yml
index 449bf963a6..68ad3cfb1e 100644
--- a/java/api/src/test/resources/application.yml
+++ b/java/api/src/test/resources/application.yml
@@ -7,7 +7,7 @@ spring:
enabled: false
datasource-pg:
driver-class-name: org.postgresql.Driver
- url: ${POSTGRES_URL:jdbc:postgresql://compreface-postgres-db:5432/frs}
+ url: ${POSTGRES_URL:jdbc:postgresql://localhost:6432/frs}
username: ${POSTGRES_USER:postgres}
password: ${POSTGRES_PASSWORD:postgres}
jpa:
From df2c6ec394afe18b7914f271ed41e626fc60f184 Mon Sep 17 00:00:00 2001
From: Samvel Aivazian
Date: Tue, 26 Oct 2021 13:18:02 +0300
Subject: [PATCH 070/702] EFSR-1166 - Reverted changes
---
dev/Dockerfile | 6 ++++--
1 file changed, 4 insertions(+), 2 deletions(-)
diff --git a/dev/Dockerfile b/dev/Dockerfile
index 03189233e1..be8c85dc12 100644
--- a/dev/Dockerfile
+++ b/dev/Dockerfile
@@ -3,10 +3,12 @@ ARG ND4J_CLASSIFIER
WORKDIR /workspace/compreface
LABEL intermidiate_frs=true
COPY pom.xml .
+COPY api/pom.xml api/
+COPY admin/pom.xml admin/
+RUN mvn -B clean install -DskipTests -Dcheckstyle.skip -Dasciidoctor.skip -Djacoco.skip -Dmaven.gitcommitid.skip -Dspring-boot.repackage.skip -Dmaven.exec.skip=true -Dmaven.install.skip -Dmaven.resources.skip
COPY api api
COPY admin admin
-COPY common common
-RUN mvn -B clean package -Dmaven.test.skip=true -Dmaven.site.skip=true -Dmaven.javadoc.skip=true -Dnd4j.classifier=$ND4J_CLASSIFIER
+RUN mvn package -Dmaven.test.skip=true -Dmaven.site.skip=true -Dmaven.javadoc.skip=true
FROM openjdk:11.0.8-jre-slim as frs_core
ARG DIR=/workspace/compreface
From 25a12f1dd1d0f5b784a34b2e554edef092939cf0 Mon Sep 17 00:00:00 2001
From: Samvel Aivazian
Date: Tue, 26 Oct 2021 13:21:10 +0300
Subject: [PATCH 071/702] EFSR-1166 - Reverted changes
---
dev/Dockerfile | 1 +
1 file changed, 1 insertion(+)
diff --git a/dev/Dockerfile b/dev/Dockerfile
index be8c85dc12..1703017b1a 100644
--- a/dev/Dockerfile
+++ b/dev/Dockerfile
@@ -8,6 +8,7 @@ COPY admin/pom.xml admin/
RUN mvn -B clean install -DskipTests -Dcheckstyle.skip -Dasciidoctor.skip -Djacoco.skip -Dmaven.gitcommitid.skip -Dspring-boot.repackage.skip -Dmaven.exec.skip=true -Dmaven.install.skip -Dmaven.resources.skip
COPY api api
COPY admin admin
+COPY common common
RUN mvn package -Dmaven.test.skip=true -Dmaven.site.skip=true -Dmaven.javadoc.skip=true
FROM openjdk:11.0.8-jre-slim as frs_core
From f880b317fe89cd0bc8ee4cb6d5dcf5e6f4ad8b66 Mon Sep 17 00:00:00 2001
From: Samvel Aivazian
Date: Tue, 26 Oct 2021 13:38:59 +0300
Subject: [PATCH 072/702] EFSR-1166 - Fixed build
---
dev/.env | 4 ++--
dev/Dockerfile | 1 +
2 files changed, 3 insertions(+), 2 deletions(-)
diff --git a/dev/.env b/dev/.env
index e79d8d7293..9f57ffee71 100644
--- a/dev/.env
+++ b/dev/.env
@@ -10,8 +10,8 @@ email_from=
email_password=
enable_email_server=false
save_images_to_db=true
-compreface_api_java_options=-Xmx8g
-compreface_admin_java_options=-Xmx1g
+compreface_api_java_options="-Xmx8g"
+compreface_admin_java_options="-Xmx1g"
ADMIN_VERSION=latest
API_VERSION=latest
FE_VERSION=latest
diff --git a/dev/Dockerfile b/dev/Dockerfile
index 1703017b1a..1f7d863107 100644
--- a/dev/Dockerfile
+++ b/dev/Dockerfile
@@ -5,6 +5,7 @@ LABEL intermidiate_frs=true
COPY pom.xml .
COPY api/pom.xml api/
COPY admin/pom.xml admin/
+COPY common/pom.xml common/
RUN mvn -B clean install -DskipTests -Dcheckstyle.skip -Dasciidoctor.skip -Djacoco.skip -Dmaven.gitcommitid.skip -Dspring-boot.repackage.skip -Dmaven.exec.skip=true -Dmaven.install.skip -Dmaven.resources.skip
COPY api api
COPY admin admin
From dbbe6b7ecc0e1c49999c3df78eef73ffe4137607 Mon Sep 17 00:00:00 2001
From: Samvel Aivazian
Date: Tue, 26 Oct 2021 13:42:35 +0300
Subject: [PATCH 073/702] EFSR-1166 - Fixed build
---
dev/.env | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/dev/.env b/dev/.env
index 9f57ffee71..e79d8d7293 100644
--- a/dev/.env
+++ b/dev/.env
@@ -10,8 +10,8 @@ email_from=
email_password=
enable_email_server=false
save_images_to_db=true
-compreface_api_java_options="-Xmx8g"
-compreface_admin_java_options="-Xmx1g"
+compreface_api_java_options=-Xmx8g
+compreface_admin_java_options=-Xmx1g
ADMIN_VERSION=latest
API_VERSION=latest
FE_VERSION=latest
From 10734f643ab7e4a110adc10b9e4d01c897d7f527 Mon Sep 17 00:00:00 2001
From: spospielov
Date: Tue, 26 Oct 2021 19:06:54 +0300
Subject: [PATCH 074/702] updated masked model parameters
---
.../src/services/facescan/plugins/facenet/facenet.py | 2 +-
.../src/services/facescan/plugins/insightface/insightface.py | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/embedding-calculator/src/services/facescan/plugins/facenet/facenet.py b/embedding-calculator/src/services/facescan/plugins/facenet/facenet.py
index db4d998edd..f2a905be4c 100644
--- a/embedding-calculator/src/services/facescan/plugins/facenet/facenet.py
+++ b/embedding-calculator/src/services/facescan/plugins/facenet/facenet.py
@@ -120,7 +120,7 @@ class Calculator(mixins.CalculatorMixin, base.BasePlugin):
# CASIA-WebFace training set, 0.9905 LFW accuracy
('20180408-102900', '100w4JIUz44Tkwte9F-wEH0DOFsY-bPaw', (1.1362496, 5.803152427), 0.4),
# CASIA-WebFace-Masked, 0.9873 LFW, 0.9667 LFW-Masked (orig model has 0.9350 on LFW-Masked)
- ('inception_resnetv1_casia_masked', '1FddVjS3JbtUOjgO0kWs43CAh0nJH2RrG', (1.1362496, 5.803152427), 0.6)
+ ('inception_resnetv1_casia_masked', '1FddVjS3JbtUOjgO0kWs43CAh0nJH2RrG', (1.1145709, 4.554903071), 0.6)
)
BATCH_SIZE = 25
diff --git a/embedding-calculator/src/services/facescan/plugins/insightface/insightface.py b/embedding-calculator/src/services/facescan/plugins/insightface/insightface.py
index bcbaed7e3e..9f05e7f27c 100644
--- a/embedding-calculator/src/services/facescan/plugins/insightface/insightface.py
+++ b/embedding-calculator/src/services/facescan/plugins/insightface/insightface.py
@@ -112,7 +112,7 @@ class Calculator(InsightFaceMixin, mixins.CalculatorMixin, base.BasePlugin):
('arcface-r50-msfdrop75', '1gNuvRNHCNgvFtz7SjhW82v2-znlAYaRO', (1.2350148, 7.071431642), 400),
('arcface-r100-msfdrop75', '1lAnFcBXoMKqE-SkZKTmi6MsYAmzG0tFw', (1.224676, 6.322647217), 400),
# CASIA-WebFace-Masked, 0.9840 LFW, 0.9667 LFW-Masked (orig mobilefacenet has 0.9482 on LFW-Masked)
- ('arcface_mobilefacenet_casia_masked', '1ltcJChTdP1yQWF9e1ESpTNYAVwxLSNLP', (1.26538905, 5.552089201), 200),
+ ('arcface_mobilefacenet_casia_masked', '1ltcJChTdP1yQWF9e1ESpTNYAVwxLSNLP', (1.22507105, 7.321198934), 200),
)
def calc_embedding(self, face_img: Array3D) -> Array3D:
From 3301863128dde3594b08bc98d6c723d7e43dcd1f Mon Sep 17 00:00:00 2001
From: Tulika
Date: Tue, 26 Oct 2021 21:51:31 +0530
Subject: [PATCH 075/702] Fix PR comments - Extract "if" statement into
separated method, call it "validateImageId()" - Change exception message to
"Image id is incorrect" - Use only needed classes from package - Refactor
Util tests - Change classname from InvalidImageIdException to
IncorrectImageIdException
---
.../trainservice/cache/EmbeddingCollection.java | 14 +++++++++-----
.../exadel/frs/core/trainservice/ItemsBuilder.java | 6 ++++++
.../trainservice/service/SubjectServiceTest.java | 13 +++++++------
.../exception/IncorrectImageIdException.java | 10 ++++++++++
.../exception/InvalidImageIdException.java | 10 ----------
5 files changed, 32 insertions(+), 21 deletions(-)
create mode 100644 java/common/src/main/java/com/exadel/frs/commonservice/exception/IncorrectImageIdException.java
delete mode 100644 java/common/src/main/java/com/exadel/frs/commonservice/exception/InvalidImageIdException.java
diff --git a/java/api/src/main/java/com/exadel/frs/core/trainservice/cache/EmbeddingCollection.java b/java/api/src/main/java/com/exadel/frs/core/trainservice/cache/EmbeddingCollection.java
index c407158f4c..f9c258a8de 100644
--- a/java/api/src/main/java/com/exadel/frs/core/trainservice/cache/EmbeddingCollection.java
+++ b/java/api/src/main/java/com/exadel/frs/core/trainservice/cache/EmbeddingCollection.java
@@ -2,7 +2,7 @@
import com.exadel.frs.commonservice.entity.Embedding;
import com.exadel.frs.commonservice.entity.EmbeddingProjection;
-import com.exadel.frs.commonservice.exception.InvalidImageIdException;
+import com.exadel.frs.commonservice.exception.IncorrectImageIdException;
import com.google.common.collect.BiMap;
import com.google.common.collect.HashBiMap;
import lombok.AccessLevel;
@@ -151,9 +151,7 @@ public synchronized Optional getSubjectNameByEmbeddingId(UUID embeddingI
}
private Optional findByEmbeddingId(UUID embeddingId, Function, T> func) {
- if (embeddingId == null) {
- throw new InvalidImageIdException();
- }
+ validImageId(embeddingId);
return projection2Index.entrySet()
.stream()
@@ -161,9 +159,15 @@ private Optional findByEmbeddingId(UUID embeddingId, Function subjectService.verifyFace(
+ assertThrows(IncorrectImageIdException.class, ()-> subjectService.verifyFace(
ProcessImageParams.builder()
.apiKey(API_KEY)
.file(file)
diff --git a/java/common/src/main/java/com/exadel/frs/commonservice/exception/IncorrectImageIdException.java b/java/common/src/main/java/com/exadel/frs/commonservice/exception/IncorrectImageIdException.java
new file mode 100644
index 0000000000..874bea270e
--- /dev/null
+++ b/java/common/src/main/java/com/exadel/frs/commonservice/exception/IncorrectImageIdException.java
@@ -0,0 +1,10 @@
+package com.exadel.frs.commonservice.exception;
+
+import static com.exadel.frs.commonservice.handler.CommonExceptionCode.EMBEDDING_NOT_FOUND;
+
+public class IncorrectImageIdException extends BasicException {
+
+ public IncorrectImageIdException() {
+ super(EMBEDDING_NOT_FOUND, "Image Id is incorrect");
+ }
+}
\ No newline at end of file
diff --git a/java/common/src/main/java/com/exadel/frs/commonservice/exception/InvalidImageIdException.java b/java/common/src/main/java/com/exadel/frs/commonservice/exception/InvalidImageIdException.java
deleted file mode 100644
index b25cd6c71f..0000000000
--- a/java/common/src/main/java/com/exadel/frs/commonservice/exception/InvalidImageIdException.java
+++ /dev/null
@@ -1,10 +0,0 @@
-package com.exadel.frs.commonservice.exception;
-
-import static com.exadel.frs.commonservice.handler.CommonExceptionCode.EMBEDDING_NOT_FOUND;
-
-public class InvalidImageIdException extends BasicException {
-
- public InvalidImageIdException() {
- super(EMBEDDING_NOT_FOUND, "Image Id is not valid");
- }
-}
\ No newline at end of file
From 51a19d4f7882b6c2832cb3ffc69756427714d75f Mon Sep 17 00:00:00 2001
From: Tulika
Date: Wed, 27 Oct 2021 17:14:29 +0530
Subject: [PATCH 076/702] Fix filtering logic for EmbeddingCollection
---
.../core/trainservice/cache/EmbeddingCollection.java | 12 ++++--------
.../trainservice/service/SubjectServiceTest.java | 8 ++++++--
2 files changed, 10 insertions(+), 10 deletions(-)
diff --git a/java/api/src/main/java/com/exadel/frs/core/trainservice/cache/EmbeddingCollection.java b/java/api/src/main/java/com/exadel/frs/core/trainservice/cache/EmbeddingCollection.java
index f9c258a8de..2ea29107e5 100644
--- a/java/api/src/main/java/com/exadel/frs/core/trainservice/cache/EmbeddingCollection.java
+++ b/java/api/src/main/java/com/exadel/frs/core/trainservice/cache/EmbeddingCollection.java
@@ -153,16 +153,12 @@ public synchronized Optional getSubjectNameByEmbeddingId(UUID embeddingI
private Optional findByEmbeddingId(UUID embeddingId, Function, T> func) {
validImageId(embeddingId);
- return projection2Index.entrySet()
+ return Optional.ofNullable(projection2Index.entrySet()
.stream()
- .filter(entry -> {
- if(embeddingId.equals(entry.getKey().getEmbeddingId()))
- return embeddingId.equals(entry.getKey().getEmbeddingId());
- else
- throw new IncorrectImageIdException();
- })
+ .filter(entry -> embeddingId.equals(entry.getKey().getEmbeddingId()))
.findFirst()
- .map(func);
+ .map(func)
+ .orElseThrow(IncorrectImageIdException::new));
}
private void validImageId(UUID embeddingId) {
diff --git a/java/api/src/test/java/com/exadel/frs/core/trainservice/service/SubjectServiceTest.java b/java/api/src/test/java/com/exadel/frs/core/trainservice/service/SubjectServiceTest.java
index 6dad259314..5c4d7ca705 100644
--- a/java/api/src/test/java/com/exadel/frs/core/trainservice/service/SubjectServiceTest.java
+++ b/java/api/src/test/java/com/exadel/frs/core/trainservice/service/SubjectServiceTest.java
@@ -215,7 +215,9 @@ void testVerifyFaces(boolean status) {
var detProbThreshold = 0.7;
var randomUUId = UUID.randomUUID();
MultipartFile file = new MockMultipartFile("anyname", new byte[]{0xA});
- EmbeddingCollection embeddingCollection = EmbeddingCollection.from(Stream.of(makeEmbedding(randomUUId,"A", API_KEY)));
+ EmbeddingCollection embeddingCollection = EmbeddingCollection.from(Stream.of(
+ makeEmbedding(randomUUId,"A", API_KEY),
+ makeEmbedding("B", API_KEY)));
when(facesApiClient.findFacesWithCalculator(any(), any(), any(), any()))
.thenReturn(findFacesResponse(2));
@@ -253,7 +255,9 @@ void testInvalidImageIdException(boolean status){
var detProbThreshold = 0.7;
var randomUUId = UUID.randomUUID();
MultipartFile file = new MockMultipartFile("anyname", new byte[]{0xA});
- EmbeddingCollection embeddingCollection = EmbeddingCollection.from(Stream.of(makeEmbedding("A", API_KEY)));
+ EmbeddingCollection embeddingCollection = EmbeddingCollection.from(Stream.of(
+ makeEmbedding("A", API_KEY),
+ makeEmbedding("B", API_KEY)));
when(facesApiClient.findFacesWithCalculator(any(), any(), any(), any()))
.thenReturn(findFacesResponse(2));
From 0b1aac7522dea693057a0aca532672c317457ca6 Mon Sep 17 00:00:00 2001
From: Samvel Aivazian
Date: Thu, 28 Oct 2021 12:31:20 +0300
Subject: [PATCH 077/702] EFSR-1166 - Returned ND4J_CLASSIFIER
---
dev/Dockerfile | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/dev/Dockerfile b/dev/Dockerfile
index 1f7d863107..0ca7dea2e7 100644
--- a/dev/Dockerfile
+++ b/dev/Dockerfile
@@ -10,7 +10,7 @@ RUN mvn -B clean install -DskipTests -Dcheckstyle.skip -Dasciidoctor.skip -Djaco
COPY api api
COPY admin admin
COPY common common
-RUN mvn package -Dmaven.test.skip=true -Dmaven.site.skip=true -Dmaven.javadoc.skip=true
+RUN mvn package -Dmaven.test.skip=true -Dmaven.site.skip=true -Dmaven.javadoc.skip=true -Dnd4j.classifier=$ND4J_CLASSIFIER
FROM openjdk:11.0.8-jre-slim as frs_core
ARG DIR=/workspace/compreface
From dbc74b20f48e1789466eb5c25f4eb8e5074e7be7 Mon Sep 17 00:00:00 2001
From: Samvel Aivazian
Date: Thu, 28 Oct 2021 17:23:39 +0300
Subject: [PATCH 078/702] EFSR-1141 - Change validation check after service
creation
---
.../main/java/com/exadel/frs/service/ModelService.java | 10 ++++++++--
.../src/test/java/com/exadel/frs/ModelServiceTest.java | 8 ++++----
.../frs/commonservice/repository/ModelRepository.java | 5 +++++
3 files changed, 17 insertions(+), 6 deletions(-)
diff --git a/java/admin/src/main/java/com/exadel/frs/service/ModelService.java b/java/admin/src/main/java/com/exadel/frs/service/ModelService.java
index bb67ba8364..9f60bcb820 100644
--- a/java/admin/src/main/java/com/exadel/frs/service/ModelService.java
+++ b/java/admin/src/main/java/com/exadel/frs/service/ModelService.java
@@ -63,6 +63,12 @@ private void verifyNameIsUnique(final String name, final Long appId) {
}
}
+ private void verifyNameIsUniqueAndCaseInsensitive(final String name, final Long appId) {
+ if (modelRepository.existsByUniqueNameAndAppId(name, appId)) {
+ throw new NameIsNotUniqueException(name);
+ }
+ }
+
public Model getModel(final String appGuid, final String modelGuid, final Long userId) {
val model = getModel(modelGuid);
val user = userService.getUser(userId);
@@ -88,7 +94,7 @@ private Model createModel(final ModelCreateDto modelCreateDto, final String appG
authManager.verifyWritePrivilegesToApp(user, app);
- verifyNameIsUnique(modelCreateDto.getName(), app.getId());
+ verifyNameIsUniqueAndCaseInsensitive(modelCreateDto.getName(), app.getId());
log.info("model type: {}", modelCreateDto.getType());
@@ -138,7 +144,7 @@ public Model cloneModel(
authManager.verifyWritePrivilegesToApp(user, model.getApp());
- verifyNameIsUnique(modelCloneDto.getName(), model.getApp().getId());
+ verifyNameIsUniqueAndCaseInsensitive(modelCloneDto.getName(), model.getApp().getId());
val clonedModel = modelCloneService.cloneModel(model, modelCloneDto);
diff --git a/java/admin/src/test/java/com/exadel/frs/ModelServiceTest.java b/java/admin/src/test/java/com/exadel/frs/ModelServiceTest.java
index 1533e07602..3018fa5f7c 100644
--- a/java/admin/src/test/java/com/exadel/frs/ModelServiceTest.java
+++ b/java/admin/src/test/java/com/exadel/frs/ModelServiceTest.java
@@ -175,7 +175,7 @@ void successCreateModel() {
modelService.createRecognitionModel(modelCreateDto, APPLICATION_GUID, USER_ID);
val varArgs = ArgumentCaptor.forClass(Model.class);
- verify(modelRepositoryMock).existsByNameAndAppId("model-name", APPLICATION_ID);
+ verify(modelRepositoryMock).existsByUniqueNameAndAppId("model-name", APPLICATION_ID);
verify(modelRepositoryMock).save(varArgs.capture());
verify(authManager).verifyWritePrivilegesToApp(user, app);
verifyNoMoreInteractions(modelRepositoryMock, authManager);
@@ -196,7 +196,7 @@ void failCreateModelNameIsNotUnique() {
.build();
when(appServiceMock.getApp(anyString())).thenReturn(app);
- when(modelRepositoryMock.existsByNameAndAppId(anyString(), anyLong())).thenReturn(true);
+ when(modelRepositoryMock.existsByUniqueNameAndAppId(anyString(), anyLong())).thenReturn(true);
assertThatThrownBy(() ->
modelService.createRecognitionModel(modelCreateDto, APPLICATION_GUID, USER_ID)
@@ -242,7 +242,7 @@ void successCloneModel() {
val clonedModel = modelService.cloneModel(modelCloneDto, APPLICATION_GUID, MODEL_GUID, USER_ID);
verify(modelRepositoryMock).findByGuid(MODEL_GUID);
- verify(modelRepositoryMock).existsByNameAndAppId("name_of_clone", APPLICATION_ID);
+ verify(modelRepositoryMock).existsByUniqueNameAndAppId("name_of_clone", APPLICATION_ID);
verify(modelCloneService).cloneModel(any(Model.class), any(ModelCloneDto.class));
verify(authManager).verifyAppHasTheModel(APPLICATION_GUID, repoModel);
verify(authManager).verifyWritePrivilegesToApp(user, app);
@@ -271,7 +271,7 @@ void failCloneModelNameIsNotUnique() {
when(modelRepositoryMock.findByGuid(anyString())).thenReturn(Optional.of(repoModel));
when(appServiceMock.getApp(anyString())).thenReturn(app);
- when(modelRepositoryMock.existsByNameAndAppId(anyString(), anyLong())).thenReturn(true);
+ when(modelRepositoryMock.existsByUniqueNameAndAppId(anyString(), anyLong())).thenReturn(true);
assertThatThrownBy(() ->
modelService.cloneModel(modelCloneDto, APPLICATION_GUID, MODEL_GUID, USER_ID)
diff --git a/java/common/src/main/java/com/exadel/frs/commonservice/repository/ModelRepository.java b/java/common/src/main/java/com/exadel/frs/commonservice/repository/ModelRepository.java
index 9ba941d87a..b995a5c286 100644
--- a/java/common/src/main/java/com/exadel/frs/commonservice/repository/ModelRepository.java
+++ b/java/common/src/main/java/com/exadel/frs/commonservice/repository/ModelRepository.java
@@ -40,6 +40,11 @@ public interface ModelRepository extends JpaRepository {
boolean existsByNameAndAppId(String name, Long appId);
+ @Query("select case when count(m) > 0 then TRUE else FALSE end " +
+ "from Model m " +
+ "where lower(m.name) = lower(:name) AND m.app.id = :appId")
+ boolean existsByUniqueNameAndAppId(String name, Long appId);
+
@Query("SELECT " +
" new com.exadel.frs.commonservice.entity.ModelSubjectProjection(m.guid, count(s.id)) " +
" FROM " +
From 0efcacc122fa0eb768722200b60cf504afe6cefb Mon Sep 17 00:00:00 2001
From: Samvel Aivazian
Date: Thu, 28 Oct 2021 17:56:48 +0300
Subject: [PATCH 079/702] EFSR-1096 - Fixed message in old password validation
check
---
.../main/java/com/exadel/frs/dto/ui/ChangePasswordDto.java | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/java/admin/src/main/java/com/exadel/frs/dto/ui/ChangePasswordDto.java b/java/admin/src/main/java/com/exadel/frs/dto/ui/ChangePasswordDto.java
index e118f83f3e..4d62e60fa5 100644
--- a/java/admin/src/main/java/com/exadel/frs/dto/ui/ChangePasswordDto.java
+++ b/java/admin/src/main/java/com/exadel/frs/dto/ui/ChangePasswordDto.java
@@ -27,8 +27,8 @@
@AllArgsConstructor
public class ChangePasswordDto {
- @NotEmpty(message = "User's old password is incorrect")
- @Size(min = 8, max = 255, message = "User's old password is incorrect")
+ @NotEmpty(message = "User's password is incorrect")
+ @Size(min = 8, max = 255, message = "User's password is incorrect")
private String oldPassword;
@NotEmpty(message = "User's new password is incorrect")
From 962db0944ccaddcfd084e619252ef90f8e7ce2ba Mon Sep 17 00:00:00 2001
From: asergeiev <89693966+asergeiev@users.noreply.github.com>
Date: Fri, 29 Oct 2021 12:23:18 +0300
Subject: [PATCH 080/702] Update README.md
---
README.md | 362 +++++++++++++++++++++++++++---------------------------
1 file changed, 181 insertions(+), 181 deletions(-)
diff --git a/README.md b/README.md
index 0362a6766a..53c54f60df 100644
--- a/README.md
+++ b/README.md
@@ -1,182 +1,182 @@
-
-
Exadel CompreFace is a leading free and open-source face recognition system
-
-
-
-
-
-
- Exadel CompreFace is a free and open-source face recognition service that can be easily integrated into any system without prior machine learning skills.
- CompreFace provides REST API for face recognition, face verification, face detection, landmark detection, age, and gender recognition and is easily deployed with docker.
-
-
-
-
-
-# Table Of Contents
-
- * [Overview](#overview)
- * [Screenshots](#screenshots)
- * [News and updates](#news-and-updates)
+
+
Exadel CompreFace is a leading free and open-source face recognition system
+
+
+
+
+
+
+ Exadel CompreFace is a free and open-source face recognition service that can be easily integrated into any system without prior machine learning skills.
+ CompreFace provides REST API for face recognition, face verification, face detection, landmark detection, age, and gender recognition and is easily deployed with docker.
+
+
+
+
+
+# Table Of Contents
+
+ * [Overview](#overview)
+ * [Screenshots](#screenshots)
+ * [News and updates](#news-and-updates)
* [Features](#features)
- * [Functionalitie](#functionalities)
- * [Getting Started with CompreFace](#getting-started-with-compreface)
- * [CompreFace SDKs](#compreface-sdks)
- * [Documentation](/docs)
- * [How to Use CompreFace](/docs/How-to-Use-CompreFace.md)
- * [Face Services and Plugins](/docs/Face-services-and-plugins.md)
- * [Rest API Description](/docs/Rest-API-description.md)
- * [Face Recognition Similarity Threshold](/docs/Face-Recognition-Similarity-Threshold.md)
- * [Configuration](/docs/Configuration.md)
- * [Architecture and Scalability](/docs/Architecture-and-scalability.md)
- * [Custom Builds](/docs/Custom-builds.md)
- * [Face data migration](/docs/Face-data-migration.md)
- * [User Roles System](/docs/User-Roles-System.md)
- * [Face Mask Detection Plugin](/docs/Mask-detection-plugin.md)
- * [Kubernetes configuration](https://github.com/exadel-inc/compreface-kubernetes)
- * [Gathering Anonymous Statistics](/docs/Gathering-anonymous-statistics.md)
- * [Contributing](#contributing)
- * [License info](#license-info)
-
-
-# Overview
-
-Exadel CompreFace is a free and open-source face recognition GitHub project.
-Essentially, it is a docker-based application that can be used as a standalone server or deployed in the cloud.
-You don’t need prior machine learning skills to set up and use CompreFace.
-
-The system provides REST API for face recognition, face verification, face detection, landmark detection, age, and gender recognition.
-The solution also features a role management system that allows you to easily control who has access to your Face Recognition Services.
-
-CompreFace is delivered as a docker-compose config and supports different models that work on CPU and GPU.
-Our solution is based on state-of-the-art methods and libraries like FaceNet and InsightFace.
-
-# Screenshots
-
-
-
-
-
-
-
-
-
-# News and updates
-
-[Subscribe](https://exadel-7026941.hs-sites.com/en/en/compreface-news-and-updates) to CompreFace News and Updates to never miss new features and product improvements.
-
-# Features
-
-The system can accurately identify people even when it has only “seen” their photo once. Technology-wise, CompreFace has several advantages over similar free face recognition solutions. CompreFace:
-
-- Supports many face recognition services: face identification, face verification, face detection, face mask detection, landmark detection,
- and age and
-gender recognition
-- Supports both CPU and GPU and is easy to scale up
-- Is open source and self-hosted, which gives you additional guarantees for data security
-- Can be deployed either in the cloud or on premises
-- Can be set up and used without machine learning expertise
-- Uses FaceNet and InsightFace libraries, which use state-of-the-art face recognition methods
-- Features a UI panel for convenient user roles and access management
-- Starts quickly with just one docker command
-
-# Functionalities
-
-- Supports many face recognition services:
- - [face detection](/docs/Face-services-and-plugins.md#face-detection)
- - [face recognition](/docs/Face-services-and-plugins.md#face-recognition)
- - [face verification](/docs/Face-services-and-plugins.md#face-verification)
- - [landmark detection plugin](/docs/Face-services-and-plugins.md#face-plugins)
- - [age recognition plugin](/docs/Face-services-and-plugins.md#face-plugins)
- - [gender recognition plugin](/docs/Face-services-and-plugins.md#face-plugins)
- - [face mask detection plugin](/docs/Face-services-and-plugins.md#face-plugins)
-- Use the ComperFace UI panel for convenient user roles and access management
-
-# Getting Started with CompreFace
-
-### Requirements
-
-1. Docker and Docker compose (or Docker Desktop)
-2. CompreFace could be run on most modern computers with [x86 processor](https://en.wikipedia.org/wiki/X86) and [AVX support](https://en.wikipedia.org/wiki/Advanced_Vector_Extensions).
- To check AVX support on Linux run `lscpu | grep avx` command
-
-### To get started (Linux, MacOS):
-
-1. Install Docker and Docker Compose
-2. Download the archive from our latest release: https://github.com/exadel-inc/CompreFace/releases
-3. Unzip the archive
-4. Open the terminal in this folder and run this command: `docker-compose up -d`
-5. Open the service in your browser: http://localhost:8000/login
-
-### To get started (Windows):
-
-1. Install Docker Desktop
-2. Download the archive from our latest release: https://github.com/exadel-inc/CompreFace/releases
-3. Unzip the archive
-4. Run Docker
-5. Open Command prompt (write `cmd` in windows search bar)
-6. Open folder where you extracted zip archive (Write `cd path_of_the_folder`, press enter).
-7. Run command: `docker-compose up -d`
-8. Open http://localhost:8000/login
-
-### Getting started for contributors
-
-Follow this [link](/dev)
-
-# CompreFace SDKs
-
-| SDK | Repository |
-| ---------- | ------ |
-| JavaScript | https://github.com/exadel-inc/compreface-javascript-sdk |
-| Python | https://github.com/exadel-inc/compreface-python-sdk |
-
-# Documentation
-
-More documentation is available [here](/docs)
-
-# Contributing
-
-We want to improve our open-source face recognition solution, so your contributions are welcome and greatly appreciated.
-
-* Just use CompreFace and [report](https://github.com/exadel-inc/CompreFace/issues) ideas and bugs on GitHub
-* Share knowledge and experience via posting guides and articles, or just improve our [documentation](https://github.com/exadel-inc/CompreFace/tree/master/docs)
-* Create [SDKs](https://github.com/topics/compreface-sdk) for favorite programming language, we will add it to our documentation
-* Integrate CompreFace support to other platforms like [Home Assistant](https://www.home-assistant.io/) or [DreamFactory](https://www.dreamfactory.com/), we will add it to our documentation
-* [Contribute](CONTRIBUTING.md) code
-* Add [plugin](/docs/Face-services-and-plugins.md#face-plugins) to face services
-* And last, but not least, you can just give a star to our free facial recognition system on GitHub
-
-For more information, visit our [contributing](CONTRIBUTING.md) guide, or create a [discussion](https://github.com/exadel-inc/CompreFace/discussions).
-
-# License info
-
-CompreFace is open-source real-time facial recognition software released under the [Apache 2.0 license](https://www.apache.org/licenses/LICENSE-2.0.html).
+ * [Functionalities](#functionalities)
+ * [Getting Started with CompreFace](#getting-started-with-compreface)
+ * [CompreFace SDKs](#compreface-sdks)
+ * [Documentation](/docs)
+ * [How to Use CompreFace](/docs/How-to-Use-CompreFace.md)
+ * [Face Services and Plugins](/docs/Face-services-and-plugins.md)
+ * [Rest API Description](/docs/Rest-API-description.md)
+ * [Face Recognition Similarity Threshold](/docs/Face-Recognition-Similarity-Threshold.md)
+ * [Configuration](/docs/Configuration.md)
+ * [Architecture and Scalability](/docs/Architecture-and-scalability.md)
+ * [Custom Builds](/docs/Custom-builds.md)
+ * [Face data migration](/docs/Face-data-migration.md)
+ * [User Roles System](/docs/User-Roles-System.md)
+ * [Face Mask Detection Plugin](/docs/Mask-detection-plugin.md)
+ * [Kubernetes configuration](https://github.com/exadel-inc/compreface-kubernetes)
+ * [Gathering Anonymous Statistics](/docs/Gathering-anonymous-statistics.md)
+ * [Contributing](#contributing)
+ * [License info](#license-info)
+
+
+# Overview
+
+Exadel CompreFace is a free and open-source face recognition GitHub project.
+Essentially, it is a docker-based application that can be used as a standalone server or deployed in the cloud.
+You don’t need prior machine learning skills to set up and use CompreFace.
+
+The system provides REST API for face recognition, face verification, face detection, landmark detection, age, and gender recognition.
+The solution also features a role management system that allows you to easily control who has access to your Face Recognition Services.
+
+CompreFace is delivered as a docker-compose config and supports different models that work on CPU and GPU.
+Our solution is based on state-of-the-art methods and libraries like FaceNet and InsightFace.
+
+# Screenshots
+
+
+
+
+
+
+
+
+
+# News and updates
+
+[Subscribe](https://exadel-7026941.hs-sites.com/en/en/compreface-news-and-updates) to CompreFace News and Updates to never miss new features and product improvements.
+
+# Features
+
+The system can accurately identify people even when it has only “seen” their photo once. Technology-wise, CompreFace has several advantages over similar free face recognition solutions. CompreFace:
+
+- Supports many face recognition services: face identification, face verification, face detection, face mask detection, landmark detection,
+ and age and
+gender recognition
+- Supports both CPU and GPU and is easy to scale up
+- Is open source and self-hosted, which gives you additional guarantees for data security
+- Can be deployed either in the cloud or on premises
+- Can be set up and used without machine learning expertise
+- Uses FaceNet and InsightFace libraries, which use state-of-the-art face recognition methods
+- Features a UI panel for convenient user roles and access management
+- Starts quickly with just one docker command
+
+# Functionalities
+
+- Supports many face recognition services:
+ - [face detection](/docs/Face-services-and-plugins.md#face-detection)
+ - [face recognition](/docs/Face-services-and-plugins.md#face-recognition)
+ - [face verification](/docs/Face-services-and-plugins.md#face-verification)
+ - [landmark detection plugin](/docs/Face-services-and-plugins.md#face-plugins)
+ - [age recognition plugin](/docs/Face-services-and-plugins.md#face-plugins)
+ - [gender recognition plugin](/docs/Face-services-and-plugins.md#face-plugins)
+ - [face mask detection plugin](/docs/Face-services-and-plugins.md#face-plugins)
+- Use the ComperFace UI panel for convenient user roles and access management
+
+# Getting Started with CompreFace
+
+### Requirements
+
+1. Docker and Docker compose (or Docker Desktop)
+2. CompreFace could be run on most modern computers with [x86 processor](https://en.wikipedia.org/wiki/X86) and [AVX support](https://en.wikipedia.org/wiki/Advanced_Vector_Extensions).
+ To check AVX support on Linux run `lscpu | grep avx` command
+
+### To get started (Linux, MacOS):
+
+1. Install Docker and Docker Compose
+2. Download the archive from our latest release: https://github.com/exadel-inc/CompreFace/releases
+3. Unzip the archive
+4. Open the terminal in this folder and run this command: `docker-compose up -d`
+5. Open the service in your browser: http://localhost:8000/login
+
+### To get started (Windows):
+
+1. Install Docker Desktop
+2. Download the archive from our latest release: https://github.com/exadel-inc/CompreFace/releases
+3. Unzip the archive
+4. Run Docker
+5. Open Command prompt (write `cmd` in windows search bar)
+6. Open folder where you extracted zip archive (Write `cd path_of_the_folder`, press enter).
+7. Run command: `docker-compose up -d`
+8. Open http://localhost:8000/login
+
+### Getting started for contributors
+
+Follow this [link](/dev)
+
+# CompreFace SDKs
+
+| SDK | Repository |
+| ---------- | ------ |
+| JavaScript | https://github.com/exadel-inc/compreface-javascript-sdk |
+| Python | https://github.com/exadel-inc/compreface-python-sdk |
+
+# Documentation
+
+More documentation is available [here](/docs)
+
+# Contributing
+
+We want to improve our open-source face recognition solution, so your contributions are welcome and greatly appreciated.
+
+* Just use CompreFace and [report](https://github.com/exadel-inc/CompreFace/issues) ideas and bugs on GitHub
+* Share knowledge and experience via posting guides and articles, or just improve our [documentation](https://github.com/exadel-inc/CompreFace/tree/master/docs)
+* Create [SDKs](https://github.com/topics/compreface-sdk) for favorite programming language, we will add it to our documentation
+* Integrate CompreFace support to other platforms like [Home Assistant](https://www.home-assistant.io/) or [DreamFactory](https://www.dreamfactory.com/), we will add it to our documentation
+* [Contribute](CONTRIBUTING.md) code
+* Add [plugin](/docs/Face-services-and-plugins.md#face-plugins) to face services
+* And last, but not least, you can just give a star to our free facial recognition system on GitHub
+
+For more information, visit our [contributing](CONTRIBUTING.md) guide, or create a [discussion](https://github.com/exadel-inc/CompreFace/discussions).
+
+# License info
+
+CompreFace is open-source real-time facial recognition software released under the [Apache 2.0 license](https://www.apache.org/licenses/LICENSE-2.0.html).
From 1bee874af42de9f8fa31aa1a943de72a805054f6 Mon Sep 17 00:00:00 2001
From: asergeiev <89693966+asergeiev@users.noreply.github.com>
Date: Fri, 29 Oct 2021 14:08:31 +0300
Subject: [PATCH 081/702] Added screenshots
---
docs/pics/13h41m46s_001_.png | Bin 0 -> 24320 bytes
docs/pics/14h03m09s_002_.png | Bin 0 -> 26117 bytes
docs/pics/14h06m24s_003_.png | Bin 0 -> 30651 bytes
3 files changed, 0 insertions(+), 0 deletions(-)
create mode 100644 docs/pics/13h41m46s_001_.png
create mode 100644 docs/pics/14h03m09s_002_.png
create mode 100644 docs/pics/14h06m24s_003_.png
diff --git a/docs/pics/13h41m46s_001_.png b/docs/pics/13h41m46s_001_.png
new file mode 100644
index 0000000000000000000000000000000000000000..5ae2b7b57079da0426ab47f494d21d8b3cbbdaff
GIT binary patch
literal 24320
zcmd43bx>SSw=O(DfZ)M`LkKRxT>}IQ9y~Y`+=9CW4VvJ=2?-F~-GU7g7zobb9$;`E
zA9&YI+$5m$l2Ii0Np&}=HTMs
z0fD@XPfmWRD66>-{Zhm^bk*gW;2q^f21yp87EJs`7M$%VJk2>T
zE6klT_n)%57!hFf=b~j~dFq|i8arSbKOIy0Fs&&U&6@F*9C4MtK;3^yZT=lPyQOQd
zTB@{Ni`_iEIVGoSZ1@eoyaiV1&Ui2x8~t7fPu8ekp|eG6x8H3sc>yyZQcJMgJbIis
zn%>TPKmH4CAaCXhHp?L0>=NEfg^6D`LX@}z6#*SfHvu60|35$O4$tgSr37~gfKD{H
z#;-QARqY=78yhhvrF;JTc@%y(>BFrU(!NOG$Gle3K8&g>8VUGrUBO+2;%G&>%Mf%{
zjq~u3;MZ_=?$lQcj9P+bg&FQ`U+rNP?hnmGJ`#Mpu2cQAS-%jP;W$O?KF49L&k1`1
zGz$Ael=~s&tXd@v_O$P&es*L@X!t8ayXU&7j0cBguvVv37`6X_>k~XN8k|4
z*m&f6L}c{!taiJta1V_Mx}gU!06bp-|8F8`Q51D2%O_mrTgx$Wcgh%&BQ
zOtAUk5tgD^gRk_yJch;M((V4k1hNV>)pr;}7mk}~`a+WhdvXqM7BxD$^qzEZtz#M$
z*xYqU>^=y8;VB9V3SJkcF^V;6wLv9oL~GtJipu8^<&LKp5`f09FNbLs7ZA?n;W`xQ_~V)a}fAJFTZl^)dZrUa#tNI*!S%zEpL00o=9MH
z%yBs}!vvW8z7+hqHo8%K_39N-r%l29vA!jyrI7^a4i={HQx%p#Q
zgA>g;+|`!yKh;{PElbbN!UiwS@oa60XT$0v>r4x5Ru_E_aA2Xpa|-mE
zw1j2vpArp%JRie!p22=_P5t=z+d#Dbg=*Qu2=plAfC0D7MCha)+ocnU!VwG4`Q_1O
zJxB>IwHK~l5Qd~Rs_M38oi)_g|CMJE31SZ>p1sr-GU@At%Z>0#4V(Ls&K)6`=^Zjt
z-A5zh~i%bt!iBg&Q!3*zF9H-!Rq$hOy
zF%1HxFs26y*Aiwb69{C6T@+qiWS_Fd$S!;?+I^V(0>@(KBumAsVIGa?T?10AwS~NX
zQH6&V-8Uc71e4%;DNR&n?ABJ@2~5D_V{ihZR*{Y5_Vu)^$6zzjr}de@IJZ48i^;Iw
zaw^QYm;eHz6xGR+2fCQBT3AnNx6A|-B%r>o2DtJ{wBLYjN(rtv?dVDB>gJR1aIHbJ
zP^^0G3qv{A!$@C63dX?oy6Az`+sUUIW_J$f2EKl6SyT%m6j
z%XV^2C8I5R(S|6?wTj1mtbnfm_L-@FcnnV3=W}#}TyEH8zEI2>7kJJ2`979_<5qJf
zyah5yWAW)KKe=-9LCg`{F=wmoQrNh24L#jnJDvsaF_*f-8Ys*R9=3TCSIk*25~V`p
zeYN))f6{S0UE;v<(UKR&$$>qll4MsrXM4;|qAKs!#bLK(K)bf)8J^Sf)vpt;HJVVB
z0KRBt%co_{r7DKLZS+ap@x)b@jwj^p7iSn^*#plKDP-?1mOA4DcapMx_B4R}?2yS0
z)&Mg#=FrPkZ)A|scpf7Bi$A+>rZTI4F+={Ysp0+hQb!z{Zu#=rl0T`F$|m=HcHL$E
zP{p#KTKTg2pNjVzgR*{G4Y;{3`w>6mSJvjFsjnLFRwnFpfjhft*Cb9WS6iFTIG{rb
zV_#>=!1F&Ohqr9NO=t&dlSZG@NPBENwHNu4zCrh{3CdyJbXEO1mDugeEy%+YU~yH1E$J<1(Oe3#E0wzeA!VL+f|t`Sktf0G|XVTL!RZjwQEdY@gz{NZxNOVWV6=X4?wDxeB~S
z&c570Iq$k~hDUT7ECo`2VS##>Ac~s&E;k}ZYwmq;L>9X)vVSczK)-4VA}v43=Aimn
za~1e;ERbRCM`8Qpt170$ZMqQ&VBNwV+6X#GLntvb|hzD{iMWz4mKBhxntQ
z_G37!s`w^`Bmy0&h4!d3*x^xr#!D2FpPq{%Ve*oHjNO(sr=St-r-;jv`|6K09Tx&R
z0eU~D@0KqpG>?7hdu=8bOMG+Lhvaz`5+YQI*LltGtK%B2-0TTn
z;2jI4_aAJI0L{Mdf0R6kfW7o};e(ky40m~quJHXfVyVyW0O=*NnwT#pdixoVal02z
z?68WxK>12tMf~db?<$}WFSk=+(hid@w$w$)<|ZTgL0kAifC=+)ut^_@1VYBo^N3(^
zX})Lk8kQ!0u`qSqLwLC}jk}!e)EXnWXva6?r5J!AdBX(y_(ky}`X9dj3*;r?1
zCq9S<;(u?Eqfz9#-wtASO7zwK;i1VfPQ>?D=a7iRf@0s+6$r&&j8{bRIUJDDMAq
z{~y~(%Kg`m^+x`68q9Loe?1Q%(Em3u`yXBZ&uD6&`eT?yooKh9JiN?5)pnbC4gx76
z%424=J%676CX{Du9u|we-Mc(pFHo4P$8C7wgt)3K7N8mTW%s;4ijBB%hjHw>J7rC`
z>u58}Qr-G$87MT}29%3--NnpQ-P&(6dwBVK+#|lBO$tktuo8WL+=gF|^W^+Wwhc@Q
zv#5sfThFm$zL`lM^G1n236DB8GYc$YdHs_4kli!zayEY4aKCk>3=jD`{OR$=n!>lw
z_yWu(mU48hvOiK+5jCCQ&_Q&mR3!%xc4&U$#{Mr@dw
zOi^nHS%055Ici0v$zi0Y)X~UGRq@y*v(0>*e&K5v;?dfTi9;2w#1rTKR^C)AYRXyY
z1=yvaJIW}f#g5@!62Gv08TI6QXCzYL>6}LLLS2!vu#Dm*ThBIs!3`RUl36Qw8p=3<;?)o*3Ym_MHFf`0#$J>!V}pi>{9
zLKF(|ES2F@l_^76-8BcMWLRWS@{4&C2}FPiNZHIE-$1D_Z>FLosliw<|4olRWp}?t
zp`ownQMa{r7*D>8ghSYvhX+p7CPi}y#T6B_dFbwPWd&cY0Yw!{K(CK+-$$~s92mqhO>
zG7vRL<*r|WEtbxP#5>~pG%nBUM#PUpkDr%G9O6$#w_2sIotLSmZxNH67pot*`>o6{
zLIq1?u$aL
z2P`p|SmMxLmWDN}1Xp7;N)z872UZ~IAr)y3yJ4P;4!MM$LvZJ;CwBj5N#3FyG(X1+
z&}o)@g<0H?lVdoF;yimAyDwik{}=Qc|IsMyN@A5)x#g=@jtf{XI$z4}+uKLQbGA`_BU>|HJ)OVeXa1
z26egK%=9$D3XzMYEJ3C{#NyVh;l7w)zPYN3avCAwPv}O7VBxQix)tfS$3HVP%OQ44
zkP(Q0bztkpb-5MIlBxwdHNtaClrm!3wZ}J`f2cCvzt(lTSeUcDPoQIK=ID}~*wxm2
zu#|LUPIuVbnMOoHe%ycmvXa_>-Ncjz@v$D_xi5iEhq0FU*N7ncMD3Dad_uIVpx7jX|&;!69_wcVuB)gfcWVyd3uAx
zF}->(aUqe4x0q>r-|CWrXh)+-Oa5^VaZg^Gcan&^X!JSoyj0%t@`E2FluHO+_ud=M
z4uP0K?bypopa9~|H4ruG;o~6a?3WE*k8nlUm(6jHKC)U~C=(ppjk8p_mPSXe8U2o|
z_#;19{6}6h{h4vgqsRU{J@@MM5Jb+Giw+Sm7zdylPzeLxk6?1sI4madYijs01xy@){#hWo-2s9
zMfjZ!`C!6D2f{kNPLz7eJ4Z@!{F4!&%j%W1*$BbRw{*j{Z#|uG+N=c&;~6wEPKerz
zrN1D(eoHnR9%pe-k5pk`@B-sqq*Y#$X-;xmiD?`QKOQ)Ig*dGT{*Tse4d)%G(bY8m;Xh(nXd-v#f
zSNP#vKeKRM6<)!!NkPL33LQ@A-p7_k2ZXy#{qjS1Q13cSDiOEuhzkSA8cgq}i;>)q
zdAR5gn(F>oRd5y0qHL^)dl#1nI>vQ1GKkMD(+GrFh^3ePB|II=_Vvly{l*^>x0qP|
zWz&(9ymx2#6ct3=-g#(EyLV=uK|uvLpkdWpCb|x4n0CmHt!kN+_S0^GISDnPoP8ys
zhK|Q-sbwdwMM1MSZ)<*tF-+!-yes8G1;x6T(uNpzNd%GmWy1aPG5z_bWhLv4
z(+6Js96mO?KDu5qp6bO(58U4A8DC$FU5q~%aft|=<1kj4O=Ne;cP}FOup3_b3E5QJ
z{j4rB0_ipeb6riEI>DL8>8uK)Y!>tLgGhgy??y3hDQ%P8<57W)N?9V62>yh0&V}Fe
z`wnQKHMUdx$mUGq2hQBmzB4JC3OGwZy1jY3?EJ$>{qvveojUqA330uflO;MYV&5HT
z!tCg5OTokKAmmEBSAIIXb0I(KPt2SZyZY%Ctt`V%ep^#f2#UdA{P+Q-CC_gwatTeK
zQ{E|n`6A#-w>#5l&i(4Rq$a_GjKSkFLb9+UTjajgu3f^!ZvwtJavwbQUvRX~rb3K`+V$PU-{ho%c(i8PwDV3Zv
zBb1STW3&)9YQ1FPq_UE_}D+{I}8
z()SHX0y`HuzfaOrZ*Z@;Qhr0WgcoG2>ydpI{(dGwJfz&uoKufn+iQf9DJ~KH*}TMX
zx@q)}I|aGCRJy$c9*arPUenvM_GOj^vd0U$v*SFBCFjyxO?(%`4kT=k{2ARP+NHJvyqazkh6mCl
zD!_i$6ialEI>&?ALzkMRvaWk!615^%q77<7g5!J@wImcHd@5-L9|0#f-|9_B6L3kp
z-*JCeIUMQp8}Wp4Dz&>$`+;ZI43
zBFEmAMID^?kVnZkD$C0+Vpb-)&w$+toWPdyoVjUzI;0qXeSW{1GmmUfJ}
zrGOd}S9fi{4tb3ba;_0_e7FNP*njIJxT#p~u3EW(ryTKS&b2prH@Y;?Y`BOBp_5XS
z>QctZ{2i`?O*#J_PpO~Tj=Xl_ASmu}a8AQpiO$wlJKIL<<1oyaX`hlLp6Ef%*s;cKkXsQkX+H=CH;<
zB7{FF11nSbQsfaglo__)gkGYsF`YX{mn$gm?6}RG{l37?CYI}f%AvJRm1YPo+x_LV
zX^(pL_V}^o5RW>`DtnyS`f;r|W`pO1&BZSe90BHx=eus{Eh2J$O4rX`H2NL+b^Hsk
zPu?OsOtf5U!nweZZc0%8;AwXJrH}9V^kZS%#j!&ph?#cSo8i3BKbsSRQ+E3j9>br<
zZ{w0ZIaHy-6XaQo{)TTg;gR_(6r<5r!TEH0*;PaD<;b0Y0V2ua7OSVy6RE;>z2u(b
ztRVnqzn3IAU?QI474`djk;*ibR@ciUhR|+Zcb#3jNNpWDyyWO9L!xTX7VsGid|&H
z-v{e|As9MY6dc}k-7(K8gJkls$=MkkIWHh!0ZV);yJsij^7Bw={1W%RA7!gp#Zk*i
zxdZI{#d{N~0LbK~Yg}4^yK>JhVtJTlg}W5I+$MQnu9@vFR?#kE)`pHuogcd^ZjQE}
zv9?o^JxzNmGy{y4*rKiI+K3we3y270f_BIN>cCuf2E`+LJ?h-r`*lnv+Wif#d}1TI
zhCTOXE71e3nM8+i4v&p)0p%WhvM0UD*xNR5o}WE?E=`+w@T#7md0Y=d9;DT24gjn-
zckfF6JO`YsVjbge6;M!USG0$U)bm*El&VEgwb`(2iT3tr9+qo962Y5c2VeGS(TGed
z<4)CTC{YkE8&}^u`oD)*rrx(7#@^ICeSY;X7*;n`HmyOj*@6OU@{R#n5$xjD++R`D
zG3x8k>J##H5Z3>6c;ZGGRDUvkn~wY`6ytM5b7|WjK9WLCl-+3tB4hiu>j8PWXD{>;
z!$l8Rn}k^!Ci$>H)}WvmFj&zP_tWuOzj?2=$EdeuYfTIQ+>%f(YWxGVSsXwzd1nsr
zllRgd;;9ab*x*Nrjg71aS1i_%{asvBC3(nvWxEx7iwwW}b2&mwU`5f4E!*{D(~6fo
zAB?s6A=g7O)I5>aopBmA^r43Nf)lTv{#$MW)FmPofGa*>fJ
ze^IzM4g!)*-vXTUik1$Po=2fn6wLEhlR}k7t!_9&)CzctI1TMz7_>dEMgm*@i+~(`#Y>79<8i3Xm#gC1W(L!CMxH2NyGXo`pB;D{1f09YnaX7XuJ({yK|*sH5K6Gs7ZfK7x)Dy
zHvfG<^(|XjO4T(0S@3X;_3zHnE$t^}Q!u`BD!?Fgq>u+{HAjPEQ^3!lecQqkL
zF)`+`3$e*NQNy(XDEy7^TtX2w>G(}ac<;EO(2pVqh#-I+toL0@FysWqIFg~>aP^fV{IfZ
zt(QNp#OR4elAq-i&0&>wR*VQijO1uJf4V1qi0d8|!u{^;B$~2u
zk<`9#89)AlV`fIeIdh35o}uoKStN_w8C<@S{TEe~=qclloMs8?f#>y))2z=3Jo7_gAJ~2;LA9BmsA_6@*Qx3EuQI>*%7?*RgPoEC!$IYs=;q2%x2bb{`^dUsiT54*WNq+Ti
z^fP4#RX>g?l8M=16vNU`B72lm&8we5S;$3U=Zfhu(b
zBG_sOL|6FU_3ND&Ew+(Q5Dt+F7wVtb%x&4tVF;C96rHK%-NpDjG64Z3t2fMSpS-r}
z`KERy?r-X#lW#LLD@p(4^pH=G<$km2@4_u`ZK@BNIYX3qZoiijTh5xYGn{UR$1st_4&_*ccb04vLz&W(rLw3
zz=_YDy#t(V_YnHCLa|lpSz8mAgAdUOI^}B9Y4l&czzSGMuyOV&CFkIn$iHX8L)>6^
zNdUR^Q(eLuQ88eyL7ge2Ncd5mmgCUK0+L
z6$uYB)rGJ^)_DU;e)Z#p>`&B{Bhw;oTDQ%(d{b47$^+|`rumEMxI^A$g{eW;UGF-t
z9PGCB3=@nrKcTk8mpz-t{56}omrh=Gf+&+|GTRC%!M@HPY!xx4AF?5SS{K7D`=IXQ?sprX7ItRk1zg;zOjC>hgGi{S_fIuOWy6}t0KYPBiUTSjlH1eHCaKmzcjyAh=
zjkw^(j;zRvkP~w6rIIP%*=Fw{B53#dh36;O<1(%93(FJH6~#*o2f=;GMJ%_-}5<0frYgH`A7D?lfRKWup&Zo
zJ29K_$N6Srk*zCcf!SFsH|xGI+1XHMX#p(;!fRx5?>?!`Uj-C12Pvxhu;D}HZdERy
z;fM0F>(0sd*Hy`6tF|)qs~Fn0RSNfS8{9G6ii|TKt5VBc0RCX0QUv>hd+tQ|wE&m*l5z$AjNjhzN!@FVMKF5ano1iXRC^|Adv1WqrfW~|=ya7n>Fdnak2aIUB!M7W
zI~#v96iBA4g?5;W9Hg?%q3}X^YmLH?W3Qmn#kA~o;Vt~MIj_m@s!HD)tT?f`oFHB<
zC5kJ=Usqh&QFb!Qnjh(Sy^yEPR!~>}=SISjn^jiXgwyoA5pnck(J5;PN0cTLQfu4G
zSLN{@i1*fncM0QI0N`3(eXsc5%nCq7L4+C^qaWV_32I_18dk**S|;7iPYOT>sqIAo
zXd!90E)5{^$M>L=Q%Y0eG_Njd@YnO$QCPTEr+{2rn6CH21N9g+$xO}2qpxN+zs)WN
z@Eng#NdT9Yk43QG1rO>MF`McTw)aejbKLJ(&n%NWjyQMNQ0sudZ*
z$wTh_g+s%4(luB7$~tz;*mddpna0xo_{|-`26M>BZhB2cZ>g@~)5;vFm3iy}qDs@7
zr5I}&iOEowCs0Qg$;E4PKQ7cC?MPcDvC9|AGee$+0EX&m`u`-$q~s5%*|6lY~9x=`!{OjQaWp
z{?di~G6IKc&%+AT!Z<(B`Jj!oxlo!aC}Un
zKg};8GVt%~I-edYR~!iQXOU%@##IOHt*urfEApxX7!I?JWi~VBdV^S3@;d0L>07EXaGuXHU{X7!fFM+={Uc#5<$
zL6o^*OU&QQQ`sXyXTV4lSh@8VBD7Jy$ga#kjv6K>g<8PDOmP2NU78pCcB8(J{|xDVb|}eDB;Ros)zXmeOeO^pU&L96xdw_%#6i@_S>Zc2{nCXP4;LNwD-h
zvWKiOYOZ86;p!;{RK;7Da+A)Y5kyb9
zYqVdCblic*AiTCCi~e1bngNNt-n>fM6c_o(pbp61Q_4IIDXQ;|RI``c75BC&I&*vt?F&AR9