
    h%                        S SK Jr  S SKrS SKJr  S SKJr  S SKrS SK	J
r
  S SKJr  S SKJrJr  S SKJr  S S	KJrJr  S
\R*                  S'    " S S5      r " S S5      rg)    )annotationsN)Path)Any)Image)IMG_FORMATS)LOGGERTORCH_VERSION)check_requirements)	TORCH_2_4select_deviceTRUEKMP_DUPLICATE_LIB_OKc                  X    \ rS rSrSrSS jrSS jrSS jrSS jrSSS jjr	SS jr
S	rg
)VisualAISearch   a  
A semantic image search system that leverages OpenCLIP for generating high-quality image and text embeddings and
FAISS for fast similarity-based retrieval.

This class aligns image and text embeddings in a shared semantic space, enabling users to search large collections
of images using natural language queries with high accuracy and speed.

Attributes:
    data (str): Directory containing images.
    device (str): Computation device, e.g., 'cpu' or 'cuda'.
    faiss_index (str): Path to the FAISS index file.
    data_path_npy (str): Path to the numpy file storing image paths.
    data_dir (Path): Path object for the data directory.
    model: Loaded CLIP model.
    index: FAISS index for similarity search.
    image_paths (list[str]): List of image file paths.

Methods:
    extract_image_feature: Extract CLIP embedding from an image.
    extract_text_feature: Extract CLIP embedding from text.
    load_or_build_index: Load existing FAISS index or build new one.
    search: Perform semantic search for similar images.

Examples:
    Initialize and search for images
    >>> searcher = VisualAISearch(data="path/to/images", device="cuda")
    >>> results = searcher.search("a cat sitting on a chair", k=10)
c                V   [         (       d   S[         S35       eSSKJn  [	        S5        [        S5      U l        SU l        SU l        [        UR                  S	S
5      5      U l        [        UR                  SS5      5      U l        U R                  R                  5       (       dM  SSKJn  ["        R$                  " U R                   SU S35        SSKJn  U" U S3SSS9  [        S
5      U l        U" SU R                  S9U l        SU l        / U l        U R1                  5         g)zDInitialize the VisualAISearch class with FAISS index and CLIP model.z1VisualAISearch requires torch>=2.4 (found torch==)r   )build_text_modelz	faiss-cpufaisszfaiss.indexz	paths.npydataimagesdevicecpu)
ASSETS_URLz( not found. Downloading images.zip from z/images.zip)safe_downloadT   )urlunzipretryzclip:ViT-B/32)r   N)r   r	   ultralytics.nn.text_modelr   r
   
__import__r   faiss_indexdata_path_npyr   getdata_dirr   r   existsultralytics.utilsr   r   warningultralytics.utils.downloadsr   modelindeximage_pathsload_or_build_index)selfkwargsr   r   r   s        a/home/james-whalen/.local/lib/python3.13/site-packages/ultralytics/solutions/similarity_search.py__init__VisualAISearch.__init__2   s    y^Mm_\]^^y>;'(
((VZZ9:#FJJx$?@}}##%%4NNdmm_,TU_T``klmAK8AN NDM%odkkJ

  "    c                    U R                   R                  [        R                  " U5      5      R	                  5       R                  5       $ )z7Extract CLIP image embedding from the given image path.)r*   encode_imager   openr   numpy)r.   paths     r0   extract_image_feature$VisualAISearch.extract_image_featureO   s2    zz&&uzz$'78<<>DDFFr3   c                    U R                   R                  U R                   R                  U/5      5      R                  5       R	                  5       $ )z6Extract CLIP text embedding from the given text query.)r*   encode_texttokenizer   r7   )r.   texts     r0   extract_text_feature#VisualAISearch.extract_text_featureS   s:    zz%%djj&9&94&&ABFFHNNPPr3   c                   [        U R                  5      R                  5       (       a  [        U R                  5      R                  5       (       af  [        R
                  " S5        U R                  R                  U R                  5      U l        [        R                  " U R                  5      U l        g[        R
                  " S5        / nU R                  R                  5        H|  nUR                  R                  5       R!                  S5      ["        ;  a  M6   UR%                  U R'                  U5      5        U R                  R%                  UR(                  5        M~     U(       d  [/        S5      e[        R0                  " U5      R3                  S5      nU R                  R5                  U5        U R                  R7                  UR8                  S	   5      U l        U R                  R;                  U5        U R                  R=                  U R                  U R                  5        [        R>                  " U R                  [        R@                  " U R                  5      5        [        R
                  " S
[C        U R                  5       S35        g! [*         a2  n[        R,                  " SUR(                   SU 35         SnAGM  SnAff = f)aF  
Load existing FAISS index or build a new one from image features.

Checks if FAISS index and image paths exist on disk. If found, loads them directly. Otherwise, builds a new
index by extracting features from all images in the data directory, normalizes the features, and saves both the
index and image paths for future use.
zLoading existing FAISS index...Nz#Building FAISS index from images....z	Skipping z: z'No image embeddings could be generated.float32   zIndexed z images.)"r   r"   r&   r#   r   infor   
read_indexr+   nploadr,   r%   iterdirsuffixlowerlstripr   appendr9   name	Exceptionr(   RuntimeErrorvstackastypenormalize_L2IndexFlatIPshapeaddwrite_indexsavearraylen)r.   vectorsfilees       r0   r-   "VisualAISearch.load_or_build_indexW   s      !((**tD4F4F/G/N/N/P/PKK9:..t/?/?@DJ!wwt'9'9:D 	9: MM))+D{{  "))#.kA=t99$?@  ''		2 , HII))G$++I6

(ZZ++GMM!,<=


w

tzz4+;+;<
""BHHT-=-=$>?hs4#3#345X>?  =499+Rs;<<=s    AJ  
K*&KKc           	     ,   U R                  U5      R                  S5      nU R                  R                  U5        U R                  R                  XB5      u  pV[        US   5       VVs/ s H3  u  pxUS   U   U:  d  M  U R                  U   [        US   U   5      4PM5     n	nnU	R                  S SS9  [        R                  " S5        U	 H"  u  p[        R                  " SU
 SUS	 35        M$     U	 Vs/ s H  oS   PM	     sn$ s  snnf s  snf )
a  
Return top-k semantically similar images to the given query.

Args:
    query (str): Natural language text query to search for.
    k (int, optional): Maximum number of results to return.
    similarity_thresh (float, optional): Minimum similarity threshold for filtering results.

Returns:
    (list[str]): List of image filenames ranked by similarity score.

Examples:
    Search for images matching a query
    >>> searcher = VisualAISearch(data="images")
    >>> results = searcher.search("red car", k=5, similarity_thresh=0.2)
rC   r   c                    U S   $ )NrD    )xs    r0   <lambda>'VisualAISearch.search.<locals>.<lambda>   s    1Q4r3   T)keyreversez
Ranked Results:z  - z | Similarity: z.4f)r?   rR   r   rS   r+   search	enumerater,   floatsortr   rE   )r.   queryksimilarity_thresh	text_featDr+   idxiresultsrN   scorers                r0   rg   VisualAISearch.search   s   " --e4;;IF	

	*::$$Y2BKERSHBU
BUYZ[\Y]^aYbfwYw3Ta %!S	"23BU 	 
 	6'("KDKK$tfOE#;?@ # &&g!g&&
 's   *D $D:Dc                $    U R                  U5      $ )z.Direct call interface for the search function.)rg   )r.   rk   s     r0   __call__VisualAISearch.__call__   s    {{5!!r3   )r%   r#   r   r   r"   r,   r+   r*   N)r/   r   returnNone)r8   r   ry   
np.ndarray)r>   strry   r{   )ry   rz   )   g?)rk   r|   rl   intrm   ri   ry   	list[str])rk   r|   ry   r   )__name__
__module____qualname____firstlineno____doc__r1   r9   r?   r-   rg   rw   __static_attributes__ra   r3   r0   r   r      s*    :#:GQ+@Z'@"r3   r   c                  >    \ rS rSrSrSS	S jjrS
S jrSSS jjrSrg)	SearchApp   a  
A Flask-based web interface for semantic image search with natural language queries.

This class provides a clean, responsive frontend that enables users to input natural language queries and
instantly view the most relevant images retrieved from the indexed database.

Attributes:
    render_template: Flask template rendering function.
    request: Flask request object.
    searcher (VisualAISearch): Instance of the VisualAISearch class.
    app (Flask): Flask application instance.

Methods:
    index: Process user queries and display search results.
    run: Start the Flask web application.

Examples:
    Start a search application
    >>> app = SearchApp(data="path/to/images", device="cuda")
    >>> app.run(debug=True)
Nc                   [        S5        SSKJnJnJn  X@l        XPl        [        XS9U l        U" [        S[        U5      R                  5       SS9U l
        U R                  R                  SU R                  S	S
/S9  g)z
Initialize the SearchApp with VisualAISearch backend.

Args:
    data (str, optional): Path to directory containing images to index and search.
    device (str, optional): Device to run inference on (e.g. 'cpu', 'cuda').
zflask>=3.0.1r   )Flaskrender_templaterequest)r   r   	templatesz/images)template_folderstatic_folderstatic_url_path/GETPOST)	view_funcmethodsN)r
   flaskr   r   r   r   searcherr   r   resolveappadd_url_ruler+   )r.   r   r   r   r   r   s         r0   r1   SearchApp.__init__   sq     	>*99.&D@'t*,,.%	
 	cTZZ%Qr3   c                    / nU R                   R                  S:X  aE  U R                   R                  R                  SS5      R	                  5       nU R                  U5      nU R                  SUS9$ )zCProcess user query and display search results in the web interface.r   rk    zsimilarity-search.html)rr   )r   methodformr$   stripr   r   )r.   rr   rk   s      r0   r+   SearchApp.index   sc    <<&(LL%%))'26<<>EmmE*G##$<g#NNr3   c                6    U R                   R                  US9  g)z'Start the Flask web application server.)debugN)r   run)r.   r   s     r0   r   SearchApp.run   s    5!r3   )r   r   r   r   )r   N)r   r|   r   r|   ry   rz   )ry   r|   )F)r   boolry   rz   )	r   r   r   r   r   r1   r+   r   r   ra   r3   r0   r   r      s    ,R,O" "r3   r   )
__future__r   ospathlibr   typingr   r7   rG   PILr   ultralytics.data.utilsr   r'   r   r	   ultralytics.utils.checksr
   ultralytics.utils.torch_utilsr   r   environr   r   ra   r3   r0   <module>r      sK    # 	     . 3 7 B%+

! "R" R"j7" 7"r3   