mirror of
https://github.com/snapotter-hq/SnapOtter.git
synced 2026-08-03 07:46:42 +02:00
feat(noise-removal): SOTA noise removal with 4 quality tiers (#57)
* feat(noise-removal): register tool in shared constants and i18n * feat(noise-removal): add SCUNet and NAFNet model architectures * feat(noise-removal): add Python denoising engine with 4 quality tiers * feat(noise-removal): add TypeScript bridge for Python sidecar * feat(noise-removal): add frontend settings with 4-tier selector * feat(noise-removal): register in tool registry and pipeline * feat(noise-removal): add Fastify API route with Zod validation * feat(noise-removal): add SCUNet and NAFNet model downloads to Docker build * test(noise-removal): add to e2e tool page rendering tests * test(noise-removal): add integration tests for API endpoint * style: fix biome formatting and import ordering * fix(noise-removal): use correct model download URLs NAFNet model is hosted on HuggingFace, not GitHub releases. Also align SCUNet URL to use the KAIR releases (same as Docker build). * fix(noise-removal): remove emojis from tier selector, simplify labels Drop emoji icons from Quick/Balanced/Quality/Maximum buttons. Replace technical algorithm names with plain descriptions users can understand. --------- Co-authored-by: stirling-image <stirling-image@users.noreply.github.com>
This commit is contained in:
co-authored by
stirling-image
parent
61794dca2d
commit
dfffc0a8cc
@@ -4,6 +4,7 @@ export { colorize } from "./colorization.js";
|
||||
export type { DetectFacesResult, FaceRegion } from "./face-detection.js";
|
||||
export { blurFaces, detectFaces } from "./face-detection.js";
|
||||
export { inpaint } from "./inpainting.js";
|
||||
export { noiseRemoval } from "./noise-removal.js";
|
||||
export { extractText } from "./ocr.js";
|
||||
export { seamCarve } from "./seam-carving.js";
|
||||
export { upscale } from "./upscaling.js";
|
||||
|
||||
@@ -0,0 +1,52 @@
|
||||
import { readFile, writeFile } from "node:fs/promises";
|
||||
import { join } from "node:path";
|
||||
import { type ProgressCallback, runPythonWithProgress } from "./bridge.js";
|
||||
|
||||
export interface NoiseRemovalOptions {
|
||||
tier?: string;
|
||||
strength?: number;
|
||||
detailPreservation?: number;
|
||||
colorNoise?: number;
|
||||
format?: string;
|
||||
quality?: number;
|
||||
}
|
||||
|
||||
export interface NoiseRemovalResult {
|
||||
buffer: Buffer;
|
||||
width: number;
|
||||
height: number;
|
||||
format: string;
|
||||
tier: string;
|
||||
}
|
||||
|
||||
export async function noiseRemoval(
|
||||
inputBuffer: Buffer,
|
||||
outputDir: string,
|
||||
options: NoiseRemovalOptions = {},
|
||||
onProgress?: ProgressCallback,
|
||||
): Promise<NoiseRemovalResult> {
|
||||
const inputPath = join(outputDir, "input_denoise.png");
|
||||
const outputPath = join(outputDir, "output_denoise.png");
|
||||
|
||||
await writeFile(inputPath, inputBuffer);
|
||||
const { stdout } = await runPythonWithProgress(
|
||||
"noise_removal.py",
|
||||
[inputPath, outputPath, JSON.stringify(options)],
|
||||
{ onProgress },
|
||||
);
|
||||
|
||||
const result = JSON.parse(stdout);
|
||||
if (!result.success) {
|
||||
throw new Error(result.error || "Noise removal failed");
|
||||
}
|
||||
|
||||
const actualOutputPath = result.output_path || outputPath;
|
||||
const buffer = await readFile(actualOutputPath);
|
||||
return {
|
||||
buffer,
|
||||
width: result.width,
|
||||
height: result.height,
|
||||
format: result.format ?? "png",
|
||||
tier: result.tier ?? options.tier ?? "balanced",
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user