
    蒾i%                     h    d Z ddlZddlZddlZddlmZmZ  ej                  e	      Z
 G d d      Zy)zk
Face preprocessing pipeline using OpenCV.
Handles face detection, alignment, cropping, and normalization.
    N)ImageExifTagsc            	       p   e Zd ZdZd Zdej                  fdZdej                  dej                  fdZ	dej                  de
fdZdefd	Zddej                  d
ededej                  fdZdej                  dej                  fdZdej                  dej                  fdZdej                  fdZy)FacePreprocessoraM  
    Preprocesses images for face recognition:
    1. Load and convert to RGB
    2. Apply EXIF rotation correction
    3. Detect face using Haarcascade
    4. Crop the largest face with padding
    5. Align using eye landmarks
    6. Resize to 160x160
    7. Apply histogram equalization
    8. Normalize pixel values to [0, 1]
    c                     t        j                  t         j                  j                  dz         | _        t        j                  t         j                  j                  dz         | _        y )Nz#haarcascade_frontalface_default.xmlzhaarcascade_eye.xml)cv2CascadeClassifierdatahaarcascadesface_cascadeeye_cascade)selfs    9/var/www/face-recognition/faces/services/preprocessing.py__init__zFacePreprocessor.__init__   sP    11HH!!$II
 00HH!!$99
    returnc                    t        |t              rKt        j                  |      }|t	        d|       t        j
                  |t        j                        }|S t        |t        j                        r7| j                  |      }t        j                  |j                  d            }|S t        |t        j                        rt        |j                        dk(  r&t        j
                  |t        j                        }|S |j                  d   dk(  r&t        j
                  |t        j                         }|S |j                  d   dk(  r|j#                         }|S t	        d|j                         t%        |d      r]|j'                  d       t        j(                  |      }| j                  |      }t        j                  |j                  d            }|S t	        d	t+        |      j,                   d
      )z
        Load image from various input types and return as RGB numpy array.
        Supports: file path (str), PIL Image, numpy array, Django UploadedFile.
        z Could not load image from path: RGB         zUnsupported image shape: readr   zUnsupported image input type: zC. Expected str (path), PIL Image, numpy array, or file-like object.)
isinstancestrr   imread
ValueErrorcvtColorCOLOR_BGR2RGBr   _apply_exif_rotationnparrayconvertndarraylenshapeCOLOR_GRAY2RGBCOLOR_BGRA2RGBcopyhasattrseekopentype__name__)r   image_inputimgpil_imgs       r   _load_imagezFacePreprocessor._load_image%   s   
 k3'**[)C{ #CK=!QRR,,sC$5$56CJU[[1++K8C((3;;u-.CJRZZ0;$$%*ll;0B0BC J ""1%*ll;0B0BC
 J	 ""1%*!&&( J !#<[=N=N<O!PQQ [&)Qjj-G//8G((7??512CJ 0k1B1K1K0L MT T r   	pil_imagec                    	 |j                         }||S d}t        j                  j                         D ]  \  }}|dk(  s|} n |||vr|S ||   }dddd}||v r|j	                  ||   d      }|S |d	k(  r!|j                  t        j                        }|S |d
k(  r!|j                  t        j                        }|S |dk(  r2|j	                  dd      j                  t        j                        }|S |dk(  r0|j	                  dd      j                  t        j                        }|S # t        t        t        f$ r Y |S w xY w)z3Apply EXIF orientation tag rotation to a PIL Image.NOrientation   i  Z   )r         T)expandr   r         )_getexifr   TAGSitemsrotate	transposer   FLIP_LEFT_RIGHTFLIP_TOP_BOTTOMAttributeErrorKeyError
IndexError)r   r2   exiforientation_keykeyvalorientationrotation_maps           r   r   z%FacePreprocessor._apply_exif_rotationR   s   "	%%'D|  "O$MM//1 S-'&)O
 &/*E  /K L l*%,,\+-Ft,T	  !%//0E0EF	  !%//0E0EF	  !%,,S,>HHI^I^_	  !%,,R,=GGH]H]^	
  *5 		s:   D0 +D0 D0 %D0 6$D0 $D0 5D0 95D0 0EEr/   c                     t        j                  |t         j                        }| j                  j	                  |dddt         j
                        }t        |      dk(  rt        d      |S )z9Detect faces using Haarcascade and return bounding boxes.皙?r:   )   rN   )scaleFactorminNeighborsminSizeflagsr   zfNo face detected in image. Please ensure the face is clearly visible, well-lit, and facing the camera.)r   r   COLOR_RGB2GRAYr   detectMultiScaleCASCADE_SCALE_IMAGEr$   r   )r   r/   grayfacess       r   _detect_faceszFacePreprocessor._detect_facesz   sn    ||C!3!34!!22)) 3 
 u:?< 
 r   c           
          t        |      dk(  rt        |d         S |D cg c]  \  }}}}||z   }}}}}t        j                  |      }t        ||         S c c}}}}w )z)Select the largest detected face by area.   r   )r$   tupler    argmax)r   rW   xywhareaslargest_idxs           r   _get_largest_facez"FacePreprocessor._get_largest_face   s`    u:?q?"+011<Aq!QQ11ii&U;'(( 2s   A
face_boxpaddingc                     |\  }}}}|j                   dd \  }}	t        d||z
        }
t        d||z
        }t        |	||z   |z         }t        |||z   |z         }||||
|f   S )z7Crop face region with padding, clamped to image bounds.Nr   r   )r%   maxmin)r   r/   rd   re   r]   r^   r_   r`   heightwidthx1y1x2y2s                 r   
_crop_facezFacePreprocessor._crop_face   s    
1a		"1AK AK A(Q)2b5"R%<  r   	face_cropc                    t        j                  |t         j                        }| j                  j	                  |ddd      }t        |      dk\  rt        |d       }|d   }|d	   }|d   |d   dz  z   |d	   |d
   dz  z   f}|d   |d   dz  z   |d	   |d
   dz  z   f}|d	   |d	   z
  }	|d   |d   z
  }
t        j                  t        j                  |	|
            }|j                  dd \  }}|dz  |dz  f}t        j                  ||d      }t        j                  ||||ft         j                  t         j                        }|S t        j!                  d       |S )z
        Align face using eye detection.
        If two eyes are found, compute the angle between them and
        apply warpAffine rotation to align the face horizontally.
        rM   
   )   rs   )rO   rP   rQ   r   c                     | d   S )Nr    )es    r   <lambda>z.FacePreprocessor._align_face.<locals>.<lambda>   s
    QqT r   )rH   r   rZ   r   Ng      ?)scale)rR   
borderModez<Could not detect 2 eyes for alignment, using unaligned crop.)r   r   rS   r   rT   r$   sortedr    degreesarctan2r%   getRotationMatrix2D
warpAffineINTER_CUBICBORDER_REPLICATEloggerdebug)r   rp   rV   eyeseyes_sortedleft_eye	right_eyeleft_centerright_centerdydxangler`   r_   centerrotation_matrixaligneds                    r   _align_facezFacePreprocessor._align_face   s    ||Is'9'9:00	 1 
 t9> >:K"1~H#AI hqkQ..hqkQ..K
 !y|q00!y|q00L a;q>1Ba;q>1BJJrzz"b12E ??2A&DAq1fa1f%F "55fe3OOnnAoo//G N 	STr   c                 H   t        j                  |t         j                        }t        j                  |      \  }}}t        j                  dd      }|j                  |      }t        j                  |||g      }t        j                  |t         j                        }	|	S )z{
        Apply histogram equalization on the L channel (LAB color space)
        to normalize lighting conditions.
        g       @)r8   r8   )	clipLimittileGridSize)r   r   COLOR_RGB2LABsplitcreateCLAHEapplymergeCOLOR_LAB2RGB)
r   r/   lab	l_channel	a_channel	b_channelclahel_equalizedlab_equalizedresults
             r   _apply_histogram_equalizationz.FacePreprocessor._apply_histogram_equalization   s|    
 ll3 1 12*-))C.'	9i#FCkk),		;	9"EFmS->->?r   c                 
   | j                  |      }t        |j                        dk(  r$t        j                  |t        j
                        }| j                  |      }| j                  |      }| j                  ||d      }| j                  |      }t        j                  |dt        j                        }| j                  |      }|j                  t        j                        dz  }	t         j#                  d       |	S )a(  
        Full preprocessing pipeline:
        1. Load image (handles str path, PIL Image, numpy array, file-like)
        2. Detect face using Haarcascade
        3. Crop the largest detected face with 20px padding
        4. Align using eye landmarks
        5. Resize to 160x160 (ArcFace input size)
        6. Apply histogram equalization on L channel (LAB)
        7. Normalize pixel values to [0, 1]
        8. Return as float32 numpy array shape (160, 160, 3)

        Raises:
            ValueError: If no face is detected in the image.
        r      )re   )   r   )interpolationg     o@zcPreprocessing complete: face detected, cropped, aligned, resized to 160x160, equalized, normalized.)r1   r$   r%   r   r   r&   rX   rc   ro   r   resizer   r   astyper    float32r   info)
r   r.   r/   rW   largest_facerp   aligned_faceresized	equalized
normalizeds
             r   
preprocesszFacePreprocessor.preprocess   s      {+ syy>Q,,sC$6$67C ""3' --e4 OOCrOB	 ''	2 **\:S__U 66w?	 %%bjj1E9
9	

 r   N)r   )r-   
__module____qualname____doc__r   r    r#   r1   r   r   listrX   r[   rc   intro   r   r   r   ru   r   r   r   r      s    

+"** +Z&ekk &ekk &P  &)% )
!bjj 
!E 
!C 
!QSQ[Q[ 
!4RZZ 4BJJ 4l 

 0 0r   r   )r   loggingr   numpyr    PILr   r   	getLoggerr-   r   r   ru   r   r   <module>r      s6   
  
  			8	$H Hr   