diff --git a/app/api/pick-object/route.ts b/app/api/pick-object/route.ts index c5e2a7304..e18f8a44c 100644 --- a/app/api/pick-object/route.ts +++ b/app/api/pick-object/route.ts @@ -80,25 +80,25 @@ export async function POST(request: Request) { // 构建 prompt(严格限制输出格式) const prompt = ` -识别图片中坐标 (${(clickX * 100).toFixed(0)}%, ${(clickY * 100).toFixed(0)}%) 位置的物品。 +请识别图片中用户点击位置(坐标 ${(clickX * 100).toFixed(0)}%, ${(clickY * 100).toFixed(0)}%)最近的那个具体物品。 + +【重要】只识别点击位置最近的那个小物品,不要识别大范围的家具或背景。 +例如:用户点击花瓶位置 → 返回"花瓶"而不是"桌子"或"屏风" 【输出要求 - 严格遵守】 返回纯 JSON,无其他文字: { "name": "物品名称", "position": "位置描述", - "confidence": 0.95, - "boundingBox": { "x": 0.1, "y": 0.2, "width": 0.3, "height": 0.4 } + "confidence": 0.95 } -【name 格式】≤8字,格式:[颜色/材质]+[物品] -正确:黑色沙发、白色花瓶、木质书架、玻璃茶几 -错误:一个黑色的皮质沙发(太长) +【name 格式】≤6字,简洁具体 +正确:橙色花瓶、白色杯子、木质椅子、吊灯 +错误:木质屏风(太大范围)、一个漂亮的花瓶(太长) 【position 格式】固定4字,从以下选择: ${POSITION_OPTIONS.join('、')} - -【boundingBox】相对坐标 0-1,精确到小数点后2位 `.trim(); console.log('[PickObject] 调用 Qwen VL 模型...'); @@ -142,41 +142,39 @@ ${POSITION_OPTIONS.join('、')} if (!jsonMatch) { throw new Error('未找到 JSON'); } - result = JSON.parse(jsonMatch[0]); + const parsed = JSON.parse(jsonMatch[0]); - // 验证和修正结果 - if (!result.name || result.name.length > 12) { - result.name = result.name?.substring(0, 8) || '未知物品'; + // 验证和修正名称 + let name = parsed.name || '物品'; + if (name.length > 8) { + name = name.substring(0, 6); } // 验证位置是否在词库中 - if (!POSITION_OPTIONS.includes(result.position as any)) { + let position = parsed.position; + if (!POSITION_OPTIONS.includes(position as any)) { // 根据点击位置推算 const posX = clickX < 0.33 ? '左' : clickX > 0.66 ? '右' : '中央'; const posY = clickY < 0.33 ? '上' : clickY > 0.66 ? '下' : '中'; if (posX === '中央') { - result.position = posY === '中' ? '中央区域' : posY === '上' ? '中央顶部' : '中央底部'; + position = posY === '中' ? '中央区域' : posY === '上' ? '中央顶部' : '中央底部'; } else { - result.position = posY === '中' ? `${posX}侧中部` : `${posX}${posY}区域`; + position = posY === '中' ? `${posX}侧中部` : `${posX}${posY}区域`; } } - // 确保 boundingBox 有效 - if (!result.boundingBox || - typeof result.boundingBox.x !== 'number' || - typeof result.boundingBox.y !== 'number') { - result.boundingBox = { - x: Math.max(0, clickX - 0.15), - y: Math.max(0, clickY - 0.15), - width: 0.3, - height: 0.3, - }; + // 确保 confidence 有效 + let confidence = parsed.confidence; + if (typeof confidence !== 'number' || confidence < 0 || confidence > 1) { + confidence = 0.85; } - // 确保 confidence 有效 - if (typeof result.confidence !== 'number' || result.confidence < 0 || result.confidence > 1) { - result.confidence = 0.85; - } + result = { + name, + position, + confidence, + boundingBox: { x: clickX, y: clickY, width: 0, height: 0 }, // 不再使用,保持兼容 + }; } catch (parseError) { console.error('[PickObject] JSON parse error:', parseError, 'Content:', responseText); @@ -192,12 +190,7 @@ ${POSITION_OPTIONS.join('、')} name: '物品', position, confidence: 0.5, - boundingBox: { - x: Math.max(0, clickX - 0.15), - y: Math.max(0, clickY - 0.15), - width: 0.3, - height: 0.3, - }, + boundingBox: { x: clickX, y: clickY, width: 0, height: 0 }, }; } diff --git a/components/canvas/ObjectPickerOverlay.tsx b/components/canvas/ObjectPickerOverlay.tsx index fda5843f7..9b82ed576 100644 --- a/components/canvas/ObjectPickerOverlay.tsx +++ b/components/canvas/ObjectPickerOverlay.tsx @@ -174,19 +174,17 @@ export function ObjectPickerOverlay() { )} - {/* 边界框显示(识别成功后) */} - {pickedObject && ( + {/* 物品名称标签(识别成功后,显示在点击位置上方) */} + {pickedObject && clickPosition && (