
    ii                         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 	 ddlmZ dZ	 ddlZdZ G d	 d
      Zy# e$ r dZY w xY w# e$ r dZY !w xY w)uH   
YOLO分类模型工具类 - 支持pt和onnx格式的分类模型调用
    N)DictAnyOptionalListUnion)Path)Image)YOLOTFc            
          e Zd ZdZd(dedefdZdefdZd)dedefd	Zd
ede	eef   fdZ
d)d
ede	defdZd
edefdZd
edefdZd Zd Zd Zdej$                  dej$                  fdZdej$                  dej$                  fdZd*dej$                  dededee   fdZd+dej$                  dedee   fdZd*dej$                  dededee   fdZd+deej$                     dedeee      fdZdeeef   fdZdedefd Zd!ee   defd"Zd,d!ee   d#edee   fd$Z d!ee   defd%Z!dedefd&Z"deee#f   fd'Z$y)-yolo_cls_model_loaderu4   YOLO分类模型加载器类，支持pt和onnx格式Ndeviceverbosec                     d| _         d| _        | j                  |      | _        d| _        d| _        d| _        d| _        i | _        || _	        d| _
        d| _        d| _        d| _        g d| _        g d| _        y)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   s      .   D:\pyccd\极耳翻折\utils\model_cls_utils.py__init__zyolo_cls_model_loader.__init__,   s     
))&1$ $ $#   *	(    messagec                 4    | j                   rt        |       yy)uZ   
        内部日志输出方法
        Args:
            message: 日志消息
        N)r   print)r   r#   s     r    _logzyolo_cls_model_loader._logG   s     <<'N r"   returnc                     |dk(  ry|r|j                  d      r|S |r|j                         rd| S t        j                  j	                         ryy)u   
        自动选择设备
        Args:
            device: 指定设备，可选 'cpu', 'cuda', 'auto' 或具体的GPU编号如 '0', '1'
        Returns:
            选择的设备字符串
        cpucudazcuda:zcuda:0)
startswithisdigittorchr*   is_available)r   r   s     r    r   z$yolo_cls_model_loader._select_deviceP   sT     U?))&1M(6(## zz&&(r"   r   c                     |syt        |      }|j                         sdd| fS ddh}|j                  j                         |vrdd|j                   d| fS y)	u   
        验证模型路径和格式
        Args:
            model_path: 模型文件路径
        Returns:
            (是否有效, 错误信息)
        )Fu   模型路径不能为空Fu   模型文件不存在: z.ptz.onnx   不支持的模型格式: u   ，支持的格式: )T )r   existssuffixlower)r   r   supported_formatss      r    _validate_model_pathz*yolo_cls_model_loader._validate_model_pathe   s~     4*%
  "3J<@@@ #G,""$,==6z7H7H6II]^o]pqqqr"   r   c                    	 | j                  |      \  }}|s|| _        y| j                  rE| j                  t	        |      k(  r-| j
                  | j                  | j                  d|        y| j                          |r|| _	        t        |      }|j                  j                         j                  dd      | _        | j                  dk(  r | j                  |fi |S | j                  dk(  r | j                   |fi |S d	| j                   | _        y# t"        $ r8}d
t	        |       | _        | j                  | j                         Y d}~yd}~ww xY w)u  
        从路径加载YOLO分类模型
        Args:
            model_path: 模型文件路径
            input_size: 输入图像尺寸，格式为(height, width)，默认为(224, 224)
            **kwargs: 其他参数
        Returns:
            是否加载成功
        FNu    模型已加载，路径相同: T.r1   ptonnxr0   u   模型加载失败: )r6   r   r   r   strr   r   r&   unload_modelr   r   r3   r4   replacer   _load_pt_model_load_onnx_model	Exception)r   r   r   kwargsis_valid	error_msgmodel_path_objes           r    load_from_pathz$yolo_cls_model_loader.load_from_path{   sX   $	"&";";J"GHi(1% $$3z?2'4+;+;+G		<ZLIJ  ", "*-N . 5 5 ; ; = E Ec2 ND  D(*t**:@@@""f,,t,,ZB6BB*DTEVEVDW(X% 	&:3q6($CD!IId++,	s0   D AD 1A4D &!D D 	E&.EEc                    	 t         sd| _        y| j                  d|        | j                  d| j                          t	        |      | _        t        | j
                  d      r%| j
                  j                  | j                         t        |      | _	        | j                          d| _        d| _        | j                  d       | j                  d	| j                          | j                  d
| j                          | j                  d| j                  rt        | j                        nd        y# t         $ r8}dt        |       | _        | j                  | j                         Y d}~yd}~ww xY w)u   
        加载PyTorch格式的模型
        Args:
            model_path: 模型文件路径
            **kwargs: 其他参数
        Returns:
            是否加载成功
        u<   ultralytics库未安装，请安装: pip install ultralyticsFu   正在加载PyTorch模型: u   使用设备: toTNu   PyTorch模型加载成功:
     格式:      输入尺寸: u     类别数: Unknownu   PyTorch模型加载失败: )ULTRALYTICS_AVAILABLEr   r&   r   r
   r   hasattrrH   r;   r   _extract_pt_model_infor   r   r   r   lenr@   )r   r   rA   rE   s       r    r>   z$yolo_cls_model_loader._load_pt_model   s>    	((f%II3J<@AIIt{{m45j)DJ tzz4(

dkk* "*oDO'')#'D $(D!II24II
4#4#4"567II((9:;IIt?O?Oc$*:*:&;U^%_`a 	&A#a&$JD!IId++,	s   D> D-D> >	E?.E::E?c                    	 t         sd| _        y| j                  d|        dg}| j                  j	                  d      rOt
        j                  j                         r1|j                  dd       | j                  d| j                          n| j                  d	       t        j                  ||
      | _        | j                  j                         d   j                  | _        | j                  j                         d   j                  | _        | j                  j                         d   j"                  }t%        |      dk(  r|d   |d   f| _        t)        |      | _        | j-                          d| _        d| _        | j                  d       | j                  d| j0                          | j                  d| j&                          | j                  d| j                          | j                  d| j                           y# t2        $ r8}dt)        |       | _        | j                  | j                         Y d}~yd}~ww xY w)u   
        加载ONNX格式的模型
        Args:
            model_path: 模型文件路径
            **kwargs: 其他参数
        Returns:
            是否加载成功
        u\   onnxruntime库未安装，请安装: pip install onnxruntime 或 pip install onnxruntime-gpuFu   正在加载ONNX模型: CPUExecutionProviderr*   r   CUDAExecutionProvideru   使用GPU设备: u   使用CPU设备)	providers         TNu   ONNX模型加载成功:rI   rJ   u     输入名称: u     输出名称: u   ONNX模型加载失败: )ONNXRUNTIME_AVAILABLEr   r&   r   r+   r-   r*   r.   insertortInferenceSessionr   
get_inputsnamer   get_outputsr   shaperO   r   r;   r   _extract_onnx_model_infor   r   r@   )r   r   rA   rS   input_shaperE   s         r    r?   z&yolo_cls_model_loader._load_onnx_model   s   0	( )G%II0=> 00I{{%%f-%**2I2I2K  $;<		-dkk];<		+,  #33# D #..99;A>CCDO#//;;=a@EED **557:@@K;1$#.q>;q>"B!*oDO))+#'D $(D!II/1II
4#4#4"567II((9:;II((9:;II()9)9(:;< 	&>s1vh$GD!IId++,	s   H HH 	I.IIc                    	 t        | j                  d      r| j                  j                  | _        yt	        d      D ci c]  }|d| 
 c}| _        yc c}w # t
        $ r'}| j                  d|        ddi| _        Y d}~yd}~ww xY w)u   提取PyTorch模型信息names  Classu$   提取PyTorch模型信息时出错: r   Class0N)rM   r   rb   r   ranger@   r&   )r   irE   s      r    rN   z,yolo_cls_model_loader._extract_pt_model_info  s    	-tzz7+#'::#3#3  =B$K#HKqAqc{NK#H #H 	-II<QC@A !8}D	-s.   1A A AA A 	B%BBc                 ~   	 | j                   j                         d   j                  }t        |      dk\  r(|d   }t	        |      D ci c]  }|d| 
 c}| _        yt	        d      D ci c]  }|d| 
 c}| _        yc c}w c c}w # t        $ r'}| j                  d|        ddi| _        Y d}~yd}~ww xY w)	u   提取ONNX模型信息r   rU   rd   rc   u!   提取ONNX模型信息时出错: re   N)r   r]   r^   rO   rf   r   r@   r&   )r   output_shapenum_classesrg   rE   s        r    r_   z.yolo_cls_model_loader._extract_onnx_model_info  s    
	-++779!<BBL< A%*2.<A+<N#O<NqAqc{N<N#O <A$K#HKqAqc{NK#H  $P#H 	-II9!=> !8}D	-s<   AB 	BB B ,B9B 
B 	B<B77B<c                 r   	 | j                   	| ` d| _         | j                  	| `d| _        d| _        d| _        d| _        t
        j                  j                         rt
        j                  j                          | j                  d       y# t        $ r}| j                  d|        Y d}~yd}~ww xY w)u   卸载模型，释放内存NFu   模型已卸载u   模型卸载时出错: )r   r   r   r   r   r-   r*   r.   empty_cacher&   r@   )r   rE   s     r    r<   z"yolo_cls_model_loader.unload_model+  s    	5zz%J!
+$#' #(D "DO$(D! zz&&(

&&(II'( 	5II/s344	5s   BB 	B6B11B6imagec                 x   	 t        |j                        dk(  r7|j                  d   dk(  r%t        j                  |t        j                        }n?t        |j                        dk(  r%t        j                  |t        j
                        }n|}t        j                  || j                  t        j                        }|j                  t        j                        dz  }t        j                  | j                  t        j                        }t        j                  | j                  t        j                        }||z
  |z  }t        j                  |d      }t        j                   |d      j                  t        j                        }|S # t"        $ r}	t%        d	t'        |	             d
}	~	ww xY w)u   
        预处理图像用于分类推理 - 与YOLO官方预处理保持一致
        Args:
            image: 输入图像 (BGR格式)
        Returns:
            预处理后的图像数组
        rV   rU   )interpolation     o@)dtyperU   r      r   axisu   图像预处理失败: N)rO   r^   cv2cvtColorCOLOR_BGR2RGBCOLOR_GRAY2RGBresizer   INTER_LINEARastypenpfloat32arrayr   r   	transposeexpand_dimsr@   RuntimeErrorr;   )
r   rn   	image_rgbimage_resizedimage_normalizedr   r   image_tensorimage_batchrE   s
             r    preprocess_imagez&yolo_cls_model_loader.preprocess_imageC  sH   	C5;;1$Q1)<LL0A0AB	U[[!Q&LL0B0BC	!	  JJy$//QTQaQabM  -33BJJ?%G 88DIIRZZ8D((4882::6C 04 73> <<(8)DL..A>EEbjjQK 	C!8QABB	Cs   FF 	F9F44F9c                    	 |j                         }| j                  \  }}t        j                  |||f      }t        j                  |t        j
                        }|j                  t        j                        dz  }t        j                  |d      }t        j                  |d      }	|	S # t        $ r}
t        dt        |
             d}
~
ww xY w)u   
        使用YOLO风格的预处理（与官方导出保持完全一致）
        Args:
            image: 输入图像 (BGR格式)
        Returns:
            预处理后的图像数组
        rq   rs   r   ru   u   YOLO风格预处理失败: N)copyr   rw   r{   rx   ry   r}   r~   r   r   r   r@   r   r;   )r   rn   original_imagetarget_htarget_wresized	rgb_image
normalized
transposedbatchedrE   s              r    preprocess_image_yolo_stylez1yolo_cls_model_loader.preprocess_image_yolo_stylek  s    	G"ZZ\N "&Hh jj(H1EFG Wc.?.?@I #))"**5=J j)<J nnZa8GN 	G!<SVHEFF	Gs   B+B. .	C7CCtop_kuse_yolo_preprocessc                    | j                   st        d      t        j                         }	 | j                  j                         dz   }d| j                  j                          }t        d| d| d       | j                  dk(  r | j                  ||fi |}n=| j                  dk(  r | j                  |||fi |}nt        d	| j                         t        j                         }	|	|z
  }
t        d
|
dd       |S # t        $ r;}dt        |       }| j                  r| j                  |       t        |      d}~ww xY w)u_  
        使用加载的分类模型进行预测
        Args:
            image: 输入图像 (BGR格式)
            top_k: 返回top-k个分类结果，默认为5
            use_yolo_preprocess: ONNX模型是否使用YOLO风格预处理，默认True
            **kwargs: 其他预测参数
        Returns:
            分类结果列表
           模型未加载u   模型u   设备: u   🚀 推理信息: z | u    | 全局置信度模式r9   r:   r0   u   ⚡ 推理耗时: .4fu   秒u   分类预测失败: N)r   r   timeperf_counterr   upperr   r%   _predict_pt_predict_onnxr@   r;   r   r&   )r   rn   r   r   rA   
start_time
model_typedevice_inforesultend_timeinference_timerE   rC   s                r    predictzyolo_cls_model_loader.predict  s^    ##011 &&(
	***002X=J$T[[%6%6%8$9:K '
|3{mC[\]   D()))%A&A""f,+++E5:MXQWX"%?@Q@Q?R#STT ((*H%
2N &~c&:#>?M 	*.s1vh7I||		)$y))		*s   CD 	E	6EE	c                 J   	  | j                   |fi |}|rt        |      dk(  rg S |d   }t        |d      r|j                  |j                  j                  j                         j                         }t        j                  |      ddd   d| }g }t        |      D ]U  \  }	}
t        |
      }t        ||
         }| j                  |      }||t        |d      |	dz   |	d}|j                  |       W |S g S # t        $ r}t!        dt#        |             d}~ww xY w)	u   
        使用PyTorch模型进行预测
        Args:
            image: 输入图像
            top_k: 返回top-k个结果
            **kwargs: 其他参数
        Returns:
            分类结果列表
        r   probsNri   rT   rt   u   类型编号   类型字符   分类置信度u   置信度排名u   索引u   PyTorch模型预测失败: )r   rO   rM   r   datar)   numpyr~   argsort	enumerateintfloatget_class_nameroundappendr@   r   r;   )r   rn   r   rA   resultsr   r   top_indicesclassification_resultsrankidxclass_id
confidence
class_nameresult_dictrE   s                   r    r   z!yolo_cls_model_loader._predict_pt  s8   %	G djj1&1Gc'la/	QZF vw'FLL,D))--/557 !jj/"5fu=)+&!*;!7ID#"3xH!&uSz!2J!%!4!4X!>J )1(2+0Q+?+/!8"&#K +11+> "8 .-	 	G!<SVHEFF	Gs#   $C= CC= ;C= =	D"DD"c                 &   	 |r| j                  |      }n| j                  |      }| j                  r`t        d|j                          t        d|j
                          t        d|j                         dd|j                         dd       | j                  j                  | j                  g| j                  |i      }| j                  r/t        d|d   j                          t        d	|d   d           |d   d   }t        j                  |t        j                  |      z
        }|t        j                  |      z  }	| j                  rt        d
|	        t        j                  |	      ddd   d| }
g }t!        |
      D ]U  \  }}t#        |      }t%        |	|         }| j'                  |      }||t)        |d      |dz   |d}|j+                  |       W |S # t,        $ r}t/        dt1        |             d}~ww xY w)u%  
        使用ONNX模型进行预测
        Args:
            image: 输入图像
            top_k: 返回top-k个结果
            use_yolo_preprocess: 是否使用YOLO风格预处理，默认True
            **kwargs: 其他参数
        Returns:
            分类结果列表
        u   输入张量形状: u   输入张量数据类型: u   输入张量值范围: [r   z, ]u   输出形状: r   u   原始logits: u   Softmax概率: Nri   rT   rt   r   u   ONNX模型预测失败: )r   r   r   r%   r^   rr   minmaxr   runr   r   r~   expsumr   r   r   r   r   r   r   r@   r   r;   )r   rn   r   r   rA   input_tensoroutputslogits
exp_logitsr   r   r   r   r   r   r   r   r   rE   s                      r    r   z#yolo_cls_model_loader._predict_onnx  s   8	D"#??F#44U; ||,\-?-?,@AB2<3E3E2FGH01A1A1CC0H<K[K[K]^aJbbcde &&**!!",/G ||wqz'7'7&89:wqz!}o67 QZ]F  78J
!33E||w/0 **U+DbD1&59K%'"&{3	cs8"5:.
!00:
 %-$.',Z';'+ax" '--k: 4 *) 	D!9#a&BCC	Ds   G(G+ +	H4HHimagesc           	         | j                   st        d      	 g }t        |      D ]Y  \  }}| j                  r#| j	                  d|dz    dt        |               | j                  ||fi |}|j                  |       [ |S # t        $ r;}dt        |       }	| j                  r| j	                  |	       t        |	      d}~ww xY w)u   
        批量分类预测
        Args:
            images: 图像列表
            top_k: 返回top-k个结果
            **kwargs: 预测参数
        Returns:
            每张图像的分类结果列表
        r   u   处理图像 rt   /u   批量分类预测失败: N)
r   r   r   r   r&   rO   r   r   r@   r;   )
r   r   r   rA   r   rg   rn   r   rE   rC   s
             r    predict_batchz#yolo_cls_model_loader.predict_batch5  s     ##011	*G%f-5<<IIacU!CK=AB%eU=f=v&	 .
 N 	*4SVH=I||		)$y))		*s   A*B 	C6CCr   c                 h    || _         | j                  r| j                  dt        |       d       yy)u   
        设置自定义类别名称
        Args:
            class_names: 类别名称字典，格式为 {class_id: class_name}
        u%   已设置自定义类别名称，共 u
    个类别N)r   r   r&   rO   )r   r   s     r    set_class_namesz%yolo_cls_model_loader.set_class_namesP  s4      +<<II=c+>N=OzZ[ r"   r   c                     | j                   || j                   v r| j                   |   S || j                  v r| j                  |   S d| S )u   
        根据类别ID获取类别名称
        Args:
            class_id: 类别ID
        Returns:
            类别名称
        rd   )r   r   )r   r   s     r    r   z$yolo_cls_model_loader.get_class_nameZ  sZ     +D<P<P0P''11t'''##H--xj!!r"   r   c                     |si S |d   S )u   
        获取top-1分类结果
        Args:
            classification_results: 分类结果列表
        Returns:
            top-1结果字典
        r    )r   r   s     r    get_top1_resultz%yolo_cls_model_loader.get_top1_resultj  s     &I &a((r"   min_confidencec                 L    g }|D ]  }|d   |k\  s|j                  |        |S )u   
        根据置信度过滤分类结果
        Args:
            classification_results: 分类结果列表
            min_confidence: 最小置信度阈值
        Returns:
            过滤后的结果列表
        r   )r   )r   r   r   filtered_resultsr   s        r    filter_by_confidencez*yolo_cls_model_loader.filter_by_confidencex  s8     ,F'(N: ''/ -  r"   c                    |s	dddddddS |D cg c]  }|d   	 }}t        |      t        t        |      d      t        t        |      d      t        t	        j
                  |      d      |d   d   |d   d   d}|S c c}w )u   
        获取分类结果的统计摘要
        Args:
            classification_results: 分类结果列表
        Returns:
            统计摘要信息
        r   g        u   无)u   总数u   最高置信度u   最低置信度u   平均置信度u   top1_类别u   top1_置信度r   rT   r   )rO   r   r   r   r~   r   )r   r   r   confidencessummarys        r    get_classification_summaryz0yolo_cls_model_loader.get_classification_summary  s     &#&#&#&$"%  @VV?UVv/0?UV 01$S%5q9$S%5q9$RWW[%91=1!4^D4Q78IJ
  Ws   Bc                 
   	 | j                  |      }| j                  r~| j                  dk(  r2t        | j                  d      r| j                  j                  |       n=| j                  dk(  r.| j                  r"|| _        | j                  | j                        S || _        | j                  r| j                  d| j                          y# t        $ r*}| j                  r| j                  d|        Y d}~yd}~ww xY w)	u   
        设置计算设备
        Args:
            device: 设备名称
        Returns:
            是否设置成功
        r9   rH   r:   u   设备已切换到: Tu   设备切换失败: NF)r   r   r   rM   r   rH   r   r   r?   r   r&   r@   )r   r   
new_devicerE   s       r    
set_devicez yolo_cls_model_loader.set_device  s    	,,V4J##$$,T1JJJMM*-&&&0&0#44T__EE$DK||		0>? 	||		045	s   BC 1C 	D C==Dc                     | 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   rO   r   )r   s    r    get_model_infoz$yolo_cls_model_loader.get_model_info  sV     !OO --#33kk OO595E5EC 0 011 11
 	
r"   )NF)N)   T)r   )g      ?)%__name__
__module____qualname____doc__r;   boolr!   r&   r   tupler6   rF   r>   r?   rN   r_   r<   r~   ndarrayr   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r"   r    r   r   )   sE   >)s )D )6C S C *s uT3Y7G ,. .% .UY .`) )4 )V93 9T 9v
--50&Cbjj &CRZZ &CP"G "G

 "GH.*RZZ .* .*d .*hlmqhr .*`/G /GC /G$t* /GbCD2:: CDc CDTX CDnrswnx CDJ*D$4 *S *SWX\]aXbSc *6\4S> \"s "s " )d4j )T ) 4:  W\  gklpgq  d PT >  <
S#X 
r"   r   )r   rw   r   r~   typingr   r   r   r   r   r   sysospathlibr   r-   torchvision.transforms
transformsPILr	   ultralyticsr
   rL   ImportErroronnxruntimerY   rW   r   r   r"   r    <module>r      s      3 3  
 	   + "  " i

 i

)  "!"  "!"s#   A A# A A #A-,A-