Spaces:
Running
Running
| const ALLOWED_KEYS = new Set(['image_id', 'label', 'bbox_2d', 'point_2d']); | |
| function normalizeCoordinateArray(value, length) { | |
| if (!Array.isArray(value) || value.length !== length || | |
| !value.every(coordinate => typeof coordinate === 'number' && Number.isFinite(coordinate))) return null; | |
| if (value.every(coordinate => coordinate >= 0 && coordinate <= 1)) { | |
| return value.map(coordinate => Math.round(coordinate * 1000)); | |
| } | |
| if (value.every(coordinate => Number.isInteger(coordinate) && coordinate >= 0 && coordinate <= 1000)) { | |
| return value.slice(); | |
| } | |
| return null; | |
| } | |
| function parseBareBoxes(text) { | |
| const number = '(-?(?:\\d+(?:\\.\\d*)?|\\.\\d+)(?:[eE][+-]?\\d+)?)'; | |
| const pattern = new RegExp(`\\[\\s*${number}\\s*,\\s*${number}\\s*,\\s*${number}\\s*,\\s*${number}\\s*\\]`, 'g'); | |
| const boxes = []; | |
| for (const match of text.matchAll(pattern)) { | |
| const coordinates = normalizeCoordinateArray(match.slice(1, 5).map(Number), 4); | |
| if (!coordinates) continue; | |
| const [xmin, ymin, xmax, ymax] = coordinates; | |
| if (xmax <= xmin || ymax <= ymin) continue; | |
| boxes.push({ | |
| imageId: 0, | |
| label: boxes.length ? `Bounding box ${boxes.length + 1}` : 'Bounding box', | |
| type: 'box', | |
| coordinates, | |
| }); | |
| } | |
| return boxes; | |
| } | |
| export function parseGroundingResponse(text, imageCount) { | |
| if (!Number.isInteger(imageCount) || imageCount < 1 || typeof text !== 'string') return null; | |
| let parsed; | |
| try { | |
| parsed = JSON.parse(text.trim()); | |
| } catch { | |
| const boxes = parseBareBoxes(text); | |
| return boxes.length ? boxes : null; | |
| } | |
| if (!Array.isArray(parsed)) { | |
| const boxes = parseBareBoxes(text); | |
| return boxes.length ? boxes : null; | |
| } | |
| const structuredCandidate = parsed.length === 0 || parsed.every(item => item && typeof item === 'object' && !Array.isArray(item)); | |
| if (!structuredCandidate) { | |
| const boxes = parseBareBoxes(text); | |
| return boxes.length ? boxes : null; | |
| } | |
| const normalized = []; | |
| let structured = true; | |
| for (const item of parsed) { | |
| if (!item || typeof item !== 'object' || Array.isArray(item) || | |
| Object.keys(item).some(key => !ALLOWED_KEYS.has(key)) || | |
| !Number.isInteger(item.image_id) || item.image_id < 0 || item.image_id >= imageCount || | |
| typeof item.label !== 'string' || !item.label.trim()) { | |
| structured = false; | |
| break; | |
| } | |
| const hasBox = Object.hasOwn(item, 'bbox_2d'); | |
| const hasPoint = Object.hasOwn(item, 'point_2d'); | |
| if (hasBox === hasPoint) { | |
| structured = false; | |
| break; | |
| } | |
| if (hasBox) { | |
| const coordinates = normalizeCoordinateArray(item.bbox_2d, 4); | |
| if (!coordinates) { | |
| structured = false; | |
| break; | |
| } | |
| const [xmin, ymin, xmax, ymax] = coordinates; | |
| if (xmax <= xmin || ymax <= ymin) { | |
| structured = false; | |
| break; | |
| } | |
| normalized.push({ imageId: item.image_id, label: item.label.trim(), type: 'box', coordinates }); | |
| } else { | |
| const coordinates = normalizeCoordinateArray(item.point_2d, 2); | |
| if (!coordinates) { | |
| structured = false; | |
| break; | |
| } | |
| normalized.push({ imageId: item.image_id, label: item.label.trim(), type: 'point', coordinates }); | |
| } | |
| } | |
| if (structured) return normalized; | |
| return null; | |
| } | |