o
    @Tj                     @   s   d Z ddlZddlZddlmZ ddlZddlZddlm	Z	m
Z
 ddlmZ ddlmZmZ ddlmZ ddlmZ dd	lmZ dd
lmZ eeZG dd dZdS )zJFace registration and identification service using insightface + pgvector.    N)Path)selecttext)AsyncSession)GLOBAL_USER_IDsettings)FaceEmbedding)User)get_or_create_user)FaceAnalyzerc                   @   s   e Zd ZdefddZdedededefdd	Z	dededed
B fddZ
dedejd
B fddZdedejdeeef d
B fddZdedejdefddZd
S )FaceServiceanalyzerc                 C   s
   || _ d S N)r   )selfr    r   9/home/ubuntu/htdocs/ootd-api/app/services/face_service.py__init__   s   
zFaceService.__init__dbimage_bytes	user_namereturnc                    s   |  |}|du rtd| ||I dH }|durH|\}}| ||jI dH }t|j| |d}	||	 | I dH  |j|j	d|dS t
||I dH \}
}| ||
jI dH }t|
j| |d}	||	 | I dH  |
j|
j	|ddS )u]  Register a face for a user.

        1. Extract face embedding from image
        2. Try matching against existing faces
        3. If match → add embedding to matched user (face takes priority)
        4. If no match → get-or-create user by name → add embedding

        Returns dict with user_id, user_name, is_new_user, confidence.
        N%   얼굴을 감지할 수 없습니다.)user_id	embedding
image_pathF)r   r   is_new_user
confidence      ?)_extract_embedding
ValueError_find_best_match_save_face_imageidr   tolistaddcommitnamer
   )r   r   r   r   r   matchmatched_userr   r   face_recordusercreatedr   r   r   register   sD   


zFaceService.registerNc                    sR   |  |}|du rtd| ||I dH }|du rdS |\}}|j|j|dS )zvIdentify user from face image.

        Returns dict with user_id, user_name, confidence or None if no match.
        Nr   )r   r   r   )r   r   r    r"   r&   )r   r   r   r   r'   r*   r   r   r   r   identifyT   s   
	zFaceService.identifyc                 C   s:   t j|t jd}t|tj}|du rtd| j|S )z6Decode image bytes and extract largest face embedding.)dtypeNu+   이미지를 디코딩할 수 없습니다.)	np
frombufferuint8cv2imdecodeIMREAD_COLORr   r   get_largest_face_embedding)r   r   arrimager   r   r   r   l   s
   zFaceService._extract_embeddingr   c                    s   t j}dddd | D  d }td}|||ttdI dH }| }|du r0dS t	|j
}d	| }	|	|k r?dS |tttj|jkI dH }
|
 }||	fS )
zFind best matching user via pgvector cosine distance.

        Returns (User, confidence) if similarity > threshold, else None.
        Confidence = 1 - cosine_distance (higher = more similar).
        [,c                 s   s    | ]}t |V  qd S r   )str).0xr   r   r   	<genexpr>   s    z/FaceService._find_best_match.<locals>.<genexpr>]a  
            SELECT fe.id, fe.user_id, fe.embedding <=> :emb AS distance
            FROM face_embeddings fe
            JOIN users u ON u.id = fe.user_id
            WHERE u.id != :global_id
            ORDER BY fe.embedding <=> :emb
            LIMIT 1
        )emb	global_idNr   )r   FACE_MATCH_THRESHOLDjoinr#   r   executer:   r   firstfloatdistancer   r	   wherer"   r   
scalar_one)r   r   r   	thresholdembedding_strqueryresultrowrF   r   user_resultr*   r   r   r   r    t   s"   
 	

zFaceService._find_best_matchr   c                    sX   t dt| }t tj| }|jddd t j d}|| | t|| S )z?Save face image to disk and return path relative to UPLOAD_DIR.facesT)parentsexist_okz.jpg)	r   r:   r   
UPLOAD_DIRmkdiruuiduuid4hexwrite_bytes)r   r   r   rel_pathdest_dirfilenamer   r   r   r!      s   zFaceService._save_face_image)__name__
__module____qualname__r   r   r   bytesr:   dictr,   r-   r/   ndarrayr   tupler	   rE   r    rT   UUIDr!   r   r   r   r   r      s6    
;

+r   )__doc__loggingrT   pathlibr   r2   numpyr/   
sqlalchemyr   r   sqlalchemy.ext.asyncior   
app.configr   r   app.models.facer   app.models.userr	   app.services.user_servicer
   app.utils.face_analyzerr   	getLoggerr[   loggerr   r   r   r   r   <module>   s    
