feat(erase-object): optional high-quality diffusion inpainting bundle (#566)

Adds an opt-in High Quality mode to the Object Eraser, backed by a new inpaint-hq feature bundle (Stable Diffusion 1.5 inpainting via diffusers). The default fast LaMa path is unchanged. Both arch archives are published to deepsafe/feature-bundles and the manifest carries their real sha256/sizes.

Verified end to end: a fresh container pulls the bundle from HuggingFace, checksum-verifies it, extracts torch/diffusers plus the fp16 model, and the HQ sidecar erases a large object with a plausible fill.

Refs #141
This commit is contained in:
SnapOtter
2026-07-19 20:47:35 +08:00
committed by GitHub
parent 84c18eb82c
commit 1bac663a2e
41 changed files with 954 additions and 35 deletions
+1
View File
@@ -17,6 +17,7 @@ export const SCRIPT_BUNDLE_MAP: Record<string, string> = {
face_landmarks: "face-detection",
red_eye_removal: "face-detection",
inpaint: "object-eraser-colorize",
inpaint_hq: "inpaint-hq",
outpaint: "object-eraser-colorize",
colorize: "object-eraser-colorize",
upscale: "upscale-enhance",
+1 -1
View File
@@ -27,7 +27,7 @@ export { enhanceFaces } from "./face-enhancement.js";
export type { FaceLandmarkPoint, FaceLandmarks, FaceLandmarksResult } from "./face-landmarks.js";
export { detectFaceLandmarks } from "./face-landmarks.js";
export { missingBundleForScript, SCRIPT_BUNDLE_MAP } from "./feature-gate.js";
export { inpaint } from "./inpainting.js";
export { type InpaintQuality, inpaint } from "./inpainting.js";
export { noiseRemoval } from "./noise-removal.js";
export type {
OcrExecutionMetadata,
+12 -1
View File
@@ -3,11 +3,21 @@ import { join } from "node:path";
import sharp from "sharp";
import { type ProgressCallback, parseStdoutJson, runPythonWithProgress } from "./bridge.js";
/**
* Inpainting backend. "fast" is the always-available LaMa ONNX path
* (`inpaint.py`); "hq" is the optional diffusion path (`inpaint_hq.py`), gated
* behind the `inpaint-hq` feature bundle. The route decides which mode to pass;
* the sidecar/per-request feature gate independently rejects "hq" when the
* bundle is absent.
*/
export type InpaintQuality = "fast" | "hq";
export async function inpaint(
inputBuffer: Buffer,
maskBuffer: Buffer,
outputDir: string,
onProgress?: ProgressCallback,
quality: InpaintQuality = "fast",
): Promise<Buffer> {
const inputPath = join(outputDir, "input_inpaint.png");
const maskPath = join(outputDir, "mask_inpaint.png");
@@ -18,7 +28,8 @@ export async function inpaint(
await writeFile(inputPath, pngInput);
await writeFile(maskPath, pngMask);
const { stdout } = await runPythonWithProgress("inpaint.py", [inputPath, maskPath, outputPath], {
const script = quality === "hq" ? "inpaint_hq.py" : "inpaint.py";
const { stdout } = await runPythonWithProgress(script, [inputPath, maskPath, outputPath], {
onProgress,
});