o
    ՚iZ                     @   s   d Z ddlZddlZddlmZmZmZmZm	Z	 ddl
Z
ddlZddlZddlmZ ddlZddlmZ ddlmZ z
ddlmZ dZW n eyO   dZY nw zddlZdZW n eyc   dZY nw G d	d
 d
ZdS )uH   
YOLO分类模型工具类 - 支持pt和onnx格式的分类模型调用
    N)DictAnyOptionalListUnion)Path)Image)YOLOTFc                	   @   s  e Zd ZdZd?dedefddZdefd	d
Zd@dedefddZdede	eef fddZ
d@dede	defddZdedefddZdedefddZdd Zdd Zdd Zdejdejfdd ZdAdejd"edee fd#d$ZdAdejd"edee fd%d&ZdAdejd"edee fd'd(ZdAd)eej d"edeee  fd*d+Zd,eeef fd-d.Zd/edefd0d1Zd2ee defd3d4ZdBd2ee d6edee fd7d8Zd2ee defd9d:Z dedefd;d<Z!deee"f fd=d>Z#dS )Cyolo_cls_model_loaderu4   YOLO分类模型加载器类，支持pt和onnx格式NTdeviceverbosec                 C   sl   d| _ d| _| || _d| _d| _d| _d| _i | _|| _	d| _
d| _d| _d| _g d| _g d| _dS )u   
        初始化分类模型加载器
        Args:
            device: 设备选择，可选 'cpu', 'cuda', 'auto'。默认为自动选择
            verbose: 是否显示详细日志信息，默认为True
        NF)   r   )g
ףp=
?gv/?gCl?)gZd;O?gy&1?g?)model
model_path_select_devicer   is_model_loadedmodel_load_errormodel_format
input_sizeclass_namesr   usr_class_namesort_session
input_nameoutput_namemeanstd)selfr   r    r   funcs/utils/model_cls_utils.py__init__,   s   
zyolo_cls_model_loader.__init__messagec                 C   s   | j r	t| dS dS )uZ   
        内部日志输出方法
        Args:
            message: 日志消息
        N)r   print)r   r    r   r   r   _logG   s   zyolo_cls_model_loader._logreturnc                 C   sF   |dkrdS |r| dr|S |r| rd| S tj r!dS dS )u   
        自动选择设备
        Args:
            device: 指定设备，可选 'cpu', 'cuda', 'auto' 或具体的GPU编号如 '0', '1'
        Returns:
            选择的设备字符串
        cpucudazcuda:zcuda:0)
startswithisdigittorchr%   is_available)r   r   r   r   r   r   P   s   

z$yolo_cls_model_loader._select_devicer   c                 C   sV   |sdS t |}| sdd| fS ddh}|j |vr)dd|j d| fS dS )	u   
        验证模型路径和格式
        Args:
            model_path: 模型文件路径
        Returns:
            (是否有效, 错误信息)
        )Fu   模型路径不能为空Fu   模型文件不存在: z.ptz.onnx   不支持的模型格式: u   ，支持的格式: )T )r   existssuffixlower)r   r   Zsupported_formatsr   r   r   _validate_model_pathe   s   z*yolo_cls_model_loader._validate_model_pathr   c              
   K   s  zm|  |\}}|s|| _W dS | jr/| jt|kr/| jdus$| jdur/| d|  W dS |   |r8|| _	t
|}|j dd| _| jdkrU| j|fi |W S | jdkrd| j|fi |W S d	| j | _W dS  ty } zd
t| | _| | j W Y d}~dS d}~ww )u  
        从路径加载YOLO分类模型
        Args:
            model_path: 模型文件路径
            input_size: 输入图像尺寸，格式为(height, width)，默认为(224, 224)
            **kwargs: 其他参数
        Returns:
            是否加载成功
        FNu    模型已加载，路径相同: T.r+   ptonnxr*   u   模型加载失败: )r/   r   r   r   strr   r   r"   unload_modelr   r   r-   r.   replacer   _load_pt_model_load_onnx_model	Exception)r   r   r   kwargsZis_valid	error_msgZmodel_path_objer   r   r   load_from_path{   s6   


z$yolo_cls_model_loader.load_from_pathc              
   K   s  zdt s	d| _W dS | d|  | d| j  t|| _t| jdr,| j| j t|| _	| 
  d| _d| _| d | d	| j  | d
| j  | d| jr]t| jnd  W dS  ty } zdt| | _| | j W Y d}~dS d}~ww )u   
        加载PyTorch格式的模型
        Args:
            model_path: 模型文件路径
            **kwargs: 其他参数
        Returns:
            是否加载成功
        u<   ultralytics库未安装，请安装: pip install ultralyticsFu   正在加载PyTorch模型: u   使用设备: toTNu   PyTorch模型加载成功:
     格式:      输入尺寸: u     类别数: ZUnknownu   PyTorch模型加载失败: )ULTRALYTICS_AVAILABLEr   r"   r   r	   r   hasattrr=   r3   r   _extract_pt_model_infor   r   r   r   lenr8   )r   r   r9   r;   r   r   r   r6      s0   	


 z$yolo_cls_model_loader._load_pt_modelc              
   K   s  zt s	d| _W dS | d|  dg}| jdr/tj r/|dd | d| j  n| d	 t	j
||d
| _| j d j| _| j d j| _| j d j}t|dkre|d |d f| _t|| _|   d| _d| _| d | d| j  | d| j  | d| j  | d| j  W dS  ty } zdt| | _| | j W Y d}~dS d}~ww )u   
        加载ONNX格式的模型
        Args:
            model_path: 模型文件路径
            **kwargs: 其他参数
        Returns:
            是否加载成功
        u\   onnxruntime库未安装，请安装: pip install onnxruntime 或 pip install onnxruntime-gpuFu   正在加载ONNX模型: ZCPUExecutionProviderr%   r   ZCUDAExecutionProvideru   使用GPU设备: u   使用CPU设备)	providers         TNu   ONNX模型加载成功:r>   r?   u     输入名称: u     输出名称: u   ONNX模型加载失败: )ONNXRUNTIME_AVAILABLEr   r"   r   r&   r(   r%   r)   insertortZInferenceSessionr   Z
get_inputsnamer   get_outputsr   shaperC   r   r3   r   _extract_onnx_model_infor   r   r8   )r   r   r9   rD   Zinput_shaper;   r   r   r   r7      sF   	


z&yolo_cls_model_loader._load_onnx_modelc              
   C   sx   zt | jdr| jj| _W dS dd tdD | _W dS  ty; } z| d|  ddi| _W Y d}~dS d}~ww )	u   提取PyTorch模型信息namesc                 S      i | ]}|d | qS Classr   .0ir   r   r   
<dictcomp>      z@yolo_cls_model_loader._extract_pt_model_info.<locals>.<dictcomp>  u$   提取PyTorch模型信息时出错: r   Class0N)rA   r   rO   r   ranger8   r"   r   r;   r   r   r   rB     s   z,yolo_cls_model_loader._extract_pt_model_infoc              
   C   s   z,| j  d j}t|dkr |d }dd t|D | _W d
S dd tdD | _W d
S  tyL } z| d|  dd	i| _W Y d
}~d
S d
}~ww )u   提取ONNX模型信息r   rF   c                 S   rP   rQ   r   rS   r   r   r   rV   $  rW   zByolo_cls_model_loader._extract_onnx_model_info.<locals>.<dictcomp>c                 S   rP   rQ   r   rS   r   r   r   rV   &  rW   rX   u!   提取ONNX模型信息时出错: rY   N)r   rL   rM   rC   rZ   r   r8   r"   )r   Zoutput_shapeZnum_classesr;   r   r   r   rN     s   z.yolo_cls_model_loader._extract_onnx_model_infoc              
   C   s   z/| j dur| ` d| _ | jdur| `d| _d| _d| _d| _tj r(tj  | 	d W dS  t
yJ } z| 	d|  W Y d}~dS d}~ww )u   卸载模型，释放内存NFu   模型已卸载u   模型卸载时出错: )r   r   r   r   r   r(   r%   r)   Zempty_cacher"   r8   r[   r   r   r   r4   +  s"   



z"yolo_cls_model_loader.unload_modelimagec           	   
   C   s   z\t |jdkr|jd dkrt|tj}n|}t|| j}|tj	d }t
dD ] }|dddd|f | j|  | j|  |dddd|f< q,t|d}tj|dd}|W S  tyq } z	tdt| d}~ww )	u   
        预处理图像用于分类推理
        Args:
            image: 输入图像 (BGR格式)
        Returns:
            预处理后的图像数组
        rG   rF   g     o@N)rF   r      r   )Zaxisu   图像预处理失败: )rC   rM   cv2ZcvtColorZCOLOR_BGR2RGBZresizer   ZastypenpZfloat32rZ   r   r   Z	transposeZexpand_dimsr8   RuntimeErrorr3   )	r   r]   Z	image_rgbZimage_resizedZimage_normalizedcZimage_tensorZimage_batchr;   r   r   r   preprocess_imageC  s   >z&yolo_cls_model_loader.preprocess_image   top_kc              
   K   s   | j stdz(| jdkr| j||fi |W S | jdkr(| j||fi |W S td| j  tyN } zdt| }| jrF| | t|d}~ww )u  
        使用加载的分类模型进行预测
        Args:
            image: 输入图像 (BGR格式)
            top_k: 返回top-k个分类结果，默认为5
            **kwargs: 其他预测参数
        Returns:
            分类结果列表
           模型未加载r1   r2   r*   u   分类预测失败: N)	r   ra   r   _predict_pt_predict_onnxr8   r3   r   r"   )r   r]   re   r9   r;   r:   r   r   r   predicte  s   



zyolo_cls_model_loader.predictc              
   K   s   zj| j |fi |}|rt|dkrg W S |d }t|drh|jdurh|jj  }t|ddd d| }g }t	|D ]%\}	}
t
|
}t||
 }| |}||t|d|	d |	d}|| q?|W S g W S  ty } z	tdt| d}~ww )	u   
        使用PyTorch模型进行预测
        Args:
            image: 输入图像
            top_k: 返回top-k个结果
            **kwargs: 其他参数
        Returns:
            分类结果列表
        r   probsNr\   rE   r^   u   类型编号   类型字符   分类置信度u   置信度排名u   索引u   PyTorch模型预测失败: )r   rC   rA   rj   datar$   numpyr`   argsort	enumerateintfloatget_class_nameroundappendr8   ra   r3   )r   r]   re   r9   resultsresultrj   top_indicesclassification_resultsrankidxclass_id
confidence
class_nameresult_dictr;   r   r   r   rg     s4   

z!yolo_cls_model_loader._predict_ptc              
   K   s   ze|  |}| j| jg| j|i}|d d }t|t| }|t| }t	|ddd d| }	g }
t
|	D ]%\}}t|}t|| }| |}||t|d|d |d}|
| q=|
W S  tyz } z	tdt| d}~ww )u   
        使用ONNX模型进行预测
        Args:
            image: 输入图像
            top_k: 返回top-k个结果
            **kwargs: 其他参数
        Returns:
            分类结果列表
        r   Nr\   rE   r^   rk   u   ONNX模型预测失败: )rc   r   runr   r   r`   Zexpmaxsumrp   rq   rr   rs   rt   ru   rv   r8   ra   r3   )r   r]   re   r9   Zinput_tensorZoutputsZlogitsZ
exp_logitsrj   ry   rz   r{   r|   r}   r~   r   r   r;   r   r   r   rh     s6   


z#yolo_cls_model_loader._predict_onnximagesc           
   
   K   s   | j stdz/g }t|D ]%\}}| jr$| d|d  dt|  | j||fi |}|| q|W S  tyU } zdt	| }	| jrM| |	 t|	d}~ww )u   
        批量分类预测
        Args:
            images: 图像列表
            top_k: 返回top-k个结果
            **kwargs: 预测参数
        Returns:
            每张图像的分类结果列表
        rf   u   处理图像 r^   /u   批量分类预测失败: N)
r   ra   rq   r   r"   rC   ri   rv   r8   r3   )
r   r   re   r9   rw   rU   r]   rx   r;   r:   r   r   r   predict_batch  s"   

z#yolo_cls_model_loader.predict_batchr   c                 C   s*   || _ | jr| dt| d dS dS )u   
        设置自定义类别名称
        Args:
            class_names: 类别名称字典，格式为 {class_id: class_name}
        u%   已设置自定义类别名称，共 u
    个类别N)r   r   r"   rC   )r   r   r   r   r   set_class_names  s   z%yolo_cls_model_loader.set_class_namesr}   c                 C   s<   | j dur|| j v r| j | S || jv r| j| S d| S )u   
        根据类别ID获取类别名称
        Args:
            class_id: 类别ID
        Returns:
            类别名称
        NrR   )r   r   )r   r}   r   r   r   rt   	  s
   



z$yolo_cls_model_loader.get_class_namerz   c                 C   s   |si S |d S )u   
        获取top-1分类结果
        Args:
            classification_results: 分类结果列表
        Returns:
            top-1结果字典
        r   r   )r   rz   r   r   r   get_top1_result  s   z%yolo_cls_model_loader.get_top1_result      ?min_confidencec                 C   s(   g }|D ]}|d |kr| | q|S )u   
        根据置信度过滤分类结果
        Args:
            classification_results: 分类结果列表
            min_confidence: 最小置信度阈值
        Returns:
            过滤后的结果列表
        rm   )rv   )r   rz   r   Zfiltered_resultsrx   r   r   r   filter_by_confidence'  s   	
z*yolo_cls_model_loader.filter_by_confidencec                 C   sn   |sdddddddS dd |D }t |tt|dtt|dtt|d|d d |d d	 d}|S )
u   
        获取分类结果的统计摘要
        Args:
            classification_results: 分类结果列表
        Returns:
            统计摘要信息
        r   g        u   无)u   总数u   最高置信度u   最低置信度u   平均置信度u   top1_类别u   top1_置信度c                 S   s   g | ]}|d  qS )rm   r   )rT   rx   r   r   r   
<listcomp>H  s    zDyolo_cls_model_loader.get_classification_summary.<locals>.<listcomp>rE   rl   rm   )rC   ru   r   minr`   r   )r   rz   ZconfidencesZsummaryr   r   r   get_classification_summary6  s"   	

	z0yolo_cls_model_loader.get_classification_summaryc              
   C   s   z>|  |}| jr-| jdkrt| jdr| j| n| jdkr-| jr-|| _| | jW S || _| j	r<| 
d| j  W dS  ty\ } z| j	rQ| 
d|  W Y d}~dS d}~ww )	u   
        设置计算设备
        Args:
            device: 设备名称
        Returns:
            是否设置成功
        r1   r=   r2   u   设备已切换到: Tu   设备切换失败: NF)r   r   r   rA   r   r=   r   r   r7   r   r"   r8   )r   r   Z
new_devicer;   r   r   r   
set_deviceU  s&   

z yolo_cls_model_loader.set_devicec                 C   s0   | j | j| j| j| j| jrt| jnd| jdS )uT   
        获取模型信息
        Returns:
            模型信息字典
        r   )u   模型路径u   模型格式u   是否已加载u   设备u   输入尺寸u   类别数量u   加载错误)r   r   r   r   r   r   rC   r   )r   r   r   r   get_model_infos  s   z$yolo_cls_model_loader.get_model_info)NT)N)rd   )r   )$__name__
__module____qualname____doc__r3   boolr   r"   r   tupler/   r<   r6   r7   rB   rN   r4   r`   Zndarrayrc   rr   r   r   ri   rg   rh   r   r   rt   r   rs   r   r   r   r   r   r   r   r   r   r
   )   s0    	0+;"1&3
 r
   )r   r_   ro   r`   typingr   r   r   r   r   timesysosZpathlibr   r(   Ztorchvision.transformsZ
transformsZPILr   Zultralyticsr	   r@   ImportErrorZonnxruntimerJ   rH   r
   r   r   r   r   <module>   s0   