Spaces:
Sleeping
Sleeping
| from unittest.mock import Mock | |
| import pytest | |
| import torch | |
| from PIL import Image | |
| from faceverification.core import image_processor | |
| from faceverification.core.image_processor import FaceNotDetectedError, ImageProcessor | |
| class FakeMTCNN: | |
| instances = [] | |
| def __init__(self, **kwargs): | |
| self.kwargs = kwargs | |
| FakeMTCNN.instances.append(self) | |
| class FakeFacenet: | |
| instances = [] | |
| def __init__(self, pretrained): | |
| self.pretrained = pretrained | |
| self.eval_called = False | |
| self.device = None | |
| FakeFacenet.instances.append(self) | |
| def eval(self): | |
| self.eval_called = True | |
| return self | |
| def to(self, device): | |
| self.device = device | |
| return self | |
| def test_init_uses_cpu_when_auto_and_cuda_is_unavailable(monkeypatch): | |
| FakeMTCNN.instances = [] | |
| FakeFacenet.instances = [] | |
| monkeypatch.setattr(image_processor.torch.cuda, "is_available", lambda: False) | |
| monkeypatch.setattr(image_processor, "MTCNN", FakeMTCNN) | |
| monkeypatch.setattr(image_processor, "InceptionResnetV1", FakeFacenet) | |
| processor = ImageProcessor( | |
| device="auto", | |
| mtcnn_thresholds=(0.1, 0.2, 0.3), | |
| facenet_pretrained="test-weights", | |
| ) | |
| assert processor.device == "cpu" | |
| assert FakeMTCNN.instances[0].kwargs == { | |
| "select_largest": False, | |
| "device": "cpu", | |
| "thresholds": [0.1, 0.2, 0.3], | |
| } | |
| assert FakeFacenet.instances[0].pretrained == "test-weights" | |
| assert FakeFacenet.instances[0].eval_called is True | |
| assert FakeFacenet.instances[0].device == "cpu" | |
| def test_init_rejects_invalid_device(): | |
| with pytest.raises(ValueError, match="Device must be"): | |
| ImageProcessor(device="gpu") | |
| def test_get_embedding_returns_normalized_embedding(): | |
| processor = ImageProcessor.__new__(ImageProcessor) | |
| processor.device = "cpu" | |
| processor.mtcnn = Mock(return_value=torch.ones((3, 2, 2))) | |
| processor.facenet = Mock(return_value=torch.tensor([[3.0, 4.0]])) | |
| image = Image.new("RGB", (10, 10)) | |
| embedding = processor.get_embedding(image) | |
| torch.testing.assert_close(embedding, torch.tensor([0.6, 0.8])) | |
| processor.mtcnn.assert_called_once_with(image) | |
| assert processor.facenet.call_args.args[0].shape == torch.Size([1, 3, 2, 2]) | |
| def test_get_embedding_keeps_batched_face_tensor_shape(): | |
| processor = ImageProcessor.__new__(ImageProcessor) | |
| processor.device = "cpu" | |
| processor.mtcnn = Mock(return_value=torch.ones((1, 3, 2, 2))) | |
| processor.facenet = Mock(return_value=torch.tensor([[3.0, 4.0]])) | |
| image = Image.new("RGB", (10, 10)) | |
| embedding = processor.get_embedding(image) | |
| torch.testing.assert_close(embedding, torch.tensor([0.6, 0.8])) | |
| assert processor.facenet.call_args.args[0].shape == torch.Size([1, 3, 2, 2]) | |
| def test_get_embedding_raises_when_no_face_is_detected(): | |
| processor = ImageProcessor.__new__(ImageProcessor) | |
| processor.mtcnn = Mock(return_value=None) | |
| with pytest.raises(FaceNotDetectedError, match="No face detected"): | |
| processor.get_embedding(Image.new("RGB", (10, 10))) | |
| def test_detect_faces_draws_boxes_and_returns_true(): | |
| processor = ImageProcessor.__new__(ImageProcessor) | |
| processor.mtcnn = Mock() | |
| processor.mtcnn.detect.return_value = ( | |
| [[1.0, 1.0, 8.0, 8.0]], | |
| [0.98765], | |
| ) | |
| image = Image.new("RGB", (10, 10), "white") | |
| annotated_image, presence = processor.detect_faces(image) | |
| assert annotated_image is image | |
| assert presence is True | |
| assert image.getpixel((1, 1)) == (255, 0, 0) | |
| def test_detect_faces_returns_false_when_no_boxes_are_detected(): | |
| processor = ImageProcessor.__new__(ImageProcessor) | |
| processor.mtcnn = Mock() | |
| processor.mtcnn.detect.return_value = (None, None) | |
| image = Image.new("RGB", (10, 10), "white") | |
| annotated_image, presence = processor.detect_faces(image) | |
| assert annotated_image is image | |
| assert presence is False | |