diff --git a/app/api/canvas/local-edit/route.ts b/app/api/canvas/local-edit/route.ts index c017dbb7c..637809b5d 100644 --- a/app/api/canvas/local-edit/route.ts +++ b/app/api/canvas/local-edit/route.ts @@ -1572,7 +1572,11 @@ function buildScaleAdjustCoordinatePrompt( scalePercent: ScaleAdjustPercent | null | undefined, sourceSize: { width: number; height: number }, cropReferences: ScaleAdjustCropReference[] = [], - options: { falSafe?: boolean; markedPhotoGuide?: boolean } = {} + options: { + falSafe?: boolean; + markedPhotoGuide?: boolean; + visualLayoutGuide?: boolean; + } = {} ) { const regionLines = regions .map((region) => { @@ -1657,9 +1661,11 @@ function buildScaleAdjustCoordinatePrompt( ].join("\n"); } - const coordinateGuideLine = options.markedPhotoGuide - ? "输入图片2是一张原图浅化后的完整画面位置参考图,不是输出底图。青色虚线框表示当前主体搜索框(对应下方 OLD),蓝色实线框表示目标最终占位(对应下方 TARGET)。请用输入图片2理解主体在真实室内画面中的位置关系,但最终图不能出现任何框线、色块、标注或参考图的浅化效果。" - : "Input image 2 is a zoomed neutral coordinate instruction map, not a photo and not output content. It enlarges the OLD and TARGET boxes so their relative scale is easy to read; use the numeric pixel coordinates below for the exact absolute position in input image 1. The OLD box/label locates the current furniture; the TARGET box/label gives the required final footprint, center, and scale factor. If the map says OLD OUTSIDE -> BG, rebuild the old full-size furniture area outside TARGET as natural background in the final photo. Read the boxes, labels, arrows, and scale text only as instructions; never output any map graphics."; + const coordinateGuideLine = options.visualLayoutGuide + ? "输入图片2是一张完整画面的粗略目标布局草稿,不是输出底图。它把 OLD 源框旧主体占位洗淡,表示旧尺寸主体需要移除并自然修复背景;同时把源框中的目标主体粗略缩放/移动到 TARGET 最终占位附近,帮助你理解最终主体大小、位置和室内画面关系。请只读取 input 2 的最终几何意图,不要复制它的淡化痕迹、粗糙拼接、局部透明边、矩形补丁、亮度变化或参考图画质;最终必须以 input 1 的真实照片为唯一画面基底重新完成无缝图片编辑。" + : options.markedPhotoGuide + ? "输入图片2是一张原图浅化后的完整画面位置参考图,不是输出底图。青色虚线框表示当前主体搜索框(对应下方 OLD),蓝色实线框表示目标最终占位(对应下方 TARGET)。请用输入图片2理解主体在真实室内画面中的位置关系,但最终图不能出现任何框线、色块、标注或参考图的浅化效果。" + : "Input image 2 is a zoomed neutral coordinate instruction map, not a photo and not output content. It enlarges the OLD and TARGET boxes so their relative scale is easy to read; use the numeric pixel coordinates below for the exact absolute position in input image 1. The OLD box/label locates the current furniture; the TARGET box/label gives the required final footprint, center, and scale factor. If the map says OLD OUTSIDE -> BG, rebuild the old full-size furniture area outside TARGET as natural background in the final photo. Read the boxes, labels, arrows, and scale text only as instructions; never output any map graphics."; return [ "Local furniture scale edit. Use input image 1 as the clean original photo and the locked camera frame.", @@ -1869,18 +1875,29 @@ export async function POST(request: NextRequest) { const useFalScaleAdjustCoordinatePrompt = useScaleAdjustCoordinateGuide && resolvedScaleAdjustModelEntry?.provider?.type === "fal-image"; - const useScaleAdjustMarkedPhotoGuide = useScaleAdjustCoordinateGuide; + const useScaleAdjustVisualLayoutGuide = + useScaleAdjustCoordinateGuide && !useFalScaleAdjustCoordinatePrompt; + const useScaleAdjustMarkedPhotoGuide = + useScaleAdjustCoordinateGuide && !useScaleAdjustVisualLayoutGuide; const annotatedBuffer = isScaleAdjustOperation ? useScaleAdjustCoordinateGuide - ? useScaleAdjustMarkedPhotoGuide - ? await buildScaleAdjustMarkedPhotoGuideBuffer({ - height: sourceHeight, + ? useScaleAdjustVisualLayoutGuide + ? await buildScaleAdjustVisualLayoutGuideBuffer({ regions, scalePercent, sourceBuffer, - width: sourceWidth, + sourceHeight, + sourceWidth, }) - : await sharp( + : useScaleAdjustMarkedPhotoGuide + ? await buildScaleAdjustMarkedPhotoGuideBuffer({ + height: sourceHeight, + regions, + scalePercent, + sourceBuffer, + width: sourceWidth, + }) + : await sharp( buildScaleAdjustZoomedCoordinateSvg({ height: sourceHeight, regions, @@ -1932,6 +1949,7 @@ export async function POST(request: NextRequest) { { falSafe: useFalScaleAdjustCoordinatePrompt, markedPhotoGuide: useScaleAdjustMarkedPhotoGuide, + visualLayoutGuide: useScaleAdjustVisualLayoutGuide, } ) : buildScaleAdjustPrompt( @@ -1999,7 +2017,9 @@ export async function POST(request: NextRequest) { annotatedReferenceSentToModel: isScaleAdjustOperation, annotatedReferenceType: isScaleAdjustOperation ? useScaleAdjustCoordinateGuide - ? useScaleAdjustMarkedPhotoGuide + ? useScaleAdjustVisualLayoutGuide + ? "scale-adjust-visual-layout-guide" + : useScaleAdjustMarkedPhotoGuide ? "scale-adjust-marked-photo-guide" : "scale-adjust-neutral-coordinate-map" : "scale-adjust-full-frame-semantic-edit-map" @@ -2020,11 +2040,13 @@ export async function POST(request: NextRequest) { scaleAdjustNeutralCoordinateMapApplied: isScaleAdjustOperation && useScaleAdjustCoordinateGuide && - !useScaleAdjustMarkedPhotoGuide, + !useScaleAdjustMarkedPhotoGuide && + !useScaleAdjustVisualLayoutGuide, scaleAdjustMarkedPhotoGuideApplied: useScaleAdjustMarkedPhotoGuide, scaleAdjustFalMarkedPhotoGuideApplied: useFalScaleAdjustCoordinatePrompt, scaleAdjustVisualLayoutGuideApplied: - isScaleAdjustOperation && !useScaleAdjustCoordinateGuide, + isScaleAdjustOperation && + (!useScaleAdjustCoordinateGuide || useScaleAdjustVisualLayoutGuide), falScaleAdjustCoordinateGuideApplied: useScaleAdjustCoordinateGuide && resolvedScaleAdjustModelEntry?.provider?.type === "fal-image", @@ -2054,7 +2076,9 @@ export async function POST(request: NextRequest) { ? useScaleAdjustCoordinateGuide ? useFalScaleAdjustCoordinatePrompt ? "fal-safe-marked-photo-box-guide-no-old-crop-v44-one-shot" - : "same-name-marked-photo-box-guide-no-old-crop-v45-one-shot" + : useScaleAdjustVisualLayoutGuide + ? "same-name-visual-layout-guide-v50-one-shot" + : "same-name-marked-photo-box-guide-no-old-crop-v45-one-shot" : "locked-plate-semantic-target-map-no-old-crop-v15" : undefined, scaleAdjustPostCompositeApplied: false, diff --git a/lib/canvas/generate-image.ts b/lib/canvas/generate-image.ts index 8159e3389..c5a6e8f40 100644 --- a/lib/canvas/generate-image.ts +++ b/lib/canvas/generate-image.ts @@ -1643,7 +1643,7 @@ async function buildScaleAdjustDeterministicFallbackImages(params: { .toColorspace("srgb") .raw() .toBuffer(); - const overlays: Array<{ buffer: Buffer; left: number; top: number }> = []; + const overlays: Array<{ input: Buffer; left: number; top: number }> = []; for (const [index, region] of verification.regions.entries()) { const targetRegion = @@ -1695,7 +1695,7 @@ async function buildScaleAdjustDeterministicFallbackImages(params: { width: sourceImage.sourceWidth, }); overlays.push({ - buffer: objectBuffer, + input: objectBuffer, left: targetBox.left, top: targetBox.top, });