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