diff --git a/persistence-service-v1/persistence-service-external-api-impl-v1/src/main/java/com/iqser/red/persistence/service/v1/external/api/impl/controller/ImageSimilaritySearchController.java b/persistence-service-v1/persistence-service-external-api-impl-v1/src/main/java/com/iqser/red/persistence/service/v1/external/api/impl/controller/ImageSimilaritySearchController.java index 92b6b939e..f9cb5656c 100644 --- a/persistence-service-v1/persistence-service-external-api-impl-v1/src/main/java/com/iqser/red/persistence/service/v1/external/api/impl/controller/ImageSimilaritySearchController.java +++ b/persistence-service-v1/persistence-service-external-api-impl-v1/src/main/java/com/iqser/red/persistence/service/v1/external/api/impl/controller/ImageSimilaritySearchController.java @@ -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 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 similarImages = this.imageSimilarityService.findSimilarImages(imageSimilaritySearchRequest.getAnnotationId(), imageSimilaritySearchRequest.getDistance(), diff --git a/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/service/ImageSimilarityService.java b/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/service/ImageSimilarityService.java index c522cdccc..147581e01 100644 --- a/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/service/ImageSimilarityService.java +++ b/persistence-service-v1/persistence-service-processor-v1/src/main/java/com/iqser/red/service/persistence/management/v1/processor/service/ImageSimilarityService.java @@ -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 findSimilarImages(String annotationId, double distance, Scope scope) throws Exception { - ImageDocument centroImage = this.imageMongoService.findById(annotationId); - log.info("image received with id {}: {}", annotationId, centroImage); - List similarImages = this.imageMongoService.findSimilarImages(centroImage, distance, scope); + ImageDocument centralImage = this.imageMongoService.findById(annotationId); + List similarImages = this.findSimilarImages(centralImage, distance, scope); log.info("received similar images: {}", similarImages); return similarImages.stream() .collect(Collectors.toList()); } + public List findSimilarImages(ImageDocument centralImage, double maxDistance, Scope scope) { + + double[] centralFeatureVector = centralImage.getFeatureVector(); + + List 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; } -} \ No newline at end of file + + @Getter + private static class ImageDistancePair { + + private final ImageDocument imageDocument; + private final double distance; + + + ImageDistancePair(ImageDocument imageDocument, double distance) { + + this.imageDocument = imageDocument; + this.distance = distance; + } + + } + +} + diff --git a/persistence-service-v1/persistence-service-shared-mongo-v1/src/main/java/com/iqser/red/service/persistence/service/v1/api/shared/mongo/service/ImageMongoService.java b/persistence-service-v1/persistence-service-shared-mongo-v1/src/main/java/com/iqser/red/service/persistence/service/v1/api/shared/mongo/service/ImageMongoService.java index 1abbc9f98..3c4328f27 100644 --- a/persistence-service-v1/persistence-service-shared-mongo-v1/src/main/java/com/iqser/red/service/persistence/service/v1/api/shared/mongo/service/ImageMongoService.java +++ b/persistence-service-v1/persistence-service-shared-mongo-v1/src/main/java/com/iqser/red/service/persistence/service/v1/api/shared/mongo/service/ImageMongoService.java @@ -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 findSimilarImages(ImageDocument centralImage, double maxDistance, Scope scope) { + public List findAll() { - double[] centralFeatureVector = centralImage.getFeatureVector(); - - List 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; - } - - } - }