diff --git a/app/api/canvas/local-edit/route.ts b/app/api/canvas/local-edit/route.ts index e5ac066c7..0a0059076 100644 --- a/app/api/canvas/local-edit/route.ts +++ b/app/api/canvas/local-edit/route.ts @@ -111,7 +111,8 @@ 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 SCALE_ADJUST_RMBG_ALPHA_CLEAN_THRESHOLD = 96; +const SCALE_ADJUST_RMBG_ALPHA_MEDIAN_SIZE = 5; const MIN_REGION_EDGE_PX = 24; const SCALE_ADJUST_OPERATION = "scale-adjust"; type LocalEditOperation = "local-edit" | typeof SCALE_ADJUST_OPERATION; @@ -1709,23 +1710,38 @@ function getScaleAdjustAlphaBoundingBox(params: { }; } -function cleanScaleAdjustAlphaChannel(params: { +async function cleanScaleAdjustAlphaChannel(params: { + height: number; rawRgba: Buffer; threshold?: number; + width: number; }) { - const { rawRgba, threshold = SCALE_ADJUST_RMBG_ALPHA_CLEAN_THRESHOLD } = params; + const { + height, + rawRgba, + threshold = SCALE_ADJUST_RMBG_ALPHA_CLEAN_THRESHOLD, + width, + } = params; const pixelCount = Math.floor(rawRgba.length / 4); + const initialAlpha = Buffer.alloc(pixelCount); + for (let index = 0; index < pixelCount; index += 1) { + const sourceAlpha = rawRgba[index * 4 + 3] || 0; + initialAlpha[index] = sourceAlpha >= threshold ? 255 : 0; + } + + const alphaData = await sharp(initialAlpha, { + raw: { channels: 1, height, width }, + }) + .median(SCALE_ADJUST_RMBG_ALPHA_MEDIAN_SIZE) + .threshold(128) + .raw() + .toBuffer(); 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))); + const nextAlpha = alphaData[index] >= 128 ? 255 : 0; cleanedRaw[alphaIndex] = nextAlpha; cleanedAlpha[index] = nextAlpha; } @@ -1807,8 +1823,10 @@ async function buildScaleAdjustRmbgStructureLockedComposite(params: { .ensureAlpha() .raw() .toBuffer(); - const { cleanedAlpha, cleanedRaw } = cleanScaleAdjustAlphaChannel({ + const { cleanedAlpha, cleanedRaw } = await cleanScaleAdjustAlphaChannel({ + height: cutoutHeight, rawRgba: cutoutRaw, + width: cutoutWidth, }); const cleanedCutout = await sharp(cleanedRaw, { raw: { channels: 4, height: cutoutHeight, width: cutoutWidth },