Skip to content

Commit 6587d34

Browse files
Merge pull request #948 from exadel-inc/EFRS-1333_ability_to_send_embeddings_instead_of_the_image_for_recognition
EFRS-1333: Added the ability to send embeddings instead of the image for recognition
2 parents fd96df0 + 1d9d59e commit 6587d34

38 files changed

+879
-46
lines changed

custom-builds/Single-Docker-File/Dockerfile

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,11 @@ ENV PYTHON_URL=http://localhost:3000
6060
ENV MAX_FILE_SIZE=5MB
6161
ENV MAX_REQUEST_SIZE=10MB
6262

63-
RUN apt-get update && apt-get install -y openjdk-11-jre-headless \
63+
RUN apt-get update \
64+
&& apt-get install -y wget apt-transport-https gnupg \
65+
&& wget -O - https://packages.adoptium.net/artifactory/api/gpg/key/public | apt-key add - \
66+
&& echo "deb https://packages.adoptium.net/artifactory/deb $(awk -F= '/^VERSION_CODENAME/{print$2}' /etc/os-release) main" | tee /etc/apt/sources.list.d/adoptium.list \
67+
&& apt-get install -y temurin-17-jdk \
6468
&& rm -rf /var/lib/apt/lists/*
6569

6670
COPY --from=admin /home/app.jar /app/admin/app.jar

dev/Dockerfile

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
FROM maven:3.6.3-jdk-11-slim as build
1+
FROM maven:3.8.2-eclipse-temurin-17 as build
22
ARG ND4J_CLASSIFIER
33
WORKDIR /workspace/compreface
44
LABEL intermidiate_frs=true
@@ -12,14 +12,14 @@ COPY admin admin
1212
COPY common common
1313
RUN mvn package -Dmaven.test.skip=true -Dmaven.site.skip=true -Dmaven.javadoc.skip=true -Dnd4j.classifier=$ND4J_CLASSIFIER
1414

15-
FROM openjdk:11.0.8-jre-slim as frs_core
15+
FROM eclipse-temurin:17-jre as frs_core
1616
ARG DIR=/workspace/compreface
1717
COPY --from=build ${DIR}/api/target/*.jar /home/app.jar
1818
ENTRYPOINT ["sh","-c","java $API_JAVA_OPTS -agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=*:5005 -jar /home/app.jar"]
1919

20-
FROM openjdk:11.0.8-jre-slim as frs_crud
20+
FROM eclipse-temurin:17-jre as frs_crud
2121
ARG DIR=/workspace/compreface
2222
COPY --from=build ${DIR}/admin/target/*.jar /home/app.jar
2323
ARG APPERY_API_KEY
2424
ENV APPERY_API_KEY ${APPERY_API_KEY}
25-
ENTRYPOINT ["sh","-c","java $ADMIN_JAVA_OPTS -agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=*:5005 -jar /home/app.jar"]
25+
ENTRYPOINT ["sh","-c","java $ADMIN_JAVA_OPTS -agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=*:5005 -jar /home/app.jar"]

docs/Rest-API-description.md

Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
+ [Face Detection Service](#face-detection-service)
2424
+ [Face Verification Service](#face-verification-service)
2525
+ [Base64 Support](#base64-support)
26+
+ [Recognition and verification using embedding](#recognition-and-verification-using-embedding)
2627

2728
To know more about face services and face plugins visit [this page](Face-services-and-plugins.md).
2829

@@ -831,3 +832,128 @@ curl -X POST "http://localhost:8000/api/v1/verification/verify?limit=<limit>&pre
831832
-d {"source_image": "<source_image_base64_value>", "target_image": "<target_image_base64_value>"}
832833
```
833834

835+
836+
837+
## Recognition and verification using embedding
838+
`since 1.2.0 version`
839+
840+
You can use computed embedding to perform recognition and verification. To obtain embedding, you can
841+
use [calculator plugin](https://github.com/exadel-inc/CompreFace/blob/EFRS-1333_ability_to_send_embeddings_instead_of_the_image_for_recognition/docs/Face-services-and-plugins.md#face-plugins)
842+
in each Face service.
843+
The base rule is to use `Content-Type: application/json` header and send JSON in the body.
844+
845+
### Recognize Faces from a Given Image, Embedding
846+
The service is used to determine similarities between input embeddings and embeddings within the Face Collection. An example:
847+
848+
```shell
849+
curl -X POST "http://localhost:8000/api/v1/recognition/embeddings/recognize?prediction_count=<prediction_count>" \
850+
-H "Content-Type: application/json" \
851+
-H "x-api-key: <service_api_key>" \
852+
-d {"embeddings": [[<array_of_embedding>], ...]}
853+
```
854+
855+
| Element | Description | Type | Required | Notes |
856+
|------------------|-------------|---------|----------|-----------------------------------------------------------------------------------------------------------------|
857+
| Content-Type | header | string | required | application/json |
858+
| x-api-key | header | string | required | an api key of the Face recognition service, created by the user |
859+
| embeddings | body | array | required | an input embeddings. The length depends on the model (e.g. 512 or 128) |
860+
| prediction_count | param | integer | optional | the maximum number of subject predictions per embedding. It returns the most similar subjects. Default value: 1 |
861+
862+
Response body on success:
863+
```json
864+
{
865+
"result": [
866+
{
867+
"embedding": [0.0627421774604647, "...", -0.0236684433507126],
868+
"similarities": [
869+
{
870+
"subject": "John",
871+
"similarity": 0.55988
872+
},
873+
"..."
874+
]
875+
},
876+
"..."
877+
]
878+
}
879+
```
880+
881+
| Element | Type | Description |
882+
|--------------|--------|--------------------------------------------------------------------------------------------|
883+
| result | array | an array that contains all the results |
884+
| embedding | array | an input embedding |
885+
| similarities | array | an array that contains results of similarity between the embedding and the input embedding |
886+
| subject | string | a subject in which the similar embedding was found |
887+
| similarity | float | a similarity between the embedding and the input embedding |
888+
889+
### Verify Faces from a Given Image, Embedding
890+
The endpoint is used to compare input embeddings to the embedding stored in Face Collection. An example:
891+
892+
```shell
893+
curl -X POST "http://localhost:8000/api/v1/recognition/embeddings/faces/{image_id}/verify" \
894+
-H "Content-Type: application/json" \
895+
-H "x-api-key: <service_api_key>" \
896+
-d {"embeddings": [[<array_of_embeddings>], ...]}
897+
```
898+
899+
| Element | Description | Type | Required | Notes |
900+
|--------------|-------------|--------|----------|------------------------------------------------------------------------|
901+
| Content-Type | header | string | required | application/json |
902+
| x-api-key | header | string | required | api key of the Face recognition service, created by the user |
903+
| embeddings | body | array | required | an input embeddings. The length depends on the model (e.g. 512 or 128) |
904+
| image_id | variable | UUID | required | an id of the source embedding within the Face Collection |
905+
906+
Response body on success:
907+
```json
908+
{
909+
"result": [
910+
{
911+
"embedding": [0.0627421774604647, "...", -0.0236684433507126],
912+
"similarity": 0.55988
913+
},
914+
"..."
915+
]
916+
}
917+
```
918+
919+
| Element | Type | Description |
920+
|-------------|--------|------------------------------------------------------------------------------|
921+
| result | array | an array that contains all the results |
922+
| embedding | array | a source embedding which we are comparing to embedding from Face Collection |
923+
| similarity | float | a similarity between the source embedding and embedding from Face Collection |
924+
925+
### Face Verification Service, Embedding
926+
The service is used to determine similarities between an input source embedding and input target embeddings. An example:
927+
928+
```shell
929+
curl -X POST "http://localhost:8000/api/v1/verification/embeddings/verify" \
930+
-H "Content-Type: application/json" \
931+
-H "x-api-key: <service_api_key>" \
932+
-d {"source": [<source_embedding>], "targets": [[<target_embedding>], ...]}
933+
```
934+
935+
| Element | Description | Type | Required | Notes |
936+
|------------------|-------------|---------|----------|--------------------------------------------------------------------------------------|
937+
| Content-Type | header | string | required | application/json |
938+
| x-api-key | header | string | required | api key of the Face verification service, created by the user |
939+
| source | body | array | required | an input embeddings. The length depends on the model (e.g. 512 or 128) |
940+
| targets | body | array | required | an array of the target embeddings. The length depends on the model (e.g. 512 or 128) |
941+
942+
Response body on success:
943+
```json
944+
{
945+
"result": [
946+
{
947+
"embedding": [0.0627421774604647, "...", -0.0236684433507126],
948+
"similarity": 0.55988
949+
},
950+
"..."
951+
]
952+
}
953+
```
954+
955+
| Element | Type | Description |
956+
|-------------|--------|--------------------------------------------------------------------|
957+
| result | array | an array that contains all the results |
958+
| embedding | array | a target embedding which we are comparing to source embedding |
959+
| similarity | float | a similarity between the source embedding and the target embedding |

java/admin/pom.xml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -167,6 +167,10 @@
167167
<groupId>org.apache.maven.plugins</groupId>
168168
<artifactId>maven-compiler-plugin</artifactId>
169169
</plugin>
170+
<plugin>
171+
<groupId>org.apache.maven.plugins</groupId>
172+
<artifactId>maven-surefire-plugin</artifactId>
173+
</plugin>
170174
<plugin>
171175
<groupId>org.springframework.boot</groupId>
172176
<artifactId>spring-boot-maven-plugin</artifactId>

java/admin/src/test/resources/application.yml

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -31,11 +31,6 @@ spring:
3131
max-request-size: ${MAX_REQUEST_SIZE:10MB}
3232
flyway:
3333
enabled: false
34-
datasource:
35-
driver-class-name: org.postgresql.Driver
36-
url: ${POSTGRES_URL:jdbc:postgresql://localhost:5432/frs_test}
37-
username: ${POSTGRES_USER:postgres}
38-
password: ${POSTGRES_PASSWORD:postgres}
3934
jpa:
4035
properties:
4136
hibernate:

java/api/pom.xml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -166,6 +166,10 @@
166166
<groupId>org.apache.maven.plugins</groupId>
167167
<artifactId>maven-compiler-plugin</artifactId>
168168
</plugin>
169+
<plugin>
170+
<groupId>org.apache.maven.plugins</groupId>
171+
<artifactId>maven-surefire-plugin</artifactId>
172+
</plugin>
169173
<plugin>
170174
<groupId>org.springframework.boot</groupId>
171175
<artifactId>spring-boot-maven-plugin</artifactId>

java/api/src/main/java/com/exadel/frs/core/trainservice/config/repository/NotificationDbConfig.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,9 @@
44
import org.springframework.boot.autoconfigure.jdbc.DataSourceProperties;
55
import org.springframework.context.annotation.Bean;
66
import org.springframework.context.annotation.Configuration;
7+
import org.springframework.context.annotation.Profile;
78

9+
@Profile("!test")
810
@Configuration
911
public class NotificationDbConfig {
1012

java/api/src/main/java/com/exadel/frs/core/trainservice/controller/EmbeddingController.java

Lines changed: 35 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,9 @@
2929
import com.exadel.frs.core.trainservice.aspect.WriteEndpoint;
3030
import com.exadel.frs.core.trainservice.dto.Base64File;
3131
import com.exadel.frs.core.trainservice.dto.EmbeddingDto;
32+
import com.exadel.frs.core.trainservice.dto.EmbeddingsRecognitionRequest;
33+
import com.exadel.frs.core.trainservice.dto.EmbeddingsVerificationProcessResponse;
34+
import com.exadel.frs.core.trainservice.dto.ProcessEmbeddingsParams;
3235
import com.exadel.frs.core.trainservice.dto.ProcessImageParams;
3336
import com.exadel.frs.core.trainservice.dto.VerificationResult;
3437
import com.exadel.frs.core.trainservice.mapper.EmbeddingMapper;
@@ -69,7 +72,7 @@
6972

7073
@Validated
7174
@RestController
72-
@RequestMapping(API_V1 + "/recognition/faces")
75+
@RequestMapping(API_V1 + "/recognition")
7376
@RequiredArgsConstructor
7477
public class EmbeddingController {
7578

@@ -81,7 +84,7 @@ public class EmbeddingController {
8184

8285
@WriteEndpoint
8386
@ResponseStatus(CREATED)
84-
@PostMapping
87+
@PostMapping("/faces")
8588
public EmbeddingDto addEmbedding(
8689
@ApiParam(value = IMAGE_WITH_ONE_FACE_DESC, required = true)
8790
@RequestParam
@@ -112,7 +115,7 @@ public EmbeddingDto addEmbedding(
112115

113116
@WriteEndpoint
114117
@ResponseStatus(CREATED)
115-
@PostMapping(consumes = MediaType.APPLICATION_JSON_VALUE)
118+
@PostMapping(value = "/faces", consumes = MediaType.APPLICATION_JSON_VALUE)
116119
public EmbeddingDto addEmbeddingBase64(
117120
@ApiParam(value = API_KEY_DESC, required = true)
118121
@RequestHeader(X_FRS_API_KEY_HEADER)
@@ -142,7 +145,7 @@ public EmbeddingDto addEmbeddingBase64(
142145
}
143146

144147
@ResponseBody
145-
@GetMapping(value = "/{embeddingId}/img", produces = MediaType.APPLICATION_OCTET_STREAM_VALUE)
148+
@GetMapping(value = "/faces/{embeddingId}/img", produces = MediaType.APPLICATION_OCTET_STREAM_VALUE)
146149
public byte[] downloadImg(HttpServletResponse response,
147150
@ApiParam(value = API_KEY_DESC, required = true)
148151
@RequestHeader(name = X_FRS_API_KEY_HEADER)
@@ -157,7 +160,7 @@ public byte[] downloadImg(HttpServletResponse response,
157160
.orElse(new byte[]{});
158161
}
159162

160-
@GetMapping
163+
@GetMapping("/faces")
161164
public Faces listEmbeddings(
162165
@ApiParam(value = API_KEY_DESC, required = true)
163166
@RequestHeader(name = X_FRS_API_KEY_HEADER)
@@ -172,7 +175,7 @@ public Faces listEmbeddings(
172175
}
173176

174177
@WriteEndpoint
175-
@DeleteMapping
178+
@DeleteMapping("/faces")
176179
public Map<String, Object> removeAllSubjectEmbeddings(
177180
@ApiParam(value = API_KEY_DESC, required = true)
178181
@RequestHeader(name = X_FRS_API_KEY_HEADER)
@@ -190,7 +193,7 @@ public Map<String, Object> removeAllSubjectEmbeddings(
190193
}
191194

192195
@WriteEndpoint
193-
@DeleteMapping("/{embeddingId}")
196+
@DeleteMapping("/faces/{embeddingId}")
194197
public EmbeddingDto deleteEmbeddingById(
195198
@ApiParam(value = API_KEY_DESC, required = true)
196199
@RequestHeader(name = X_FRS_API_KEY_HEADER)
@@ -204,7 +207,7 @@ public EmbeddingDto deleteEmbeddingById(
204207
}
205208

206209
@WriteEndpoint
207-
@PostMapping("/delete")
210+
@PostMapping("/faces/delete")
208211
public List<EmbeddingDto> deleteEmbeddingsById(
209212
@ApiParam(value = API_KEY_DESC, required = true)
210213
@RequestHeader(name = X_FRS_API_KEY_HEADER)
@@ -220,7 +223,7 @@ public List<EmbeddingDto> deleteEmbeddingsById(
220223
return dtoList;
221224
}
222225

223-
@PostMapping(value = "/{embeddingId}/verify", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
226+
@PostMapping(value = "/faces/{embeddingId}/verify", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
224227
public VerificationResult recognizeFile(
225228
@ApiParam(value = API_KEY_DESC, required = true)
226229
@RequestHeader(X_FRS_API_KEY_HEADER)
@@ -263,7 +266,7 @@ public VerificationResult recognizeFile(
263266
);
264267
}
265268

266-
@PostMapping(value = "/{embeddingId}/verify", consumes = MediaType.APPLICATION_JSON_VALUE)
269+
@PostMapping(value = "/faces/{embeddingId}/verify", consumes = MediaType.APPLICATION_JSON_VALUE)
267270
public VerificationResult recognizeBase64(
268271
@ApiParam(value = API_KEY_DESC, required = true)
269272
@RequestHeader(X_FRS_API_KEY_HEADER)
@@ -307,6 +310,28 @@ public VerificationResult recognizeBase64(
307310
);
308311
}
309312

313+
@PostMapping(value = "/embeddings/faces/{imageId}/verify", consumes = MediaType.APPLICATION_JSON_VALUE)
314+
public EmbeddingsVerificationProcessResponse recognizeEmbeddings(
315+
@ApiParam(value = API_KEY_DESC, required = true)
316+
@RequestHeader(X_FRS_API_KEY_HEADER)
317+
final String apiKey,
318+
@ApiParam(value = IMAGE_ID_DESC, required = true)
319+
@PathVariable
320+
final UUID imageId,
321+
@RequestBody
322+
@Valid
323+
final EmbeddingsRecognitionRequest recognitionRequest
324+
) {
325+
ProcessEmbeddingsParams processParams =
326+
ProcessEmbeddingsParams.builder()
327+
.apiKey(apiKey)
328+
.embeddings(recognitionRequest.getEmbeddings())
329+
.additionalParams(Map.of(IMAGE_ID, imageId))
330+
.build();
331+
332+
return subjectService.verifyEmbedding(processParams);
333+
}
334+
310335
@RequiredArgsConstructor
311336
private static final class Faces {
312337

java/api/src/main/java/com/exadel/frs/core/trainservice/controller/RecognizeController.java

Lines changed: 28 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,9 +37,12 @@
3737
import static com.exadel.frs.core.trainservice.system.global.Constants.STATUS_DESC;
3838
import static com.exadel.frs.core.trainservice.system.global.Constants.X_FRS_API_KEY_HEADER;
3939
import com.exadel.frs.core.trainservice.dto.Base64File;
40+
import com.exadel.frs.core.trainservice.dto.EmbeddingsRecognitionProcessResponse;
41+
import com.exadel.frs.core.trainservice.dto.EmbeddingsRecognitionRequest;
4042
import com.exadel.frs.core.trainservice.dto.FacesRecognitionResponseDto;
43+
import com.exadel.frs.core.trainservice.dto.ProcessEmbeddingsParams;
4144
import com.exadel.frs.core.trainservice.dto.ProcessImageParams;
42-
import com.exadel.frs.core.trainservice.service.FaceProcessService;
45+
import com.exadel.frs.core.trainservice.service.EmbeddingsProcessService;
4346
import io.swagger.annotations.ApiParam;
4447
import java.util.Collections;
4548
import javax.validation.Valid;
@@ -61,7 +64,7 @@
6164
@Validated
6265
public class RecognizeController {
6366

64-
private final FaceProcessService recognitionService;
67+
private final EmbeddingsProcessService recognitionService;
6568

6669
@PostMapping(value = "/recognition/recognize", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
6770
public FacesRecognitionResponseDto recognize(
@@ -142,4 +145,27 @@ public FacesRecognitionResponseDto recognizeBase64(
142145

143146
return (FacesRecognitionResponseDto) recognitionService.processImage(processImageParams);
144147
}
148+
149+
@PostMapping(value = "/recognition/embeddings/recognize", consumes = MediaType.APPLICATION_JSON_VALUE)
150+
public EmbeddingsRecognitionProcessResponse recognizeEmbeddings(
151+
@ApiParam(value = API_KEY_DESC, required = true)
152+
@RequestHeader(X_FRS_API_KEY_HEADER)
153+
final String apiKey,
154+
@ApiParam(value = PREDICTION_COUNT_DESC, example = NUMBER_VALUE_EXAMPLE)
155+
@RequestParam(value = PREDICTION_COUNT_REQUEST_PARAM, required = false, defaultValue = PREDICTION_COUNT_DEFAULT_VALUE)
156+
@Min(value = 1, message = PREDICTION_COUNT_MIN_DESC)
157+
final Integer predictionCount,
158+
@RequestBody
159+
@Valid
160+
final EmbeddingsRecognitionRequest recognitionRequest
161+
) {
162+
ProcessEmbeddingsParams processParams =
163+
ProcessEmbeddingsParams.builder()
164+
.apiKey(apiKey)
165+
.embeddings(recognitionRequest.getEmbeddings())
166+
.additionalParams(Collections.singletonMap(PREDICTION_COUNT, predictionCount))
167+
.build();
168+
169+
return (EmbeddingsRecognitionProcessResponse) recognitionService.processEmbeddings(processParams);
170+
}
145171
}

0 commit comments

Comments
 (0)