Spaces:
Running
on
Zero
Running
on
Zero
File size: 18,266 Bytes
e6a18b7 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 |
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
|