Spaces:
Running
on
Zero
Running
on
Zero
Upload 5 files
Browse files- content_generator.py +25 -147
- functional_zone_detector.py +6 -45
- functional_zone_identifier.py +27 -64
- scene_zone_identifier.py +6 -5
- template_processor.py +1 -58
content_generator.py
CHANGED
@@ -15,7 +15,7 @@ class ContentGenerator:
|
|
15 |
"""初始化內容生成器"""
|
16 |
self.logger = logging.getLogger(self.__class__.__name__)
|
17 |
|
18 |
-
# 預載入默認替換內容
|
19 |
self.default_replacements = self._generate_default_replacements()
|
20 |
|
21 |
self.logger.debug("ContentGenerator initialized successfully")
|
@@ -238,7 +238,7 @@ class ContentGenerator:
|
|
238 |
if not detected_objects:
|
239 |
return "various elements"
|
240 |
|
241 |
-
# 計算物件統計
|
242 |
object_counts = {}
|
243 |
total_confidence = 0
|
244 |
|
@@ -277,40 +277,21 @@ class ContentGenerator:
|
|
277 |
else:
|
278 |
descriptions.append(f"{count} {clean_name}s")
|
279 |
|
280 |
-
#
|
281 |
-
|
282 |
-
|
283 |
-
|
284 |
-
|
285 |
-
if not valid_descriptions:
|
286 |
-
return "various elements"
|
287 |
-
|
288 |
-
# 組合描述 - 修正邏輯以避免不完整的結尾
|
289 |
-
if len(valid_descriptions) == 1:
|
290 |
-
return valid_descriptions[0]
|
291 |
-
elif len(valid_descriptions) == 2:
|
292 |
-
return f"{valid_descriptions[0]} and {valid_descriptions[1]}"
|
293 |
else:
|
294 |
-
|
295 |
-
main_items = ", ".join(valid_descriptions[:-1])
|
296 |
-
last_item = valid_descriptions[-1]
|
297 |
-
# 確保 main_items 和 last_item 都不為空
|
298 |
-
if main_items and last_item:
|
299 |
-
return f"{main_items}, and {last_item}"
|
300 |
-
elif main_items:
|
301 |
-
return main_items
|
302 |
-
elif last_item:
|
303 |
-
return last_item
|
304 |
-
else:
|
305 |
-
return "various elements"
|
306 |
|
307 |
except Exception as e:
|
308 |
self.logger.warning(f"Error generating objects summary: {str(e)}")
|
309 |
return "various elements"
|
310 |
|
311 |
def get_placeholder_replacement(self, placeholder: str, fillers: Dict,
|
312 |
-
|
313 |
-
|
314 |
"""
|
315 |
獲取特定佔位符的替換內容,確保永遠不返回空值
|
316 |
|
@@ -324,36 +305,18 @@ class ContentGenerator:
|
|
324 |
Returns:
|
325 |
str: 替換內容
|
326 |
"""
|
327 |
-
try:
|
328 |
-
#
|
329 |
-
|
330 |
-
'primary_objects'
|
331 |
-
'
|
332 |
-
|
333 |
-
|
334 |
-
|
335 |
-
|
336 |
-
|
337 |
-
|
338 |
-
|
339 |
-
content_type = dynamic_placeholders_mapping[placeholder]
|
340 |
-
|
341 |
-
# 根據內容類型和當前檢測物件生成不同的描述
|
342 |
-
if content_type == 'full_summary':
|
343 |
-
return self.generate_objects_summary(detected_objects)
|
344 |
-
elif content_type == 'simple_summary':
|
345 |
-
# 避免重複敘述
|
346 |
-
return self._generate_simplified_objects_summary(detected_objects)
|
347 |
-
elif content_type == 'area_focus':
|
348 |
-
# 以圖片中的area 作為重點描述
|
349 |
-
return self._generate_area_focused_summary(detected_objects)
|
350 |
-
elif content_type == 'zones_focus':
|
351 |
-
# 以圖片中的zones 作為重點描述
|
352 |
-
return self._generate_zones_summary(detected_objects)
|
353 |
-
elif content_type == 'elements_focus':
|
354 |
-
# 以圖片中物品作為重點描述
|
355 |
-
return self._generate_elements_summary(detected_objects)
|
356 |
-
|
357 |
# 檢查預定義替換內容
|
358 |
if placeholder in all_replacements:
|
359 |
replacement = all_replacements[placeholder]
|
@@ -383,7 +346,7 @@ class ContentGenerator:
|
|
383 |
if scene_specific_replacement and scene_specific_replacement.strip():
|
384 |
return scene_specific_replacement.strip()
|
385 |
|
386 |
-
# 通用備用字典
|
387 |
fallback_replacements = {
|
388 |
# 交通和城市相關
|
389 |
"crossing_pattern": "pedestrian crosswalks",
|
@@ -442,7 +405,7 @@ class ContentGenerator:
|
|
442 |
# 最終備用:將下劃線轉換為有意義的短語
|
443 |
cleaned_placeholder = placeholder.replace('_', ' ')
|
444 |
|
445 |
-
#
|
446 |
if placeholder.endswith('_pattern'):
|
447 |
return f"{cleaned_placeholder.replace(' pattern', '')} arrangement"
|
448 |
elif placeholder.endswith('_behavior'):
|
@@ -458,94 +421,9 @@ class ContentGenerator:
|
|
458 |
|
459 |
except Exception as e:
|
460 |
self.logger.warning(f"Error getting replacement for placeholder '{placeholder}': {str(e)}")
|
|
|
461 |
return placeholder.replace('_', ' ') if placeholder else "scene elements"
|
462 |
|
463 |
-
def _generate_simplified_objects_summary(self, detected_objects: List[Dict]) -> str:
|
464 |
-
"""生成簡化的物件摘要,避免與詳細摘要重複"""
|
465 |
-
try:
|
466 |
-
if not detected_objects:
|
467 |
-
return "scene elements"
|
468 |
-
|
469 |
-
# 只取最重要的前3個物件
|
470 |
-
object_counts = {}
|
471 |
-
for obj in detected_objects:
|
472 |
-
class_name = obj.get("class_name", "unknown")
|
473 |
-
confidence = obj.get("confidence", 0.5)
|
474 |
-
|
475 |
-
if class_name not in object_counts:
|
476 |
-
object_counts[class_name] = {"count": 0, "total_confidence": 0}
|
477 |
-
|
478 |
-
object_counts[class_name]["count"] += 1
|
479 |
-
object_counts[class_name]["total_confidence"] += confidence
|
480 |
-
|
481 |
-
# 排序並取前3個
|
482 |
-
sorted_objects = []
|
483 |
-
for class_name, stats in object_counts.items():
|
484 |
-
count = stats["count"]
|
485 |
-
avg_confidence = stats["total_confidence"] / count
|
486 |
-
importance = count * 0.6 + avg_confidence * 0.4
|
487 |
-
sorted_objects.append((class_name, count, importance))
|
488 |
-
|
489 |
-
sorted_objects.sort(key=lambda x: x[2], reverse=True)
|
490 |
-
top_objects = sorted_objects[:3]
|
491 |
-
|
492 |
-
if top_objects:
|
493 |
-
primary_object = top_objects[0]
|
494 |
-
clean_name = primary_object[0].replace('_', ' ')
|
495 |
-
count = primary_object[1]
|
496 |
-
|
497 |
-
if count == 1:
|
498 |
-
article = "an" if clean_name[0].lower() in 'aeiou' else "a"
|
499 |
-
return f"{article} {clean_name}"
|
500 |
-
else:
|
501 |
-
return f"{count} {clean_name}s"
|
502 |
-
|
503 |
-
return "scene elements"
|
504 |
-
|
505 |
-
except Exception as e:
|
506 |
-
self.logger.warning(f"Error generating simplified summary: {str(e)}")
|
507 |
-
return "scene elements"
|
508 |
-
|
509 |
-
def _generate_area_focused_summary(self, detected_objects: List[Dict]) -> str:
|
510 |
-
"""生成區域導向的摘要"""
|
511 |
-
try:
|
512 |
-
# 根據檢測到的物件推斷主要功能區域
|
513 |
-
furniture_objects = [obj for obj in detected_objects if obj.get("class_name") in ["chair", "dining table", "sofa", "bed"]]
|
514 |
-
|
515 |
-
if any(obj.get("class_name") == "dining table" for obj in furniture_objects):
|
516 |
-
return "dining area"
|
517 |
-
elif any(obj.get("class_name") == "sofa" for obj in furniture_objects):
|
518 |
-
return "seating area"
|
519 |
-
elif any(obj.get("class_name") == "bed" for obj in furniture_objects):
|
520 |
-
return "sleeping area"
|
521 |
-
elif furniture_objects:
|
522 |
-
return "furnished area"
|
523 |
-
else:
|
524 |
-
return "activity area"
|
525 |
-
|
526 |
-
except Exception as e:
|
527 |
-
self.logger.warning(f"Error generating area-focused summary: {str(e)}")
|
528 |
-
return "functional area"
|
529 |
-
|
530 |
-
def _generate_zones_summary(self, detected_objects: List[Dict]) -> str:
|
531 |
-
"""生成區域描述摘要"""
|
532 |
-
try:
|
533 |
-
return "organized areas of activity"
|
534 |
-
except Exception as e:
|
535 |
-
return "functional zones"
|
536 |
-
|
537 |
-
def _generate_elements_summary(self, detected_objects: List[Dict]) -> str:
|
538 |
-
"""生成元素導向的摘要"""
|
539 |
-
try:
|
540 |
-
if len(detected_objects) > 5:
|
541 |
-
return "diverse elements"
|
542 |
-
elif len(detected_objects) > 2:
|
543 |
-
return "multiple elements"
|
544 |
-
else:
|
545 |
-
return "key elements"
|
546 |
-
except Exception as e:
|
547 |
-
return "scene elements"
|
548 |
-
|
549 |
def get_scene_based_default(self, placeholder: str, scene_type: str) -> Optional[str]:
|
550 |
"""
|
551 |
基於場景類型提供智能默認值
|
|
|
15 |
"""初始化內容生成器"""
|
16 |
self.logger = logging.getLogger(self.__class__.__name__)
|
17 |
|
18 |
+
# 預載入默認替換內容
|
19 |
self.default_replacements = self._generate_default_replacements()
|
20 |
|
21 |
self.logger.debug("ContentGenerator initialized successfully")
|
|
|
238 |
if not detected_objects:
|
239 |
return "various elements"
|
240 |
|
241 |
+
# 計算物件統計
|
242 |
object_counts = {}
|
243 |
total_confidence = 0
|
244 |
|
|
|
277 |
else:
|
278 |
descriptions.append(f"{count} {clean_name}s")
|
279 |
|
280 |
+
# 組合描述
|
281 |
+
if len(descriptions) == 1:
|
282 |
+
return descriptions[0]
|
283 |
+
elif len(descriptions) == 2:
|
284 |
+
return f"{descriptions[0]} and {descriptions[1]}"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
285 |
else:
|
286 |
+
return ", ".join(descriptions[:-1]) + f", and {descriptions[-1]}"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
287 |
|
288 |
except Exception as e:
|
289 |
self.logger.warning(f"Error generating objects summary: {str(e)}")
|
290 |
return "various elements"
|
291 |
|
292 |
def get_placeholder_replacement(self, placeholder: str, fillers: Dict,
|
293 |
+
all_replacements: Dict, detected_objects: List[Dict],
|
294 |
+
scene_type: str) -> str:
|
295 |
"""
|
296 |
獲取特定佔位符的替換內容,確保永遠不返回空值
|
297 |
|
|
|
305 |
Returns:
|
306 |
str: 替換內容
|
307 |
"""
|
308 |
+
try:
|
309 |
+
# 優先處理動態內容生成的佔位符
|
310 |
+
dynamic_placeholders = [
|
311 |
+
'primary_objects', 'detected_objects_summary', 'main_objects',
|
312 |
+
'functional_area', 'functional_zones_description', 'scene_elements'
|
313 |
+
]
|
314 |
+
|
315 |
+
if placeholder in dynamic_placeholders:
|
316 |
+
dynamic_content = self.generate_objects_summary(detected_objects)
|
317 |
+
if dynamic_content and dynamic_content.strip():
|
318 |
+
return dynamic_content.strip()
|
319 |
+
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
320 |
# 檢查預定義替換內容
|
321 |
if placeholder in all_replacements:
|
322 |
replacement = all_replacements[placeholder]
|
|
|
346 |
if scene_specific_replacement and scene_specific_replacement.strip():
|
347 |
return scene_specific_replacement.strip()
|
348 |
|
349 |
+
# 通用備用字典
|
350 |
fallback_replacements = {
|
351 |
# 交通和城市相關
|
352 |
"crossing_pattern": "pedestrian crosswalks",
|
|
|
405 |
# 最終備用:將下劃線轉換為有意義的短語
|
406 |
cleaned_placeholder = placeholder.replace('_', ' ')
|
407 |
|
408 |
+
# 對常見模式提供更好的默認值
|
409 |
if placeholder.endswith('_pattern'):
|
410 |
return f"{cleaned_placeholder.replace(' pattern', '')} arrangement"
|
411 |
elif placeholder.endswith('_behavior'):
|
|
|
421 |
|
422 |
except Exception as e:
|
423 |
self.logger.warning(f"Error getting replacement for placeholder '{placeholder}': {str(e)}")
|
424 |
+
# 確保即使在異常情況下也返回有意義的內容
|
425 |
return placeholder.replace('_', ' ') if placeholder else "scene elements"
|
426 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
427 |
def get_scene_based_default(self, placeholder: str, scene_type: str) -> Optional[str]:
|
428 |
"""
|
429 |
基於場景類型提供智能默認值
|
functional_zone_detector.py
CHANGED
@@ -230,14 +230,7 @@ class FunctionalZoneDetector:
|
|
230 |
region = zone_data.get("region", "")
|
231 |
description = zone_data.get("description", "")
|
232 |
|
233 |
-
#
|
234 |
-
kitchen_objects = ["refrigerator", "microwave", "oven", "sink", "dishwasher", "stove"]
|
235 |
-
explicit_kitchen_detected = any(
|
236 |
-
any(kitchen_item in obj.lower() for kitchen_item in kitchen_objects)
|
237 |
-
for obj in objects
|
238 |
-
)
|
239 |
-
|
240 |
-
# 基於物件內容確定功能類型(保持原有順序,但加強廚房確認, 因為與dining room混淆)
|
241 |
if any("dining" in obj.lower() or "table" in obj.lower() for obj in objects):
|
242 |
base_name = "dining area"
|
243 |
elif any("chair" in obj.lower() or "sofa" in obj.lower() for obj in objects):
|
@@ -248,52 +241,20 @@ class FunctionalZoneDetector:
|
|
248 |
base_name = "workspace area"
|
249 |
elif any("plant" in obj.lower() or "vase" in obj.lower() for obj in objects):
|
250 |
base_name = "decorative area"
|
251 |
-
elif
|
252 |
-
# 只有在明確檢測到廚房設備時才使用 kitchen area
|
253 |
base_name = "kitchen area"
|
254 |
else:
|
255 |
-
#
|
256 |
-
if "dining" in description.lower()
|
257 |
-
# 只有當描述中提到 dining 且確實有桌子時才使用 dining area
|
258 |
base_name = "dining area"
|
259 |
elif "seating" in description.lower() or "relaxation" in description.lower():
|
260 |
base_name = "seating area"
|
261 |
-
elif "work" in description.lower()
|
262 |
-
# 只有當描述中提到 work 且確實有工作設備時才使用 workspace area
|
263 |
base_name = "workspace area"
|
264 |
elif "decorative" in description.lower():
|
265 |
base_name = "decorative area"
|
266 |
else:
|
267 |
-
|
268 |
-
if objects:
|
269 |
-
# 根據最常見的物件類型決定區域名稱
|
270 |
-
object_counts = {}
|
271 |
-
for obj in objects:
|
272 |
-
obj_lower = obj.lower()
|
273 |
-
if "chair" in obj_lower:
|
274 |
-
object_counts["seating"] = object_counts.get("seating", 0) + 1
|
275 |
-
elif "table" in obj_lower:
|
276 |
-
object_counts["dining"] = object_counts.get("dining", 0) + 1
|
277 |
-
elif "person" in obj_lower:
|
278 |
-
object_counts["activity"] = object_counts.get("activity", 0) + 1
|
279 |
-
else:
|
280 |
-
object_counts["general"] = object_counts.get("general", 0) + 1
|
281 |
-
|
282 |
-
# 選擇最常見的類型
|
283 |
-
if object_counts:
|
284 |
-
most_common = max(object_counts, key=object_counts.get)
|
285 |
-
if most_common == "seating":
|
286 |
-
base_name = "seating area"
|
287 |
-
elif most_common == "dining":
|
288 |
-
base_name = "dining area"
|
289 |
-
elif most_common == "activity":
|
290 |
-
base_name = "activity area"
|
291 |
-
else:
|
292 |
-
base_name = "functional area"
|
293 |
-
else:
|
294 |
-
base_name = "functional area"
|
295 |
-
else:
|
296 |
-
base_name = "functional area"
|
297 |
|
298 |
# 為次要區域添加位置標識以區分
|
299 |
if priority_level == "secondary" and region:
|
|
|
230 |
region = zone_data.get("region", "")
|
231 |
description = zone_data.get("description", "")
|
232 |
|
233 |
+
# 基於物件內容確定功能類型
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
234 |
if any("dining" in obj.lower() or "table" in obj.lower() for obj in objects):
|
235 |
base_name = "dining area"
|
236 |
elif any("chair" in obj.lower() or "sofa" in obj.lower() for obj in objects):
|
|
|
241 |
base_name = "workspace area"
|
242 |
elif any("plant" in obj.lower() or "vase" in obj.lower() for obj in objects):
|
243 |
base_name = "decorative area"
|
244 |
+
elif any("refrigerator" in obj.lower() or "microwave" in obj.lower() for obj in objects):
|
|
|
245 |
base_name = "kitchen area"
|
246 |
else:
|
247 |
+
# 基於描述內容推斷
|
248 |
+
if "dining" in description.lower():
|
|
|
249 |
base_name = "dining area"
|
250 |
elif "seating" in description.lower() or "relaxation" in description.lower():
|
251 |
base_name = "seating area"
|
252 |
+
elif "work" in description.lower():
|
|
|
253 |
base_name = "workspace area"
|
254 |
elif "decorative" in description.lower():
|
255 |
base_name = "decorative area"
|
256 |
else:
|
257 |
+
base_name = "functional area"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
258 |
|
259 |
# 為次要區域添加位置標識以區分
|
260 |
if priority_level == "secondary" and region:
|
functional_zone_identifier.py
CHANGED
@@ -688,7 +688,7 @@ class FunctionalZoneIdentifier:
|
|
688 |
if not high_conf_objects:
|
689 |
high_conf_objects = detected_objects # 後備到所有物件
|
690 |
|
691 |
-
#
|
692 |
processed_objects = set() # 避免重複處理相同類型的物件
|
693 |
|
694 |
for obj in high_conf_objects[:3]: # 限制為前3個物件
|
@@ -788,6 +788,7 @@ class FunctionalZoneIdentifier:
|
|
788 |
區域描述字串
|
789 |
"""
|
790 |
try:
|
|
|
791 |
descriptions = {
|
792 |
"bed": "Sleeping and rest area",
|
793 |
"sofa": "Seating and relaxation area",
|
@@ -796,42 +797,10 @@ class FunctionalZoneIdentifier:
|
|
796 |
"tv": "Entertainment and media area",
|
797 |
"laptop": "Work and computing area",
|
798 |
"potted plant": "Decorative and green space area",
|
|
|
799 |
"car": "Vehicle and transportation area",
|
800 |
"person": "Activity and social area"
|
801 |
}
|
802 |
-
|
803 |
-
# 只有在明確的廚房場景中才使用廚房描述
|
804 |
-
kitchen_related_objects = ["refrigerator", "microwave", "oven", "sink", "dishwasher", "stove"]
|
805 |
-
|
806 |
-
if class_name in kitchen_related_objects:
|
807 |
-
# 檢查場景類型是否真的是廚房相關
|
808 |
-
kitchen_scene_types = ["kitchen", "professional_kitchen", "cooking_area"]
|
809 |
-
|
810 |
-
if scene_type in kitchen_scene_types:
|
811 |
-
# 只有在明確的廚房場景中才使用廚房描述
|
812 |
-
if class_name == "refrigerator":
|
813 |
-
descriptions[class_name] = "Food storage and kitchen area"
|
814 |
-
elif class_name == "microwave":
|
815 |
-
descriptions[class_name] = "Food preparation area"
|
816 |
-
elif class_name == "oven":
|
817 |
-
descriptions[class_name] = "Cooking area"
|
818 |
-
elif class_name == "sink":
|
819 |
-
descriptions[class_name] = "Washing and preparation area"
|
820 |
-
else:
|
821 |
-
descriptions[class_name] = "Kitchen appliance area"
|
822 |
-
else:
|
823 |
-
# === 修正:非廚房場景中的廚房物件使用中性描述 ===
|
824 |
-
# 在餐廳、客廳等場景中,即使檢測到這些物件也不使用廚房描述
|
825 |
-
if class_name == "refrigerator":
|
826 |
-
descriptions[class_name] = "Storage area"
|
827 |
-
elif class_name == "microwave":
|
828 |
-
descriptions[class_name] = "Appliance area"
|
829 |
-
elif class_name == "oven":
|
830 |
-
descriptions[class_name] = "Equipment area"
|
831 |
-
elif class_name == "sink":
|
832 |
-
descriptions[class_name] = "Utility area"
|
833 |
-
else:
|
834 |
-
descriptions[class_name] = "Equipment area"
|
835 |
|
836 |
return descriptions.get(class_name, f"Functional area with {class_name}")
|
837 |
|
@@ -930,36 +899,30 @@ class FunctionalZoneIdentifier:
|
|
930 |
"surfboard": "sports area",
|
931 |
"tennis racket": "sports area",
|
932 |
|
933 |
-
#
|
934 |
-
"bottle": "
|
935 |
-
"wine glass": "
|
936 |
-
"cup": "
|
937 |
-
"fork": "
|
938 |
-
"knife": "
|
939 |
-
"spoon": "
|
940 |
-
"bowl": "
|
941 |
-
"
|
942 |
-
|
943 |
-
|
944 |
-
"
|
945 |
-
"
|
946 |
-
"
|
947 |
-
"
|
948 |
-
"
|
949 |
-
"
|
950 |
-
"
|
951 |
-
"
|
952 |
-
"
|
953 |
-
"
|
954 |
-
|
955 |
-
|
956 |
-
"
|
957 |
-
"oven": "kitchen appliance area",
|
958 |
-
"microwave": "kitchen appliance area",
|
959 |
-
"toaster": "kitchen appliance area",
|
960 |
-
"sink": "kitchen appliance area",
|
961 |
-
|
962 |
-
# 其他物品
|
963 |
"book": "miscellaneous area",
|
964 |
"clock": "miscellaneous area",
|
965 |
"vase": "decorative area",
|
|
|
688 |
if not high_conf_objects:
|
689 |
high_conf_objects = detected_objects # 後備到所有物件
|
690 |
|
691 |
+
# 基於個別重要物件創建區域
|
692 |
processed_objects = set() # 避免重複處理相同類型的物件
|
693 |
|
694 |
for obj in high_conf_objects[:3]: # 限制為前3個物件
|
|
|
788 |
區域描述字串
|
789 |
"""
|
790 |
try:
|
791 |
+
# 物件特定描述
|
792 |
descriptions = {
|
793 |
"bed": "Sleeping and rest area",
|
794 |
"sofa": "Seating and relaxation area",
|
|
|
797 |
"tv": "Entertainment and media area",
|
798 |
"laptop": "Work and computing area",
|
799 |
"potted plant": "Decorative and green space area",
|
800 |
+
"refrigerator": "Food storage and kitchen area",
|
801 |
"car": "Vehicle and transportation area",
|
802 |
"person": "Activity and social area"
|
803 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
804 |
|
805 |
return descriptions.get(class_name, f"Functional area with {class_name}")
|
806 |
|
|
|
899 |
"surfboard": "sports area",
|
900 |
"tennis racket": "sports area",
|
901 |
|
902 |
+
# 廚房與食品(Kitchen)
|
903 |
+
"bottle": "kitchen area",
|
904 |
+
"wine glass": "kitchen area",
|
905 |
+
"cup": "kitchen area",
|
906 |
+
"fork": "kitchen area",
|
907 |
+
"knife": "kitchen area",
|
908 |
+
"spoon": "kitchen area",
|
909 |
+
"bowl": "kitchen area",
|
910 |
+
"banana": "kitchen area",
|
911 |
+
"apple": "kitchen area",
|
912 |
+
"sandwich": "kitchen area",
|
913 |
+
"orange": "kitchen area",
|
914 |
+
"broccoli": "kitchen area",
|
915 |
+
"carrot": "kitchen area",
|
916 |
+
"hot dog": "kitchen area",
|
917 |
+
"pizza": "kitchen area",
|
918 |
+
"donut": "kitchen area",
|
919 |
+
"cake": "kitchen area",
|
920 |
+
"dining table": "furniture arrangement area",
|
921 |
+
"refrigerator": "kitchen area",
|
922 |
+
"oven": "kitchen area",
|
923 |
+
"microwave": "kitchen area",
|
924 |
+
"toaster": "kitchen area",
|
925 |
+
"sink": "kitchen area",
|
|
|
|
|
|
|
|
|
|
|
|
|
926 |
"book": "miscellaneous area",
|
927 |
"clock": "miscellaneous area",
|
928 |
"vase": "decorative area",
|
scene_zone_identifier.py
CHANGED
@@ -47,10 +47,10 @@ class SceneZoneIdentifier:
|
|
47 |
try:
|
48 |
zones = {}
|
49 |
|
50 |
-
#
|
51 |
primary_zone = self.functional_detector.identify_primary_functional_area(detected_objects)
|
52 |
if primary_zone:
|
53 |
-
#
|
54 |
descriptive_key = self.functional_detector.generate_descriptive_zone_key_from_data(primary_zone, "primary")
|
55 |
zones[descriptive_key] = primary_zone
|
56 |
|
@@ -116,7 +116,7 @@ class SceneZoneIdentifier:
|
|
116 |
"description": f"Pedestrian area with {len(objs)} {'people' if len(objs) > 1 else 'person'}"
|
117 |
}
|
118 |
|
119 |
-
#
|
120 |
vehicle_objs = [obj for obj in detected_objects if obj["class_id"] in [1, 2, 3, 5, 6, 7]]
|
121 |
if vehicle_objs:
|
122 |
vehicle_regions = {}
|
@@ -250,6 +250,7 @@ class SceneZoneIdentifier:
|
|
250 |
# 5. Step D: 分析車輛交通區域(Vehicle Zones)
|
251 |
if vehicle_objs:
|
252 |
traffic_zones = self.pattern_analyzer.analyze_traffic_zones(vehicle_objs)
|
|
|
253 |
for zone_key, zone_info in traffic_zones.items():
|
254 |
if zone_key in zones:
|
255 |
suffix = 1
|
@@ -354,7 +355,7 @@ class SceneZoneIdentifier:
|
|
354 |
try:
|
355 |
zones = {}
|
356 |
|
357 |
-
#
|
358 |
# 由於店面不能直接檢測,從情境推斷
|
359 |
# 例如,尋找有標誌、行人和小物件的區域
|
360 |
storefront_regions = {}
|
@@ -372,7 +373,7 @@ class SceneZoneIdentifier:
|
|
372 |
reverse=True)[:2] # 前2個區域
|
373 |
|
374 |
for idx, (region, objs) in enumerate(main_storefront_regions):
|
375 |
-
#
|
376 |
spatial_desc = self._get_directional_description(region)
|
377 |
if spatial_desc and spatial_desc != "central":
|
378 |
zone_key = f"{spatial_desc} commercial area"
|
|
|
47 |
try:
|
48 |
zones = {}
|
49 |
|
50 |
+
# 主要功能區域(基於物件關聯性而非場景類型)
|
51 |
primary_zone = self.functional_detector.identify_primary_functional_area(detected_objects)
|
52 |
if primary_zone:
|
53 |
+
# 基於區域內容生成描述性鍵名
|
54 |
descriptive_key = self.functional_detector.generate_descriptive_zone_key_from_data(primary_zone, "primary")
|
55 |
zones[descriptive_key] = primary_zone
|
56 |
|
|
|
116 |
"description": f"Pedestrian area with {len(objs)} {'people' if len(objs) > 1 else 'person'}"
|
117 |
}
|
118 |
|
119 |
+
# 識別車輛區域,適用於街道和停車場
|
120 |
vehicle_objs = [obj for obj in detected_objects if obj["class_id"] in [1, 2, 3, 5, 6, 7]]
|
121 |
if vehicle_objs:
|
122 |
vehicle_regions = {}
|
|
|
250 |
# 5. Step D: 分析車輛交通區域(Vehicle Zones)
|
251 |
if vehicle_objs:
|
252 |
traffic_zones = self.pattern_analyzer.analyze_traffic_zones(vehicle_objs)
|
253 |
+
# analyze_traffic_zones 內部已用英文 debug,直接更新
|
254 |
for zone_key, zone_info in traffic_zones.items():
|
255 |
if zone_key in zones:
|
256 |
suffix = 1
|
|
|
355 |
try:
|
356 |
zones = {}
|
357 |
|
358 |
+
# 識別店面區域
|
359 |
# 由於店面不能直接檢測,從情境推斷
|
360 |
# 例如,尋找有標誌、行人和小物件的區域
|
361 |
storefront_regions = {}
|
|
|
373 |
reverse=True)[:2] # 前2個區域
|
374 |
|
375 |
for idx, (region, objs) in enumerate(main_storefront_regions):
|
376 |
+
# 生成基於位置的描述性鍵名
|
377 |
spatial_desc = self._get_directional_description(region)
|
378 |
if spatial_desc and spatial_desc != "central":
|
379 |
zone_key = f"{spatial_desc} commercial area"
|
template_processor.py
CHANGED
@@ -50,7 +50,6 @@ class TemplateProcessor:
|
|
50 |
str: 修復後的模板字符串
|
51 |
"""
|
52 |
try:
|
53 |
-
# 原有的語法修復邏輯
|
54 |
# 修復 "In , " 模式
|
55 |
filled_template = re.sub(r'\bIn\s*,\s*', 'In this scene, ', filled_template)
|
56 |
filled_template = re.sub(r'\bAt\s*,\s*', 'At this location, ', filled_template)
|
@@ -61,63 +60,7 @@ class TemplateProcessor:
|
|
61 |
|
62 |
# 修復開頭的逗號
|
63 |
filled_template = re.sub(r'^[,\s]*', '', filled_template)
|
64 |
-
|
65 |
-
# 1. 修復不完整的 "and." 結尾問題
|
66 |
-
# 處理 "物件列表, and." 的模式,將其修正為完整的句子
|
67 |
-
filled_template = re.sub(r',\s*and\s*\.\s*', '. ', filled_template)
|
68 |
-
filled_template = re.sub(r'\s+and\s*\.\s*', '. ', filled_template)
|
69 |
-
|
70 |
-
# 2. 處理重複的物件列表模式
|
71 |
-
# 識別並移除重複的完整物件描述片段
|
72 |
-
# 針對 "數字 + 物件名稱" 的重複模式
|
73 |
-
object_pattern = r'(\b\d+\s+\w+(?:\s+\w+)*(?:,\s*\d+\s+\w+(?:\s+\w+)*)*(?:,\s*(?:a|an)\s+\w+(?:\s+\w+)*)*)'
|
74 |
-
|
75 |
-
# 找到所有物件列表片段
|
76 |
-
object_matches = re.findall(object_pattern, filled_template)
|
77 |
-
if object_matches:
|
78 |
-
# 移除重複的物件列表
|
79 |
-
seen_objects = set()
|
80 |
-
for obj_desc in object_matches:
|
81 |
-
# 標準化物件描述用於比較(移除多餘空格)
|
82 |
-
normalized_desc = re.sub(r'\s+', ' ', obj_desc.strip().lower())
|
83 |
-
if normalized_desc in seen_objects:
|
84 |
-
|
85 |
-
# 找到重複的物件描述,移除後續出現的實例
|
86 |
-
escaped_desc = re.escape(obj_desc)
|
87 |
-
pattern = r'\.\s*' + escaped_desc + r'(?=\s*\.|\s*$)'
|
88 |
-
filled_template = re.sub(pattern, '', filled_template, count=1)
|
89 |
-
else:
|
90 |
-
seen_objects.add(normalized_desc)
|
91 |
-
|
92 |
-
# 3. 處理重複的句子片段
|
93 |
-
# 將文本分割為句子,檢查是否有完整句子的重複
|
94 |
-
sentences = re.split(r'(?<=[.!?])\s+', filled_template)
|
95 |
-
unique_sentences = []
|
96 |
-
seen_sentences = set()
|
97 |
-
|
98 |
-
for sentence in sentences:
|
99 |
-
if sentence.strip(): # 忽略空句子
|
100 |
-
# 標準化句子用於比較(移除標點符號和多餘空格)
|
101 |
-
normalized_sentence = re.sub(r'[^\w\s]', '', sentence.lower().strip())
|
102 |
-
normalized_sentence = re.sub(r'\s+', ' ', normalized_sentence)
|
103 |
-
|
104 |
-
# 只有當句子足夠長且確實重複時才移除
|
105 |
-
if len(normalized_sentence) > 10 and normalized_sentence not in seen_sentences:
|
106 |
-
unique_sentences.append(sentence.strip())
|
107 |
-
seen_sentences.add(normalized_sentence)
|
108 |
-
elif len(normalized_sentence) <= 10:
|
109 |
-
# 短句子直接保留,避免過度清理
|
110 |
-
unique_sentences.append(sentence.strip())
|
111 |
-
|
112 |
-
# 重新組合句子
|
113 |
-
if unique_sentences:
|
114 |
-
filled_template = ' '.join(unique_sentences)
|
115 |
-
|
116 |
-
# 4. 清理可能產生的多餘空格和標點符號
|
117 |
-
filled_template = re.sub(r'\s+', ' ', filled_template)
|
118 |
-
filled_template = re.sub(r'\s*\.\s*\.\s*', '. ', filled_template) # 移除連續句號
|
119 |
-
filled_template = re.sub(r'\s*,\s*\.\s*', '. ', filled_template) # 修正 ", ."
|
120 |
-
|
121 |
# 確保首字母大寫
|
122 |
if filled_template and not filled_template[0].isupper():
|
123 |
filled_template = filled_template[0].upper() + filled_template[1:]
|
|
|
50 |
str: 修復後的模板字符串
|
51 |
"""
|
52 |
try:
|
|
|
53 |
# 修復 "In , " 模式
|
54 |
filled_template = re.sub(r'\bIn\s*,\s*', 'In this scene, ', filled_template)
|
55 |
filled_template = re.sub(r'\bAt\s*,\s*', 'At this location, ', filled_template)
|
|
|
60 |
|
61 |
# 修復開頭的逗號
|
62 |
filled_template = re.sub(r'^[,\s]*', '', filled_template)
|
63 |
+
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
64 |
# 確保首字母大寫
|
65 |
if filled_template and not filled_template[0].isupper():
|
66 |
filled_template = filled_template[0].upper() + filled_template[1:]
|