export interface ImageStats { width: number; height: number; mode: string; has_alpha: boolean; transparent_ratio: number; corner_alpha: number[]; premultiplied_white: boolean; /** Fraction of OPAQUE pixels still chroma-key green — a keying fringe that * prints as a green outline on the garment (#763). Optional so an older * image_stats.py that does not emit it degrades to "not measured". */ chroma_halo_ratio?: number; /** Fraction of the opaque design occupied by a solid near-black RECTANGLE — the * slab artifact some models emit (#763). Rectangularity-gated, so a black * silhouette or black linework reads 0. */ black_box_ratio?: number; } /** Measurements of a rendered product MOCKUP (not a design). See * python/mockup_stats.py for what each field means and why it exists. */ export interface MockupStats { width: number; height: number; min_dimension: number; garment_ratio: number; design_coverage: number; largest_flat_run: number; chroma_green_ratio: number; dominant_share: number; distinct_colors: number; empty: boolean; } export interface TransparencyResult { outputPath: string; /** true when all four corners keyed fully transparent (make_transparent exit 0). */ cornersClean: boolean; width?: number; height?: number; } export interface MakeTransparentOptions { /** * How to match the background. * - 'box' (default): tight color box around the auto-detected corner chroma, guarded by a * sanity check that refuses a background far from pure #00FF00 (exit 4 -> `chroma_background`). * - 'dominance': "green dominates" test — robust to the tinted / muted / gradient greens the AI * models actually produce. Only clears pixels where green clearly outweighs red AND blue, so * charcoal / white / warm (yellow, gold, orange) art is preserved. No sanity check. */ mode?: 'box' | 'dominance'; /** Bypass the box-mode chroma sanity check (`--force-chroma`). Ignored in dominance mode. */ force?: boolean; } export interface RecomposeFillOptions { /** Visible-face rectangles (fractions of the print area) the art is composed into — one per * physical face the file covers; the background still fills the whole area. A face with * `rotate180` renders inverted on the product (far side of a fold), so the art is flipped * there. Wrap-style goods: drawstring bags, sock legs, zipper wallets. */ faces?: { x: number; y: number; w: number; h: number; rotate180?: boolean; }[]; /** Compose onto a TRANSPARENT canvas (placed-style wrap goods: per-face art, transparency * preserved, transparent pixels premultiplied white). */ transparent?: boolean; } export interface EnsureResolutionResult { outputPath: string; /** true when the design was actually upscaled (false = already >= the floor, unchanged). */ upscaled: boolean; width?: number; height?: number; } export interface OcrResult { /** False = OCR could not run at all. Distinct from "ran and found nothing". */ available: boolean; text: string; /** Mean per-word confidence 0-100, or null when the engine reported none. */ confidence: number | null; /** Which engine answered — useful when a caller wants to explain a result. */ engine?: 'native' | 'wasm'; } export interface Imaging { downloadToTemp(url: string, ext?: string): Promise; makeTransparent(inputPath: string, opts?: MakeTransparentOptions): Promise; readBytes(path: string): Promise; imageSize(path: string): Promise<{ width: number; height: number; } | undefined>; /** Full quality stats (alpha, transparency, premultiply). Undefined if Python/Pillow missing. */ imageStats(path: string): Promise; /** Measure a rendered product mockup (chroma leak, blankness, resolution). */ mockupStats(path: string): Promise; /** Read text from an image. * * `confidence` is tesseract's mean per-word score (0-100), or null when the * engine could not report one. It matters as much as the text: tesseract * returns fluent-looking garbage on stylized display faces, so a caller that * treats every non-empty result as authoritative will assert a misspelling on * a design that is perfectly fine. Measured locally on a distorted design: * garbage came back at confidence 24, correct reads at 96. */ ocr(imagePath: string): Promise; /** Dominant design colors mapped to Printful's fixed embroidery thread palette (CIE Lab). */ threadColors(inputPath: string, max?: number): Promise; /** Upscale a design to a minimum long-side resolution (Lanczos, white-premultiplied) so it * clears the fulfillment platform's low-resolution QC gate. No-op if already large enough. */ ensureResolution(inputPath: string, minLongSide: number): Promise; cleanup(paths: string[]): Promise; } export declare class LocalImaging implements Imaging { private tmpRoot; private dir; downloadToTemp(url: string, ext?: string): Promise; makeTransparent(inputPath: string, opts?: MakeTransparentOptions): Promise; readBytes(path: string): Promise; imageSize(path: string): Promise<{ width: number; height: number; } | undefined>; mockupStats(path: string): Promise; imageStats(path: string): Promise; threadColors(inputPath: string, max?: number): Promise; ensureResolution(inputPath: string, minLongSide: number): Promise; ocr(imagePath: string): Promise; cleanup(paths: string[]): Promise; } /** Reconstruct text and mean word confidence from tesseract's TSV output. * * Exported for tests: the confidence number is the whole reason we ask for TSV * instead of plain text, so it needs to be verifiable without a tesseract * install. */ export declare function parseTesseractTsv(tsv: string): { text: string; confidence: number | null; };