fix: exclude protected people from furniture scale masks
This commit is contained in:
parent
970bd89638
commit
fa7d9d81ff
@ -1356,6 +1356,24 @@ function isScaleAdjustFurnitureProductLikePrompt(prompt: string) {
|
||||
);
|
||||
}
|
||||
|
||||
type ScaleAdjustProtectedPersonSide = "auto" | "left" | "right";
|
||||
|
||||
function getScaleAdjustProtectedPersonSide(
|
||||
prompt: string
|
||||
): ScaleAdjustProtectedPersonSide | null {
|
||||
const text = normalizeScaleAdjustModelText(prompt);
|
||||
if (
|
||||
!/person|people|human|woman|man|model|girl|boy|face|body|人物|人像|人|模特|女人|男人|女孩|男孩|脸|身体/.test(
|
||||
text
|
||||
)
|
||||
) {
|
||||
return null;
|
||||
}
|
||||
if (/right|右侧|右边|右方|右/.test(text)) return "right";
|
||||
if (/left|左侧|左边|左方|左/.test(text)) return "left";
|
||||
return "auto";
|
||||
}
|
||||
|
||||
function hasScaleAdjustPersonPixelSignal(params: {
|
||||
box: { left: number; top: number; width: number; height: number };
|
||||
raw: Buffer;
|
||||
@ -1550,11 +1568,61 @@ function buildScaleAdjustStructureInitialMask(params: {
|
||||
});
|
||||
}
|
||||
|
||||
function buildScaleAdjustProtectedPersonSeedMask(params: {
|
||||
box: { left: number; top: number; width: number; height: number };
|
||||
raw: Buffer;
|
||||
side: ScaleAdjustProtectedPersonSide;
|
||||
width: number;
|
||||
}) {
|
||||
const { box, raw, side, width } = params;
|
||||
const seed = new Uint8Array(box.width * box.height);
|
||||
let seedCount = 0;
|
||||
const sideAllowsX = (localX: number) => {
|
||||
if (side === "right") return localX >= box.width * 0.52;
|
||||
if (side === "left") return localX <= box.width * 0.48;
|
||||
return true;
|
||||
};
|
||||
|
||||
for (let localY = 0; localY < box.height; localY += 1) {
|
||||
const y = box.top + localY;
|
||||
for (let localX = 0; localX < box.width; localX += 1) {
|
||||
if (!sideAllowsX(localX)) continue;
|
||||
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);
|
||||
const likelySkin = isScaleAdjustLikelySkinPixel(r, g, b);
|
||||
const likelyWhiteFabric =
|
||||
isScaleAdjustLikelyWhiteFabricPixel(r, g, b, luma, saturation) &&
|
||||
luma < 246 &&
|
||||
saturation < 0.22;
|
||||
if (!likelySkin && !likelyWhiteFabric) continue;
|
||||
seed[localY * box.width + localX] = 255;
|
||||
seedCount += 1;
|
||||
}
|
||||
}
|
||||
|
||||
const seedRatio = seedCount / Math.max(1, box.width * box.height);
|
||||
if (seedCount < 24 || seedRatio < 0.0006) return null;
|
||||
|
||||
return dilateScaleAdjustStructureMask({
|
||||
height: box.height,
|
||||
mask: seed,
|
||||
radius: Math.max(4, Math.round(Math.min(box.width, box.height) * 0.032)),
|
||||
width: box.width,
|
||||
});
|
||||
}
|
||||
|
||||
function filterScaleAdjustStructureComponents(params: {
|
||||
box: { width: number; height: number };
|
||||
mask: Uint8Array;
|
||||
protectedPersonMask?: Uint8Array | null;
|
||||
protectedPersonSide?: ScaleAdjustProtectedPersonSide | null;
|
||||
}) {
|
||||
const { box, mask } = params;
|
||||
const { box, mask, protectedPersonMask = null, protectedPersonSide = null } =
|
||||
params;
|
||||
const width = box.width;
|
||||
const height = box.height;
|
||||
const visited = new Uint8Array(mask.length);
|
||||
@ -1582,6 +1650,7 @@ function filterScaleAdjustStructureComponents(params: {
|
||||
let minY = y;
|
||||
let maxX = x;
|
||||
let maxY = y;
|
||||
let protectedPersonOverlap = 0;
|
||||
|
||||
while (head < queueX.length) {
|
||||
const cx = queueX[head] || 0;
|
||||
@ -1592,6 +1661,9 @@ function filterScaleAdjustStructureComponents(params: {
|
||||
minY = Math.min(minY, cy);
|
||||
maxX = Math.max(maxX, cx);
|
||||
maxY = Math.max(maxY, cy);
|
||||
if (protectedPersonMask?.[cy * width + cx]) {
|
||||
protectedPersonOverlap += 1;
|
||||
}
|
||||
|
||||
for (let dy = -1; dy <= 1; dy += 1) {
|
||||
for (let dx = -1; dx <= 1; dx += 1) {
|
||||
@ -1616,7 +1688,27 @@ function filterScaleAdjustStructureComponents(params: {
|
||||
const centerYRatio = (minY + componentHeight / 2) / height;
|
||||
const touchesTop = minY <= height * 0.04;
|
||||
const touchesRight = maxX >= width * 0.88;
|
||||
const touchesLeft = minX <= width * 0.12;
|
||||
const edgeSliver = touchesRight && componentWidth < width * 0.18;
|
||||
const touchesProtectedPersonSide =
|
||||
protectedPersonSide === "right"
|
||||
? maxX >= width * 0.68
|
||||
: protectedPersonSide === "left"
|
||||
? minX <= width * 0.32
|
||||
: protectedPersonSide === "auto"
|
||||
? touchesLeft || touchesRight
|
||||
: false;
|
||||
const protectedPersonOverlapRatio =
|
||||
protectedPersonOverlap / Math.max(1, area);
|
||||
const likelyProtectedPersonComponent =
|
||||
Boolean(protectedPersonMask) &&
|
||||
protectedPersonOverlap > 0 &&
|
||||
(protectedPersonOverlapRatio >= 0.012 ||
|
||||
(touchesProtectedPersonSide &&
|
||||
protectedPersonOverlap >= Math.max(6, area * 0.002))) &&
|
||||
(touchesProtectedPersonSide ||
|
||||
componentHeight >= height * 0.16 ||
|
||||
componentWidth >= width * 0.16);
|
||||
const broadTopBackground =
|
||||
touchesTop &&
|
||||
centerYRatio <= 0.45 &&
|
||||
@ -1642,7 +1734,8 @@ function filterScaleAdjustStructureComponents(params: {
|
||||
likelyStructure &&
|
||||
centeredEnough &&
|
||||
!edgeSliver &&
|
||||
!broadTopBackground
|
||||
!broadTopBackground &&
|
||||
!likelyProtectedPersonComponent
|
||||
) {
|
||||
if (fillSurfaceComponent) {
|
||||
for (let fillY = minY; fillY <= maxY; fillY += 1) {
|
||||
@ -2446,6 +2539,7 @@ async function buildScaleAdjustStructureLockedComposite(params: {
|
||||
includePersonPixels?: boolean;
|
||||
placement: ReturnType<typeof getScaleAdjustVisualGuidePlacement>;
|
||||
preferPixelStructure?: boolean;
|
||||
protectedPersonSide?: ScaleAdjustProtectedPersonSide | null;
|
||||
raw: Buffer;
|
||||
sourceBuffer: Buffer;
|
||||
sourceImageUrl?: string;
|
||||
@ -2456,6 +2550,7 @@ async function buildScaleAdjustStructureLockedComposite(params: {
|
||||
includePersonPixels = false,
|
||||
placement,
|
||||
preferPixelStructure = false,
|
||||
protectedPersonSide = null,
|
||||
raw,
|
||||
sourceBuffer,
|
||||
sourceImageUrl,
|
||||
@ -2487,9 +2582,20 @@ async function buildScaleAdjustStructureLockedComposite(params: {
|
||||
raw,
|
||||
width: sourceWidth,
|
||||
});
|
||||
const protectedPersonMask =
|
||||
preferPixelStructure && protectedPersonSide
|
||||
? buildScaleAdjustProtectedPersonSeedMask({
|
||||
box: placement.sourceBox,
|
||||
raw,
|
||||
side: protectedPersonSide,
|
||||
width: sourceWidth,
|
||||
})
|
||||
: null;
|
||||
const mask = filterScaleAdjustStructureComponents({
|
||||
box: placement.sourceBox,
|
||||
mask: initialMask,
|
||||
protectedPersonMask,
|
||||
protectedPersonSide,
|
||||
});
|
||||
const structureBbox = getScaleAdjustMaskBoundingBox({
|
||||
height: placement.sourceBox.height,
|
||||
@ -2781,6 +2887,9 @@ async function buildScaleAdjustVisualLayoutGuideBuffer(params: {
|
||||
const preferPixelStructure = isScaleAdjustFurnitureProductLikePrompt(
|
||||
region.prompt
|
||||
);
|
||||
const protectedPersonSide = preferPixelStructure
|
||||
? getScaleAdjustProtectedPersonSide(region.prompt)
|
||||
: null;
|
||||
const hasPersonPixels =
|
||||
!preferPixelStructure &&
|
||||
!!sourceRaw &&
|
||||
@ -2797,6 +2906,7 @@ async function buildScaleAdjustVisualLayoutGuideBuffer(params: {
|
||||
includePersonPixels,
|
||||
placement,
|
||||
preferPixelStructure,
|
||||
protectedPersonSide,
|
||||
raw: sourceRaw,
|
||||
sourceBuffer,
|
||||
sourceImageUrl,
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user