fix: clean cutout alpha for scale drafts

This commit is contained in:
134 2026-07-03 20:02:00 +08:00
parent ea52d65971
commit 27b0098bf9

View File

@ -111,6 +111,7 @@ const SCALE_ADJUST_STRUCTURE_LOCK_MIN_MASK_RATIO = 0.006;
const SCALE_ADJUST_STRUCTURE_LOCK_COMPONENT_MIN_RATIO = 0.00018;
const SCALE_ADJUST_RMBG_MIN_ALPHA_RATIO = 0.012;
const SCALE_ADJUST_RMBG_MAX_SOLID_ALPHA_RATIO = 0.92;
const SCALE_ADJUST_RMBG_ALPHA_CLEAN_THRESHOLD = 42;
const MIN_REGION_EDGE_PX = 24;
const SCALE_ADJUST_OPERATION = "scale-adjust";
type LocalEditOperation = "local-edit" | typeof SCALE_ADJUST_OPERATION;
@ -1708,17 +1709,28 @@ function getScaleAdjustAlphaBoundingBox(params: {
};
}
function extractScaleAdjustAlphaChannel(params: {
height: number;
function cleanScaleAdjustAlphaChannel(params: {
rawRgba: Buffer;
width: number;
threshold?: number;
}) {
const { height, rawRgba, width } = params;
const alpha = Buffer.alloc(width * height);
for (let index = 0; index < width * height; index += 1) {
alpha[index] = rawRgba[index * 4 + 3] || 0;
const { rawRgba, threshold = SCALE_ADJUST_RMBG_ALPHA_CLEAN_THRESHOLD } = params;
const pixelCount = Math.floor(rawRgba.length / 4);
const cleanedRaw = Buffer.from(rawRgba);
const cleanedAlpha = Buffer.alloc(pixelCount);
const range = Math.max(1, 255 - threshold);
for (let index = 0; index < pixelCount; index += 1) {
const alphaIndex = index * 4 + 3;
const sourceAlpha = rawRgba[alphaIndex] || 0;
const nextAlpha =
sourceAlpha < threshold
? 0
: Math.max(0, Math.min(255, Math.round(((sourceAlpha - threshold) / range) * 255)));
cleanedRaw[alphaIndex] = nextAlpha;
cleanedAlpha[index] = nextAlpha;
}
return alpha;
return { cleanedAlpha, cleanedRaw };
}
async function resizeScaleAdjustAlphaMask(params: {
@ -1795,9 +1807,17 @@ async function buildScaleAdjustRmbgStructureLockedComposite(params: {
.ensureAlpha()
.raw()
.toBuffer();
const { cleanedAlpha, cleanedRaw } = cleanScaleAdjustAlphaChannel({
rawRgba: cutoutRaw,
});
const cleanedCutout = await sharp(cleanedRaw, {
raw: { channels: 4, height: cutoutHeight, width: cutoutWidth },
})
.png({ compressionLevel: 6 })
.toBuffer();
const alphaBbox = getScaleAdjustAlphaBoundingBox({
height: cutoutHeight,
rawRgba: cutoutRaw,
rawRgba: cleanedRaw,
width: cutoutWidth,
});
if (!alphaBbox) return null;
@ -1816,11 +1836,7 @@ async function buildScaleAdjustRmbgStructureLockedComposite(params: {
const sameSizeCutout =
Math.abs(cutoutWidth - placement.sourceBox.width) <= 2 &&
Math.abs(cutoutHeight - placement.sourceBox.height) <= 2;
const fullAlphaMask = extractScaleAdjustAlphaChannel({
height: cutoutHeight,
rawRgba: cutoutRaw,
width: cutoutWidth,
});
const fullAlphaMask = cleanedAlpha;
const sourceMaskBuffer = sameSizeCutout
? fullAlphaMask
: await resizeScaleAdjustAlphaMask({
@ -1852,7 +1868,7 @@ async function buildScaleAdjustRmbgStructureLockedComposite(params: {
maskBuffer: Buffer.from(sourceWashMask),
});
const subjectCutout = await sharp(normalizedCutout, { failOn: "none" })
const subjectCutout = await sharp(cleanedCutout, { failOn: "none" })
.extract(alphaBbox)
.png({ compressionLevel: 6 })
.toBuffer();