fix: improve structure locked scale drafts

This commit is contained in:
134 2026-07-03 18:12:01 +08:00
parent d3cfb4a6b7
commit ce4e056f20

View File

@ -1149,6 +1149,51 @@ function isScaleAdjustLikelyWhiteFabricPixel(
);
}
function isScaleAdjustPersonLikePrompt(prompt: string) {
const text = normalizeScaleAdjustModelText(prompt);
return /person|people|human|woman|man|girl|boy|model|face|body|hand|arm|leg|clothing|人物|人像|真人|模特|女人|男人|女孩|男孩|小孩|脸|身体|手|胳膊|腿|衣服/.test(
text
);
}
function hasScaleAdjustPersonPixelSignal(params: {
box: { left: number; top: number; width: number; height: number };
raw: Buffer;
width: number;
}) {
const { box, raw, width } = params;
let sampled = 0;
let skin = 0;
let whiteFabric = 0;
const step = Math.max(1, Math.floor(Math.min(box.width, box.height) / 260));
for (let localY = 0; localY < box.height; localY += step) {
const y = box.top + localY;
for (let localX = 0; localX < box.width; localX += step) {
const x = box.left + localX;
const rawIndex = getScaleAdjustRawPixelIndex(width, x, y);
const r = raw[rawIndex] || 0;
const g = raw[rawIndex + 1] || 0;
const b = raw[rawIndex + 2] || 0;
const { luma, saturation } = getScaleAdjustPixelStats(r, g, b);
sampled += 1;
if (isScaleAdjustLikelySkinPixel(r, g, b)) {
skin += 1;
} else if (
isScaleAdjustLikelyWhiteFabricPixel(r, g, b, luma, saturation) &&
luma < 242
) {
whiteFabric += 1;
}
}
}
if (sampled <= 0) return false;
const skinRatio = skin / sampled;
const fabricRatio = whiteFabric / sampled;
return skin >= 18 && skinRatio >= 0.0025 && fabricRatio >= 0.035;
}
function estimateScaleAdjustBoxBackground(params: {
box: { left: number; top: number; width: number; height: number };
raw: Buffer;
@ -1230,10 +1275,11 @@ function dilateScaleAdjustStructureMask(params: {
function buildScaleAdjustStructureInitialMask(params: {
box: { left: number; top: number; width: number; height: number };
includePersonPixels?: boolean;
raw: Buffer;
width: number;
}) {
const { box, raw, width } = params;
const { box, includePersonPixels = false, raw, width } = params;
const background = estimateScaleAdjustBoxBackground(params);
const mask = new Uint8Array(box.width * box.height);
@ -1247,16 +1293,38 @@ function buildScaleAdjustStructureInitialMask(params: {
const g = raw[rawIndex + 1] || 0;
const b = raw[rawIndex + 2] || 0;
const { luma, saturation } = getScaleAdjustPixelStats(r, g, b);
if (isScaleAdjustLikelySkinPixel(r, g, b)) continue;
if (isScaleAdjustLikelyWhiteFabricPixel(r, g, b, luma, saturation)) {
continue;
}
const likelySkin = isScaleAdjustLikelySkinPixel(r, g, b);
const likelyWhiteFabric = isScaleAdjustLikelyWhiteFabricPixel(
r,
g,
b,
luma,
saturation
);
const colorDistance = Math.sqrt(
(r - background.r) ** 2 +
(g - background.g) ** 2 +
(b - background.b) ** 2
);
if (includePersonPixels && likelySkin) {
mask[localY * box.width + localX] = 255;
continue;
}
if (
includePersonPixels &&
likelyWhiteFabric &&
luma < 246 &&
(colorDistance > 12 || luma < background.luma - 4)
) {
mask[localY * box.width + localX] = 255;
continue;
}
if (likelySkin) continue;
if (likelyWhiteFabric) {
continue;
}
const darkStructure = luma < Math.min(136, background.luma - 38);
const saturatedStructure =
saturation > 0.16 && luma < 218 && colorDistance > 38;
@ -1596,14 +1664,22 @@ async function buildScaleAdjustMaskWashPatch(params: {
}
async function buildScaleAdjustStructureLockedComposite(params: {
includePersonPixels?: boolean;
placement: ReturnType<typeof getScaleAdjustVisualGuidePlacement>;
raw: Buffer;
sourceBuffer: Buffer;
sourceWidth: number;
}) {
const { placement, raw, sourceBuffer, sourceWidth } = params;
const {
includePersonPixels = false,
placement,
raw,
sourceBuffer,
sourceWidth,
} = params;
const initialMask = buildScaleAdjustStructureInitialMask({
box: placement.sourceBox,
includePersonPixels,
raw,
width: sourceWidth,
});
@ -1860,6 +1936,13 @@ async function buildScaleAdjustVisualLayoutGuideBuffer(params: {
(targetWidthFactor > 1.08 || targetHeightFactor > 1.08);
const structureLockedComposite = shouldUseStructureLock
? await buildScaleAdjustStructureLockedComposite({
includePersonPixels:
isScaleAdjustPersonLikePrompt(region.prompt) ||
hasScaleAdjustPersonPixelSignal({
box: placement.sourceBox,
raw: sourceRaw,
width: sourceWidth,
}),
placement,
raw: sourceRaw,
sourceBuffer,
@ -3154,7 +3237,8 @@ export async function POST(request: NextRequest) {
: [];
const useScaleAdjustPatchTargetDraftPrimary = Boolean(scaleAdjustPatchReference);
const includeScaleAdjustPatchSourceReference =
scaleAdjustPatchReference?.structureLockedDraft === true;
Boolean(scaleAdjustPatchReference) &&
scaleAdjustPatchReference?.structureLockedDraft !== true;
const prompt = isScaleAdjustOperation
? scaleAdjustPatchReference
? buildScaleAdjustPatchPrompt(