File size: 5,019 Bytes
6d0f6ff |
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 |
from smolagents import tool
import os
import json
import math
import cv2
import numpy as np
from typing import Optional
@tool
def analyze_spatial_relationships(image_path: Optional[str] = None, object_type1: Optional[str] = None, object_type2: Optional[str] = None) -> str:
"""Analyze spatial relationships between objects in an image.
Args:
image_path: Path to the image file to analyze.
object_type1: First object type to analyze.
object_type2: Second object type to analyze.
Returns:
A description of spatial relationships between the objects.
"""
try:
# Check if image path exists
if not image_path or not os.path.exists(image_path):
return f"Error: Image path '{image_path}' does not exist."
# Check if detection data exists, if not run detection
detection_data_path = f"{os.path.splitext(image_path)[0]}_detection_data.json"
if not os.path.exists(detection_data_path):
# Run detection first
from tools.detect_objects import detect_objects_with_roboflow
detect_objects_with_roboflow(image_path=image_path)
if not os.path.exists(detection_data_path):
return f"Error: Detection failed. Could not create detection data."
# Load existing detection data
with open(detection_data_path, "r") as f:
result = json.load(f)
# Load the original image to get dimensions
img = cv2.imread(image_path)
if img is None:
return f"Error: Could not read image at '{image_path}'."
img_height, img_width = img.shape[:2]
# Define "close" threshold as 30% of image width
close_threshold = img_width * 0.3
# Extract objects of the specified types
objects1 = []
objects2 = []
if "predictions" in result:
for prediction in result["predictions"]:
class_name = prediction["class"]
# Check if this prediction matches either object type
if object_type1 and object_type1.lower() in class_name.lower():
if "x" in prediction and "y" in prediction:
objects1.append({
"class": class_name,
"x": prediction["x"],
"y": prediction["y"],
"width": prediction["width"],
"height": prediction["height"],
"confidence": prediction["confidence"]
})
if object_type2 and object_type2.lower() in class_name.lower():
if "x" in prediction and "y" in prediction:
objects2.append({
"class": class_name,
"x": prediction["x"],
"y": prediction["y"],
"width": prediction["width"],
"height": prediction["height"],
"confidence": prediction["confidence"]
})
# If no objects found, return appropriate message
if not objects1:
return f"No objects matching '{object_type1}' were found in the image."
if not objects2:
return f"No objects matching '{object_type2}' were found in the image."
# Analyze spatial relationships
close_pairs = []
for obj1 in objects1:
for obj2 in objects2:
# Calculate Euclidean distance between centers
distance = math.sqrt(
(obj1["x"] - obj2["x"])**2 +
(obj1["y"] - obj2["y"])**2
)
# Check if they are close
if distance <= close_threshold:
close_pairs.append({
"object1": obj1["class"],
"object2": obj2["class"],
"distance": distance,
"distance_percent": (distance / img_width) * 100
})
# Format the results
if close_pairs:
output = f"Found {len(close_pairs)} instances where {object_type1} is close to {object_type2}:\n"
for i, pair in enumerate(close_pairs, 1):
output += f"{i}. {pair['object1']} is close to {pair['object2']} (distance: {pair['distance']:.1f} pixels, {pair['distance_percent']:.1f}% of image width)\n"
output += f"\nClose is defined as objects with centers within {close_threshold:.1f} pixels (30% of image width) from each other."
return output
else:
return f"No instances found where {object_type1} is close to {object_type2}. Close is defined as objects with centers within {close_threshold:.1f} pixels (30% of image width) from each other."
except Exception as e:
return f"Error analyzing spatial relationships: {str(e)}" |