RED-3813: image similarity refactoring #690
@ -2,6 +2,7 @@ package com.iqser.red.persistence.service.v1.external.api.impl.controller;
|
||||
|
||||
import static com.iqser.red.service.persistence.management.v1.processor.roles.ActionRoles.GET_SIMILAR_IMAGES;
|
||||
|
||||
import java.time.OffsetDateTime;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
@ -11,6 +12,7 @@ import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import com.iqser.red.commons.spring.ErrorMessage;
|
||||
import com.iqser.red.service.persistence.management.v1.processor.service.ImageSimilarityService;
|
||||
import com.iqser.red.service.persistence.service.v1.api.external.resource.ImageSimilaritySearchResource;
|
||||
import com.iqser.red.service.persistence.service.v1.api.shared.model.image.ImageSimilaritySearchRequest;
|
||||
@ -31,16 +33,13 @@ public class ImageSimilaritySearchController implements ImageSimilaritySearchRes
|
||||
|
||||
@SneakyThrows
|
||||
@PreAuthorize("hasAuthority('" + GET_SIMILAR_IMAGES + "')")
|
||||
public ResponseEntity<ImageSimilaritySearchResponse> getSimilarImages(@RequestBody ImageSimilaritySearchRequest imageSimilaritySearchRequest) {
|
||||
public ResponseEntity getSimilarImages(@RequestBody ImageSimilaritySearchRequest imageSimilaritySearchRequest) {
|
||||
|
||||
log.info("received similiar image search request {}", imageSimilaritySearchRequest);
|
||||
if (imageSimilaritySearchRequest.getAnnotationId() == null || imageSimilaritySearchRequest.getScope() == null || (imageSimilaritySearchRequest.getScope().getFileId()
|
||||
== null
|
||||
&& imageSimilaritySearchRequest.getScope().getTemplateId()
|
||||
== null
|
||||
&& imageSimilaritySearchRequest.getScope().getDossierId()
|
||||
== null)) {
|
||||
return new ResponseEntity<>(null, HttpStatus.BAD_REQUEST);
|
||||
if (imageSimilaritySearchRequest.getAnnotationId().isEmpty() || (imageSimilaritySearchRequest.getScope().getFileId().isEmpty() && imageSimilaritySearchRequest.getScope()
|
||||
.getTemplateId()
|
||||
.isEmpty() && imageSimilaritySearchRequest.getScope().getDossierId().isEmpty())) {
|
||||
return new ResponseEntity<>(new ErrorMessage(OffsetDateTime.now(), "Required parameter missing"), HttpStatus.BAD_REQUEST);
|
||||
}
|
||||
List<ImageDocument> similarImages = this.imageSimilarityService.findSimilarImages(imageSimilaritySearchRequest.getAnnotationId(),
|
||||
imageSimilaritySearchRequest.getDistance(),
|
||||
|
||||
@ -18,6 +18,7 @@ import com.knecon.fforesight.service.layoutparser.internal.api.data.redaction.Do
|
||||
import com.knecon.fforesight.service.layoutparser.internal.api.data.redaction.NodeType;
|
||||
import com.knecon.fforesight.tenantcommons.TenantContext;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
@Service
|
||||
@ -60,15 +61,53 @@ public class ImageSimilarityService {
|
||||
|
||||
public List<ImageDocument> findSimilarImages(String annotationId, double distance, Scope scope) throws Exception {
|
||||
|
||||
ImageDocument centroImage = this.imageMongoService.findById(annotationId);
|
||||
log.info("image received with id {}: {}", annotationId, centroImage);
|
||||
List<ImageDocument> similarImages = this.imageMongoService.findSimilarImages(centroImage, distance, scope);
|
||||
ImageDocument centralImage = this.imageMongoService.findById(annotationId);
|
||||
List<ImageDocument> similarImages = this.findSimilarImages(centralImage, distance, scope);
|
||||
log.info("received similar images: {}", similarImages);
|
||||
return similarImages.stream()
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
|
||||
public List<ImageDocument> findSimilarImages(ImageDocument centralImage, double maxDistance, Scope scope) {
|
||||
|
||||
double[] centralFeatureVector = centralImage.getFeatureVector();
|
||||
|
||||
List<ImageDocument> allImages = this.imageMongoService.findAll();
|
||||
|
||||
return allImages.stream()
|
||||
.filter(image -> !image.getImageId().equals(centralImage.getImageId()))
|
||||
.filter(image -> filterForScope(image, scope))
|
||||
.map(image -> new ImageSimilarityService.ImageDistancePair(image, calculateManhattanDistance(image.getFeatureVector(), centralFeatureVector)))
|
||||
.filter(image -> image.getDistance() <= maxDistance)
|
||||
.map(ImageSimilarityService.ImageDistancePair::getImageDocument)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
|
||||
private boolean filterForScope(ImageDocument image, Scope scope) {
|
||||
|
||||
if (!scope.getFileId().isEmpty()) {
|
||||
return image.getFileId() != null && image.getFileId().equals(scope.getFileId());
|
||||
} else if (!scope.getDossierId().isEmpty()) {
|
||||
return image.getDossierId() != null && image.getDossierId().equals(scope.getDossierId());
|
||||
} else if (!scope.getTemplateId().isEmpty()) {
|
||||
return image.getTemplateId() != null && image.getTemplateId().equals(scope.getTemplateId());
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
private double calculateManhattanDistance(double[] vector1, double[] vector2) {
|
||||
|
||||
double distance = 0.0;
|
||||
for (int i = 0; i < vector1.length; i++) {
|
||||
distance += Math.abs(vector1[i] - vector2[i]);
|
||||
}
|
||||
return distance;
|
||||
}
|
||||
|
||||
|
||||
public static double[] parseRepresentationVector(String representationHash) {
|
||||
|
||||
double[] doubleArray = new double[representationHash.length()];
|
||||
@ -76,11 +115,8 @@ public class ImageSimilarityService {
|
||||
for (int i = 0; i < representationHash.length(); i++) {
|
||||
char c = representationHash.charAt(i);
|
||||
if (Character.isDigit(c)) {
|
||||
// Convert numeric characters directly to their numeric values
|
||||
doubleArray[i] = Character.getNumericValue(c);
|
||||
} else if (Character.isLetter(c)) {
|
||||
// Convert alphabetic characters to their position in the alphabet
|
||||
// 'A' or 'a' -> 10, 'B' or 'b' -> 11, ..., 'F' or 'f' -> 15
|
||||
doubleArray[i] = 10 + Character.toUpperCase(c) - 'A';
|
||||
} else {
|
||||
throw new IllegalArgumentException("Invalid character in input string: " + c);
|
||||
@ -90,4 +126,21 @@ public class ImageSimilarityService {
|
||||
return doubleArray;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Getter
|
||||
private static class ImageDistancePair {
|
||||
|
||||
private final ImageDocument imageDocument;
|
||||
private final double distance;
|
||||
|
||||
|
||||
ImageDistancePair(ImageDocument imageDocument, double distance) {
|
||||
|
||||
this.imageDocument = imageDocument;
|
||||
this.distance = distance;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@ -2,16 +2,13 @@ package com.iqser.red.service.persistence.service.v1.api.shared.mongo.service;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.springframework.data.mongodb.core.MongoOperations;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import com.iqser.red.service.persistence.service.v1.api.shared.model.utils.Scope;
|
||||
import com.iqser.red.service.persistence.service.v1.api.shared.mongo.document.ImageDocument;
|
||||
import com.iqser.red.service.persistence.service.v1.api.shared.mongo.repository.ImageDocumentRepository;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
@Service
|
||||
@ -40,42 +37,9 @@ public class ImageMongoService {
|
||||
}
|
||||
|
||||
|
||||
public List<ImageDocument> findSimilarImages(ImageDocument centralImage, double maxDistance, Scope scope) {
|
||||
public List<ImageDocument> findAll() {
|
||||
|
||||
double[] centralFeatureVector = centralImage.getFeatureVector();
|
||||
|
||||
List<ImageDocument> allImages = this.imageDocumentRepository.findAll();
|
||||
|
||||
return allImages.stream()
|
||||
.filter(image -> !image.getImageId().equals(centralImage.getImageId()))
|
||||
.filter(image -> filterForScope(image, scope))
|
||||
.map(image -> new ImageDistancePair(image, calculateManhattanDistance(image.getFeatureVector(), centralFeatureVector)))
|
||||
.filter(image -> image.getDistance() <= maxDistance)
|
||||
.map(ImageDistancePair::getImageDocument)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
|
||||
private boolean filterForScope(ImageDocument image, Scope scope) {
|
||||
|
||||
if (scope.getTemplateId() != null && scope.getTemplateId().length() > 0) {
|
||||
return image.getTemplateId() != null && image.getTemplateId().equals(scope.getTemplateId());
|
||||
} else if (scope.getDossierId() != null && scope.getDossierId().length() > 0) {
|
||||
return image.getDossierId() != null && image.getDossierId().equals(scope.getDossierId());
|
||||
} else if (scope.getFileId() != null && scope.getFileId().length() > 0) {
|
||||
return image.getFileId() != null && image.getFileId().equals(scope.getFileId());
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
private double calculateManhattanDistance(double[] vector1, double[] vector2) {
|
||||
|
||||
double distance = 0.0;
|
||||
for (int i = 0; i < vector1.length; i++) {
|
||||
distance += Math.abs(vector1[i] - vector2[i]);
|
||||
}
|
||||
return distance;
|
||||
return this.imageDocumentRepository.findAll();
|
||||
}
|
||||
|
||||
|
||||
@ -84,20 +48,4 @@ public class ImageMongoService {
|
||||
imageDocumentRepository.saveAll(imageDocuments);
|
||||
}
|
||||
|
||||
|
||||
@Getter
|
||||
private static class ImageDistancePair {
|
||||
|
||||
private final ImageDocument imageDocument;
|
||||
private final double distance;
|
||||
|
||||
|
||||
ImageDistancePair(ImageDocument imageDocument, double distance) {
|
||||
|
||||
this.imageDocument = imageDocument;
|
||||
this.distance = distance;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user