fix(canvas): quarantine unstable detailed inspection behind explicit opt-in
This commit is contained in:
parent
d228d01244
commit
9a5ed91e32
@ -41,7 +41,7 @@ async function stageImage(image: Omit<PreparedImage, "url">, role: "template" |
|
||||
return { ...image, url: await entry.url };
|
||||
}
|
||||
|
||||
export async function prepareLayoutReplaceInputs(sourceUrl: string, referenceUrl: string) {
|
||||
export async function prepareLayoutReplaceInputs(sourceUrl: string, referenceUrl: string, options?: { checkText?: boolean }) {
|
||||
if (sourceUrl === referenceUrl) throw new Error("新商品图和目标模版不能是同一张图片");
|
||||
const [originalSource, originalTemplate] = await Promise.all([
|
||||
readImage(sourceUrl), readImage(referenceUrl),
|
||||
@ -52,7 +52,7 @@ export async function prepareLayoutReplaceInputs(sourceUrl: string, referenceUrl
|
||||
// source so an incorrect crop cannot silently redefine the product identity.
|
||||
const [{ box, identity }, textBoundingBoxes] = await Promise.all([
|
||||
locateLayoutReplaceProduct(originalSource.buffer, originalTemplate.buffer),
|
||||
locateLayoutReplaceTemplateText(originalTemplate.buffer),
|
||||
options?.checkText === false ? Promise.resolve([]) : locateLayoutReplaceTemplateText(originalTemplate.buffer),
|
||||
]);
|
||||
const crop = layoutReplaceProductCrop(box, originalSource.width, originalSource.height);
|
||||
const productBuffer = await sharp(originalSource.buffer).extract(crop).png().toBuffer();
|
||||
|
||||
@ -135,7 +135,8 @@ export async function executeCanvasLayoutReplaceTask(params: {
|
||||
throw new Error("AI 模版替换未配置可用的非 FAL 多图编辑模型");
|
||||
}
|
||||
|
||||
const inputs = await prepareLayoutReplaceInputs(sourceImageUrl, referenceImageUrl);
|
||||
const detailedChecks = process.env.CANVAS_LAYOUT_REPLACE_DETAILED_CHECKS === "1";
|
||||
const inputs = await prepareLayoutReplaceInputs(sourceImageUrl, referenceImageUrl, { checkText: detailedChecks });
|
||||
const referenceGeometry = inputs.referenceGeometry;
|
||||
const sourceProfile = await resolveCanvasLayoutReplaceSourceProfile(sourceImageUrl, inputs.source.buffer);
|
||||
const sourceProductAspectRatio = sourceProfile?.silhouetteAspectRatio || inputs.productAspectRatio;
|
||||
@ -200,6 +201,7 @@ export async function executeCanvasLayoutReplaceTask(params: {
|
||||
verify: result => verifyCanvasLayoutReplacement({
|
||||
sourceBuffer: inputs.sourceOriginalBuffer, templateBuffer: inputs.template.buffer,
|
||||
sourceIdentity: inputs.sourceIdentity,
|
||||
detailedChecks,
|
||||
productBuffer: inputs.source.buffer,
|
||||
textBoundingBoxes: inputs.textBoundingBoxes,
|
||||
resultUrl: result.images[0].originalUrl,
|
||||
|
||||
@ -112,6 +112,7 @@ export async function verifyCanvasLayoutReplacement(params: {
|
||||
sourceIdentity?: string;
|
||||
productBuffer?: Buffer;
|
||||
textBoundingBoxes?: NormalizedReplacementBox[];
|
||||
detailedChecks?: boolean;
|
||||
}) {
|
||||
const resultBuffer = params.resultBuffer || await fetchImageBufferFromUrl(params.resultUrl);
|
||||
const prompt = [LAYOUT_REPLACE_QUALITY_PROMPT, params.sourceIdentity
|
||||
@ -119,6 +120,9 @@ export async function verifyCanvasLayoutReplacement(params: {
|
||||
const board = await makeLayoutReplaceQualityBoard({ template: params.templateBuffer, source: params.sourceBuffer,
|
||||
product: params.productBuffer || params.sourceBuffer, result: resultBuffer });
|
||||
const quality = await inspectLayoutImages([board], prompt, parseLayoutReplaceQuality);
|
||||
// Fine-grained OCR/support classification is experimental: real fixtures
|
||||
// showed provider-dependent false rejections. Keep it opt-in until stable.
|
||||
if (params.detailedChecks === false) return quality;
|
||||
if (quality.sourceProductPreserved) {
|
||||
// Read the actual result product at the same scale as the source crop.
|
||||
// Poster copy and floor shadows must not obscure its support structure.
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user