import cv2
import numpy as np
from typing import Dict, List, Tuple
import logging

logger = logging.getLogger(__name__)

class ReferenceDataExtractor:
    """참조 이미지에서 Ground Truth 데이터 추출"""
    
    def __init__(self):
        self.detector = self._initialize_detector()
    
    def _initialize_detector(self):
        """객체 감지기 초기화"""
        # YOLO 또는 다른 객체 감지기 사용
        try:
            import ultralytics
            # 더 강력한 YOLO 모델 사용 (yolov8s)
            return ultralytics.YOLO('yolov8s.pt')
        except ImportError:
            logger.warning("YOLO not available, using OpenCV detector")
            return None
    
    def extract_reference_data(self, image_path: str) -> Dict:
        """
        참조 이미지에서 객체 정보 추출
        Args:
            image_path: 참조 이미지 경로
        Returns:
            추출된 객체 정보
        """
        image = cv2.imread(image_path)
        if image is None:
            raise ValueError(f"Cannot load image: {image_path}")
        
        # 객체 감지 수행
        if self.detector:
            logger.info(f"YOLO 객체 감지 시작 - 이미지 크기: {image.shape}")
            results = self.detector(image)
            objects = self._parse_yolo_results(results)
            
            # 상세한 디버깅 로그 추가
            logger.info(f"YOLO 원본 감지 결과: {len(results)}개")
            logger.info(f"confidence 필터링 후: {len(objects)}개 객체")
            
            # 감지된 객체들의 상세 정보 로그
            for i, obj in enumerate(objects):
                logger.info(f"  객체 {i+1}: bbox={obj['bbox']}, confidence={obj['confidence']:.3f}, class={obj['class']}")
            
            if len(objects) == 0:
                logger.warning("YOLO에서 객체를 감지하지 못했습니다. OpenCV fallback 사용")
                objects = self._extract_with_opencv(image)
                logger.info(f"OpenCV fallback 결과: {len(objects)}개 객체")
        else:
            logger.info("YOLO 감지기 없음, OpenCV fallback 직접 사용")
            objects = self._extract_with_opencv(image)
            logger.info(f"OpenCV fallback 결과: {len(objects)}개 객체")
        
        return {
            'objects': objects,
            'bounding_boxes': [obj['bbox'] for obj in objects],
            'class_labels': [obj['class'] for obj in objects],
            'confidence_scores': [obj['confidence'] for obj in objects]
        }
    
    def _parse_yolo_results(self, results) -> List[Dict]:
        """YOLO 결과 파싱 (개선된 버전)"""
        objects = []
        confidence_threshold = 0.001  # 극도로 낮은 threshold로 설정 (0.01 → 0.001)
        
        logger.info(f"YOLO 결과 파싱 시작 - confidence threshold: {confidence_threshold}")
        
        total_detections = 0
        filtered_by_confidence = 0
        filtered_by_size = 0
        
        for result in results:
            boxes = result.boxes
            if boxes is not None:
                total_detections += len(boxes)
                for box in boxes:
                    confidence = box.conf[0].cpu().numpy()
                    
                    # confidence 필터링 추가
                    if confidence >= confidence_threshold:
                        x1, y1, x2, y2 = box.xyxy[0].cpu().numpy()
                        class_id = int(box.cls[0].cpu().numpy())
                        
                        # 바운딩 박스 크기 검증 (매우 관대한 필터)
                        width = x2 - x1
                        height = y2 - y1
                        if width > 1 and height > 1:  # 최소 크기 필터 매우 완화 (5 → 1)
                            objects.append({
                                'bbox': [x1, y1, x2, y2],
                                'confidence': float(confidence),
                                'class': class_id
                            })
                            logger.debug(f"  감지된 객체: confidence={confidence:.3f}, size=({width:.1f}x{height:.1f}), class={class_id}")
                        else:
                            filtered_by_size += 1
                            logger.debug(f"  크기 필터링: confidence={confidence:.3f}, size=({width:.1f}x{height:.1f})")
                    else:
                        filtered_by_confidence += 1
                        logger.debug(f"  confidence 필터링: {confidence:.3f} < {confidence_threshold}")
        
        logger.info(f"YOLO 파싱 결과: 총 {total_detections}개 감지, confidence 필터링 {filtered_by_confidence}개, 크기 필터링 {filtered_by_size}개, 최종 {len(objects)}개 객체")
        
        # 객체가 감지되지 않은 경우 더 낮은 threshold로 재시도
        if len(objects) == 0:
            logger.warning("기본 threshold에서 객체 감지 실패, 더 낮은 threshold로 재시도")
            confidence_threshold = 0.0001  # 극도로 낮은 threshold로 재시도
            
            for result in results:
                boxes = result.boxes
                if boxes is not None:
                    for box in boxes:
                        confidence = box.conf[0].cpu().numpy()
                        
                        if confidence >= confidence_threshold:
                            x1, y1, x2, y2 = box.xyxy[0].cpu().numpy()
                            class_id = int(box.cls[0].cpu().numpy())
                            
                            width = x2 - x1
                            height = y2 - y1
                            if width > 0.5 and height > 0.5:  # 극도로 낮은 크기 필터
                                objects.append({
                                    'bbox': [x1, y1, x2, y2],
                                    'confidence': float(confidence),
                                    'class': class_id
                                })
        
        return objects
    
    def _extract_with_opencv(self, image: np.ndarray) -> List[Dict]:
        """OpenCV를 사용한 개선된 객체 추출"""
        try:
            # 이미지 전처리
            processed_image = self._preprocess_image(image)
            
            # 다중 방법으로 객체 탐지
            objects = []
            
            # 방법 1: 개선된 Canny 엣지 검출
            objects.extend(self._detect_with_improved_canny(processed_image))
            
            # 방법 2: 히스토그램 균등화 + 그래디언트 기반 탐지
            objects.extend(self._detect_with_histogram_equalization(processed_image))
            
            # 방법 3: 동적 임계값 기반 탐지
            objects.extend(self._detect_with_dynamic_threshold(processed_image))
            
            # 중복 제거 및 필터링
            objects = self._filter_and_merge_objects(objects)
            
            return objects
            
        except Exception as e:
            logger.error(f"OpenCV 객체 추출 중 오류 발생: {str(e)}")
            # 오류 발생 시 기본 방법 사용
            return self._extract_with_basic_opencv(image)
    
    def _preprocess_image(self, image: np.ndarray) -> np.ndarray:
        """이미지 전처리 (대비 개선, 노이즈 제거, 엣지 강화)"""
        # 그레이스케일 변환
        gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
        
        # 히스토그램 균등화
        equalized = cv2.equalizeHist(gray)
        
        # 가우시안 블러로 노이즈 제거
        blurred = cv2.GaussianBlur(equalized, (5, 5), 0)
        
        # 대비 개선
        clahe = cv2.createCLAHE(clipLimit=2.0, tileGridSize=(8, 8))
        enhanced = clahe.apply(blurred)
        
        return enhanced
    
    def _detect_with_improved_canny(self, image: np.ndarray) -> List[Dict]:
        """개선된 Canny 엣지 검출"""
        objects = []
        
        # 동적 임계값 계산
        median = np.median(image)
        lower = int(max(0, 0.7 * median))
        upper = int(min(255, 1.3 * median))
        
        # Canny 엣지 검출
        edges = cv2.Canny(image, lower, upper)
        
        # 모폴로지 연산으로 엣지 강화
        kernel = np.ones((3, 3), np.uint8)
        edges = cv2.morphologyEx(edges, cv2.MORPH_CLOSE, kernel)
        
        # 컨투어 찾기
        contours, _ = cv2.findContours(edges, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
        
        for contour in contours:
            area = cv2.contourArea(contour)
            if area > 500:  # 최소 면적 필터링
                x, y, w, h = cv2.boundingRect(contour)
                if w > 30 and h > 30:  # 최소 크기 필터링
                    objects.append({
                        'bbox': [x, y, x + w, y + h],
                        'confidence': 0.7,
                        'class': 0,
                        'method': 'improved_canny'
                    })
        
        return objects
    
    def _detect_with_histogram_equalization(self, image: np.ndarray) -> List[Dict]:
        """히스토그램 균등화 + 그래디언트 기반 탐지"""
        objects = []
        
        # Sobel 그래디언트 계산
        grad_x = cv2.Sobel(image, cv2.CV_64F, 1, 0, ksize=3)
        grad_y = cv2.Sobel(image, cv2.CV_64F, 0, 1, ksize=3)
        gradient_magnitude = np.sqrt(grad_x**2 + grad_y**2)
        
        # 그래디언트 크기 정규화
        gradient_magnitude = np.uint8(255 * gradient_magnitude / np.max(gradient_magnitude))
        
        # 동적 임계값 적용
        threshold = np.mean(gradient_magnitude) + np.std(gradient_magnitude)
        binary = cv2.threshold(gradient_magnitude, threshold, 255, cv2.THRESH_BINARY)[1]
        
        # 모폴로지 연산
        kernel = np.ones((5, 5), np.uint8)
        binary = cv2.morphologyEx(binary, cv2.MORPH_CLOSE, kernel)
        binary = cv2.morphologyEx(binary, cv2.MORPH_OPEN, kernel)
        
        # 컨투어 찾기
        contours, _ = cv2.findContours(binary, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
        
        for contour in contours:
            area = cv2.contourArea(contour)
            if area > 800:  # 최소 면적 필터링
                x, y, w, h = cv2.boundingRect(contour)
                if w > 40 and h > 40:  # 최소 크기 필터링
                    objects.append({
                        'bbox': [x, y, x + w, y + h],
                        'confidence': 0.75,
                        'class': 0,
                        'method': 'histogram_equalization'
                    })
        
        return objects
    
    def _detect_with_dynamic_threshold(self, image: np.ndarray) -> List[Dict]:
        """동적 임계값 기반 탐지"""
        objects = []
        
        # Otsu 임계값 계산
        threshold_value, binary = cv2.threshold(image, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)
        
        # 적응적 임계값도 시도
        adaptive_binary = cv2.adaptiveThreshold(
            image, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C, cv2.THRESH_BINARY, 11, 2
        )
        
        # 두 결과를 결합
        combined = cv2.bitwise_or(binary, adaptive_binary)
        
        # 모폴로지 연산
        kernel = np.ones((3, 3), np.uint8)
        combined = cv2.morphologyEx(combined, cv2.MORPH_CLOSE, kernel)
        
        # 컨투어 찾기
        contours, _ = cv2.findContours(combined, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
        
        for contour in contours:
            area = cv2.contourArea(contour)
            if area > 600:  # 최소 면적 필터링
                x, y, w, h = cv2.boundingRect(contour)
                if w > 35 and h > 35:  # 최소 크기 필터링
                    objects.append({
                        'bbox': [x, y, x + w, y + h],
                        'confidence': 0.65,
                        'class': 0,
                        'method': 'dynamic_threshold'
                    })
        
        return objects
    
    def _filter_and_merge_objects(self, objects: List[Dict]) -> List[Dict]:
        """중복 제거 및 객체 필터링"""
        if not objects:
            return []
        
        # IoU 기반 중복 제거
        filtered_objects = []
        
        for obj in objects:
            is_duplicate = False
            for existing_obj in filtered_objects:
                if self._calculate_iou(obj['bbox'], existing_obj['bbox']) > 0.5:
                    # 더 높은 신뢰도를 가진 객체 유지
                    if obj['confidence'] > existing_obj['confidence']:
                        filtered_objects.remove(existing_obj)
                        filtered_objects.append(obj)
                    is_duplicate = True
                    break
            
            if not is_duplicate:
                filtered_objects.append(obj)
        
        # 신뢰도 순으로 정렬
        filtered_objects.sort(key=lambda x: x['confidence'], reverse=True)
        
        # 상위 5개 객체만 반환
        return filtered_objects[:5]
    
    def _calculate_iou(self, bbox1: List[float], bbox2: List[float]) -> float:
        """IoU 계산"""
        x1_1, y1_1, x2_1, y2_1 = bbox1
        x1_2, y1_2, x2_2, y2_2 = bbox2
        
        # 교집합 영역 계산
        x1_i = max(x1_1, x1_2)
        y1_i = max(y1_1, y1_2)
        x2_i = min(x2_1, x2_2)
        y2_i = min(y2_1, y2_2)
        
        if x2_i <= x1_i or y2_i <= y1_i:
            return 0.0
        
        intersection = (x2_i - x1_i) * (y2_i - y1_i)
        area1 = (x2_1 - x1_1) * (y2_1 - y1_1)
        area2 = (x2_2 - x1_2) * (y2_2 - y1_2)
        union = area1 + area2 - intersection
        
        return intersection / union if union > 0 else 0.0
    
    def _extract_with_basic_opencv(self, image: np.ndarray) -> List[Dict]:
        """기본 OpenCV 방법 (오류 발생 시 대체) - 개선된 버전"""
        gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
        
        # 더 관대한 Canny 엣지 검출 (임계값 완화)
        edges = cv2.Canny(gray, 30, 100)  # 50, 150 → 30, 100
        
        # 컨투어 찾기
        contours, _ = cv2.findContours(edges, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
        
        objects = []
        for contour in contours:
            area = cv2.contourArea(contour)
            if area > 500:  # 최소 면적 필터링 완화 (1000 → 500)
                x, y, w, h = cv2.boundingRect(contour)
                if w > 20 and h > 20:  # 최소 크기 필터링 완화 (50 → 20)
                    objects.append({
                        'bbox': [x, y, x + w, y + h],
                        'confidence': 0.3,  # 더 낮은 신뢰도 (0.5 → 0.3)
                        'class': 0,
                        'method': 'basic_fallback'
                    })
        
        # 객체가 여전히 없는 경우 더 관대한 방법 시도
        if len(objects) == 0:
            logger.warning("기본 OpenCV 방법으로 객체 감지 실패, 더 관대한 방법 시도")
            objects = self._extract_with_adaptive_opencv(image)
        
        return objects
    
    def _extract_with_adaptive_opencv(self, image: np.ndarray) -> List[Dict]:
        """적응적 OpenCV 방법 (더 관대한 임계값)"""
        gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
        
        # 적응적 임계값 적용
        adaptive_thresh = cv2.adaptiveThreshold(
            gray, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C, cv2.THRESH_BINARY, 11, 2
        )
        
        # 모폴로지 연산으로 노이즈 제거
        kernel = np.ones((3, 3), np.uint8)
        cleaned = cv2.morphologyEx(adaptive_thresh, cv2.MORPH_CLOSE, kernel)
        
        # 컨투어 찾기
        contours, _ = cv2.findContours(cleaned, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
        
        objects = []
        for contour in contours:
            area = cv2.contourArea(contour)
            if area > 200:  # 매우 낮은 면적 임계값 (500 → 200)
                x, y, w, h = cv2.boundingRect(contour)
                if w > 10 and h > 10:  # 매우 낮은 크기 임계값 (20 → 10)
                    objects.append({
                        'bbox': [x, y, x + w, y + h],
                        'confidence': 0.2,  # 매우 낮은 신뢰도 (0.3 → 0.2)
                        'class': 0,
                        'method': 'adaptive_fallback'
                    })
        
        # 여전히 객체가 없는 경우 이미지 전체를 하나의 객체로 처리
        if len(objects) == 0:
            logger.warning("모든 OpenCV 방법 실패, 이미지 전체를 하나의 객체로 처리")
            h, w = image.shape[:2]
            objects.append({
                'bbox': [0, 0, w, h],
                'confidence': 0.1,  # 최소 신뢰도
                'class': 0,
                'method': 'full_image_fallback'
            })
        
        return objects
    
    def _create_enhanced_fallback_data(self, image_path: str) -> Dict:
        """향상된 fallback 데이터 생성 (객체 감지 실패 시)"""
        logger.info("향상된 fallback 데이터 생성 시작")
        
        # 이미지 로드
        image = cv2.imread(image_path)
        if image is None:
            raise ValueError(f"Cannot load image: {image_path}")
        
        h, w = image.shape[:2]
        
        # 이미지를 여러 영역으로 분할하여 객체로 처리
        objects = []
        
        # 방법 1: 이미지를 4개 영역으로 분할
        quarter_w, quarter_h = w // 2, h // 2
        regions = [
            [0, 0, quarter_w, quarter_h],  # 좌상단
            [quarter_w, 0, w, quarter_h],  # 우상단
            [0, quarter_h, quarter_w, h],  # 좌하단
            [quarter_w, quarter_h, w, h]   # 우하단
        ]
        
        for i, region in enumerate(regions):
            x1, y1, x2, y2 = region
            objects.append({
                'bbox': [x1, y1, x2, y2],
                'confidence': 0.4,  # 중간 신뢰도
                'class': i % 3,  # 클래스 다양화
                'method': 'region_split'
            })
        
        # 방법 2: 이미지 전체를 하나의 객체로도 추가
        objects.append({
            'bbox': [0, 0, w, h],
            'confidence': 0.3,
            'class': 0,
            'method': 'full_image'
        })
        
        logger.info(f"향상된 fallback 데이터 생성 완료: {len(objects)}개 객체")
        
        return {
            'objects': objects,
            'bounding_boxes': [obj['bbox'] for obj in objects],
            'class_labels': [obj['class'] for obj in objects],
            'confidence_scores': [obj['confidence'] for obj in objects]
        }
    
    def create_unified_ground_truth(self, reference_image_path: str, model_3d_path: str = None) -> Dict:
        """
        표준화된 Ground Truth 데이터 생성
        Args:
            reference_image_path: 참조 이미지 경로
            model_3d_path: 3D 모델 경로 (선택사항)
        Returns:
            표준화된 Ground Truth 데이터
        """
        try:
            # 참조 이미지에서 객체 정보 추출
            reference_data = self.extract_reference_data(reference_image_path)
            
            # 객체 감지 결과 검증 및 개선
            if len(reference_data['objects']) == 0:
                logger.warning("객체 감지 실패, 대체 Ground Truth 생성")
                reference_data = self._create_enhanced_fallback_data(reference_image_path)
            
            # 표준 메시 구조 생성
            unified_ground_truth = {
                'vertices': self._generate_standard_vertices(reference_data),
                'faces': self._generate_standard_faces(reference_data),
                'center': self._calculate_center(reference_data),
                'scale': self._calculate_scale(reference_data),
                'objects': reference_data['objects'],
                'bounding_boxes': reference_data['bounding_boxes'],
                'class_labels': reference_data['class_labels'],
                'confidence_scores': reference_data['confidence_scores'],
                'metadata': {
                    'source_image': reference_image_path,
                    'extraction_method': 'yolo' if self.detector else 'opencv',
                    'timestamp': self._get_timestamp(),
                    'version': '1.0',
                    'object_count': len(reference_data['objects']),
                    'fallback_used': len(reference_data['objects']) == 0
                }
            }
            
            # 데이터 검증
            self._validate_ground_truth_structure(unified_ground_truth)
            
            logger.info(f"Ground Truth 생성 완료: {len(reference_data['objects'])}개 객체, {len(unified_ground_truth['vertices'])}개 vertices")
            return unified_ground_truth
            
        except Exception as e:
            logger.error(f"Ground Truth 생성 중 오류 발생: {str(e)}")
            # 오류 발생 시 기본 구조 반환
            return self._create_fallback_ground_truth(reference_image_path)
    
    def _generate_standard_vertices(self, reference_data: Dict) -> List[List[float]]:
        """표준 vertices 생성"""
        vertices = []
        
        for bbox in reference_data['bounding_boxes']:
            x1, y1, x2, y2 = bbox
            
            # 바운딩 박스를 3D 정육면체로 변환
            # Z축은 기본값으로 설정
            z_min, z_max = 0.0, 1.0
            
            # 8개 꼭짓점 생성 (정육면체)
            cube_vertices = [
                [x1, y1, z_min], [x2, y1, z_min], [x2, y2, z_min], [x1, y2, z_min],  # 하단
                [x1, y1, z_max], [x2, y1, z_max], [x2, y2, z_max], [x1, y2, z_max]   # 상단
            ]
            vertices.extend(cube_vertices)
        
        # 객체가 없는 경우 기본 정육면체 생성
        if not vertices:
            # 이미지 전체를 하나의 객체로 처리
            logger.warning("객체 감지 실패. 이미지 전체를 Ground Truth로 사용")
            
            # 이미지 크기 기반 3D 박스 생성
            image = cv2.imread(reference_image_path)
            if image is not None:
                h, w = image.shape[:2]
                # 이미지 크기에 비례한 3D 박스 생성
                scale_factor = min(w, h) / 100.0  # 정규화
                
                vertices = [
                    [0, 0, 0], [scale_factor, 0, 0], [scale_factor, scale_factor, 0], [0, scale_factor, 0],  # 하단
                    [0, 0, scale_factor], [scale_factor, 0, scale_factor], [scale_factor, scale_factor, scale_factor], [0, scale_factor, scale_factor]   # 상단
                ]
                
                # bounding_boxes도 업데이트
                reference_data['bounding_boxes'] = [[0, 0, w, h]]
                reference_data['objects'] = [{
                    'bbox': [0, 0, w, h],
                    'confidence': 0.5,
                    'class': 0
                }]
            else:
                # 이미지 로드 실패 시 기본값
                vertices = [
                    [0, 0, 0], [1, 0, 0], [1, 1, 0], [0, 1, 0],
                    [0, 0, 1], [1, 0, 1], [1, 1, 1], [0, 1, 1]
                ]
        
        return vertices
    
    def _generate_standard_faces(self, reference_data: Dict) -> List[List[int]]:
        """표준 faces 생성"""
        faces = []
        
        # 각 바운딩 박스마다 12개 삼각형 면 생성 (정육면체)
        for i, bbox in enumerate(reference_data['bounding_boxes']):
            base_idx = i * 8  # 각 객체마다 8개 꼭짓점
            
            # 정육면체의 12개 면 (각 면은 2개 삼각형으로 구성)
            cube_faces = [
                # 하단 면
                [base_idx, base_idx+1, base_idx+2], [base_idx, base_idx+2, base_idx+3],
                # 상단 면
                [base_idx+4, base_idx+7, base_idx+6], [base_idx+4, base_idx+6, base_idx+5],
                # 앞면
                [base_idx, base_idx+4, base_idx+5], [base_idx, base_idx+5, base_idx+1],
                # 뒷면
                [base_idx+2, base_idx+6, base_idx+7], [base_idx+2, base_idx+7, base_idx+3],
                # 왼쪽 면
                [base_idx, base_idx+3, base_idx+7], [base_idx, base_idx+7, base_idx+4],
                # 오른쪽 면
                [base_idx+1, base_idx+5, base_idx+6], [base_idx+1, base_idx+6, base_idx+2]
            ]
            faces.extend(cube_faces)
        
        # 객체가 없는 경우 기본 정육면체 면 생성
        if not faces:
            faces = [
                [0, 1, 2], [0, 2, 3],  # 하단
                [4, 7, 6], [4, 6, 5],  # 상단
                [0, 4, 5], [0, 5, 1],  # 앞면
                [2, 6, 7], [2, 7, 3],  # 뒷면
                [0, 3, 7], [0, 7, 4],  # 왼쪽
                [1, 5, 6], [1, 6, 2]   # 오른쪽
            ]
        
        return faces
    
    def _calculate_center(self, reference_data: Dict) -> List[float]:
        """중심점 계산"""
        if not reference_data['bounding_boxes']:
            return [0.5, 0.5, 0.5]  # 기본 중심점
        
        # 모든 바운딩 박스의 중심점 계산
        centers = []
        for bbox in reference_data['bounding_boxes']:
            x1, y1, x2, y2 = bbox
            center_x = (x1 + x2) / 2
            center_y = (y1 + y2) / 2
            center_z = 0.5  # Z축은 기본값
            centers.append([center_x, center_y, center_z])
        
        # 전체 중심점 계산
        if centers:
            center = [sum(c[i] for c in centers) / len(centers) for i in range(3)]
        else:
            center = [0.5, 0.5, 0.5]
        
        return center
    
    def _calculate_scale(self, reference_data: Dict) -> List[float]:
        """스케일 계산"""
        if not reference_data['bounding_boxes']:
            return [1.0, 1.0, 1.0]  # 기본 스케일
        
        # 모든 바운딩 박스의 크기 계산
        scales = []
        for bbox in reference_data['bounding_boxes']:
            x1, y1, x2, y2 = bbox
            scale_x = abs(x2 - x1)
            scale_y = abs(y2 - y1)
            scale_z = 1.0  # Z축은 기본값
            scales.append([scale_x, scale_y, scale_z])
        
        # 평균 스케일 계산
        if scales:
            scale = [sum(s[i] for s in scales) / len(scales) for i in range(3)]
        else:
            scale = [1.0, 1.0, 1.0]
        
        return scale
    
    def _validate_ground_truth_structure(self, ground_truth: Dict) -> bool:
        """Ground Truth 구조 검증"""
        try:
            # 필수 키 확인
            required_keys = ['vertices', 'faces', 'objects', 'bounding_boxes']
            for key in required_keys:
                if key not in ground_truth:
                    logger.error(f"Ground Truth에 필수 키 '{key}'가 없습니다")
                    return False
            
            # vertices 검증
            vertices = ground_truth['vertices']
            if len(vertices) < 3:
                logger.error(f"vertices 개수가 너무 적습니다: {len(vertices)}")
                return False
            
            # objects 검증
            objects = ground_truth['objects']
            if len(objects) == 0:
                logger.warning("Ground Truth에 객체가 없습니다")
                return False
            
            logger.info(f"Ground Truth 구조 검증 완료: {len(vertices)}개 vertices, {len(objects)}개 객체")
            return True
            
        except Exception as e:
            logger.error(f"Ground Truth 구조 검증 중 오류: {e}")
            return False
    
    def _create_fallback_ground_truth(self, reference_image_path: str) -> Dict:
        """오류 발생 시 대체 Ground Truth 생성"""
        logger.warning("대체 Ground Truth 생성 중...")
        
        return {
            'vertices': [
                [0, 0, 0], [1, 0, 0], [1, 1, 0], [0, 1, 0],  # 하단
                [0, 0, 1], [1, 0, 1], [1, 1, 1], [0, 1, 1]   # 상단
            ],
            'faces': [
                [0, 1, 2], [0, 2, 3],  # 하단
                [4, 7, 6], [4, 6, 5],  # 상단
                [0, 4, 5], [0, 5, 1],  # 앞면
                [2, 6, 7], [2, 7, 3],  # 뒷면
                [0, 3, 7], [0, 7, 4],  # 왼쪽
                [1, 5, 6], [1, 6, 2]   # 오른쪽
            ],
            'center': [0.5, 0.5, 0.5],
            'scale': [1.0, 1.0, 1.0],
            'objects': [],
            'bounding_boxes': [],
            'class_labels': [],
            'confidence_scores': [],
            'metadata': {
                'source_image': reference_image_path,
                'extraction_method': 'fallback',
                'timestamp': self._get_timestamp(),
                'version': '1.0',
                'error': 'fallback_created'
            }
        }
    
    def _get_timestamp(self) -> str:
        """현재 시간 스탬프 반환"""
        from datetime import datetime
        return datetime.now().isoformat()