fix: clarify FAL scale adjust guidance
This commit is contained in:
parent
1d0af8c2d8
commit
619cc2bf5b
@ -97,8 +97,10 @@ const MAX_REFERENCE_IMAGES_PER_REGION = 3;
|
||||
const MAX_SCALE_ADJUST_CROP_REFERENCES = 4;
|
||||
const SCALE_ADJUST_CROP_PADDING_RATIO = 0.18;
|
||||
const SCALE_ADJUST_CROP_MAX_EDGE = 1536;
|
||||
const SCALE_ADJUST_FAL_MOCKUP_PADDING_RATIO = 0.04;
|
||||
const SCALE_ADJUST_FAL_MOCKUP_BLUR_PX = 38;
|
||||
const SCALE_ADJUST_FAL_MOCKUP_PADDING_RATIO = 0.02;
|
||||
const SCALE_ADJUST_FAL_MOCKUP_BLUR_PX = 30;
|
||||
const SCALE_ADJUST_FAL_MOCKUP_SUBJECT_OPACITY = 0.82;
|
||||
const SCALE_ADJUST_FAL_MOCKUP_WORKSPACE_OPACITY = 0.32;
|
||||
const MIN_REGION_EDGE_PX = 24;
|
||||
const SCALE_ADJUST_OPERATION = "scale-adjust";
|
||||
type LocalEditOperation = "local-edit" | typeof SCALE_ADJUST_OPERATION;
|
||||
@ -569,6 +571,7 @@ async function createFeatherAlphaMask(params: {
|
||||
height: number;
|
||||
feather: number;
|
||||
inset: number;
|
||||
opacity?: number;
|
||||
}) {
|
||||
const { width, height } = params;
|
||||
const feather = Math.max(1, Math.round(params.feather));
|
||||
@ -576,12 +579,17 @@ async function createFeatherAlphaMask(params: {
|
||||
const inset = clamp(Math.round(params.inset), 1, maxInset);
|
||||
const innerWidth = Math.max(1, width - inset * 2);
|
||||
const innerHeight = Math.max(1, height - inset * 2);
|
||||
const alpha = clamp(
|
||||
Math.round((params.opacity ?? 1) * 255),
|
||||
0,
|
||||
255
|
||||
);
|
||||
const svg = Buffer.from(`
|
||||
<svg width="${width}" height="${height}" viewBox="0 0 ${width} ${height}" xmlns="http://www.w3.org/2000/svg">
|
||||
<defs>
|
||||
<filter id="soft"><feGaussianBlur stdDeviation="${feather}" /></filter>
|
||||
</defs>
|
||||
<rect x="${inset}" y="${inset}" width="${innerWidth}" height="${innerHeight}" fill="#fff" filter="url(#soft)" />
|
||||
<rect x="${inset}" y="${inset}" width="${innerWidth}" height="${innerHeight}" fill="rgb(${alpha},${alpha},${alpha})" filter="url(#soft)" />
|
||||
</svg>
|
||||
`);
|
||||
|
||||
@ -599,11 +607,13 @@ async function addFeatherAlpha(params: {
|
||||
height: number;
|
||||
feather: number;
|
||||
inset: number;
|
||||
opacity?: number;
|
||||
}) {
|
||||
const mask = await createFeatherAlphaMask({
|
||||
feather: params.feather,
|
||||
height: params.height,
|
||||
inset: params.inset,
|
||||
opacity: params.opacity,
|
||||
width: params.width,
|
||||
});
|
||||
|
||||
@ -657,6 +667,7 @@ async function buildScaleAdjustFalTargetMockup(params: {
|
||||
feather: workspaceFeather,
|
||||
height: workspacePixelRect.height,
|
||||
inset: Math.max(workspaceFeather * 2.6, workspaceFeather + 1),
|
||||
opacity: SCALE_ADJUST_FAL_MOCKUP_WORKSPACE_OPACITY,
|
||||
width: workspacePixelRect.width,
|
||||
});
|
||||
|
||||
@ -677,6 +688,7 @@ async function buildScaleAdjustFalTargetMockup(params: {
|
||||
feather: subjectFeather,
|
||||
height: targetPixelRect.height,
|
||||
inset: Math.max(subjectFeather * 1.6, subjectFeather + 1),
|
||||
opacity: SCALE_ADJUST_FAL_MOCKUP_SUBJECT_OPACITY,
|
||||
width: targetPixelRect.width,
|
||||
});
|
||||
|
||||
@ -1094,7 +1106,7 @@ function buildFalScaleAdjustMockupPrompt(
|
||||
return [
|
||||
"这是一项室内家用场景里的家具/物体局部比例调整任务。输入图片1是一张粗略目标效果草图,不是最终结果,也不是标注图。",
|
||||
`最终输出必须是一张真实、干净、自然的单张照片,并保持原始画幅 ${sourceSize.width} x ${sourceSize.height} px。`,
|
||||
"输入图片1已经把目标主体粗略放到了用户希望的最终大小和位置;请把它作为最终布局意图来理解,但不要直接复制其中的模糊、拼接边缘、软边、矩形块或局部贴片痕迹。",
|
||||
"输入图片1用半透明软占位表达目标主体的最终大小和位置;请把它作为布局意图来理解,但不要直接复制其中的模糊、拼接边缘、软边、矩形块或局部贴片痕迹。",
|
||||
"核心目标:只调整被选中的家具/物体主体比例和位置;不要恢复成原来的大尺寸,也不要返回几乎未修改的原图。",
|
||||
"请用 AI 自然重建原主体移除后露出的背景,让地毯、地板、窗户、墙面、桌面、阴影、遮挡和透视连续自然。",
|
||||
"请保留目标主体的真实材质、结构、轮廓、透视和光照;主体不能透明、幽灵化、变形、被裁切,不能变成说明图或标注元素。",
|
||||
@ -1107,6 +1119,17 @@ function buildFalScaleAdjustMockupPrompt(
|
||||
].join("\n");
|
||||
}
|
||||
|
||||
function buildFalScaleAdjustSystemPrompt() {
|
||||
return [
|
||||
"You are an image editing model for local furniture scale adjustment.",
|
||||
"Interpret the input image as a soft layout guide, not as a pixel layer to copy.",
|
||||
"Generate a clean realistic final photo by editing the target furniture itself and naturally rebuilding only the tiny surrounding background that becomes visible.",
|
||||
"Do not paste, composite, clone, crop, resize, or copy rectangular image patches from the guide.",
|
||||
"Never output guide artifacts: no blurred blocks, seams, rectangular borders, arrows, labels, masks, UI marks, or color-coded annotations.",
|
||||
"Keep the camera frame, lighting, wall, floor, rug, windows, background furniture, text, logos, and all non-target regions stable.",
|
||||
].join("\n");
|
||||
}
|
||||
|
||||
export async function POST(request: NextRequest) {
|
||||
try {
|
||||
const session = await auth();
|
||||
@ -1395,6 +1418,11 @@ export async function POST(request: NextRequest) {
|
||||
sourceWidth,
|
||||
},
|
||||
displayPrompt,
|
||||
falSafetyTolerance: useFalScaleAdjustMockup ? "6" : undefined,
|
||||
falSystemPrompt: useFalScaleAdjustMockup
|
||||
? buildFalScaleAdjustSystemPrompt()
|
||||
: undefined,
|
||||
falThinkingLevel: useFalScaleAdjustMockup ? "high" : undefined,
|
||||
imageCount: useFalScaleAdjustMockup ? 4 : 1,
|
||||
imageSize:
|
||||
typeof body.imageSize === "string" && body.imageSize.trim()
|
||||
|
||||
@ -24,6 +24,8 @@ interface FalImageParams {
|
||||
quality?: string;
|
||||
maskImageUrl?: string;
|
||||
openaiApiKey?: string;
|
||||
systemPrompt?: string;
|
||||
thinkingLevel?: string;
|
||||
syncMode?: boolean;
|
||||
}
|
||||
|
||||
@ -655,6 +657,8 @@ function buildFalRequestPayload(params: {
|
||||
const safetyTolerance = normalizeSafetyTolerance(params.imageParams?.safetyTolerance);
|
||||
const seed = normalizeSeed(params.imageParams?.seed);
|
||||
const imageCount = Math.max(1, Math.min(6, Number(params.imageParams?.imageCount || 1)));
|
||||
const systemPrompt = String(params.imageParams?.systemPrompt || "").trim();
|
||||
const thinkingLevel = String(params.imageParams?.thinkingLevel || "").trim().toLowerCase();
|
||||
const activeModelId = params.overrideModelId || params.modelId;
|
||||
const isGptImage2Edit = isFalGptImage2EditModelId(activeModelId);
|
||||
const falEndpointId = isGptImage2Edit
|
||||
@ -752,6 +756,15 @@ function buildFalRequestPayload(params: {
|
||||
}
|
||||
}
|
||||
|
||||
if (isFalNanoBananaModelId(falEndpointId)) {
|
||||
if (systemPrompt) {
|
||||
input.system_prompt = systemPrompt;
|
||||
}
|
||||
if (thinkingLevel === "minimal" || thinkingLevel === "high") {
|
||||
input.thinking_level = thinkingLevel;
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
prompt,
|
||||
imageUrls,
|
||||
|
||||
@ -67,6 +67,9 @@ export interface CanvasGenerateImageRequest {
|
||||
aspectRatio?: string;
|
||||
imageSize?: string;
|
||||
imageCount?: number;
|
||||
falSafetyTolerance?: string | number;
|
||||
falSystemPrompt?: string;
|
||||
falThinkingLevel?: "minimal" | "high";
|
||||
promptExtend?: boolean;
|
||||
referenceImage?: string;
|
||||
referenceImages?: string[];
|
||||
@ -1930,10 +1933,13 @@ export async function generateCanvasImage(
|
||||
|
||||
const imageParams = {
|
||||
aspectRatio,
|
||||
safetyTolerance: request.falSafetyTolerance,
|
||||
imageSize,
|
||||
imageCount: 1,
|
||||
negativePrompt,
|
||||
promptExtend,
|
||||
systemPrompt: request.falSystemPrompt,
|
||||
thinkingLevel: request.falThinkingLevel,
|
||||
};
|
||||
|
||||
let executeGenerateBatch:
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user