mirror of
https://github.com/snapotter-hq/SnapOtter.git
synced 2026-08-03 07:46:42 +02:00
feat: AI face enhancement with GFPGAN and CodeFormer (#61)
* feat(shared): add enhance-faces tool definition and i18n strings * feat(ai): add face enhancement script with GFPGAN and CodeFormer support Detects faces via MediaPipe dual-model approach, then enhances using GFPGAN (proven) or CodeFormer (via codeformer-pip) with auto fallback. Supports strength-based alpha blending with original image. * feat(ai): add TypeScript bridge for face enhancement * feat(api): add enhance-faces route with GFPGAN/CodeFormer support * feat(web): add enhance-faces settings component and register in tool registry * feat(docker): add CodeFormer dependency and model download - Add codeformer-pip to both CPU and GPU requirements - Download CodeFormer model (~375MB) at Docker build time - Add CodeFormer to smoke test verification * fix(enhance-faces): address code review findings - Skip alpha blend for CodeFormer (strength already applied via fidelity weight) - Hide "only enhance main face" checkbox when Best (CodeFormer) is selected - Fix sensitivity slider labels (swap More/Fewer faces to match actual behavior) - Register EnhanceFacesControls in pipeline step settings - Remove model names from user-facing descriptions * fix(enhance-faces): fix CodeFormer integration and Docker setup - Add codeformer-pip install to Dockerfile with --no-deps to avoid numpy 2.x conflict - Re-pin numpy==1.26.4 after codeformer-pip install - Pin codeformer-pip==0.0.4 in requirements files - Broaden auto-mode fallback to catch any Exception from CodeFormer --------- Co-authored-by: stirling-image <stirling-image@users.noreply.github.com>
This commit is contained in:
co-authored by
stirling-image
parent
9ddeac92b6
commit
8071fe61c5
@@ -0,0 +1,47 @@
|
||||
import { readFile, writeFile } from "node:fs/promises";
|
||||
import { join } from "node:path";
|
||||
import { type ProgressCallback, runPythonWithProgress } from "./bridge.js";
|
||||
|
||||
export interface EnhanceFacesOptions {
|
||||
model?: "auto" | "gfpgan" | "codeformer";
|
||||
strength?: number;
|
||||
onlyCenterFace?: boolean;
|
||||
sensitivity?: number;
|
||||
}
|
||||
|
||||
export interface EnhanceFacesResult {
|
||||
buffer: Buffer;
|
||||
facesDetected: number;
|
||||
faces: Array<{ x: number; y: number; w: number; h: number }>;
|
||||
model: string;
|
||||
}
|
||||
|
||||
export async function enhanceFaces(
|
||||
inputBuffer: Buffer,
|
||||
outputDir: string,
|
||||
options: EnhanceFacesOptions = {},
|
||||
onProgress?: ProgressCallback,
|
||||
): Promise<EnhanceFacesResult> {
|
||||
const inputPath = join(outputDir, "input_enhance_faces.png");
|
||||
const outputPath = join(outputDir, "output_enhance_faces.png");
|
||||
|
||||
await writeFile(inputPath, inputBuffer);
|
||||
const { stdout } = await runPythonWithProgress(
|
||||
"enhance_faces.py",
|
||||
[inputPath, outputPath, JSON.stringify(options)],
|
||||
{ onProgress },
|
||||
);
|
||||
|
||||
const result = JSON.parse(stdout);
|
||||
if (!result.success) {
|
||||
throw new Error(result.error || "Face enhancement failed");
|
||||
}
|
||||
|
||||
const buffer = await readFile(outputPath);
|
||||
return {
|
||||
buffer,
|
||||
facesDetected: result.facesDetected,
|
||||
faces: result.faces ?? [],
|
||||
model: result.model ?? "unknown",
|
||||
};
|
||||
}
|
||||
@@ -3,6 +3,7 @@ export { isGpuAvailable, shutdownDispatcher } from "./bridge.js";
|
||||
export { colorize } from "./colorization.js";
|
||||
export type { DetectFacesResult, FaceRegion } from "./face-detection.js";
|
||||
export { blurFaces, detectFaces } from "./face-detection.js";
|
||||
export { enhanceFaces } from "./face-enhancement.js";
|
||||
export { inpaint } from "./inpainting.js";
|
||||
export { noiseRemoval } from "./noise-removal.js";
|
||||
export { extractText } from "./ocr.js";
|
||||
|
||||
Reference in New Issue
Block a user