fix: enlarge scale adjust coordinate map
This commit is contained in:
parent
ffd13c0f31
commit
b3b4fa8ed2
@ -1249,6 +1249,128 @@ function buildScaleAdjustAnnotationSvg(params: {
|
||||
`);
|
||||
}
|
||||
|
||||
function buildScaleAdjustZoomedCoordinateSvg(params: {
|
||||
width: number;
|
||||
height: number;
|
||||
regions: NormalizedLocalEditRegion[];
|
||||
scalePercent?: ScaleAdjustPercent | null;
|
||||
}) {
|
||||
const { width, height, regions, scalePercent } = params;
|
||||
const minSide = Math.max(1, Math.min(width, height));
|
||||
const strokeWidth = Math.max(3, Math.round(minSide * 0.006));
|
||||
const fontSize = Math.max(22, Math.round(minSide * 0.026));
|
||||
const smallFontSize = Math.max(18, Math.round(fontSize * 0.72));
|
||||
const panelMarginX = Math.round(width * 0.1);
|
||||
const panelMarginY = Math.round(height * 0.12);
|
||||
const panelLeft = panelMarginX;
|
||||
const panelTop = panelMarginY;
|
||||
const panelWidth = Math.max(1, width - panelMarginX * 2);
|
||||
const panelHeight = Math.max(1, height - panelMarginY * 2);
|
||||
const sourceColor = "#9ca3af";
|
||||
const targetColor = "#111827";
|
||||
const helperColor = "#4b5563";
|
||||
const padX = Math.round(fontSize * 0.42);
|
||||
const labelHeight = Math.round(fontSize * 1.35);
|
||||
const label = (text: string, x: number, y: number, color: string) => {
|
||||
const labelWidth = Math.min(
|
||||
Math.round(text.length * fontSize * 0.58 + padX * 2),
|
||||
Math.max(160, width - 16)
|
||||
);
|
||||
const left = clamp(x, 8, Math.max(8, width - labelWidth - 8));
|
||||
const top = clamp(y, 8, Math.max(8, height - labelHeight - 8));
|
||||
return `
|
||||
<rect x="${left}" y="${top}" width="${labelWidth}" height="${labelHeight}" rx="${Math.round(labelHeight * 0.22)}" fill="${color}" opacity="0.92" />
|
||||
<text x="${left + padX}" y="${top + Math.round(labelHeight * 0.68)}" fill="#ffffff" font-size="${fontSize}" font-family="Arial, Helvetica, sans-serif" font-weight="800">${escapeXml(text)}</text>
|
||||
`;
|
||||
};
|
||||
|
||||
const regionSvg = regions
|
||||
.map((region) => {
|
||||
const source = region.pixelRect;
|
||||
const targetRect = getScaleAdjustTargetRect(region, scalePercent);
|
||||
const target =
|
||||
region.targetPixelRect || rectToPixels(targetRect, width, height);
|
||||
const sourceRight = source.left + source.width;
|
||||
const sourceBottom = source.top + source.height;
|
||||
const targetRight = target.left + target.width;
|
||||
const targetBottom = target.top + target.height;
|
||||
const unionLeft = Math.min(source.left, target.left);
|
||||
const unionTop = Math.min(source.top, target.top);
|
||||
const unionRight = Math.max(sourceRight, targetRight);
|
||||
const unionBottom = Math.max(sourceBottom, targetBottom);
|
||||
const marginX = Math.max(12, Math.round((unionRight - unionLeft) * 0.35));
|
||||
const marginY = Math.max(12, Math.round((unionBottom - unionTop) * 0.35));
|
||||
const viewLeft = unionLeft - marginX;
|
||||
const viewTop = unionTop - marginY;
|
||||
const viewWidth = Math.max(1, unionRight - unionLeft + marginX * 2);
|
||||
const viewHeight = Math.max(1, unionBottom - unionTop + marginY * 2);
|
||||
const scale = Math.min(
|
||||
panelWidth / viewWidth,
|
||||
panelHeight / viewHeight
|
||||
);
|
||||
const drawnWidth = viewWidth * scale;
|
||||
const drawnHeight = viewHeight * scale;
|
||||
const originX = panelLeft + (panelWidth - drawnWidth) / 2 - viewLeft * scale;
|
||||
const originY = panelTop + (panelHeight - drawnHeight) / 2 - viewTop * scale;
|
||||
const project = (box: { left: number; top: number; width: number; height: number }) => ({
|
||||
left: Math.round(originX + box.left * scale),
|
||||
top: Math.round(originY + box.top * scale),
|
||||
width: Math.max(1, Math.round(box.width * scale)),
|
||||
height: Math.max(1, Math.round(box.height * scale)),
|
||||
});
|
||||
const sourceBox = project(source);
|
||||
const targetBox = project(target);
|
||||
const sourceCenterX = sourceBox.left + sourceBox.width / 2;
|
||||
const sourceCenterY = sourceBox.top + sourceBox.height / 2;
|
||||
const targetCenterX = targetBox.left + targetBox.width / 2;
|
||||
const targetCenterY = targetBox.top + targetBox.height / 2;
|
||||
const widthFactor = source.width > 0 ? target.width / source.width : 1;
|
||||
const heightFactor = source.height > 0 ? target.height / source.height : 1;
|
||||
const actionLabel =
|
||||
widthFactor < 0.92 || heightFactor < 0.92
|
||||
? "SHRINK"
|
||||
: widthFactor > 1.08 || heightFactor > 1.08
|
||||
? "ENLARGE"
|
||||
: "MOVE";
|
||||
const scaleLabel = `${actionLabel} ${widthFactor.toFixed(2)}W ${heightFactor.toFixed(2)}H`;
|
||||
const radius = Math.max(10, Math.round(strokeWidth * 2.2));
|
||||
const centerMoved =
|
||||
Math.hypot(targetCenterX - sourceCenterX, targetCenterY - sourceCenterY) >
|
||||
Math.max(10, minSide * 0.018);
|
||||
|
||||
return `
|
||||
<rect x="${sourceBox.left}" y="${sourceBox.top}" width="${sourceBox.width}" height="${sourceBox.height}" rx="${radius}" fill="#e5e7eb" opacity="0.72" stroke="${sourceColor}" stroke-width="${strokeWidth}" />
|
||||
<rect x="${targetBox.left}" y="${targetBox.top}" width="${targetBox.width}" height="${targetBox.height}" rx="${radius}" fill="#d1d5db" opacity="0.82" stroke="${targetColor}" stroke-width="${Math.max(3, Math.round(strokeWidth * 1.25))}" />
|
||||
<circle cx="${sourceCenterX}" cy="${sourceCenterY}" r="${Math.max(4, Math.round(strokeWidth * 1.25))}" fill="${sourceColor}" />
|
||||
<circle cx="${targetCenterX}" cy="${targetCenterY}" r="${Math.max(5, Math.round(strokeWidth * 1.55))}" fill="${targetColor}" />
|
||||
${
|
||||
centerMoved
|
||||
? `<line x1="${sourceCenterX}" y1="${sourceCenterY}" x2="${targetCenterX}" y2="${targetCenterY}" stroke="${helperColor}" stroke-width="${Math.max(3, Math.round(strokeWidth * 0.9))}" stroke-linecap="round" marker-end="url(#scale-adjust-zoom-arrow)" opacity="0.78" />`
|
||||
: ""
|
||||
}
|
||||
${label(`OLD ${region.index}`, sourceBox.left, sourceBox.top - labelHeight - strokeWidth, sourceColor)}
|
||||
${label(`TARGET ${region.index}`, targetBox.left, targetBox.top + strokeWidth, targetColor)}
|
||||
${label(scaleLabel, targetBox.left, targetBox.top + targetBox.height + strokeWidth * 2, helperColor)}
|
||||
${label("OLD OUTSIDE = BACKGROUND", sourceBox.left, sourceBox.top + sourceBox.height + strokeWidth * 4 + labelHeight, helperColor)}
|
||||
`;
|
||||
})
|
||||
.join("");
|
||||
|
||||
return Buffer.from(`
|
||||
<svg width="${width}" height="${height}" viewBox="0 0 ${width} ${height}" xmlns="http://www.w3.org/2000/svg">
|
||||
<defs>
|
||||
<marker id="scale-adjust-zoom-arrow" markerWidth="10" markerHeight="10" refX="8" refY="5" orient="auto" markerUnits="strokeWidth">
|
||||
<path d="M 0 0 L 10 5 L 0 10 z" fill="${helperColor}" />
|
||||
</marker>
|
||||
</defs>
|
||||
<rect x="0" y="0" width="${width}" height="${height}" fill="#f5f7fb" />
|
||||
<rect x="${panelLeft}" y="${panelTop}" width="${panelWidth}" height="${panelHeight}" rx="${Math.round(minSide * 0.018)}" fill="#ffffff" stroke="#cbd5e1" stroke-width="${Math.max(2, Math.round(strokeWidth * 0.65))}" />
|
||||
<text x="${panelLeft}" y="${Math.max(fontSize + 8, panelTop - Math.round(fontSize * 0.45))}" fill="#111827" font-size="${smallFontSize}" font-family="Arial, Helvetica, sans-serif" font-weight="800">LOCAL SCALE MAP - USE PROMPT PIXELS FOR ABSOLUTE POSITION</text>
|
||||
${regionSvg}
|
||||
</svg>
|
||||
`);
|
||||
}
|
||||
|
||||
function buildLocalEditPrompt(regions: NormalizedLocalEditRegion[]) {
|
||||
const regionLines = regions
|
||||
.map((region) => {
|
||||
@ -1438,7 +1560,7 @@ function buildScaleAdjustCoordinatePrompt(
|
||||
"中文硬约束:只调整 OLD 框中的主要家具主体。TARGET 框是最终占位;如果是缩小,OLD 框里超出 TARGET 的旧家具必须消失并补成自然背景;如果是放大,同一件家具必须明显变大。禁止整图缩放、整图裁切、整图重绘。",
|
||||
"一次成功硬约束:不要保守返回原尺寸。目标家具的可见轮廓必须明显从 OLD 原始占位变到 TARGET 占位;如果最终看起来和原图一样,就是失败。",
|
||||
`Return one seamless realistic photo with the same frame and pixel size as input image 1: ${sourceSize.width} x ${sourceSize.height} px.`,
|
||||
"Input image 2 is a neutral coordinate instruction map, not a photo and not output content. The OLD box/label locates the current furniture in input image 1; the TARGET box/label gives the required final footprint, center, and scale factor. If the map says OLD OUTSIDE = BACKGROUND, the old full-size furniture outside TARGET must disappear and be healed as natural background in the final photo. Read the boxes, labels, arrows, and scale text only as instructions; never output any map graphics.",
|
||||
"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 = BACKGROUND, the old full-size furniture outside TARGET must disappear and be healed as natural background in the final photo. Read the boxes, labels, arrows, and scale text only as instructions; never output any map graphics.",
|
||||
cropLines ? `${cropLines}` : "",
|
||||
"",
|
||||
"Task:",
|
||||
@ -1463,7 +1585,7 @@ function buildScaleAdjustCoordinatePrompt(
|
||||
function buildScaleAdjustCoordinateSystemPrompt() {
|
||||
return [
|
||||
"You are an image editing model for local furniture scale adjustment.",
|
||||
"Input image 1 is the only final photo base. Input image 2 is a neutral coordinate instruction map, not a photo layer.",
|
||||
"Input image 1 is the only final photo base. Input image 2 is a zoomed neutral coordinate instruction map, not a photo layer.",
|
||||
"Follow both English and Chinese instructions. The Chinese lines are hard constraints for avoiding unchanged furniture and full-canvas zoom.",
|
||||
"Generate a clean realistic final photo by editing the target furniture itself and naturally rebuilding only the tiny surrounding background that becomes visible.",
|
||||
"Prefer a clear visible size change over a conservative unchanged result; unchanged furniture is a failed edit.",
|
||||
@ -1640,7 +1762,7 @@ export async function POST(request: NextRequest) {
|
||||
const annotatedBuffer = isScaleAdjustOperation
|
||||
? useScaleAdjustCoordinateGuide
|
||||
? await sharp(
|
||||
buildScaleAdjustAnnotationSvg({
|
||||
buildScaleAdjustZoomedCoordinateSvg({
|
||||
height: sourceHeight,
|
||||
regions,
|
||||
scalePercent,
|
||||
@ -1799,7 +1921,7 @@ export async function POST(request: NextRequest) {
|
||||
.length,
|
||||
scaleAdjustPromptStrategy: isScaleAdjustOperation
|
||||
? useScaleAdjustCoordinateGuide
|
||||
? "same-name-neutral-coordinate-map-no-old-crop-v39-one-shot"
|
||||
? "same-name-zoomed-coordinate-map-no-old-crop-v41-one-shot"
|
||||
: "locked-plate-semantic-target-map-no-old-crop-v15"
|
||||
: undefined,
|
||||
scaleAdjustPostCompositeApplied: false,
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user