Upload analyze_spatial_relationships.py

#282
by Loucif - opened
tools/analyze_spatial_relationships.py ADDED
@@ -0,0 +1,119 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from smolagents import tool
2
+ import os
3
+ import json
4
+ import math
5
+ import cv2
6
+ import numpy as np
7
+ from typing import Optional
8
+
9
+ @tool
10
+ def analyze_spatial_relationships(image_path: Optional[str] = None, object_type1: Optional[str] = None, object_type2: Optional[str] = None) -> str:
11
+ """Analyze spatial relationships between objects in an image.
12
+ Args:
13
+ image_path: Path to the image file to analyze.
14
+ object_type1: First object type to analyze.
15
+ object_type2: Second object type to analyze.
16
+ Returns:
17
+ A description of spatial relationships between the objects.
18
+ """
19
+ try:
20
+ # Check if image path exists
21
+ if not image_path or not os.path.exists(image_path):
22
+ return f"Error: Image path '{image_path}' does not exist."
23
+
24
+ # Check if detection data exists, if not run detection
25
+ detection_data_path = f"{os.path.splitext(image_path)[0]}_detection_data.json"
26
+
27
+ if not os.path.exists(detection_data_path):
28
+ # Run detection first
29
+ from tools.detect_objects import detect_objects_with_roboflow
30
+ detect_objects_with_roboflow(image_path=image_path)
31
+
32
+ if not os.path.exists(detection_data_path):
33
+ return f"Error: Detection failed. Could not create detection data."
34
+
35
+ # Load existing detection data
36
+ with open(detection_data_path, "r") as f:
37
+ result = json.load(f)
38
+
39
+ # Load the original image to get dimensions
40
+ img = cv2.imread(image_path)
41
+ if img is None:
42
+ return f"Error: Could not read image at '{image_path}'."
43
+
44
+ img_height, img_width = img.shape[:2]
45
+
46
+ # Define "close" threshold as 30% of image width
47
+ close_threshold = img_width * 0.3
48
+
49
+ # Extract objects of the specified types
50
+ objects1 = []
51
+ objects2 = []
52
+
53
+ if "predictions" in result:
54
+ for prediction in result["predictions"]:
55
+ class_name = prediction["class"]
56
+
57
+ # Check if this prediction matches either object type
58
+ if object_type1 and object_type1.lower() in class_name.lower():
59
+ if "x" in prediction and "y" in prediction:
60
+ objects1.append({
61
+ "class": class_name,
62
+ "x": prediction["x"],
63
+ "y": prediction["y"],
64
+ "width": prediction["width"],
65
+ "height": prediction["height"],
66
+ "confidence": prediction["confidence"]
67
+ })
68
+
69
+ if object_type2 and object_type2.lower() in class_name.lower():
70
+ if "x" in prediction and "y" in prediction:
71
+ objects2.append({
72
+ "class": class_name,
73
+ "x": prediction["x"],
74
+ "y": prediction["y"],
75
+ "width": prediction["width"],
76
+ "height": prediction["height"],
77
+ "confidence": prediction["confidence"]
78
+ })
79
+
80
+ # If no objects found, return appropriate message
81
+ if not objects1:
82
+ return f"No objects matching '{object_type1}' were found in the image."
83
+
84
+ if not objects2:
85
+ return f"No objects matching '{object_type2}' were found in the image."
86
+
87
+ # Analyze spatial relationships
88
+ close_pairs = []
89
+
90
+ for obj1 in objects1:
91
+ for obj2 in objects2:
92
+ # Calculate Euclidean distance between centers
93
+ distance = math.sqrt(
94
+ (obj1["x"] - obj2["x"])**2 +
95
+ (obj1["y"] - obj2["y"])**2
96
+ )
97
+
98
+ # Check if they are close
99
+ if distance <= close_threshold:
100
+ close_pairs.append({
101
+ "object1": obj1["class"],
102
+ "object2": obj2["class"],
103
+ "distance": distance,
104
+ "distance_percent": (distance / img_width) * 100
105
+ })
106
+
107
+ # Format the results
108
+ if close_pairs:
109
+ output = f"Found {len(close_pairs)} instances where {object_type1} is close to {object_type2}:\n"
110
+ for i, pair in enumerate(close_pairs, 1):
111
+ output += f"{i}. {pair['object1']} is close to {pair['object2']} (distance: {pair['distance']:.1f} pixels, {pair['distance_percent']:.1f}% of image width)\n"
112
+
113
+ output += f"\nClose is defined as objects with centers within {close_threshold:.1f} pixels (30% of image width) from each other."
114
+ return output
115
+ else:
116
+ 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."
117
+
118
+ except Exception as e:
119
+ return f"Error analyzing spatial relationships: {str(e)}"