fix: keep product out of scale repair crop prompt

This commit is contained in:
134 2026-07-08 01:38:51 +08:00
parent 2cb6f51a63
commit d69346de42

View File

@ -124,6 +124,7 @@ type ScaleAdjustCropReference = {
url: string;
};
type ScaleAdjustPatchReference = {
backgroundPlateCropUrl?: string | null;
backgroundPlateFullUrl?: string | null;
compositeClipMaskUrl?: string | null;
repairBlendMaskUrl?: string | null;
@ -4024,6 +4025,7 @@ async function createScaleAdjustPatchReference(params: {
]);
return {
backgroundPlateCropUrl: null,
backgroundPlateFullUrl: backgroundPlateFullUpload?.url || null,
compositeClipMaskUrl: compositeClipMaskUpload?.url || null,
repairBlendMaskUrl: repairBlendMaskUpload?.url || null,
@ -4051,6 +4053,12 @@ async function createScaleAdjustPatchReference(params: {
.png({ compressionLevel: 6 })
.toBuffer(),
]);
const backgroundPlateCropBuffer = targetDraftReference.backgroundPlateBuffer
? await sharp(targetDraftReference.backgroundPlateBuffer, { failOn: "none" })
.extract(workspacePixelRect)
.png({ compressionLevel: 6 })
.toBuffer()
: null;
const [sourceCropUpload, targetDraftUpload, targetDraftFullUpload] =
await Promise.all([
uploadScaleAdjustPrepAsset(
@ -4068,6 +4076,7 @@ async function createScaleAdjustPatchReference(params: {
]);
const [
backgroundPlateFullUpload,
backgroundPlateCropUpload,
productLockOverlayUpload,
productLockProtectMaskUpload,
compositeClipMaskUpload,
@ -4079,6 +4088,10 @@ async function createScaleAdjustPatchReference(params: {
`canvas-scale-adjust-background-plate-${suffix}.jpg`,
backgroundPlateUploadBuffer
),
uploadOptionalScaleAdjustAsset(
`canvas-scale-adjust-background-plate-crop-${suffix}.png`,
backgroundPlateCropBuffer
),
uploadOptionalScaleAdjustAsset(
`canvas-scale-adjust-product-lock-overlay-${suffix}.png`,
targetDraftReference.productLockOverlayBuffer
@ -4112,6 +4125,7 @@ async function createScaleAdjustPatchReference(params: {
}
return {
backgroundPlateCropUrl: backgroundPlateCropUpload?.url || null,
backgroundPlateFullUrl: backgroundPlateFullUpload?.url || null,
compositeClipMaskUrl: compositeClipMaskUpload?.url || null,
repairBlendMaskUrl: repairBlendMaskUpload?.url || null,
@ -5148,6 +5162,54 @@ function buildScaleAdjustProductLockBackgroundPrompt(
].join("\n");
}
function buildScaleAdjustProductLockBackgroundCropPrompt(
regions: NormalizedLocalEditRegion[],
scalePercent: ScaleAdjustPercent | null | undefined,
patchReference: ScaleAdjustPatchReference,
sourceSize: { width: number; height: number }
) {
const workspace = patchReference.workspacePixelRect;
const regionLines = regions
.map((region) => {
const targetRect = getScaleAdjustTargetRect(region, scalePercent);
const targetPixelRect =
region.targetPixelRect ||
rectToPixels(targetRect, sourceSize.width, sourceSize.height);
const sourceInCrop = {
left: Math.max(0, region.pixelRect.left - workspace.left),
top: Math.max(0, region.pixelRect.top - workspace.top),
width: region.pixelRect.width,
height: region.pixelRect.height,
};
const targetInCrop = {
left: Math.max(0, targetPixelRect.left - workspace.left),
top: Math.max(0, targetPixelRect.top - workspace.top),
width: targetPixelRect.width,
height: targetPixelRect.height,
};
return `Region ${region.index}: in this crop, erase the old selected furniture/product footprint around x=${sourceInCrop.left}, y=${sourceInCrop.top}, w=${sourceInCrop.width}, h=${sourceInCrop.height}. Keep the future target footprint x=${targetInCrop.left}, y=${targetInCrop.top}, w=${targetInCrop.width}, h=${targetInCrop.height} empty and clean; the locked product pixels will be composited there after generation. User request: ${region.prompt}.`;
})
.join("\n");
return [
"Cropped background repair for product-locked scale adjustment.",
`Return exactly one clean realistic crop with the same framing, aspect ratio, and pixel extent as input image 1. This crop maps to full-photo pixels left=${workspace.left}, top=${workspace.top}, width=${workspace.width}, height=${workspace.height}.`,
"Input image 1 is the original crop from the source photo. Input image 2 is a rough empty-background repair guide crop where the selected furniture/product footprint has been wiped.",
"Important: do not draw, recreate, move, scale, duplicate, or redesign the selected furniture/product in your output. The system will composite the locked original product pixels into the target footprint after your crop is returned.",
"Your only job is to repair the old footprint into natural wall, curtain, floor, rug, shadow, sofa edge, and background, while keeping non-target people and non-target objects stable.",
"If input image 2 contains haze, gray wash, ghost silhouettes, crop edges, black holes, rough mask shapes, or pasted fragments, treat them as repair hints only. Do not preserve them as visible content.",
"Preserve any non-target person exactly: do not change face, body, hands, legs, clothes, pose, hair, or book. Do not cover the person with furniture.",
"Keep the target footprint empty and natural; do not place the selected furniture/product there.",
"Do not output boxes, masks, labels, coordinate marks, UI marks, arrows, guide overlays, text, seams, rectangular patch edges, haze blocks, ghost copies, or old-size remnants.",
"中文硬约束:这是产品结构锁定的局部背景修补。你只输出干净背景裁切,不要生成目标家具/商品;旧位置的目标家具/商品必须擦除并补成自然墙面、窗帘、地面、阴影和背景。目标家具/商品会在你输出后由系统用原图锁定像素叠回。最终不能有旧尺寸残影、矩形雾块、贴片边、黑洞、标注框,也不能改变人物。",
"",
"Region instructions:",
regionLines,
"",
"Success check before returning: the selected furniture/product is absent from the crop, the old footprint is naturally repaired, the future target footprint is clean, and the crop looks like one natural photograph.",
].join("\n");
}
export async function POST(request: NextRequest) {
try {
const session = await auth();
@ -5506,6 +5568,10 @@ export async function POST(request: NextRequest) {
const useScaleAdjustProductLockMaskEdit =
useScaleAdjustProductLockComposite &&
canUseScaleAdjustProductLockMaskEdit;
const useScaleAdjustProductLockCropBackground =
useScaleAdjustProductLockComposite &&
!useScaleAdjustFullFrameStructurePatch &&
Boolean(scaleAdjustPatchReference?.backgroundPlateCropUrl);
const useScaleAdjustPatchTargetDraftPrimary = Boolean(scaleAdjustPatchReference);
const includeScaleAdjustPatchSourceReference =
Boolean(scaleAdjustPatchReference);
@ -5531,6 +5597,13 @@ export async function POST(request: NextRequest) {
scalePercent,
{ width: sourceWidth, height: sourceHeight }
)
: useScaleAdjustProductLockCropBackground
? buildScaleAdjustProductLockBackgroundCropPrompt(
regions,
scalePercent,
scaleAdjustPatchReference,
{ width: sourceWidth, height: sourceHeight }
)
: buildScaleAdjustPatchPrompt(
regions,
scalePercent,
@ -5613,6 +5686,13 @@ export async function POST(request: NextRequest) {
...scaleAdjustCropReferenceImageUrls,
...manualRegionReferenceImageUrls,
]
: useScaleAdjustProductLockCropBackground
? [
scaleAdjustPatchReference.sourceCropUrl,
scaleAdjustPatchReference.backgroundPlateCropUrl as string,
...scaleAdjustCropReferenceImageUrls,
...manualRegionReferenceImageUrls,
]
: useScaleAdjustPatchTargetDraftPrimary
? [
scaleAdjustPatchReference.targetDraftUrl,