
    钾ie"                         d Z ddlZddlZddlZddlZddlZddlmZ ddl	m
Z
 ddlmZ ddlmZ ddlmZ  ej"                  e      Z G d	 d
      Zy)zW
Face recognition service using DeepFace (ArcFace) and pgvector for similarity search.
    N)DeepFace)ContentFile)CosineDistance)
FaceRecord   )FacePreprocessorc                       e Zd ZdZd Zdej                  defdZ	 dde	de
defd	Zdd
ede
fdZdefdZdedefdZy)FaceRecognitionServicez{
    Provides face registration and identification using ArcFace embeddings
    and pgvector cosine similarity search.
    c                 "    t               | _        y N)r   preprocessor)selfs    7/var/www/face-recognition/faces/services/recognition.py__init__zFaceRecognitionService.__init__   s    ,.    preprocessed_facereturnc                    	 |dz  j                  t        j                        }d}	 t        j                  dd      5 }|j
                  }t        j                  |t        j                  |t        j                               ddd       t        j                  |ddd      }|rt        |      d	k(  rt        d
      |d	   d   }t        |      dk7  rt        j                  dt        |             ||r6t         j"                  j%                  |      rt!        j&                  |       S S S # 1 sw Y   xY w# |r6t         j"                  j%                  |      rt!        j&                  |       w w w xY w# t        $ r  t(        $ r<}t        j+                  dt-        |             t        dt-        |             |d}~ww xY w)a  
        Extract a 512-dimensional ArcFace embedding from a preprocessed face image.

        Args:
            preprocessed_face: Preprocessed face as float32 numpy array,
                               shape (160, 160, 3), values in [0, 1].

        Returns:
            List of 512 float values representing the face embedding.

        Raises:
            RuntimeError: If DeepFace fails to extract the embedding.
           N.jpgF)suffixdeleteArcFaceskip)img_path
model_namedetector_backendenforce_detectionr   z DeepFace returned no embeddings.	embeddingi   z:Expected 512-dim embedding, got %d-dim. Proceeding anyway.z(DeepFace embedding extraction failed: %sz"Failed to extract face embedding: )astypenpuint8tempfileNamedTemporaryFilenamecv2imwritecvtColorCOLOR_RGB2BGRr   	representlenRuntimeErrorloggerwarningospathexistsunlink	Exceptionerrorstr)r   r   
face_uint8	temp_pathtmp_fileresultsr   es           r   extract_embeddingz(FaceRecognitionService.extract_embedding   s   /	+c199"((CJ I!)00!%  (IKK!Z1B1BC	 #,,&(%+&+	 #g,!"3&'IJJ#AJ{3	y>S(NNTI
 ! 	!:IIi( ";9? > 	!:IIi( ";9  	 	LLCSVL4SVH=	sG   $E7 D: AD.A2D: 56E7 .D73D: ::E44E7 7G	7G  GNr%   metadatac                    |i }| j                   j                  |      }| j                  |      }t        |d      r?|j	                  d       |j                         }t        |d| d      }t        ||      }n|dz  j                  t        j                        }	t        j                  |	t        j                        }
t        j                  d|
      \  }}|st        d      t        |j!                         | d      }t#        |||      }|j$                  j'                  |j(                  |d	
       |j'                          t*        j-                  d||j.                  t1        |             |S )a%  
        Register a new face:
        1. Preprocess image via FacePreprocessor
        2. Extract 512-dim ArcFace embedding
        3. Save FaceRecord to database

        Args:
            name: Name to associate with the face.
            image_input: Image as file path, PIL Image, numpy array, or file-like.
            metadata: Optional metadata dict.

        Returns:
            The saved FaceRecord instance.

        Raises:
            ValueError: If no face is detected.
            RuntimeError: If embedding extraction fails.
        readr   r%   r   )r%   r   z+Failed to encode preprocessed face to JPEG.)r%   r   r<   Fsavez7Registered face for '%s' (id=%d) with %d-dim embedding.)r   
preprocessr;   hasattrseekr>   getattrr   r    r!   r"   r&   r(   r)   imencoder,   tobytesr   photor@   r%   r-   infoidr+   )r   r%   image_inputr<   preprocessedr   photo_content
photo_name
photo_filer6   face_bgrsuccessbufferrecords                 r   register_facez$FaceRecognitionService.register_face]   sR   * H ((33K@ **<8	 ;'Q',,.M fdmDJ$]DJ ',44RXX>J||J0A0ABH!ll68<OGV"#PQQ$V^^%5tfDMJJ 

 	*//:EBEII	N		
 r   	thresholdc                    | j                   j                  |      }| j                  |      }t        j                  j                         dk(  rddddt        d      dS t        j                  j                  t        d|      	      j                  d
      j                         }|ddddt        d      dS t        |j                        }||k  rbt        d|z
  d      }t        j                  d|j                  |j                   ||       d|j                  ||j                   t        |d      dS t        j                  d|j                  |j                   ||       ddddt        |d      dS )a  
        Identify a face against registered embeddings using cosine similarity.

        Args:
            image_input: Image as file path, PIL Image, numpy array, or file-like.
            threshold: Maximum cosine distance for a match (default 0.4).

        Returns:
            Dict with keys: matched, name, confidence, id, distance.

        Raises:
            ValueError: If no face is detected.
            RuntimeError: If embedding extraction fails.
        r   FUnknowng        Ninf)matchedr%   
confidencerI   distancer   )rZ   rZ   g      ?   zCFace identified as '%s' (id=%d) with confidence=%.4f, distance=%.4fTzGFace not matched. Nearest: '%s' (id=%d), distance=%.4f > threshold=%.4f)r   rA   r;   r   objectscountfloatannotater   order_byfirstrZ   roundr-   rH   r%   rI   )r   rJ   rT   rK   query_embeddingnearestrZ   rY   s           r   identify_facez$FaceRecognitionService.identify_face   s     ((33K@ 00> ##%* !!!%L  '''_E (  Xj!UW 	 ? !!!%L  ))*y sX~q1JKKU

  (jj!(A.  KKY

 !!!!(A. r   c                 x    t         j                  j                         j                  dddd      }t	        |      S )z
        Return all registered faces with id, name, created_at, metadata.
        Excludes embeddings for performance.

        Returns:
            List of dicts with face record info.
        rI   r%   
created_atr<   )r   r\   allvalueslist)r   recordss     r   list_registered_facesz,FaceRecognitionService.list_registered_faces   s8     $$((*11&,

 G}r   face_idc                 V   	 t         j                  j                  |      }|j                  r|j                  j	                  d       |j	                          t
        j                  d||j                         y# t         j                  $ r t
        j                  d|       Y yw xY w)z
        Delete a registered face by ID.

        Args:
            face_id: The ID of the face record to delete.

        Returns:
            True if the record was deleted, False if not found.
        )rI   Fr?   z!Deleted face record id=%d ('%s').Tz)Face record id=%d not found for deletion.)
r   r\   getrG   r   r-   rH   r%   DoesNotExistr.   )r   rm   rR   s      r   delete_facez"FaceRecognitionService.delete_face   s    
	''++w+7F||###/MMOKK;WfkkR&& 	NNFP	s   A9A< <)B('B(r   )g?)__name__
__module____qualname____doc__r   r!   ndarrayrj   r;   r5   dictr   rS   r^   re   rl   intboolrq    r   r   r
   r
      s}    
/=2:: =$ =@ 8<>>04>	>@QE QD Qft 3 4 r   r
   )ru   loggingr#   r/   r&   numpyr!   deepfacer   django.core.files.baser   pgvector.djangor   faces.modelsr   preprocessingr   	getLoggerrr   r-   r
   rz   r   r   <module>r      sH      	 
   . * # +			8	$| |r   