ppu-ocv 3.1.0 → 3.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,10 +1,10 @@
1
1
  # ppu-ocv
2
2
 
3
- A type-safe, modular, chainable image processing library built on top of OpenCV.js with a fluent API leveraging pipeline processing.
3
+ [![NPM](https://img.shields.io/npm/dw/ppu-ocv)](https://www.npmjs.com/package/ppu-ocv) [![JSR](https://jsr.io/badges/@snowfluke/ppu-ocv)](https://jsr.io/@snowfluke/ppu-ocv)
4
4
 
5
- ![ppu-ocv pipeline demo](https://raw.githubusercontent.com/PT-Perkasa-Pilar-Utama/ppu-ocv/refs/heads/main/assets/ppu-ocv-demo.jpg)
5
+ A type-safe, modular, chainable image processing library built on top of OpenCV.js with a fluent API leveraging pipeline processing. Decoupled canvas utilities run anywhere — Node, Bun, browsers, browser extensions, and service workers — with or without OpenCV.
6
6
 
7
- Image manipulation as easy as:
7
+ ![ppu-ocv pipeline demo](https://raw.githubusercontent.com/PT-Perkasa-Pilar-Utama/ppu-ocv/refs/heads/main/assets/ppu-ocv-demo.jpg)
8
8
 
9
9
  ```ts
10
10
  const processor = new ImageProcessor(canvas);
@@ -17,23 +17,33 @@ const result = processor
17
17
  .dilate({ size: [20, 20], iter: 5 })
18
18
  .toCanvas();
19
19
 
20
- // Memory cleanup
21
20
  processor.destroy();
22
21
  ```
23
22
 
24
- This work is based on https://github.com/TechStark/opencv-js.
23
+ Based on [TechStark/opencv-js](https://github.com/TechStark/opencv-js).
24
+
25
+ ## Table of Contents
25
26
 
26
- ## Why use this library?
27
+ - [Why ppu-ocv?](#why-ppu-ocv)
28
+ - [Installation](#installation)
29
+ - [Usage (Node.js / Bun)](#usage-nodejs--bun)
30
+ - [Canvas-only Usage (no OpenCV)](#canvas-only-usage-no-opencv)
31
+ - [Web / Browser Support](#web--browser-support)
32
+ - [Built-in Pipeline Operations](#built-in-pipeline-operations)
33
+ - [Extending Operations](#extending-operations)
34
+ - [Class Documentation](#class-documentation)
35
+ - [Migrating from v2](#migrating-from-v2)
36
+ - [Contributing](#contributing)
37
+ - [License](#license)
27
38
 
28
- OpenCV is powerful but can be cumbersome to use directly. This library provides:
39
+ ## Why ppu-ocv?
29
40
 
30
- 1. **Simplified API**: Transform complex OpenCV calls into simple chainable methods
31
- 2. **Reduced Boilerplate**: No need to manage memory, conversions, or dimensions manually
32
- 3. **Development Speed**: Add image processing to your app in minutes, not hours
33
- 4. **Extensibility**: Custom operations for your specific needs without library modifications
34
- 5. **TypeScript Integration**: Full IntelliSense support with parameter validation
35
- 6. **Web Support**: Supports running directly in the browser
36
- 7. **Loosely Coupled**: Canvas utilities are fully decoupled from OpenCV. Usable in Browser Extensions, Service Workers, and other constrained environments where OpenCV cannot be initialised
41
+ - **Simplified API** chainable methods that hide OpenCV's verbose Mat allocation
42
+ - **No memory management** automatic Mat lifecycle within the pipeline
43
+ - **Type-safe** full TypeScript inference for operations and options
44
+ - **Extensible** register custom operations with `registry.register(...)` without forking
45
+ - **Cross-platform** same API in Node, Bun, browsers, and constrained runtimes
46
+ - **Loosely coupled** canvas utilities work standalone; OpenCV is only loaded when actually needed
37
47
 
38
48
  ## Installation
39
49
 
@@ -125,9 +135,7 @@ const buffer = await CanvasProcessor.prepareBuffer(cropped);
125
135
  import { CanvasProcessor, CanvasToolkit } from "ppu-ocv/canvas-web";
126
136
 
127
137
  const response = await fetch("/image.jpg");
128
- const canvas = await CanvasProcessor.prepareCanvas(
129
- await response.arrayBuffer(),
130
- );
138
+ const canvas = await CanvasProcessor.prepareCanvas(await response.arrayBuffer());
131
139
  ```
132
140
 
133
141
  ## Web / Browser Support
@@ -171,9 +179,7 @@ processor.destroy();
171
179
  await ImageProcessor.initRuntime();
172
180
 
173
181
  const response = await fetch("/my-image.jpg");
174
- const canvas = await CanvasProcessor.prepareCanvas(
175
- await response.arrayBuffer(),
176
- );
182
+ const canvas = await CanvasProcessor.prepareCanvas(await response.arrayBuffer());
177
183
 
178
184
  const processor = new ImageProcessor(canvas);
179
185
  processor
@@ -296,8 +302,8 @@ regions.sort((a, b) => b.area - a.area); // largest first
296
302
 
297
303
  **Region detection** (returns data, does not mutate)
298
304
 
299
- | Method | Options | Description |
300
- | ------------- | ------------------------------------------------ | ------------------------------------------------------------ |
305
+ | Method | Options | Description |
306
+ | ------------- | ---------------------------------------------------------------------------------------- | -------------------------------------------------------------- |
301
307
  | `findRegions` | `foreground?` (`"light"`), `thresh?` (127), `minArea?`, `maxArea?`, `padding?`, `scale?` | 8-connected flood-fill on a binary canvas → `DetectedRegion[]` |
302
308
 
303
309
  `DetectedRegion` shape: `{ bbox: BoundingBox, area: number }` where `bbox` is `{ x0, y0, x1, y1 }` (x1/y1 exclusive). Equivalent to OpenCV's `findContours(RETR_EXTERNAL) + boundingRect` — all matched bboxes agree within ±1 px on solid binary images. ³
@@ -368,37 +374,24 @@ A collection of utility functions for analyzing image properties (requires OpenC
368
374
 
369
375
  ## Contributing
370
376
 
371
- Contributions are welcome! If you would like to contribute, please follow these steps:
377
+ See [CONTRIBUTING.md](./CONTRIBUTING.md) for the full guide setup, commit conventions, quality checks, and PR flow. Also:
372
378
 
373
- 1. **Fork the Repository:** Create your own fork of the project.
374
- 2. **Create a Feature Branch:** Use a descriptive branch name for your changes.
375
- 3. **Implement Changes:** Make your modifications, add tests, and ensure everything passes.
376
- 4. **Submit a Pull Request:** Open a pull request to discuss your changes and get feedback.
379
+ - [Code of Conduct](./CODE_OF_CONDUCT.md) community standards.
380
+ - [Security policy](./SECURITY.md) how to report vulnerabilities privately.
381
+ - [Issue tracker](https://github.com/PT-Perkasa-Pilar-Utama/ppu-ocv/issues) bug reports, feature requests, and docs gaps each have a template.
377
382
 
378
- ### Running Tests
379
-
380
- This project uses Bun for testing. To run the tests locally, execute:
383
+ Quick local commands:
381
384
 
382
385
  ```bash
383
- bun test
386
+ bun install
387
+ bun test # run unit tests
388
+ bun run fmt # check formatting
389
+ bun run lint # check lint
390
+ bun run type-check # tsgo --noEmit
391
+ bun task build # emit ./lib
392
+ bun task bench # micro-bench the operations registry
384
393
  ```
385
394
 
386
- Ensure that all tests pass before submitting your pull request.
387
-
388
- ## Scripts
389
-
390
- Recommended development environment is in a Linux-based environment.
391
-
392
- Library template: https://github.com/aquapi/lib-template
393
-
394
- ### [Build](./scripts/build.ts)
395
-
396
- Emit `.js` and `.d.ts` files to [`lib`](./lib).
397
-
398
- ### [Publish](./scripts/publish.ts)
399
-
400
- Move [`package.json`](./package.json), [`README.md`](./README.md) to [`lib`](./lib) and publish the package.
401
-
402
395
  ## Migrating from v2
403
396
 
404
397
  See [MIGRATION.md](./MIGRATION.md) for a full guide. The short version:
@@ -1,6 +1,6 @@
1
1
  export type { BoundingBox, Coordinate, Points } from "./index.interface.js";
2
2
  export { getPlatform, setPlatform } from "./canvas-factory.js";
3
- export type { CanvasLike, CanvasPlatform, Context2DLike, } from "./canvas-factory.js";
3
+ export type { CanvasLike, CanvasPlatform, Context2DLike } from "./canvas-factory.js";
4
4
  export { webPlatform } from "./platform/web.js";
5
5
  export { CanvasToolkitBase as CanvasToolkit, CanvasToolkitBase, type ContourLike, } from "./canvas-toolkit.base.js";
6
6
  export { CanvasProcessor, type DetectedRegion } from "./canvas-processor.js";
package/index.canvas.d.ts CHANGED
@@ -2,7 +2,7 @@ export { Canvas, createCanvas, ImageData, loadImage } from "@napi-rs/canvas";
2
2
  export type { SKRSContext2D } from "@napi-rs/canvas";
3
3
  export type { BoundingBox, Coordinate, Points } from "./index.interface.js";
4
4
  export { getPlatform, setPlatform } from "./canvas-factory.js";
5
- export type { CanvasLike, CanvasPlatform, Context2DLike, } from "./canvas-factory.js";
5
+ export type { CanvasLike, CanvasPlatform, Context2DLike } from "./canvas-factory.js";
6
6
  export { CanvasToolkitBase, type ContourLike } from "./canvas-toolkit.base.js";
7
7
  export { CanvasToolkit } from "./canvas-toolkit.js";
8
8
  export { CanvasProcessor, type DetectedRegion } from "./canvas-processor.js";
package/index.d.ts CHANGED
@@ -3,9 +3,9 @@ export { cv };
3
3
  export { Canvas, createCanvas, ImageData, loadImage } from "@napi-rs/canvas";
4
4
  export type { SKRSContext2D } from "@napi-rs/canvas";
5
5
  export type { BoundingBox, Coordinate, Points } from "./index.interface.js";
6
- export { executeOperation, OperationRegistry, registry, } from "./pipeline/index.js";
6
+ export { executeOperation, OperationRegistry, registry } from "./pipeline/index.js";
7
7
  export { getPlatform, setPlatform } from "./canvas-factory.js";
8
- export type { CanvasLike, CanvasPlatform, Context2DLike, } from "./canvas-factory.js";
8
+ export type { CanvasLike, CanvasPlatform, Context2DLike } from "./canvas-factory.js";
9
9
  export { CanvasToolkitBase, type ContourLike } from "./canvas-toolkit.base.js";
10
10
  export { CanvasToolkit } from "./canvas-toolkit.js";
11
11
  export { CanvasProcessor, type DetectedRegion } from "./canvas-processor.js";
package/index.web.d.ts CHANGED
@@ -1,10 +1,10 @@
1
1
  import { cv } from "./cv-provider.js";
2
2
  export { cv };
3
3
  export { getPlatform, setPlatform } from "./canvas-factory.js";
4
- export type { CanvasLike, CanvasPlatform, Context2DLike, } from "./canvas-factory.js";
4
+ export type { CanvasLike, CanvasPlatform, Context2DLike } from "./canvas-factory.js";
5
5
  export { webPlatform } from "./platform/web.js";
6
6
  export type { BoundingBox, Coordinate, Points } from "./index.interface.js";
7
- export { executeOperation, OperationRegistry, registry, } from "./pipeline/index.js";
7
+ export { executeOperation, OperationRegistry, registry } from "./pipeline/index.js";
8
8
  export { CanvasToolkitBase as CanvasToolkit, CanvasToolkitBase, type ContourLike, } from "./canvas-toolkit.base.js";
9
9
  export { CanvasProcessor, type DetectedRegion } from "./canvas-processor.js";
10
10
  export { Contours } from "./contours.js";
@@ -1,10 +1,5 @@
1
1
  import { cv } from "../cv-provider.js";
2
2
  import type { OperationResult, PartialOptions } from "../pipeline/types.js";
3
- declare module "../pipeline/types" {
4
- interface RegisteredOperations {
5
- adaptiveThreshold: AdaptiveThresholdOptions;
6
- }
7
- }
8
3
  export interface AdaptiveThresholdOptions extends PartialOptions {
9
4
  /** Upper threshold value (0-255) */
10
5
  upper: number;
@@ -1,10 +1,5 @@
1
1
  import { cv } from "../cv-provider.js";
2
2
  import type { OperationResult, PartialOptions } from "../pipeline/types.js";
3
- declare module "../pipeline/types" {
4
- interface RegisteredOperations {
5
- blur: BlurOptions;
6
- }
7
- }
8
3
  export interface BlurOptions extends PartialOptions {
9
4
  /** Size of the blur [x, y] */
10
5
  size: [number, number];
@@ -1,10 +1,5 @@
1
1
  import type { OperationResult, PartialOptions } from "../pipeline/types.js";
2
2
  import { cv } from "../cv-provider.js";
3
- declare module "../pipeline/types" {
4
- interface RegisteredOperations {
5
- border: BorderOptions;
6
- }
7
- }
8
3
  export interface BorderOptions extends PartialOptions {
9
4
  /** Size of the border in pixels */
10
5
  size: number;
@@ -1,10 +1,5 @@
1
1
  import type { OperationResult, PartialOptions } from "../pipeline/types.js";
2
2
  import { cv } from "../cv-provider.js";
3
- declare module "../pipeline/types" {
4
- interface RegisteredOperations {
5
- canny: CannyOptions;
6
- }
7
- }
8
3
  export interface CannyOptions extends PartialOptions {
9
4
  /** Lower threshold for the hysteresis procedure (0-255) */
10
5
  lower: number;
@@ -1,10 +1,5 @@
1
1
  import type { OperationResult, RequiredOptions } from "../pipeline/types.js";
2
2
  import { cv } from "../cv-provider.js";
3
- declare module "../pipeline/types" {
4
- interface RegisteredOperations {
5
- convert: ConvertOptions;
6
- }
7
- }
8
3
  export interface ConvertOptions extends RequiredOptions {
9
4
  /** Desired matrix type (cv.CV_...) if negative, it will be the same as input */
10
5
  rtype: number;
@@ -1,10 +1,5 @@
1
1
  import type { OperationResult, PartialOptions } from "../pipeline/types.js";
2
2
  import { cv } from "../cv-provider.js";
3
- declare module "../pipeline/types" {
4
- interface RegisteredOperations {
5
- dilate: DilateOptions;
6
- }
7
- }
8
3
  export interface DilateOptions extends PartialOptions {
9
4
  /** Size of the block [x, y] */
10
5
  size: [number, number];
@@ -1,10 +1,5 @@
1
1
  import type { OperationResult, PartialOptions } from "../pipeline/types.js";
2
2
  import { cv } from "../cv-provider.js";
3
- declare module "../pipeline/types" {
4
- interface RegisteredOperations {
5
- erode: ErodeOptions;
6
- }
7
- }
8
3
  export interface ErodeOptions extends PartialOptions {
9
4
  /** Size of the block [x, y] */
10
5
  size: [number, number];
@@ -1,10 +1,5 @@
1
1
  import type { OperationResult, PartialOptions } from "../pipeline/types.js";
2
2
  import { cv } from "../cv-provider.js";
3
- declare module "../pipeline/types" {
4
- interface RegisteredOperations {
5
- grayscale: GrayscaleOptions;
6
- }
7
- }
8
3
  export interface GrayscaleOptions extends PartialOptions {
9
4
  }
10
5
  export declare function grayscale(img: cv.Mat, options: GrayscaleOptions): OperationResult;
@@ -1,10 +1,5 @@
1
1
  import type { OperationResult, PartialOptions } from "../pipeline/types.js";
2
2
  import { cv } from "../cv-provider.js";
3
- declare module "../pipeline/types" {
4
- interface RegisteredOperations {
5
- invert: InvertOptions;
6
- }
7
- }
8
3
  export interface InvertOptions extends PartialOptions {
9
4
  }
10
5
  export declare function invert(img: cv.Mat, options: InvertOptions): OperationResult;
@@ -1,10 +1,5 @@
1
1
  import type { OperationResult, PartialOptions } from "../pipeline/types.js";
2
2
  import { cv } from "../cv-provider.js";
3
- declare module "../pipeline/types" {
4
- interface RegisteredOperations {
5
- morphologicalGradient: MorphologicalGradientOptions;
6
- }
7
- }
8
3
  export interface MorphologicalGradientOptions extends PartialOptions {
9
4
  /** Kernel size for the morphological gradient operation [x, y] */
10
5
  size: [number, number];
@@ -1,10 +1,5 @@
1
1
  import type { OperationResult, RequiredOptions } from "../pipeline/types.js";
2
2
  import { cv } from "../cv-provider.js";
3
- declare module "../pipeline/types" {
4
- interface RegisteredOperations {
5
- resize: ResizeOptions;
6
- }
7
- }
8
3
  export interface ResizeOptions extends RequiredOptions {
9
4
  /** Width of the resized image */
10
5
  width: number;
@@ -1,10 +1,5 @@
1
1
  import type { OperationResult, RequiredOptions } from "../pipeline/types.js";
2
2
  import { cv } from "../cv-provider.js";
3
- declare module "../pipeline/types" {
4
- interface RegisteredOperations {
5
- rotate: RotateOptions;
6
- }
7
- }
8
3
  export interface RotateOptions extends RequiredOptions {
9
4
  /** Angle of rotation in degrees (positive for counter-clockwise) */
10
5
  angle: number;
@@ -1,10 +1,5 @@
1
1
  import type { OperationResult, PartialOptions } from "../pipeline/types.js";
2
2
  import { cv } from "../cv-provider.js";
3
- declare module "../pipeline/types" {
4
- interface RegisteredOperations {
5
- threshold: ThresholdOptions;
6
- }
7
- }
8
3
  export interface ThresholdOptions extends PartialOptions {
9
4
  /** Lower threshold value (0-255) */
10
5
  lower: number;
@@ -1,11 +1,6 @@
1
1
  import { cv } from "../cv-provider.js";
2
2
  import type { BoundingBox, Points } from "../index.interface.js";
3
3
  import type { OperationResult, RequiredOptions } from "../pipeline/types.js";
4
- declare module "../pipeline/types" {
5
- interface RegisteredOperations {
6
- warp: WarpOptions;
7
- }
8
- }
9
4
  export interface WarpOptions extends RequiredOptions {
10
5
  /** Four points of the source image containing x and y point in
11
6
  * topLeft, topRight, bottomLeft and BottomRight.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ppu-ocv",
3
- "version": "3.1.0",
3
+ "version": "3.1.2",
4
4
  "description": "A type-safe, modular, chainable image processing library built on top of OpenCV.js with a fluent API leveraging pipeline processing.",
5
5
  "keywords": [
6
6
  "open-cv",
@@ -45,21 +45,29 @@
45
45
  },
46
46
  "scripts": {
47
47
  "task": "bun scripts/task.ts",
48
+ "build": "bun task build",
48
49
  "build:test": "bun task build && bun test",
49
50
  "build:publish": "bun task build && bun task report-size && bun task publish",
50
- "lint": "prettier --check ./src",
51
- "lint:fix": "prettier --write ./src",
52
- "demo": "bunx -y serve -l 4567 ."
51
+ "type-check": "bunx tsgo --noEmit",
52
+ "test": "bun test",
53
+ "lint": "oxlint",
54
+ "lint:fix": "oxlint --fix",
55
+ "fmt": "oxfmt --check",
56
+ "fmt:fix": "oxfmt .",
57
+ "demo": "bunx -y serve -l 4567 .",
58
+ "prepare": "husky"
53
59
  },
54
60
  "devDependencies": {
55
- "@stylistic/eslint-plugin": "latest",
56
61
  "@types/bun": "latest",
57
62
  "@types/uglify-js": "latest",
63
+ "@typescript/native-preview": "^7.0.0-dev.20260513.1",
64
+ "husky": "^9.1.7",
65
+ "lint-staged": "^16.0.0",
58
66
  "mitata": "latest",
59
- "prettier": "^3.8.1",
67
+ "oxfmt": "^0.48.0",
68
+ "oxlint": "^1.63.0",
60
69
  "tsx": "latest",
61
70
  "typescript": "latest",
62
- "typescript-eslint": "latest",
63
71
  "uglify-js": ">=2.4.24"
64
72
  },
65
73
  "repository": {
@@ -1,4 +1,4 @@
1
- import { cv } from "../cv-provider.js";
1
+ import type { cv } from "../cv-provider.js";
2
2
  import type { OperationFunction, OperationName, OperationOptions, OperationResult } from "./index.js";
3
3
  export declare class OperationRegistry {
4
4
  private operations;
@@ -1,4 +1,18 @@
1
- import { cv } from "../cv-provider.js";
1
+ import type { cv } from "../cv-provider.js";
2
+ import type { AdaptiveThresholdOptions } from "../operations/adaptive-threshold.js";
3
+ import type { BlurOptions } from "../operations/blur.js";
4
+ import type { BorderOptions } from "../operations/border.js";
5
+ import type { CannyOptions } from "../operations/canny.js";
6
+ import type { ConvertOptions } from "../operations/convert.js";
7
+ import type { DilateOptions } from "../operations/dilate.js";
8
+ import type { ErodeOptions } from "../operations/erode.js";
9
+ import type { GrayscaleOptions } from "../operations/grayscale.js";
10
+ import type { InvertOptions } from "../operations/invert.js";
11
+ import type { MorphologicalGradientOptions } from "../operations/morphological-gradient.js";
12
+ import type { ResizeOptions } from "../operations/resize.js";
13
+ import type { RotateOptions } from "../operations/rotate.js";
14
+ import type { ThresholdOptions } from "../operations/threshold.js";
15
+ import type { WarpOptions } from "../operations/warp.js";
2
16
  export interface OperationResult {
3
17
  img: cv.Mat;
4
18
  width: number;
@@ -14,11 +28,32 @@ export interface PartialOptions {
14
28
  }
15
29
  export type OperationFunction<T> = (img: cv.Mat, options: T) => OperationResult;
16
30
  /**
17
- * @description
18
- * Central registry mapping operation names to their specific option types.
19
- * Operation modules MUST augment this interface.
31
+ * Central registry mapping operation names to their option types. Each entry
32
+ * is the options type exported by the corresponding `src/operations/*.ts`
33
+ * file. Adding a new operation requires three changes: create the file,
34
+ * export the Options type, and add the entry below.
35
+ *
36
+ * Previously this used `declare module` augmentation so each operation file
37
+ * could register itself. JSR rejects that pattern because it modifies global
38
+ * types, so the registry is now explicit. Consumers can still extend this
39
+ * interface from their own code via `declare module "ppu-ocv"` — that's why
40
+ * it stays an interface rather than a type alias.
20
41
  */
21
42
  export interface RegisteredOperations {
43
+ adaptiveThreshold: AdaptiveThresholdOptions;
44
+ blur: BlurOptions;
45
+ border: BorderOptions;
46
+ canny: CannyOptions;
47
+ convert: ConvertOptions;
48
+ dilate: DilateOptions;
49
+ erode: ErodeOptions;
50
+ grayscale: GrayscaleOptions;
51
+ invert: InvertOptions;
52
+ morphologicalGradient: MorphologicalGradientOptions;
53
+ resize: ResizeOptions;
54
+ rotate: RotateOptions;
55
+ threshold: ThresholdOptions;
56
+ warp: WarpOptions;
22
57
  }
23
58
  export type OperationName = keyof RegisteredOperations;
24
59
  export type OperationOptions<N extends OperationName> = RegisteredOperations[N];