UNPKG

ppu-ocv

Version:

A type-safe, modular, chainable image processing library built on top of OpenCV.js with a fluent API leveraging pipeline processing.

186 lines (185 loc) 8.01 kB
import type { CanvasLike } from "./canvas-factory.js"; import type { DetectedRegion, FindRegionsOptions } from "./canvas-regions.js"; export type { DetectedRegion } from "./canvas-regions.js"; /** * Canvas-native image processing with no OpenCV dependency. * * Provides two distinct APIs: * - **Static I/O** (`prepareCanvas`, `prepareBuffer`) — format conversion helpers. * - **Chainable instance operations** (`resize`, `grayscale`, `convert`, `invert`, * `threshold`, `border`, `rotate`) — lightweight canvas-native pipeline, usable * in constrained environments where OpenCV cannot run. * - **Region detection** (`findRegions`) — 8-connected flood-fill bbox detection * on binary images, with optional padding and coordinate scaling. * * @example * ```ts * import { CanvasProcessor } from "ppu-ocv/canvas"; * * const canvas = await CanvasProcessor.prepareCanvas(arrayBuffer); * * // Pixel pipeline * const result = new CanvasProcessor(canvas) * .resize({ width: 800, height: 600 }) * .grayscale() * .threshold({ thresh: 127 }) * .border({ size: 10, color: "white" }) * .toCanvas(); * * // Region detection (equivalent to findContours + boundingRect) * const regions = new CanvasProcessor(binaryCanvas).findRegions({ * foreground: "light", * minArea: 20, * padding: { vertical: 0.4, horizontal: 0.6 }, * scale: originalWidth / processedWidth, * }); * ``` */ export declare class CanvasProcessor { private _canvas; /** Create a processor wrapping the given canvas. */ constructor(source: CanvasLike); /** Current canvas width in pixels. */ get width(): number; /** Current canvas height in pixels. */ get height(): number; /** * Scale the canvas to new dimensions. * Uses the platform's native drawImage interpolation (bilinear in most runtimes). */ resize(options: { width: number; height: number; }): this; /** * Convert to grayscale using BT.601 luma coefficients * (matches OpenCV's COLOR_RGBA2GRAY: 0.299R + 0.587G + 0.114B). * * The result is still RGBA — R, G, and B channels all equal the luma value. * The alpha channel is preserved unchanged. */ grayscale(): this; /** * Apply a linear per-pixel transformation: `dst = clamp(alpha * src + beta)`. * Applies independently to R, G, and B channels; alpha channel is unchanged. * * This is the canvas-native equivalent of OpenCV's `Mat.convertTo(dst, rtype, alpha, beta)`, * limited to the pixel-math aspect. `rtype` is not applicable here — canvas ImageData * is always 8-bit RGBA (`Uint8ClampedArray`), so type conversion has no meaning. * * Useful for brightness/contrast adjustment: * - `alpha > 1` increases contrast * - `beta > 0` increases brightness * - `alpha = 0.5, beta = 0` halves contrast */ convert(options?: { alpha?: number; beta?: number; }): this; /** * Invert all RGB channels: `dst = 255 - src`. * Alpha channel is preserved unchanged. * Equivalent to OpenCV's `cv.bitwise_not`. */ invert(): this; /** * Apply binary threshold: pixels with luma above `thresh` become `maxValue`, * all others become 0. * * Equivalent to OpenCV's `cv.threshold(src, dst, thresh, maxval, THRESH_BINARY)`. * Operates on the luma of each pixel (R channel is used directly when the * image is already grayscale, i.e. R === G === B). * * Note: Otsu's automatic threshold (`THRESH_OTSU`) is not supported * canvas-natively — use a fixed `thresh` value instead. */ threshold(options?: { thresh?: number; maxValue?: number; }): this; /** * Add a uniform border around the canvas. * Equivalent to OpenCV's `cv.copyMakeBorder` with `BORDER_CONSTANT`. * * @param options.size Border width in pixels (default 10) * @param options.color CSS color string for the border fill (default "white") */ border(options?: { size?: number; color?: string; }): this; /** * Rotate the canvas around its centre (or a custom pivot) while keeping the * original canvas dimensions. Pixels that fall outside the bounds after * rotation are clipped (transparent). * * Positive `angle` rotates counter-clockwise, matching the convention used * by OpenCV's `getRotationMatrix2D`. * * @param options.angle Rotation angle in degrees * @param options.cx Pivot X (default: canvas centre) * @param options.cy Pivot Y (default: canvas centre) */ rotate(options: { angle: number; cx?: number; cy?: number; }): this; /** * Detect connected regions on a binary (black-and-white) canvas and return * their bounding boxes and pixel areas. * * Uses an 8-connected DFS flood-fill — equivalent to OpenCV's * `findContours` with `RETR_LIST + CHAIN_APPROX_SIMPLE` on a binary image, * returning bounding-box level information. * * Best called after `.grayscale().threshold()` to ensure a clean binary input. * * @param options.foreground Which pixel tone is the foreground to detect: * `"light"` (white regions, default) or `"dark"` (black regions). * @param options.thresh Luma threshold that separates foreground from background (default 127). * For `foreground: "light"`: pixel is foreground when `r > thresh`. * For `foreground: "dark"`: pixel is foreground when `r <= thresh`. * **Use `thresh: 0` when working on a resized binary image** — resizing * introduces anti-aliased gray border pixels (values 1–127) that the * default threshold would miss, matching OpenCV's behaviour of treating * any non-zero pixel as contour-adjacent. * @param options.minArea Ignore regions smaller than this many pixels (default 1). * @param options.maxArea Ignore regions larger than this many pixels (default unlimited). * @param options.padding Expand each detected bbox by a fraction of its **height**. * Mirrors `extractBoxesFromContours` padding logic: * `vertical` and `horizontal` are both applied as * `Math.round(bboxHeight × factor)` and clamped to the canvas bounds. * Default: no padding. * @param options.scale Multiply all bbox coordinates by this factor after padding. * Use `originalWidth / processedWidth` (i.e. `1 / resizeRatio`) * to convert from a resized canvas back to original image coordinates. * Default: 1 (no scaling). * * @example * ```ts * // Direct equivalent of extractBoxesFromContours with default padding: * const regions = new CanvasProcessor(binaryCanvas).findRegions({ * foreground: "light", * minArea: 20, * padding: { vertical: 0.4, horizontal: 0.6 }, * scale: originalWidth / processedWidth, * }); * ``` */ findRegions(options?: FindRegionsOptions): DetectedRegion[]; /** * Return the current canvas state. */ toCanvas(): CanvasLike; /** * Convert an ArrayBuffer (image file bytes) or string URI to a CanvasLike. * If the value is already a CanvasLike it is returned as-is. */ static prepareCanvas(file: ArrayBuffer | string | CanvasLike): Promise<CanvasLike>; /** * Convert a CanvasLike to an ArrayBuffer (PNG bytes). * If the value is already an ArrayBuffer it is returned as-is. */ static prepareBuffer(canvas: CanvasLike): Promise<ArrayBuffer>; }