Spaces:
Running
on
Zero
Running
on
Zero
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 "" | |