o
    Ďi &                     @   s   d Z dgZddlZddlmZmZ ddlmZ ddlm	Z	 ddl
ZddlZddlZddlm  mZ ddlmZ ddlmZ dd	lmZ dd
lmZ G dd dZdS )zClassifier functionality of SpeciesNet.

Defines the SpeciesNetClassifier class, responsible for image classification for
SpeciesNet. It handles loading of classification models, preprocessing of input images,
and generating species predictions.
SpeciesNetClassifier    N)AnyOptional)logging)format_timespan)Failure)BBox)	ModelInfo)PreprocessedImagec                
   @   s   e Zd ZdZdZdZdZ		ddedee dee d	dfd
dZ			ddee
jj deee  ded	ee fddZdedee d	eeef fddZdee deee  d	eeeef  fddZdS )r   z#Classifier component of SpeciesNet.i  g333333?i  N
model_nametarget_species_txtdevicereturnc                    s|  t   }t|_|durtd| |_ntj r d_ntj	j
 r*d_nd_tjjjjdd_j  j D ]}d|_qCtjjdd	d
}dd t| D _W d   n1 siw   Y  |durt|dd	d
}fdd| D _W d   n1 sw   Y  dd j D   fddjD _t   }tdt|| j  dS )a  Loads the classifier resources.

        Args:
            model_name:
                String value identifying the model to be loaded. It can be a Kaggle
                identifier (starting with `kaggle:`), a HuggingFace identifier (starting
                with `hf:`) or a local folder to load the model from.
            device:
                Specific device identifier, e.g. "cpu" or "cuda".  If None, "cuda"
                and "mps" will be used if available.
        Nz Using caller-supplied device %s.cudampscpuF)map_locationweights_onlyrzutf-8)modeencodingc                 S   s   i | ]	\}}||  qS  )strip).0idxliner   r   Q/home/jeff/fluffinator/venv/lib/python3.10/site-packages/speciesnet/classifier.py
<dictcomp>_   s    z1SpeciesNetClassifier.__init__.<locals>.<dictcomp>c                    s&   g | ]}|   j v r|  qS r   )r   labelsvalues)r   r   selfr   r   
<listcomp>d   s
    z1SpeciesNetClassifier.__init__.<locals>.<listcomp>c                 S   s   i | ]\}}||qS r   r   )r   r   labelr   r   r   r   i       c                       g | ]} | qS r   r   )r   r#   )labels_to_idxr   r   r"   j       z(Loaded SpeciesNetClassifier in %s on %s.)timer	   
model_infor   infor   torchr   is_availablebackendsr   load
classifiermodeleval
parametersrequires_gradopenclassifier_labels	enumerate	readlinesr   target_labelsitems
target_idxr   upper)r!   r   r   r   
start_timeparamfpend_timer   )r&   r!   r   __init__1   sB   





zSpeciesNetClassifier.__init__Timgbboxesresizec              	   C   s  |du rdS t |}t |tj}| jjdkrD|rCt |t|d j	|j
 t|d j|j t|d j
|j
 t|d j|j }n | jjdkrdtt|j
dtj  |j
tj }t |||jg}|rrt j|tjtjgdd}t |tj}|g d}t| |j|j
S )	a  Preprocesses an image according to this classifier's needs.

        This method prepares an input image for classification. It handles
        image loading, cropping, and resizing to the expected
        input size for the classifier model.

        In `always_crop` mode images are cropped according to the bounding boxes
        provided. In `full_image` mode the top and bottom of the image are cropped
        to prevent learning correlations between camera brand and species priors.
        See the paper for more details.

        Args:
            img:
                PIL image to preprocess. If `None`, no preprocessing is performed.
            bboxes:
                Optional list of bounding boxes. Needed for some types of classifiers to
                crop the image to specific bounding boxes during preprocessing.
            resize:
                Whether to resize the image to some expected dimensions.

        Returns:
            A preprocessed image, or `None` if no PIL image was provided initially.
        Nalways_cropr   
full_imageg      ?F)	antialias)      r   )Fpil_to_tensorconvert_image_dtyper+   float32r)   type_cropintyminheightxminwidthmaxr   MAX_CROP_RATIOMAX_CROP_SIZEcenter_croprC   IMG_SIZEuint8permuter
   numpy)r!   rA   rB   rC   
img_tensortarget_heightr   r   r   
preprocesss   s:   


zSpeciesNetClassifier.preprocessfilepathc                 C   s   |  |g|gd S )aP  Runs inference on a given preprocessed image.

        Args:
            filepath:
                Location of image to run inference on. Used for reporting purposes only,
                and not for loading the image.
            img:
                Preprocessed image to run inference on. If `None`, a failure message is
                reported back.

        Returns:
            A dict containing either the top-5 classifications for the given image (in
            decreasing order of confidence scores), or a failure message if no
            preprocessed image was provided.
        r   )batch_predict)r!   r_   rA   r   r   r   predict   s   zSpeciesNetClassifier.predict	filepathsimgsc                    sR  i g }g }t ||D ] \}}|du r|tjjgd|< q|| ||jd  q|s4t S tj	|dtj
d}t|j}| tjdd}tj|ddd	\}}	tt || |	 D ]5\ \}}
}|fd
d|D |
 dd|< tdr| d j fddjD d qjfdd|D S )a  Runs inference on a batch of preprocessed images.

        Args:
            filepaths:
                List of image locations to run inference on. Used for reporting purposes
                only, and not for loading the images.
            imgs:
                List of preprocessed images to run inference on. If an image is `None`,
                a corresponding failure message is reported back.

        Returns:
            A list of dict results. Each dict result contains either the top-5
            classifications for the corresponding image (in decreasing order of
            confidence scores), or a failure message if no preprocessed image was
            provided.
        N)r_   failures   r   )axisdtype)dim   )kri   c                    s   g | ]} j | qS r   )r   r   r   r    r   r   r"      r$   z6SpeciesNetClassifier.batch_predict.<locals>.<listcomp>)classesscores)r_   classificationsr:   ro   c                    s   g | ]
}t   | qS r   )floatrl   )file_idxlogitsr   r   r"     s    )target_classestarget_logitsc                    r%   r   r   )r   r_   )predictionsr   r   r"   	  r'   )zipr   
CLASSIFIERnameappendarrlistr   npstackrL   r+   
from_numpytor   r0   r   softmaxtopkr6   r[   tolisthasattrupdater8   r:   )r!   rb   rc   inference_filepaths	batch_arrr_   rA   batch_tensorrn   indices
scores_arrindices_arrr   )rq   rr   ru   r!   r   r`      sF   


	z"SpeciesNetClassifier.batch_predict)NN)NT)__name__
__module____qualname____doc__rX   rU   rV   strr   r@   PILImager{   r   boolr
   r^   dictr   ra   r`   r   r   r   r   r   *   sR    
E


A


)r   __all__r(   typingr   r   abslr   humanfriendlyr   r[   r|   	PIL.Imager   r+   !torchvision.transforms.functional
transforms
functionalrI   speciesnet.constantsr   speciesnet.utilsr   r	   r
   r   r   r   r   r   <module>   s    