o
    <i#%                    @   s6  d Z ddlZddlZddlZddl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mZ ddlZddlZddlZddlZddlmZ ddlmZ ddlZddlmZmZ ddlZddl	Z	z
ddlmZ d	ZW n eyu   d
ZY nw G dd dZddedede de!de"f
ddZ#ddede de!fddZ$dS )u0   
PyCV基础类 - 图像处理功能的基础类
    N)DictAnyOptional)Path)datetime)ImageThreadPoolExecutoras_completedYOLOTFc                   @   s  e Zd ZdZddedefddZdefd	d
ZddedefddZdede	eef fddZ
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edefddZdd Zdd Zdd Zdedefd d!Zdded#efd$d%Zd&edefd'd(Zdded)edefd*d+Zd&efd,d-Zd.d/ Zdedefd0d1Zdd#efd3dZdedefd4d5Zdded7edefd8d9Zdedefd:d;Zd<d= Zd>d? Zde ee!f fd@dAZ"ddBe#j$dCede#j$fdDdEZ%dBe#j$de!fdFdGZ&dBe#j$de!fdHdIZ'dBe#j$dJe(dKede!fdLdMZ)dBe#j$dJe(fdNdOZ*dBe#j$dJe(fdPdQZ+ddBe#j$dCede#j$fdSdTZ,dUdV Z-dWdX Z.dYedefdZd[Z/dd]e#j$d^ede0fd_d`Z1daedefdbdcZ2dde0defdedfZ3dde0defdgdhZ4de(fdidjZ5dkdl Z6defdmdnZ7dedefdodpZ8defdqdrZ9dsedefdtduZ:defdvdwZ;dxede(fdydzZ<ddxed|e0defd}d~Z=ded|e0defddZ>de(de(de0fddZ?dede(fddZ@ddxed|e0de	eef fddZAdBe#j$dede#j$fddZBdBe#j$defddZCdde(dedefddZDde(de(de(fddZEdde#j$de(de(dede#j$de(fddZFddBe#j$de(dede0ddf
ddZGdsede(fddZHde	dedede	fddZIde(de(fddZJdBe#j$dede(fddZKddBe#j$dede(de0fddZLdBe#j$dede(fddZMdBe#j$dede(fddZNdBe#j$dede(fddZOddBe#j$dede(de0fddZPddBe#j$dede(de0fddZQde(de(fddZJdBe#j$de	de	de	def
ddƄZRdBe#j$de(de(fddȄZSdBe#j$dede	de(fdd̄ZTdde(de(dede(fdd҄ZUdS )yolo_model_loderu:   YOLO模型加载器类，使用纯ONNX Runtime进行推理NTdeviceverbosec                 C   sz   d| _ d| _| || _d| _d| _d| _d| _d| _ddi| _	|| _
d| _d| _g | _d| _d| _d| _d| _i | _dS )	u   
        初始化模型加载器
        Args:
            device: 设备选择，可选 'cpu', 'cuda', 'auto'。默认为自动选择
            verbose: 是否显示详细日志信息，默认为True
        NFdetectonnx  r   Class0      ?)session
model_path_select_devicer   is_model_loadedmodel_load_error
model_typemodel_format
input_sizeclass_namesr   usr_class_name
input_nameoutput_namesinput_shapeis_warmed_upuse_independent_confglobal_confclass_conf_thresholds)selfr   r    r'   funcs\utils\model_utils.py__init__=   s$   

zyolo_model_loder.__init__messagec                 C   s   | j r	t| dS dS )uZ   
        内部日志输出方法
        Args:
            message: 日志消息
        N)r   print)r&   r*   r'   r'   r(   _log[   s   zyolo_model_loder._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cudacuda:cuda:0)
startswithisdigittorchr/   is_available)r&   r   r'   r'   r(   r   d   s   

zyolo_model_loder._select_devicer   c                 C   sV   |sdS t |}| sdd| fS h d}|j |vr)dd|j d| fS dS )u   
        验证模型路径和格式
        Args:
            model_path: 模型文件路径
        Returns:
            (是否有效, 错误信息)
        )Fu   模型路径不能为空Fu   模型文件不存在: >   .ptz.engine.onnxz.torchscript   不支持的模型格式: u   ，支持的格式: )T )r   existssuffixlower)r&   r   Zsupported_formatsr'   r'   r(   _validate_model_pathy   s   z%yolo_model_loder._validate_model_pathc              
   K   s&  zk|  |\}}|s|| _W dS | jr%| jt|kr%| d|  W dS |   t|j	 }| d|  | d|  |dkrN| j
|fi |W S |dkr\| j|fi |W S d| d	| _| | j W dS  ty } zd
t| | _| | j d| _d| _W Y d}~dS d}~ww )ub  
        从路径加载YOLO模型，根据文件后缀选择加载方式
        - .onnx 文件使用 onnxruntime 直接加载
        - .pt 文件使用 ultralytics YOLO 加载
        Args:
            model_path: 模型文件路径
            **kwargs: 其他参数，如 imgsz, conf, iou 等
        Returns:
            是否加载成功
        Fu    模型已加载，路径相同: T   正在加载模型:    检测到文件格式: r7   r6   r8      ，仅支持 .onnx 和 .pt   模型加载失败: N)r=   r   r   r   strr,   unload_modelr   r;   r<   _load_onnx_model_load_pytorch_model	Exceptionr   )r&   r   kwargsis_valid	error_msgfile_exter'   r'   r(   load_from_path   s8   zyolo_model_loder.load_from_pathc              
   K   s  zaddl }| d | | j}t }|j||d| _t | }|   t|| _	d| _
d| _d| _| d|dd	 | d
| j  | d| j  | dt| j  |   W dS  ty } zdt| | _| | j d| _d| _d| _	W Y d}~dS d}~ww )u(   使用ONNX Runtime直接加载ONNX模型r   Nu3   🔄 ONNX模型：使用ONNX Runtime直接加载...)	providersr   Tu%   ✅ ONNX模型加载完成，耗时: .2f   秒u     输入名称: u     输入形状: u     输出数量: u   ONNX模型加载失败: F)onnxruntimer,   _get_onnx_providersr   timeZInferenceSessionr   _extract_onnx_model_inforB   r   r   r   r   r   r!   lenr    _warmup_onnx_modelrF   )r&   r   rG   ortrM   
start_time	load_timerK   r'   r'   r(   rD      s6   

z!yolo_model_loder._load_onnx_modelc              
   K   sx  zt s	d| _W dS | d ddlm} t }||dd| _t | }t| jdr2| j| j	 t
|| _d	| _z|   W n% tye } z| d
|  d| _d| _ddi| _W Y d}~nd}~ww d| _d| _| d|dd | d| j  | d| j  | d| jrt| jnd  W dS  ty } zdt
| | _| | j W Y d}~dS d}~ww )u)   使用ultralytics YOLO加载PyTorch模型<   ultralytics库未安装，请安装: pip install ultralyticsFu4   🔄 PyTorch模型：使用ultralytics YOLO加载...r   r   r   )tasktoptu:   ⚠️ 提取PyTorch模型信息失败，使用默认值: r   r   NTu(   ✅ PyTorch模型加载完成，耗时: rN   rO   u
     类型: u     输入尺寸: u     类别数: Unknownu   PyTorch模型加载失败: )ULTRALYTICS_AVAILABLEr   r,   ultralyticsr   rR   modelhasattrr[   r   rB   r   r   _extract_pytorch_model_inforF   r   r   r   r   rT   )r&   r   rG   r   rW   rX   rK   r'   r'   r(   rE      sF   

 z$yolo_model_loder._load_pytorch_modeltarget_devicec              
   C   sN  z|dkrddddfg}|  d |W S tj s%|  d | dW S d}d	|v r=zt|d	d
 }W n   d}Y |tj krO|  d| d d}|dddd}d|fdg}|  d|  tj rtj|}tj	|j
d }|  d| d|dd |W S  ty } z|  d|  di fgW  Y d}~S d}~ww )u'   获取ONNX Runtime执行提供者配置r.   CPUExecutionProvider      Zintra_op_num_threadsZinter_op_num_threadsu"   🔧 配置ONNX CPU执行提供者#   ⚠️ CUDA不可用，回退到CPUr   :      ⚠️ GPU     不存在，使用GPU 0ZkSameAsRequestedZ	HEURISTICT	device_idarena_extend_strategycudnn_conv_algo_searchdo_copy_in_default_streamCUDAExecutionProvideru)   🔧 配置ONNX CUDA执行提供者: GPU=   @u      GPU设备:  (.1fGB)u.   ⚠️ 配置ONNX提供者失败，使用CPU: N)r,   r4   r/   r5   rQ   intsplitdevice_countget_device_nameget_device_propertiestotal_memoryrF   )r&   rc   rM   gpu_idcuda_provider_optionsgpu_name
gpu_memoryrK   r'   r'   r(   rQ     sN   

*


z$yolo_model_loder._get_onnx_providersc                 C   sr   | j sdS | j  d }|j| _|j| _| jr$t| jdkr$| jd | _dd | j  D | _	ddi| _
d| _dS )	u   提取ONNX模型信息Nr   re   c                 S   s   g | ]}|j qS r'   )name).0outputr'   r'   r(   
<listcomp>a  s    z=yolo_model_loder._extract_onnx_model_info.<locals>.<listcomp>r   r   )r   Z
get_inputsr   r   shaper!   rT   r   Zget_outputsr    r   r   )r&   Z
input_infor'   r'   r(   rS   R  s   

z)yolo_model_loder._extract_onnx_model_infoc                 C   s   | j sdS t| j dr;t| j j drt| j j jdd| _nt| j j dr;| j j j}t|tr8d|v r8|d | _nd| _t| j drG| j j| _	nt| j drZt| j j drZ| j j j| _	d| _
dS )	u   提取PyTorch模型信息Nr`   argsimgszr   yamlnamesr   )r`   ra   getattrr   r   r   
isinstancedictr   r   r   )r&   Z	yaml_dictr'   r'   r(   rb   g  s   

z,yolo_model_loder._extract_pytorch_model_infoc              
   C   s   | j r| jrdS zA| jrGg }| jD ]}t|ts|dkr"|d q|| qtjj| 	tj
}| j d| j|i d| _| d W dS W dS  tyd } z| d|  W Y d}~dS d}~ww )u   预热ONNX模型Nr   rj   Tu   🔥 ONNX模型预热完成   ⚠️ ONNX模型预热失败: )r   r"   r!   r   rB   appendnprandomZrandastypefloat32runr   r,   rF   )r&   r   dimdummy_inputrK   r'   r'   r(   rU     s$   
z#yolo_model_loder._warmup_onnx_modelc           
   
   C   s  zddl }|dkrddd}d|fg}| jrtd |W S tj s/| jr)td	 | dW S d}d
|v rGzt|d
d }W n   d}Y |tj	 kr[| jrYtd| d d}|dddd}d|fdg}| jrtd|  td|  tj rtj
|}tj|jd }td| d|dd |W S  ty   | jrtd Y dS  ty }	 z| jrtdt|	  W Y d}	~	dS d}	~	ww )uW   为指定设备优化ONNX Runtime执行提供者配置 - 按测试程序的可靠模式r   Nr.   re   rf   rg   rd   u"   🚀 配置ONNX CPU执行提供者rh   ri   rj   rk   rl   kNextPowerOfTwo
EXHAUSTIVETrm   rr   u)   🚀 配置ONNX CUDA执行提供者: GPU=u      CUDA提供者device_id: rs   u      GPU信息: rt   ru   rv   3   ⚠️ 无法导入onnxruntime，使用默认配置u(   ⚠️ 配置ONNX执行提供者失败: )rP   r   r+   r4   r/   r5   #_optimize_onnx_providers_for_devicerw   rx   ry   rz   r{   r|   ImportErrorrF   rB   )
r&   rc   rV   cpu_provider_optionsrM   r}   r~   r   r   rK   r'   r'   r(   r     sd   
.

z4yolo_model_loder._optimize_onnx_providers_for_devicerf   warmup_runsc              
   C   sJ  z| j r{t| j dr~| jrtd| d tjjdddtjd}g }t|D ],}t	
 }| j j|ddd	|d
}t	
 | }|| |dkrR| jrRtd|dd q&t|dkrut|dd t|dd  }	| jrutd|	dd d| _W dS W dS W dS  ty }
 z| jrtd|
  W Y d}
~
dS W Y d}
~
dS d}
~
ww )u"   在指定设备上预热ONNX模型predictu   🔥 在设备 u    上预热ONNX模型...r         r      dtypeF      ?r   saveconfr         首次预热: .3frO   rj   Nu%   ✅ ONNX预热完成，平均时间: Tr   )r`   ra   r   r+   r   r   randintuint8rangerR   perf_counterr   r   rT   sumr"   rF   )r&   rc   r   r   warmup_timesirW   _warmup_timeavg_warmup_timerK   r'   r'   r(   _warmup_onnx_model_on_device  sB   
 "z-yolo_model_loder._warmup_onnx_model_on_deviceexpected_devicec           
   
   C   s   zP| j sW dS tjjdddtjd}t }| j j|dd|d}t | }|du}| jrN|dkr3d	nd
| d}|r=dnd}t	d| d| d|dd |W S  t
yo }	 z| jrdt	dt|	  W Y d}	~	dS d}	~	ww )u.   验证ONNX模型是否在期望设备上运行Fr   r   r   r   )r   r   r   Nr.   CPUzGPU ()u   ✅ 运行正常u   ❌ 运行异常u   🔍 ONNX设备验证:  - u
   , 耗时: r   rO   u   ❌ ONNX设备验证失败: )r`   r   r   r   r   rR   r   r   r   r+   rF   rB   )
r&   r   Z
test_inputrW   resultinference_timesuccessZdevice_typestatusrK   r'   r'   r(   _verify_onnx_device_usage
  s0   z*yolo_model_loder._verify_onnx_device_usageforce_devicec              
   K   s"  zgt s
d| _W dS | |\}}|s|| _W dS | j}| j}|dur| dkr1d}| d nt| dkrLtj rDd}| d na| d	 d}nY| 	d
r|
dd }tj r|t|tj k r|| }| d|  td|  n)| d| d d}n| dkr| d}| d|  n	| d| d || _| jr| jt|kr| j|kr| jdur| d| d|  W dS |   | d|  | d|  t|j }	| d|	  |	dkr| j|fi |}
n|	dkr
| j|fi |}
nd|	 d| _| | j W dS |
s#|| _W dS | d | d| j  | d | j  | d!| j  | d"| j  | d#| j  | d$| jrat| jnd%  W dS  ty } z|| _d&t| | _| d'| j  W Y d}~dS d}~ww )(u(  
        从路径加载YOLO模型，支持强制指定设备
        Args:
            model_path: 模型文件路径
            force_device: 强制指定设备，可选值：
                - 'cpu': 强制使用CPU
                - 'cuda': 使用第一个GPU
                - 'cuda:0', 'cuda:1': 指定具体GPU
                - 'auto': 自动选择（默认行为）
                - None: 使用初始化时的设备设置
            **kwargs: 其他参数，如 imgsz, conf, iou 等
        Returns:
            是否加载成功
        rY   FNr.   u   🔧 强制使用CPU设备r/   r1   u   🔧 强制使用GPU: cuda:0rh   r0   ri   rj   u   🔧 强制使用GPU: rk   u    不可用，回退到CPUautou   🔧 自动选择设备: u   ⚠️ 无效的设备参数 'u   '，使用默认设备u)   模型已加载，路径和设备相同:  -> Tr>   u   目标设备: r?   r7   r6   r8   r@   u   ✅ 模型加载成功:u      路径: u      设备: u      类型: u      格式: u      输入尺寸: u      类别数: r]   rA      ❌ )r^   r   r=   r   r<   r,   r4   r/   r5   r2   rx   rw   ry   r+   r   r   r   rB   r`   rC   r   r;   rD   rE   r   r   r   r   rT   rF   )r&   r   r   rG   rH   rI   Zoriginal_devicerc   r}   rJ   r   rK   r'   r'   r(   load_from_path_with_device.  s   






"z+yolo_model_loder.load_from_path_with_devicec              
   C   s  zf| j rat| j drdt| j j  j}|dkr/|jdkr$| d W dS | d|  W dS d|v r<t|dd nd}|jdkrS|j	|krS| d	|  W dS | d
| d|  W dS W dS W dS  t
y } z| d|  W Y d}~dS d}~ww )u}   
        验证模型是否正确放置在指定设备上
        Args:
            expected_device: 期望的设备
        r`   r.   u   ✅ 模型已正确放置在CPUu   ⚠️ 预期CPU但模型在 ri   rj   r   r/   u   ✅ 模型已正确放置在 u   ⚠️ 预期 u    但模型在 u!   ⚠️ 无法验证设备放置: N)r`   ra   next
parametersr   typer,   rw   rx   indexrF   )r&   r   model_deviceZexpected_cuda_idrK   r'   r'   r(   _verify_device_placement  s    
z)yolo_model_loder._verify_device_placementc              
   C   s   z2ddl }d|jd< d|jd< d|jd< d|jd< d|jd	< d|jd
< d|jd< | d d| _W dS  tyM } z| d|  W Y d}~dS d}~ww )u   设置ONNX Runtime优化选项r   N0ZORT_DISABLE_ALL_OPTIMIZATION1ZORT_ENABLE_BASIC_OPTIMIZATIONZ ORT_ENABLE_EXTENDED_OPTIMIZATIONZCUDA_LAUNCH_BLOCKINGZCUDA_CACHE_DISABLEZORT_CUDA_GRAPH_CAPTURE3ZORT_LOG_SEVERITY_LEVELu%   ✅ ONNX Runtime优化选项已设置Tu%   ⚠️ 设置ONNX优化选项失败: )osenvironr,   Zonnx_optimizedrF   )r&   r   rK   r'   r'   r(   _set_onnx_optimization_options  s   







z/yolo_model_loder._set_onnx_optimization_optionsc                 C   s   zGddl }|dkr6tj r6d|v rt|dd nddddd	d
}d|fdg}| d|d   |W S ddd}d|fg}| d |W S  tyV   | d Y dS w )u'   优化ONNX Runtime执行提供者配置r   Nr.   ri   rj   r   l        r   T)rn   ro   Zgpu_mem_limitrp   rq   rr   rd   u$   🚀 配置CUDA执行提供者: GPU=rn   re   rf   rg   u   🚀 配置CPU执行提供者r   )rP   r4   r/   r5   rw   rx   r,   r   )r&   rc   rV   r~   rM   r   r'   r'   r(   _optimize_onnx_providers  s2   	

z)yolo_model_loder._optimize_onnx_providersr   c           
   
   C   s$  zv| j rqt| j drt| d| d tjjdddtjd}g }t|D ]+}t	 }| j j
|ddd	| jd
}t	 | }|| |dkrO| d|dd q$t|dd tdt|d  }| d|dd d| _W dS W dS W dS  ty }	 z| d|	  W Y d}	~	dS d}	~	ww )u+   预热ONNX模型以提高后续推理速度r   u   🔥 开始预热ONNX模型 (u   次)r   r   r   r   Fr   r   r   r   rO   rj   Nu.   ✅ ONNX预热完成，优化后平均时间: Tr   )r`   ra   r,   r   r   r   r   r   rR   r   r   r   r   r   maxrT   r"   rF   )
r&   r   r   r   r   rW   r   r   r   rK   r'   r'   r(   rU     s6   
"c                 K   s   | j |fddi|S )u   
        强制加载模型到CPU的便捷方法
        Args:
            model_path: 模型文件路径
            **kwargs: 其他参数
        Returns:
            是否加载成功
        r   r.   r   )r&   r   rG   r'   r'   r(   load_cpu  s   	zyolo_model_loder.load_cpur   r}   c                 K   s   | j |fdd| i|S )u   
        强制加载模型到指定GPU的便捷方法
        Args:
            model_path: 模型文件路径
            gpu_id: GPU编号，默认为0
            **kwargs: 其他参数
        Returns:
            是否加载成功
        r   r0   r   )r&   r   r}   rG   r'   r'   r(   load_gpu'  s   
zyolo_model_loder.load_gpuc                 C   s6   | j r| jsd| _dS | d|  | j| j|dS )u   
        将已加载的模型重新加载到指定设备
        Args:
            target_device: 目标设备 ('cpu', 'cuda:0', 'cuda:1', 等)
        Returns:
            是否重新加载成功
        u*   没有已加载的模型可以重新加载Fu"   🔄 重新加载模型到设备: )r   )r   r   r   r,   r   )r&   rc   r'   r'   r(   reload_to_device3  s
   z!yolo_model_loder.reload_to_devicec              
   C   s   zC| j rAt| j dr| j j| _nd| _t| j dr| j j| _nd| _t| j dr.| j j| _nd| _| d| j d| j  W dS W dS  t	yg } z| d	|  d| _d| _d| _W Y d}~dS d}~ww )
u!   提取模型信息，安全版本rZ   r   r   r   r   Nu!   模型信息提取成功: 类型=u	   , 尺寸=u   提取模型信息时出错: )
r`   ra   rZ   r   r   r   r   r   r,   rF   r&   rK   r'   r'   r(   _extract_model_infoB  s(    z$yolo_model_loder._extract_model_infoc              
   C   sD  zzH| j dur| ` d| _ | d t| dr0| jdur0t| jdr&| j  | `d| _| d tj r:tj  d| _	d| _
d| _| d W n tyc } z| d|  W Y d}~nd}~ww W d| _ t| drpd| _d| _	d| _
d| _d| _d| _d| _dS d| _ t| drd| _d| _	d| _
d| _d| _d| _d| _w )	u   卸载当前模型Nu   ONNX会话已卸载r`   r.   u   PyTorch模型已卸载Fu   模型卸载完成u   卸载模型时出错: )r   r,   ra   r`   r.   r4   r/   r5   Zempty_cacher   r   r"   rF   r   r   r   r   r   r'   r'   r(   rC   a  sP   








zyolo_model_loder.unload_modelc              	   C   s&   | j | j| j| j| j| j| j| jdS )u]   
        获取模型信息
        Returns:
            包含模型信息的字典
        )Z	is_loadedr   r   r   r   r   r   error)r   r   r   r   r   r   r   r   r&   r'   r'   r(   get_model_info  s   zyolo_model_loder.get_model_infoimagetarget_sizec                 C   s   | j std|du r| jr| jnd}t|jdkr"t|tj}n|}|jdd \}}t|| || }t	|| t	|| }}t
|||f}	tj||dfdtjd}
|| d }|| d }|	|
||| ||| f< |
S )u   
        图像预处理
        Args:
            image: 输入图像 (BGR格式)
            target_size: 目标尺寸，如果为None则使用模型默认尺寸
        Returns:
            预处理后的图像
           模型未加载Nr   r   rf   r   r   )r   RuntimeErrorr   rT   r   cv2cvtColorCOLOR_BGR2RGBminrw   resizer   fullr   )r&   r   r   	image_rgbhwscalenew_hnew_wresizedpaddedy_offsetx_offsetr'   r'   r(   preprocess_image  s    	z!yolo_model_loder.preprocess_imagec              
   K   s  | j std|  }|dkr| nd}| jr| j nd}td| d|  zst }| jdkrzd|v r@t|	dd	 nd
}|
 }|dkrOd| nd|d< d|d< d|d< d|d< d|d< d|vrkd|d< d|d< | jj|fi |}	nd|vr||d< | j|fi |}	t | }
td|
dd |	W S  ty } zd| d| dt| }td|  t|d}~ww )u  
        优化的预测方法，特别针对ONNX模型，每次推理都显示模型信息
        Args:
            image: 输入图像 (BGR格式)
            **kwargs: 预测参数，如 conf, iou, save 等
        Returns:
            预测结果
        r   r.   r   UNKNOWN   🚀 推理信息:    模型 | 设备: r   ri   rj   r   r0   r   FZbatchr   Zsave_txtZ	save_cropr   r   r      ⚡ 推理耗时: .4frO   u   预测失败 (/): r   N)r   r   _get_actual_model_deviceupperr   r+   rR   r   rw   rx   copyr`   r   rF   rB   )r&   r   rG   actual_devicedevice_displaymodel_type_displayrW   r}   Zonnx_kwargsresultsr   rK   rI   r'   r'   r(   r     s@   	
zyolo_model_loder.predictc              
   K   s  | j std|d|  }|dkr| nd}| jr!| j nd}zt }| }d|v r;|d= | j	r;| 
d ||d< d|d	< | jr| jrtd
| d| d | j	rz| 
d | 
d| j  | j D ]\}}	| 
d| d|	  qjt| j| jrt| j n| j}
|
|d< | j	r| 
d|
  | |||}|r| |}n#td
| d| d | j|d< | j	r| 
d| j  | |||}t | }td|dd |W S  ty } zd| d| dt| }td|  t|d}~ww )u  
        增强预测方法，自动处理独立置信度设置，移除外部conf参数依赖
        
        Args:
            image: 输入图像 (BGR格式)
            **kwargs: 预测参数，如 iou, imgsz, device, save, verbose 等
                     注意：conf参数会被自动处理，外部传入的conf参数会被忽略
            
        Returns:
            预测结果
        r   r   r.   r   r   r   u7   已移除外部conf参数，使用内部置信度管理Fr   r   r   u    | 独立置信度模式u   独立置信度设置:u     全局置信度: z  : u#   使用最低置信度进行预测: u    | 全局置信度模式u   使用全局置信度: r   r   rO   u   predictEx失败 (r   r   r   N)r   r   getr   r   r   rR   r   r   r   r,   r#   r%   r+   r$   itemsr   values_execute_prediction'_apply_independent_confidence_filteringrF   rB   )r&   r   rG   rc   r   r   rW   predict_kwargs	class_keyZconf_valmin_confr   r   rK   rI   r'   r'   r(   	predictEx  sV   

"

zyolo_model_loder.predictExr  r   c              
   C   sR   z| j dkr| ||W S | ||W S  ty( } z	tdt| d}~ww )u*  
        执行实际的预测操作，根据模型格式选择推理方式
        
        Args:
            image: 输入图像
            predict_kwargs: 预测参数字典
            model_type_display: 模型类型显示名称
            
        Returns:
            预测结果
        r   u   执行预测失败: N)r   _onnx_predict_pytorch_predictrF   r   rB   )r&   r   r  r   rK   r'   r'   r(   r  Q  s   
z$yolo_model_loder._execute_predictionc           
      C   s   | j sAg }|d |d| j  |d| j  |d| j  |d| j  dd| }| d|  t|| j	sHtd	| 
||d
d}t }| j d| j	|i}t | }| ||j|}	| jrztd|dd |	S )u!   使用纯ONNX Runtime进行推理u   ONNX会话未初始化u   模型加载状态: u   模型路径: u   模型格式: u   加载错误: u   ONNX推理失败 -  | r   u   ONNX输入名称未设置r   r   Nu   🔥 ONNX纯推理耗时: r   rO   )r   r   r   r   r   r   joinr,   r   r   _preprocess_for_onnxr   rR   r   _postprocess_onnx_outputsr   r   r+   )
r&   r   r  Zerror_detailsZ
full_errorZprocessed_imagerW   outputsr   r   r'   r'   r(   r	  h  s(   
zyolo_model_loder._onnx_predictc                 C   s0   t | dr
| jdu rtd| jj|fi |S )u$   使用ultralytics进行PyTorch推理r`   Nu   PyTorch模型未加载)ra   r`   r   r   )r&   r   r  r'   r'   r(   r
    s   z!yolo_model_loder._pytorch_predictr   c                 C   s   t |jdkr|jd dkrt|tj}n|}|jdd \}}t|| || }t|| t|| }}t|||f}	tj	||dfdtj
d}
|| d }|| d }|	|
||| ||| f< |
tjd }|ddd}t|d}|S )	u   为ONNX推理预处理图像r   rf   Nr   r   g     o@r   rj   )rT   r   r   r   r   r   rw   r   r   r   r   r   r   Z	transposeZexpand_dims)r&   r   r   r   r   r   r   r   r   r   r   r   r   Z
normalizedZ
transposedZbatchedr'   r'   r(   r    s   z%yolo_model_loder._preprocess_for_onnxc              
   C   sP  zddl m} ddlm}m} ddl}||d }|dd}	|dd}
|||	|
d	d
}g }t|D ]H\}}t	|r^|dd}|||f|ddddf |dd |ddddf< |t
j|t
jdd| jt	|rs|  ndd}|| q4|W S  ty } z| jrtd|  G dd d}||gW  Y d}~S d}~ww )u6   将ONNX输出后处理为与ultralytics兼容的格式r   )Results)non_max_suppressionscale_boxesNr   r   iou?i,  )Z
conf_thresZ	iou_thresmax_detr   r   re   rf   r   )Zorig_imgpathr   boxesu1   ⚠️ ONNX后处理失败，返回原始输出: c                   @   s   e Zd Zdd ZdS )z@yolo_model_loder._postprocess_onnx_outputs.<locals>.SimpleResultc                 S   s   || _ d | _d S N)raw_outputsr  )r&   r  r'   r'   r(   r)     s   
zIyolo_model_loder._postprocess_onnx_outputs.<locals>.SimpleResult.__init__N)__name__
__module____qualname__r)   r'   r'   r'   r(   SimpleResult  s    r  )Zultralytics.engine.resultsr  Zultralytics.utils.opsr  r  r4   Z
from_numpyr   	enumeraterT   r   zerosr   r   r.   numpyr   rF   r   r+   )r&   r  Zoriginal_shaper  r  r  r  r4   Zpredictionsconf_thresholdZiou_threshold
detectionsr   r   Zdetr   r   rK   r  r'   r'   r(   r    sB   8z*yolo_model_loder._postprocess_onnx_outputsc                    sD  z|r
t |dkr|W S g }| jr(td tdt| j  td| j  i }t| jdrC| jjrC| jj}| jrBtd|  n| jrJtd |D ]}t|dr|j	d	ur|j	}t |dkrj|
| qL|j  }|j  }g }tt |D ]}	t||	 }
t||	 }| j}d
}d	}|
| jv r| j|
 }d|
 d}|
}n|
|v r||
 }|| jv r| j| }d| d}|}|d	u r| j D ]O t trd|
 d|
 t|
    g}|
|v r||
 }||| | g  |v st fdd|D r| j  }d  d} } nq||k}|
| | jrY|
|v r8d||
d dnd}td|
 | d|dd| d|dd| d|rTdnd  qt|rdd	l}|j||jd}dd	l}||}||j	|_	|j	j| |j	_|
| t |}t |}| jrtd| d | d! qLdd	l}dd	l}||}||j	|_	|!dt |j	jj"d"kr|j	jj"d" nd#f}|#|j	jj$|j	_|
| | jrtd$ qL|
| qL| jrtd% |W S  t%y! } z td&|  | jrdd	l&}td'|'   |W  Y d	}~S d	}~ww )(u   
        应用独立置信度过滤
        
        Args:
            results: 原始预测结果
            
        Returns:
            过滤后的预测结果
        r   u*          🔍 开始独立置信度过滤...u)          📋 当前独立置信度设置: u          📋 全局置信度: r   u#          🏷️ 模型类别映射: u,          ⚠️ 模型没有类别名称信息r  Nu   全局u	   独立ID(r   u
   独立名(classClassc                 3   s    | ]} |kV  qd S r  r'   )r   matchZset_class_namer'   r(   	<genexpr>B      zKyolo_model_loder._apply_independent_confidence_filtering.<locals>.<genexpr>u   独立匹配((u   未知r9   u            🎯 类别ID=u   , 置信度=r   z, u   阈值=u   , 匹配键=u	   ✅保留u	   ❌过滤r   u            📊 过滤结果: r   u    个检测框rj      u:            ❌ 所有检测框都被独立置信度过滤掉u&          ✅ 独立置信度过滤完成u(          ❌ 独立置信度过滤失败: u          📍 错误详情: )(rT   r   r+   r   r%   r$   ra   r`   r   r  r   r   r.   r   clsr   rw   floatkeysr   rB   r<   r   extendanyr   r4   Ztensorboolr   Zdeepcopydatar   emptyr   r[   r   rF   	traceback
format_exc)r&   r   Zfiltered_resultsZmodel_namesr   r  confidences	class_idsZ	keep_maskr   class_id
confidenceZclass_thresholdZthreshold_sourceZ	match_keyZmodel_class_nameZpossible_matchesZ
model_nameZkeepZmodel_name_infor4   Zkeep_indicesr   Zfiltered_resultoriginal_countZfiltered_countZempty_tensor_2drK   r3  r'   r&  r(   r    s   







	"

"



0
z8yolo_model_loder._apply_independent_confidence_filteringimagesc           	   
   K   s   | j stdz.g }t|D ]$\}}| jr$| d|d  dt|  | j|fi |}|| q|W S  tyT } zdt	| }| jrL| | t|d}~ww )u   
        批量预测
        Args:
            images: 图像列表
            **kwargs: 预测参数
        Returns:
            预测结果列表
        r   u   处理图像 rj   r   u   批量预测失败: N)
r   r   r  r   r,   rT   r   r   rF   rB   )	r&   r:  rG   r   r   r   r   rK   rI   r'   r'   r(   predict_batch  s"   	
zyolo_model_loder.predict_batch
   
test_imagerunsc                 C   s  | j s	td dS |  }|dkr| nd}| jr| j nd}td| d| d|  g }t|D ]Y}t }z| jd	krL| jj	|d
dddd}	n	| j|d
ddd}	W n t
yt }
 ztd|d  d|
  W Y d}
~
q4d}
~
ww t | }|| |dkrtd|dd q4|std dS t|t| }t|}t|}td| d| d td|dd td|dd td|dd |S )u   
        对比模型性能
        Args:
            test_image: 测试图像
            runs: 测试轮数
        Returns:
            平均推理时间
        u.   ❌ 模型未加载，无法进行性能测试        r.   r   r   u   📊 开始性能测试: r   u    | 测试轮数: r   g?r  F)r   r  r   r   )r   r  r   u   ⚠️ 测试轮次 rj   	    失败: Nr   u      首次推理: r   rO   u"   ❌ 所有测试轮次都失败了u   📈 性能测试结果 (r   z):u      平均时间: u      最快时间: u      最慢时间: )r   r+   r   r   r   r   rR   r   r`   r   rF   r   r   rT   r   r   )r&   r=  r>  r   r   r   timesr   rW   r   rK   r   Zavg_timeZmin_timeZmax_timer'   r'   r(   benchmark_model  sH   	

z yolo_model_loder.benchmark_modelenabledc              
   C   sl   z|| _ |rdnd}| jr| d|  W dS  ty5 } z| jr*| d|  W Y d}~dS d}~ww )u   
        开启或关闭分类独立置信度功能
        
        Args:
            enabled: True启用分类独立置信度, False使用全局置信度
            
        Returns:
            bool: 设置是否成功
           启用   禁用u   分类独立置信度功能已Tu)   设置分类独立置信度功能失败: NF)r#   r   r,   rF   )r&   rC  r   rK   r'   r'   r(   set_independent_conf_enabled  s   
z-yolo_model_loder.set_independent_conf_enabledr!  c              
   C   s   z*d|  krdksn | j r| d|  W dS || _| j r(| d|  W dS  tyH } z| j r=| d|  W Y d}~dS d}~ww )	u   
        设置全局置信度阈值
        
        Args:
            conf_threshold: 置信度阈值 (0.0 - 1.0)
            
        Returns:
            bool: 设置是否成功
        r?        ?u3   置信度阈值必须在0.0-1.0之间，当前值: Fu#   全局置信度阈值已设置为: Tu   设置全局置信度失败: N)r   r,   r$   rF   )r&   r!  rK   r'   r'   r(   set_global_confidence  s   
z&yolo_model_loder.set_global_confidencec              
   C   s  zhd|  krdksn | j r| d|  W dS t|tr$|}|}n/t|trC|dk r;| j r8| d|  W dS |}| |}n| j rP| dt|  W dS || j|< | j rf| d| d	|  W d
S  ty } z| j r}| dt|  W Y d}~dS d}~ww )u>  
        设置单个分类的置信度阈值（支持类别ID或类型名）
        
        Args:
            class_identifier: 类别标识符，可以是int(类别ID)或str(类型名)
            conf_threshold: 置信度阈值 (0.0-1.0)
            
        Returns:
            bool: 设置是否成功
        r?  rG  u*   置信度阈值必须在0.0-1.0范围内: Fr   u+   类别ID必须是非负整数，当前值: u#   不支持的类别标识符类型: u   类别 u    置信度阈值已设置为: Tu   设置分类置信度失败: N)	r   r,   r   rB   rw   get_class_namer   r%   rF   )r&   Zclass_identifierr!  r  Zdisplay_namerK   r'   r'   r(   set_class_confidence  s8   


z%yolo_model_loder.set_class_confidencec                 C   s   | j | jt| jt| jd}| jr5t| j }t| j }|dd|d|d< t| j||d< |S d|d< | j|d< |S )u{   
        获取独立置信度设置状态
        
        Returns:
            dict: 独立置信度状态信息
        )u   独立置信度启用u   全局置信度u   类别置信度设置数量u   类别置信度详情r   r   u   类别置信度范围u   推荐预测置信度u	   未设置)r#   r$   rT   r%   r   r   r  r   )r&   r   r  Zmax_confr'   r'   r(   !get_independent_confidence_status9  s   
z2yolo_model_loder.get_independent_confidence_statusc                 C   s*   d| _ d| _i | _| jr| d dS dS )u9   
        重置所有置信度设置为默认值
        Fr   u$   置信度设置已重置为默认值N)r#   r$   r%   r   r,   r   r'   r'   r(   reset_confidence_settingsR  s   z*yolo_model_loder.reset_confidence_settingsc              
   C   sl  z| j sW dS | jdkrr| jrrzD| j }d|v rQtjdd}|dur>|dkr>dd |d	D }|r>d
|d  W W S t| drMd
| j	v rM| j	W W S W W dS W W dS  t
yq } z| jrg| d|  W Y d}~nd}~ww | jdv rt| drz`t| jdrt| jjdrt| jj d}|dur|j	}|jdkr|jdurd
|j W W S dW W S |jW W S t| jdrt| jj	}|W W S t| jdrt| jjdrt| jjj	W W S W n t
y } z| jr| d|  W Y d}~nd}~ww | jr| d | j	r| j	W S dW S  t
y5 } z| jr"| d|  | j	r)| j	ndW  Y d}~S d}~ww )u   
        获取模型实际运行的设备
        
        Returns:
            str: 实际设备名称，如 'cpu', 'cuda:0', 'cuda:1' 等
        unknownr   rr   ZCUDA_VISIBLE_DEVICESNr9   c                 S   s    g | ]}|   rt|qS r'   )stripr3   rw   r   xr'   r'   r(   r   p  s     z=yolo_model_loder._get_actual_model_device.<locals>.<listcomp>,r0   r   r   r1   r.   u(   ⚠️ 检查ONNX会话提供者失败: )r\   ZpthZtorchscriptr`   r   r/   u(   ⚠️ 检查PyTorch模型设备失败: u=   ⚠️ 无法获取模型实际设备，使用设置的设备u*   ⚠️ 获取模型实际设备时出错: )r   r   r   Zget_providersr   r   r   rx   ra   r   rF   r   r,   r`   r   r   r   r   rB   )r&   rM   Zcuda_visibleZgpu_idsrK   Zfirst_paramZparam_devicer   r'   r'   r(   r   ]  sd   


"

z)yolo_model_loder._get_actual_model_devicec              
   C   s   z&|  |}| jrt| jdr| j| || _| jr$| d| j  W dS  tyD } z| jr9| d|  W Y d}~dS d}~ww )u   
        设置计算设备
        Args:
            device: 设备名称
        Returns:
            是否设置成功
        r[   u   设备已切换到: Tu   设备切换失败: NF)	r   r   ra   r`   r[   r   r   r,   rF   )r&   r   Z
new_devicerK   r'   r'   r(   
set_device  s   
zyolo_model_loder.set_devicec                 C   s
   || _ d S r  )r   )r&   r   r'   r'   r(   set_class_names  s   
z yolo_model_loder.set_class_namesr7  c                 C   s<   | j dur|| j v r| j|d| S | j|d| S )u   
        根据类别ID获取类别名称（可以根据实际模型自定义）
        
        Args:
            class_id: 类别ID
            
        Returns:
            str: 类别名称
        Nr$  )r   r   r   )r&   r7  r'   r'   r(   rI    s   


zyolo_model_loder.get_class_namec                 C   s  g }z|rt |dkr| jr| d |W S |d }|jdu s't |jdkr2| jr/| d |W S |jj  }|jj  }|jj  }| jrX| dt | d t	t |D ]}|| 
t\}}	}
}t|| }t|| }t||
 d }t|	| d }t|
| }t||	 }|| }| |}|}|}|}t|jdrzt|jj|   }|| d }W n   Y ||t|t|	t|
t|||||d	t|d
t|d
t|d
||d}|| | jr| d| d| d| d|dd| d|	 d|
 d| d|  q^| jr | dt | d |W S  tyE } z| jr9| dt|  |W  Y d}~S d}~ww )u  
        从YOLO模型推理结果中提取检测框信息为结构化列表
        
        Args:
            model_results: YOLO模型推理结果
            
        Returns:
            list: 检测框信息列表，每个元素包含：
                {
                    "类型编号": int,           # 类别ID
                    "类型字符": str,           # 类别名称
                    "AOI位置": {              # 边界框坐标
                        "x1": int,
                        "y1": int, 
                        "x2": int,
                        "y2": int,
                        "center_x": int,
                        "center_y": int,
                        "width": int,
                        "height": int
                    },
                    "检测置信度": float,       # 原始检测置信度
                    "分类置信度": float,       # 分类置信度（如果可用）
                    "综合置信度": float,       # 综合置信度分数
                    "面积": int,              # 检测框面积
                    "索引": int               # 检测结果索引
                }
        r   u   ⚠️ 无检测结果可提取Nu   ⚠️ 未检测到任何目标u   📋 开始提取     个检测结果rf   cls_confx1y1x2y2center_xcenter_ywidthheightre      类型编号   类型字符	   AOI位置   检测置信度u   分类置信度u   综合置信度   面积   索引u
     - 检测r   z(ID:u   ), 置信度:r   u
   , 位置:[rQ  u
   ], 面积:u   ✅ 成功提取 u   ❌ 提取检测结果失败: )rT   r   r,   r  Zxyxyr.   r   r   r+  r   r   rw   r,  rI  ra   rU  roundr   rF   rB   )r&   model_resultsdetection_listr   r  r5  r6  r   rW  rX  rY  rZ  r8  r7  r[  r\  r]  r^  area
class_nameZdetection_confidenceZclassification_confidenceZcombined_confidenceZdetection_inforK   r'   r'   r(   extract_detection_list  s   




 z'yolo_model_loder.extract_detection_listrh  c           	   	   C   s   |s	di i i dS i }g }g }|D ]}|d }| |dd ||< ||d  ||d  qddl}t||tt|dtt|dt||dt||dd	t|t|t||d
t	|dd}|S )u   
        获取检测结果的统计摘要
        
        Args:
            detection_list: 检测结果列表
            
        Returns:
            dict: 统计摘要信息
        r   )   总数   类别统计   置信度统计u   面积统计ra  rj   rc  rd  Nre   )u	   最小值u	   最大值	   平均值u	   中位数rf   )u   最小面积u   最大面积u   平均面积u	   总面积)
r   r   r   rT   rf  r   r   meanmedianr   )	r&   rh  Zclass_countr5  Zareas	detectionrj  r   summaryr'   r'   r(   get_detection_summaryA  s:   
z&yolo_model_loder.get_detection_summaryr   overlap_thresholdc              
   C   s  z|r	t |dkr|W S | jr"| d|d | dt |  i }|D ]}|d }||vr4g ||< || | q&g }d}| D ]Y\}}|rP|d d nd| }	| jri| d	|	 d
| dt | d t |dkru|| qD| ||}
t |t |
 }||7 }| jr| d| dt |
 d ||
 qDt|D ]\}}||d< q| jr| d | dt |  | dt |  | d| d |W S  ty } z| jr| dt	|  |W  Y d}~S d}~ww )u+  
        合并同类别的重叠检测框
        
        Args:
            detection_list: 检测结果列表
            overlap_threshold: 重叠度阈值 (0.0-1.0)，高于此阈值的同类框将被合并
            
        Returns:
            list: 合并后的检测结果列表
        rj   u+   🔄 开始合并同类框，重叠阈值: rN   u      原始检测框数量: r`  r   ra  r$  u      处理类别 z (ID:u   )，共 u    个框u        合并了 u    个框，剩余 u    个re  u   ✅ 合并完成:u      原始框数: u      合并后框数: u      总共合并: u   ❌ 合并同类框失败: N)
rT   r   r,   r   r  r.  _merge_boxes_in_classr  rF   rB   )r&   rh  ru  Zclass_groupsrr  r7  merged_listZtotal_mergedr"  rj  Zmerged_class_boxesmerged_countr   rK   r'   r'   r(   merge_same_class_boxess  sR   "


z'yolo_model_loder.merge_same_class_boxesr"  c                 C   s   t |dkr|S g }t }t|D ]c\}}||v rq|g}|h}t|D ]!\}	}
||	ks0|	|v r1q$| ||
}||krE||
 ||	 q$t |dkrj| |}|| || | jri| 	dt | d q|| || q|S )u  
        合并同一类别中的重叠框
        
        Args:
            detections: 同一类别的检测结果列表
            overlap_threshold: 重叠度阈值
            
        Returns:
            list: 合并后的检测结果列表
        rj   u          合并 u    个重叠框)
rT   setr  _calculate_overlap_ratior   add_merge_multiple_boxesupdater   r,   )r&   r"  ru  Zmerged_boxesZused_indicesr   Zdetection_iZoverlapping_boxesZoverlapping_indicesjZdetection_joverlap_ratio
merged_boxr'   r'   r(   rv    s6   





z&yolo_model_loder._merge_boxes_in_class
detection1
detection2c              
   C   s"  zp|d }|d }|d |d |d |d f\}}}}|d |d |d |d f\}	}
}}t ||	}t ||
}t||}t||}||ksI||krLW dS || ||  }|d }|d }t||}|dkrl|| }|W S d}|W S  ty } z| jr| d	t|  W Y d
}~dS d
}~ww )u  
        计算两个检测框的重叠度 (重叠面积 / 较小框面积)
        
        Args:
            detection1: 第一个检测框
            detection2: 第二个检测框
            
        Returns:
            float: 重叠度比例 (0.0-1.0)
        rb  rW  rX  rY  rZ  r?  rd  r   u   ⚠️ 计算重叠度失败: N)r   r   rF   r   r,   rB   )r&   r  r  Zaoi1Zaoi2Zx1_1Zy1_1Zx2_1Zy2_1Zx1_2Zy1_2Zx2_2Zy2_2
overlap_x1
overlap_y1
overlap_x2
overlap_y2overlap_areaZarea1Zarea2Zsmaller_arear  rK   r'   r'   r(   r{    s2   $$




z)yolo_model_loder._calculate_overlap_ratior  c                 C   sF  t |dkr
|d S t|dd d}tdd |D }tdd |D }td	d |D }td
d |D }t|| d }t|| d }t|| }	t|| }
|	|
 }dd |D }t|}|d |d t|t|t|t||||	|
dt|dt|dt|d||d d}| jr| d|dd|	 d|
 d|  |S )u   
        合并多个检测框为一个
        
        Args:
            boxes: 要合并的检测框列表
            
        Returns:
            dict: 合并后的检测框
        rj   r   c                 S      | d S )Nrc  r'   rP  r'   r'   r(   <lambda>%      z8yolo_model_loder._merge_multiple_boxes.<locals>.<lambda>)keyc                 s       | ]	}|d  d V  qdS )rb  rW  Nr'   r   Zboxr'   r'   r(   r'  (      z9yolo_model_loder._merge_multiple_boxes.<locals>.<genexpr>c                 s   r  )rb  rX  Nr'   r  r'   r'   r(   r'  )  r  c                 s   r  )rb  rY  Nr'   r  r'   r'   r(   r'  *  r  c                 s   r  )rb  rZ  Nr'   r  r'   r'   r(   r'  +  r  rf   c                 S   s   g | ]}|d  qS )rc  r'   r  r'   r'   r(   r   5  s    z:yolo_model_loder._merge_multiple_boxes.<locals>.<listcomp>r`  ra  rV  re   re  r_  u            合并框: 置信度r   u   , 尺寸rP  u   , 面积)rT   r   r   rw   rf  r   r,   )r&   r  Zbest_boxZx1_minZy1_minZx2_maxZy2_maxr[  r\  r]  r^  ri  r5  Zmax_confidencer  r'   r'   r(   r}    sD   

$z&yolo_model_loder._merge_multiple_boxesc           	   
   C   s   z3t |}|dkrdW S | ||}|  || |t | }d| dt | d| d}d|fW S  ty[ } zdt| }| jrM| d	|  d
|fW  Y d}~S d}~ww )u7  
        就地合并重叠的同类检测框（直接修改原列表）
        
        Args:
            detection_list: 检测结果列表（会被直接修改）
            overlap_threshold: 重叠度阈值
            
        Returns:
            tuple[bool, str]: (是否成功, 操作信息)
        rj   T#   检测框数量 <= 1，无需合并u   合并完成: 原始u   个框 → 合并后u   个框 (合并了u   个)u   合并检测框失败: r   FN)Tr  )rT   ry  clearr.  rF   rB   r   r,   )	r&   rh  ru  r9  rw  rx  Z
result_msgrK   rI   r'   r'   r(   $merge_overlapping_detections_inplaceR  s"   

z5yolo_model_loder.merge_overlapping_detections_inplacedeleted_listc           (      C   sv  z|du st |dkr| jr| d |W S | }| jr)| dt | d dddd	d
dd}g d}|D ]R}z|d }|d }|d }	|d }
|d }|d }|	d }|	d }|	d }|	d }||v rn|| }n||t |  }t|||f||f|d | d|
ddg}tj}d}d}d}d}g }|D ]}t||||\\}}}t||}|	||  qt || d }|}|| }|dk r|| }|| |j
d kr|j
d | d  }| } t| |d |d f|| d || f|d! t| d"|d#d| t|D ]\}!}||!d |  }"t||||"f||d$| q|	d% }#|	d& }$| jrK| d'| d(| d)|
dd*| d+| d+| d+| d, W q8 tyk }% z| jra| d-|%  W Y d}%~%q8d}%~%w ty }& z| jr| d.t|&  W Y d}&~&q8d}&~&ww | jr| d/ |W S  ty }' z| jr| d0t|'  |W  Y d}'~'S d}'~'ww )1u,  
        根据检测结果列表在图像上标注检测框
        
        Args:
            image: 输入图像 (numpy.ndarray)
            deleted_list: 检测结果列表，每个元素包含完整的检测信息
            
        Returns:
            np.ndarray: 标注后的图像
        Nr   u(   ⚠️ 检测列表为空，无需标注u   🎯 开始标注 rT  r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   rj   rf   r   re      ))   r   r  )r      r   )r   r  r   )r  r   r   r`  ra  rb  rc  rd  re  rW  rX  rY  rZ  rf   z :r    r   rj      r  r<  r   皙?皙?r   r   r   r[  r\  u     ✅ 标注完成 - 索引:u	   , 类别:u   , 置信度:u
   , 坐标:[rQ  ]u/     ❌ 检测数据格式错误，缺少字段: u&     ❌ 标注单个检测结果失败: u"   ✅ 所有检测结果标注完成u   ❌ 标注检测结果失败: )rT   r   r,   r   r   	rectangleFONT_HERSHEY_SIMPLEXgetTextSizer   r   r   ZaddWeightedr  putTextKeyErrorrF   rB   )(r&   r   r  Zannotated_imageclass_colorsZdefault_colorsrr  r7  rj  Zaoi_positionr8  ri  r   rW  rX  rY  rZ  colorZlabel_partsfont
font_scale	thicknessline_heightZmax_text_widthZtext_heightsZ
label_part
text_widthtext_heightbaselinetotal_heightZlabel_xZlabel_yZoverlayr   text_yr[  r\  ZkederK   r'   r'   r(   draw_detections_from_listv  s   




	

z*yolo_model_loder.draw_detections_from_listc              
   C   s|  z|sW dS |  |}|d }|d }|d d }d| }ddd	 | D }d
|d}	tj}
d}d}|||	g}d}d}|D ]}t||
||\\}}}t||}||| d 7 }qBt|d|d |d fdd t|d|d |d fdd d}|D ]}t||d|f|
|d| |d7 }qW dS  t	y } z| j
r| dt|  W Y d}~dS W Y d}~dS d}~ww )u   
        在图像顶部绘制检测统计摘要
        
        Args:
            image: 要绘制的图像
            deleted_list: 检测结果列表
        Nrl  rm  rn  ro  ztotal_count : r  c                 S   s   g | ]\}}| d | qS )ri   r'   )r   r+  countr'   r'   r(   r   5	  s    z;yolo_model_loder.draw_detection_summary.<locals>.<listcomp>zavg_confidence: r   g333333?rf   r   r  )r<  r<  r  r   r   r   r   r           u!   ⚠️ 绘制统计摘要失败: )rt  r  r  r   r  r  r   r  r  rF   r   r,   rB   )r&   r   r  rs  total_countZclass_statsZavg_confidenceZsummary_textZ
class_textZconfidence_textr  r  r  texts	max_widthr  textr  r  r  r   rK   r'   r'   r(   draw_detection_summary	  sR   




"z'yolo_model_loder.draw_detection_summaryFdeleted_itemscale_masksc                 C   s\  zlt |ts| jr| d W dS |dd}|di }|dd}| ||}| jrc| d|  |rW| d	|d
 dd|d d | d|d  d|d   | d|r^dnd  g |d< d|d< d|d< d|d< d|d< |sd|d< | jr| d W dS |d dsd!|d< | jr| d"|d#d$  W dS |d%d}|dkrd&|d< | jr| d' W dS | jr| d(| d) g }t|d* D ]\}	}
z|
d+ }| jr| d,|	 d-|
d.d/ d0|j  t|jd1kr|jd d2kr|d }n|jd1 d2kr|d n
|dddddf }|j	t
jt
jfv r2|d3kt
jd4 }n
|dkt
jd4 }t
|dkrS| jrQ| d5|	 d6 W q| |||||d7}|r|d8r|d8 D ]}|
d9|	|d:< |
d.d|d;< |
d<d|d=< ql||d8  | jr| d>t|d8  d? n| jr| d5|	 d@t
|dk dA W q ty } z%| jr| dB|	 dCt|  ddl}| dD|   W Y d}~qd}~ww |rZ|jdEdF dGdH t|D ]\}}||dI< |dk|dJ< q||d< t||d< d|d< d |d< |dKd}|t|d* k r<|d* | d+ |d< |d j|d< | jrW| dLt| d? | dM|d dN   W dGS dO|d< | jrk| dP| dQ W dS  ty } z3| jr| dRt|  t |trg |d< d|d< d|d< d#|d< d|d< d|d< W Y d}~dS d}~ww )Su[  
        从单个deleted字典中提取轮廓信息，直接使用其内部的sam_result
        
        Args:
            deleted_item: 单个检测结果字典，包含sam_result字段
            scale_masks: bool, 是否将掩码数组也缩放到原始图像尺寸
                - True: 缩放掩码到原始尺寸（消耗更多内存，但坐标一致）
                - False: 保持掩码为推理尺寸，只缩放坐标点（默认，效率更高）
            
        Returns:
            bool: 是否成功提取轮廓
            
        该方法会在deleted_item中添加以下字段：
            "轮廓列表": [轮廓信息列表]
            "轮廓总数": int
            "主要轮廓索引": int  
            "轮廓处理状态": str
            "原始完整掩码": np.ndarray
            "掩码形状": tuple
        u   ❌ 输入不是字典格式Fra  r]   rb  
sam_resultNu"   🔍 提取单个检测项轮廓: u     📐 缩放信息: scale_x=scale_xr   
, scale_y=scale_yu        推理尺寸: inference_sizeu   , 原始尺寸: original_sizeu     🎯 掩码缩放模式: rD  rE     轮廓列表r      轮廓总数r      主要轮廓索引u   原始完整掩码r   r      掩码形状Zno_sam_result   轮廓处理状态u     ❌ 无sam_result数据r   Z
sam_failedu     ❌ SAM处理失败: r   u   未知错误
mask_countZno_masksu     ❌ SAM结果中无掩码u     📊 处理 u    个SAM掩码masks
mask_arrayu       处理掩码 	   : 面积=	mask_areazN/Au	   , 形状=rf   rj   r   r   u         ⚠️ 掩码 u    为空掩码，跳过r   contour_list
mask_indexu   来源掩码索引u   来源掩码面积
is_largestu   是否来自最大掩码u         ✅ 提取到 
    个轮廓u,    未提取到轮廓 (掩码非零像素数: r   u         ❌ 处理掩码     时出错: u           详细错误: c                 S   r  )N   轮廓面积r'   r  r'   r'   r(   r  	  r  zByolo_model_loder.extract_single_deleted_contours.<locals>.<lambda>T)r  reverse   轮廓索引   是否主要轮廓largest_mask_indexu     ✅ 成功提取 u        主要轮廓面积: r  Zno_contours_foundu
     ❌ 从 u%    个掩码中未提取到任何轮廓u%   ❌ 提取单个deleted轮廓失败: )r   r   r   r,   r   _calculate_scale_infor  r   rT   r   r   r   float64r   r   r   _extract_contours_from_maskr.  rF   rB   r3  r4  sort)r&   r  r  rj  aoi_infor  
scale_infor  Zall_contours_infoZmask_idx	mask_infor  binary_maskZcontours_infocontourmask_er3  idxr  rK   r'   r'   r(   extract_single_deleted_contoursZ	  s   

"

&
."z0yolo_model_loder.extract_single_deleted_contoursr  c                 C   sf  z| d}|du r| jr| d W dS t|dr%|jdd \}}n| jr-| d W dS d}d\}}|rf| d}	|	rR|	\}}||f}| jrR| d	|  |sf| d
}
|
rf| jrf| d|
  |s|rd|v r|d r|d d }| d}|durt|drt|jdkr|jdd \}}||f}| jr| d|  |sd}|\}}| jr| d|  || }|| }|||||f||||d|r| drdndd
}| jr| d | d| d|  | d| d|  | d|dd|d | d|d   |W S  ty2 } z| jr'| dt|  W Y d}~dS d}~ww ) u  
        计算从SAM推理尺寸到原始图像尺寸的缩放信息（支持动态推理尺寸）
        
        Args:
            deleted_item: 检测结果字典，包含原始图像信息
            sam_result: SAM推理结果，包含推理参数和实际推理尺寸
            
        Returns:
            dict: 缩放信息字典，如果无法计算则返回None
        r   Nu&       ❌ 无法获取原始图像信息r   rf   u#       ❌ 原始图像格式不支持NNinference_actual_sizeu0       📐 从sam_result获取实际推理尺寸: r  u'       📐 sam_result中的原始尺寸: r  r   r  u,       📐 从掩码数组获取推理尺寸: )r   r   u:       ⚠️ 无法获取实际推理尺寸，使用默认: TZdynamicfallback)
r  r  r  r  inference_widthinference_heightoriginal_widthoriginal_heightZis_dynamic_inferenceinference_methodu0       ✅ 计算缩放信息成功 (动态推理):u          原始尺寸: rP  u          推理尺寸: u          缩放比例: X=r   , Y=u          推理方法: r  u"       ❌ 计算缩放信息失败: )r   r   r,   ra   r   rT   rF   rB   )r&   r  r  original_imager  r  r  r  r  Zactual_sizeZoriginal_size_from_samZ
first_maskr  r  r  r  rK   r'   r'   r(   r  
  s   







z&yolo_model_loder._calculate_scale_infoseg_maskr  r  r  c           B         sl  ddl }ddl}z
t||js||}|j|jkr)||dk|dkB r)|}nC|j|j|j	fv rO|
|dkrD|dk|jd }n(|dk|jd }n|j|jkr`|dk|jd }n||j}|dkd }| jr||dk}	| d|j d|	  |||j|j\}
}|
s| jr| d	 W dS | jr| d
t|
 d |dd}|dd}g }g }t|
D ]8\}}z||}||d}|dk r| jr| d| d|  W q|| ||\}}}}||}|d dkrt|d |d  }t|d |d  }n||d  ||d  }}||}||}|dkr1|| nd}|dkrCd|j | ||  nd}|| }|dkrR|| nd} d}!t|dkrz9||}"|"d }#|#d dkr|#d dkrt
|#d }$t|#d }%|$dkr| d|%|% |$|$   nd}!W n   d}!Y |r|dd |ddt|  }t| }t|  }t| }t|  }t| }t|   }|t
  }| jr| d ddd |dkr|| nd}&i }'|durt|jdkrz|j!|j|jd}(|"|(|gd |jdd \})}*|j#|(|*|)f|j$d}+|+dk}	|%|	r||	 },t|,dkr|j&|,dd }-|j'|,dd }.t(t)|-d dt(t)|-d dt(t)|-d dd!t(t)|.d dt(t)|.d dt(t)|.d dd!tt|,d"}'| jr| d#| d$t|, d%|-d d&d'|-d d&d'|-d d&d( n7dddd!dddd!dd"}'| jr| d#| d) ndddd!dddd!dd"}'| jr| d#| d* W nR t*y }/ z%| jr| d+| d,t+|/  dddd!dddd!dd"}'W Y d}/~/n%d}/~/ww dddd!dddd!dd"}'| jr?|dur?| d#| d- t|t|t(|d||||d.|| || || | || | d/||d0|| || d0t(|dt(|dt(| dt(|!dt(|&d|'d1dddd!|'d2dddd!|'d3dd4t|d5d5dddd|j|,d6d- d7d8}0||0 | jr| d#| dt| d9|d:d;t|  W q t*y }1 z| jr| d<| d=t+|1  W Y d}1~1qd}1~1ww |sW dS t|D ]\}2}0z|
|2 }|r|r|dd |dd|d>|jdd }3|3\}4}5||}6|| g |j.}7|6| g |j.}8|j!|4|5f|jd}(|"|(|7gd |j!|4|5f|jd}9|j/|9|7gd6dtt
 d? |7,d6d- |0d@ dA< |8,d6d- |0d@ dB< d>|0d@ dC< | jr| dD|2 dE|5 dF|4  | dG ddHd n|j0||jd}(|"|(|gd |j0||jd}9|j/|9|gd6ddd? ||}6|,d6d- }:|6,d6d- };|rD|dd |dd|d>|j}3 fdIdJ|:D }< fdKdJ|;D }=|<|0d@ dA< |=|0d@ dB< d>|0d@ dC< | jrC| dD|2 dL | dG ddHd n|:|0d@ dA< |;|0d@ dB< d>|0d@ dC< |rx|dd|dd|dM|j|d>|j|dN|0d@ dO< nd|0d@ dO< |(|0d@ dP< |9|0d@ dQ< | jr||(dk}	||9dk}>| dD|2 dR|	 dS|>  W q  t*y }? z6| jr| dT|2 dUt+|?  |j0||jd|0d@ dP< |j0||jd|0d@ dQ< g |0d@ dB< W Y d}?~?q d}?~?ww |r|1|}@|@t|k rd||@ dV< nd6}@|t||@||jdWW S  t*y5 }A z| jr*| dXt+|A  W Y d}A~AdS d}A~Aww )Yu  
        从分割掩码中提取轮廓信息
        
        Args:
            seg_mask: 分割掩码数组
            aoi_info: AOI位置信息
            scale_info: 缩放信息字典，包含从推理尺寸到原始图像的缩放参数
                {
                    "scale_x": float,           # x方向缩放因子
                    "scale_y": float,           # y方向缩放因子
                    "inference_size": tuple,    # SAM推理尺寸
                    "original_size": tuple      # 原始图像尺寸
                }
            scale_masks: bool, 是否将掩码数组也缩放到原始图像尺寸
                - True: 缩放掩码到原始尺寸（消耗更多内存，但坐标一致）
                - False: 保持掩码为推理尺寸，只缩放坐标点（默认，效率更高）
            original_image: 原始图像数据，用于计算轮廓内像素的RGB统计信息
            
        Returns:
            dict: 轮廓信息字典，包含轮廓列表和统计信息
        r   Nr   rG  r      u           掩码处理: 形状=u   , 非零像素=u           未找到任何轮廓u           找到 r  rW  rX  Tr  u             跳过过小轮廓 r  Zm00Zm10Zm01rf   re   rj   r  r  u&             应用坐标缩放: scale_x=r   r  r   r   )Zinterpolation)Zaxis)RGB)	   RGB均值	   RGB中值   像素数量u             轮廓 u    RGB统计: 像素数=u   , RGB均值=(ru   rQ  r   u'    RGB统计: 掩码区域无有效像素u    RGB统计: 掩码为空u             ⚠️ 计算轮廓 u    RGB统计时出错: u1    RGB统计: 原始图像不是3通道彩色图像rP  yr]  r^  )rW  rX  rY  rZ  )rP  r  r  r  r  )u   凸性u	   圆形度u	   矩形度u	   离心率u	   紧密度r  r  r  Fr   )   单独轮廓掩码   轮廓边界掩码   轮廓点坐标   轮廓凸包点r  u   原始轮廓点)r  r  u   轮廓长度	   边界框   绝对边界框u   轮廓中心   绝对中心u   轮廓特征u   轮廓点数r  filter   轮廓掩码数据u	   , 周长=rN   u	   , 点数=u       ⚠️ 处理轮廓 r  r  )r  r  r   r     掩码坐标系u       轮廓 u!   : 掩码已缩放到原始尺寸 rP  u            缩放因子: X=r  c                    ,   g | ]}t |d    t |d  gqS r   rj   rw   r   r\   r  r  r'   r(   r        , z@yolo_model_loder._extract_contours_from_mask.<locals>.<listcomp>c                    r  r	  r
  r  r  r'   r(   r     r  u,   : 坐标已缩放，掩码保持推理尺寸r  )r  r  r  r  Zmasks_scaled   缩放信息r  r  u   : 掩码像素=u   , 边界像素=u       ⚠️ 生成轮廓 u    掩码数据时出错: r  )r  r  Zmain_contour_indexu   原始掩码r  u$   ⚠️ 从掩码提取轮廓失败: )2r   r   r   ndarrayarrayr   r   allr   r  r   r   r   r   r,   r   ZfindContoursZRETR_EXTERNALZCHAIN_APPROX_SIMPLErT   r   r  ZcontourAreaZ	arcLengthr   ZboundingRectZmomentsrw   Z
convexHullZpiZ
fitEllipser   sqrtr  fillPolyr   ZINTER_NEARESTr/  rp  rq  rf  r,  rF   rB   reshapetolistint32drawContoursZ
zeros_likeargmax)Br&   r  r  r  r  r  r   r   r  Zmask_pixelsZcontoursr   Zaoi_x1Zaoi_y1r  Zcontour_areasr  r  ri  Z	perimeterrP  r  r   r   McxcyhullZ	hull_areaZ	convexityZcircularityZ	rect_areaZrectangularityZeccentricityZellipseZaxesabZcompactnessZ	rgb_statsZsingle_contour_maskZ
roi_heightZ	roi_widthZroi_maskZmasked_pixelsZrgb_meanZ
rgb_medianZrgb_econtour_infoZ	contour_er   r  Ztarget_heightZtarget_widthZcurrent_hullZscaled_contourZscaled_hullZcontour_boundary_maskcontour_pointshull_pointsZscaled_contour_pointsZscaled_hull_pointsZboundary_pixelsr  Zmain_contour_idxrK   r'   r  r(   r  s
  s  
"





$
(
@(




,,
 


	
z,yolo_model_loder._extract_contours_from_maskr  333333?	draw_type
mask_alphac           
   
   C   s  zt |ts| jr| d W dS |ddkr*| jr'| d|dd  W dS |dg }|s=| jr:| d W dS |d	d
}|dd}| jr\| d| d| dt|  | |}|dkrm| ||| nb|dkrz| |||| nU|dkr| 	||| nI|dkr| 
||| n=|dkr| ||| n1|dkr| ||||d  n"|dkr| ||||d  n| jr| d| d | ||| | ||| | jr| d|  W dS W dS  ty }	 z| jr| dt|	  W Y d}	~	dS d}	~	ww )u:  
        在图像上绘制检测项的轮廓掩码 (直接修改输入图像)
        
        Args:
            image: 输入图像 (BGR格式) - 将被直接修改
            deleted_item: 检测结果字典，包含轮廓信息
            draw_type: 绘制类型，可选值：
                - "contour": 绘制轮廓线条（默认）
                - "mask": 绘制半透明填充掩码
                - "hull": 绘制凸包
                - "boundary": 绘制边界线
                - "points": 绘制轮廓点
                - "combined": 组合绘制（轮廓+凸包+中心点）
                - "all": 绘制所有类型
            mask_alpha: 掩码透明度，范围0-1（仅在draw_type为"mask"时使用）
        
        Returns:
            None: 直接修改输入图像，无返回值
        u"   ❌ deleted_item不是字典格式Nr  r   u!   ⚠️ 轮廓处理状态异常: rM  r  u   ⚠️ 轮廓列表为空ra  r]   r`  r   u   🎨 绘制轮廓: u
   , 类型: u   , 轮廓数: r  maskr  boundarypointsZcombinedgffffff?r  u   ⚠️ 未知的绘制类型: u   ，使用默认轮廓线条u   ✅ 轮廓绘制完成: u   ❌ 绘制轮廓失败: )r   r   r   r,   r   rT   _get_draw_colors_draw_contour_lines_draw_filled_masks_draw_convex_hulls_draw_boundaries_draw_contour_points_draw_combined_draw_all_types_add_contour_labelsrF   rB   )
r&   r   r  r#  r$  r  rj  r7  colorsrK   r'   r'   r(   draw_deleted_mask  s\   


 
z"yolo_model_loder.draw_deleted_maskc              	   C   sZ   ddddddd}| |d}|g |d	R td
d |D tdd |D dddddS )u   获取绘制颜色方案r  r  r  r  r  r  r  )r  r  r  d   c                 s   s    | ]
}t d |d V  qdS )r   2   N)r   r   cr'   r'   r(   r'  Y      z4yolo_model_loder._get_draw_colors.<locals>.<genexpr>c                 s   s    | ]
}t d |d V  qdS )r   r4  N)r   r5  r'   r'   r(   r'  Z  r7  r  r  )r  r%  r  r&  r'  centerr  
background)r   tuple)r&   r7  r  
base_colorr'   r'   r(   r(  H  s"   	z!yolo_model_loder._get_draw_colorsr;  contour_indextotal_contoursc                 C   s   |dkr|S |t d|d  }ddl}ddl}||gg}|||j}|d \}	}
}t|d }|	| d }t|d d }t|d d }t dtd	t|
| }t d
td	t|| }||||ggg}|||j}t	dd |d D }|S )u  
        为单个轮廓生成色移颜色
        
        Args:
            base_color: 基础颜色 (B, G, R)
            contour_index: 轮廓索引
            total_contours: 总轮廓数
            
        Returns:
            tuple: 色移后的颜色 (B, G, R)
        rj   r   Nr  <      r  r  r   P   c                 s   $    | ]}t d tdt|V  qdS r   r   Nr   r   rw   rO  r'   r'   r(   r'       " z6yolo_model_loder._get_contour_color.<locals>.<genexpr>)
r   r   r   r   r   ZCOLOR_BGR2HSVrw   r   ZCOLOR_HSV2BGRr:  )r&   r;  r<  r=  Zshift_ratior   r   Z	bgr_arrayZ	hsv_arrayr   svZ	hue_shiftr   Z	sat_shiftZ	val_shiftZnew_sZnew_vZnew_hsvZnew_bgrZresult_colorr'   r'   r(   _get_contour_colora  s$   z#yolo_model_loder._get_contour_colorr  c                 C   s4   | di }|r| dd| dddS dddS )u   
        获取AOI偏移信息
        
        Args:
            contour_info: 轮廓信息字典
            
        Returns:
            dict: 包含x_offset, y_offset的偏移信息
        r  rW  r   rX  r   r   r   )r&   r  abs_bboxr'   r'   r(   _get_aoi_offset  s   


z yolo_model_loder._get_aoi_offsetr  r1  c                 C   s  |d }t |}t|D ]\}}|dds#| jr"| d| d q|di }|dg }	|	rt |	dkrztj|	tjd	}
|d
d}|dkrk| |}|
dddf  |d 7  < |
dddf  |d 7  < t |
j	dkr|
j	d dkr|

d}| |||}tdd |D }|dd}|rdnd}t||gd|| | jr| dt |
 d| d|  W q ty } z| jr| dt|  W Y d}~qd}~ww qdS )uW   绘制轮廓线条，直接修改输入图像，每个轮廓使用不同的色移颜色r  r  Fu         轮廓    : 跳过绘制 (filter=false)r  r   r   r   r  r  r  Nr   rj   r   rf   r   rj   rf   c                 s   rA  rB  rC  r5  r'   r'   r(   r'    rD  z7yolo_model_loder._draw_contour_lines.<locals>.<genexpr>r  r   r   u         绘制轮廓线条: u    个点, 颜色: u
   , 厚度: u'         ⚠️ 绘制轮廓线条失败: )rT   r  r   r   r,   r   r  r  rK  r   r  rG  r:  r   r  rF   rB   )r&   r   r  r1  r;  r=  r<  r  contour_datar   r'  coord_systemr  r  contour_color
safe_coloris_mainr  rK   r'   r'   r(   r)    sF   

 z$yolo_model_loder._draw_contour_linesalphac           *      C   s  |sdS t |d dkr|d dd n|d }t |}| jr*| d| d|  t|D ]\}}|ddsF| jrE| d	| d
 q.|di }	| |||}
tdd |
D }|	dg }|rt |dkrztj|tj	d}|	dd}|dkr| 
|}|dddf  |d 7  < |dddf  |d 7  < tj|jdd tjd}|d}t||gd |dk}t|rtdD ]}|||f d|  || |  tj|||f< q| jrt|}| d	| d| d|  W q. ty } z| jr| d| dt|  W Y d}~q.d}~ww |	d}|durz|	dd}|	d}|dkr|r|d d!}|d"d!}t|jd | }t|jd | }t|||f}| 
|}|d |d }}|j\}} ||| }!}"|||  }#}$td|!t|jd |"}!}"td|#t|jd |$}#}$|"|!kr|$|#kr|"|! |$|# }%}&|%|ks|&| krt||&|%f}'n|}'|'dk}t|r||!|"|#|$f }(tdD ]}|(||f d|  || |  tj|(||f< q| jrt|}| d	| d| d#|  n| 
|}|d |d }}|j\}} ||| }!}"|||  }#}$td|!t|jd |"}!}"td|#t|jd |$}#}$|"|!kr|$|#kr|"|! |$|# }%}&|%|ksn|&| krwt||&|%f})n|})|)dk}t|r||!|"|#|$f }(tdD ]}|(||f d|  || |  tj|(||f< q| jrt|}| d	| d| d$|  W q. ty } z| jr| d%| dt|  W Y d}~q.d}~ww q.| jr| d& dS dS )'u`   绘制半透明填充掩码，直接修改输入图像，每个轮廓使用不同的色移颜色Nr%  r   r  u         开始绘制 u    个填充掩码，透明度: r  Fu           轮廓 rL  r  c                 s   rA  rB  rC  r5  r'   r'   r(   r'    rD  z6yolo_model_loder._draw_filled_masks.<locals>.<genexpr>r   r   r   r  r  r  r   rj   r   rf   rM  r   u	   : 绘制 u    个像素，颜色 u           ⚠️ 绘制轮廓 r@  r  r  r  rG  r  u#    个像素 (缩放掩码)，颜色 u#    个像素 (原始掩码)，颜色 u"           ⚠️ 绘制轮廓掩码 u(         ✅ 完成所有填充掩码绘制)rT   r   r,   r  r   rG  r:  r   r  r  rK  r  r   r   r  r   r  r/  r   r   r   rF   rB   rw   r   r   r   )*r&   r   r  r1  rS  r;  r=  r<  r  rN  rP  rQ  r   r'  rO  r  r%  r  Z	mask_boolr6  Zpixels_drawnrK   Zsingle_maskr  r  r  Z
original_hZ
original_wZscaled_maskr   r   mask_hmask_wrX  rZ  rW  rY  target_htarget_wZ
final_maskZregionresized_maskr'   r'   r(   r*    s   (















Yz#yolo_model_loder._draw_filled_masksc                 C   s   |d }t |}t|D ]h\}}|ddsq|di }|dg }	|	rttj|	tjd}
| |}|
dddf  |d	 7  < |
ddd
f  |d 7  < | |||}tdd |D }|dd}|rhdnd
}t	
||
gd|| qdS )uQ   绘制凸包，直接修改输入图像，每个轮廓使用不同的色移颜色r  r  Fr  r  r   Nr   r   rj   r   c                 s   rA  rB  rC  r5  r'   r'   r(   r'    rD  z6yolo_model_loder._draw_convex_hulls.<locals>.<genexpr>r  rf   r   )rT   r  r   r   r  r  rK  rG  r:  r   r  )r&   r   r  r1  r;  r=  r<  r  	hull_datar!  r'  r  rP  rQ  rR  r  r'   r'   r(   r+  {  s&   
z#yolo_model_loder._draw_convex_hullsc                 C   sP  |d }t |}t|D ]\}}|ddsq|di }|d}	|	dur| |}
|
d |
d }}|	j\}}||| }}||| }}td	|t|jd	 |}}td	|t|jd
 |}}||kr||kr|| || }}||ks|||krt|	||f}n|	}| 	|||}t
dd |D }||||||f |d	k< qdS )uT   绘制边界线，直接修改输入图像，每个轮廓使用不同的色移颜色r&  r  Fr  r  Nr   r   r   rj   c                 s   rA  rB  rC  r5  r'   r'   r(   r'    rD  z4yolo_model_loder._draw_boundaries.<locals>.<genexpr>)rT   r  r   rK  r   r   r   r   r   rG  r:  )r&   r   r  r1  r;  r=  r<  r  	mask_dataZboundary_maskr  r   r   rT  rU  rX  rZ  rW  rY  rV  rW  rX  rP  rQ  r'   r'   r(   r,    s2   


z!yolo_model_loder._draw_boundariesc              	   C   s   |d }t |}t|D ]a\}}|ddsq|di }|dg }	|	rm| |}
| |||}tdd |D }tdt |	d	 }td
t |	|D ]!}|	| }|d
 |
d  }|d |
d  }t	|||fd|d qKqdS )uT   绘制轮廓点，直接修改输入图像，每个轮廓使用不同的色移颜色r'  r  Fr  r   c                 s   rA  rB  rC  r5  r'   r'   r(   r'    rD  z8yolo_model_loder._draw_contour_points.<locals>.<genexpr>rj   r4  r   r   r   rf   r   N)
rT   r  r   rK  rG  r:  r   r   r   circle)r&   r   r  r1  r;  r=  r<  r  rN  r   r  rP  rQ  stepr   ZpointrP  r  r'   r'   r(   r-    s&   
z%yolo_model_loder._draw_contour_pointsr  c              	   C   s  |  |||| | ||| |D ]l}|ddsq|di }|dg }|r}t|dkr}tj|tjd}| |}	|dddf  |	d	 7  < |ddd
f  |	d 7  < tt|D ]}
t	||
 }t	||
d
 t|  }| 
||||d d
 q]q|D ]F}|ddsq|di }|r|dd|dd}}|dd}|rdnd}t|||f||d d t|||f|d
 |d d
 qdS )uD   组合绘制（轮廓+凸包+中心点），直接修改输入图像r  Fr  r  rf   r   Nr   r   rj   r   r  r  rP  r  r  r  r   r8  r   r  )r*  r)  r   rT   r   r  r  rK  r   r:  _draw_dashed_liner   r[  )r&   r   r  r1  rS  r  rY  r!  r'  r  r   Zstart_pointZ	end_pointr8  r  r  rR  Zradiusr'   r'   r(   r.    s:   
zyolo_model_loder._draw_combinedc           	      C   s   |  |||| | ||| | ||| | ||| | ||| |D ]8}|dds/q&|di }|r^|dd|dd}}t|||fd|d d	 t|||fd
|d d q&dS )u-   绘制所有类型，直接修改输入图像r  Fr  rP  r   r  re   r8  r   r  r  rj   N)r*  r,  r+  r)  r-  r   r   r[  )	r&   r   r  r1  rS  r  r8  r  r  r'   r'   r(   r/    s   z yolo_model_loder._draw_all_typesc                 C   sd   | di }| di }|r)|r)| dd| dd }| dd| dd }nd\}}||d	S )
u   获取AOI偏移量r  r  rW  r   rP  rX  r  r  rH  rI  )r&   r  rJ  Zrel_bboxr   r   r'   r'   r(   rK  $  s   
pt1pt2r  r  c                 C   s<  ddl }||d |d  d |d |d  d  }d}d}	||k r.t||||| dS t|||	  }
t|
D ]a}|||	  | }|||	  | | }t|d |d |d  |  }t|d |d |d  |  }t|d |d |d  |  }t|d |d |d  |  }t|||f||f|| q:dS )u   绘制虚线r   Nrf   rj   r  r   )mathr  r   linerw   r   )r&   r   r^  r_  r  r  r`  ZdistZdash_lengthZ
gap_lengthZ
num_dashesr   Zstart_ratioZ	end_ratioZstart_xZstart_yZend_xZend_yr'   r'   r(   r]  2  s"   .    z"yolo_model_loder._draw_dashed_linec              
   C   s  ze| dd}| dd}| dd}|dkr]| dg }|t|k r`|| }| di }	|	rc|	 d	d|	 d
d}
}| d| d| dd g}| |||
d |d f| W dS W dS W dS W dS  ty } z| jrz| dt|  |W  Y d}~S d}~ww )u3   添加轮廓标签信息，直接修改输入图像ra  r]   r  r   r  r   r  r  rP  r  z
Contours: zArea: r  r<  r  u   ⚠️ 添加标签失败: N)r   rT   _draw_multi_line_textrF   r   r,   rB   )r&   r   r  r1  rj  Zcontour_countZmain_idxr  Zmain_contourr8  r  r  labelsrK   r'   r'   r(   r0  K  s2   "z$yolo_model_loder._add_contour_labelsr  positionc              
   C   s  t j}d}d}d}|\}	}
d}|D ]}t ||||\\}}}t||}qt|| d }t ||	d |
d f|	| d |
| f|d d t ||	d |
d f|	| d |
| f|d	 d t|D ]\}}|
|d |  }t |||	|f|||d	 | qcd
S )u   绘制多行文本r   rj   r  r   r<  r  r9  r   r  N)r   r  r  r   rT   r  r  r  )r&   r   r  rd  r1  r  r  r  r  rP  r  r  r  r  r  r   r  r   r  r'   r'   r(   rb  k  s(   && z&yolo_model_loder._draw_multi_line_textr  main_aoisub_aoi	tolerancec           H         s  zJt  fdddD rt fdddD sddiW S  d  d  }d d  } d	  d d
   d  d d
  g}d	 d d
  d d d
  g} d dkrc d  d  ntd}d dkrud d  ntd}	 d	  d }
}|
 d  | d  }}d	 d }}|d  |d  }}|d |d  }|d |d  }t|d
 |d
  }ddl}||||}|dk r|d7 }t||k rt||k rd}nt|t|kr|dkrdnd}n|dkrdnd}tdt|
| || }tdt|| || }||d
 |d
  }tt|
| t|| }tt|| t|| }||d
 |d
  }t|
|}t||}t	||} t	||}!|| k oa||!k }"|"rstd| | td|!|  nd}#|#dkr|dkrd}$n"d}$n|#|kr|#|krd}$n|#|krd}$n
|#|krd}$nd}$|| |# }%|%dkr|#|% nd}&|dkr|| ntd}'d dkrш d d  ntd}(d dkr d d  ntd})t||	 }*t|'d dk rd}+n
|'dkrd}+nd }+t|
| |k},t|| |k}-t|| |k}.t|| |k}/t|d |d  |k}0t|d |d  |k}1||
 }2|| }3|| }4|| }5t	|
|}6t	||}7t||}8t||}9|6|7|8|6 |9|7 d}:|"swt|
| || nt	|t|
| t	|||  };|"st|| || nt	|t|| t	|||! }<||k}=||kp||k }>||kp||
k }?||kr|| k rd!}@nG|| k r|| k rd"}@n8|| k r||krd#}@n*||kr||krd$}@nt||krt||krd%}@nt||krd&}@nd'}@|$dkrd}An
|$dkrd}And(}A|'d)kr(d*}Bn
|'d+k r0d,}Bnd-}B|tdks@|	tdkrCd.}Cnt||	 }Dt||	}Edt	d|D|E  }C d	  d  d  d ||t
|d/d0d	 d d d ||t
|	d/d0d1|t
|d
||t
|d
t
|d
t
|dd2|"|$|#t
|dkr|#| ndd/t
|dkr|#| ndd/|%t
|&d/d3t
|'d/t
|(d/t
|)d/t
|*d/|+d4|,|-|.|/|0|1||2|3|4|5d5|:|;|<|=|>|?d6|@|A|Bt
|Cd/d7d8}F| jrI| d9 | d: d	  d; d  d< d  d	 d   | d=d	  d;d  d<d  d	d   | d>| d?|d@dA|$  | dB|2 dC|3 dD|4 dE|5  |FW S  tyu }G z| jrb| dFt|G  ddGt|G iW  Y d}G~GS d}G~Gww )Hux  
        计算两个AOI框的各类数学关系
        
        Args:
            main_aoi: 主框 {'x': int, 'y': int, 'width': int, 'height': int}
            sub_aoi: 副框 {'x': int, 'y': int, 'width': int, 'height': int}
            tolerance: 对齐判断的容差（像素）
            
        Returns:
            dict: 包含各类数学关系的字典
        c                 3       | ]}| v V  qd S r  r'   r   r  )re  r'   r(   r'    r(  z?yolo_model_loder.calculate_aoi_relationships.<locals>.<genexpr>r  c                 3   rh  r  r'   ri  )rf  r'   r(   r'    r(  u   错误u   AOI数据格式不完整r]  r^  rP  rf   r  r   infrj   Nih  u   居中u   右侧u   左侧u   下方u   上方u   相切u   分离u   完全重合u   主框包含副框u   副框包含主框u   部分重叠rG  g?u   尺寸相近u   主框更大u   副框更大u   第一象限u   第二象限u   第三象限u   第四象限u   原点u   Y轴u   X轴u	   无包含g?u   主框大于副框g?u   副框大于主框u   大小相等r?  r   )rP  r  r]  r^  rd  u	   中心点u	   长宽比)u   主框u   副框)u   相对位置u   中心距离u   水平距离u   垂直距离u   最近边距离u   最远边距离u   连线角度)u   是否重叠u   重叠类型u   重叠面积u   重叠占主框比例u   重叠占副框比例u   并集面积u	   交并比)u   面积比例u   宽度比例u   高度比例u   长宽比差异u   尺寸关系)u   左边缘对齐u   右边缘对齐u   顶边缘对齐u   底边缘对齐u   水平中心线对齐u   垂直中心线对齐u   对齐容差u   左边距离u   右边距离u   上边距离u   下边距离)u   最小外接矩形u   最小水平间隙u   最小垂直间隙u   是否相邻u   是否同行u   是否同列)u   象限关系u   包含关系u   相对大小u   形状相似度)u   基础信息u   位置关系u   重叠关系u   尺寸比较u   对齐关系u   边界关系u   几何特征u   🔢 计算AOI关系完成:u      主框: (rQ  z) u      副框: (u      关系: u
   , 距离: ru   u
   , 重叠: u      边界距离: 左=u   , 右=u   , 上=u   , 下=u   ❌ 计算AOI关系出错: u   计算出错: )r  r,  r   r  r`  ZdegreesZatan2absr   r   rf  r   r,   rF   rB   )Hr&   re  rf  rg  Z	main_areaZsub_areaZmain_centerZ
sub_centerZmain_aspect_ratioZsub_aspect_ratioZmain_x1Zmain_y1Zmain_x2Zmain_y2Zsub_x1Zsub_y1Zsub_x2Zsub_y2Z	center_dxZ	center_dyZcenter_distancer`  ZangleZrelative_positionZdxZdyZnearest_distanceZfar_dxZfar_dyZfarthest_distancer  r  r  r  Zis_overlappingr  Zoverlap_typeZ
union_arear  Z
area_ratioZwidth_ratioZheight_ratioZaspect_ratio_diffZsize_relationshipZleft_alignedZright_alignedZtop_alignedZbottom_alignedZh_center_alignedZv_center_alignedZleft_distanceZright_distanceZtop_distanceZbottom_distanceZmin_bounding_x1Zmin_bounding_y1Zmin_bounding_x2Zmin_bounding_y2Zmin_bounding_rectZh_gapZv_gapZis_adjacentZsame_rowZsame_columnZquadrantZcontainmentZrelative_sizeZshape_similarityZ
ratio_diffZ	max_ratior   rK   r'   )re  rf  r(   calculate_aoi_relationships  s|  
(($$



&



&&




66





	
R
22"z,yolo_model_loder.calculate_aoi_relationships)NTr  )rf   )r   )r   )r   )r<  )r   )F)NFN)r  r"  )r"  )r  )r  )Vr  r  r  __doc__rB   r0  r)   r,   r   r:  r=   rL   rD   rE   listrQ   rS   rb   rU   r   rw   r   r   r   r   r   r   r   r   r   r   rC   r   r   r   r   r  r   r   r  r   r  r	  r
  r  r  r  r;  r,  rB  rF  rH  rJ  rK  rL  r   rR  rS  rI  rk  rt  ry  rv  r{  r}  r  r  r  r  r  r  r2  r(  rG  rK  r)  r*  r+  r,  r-  r.  r/  r]  r0  rb  rl  r'   r'   r'   r(   r   :   s    	0+08H)$o(#*%@P"7 &61Fw2C3. ;$ *; 2(h   "P06 ,+   r   sam_model_listr  
batch_size
seg_paramsr-   c           #         s^  | r|s| S |du ri }| dd| dd| dd| dd	| d
d	}dd | D }|s:td d	S dd t|D }|sKtd d	S t|}t|}|rctd td| d|  fddddlm}	m}
 t }g }t|D ]\}\}}|| }|| }|	||||f q~g }|	|dd  fdd|D }|
|D ]p}z3|
 }|	| |rt|td|d  dkrt|| d }td|dd t| d!| d" W q ty } z.|| }|\}}}}|	d	||dddd# |rtd$| d%| d&t|  W Y d}~qd}~ww W d   n	1 s)w   Y  t }|| }td'd( |D }t|| }td)d( |D }|rtd* td+| d,| d-|  td.|d/d0 t|dkr|t| } |dkr|| nd}!|dkrt|| nd}"td1| d/d0 td2|!d3d4 td5|"d3d6 |dkS )7u  
    多模型FastSAM分割推理（优化版）
    
    使用ThreadPoolExecutor进行高效的并行处理，减少线程管理和同步开销。
    
    主要优化：
    1. 使用ThreadPoolExecutor替代手动线程管理
    2. 预分配任务，避免队列竞争  
    3. 减少不必要的内存拷贝
    4. 简化日志输出
    
    Args:
        sam_model_list: FastSAM模型实例列表 [yolo_model_loder, ...]
        deleted_list: 检测项列表，每个检测项必须包含'image'字段
        batch_size: 保留参数（兼容性），实际不使用
        seg_params: 分割参数字典
    
    Returns:
        bool: 是否所有任务都成功完成
    Nu   置信度阈值r"  	   IOU阈值r     输入尺寸r   u   高精掩码Fr   c                 S   s    g | ]}t |tr|jr|qS r'   )r   r   r   )r   mr'   r'   r(   r     s
    
z,mulity_sam_run_optimized.<locals>.<listcomp>u"   ❌ 没有可用的已加载模型c                 S   s*   g | ]\}}t |trd |v r||fqS )r   )r   r   )r   r   itemr'   r'   r(   r     s    u   ❌ 没有有效的任务u*   🚀 开始FastSAM并行推理 (优化版)u      可用模型数量: u   , 任务数: c                    s  | \}}}}t  }z{t  }tt|d jdd }|d jdd \}}	|j|d |j| ddd}
t  | }d}d}d}|
r`|
d jdur`|
d jj}t	|}|dkrL|g t
|d|d	|||	fdd
}tj||ddd}d}t|D ]7}||   }||  }||7 }|dkr|jdd |d< ||t||jt|jdd}|d | q|dkrt| }d	|d | d< ||d< t||  |d< t||d< t
|| d|d< dd |D |d< ||d< ||  }||   |d< t||d< d	|d< t
|d|d< ||d< ||d< d |v rE|d  d!d|d  d"d }t
|t|d d|d#< |d# |d$< d	}t|}n'dg t
|d|dd%|||	fdd&	|d< ndg t
|d|dd'|||	fdd&	|d< |s|dddt
|d||d( W nN ty } zAdg d|dt|d)t v r|ndd*t v rd+t v r||	fnddd&	|d< |dddt||d, d}d}d}W Y d}~nd}~ww t  | }||||||d-S ).u   处理单个分割任务r   Nrf   r<  F)r   Zretina_masksr   r   r  r  r   r   re   T)r  r  r   processing_workerr   r  r  r  r   rj   )r   r  )r  r  r  Z
mask_shapeZ
mask_dtyper  r  r  r  Zlargest_mask_areatotal_mask_areaZaverage_mask_areac                 S   s   g | ]}t | qS r'   )rw   ru  )r   ri  r'   r'   r(   r   ,  s    zImulity_sam_run_optimized.<locals>.process_single_task.<locals>.<listcomp>
mask_areasr     分割掩码   掩码面积   有精细分割   分割推理时间   分割掩码数量   处理workerrb  r]  r^  u   掩码覆盖率Zlargest_mask_coverageu   找到掩码但都无效)	r  r  r   rv  r   r   r  r  r  u   未检测到分割掩码)ry  rz  r{  r|  r}  r~  seg_imgsz_curr  r  )ry  rz  r{  u   分割错误r~  r   task_id	worker_idr  r   Ztotal_task_time)rR   r   r   r   r   r`   r   r  r1  rT   rf  r4   r   viewr   r.   r   ru  rw   rB   r   r   r  r   r~  rF   locals)r   r  detect_itemr`   r  Ztask_start_timeZinference_startr  r  r  Zseg_resultsr   r   r  r  r  r  rx  rw  r  rZ  Zmask_area_singler  Zlargest_idxZlargest_areaZbox_arearK   Ztask_total_time)seg_confidence_threshold	seg_imgszseg_iou_thresholdseg_retina_masksr'   r(   process_single_task  s  

 

	"
z5mulity_sam_run_optimized.<locals>.process_single_taskr   r   zFastSAM-Worker)Zmax_workersZthread_name_prefixc                    s   i | ]	}  ||qS r'   )Zsubmit)r   r   )executorr  r'   r(   
<dictcomp>  s    z,mulity_sam_run_optimized.<locals>.<dictcomp>rj   r<  r3  u   📊 进度: z.0fz% (r   r   r  u   ❌ Worker-u    处理任务 r@  c                 s   s    | ]	}|d  rdV  qdS )r   rj   Nr'   r   rr'   r'   r(   r'    r  z+mulity_sam_run_optimized.<locals>.<genexpr>c                 s   s    | ]}|d  V  qdS )r   Nr'   r  r'   r'   r(   r'    r(  u&   
📊 FastSAM推理完成 (优化版):u      总任务: u
   , 成功: u
   , 失败: u      总耗时: r   rO   u      平均推理时间: u      理论加速比: rN   rP  u      实际吞吐量: u    任务/秒)r   r+   r  rT   concurrent.futuresr	   r
   rR   r   r   r   r   rF   rB   r   )#ro  r  rp  rq  r   Zavailable_modelsZvalid_tasksZtotal_tasksZnum_workersr	   r
   rW   Z	task_argsr   r  r  Z	model_idxr`   r   Zfuture_to_taskZfuturer   ZprogressrK   Ztask_args_for_futurer   r  Zend_timeZ
total_timeZsuccess_countZfailed_countZtotal_inference_timeZavg_inference_timeZtheoretical_speedupZ
throughputr'   )r  r  r  r  r  r  r(   mulity_sam_run_optimized  s    <
"	r  detect_model_listdetect_paramsc              
   C   s  ddl }| r
|du rdS |du ri }|dd}|dd}|dd}|d	d
}d}	d}
t| D ]0\}}|du r;q2t|drT| sS|}	|}
|rQtd|   nq2|}	|}
|rbtd| d  |	du rtd}t| D ]1\}}|du rxqoz#t|dr| }|dd}||k r|}|}	|}
n|}	|}
W  nW qo   Y qo|	r|rtd|
 d|dd |	du r|rtd | D ]5}|dur|}	| |}
t|drz|j	|d |rtd|
 d W n   |rtd|
 d Y  nq|	du r|rtd dS z4|rtd|
 d  |
 }|	j||	j||dd!}|
 }|| }|r1td"|
 d#|d$d% |W S  tyV } z|rKtd&|
 d't|  W Y d}~dS d}~ww )(u  
    多模型YOLO检测推理（优化版）- 负载均衡模式
    
    从多个检测模型中挑选一个不忙的模型来处理单个图像
    
    Args:
        detect_model_list: YOLO检测模型实例列表 [yolo_model_loder, ...]（已初始化完成）
        src_image: 输入图像 (np.ndarray)
        batch_size: 保留参数（兼容性），实际不使用
        detect_params: 检测参数字典
    
    Returns:
        model_results: 检测结果，如果所有模型都忙则返回None
    r   Nrr  r  rs  r   r   Fu   最大等待时间g      @r   is_model_busyu   🎯 选择空闲模型 u   🎯 选择模型 u    (无状态管理)rj  
get_statusZtask_durationu   🎯 选择负载最轻模型 u
    (负载: r   zs)uC   ⚠️ 所有模型都忙，等待或使用第一个可用模型...wait_until_idle)Ztimeoutu   ✅ 模型 u    已变为空闲u   ⚠️ 等待模型 u    空闲超时，强制使用u   ❌ 没有可用的检测模型u   🚀 使用模型 u    进行检测推理)r   r  r   r   u   ⚡ 模型 u    推理完成，耗时: r   rO   u   ❌ 模型 u    推理失败: )rR   r   r  ra   r  r+   r,  r  r   r  r   r  r   rF   rB   Zmodel_results_list)r  Z	src_imagerp  r  rR   Zmodel_iou_thresholdr   r   Zmax_wait_timeZavailable_modelZselected_model_idxr   r`   Zmin_loadr   Zcurrent_loadZmodel_startrg  Z	model_endr   rK   r'   r'   r(   mulity_detect_run_optimized  s   



r  r  )%rm  rer   r   r   typingr   r   r   rR   sysr   Zpathlibr   r   shutilZ	threadingqueuer4   Ztorchvision.transformsZ
transformsZPILr   r  r	   r
   r_   r   r^   r   r   rn  rw   r   r0  r  r  r'   r'   r'   r(   <module>   sz                                    ;