fix: 去掉矩形轮廓框,优化物品识别prompt更聚焦点击位置
This commit is contained in:
parent
f7debfb55f
commit
13e72c9134
@ -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 },
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@ -174,19 +174,17 @@ export function ObjectPickerOverlay() {
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* 边界框显示(识别成功后) */}
|
||||
{pickedObject && (
|
||||
{/* 物品名称标签(识别成功后,显示在点击位置上方) */}
|
||||
{pickedObject && clickPosition && (
|
||||
<div
|
||||
className="absolute border-2 border-dashed border-green-500 rounded pointer-events-none"
|
||||
className="absolute pointer-events-none"
|
||||
style={{
|
||||
left: `${pickedObject.boundingBox.x * 100}%`,
|
||||
top: `${pickedObject.boundingBox.y * 100}%`,
|
||||
width: `${pickedObject.boundingBox.width * 100}%`,
|
||||
height: `${pickedObject.boundingBox.height * 100}%`,
|
||||
left: `${clickPosition.x * 100}%`,
|
||||
top: `${clickPosition.y * 100}%`,
|
||||
transform: 'translate(-50%, -100%)',
|
||||
}}
|
||||
>
|
||||
{/* 物品名称标签 */}
|
||||
<div className="absolute -top-7 left-0 bg-green-500 text-white text-xs px-2 py-1 rounded whitespace-nowrap">
|
||||
<div className="bg-green-500 text-white text-xs px-2 py-1 rounded whitespace-nowrap mb-2 shadow-lg">
|
||||
{pickedObject.objectName}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user