1+ """Utility functions for parsing Gemini model responses."""
2+
3+ import json
4+ import logging
5+ from typing import Dict , List , Optional , Tuple , Union
6+
7+ from PIL import Image
8+
9+
10+ def parse_gemini_point_response (
11+ response : str ,
12+ image_size : Optional [Tuple [int , int ]] = None ,
13+ normalize : bool = True ,
14+ ) -> Dict [str , List [Dict [str , Union [List [float ], str ]]]]:
15+ """Parse Gemini point response to extract point coordinates and labels.
16+
17+ Args:
18+ response: Raw response from Gemini
19+ image_size: Optional size of the input image (width, height) for normalization
20+ normalize: Whether to normalize points to 0-1000 range
21+
22+ Returns:
23+ Dictionary with points and labels
24+ """
25+ try :
26+ # Extract JSON from response
27+ start = response .find ("[" )
28+ end = response .rfind ("]" ) + 1
29+ json_str = response [start :end ]
30+ points_data = json .loads (json_str )
31+
32+ # Validate and normalize points
33+ normalized_points = []
34+ for item in points_data :
35+ point = item ["point" ]
36+ if not isinstance (point , list ) or len (point ) != 2 :
37+ raise ValueError (f"Invalid point format: { point } " )
38+
39+ if normalize and image_size :
40+ # Convert to normalized coordinates [0-1000]
41+ y , x = point
42+ y = y * 1000 / image_size [1 ]
43+ x = x * 1000 / image_size [0 ]
44+ point = [y , x ]
45+
46+ normalized_points .append ({
47+ "point" : point ,
48+ "label" : str (item ["label" ])
49+ })
50+
51+ return {"points" : normalized_points }
52+ except (json .JSONDecodeError , KeyError , ValueError ) as e :
53+ logging .error (f"Failed to parse Gemini point response: { e } " )
54+ logging .error (f"Response: { response } " )
55+ raise ValueError ("Failed to parse Gemini point response" ) from e
56+
57+
58+ def parse_gemini_detection_response (
59+ response : str ,
60+ image_size : Optional [Tuple [int , int ]] = None ,
61+ normalize : bool = True ,
62+ ) -> Dict [str , List [Dict [str , Union [List [float ], str ]]]]:
63+ """Parse Gemini detection response to extract bounding boxes and labels.
64+
65+ Args:
66+ response: Raw response from Gemini
67+ image_size: Optional size of the input image (width, height) for normalization
68+ normalize: Whether to normalize boxes to 0-1000 range
69+
70+ Returns:
71+ Dictionary with bounding boxes and labels
72+ """
73+ try :
74+ # Extract JSON from response
75+ start = response .find ("[" )
76+ end = response .rfind ("]" ) + 1
77+ json_str = response [start :end ]
78+ detection_data = json .loads (json_str )
79+
80+ # Validate and normalize boxes
81+ normalized_boxes = []
82+ for item in detection_data :
83+ box = item ["box_2d" ]
84+ if not isinstance (box , list ) or len (box ) != 4 :
85+ raise ValueError (f"Invalid box format: { box } " )
86+
87+ if normalize and image_size :
88+ # Convert to normalized coordinates [0-1000]
89+ x1 , y1 , x2 , y2 = box
90+ x1 = x1 * 1000 / image_size [0 ]
91+ y1 = y1 * 1000 / image_size [1 ]
92+ x2 = x2 * 1000 / image_size [0 ]
93+ y2 = y2 * 1000 / image_size [1 ]
94+ box = [x1 , y1 , x2 , y2 ]
95+
96+ normalized_boxes .append ({
97+ "box_2d" : box ,
98+ "label" : str (item ["label" ])
99+ })
100+
101+ return {"detections" : normalized_boxes }
102+ except (json .JSONDecodeError , KeyError , ValueError ) as e :
103+ logging .error (f"Failed to parse Gemini detection response: { e } " )
104+ logging .error (f"Response: { response } " )
105+ raise ValueError ("Failed to parse Gemini detection response" ) from e
106+
107+
108+ def denormalize_point (
109+ point : List [float ],
110+ image_size : Tuple [int , int ],
111+ ) -> List [float ]:
112+ """Convert normalized point [0-1000] back to image coordinates.
113+
114+ Args:
115+ point: Normalized point coordinates [y, x]
116+ image_size: Size of the image (width, height)
117+
118+ Returns:
119+ Point coordinates in image space
120+ """
121+ y , x = point
122+ y = y * image_size [1 ] / 1000
123+ x = x * image_size [0 ] / 1000
124+ return [y , x ]
125+
126+
127+ def denormalize_box (
128+ box : List [float ],
129+ image_size : Tuple [int , int ],
130+ ) -> List [float ]:
131+ """Convert normalized box [0-1000] back to image coordinates.
132+
133+ Args:
134+ box: Normalized box coordinates [x1, y1, x2, y2]
135+ image_size: Size of the image (width, height)
136+
137+ Returns:
138+ Box coordinates in image space
139+ """
140+ x1 , y1 , x2 , y2 = box
141+ x1 = x1 * image_size [0 ] / 1000
142+ y1 = y1 * image_size [1 ] / 1000
143+ x2 = x2 * image_size [0 ] / 1000
144+ y2 = y2 * image_size [1 ] / 1000
145+ return [x1 , y1 , x2 , y2 ]
0 commit comments