import logging import traceback from typing import Dict, List, Any logger = logging.getLogger(__name__) class RegionAnalyzer: """ 負責處理圖像區域劃分和基礎空間分析功能 專注於3x3網格的區域劃分、物件分布分析和空間多樣性計算 """ def __init__(self): """初始化區域分析器,定義3x3網格區域""" try: # 定義圖像的3x3網格區域 self.regions = { "top_left": (0, 0, 1/3, 1/3), "top_center": (1/3, 0, 2/3, 1/3), "top_right": (2/3, 0, 1, 1/3), "middle_left": (0, 1/3, 1/3, 2/3), "middle_center": (1/3, 1/3, 2/3, 2/3), "middle_right": (2/3, 1/3, 1, 2/3), "bottom_left": (0, 2/3, 1/3, 1), "bottom_center": (1/3, 2/3, 2/3, 1), "bottom_right": (2/3, 2/3, 1, 1) } logger.info("RegionAnalyzer initialized successfully with 3x3 grid regions") except Exception as e: logger.error(f"Failed to initialize RegionAnalyzer: {str(e)}") logger.error(traceback.format_exc()) raise def determine_region(self, x: float, y: float) -> str: """ 判斷點位於哪個區域 Args: x: 標準化x座標 (0-1) y: 標準化y座標 (0-1) Returns: 區域名稱 """ try: for region_name, (x1, y1, x2, y2) in self.regions.items(): if x1 <= x < x2 and y1 <= y < y2: return region_name logger.warning(f"Point ({x}, {y}) does not fall into any defined region") return "unknown" except Exception as e: logger.error(f"Error determining region for point ({x}, {y}): {str(e)}") logger.error(traceback.format_exc()) return "unknown" def get_spatial_description_phrase(self, region: str) -> str: """ 將region ID轉換為完整的空間描述短語,包含適當的介詞結構 Args: region: 區域標識符(如 "middle_center", "top_left") Returns: str: 完整的空間描述短語,空值時返回空字串 """ try: # 處理空值或無效輸入 if not region or region.strip() == "" or region == "unknown": return "within the visible area" # 清理region格式,移除底線 clean_region = region.replace('_', ' ').strip().lower() # 根據區域位置生成自然語言描述 region_mappings = { "top left": "in the upper left area", "top center": "in the upper area", "top right": "in the upper right area", "middle left": "on the left side", "middle center": "in the center", "center": "in the center", "middle right": "on the right side", "bottom left": "in the lower left area", "bottom center": "in the lower area", "bottom right": "in the lower right area" } # 直接映射匹配 if clean_region in region_mappings: return region_mappings[clean_region] # 模糊匹配方位的處理 if "top" in clean_region and "left" in clean_region: return "in the upper left area" elif "top" in clean_region and "right" in clean_region: return "in the upper right area" elif "bottom" in clean_region and "left" in clean_region: return "in the lower left area" elif "bottom" in clean_region and "right" in clean_region: return "in the lower right area" elif "top" in clean_region: return "in the upper area" elif "bottom" in clean_region: return "in the lower area" elif "left" in clean_region: return "on the left side" elif "right" in clean_region: return "on the right side" elif "center" in clean_region or "middle" in clean_region: return "in the center" else: # 對於無法辨識的區域,返回通用描述 return f"in the {clean_region} area" except Exception as e: logger.warning(f"Error generating spatial description for region '{region}': {str(e)}") return "" def get_contextual_spatial_description(self, region: str, object_type: str = "") -> str: """ 根據物件類型提供更具情境的空間描述 Args: region: 區域標識符 object_type: 物件類型,用於優化描述語境 Returns: str: 情境化的空間描述短語 """ try: # 獲取基礎空間描述 base_description = self.get_spatial_description_phrase(region) if not base_description: return "" # 根據物件類型調整描述語境 if object_type: object_type_lower = object_type.lower() # 對於辨識到人相關,用更自然的位置描述 if "person" in object_type_lower or "people" in object_type_lower: if "center" in base_description: return "in the central area" elif "upper" in base_description: return "in the background" elif "lower" in base_description: return "in the foreground" # 對於車輛,強調道路位置 elif any(vehicle in object_type_lower for vehicle in ["car", "vehicle", "truck", "bus"]): if "left" in base_description: return "on the left side of the scene" elif "right" in base_description: return "on the right side of the scene" elif "center" in base_description: return "in the central area" # 對於交通設施,使用更具體的位置描述 elif "traffic" in object_type_lower: if "upper" in base_description: return "positioned in the upper portion" elif "center" in base_description: return "centrally positioned" else: return base_description.replace("in the", "positioned in the") return base_description except Exception as e: logger.warning(f"Error generating contextual spatial description: {str(e)}") return self.get_spatial_description_phrase(region) def validate_region_input(self, region: str) -> bool: """ 驗證region輸入是否有效 Args: region: 待驗證的區域標識符 Returns: bool: 是否為有效的region """ try: if not region or region.strip() == "": return False # 清理並檢查是否為已知區域 clean_region = region.replace('_', ' ').strip().lower() known_regions = [ "top left", "top center", "top right", "middle left", "middle center", "middle right", "bottom left", "bottom center", "bottom right", "center", "unknown" ] # 直接匹配或包含關鍵詞匹配 if clean_region in known_regions: return True # 檢查是否包含有效的位置關鍵詞組合 position_keywords = ["top", "bottom", "left", "right", "center", "middle"] has_valid_keyword = any(keyword in clean_region for keyword in position_keywords) return has_valid_keyword except Exception as e: logger.warning(f"Error validating region input '{region}': {str(e)}") return False def get_enhanced_directional_description(self, region: str) -> str: """ 增強版的方位描述生成,提供更豐富的方位資訊 擴展原有的get_directional_description方法功能 Args: region: 區域名稱 Returns: str: 增強的方位描述字串 """ try: if not self.validate_region_input(region): return "central" region_lower = region.replace('_', ' ').strip().lower() # 用比較準確的方位映射 direction_mappings = { "top left": "northwest", "top center": "north", "top right": "northeast", "middle left": "west", "middle center": "central", "center": "central", "middle right": "east", "bottom left": "southwest", "bottom center": "south", "bottom right": "southeast" } if region_lower in direction_mappings: return direction_mappings[region_lower] # 模糊匹配邏輯保持與原方法相同 if "top" in region_lower and "left" in region_lower: return "northwest" elif "top" in region_lower and "right" in region_lower: return "northeast" elif "bottom" in region_lower and "left" in region_lower: return "southwest" elif "bottom" in region_lower and "right" in region_lower: return "southeast" elif "top" in region_lower: return "north" elif "bottom" in region_lower: return "south" elif "left" in region_lower: return "west" elif "right" in region_lower: return "east" else: return "central" except Exception as e: logger.error(f"Error getting enhanced directional description for region '{region}': {str(e)}") return "central" def analyze_regions(self, detected_objects: List[Dict]) -> Dict: """ 分析物件在各區域的分布情況 Args: detected_objects: 包含位置資訊的檢測物件列表 Returns: 包含區域分析結果的字典 """ try: if not detected_objects: logger.warning("No detected objects provided for region analysis") return { "counts": {region: 0 for region in self.regions.keys()}, "main_focus": [], "objects_by_region": {region: [] for region in self.regions.keys()} } # 計算每個區域的物件數量 region_counts = {region: 0 for region in self.regions.keys()} region_objects = {region: [] for region in self.regions.keys()} for obj in detected_objects: try: region = obj.get("region", "unknown") if region in region_counts: region_counts[region] += 1 region_objects[region].append({ "class_id": obj.get("class_id"), "class_name": obj.get("class_name") }) else: logger.warning(f"Unknown region '{region}' found in object") except Exception as e: logger.error(f"Error processing object in region analysis: {str(e)}") continue # 確定主要焦點區域(按物件數量排序的前1-2個區域) sorted_regions = sorted(region_counts.items(), key=lambda x: x[1], reverse=True) main_regions = [region for region, count in sorted_regions if count > 0][:2] result = { "counts": region_counts, "main_focus": main_regions, "objects_by_region": region_objects } logger.info(f"Region analysis completed. Main focus areas: {main_regions}") return result except Exception as e: logger.error(f"Error in region analysis: {str(e)}") logger.error(traceback.format_exc()) # 返回空的結果結構而不是拋出異常 return { "counts": {region: 0 for region in self.regions.keys()}, "main_focus": [], "objects_by_region": {region: [] for region in self.regions.keys()} } def create_distribution_map(self, detected_objects: List[Dict]) -> Dict: """ 創建物件在各區域分布的詳細地圖,用於空間分析 Args: detected_objects: 檢測到的物件列表 Returns: 包含各區域分布詳情的字典 """ try: if not detected_objects: logger.warning("No detected objects provided for distribution map creation") return self._get_empty_distribution_map() distribution = {} # 初始化所有區域 for region in self.regions.keys(): distribution[region] = { "total": 0, "objects": {}, "density": 0 } # 填充分布資料 for obj in detected_objects: try: region = obj.get("region", "unknown") class_id = obj.get("class_id") class_name = obj.get("class_name", "unknown") if region not in distribution: logger.warning(f"Unknown region '{region}' found, skipping object") continue distribution[region]["total"] += 1 if class_id not in distribution[region]["objects"]: distribution[region]["objects"][class_id] = { "name": class_name, "count": 0, "positions": [] } distribution[region]["objects"][class_id]["count"] += 1 # 儲存位置資訊用於空間關係分析 normalized_center = obj.get("normalized_center") if normalized_center: distribution[region]["objects"][class_id]["positions"].append(normalized_center) except Exception as e: logger.error(f"Error processing object in distribution map: {str(e)}") continue # 計算每個區域的物件密度 for region, data in distribution.items(): # 假設所有區域在網格中大小相等 data["density"] = data["total"] / 1 logger.info("Distribution map created successfully") return distribution except Exception as e: logger.error(f"Error creating distribution map: {str(e)}") logger.error(traceback.format_exc()) return self._get_empty_distribution_map() def calculate_spatial_diversity(self, detected_objects: List[Dict]) -> float: """ 計算物件空間分布的多樣性 評估物件是否分散在不同區域,避免所有物件集中在單一區域 Args: detected_objects: 檢測到的物件列表 Returns: 空間多樣性評分 (0.0-1.0) """ try: if not detected_objects: logger.warning("No detected objects provided for spatial diversity calculation") return 0.0 regions = set() for obj in detected_objects: region = obj.get("region", "center") regions.add(region) unique_regions = len(regions) diversity_score = min(unique_regions / 2.0, 1.0) logger.info(f"Spatial diversity calculated: {diversity_score:.3f} (regions: {unique_regions})") return diversity_score except Exception as e: logger.error(f"Error calculating spatial diversity: {str(e)}") logger.error(traceback.format_exc()) return 0.0 def get_directional_description(self, region: str) -> str: """ 將區域名稱轉換為方位描述(東西南北) Args: region: 區域名稱 Returns: 方位描述字串 """ try: region_lower = region.lower() if "top" in region_lower and "left" in region_lower: return "northwest" elif "top" in region_lower and "right" in region_lower: return "northeast" elif "bottom" in region_lower and "left" in region_lower: return "southwest" elif "bottom" in region_lower and "right" in region_lower: return "southeast" elif "top" in region_lower: return "north" elif "bottom" in region_lower: return "south" elif "left" in region_lower: return "west" elif "right" in region_lower: return "east" else: return "central" except Exception as e: logger.error(f"Error getting directional description for region '{region}': {str(e)}") return "central" def _get_empty_distribution_map(self) -> Dict: """ 返回空的分布地圖結構 Returns: 空的分布地圖字典 """ distribution = {} for region in self.regions.keys(): distribution[region] = { "total": 0, "objects": {}, "density": 0 } return distribution