diff --git a/src/hope_dedup_engine/apps/api/admin/image.py b/src/hope_dedup_engine/apps/api/admin/image.py index 8b721863..71967a2f 100644 --- a/src/hope_dedup_engine/apps/api/admin/image.py +++ b/src/hope_dedup_engine/apps/api/admin/image.py @@ -27,6 +27,3 @@ def has_add_permission(self, request): def has_change_permission(self, request, obj=None): return False - - def has_delete_permission(self, request, obj=None): - return obj is not None diff --git a/src/hope_dedup_engine/apps/api/deduplication/process.py b/src/hope_dedup_engine/apps/api/deduplication/process.py index c6a69737..33bfb67c 100644 --- a/src/hope_dedup_engine/apps/api/deduplication/process.py +++ b/src/hope_dedup_engine/apps/api/deduplication/process.py @@ -94,10 +94,11 @@ def find_duplicates(deduplication_set_id: str, serialized_lock: str) -> None: deduplication_set.state = deduplication_set.State.CLEAN deduplication_set.save() - if lock_enabled: - lock.release() - except Exception: deduplication_set.state = DeduplicationSet.State.ERROR deduplication_set.save() raise + + finally: + if lock_enabled: + lock.release() diff --git a/src/hope_dedup_engine/apps/core/exceptions.py b/src/hope_dedup_engine/apps/core/exceptions.py index 365ffc75..a6bafdfb 100644 --- a/src/hope_dedup_engine/apps/core/exceptions.py +++ b/src/hope_dedup_engine/apps/core/exceptions.py @@ -16,3 +16,12 @@ class DownloaderKeyError(Exception): def __init__(self, key: str) -> None: self.key = key super().__init__(f"Downloader key '{key}' does not exist.") + + +class NotCompliantImageError(Exception): + """ + Exception raised when an image is not compliant with the expected parameters. + """ + + def __init__(self, message: str) -> None: + super().__init__(message) diff --git a/src/hope_dedup_engine/apps/faces/services/image_processor.py b/src/hope_dedup_engine/apps/faces/services/image_processor.py index a2bf9870..f2148fc1 100644 --- a/src/hope_dedup_engine/apps/faces/services/image_processor.py +++ b/src/hope_dedup_engine/apps/faces/services/image_processor.py @@ -11,6 +11,7 @@ import numpy as np from constance import config +from hope_dedup_engine.apps.core.exceptions import NotCompliantImageError from hope_dedup_engine.apps.faces.managers import DNNInferenceManager, StorageManager @@ -97,19 +98,19 @@ def _get_face_detections_dnn( # Decode image from binary buffer to 3D numpy array (height, width, channels of BlueGreeRed color space) image = cv2.imdecode(img_array, cv2.IMREAD_COLOR) (h, w) = image.shape[:2] + _h, _w = ( + self.blob_from_image_cfg.shape["height"], + self.blob_from_image_cfg.shape["width"], + ) + if h < _h or w < _w: + raise NotCompliantImageError( + f"Image {filename} too small: '{h}x{w}'. It needs to be at least '{_h}x{_w}'." + ) + # Create a blob (4D tensor) from the image blob = cv2.dnn.blobFromImage( - image=cv2.resize( - image, - dsize=( - self.blob_from_image_cfg.shape["height"], - self.blob_from_image_cfg.shape["width"], - ), - ), - size=( - self.blob_from_image_cfg.shape["height"], - self.blob_from_image_cfg.shape["width"], - ), + image=cv2.resize(image, dsize=(_h, _w)), + size=(_h, _w), scalefactor=self.blob_from_image_cfg.scale_factor, mean=self.blob_from_image_cfg.mean_values, ) @@ -161,7 +162,9 @@ def encode_face(self, filename: str, encodings_filename: str) -> None: encodings: list[np.ndarray[np.float32, Any]] = [] face_regions = self._get_face_detections_dnn(filename) if not face_regions: - self.logger.warning("No face regions detected in image %s", filename) + raise NotCompliantImageError( + f"No face regions detected in image '{filename}'." + ) else: for region in face_regions: diff --git a/src/hope_dedup_engine/config/fragments/constance.py b/src/hope_dedup_engine/config/fragments/constance.py index 32fe0b38..0abde749 100644 --- a/src/hope_dedup_engine/config/fragments/constance.py +++ b/src/hope_dedup_engine/config/fragments/constance.py @@ -67,7 +67,7 @@ int, ), "FACE_ENCODINGS_MODEL": ( - "small", + "large", """ Specifies the model type used for encoding face landmarks. It can be either 'small' which is faster and detects only 5 key facial landmarks, or 'large' which is more precise and identifies 68 key facial landmarks diff --git a/tests/extras/demoapp/demo_images/1262304094482.jpg b/tests/extras/demoapp/demo_images/1262304094482.jpg new file mode 100644 index 00000000..db7670f1 Binary files /dev/null and b/tests/extras/demoapp/demo_images/1262304094482.jpg differ diff --git a/tests/extras/demoapp/demo_images/1262304151314.jpg b/tests/extras/demoapp/demo_images/1262304151314.jpg new file mode 100644 index 00000000..df9e262a Binary files /dev/null and b/tests/extras/demoapp/demo_images/1262304151314.jpg differ diff --git a/tests/extras/demoapp/demo_images/1262304158968.jpg b/tests/extras/demoapp/demo_images/1262304158968.jpg new file mode 100644 index 00000000..b7ec4e32 Binary files /dev/null and b/tests/extras/demoapp/demo_images/1262304158968.jpg differ diff --git a/tests/extras/demoapp/demo_images/2781_V.png b/tests/extras/demoapp/demo_images/2781_V.png new file mode 100644 index 00000000..a52e6adf Binary files /dev/null and b/tests/extras/demoapp/demo_images/2781_V.png differ diff --git a/tests/extras/demoapp/demo_images/370_C.jpg b/tests/extras/demoapp/demo_images/370_C.jpg new file mode 100644 index 00000000..0ff5e8d1 Binary files /dev/null and b/tests/extras/demoapp/demo_images/370_C.jpg differ diff --git a/tests/extras/demoapp/demo_images/Aaron_Eckhart_0001.jpg b/tests/extras/demoapp/demo_images/Aaron_Eckhart_0001.jpg index 4d2fb8db..3da86181 100644 Binary files a/tests/extras/demoapp/demo_images/Aaron_Eckhart_0001.jpg and b/tests/extras/demoapp/demo_images/Aaron_Eckhart_0001.jpg differ diff --git a/tests/extras/demoapp/demo_images/Aaron_Guiel_0001.jpg b/tests/extras/demoapp/demo_images/Aaron_Guiel_0001.jpg index c2fb5d0f..bac4a9f8 100644 Binary files a/tests/extras/demoapp/demo_images/Aaron_Guiel_0001.jpg and b/tests/extras/demoapp/demo_images/Aaron_Guiel_0001.jpg differ diff --git a/tests/extras/demoapp/demo_images/Aaron_Peirsol_0001.jpg b/tests/extras/demoapp/demo_images/Aaron_Peirsol_0001.jpg index b1cc3287..df2b0923 100644 Binary files a/tests/extras/demoapp/demo_images/Aaron_Peirsol_0001.jpg and b/tests/extras/demoapp/demo_images/Aaron_Peirsol_0001.jpg differ diff --git a/tests/extras/demoapp/demo_images/Aaron_Peirsol_0002.jpg b/tests/extras/demoapp/demo_images/Aaron_Peirsol_0002.jpg index cf561ab4..d934ad0e 100644 Binary files a/tests/extras/demoapp/demo_images/Aaron_Peirsol_0002.jpg and b/tests/extras/demoapp/demo_images/Aaron_Peirsol_0002.jpg differ diff --git a/tests/extras/demoapp/demo_images/Cathy_Freeman_0001.jpg b/tests/extras/demoapp/demo_images/Cathy_Freeman_0001.jpg index f2d6b5d8..c3921aea 100644 Binary files a/tests/extras/demoapp/demo_images/Cathy_Freeman_0001.jpg and b/tests/extras/demoapp/demo_images/Cathy_Freeman_0001.jpg differ diff --git a/tests/extras/demoapp/demo_images/Cathy_Freeman_0002.jpg b/tests/extras/demoapp/demo_images/Cathy_Freeman_0002.jpg index e4f8f62c..233d3f1c 100644 Binary files a/tests/extras/demoapp/demo_images/Cathy_Freeman_0002.jpg and b/tests/extras/demoapp/demo_images/Cathy_Freeman_0002.jpg differ diff --git a/tests/extras/demoapp/demo_images/I3-2024-10-22_082459.6129460000.jpg b/tests/extras/demoapp/demo_images/I3-2024-10-22_082459.6129460000.jpg new file mode 100644 index 00000000..43bcfd76 Binary files /dev/null and b/tests/extras/demoapp/demo_images/I3-2024-10-22_082459.6129460000.jpg differ diff --git a/tests/extras/demoapp/demo_images/I3-2024-10-22_082459.6129460000_.jpg b/tests/extras/demoapp/demo_images/I3-2024-10-22_082459.6129460000_.jpg new file mode 100644 index 00000000..43bcfd76 Binary files /dev/null and b/tests/extras/demoapp/demo_images/I3-2024-10-22_082459.6129460000_.jpg differ diff --git a/tests/extras/demoapp/demo_images/Ziwang_Xu_0001.jpg b/tests/extras/demoapp/demo_images/Ziwang_Xu_0001.jpg index 2665ebc0..b8495b82 100644 Binary files a/tests/extras/demoapp/demo_images/Ziwang_Xu_0001.jpg and b/tests/extras/demoapp/demo_images/Ziwang_Xu_0001.jpg differ diff --git a/tests/extras/demoapp/demo_images/Zoe_Ball_0001.jpg b/tests/extras/demoapp/demo_images/Zoe_Ball_0001.jpg index f26223d2..c41d655f 100644 Binary files a/tests/extras/demoapp/demo_images/Zoe_Ball_0001.jpg and b/tests/extras/demoapp/demo_images/Zoe_Ball_0001.jpg differ diff --git a/tests/extras/demoapp/demo_images/too_small.jpg b/tests/extras/demoapp/demo_images/too_small.jpg new file mode 100644 index 00000000..60fca585 Binary files /dev/null and b/tests/extras/demoapp/demo_images/too_small.jpg differ diff --git a/tests/extras/demoapp/demo_images/without_face.jpg b/tests/extras/demoapp/demo_images/without_face.jpg index e3b70996..d870b9ba 100644 Binary files a/tests/extras/demoapp/demo_images/without_face.jpg and b/tests/extras/demoapp/demo_images/without_face.jpg differ diff --git a/tests/faces/conftest.py b/tests/faces/conftest.py index a2bb53e2..28014c71 100644 --- a/tests/faces/conftest.py +++ b/tests/faces/conftest.py @@ -129,7 +129,7 @@ def mock_image_processor( @pytest.fixture def image_bytes_io(): img_byte_arr = BytesIO() - image = Image.new("RGB", (100, 100), color="red") + image = Image.new("RGB", (300, 300), color="red") image.save(img_byte_arr, format="JPEG") img_byte_arr.seek(0) img_byte_arr.fake_open = lambda *_: BytesIO(img_byte_arr.getvalue()) diff --git a/tests/faces/faces_const.py b/tests/faces/faces_const.py index 89ac631e..0efda667 100644 --- a/tests/faces/faces_const.py +++ b/tests/faces/faces_const.py @@ -44,8 +44,8 @@ } FACE_REGIONS_INVALID: Final[list[list[tuple[int, int, int, int]]]] = [[], [(0, 0, 10)]] FACE_REGIONS_VALID: Final[list[tuple[int, int, int, int]]] = [ - (10, 10, 20, 20), - (30, 30, 40, 40), + (40, 40, 80, 80), + (120, 120, 160, 160), ] BLOB_FROM_IMAGE_SCALE_FACTOR: Final[float] = 1.0 BLOB_FROM_IMAGE_MEAN_VALUES: Final[tuple[float, float, float]] = (104.0, 177.0, 123.0) @@ -56,8 +56,8 @@ (0, 0, 0.15, 0.1, 0.1, 0.2, 0.2), # with confidence 0.15 -> invalid detection ] IMAGE_SIZE: Final[tuple[int, int, int]] = ( - 100, - 100, + 400, + 400, 3, ) # Size of the image after decoding (h, w, number of channels) RESIZED_IMAGE_SIZE: Final[tuple[int, int, int]] = (