"""
极耳翻折检测器实现
从原始ear_fold_detection_plugin.py中提取的检测逻辑
"""

import cv2
import numpy as np
import time
from typing import Dict, Any, List, Tuple, Optional
import threading
import json
import os
import sys
import random
from datetime import datetime

# 添加工程根目录到系统路径
current_dir = os.path.dirname(__file__)
parent_dir = os.path.dirname(current_dir)
if parent_dir not in sys.path:
    sys.path.append(parent_dir)

from .base_detector import BaseDetector

# 尝试导入utils模块
try:
    from utils.model_utils import yolo_model_loder
    from utils.model_cls_utils import yolo_cls_model_loader
    from utils import utils
except ImportError as e:
    print(f"⚠️ 无法导入utils模块: {e}")
    # 创建一个临时的utils类来处理基本图像操作
    class TempUtils:
        @staticmethod
        def get_cv_img_fronm_aoi(image, aoi):
            """从图像中根据AOI提取子图像，包含边界检查"""
            try:
                if image is None:
                    print("错误：输入图像为None")
                    return np.zeros((100, 100, 3), dtype=np.uint8)
                
                img_h, img_w = image.shape[:2]
                x, y, w, h = int(aoi['x']), int(aoi['y']), int(aoi['width']), int(aoi['height'])
                
                # 边界检查和修正
                x = max(0, min(x, img_w - 1))
                y = max(0, min(y, img_h - 1))
                x2 = min(x + w, img_w)
                y2 = min(y + h, img_h)
                
                # 确保提取区域有效
                if x >= x2 or y >= y2:
                    print(f"警告：无效AOI区域 x={x}, y={y}, w={w}, h={h}, 图像尺寸={img_w}x{img_h}")
                    return np.zeros((max(1, h), max(1, w), 3), dtype=np.uint8)
                
                result = image[y:y2, x:x2]
                
                # 如果结果尺寸不匹配，进行填充
                if result.shape[0] != h or result.shape[1] != w:
                    padded = np.zeros((h, w, 3), dtype=np.uint8)
                    result_h, result_w = result.shape[:2]
                    padded[:result_h, :result_w] = result
                    return padded
                    
                return result
            except Exception as e:
                print(f"图像提取失败: {e}")
                return np.zeros((aoi.get('height', 100), aoi.get('width', 100), 3), dtype=np.uint8)
    
    utils = TempUtils()
    
    # 创建临时的模型加载器类
    class TempModelLoader:
        def __init__(self):
            self.model_load_error = "模型加载器未正确导入"
        
        def load_from_path_with_device(self, path, device):
            print(f"⚠️ 临时模型加载器: {path}")
            return False
    
    yolo_model_loder = TempModelLoader
    yolo_cls_model_loader = TempModelLoader


def mirror_point_across_line(point, line_point1, line_point2):
    """
    计算一个点关于一条直线的镜像点
    
    Args:
        point: 要镜像的点 (x, y)
        line_point1: 直线上的第一个点 (x1, y1)
        line_point2: 直线上的第二个点 (x2, y2)
        
    Returns:
        tuple: 镜像后的点坐标 (x', y')
    """
    try:
        px, py = point
        x1, y1 = line_point1
        x2, y2 = line_point2
        
        # 处理垂直线的特殊情况
        if x2 == x1:
            # 垂直线：x = x1
            mirror_x = 2 * x1 - px
            mirror_y = py
            return (mirror_x, mirror_y)
        
        # 处理水平线的特殊情况
        if y2 == y1:
            # 水平线：y = y1
            mirror_x = px
            mirror_y = 2 * y1 - py
            return (mirror_x, mirror_y)
        
        # 一般情况：计算直线方程 ax + by + c = 0
        # 从两点式转换为一般式
        a = y2 - y1
        b = x1 - x2
        c = x2 * y1 - x1 * y2
        
        # 计算镜像点公式
        denominator = a * a + b * b
        if denominator == 0:
            return point  # 两点重合，无法确定直线
        
        factor = 2 * (a * px + b * py + c) / denominator
        mirror_x = px - a * factor
        mirror_y = py - b * factor
        
        return (mirror_x, mirror_y)
    except Exception as e:
        print(f"镜像点计算异常: {e}")
        return point


class EarFoldDetector(BaseDetector):
    """极耳翻折检测器"""
    
    def __init__(self):
        super().__init__(
            name="极耳翻折检测器",
            version="1.0.0",
            description="基于YOLO的极耳翻折单图检测算法"
        )
        
        # 模型相关
        self.cut_model = None
        self.detect_model = None
        self.gm_cls_model = None
        
        # 模型锁
        self.cut_model_lock = threading.Lock()
        self.detect_model_lock = threading.Lock()
        self.gm_cls_model_lock = threading.Lock()
        
        # CUDA设备
        self.cuda_name = "cuda:0"
        
        # 运行计数
        self.run_count = 0
        self.error_count = 0
        
        # 参数缓存
        self.cached_params = None

    def get_config_filename(self) -> str:
        """返回当前检测器的配置文件名"""
        return "ear_fold_detection_params.json"
    
    def initialize(self, config_params: Dict[str, Any] = None) -> bool:
        """
        初始化检测器，加载模型和配置
        
        Args:
            config_params: 配置参数字典，如果为None则使用默认配置
        """
        try:
            print("🚀 初始化极耳翻折检测器...")
            
            # 使用传入的配置参数
            if config_params is not None:
                self.parameters = config_params
            else:
                self.parameters = self.get_default_parameters()
            
            # 初始化模型
            self.cut_model = yolo_model_loder()
            self.detect_model = yolo_model_loder()
            self.gm_cls_model = yolo_cls_model_loader()
            
            # 执行模型预热
            self._warm_up_models()
            
            print("✅ 检测器初始化成功")
            self.initialized = True
            return True
            
        except Exception as e:
            print(f"❌ 检测器初始化失败: {str(e)}")
            self.initialized = False
            return False
    
    def _warm_up_models(self):
        """
        预热模型：使用虚拟数据进行一次推理，确保模型完全加载并优化
        """
        try:
            print("🔥 开始模型预热...")
            
            # 获取模型参数
            cut_model_params = self.parameters.get('极耳裁切模型参数', {})
            detect_model_params = self.parameters.get('极耳翻折检测模型参数', {})
            gm_cls_model_params = self.parameters.get('隔膜检测模型参数', {})
            
            # 预热裁切模型
            if self.cut_model and cut_model_params.get("模型路径"):
                try:
                    cut_imgsz = cut_model_params.get("输入尺寸", 512)
                    cut_model_path = cut_model_params.get("模型路径", "")
                    
                    if cut_model_path:
                        print(f"  🔥 预热裁切模型 ({cut_imgsz}x{cut_imgsz})...")
                        # 加载模型
                        if self.cut_model.load_from_path_with_device(cut_model_path, self.cuda_name):
                            # 创建虚拟输入图像
                            dummy_image = np.random.randint(0, 255, (cut_imgsz, cut_imgsz, 3), dtype=np.uint8)
                            with self.cut_model_lock:
                                self.cut_model.predictEx(dummy_image, device=self.cuda_name, imgsz=cut_imgsz, verbose=False)
                            print("    ✅ 裁切模型预热完成")
                        else:
                            print(f"    ⚠️ 裁切模型加载失败: {self.cut_model.model_load_error}")
                except Exception as e:
                    print(f"    ❌ 裁切模型预热失败: {e}")
            
            # 预热检测模型
            if self.detect_model and detect_model_params.get("模型路径"):
                try:
                    detect_imgsz = detect_model_params.get("输入尺寸", 512)
                    detect_model_path = detect_model_params.get("模型路径", "")
                    detect_confidence = detect_model_params.get("置信度阈值", 0.4)
                    detect_iou = detect_model_params.get("IOU阈值", 0.9)
                    
                    if detect_model_path:
                        print(f"  🔥 预热检测模型 ({detect_imgsz}x{detect_imgsz})...")
                        # 加载模型
                        if self.detect_model.load_from_path_with_device(detect_model_path, self.cuda_name):
                            # 设置类别名称
                            usr_class_name = {
                                'class1':'JE',  # 极耳
                                'class3':'je',  # 折痕
                                'class4':'za',  # 月牙
                                'class0':'GMqiaoqi',# 内插
                                'class2':'gemo', # 内嵌(隔膜内部)
                                'class5':'fenceng',  # 分层
                            }
                            self.detect_model.set_class_names(usr_class_name)
                            
                            # 设置置信度参数
                            independent_conf = detect_model_params.get('独立置信度参数', {})
                            independent_conf_enabled = independent_conf.get("启用独立置信度", False)
                            
                            self.detect_model.use_independent_conf = independent_conf_enabled
                            self.detect_model.global_conf = detect_confidence
                            
                            if independent_conf_enabled:
                                independent_conf_values = {
                                    'JE': independent_conf.get("极耳置信度", 0.1),
                                    'je': independent_conf.get("折痕置信度", 0.1),
                                    'za': independent_conf.get("月牙置信度", 0.1),
                                    'GMqiaoqi': independent_conf.get("极耳内插置信度", 0.1),
                                    'gemo': independent_conf.get("极耳内嵌置信度", 0.1),
                                    'fenceng': independent_conf.get("分层置信度", 0.1),
                                }
                                
                                for class_name, conf_value in independent_conf_values.items():
                                    self.detect_model.set_class_confidence(class_name, conf_value)
                            
                            # 创建虚拟输入图像
                            dummy_image = np.random.randint(0, 255, (detect_imgsz, detect_imgsz, 3), dtype=np.uint8)
                            with self.detect_model_lock:
                                self.detect_model.predictEx(
                                    dummy_image, 
                                    device=self.cuda_name, 
                                    iou=detect_iou, 
                                    imgsz=detect_imgsz, 
                                    verbose=False
                                )
                            print("    ✅ 检测模型预热完成")
                        else:
                            print(f"    ⚠️ 检测模型加载失败: {self.detect_model.model_load_error}")
                except Exception as e:
                    print(f"    ❌ 检测模型预热失败: {e}")
            
            # 预热隔膜分类模型
            if self.gm_cls_model and gm_cls_model_params.get("模型路径"):
                try:
                    gm_imgsz = gm_cls_model_params.get("输入尺寸", 256)
                    gm_model_path = gm_cls_model_params.get("模型路径", "")
                    
                    if gm_model_path:
                        print(f"  🔥 预热隔膜分类模型 ({gm_imgsz}x{gm_imgsz})...")
                        # 加载模型
                        if self.gm_cls_model.load_from_path(gm_model_path, self.cuda_name):
                            # 创建虚拟输入图像
                            dummy_image = np.random.randint(0, 255, (gm_imgsz, gm_imgsz, 3), dtype=np.uint8)
                            with self.gm_cls_model_lock:
                                self.gm_cls_model.predict(dummy_image, device=self.cuda_name, imgsz=gm_imgsz)
                            print("    ✅ 隔膜分类模型预热完成")
                        else:
                            print(f"    ⚠️ 隔膜分类模型加载失败: {self.gm_cls_model.model_load_error}")
                except Exception as e:
                    print(f"    ❌ 隔膜分类模型预热失败: {e}")
            
            print("🔥 模型预热完成！")
            
        except Exception as e:
            print(f"❌ 模型预热异常: {e}")
            # 预热失败不影响初始化，只是性能可能会差一些

    def detect(self, image: np.ndarray, params: Dict[str, Any] = None) -> Dict[str, Any]:
        """
        执行检测（仅翻折检测）
        按照run_all的输出格式设计，主要输出标注相关信息
        """
        if not self.initialized:
            return {
                '错误信息': '检测器未初始化'
            }
        
        print("🔍 开始极耳翻折检测...")
        # 使用传入的参数或默认参数
        detection_params = params if params is not None else self.parameters
        
        try:
            start_time = time.perf_counter()
            
            # 执行核心检测逻辑
            raw_result = self._run_detection(image, detection_params)
            
            # 按照run_all格式构建结果
            result = {}
            
            # 如果检测异常结束，直接返回错误信息
            if '异常结束' in raw_result:
                result['错误信息'] = '推理异常结束'
                return result
            
            # 添加cbv1_text_items（文本项）
            result['cbv1_text_items'] = raw_result.get('cbv1_text_items', [])
            result['result_info'] = raw_result.get('result_info', {})
            result['结果'] = raw_result.get('结果', 'NG')
            # 添加单图检测结果（类似上极耳结果或下极耳结果的结构）

            # 计算坐标还原到原图的标注项
            original_height, original_width = image.shape[:2]
            model_params = detection_params.get('极耳翻折检测模型参数', {})
            model_input_size = model_params.get("输入尺寸", 512)
            
            # 计算缩放比例
            scale_x = original_width / model_input_size
            scale_y = original_height / model_input_size
            
            # 将检测结果坐标还原到原始图像尺寸
            result['draw_items_original'] = self._scale_draw_items_to_original(
                raw_result.get('draw_items', []), scale_x, scale_y
            )
            
            end_time = time.perf_counter()
            print(f"检测耗时: {end_time - start_time:.2f}秒")
            
            return result
            
        except Exception as e:
            print(f"❌ 检测异常: {str(e)}")
            import traceback
            traceback.print_exc()
            
            return {
                '错误信息': f'检测异常: {str(e)}'
            }
    
    def _scale_draw_items_to_original(self, draw_items, scale_x, scale_y):
        """
        将512x512图像的绘图项坐标还原到原始图像尺寸
        参考主程序detect_only的坐标还原逻辑
        
        Args:
            draw_items: 绘图项列表（512x512尺寸）
            scale_x: X方向缩放比例
            scale_y: Y方向缩放比例
            
        Returns:
            dict: 还原到原始图像尺寸的绘图项字典
        """
        draw_items_original = {}
        
        if not draw_items:
            return draw_items_original
            
        for i, item in enumerate(draw_items):
            # 深拷贝item以避免修改原始数据
            scaled_item = item.copy()
            
            # 根据不同类型的绘制项进行坐标还原
            if item.get('type') == 'rectangle':
                if 'topLeft' in scaled_item and 'bottomRight' in scaled_item:
                    # 还原矩形坐标
                    scaled_item['topLeft'] = {
                        'x': float(item['topLeft']['x'] * scale_x),
                        'y': float(item['topLeft']['y'] * scale_y)
                    }
                    scaled_item['bottomRight'] = {
                        'x': float(item['bottomRight']['x'] * scale_x),
                        'y': float(item['bottomRight']['y'] * scale_y)
                    }
            
            elif item.get('type') == 'text':
                if 'position' in scaled_item:
                    # 还原文本位置
                    scaled_item['position'] = {
                        'x': float(item['position']['x'] * scale_x),
                        'y': float(item['position']['y'] * scale_y)
                    }
            
            elif item.get('type') == 'polygon':
                if 'points' in scaled_item:
                    # 还原多边形所有点的坐标
                    scaled_points = []
                    for point in item['points']:
                        scaled_points.append({
                            'x': float(point['x'] * scale_x),
                            'y': float(point['y'] * scale_y)
                        })
                    scaled_item['points'] = scaled_points
            
            elif item.get('type') == 'circle':
                if 'center' in scaled_item:
                    # 还原圆心位置
                    scaled_item['center'] = {
                        'x': float(item['center']['x'] * scale_x),
                        'y': float(item['center']['y'] * scale_y)
                    }
                if 'radius' in scaled_item:
                    # 还原半径（使用平均缩放比例）
                    avg_scale = (scale_x + scale_y) / 2
                    scaled_item['radius'] = float(item['radius'] * avg_scale)
            
            draw_items_original[f"item_{i}"] = scaled_item
        
        return draw_items_original

    def _run_detection(self, image: np.ndarray, params: Dict[str, Any]) -> Dict[str, Any]:
        """
        核心检测逻辑 - 基于原始run_delect方法的完整实现
        """
        # 返回值
        result = {}
        result['result_image'] = {}
        result['result_info'] = {}
        result['result_info']['算法错误'] = []
        result['result_info']['算法消息'] = []
        result['结果'] = 'NG'
        
        # 获取检测模型参数
        model_params = params.get('极耳翻折检测模型参数', {})
        model_path = model_params.get("模型路径", "")
        model_confidence_threshold = model_params.get("置信度阈值", 0.4)
        model_iou_threshold = model_params.get("IOU阈值", 0.9)
        imgsz = model_params.get("输入尺寸", 512)
        independent_conf = model_params.get('独立置信度参数', {})
        independent_conf_enabled = independent_conf.get("启用独立置信度", False)
        horizontal_texture_check = model_params.get("横向纹理附加检测", True)
        horizontal_texture_param = model_params.get("横向纹理附加检测参数", {})
        
        # 用户参数
        usr_params = params.get('极耳翻折用户参数', {})
        fanzhe_protect_ratio = usr_params.get("翻折保护距离（极耳高度比例）", 0.4)
        fanzhe_protect_start = usr_params.get("翻折起点保护距离（极耳高度比例）", 0.01)
        fanzhe_protect_expand = usr_params.get("翻折起点扩展距离（极耳高度比例）", 0.0)
        JE_height = usr_params.get("极耳高度（像素）", 150)
        JE_height_ratio = usr_params.get("极耳高度保护阈值", 0.6)
        JE_width = usr_params.get("极耳宽度（像素）", 400)
        JE_width_ratio = usr_params.get("极耳宽度保护阈值", 1.5)
        JE_fenceng_threshold = usr_params.get("分层保护阈值", 0.2)
        
        yueya_wh_ng_enable = usr_params.get("月牙宽高NG", False)
        yueya_width_ng = usr_params.get("月牙宽度", 0.8)
        yueya_height_ng = usr_params.get("月牙高度", 0.5)
        
        enable_auto_scaling = usr_params.get("启用自动缩放功能", False)
        detect_img_height_scale = usr_params.get("检测后图片高度缩放", 1.0)
        detect_img_width_scale = usr_params.get("检测后图片宽度缩放", 1.0)
        
        enable_pos_filter = usr_params.get("启用相对位置筛选器", True)
        pos_filter_threshold = usr_params.get("重叠度阈值", 0.8)
        
        gray_detect_enable = usr_params.get("启用灰度检测", True)
        gray_ng_enable = usr_params.get("灰度范围NG", False)
        gray_inward_ratio = usr_params.get("灰度内缩", 0.7)
        gray_diff_max = usr_params.get("灰度差值MAX", 255)
        gray_diff_min = usr_params.get("灰度差值MIN", 0)
        
        save_image_by_model = usr_params.get("算法存图", True)
        save_image_path = usr_params.get("算法存图路径", "")
        save_image_max_size = usr_params.get("算法存图最大占用硬盘(MB)", 4096)
        
        # 隔膜检测参数
        gm_cls_model_params = params.get('隔膜检测模型参数', {})
        gm_cls_model_path = gm_cls_model_params.get("模型路径", "")
        gm_cls_model_imgsz = gm_cls_model_params.get("输入尺寸", 256)
        
        # 隔膜用户参数
        gm_cls_usr_params = params.get('隔膜用户参数', {})
        enable_gm_cls = gm_cls_usr_params.get("启用隔膜分析", True)
        enable_gm_cls_all = gm_cls_usr_params.get("隔膜全检", False)
        
        src_image = image
        # 缩放到模型输入尺寸
        original_height, original_width = src_image.shape[:2]
        src_image = cv2.resize(src_image, (imgsz, imgsz))
        print(f"🔧 缩放图像: {original_width}x{original_height} -> {imgsz}x{imgsz}")
        
        start_time = time.perf_counter()
        model_start = start_time
        
        # 加载模型
        load_result = self.detect_model.load_from_path_with_device(model_path, self.cuda_name)
        if not load_result:
            print(f"模型加载失败: {self.detect_model.model_load_error}")
            result['result_info']['算法错误'].append(f"翻折模型加载失败: {self.detect_model.model_load_error}")
            return result
        
        # 设置类别名称
        usr_class_name = {
            'class1':'JE',  # 极耳
            'class3':'je',  # 折痕
            'class4':'za',  # 月牙
            'class0':'GMqiaoqi',# 内插
            'class2':'gemo', # 内嵌(隔膜内部)
            'class5':'fenceng',  # 分层
        }
        self.detect_model.set_class_names(usr_class_name)
        
        # 设置置信度参数
        self.detect_model.use_independent_conf = independent_conf_enabled
        self.detect_model.global_conf = model_confidence_threshold
        
        # 设置独立置信度
        if independent_conf_enabled:
            independent_conf_values = {
                'JE': independent_conf.get("极耳置信度", 0.1),
                'je': independent_conf.get("折痕置信度", 0.1),
                'za': independent_conf.get("月牙置信度", 0.1),
                'GMqiaoqi': independent_conf.get("极耳内插置信度", 0.1),
                'gemo': independent_conf.get("极耳内嵌置信度", 0.1),
                'fenceng': independent_conf.get("分层置信度", 0.1),
            }
            
            # 使用函数设置各类别置信度
            for class_name, conf_value in independent_conf_values.items():
                self.detect_model.set_class_confidence(class_name, conf_value)
        
        # 执行检测推理
        with self.detect_model_lock:
            try:
                model_results = self.detect_model.predictEx(
                    src_image,
                    device=self.cuda_name,
                    iou=model_iou_threshold,
                    imgsz=imgsz,
                    verbose=False
                )
            except Exception as e:
                print(f"❌ 模型推理失败: {str(e)}")
                result['result_info']['算法错误'].append(f"翻折模型推理失败: {str(e)}")
                result['异常结束'] = True
                return result
        
        model_end = time.perf_counter()
        model_cost = model_end - model_start
        print(f"翻折模型推理时间: {model_end - model_start:.4f} 秒")   
        cur_delect_model = self.detect_model

        # 提取瑕疵列表
        deleted_list = cur_delect_model.extract_detection_list(model_results)
        extract_detection_list_end = time.perf_counter()
        print(f"提取检测列表时间: {extract_detection_list_end - model_end:.4f} 秒")     

        # 合并检测列表
        merge_success, merge_msg = self.detect_model.merge_overlapping_detections_inplace(deleted_list, 0.3)
        for detect_item in deleted_list:
            if detect_item['类型字符'] in usr_class_name:
                detect_item['类型字符'] = usr_class_name[detect_item['类型字符']]

        merge_end = time.perf_counter()
        print(f"合并检测列表时间: {merge_end - extract_detection_list_end:.4f} 秒")  

        # save_image_by_model = usr_params.get("算法存图", False)
        # save_image_path = usr_params.get("算法存图路径", "")
        # save_image_max_size = usr_params.get("算法存图最大占用硬盘(MB)", 4096)

        has_other_item = False
        if len(deleted_list) != 1:
            has_other_item = True




                
        # 自动缩放结果和图像 根据我输入的宽度和高度比例自动缩放图像
        if enable_auto_scaling:
            if detect_img_width_scale != 1.0 or detect_img_height_scale != 1.0:
                # 获取原始图像尺寸
                original_height, original_width = src_image.shape[:2]
                
                # 计算新的尺寸
                new_width = int(original_width * detect_img_width_scale)
                new_height = int(original_height * detect_img_height_scale)
                
                print(f"🔧 自动缩放图像: {original_width}x{original_height} -> {new_width}x{new_height}")
                print(f"   宽度缩放比例: {detect_img_width_scale}, 高度缩放比例: {detect_img_height_scale}")
                
                # 缩放原始图像
                src_image = cv2.resize(src_image, (new_width, new_height), interpolation=cv2.INTER_LINEAR)
                
                # 缩放所有检测结果的坐标
                for detect_item in deleted_list:
                    aoi_pos = detect_item.get('AOI位置', {})
                    if aoi_pos:
                        # 缩放检测框坐标
                        aoi_pos['x1'] = int(aoi_pos['x1'] * detect_img_width_scale)
                        aoi_pos['y1'] = int(aoi_pos['y1'] * detect_img_height_scale)
                        aoi_pos['x2'] = int(aoi_pos['x2'] * detect_img_width_scale)
                        aoi_pos['y2'] = int(aoi_pos['y2'] * detect_img_height_scale)
                        aoi_pos['center_x'] = int(aoi_pos['center_x'] * detect_img_width_scale)
                        aoi_pos['center_y'] = int(aoi_pos['center_y'] * detect_img_height_scale)
                        
                        # 重新计算宽度和高度
                        aoi_pos['width'] = aoi_pos['x2'] - aoi_pos['x1']
                        aoi_pos['height'] = aoi_pos['y2'] - aoi_pos['y1']
                        
                        # 更新面积
                        detect_item['面积'] = aoi_pos['width'] * aoi_pos['height']
                
                print(f"✅ 图像和检测结果缩放完成，共缩放了 {len(deleted_list)} 个检测框")
            else:
                print(f"🔧 未启用图像缩放 (宽度比例: {detect_img_width_scale}, 高度比例: {detect_img_height_scale})")

        auto_bg_end = time.perf_counter()

        print(f"自动缩放时间: {auto_bg_end - merge_end:.4f} 秒")



        # 翻折逻辑的特殊处理
        je_aoi_list = []
        other_aoi_list = []
        has_yueya = False
        has_fanzhe = False
        has_fenceng = False
        has_neicha = False
        has_neiqian = False
        fanzhe_result = 0
        has_horizontal_texture = False
        for detect_item in deleted_list:
            if detect_item['类型字符'] == 'JE':
                temp_aoi = {}
                temp_aoi['x'] = detect_item['AOI位置']['x1']
                temp_aoi['y'] = detect_item['AOI位置']['y1']
                temp_aoi['width'] = detect_item['AOI位置']['width']
                temp_aoi['height'] = detect_item['AOI位置']['height']
                image0 = utils.get_cv_img_fronm_aoi(src_image, temp_aoi)
                detect_item['image'] = image0
                je_aoi_list.append(temp_aoi)

        use_pos_filter = False              
        if len(je_aoi_list) == 1 and enable_pos_filter:
            use_pos_filter = True

        for detect_item in deleted_list:
            temp_aoi = {}
            temp_aoi['x'] = detect_item['AOI位置']['x1']
            temp_aoi['y'] = detect_item['AOI位置']['y1']
            temp_aoi['width'] = detect_item['AOI位置']['width']
            temp_aoi['height'] = detect_item['AOI位置']['height']
            image0 = utils.get_cv_img_fronm_aoi(src_image, temp_aoi)
            detect_item['image'] = image0
            need_append = True
            if detect_item['类型字符'] == 'za':
                has_yueya = True
            if detect_item['类型字符'] == 'je':
                has_fanzhe = True
            if detect_item['类型字符'] == 'fenceng':
                has_fenceng = True
            if detect_item['类型字符'] == 'GMqiaoqi':
                has_neicha = True
            if detect_item['类型字符'] == 'gemo':
                has_neiqian = True

            if use_pos_filter:
                # 检查当前检测项是否超出边界（安全检查）
                if len(je_aoi_list) == 0:
                    continue
                    
                # 获取极耳AOI信息
                je_aoi = je_aoi_list[0]
                
                # 计算两个矩形的包含度（当前检测项被极耳AOI包含的比例）
                def calculate_containment_ratio(item_aoi, container_aoi):
                    """计算item_aoi被container_aoi包含的比例"""
                    # 计算交集
                    x1 = max(item_aoi['x'], container_aoi['x'])
                    y1 = max(item_aoi['y'], container_aoi['y'])
                    x2 = min(item_aoi['x'] + item_aoi['width'], container_aoi['x'] + container_aoi['width'])
                    y2 = min(item_aoi['y'] + item_aoi['height'], container_aoi['y'] + container_aoi['height'])
                    
                    # 如果没有交集，返回0
                    if x1 >= x2 or y1 >= y2:
                        return 0.0
                    
                    # 计算交集面积
                    intersection_area = (x2 - x1) * (y2 - y1)
                    # 计算检测项面积
                    item_area = item_aoi['width'] * item_aoi['height']
                    
                    # 返回包含度（检测项被容器包含的比例）
                    if item_area == 0:
                        return 0.0
                    return intersection_area / item_area
                
                

                if detect_item['类型字符'] == 'za':
                    # 如果和这个item被je_aoi_list[0]包含小于0.9则置detect_item['不显示'] = True
                    containment = calculate_containment_ratio(temp_aoi, je_aoi)
                    if containment < pos_filter_threshold:
                        detect_item['不显示'] = True
                        need_append = False
                        print(f"月牙检测项包含度不足: {containment:.3f} < {pos_filter_threshold}，设置为不显示")

                if detect_item['类型字符'] == 'je':
                    # 如果和这个item被je_aoi_list[0]包含小于0.9则置detect_item['不显示'] = True
                    containment = calculate_containment_ratio(temp_aoi, je_aoi)
                    if containment < pos_filter_threshold:
                        detect_item['不显示'] = True
                        need_append = False
                        print(f"折痕检测项包含度不足: {containment:.3f} < {pos_filter_threshold}，设置为不显示")

                if detect_item['类型字符'] == 'fenceng':
                    # 如果和这个item被je_aoi_list[0]包含小于0.9则置detect_item['不显示'] = True
                    containment = calculate_containment_ratio(temp_aoi, je_aoi)
                    if containment < pos_filter_threshold:
                        detect_item['不显示'] = True
                        need_append = False
                        print(f"分层检测项包含度不足: {containment:.3f} < {pos_filter_threshold}，设置为不显示")

                if detect_item['类型字符'] == 'GMqiaoqi':
                    # 如果和这个item的宽度方向中心比je_aoi_list[0]最左侧点靠右则置detect_item['不显示'] = True
                    item_center = temp_aoi['x'] + temp_aoi['width'] / 2
                    je_left = je_aoi['x']
                    if item_center > je_left:
                        detect_item['不显示'] = True
                        need_append = False
                        print(f"内插未超出左边界: item_center={item_center} > je_left={je_left}，设置为不显示")


                        
                if detect_item['类型字符'] == 'gemo':
                    # 如果和这个item的宽度方向中心点比je_aoi_list[0]最左侧点靠右则置detect_item['不显示'] = True
                    item_center = temp_aoi['x'] + temp_aoi['width'] / 2
                    je_left = je_aoi['x']
                    if item_center > je_left:
                        detect_item['不显示'] = True
                        need_append = False
                        print(f"内嵌未超出左边界: item_center={item_center} > je_left={je_left}，设置为不显示")


            if detect_item['类型字符'] == 'je':
                detect_item['类型字符'] = 'zheheng'
            if detect_item['类型字符'] == 'JE':
                #je_aoi_list.append(temp_aoi)
                pass
            else:
                temp_item = {}
                temp_item['image'] = image0
                temp_item['aoi'] = temp_aoi
                temp_item['类型字符'] = detect_item['类型字符']
                temp_item['索引'] = detect_item['索引']
                if horizontal_texture_check and detect_item['类型字符'] in ['zheheng'] and len(je_aoi_list) == 1:
                    print(horizontal_texture_param)
                    if (temp_aoi['height'] > temp_aoi['width'] * horizontal_texture_param['横纹最低长宽比']
                        and temp_aoi['x'] - je_aoi_list[0]['x'] > je_aoi_list[0]['width'] * horizontal_texture_param['横纹最低风险高度']
                        and temp_aoi['height'] / je_aoi_list[0]['height'] > horizontal_texture_param['横纹最低风险宽度']):
                        detect_item['不显示'] = True
                        has_horizontal_texture = True
                        print(f"🔍 检测到可能的横向纹理误检，长宽比{temp_aoi['height'] / temp_aoi['width']}，AOI索引 {detect_item['索引']} 被标记为不显示")
                        pass
                    else:
                        if need_append:
                            other_aoi_list.append(temp_item)
                else:
                    if need_append:
                        other_aoi_list.append(temp_item)
                        print(f"AOI索引 {detect_item['索引']}，类型 {detect_item['类型字符']} 被添加到其他AOI列表，包含度检查通过")

        aoi_seg_end = time.perf_counter()
        print(f"检测框处理时间: {aoi_seg_end - auto_bg_end:.4f} 秒") 

        # 检测框计算
        ng_info = ''   
        # 保存检测列表图像
        # 极耳翻折判定逻辑
            # 0:正常 1:翻折 2:月牙 3：无极耳无翻折 4（1）：多极耳 5：小翻折 
        je_aoi = None

        see_gm = False
        if len(je_aoi_list) <= 0:
            if len(other_aoi_list) > 0:
                result['result_info']['算法错误'].append("❌ 未检测到极耳,有翻折")
                fanzhe_result = 1
            else:
                result['result_info']['算法错误'].append("❌ 未检测到极耳及其他翻折")
                fanzhe_result = 1
        
        if len(je_aoi_list) > 1:
            result['result_info']['算法错误'].append("❌ 检测到多个极耳,理论上拆图只拆单个极耳")
            ng_info = ng_info + 'duojier;'
            fanzhe_result = 1
            
        fenceng_up = 10
        yueya_down = 10
        if len(je_aoi_list) == 1:
            je_aoi = je_aoi_list[0]
            if je_aoi['width'] < JE_height * JE_height_ratio:
                result['result_info']['算法错误'].append("❌ 极耳高度过低，判定为首极耳翻折")
                fanzhe_result = 1
                ng_info = ng_info + 'gaoduguodi;'

            if je_aoi['height'] > JE_width * JE_width_ratio:
                result['result_info']['算法错误'].append("❌ 极耳宽度过大，判定为错位")
                fanzhe_result = 1
                ng_info = ng_info + 'cuowei;'

            if len(other_aoi_list) > 0:
                #fanzhe_result = 2
                pass
            for other_item in other_aoi_list:
                other_aoi = other_item['aoi']
                other_image = other_item['image']
                other_aoi_result = self.detect_model.calculate_aoi_relationships(je_aoi, other_aoi)
                other_aoi['与极耳关系'] = other_aoi_result
                # 判定为翻折的逻辑
                other_item['pos'] = 0
                #if other_aoi_result['重叠关系']['重叠占副框比例'] < 0.95:
                if other_aoi['x'] < je_aoi['x'] - 2:
                    if other_aoi_result['位置关系']['水平距离']  < je_aoi['width'] * 0.5 : 
                        other_item['pos'] = 1
                        fanzhe_result = 1
                        result['result_info']['算法错误'].append("❌ 检测到极耳翻折")
                        ng_info = ng_info + 'neicha;'
                    #break
                else:
                    if other_item['类型字符'] == 'za':
                        yueya_down = min(abs(other_aoi['x'] - je_aoi['x'] ) / float(je_aoi['height']), yueya_down)
                        if fanzhe_result == 0: 
                            fanzhe_result = 2
                        if yueya_wh_ng_enable:
                            if other_aoi['width'] / je_aoi['width'] > yueya_width_ng and other_aoi['height'] / other_aoi['width'] > yueya_height_ng:
                                fanzhe_result = 1 
                                result['result_info']['算法错误'].append("❌ 检测到月牙宽高异常")
                        result['result_info']['算法消息'].append(" 检测到月牙")
                    elif other_item['类型字符'] == 'fenceng':
                        fenceng_up = min(abs(other_aoi['x'] - je_aoi['x'] - je_aoi['width']) / float(je_aoi['width']), fenceng_up)
                        fenceng_pos = abs(other_aoi['x'] - je_aoi['x'] - je_aoi['width'])
                        if fenceng_pos > je_aoi['width'] * JE_fenceng_threshold:
                            if len(other_aoi_list) == 1:
                                if fanzhe_result == 0 or fanzhe_result == 2:
                                    fanzhe_result = 6                                         
                            else:
                                fanzhe_result = 1
                                ng_info = ng_info + 'dafenceng;'                          
                        else:
                            if fanzhe_result == 0 or fanzhe_result == 2:
                                fanzhe_result = 6
                        result['result_info']['算法消息'].append(" 检测到分层")
                    else:
                        # 计算other_aoi的中心点
                        other_center_x = other_aoi['x'] + other_aoi['width'] // 2
                        other_center_y = other_aoi['y'] + other_aoi['height'] // 2

                        # 计算je_aoi的中心点和上下半部分分界线
                        je_center_y = je_aoi['y'] + je_aoi['height'] // 2

                        # 计算other_aoi的四个角点
                        other_corners = [
                            (other_aoi['x'], other_aoi['y']),                                    # 左上角
                            (other_aoi['x'] + other_aoi['width'], other_aoi['y']),                 # 右上角
                            (other_aoi['x'] + other_aoi['width'], other_aoi['y'] + other_aoi['height']), # 右下角
                            (other_aoi['x'], other_aoi['y'] + other_aoi['height'])                 # 左下角
                        ]

                        # 根据other_aoi在je_aoi的位置选择镜像对角线方向
                        if other_center_y < je_center_y:
                            # other_aoi在je_aoi的上半部分，使用other_aoi的左上到右下对角线
                            diagonal_p1 = other_corners[0]  # 左上角
                            diagonal_p2 = other_corners[2]  # 右下角
                            target_point = other_corners[1]  # 右上角进行镜像
                            position_desc = "上半部分，沿左上-右下对角线镜像右上角"
                        else:
                            # other_aoi在je_aoi的下半部分，使用other_aoi的右上到左下对角线
                            diagonal_p1 = other_corners[1]  # 右上角
                            diagonal_p2 = other_corners[3]  # 左下角
                            target_point = other_corners[2]  # 右下角进行镜像
                            position_desc = "下半部分，沿右上-左下对角线镜像右下角"

                        # 计算镜像点
                        cur_pnt = mirror_point_across_line(target_point, diagonal_p1, diagonal_p2)

       

                        # 将镜像点信息添加到检测项中
                        other_item['镜像分析'] = {
                            '中心位置': (other_center_x, other_center_y),
                            '位置描述': position_desc,
                            '原始角点': target_point,
                            '镜像点': cur_pnt,
                            '对角线端点': [diagonal_p1, diagonal_p2]
                        }

                        isNG = False
                        if cur_pnt[0] - je_aoi['x']  < je_aoi['width'] * fanzhe_protect_ratio :
                            fanzhe_result = 1
                            ng_info = ng_info + 'dafanzhe;'
                            result['result_info']['算法错误'].append("❌ 检测到极耳翻折")
                            isNG = True

                        #other_distance = min(abs(other_aoi['y'] - je_aoi['y']), abs(other_aoi['y'] + other_aoi['height'] - je_aoi['y'] - je_aoi['height']))
                        #print(f"  上距离: {abs(other_aoi['y'] - je_aoi['y'])}, 下距离: {other_aoi['y'] + other_aoi['height'] - je_aoi['y'] - je_aoi['height']}, 最小垂直距离: {other_distance}")

                        #if (other_aoi['x'] - je_aoi['x'] <  je_aoi['width'] * fanzhe_protect_start * + je_aoi['width'] * fanzhe_protect_expand *
                            #max(0.0, abs(other_distance) / (je_aoi['height']/2.0))) and not isNG:
                        if other_aoi['x'] - je_aoi['x'] <  je_aoi['width'] * fanzhe_protect_start and not isNG:
                            #print(f" 保护距离判定: 水平距离 {other_aoi['x'] - je_aoi['x']} < 保护距离 {je_aoi['width'] * fanzhe_protect_expand * max(0.0, 1.0 - abs(other_distance) / (je_aoi['height']/2.0))}，且未被之前的翻折判定为NG")
                            fanzhe_result = 1
                            ng_info = ng_info + 'fanzheguoshen;'
                            result['result_info']['算法错误'].append("❌ 检测到极耳翻折")
                            print(f"  保护距离判定:判定阈值{fanzhe_protect_start} 水平距离 {other_aoi['x'] - je_aoi['x']} > 保护距离 {je_aoi['width'] * fanzhe_protect_start}，且未被之前的翻折判定为NG")
                            isNG = True

                        if not isNG:
                            if fanzhe_result != 1:
                                fanzhe_result = 5
                            result['result_info']['算法消息'].append("    检测到小翻折")                           

        if fenceng_up < 1.0 and yueya_down < 0.1:
            fanzhe_result = 1
            ng_info = ng_info + 'dafencengC2;'  
        check_je_end = time.perf_counter()

        print(f"翻折识别时间: {check_je_end - aoi_seg_end:.4f} 秒")

        use_gm_cnn = False
        cls_load_success = self.gm_cls_model.load_from_path(gm_cls_model_path)
        if enable_gm_cls:            
            if fanzhe_result != 1 and fanzhe_result != 0:
                use_gm_cnn = True
        if enable_gm_cls_all:
            use_gm_cnn = True
        if has_horizontal_texture:
            use_gm_cnn = True  

        gm_result = {}

        gm_aoi = None
        if je_aoi is not None:
            gm_aoi = je_aoi.copy()
            gm_aoi['x'] = gm_aoi['x'] - int(JE_height)
            gm_aoi['y'] = gm_aoi['y'] - int(gm_aoi['height'] * 0.5)
            gm_aoi['height'] = gm_aoi['height'] * 2
            gm_aoi['width'] = int(JE_height)
            if gm_aoi['x'] < 0:
                gm_aoi['width'] = gm_aoi['width'] + int(gm_aoi['x'])
                gm_aoi['x'] = 0
            jegm_aoi = gm_aoi.copy()
            jegm_aoi['width'] = jegm_aoi['width'] * 2                
            gm_image_cls = utils.get_cv_img_fronm_aoi_ex(src_image, gm_aoi)
            gm_image_cls = cv2.resize(gm_image_cls, (256, 256))


        gray_detect_data = None
            
        #print(f"🔍 灰度检测参数检查: gray_detect_enable={gray_detect_enable}, gray_ng_enable={gray_ng_enable}, gray_inward_ratio={gray_inward_ratio}")
        
        if gray_detect_enable and gm_aoi is not None:
            # 对JE框图像适用gray_inward_ratio缩放，并再范围内取九点，去掉最高最低值，再取出均值和中值作为极耳参数
            # 对于gm_image_cls，直接范围内取九点，，去掉最高最低值，再取出均值和中值作为极耳参数
            # 分别计算两个图的最高值和均值差值
            # 查看是否gray_inward_ratio，否的话不检测结果
            # 是的话检测双方差值是否在gray_diff_max和gray_diff_min范围内，在就检测fanzhe_result如果不等于1就置位成1
            
            try:
                print(f"🔍 灰度检测条件检查: je_aoi={je_aoi is not None}, gray_inward_ratio={gray_inward_ratio}")
                if je_aoi is not None and gray_inward_ratio > 0:
                    # 直接在原图上计算JE内缩区域的坐标，避免任何图像操作
                    je_x, je_y, je_w, je_h = je_aoi['x'], je_aoi['y'], je_aoi['width'], je_aoi['height']
                    
                    # 计算内缩后的坐标
                    inward_w = int(je_w * gray_inward_ratio)
                    inward_h = int(je_h * gray_inward_ratio)
                    inward_start_x = je_x + (je_w - inward_w) // 2
                    inward_start_y = je_y + (je_h - inward_h) // 2
                    
                    # 直接在原图上采样JE区域的9个点
                    je_points = []
                    for i in range(3):
                        for j in range(3):
                            y = inward_start_y + inward_h * (i + 1) // 4
                            x = inward_start_x + inward_w * (j + 1) // 4
                            if y < src_image.shape[0] and x < src_image.shape[1]:
                                if len(src_image.shape) == 3:
                                    je_points.append(int(src_image[y, x, 0]))  # 直接取第0通道
                                else:
                                    je_points.append(int(src_image[y, x]))
                    
                    # 直接在GM图像上采样9个点
                    gm_points = []
                    gm_h, gm_w = gm_image_cls.shape[:2]
                    for i in range(3):
                        for j in range(3):
                            y = gm_h * (i + 1) // 4
                            x = gm_w * (j + 1) // 4
                            if y < gm_h and x < gm_w:
                                if len(gm_image_cls.shape) == 3:
                                    gm_points.append(int(gm_image_cls[y, x, 0]))  # 直接取第0通道
                                else:
                                    gm_points.append(int(gm_image_cls[y, x]))
                    
                    # 快速统计计算，避免多次调用函数
                    # JE统计
                    if len(je_points) >= 3:
                        je_points.sort()
                        je_filtered = je_points[1:-1] if len(je_points) > 2 else je_points
                        je_mean = sum(je_filtered) / len(je_filtered)
                        je_median = je_filtered[len(je_filtered)//2]
                        je_max = je_points[-1]
                    else:
                        je_mean = je_median = je_max = 0
                        
                    # GM统计
                    if len(gm_points) >= 3:
                        gm_points.sort()
                        gm_filtered = gm_points[1:-1] if len(gm_points) > 2 else gm_points
                        gm_mean = sum(gm_filtered) / len(gm_filtered)
                        gm_median = gm_filtered[len(gm_filtered)//2]
                        gm_max = gm_points[-1]
                    else:
                        gm_mean = gm_median = gm_max = 0
                    
                    # 计算差值
                    max_diff = abs(je_max - gm_max)
                    mean_diff = abs(je_mean - gm_mean)
                    
                    # 存储灰度检测数据用于图片显示
                    gray_detect_data = {
                        'je_mean': je_mean,
                        'je_median': je_median,
                        'je_max': je_max,
                        'gm_mean': gm_mean,
                        'gm_median': gm_median,
                        'gm_max': gm_max,
                        'max_diff': max_diff,
                        'mean_diff': mean_diff,
                        'median_diff': abs(je_median - gm_median)
                    }
                    
                    print(f"✅ 灰度检测数据已创建: JE均值={je_mean:.1f}, GM均值={gm_mean:.1f}, 差值={mean_diff:.1f}")
                    
                    # 判断差值是否在范围内
                    max_diff_in_range = gray_diff_min <= max_diff <= gray_diff_max
                    mean_diff_in_range = gray_diff_min <= mean_diff <= gray_diff_max
                    

                    
                    # 如果启用灰度NG判定且差值不在范围内，且当前结果不是NG(1)，则置为NG
                    if gray_ng_enable and not (max_diff_in_range and mean_diff_in_range):
                        if fanzhe_result != 1:
                            fanzhe_result = 1
                            ng_info = ng_info + 'huiduNG;'
                            result['result_info']['算法错误'].append(f"❌ 灰度差值异常: 最高值差值={max_diff:.2f}, 均值差值={mean_diff:.2f}")
                    else:
                        pass
                
                else:
                    print(f"⚠️ 跳过灰度检测: je_aoi={je_aoi is not None}, gray_inward_ratio={gray_inward_ratio}")
                    if je_aoi is None:
                        print(f"   - je_aoi为空，检查极耳检测是否成功")
                    if gray_inward_ratio <= 0:
                        print(f"   - gray_inward_ratio={gray_inward_ratio}，需要>0")
                    # 跳过灰度检测，保持初始值 None
            except Exception as e:
                print(f"❌ 灰度检测异常: {str(e)}")
                result['result_info']['算法错误'].append(f"灰度检测异常: {str(e)}")
                # 异常情况下设置空的灰度检测数据
                gray_detect_data = None
        else:
            print(f"ℹ️ 灰度检测已禁用: gray_detect_enable={gray_detect_enable}")


        if je_aoi is not None and use_gm_cnn:    
            if cls_load_success:
                custom_class_names = {
                    0: "OK",
                    1: "fanguang",
                    2: "fanguang_pluse",
                    3: "dazhou",
                    4: "neicha"
                }
                self.gm_cls_model.set_class_names(custom_class_names)
                results = self.gm_cls_model.predict(gm_image_cls, top_k=5,use_yolo_preprocess=True)
                #for result0 in results:
                #    print(f"隔膜分类结果Test: {result0}")
                top1_result = self.gm_cls_model.get_top1_result(results)
                print(f"隔膜分类Top1结果: {top1_result}")
                if top1_result:
                    gm_result["类别"] = top1_result['类型字符']
                    gm_result["置信度"] = top1_result['分类置信度']
                    if top1_result['类型字符'] == 'OK'  or top1_result['类型字符'] == 'fanguang' or top1_result['类型字符'] == 'fanguang_pluse':
                        gm_result["结果"]  = "OK"
                    else:
                        gm_result["结果"]  = "NG"
                        fanzhe_result = 1
                        ng_info = ng_info + 'gemoNG;'
            else:
                gm_result["模型错误"] = self.gm_cls_model.model_load_error
            if not cls_load_success:
                gm_result["模型错误"] = self.gm_cls_model.model_load_error

        gm_je_end = time.perf_counter()
        gm_result["推理耗时"] = gm_je_end - check_je_end
        print(f"隔膜识别时间: {gm_je_end - check_je_end:.4f} 秒")



        result['结果'] = 'OK'
        if fanzhe_result == 0:
            result['结果'] = 'OK'
        elif fanzhe_result == 1:
            result['结果'] = 'NG'
        elif fanzhe_result == 2:
            result['结果'] = 'OK'
            result['异常'] = '月牙'
        elif fanzhe_result == 3:
            result['结果'] = 'NG'
            result['异常'] = '无极耳'
        elif fanzhe_result == 4:
            result['结果'] = 'NG'
            result['异常'] = '多极耳'
        elif fanzhe_result == 5:
            result['结果'] = 'OK'
            result['异常'] = '小翻折'
        elif fanzhe_result == 6:
            result['结果'] = 'OK'
            result['异常'] = '分层或月牙'

        
        # if self.need_save_image:
        #     if save_image_by_model:
        #         if not has_other_item:
        #             with self.save_lock:
        #                 if self.save_ok_count > 0:
        #                     if random.randint(1, 1000) == 1:
        #                         self.save_ok_count -= 1
        #                         save_dict={}
        #                         save_dict["name"] = f"翻折检测_{datetime.now().strftime('%Y%m%d_%H%M%S_%f')}_OK.png"
        #                         save_dict["image"] = src_image.copy()
        #                         if len(self.save_list) < 5:
        #                             self.save_list.append(save_dict)
        #         else:
        #             with self.save_lock:
        #                 self.save_ok_count += 1
        #                 save_dict={}
        #                 if  result['结果'] == 'OK':
        #                     save_dict["name"] = f"翻折检测_{datetime.now().strftime('%Y%m%d_%H%M%S_%f')}_OK_有瑕疵.png"
        #                 else:
        #                     save_dict["name"] = f"翻折检测_{datetime.now().strftime('%Y%m%d_%H%M%S_%f')}_NG_有瑕疵.png"
        #                 save_dict["image"] = src_image.copy()
        #                 if len(self.save_list) < 5:
        #                     self.save_list.append(save_dict)
        # else:
        #     self.need_save_image = True

        # 生成JSON绘图数据
        draw_items = []
        cbv1_text_items = []

        # 1. 绘制所有检测框
        for detect_item in deleted_list:
            if "不显示" in detect_item:
                continue
            aoi_pos = detect_item.get('AOI位置', {})
            if aoi_pos:
                x1, y1 = aoi_pos['x1'], aoi_pos['y1']
                x2, y2 = aoi_pos['x2'], aoi_pos['y2']
                class_name = detect_item.get('类型字符', 'unknown')
                confidence = detect_item.get('综合置信度', 0)
                conf_str = f"{class_name}:{confidence:.3f}"
                cbv1_text_items.append(conf_str)
                # 根据类别设置颜色
                color_map = {
                    'JE': {'r': 0, 'g': 255, 'b': 0},      # 绿色 - 极耳
                    'je': {'r': 255, 'g': 0, 'b': 0},      # 红色 - 折痕
                    'za': {'r': 255, 'g': 255, 'b': 0},    # 黄色 - 月牙
                    'GMqiaoqi': {'r': 255, 'g': 0, 'b': 255}, # 紫色 - 内插
                    'gemo': {'r': 0, 'g': 255, 'b': 255},  # 青色 - 内嵌
                    'fenceng': {'r': 255, 'g': 165, 'b': 0}, # 橙色 - 分层
                }
                color = color_map.get(class_name, {'r': 128, 'g': 128, 'b': 128})  # 默认灰色
                
                # 检测框矩形
                rect_item = {
                    "type": "rectangle",
                    "topLeft": {"x": float(x1), "y": float(y1)},
                    "bottomRight": {"x": float(x2), "y": float(y2)},
                    "lineStyle": 0,  # Solid
                    "penWidth": 2,
                    "color": color,
                    "show": True
                }
                draw_items.append(rect_item)
                
                # 检测框标签文本
                label_text = f"{class_name}({confidence:.3f})"
                text_item = {
                    "type": "text",
                    "text": label_text,
                    "fontFamily": "Arial",
                    "fontSize": 12,
                    "color": color,
                    "position": {"x": float(x1), "y": float(y1 - 5)},
                    "show": True
                }
                draw_items.append(text_item)
      
        # 2. 绘制镜像分析可视化
        for other_item in other_aoi_list:
            if "不显示" in other_item:
                continue
            if '镜像分析' in other_item:
                mirror_analysis = other_item['镜像分析']
                
                # 获取关键点坐标
                mirror_point = mirror_analysis['镜像点']
                original_point = mirror_analysis['原始角点']
                diagonal_points = mirror_analysis['对角线端点']
                diagonal_p1, diagonal_p2 = diagonal_points
                
                # 绘制对角线（蓝色虚线）
                diagonal_line = {
                    "type": "polygon",
                    "isClosed": False,
                    "lineStyle": 1,  # Dash
                    "penWidth": 2,
                    "color": {"r": 255, "g": 0, "b": 0},  # 蓝色
                    "points": [
                        {"x": float(diagonal_p1[0]), "y": float(diagonal_p1[1])},
                        {"x": float(diagonal_p2[0]), "y": float(diagonal_p2[1])}
                    ],
                    "show": True
                }
                draw_items.append(diagonal_line)
                
                # 绘制原始角点到镜像点的连线（绿色实线）
                mirror_line = {
                    "type": "polygon",
                    "isClosed": False,
                    "lineStyle": 0,  # Solid
                    "penWidth": 2,
                    "color": {"r": 0, "g": 255, "b": 0},  # 绿色
                    "points": [
                        {"x": float(original_point[0]), "y": float(original_point[1])},
                        {"x": float(mirror_point[0]), "y": float(mirror_point[1])}
                    ],
                    "show": True
                }
                draw_items.append(mirror_line)
                
                # 绘制镜像点到对角线端点的连线（黄色细线）
                for diag_point in diagonal_points:
                    connection_line = {
                        "type": "polygon",
                        "isClosed": False,
                        "lineStyle": 0,  # Solid
                        "penWidth": 1,
                        "color": {"r": 255, "g": 255, "b": 0},  # 黄色
                        "points": [
                            {"x": float(mirror_point[0]), "y": float(mirror_point[1])},
                            {"x": float(diag_point[0]), "y": float(diag_point[1])}
                        ],
                        "show": True
                    }
                    draw_items.append(connection_line)
                
                # 绘制关键点标记
                # 原始角点（红色圆点）
                original_circle = {
                    "type": "circle",
                    "center": {"x": float(original_point[0]), "y": float(original_point[1])},
                    "radius": 5.0,
                    "lineStyle": 0,
                    "penWidth": 2,
                    "color": {"r": 0, "g": 0, "b": 255},  # 红色
                    "show": True
                }
                draw_items.append(original_circle)
                
                # 镜像点（绿色圆点）
                mirror_circle = {
                    "type": "circle",
                    "center": {"x": float(mirror_point[0]), "y": float(mirror_point[1])},
                    "radius": 5.0,
                    "lineStyle": 0,
                    "penWidth": 2,
                    "color": {"r": 0, "g": 255, "b": 0},  # 绿色
                    "show": True
                }
                draw_items.append(mirror_circle)
                
                # 对角线端点（蓝色小圆点）
                for diag_point in diagonal_points:
                    diag_circle = {
                        "type": "circle",
                        "center": {"x": float(diag_point[0]), "y": float(diag_point[1])},
                        "radius": 3.0,
                        "lineStyle": 0,
                        "penWidth": 1,
                        "color": {"r": 255, "g": 0, "b": 0},  # 蓝色
                        "show": True
                    }
                    draw_items.append(diag_circle)
        
        # 3. 绘制极耳保护区域
        if je_aoi is not None:
            # 计算保护区域边界线
            protect_x = int(je_aoi['x'] + je_aoi['width'] * fanzhe_protect_ratio)
            
            # 绘制垂直的保护边界线（红色粗线）
            protect_line = {
                "type": "polygon",
                "isClosed": False,
                "lineStyle": 0,  # Solid
                "penWidth": 3,
                "color": {"r": 255, "g": 0, "b": 0},  # 红色
                "points": [
                    {"x": float(protect_x), "y": float(je_aoi['y'])},
                    {"x": float(protect_x), "y": float(je_aoi['y'] + je_aoi['height'])}
                ],
                "show": True
            }
            draw_items.append(protect_line)
            
            # 绘制起始保护区域矩形（红色边框）
            start_rect_right = int(je_aoi['x'] + je_aoi['width'] * fanzhe_protect_start)
            protect_rect = {
                "type": "rectangle",
                "topLeft": {"x": float(je_aoi['x']), "y": float(je_aoi['y'])},
                "bottomRight": {"x": float(start_rect_right), "y": float(je_aoi['y'] + je_aoi['height'])},
                "lineStyle": 1,  # Dash
                "penWidth": 2,
                "color": {"r": 255, "g": 0, "b": 0},  # 红色
                "show": True
            }
            draw_items.append(protect_rect)
        # 4. 添加隔膜结果
        if '结果' in gm_result:
            gm_text = f"GM: {gm_result['结果']}({gm_result['类别']},{gm_result['推理耗时']:.4f}s)"
            
            # 添加隔膜文本
            gm_text_color = {"r": 0, "g": 255, "b": 0} if gm_result['结果'] == "OK" else {"r": 255, "g": 0, "b": 0}
            gm_text_item = {
                "type": "text",
                "text": gm_text,
                "fontFamily": "Arial",
                "fontSize": 14,
                "color": gm_text_color,
                "position": {"x": 10.0, "y": 100.0},  # 调整到Y=100，避免与状态文本重叠
                "show": True
            }
            draw_items.append(gm_text_item)
            
            # 添加隔膜AOI矩形
            if 'gm_aoi' in locals():
                gm_rect_color = {"r": 0, "g": 255, "b": 0} if gm_result['结果'] == "OK" else {"r": 255, "g": 0, "b": 0}
                gm_rect_item = {
                    "type": "rectangle",
                    "topLeft": {"x": float(gm_aoi['x']), "y": float(gm_aoi['y'])},
                    "bottomRight": {"x": float(gm_aoi['x'] + gm_aoi['width']), "y": float(gm_aoi['y'] + gm_aoi['height'])},
                    "lineStyle": 0,  # Solid
                    "penWidth": 2,
                    "color": gm_rect_color,
                    "show": True
                }
                draw_items.append(gm_rect_item)


        # 5. 添加状态文本信息

        status_texts = [
            f"Model: {model_cost:.4f}s",
            f"Result: {result['结果']}",
            f"count: {self.run_count:d}",
        ]
        
        for i, text in enumerate(status_texts):
            text_color = {"r": 0, "g": 255, "b": 0} if fanzhe_result == 0 and i == 1 else {"r": 0, "g": 255, "b": 0}
            if result['结果'] != "OK" and i == 1:
                text_color = {"r": 255, "g": 0, "b": 0}  # NG用红色
            
            text_item = {
                "type": "text",
                "text": text,
                "fontFamily": "Arial",
                "fontSize": 14,
                "color": text_color,
                "position": {"x": 10.0, "y": 25.0 + i * 25.0},
                "show": True
            }
            draw_items.append(text_item)
        
        # 6. 添加灰度检测信息
        if gray_detect_data is not None:
            print(f"🔍 准备显示灰度检测数据: JE={gray_detect_data['je_mean']:.1f}, GM={gray_detect_data['gm_mean']:.1f}")
            
            
            # 显示极耳数据
            temp_str = f"极耳: 平均值={gray_detect_data['je_mean']:.1f} 中值={gray_detect_data['je_median']:.1f}"
            temp_str_en = f"JE: Mean={gray_detect_data['je_mean']:.1f} Median={gray_detect_data['je_median']:.1f}"
            je_text_item = {
                "type": "text",
                "text": temp_str_en,
                "fontFamily": "Arial",
                "fontSize": 14,
                "color": {"r": 255, "g": 255, "b": 0},  # 黄色
                "position": {"x": 10.0, "y": 125.0},
                "show": True
            }
            draw_items.append(je_text_item)
            cbv1_text_items.append(temp_str)


            # 显示隔膜数据
            temp_str = f"隔膜: 平均值={gray_detect_data['gm_mean']:.1f} 中值={gray_detect_data['gm_median']:.1f}"
            temp_str_en = f"GM: Mean={gray_detect_data['gm_mean']:.1f} Median={gray_detect_data['gm_median']:.1f}"
            gm_text_item = {
                "type": "text",
                "text": temp_str_en,
                "fontFamily": "Arial",
                "fontSize": 14,
                "color": {"r": 255, "g": 255, "b": 0},  # 黄色
                "position": {"x": 10.0, "y": 150.0},
                "show": True
            }
            draw_items.append(gm_text_item)
            cbv1_text_items.append(temp_str)
            
            # 显示差值数据
            temp_str = f"极耳，隔膜: 平均值差={gray_detect_data['mean_diff']:.1f} 中值差={gray_detect_data['median_diff']:.1f}"
            temp_str_en = f"Diff: Mean Diff={gray_detect_data['mean_diff']:.1f} Median Diff={gray_detect_data['median_diff']:.1f}"
            diff_text_item = {
                "type": "text",
                "text": temp_str_en,
                "fontFamily": "Arial",
                "fontSize": 14,
                "color": {"r": 0, "g": 255, "b": 255},  # 青色
                "position": {"x": 10.0, "y": 175.0},
                "show": True
            }
            draw_items.append(diff_text_item)
            cbv1_text_items.append(temp_str)


            #ng_info
        else:
            print(f"⚠️ 灰度检测数据为空，未显示")

        # 6. 添加NG原因信息
        print(f"准备显示结果数据")
        ng_text_item = {
            "type": "text",
            "text": ng_info,
            "fontFamily": "Arial",
            "fontSize": 14,
            "color": {"r": 0, "g": 255, "b": 255},  # 青色
            "position": {"x": 10.0, "y": 200.0},
            "show": True
        }
        draw_items.append(ng_text_item)
        # ng_text_item 是绘图JSON对象，不加入 cbv1_text_items（该列表只存字符串）
        cbv1_text_items.append(ng_info)  # 只添加文本内容

        # 将JSON绘图数据添加到结果中
        result['draw_items'] = draw_items
        result['cbv1_text_items'] = cbv1_text_items

        #result['result_image']['结果图'] = self.detect_model.draw_detections_from_list(image, deleted_list)

        return result
    
    def get_available_functions(self) -> Dict[str, str]:
        """
        获取检测器可用的检测函数列表
        """
        return {
            'detect': '翻折检测（仅检测）'
        }
    
    def call_function(self, function_name: str, image: np.ndarray, params: Dict[str, Any] = None) -> Dict[str, Any]:
        """
        调用指定的检测函数
        """
        if function_name == 'detect':
            return self.detect(image, params)
        else:
            raise ValueError(f"未知的函数名称: {function_name}")
    
    def run_all(self, image: np.ndarray, params: Dict[str, Any] = None) -> Dict[str, Any]:
        """
        完整检测流程：极耳裁切 + 翻折检测
        从主程序迁移的 run_all 函数
        """
        if not self.initialized:
            return {
                '错误信息': '检测器未初始化'
            }
        
        # 使用传入的参数或默认参数
        detection_params = params if params is not None else self.parameters
        #print(detection_params)
        try:
            start_time = time.perf_counter()
            result = {}
            result_up = {}
            result_down = {}
            
            # 执行裁切
            result_all = self._run_cut(image, detection_params)
            #print(f"裁切结果: {result_all}")
            if '上极耳裁切框' in result_all:
                result['上极耳裁切框'] = result_all['上极耳裁切框']
            if '下极耳裁切框' in result_all:
                result['下极耳裁切框'] = result_all['下极耳裁切框']
            if '上极耳AOI' in result_all:
                result['上极耳AOI'] = result_all['上极耳AOI']
            if '下极耳AOI' in result_all:
                result['下极耳AOI'] = result_all['下极耳AOI']
            
            if '异常结束' in result_all:
                print("❌ 检测异常结束，跳过后续步骤")
                result['错误信息'] = '推理异常结束'
                return result
            
            result['cbv1_text_items'] = []
            
            # 处理上极耳
            if 'up_jr_image' in result_all['result_image']:
                up_image = result_all['result_image']['up_jr_image']
                try:
                    result_up = self._run_detection(up_image, detection_params)
                    if "cbv1_text_items" in result_up:
                        for item in result_up["cbv1_text_items"]:
                            result['cbv1_text_items'].append("上极耳:" + item)
                    
                    if '异常结束' in result_up:
                        result['错误信息'] = '推理异常结束'
                        return result
                except Exception as e:
                    print(f"❌ 上极耳检测失败: {str(e)}")
                    result['错误信息'] = f'上极耳检测失败: {str(e)}'
                    return result
            
            # 处理下极耳
            if 'down_jr_image' in result_all['result_image']:
                down_image = result_all['result_image']['down_jr_image']
                try:
                    result_down = self._run_detection(down_image, detection_params)
                    if "cbv1_text_items" in result_down:
                        for item in result_down["cbv1_text_items"]:
                            result['cbv1_text_items'].append("下极耳:" + item)
                    
                    if '异常结束' in result_down:
                        result['错误信息'] = '推理异常结束'
                        return result
                except Exception as e:
                    print(f"❌ 下极耳检测失败: {str(e)}")
                    result['错误信息'] = f'下极耳检测失败: {str(e)}'
                    return result
            
            result['上极耳结果'] = result_up
            result['下极耳结果'] = result_down
            
            # 复原检测结果到原图标注
            result['draw_items_original'] = self._restore_draw_items_to_original(result_up, result_down, result_all)
            
            # 复原完成后清除上下极耳自身的标注数据
            if result_up:
                result_up.pop('draw_items', None)
                result_up.pop('draw_items_original', None)
                result_up.pop('result_image', None)  # 清除图像数据
            if result_down:
                result_down.pop('draw_items', None)
                result_down.pop('draw_items_original', None)
                result_down.pop('result_image', None)  # 清除图像数据
            
            # 清除中间变量中的图像数据（防止意外返回）
            if 'result_image' in result:
                result.pop('result_image', None)
            
            end_time = time.perf_counter()
            print(f"检测耗时: {end_time - start_time:.2f}秒")

            return result
            
        except Exception as e:
            print(f"❌ 完整检测异常: {str(e)}")
            return {
                '错误信息': f'完整检测异常: {str(e)}'
            }

    def _debug_print_dict_types(self, data, path="", max_depth=3, current_depth=0):
        """递归打印字典中每个值的数据类型，查找numpy数组"""
        import numpy as np
        
        if current_depth > max_depth:
            print(f"  {path}: <递归深度超限>")
            return
            
        if isinstance(data, dict):
            for key, value in data.items():
                current_path = f"{path}.{key}" if path else key
                if isinstance(value, np.ndarray):
                    print(f"  ❌ {current_path}: numpy.ndarray (shape={value.shape}) <-- 发现numpy数组!")
                elif isinstance(value, (dict, list)):
                    self._debug_print_dict_types(value, current_path, max_depth, current_depth + 1)
                else:
                    print(f"  ✅ {current_path}: {type(value).__name__}")
        elif isinstance(data, list):
            for i, item in enumerate(data):
                current_path = f"{path}[{i}]" if path else f"[{i}]"
                if isinstance(item, np.ndarray):
                    print(f"  ❌ {current_path}: numpy.ndarray (shape={item.shape}) <-- 发现numpy数组!")
                elif isinstance(item, (dict, list)):
                    self._debug_print_dict_types(item, current_path, max_depth, current_depth + 1)
                else:
                    print(f"  ✅ {current_path}: {type(item).__name__}")
        else:
            if isinstance(data, np.ndarray):
                print(f"  ❌ {path}: numpy.ndarray (shape={data.shape}) <-- 发现numpy数组!")
            else:
                print(f"  ✅ {path}: {type(data).__name__}")
    
    def _run_cut(self, image: np.ndarray, params: Dict[str, Any]) -> Dict[str, Any]:
        """
        执行极耳裁切
        从主程序的 run_cut 方法迁移实现
        """
        # 返回值
        result = {}
        result['result_image'] = {}
        result['result_info'] = {}
        result['result_info']['算法错误'] = []
        result['result_info']['算法消息'] = []
        result['结果'] = 'NG'
        
        # 裁切模型参数
        cut_params = params.get("极耳裁切模型参数", {})
        cut_model_path = cut_params.get("模型路径", "")
        cut_confidence = cut_params.get("置信度", 0.4)
        cut_iou_threshold = cut_params.get("裁切IOU阈值", 0.9)
        cut_input_size = cut_params.get("输入尺寸", 512)
        cut_merge_detect = cut_params.get("合并检测框", True)
        
        # 裁切用户参数
        usr_dict = params.get("极耳裁切用户参数", {})
        top_jr_width = usr_dict.get("上极耳宽度", 400)
        top_jr_height = usr_dict.get("上极耳高度", 150)
        bottom_jr_width = usr_dict.get("下极耳宽度", 400)
        bottom_jr_height = usr_dict.get("下极耳高度", 150)
        jr_width_expand_ratio = usr_dict.get("极耳宽度扩展冗余", 0.5)
        jr_height_expand_ratio = usr_dict.get("极耳高度扩展冗余", 0.5)
        jier_spacing = usr_dict.get("极耳间距", 800)     
        gm_get_deep  = usr_dict.get("隔膜获取深度", 0.5)

        start_time = time.perf_counter()
        # 缩放原图
        src_image = image
        src_height, src_width = src_image.shape[:2]
        resize_image = cv2.resize(src_image, (cut_input_size, cut_input_size))
        resize_time = time.perf_counter()
        print(f"⏱️ 缩放时间: {resize_time - start_time:.4f} 秒")   

        # 极耳裁切
        load_result = self.cut_model.load_from_path_with_device(cut_model_path, self.cuda_name)
        if not load_result:
            print(f"模型加载失败: {self.cut_model.model_load_error}")
            result['result_info']['算法错误'].append(f"裁切模型加载失败: {self.cut_model.model_load_error}")
            return result        
        
        cut_load_time = time.perf_counter()
        print(f"⏱️ 裁切模型加载时间: {cut_load_time - resize_time:.4f} 秒")
        
        self.cut_model.use_independent_conf = False
        self.cut_model.global_conf = cut_confidence
        usr_class_name = {
            'class0':'jier',  # 极耳
        }
        self.cut_model.set_class_names(usr_class_name)

        with self.cut_model_lock:
            try:
                model_results = self.cut_model.predictEx(
                    resize_image,
                    device=self.cuda_name,
                    iou=cut_iou_threshold,
                    imgsz=cut_input_size,
                    verbose=False
                )
            except Exception as e:
                print(f"裁切模型推理失败: {e}")
                result['result_info']['算法错误'].append(f"裁切模型推理失败: {e}")
                result['异常结束'] = True
                return result

        cut_predict_time = time.perf_counter()
        print(f"⏱️ 裁切模型推理时间: {cut_predict_time - cut_load_time:.4f} 秒")        

        # 提取瑕疵列表
        cut_jr_list = self.cut_model.extract_detection_list(model_results)
        extract_detection_list_end = time.perf_counter()
        print(f"提取极耳框时间: {extract_detection_list_end - cut_predict_time:.4f} 秒")

        # 合并重叠检测框
        merge_success, merge_msg = self.cut_model.merge_overlapping_detections_inplace(cut_jr_list, 0.01)
        cut_merge_detect_end = time.perf_counter()
        print(f"合并极耳框时间: {cut_merge_detect_end - extract_detection_list_end:.4f} 秒")

        # 检查裁切扩充后是否会超出图像边界
        for jier in cut_jr_list:
            # 计算缩放比例
            scale_x = src_width / cut_input_size
            scale_y = src_height / cut_input_size
            
            # 将检测框坐标转换回原图尺寸
            orig_x1 = int(jier['AOI位置']['x1'] * scale_x)
            orig_y1 = int(jier['AOI位置']['y1'] * scale_y)
            orig_width = int(jier['AOI位置']['width'] * scale_x)
            orig_height = int(jier['AOI位置']['height'] * scale_y)
            orig_center_x = orig_x1 + orig_width // 2
            orig_center_y = orig_y1 + orig_height // 2
            
            # 判断是上极耳还是下极耳（基于图片中心线）
            is_up_jier = orig_center_y < src_height / 2
            
            # 根据极耳类型选择相应的裁切参数
            if is_up_jier:
                jr_width_param = top_jr_width
                jr_height_param = top_jr_height
            else:
                jr_width_param = bottom_jr_width
                jr_height_param = bottom_jr_height
            
            # 计算裁切扩充后的AOI边界
            # 极耳是竖直放置的，参数中的"宽"对应图片的"高"，"高"对应图片的"宽"
            cut_aoi_x = orig_center_x - int(jr_height_param * (1 + jr_height_expand_ratio) / 2)
            cut_aoi_y = orig_center_y - int(jr_width_param * (1 + jr_width_expand_ratio) / 2)
            cut_aoi_width = int(jr_height_param * (1 + jr_height_expand_ratio))
            cut_aoi_height = int(jr_width_param * (1 + jr_width_expand_ratio))
            
            # 额外扩展
            cut_aoi_x = cut_aoi_x - jr_height_param
            cut_aoi_width = cut_aoi_width + jr_height_param
            
            # 检查是否超出图像边界
            if (cut_aoi_x < 0 or 
                cut_aoi_y < 0 or 
                cut_aoi_x + cut_aoi_width > src_width or 
                cut_aoi_y + cut_aoi_height > src_height):
                
                print(f"⚠️ 检测框会超出图像边界，将置信度降低到0.1: {jier.get('类型字符', 'unknown')}")
                print(f"   原始置信度: {jier['综合置信度']:.3f}")
                print(f"   裁切AOI: x={cut_aoi_x}, y={cut_aoi_y}, w={cut_aoi_width}, h={cut_aoi_height}")
                print(f"   图像尺寸: w={src_width}, h={src_height}")
                jier['综合置信度'] = 0.1

        # 根据置信度，只保留两个结果
        cut_jr_list = sorted(cut_jr_list, key=lambda x: x['综合置信度'], reverse=True)[:2]
        
        # 如果只有一个极耳，根据极耳是上极耳还是下极耳平移jier_spacing距离生成另一个极耳
        src_jr_list = []
        for jier in cut_jr_list:
            # 先复制整个字典，保留所有原始内容
            temp_item = jier.copy()
            temp_aoi = {}
            
            scale_x = src_width / cut_input_size
            scale_y = src_height / cut_input_size
            
            # 将AOI位置按比例放大回原图尺寸
            temp_aoi['x'] = int(jier['AOI位置']['x1'] * scale_x)
            temp_aoi['y'] = int(jier['AOI位置']['y1'] * scale_y)
            temp_aoi['width'] = int(jier['AOI位置']['width'] * scale_x)
            temp_aoi['height'] = int(jier['AOI位置']['height'] * scale_y)
            
            # 更新缩放后的AOI信息
            temp_item['aoi'] = temp_aoi
            
            # 同时更新AOI位置中的坐标（保持完整的结构）
            temp_item['AOI位置']['x1'] = int(jier['AOI位置']['x1'] * scale_x)
            temp_item['AOI位置']['y1'] = int(jier['AOI位置']['y1'] * scale_y)
            temp_item['AOI位置']['x2'] = int(jier['AOI位置']['x2'] * scale_x)
            temp_item['AOI位置']['y2'] = int(jier['AOI位置']['y2'] * scale_y)
            temp_item['AOI位置']['center_x'] = int(jier['AOI位置']['center_x'] * scale_x)
            temp_item['AOI位置']['center_y'] = int(jier['AOI位置']['center_y'] * scale_y)
            temp_item['AOI位置']['width'] = int(jier['AOI位置']['width'] * scale_x)
            temp_item['AOI位置']['height'] = int(jier['AOI位置']['height'] * scale_y)
            temp_item['面积'] = temp_item['AOI位置']['width'] * temp_item['AOI位置']['height']
            
            src_jr_list.append(temp_item) 

        aoi_resize_time = time.perf_counter()
        print(f"还原检测框时间: {aoi_resize_time - cut_merge_detect_end:.4f} 秒")

        # 检查极耳数量并处理
        if len(src_jr_list) == 1:
            # 如果只有一个极耳，根据位置生成另一个极耳
            single_jier = src_jr_list[0]
            single_center_y = single_jier['AOI位置']['center_y']
            
            # 判断单个极耳是上极耳还是下极耳（基于图片中心线）
            is_up_jier = single_center_y < src_height / 2
            
            # 复制原极耳创建第二个极耳
            second_jier = single_jier.copy()
            second_jier['AOI位置'] = single_jier['AOI位置'].copy()
            
            if is_up_jier:
                # 原极耳是上极耳，生成下极耳
                print(f"检测到上极耳，生成下极耳，间距: {jier_spacing}")
                second_jier['AOI位置']['center_y'] += jier_spacing
                second_jier['AOI位置']['y1'] += jier_spacing
                second_jier['AOI位置']['y2'] += jier_spacing
            else:
                # 原极耳是下极耳，生成上极耳
                print(f"检测到下极耳，生成上极耳，间距: {jier_spacing}")
                second_jier['AOI位置']['center_y'] -= jier_spacing
                second_jier['AOI位置']['y1'] -= jier_spacing
                second_jier['AOI位置']['y2'] -= jier_spacing
            
            # 更新第二个极耳的面积等信息
            second_jier['面积'] = second_jier['AOI位置']['width'] * second_jier['AOI位置']['height']
            second_jier['索引'] = 1  # 设置为第二个极耳
            
            # 将第二个极耳添加到列表中
            src_jr_list.append(second_jier)
            print(f"✅ 成功生成第二个极耳，现在共有 {len(src_jr_list)} 个极耳")
            
        elif len(src_jr_list) != 2:
            print(f"⚠️ 检测到的极耳数量异常: {len(src_jr_list)}，预期为2")
            result['result_info']['算法错误'].append(f"检测到的极耳数量异常: {len(src_jr_list)}，预期为2")
            return result

        up_jier = None
        down_jier = None
        
        # 通过比较两个极耳框的中心位置来确定上下关系
        if len(src_jr_list) == 2:
            jier1 = src_jr_list[0]
            jier2 = src_jr_list[1]
            
            # 比较两个极耳框的center_y坐标，较小的为上极耳
            if jier1['AOI位置']['center_y'] < jier2['AOI位置']['center_y']:
                up_jier = jier1
                down_jier = jier2
            else:
                up_jier = jier2
                down_jier = jier1

        result['上极耳AOI'] = up_jier['AOI位置'].copy()
        result['下极耳AOI'] = down_jier['AOI位置'].copy()
        up_jier["cut_aoi"] = {}
        down_jier["cut_aoi"] = {}
        up_jier['center_x'] = up_jier['AOI位置']['x1'] + up_jier['AOI位置']['width'] // 2
        up_jier['center_y'] = up_jier['AOI位置']['y1'] + up_jier['AOI位置']['height'] // 2
        down_jier['center_x'] = down_jier['AOI位置']['x1'] + down_jier['AOI位置']['width'] // 2
        down_jier['center_y'] = down_jier['AOI位置']['y1'] + down_jier['AOI位置']['height'] // 2

        # 极耳是竖直放置的，参数中的"宽"对应图片的"高"，"高"对应图片的"宽"
        up_jier["cut_aoi"]['x'] = up_jier['center_x'] - int(top_jr_height * (1 + jr_height_expand_ratio) / 2)
        up_jier["cut_aoi"]['y'] = up_jier['center_y'] - int(top_jr_width * (1 + jr_width_expand_ratio) / 2)
        up_jier["cut_aoi"]['width'] = int(top_jr_height * (1 + jr_height_expand_ratio))   
        up_jier["cut_aoi"]['height'] = int(top_jr_width * (1 + jr_width_expand_ratio))

        down_jier["cut_aoi"]['x'] = down_jier['center_x'] - int(bottom_jr_height * (1 + jr_height_expand_ratio) / 2)
        down_jier["cut_aoi"]['y'] = down_jier['center_y'] - int(bottom_jr_width * (1 + jr_width_expand_ratio) / 2)
        down_jier["cut_aoi"]['width'] = int(bottom_jr_height * (1 + jr_height_expand_ratio))   
        down_jier["cut_aoi"]['height'] = int(bottom_jr_width * (1 + jr_width_expand_ratio))

        up_jier["cut_aoi"]['x'] =  int(up_jier["cut_aoi"]['x'] - top_jr_height * gm_get_deep)
        up_jier["cut_aoi"]['width'] =  int(up_jier["cut_aoi"]['width'] + top_jr_height  * gm_get_deep)

        down_jier["cut_aoi"]['x'] = int(down_jier["cut_aoi"]['x'] - bottom_jr_height * gm_get_deep)
        down_jier["cut_aoi"]['width'] =  int(down_jier["cut_aoi"]['width'] + bottom_jr_height * gm_get_deep)

        result['上极耳裁切框'] = up_jier["cut_aoi"].copy()
        result['下极耳裁切框'] = down_jier["cut_aoi"].copy()

        print("上极耳裁切框:" + str(result['上极耳裁切框']))
        print("下极耳裁切框:" + str(result['下极耳裁切框']))

        up_jr_image = utils.get_cv_img_fronm_aoi_ex(src_image, up_jier["cut_aoi"])
        down_jr_image = utils.get_cv_img_fronm_aoi_ex(src_image, down_jier["cut_aoi"])

        up_jr_image = cv2.resize(up_jr_image, (512, 512))
        down_jr_image = cv2.resize(down_jr_image, (512, 512))

        cut_time = time.perf_counter()
        result['result_image']['up_jr_image'] = up_jr_image
        result['result_image']['down_jr_image'] = down_jr_image
        
        # 计算实际裁切起点（与 get_cv_img_fronm_aoi 的 clamp 保持一致）
        up_actual_x = max(0, up_jier["cut_aoi"]['x'])
        up_actual_y = max(0, up_jier["cut_aoi"]['y'])
        down_actual_x = max(0, down_jier["cut_aoi"]['x'])
        down_actual_y = max(0, down_jier["cut_aoi"]['y'])

        # 添加上下极耳裁切位置和缩放信息，用于复原标注
        result['极耳裁切信息'] = {
            '上极耳': {
                '裁切区域': {'x': up_actual_x, 'y': up_actual_y,
                             'width': up_jier["cut_aoi"]['width'],
                             'height': up_jier["cut_aoi"]['height']},
                '检测框': {
                    'x1': up_jier['AOI位置']['x1'],
                    'y1': up_jier['AOI位置']['y1'], 
                    'x2': up_jier['AOI位置']['x2'],
                    'y2': up_jier['AOI位置']['y2'],
                    'center_x': up_jier['AOI位置']['center_x'],
                    'center_y': up_jier['AOI位置']['center_y'],
                    'width': up_jier['AOI位置']['width'],
                    'height': up_jier['AOI位置']['height']
                },
                '缩放信息': {
                    '原始尺寸': (up_jier["cut_aoi"]['height'], up_jier["cut_aoi"]['width']),
                    '缩放后尺寸': (512, 512),
                    '缩放比例_x': 512 / up_jier["cut_aoi"]['width'],
                    '缩放比例_y': 512 / up_jier["cut_aoi"]['height']
                }
            },
            '下极耳': {
                '裁切区域': {'x': down_actual_x, 'y': down_actual_y,
                             'width': down_jier["cut_aoi"]['width'],
                             'height': down_jier["cut_aoi"]['height']},
                '检测框': {
                    'x1': down_jier['AOI位置']['x1'],
                    'y1': down_jier['AOI位置']['y1'],
                    'x2': down_jier['AOI位置']['x2'], 
                    'y2': down_jier['AOI位置']['y2'],
                    'center_x': down_jier['AOI位置']['center_x'],
                    'center_y': down_jier['AOI位置']['center_y'],
                    'width': down_jier['AOI位置']['width'],
                    'height': down_jier['AOI位置']['height']
                },
                '缩放信息': {
                    '原始尺寸': (down_jier["cut_aoi"]['height'], down_jier["cut_aoi"]['width']),
                    '缩放后尺寸': (512, 512),
                    '缩放比例_x': 512 / down_jier["cut_aoi"]['width'],
                    '缩放比例_y': 512 / down_jier["cut_aoi"]['height']
                }
            },
            '源图像尺寸': {
                'width': src_width,
                'height': src_height
            }
        }
        
        result['结果'] = 'OK'
        print(f"裁切出图执行时间: {cut_time - aoi_resize_time:.4f} 秒")   
        return result
    
    def _restore_draw_items_to_original(self, result_up, result_down, result_all):
        """
        将512x512裁切图像的检测结果复原到原图坐标系
        从主程序的 restore_draw_items_to_original 方法迁移实现
        
        Args:
            result_up: 上极耳检测结果
            result_down: 下极耳检测结果  
            result_all: 裁切结果，包含极耳裁切信息
            
        Returns:
            dict: 复原到原图的绘图项字典，每个项目以唯一ID为键
        """
        def restore_coordinates_to_original(x_crop, y_crop, ear_type, cut_info):
            """将512x512裁切图像坐标复原到原图坐标"""
            ear_info = cut_info[ear_type]
            
            # 1. 从512x512缩放回裁切区域坐标
            x_cut = x_crop / ear_info['缩放信息']['缩放比例_x']
            y_cut = y_crop / ear_info['缩放信息']['缩放比例_y']
            
            # 2. 加上裁切区域偏移，得到原图坐标
            x_original = x_cut + ear_info['裁切区域']['x']
            y_original = y_cut + ear_info['裁切区域']['y']
            
            return float(x_original), float(y_original)
        
        # 获取裁切信息
        cut_info = result_all.get('极耳裁切信息', {})
        if not cut_info:
            return {}
        
        result_dict = {}
        item_counter = 0
        
        # 复原上极耳检测结果
        if result_up and 'draw_items' in result_up:
            for item in result_up['draw_items']:
                restored_item = item.copy()
                restored_item['来源'] = '上极耳'  # 添加来源标记
                
                if item['type'] == 'rectangle':
                    # 复原矩形坐标
                    x1, y1 = restore_coordinates_to_original(
                        item['topLeft']['x'], item['topLeft']['y'], 
                        '上极耳', cut_info
                    )
                    x2, y2 = restore_coordinates_to_original(
                        item['bottomRight']['x'], item['bottomRight']['y'], 
                        '上极耳', cut_info
                    )
                    restored_item['topLeft'] = {"x": x1, "y": y1}
                    restored_item['bottomRight'] = {"x": x2, "y": y2}
                    
                elif item['type'] == 'circle':
                    # 复原圆心坐标
                    cx, cy = restore_coordinates_to_original(
                        item['center']['x'], item['center']['y'], 
                        '上极耳', cut_info
                    )
                    restored_item['center'] = {"x": cx, "y": cy}
                    # 半径也需要根据缩放比例调整
                    scale_avg = (cut_info['上极耳']['缩放信息']['缩放比例_x'] + 
                               cut_info['上极耳']['缩放信息']['缩放比例_y']) / 2
                    restored_item['radius'] = item['radius'] / scale_avg
                    
                elif item['type'] == 'polygon':
                    # 复原多边形/线条坐标
                    restored_points = []
                    for point in item['points']:
                        px, py = restore_coordinates_to_original(
                            point['x'], point['y'], 
                            '上极耳', cut_info
                        )
                        restored_points.append({"x": px, "y": py})
                    restored_item['points'] = restored_points
                    
                elif item['type'] == 'text':
                    # 复原文本位置
                    tx, ty = restore_coordinates_to_original(
                        item['position']['x'], item['position']['y'], 
                        '上极耳', cut_info
                    )
                    restored_item['position'] = {"x": tx, "y": ty}
                
                # 使用序号作为键名
                item_key = f"item_{item_counter:03d}"
                result_dict[item_key] = restored_item
                item_counter += 1
        
        # 复原下极耳检测结果
        if result_down and 'draw_items' in result_down:
            for item in result_down['draw_items']:
                restored_item = item.copy()
                restored_item['来源'] = '下极耳'  # 添加来源标记
                
                if item['type'] == 'rectangle':
                    # 复原矩形坐标
                    x1, y1 = restore_coordinates_to_original(
                        item['topLeft']['x'], item['topLeft']['y'], 
                        '下极耳', cut_info
                    )
                    x2, y2 = restore_coordinates_to_original(
                        item['bottomRight']['x'], item['bottomRight']['y'], 
                        '下极耳', cut_info
                    )
                    restored_item['topLeft'] = {"x": x1, "y": y1}
                    restored_item['bottomRight'] = {"x": x2, "y": y2}
                    
                elif item['type'] == 'circle':
                    # 复原圆心坐标
                    cx, cy = restore_coordinates_to_original(
                        item['center']['x'], item['center']['y'], 
                        '下极耳', cut_info
                    )
                    restored_item['center'] = {"x": cx, "y": cy}
                    # 半径也需要根据缩放比例调整
                    scale_avg = (cut_info['下极耳']['缩放信息']['缩放比例_x'] + 
                               cut_info['下极耳']['缩放信息']['缩放比例_y']) / 2
                    restored_item['radius'] = item['radius'] / scale_avg
                    
                elif item['type'] == 'polygon':
                    # 复原多边形/线条坐标
                    restored_points = []
                    for point in item['points']:
                        px, py = restore_coordinates_to_original(
                            point['x'], point['y'], 
                            '下极耳', cut_info
                        )
                        restored_points.append({"x": px, "y": py})
                    restored_item['points'] = restored_points
                    
                elif item['type'] == 'text':
                    # 复原文本位置
                    tx, ty = restore_coordinates_to_original(
                        item['position']['x'], item['position']['y'], 
                        '下极耳', cut_info
                    )
                    restored_item['position'] = {"x": tx, "y": ty}
                
                # 使用序号作为键名
                item_key = f"item_{item_counter:03d}"
                result_dict[item_key] = restored_item
                item_counter += 1
        
        return result_dict
    
    def get_default_parameters(self) -> Dict[str, Any]:
        """
        获取默认参数配置（从原始配置文件中提取）
        """

               # 极耳裁切模型参数
        jrcut_dict = {
            "模型路径": "",
            "置信度": 0.4,
            "裁切IOU阈值": 0.9,
            "输入尺寸": 512,
            "合并检测框": True,
        }
        
        # 极耳裁切用户参数
        jrcut_usr_dict = {
            "上极耳宽度": 400,
            "上极耳高度": 150,
            "下极耳宽度": 400,
            "下极耳高度": 150,
            "极耳宽度扩展冗余": 0.1,
            "极耳高度扩展冗余": 0.2,
            "极耳间距": 800,
            "隔膜获取深度": 0.5,
        }
        
        # 独立置信度参数
        independent_conf = {
            "启用独立置信度": False,
            "极耳置信度": 0.1,
            "折痕置信度": 0.1,
            "月牙置信度": 0.1,
            "极耳内插置信度": 0.1,
            "极耳内嵌置信度": 0.1,
            "分层置信度": 0.1,
        }
        
        # 横向纹理附加检测参数
        # 独立置信度参数默认值
        independent_conf = {
            "启用独立置信度": False,
            "极耳置信度": 0.1,
            "折痕置信度": 0.1,
            "月牙置信度": 0.1,
            "极耳内插置信度": 0.1,
            "极耳内嵌置信度": 0.1,
            "分层置信度": 0.1,
        }
        
        # 横向纹理附加检测参数默认值
        horizontal_texture_param = {
            "横纹最低长宽比": 3.5,
            "横纹最低风险高度": 0.2,
            "横纹最低风险宽度": 0.7,
        }
        
        # 极耳翻折检测模型参数
        deleted_dict = {
            "模型路径": "",
            "置信度阈值": 0.4,
            "IOU阈值": 0.9,
            "输入尺寸": 512,
            "独立置信度参数": independent_conf,
            "横向纹理附加检测": True,
            "横向纹理附加检测参数": horizontal_texture_param,
        }
        
        # 极耳翻折用户参数
        fold_usr_dict = {
            "翻折保护距离（极耳高度比例）": 0.4,
            "翻折起点保护距离（极耳高度比例）": 0.2,
            "翻折起点扩展距离（极耳高度比例）": 0.2,
            "极耳高度（像素）": 150,
            "极耳高度保护阈值": 0.6,
            "极耳宽度（像素）": 400,
            "极耳宽度保护阈值": 1.5,
            "分层保护阈值": 0.2,
            "月牙宽高NG": False,
            "月牙宽度": 0.8,
            "月牙高度": 0.5,
            "启用自动缩放功能": False,
            "检测后图片高度缩放": 1.0,
            "检测后图片宽度缩放": 1.0,
            "启用相对位置筛选器": True,
            "重叠度阈值": 0.8,

            "启用灰度检测": True,
            "灰度范围NG": False,
            "灰度内缩": 0.7,
            "灰度差值MAX": 255,
            "灰度差值MIN": 0,

            "算法存图":False,
            "算法存图路径":"path",
            "算法存图最大占用硬盘(MB)":4096,
        }
        
        # 隔膜检测模型参数
        gm_dict = {
            "模型路径": "",
            "输入尺寸": 256,
        }
        
        # 隔膜用户参数
        gm_usr_dict = {
            "启用隔膜分析": True,
            "隔膜全检": False,
        }

        update_dict = {
            "更新脚本地址":"",
            "更新UI地址":"",
        }        
        # 组织所有参数
        all_parameters = {
            "极耳裁切模型参数": jrcut_dict,
            "极耳裁切用户参数": jrcut_usr_dict,
            "极耳翻折检测模型参数": deleted_dict,
            "极耳翻折用户参数": fold_usr_dict,
            "隔膜检测模型参数": gm_dict,
            "隔膜用户参数": gm_usr_dict,
        }

        return all_parameters
    
    def warm_up(self) -> None:
        """
        重写基类的预热方法
        
        如果检测器已初始化，再次执行模型预热
        如果未初始化，只记录警告信息
        """
        if self.initialized:
            print("🔥 执行检测器预热...")
            self._warm_up_models()
        else:
            print("⚠️ 检测器未初始化，无法执行预热")
    
    def cleanup(self):
        """清理资源"""
        super().cleanup()
        self.cut_model = None
        self.detect_model = None
        self.gm_cls_model = None
        print("🧹 检测器资源已清理")

