VisionScout / scene_zone_identifier.py
DawnC's picture
Upload 59 files
e6a18b7 verified
raw
history blame
71.1 kB
import logging
import traceback
import numpy as np
from typing import Dict, List, Any, Optional
logger = logging.getLogger(__name__)
class SceneZoneIdentifier:
"""
負責不同場景類型的區域識別邏輯
專注於根據場景類型執行相應的功能區域識別策略
"""
def __init__(self):
"""初始化場景區域辨識器"""
try:
logger.info("SceneZoneIdentifier initialized successfully")
except Exception as e:
logger.error(f"Failed to initialize SceneZoneIdentifier: {str(e)}")
logger.error(traceback.format_exc())
raise
def identify_indoor_zones(self, category_regions: Dict, detected_objects: List[Dict], scene_type: str) -> Dict:
"""
平衡化的室內功能區域識別並標準化命名
採用通用的物件關聯性分析,避免只針對特定場景
Args:
category_regions: 按類別和區域分組的物件字典
detected_objects: 檢測到的物件列表
scene_type: 場景類型
Returns:
識別出的室內功能區域字典,使用描述性鍵名
"""
try:
zones = {}
# 主要功能區域(基於物件關聯性而非場景類型)
primary_zone = self._identify_primary_functional_area(detected_objects)
if primary_zone:
# 基於區域內容生成描述性鍵名
descriptive_key = self._generate_descriptive_zone_key_from_data(primary_zone, "primary")
zones[descriptive_key] = primary_zone
# 只有明確證據且物件數量足夠時創建次要功能區域
if len(zones) >= 1 and len(detected_objects) >= 6:
secondary_zone = self._identify_secondary_functional_area(detected_objects, zones)
if secondary_zone:
# 基於區域內容生成描述性鍵名
descriptive_key = self._generate_descriptive_zone_key_from_data(secondary_zone, "secondary")
zones[descriptive_key] = secondary_zone
logger.info(f"Identified {len(zones)} indoor zones for scene type '{scene_type}'")
return zones
except Exception as e:
logger.error(f"Error identifying indoor zones: {str(e)}")
logger.error(traceback.format_exc())
return {}
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 ""
def identify_outdoor_general_zones(self, category_regions: Dict, detected_objects: List[Dict], scene_type: str) -> Dict:
"""
識別一般戶外場景的功能區域
Args:
category_regions: 按類別和區域分組的物件字典
detected_objects: 檢測到的物件列表
scene_type: 特定戶外場景類型
Returns:
戶外功能區域字典
"""
try:
zones = {}
# 識別行人區域
people_objs = [obj for obj in detected_objects if obj["class_id"] == 0]
if people_objs:
people_regions = {}
for obj in people_objs:
region = obj["region"]
if region not in people_regions:
people_regions[region] = []
people_regions[region].append(obj)
if people_regions:
# 找到主要的行人活動區域
main_people_regions = sorted(people_regions.items(),
key=lambda x: len(x[1]),
reverse=True)[:2] # 取前2個區域
for idx, (region, objs) in enumerate(main_people_regions):
if len(objs) > 0:
# 生成基於位置的描述性鍵名
spatial_desc = self._get_directional_description(region)
if spatial_desc and spatial_desc != "central":
zone_key = f"{spatial_desc} pedestrian area"
else:
zone_key = "main pedestrian area" if idx == 0 else "secondary pedestrian area"
zones[zone_key] = {
"region": region,
"objects": ["person"] * len(objs),
"description": f"Pedestrian area with {len(objs)} {'people' if len(objs) > 1 else 'person'}"
}
# 識別車輛區域,適用於街道和停車場
vehicle_objs = [obj for obj in detected_objects if obj["class_id"] in [1, 2, 3, 5, 6, 7]]
if vehicle_objs:
vehicle_regions = {}
for obj in vehicle_objs:
region = obj["region"]
if region not in vehicle_regions:
vehicle_regions[region] = []
vehicle_regions[region].append(obj)
if vehicle_regions:
main_vehicle_region = max(vehicle_regions.items(),
key=lambda x: len(x[1]),
default=(None, []))
if main_vehicle_region[0] is not None:
vehicle_types = [obj["class_name"] for obj in main_vehicle_region[1]]
zones["vehicle_zone"] = {
"region": main_vehicle_region[0],
"objects": vehicle_types,
"description": f"Traffic area with {', '.join(list(set(vehicle_types))[:3])}"
}
# 針對公園區域的特殊處理
if scene_type == "park_area":
zones.update(self._identify_park_recreational_zones(detected_objects))
# 針對停車場的特殊處理
if scene_type == "parking_lot":
zones.update(self._identify_parking_zones(detected_objects))
logger.info(f"Identified {len(zones)} outdoor zones for scene type '{scene_type}'")
return zones
except Exception as e:
logger.error(f"Error identifying outdoor general zones: {str(e)}")
logger.error(traceback.format_exc())
return {}
def identify_intersection_zones(self, category_regions: Dict, detected_objects: List[Dict], viewpoint: str) -> Dict:
"""
辨識城市十字路口的功能區域,無論是否有行人,只要偵測到紅綠燈就一定顯示 Traffic Control Area;
若有行人,則額外建立 Crossing Zone 並把行人 + 同 region 的紅綠燈歸在一起。
Args:
category_regions: 按類別和 region 分組的物件字典
detected_objects: YOLO 檢測到的所有物件列表
viewpoint: 偵測到的視角字串
Returns:
zones: 最終的十字路口功能區域字典
"""
try:
zones = {}
# 1. 按 class_id 分出行人、車輛、紅綠燈
pedestrian_objs = [obj for obj in detected_objects if obj["class_id"] == 0]
vehicle_objs = [obj for obj in detected_objects if obj["class_id"] in [1, 2, 3, 5, 7]]
traffic_light_objs = [obj for obj in detected_objects if obj["class_id"] == 9]
# 2. Step A: 無條件建立 Traffic Control Area
# 把每個 region 下的紅綠燈都先分群,生成對應 zone,確保「只要偵測到紅綠燈就一定顯示」
signal_regions_all = {}
for t in traffic_light_objs:
region = t["region"]
signal_regions_all.setdefault(region, []).append(t)
for idx, (region, signals) in enumerate(signal_regions_all.items()):
# 先決定 zone_key (依 direction 或 primary/auxiliary)
direction = self._get_directional_description(region)
if direction and direction != "central":
zone_key = f"{direction} traffic control area"
else:
zone_key = "primary traffic control area" if idx == 0 else "auxiliary traffic control area"
# 確保命名不衝突
if zone_key in zones:
suffix = 1
new_key = f"{zone_key} ({suffix})"
while new_key in zones:
suffix += 1
new_key = f"{zone_key} ({suffix})"
zone_key = new_key
zones[zone_key] = {
"region": region,
"objects": ["traffic light"] * len(signals),
"description": f"Traffic control area with {len(signals)} traffic lights in {region}"
}
# (用於後面計算 Crossing 使用掉的 traffic light)
used_tl_count_per_region = dict.fromkeys(signal_regions_all.keys(), 0)
# 3. Step B: 如果有行人,就建立 Crossing Zone,並移除已被打包的紅綠燈
if pedestrian_objs:
# 先呼叫 _analyze_crossing_patterns,讓它回傳「行人 + 同 region 的紅綠燈」區
crossing_zones = self._analyze_crossing_patterns(pedestrian_objs, traffic_light_objs)
# 把 Crossing Zone 加到最終 zones,並同時記錄已使用掉的紅綠燈數量
for zone_key, zone_info in crossing_zones.items():
region = zone_info.get("region", "")
obj_list = zone_info.get("objects", [])
# 如果該 zone_info["objects"] 裡含有紅綠燈,就累加到 used_tl_count_per_region
count_in_zone = obj_list.count("traffic light")
if count_in_zone > 0:
used_tl_count_per_region[region] = used_tl_count_per_region.get(region, 0) + count_in_zone
# 加入最終結果
# 如果 key 重複,也可以在此加上 index,或直接覆蓋
if zone_key in zones:
suffix = 1
new_key = f"{zone_key} ({suffix})"
while new_key in zones:
suffix += 1
new_key = f"{zone_key} ({suffix})"
zone_key = new_key
zones[zone_key] = {
"region": region,
"objects": obj_list,
"description": zone_info.get("description", "")
}
# 4. Step C: 計算並顯示 debug 資訊 (Total / Used / Remaining)
for region, signals in signal_regions_all.items():
total = len(signals)
used = used_tl_count_per_region.get(region, 0)
remaining = total - used
# print(f"[DEBUG] Region '{region}': Total TL = {total}, Used in crossing = {used}, Remaining = {remaining}")
# 5. Step D: 分析車輛交通區域(Vehicle Zones)
if vehicle_objs:
traffic_zones = self._analyze_traffic_zones(vehicle_objs)
# _analyze_traffic_zones 內部已用英文 debug,直接更新
for zone_key, zone_info in traffic_zones.items():
if zone_key in zones:
suffix = 1
new_key = f"{zone_key} ({suffix})"
while new_key in zones:
suffix += 1
new_key = f"{zone_key} ({suffix})"
zone_key = new_key
zones[zone_key] = zone_info
logger.info(f"Identified {len(zones)} intersection zones")
return zones
except Exception as e:
logger.error(f"Error in identify_intersection_zones: {str(e)}")
logger.error(traceback.format_exc())
return {}
def identify_aerial_view_zones(self, category_regions: Dict, detected_objects: List[Dict], scene_type: str) -> Dict:
"""
辨識空中視角場景的功能區域
專注於模式和流動而非特定區域
Args:
category_regions: 按類別和區域分組的物件字典
detected_objects: 檢測到的物件列表
scene_type: 特定場景類型
Returns:
空中視角功能區域字典
"""
try:
zones = {}
# 識別行人模式
people_objs = [obj for obj in detected_objects if obj["class_id"] == 0]
if people_objs:
# 將位置轉換為數組進行模式分析
positions = np.array([obj["normalized_center"] for obj in people_objs])
if len(positions) >= 3:
# 計算分布指標
x_coords = positions[:, 0]
y_coords = positions[:, 1]
x_mean = np.mean(x_coords)
y_mean = np.mean(y_coords)
x_std = np.std(x_coords)
y_std = np.std(y_coords)
# 判斷人群是否組織成線性模式
if x_std < 0.1 or y_std < 0.1:
# 沿一個軸的線性分布
pattern_direction = "vertical" if x_std < y_std else "horizontal"
zones["pedestrian_pattern"] = {
"region": "central",
"objects": ["person"] * len(people_objs),
"description": f"Aerial view shows a {pattern_direction} pedestrian movement pattern"
}
else:
# 更分散的模式
zones["pedestrian_distribution"] = {
"region": "wide",
"objects": ["person"] * len(people_objs),
"description": f"Aerial view shows pedestrians distributed across the area"
}
# 識別車輛模式進行交通分析
vehicle_objs = [obj for obj in detected_objects if obj["class_id"] in [1, 2, 3, 5, 6, 7]]
if vehicle_objs:
zones.update(self._analyze_aerial_traffic_patterns(vehicle_objs))
# 針對十字路口特定空中視角的處理
if "intersection" in scene_type:
zones.update(self._identify_aerial_intersection_features(detected_objects))
# 針對廣場空中視角的處理
if "plaza" in scene_type:
zones.update(self._identify_aerial_plaza_features(people_objs))
logger.info(f"Identified {len(zones)} aerial view zones")
return zones
except Exception as e:
logger.error(f"Error identifying aerial view zones: {str(e)}")
logger.error(traceback.format_exc())
return {}
def identify_asian_cultural_zones(self, category_regions: Dict, detected_objects: List[Dict], scene_type: str) -> Dict:
"""
辨識有亞洲文化背景的場景功能區域
Args:
category_regions: 按類別和區域分組的物件字典
detected_objects: 檢測到的物件列表
scene_type: 特定場景類型
Returns:
亞洲文化功能區域字典
"""
try:
zones = {}
# 識別店面區域
# 由於店面不能直接檢測,從情境推斷
# 例如,尋找有標誌、行人和小物件的區域
storefront_regions = {}
for obj in detected_objects:
if obj["class_id"] == 0: # Person
region = obj["region"]
if region not in storefront_regions:
storefront_regions[region] = []
storefront_regions[region].append(obj)
# 將人最多的區域作為店面區域
if storefront_regions:
main_storefront_regions = sorted(storefront_regions.items(),
key=lambda x: len(x[1]),
reverse=True)[:2] # 前2個區域
for idx, (region, objs) in enumerate(main_storefront_regions):
# 生成基於位置的描述性鍵名
spatial_desc = self._get_directional_description(region)
if spatial_desc and spatial_desc != "central":
zone_key = f"{spatial_desc} commercial area"
else:
zone_key = "main commercial area" if idx == 0 else "secondary commercial area"
zones[zone_key] = {
"region": region,
"objects": [obj["class_name"] for obj in objs],
"description": f"Asian commercial storefront with pedestrian activity"
}
# 辨識行人通道
zones.update(self._identify_asian_pedestrian_pathway(detected_objects))
# 辨識攤販區域(小攤/商店 - 從情境推斷)
zones.update(self._identify_vendor_zones(detected_objects))
# 針對夜市的特殊處理
if scene_type == "asian_night_market":
zones["food_stall_zone"] = {
"region": "middle_center",
"objects": ["inferred food stalls"],
"description": "Food stall area typical of Asian night markets"
}
logger.info(f"Identified {len(zones)} Asian cultural zones")
return zones
except Exception as e:
logger.error(f"Error identifying Asian cultural zones: {str(e)}")
logger.error(traceback.format_exc())
return {}
def identify_upscale_dining_zones(self, category_regions: Dict, detected_objects: List[Dict]) -> Dict:
"""
辨識高級餐飲設置的功能區域
Args:
category_regions: 按類別和區域分組的物件字典
detected_objects: 檢測到的物件列表
Returns:
高級餐飲功能區域字典
"""
try:
zones = {}
# 辨識餐桌區域
dining_items = []
dining_regions = {}
for obj in detected_objects:
if obj["class_id"] in [40, 41, 42, 43, 44, 45, 60]: # Wine glass, cup, fork, knife, spoon, bowl, table
region = obj["region"]
if region not in dining_regions:
dining_regions[region] = []
dining_regions[region].append(obj)
dining_items.append(obj["class_name"])
if dining_items:
main_dining_region = max(dining_regions.items(),
key=lambda x: len(x[1]),
default=(None, []))
if main_dining_region[0] is not None:
zones["formal_dining_zone"] = {
"region": main_dining_region[0],
"objects": list(set(dining_items)),
"description": f"Formal dining area with {', '.join(list(set(dining_items))[:3])}"
}
# 識別裝飾區域,增強檢測
zones.update(self._identify_upscale_decorative_zones(detected_objects))
# 識別座位安排區域
zones.update(self._identify_dining_seating_zones(detected_objects))
# 識別服務區域(如果與餐飲區域不同)
zones.update(self._identify_serving_zones(detected_objects, zones))
logger.info(f"Identified {len(zones)} upscale dining zones")
return zones
except Exception as e:
logger.error(f"Error identifying upscale dining zones: {str(e)}")
logger.error(traceback.format_exc())
return {}
def identify_financial_district_zones(self, category_regions: Dict, detected_objects: List[Dict]) -> Dict:
"""
金融區場景的功能區域
Args:
category_regions: 按類別和區域分組的物件字典
detected_objects: 檢測到的物件列表
Returns:
金融區功能區域字典
"""
try:
zones = {}
# 識別交通區域
traffic_items = []
traffic_regions = {}
for obj in detected_objects:
if obj["class_id"] in [1, 2, 3, 5, 6, 7, 9]: # 各種車輛和交通燈
region = obj["region"]
if region not in traffic_regions:
traffic_regions[region] = []
traffic_regions[region].append(obj)
traffic_items.append(obj["class_name"])
if traffic_items:
main_traffic_region = max(traffic_regions.items(),
key=lambda x: len(x[1]),
default=(None, []))
if main_traffic_region[0] is not None:
zones["traffic_zone"] = {
"region": main_traffic_region[0],
"objects": list(set(traffic_items)),
"description": f"Urban traffic area with {', '.join(list(set(traffic_items))[:3])}"
}
# 側邊建築區域(從場景情境推斷)
zones.update(self._identify_building_zones(detected_objects))
# 行人區域
zones.update(self._identify_financial_pedestrian_zones(detected_objects))
logger.info(f"Identified {len(zones)} financial district zones")
return zones
except Exception as e:
logger.error(f"Error identifying financial district zones: {str(e)}")
logger.error(traceback.format_exc())
return {}
def identify_landmark_zones(self, landmark_objects: List[Dict]) -> Dict:
"""
辨識與地標相關的功能區域
Args:
landmark_objects: 被辨識為地標的物體列表
Returns:
地標相關的功能區域字典
"""
try:
landmark_zones = {}
# 如果沒有任何地標,就直接回空字典
if not landmark_objects:
logger.warning("No landmark objects provided to identify_landmark_zones")
return landmark_zones
# 只取第一個地標來示範:至少產生一個地標
landmark = landmark_objects[0]
# 確保傳入的 landmark 是 dict
if not isinstance(landmark, dict):
logger.warning("First landmark object is not a dict")
return landmark_zones
# 從 landmark dict 拿出必要欄位
landmark_id = landmark.get("landmark_id", "unknown_landmark")
landmark_name = landmark.get("class_name", "Landmark")
landmark_type = landmark.get("landmark_type", "architectural")
landmark_region = landmark.get("region", "middle_center")
# 如果 location 沒提供,就給預設 "this area"
location = landmark.get("location")
if not location:
location = "this area"
# 為地標創建主要觀景區
zone_id = f"{landmark_name.lower().replace(' ', '_')}_viewing_area"
zone_name = f"{landmark_name} Viewing Area"
# 根據地標類型調整描述,並確保帶入地點
if landmark_type == "natural":
zone_description = (
f"Scenic viewpoint for observing {landmark_name}, "
f"a notable natural landmark in {location}."
)
primary_function = "Nature observation and photography"
elif landmark_type == "monument":
zone_description = (
f"Viewing area around {landmark_name}, "
f"a significant monument in {location}."
)
primary_function = "Historical appreciation and cultural tourism"
else: # architectural
zone_description = (
f"Area centered around {landmark_name}, "
f"where visitors can observe and appreciate this iconic structure in {location}."
)
primary_function = "Architectural tourism and photography"
# 確定與地標相關的物體(如果被偵測到)
related_objects = []
for o in landmark_objects:
cn = o.get("class_name", "").lower()
if cn in ["person", "camera", "cell phone", "backpack"]:
related_objects.append(cn)
# 建立地標功能區
landmark_zones[zone_id] = {
"name": zone_name,
"description": zone_description,
"objects": ["landmark"] + related_objects,
"region": landmark_region,
"primary_function": primary_function
}
# 創建相關輔助功能區,如攝影區、紀念品販賣區
auxiliary_zones = self._create_landmark_auxiliary_zones(landmark, 0)
if auxiliary_zones:
landmark_zones.update(auxiliary_zones)
logger.info(f"Identified {len(landmark_zones)} landmark zones")
return landmark_zones
except Exception as e:
logger.error(f"Error in identify_landmark_zones: {str(e)}")
logger.error(traceback.format_exc())
return {}
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 _analyze_crossing_patterns(self, pedestrians: List[Dict], traffic_lights: List[Dict]) -> Dict:
"""
Analyze pedestrian crossing patterns to identify crossing zones.
若同一 region 中同時有行人與紅綠燈,則將兩者都放入該區域的 objects。
Args:
pedestrians: 行人物件列表(每個 obj 應包含 'class_id', 'region', 'confidence' 等)
traffic_lights: 紅綠燈物件列表(每個 obj 應包含 'class_id', 'region', 'confidence' 等)
Returns:
crossing_zones: 字典,key 為 zone 名稱,value 包含 'region', 'objects', 'description'
"""
try:
crossing_zones = {}
# 如果沒有任何行人,就不辨識任何 crossing zone
if not pedestrians:
return crossing_zones
# (1) 按照 region 分組行人
pedestrian_regions = {}
for p in pedestrians:
region = p["region"]
pedestrian_regions.setdefault(region, []).append(p)
# (2) 針對每個 region,看是否同時有紅綠燈
# 建立一個 mapping: region -> { "pedestrians": [...], "traffic_lights": [...] }
combined_regions = {}
for region, peds in pedestrian_regions.items():
# 取得該 region 下所有紅綠燈
tls_in_region = [t for t in traffic_lights if t["region"] == region]
combined_regions[region] = {
"pedestrians": peds,
"traffic_lights": tls_in_region
}
# (3) 按照行人數量排序,找出前兩個需要建立 crossing zone 的 region
sorted_regions = sorted(
combined_regions.items(),
key=lambda x: len(x[1]["pedestrians"]),
reverse=True
)
# (4) 將前兩個 region 建立 Crossing Zone,objects 同時包含行人與紅綠燈
for idx, (region, group) in enumerate(sorted_regions[:2]):
peds = group["pedestrians"]
tls = group["traffic_lights"]
has_nearby_signals = len(tls) > 0
# 生成 zone_name(基於 region 方向 + idx 決定主/次 crossing)
direction = self._get_directional_description(region)
if direction and direction != "central":
zone_name = f"{direction} crossing area"
else:
zone_name = "main crossing area" if idx == 0 else "secondary crossing area"
# 組合 description
description = f"Pedestrian crossing area with {len(peds)} "
description += "person" if len(peds) == 1 else "people"
if direction:
description += f" in {direction} direction"
if has_nearby_signals:
description += " near traffic signals"
# ======= 將行人 + 同區紅綠燈一併放入 objects =======
obj_list = ["pedestrian"] * len(peds)
if has_nearby_signals:
obj_list += ["traffic light"] * len(tls)
crossing_zones[zone_name] = {
"region": region,
"objects": obj_list,
"description": description
}
return crossing_zones
except Exception as e:
logger.error(f"Error in _analyze_crossing_patterns: {str(e)}")
logger.error(traceback.format_exc())
return {}
def _analyze_traffic_zones(self, vehicles: List[Dict]) -> Dict:
"""
分析車輛分布以識別具有方向感知的交通區域
Args:
vehicles: 車輛物件列表
Returns:
識別出的交通區域字典
"""
try:
traffic_zones = {}
if not vehicles:
return traffic_zones
# 按區域分組車輛
vehicle_regions = {}
for v in vehicles:
region = v["region"]
if region not in vehicle_regions:
vehicle_regions[region] = []
vehicle_regions[region].append(v)
# 為有車輛的區域創建交通區域
main_traffic_region = max(vehicle_regions.items(), key=lambda x: len(x[1]), default=(None, []))
if main_traffic_region[0] is not None:
region = main_traffic_region[0]
vehicles_in_region = main_traffic_region[1]
# 獲取車輛類型列表用於描述
vehicle_types = [v["class_name"] for v in vehicles_in_region]
unique_types = list(set(vehicle_types))
# 獲取方向描述
direction = self._get_directional_description(region)
# 創建描述性區域
traffic_zones["vehicle_zone"] = {
"region": region,
"objects": vehicle_types,
"description": f"Vehicle traffic area with {', '.join(unique_types[:3])}" +
(f" in {direction} area" if direction else "")
}
# 如果車輛分布在多個區域,創建次要區域
if len(vehicle_regions) > 1:
# 獲取第二大車輛聚集區域
sorted_regions = sorted(vehicle_regions.items(), key=lambda x: len(x[1]), reverse=True)
if len(sorted_regions) > 1:
second_region, second_vehicles = sorted_regions[1]
direction = self._get_directional_description(second_region)
vehicle_types = [v["class_name"] for v in second_vehicles]
unique_types = list(set(vehicle_types))
traffic_zones["secondary_vehicle_zone"] = {
"region": second_region,
"objects": vehicle_types,
"description": f"Secondary traffic area with {', '.join(unique_types[:2])}" +
(f" in {direction} direction" if direction else "")
}
return traffic_zones
except Exception as e:
logger.error(f"Error analyzing traffic zones: {str(e)}")
logger.error(traceback.format_exc())
return {}
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 _identify_park_recreational_zones(self, detected_objects: List[Dict]) -> Dict:
"""
識別公園的休閒活動區域
Args:
detected_objects: 檢測到的物件列表
Returns:
休閒區域字典
"""
try:
zones = {}
# 尋找休閒物件(運動球、風箏等)
rec_items = []
rec_regions = {}
for obj in detected_objects:
if obj["class_id"] in [32, 33, 34, 35, 38]: # sports ball, kite, baseball bat, glove, tennis racket
region = obj["region"]
if region not in rec_regions:
rec_regions[region] = []
rec_regions[region].append(obj)
rec_items.append(obj["class_name"])
if rec_items:
main_rec_region = max(rec_regions.items(),
key=lambda x: len(x[1]),
default=(None, []))
if main_rec_region[0] is not None:
zones["recreational_zone"] = {
"region": main_rec_region[0],
"objects": list(set(rec_items)),
"description": f"Recreational area with {', '.join(list(set(rec_items)))}"
}
return zones
except Exception as e:
logger.error(f"Error identifying park recreational zones: {str(e)}")
logger.error(traceback.format_exc())
return {}
def _identify_parking_zones(self, detected_objects: List[Dict]) -> Dict:
"""
停車場的停車區域
Args:
detected_objects: 檢測到的物件列表
Returns:
停車區域字典
"""
try:
zones = {}
# 尋找停放的汽車
car_objs = [obj for obj in detected_objects if obj["class_id"] == 2] # cars
if len(car_objs) >= 3:
# 檢查汽車是否按模式排列(簡化)
car_positions = [obj["normalized_center"] for obj in car_objs]
# 通過分析垂直位置檢查行模式
y_coords = [pos[1] for pos in car_positions]
y_clusters = {}
# 簡化聚類 - 按相似y坐標分組汽車
for i, y in enumerate(y_coords):
assigned = False
for cluster_y in y_clusters.keys():
if abs(y - cluster_y) < 0.1: # 圖像高度的10%內
y_clusters[cluster_y].append(i)
assigned = True
break
if not assigned:
y_clusters[y] = [i]
# 如果有行模式
if max(len(indices) for indices in y_clusters.values()) >= 2:
zones["parking_row"] = {
"region": "central",
"objects": ["car"] * len(car_objs),
"description": f"Organized parking area with vehicles arranged in rows"
}
else:
zones["parking_area"] = {
"region": "wide",
"objects": ["car"] * len(car_objs),
"description": f"Parking area with {len(car_objs)} vehicles"
}
return zones
except Exception as e:
logger.error(f"Error identifying parking zones: {str(e)}")
logger.error(traceback.format_exc())
return {}
def _analyze_aerial_traffic_patterns(self, vehicle_objs: List[Dict]) -> Dict:
"""
分析空中視角的車輛交通模式
Args:
vehicle_objs: 車輛物件列表
Returns:
交通模式區域字典
"""
try:
zones = {}
if not vehicle_objs:
return zones
# 將位置轉換為數組進行模式分析
positions = np.array([obj["normalized_center"] for obj in vehicle_objs])
if len(positions) >= 2:
# 計算分布指標
x_coords = positions[:, 0]
y_coords = positions[:, 1]
x_mean = np.mean(x_coords)
y_mean = np.mean(y_coords)
x_std = np.std(x_coords)
y_std = np.std(y_coords)
# 判斷車輛是否組織成車道
if x_std < y_std * 0.5:
# 車輛垂直對齊 - 表示南北交通
zones["vertical_traffic_flow"] = {
"region": "central_vertical",
"objects": [obj["class_name"] for obj in vehicle_objs[:5]],
"description": "North-south traffic flow visible from aerial view"
}
elif y_std < x_std * 0.5:
# 車輛水平對齊 - 表示東西交通
zones["horizontal_traffic_flow"] = {
"region": "central_horizontal",
"objects": [obj["class_name"] for obj in vehicle_objs[:5]],
"description": "East-west traffic flow visible from aerial view"
}
else:
# 車輛多方向 - 表示十字路口
zones["intersection_traffic"] = {
"region": "central",
"objects": [obj["class_name"] for obj in vehicle_objs[:5]],
"description": "Multi-directional traffic at intersection visible from aerial view"
}
return zones
except Exception as e:
logger.error(f"Error analyzing aerial traffic patterns: {str(e)}")
logger.error(traceback.format_exc())
return {}
def _identify_aerial_intersection_features(self, detected_objects: List[Dict]) -> Dict:
"""
空中視角十字路口特徵
Args:
detected_objects: 檢測到的物件列表
Returns:
十字路口特徵區域字典
"""
try:
zones = {}
# 檢查交通信號
traffic_light_objs = [obj for obj in detected_objects if obj["class_id"] == 9]
if traffic_light_objs:
zones["traffic_control_pattern"] = {
"region": "intersection",
"objects": ["traffic light"] * len(traffic_light_objs),
"description": f"Intersection traffic control with {len(traffic_light_objs)} signals visible from above"
}
# 人行道從空中視角的情境推斷
zones["crossing_pattern"] = {
"region": "central",
"objects": ["inferred crosswalk"],
"description": "Crossing pattern visible from aerial perspective"
}
return zones
except Exception as e:
logger.error(f"Error identifying aerial intersection features: {str(e)}")
logger.error(traceback.format_exc())
return {}
def _identify_aerial_plaza_features(self, people_objs: List[Dict]) -> Dict:
"""
識別空中視角廣場特徵
Args:
people_objs: 行人物件列表
Returns:
廣場特徵區域字典
"""
try:
zones = {}
if people_objs:
# 檢查人群是否聚集在中央區域
central_people = [obj for obj in people_objs
if "middle" in obj["region"]]
if central_people:
zones["central_gathering"] = {
"region": "middle_center",
"objects": ["person"] * len(central_people),
"description": f"Central plaza gathering area with {len(central_people)} people viewed from above"
}
return zones
except Exception as e:
logger.error(f"Error identifying aerial plaza features: {str(e)}")
logger.error(traceback.format_exc())
return {}
def _identify_asian_pedestrian_pathway(self, detected_objects: List[Dict]) -> Dict:
"""
亞洲文化場景中的行人通道
Args:
detected_objects: 檢測到的物件列表
Returns:
行人通道區域字典
"""
try:
zones = {}
pathway_items = []
pathway_regions = {}
# 提取人群用於通道分析
people_objs = [obj for obj in detected_objects if obj["class_id"] == 0]
# 分析人群是否形成線形(商業街的特徵)
people_positions = [obj["normalized_center"] for obj in people_objs]
structured_path = False
path_direction = "meandering"
if len(people_positions) >= 3:
# 檢查人群是否沿相似y坐標排列(水平路徑)
y_coords = [pos[1] for pos in people_positions]
y_mean = sum(y_coords) / len(y_coords)
y_variance = sum((y - y_mean)**2 for y in y_coords) / len(y_coords)
horizontal_path = y_variance < 0.05 # 低變異表示水平對齊
# 檢查人群是否沿相似x坐標排列(垂直路徑)
x_coords = [pos[0] for pos in people_positions]
x_mean = sum(x_coords) / len(x_coords)
x_variance = sum((x - x_mean)**2 for x in x_coords) / len(x_coords)
vertical_path = x_variance < 0.05 # 低變異表示垂直對齊
structured_path = horizontal_path or vertical_path
path_direction = "horizontal" if horizontal_path else "vertical" if vertical_path else "meandering"
# 收集通道物件(人、自行車、摩托車在中間區域)
for obj in detected_objects:
if obj["class_id"] in [0, 1, 3]: # Person, bicycle, motorcycle
y_pos = obj["normalized_center"][1]
# 按垂直位置分組(圖像中間可能是通道)
if 0.25 <= y_pos <= 0.75:
region = obj["region"]
if region not in pathway_regions:
pathway_regions[region] = []
pathway_regions[region].append(obj)
pathway_items.append(obj["class_name"])
if pathway_items:
path_desc = "Pedestrian walkway with people moving through the commercial area"
if structured_path:
path_desc = f"{path_direction.capitalize()} pedestrian walkway with organized foot traffic"
zones["pedestrian_pathway"] = {
"region": "middle_center", # 假設:通道通常在中間
"objects": list(set(pathway_items)),
"description": path_desc
}
return zones
except Exception as e:
logger.error(f"Error identifying Asian pedestrian pathway: {str(e)}")
logger.error(traceback.format_exc())
return {}
def _identify_vendor_zones(self, detected_objects: List[Dict]) -> Dict:
"""
識別攤販區域
Args:
detected_objects: 檢測到的物件列表
Returns:
攤販區域字典
"""
try:
zones = {}
# 識別攤販區域(小攤/商店 - 從情境推斷)
has_small_objects = any(obj["class_id"] in [24, 26, 39, 41] for obj in detected_objects) # bags, bottles, cups
has_people = any(obj["class_id"] == 0 for obj in detected_objects)
if has_small_objects and has_people:
# 可能的攤販區域是人群和小物件聚集的地方
small_obj_regions = {}
for obj in detected_objects:
if obj["class_id"] in [24, 26, 39, 41, 67]: # bags, bottles, cups, phones
region = obj["region"]
if region not in small_obj_regions:
small_obj_regions[region] = []
small_obj_regions[region].append(obj)
if small_obj_regions:
main_vendor_region = max(small_obj_regions.items(),
key=lambda x: len(x[1]),
default=(None, []))
if main_vendor_region[0] is not None:
vendor_items = [obj["class_name"] for obj in main_vendor_region[1]]
zones["vendor_zone"] = {
"region": main_vendor_region[0],
"objects": list(set(vendor_items)),
"description": "Vendor or market stall area with small merchandise"
}
return zones
except Exception as e:
logger.error(f"Error identifying vendor zones: {str(e)}")
logger.error(traceback.format_exc())
return {}
def _identify_upscale_decorative_zones(self, detected_objects: List[Dict]) -> Dict:
"""
識別高級餐飲的裝飾區域
Args:
detected_objects: 檢測到的物件列表
Returns:
裝飾區域字典
"""
try:
zones = {}
decor_items = []
decor_regions = {}
# 尋找裝飾元素(花瓶、酒杯、未使用的餐具)
for obj in detected_objects:
if obj["class_id"] in [75, 40]: # Vase, wine glass
region = obj["region"]
if region not in decor_regions:
decor_regions[region] = []
decor_regions[region].append(obj)
decor_items.append(obj["class_name"])
if decor_items:
main_decor_region = max(decor_regions.items(),
key=lambda x: len(x[1]),
default=(None, []))
if main_decor_region[0] is not None:
zones["decorative_zone"] = {
"region": main_decor_region[0],
"objects": list(set(decor_items)),
"description": f"Decorative area with {', '.join(list(set(decor_items)))}"
}
return zones
except Exception as e:
logger.error(f"Error identifying upscale decorative zones: {str(e)}")
logger.error(traceback.format_exc())
return {}
def _identify_dining_seating_zones(self, detected_objects: List[Dict]) -> Dict:
"""
識別餐廳座位安排區域
Args:
detected_objects: 檢測到的物件列表
Returns:
座位區域字典
"""
try:
zones = {}
# 識別座位安排區域
chairs = [obj for obj in detected_objects if obj["class_id"] == 56] # chairs
if len(chairs) >= 2:
chair_regions = {}
for obj in chairs:
region = obj["region"]
if region not in chair_regions:
chair_regions[region] = []
chair_regions[region].append(obj)
if chair_regions:
main_seating_region = max(chair_regions.items(),
key=lambda x: len(x[1]),
default=(None, []))
if main_seating_region[0] is not None:
zones["dining_seating_zone"] = {
"region": main_seating_region[0],
"objects": ["chair"] * len(main_seating_region[1]),
"description": f"Formal dining seating arrangement with {len(main_seating_region[1])} chairs"
}
return zones
except Exception as e:
logger.error(f"Error identifying dining seating zones: {str(e)}")
logger.error(traceback.format_exc())
return {}
def _identify_serving_zones(self, detected_objects: List[Dict], existing_zones: Dict) -> Dict:
"""
識別服務區域
Args:
detected_objects: 檢測到的物件列表
existing_zones: 已存在的功能區域
Returns:
服務區域字典
"""
try:
zones = {}
serving_items = []
serving_regions = {}
# 服務區域可能有瓶子、碗、容器
for obj in detected_objects:
if obj["class_id"] in [39, 45]: # Bottle, bowl
# 檢查是否在與主餐桌不同的區域
if "formal_dining_zone" in existing_zones and obj["region"] != existing_zones["formal_dining_zone"]["region"]:
region = obj["region"]
if region not in serving_regions:
serving_regions[region] = []
serving_regions[region].append(obj)
serving_items.append(obj["class_name"])
if serving_items:
main_serving_region = max(serving_regions.items(),
key=lambda x: len(x[1]),
default=(None, []))
if main_serving_region[0] is not None:
zones["serving_zone"] = {
"region": main_serving_region[0],
"objects": list(set(serving_items)),
"description": f"Serving or sideboard area with {', '.join(list(set(serving_items)))}"
}
return zones
except Exception as e:
logger.error(f"Error identifying serving zones: {str(e)}")
logger.error(traceback.format_exc())
return {}
def _identify_building_zones(self, detected_objects: List[Dict]) -> Dict:
"""
識別建築區域(從場景情境推斷)
Args:
detected_objects: 檢測到的物件列表
Returns:
建築區域字典
"""
try:
zones = {}
# 側邊建築區域(從場景情境推斷)
# 檢查是否有實際可能包含建築物的區域
left_side_regions = ["top_left", "middle_left", "bottom_left"]
right_side_regions = ["top_right", "middle_right", "bottom_right"]
# 檢查左側
left_building_evidence = True
for region in left_side_regions:
# 如果此區域有很多車輛或人群,不太可能是建築物
vehicle_in_region = any(obj["region"] == region and obj["class_id"] in [1, 2, 3, 5, 7]
for obj in detected_objects)
people_in_region = any(obj["region"] == region and obj["class_id"] == 0
for obj in detected_objects)
if vehicle_in_region or people_in_region:
left_building_evidence = False
break
# 檢查右側
right_building_evidence = True
for region in right_side_regions:
# 如果此區域有很多車輛或人群,不太可能是建築物
vehicle_in_region = any(obj["region"] == region and obj["class_id"] in [1, 2, 3, 5, 7]
for obj in detected_objects)
people_in_region = any(obj["region"] == region and obj["class_id"] == 0
for obj in detected_objects)
if vehicle_in_region or people_in_region:
right_building_evidence = False
break
# 如果證據支持,添加建築區域
if left_building_evidence:
zones["building_zone_left"] = {
"region": "middle_left",
"objects": ["building"], # 推斷
"description": "Tall buildings line the left side of the street"
}
if right_building_evidence:
zones["building_zone_right"] = {
"region": "middle_right",
"objects": ["building"], # 推斷
"description": "Tall buildings line the right side of the street"
}
return zones
except Exception as e:
logger.error(f"Error identifying building zones: {str(e)}")
logger.error(traceback.format_exc())
return {}
def _identify_financial_pedestrian_zones(self, detected_objects: List[Dict]) -> Dict:
"""
識別金融區的行人區域
Args:
detected_objects: 檢測到的物件列表
Returns:
行人區域字典
"""
try:
zones = {}
# 識別行人區域(如果有人群)
people_objs = [obj for obj in detected_objects if obj["class_id"] == 0]
if people_objs:
people_regions = {}
for obj in people_objs:
region = obj["region"]
if region not in people_regions:
people_regions[region] = []
people_regions[region].append(obj)
if people_regions:
main_pedestrian_region = max(people_regions.items(),
key=lambda x: len(x[1]),
default=(None, []))
if main_pedestrian_region[0] is not None:
zones["pedestrian_zone"] = {
"region": main_pedestrian_region[0],
"objects": ["person"] * len(main_pedestrian_region[1]),
"description": f"Pedestrian area with {len(main_pedestrian_region[1])} people navigating the financial district"
}
return zones
except Exception as e:
logger.error(f"Error identifying financial pedestrian zones: {str(e)}")
logger.error(traceback.format_exc())
return {}
def _create_landmark_auxiliary_zones(self, landmark: Dict, index: int) -> Dict:
"""
創建地標相關的輔助區域(攝影區、紀念品區等)
Args:
landmark: 地標物件字典
index: 地標索引
Returns:
輔助區域字典
"""
try:
auxiliary_zones = {}
landmark_region = landmark.get("region", "middle_center")
landmark_name = landmark.get("class_name", "Landmark")
# 創建攝影區
# 根據地標位置調整攝影區位置(地標前方通常是攝影區)
region_mapping = {
"top_left": "bottom_right",
"top_center": "bottom_center",
"top_right": "bottom_left",
"middle_left": "middle_right",
"middle_center": "bottom_center",
"middle_right": "middle_left",
"bottom_left": "top_right",
"bottom_center": "top_center",
"bottom_right": "top_left"
}
photo_region = region_mapping.get(landmark_region, landmark_region)
photo_key = f"{landmark_name.lower().replace(' ', '_')}_photography_spot"
auxiliary_zones[photo_key] = {
"name": f"{landmark_name} Photography Spot",
"description": f"Popular position for photographing {landmark_name} with optimal viewing angle.",
"objects": ["camera", "person", "cell phone"],
"region": photo_region,
"primary_function": "Tourist photography"
}
# 如果是著名地標,可能有紀念品販售區
if landmark.get("confidence", 0) > 0.7: # 高置信度地標更可能有紀念品區
# 根據地標位置找到適合的紀念品區位置(通常在地標附近但不直接在地標上)
adjacent_regions = {
"top_left": ["top_center", "middle_left"],
"top_center": ["top_left", "top_right"],
"top_right": ["top_center", "middle_right"],
"middle_left": ["top_left", "bottom_left"],
"middle_center": ["middle_left", "middle_right"],
"middle_right": ["top_right", "bottom_right"],
"bottom_left": ["middle_left", "bottom_center"],
"bottom_center": ["bottom_left", "bottom_right"],
"bottom_right": ["bottom_center", "middle_right"]
}
if landmark_region in adjacent_regions:
souvenir_region = adjacent_regions[landmark_region][0] # 選擇第一個相鄰區域
souvenir_key = f"{landmark_name.lower().replace(' ', '_')}_souvenir_area"
auxiliary_zones[souvenir_key] = {
"name": f"{landmark_name} Souvenir Area",
"description": f"Area where visitors can purchase souvenirs and memorabilia related to {landmark_name}.",
"objects": ["person", "handbag", "backpack"],
"region": souvenir_region,
"primary_function": "Tourism commerce"
}
return auxiliary_zones
except Exception as e:
logger.error(f"Error creating landmark auxiliary zones: {str(e)}")
logger.error(traceback.format_exc())
return {}