Spaces:
Running
on
Zero
Running
on
Zero
import os | |
import json | |
from typing import Dict, List, Tuple, Any, Optional | |
from scene_type import SCENE_TYPES | |
from scene_detail_templates import SCENE_DETAIL_TEMPLATES | |
from object_template_fillers import OBJECT_TEMPLATE_FILLERS | |
from activity_templates import ACTIVITY_TEMPLATES | |
from safety_templates import SAFETY_TEMPLATES | |
from confifence_templates import CONFIDENCE_TEMPLATES | |
class SceneDescriptor: | |
""" | |
Generates natural language descriptions of scenes. | |
Handles scene descriptions, activity inference, and safety concerns identification. | |
""" | |
def __init__(self, scene_types=None, object_categories=None): | |
""" | |
Initialize the scene descriptor | |
Args: | |
scene_types: Dictionary of scene type definitions | |
""" | |
self.scene_types = scene_types or {} | |
self.SCENE_TYPES = scene_types or {} | |
if object_categories: | |
self.OBJECT_CATEGORIES = object_categories | |
else: | |
# 從 JSON 加載或使用默認值 | |
self.OBJECT_CATEGORIES = self._load_json_data("object_categories") or { | |
"furniture": [56, 57, 58, 59, 60, 61], | |
"electronics": [62, 63, 64, 65, 66, 67, 68, 69, 70], | |
"kitchen_items": [39, 40, 41, 42, 43, 44, 45], | |
"food": [46, 47, 48, 49, 50, 51, 52, 53, 54, 55], | |
"vehicles": [1, 2, 3, 4, 5, 6, 7, 8], | |
"personal_items": [24, 25, 26, 27, 28, 73, 78, 79] | |
} | |
# 加載所有模板數據 | |
self._load_templates() | |
def _load_templates(self): | |
"""Load all template data from script or fallback to imported defaults""" | |
self.confidence_templates = CONFIDENCE_TEMPLATES | |
self.scene_detail_templates = SCENE_DETAIL_TEMPLATES | |
self.object_template_fillers = OBJECT_TEMPLATE_FILLERS | |
self.safety_templates = SAFETY_TEMPLATES | |
self.activity_templates = ACTIVITY_TEMPLATES | |
def _initialize_fallback_templates(self): | |
"""Initialize fallback templates when no external data is available""" | |
# 只在無法從文件或導入加載時使用 | |
self.confidence_templates = { | |
"high": "{description} {details}", | |
"medium": "This appears to be {description} {details}", | |
"low": "This might be {description}, but the confidence is low. {details}" | |
} | |
# 僅提供最基本的模板作為後備 | |
self.scene_detail_templates = { | |
"default": ["A space with various objects."] | |
} | |
self.object_template_fillers = { | |
"default": ["various items"] | |
} | |
self.safety_templates = { | |
"general": "Pay attention to {safety_element}." | |
} | |
self.activity_templates = { | |
"default": ["General activity"] | |
} | |
def _get_alternative_scenes(self, scene_scores: Dict[str, float], | |
threshold: float, top_k: int = 2) -> List[Dict]: | |
""" | |
Get alternative scene interpretations with their scores. | |
Args: | |
scene_scores: Dictionary of scene type scores | |
threshold: Minimum confidence threshold | |
top_k: Number of alternatives to return | |
Returns: | |
List of dictionaries with alternative scenes | |
""" | |
# Sort scenes by score in descending order | |
sorted_scenes = sorted(scene_scores.items(), key=lambda x: x[1], reverse=True) | |
# Skip the first one (best match) and take the next top_k | |
alternatives = [] | |
for scene_type, score in sorted_scenes[1:1+top_k]: | |
if score >= threshold: | |
alternatives.append({ | |
"type": scene_type, | |
"name": self.SCENE_TYPES.get(scene_type, {}).get("name", "Unknown"), | |
"confidence": score | |
}) | |
return alternatives | |
def _infer_possible_activities(self, scene_type: str, detected_objects: List[Dict]) -> List[str]: | |
""" | |
Infer possible activities based on scene type and detected objects. | |
Args: | |
scene_type: Identified scene type | |
detected_objects: List of detected objects | |
Returns: | |
List of possible activities | |
""" | |
activities = [] | |
if scene_type.startswith("aerial_view_"): | |
if scene_type == "aerial_view_intersection": | |
# 使用預定義的十字路口活動 | |
activities.extend(self.activity_templates.get("aerial_view_intersection", [])) | |
# 添加與行人和車輛相關的特定活動 | |
pedestrians = [obj for obj in detected_objects if obj["class_id"] == 0] | |
vehicles = [obj for obj in detected_objects if obj["class_id"] in [2, 5, 7]] # Car, bus, truck | |
if pedestrians and vehicles: | |
activities.append("Waiting for an opportunity to cross the street") | |
activities.append("Obeying traffic signals") | |
elif scene_type == "aerial_view_commercial_area": | |
activities.extend(self.activity_templates.get("aerial_view_commercial_area", [])) | |
elif scene_type == "aerial_view_plaza": | |
activities.extend(self.activity_templates.get("aerial_view_plaza", [])) | |
else: | |
# 處理其他未明確定義的空中視角場景 | |
aerial_activities = [ | |
"Street crossing", | |
"Waiting for signals", | |
"Following traffic rules", | |
"Pedestrian movement" | |
] | |
activities.extend(aerial_activities) | |
if scene_type in self.activity_templates: | |
activities.extend(self.activity_templates[scene_type]) | |
elif "default" in self.activity_templates: | |
activities.extend(self.activity_templates["default"]) | |
detected_class_ids = [obj["class_id"] for obj in detected_objects] | |
# Add activities based on specific object combinations | |
if 62 in detected_class_ids and 57 in detected_class_ids: # TV and sofa | |
activities.append("Watching shows or movies") | |
if 63 in detected_class_ids: # laptop | |
activities.append("Using a computer/laptop") | |
if 67 in detected_class_ids: # cell phone | |
activities.append("Using a mobile phone") | |
if 73 in detected_class_ids: # book | |
activities.append("Reading") | |
if any(food_id in detected_class_ids for food_id in [46, 47, 48, 49, 50, 51, 52, 53, 54, 55]): | |
activities.append("Eating or preparing food") | |
# Person-specific activities | |
if 0 in detected_class_ids: # Person | |
if any(vehicle in detected_class_ids for vehicle in [1, 2, 3, 5, 7]): # Vehicles | |
activities.append("Commuting or traveling") | |
if 16 in detected_class_ids: # Dog | |
activities.append("Walking a dog") | |
if 24 in detected_class_ids or 26 in detected_class_ids: # Backpack or handbag | |
activities.append("Carrying personal items") | |
# Remove duplicates | |
return list(set(activities)) | |
def _identify_safety_concerns(self, detected_objects: List[Dict], scene_type: str) -> List[str]: | |
""" | |
Identify potential safety concerns based on objects and scene type. | |
Args: | |
detected_objects: List of detected objects | |
scene_type: Identified scene type | |
Returns: | |
List of potential safety concerns | |
""" | |
concerns = [] | |
detected_class_ids = [obj["class_id"] for obj in detected_objects] | |
# ORIGINAL SAFETY CONCERNS LOGIC | |
# General safety concerns | |
if 42 in detected_class_ids or 43 in detected_class_ids: # Fork or knife | |
concerns.append("Sharp utensils present") | |
if 76 in detected_class_ids: # Scissors | |
concerns.append("Cutting tools present") | |
# Traffic-related concerns | |
if scene_type in ["city_street", "parking_lot"]: | |
if 0 in detected_class_ids: # Person | |
if any(vehicle in detected_class_ids for vehicle in [2, 3, 5, 7, 8]): # Vehicles | |
concerns.append("Pedestrians near vehicles") | |
if 9 in detected_class_ids: # Traffic light | |
concerns.append("Monitor traffic signals") | |
# Identify crowded scenes | |
person_count = detected_class_ids.count(0) | |
if person_count > 5: | |
concerns.append(f"Crowded area with multiple people ({person_count})") | |
# Scene-specific concerns | |
if scene_type == "kitchen": | |
if 68 in detected_class_ids or 69 in detected_class_ids: # Microwave or oven | |
concerns.append("Hot cooking equipment") | |
# Potentially unstable objects | |
for obj in detected_objects: | |
if obj["class_id"] in [39, 40, 41, 45]: # Bottle, wine glass, cup, bowl | |
if obj["region"] in ["top_left", "top_center", "top_right"] and obj["normalized_area"] > 0.05: | |
concerns.append(f"Elevated {obj['class_name']} might be unstable") | |
# NEW SAFETY CONCERNS LOGIC FOR ADDITIONAL SCENE TYPES | |
# Upscale dining safety concerns | |
if scene_type == "upscale_dining": | |
# Check for fragile items | |
if 40 in detected_class_ids: # Wine glass | |
concerns.append("Fragile glassware present") | |
# Check for lit candles (can't directly detect but can infer from context) | |
# Look for small bright spots that might be candles | |
if any(obj["class_id"] == 41 for obj in detected_objects): # Cup (which might include candle holders) | |
# We can't reliably detect candles, but if the scene appears to be formal dining, | |
# we can suggest this as a possibility | |
concerns.append("Possible lit candles or decorative items requiring care") | |
# Check for overcrowded table | |
table_objs = [obj for obj in detected_objects if obj["class_id"] == 60] # Dining table | |
if table_objs: | |
table_region = table_objs[0]["region"] | |
items_on_table = 0 | |
for obj in detected_objects: | |
if obj["class_id"] in [39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55]: | |
if obj["region"] == table_region: | |
items_on_table += 1 | |
if items_on_table > 8: | |
concerns.append("Dining table has multiple items which should be handled with care") | |
# Asian commercial street safety concerns | |
elif scene_type == "asian_commercial_street": | |
# Check for crowded walkways | |
if 0 in detected_class_ids: # Person | |
person_count = detected_class_ids.count(0) | |
if person_count > 3: | |
# Calculate person density (simplified) | |
person_positions = [] | |
for obj in detected_objects: | |
if obj["class_id"] == 0: | |
person_positions.append(obj["normalized_center"]) | |
if len(person_positions) >= 2: | |
# Calculate average distance between people | |
total_distance = 0 | |
count = 0 | |
for i in range(len(person_positions)): | |
for j in range(i+1, len(person_positions)): | |
p1 = person_positions[i] | |
p2 = person_positions[j] | |
distance = ((p2[0] - p1[0])**2 + (p2[1] - p1[1])**2)**0.5 | |
total_distance += distance | |
count += 1 | |
if count > 0: | |
avg_distance = total_distance / count | |
if avg_distance < 0.1: # Close proximity | |
concerns.append("Crowded walkway with limited personal space") | |
# Check for motorcycles/bicycles near pedestrians | |
if (1 in detected_class_ids or 3 in detected_class_ids) and 0 in detected_class_ids: # Bicycle/motorcycle and person | |
concerns.append("Two-wheeled vehicles in pedestrian areas") | |
# Check for potential trip hazards | |
# We can't directly detect this, but can infer from context | |
if scene_type == "asian_commercial_street" and "bottom" in " ".join([obj["region"] for obj in detected_objects if obj["class_id"] == 0]): | |
# If people are in bottom regions, they might be walking on uneven surfaces | |
concerns.append("Potential uneven walking surfaces in commercial area") | |
# Financial district safety concerns | |
elif scene_type == "financial_district": | |
# Check for heavy traffic conditions | |
vehicle_count = sum(1 for obj_id in detected_class_ids if obj_id in [2, 5, 7]) # Car, bus, truck | |
if vehicle_count > 5: | |
concerns.append("Heavy vehicle traffic in urban area") | |
# Check for pedestrians crossing busy streets | |
if 0 in detected_class_ids: # Person | |
person_count = detected_class_ids.count(0) | |
vehicle_nearby = any(vehicle in detected_class_ids for vehicle in [2, 3, 5, 7]) | |
if person_count > 0 and vehicle_nearby: | |
concerns.append("Pedestrians navigating busy urban traffic") | |
# Check for traffic signals | |
if 9 in detected_class_ids: # Traffic light | |
concerns.append("Observe traffic signals when navigating this area") | |
else: | |
# If no traffic lights detected but it's a busy area, it's worth noting | |
if vehicle_count > 3: | |
concerns.append("Busy traffic area potentially without visible traffic signals in view") | |
# Time of day considerations | |
# We don't have direct time data, but can infer from vehicle lights | |
vehicle_objs = [obj for obj in detected_objects if obj["class_id"] in [2, 5, 7]] | |
if vehicle_objs and any("lighting_conditions" in obj for obj in detected_objects): | |
# If vehicles are present and it might be evening/night | |
concerns.append("Reduced visibility conditions during evening commute") | |
# Urban intersection safety concerns | |
elif scene_type == "urban_intersection": | |
# Check for pedestrians in crosswalks | |
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 [2, 3, 5, 7]] | |
if pedestrian_objs: | |
# Calculate distribution of pedestrians to see if they're crossing | |
pedestrian_positions = [obj["normalized_center"] for obj in pedestrian_objs] | |
# Simplified check for pedestrians in crossing pattern | |
if len(pedestrian_positions) >= 3: | |
# Check if pedestrians are distributed across different regions | |
pedestrian_regions = set(obj["region"] for obj in pedestrian_objs) | |
if len(pedestrian_regions) >= 2: | |
concerns.append("Multiple pedestrians crossing the intersection") | |
# Check for traffic signal observation | |
if 9 in detected_class_ids: # Traffic light | |
concerns.append("Observe traffic signals when crossing") | |
# Check for busy intersection | |
if len(vehicle_objs) > 3: | |
concerns.append("Busy intersection with multiple vehicles") | |
# Check for pedestrians potentially jay-walking | |
if pedestrian_objs and not 9 in detected_class_ids: # People but no traffic lights | |
concerns.append("Pedestrians should use designated crosswalks") | |
# Visibility concerns based on lighting | |
# This would be better with actual lighting data | |
pedestrian_count = len(pedestrian_objs) | |
if pedestrian_count > 5: | |
concerns.append("High pedestrian density at crossing points") | |
# Transit hub safety concerns | |
elif scene_type == "transit_hub": | |
# These would be for transit areas like train stations or bus terminals | |
if 0 in detected_class_ids: # Person | |
person_count = detected_class_ids.count(0) | |
if person_count > 8: | |
concerns.append("Crowded transit area requiring careful navigation") | |
# Check for luggage/bags that could be trip hazards | |
if 24 in detected_class_ids or 28 in detected_class_ids: # Backpack or suitcase | |
concerns.append("Luggage and personal items may create obstacles") | |
# Public transportation vehicles | |
if any(vehicle in detected_class_ids for vehicle in [5, 6, 7]): # Bus, train, truck | |
concerns.append("Stay clear of arriving and departing transit vehicles") | |
# Shopping district safety concerns | |
elif scene_type == "shopping_district": | |
# Check for crowded shopping areas | |
if 0 in detected_class_ids: # Person | |
person_count = detected_class_ids.count(0) | |
if person_count > 5: | |
concerns.append("Crowded shopping area with multiple people") | |
# Check for shopping bags and personal items | |
if 24 in detected_class_ids or 26 in detected_class_ids: # Backpack or handbag | |
concerns.append("Mind personal belongings in busy retail environment") | |
# Check for store entrances/exits which might have automatic doors | |
# We can't directly detect this, but can infer from context | |
if scene_type == "shopping_district" and 0 in detected_class_ids: | |
concerns.append("Be aware of store entrances and exits with potential automatic doors") | |
return concerns | |