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
TypeScript
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>;
}