o
    Hi9                    @   s  d Z ddlZddlZddlmZ ddlZddlZddlm	Z	m
Z
mZmZ ddl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 ZdddZdd Zdedee	ee
f  fddZde	ee
f dedefddZdde
dededefddZdedeej fddZdejdedefddZ dedefd d!Z!dejde	ee
f fd"d#Z"d$ede	ee
f fd%d&Z#d$edee	ee
f  fd'd(Z$dd$ed)e
de
fd*d+Z%dd,d-Z&dd.d/Z'dd0d1Z(dd2d3Z)d4d5 Z*d6d7 Z+d8d9 Z,d:d; Z-d<d= Z.d>d? Z/ddDdEZ0ddHdIZ1	F	@	ddJdKZ2dLdM Z3dNdO Z4dPdQ Z5dRd	dCdSdSdSdRdSdTi f
dUdVZ6dRdWdWdXd	dCdSdSdSdRdSi fdYdZZ7dRdWd	dXd	dCdSdSdSdRdSi fd[d\Z8dRdWd	d	dCdSdSdSdRdSi fd]d^Z9dRd	dCdSdTdSdSdRd_i f
d`daZ:dRd	dCdSdbdSdSdRd_i f
dcddZ;dedf Z<dgdh Z=	R			C	S	TddidjZ>	R			C	S	bddkdlZ?ddodpZ@ddqdrZAddsdtZBddwdxZCddydzZDdd{d|ZEdd}d~ZFdddZGdddZHdddZIdd ZJ	@dddZK	dddZLdddZMdddZNdddZO	@	dddZPdd ZQdd ZRg dfddZSdddZTd ddZUdddZV		dddZWdddZXdddZYdddZZdddZ[dddZ\dddZ]dddZ^dd Z_dd Z`dddZadddZbdddÄZcdededfddǄZedeffddʄZgddededfdd̄Zhddd΄ZiddЄ Zj		ddd҄Zk		dddԄZld	dejdemdendeddedejfddڄZod
dejdemdedejfdd܄Zp	@		nddd߄Zq		ndddZrdddZsddejdemdejfddZtdS (  u   
公用工具函数模块
    N)property_interning_dict)DictAnyOptionalList)Image	ImageDraw	ImageFontc                 C   s4   | ds	d| }tj| d| }t|}|S )u8   
    使用glob模块读取指定后缀名的文件
    .*)
startswithospathjoinglob)Z	directory	extensionpatternZ	file_list r   
.\utils.pyget_files_by_extension_glob   s
   

r   C://Windows//Fonts//simsun.ttc      r   r   c           
      C   sT   t t| tj}t|}t||}|j	||||d tt
|tj}	|	S )u.   
    在OpenCV图像上添加中文文字
    )fontfill)r   Z	fromarraycv2cvtColorZCOLOR_BGR2RGBr   ZDrawr	   ZtruetypetextnparrayZCOLOR_RGB2BGR)
imgr   ZpositionZ	font_pathZ	font_sizecolorZimg_pilZdrawr   Zimg_cvr   r   r   put_Text_cn&   s   
r#   c              
   C   s~   t j| sdt j| v rt j| }n| }|r=t j|s=z
t j|dd W dS  ty< } zW Y d}~dS d}~ww dS )u3   检测路径是否存在，不存在则逐级创建r
   T)exist_okNF)r   r   isfilebasenamedirnameexistsmakedirs	Exception)	file_pathZdir_pather   r   r   ensure_path_exists;   s   r-   r+   returnc              
   C   sv   z t | ddd}t|W  d   W S 1 sw   Y  W dS  ty: } ztd|  W Y d}~dS d}~ww )u   
    加载JSON文件为字典
    
    Args:
        file_path: JSON文件路径
        
    Returns:
        Dict: JSON数据字典，失败返回None
    rutf-8encodingNu   加载JSON文件失败: )openjsonloadr*   print)r+   fr,   r   r   r   load_json_fileO   s   
(r8   datac              
   C   s   z%t |ddd}tj| |ddd W d   W dS 1 sw   Y  W dS  ty? } ztd	|  W Y d}~dS d}~ww )
u   
    保存字典到JSON文件
    
    Args:
        data: 要保存的数据字典
        file_path: 保存路径
        
    Returns:
        bool: 是否保存成功
    wr0   r1   F   Zensure_asciiindentNT   保存JSON文件失败: r3   r4   dumpr*   r6   )r9   r+   r7   r,   r   r   r   save_json_filea   s   
rA   r0   filepathr2   c              
      s    fdd z) | }t |d|d}tj||ddd W d   W d	S 1 s(w   Y  W d	S  tyI } ztd
|  W Y d}~dS d}~ww )us  
    保存JSON文件的增强版本，支持不可序列化的数据
    对于不可序列化的对象，会使用pickle进行序列化并转换为base64字符串
    
    Args:
        data: 要保存的数据
        filepath: 文件路径
        encoding: 文件编码，默认为utf-8
    
    Returns:
        bool: 保存成功返回True，失败返回False
    c                    s   zt |  | W S  ttfyu   t| tr# fdd|  D  Y S t| ttfr5 fdd| D  Y S t| t	rGd fdd| D i Y S zt
| }t|d}|t| jdW  Y S  tyt   t| t| jd	 Y  Y S w w )
u+   递归处理对象，使其可JSON序列化c                    s   i | ]	\}}| |qS r   r   ).0kvmake_serializabler   r   
<dictcomp>   s    z@save_json_file_ex.<locals>.make_serializable.<locals>.<dictcomp>c                       g | ]} |qS r   r   rC   itemrF   r   r   
<listcomp>       z@save_json_file_ex.<locals>.make_serializable.<locals>.<listcomp>__set__c                    rI   r   r   rJ   rF   r   r   rL      rM   ascii)Z__pickled____type__)__str__rP   )r4   dumps	TypeError
ValueError
isinstancedictitemslisttuplesetpicklebase64Z	b64encodedecodetype__name__r*   str)objZpickledZencodedrF   r   r   rG      s$   



z,save_json_file_ex.<locals>.make_serializabler:   r1   Fr;   r<   NTr>   r?   )r9   rB   r2   Zserializable_datar7   r,   r   rF   r   save_json_file_ext   s   
rb   c              
   C   s   z,t j| t jd}|  dd }|dv r#t|tjtjB }|W S t|tj	}|W S  t
yF } ztd|  W Y d}~dS d}~ww )u   
    加载浮点数图像文件（支持中文路径）
    
    Args:
        file_path: 图像文件路径
        
    Returns:
        numpy.ndarray: 浮点数图像数组，失败返回None
    dtyper
   )ZexrZhdrtifftifu   加载浮点数图像失败: N)r   Zfromfileuint8lowersplitr   ZimdecodeZIMREAD_ANYDEPTHZIMREAD_ANYCOLORZIMREAD_COLORr*   r6   )r+   Zimg_dataextr!   r,   r   r   r   load_image_unicode   s   
rl   imagec              
   C   sp   zt j|d }t|| \}}|r|| W dS W dS  ty7 } ztd|  W Y d}~dS d}~ww )u   
    保存图像文件（支持中文路径）
    
    Args:
        image: 图像数组
        file_path: 保存路径
        
    Returns:
        bool: 是否保存成功
       TF   保存图像失败: N)r   r   splitextr   imencodetofiler*   r6   )rm   r+   rk   successencoded_imgr,   r   r   r   save_image_unicode   s   
ru   c                 C   s   t j| ot j| S )u   
    验证文件是否存在
    
    Args:
        file_path: 文件路径
        
    Returns:
        bool: 文件是否存在
    )r   r   r(   r%   )r+   r   r   r   validate_file_exists   s   
rv   c                 C   sN   | du r	dddS | j }d|d |d t|dkr|d ndt| j| jd	S )
u~   
    获取图像信息
    
    Args:
        image: 图像数组
        
    Returns:
        Dict: 图像信息字典
    NFu   图像为空)validerrorTr   rn   r;   )rw   heightwidthchannelsrd   size)shapelenr`   rd   r|   )rm   r}   r   r   r   get_image_info   s   

r   r   c           	   	      s  ddl  ddl}d}||| |j|jB }|D ]}z
 | W   S   jy-   Y qw d}||| |j}|D ]}z
 | W   S   jyP   Y q:w d}||| |j}|D ]}z
 | W   S   jys   Y q]w  fdd}|| }|r|d S i S )u  
    从字符串中提取JSON格式的内容并转换为字典
    
    Args:
        text (str): 包含JSON内容的字符串
        
    Returns:
        Dict[str, Any]: 解析后的字典，如果没有找到有效JSON则返回空字典
        
    Examples:
        >>> text = "这是一段文字```json
{"破损-1": {"位置": [100, 200]}}
```更多文字"
        >>> result = extract_json_from_string(text)
        >>> print(result)
        {'破损-1': {'位置': [100, 200]}}
    r   N```json\s*\n?(.*?)\n?``````\s*\n?(.*?)\n?```z\{[^{}]*(?:\{[^{}]*\}[^{}]*)*\}c              	         g }g }d}t | D ]C\}}|dkr|s|}|| q
|dkrM|rM|  |sM|dkrM| ||d  }z |}|| W n
  jyJ   Y nw d}q
|S )u   递归查找JSON对象re   {}rn   	enumerateappendpoploadsJSONDecodeError)sresultsstackstarticharjson_strra   r4   r   r   find_json_objects4  s,   
z3extract_json_from_string.<locals>.find_json_objects)r4   refindallDOTALL
IGNORECASEr   stripr   )	r   r   json_block_patternmatchesmatchcode_block_patternZbrace_patternr   Zjson_objectsr   r   r   extract_json_from_string  s>   r   c              	      s   ddl  ddl}g }d}||| |j|jB }|D ]}z| |  W q  jy1   Y qw |sYd}||| |j}|D ]}z| |  W q@  jyX   Y q@w |se fdd}|| }|S )u   
    从字符串中提取所有JSON格式的内容
    
    Args:
        text (str): 包含JSON内容的字符串
        
    Returns:
        List[Dict[str, Any]]: 所有解析成功的JSON字典列表
    r   Nr   r   c              	      r   )Nre   r   r   rn   r   )r   Zobjectsr   r   r   r   r   ra   r   r   r   find_all_json_objectsy  s,   
z;extract_all_json_from_string.<locals>.find_all_json_objects)	r4   r   r   r   r   r   r   r   r   )r   r   r   r   r   r   r   r   r   r   r   extract_all_json_from_stringS  s0   
r   default_valuec                 C   s0   zt | }|r
|W S |W S  ty   | Y S w )u  
    安全地从字符串中提取JSON，失败时返回默认值
    
    Args:
        text (str): 包含JSON的字符串
        default_value (Any): 提取失败时的默认返回值
        
    Returns:
        Any: 提取的JSON数据或默认值
    )r   r*   )r   r   resultr   r   r   safe_extract_json  s   r   c           
      C   s  ddddt jd}|du ri }i ||}t| jdkr#t | t jn| }|d dkr9t j|t jd	d
|d d}n9|d dkrMt j|t jd
d	|d d}n%t j|t jd	d
|d d}t j|t jd
d	|d d}t	|d |d  }t
t|}t ||d |d |d \}}	|	S )  
    使用Sobel算子检测特定方向的边缘
    
    Args:
        image: 输入图像
        params: 参数字典，包含以下键值:
            - direction: str, 检测方向 ('horizontal', 'vertical', 'both')
            - ksize: int, Sobel核大小 (1, 3, 5, 7)
            - threshold: int, 二值化阈值 (0-255)
            - max_value: int, 二值化最大值 (0-255)
            - threshold_type: int, 阈值类型 (cv2.THRESH_BINARY, cv2.THRESH_BINARY_INV等)
    
    Returns:
        edges: 检测到的边缘图像
    both   2   r   )	directionksize	threshold	max_valuethreshold_typeNr   
horizontalrn   r   r   r   verticalr;   r   r   r   r   THRESH_BINARYr~   r}   r   COLOR_BGR2GRAYSobelCV_64Fr   sqrtrh   absoluter   
rm   paramsdefault_paramsfinal_paramsgraysobelsobelxsobely_edgesr   r   r   detect_edges_by_direction  s,   	 r   c           
      C   s  ddddddt jd}|du ri }i ||}t| jdkr%t | t jn| }|d	 dkr;t j|t jd
d|d d}n9|d	 dkrOt j|t jdd
|d d}n%t j|t jd
d|d d}t j|t jdd
|d d}t	|d |d  }t
t|}t ||d |d |d \}}	|	S )r   r   r   Tr   Fr   r   r   	normalizer   apply_thresholdr   r   Nr   rn   r   r   r   r   r;   r   r   r   r   r   r   r   r   detect_edges_gradient  s0   
 r   c                 C   s  ddddddt jd}|du ri }i ||}t| jdkr%t | t jn| }|d	 dkr;t j|t jd
d|d d}nt j|t jdd
|d d}t	|dk|d}|d rm|
 dkrgt||
  d }nt|}n
tt|dd}|d rt ||d |d |d \}}|S )u  
    检测从暗到亮的变化（正梯度）
    
    Args:
        image: 输入图像
        params: 参数字典，包含以下键值:
            - direction: str, 检测方向 ('horizontal', 'vertical')
            - ksize: int, Sobel核大小 (1, 3, 5, 7)
            - normalize: bool, 是否归一化到0-255
            - threshold: int, 可选的二值化阈值
            - apply_threshold: bool, 是否应用二值化
    
    Returns:
        positive_edges: 正梯度边缘图像
    r   r   Tr   Fr   r   Nr   rn   r   r   r   r   r   r   r   r   r   r   r~   r}   r   r   r   r   r   wheremaxrh   clipr   )rm   r   r   r   r   r   positive_edgesr   r   r   r   detect_positive_gradient  s4    r   c                 C   s  ddddddt jd}|du ri }i ||}t| jdkr%t | t jn| }|d	 dkr;t j|t jd
d|d d}nt j|t jdd
|d d}t	|dk | d}|d rn|
 dkrht||
  d }nt|}n
tt|dd}|d rt ||d |d |d \}}|S )u  
    检测从亮到暗的变化（负梯度）
    
    Args:
        image: 输入图像
        params: 参数字典，包含以下键值:
            - direction: str, 检测方向 ('horizontal', 'vertical')
            - ksize: int, Sobel核大小 (1, 3, 5, 7)
            - normalize: bool, 是否归一化到0-255
            - threshold: int, 可选的二值化阈值
            - apply_threshold: bool, 是否应用二值化
    
    Returns:
        negative_edges: 负梯度边缘图像
    r   r   Tr   Fr   r   Nr   rn   r   r   r   r   r   r   r   r   r   )rm   r   r   r   r   r   negative_edgesr   r   r   r   detect_negative_gradientQ  s4    r   c              
   C   s   zd|  dd }|dv rtjdg}td| |\}}n;|dkr0tjdg}td| |\}}n(|d	v r=td
| \}}n|dv rJtd| \}}ntjdg}td| |\}}|rb|| W dS W dS  ty~ } ztd|  W Y d}~dS d}~ww )u   
    保存图像到中文路径
    
    Args:
        image: OpenCV图像数组
        file_path: 包含中文的文件路径
    
    Returns:
        bool: 是否保存成功
    r
   re   )ZjpgZjpeg_   z.jpgZpngr   z.png)Zbmpz.bmp)rg   rf   z.tiffTFro   N)	ri   rj   r   ZIMWRITE_JPEG_QUALITYrq   ZIMWRITE_PNG_COMPRESSIONrr   r*   r6   )rm   r+   rk   Zencode_paramrs   rt   r,   r   r   r   save_image_chinese_path  s.   



r   c                  C   s6   t  } td |  dD ]	}td|  qd S )Nu   详细错误堆栈:
  )	traceback
format_excr6   r   rj   )Zerror_tracebackliner   r   r   error_message_show  s
   r   c           	      C   sD   | d }| d }| d }| d }|}|}|| }|| }||||fS Nr   rn   r;   r   r   )	rectxyrz   ry   x1y1x2y2r   r   r   xyxy_from_xywh  s   r   c           	      C   sL   | d }| d }| d }| d }|}|}t || }t || }||||fS r   )abs)	r   x0y0r   r   r   r   r:   hr   r   r   xywh_from_xyxy  s   r   c                 C   s,   | d }| d }| d }| d }||||fS )u   
    从AOI字典中获取xywh格式的坐标
    
    Args:
        aoi: AOI字典，包含'x', 'y', 'width', 'height'
        
    Returns:
        tuple: (x, y, width, height)
    r   r   rz   ry   r   )aoir   r   rz   ry   r   r   r   xywh_from_aoi  s
   
r   c                 C   s   t t| S N)r   r   )r   r   r   r   xyxy_from_aoi  s   r   
        r   c                 C   s  | sdS | D ]} i }| d d }| d d }| d d }	| d d }
tt|| | d }t|D ]}dg || | i d|| < q3d}i }|D ]}d}| D ]}|d }|d }t|| }|| d }tt|t| d }t|}t|||  }|| d	kr nT||k r|}||| d
 vri || d
 |< ||| d
 | d< g || d
 | d< || d
 | d | qP|| d
 | d  |7  < || d
 | d | qPt|| d
 |krt|| d
 }t|| d
 	 dd d}||| d< || }qJ|S )u  
    对一组数值进行投票，返回最常见的数值及其出现次数
    
    Args:
        dataList: 数值列表
        diff: 允许的数值差异范围，默认10
        
    Returns:
        (most_common_value, count): 最常见的数值及其出现次数
    Nr   r   rn   re   )valueZdatascur_votediff_collectr         ?r;   r   nolinesc                 S   s   | d d S )Nrn   r   r   r   r   r   r   <lambda>,  s    zvote_for_diff.<locals>.<lambda>keyZlineList)
r6   intr   rangefloatr`   r   r~   sortedrW   )dataListdiffZmin_voteZmax_voteZ	vote_diffr9   Z	vote_dictZstart_valueZ
start_dictZ	end_valueZend_dictZnum_of_voter   Zcur_maxZcur_dictZ
vote_paramZold_invr   Z	interDictZcur_deccur_diffZcur_invZcur_strZcur_modZsorted_listr   r   r   vote_for_diff  s^   
r   r;      c                 C   s  g }g }| D ]'}|d }t |d ||r-t|}	|	d |	g  |	d ||	 qtt|D ]S}
||
 }d}t|
d t|D ]A}|| }t|d |d  }t|| }||k r||d k r|}||d< ||k rg }|t| ||d  |d | qEq4g }tt|D ]<}
||
 }t|d dkr|d jdd d	 |d }|d d d }|d d d }|||g|t|t|d
 q|jdd d	 t	 }g }|D ]#}|d }|d }||vr||vr||d  |
| |
| q|S )u  
    对一组线段进行投票，返回与参考线平行，且满足距离要求的线段组
    过滤重复使用的线段，保留最接近目标宽度的线段对

    Args:
        dataList: 线段列表，每个元素为 (x0, y0, x1, y1)
        line_info0: 参考线段信息
        arg_area: 允许的面积差异范围，默认2
        target_width: 目标宽度，默认180
        target_area: 目标宽度容差，默认10
        
    Returns:
        list: 最优的线段对列表，无重复使用的线段
    rn   逖 re   r   r;   r   c                 S      | d S Nr   r   r   r   r   r   r   l      z#vote_for_parallel.<locals>.<lambda>r   )	line_pairr   	line0_key	line1_keyc                 S   r   )Nr   r   r   r   r   r   r   y  r   r  r  r  )calculate_lines_parallelismcopyr   r   r~   r   r   sortrY   rZ   add)r   
line_info0arg_areatarget_widthtarget_arearesult_lines	dataList0	line_infor   line_info_tempmark_itr	mark_infomin_diffcur_itrcur_info	cur_widthr   cur_info_tempZcandidate_pairsline0line1Z
diff_valueZ
used_linesZfiltered_pairsZ	pair_infor  r  r   r   r   vote_for_parallel8  sp   






r  c                 K   s  g }g }| D ]"}	|	d }
t |d |
|r(t|	}|d |g  || qtt|D ]S}|| }d}t|d t|D ]A}|| }t|d |d  }t|| }||k r||d k r|}||d< ||k rg }|t| ||d  |d | q@q/|jdd d tt|D ]1}|| }t|d dkr|d jd	d d |d }|d d d }||g}t| || q|S )
u  
    对一组线段进行投票，返回与参考线平行，且满足距离要求的线段组

    Args:
        dataList: 线段列表，每个元素为 (x0, y0, x1, y1)
        line0: 第一条线段
        line1: 第二条线段
        arg_area: 允许的面积差异范围，默认2
        target_width: 目标宽度，默认200
        target_area: 目标宽度容差，默认2
        
    Returns:
        (most_common_line, count): 最常见的线段及其出现次数
    rn   r   r   r;   r   c                 S   r   Nr;   r   r   r   r   r   r     r   z/vote_for_parallel_func_filter.<locals>.<lambda>r   c                 S   r   r   r   r   r   r   r   r     r   )	r  r  r   r   r~   r   r   r  r6   )r   r  r	  r
  r  funcZfunc_paramsr  r  r  r   r  r  r  r  r  r  r  r   r  r  r  Z	line_coper   r   r   vote_for_parallel_func_filter  sP   




r  c                 C   s@   |d }|d }||d  }||d  }| ||||f }|S Nr   r   rz   ry   r   )rm   r   r   r   r   r   	aoi_imager   r   r   get_cv_img_fronm_aoi  s   r  c                 C   s   | du s|du r
dS | j dd \}}|d }|d }||d  }||d  }td|}td|}	t||}
t||}|
|ksC||	krEdS | |	|||
f }|S )u<  
    从图像中根据AOI提取子图像，如果AOI超过图像边界则取最大重叠部分
    
    Args:
        image: 输入图像 (numpy.ndarray)
        aoi: AOI字典，包含'x', 'y', 'width', 'height'
        
    Returns:
        numpy.ndarray: 提取的子图像，如果没有重叠则返回None
    Nr;   r   r   rz   ry   r   )r}   r   min)rm   r   
img_height	img_widthr   r   r   r   Z
overlap_x0Z
overlap_y0Z
overlap_x1Z
overlap_y1r  r   r   r   get_cv_img_fronm_aoi_ex  s   



r#  c                 C   s(   i }| |d< ||d< ||d< ||d< |S r  r   )r   r   rz   ry   r   r   r   r   get_aoi_xywh  s   r$  rn      leftc              	   C   sH  |}|}|}g |
d< t |D ]}|dk s|dk r"|
d d  d S |
d d| d| d|  d }t|}t|}t|}|	dkrOt| ||||dd}n7|	d	kr^t| ||||d	d}n(|	d
krmt| ||||d
d}n|	dkr|t| ||||dd}n
t| ||||dd}|d u r||8 }||8 }||7 }|
d d q|  S d S )N   尝试信息r   u'   线段尝试参数过低，停止尝试u(   开始尝试：当前参数 : threshold=, minLineLength=, maxLineGap=r&  )dirtyperightupdownu*   未找到线段，降低线段检测参数)r   r   r   C_HoughLinesP_find_verticalC_HoughLinesP_find_horizontal)rm   rhor   minLineLength
maxLineGapthreshold_diffminLineLength_diffmaxLineGap_diff	try_timesZdir_typereturn_dictZcur_thresholdZcur_minLineLengthZcur_maxLineGap	try_indexZcur_liner   r   r   C_Find_Line_Who_Strong  sP   ' r9  d      c                 C   s  |}|}|}|}|}|}d}d}g }g }g |d< t |D ]}|dk s+|dk s+|dk r5|d d  n|dk sA|dk sA|dk rK|d d  np|d d| d| d|  |d d	| d| d|  t| dt|t|t|d
d}t| dt|t|t|dd}|d u r||8 }||	8 }||
7 }|d d |d u r||8 }||	8 }||
7 }|d d |d u s|d u rqt|d d |d d  }||k r|d d| d| d| d ||d 8 }||	d 8 }||
7 }||d 8 }||	d 8 }||
7 }n6||kr=|d d| d| d| d ||d 7 }||	d 7 }||
d 8 }||7 }||	d 7 }||
7 }t|| |kr|d d| d| d| d| d| 
 |d d| d| d|  |d d| d| d|  |d |d< t|| }||k r|}g }g }||g}|d| d| d|  |d| d| d|  ||k r nq||d< t|dkrd|d< |D ]
}|d | q|S d |d< |d d! g }|S )"Nr   r'  rn   u0   上边沿检测检测参数过低，停止尝试u0   下边沿线段检测参数过低，停止尝试u1   开始尝试：上边沿当前参数 : threshold=r(  r)  u1   开始尝试：下边沿当前参数 : threshold=r,  r0  r   r1  r2  r*  r-  <   未找到上边沿线段，降低上边沿线段检测参数<   未找到下边沿线段，降低下边沿线段检测参数r   u   当前高度 u    小于目标高度    ±,   ,可能是重要线段被忽略,继续尝试r;   u    大于目标高度 ,   ,可能是干扰线段被记录,继续尝试u   成功找到上边沿线段 u    和下边沿线段 u   ,高度 u    符合目标高度 u"   上边沿最终参数 : threshold=u"   下边沿最终参数 : threshold=   尝试次数   成功   结果   失败3   所有尝试均未成功找到符合要求的线段)r   r   r/  r   r   r~   )rm   r0  ry   areamin_arear   r1  r2  r3  r4  r5  r6  r7  Zup_thresholdZdown_thresholdZup_minLineLengthZdown_minLineLengthZup_maxLineGapZdown_maxLineGapr   Zmin_DiffZ	cur_linesZ
info_linesr8  Zup_lineZ	down_lineZ
cur_heightinfo	line_listr   r   r   C_Find_Height_Auto_LikeD  s     


"

",  

rK  c           !      C   s.  t | t j}i }t||||||||||	|
||d}d|v r@g |d< |d D ]}|dddd}|dd}|d | q&d	|v rJ|d	 |d	< d
|v rT|d
 |d
< |sXg S g }| jdd \}}|jdd \}}|D ]&}|d }|\}}}}|}|d | }|}|d | } ||||| gg qn|S )u|  
    自动查找符合指定宽度的左右边沿线段
    通过旋转图像90度调用C_Find_Height_Auto_Like来实现
    
    Args:
        image: 输入图像
        rho: 距离分辨率，默认1
        width: 目标宽度，默认100
        area: 宽度容差，默认30
        min_area: 最小容差，默认20
        threshold: 初始累加器阈值，默认30
        minLineLength: 初始最小线段长度，默认50
        maxLineGap: 初始最大线段间隙，默认5
        threshold_diff: 阈值递减步长，默认5
        minLineLength_diff: 最小线段长度递减步长，默认5
        maxLineGap_diff: 最大间隙递增步长，默认1
        try_times: 最大尝试次数，默认5
        return_dict: 返回详细信息的字典
        
    Returns:
        list: 包含左右边沿线段的列表 [left_line, right_line]，失败返回空列表
    )r0  ry   rG  rH  r   r1  r2  r3  r4  r5  r6  r7  r'  u	   上边沿u	   左边沿u	   下边沿u	   右边沿u   高度u   宽度rB  rD  Nr;   r   rn   )r   rotateZROTATE_90_CLOCKWISErK  replacer   r}   )!rm   r0  rz   rG  rH  r   r1  r2  r3  r4  r5  r6  r7  Zrotated_imageZinternal_return_dictZheight_linesrI  Zconverted_infoZoriginal_linesZoriginal_heightZoriginal_widthZrotated_heightZrotated_widthZ
line_groupr   r   r   r   r   Zoriginal_x1Zoriginal_y1Zoriginal_x2Zoriginal_y2r   r   r   C_Find_Width_Auto_Like  sR   rN  c                 C   s  |}|}|}|}|}|}g |d< t |
D ]x}|dk s#|dk s#|dk r-|d d  na|dk s9|dk s9|dk rC|d d  nK|d d| d| d|  |d d| d| d|  t| dt|t|t|d	d
}t| dt|t|t|dd
}|d u r||8 }||8 }||	7 }|d d |d u r||8 }||8 }||	7 }|d d |d u s|d u rqt|d d |d d  }||| k r|d d| d| d| d ||d 8 }||d 8 }||	7 }||d 8 }||d 8 }||	d 7 }q||| kr>|d d| d| d| d ||d 7 }||d 7 }||	d 8 }||7 }||d 7 }||	d 8 }q|d d| d| d| d| d| 
 |d d| d| d|  |d d| d| d|  |d |d< d|d< g }|| || |  S |d d| d| d|  |d d| d| d|  |
|d< d|d< |d d  g }|S )!Nr'  rn   u0   右边沿检测检测参数过低，停止尝试u0   左边沿线段检测参数过低，停止尝试u1   开始尝试：右边沿当前参数 : threshold=r(  r)  u1   开始尝试：左边沿当前参数 : threshold=r+  r<  r&  r=  r>  r   u   当前宽度 u    小于目标宽度 r?  r@  r;   u    大于目标宽度 rA  u   成功找到右边沿线段 u    和左边沿线段 u   ,宽度 u    符合目标宽度 u"   右边沿最终参数 : threshold=u"   左边沿最终参数 : threshold=rB  rC  rD  rE  rF  )r   r   r.  r   r   )rm   r0  rz   rG  r   r1  r2  r3  r4  r5  r6  r7  Zright_thresholdZleft_thresholdZright_minLineLengthZleft_minLineLengthZright_maxLineGapZleft_maxLineGapr8  Z
right_lineZ	left_liner  rJ  r   r   r   C_Find_Width_Auto_Like0  s     

"",  

  rO  r   c              	   C      t |	D ]B}t| ||||  |||  |||  |d}|d urF|d |
d< |||  |
d< |||  |
d< |||  |
d< d|
d< |  S q|	|
d< d	|
d< d S 
Nr<  rn   rB  r   r1  r2  rC  rD  rE  )r   r.  rm   r0  r   r1  r2  r*  r3  r4  r5  r6  r7  r8  r   r   r   r   %C_HoughLinesP_find_vertical_auto_feedP  (   


	rS  r,  c              	   C   rP  rQ  )r   r/  rR  r   r   r   'C_HoughLinesP_find_horizontal_auto_feedq  rT  rU  c                 C   s|  | j dd \}}|\}}|| }|| }t| j dkrutj||| j d f| jd}t|D ]D}	t|D ]=}
t|	| }t|	d | }t|
| }t|
d | }t| j d D ]}| |||||f }t|||	|
|f< qYq4q.|S tj||f| jd}t|D ]8}	t|D ]1}
t|	| }t|	d | }t|
| }t|
d | }| ||||f }t|||	|
f< qq|S )u"   使用中值的方式进行 resizeNr;   r   rc   rn   )r}   r~   r   zerosrd   r   r   median)rm   target_sizer   r:   target_wtarget_hscale_xscale_yr   r   jstart_yend_ystart_xend_xcregionr   r   r   median_resize  s:   	rd  c                 C   s  | j dd \}}|\}}|| }|| }	t| j dkrtj||| j d f| jd}
t|D ]}t|D ]}t||	 }t|d |	 }t|| }t|d | }t| j d D ]e}| |||||f }t| }t|}|dkr}d|
|||f< qY|dk rt	d|| }nt
||d }|dk rt	d|| }nt
||d }||kr||}}|||d  }t||
|||f< qYq4q.|
S tj||f| jd}
t|D ]}t|D ]}t||	 }t|d |	 }t|| }t|d | }| ||||f }t| }t|}|dkrd|
||f< q|dk r#t	d|| }nt
||d }|dk r7t	d|| }nt
||d }||krH||}}|||d  }t||
||f< qq|
S )u:   使用排序后指定范围平均值的方式进行 resizeNr;   r   rc   rn   r   )r}   r~   r   rV  rd   r   r   r  flattenr   r   mean)rm   rX  Z	start_pntZend_pntr   r:   rY  rZ  r[  r\  r   r   r]  r^  r_  r`  ra  rb  rc  Zsorted_regionZ
region_lenZ	start_idxZend_idxZselected_valuesr   r   r   check_resize  sr   
L




#rg  c           	      C      t j| |tjd |||d}t|dd}|du rdS t|\}}|du r&dS |du r,dS d}|dkr8|d }|S |dkrB|d	 }|S |d
krJ|}|S |d }|S )F  
    使用Hough变换检测垂直线段
    
    Args:
        image: 输入图像
        rho: 距离分辨率
        theta: 角度分辨率
        threshold: 累加器阈值
        minLineLength: 最小线段长度
        maxLineGap: 最大线段间隙
        
    Returns:
        lines: 检测到的线段列表
    r;   thetar   r1  r2  r   angle_toleranceNr&  r   r+  re   all)r   HoughLinesPr   pifilter_vertical_linessort_lines_by_center_x	rm   r0  r   r1  r2  r*  r   center_x_listr   r   r   r   r.    s*   
r.  c           	      C   rh  )ri  r;   rj  r   rl  Nr,  r   r-  re   rn  )r   ro  r   rp  filter_horizontal_linessort_lines_by_center_yrs  r   r   r   r/  E  s*   
r/  Z   Fc                 C   s   | du s
t | dkr|rdg fS dS dd }g }g }| D ]-}|d \}}	}
}|||	|
|}t|| }|dkr;d| }||krI|| || q|rQt|nd}|rY||fS |S )u  
    根据角度筛选CV直线组
    
    Args:
        lines: cv2.HoughLinesP 返回的直线数组 [[[x1, y1, x2, y2]], ...]
        target_angle: 目标角度（度），默认90度（垂直线）
        angle_tolerance: 角度容差（度），默认±10度
        return_angles: 是否同时返回每条线的角度信息，默认False
        
    Returns:
        filtered_lines: 筛选后的直线数组
        line_angles: 如果return_angles=True，则返回(filtered_lines, angles)元组
    Nr   c                 S   D   || krdS || ||   }t |}t |}|dk r |d7 }|S )u*   计算直线与水平线的夹角（度）     V@r   r   r   Zarctandegreesr   r   r   r   Zslope	angle_radZ	angle_degr   r   r   calculate_line_angle  s   

z3filter_lines_by_angle.<locals>.calculate_line_anglerw  r   )r~   r   r   r   r    )r   target_anglerm  Zreturn_anglesr~  filtered_linesZline_anglesr   r   r   r   r   angle
angle_diffr   r   r   filter_lines_by_angley  s*   

r  c                 C      t | d|dS )u   
    筛选垂直线（90度左右）
    
    Args:
        lines: cv2.HoughLinesP 返回的直线数组
        angle_tolerance: 角度容差（度），默认±10度
        
    Returns:
        vertical_lines: 垂直线数组
    rw  r  rm  r  r   rm  r   r   r   rq       rq  c                 C   r  )u   
    筛选水平线（0度或180度左右）
    
    Args:
        lines: cv2.HoughLinesP 返回的直线数组
        angle_tolerance: 角度容差（度），默认±10度
        
    Returns:
        horizontal_lines: 水平线数组
    r   r  r  r  r   r   r   ru    r  ru  -      c                 C   s   t | ||dS )u(  
    筛选对角线（45度或135度左右）
    
    Args:
        lines: cv2.HoughLinesP 返回的直线数组
        target_angle: 目标角度（45或135度）
        angle_tolerance: 角度容差（度），默认±15度
        
    Returns:
        diagonal_lines: 对角线数组
    r  r  )r   r  rm  r   r   r   filter_diagonal_lines  s   r  c                 C   s   | du s
t | dkrdddg dS dd }g }g }g }g }| D ]9}|d \}	}
}}||	|
||}|| t|d |krC|| q||ksM|d| krS|| q|| q|r`t|nd|rht|nd|rst||dS d|dS )u  
    将直线按角度分类为垂直线、水平线和其他线
    
    Args:
        lines: cv2.HoughLinesP 返回的直线数组
        vertical_tolerance: 垂直线角度容差
        horizontal_tolerance: 水平线角度容差
        
    Returns:
        dict: 包含分类结果的字典
        {
            'vertical': 垂直线数组,
            'horizontal': 水平线数组,
            'others': 其他角度线数组,
            'angles': 所有线的角度列表
        }
    Nr   )r   r   Zothersanglesc                 S   rx  Nry  r   r   rz  r|  r   r   r   r~       

z5classify_lines_by_angle.<locals>.calculate_line_anglerw  r   )r~   r   r   r   r    )r   Zvertical_toleranceZhorizontal_tolerancer~  Zvertical_linesZhorizontal_linesZother_lines
all_anglesr   r   r   r   r   r  r   r   r   classify_lines_by_angle  s8   

r  c           
      C   s   | du s
t | dkrdS g }| D ]*}|d \}}}}t|| d || d  }	|	|kr:|du s5|	|kr:|| q|rBt|S dS )u  
    根据线段长度筛选直线
    
    Args:
        lines: cv2.HoughLinesP 返回的直线数组
        min_length: 最小长度
        max_length: 最大长度，None表示不限制
        
    Returns:
        filtered_lines: 筛选后的直线数组
    Nr   r;   )r~   r   r   r   r    )
r   
min_length
max_lengthr  r   r   r   r   r   lengthr   r   r   filter_lines_by_length  s   
r  c                 C   s  | du s
t | dkr|rdddg g dfS dS dd }dd }g }g }g }	g }
g }| D ]}|d \}}}}|||||}|||||}|| |	| d}|rd	}|D ]5\}}|dk ra|d
7 }|d
kri|d
8 }||kr~||  krw|kr}n qUd} nqU||ks||krd} nqU||ko|du p||k}|r|r|| |
| || q+|rt|nd}|rt | t |||	|
|d}||fS |S )u  
    高级直线筛选函数，支持多个角度范围和长度筛选
    
    Args:
        lines: cv2.HoughLinesP 返回的直线数组
        angle_ranges: 角度范围列表，例如 [(85, 95), (175, 185)] 或 [(85, 95), (-5, 5)]
        min_length: 最小长度
        max_length: 最大长度
        return_details: 是否返回详细信息
        
    Returns:
        filtered_lines: 筛选后的直线数组
        details: 如果return_details=True，返回详细信息字典
    Nr   )totalfilteredr  Zlengthsc                 S   rx  r  rz  r|  r   r   r   r~  N  r  z2advanced_line_filter.<locals>.calculate_line_anglec                 S   s   t ||  d || d  S r  )r   r   )r   r   r   r   r   r   r   calculate_line_lengthX  s   z3advanced_line_filter.<locals>.calculate_line_lengthTFr   )r  r  r  all_lengthsfiltered_anglesfiltered_lengths)r~   r   r   r    )r   Zangle_rangesr  r  return_detailsr~  r  r  r  r  r  r  r   r   r   r   r   r  r  Zangle_matchZ	min_angleZ	max_angleZlength_matchr  detailsr   r   r   advanced_line_filter:  sj   





r  Tc                 C   s   | du s
t | dkrdg fS g }| D ]}|d \}}}}|| d }|||f q|jdd | d dd |D }	d	d |D }
t|	|
fS )
uZ  
    按照直线中心点的X坐标排序
    
    Args:
        lines: cv2.HoughLinesP 返回的直线数组 [[[x1, y1, x2, y2]], ...]
        ascending: 是否升序排列，True为升序，False为降序
        
    Returns:
        sorted_lines: 按X坐标排序后的直线数组
        center_x_list: 对应的中心点X坐标列表
    Nr          @c                 S   r   Nrn   r   rK   r   r   r   r     r   z(sort_lines_by_center_x.<locals>.<lambda>r   reversec                 S      g | ]}|d  qS r   r   rJ   r   r   r   rL     rM   z*sort_lines_by_center_x.<locals>.<listcomp>c                 S   r  rn   r   rJ   r   r   r   rL     rM   r~   r   r  r   r    )r   	ascendingZlines_with_center_xr   r   r   r   r   center_xsorted_linesrt  r   r   r   rr       rr  c                 C   s   | du s
t | dkrdg fS g }| D ]}|d \}}}}|| d }|||f q|jdd | d dd |D }	d	d |D }
t|	|
fS )
uZ  
    按照直线中心点的Y坐标排序
    
    Args:
        lines: cv2.HoughLinesP 返回的直线数组 [[[x1, y1, x2, y2]], ...]
        ascending: 是否升序排列，True为升序，False为降序
        
    Returns:
        sorted_lines: 按Y坐标排序后的直线数组
        center_y_list: 对应的中心点Y坐标列表
    Nr   r  c                 S   r   r  r   r  r   r   r   r     r   z(sort_lines_by_center_y.<locals>.<lambda>r  c                 S   r  r  r   rJ   r   r   r   rL     rM   z*sort_lines_by_center_y.<locals>.<listcomp>c                 S   r  r  r   rJ   r   r   r   rL     rM   r  )r   r  Zlines_with_center_yr   r   r   r   r   center_yr  Zcenter_y_listr   r   r   rv    r  rv  r   c              	   C   s  | du s
t | dkr|rdg g dfS dS g }t| D ]9\}}|d \}}}	}
||	 d }||
 d }| dkr<|}n| dkrE|}ntd||||||d q|jd	d
 | d dd |D }|rdd |D dd |D dd |D ||d}t||fS t|S )u  
    通用的直线中心点排序函数
    
    Args:
        lines: cv2.HoughLinesP 返回的直线数组
        sort_by: 排序依据，'x' 或 'y'
        ascending: 是否升序排列
        return_details: 是否返回详细信息
        
    Returns:
        sorted_lines: 排序后的直线数组
        details: 如果return_details=True，返回详细信息字典
    Nr   )center_coordsoriginal_indicesr  r   r   u   sort_by 必须是 'x' 或 'y')r   r  r  
sort_valueoriginal_indexc                 S   r   )Nr  r   r  r   r   r   r     r   z&sort_lines_by_center.<locals>.<lambda>r  c                 S   r  )r   r   rJ   r   r   r   rL     rM   z(sort_lines_by_center.<locals>.<listcomp>c                 S   s   g | ]
}|d  |d fqS )r  r  r   rJ   r   r   r   rL     s    c                 S   r  )r  r   rJ   r   r   r   rL     rM   c                 S   r  )r  r   rJ   r   r   r   rL     rM   )r  Zsort_valuesr  sort_byr  )r~   r   ri   rT   r   r  r   r    )r   r  r  r  r  r   r   r   r   r   r   r  r  r  r  r  r   r   r   sort_lines_by_center  s@   
	
r  c                 C   s   | du s
t | dkrdS |dd \}}g }| D ]S}|d \}}t|}t|}	|| }
||	 }tt|d |d  }t|
||	   }t|||  }t|
||	   }t|||  }|||||gg q|rst|S dS )u   
    简化版本的HoughLines转换函数
    使用参数方程直接计算端点
    
    Args:
        lines: HoughLines返回的结果
        image_shape: 图像形状
        
    Returns:
        numpy.ndarray: HoughLinesP格式的线段数组
    Nr   r;   )r~   r   ZcosZsinr   r   r   r    )r   Zimage_shapery   rz   Zline_segmentsr   r0  rk  Z	cos_thetaZ	sin_thetar   r   r   r   r   r   r   r   r   r   convert_hough_lines_simple  s"   

r  r   c                 C   s6  ddl }ddl}t|jdkr|||j}n| }||||}	||	|j	|j
\}
}g }|
D ]g}||}||k r=q1||\}}}}t|| }t|| }t|| | }t|| | }| jdd \}}tdt||d }tdt||d }tdt||}tdt||}|||||g q1|S )uK  
    在缩小的图像中查找指定灰度范围的区域，并返回在原图中对应的矩形坐标
    
    Args:
        cv_image: 原始图像 (numpy.ndarray)
        cw_midan_image: 缩小后的图像 (numpy.ndarray) 
        scale_factor: 缩放倍数，即cv_image缩小了多少倍得到cw_midan_image
        gray_min: 灰度最小值 (int, 0-255)
        gray_max: 灰度最大值 (int, 0-255)
        min_area: 最小区域面积，过滤掉过小的区域 (int)
        
    Returns:
        list: 矩形列表，每个矩形格式为 [x1, y1, x2, y2] (原图坐标)
    r   Nr   r;   rn   )r   numpyr~   r}   r   r   r  inRangeZfindContoursZRETR_EXTERNALZCHAIN_APPROX_SIMPLEZcontourAreaZboundingRectr   r   r   r   )cv_imagecw_midan_imagescale_factorgray_mingray_maxrH  r   r   gray_scaledmaskZcontoursr   	rect_listZcontourrG  r   r   r:   r   Zx1_origZy1_origZx2_origZy2_origorig_height
orig_widthr   r   r   !find_gray_regions_in_scaled_image9  s0   
r  c                 C   s
  ddl }ddl}t| jd t|jd  }t|jdkr%|||j}n| }||||}	|	|	dk}
g }t
|
d |
d D ]@\}}tt|| d }tt|| d }| jdd \}}d|  krm|k rn qBd|  kry|k rn qB|||g qB|S )u  
    在缩小的图像中查找指定灰度范围的所有点，并返回在原图中对应的点坐标
    
    Args:
        cv_image: 原始图像 (numpy.ndarray)
        cw_midan_image: 缩小后的图像 (numpy.ndarray)
        scale_factor: 缩放倍数
        gray_min: 灰度最小值 (int, 0-255)
        gray_max: 灰度最大值 (int, 0-255)
        
    Returns:
        list: 点坐标列表，每个点格式为 [x, y] (原图坐标)
    r   Nr   rn   r   r;   )r   r  r   r}   r~   r   r   r  r  r   zipr   r   )r  r  r  r  r  r   r   Z
cur_factorr  r  Zpoints_scaledZpoints_origr   r   Zx_origZy_origr  r  r   r   r    find_gray_points_in_scaled_imaget  s"   0r  unionc                    sr  | rt | dkr
g S ddl d fdd	}dd }dd	 | D }d
gt | }g }tt |D ]}|| r6q/|| g}	d||< |g}
|
ry|
d}|| }tt |D ]#}|| rZqS|||| d}||krv|	||  d||< |
| qS|
sDt |	dkr||	d  q/dd	 |	D }dd	 |	D }dd	 |	D }dd	 |	D }t|t|t|t|g}|| q/|S )u  
    合并距离较近的矩形框
    
    Args:
        rect_list: 矩形列表，每个矩形格式为 [x1, y1, x2, y2]
        distance_threshold: 距离阈值，小于此距离的框会被合并
        merge_method: 合并方法
            - 'union': 取所有框的外接矩形（默认）
            - 'center': 基于中心点距离合并
            - 'overlap': 基于重叠区域合并
        
    Returns:
        list: 合并后的矩形列表，格式为 [x1, y1, x2, y2]
    r   Nr   c                    s   | \}}}}|\}}}	}
|dkr7|| d }|| d }||	 d }||
 d }  || d || d  S |dkrrt||}t||	}t||}t||
}||krY||krYdS td|| }td|| }  || ||  S dS )u!   计算两个矩形之间的距离centerr;   r   r   N)r   r   r   )rect1rect2methodx1_1y1_1x2_1y2_1x1_2y1_2x2_2y2_2Z	center1_xZ	center1_yZ	center2_xZ	center2_yr&  r+  topbottomdxdyr   r   r   calculate_distance  s&   



z2merge_close_rectangles.<locals>.calculate_distancec                 S   sL   | \}}}}|\}}}}	t ||}
t ||}t||}t||	}|
|||gS )u'   合并两个矩形为一个外接矩形)r   r   )r  r  r  r  r  r  r  r  r  r  Z	merged_x1Z	merged_y1Z	merged_x2Z	merged_y2r   r   r   merge_two_rectangles  s   



z4merge_close_rectangles.<locals>.merge_two_rectanglesc                 S      g | ]}t |qS r   rX   rC   r   r   r   r   rL     rM   z*merge_close_rectangles.<locals>.<listcomp>FTrn   c                 S   r  r  r   r  r   r   r   rL   	  rM   c                 S   r  r  r   r  r   r   r   rL   	  rM   c                 S   r  r;   r   r  r   r   r   rL   	  rM   c                 S   r  r   r   r  r   r   r   rL   	  rM   )r   )r~   r  r   r   r   r   r   )r  distance_thresholdmerge_methodr  r  Z
rectanglesZmergedr   r   Zcurrent_groupqueueZcurrent_idxcurrent_rectr]  distanceall_x1all_y1all_x2all_y2merged_rectr   r  r   merge_close_rectangles  sP   


r  c                    sv   rt  dkr
g S ddl}ddlm} g } D ]}|\}}}}	|| d }
||	 d }||
|g q||}||dd|}|j}t|}g }|D ]k}|dkri|	||kd }|D ]	}| |  q^qM|	||kd } fdd	|D }t |dkr||d  qMd
d	 |D }dd	 |D }dd	 |D }dd	 |D }t
|t
|t|t|g}|| qM|S )u   
    基于中心点距离合并矩形框
    
    Args:
        rect_list: 矩形列表，每个矩形格式为 [x1, y1, x2, y2]
        distance_threshold: 中心点距离阈值
        
    Returns:
        list: 合并后的矩形列表
    r   N)DBSCANr;   rn   )ZepsZmin_samplesre   c                    s   g | ]} | qS r   r   )rC   idxr  r   r   rL   >	  rM   z7merge_rectangles_by_center_distance.<locals>.<listcomp>c                 S   r  r  r   r  r   r   r   rL   D	  rM   c                 S   r  r  r   r  r   r   r   rL   E	  rM   c                 S   r  r  r   r  r   r   r   rL   F	  rM   c                 S   r  r  r   r  r   r   r   rL   G	  rM   )r~   r  Zsklearn.clusterr  r   r    ZfitZlabels_rZ   r   r   r   )r  r  r   r  Zcentersr   r   r   r   r   r  r  Z
clusteringlabelsZunique_labelsr   labelindicesr  Zgroup_rectsr  r  r  r  r  r   r  r   #merge_rectangles_by_center_distance	  sH   
r  c              	   C   sV  | rt | dkr
g S dd }dd | D }t|D ]}d}g }dgt | }tt |D ]t}	||	 r3q,||	 }
d||	< td}d	}t|	d
 t |D ]}|| rQqJ||
|| }||krd||k rd|}|}qJ|d	kr|| }t|
d |d t|
d
 |d
 t|
d |d t|
d |d g}|| d||< d}q,||
 q,|}|s |S q|S )u  
    迭代式合并矩形框，直到没有可合并的框为止
    
    Args:
        rect_list: 矩形列表
        distance_threshold: 距离阈值
        max_iterations: 最大迭代次数，防止无限循环
        
    Returns:
        list: 合并后的矩形列表
    r   c                 S   s   | \}}}}|\}}}}	t ||}
t||}t ||}t||	}|
|kr*||kr*dS t d|
| }t d|| }|| ||  d S )u'   计算两个矩形之间的最小距离r   r   )r   r   )r  r  r  r  r  r  r  r  r  r  r&  r+  r  r  r  r  r   r   r   calculate_min_distanceb	  s   



z:merge_rectangles_iterative.<locals>.calculate_min_distancec                 S   r  r   r  r  r   r   r   rL   s	  rM   z.merge_rectangles_iterative.<locals>.<listcomp>FTinfre   rn   r;   r   )r~   r   r   r   r   r   )r  r  Zmax_iterationsr  Zcurrent_rectsZ	iterationZ
merged_anyZ	new_rectsusedr   r  Zmin_distanceZ	merge_idxr]  r  r  r  r   r   r   merge_rectangles_iterativeS	  sP   
r  c           
      C   s,   t | |||||}|sg S t|||d}	|	S )u  
    在缩小图像中查找灰度区域并自动合并相近的区域
    
    Args:
        cv_image: 原始图像
        cw_midan_image: 缩小后的图像
        scale_factor: 缩放倍数
        gray_min: 灰度最小值
        gray_max: 灰度最大值
        min_area: 最小区域面积
        merge_distance: 合并距离阈值
        merge_method: 合并方法
        
    Returns:
        list: 合并后的矩形列表
    )r  r  )r  r  )
r  r  r  r  r  rH  Zmerge_distancer  r  Zmerged_rectsr   r   r   find_and_merge_gray_regions	  s   r  c                 C   s4  ddl }|  }t| jdkr| j\}}}n| j\}}t||d |d }|dkr+|S t| jdkrl||d|ddddf< |||| |ddddf< ||ddd|ddf< ||dd|| |ddf< |S ||d|ddf< |||| |ddf< ||ddd|f< ||dd|| |f< |S )u%  
    将图像四周宽度为w的范围内的灰度数据改为指定值
    
    Args:
        image: 输入图像 (numpy.ndarray)
        border_width: 边框宽度 (int)
        gray_value: 指定的灰度值 (int, 0-255)
        
    Returns:
        numpy.ndarray: 处理后的图像
    r   Nr   r;   r  r  r~   r}   r   )rm   border_width
gray_valuer   r   ry   rz   r{   r   r   r   set_border_gray_value	  s&   
r  c                 C   sn  ddl }|  }t| jdkr| j\}}}n| j\}}t||d |d }|dkr+|S ||||}	t| jdkr~t|D ]>}
|	|
 ||
ddddf< |	|
 ||d |
 ddddf< |	|
 |dd|
ddf< |	|
 |dd|d |
 ddf< q=|S t|D ]2}
|	|
 ||
ddf< |	|
 ||d |
 ddf< |	|
 |dd|
f< |	|
 |dd|d |
 f< q|S )uI  
    将图像四周宽度为w的范围内设置为渐变值
    
    Args:
        image: 输入图像 (numpy.ndarray)
        border_width: 边框宽度 (int)
        inner_value: 内侧灰度值 (int, 0-255)
        outer_value: 外侧灰度值 (int, 0-255)
        
    Returns:
        numpy.ndarray: 处理后的图像
    r   Nr   r;   rn   )r  r  r~   r}   r   Zlinspacer   )rm   r  Zinner_valueZouter_valuer   r   ry   rz   r{   Zgradientr   r   r   r   set_border_gradient	  s,   
"$r  )r  r  r&  r+  c           	      C   st  ddl }|  }t| jdkr| j\}}}n| j\}}t||d |d }|dkr+|S t| jdkr|d|v rC||d|ddddf< d|v rV|||| |ddddf< d|v rg||ddd|ddf< d|v rz||dd|| |ddf< |S d|v r||d|ddf< d|v r|||| |ddf< d|v r||ddd|f< d|v r||dd|| |f< |S )	u  
    选择性地将图像指定边的宽度为w的范围内的灰度数据改为指定值
    
    Args:
        image: 输入图像 (numpy.ndarray)
        border_width: 边框宽度 (int)
        gray_value: 指定的灰度值 (int, 0-255)
        sides: 要修改的边 (list)，可选: 'top', 'bottom', 'left', 'right'
        
    Returns:
        numpy.ndarray: 处理后的图像
    r   Nr   r;   r  r  r&  r+  r  )	rm   r  r  Zsidesr   r   ry   rz   r{   r   r   r   set_selective_border_gray_value5
  s6   
r  c                 C   s  ddl }|  }t| jdkr| j\}}}d}	n| j\}}d}	t||d |d }|dkr/|S |du r7dd }t|D ]L}
t|D ]E}|
|k pV|
|| kpV||k pV||| k}|r|	rt||
|ddf }|||
|rs|||
|ddf< qA||
|f }|||
|r|||
|f< qAq;|S )	uZ  
    基于条件函数设置边框灰度值
    
    Args:
        image: 输入图像 (numpy.ndarray)
        border_width: 边框宽度 (int)
        gray_value: 指定的灰度值 (int, 0-255)
        condition_func: 条件函数，接收(x, y, original_value)，返回bool
        
    Returns:
        numpy.ndarray: 处理后的图像
    r   Nr   TFr;   c                 S   s   dS )NTr   )r   r   valr   r   r   r   
  s    z/set_border_based_on_condition.<locals>.<lambda>)r  r  r~   r}   r   r   )rm   r  r  Zcondition_funcr   r   ry   rz   r{   Zis_colorr   r   Z	in_borderoriginal_valuer   r   r   set_border_based_on_conditiong
  s>   

r  c           	      C   s   t | jdkrLt| }tdD ]9}| dddd|f tj}|| tj}|| }|||  }t|dd}|tj|dddd|f< q|S | tj}|tj}|| }|||  }t|dd}|tj}|S )   
    中心拉伸
    r   Nr   r   )	r~   r}   r   
zeros_liker   astypefloat32r   rh   )	rm   Z	center_cvalphar   r   channelZcenter_datar   new_channelr   r   r   enhance_center_stretch
  s"   
 r  c                 C   s   d }|| | j d krd}d}t| j dkra| j \}}}g }t|D ]:}| d d d d |f }	tj|	dd}	|| |	j d krGt|	}
|
}
nt|	d d || f }
|
}
||
 q"|}|S tj|dd}t|d d || f }
|
}
|S )Nr   r   axis)r}   r~   r   r   r  rW  r   )center_imagestart_wend_wcenter_meanry   rz   r{   channel_meansr   channel_mean0channel_meanr   r   r   enhance_center_get_center_mean
  s,   
r        ?r   c                 C   s  d}d}	d}
|dkr|j d }
|| |j d krd}d}n|j d }
|| |j d kr/d}d}t|j dkr|j \}}}g }t|D ]B}|dddd|f }tj|dd}|| |
krft|}|| }nt|j || t|dd|| f }|| }|| qB|}	ntj|	dd}	t|	|| ddf }|| }|dkrt|j dkr|j \}}}td||f}tdD ]-}|dddd|f }|jddd}|}|	dur|	| }|| }||dddd|f< q|}n|jddd}|}|	dur|	}|| }|}n|d	krot|j dkrX|j \}}}t|d|f}tdD ]/}|dddd|f }|jddd}|}|	durD|	| }|| }||dddd|f< q%|}nz|jddd}|}|	durh|	}|| }|}nct|j dkr|j \}}}td||f}tdD ]/}|dddd|f }|jddd}|}|	dur|	| }|| }||dddd|f< q|}n|jddd}|}|	dur|	}|| }|}t| j dkr+t	| }tdD ]=}| dddd|f 
tj}|dddd|f 
tj}|||  }t|dd
}|
tj|dddd|f< q|
tj}||	fS | 
tj}|||  }t|dd
}|
tj}|
tj}||	fS )r  r   Nr   rn   r   r  T)r  Zkeepdimsr   r   )r}   r~   r   r   r  rf  r6   r   rV  r  r  r  r   rh   )rm   r  center_valuer  dirr  r  	gray_diffuse_datar  max_wry   rz   r{   r	  r   r
  r  center_heightcenter_widthcenter_channelshorizontal_datacenter_channelr  center_value0vertical_datar   r   	use_data0r  r   r   r   enhance_center_stretch_image
  s   










"r  r{  c                 C   s2  ddl }t| d tst| d |jr| d \}}}}n| \}}}}t|d ts1t|d |jr:|d \}}	}
}n|\}}	}
}||| || g}||
| ||	 g}|j|}|j|}|dksj|dkrr|dkrpdS dS || }|| }|||}||dd}|	t
|}|dkr||S |S )u{  
    计算两条线段之间的夹角
    
    Args:
        line1: 第一条线段，格式为 [x1, y1, x2, y2] 或 [[x1, y1, x2, y2]]
        line2: 第二条线段，格式为 [x1, y1, x2, y2] 或 [[x1, y1, x2, y2]]
        unit: 角度单位，'degrees'(度) 或 'radians'(弧度)
        
    Returns:
        float: 两条线段之间的夹角(0-90度或0-π/2弧度)
    r   Nr{          g      r  )r  rU   rX   ndarrayr    ZlinalgZnormdotr   Zarccosr   r{  )r  line2unitr   r  r  r  r  r  r  r  r  vector1vector2Zlength1Zlength2Zunit_vector1Zunit_vector2Zdot_productr}  r   r   r   calculate_angle_between_linesq  s*   
r#  c                 C   s:  ddl }t| d tst| d |jr| d \}}}}n| \}}}}t|d ts1t|d |jr:|d \}}	}
}n|\}}	}
}||| || g}||
| ||	 g}||d |d }||d |d }|| }||jkr|d|j 8 }||jkss||j k r|d|j 7 }||j k s|dkr||S |S )u  
    计算两条线段之间的有向夹角（考虑方向）
    
    Args:
        line1: 第一条线段
        line2: 第二条线段
        unit: 角度单位
        
    Returns:
        float: 两条线段之间的有向夹角(-180到180度或-π到π弧度)
    r   Nrn   r;   r{  )r  rU   rX   r  r    Zarctan2rp  r{  )r  r  r   r   r  r  r  r  r  r  r  r  r!  r"  Zangle1Zangle2r  r   r   r   calculate_angle_with_direction  s*   


r$  c                 C   s(   t | |d}||kp|d| k}||fS )u  
    判断两条线是否平行
    
    Args:
        line1: 第一条线段
        line2: 第二条线段
        angle_threshold: 角度阈值(度)，小于此值认为平行
        
    Returns:
        tuple: (is_parallel, angle) 是否平行和夹角
    r{  rw  )r#  )r  r  angle_thresholdr  Zis_parallelr   r   r   r    s   r  c                 C   s$   t | |d}t|d |k}||fS )u  
    判断两条线是否垂直
    
    Args:
        line1: 第一条线段
        line2: 第二条线段
        angle_threshold: 角度阈值(度)，与90度的差值小于此值认为垂直
        
    Returns:
        tuple: (is_perpendicular, angle) 是否垂直和夹角
    r{  rw  )r#  r   )r  r  r%  r  Zis_perpendicularr   r   r    calculate_lines_perpendicularity  s   r&  c           	      C   sR  ddddddd}|du ri }i ||}t | jdkr#t| tjn| }|d dkr9tj|tjd	d
|d d}n9|d dkrMtj|tjd
d	|d d}n%tj|tjd	d
|d d}tj|tjd
d	|d d}t|d |d  }t	|}|d r|
 d
kr||
  d }t|}|d rt||d k||d k@ |d
}|S |}|S )u[  
    使用Sobel算子检测特定方向的边缘 (v2版本 - 支持阈值范围)
    
    Args:
        image: 输入图像
        params: 参数字典，包含以下键值:
            - direction: str, 检测方向 ('horizontal', 'vertical', 'both')
            - ksize: int, Sobel核大小 (1, 3, 5, 7)
            - threshold_min: int, 最小阈值 (0-255)
            - threshold_max: int, 最大阈值 (0-255)
            - apply_threshold: bool, 是否应用阈值范围过滤
            - normalize: bool, 是否归一化到0-255
    
    Returns:
        edges: 检测到的边缘图像
    r   r   r   r   Tr   r   threshold_minthreshold_maxr   r   Nr   rn   r   r   r   r   r;   r   r   r(  r)  )r~   r}   r   r   r   r   r   r   r   r   r   rh   r   )	rm   r   r   r   r   r   r   r   r   r   r   r   detect_edges_gradient_v2  sB   
 



r*  c                 C   s   ddddddd}|du ri }i ||}t | jdkr#t| tjn| }|d dkr9tj|tjd	d
|d d}ntj|tjd
d	|d d}t|d
k|d
}|d ra|	 d
kra||	  d }t
|}|d r{t||d k||d k@ |d
}|S )uV  
    检测从暗到亮的变化（正梯度）(v2版本 - 支持阈值范围)
    
    Args:
        image: 输入图像
        params: 参数字典，包含以下键值:
            - direction: str, 检测方向 ('horizontal', 'vertical')
            - ksize: int, Sobel核大小 (1, 3, 5, 7)
            - threshold_min: int, 最小阈值 (0-255)
            - threshold_max: int, 最大阈值 (0-255)
            - apply_threshold: bool, 是否应用阈值范围过滤
            - normalize: bool, 是否归一化到0-255
    
    Returns:
        positive_edges: 正梯度边缘图像
    r   r   r   r   Tr'  Nr   rn   r   r   r   r   r   r(  r)  r~   r}   r   r   r   r   r   r   r   r   rh   )rm   r   r   r   r   r   r   r   r   r   detect_positive_gradient_v2C  s6   
 


r,  c                 C   s   ddddddd}|du ri }i ||}t | jdkr#t| tjn| }|d dkr9tj|tjd	d
|d d}ntj|tjd
d	|d d}t|d
k | d
}|d rb|	 d
krb||	  d }t
|}|d r|t||d k||d k@ |d
}|S )uV  
    检测从亮到暗的变化（负梯度）(v2版本 - 支持阈值范围)
    
    Args:
        image: 输入图像
        params: 参数字典，包含以下键值:
            - direction: str, 检测方向 ('horizontal', 'vertical')
            - ksize: int, Sobel核大小 (1, 3, 5, 7)
            - threshold_min: int, 最小阈值 (0-255)
            - threshold_max: int, 最大阈值 (0-255)
            - apply_threshold: bool, 是否应用阈值范围过滤
            - normalize: bool, 是否归一化到0-255
    
    Returns:
        negative_edges: 负梯度边缘图像
    r   r   r   r   Tr'  Nr   rn   r   r   r   r   r   r(  r)  r+  )rm   r   r   r   r   r   r   r   r   r   detect_negative_gradient_v2  s6   
 


r-  c                 C   s   |   }t|ttfrt|g}nt|}t| jdkr*|d |||d k < |S t| jdkre| jd dkretdD ](}t||krH|| n|d }||dddd|f |dddd|f |k < q<|S )ub  
    对图像的每个通道应用最小值阈值处理
    
    Args:
        image: cv2图像，可以是单通道(H,W)或三通道(H,W,3)
        min_values: 每个通道的最小值，可以是单个数值或包含每个通道最小值的列表/数组
    
    Returns:
        处理后的图像，低于最小值的像素被设置为最小值
    r;   r   r   N)	r  rU   r   r   r   r    r~   r}   r   )rm   
min_valuesr   r   min_valr   r   r   apply_channel_min_threshold  s   
2r0  c                 C   sJ  |   tj}t|ttfrt|g}nt|}dd }t| j	dkrP| j	\}}|d }t
|D ]}t
|D ]}|||f |k rM|| |||||f< q9q3nOt| j	dkr| j	d dkr| j	\}}}	t
|D ]6}t
|D ]/}t
dD ](}
t||
kr||
 n|d }||||
f |k r|| ||}||
 ||||
f< qtqnqh|| jS )u  
    对图像的每个通道应用最小值阈值处理，用周围3x3邻域的中值替换低于最小值的像素
    
    Args:
        image: cv2图像，可以是单通道(H,W)或三通道(H,W,3)
        min_values: 每个通道的最小值，可以是单个数值或包含每个通道最小值的列表/数组
    
    Returns:
        处理后的图像，低于最小值的像素被3x3邻域中值替换
    c                 S   s  | j dd \}}td|d }t||d }td|d }t||d }| ||||f }	t| j dkrtj|	j td}
|| }|| }d|  krS|
j d k rjn nd|  krb|
j d k rjn nd|
||f< |	|
 }t|dkryt|S | ||f S tj|	j dd td}
|| }|| }d|  kr|
j d k rn nd|  kr|
j d k rn nd|
||f< g }t|	j d D ]$}|	dddd|f |
 }|	t|dkrt|n| |||f  qt
|S )u-   获取3x3邻域（除了中心点）的中值Nr;   r   rn   rc   F)r}   r   r   r~   r   onesboolrW  r   r   r    )r!   rowcolr   r:   Z	start_rowZend_rowZ	start_colZend_colZneighborhoodr  
center_row
center_colvalid_pixelsZmediansrb  r   r   r   get_3x3_neighbor_median  s0   <"<,
zBapply_channel_min_threshold_mdian.<locals>.get_3x3_neighbor_medianr;   r   r   )r  r  r   r  rU   r   r   r    r~   r}   r   rd   )rm   r.  r   r8  r   r:   r/  r3  r4  rb  r   Zneighbor_mediansr   r   r   !apply_channel_min_threshold_mdian  s8   
'

r9  skipc              
   C   s  |   tj}t|ttfrt|g}nt|}|du r+tjdt	d}d|d< ntj|t	d}ddd	}t
| jd
kru| j\}}	|d }
t|D ](}t|	D ]!}|||f }|r_||
kn||
k }|rr|| ||||||||f< qQqKn`t
| jdkr| jd
 dkr| j\}}	}t|D ]G}t|	D ]@}tdD ]9}t
||kr|| n|d }
||||f }|r||
kn||
k }|r|| |||||}|dur|| ||||f< qqq|| jS )u  
    使用自定义掩膜对图像进行阈值处理，用邻域统计值替换目标像素
    
    Args:
        image: cv2图像，可以是单通道(H,W)或三通道(H,W,3)
        threshold_values: 每个通道的阈值，可以是单个数值或包含每个通道阈值的列表/数组
        mask: 掩膜数组，指定要计算统计值的邻域区域，True的位置参与计算
        above_threshold: 布尔值，True表示处理高于阈值的像素，False表示处理低于阈值的像素
        use_median: 布尔值，True使用中值，False使用均值
        boundary_mode: 边界处理模式，'skip', 'pad_zero', 'pad_edge', 'pad_reflect', 'ignore_pixel'
    
    Returns:
        处理后的图像
    N)r   r   rc   F)rn   rn   Tr:  c                 S   s8  | j d d \}}|j \}}	|d |	d }
}g }t|D ]4}t|	D ],}|||f rQ||
 | }|| | }|dkr`d|  krH|k r_n q$d|  krT|k r_n q$|| ||f  q$|dkrd|  krn|k rn nd|  krz|k rn n
|| ||f  q$|d q$|dkrtdt|d |}tdt|d |}|| ||f  q$|dkr|dk r| }n||kr|d || d  }|dk r| }n||kr|d || d  }d|  kr|k rn nd|  kr|k rn n
|| ||f  q$|| ||f  q$|dkrQd|  kr|k r-n nd|  kr,|k sHn t| j dkr;| ||f n| ||d d f     S || ||f  q$qt|dkrqt| j dkrh| ||f S | ||d d f S t|}t| j dkr|rt|S t	|S |rtj|dd	S tj	|dd	S )
Nr;   r:  r   Zpad_zeroZpad_edgern   Zpad_reflectZignore_pixelr  )
r}   r   r   r   r   r~   r   r    rW  rf  )r!   r3  r4  r  
use_medianboundary_moder   r:   Zmask_hZmask_wr5  r6  Zvalid_pixels_listZmask_rZmask_cZimg_rZimg_cZ	clamped_rZ	clamped_cr7  r   r   r   get_neighbor_statisticR  sb   
00
4
660.


zAapply_channel_threshold_with_mask.<locals>.get_neighbor_statisticr;   r   r   )Tr:  )r  r  r   r  rU   r   r   r    r1  r2  r~   r}   r   rd   )rm   Zthreshold_valuesr  Zabove_thresholdr;  r<  r   r=  r   r:   Zthreshold_valr3  r4  Z	pixel_valZshould_processrb  r   Zneighbor_statsr   r   r   !apply_channel_threshold_with_mask3  sJ   


L
	r>  r   r   r   c                 C   s  | du rt d| jdd \}}||kr|  S t| jdkr2| jd }tj|||f|| jd}nt|tt	fr=|d n|}tj||f|| jd}||kr|dkr`| dd|| df }	|	S |dkrp| ddd|f }	|	S |d	kr|| d }
| dd|
|
| f }	|	S t d
|| }|dkrt| jdkr| |dd|df< |S | |dd|df< |S |dkrt| jdkr| |ddd|f< |S | |ddd|f< |S |d	kr|d }
t| jdkr| |dd|
|
| f< |S | |dd|
|
| f< |S t d
)un  
    根据目标宽度调整图像，如果超出则截断，如果不足则填补
    
    Args:
        image: 输入图像 (numpy.ndarray)
        target_width: 目标宽度 (int)
        direction: 截断或填补方向 (str)
            - 'left': 从左侧截断或在左侧填补
            - 'right': 从右侧截断或在右侧填补
            - 'center': 居中截断或居中填补
        fill_color: 填补颜色，默认黑色 (tuple)
            - 灰度图: (0,) 或 0
            - 彩色图: (B, G, R) 如 (0, 0, 0) 表示黑色
            
    Returns:
        numpy.ndarray: 处理后的图像
    N   输入图像不能为空r;   r   rc   r   r&  r+  r  u6   direction 参数必须是 'left', 'right' 或 'center'
rT   r}   r  r~   r   fullrd   rU   rY   rX   )rm   r
  r   
fill_colorcurrent_heightcurrent_widthr{   target_image
fill_valuecroppedr`  Zpadding_widthr   r   r   %resize_image_with_padding_or_cropping  sV   

rI  r  c                 C   s  | du rt d| jdd \}}||kr|  S t| jdkr2| jd }tj|||f|| jd}nt|tt	fr=|d n|}tj||f|| jd}||kr|dkr`| || dddf }	|	S |dkrp| d|ddf }	|	S |d	kr|| d }
| |
|
| ddf }	|	S t d
|| }|dkr| ||dddf< |S |dkr| |d|ddf< |S |d	kr|d }
| ||
|
| ddf< |S t d
)u  
    根据目标高度调整图像，如果超出则截断，如果不足则填补
    
    Args:
        image: 输入图像 (numpy.ndarray)
        target_height: 目标高度 (int)
        direction: 截断或填补方向 (str)
            - 'top': 从顶部截断或在顶部填补
            - 'bottom': 从底部截断或在底部填补
            - 'center': 居中截断或居中填补
        fill_color: 填补颜色，默认黑色 (tuple)
            
    Returns:
        numpy.ndarray: 处理后的图像
    Nr@  r;   r   rc   r   r  r  r  u6   direction 参数必须是 'top', 'bottom' 或 'center'rA  )rm   Ztarget_heightr   rC  rD  rE  r{   rF  rG  rH  r^  Zpadding_heightr   r   r   ,resize_image_height_with_padding_or_cropping  sD   
	rJ  r   rz   c                 C   s  ddl }ddl}| du rtd|  }|jdd \}}| }|dvr)td|dkr1td|dkrFt||}d|d|ddf< |S |d	kr]t||}d||| |ddf< |S |d
krrt||}d|ddd|f< |S |dkrt||}d|dd|| |f< |S )u&  
    将图片指定方向的边缘像素设置为纯黑色
    
    Args:
        image: 输入图像 (numpy.ndarray)
        direction: 方向 ('up', 'down', 'left', 'right')
        width: 要设置为黑色的像素宽度
        
    Returns:
        numpy.ndarray: 处理后的图像
    r   Nr@  r;   r,  r-  r&  r+  :   方向参数必须是 'up', 'down', 'left', 'right' 之一   宽度必须大于0r,  r-  r&  r+  )r   r  rT   r  r}   ri   r   )rm   r   rz   r   r   result_imagery   r"  r   r   r   set_edge_pixels_blackY  s6   



rO  edge_configsc                 C   s>   |   }|D ]}|d}|d}|r|rt|||}q|S )u  
    同时设置多个方向的边缘为黑色
    
    Args:
        image: 输入图像
        edge_configs: 边缘配置列表，每个元素为 {'direction': str, 'width': int}
        
    Returns:
        numpy.ndarray: 处理后的图像
        
    Example:
        edge_configs = [
            {'direction': 'up', 'width': 10},
            {'direction': 'left', 'width': 20}
        ]
    r   rz   )r  getrO  )rm   rP  rN  Zconfigr   rz   r   r   r   set_multiple_edges_black  s   

rR  c           
      C   s@  ddl }ddl}| du rtd|  }|jdd \}}| }|dvr)td|dkr1tdt|jdkrFt|tt	frC|d n|}	n|}	|dkr]t
||}|	|d|ddf< |S |d	krtt
||}|	||| |ddf< |S |d
krt
||}|	|ddd|f< |S |dkrt
||}|	|dd|| |f< |S )uW  
    将图片指定方向的边缘像素设置为指定颜色（扩展版本）
    
    Args:
        image: 输入图像
        direction: 方向 ('up', 'down', 'left', 'right')
        width: 要设置的像素宽度
        color: 颜色值，默认为黑色 (0, 0, 0)
        
    Returns:
        numpy.ndarray: 处理后的图像
    r   Nr@  r;   rK  rL  rM  r,  r-  r&  r+  )r   r  rT   r  r}   ri   r~   rU   rX   rY   r   )
rm   r   rz   r"   r   r   rN  ry   r"  rG  r   r   r   set_edge_pixels_with_color  s<   




rS  c              
   C   sn  dddddddt jd}d}zt j }|d	kr#d}td
| d ntd W n tyC } ztdt|  W Y d}~nd}~ww |du rJi }i ||}t| jdkr|rzt 	 }|
|  t j|t j}| }	W n. ty } ztdt|  t | t j}	d}W Y d}~nd}~ww t | t j}	n| }	|rzt 	 }|
|	 |d }
|d dkrt jt jt jdd	|
}||}| tj}n|d dkrt jt jt jd	d|
}||}| tj}n|d dkrzt jt jt jdd	|
}t jt jt jd	d|
}||}||}z)t j||}t j||}t j||}t j|}| tj}td W n; tyy } z*tdt|  | tj}| tj}t|d |d  }W Y d}~n	d}~ww tdW n ty } ztdt|  d}W Y d}~nd}~ww |s|d dkrt j|	t jdd	|d d}nF|d dkrt j|	t jd	d|d d}n1|d dkrt j|	t jdd	|d d}t j|	t jd	d|d d}t|d |d  }ntd|d  }|dkrt|d	k|d	}n|dkr!t|d	k | d	}n|dkr,t|}ntd|d rD| d	krD||  d }tt |d	d}|d  r|rz!t 	 }|
| t j!||d! |d" |d# \}}| }W |S  ty } z td$t|  t !||d! |d" |d# \}}W Y d}~|S d}~ww t !||d! |d" |d# \}}|S )%u}  
    通用边缘检测函数，使用Sobel算子检测特定方向和类型的边缘
    
    Args:
        image: 输入图像
        params: 参数字典，包含以下键值:
            - direction: str, 检测方向 ('horizontal', 'vertical', 'both')
                * 'horizontal': 检测水平方向的灰度变化（垂直边缘）
                * 'vertical': 检测垂直方向的灰度变化（水平边缘）
                * 'both': 两个方向都检测
            - edge_type: str, 边缘类型 ('all', 'positive', 'negative')
                * 'all': 检测所有边缘（默认）
                * 'positive': 只检测正梯度（从暗到亮）
                * 'negative': 只检测负梯度（从亮到暗）
            - ksize: int, Sobel核大小 (1, 3, 5, 7)
            - normalize: bool, 是否归一化到0-255
            - threshold: int, 二值化阈值 (0-255)
            - apply_threshold: bool, 是否应用二值化
            - max_value: int, 二值化最大值 (0-255)
            - threshold_type: int, 阈值类型 (cv2.THRESH_BINARY等)
    
    Returns:
        edges: 检测到的边缘图像
    r   rn  r   Tr   Fr   )r   	edge_typer   r   r   r   r   r   r   u!   ✅ 使用CUDA加速 (设备数: )u%   ⚠️  CUDA不可用，设备数为0u   ⚠️  CUDA初始化失败: Nu/   ⚠️  CUDA颜色转换失败，回退到CPU: r   r   r   rn   r   u"   ✅ GPU手动magnitude计算成功u%   ⚠️  GPU计算失败，回退CPU: r;   u=   direction 参数必须是 'horizontal', 'vertical' 或 'both'u/   ⚠️  CUDA Sobel计算失败，回退到CPU: r   rT  Zpositivenegativeu:   edge_type 参数必须是 'all', 'positive' 或 'negative'r   r   r   r   r   u/   ⚠️  CUDA阈值处理失败，回退到CPU: )"r   r   ZcudaZgetCudaEnabledDeviceCountr6   r*   r`   r~   r}   Zcuda_GpuMatZuploadr   r   ZdownloadZcreateSobelFilterZCV_8UC1ZCV_32FZapplyr  r   float64Zmultiplyr  r   rT   r   r   ri   r   r   r   rh   r   r   )rm   r   r   Zuse_cudaZ
cuda_countr,   r   Z	gpu_imageZgpu_grayr   r   Zsobel_filterZ	gpu_sobelr   Zsobel_filter_xZsobel_filter_yZ
gpu_sobelxZ
gpu_sobelyZgpu_sobelx_squaredZgpu_sobely_squaredZgpu_sum_squaresZgpu_magnitudee2r   r   rT  r   Z	gpu_edgesr   Z
gpu_resultr   r   r   detect_edges_universal\  s  






"






	rY  c                 C   s  | du rdS | j }|tjkr|  S t| jdkrNt| d}t| d}||krDt| tj	||}|| ||  d tj}|S tj
| tjd}|S t| jdkrg }| j\}}}	t|	D ]D}
| dddd|
f }t|d}t|d}||krt|tj	||}|| ||  d tj}ntj
|tjd}|| qatj|dd	}|S t| d}t| d}||krt| tj	||}|| ||  d tj}|S tj
| tjd}|S )
u   
    将图像标准化为适合显示的格式
    
    Args:
        image: 输入图像，任意数据类型
        
    Returns:
        numpy.ndarray: uint8格式的图像，像素值范围0-255
    Nr;   rn   c   g     o@rc   r   a   r  )rd   r   rh   r  r~   r}   Z
percentiler   r  rW  r  r   r   r   )rm   rd   Zp1Zp99Zclipped_imageZ
normalizedr{   ry   rz   Znum_channelsrb  r   Zclipped_channelZnormalized_channelr   r   r   normalize_image_for_display)  sH   

41r\  c           (      C   st  d}d}	d}
|dkr|j d }
|| |j d krd}d}n|j d }
|| |j d kr/d}d}t|j dkr|j \}}}g }t|D ]C}|dddd|f }t| }t|}|| |krft|}n|dkrq|||  n||d }t|}|| }|| qB|}	n3t| }t|}|| |krt|}n|dkr|||  n||d }t|}|| }|}	dd }|dkrlt|j dkr0|j \}}}td||f}tdD ]L}|dddd|f }g }t|D ]}|dd|f }||||}|| qt	|
dd}|} |	dur|	| } ||  }||dddd|f< q|}n|j \}}g }t|D ]}|dd|f }||||}|| q;t	|
dd}|} |	durd|	} ||  }|}nR|d	krt|j dkr|j \}}}t|d|f}!tdD ]N}|dddd|f }g }t|D ]}"||"ddf }#||#||}|| qt	|
dd}|} |	dur|	| } ||  }||!dddd|f< q|!}n|j \}}g }t|D ]}"||"ddf }#||#||}|| qt	|
dd}!|} |	dur|	} |!|  }!|!}nt|j dkr|j \}}}td||f}tdD ]N}|dddd|f }g }t|D ]}|dd|f }||||}|| qEt	|
dd}|} |	durp|	| } ||  }||dddd|f< q2|}n:|j \}}g }t|D ]}|dd|f }||||}|| qt	|
dd}|} |	dur|	} ||  }|}t| j dkrt| }$tdD ]=}| dddd|f tj}%|dddd|f tj}&|%||&  }'t|'dd
}'|'tj|$dddd|f< q|tj}|$|	fS | tj}%|%||  }'t|'dd
}'|'tj}$|tj}|$|	fS )uU   
    中心拉伸 - 在垂直均值和水平均值计算时也应用掐头去尾
    r   Nr   rn   r   c                 S   sh   t | dkrdS t|  }t |}|| |krt|S |dkr)|||  n||d }t|S )   计算掐头去尾的均值r   N)r~   r   r  re  rf  r9   r  r  Zsorted_dataZ	total_lenZtrimmed_datar   r   r   calculate_trimmed_mean  s   
"
z?enhance_center_stretch_image_ex.<locals>.calculate_trimmed_meanre   r   r   )r}   r~   r   r   r  re  rf  r   rV  r    reshaper  r  r  r   rh   )(rm   r  r  r  r  r  r  r  r  r  r  ry   rz   r{   r	  r   channel_datasorted_pixelstotal_pixelsr  trimmed_pixelsr_  r  r  r  r  r  trimmed_meansr4  column_datatrimmed_meanr  r  r  r3  row_datar   r   r  r  r   r   r   enhance_center_stretch_image_ex  s  

"
"












"ri  c           .      C   s  | du s|du r
dS | j }| j}	| tj}
|tj}d}d}d}|dkr9|jd }|| |jd kr8d}d}n|jd }|| |jd krKd}d}t|jdkr|j\}}}g }t|D ]C}|dddd|f }t| }t|}|| |krt	|}n|dkr|||  n||d }t	|}|| }|
| q^|}n3t| }t|}|| |krt	|}n|dkr|||  n||d }t	|}|| }|}dd }|dkrt|jdkrW|j\}}}tjd||ftjd	}tdD ]T}|dddd|f }g }t|D ]} |dd| f }!||!||}"|
|" qtj|tjd	dd
}#t|}$|durCt|| }$|#|$ }#|#|dddd|f< q|}n|j\}}g }t|D ]} |dd| f }!||!||}"|
|" qbtj|tjd	dd
}t|}$|durt|}$||$ }|}nt|dkrWt|jdkr|j\}}}tj|d|ftjd	}%tdD ]U}|dddd|f }g }t|D ]}&||&ddf }'||'||}"|
|" qtj|tjd	d
d}#t|}$|durt|| }$|#|$ }#|#|%dddd|f< q|%}n|j\}}g }t|D ]}&||&ddf }'||'||}"|
|" q tj|tjd	d
d}%t|}$|durPt|}$|%|$ }%|%}nt|jdkr|j\}}}tjd||ftjd	}tdD ]U}|dddd|f }g }t|D ]} |dd| f }!||!||}"|
|" qtj|tjd	dd
}#t|}$|durt|| }$|#|$ }#|#|dddd|f< qt|}nA|j\}}g }t|D ]} |dd| f }!||!||}"|
|" qtj|tjd	dd
}t|}$|durt|}$||$ }|}t|
jdkrPtj|
tjd	}(tdD ],}|
dddd|f })|dddd|f }*|)t||*  }+|+|(dddd|f< q"n|
t||  }(|tjkrjt|(ddtj},n~|tjkr|t|(ddtj},nl|tjkrt|(ddtj},nZ|tjtjfv r|tjkr|(tj},nD|(},nAz0t|tjrt|}-t|(|-j|-j|},nt|tjr|(|},n|(tj},W n ty   |(tj},Y nw |,j|	ksJ d|,j d|	 d|,|fS )u  
    中心拉伸 - 支持多种数据类型，在垂直均值和水平均值计算时也应用掐头去尾
    修正版本，确保输入输出接口不变
    
    Args:
        image: 输入图像，支持各种数据类型（uint8, uint16, int16, float32, float64等）
        center_image: 中心图像，用于计算基准值
        center_value: 中心值，默认50
        alpha: 拉伸强度，默认1.0
        dir: 方向，'horizontal'或'vertical'，默认'horizontal'
        start_w: 掐头去尾的起始像素数，默认5
        end_w: 掐头去尾的结束像素数，默认5
        gray_diff: 灰度差值调整，默认0
        
    Returns:
        tuple: (result_image, center_mean) - 处理后的图像和中心均值
    N)NNr   r   rn   r   c                 S   sp   t | dkrdS t|  }t |}|| |kr tt|S |dkr+|||  n||d }tt|S )r]  r   r  N)r~   r   r  re  r   rf  r^  r   r   r   r_    s   "z@enhance_center_stretch_image_ex2.<locals>.calculate_trimmed_meanrc   re   r   r   i  i i  u   输出形状 u    与输入形状 u
    不匹配)rd   r}   r  r   rW  r~   r   r  re  rf  r   rV  r    r`  r   r  rh   r   Zuint16Zint16r  Z
issubdtypeZintegerZiinfor   r   Zfloatingr*   ).rm   r  r  r  r  r  r  r  Zoriginal_dtypeZoriginal_shapeZimage_floatZcenter_image_floatr  r  r  ry   rz   r{   r	  r   ra  rb  rc  r  rd  r_  r  r  r  r  r  re  r4  rf  rg  r  r  r  r3  rh  Zresult_floatr   r  r  r   rI  r   r   r    enhance_center_stretch_image_ex2V  sF  

"
"












$rj  	info_dict
font_scaleline_spacingdebug_enabledc                    s  dd }z| du r|ddW S |du r|| dW S t |ts*|| dt| W S t|dkr6|| dW S | jdd	 \}}|du rMtd
td|d }|du rWtd| }d4dtdtdtdt	f fdd  |}|ss|| dW S t
j}	tdtd	| }
d}g }|D ]d}z(t|dkr|dd d }t
||	||
d }t||d }||d  W q ty } z.|rtd|dd  dt|  t|t|t|d  }|t|d  W Y d}~qd}~ww t|| d }t||d  }tj||dftjd!}|}t|D ]g\}}z)t|dkr|dd d }d}|}t
||||f|	|d"|
t
j ||7 }W q
 tyq } z-|rOtd#| d$t|  t
|d%| d&d|f|	|d'|
t
j ||7 }W Y d}~q
d}~ww ||krz*||kr|| }t
j|ddd|t
jd(d)}n|| }t
j| ddd|t
jd(d)} W n8 ty } z+|rtd*t|  t||}| ddd|f } |ddd|f }W Y d}~nd}~ww t| |g}|rtd+ td,| d-|  td.| d-|  td/|jd  d-|jd   td0t|  td1|  |W S  tyO } z$d2t| }|r@td3|  ddl}|  || |W  Y d}~S d}~ww )5uw  
    在图片下方拼接黑底白字的字典信息
    
    Args:
        image: 输入图像 (BGR格式)
        info_dict: 要显示的字典信息
        font_scale: 字体大小，None时自动计算
        line_spacing: 行间距，None时自动计算
        debug_enabled: 是否启用调试信息
        
    Returns:
        np.ndarray: 拼接后的图像
    c                 S   s  z| dur| j dd \}}nd\}}tj||dftjd} tj}tdtd|d }td	td| }td
| }g }|	d td|d }	t
|d}
d}|
D ]"}t|d | |	krj||rfd| n|7 }qS|rs|	d|  |}qS|r|	d|  t|| d }tj||dftjd}|}|D ]}|drd}nd}t||d|f||||tj ||7 }qt| |g}|W S  ty } z:tjdtjd}t|ddtjddd t|ddtjddd	 t|t
|dd dtjddd	 |W  Y d}~S d}~ww ) u!   创建包含错误信息的图像Nr;   )i  i  r   rc   333333?333333?i   rn   #   u
   ❌ ERROR:r        z   r;  u   ❌r   r   r   r   r   r   i  r   CRITICAL ERRORr   r   ffffff?zCannot display errorr   r:  r   r   r      皙?)r}   r   rV  rh   r   FONT_HERSHEY_SIMPLEXr   r   r   r   r`   rj   r~   r   putTextLINE_AAvstackr*   )original_imageerror_messager!  r"  r   Zerror_font_scalefont_thicknessZerror_line_spacingZerror_linesZmax_chars_per_lineZwordsZcurrent_lineZwordZerror_text_height
error_areay_offsetr   r"   combined_imager,   Zfallback_imager   r   r   create_error_imaget  sp   








z5append_dict_info_to_image.<locals>.create_error_imageNzInput image is NonezInput dictionary is Nonez Input is not a dictionary, got: r   zInput dictionary is emptyr;   r}  rp  i  r   rt  r   dprefix	max_depthr.   c                    s  g }|dkr| dgS z|   D ]\}}zyt|tr5|| | d  ||d |d }|| nXt|ttfrnt|dkrO|| | d|  n>|| | d|d  d	|d  d
|d  dt| d nt|tr|| | d|d n|| | d|  W q t	y } z|| | dt
| d W Y d}~qd}~ww W |S  t	y } z|| dt
| d W Y d}~|S d}~ww )u!   将字典转换为文本行列表r   ...:r   rn   r%  : z: [z, z, ..., re   u   ] (共u   项).3f
: <Error: >Nz<Dict processing error: )rW   rU   rV   r   extendrX   rY   r~   r   r*   r`   )r  r  r  r   r   r   Z	sub_linesr,   dict_to_text_linesr   r   r    s:   
>
*$z5append_dict_info_to_image.<locals>.dict_to_text_linesz*Failed to convert dictionary to text linesrn   r:  r[  r  z+Warning: Failed to get text size for line: r   z..., error: r   r;  (   rc   r   zWarning: Failed to draw line r  z<Line z error>)r   r   r   r?  r   z*Warning: Failed to handle width mismatch: u   📝 字典信息拼接完成:u      原图尺寸: r   u      文本区域尺寸: u      最终图像尺寸: u      文本行数: u      字体大小: z/Unexpected error in append_dict_info_to_image:    ❌ )rt  r   )rU   rV   r^   r~   r}   r   r   r   r`   rX   r   r~  ZgetTextSizer   r*   r6   r   rV  rh   r   r  r  copyMakeBorderBORDER_CONSTANTr  r   	print_exc)rm   rk  rl  rm  rn  r  r!  r"  Z
text_linesr   r  Zmax_text_widthZtext_heightsr   Z	text_sizer,   Ztotal_text_heightZtext_area_width	text_arear  r   Zx_posZy_posZpaddingZ	min_widthr  	error_msgr   r   r  r   append_dict_info_to_imagef  s   O
 # 






 	 r  c                 C   s  dd }zT| du s|du r|| dW S t |ts$|| dt| W S | jdd \}}g }z| D ]~\}}zWt |trL|| dt| d nBt |ttfra|| d	t| d
 n-t |t	rr|| d|d nt
|}	t|	dkr|	dd d }	|| d|	  W q4 ty }
 z|| dt
|
dd  d W Y d}
~
q4d}
~
ww W n ty }
 z|| dt
|
 W  Y d}
~
W S d}
~
ww |sdg}tdtd|d }td| }t|| d }tj||dftjd}tj}tdtd| }|}|D ]G}z%t|dkr|dd d }t||d|f||d|tj ||7 }W q
 tyQ }
 z|rFtd t
|
  W Y d}
~
q
d}
~
ww t| |gW S  ty }
 zd!t
|
 }|rstd"|  || |W  Y d}
~
S d}
~
ww )#uz   
    简化版本：在图片下方拼接关键信息
    
    只显示字典的第一层信息，适合快速预览
    c                 S   s   zN| dur| j dd \}}nd\}}tj||dftjd} tjd|dftjd}t|ddtjd	d
d t|t|dd dtjddd t| |gW S    tjdtjd}t|ddtjdd
d | Y S )u!   创建简单的错误信息图像Nr;   ),  i  r   rc   P   zERROR:)r      ro  ru  <   )r   7   r}  r   rn   )r|  r  r   rw  rx  r   )	r}   r   rV  rh   r   r  r~  r`   r  )r  r  r!  r"  r  fallbackr   r   r   create_simple_error_image  s   $z:append_simple_dict_info.<locals>.create_simple_error_imageNzInput is NonezNot a dict: r;   z: {Dict,zitems}z: [List,zitems]r  r  r  %   r  r  r;  r  zDict processing error: z<Empty or invalid dictionary>r   r  i  r  r   rc   rn   r  M   r   r   z%Warning: Failed to draw simple line: zSimple dict error: r  )rU   rV   r^   r}   rW   r   r~   rX   rY   r   r`   r*   r   r   r   r   rV  rh   r   r~  r  r  r6   r  )rm   rk  rn  r  r!  r"  Zsimple_linesr   r   Z	value_strr,   rl  rm  total_heightr  r   r  r  r   r  r   r   r   append_simple_dict_info  s   


."
r  r  c           %      C   s  zp| r
t | dkrtddd | D }|stdt |dkr(|d  W S g }d}d}	t|D ]0\}
}|jdd \}}t |jd	krK|jd nd}||
||||d
 t||}t|	|}	q2|rtd td|	  td|  tdt |  td|  td| d g }|D ](}|d }t |jdkrt	|tj
}n|jd dkrt	|tj}|| q| dkrg }d}t|D ]z\}
}|jdd \}}||kr|}n>|| }|dkrtj|d|ddtj|d}n(|dkrtj||dddtj|d}n|d }|| }tj|||ddtj|d}|| ||jd 7 }|rEtd|
 d| d| d|jd  d|jd  
 q||t |d   }|}tj||d	f|tjd}d}t|D ]\}
}|jd }||dd||| f< ||| 7 }qdn| dkrKg }d}t|D ]}\}
}|jdd \}}||	kr|}n?|	| }|dkrtj|ddd|tj|d}n(|dkrtj|dd|dtj|d}n|d }|| } tj|dd|| tj|d}|| ||jd 7 }|rtd|
 d| d| d|jd  d|jd  
 q|	}||t |d   }tj||d	f|tjd}d}!t|D ]\}
}|jd }"|||!|!|" ddf< |!|"| 7 }!q-ntd |rotd! td"|jd  d|jd   td#t |  |W S  ty }# zMtd$t|#  tjd%tjd}$t|$d&d'tjd(d)d t|$t|#dd* d+tjd,d-d t|$d.| rt | nd d/tjd,d-d |$W  Y d}#~#S d}#~#ww )0uY  
    将多张图片拼接到一起，以最大图为基础，小图添加黑边补齐
    
    Args:
        image_list: 图像列表 [image1, image2, image3, ...]
        direction: 拼接方向 ('horizontal' 或 'vertical')
        gap_size: 图像间的黑条宽度（像素）
        align: 对齐方式
            - horizontal拼接时: 'top', 'center', 'bottom'
            - vertical拼接时: 'left', 'center', 'right'
        fill_color: 填充颜色，默认黑色 (B, G, R)
        debug_enabled: 是否启用调试信息
        
    Returns:
        np.ndarray: 拼接后的图像
    r      图像列表不能为空c                 S      g | ]}|d ur|qS r   r   rC   r!   r   r   r   rL          z3concatenate_images_with_padding.<locals>.<listcomp>   没有有效的图像rn   Nr;   r   )indexrm   ry   rz   r{   u   📏 图像尺寸分析:u      最大宽度: u      最大高度:       图像数量: u      拼接方向: u      间隙大小: Zpxrm      r   r  r  r  u	      图像r  r   z -> rc   r   r&  r+  u5   direction 参数必须是 'horizontal' 或 'vertical'u   🎯 拼接完成:u      最终尺寸: u      拼接图像数: u   ❌ 图像拼接失败: rv  zIMAGE CONCAT ERRORrx  ry  ru  r  rz  r   r   zImages: r{  )r~   rT   r  r   r}   r   r   r6   r   r   COLOR_GRAY2BGRZCOLOR_RGBA2BGRri   r  r  r   rB  rh   r*   r`   rV  r  r~  )%
image_listr   gap_sizeZalignrC  rn  valid_imagesZ
image_info
max_height	max_widthr   r!   ry   rz   r{   Zprocessed_imagesrI  Zaligned_imagesZtotal_widthrD  rE  Zaligned_imgZpadding_neededZpad_topZ
pad_bottomZfinal_widthZfinal_heightrN  Zx_offsetr"  r  Zpad_leftZ	pad_rightr  r!  r,   Zerror_imager   r   r   concatenate_images_with_padding  s  





2







2
 



r    8  c              
   C   s(  z| r	t | dkrtddd | D }|stdt |}|dkr)|d  W S tdd |D | }td	d |D | }	td
d |D }
tdd |D }|rttd td|  td|dd|	d td|
 d|  |dkr|
d | }||kr|rtd t|d|d||W S |rtd t|d|d||W S |dkr|
| ||d   }||kr|rtd t|d|d||W S |rtd t|d|||W S |rtd tdt	t
t
|}t|||||W S  ty } ztdt|  t| d|d||W  Y d}~S d}~ww )uv  
    智能布局版本：自动选择横向或垂直拼接，或创建网格布局
    
    Args:
        image_list: 图像列表
        max_width: 最大输出宽度
        max_height: 最大输出高度
        gap_size: 间隙大小
        fill_color: 填充颜色
        debug_enabled: 调试模式
        
    Returns:
        np.ndarray: 拼接后的图像
    r   r  c                 S   r  r   r   r  r   r   r   rL     r  z3concatenate_images_smart_layout.<locals>.<listcomp>r  rn   c                 s       | ]}|j d  V  qdS rn   Nr}   r  r   r   r   	<genexpr>      z2concatenate_images_smart_layout.<locals>.<genexpr>c                 s   r  r   Nr  r  r   r   r   r    r  c                 s   r  r  r  r  r   r   r   r    r  c                 s   r  r  r  r  r   r   r   r    r  u   📊 智能布局分析:r  u      平均尺寸: z.0fr   u      最大尺寸: r;   u      选择: 横向拼接r   r  u/      选择: 垂直拼接（横向超出限制）r   r  u      选择: 网格布局r   u   ❌ 智能布局失败: N)r~   rT   r  sumr   r6   r  create_grid_layoutr   r   r   Zceilr   r*   r`   )r  r  r  r  rC  rn  r  
num_imagesZ	avg_widthZ
avg_heightZmax_img_widthZmax_img_heightZhorizontal_widthcolsr,   r   r   r   concatenate_images_smart_layout  sn   

r  c                 C   s  z| st dt| }|| d | }tdd | D }tdd | D }|r;td| d|  td| d|  g }	t|D ]x}
g }t|D ]_}|
| | }||k r| | }t|jd	krgt|tj}|jd
d	 \}}|| }|| }tj	||d	 ||d	  |d	 ||d	  tj
|d}|| qItj||df|tjd}|| qI|rt|d|d|d}|	| qA|	rt|	d|d|d}|W S t d ty } ztdt|  t| d|d||W  Y d
}~S d
}~ww )u  
    创建网格布局
    
    Args:
        image_list: 图像列表
        cols: 列数
        gap_size: 间隙大小
        fill_color: 填充颜色
        debug_enabled: 调试模式
        
    Returns:
        np.ndarray: 网格布局的图像
    r  rn   c                 s   r  r  r  r  r   r   r   r  %  r  z%create_grid_layout.<locals>.<genexpr>c                 s   r  r  r  r  r   r   r   r  &  r  u   🔳 创建网格布局: r   u      单元格尺寸: r;   Nr  r   rc   r   r  Fr   u   无法创建网格布局u   ❌ 网格布局创建失败: )rT   r~   r   r6   r   r}   r   r   r  r  r  r   r   rB  rh   r  r*   r`   )r  r  r  rC  rn  r  Zrowsr  r  Z	grid_rowsr3  Z
row_imagesr4  Zimg_idxr!   r   r:   Zpad_hZpad_wZ
padded_imgZ	empty_imgZ
row_concatr   r,   r   r   r   r    s`   
r  r   r   r   r   c              
   C   s   zQ| du s	|du r| W S t |d }t |d }|t |d  }|t |d  }|  }	t|	||f||f|| |durOt|	t|||d ftjd|d |	W S  tyn }
 ztd	t|
  | W  Y d}
~
S d}
~
ww )
uc  
    在图像上绘制AOI框
    
    Args:
        image: 输入图像 (BGR格式)
        aoi: AOI字典 {'x': int, 'y': int, 'width': int, 'height': int}
        color: 绘制颜色 (B, G, R)，默认绿色
        thickness: 线条粗细
        label: 标签文字，None则不显示
        
    Returns:
        np.ndarray: 绘制后的图像
    Nr   r   rz   ry   r   ro  r;   u   ❌ 绘制AOI失败: )	r   r  r   Z	rectangler  r`   r~  r*   r6   )rm   r   r"   Z	thicknessr  r   r   r   r   rN  r,   r   r   r   draw_aoi_on_image_  s&   
r  )r   r   r   )r0   r   )r   r   r   r   )r;   r   r   )r;   r   r   N)rn   r   r   r%  r&  )rn   r   r   r%  r,  )rw  r   F)r   )r  r  )r   r   )r   N)Nr   NF)T)r   TF)r   r   r   )r   r   )r;  r  )r   )r;  r   )r   r   r   r;  r  r  )r%  r%  )r   r  r   r%  r%  r   )r{  )r%  )NFTr:  )r&  r?  )r  r?  )r?  )NNF)F)r   r   r  r?  F)r  r  r   r?  F)r   r?  F)r  r;   N)u__doc__r4   r   Zxml.sax.handlerr   r   r  r   typingr   r   r   r   r   r[   r\   r  ZPILr   r   r	   r   r   r#   r-   r`   r8   r2  rA   rb   r  rl   ru   rv   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r  r  r  r#  r$  r9  rK  rN  rO  rS  rU  rd  rg  r.  r/  r  rq  ru  r  r  r  r  rr  rv  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r#  r$  r  r&  r*  r,  r-  r0  r9  r>  rI  rJ  r   rO  rX   rR  rS  rY  r\  ri  rj  rV   r   r  r  r  r  r  r  r   r   r   r   <module>   s^  
4QB

4
<
::*

OW
H	&	
8
`
`
O
"
 '\
3

4
<



?

[

 ;+
<

-
r
@T
$47
24  80

F
== V 
TI7 
1 NV X  *  "h PY"P