import logging import traceback from typing import Dict, List, Any, Optional logger = logging.getLogger(__name__) class FunctionalZoneDetector: """ 負責基於物件關聯性的功能區域識別 處理物件組合分析和描述性區域命名 """ def __init__(self): """初始化功能區域檢測器""" try: logger.info("FunctionalZoneDetector initialized successfully") except Exception as e: logger.error(f"Failed to initialize FunctionalZoneDetector: {str(e)}") logger.error(traceback.format_exc()) raise def identify_primary_functional_area(self, detected_objects: List[Dict]) -> Dict: """ 識別主要功能區域,基於最強的物件關聯性組合 採用通用邏輯處理各種室內場景 Args: detected_objects: 檢測到的物件列表 Returns: 主要功能區域字典或None """ try: # 用餐區域檢測(桌椅組合) dining_area = self.detect_functional_combination( detected_objects, primary_objects=[60], # dining table supporting_objects=[56, 40, 41, 42, 43], # chair, wine glass, cup, fork, knife min_supporting=2, description_template="Dining area with table and seating arrangement" ) if dining_area: return dining_area # 休息區域檢測(沙發電視組合或床) seating_area = self.detect_functional_combination( detected_objects, primary_objects=[57, 59], # sofa, bed supporting_objects=[62, 58, 56], # tv, potted plant, chair min_supporting=1, description_template="Seating and relaxation area" ) if seating_area: return seating_area # 工作區域檢測(電子設備與家具組合) work_area = self.detect_functional_combination( detected_objects, primary_objects=[63, 66], # laptop, keyboard supporting_objects=[60, 56, 64], # dining table, chair, mouse min_supporting=2, description_template="Workspace area with electronics and furniture" ) if work_area: return work_area return None except Exception as e: logger.error(f"Error identifying primary functional area: {str(e)}") logger.error(traceback.format_exc()) return None def identify_secondary_functional_area(self, detected_objects: List[Dict], existing_zones: Dict) -> Dict: """ 識別次要功能區域,避免與主要區域重疊 Args: detected_objects: 檢測到的物件列表 existing_zones: 已存在的功能區域 Returns: 次要功能區域字典或None """ try: # 獲取已使用的區域 used_regions = set(zone.get("region") for zone in existing_zones.values()) # 裝飾區域檢測(植物集中區域) decorative_area = self.detect_functional_combination( detected_objects, primary_objects=[58], # potted plant supporting_objects=[75], # vase min_supporting=0, min_primary=3, # 至少需要3個植物 description_template="Decorative area with plants and ornamental items", exclude_regions=used_regions ) if decorative_area: return decorative_area # 儲存區域檢測(廚房電器組合) storage_area = self.detect_functional_combination( detected_objects, primary_objects=[72, 68, 69], # refrigerator, microwave, oven supporting_objects=[71], # sink min_supporting=0, min_primary=2, description_template="Kitchen appliance and storage area", exclude_regions=used_regions ) if storage_area: return storage_area return None except Exception as e: logger.error(f"Error identifying secondary functional area: {str(e)}") logger.error(traceback.format_exc()) return None def detect_functional_combination(self, detected_objects: List[Dict], primary_objects: List[int], supporting_objects: List[int], min_supporting: int, description_template: str, min_primary: int = 1, exclude_regions: set = None) -> Dict: """ 通用的功能組合檢測方法 基於主要物件和支持物件的組合判斷功能區域 Args: detected_objects: 檢測到的物件列表 primary_objects: 主要物件的class_id列表 supporting_objects: 支持物件的class_id列表 min_supporting: 最少需要的支持物件數量 description_template: 描述模板 min_primary: 最少需要的主要物件數量 exclude_regions: 需要排除的區域集合 Returns: 功能區域資訊字典,如果不符合條件則返回None """ try: if exclude_regions is None: exclude_regions = set() # 收集主要物件 primary_objs = [obj for obj in detected_objects if obj.get("class_id") in primary_objects and obj.get("confidence", 0) >= 0.4] # 收集支持物件 supporting_objs = [obj for obj in detected_objects if obj.get("class_id") in supporting_objects and obj.get("confidence", 0) >= 0.4] # 檢查是否滿足最少數量要求 if len(primary_objs) < min_primary or len(supporting_objs) < min_supporting: return None # 按區域組織物件 region_combinations = {} all_relevant_objs = primary_objs + supporting_objs for obj in all_relevant_objs: region = obj.get("region") # 排除指定區域 if region in exclude_regions: continue if region not in region_combinations: region_combinations[region] = {"primary": [], "supporting": [], "all": []} region_combinations[region]["all"].append(obj) if obj.get("class_id") in primary_objects: region_combinations[region]["primary"].append(obj) else: region_combinations[region]["supporting"].append(obj) # 找到最佳區域組合 best_region = None best_score = 0 for region, objs in region_combinations.items(): # 計算該區域的評分 primary_count = len(objs["primary"]) supporting_count = len(objs["supporting"]) # 必須滿足最低要求 if primary_count < min_primary or supporting_count < min_supporting: continue # 計算組合評分(主要物件權重較高) score = primary_count * 2 + supporting_count if score > best_score: best_score = score best_region = region if best_region is None: return None best_combination = region_combinations[best_region] all_objects = [obj["class_name"] for obj in best_combination["all"]] return { "region": best_region, "objects": all_objects, "description": description_template } except Exception as e: logger.error(f"Error detecting functional combination: {str(e)}") logger.error(traceback.format_exc()) return None def generate_descriptive_zone_key_from_data(self, zone_data: Dict, priority_level: str) -> str: """ 基於區域與物品名產生一個比較有描述性的區域 Args: zone_data: 區域數據字典 priority_level: 優先級別(primary/secondary) Returns: str: 描述性區域鍵名 """ try: objects = zone_data.get("objects", []) region = zone_data.get("region", "") description = zone_data.get("description", "") # 基於物件內容確定功能類型 if any("dining" in obj.lower() or "table" in obj.lower() for obj in objects): base_name = "dining area" elif any("chair" in obj.lower() or "sofa" in obj.lower() for obj in objects): base_name = "seating area" elif any("bed" in obj.lower() for obj in objects): base_name = "sleeping area" elif any("laptop" in obj.lower() or "keyboard" in obj.lower() for obj in objects): base_name = "workspace area" elif any("plant" in obj.lower() or "vase" in obj.lower() for obj in objects): base_name = "decorative area" elif any("refrigerator" in obj.lower() or "microwave" in obj.lower() for obj in objects): base_name = "kitchen area" else: # 基於描述內容推斷 if "dining" in description.lower(): base_name = "dining area" elif "seating" in description.lower() or "relaxation" in description.lower(): base_name = "seating area" elif "work" in description.lower(): base_name = "workspace area" elif "decorative" in description.lower(): base_name = "decorative area" else: base_name = "functional area" # 為次要區域添加位置標識以區分 if priority_level == "secondary" and region: spatial_context = self.get_spatial_context_description(region) if spatial_context: return f"{spatial_context} {base_name}" return base_name except Exception as e: logger.warning(f"Error generating descriptive zone key: {str(e)}") return "activity area" def get_spatial_context_description(self, region: str) -> str: """ 獲取空間上下文描述 Args: region: 區域位置標識 Returns: str: 空間上下文描述 """ try: spatial_mapping = { "top_left": "upper left", "top_center": "upper", "top_right": "upper right", "middle_left": "left side", "middle_center": "central", "middle_right": "right side", "bottom_left": "lower left", "bottom_center": "lower", "bottom_right": "lower right" } return spatial_mapping.get(region, "") except Exception as e: logger.warning(f"Error getting spatial context for region '{region}': {str(e)}") return ""