File size: 4,434 Bytes
280c0fd
 
 
 
 
 
be986b5
 
280c0fd
be986b5
280c0fd
be986b5
280c0fd
 
 
be986b5
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
280c0fd
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
be986b5
f59d050
 
 
be986b5
f59d050
 
 
 
be986b5
 
 
 
 
 
 
 
 
 
 
 
f59d050
be986b5
f59d050
 
 
 
 
 
 
 
 
be986b5
 
 
 
 
 
 
 
 
 
 
f59d050
be986b5
 
 
 
f59d050
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
import cv2
import numpy as np
from typing import Union
from PIL import Image
from io import BytesIO
import imghdr
import cv2
import os
from fastapi import HTTPException
import io
from app.utils.file_utils import get_file_content
import logging

SUPPORTED_IMAGE_FORMATS = ['.jpg', '.jpeg', '.png', '.bmp', '.gif', '.tiff', '.webp']

# Set up logging
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)

# Define the paths to the XML files
current_dir = os.path.dirname(os.path.abspath(__file__))
project_root = os.path.dirname(os.path.dirname(current_dir))
xml_paths = {
    'frontal': os.path.join(project_root, 'models', 'haarcascade_frontalface_default.xml'),
    'frontal_alt': os.path.join(project_root, 'models', 'haarcascade_frontalface_alt2.xml'),
    'profile': os.path.join(project_root, 'models', 'haarcascade_profileface.xml')
}

# Try to load the pre-trained face detection models
face_cascades = {}
for name, path in xml_paths.items():
    try:
        if not os.path.exists(path):
            logger.error(f"Error: XML file not found at {path}")
            continue
        cascade = cv2.CascadeClassifier(path)
        if cascade.empty():
            logger.error(f"Error: Unable to load the cascade classifier. XML file is empty or invalid: {path}")
        else:
            face_cascades[name] = cascade
            logger.info(f"Successfully loaded face detection model from: {path}")
    except Exception as e:
        logger.error(f"Error loading face detection model {name}: {str(e)}")
    
def verify_image_format(firebase_filename: str):
    content = get_file_content(firebase_filename)
    file_ext = '.' + (imghdr.what(BytesIO(content)) or '')
    if file_ext not in SUPPORTED_IMAGE_FORMATS:
        raise HTTPException(status_code=400, detail=f"Unsupported image format. Supported formats are: {', '.join(SUPPORTED_IMAGE_FORMATS)}")

def preprocess_image(image: Union[str, np.ndarray, Image.Image], hash_size: int = 32) -> np.ndarray:
    if isinstance(image, str):
        content = get_file_content(image)
        img = Image.open(BytesIO(content))
        img = strip_metadata(img)
        image = np.array(img)
    elif isinstance(image, Image.Image):
        image = strip_metadata(image)
        image = np.array(image)
    
    if len(image.shape) == 3:
        image = cv2.cvtColor(image, cv2.COLOR_RGB2GRAY)
    
    image = cv2.resize(image, (hash_size, hash_size), interpolation=cv2.INTER_AREA)
    image = cv2.normalize(image, None, 0, 255, cv2.NORM_MINMAX)
    return image

def strip_metadata(img: Image.Image) -> Image.Image:
    data = list(img.getdata())
    img_without_exif = Image.new(img.mode, img.size)
    img_without_exif.putdata(data)
    return img_without_exif

def detect_face(image_input) -> bool:
    """
    Enhanced face detection using cascaded classifiers.
    Args:
        image_input: Either raw image bytes or a filename
    Returns:
        bool: True if any faces are detected, False otherwise
    """
    try:
        # Determine if the input is bytes or a filename
        if isinstance(image_input, bytes):
            # Decode image from bytes
            nparr = np.frombuffer(image_input, np.uint8)
            img = cv2.imdecode(nparr, cv2.IMREAD_COLOR)
        elif isinstance(image_input, str):
            # Read image from file
            img = cv2.imread(image_input)
        else:
            logger.error("Invalid input type for detect_face")
            return False

        if img is None:
            logger.error("Failed to load image in detect_face")
            return False

        # Convert to grayscale
        gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
        
        # Enhance contrast to help with detection
        clahe = cv2.createCLAHE(clipLimit=2.0, tileGridSize=(8,8))
        enhanced_gray = clahe.apply(gray)
        
        # Try each cascade classifier
        for name, cascade in face_cascades.items():
            faces = cascade.detectMultiScale(
                enhanced_gray,
                scaleFactor=1.1,
                minNeighbors=4,
                minSize=(30, 30)
            )
            if len(faces) > 0:
                logger.info(f"Face detected using {name} classifier")
                return True
        
        logger.info("No face detected")
        return False
    except Exception as e:
        logger.error(f"Error in detect_face: {str(e)}")
        return False