Spaces:
Sleeping
Sleeping
Download frontend/src/api/facezoomClient.ts from junyoung137/image-tool: direct link, hf CLI and curl.
- Browser
- Download file 5.48 kB
-
https://huggingface.co/spaces/junyoung137/image-tool/resolve/main/frontend/src/api/facezoomClient.ts
- Command line
-
hf download hf://spaces/junyoung137/image-tool/frontend/src/api/facezoomClient.ts
-
curl -L -o facezoomClient.ts https://huggingface.co/spaces/junyoung137/image-tool/resolve/main/frontend/src/api/facezoomClient.ts
5.48 kB
| /** | |
| * facezoomClient.ts — API client for the Face Zoom feature. | |
| * | |
| * Completely independent of client.ts, upscaleClient.ts, bgremoveClient.ts, | |
| * facerestoreClient.ts, and objectRemovalClient.ts. | |
| * Do not import from or share state with any other feature's client. | |
| * | |
| * Endpoints | |
| * --------- | |
| * POST /facezoom/upload — submit image, get job_id back immediately | |
| * GET /facezoom/result/{job_id} — poll status; completed response carries | |
| * per-face results (face_index, image URL, | |
| * codeformer_applied, similarity) | |
| * POST /facezoom/download — JSON body {job_id, face_indices} → ZIP blob | |
| * | |
| * ZIP download pattern | |
| * -------------------- | |
| * Unlike all other features (single image via GET), Face Zoom downloads a ZIP | |
| * via POST with a JSON body (because the caller selects which face indices to | |
| * include). The response is a binary blob (application/zip) — we convert it | |
| * to an object URL and trigger a temporary <a> click. A plain <a href download> | |
| * is NOT used because cross-origin binary POSTs require a fetch first. | |
| */ | |
| export const FACEZOOM_API_BASE = import.meta.env.VITE_API_BASE ?? "http://localhost:8000"; | |
| // --------------------------------------------------------------------------- | |
| // Types | |
| // --------------------------------------------------------------------------- | |
| export interface FaceZoomFaceResult { | |
| face_index: number; | |
| result_image_url: string; // full http://localhost:8000/storage/… URL | |
| codeformer_applied: boolean; // true = CodeFormer accepted; false = ESRGAN-only | |
| similarity: number; // ArcFace cosine sim (0.0–1.0) | |
| } | |
| export interface FaceZoomResult { | |
| job_id: string; | |
| status: "pending" | "processing" | "completed" | "failed"; | |
| error_message: string | null; | |
| faces: FaceZoomFaceResult[]; // empty until completed | |
| } | |
| // --------------------------------------------------------------------------- | |
| // Upload | |
| // --------------------------------------------------------------------------- | |
| /** | |
| * POST /facezoom/upload — submit an image for face detection + zoom restoration. | |
| * | |
| * Backend detects all faces, crops each with 40% margin, upscales 4× via | |
| * RealESRGAN, then runs CodeFormer with ArcFace identity check per face. | |
| * Returns job_id immediately; poll getFaceZoomResult() for per-face results. | |
| */ | |
| export async function uploadForFaceZoom(file: File): Promise<{ job_id: string }> { | |
| const formData = new FormData(); | |
| formData.append("file", file); | |
| // Send "preserve" (w=0.95) — Face Zoom targets small crops (60-80 px) where | |
| // higher fidelity avoids uncanny eye sharpening that "balanced" (w=0.85) can | |
| // introduce. No per-preset picker is exposed in the UI. | |
| formData.append("fidelity_preset", "preserve"); | |
| const response = await fetch(`${FACEZOOM_API_BASE}/facezoom/upload`, { | |
| method: "POST", | |
| body: formData, | |
| }); | |
| if (!response.ok) { | |
| let detail = `HTTP ${response.status}`; | |
| try { | |
| const json = await response.json(); | |
| if (json?.detail) detail = json.detail; | |
| } catch { /* ignore */ } | |
| throw new Error(detail); | |
| } | |
| return response.json() as Promise<{ job_id: string }>; | |
| } | |
| // --------------------------------------------------------------------------- | |
| // Result polling | |
| // --------------------------------------------------------------------------- | |
| /** | |
| * GET /facezoom/result/{job_id} — poll the current status of a face zoom job. | |
| * | |
| * When status === "completed", the faces array contains one entry per detected | |
| * face with its result image URL, whether CodeFormer was applied, and the | |
| * ArcFace cosine similarity score. The array is empty while the job is still | |
| * pending or processing. | |
| */ | |
| export async function getFaceZoomResult(jobId: string): Promise<FaceZoomResult> { | |
| const response = await fetch(`${FACEZOOM_API_BASE}/facezoom/result/${jobId}`); | |
| if (!response.ok) throw new Error(`HTTP ${response.status}`); | |
| return response.json() as Promise<FaceZoomResult>; | |
| } | |
| // --------------------------------------------------------------------------- | |
| // ZIP download | |
| // --------------------------------------------------------------------------- | |
| /** | |
| * POST /facezoom/download — download a ZIP of selected face crops. | |
| * | |
| * Sends {job_id, face_indices} as JSON, expects a binary ZIP response. | |
| * Converts the blob to an object URL and triggers a synthetic <a> click so | |
| * the browser saves it as "face_zoom_results.zip". | |
| * | |
| * @param jobId The completed job's ID. | |
| * @param faceIndices Which face indices to include (subset or all). | |
| */ | |
| export async function downloadFaceZoomZip( | |
| jobId: string, | |
| faceIndices: number[], | |
| ): Promise<void> { | |
| const response = await fetch(`${FACEZOOM_API_BASE}/facezoom/download`, { | |
| method: "POST", | |
| headers: { "Content-Type": "application/json" }, | |
| body: JSON.stringify({ job_id: jobId, face_indices: faceIndices }), | |
| }); | |
| if (!response.ok) { | |
| let detail = `HTTP ${response.status}`; | |
| try { | |
| const json = await response.json(); | |
| if (json?.detail) detail = json.detail; | |
| } catch { /* ignore */ } | |
| throw new Error(detail); | |
| } | |
| const blob = await response.blob(); | |
| const url = URL.createObjectURL(blob); | |
| const a = document.createElement("a"); | |
| a.href = url; | |
| a.download = "face_zoom_results.zip"; | |
| document.body.appendChild(a); | |
| a.click(); | |
| document.body.removeChild(a); | |
| URL.revokeObjectURL(url); | |
| } | |