ppu-ocv 3.0.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 +91 -46
- package/canvas-factory.d.ts +6 -0
- package/canvas-processor.d.ts +185 -3
- package/canvas-processor.js +1 -1
- package/index.canvas-web.d.ts +2 -2
- package/index.canvas.d.ts +2 -2
- package/index.d.ts +3 -3
- package/index.web.d.ts +3 -3
- package/operations/adaptive-threshold.d.ts +0 -5
- package/operations/blur.d.ts +0 -5
- package/operations/border.d.ts +0 -5
- package/operations/canny.d.ts +0 -5
- package/operations/convert.d.ts +0 -5
- package/operations/dilate.d.ts +0 -5
- package/operations/erode.d.ts +0 -5
- package/operations/grayscale.d.ts +0 -5
- package/operations/invert.d.ts +0 -5
- package/operations/morphological-gradient.d.ts +0 -5
- package/operations/resize.d.ts +0 -5
- package/operations/rotate.d.ts +0 -5
- package/operations/threshold.d.ts +0 -5
- package/operations/warp.d.ts +0 -5
- package/package.json +15 -7
- package/pipeline/registry.d.ts +1 -1
- package/pipeline/types.d.ts +39 -4
package/README.md
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
# ppu-ocv
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
[](https://www.npmjs.com/package/ppu-ocv) [](https://jsr.io/@snowfluke/ppu-ocv)
|
|
4
4
|
|
|
5
|
-
|
|
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
|
-
|
|
7
|
+

|
|
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
|
-
|
|
23
|
+
Based on [TechStark/opencv-js](https://github.com/TechStark/opencv-js).
|
|
25
24
|
|
|
26
|
-
##
|
|
25
|
+
## Table of Contents
|
|
27
26
|
|
|
28
|
-
|
|
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)
|
|
29
38
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
39
|
+
## Why ppu-ocv?
|
|
40
|
+
|
|
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
|
|
@@ -251,13 +257,65 @@ See: [How to extend ppu-ocv operations](./docs/how-to-extend-ppu-ocv-operations.
|
|
|
251
257
|
|
|
252
258
|
#### `CanvasProcessor`
|
|
253
259
|
|
|
254
|
-
Canvas
|
|
260
|
+
Canvas-native image processing with **no OpenCV dependency**. Available from all entry points including `ppu-ocv/canvas` and `ppu-ocv/canvas-web`. Provides a chainable instance API alongside static I/O helpers.
|
|
261
|
+
|
|
262
|
+
```ts
|
|
263
|
+
const result = new CanvasProcessor(canvas)
|
|
264
|
+
.resize({ width: 360, height: 640 })
|
|
265
|
+
.grayscale()
|
|
266
|
+
.threshold({ thresh: 127 })
|
|
267
|
+
.invert()
|
|
268
|
+
.border({ size: 10, color: "white" })
|
|
269
|
+
.toCanvas();
|
|
270
|
+
|
|
271
|
+
// Detect connected white regions on a binary image
|
|
272
|
+
const regions = new CanvasProcessor(binaryCanvas).findRegions({
|
|
273
|
+
foreground: "light",
|
|
274
|
+
minArea: 20,
|
|
275
|
+
// thresh: 0 ← use on resized binary images to match OpenCV (any non-zero pixel = foreground)
|
|
276
|
+
// padding: { vertical: 0.4, horizontal: 0.6 } ← expand bbox by fraction of height
|
|
277
|
+
// scale: 1 / resizeRatio ← map coords back to original image space
|
|
278
|
+
});
|
|
279
|
+
regions.sort((a, b) => b.area - a.area); // largest first
|
|
280
|
+
// regions[0] → { bbox: { x0, y0, x1, y1 }, area }
|
|
281
|
+
```
|
|
282
|
+
|
|
283
|
+
**Static I/O**
|
|
255
284
|
|
|
256
285
|
| Method | Args | Description |
|
|
257
286
|
| ---------------------- | ----------- | ----------------------------------------------------- |
|
|
258
287
|
| static `prepareCanvas` | ArrayBuffer | Load image bytes into a `CanvasLike` |
|
|
259
288
|
| static `prepareBuffer` | CanvasLike | Export a `CanvasLike` to an `ArrayBuffer` (PNG bytes) |
|
|
260
289
|
|
|
290
|
+
**Instance operations** (chainable, return `this`)
|
|
291
|
+
|
|
292
|
+
| Method | Options | OpenCV equivalent | Fidelity |
|
|
293
|
+
| ----------- | ---------------------------------- | ------------------------- | -------------- |
|
|
294
|
+
| `resize` | `width`, `height` | `cv.resize` INTER_LINEAR | 1:1 (↓), ≈ (↑) |
|
|
295
|
+
| `grayscale` | — | `COLOR_RGBA2GRAY` | **1:1** |
|
|
296
|
+
| `convert` | `alpha?`, `beta?` | `Mat.convertTo` (α·x + β) | **1:1** |
|
|
297
|
+
| `invert` | — | `cv.bitwise_not` | **1:1** ¹ |
|
|
298
|
+
| `threshold` | `thresh?` (127), `maxValue?` (255) | `THRESH_BINARY` | **1:1** |
|
|
299
|
+
| `border` | `size?` (10), `color?` (CSS) | `BORDER_CONSTANT` | **1:1** |
|
|
300
|
+
| `rotate` | `angle`, `cx?`, `cy?` | `warpAffine` | ≈ (±6 px) ² |
|
|
301
|
+
| `toCanvas` | — | — | — |
|
|
302
|
+
|
|
303
|
+
**Region detection** (returns data, does not mutate)
|
|
304
|
+
|
|
305
|
+
| Method | Options | Description |
|
|
306
|
+
| ------------- | ---------------------------------------------------------------------------------------- | -------------------------------------------------------------- |
|
|
307
|
+
| `findRegions` | `foreground?` (`"light"`), `thresh?` (127), `minArea?`, `maxArea?`, `padding?`, `scale?` | 8-connected flood-fill on a binary canvas → `DetectedRegion[]` |
|
|
308
|
+
|
|
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. ³
|
|
310
|
+
|
|
311
|
+
**`thresh` option** — pixel value threshold for foreground detection (default `127`). For resized binary images, use `thresh: 0` so anti-aliased border pixels (values 1–127) are included as foreground, matching OpenCV's non-zero threshold. With `thresh: 0` + `padding` + `scale`, full-pipeline IoU vs OpenCV is **98.4%** (all 21/21 boxes matched).
|
|
312
|
+
|
|
313
|
+
> ¹ Canvas `invert` preserves the alpha channel; OpenCV `bitwise_not` also inverts alpha. Results are identical when the source is opaque (alpha=255).
|
|
314
|
+
>
|
|
315
|
+
> ² Canvas uses anti-aliased bilinear interpolation; OpenCV uses plain bilinear. Difference is visually imperceptible and has no impact on OCR quality.
|
|
316
|
+
>
|
|
317
|
+
> ³ `RETR_LIST` may return additional inner-hole contours for white regions that contain dark sub-regions; `findRegions` counts each connected white component once regardless of interior holes.
|
|
318
|
+
|
|
261
319
|
#### `ImageProcessor`
|
|
262
320
|
|
|
263
321
|
Requires OpenCV. Available from `ppu-ocv` and `ppu-ocv/web`.
|
|
@@ -316,37 +374,24 @@ A collection of utility functions for analyzing image properties (requires OpenC
|
|
|
316
374
|
|
|
317
375
|
## Contributing
|
|
318
376
|
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
1. **Fork the Repository:** Create your own fork of the project.
|
|
322
|
-
2. **Create a Feature Branch:** Use a descriptive branch name for your changes.
|
|
323
|
-
3. **Implement Changes:** Make your modifications, add tests, and ensure everything passes.
|
|
324
|
-
4. **Submit a Pull Request:** Open a pull request to discuss your changes and get feedback.
|
|
377
|
+
See [CONTRIBUTING.md](./CONTRIBUTING.md) for the full guide — setup, commit conventions, quality checks, and PR flow. Also:
|
|
325
378
|
|
|
326
|
-
|
|
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.
|
|
327
382
|
|
|
328
|
-
|
|
383
|
+
Quick local commands:
|
|
329
384
|
|
|
330
385
|
```bash
|
|
331
|
-
bun
|
|
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
|
|
332
393
|
```
|
|
333
394
|
|
|
334
|
-
Ensure that all tests pass before submitting your pull request.
|
|
335
|
-
|
|
336
|
-
## Scripts
|
|
337
|
-
|
|
338
|
-
Recommended development environment is in a Linux-based environment.
|
|
339
|
-
|
|
340
|
-
Library template: https://github.com/aquapi/lib-template
|
|
341
|
-
|
|
342
|
-
### [Build](./scripts/build.ts)
|
|
343
|
-
|
|
344
|
-
Emit `.js` and `.d.ts` files to [`lib`](./lib).
|
|
345
|
-
|
|
346
|
-
### [Publish](./scripts/publish.ts)
|
|
347
|
-
|
|
348
|
-
Move [`package.json`](./package.json), [`README.md`](./README.md) to [`lib`](./lib) and publish the package.
|
|
349
|
-
|
|
350
395
|
## Migrating from v2
|
|
351
396
|
|
|
352
397
|
See [MIGRATION.md](./MIGRATION.md) for a full guide. The short version:
|
package/canvas-factory.d.ts
CHANGED
|
@@ -29,6 +29,12 @@ export interface Context2DLike {
|
|
|
29
29
|
strokeRect(x: number, y: number, w: number, h: number): void;
|
|
30
30
|
strokeStyle: string | CanvasGradient | CanvasPattern;
|
|
31
31
|
lineWidth: number;
|
|
32
|
+
fillStyle: string | CanvasGradient | CanvasPattern;
|
|
33
|
+
fillRect(x: number, y: number, w: number, h: number): void;
|
|
34
|
+
save(): void;
|
|
35
|
+
restore(): void;
|
|
36
|
+
translate(x: number, y: number): void;
|
|
37
|
+
rotate(angle: number): void;
|
|
32
38
|
}
|
|
33
39
|
/** Platform-specific canvas operations */
|
|
34
40
|
export interface CanvasPlatform {
|
package/canvas-processor.d.ts
CHANGED
|
@@ -1,10 +1,192 @@
|
|
|
1
|
+
import type { BoundingBox } from "./index.interface.js";
|
|
1
2
|
import type { CanvasLike } from "./canvas-factory.js";
|
|
2
3
|
/**
|
|
3
|
-
*
|
|
4
|
-
|
|
5
|
-
|
|
4
|
+
* A detected region returned by {@link CanvasProcessor.findRegions}.
|
|
5
|
+
*/
|
|
6
|
+
export interface DetectedRegion {
|
|
7
|
+
/** Axis-aligned bounding box of the region (x1/y1 are exclusive). */
|
|
8
|
+
bbox: BoundingBox;
|
|
9
|
+
/** Number of foreground pixels in the region. */
|
|
10
|
+
area: number;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Canvas-native image processing with no OpenCV dependency.
|
|
14
|
+
*
|
|
15
|
+
* Provides two distinct APIs:
|
|
16
|
+
* - **Static I/O** (`prepareCanvas`, `prepareBuffer`) — format conversion helpers.
|
|
17
|
+
* - **Chainable instance operations** (`resize`, `grayscale`, `convert`, `invert`,
|
|
18
|
+
* `threshold`, `border`, `rotate`) — lightweight canvas-native pipeline, usable
|
|
19
|
+
* in constrained environments where OpenCV cannot run.
|
|
20
|
+
* - **Region detection** (`findRegions`) — 8-connected flood-fill bbox detection
|
|
21
|
+
* on binary images, with optional padding and coordinate scaling.
|
|
22
|
+
*
|
|
23
|
+
* @example
|
|
24
|
+
* ```ts
|
|
25
|
+
* import { CanvasProcessor } from "ppu-ocv/canvas";
|
|
26
|
+
*
|
|
27
|
+
* const canvas = await CanvasProcessor.prepareCanvas(arrayBuffer);
|
|
28
|
+
*
|
|
29
|
+
* // Pixel pipeline
|
|
30
|
+
* const result = new CanvasProcessor(canvas)
|
|
31
|
+
* .resize({ width: 800, height: 600 })
|
|
32
|
+
* .grayscale()
|
|
33
|
+
* .threshold({ thresh: 127 })
|
|
34
|
+
* .border({ size: 10, color: "white" })
|
|
35
|
+
* .toCanvas();
|
|
36
|
+
*
|
|
37
|
+
* // Region detection (equivalent to findContours + boundingRect)
|
|
38
|
+
* const regions = new CanvasProcessor(binaryCanvas).findRegions({
|
|
39
|
+
* foreground: "light",
|
|
40
|
+
* minArea: 20,
|
|
41
|
+
* padding: { vertical: 0.4, horizontal: 0.6 },
|
|
42
|
+
* scale: originalWidth / processedWidth,
|
|
43
|
+
* });
|
|
44
|
+
* ```
|
|
6
45
|
*/
|
|
7
46
|
export declare class CanvasProcessor {
|
|
47
|
+
private _canvas;
|
|
48
|
+
constructor(source: CanvasLike);
|
|
49
|
+
get width(): number;
|
|
50
|
+
get height(): number;
|
|
51
|
+
/**
|
|
52
|
+
* Scale the canvas to new dimensions.
|
|
53
|
+
* Uses the platform's native drawImage interpolation (bilinear in most runtimes).
|
|
54
|
+
*/
|
|
55
|
+
resize(options: {
|
|
56
|
+
width: number;
|
|
57
|
+
height: number;
|
|
58
|
+
}): this;
|
|
59
|
+
/**
|
|
60
|
+
* Convert to grayscale using BT.601 luma coefficients
|
|
61
|
+
* (matches OpenCV's COLOR_RGBA2GRAY: 0.299R + 0.587G + 0.114B).
|
|
62
|
+
*
|
|
63
|
+
* The result is still RGBA — R, G, and B channels all equal the luma value.
|
|
64
|
+
* The alpha channel is preserved unchanged.
|
|
65
|
+
*/
|
|
66
|
+
grayscale(): this;
|
|
67
|
+
/**
|
|
68
|
+
* Apply a linear per-pixel transformation: `dst = clamp(alpha * src + beta)`.
|
|
69
|
+
* Applies independently to R, G, and B channels; alpha channel is unchanged.
|
|
70
|
+
*
|
|
71
|
+
* This is the canvas-native equivalent of OpenCV's `Mat.convertTo(dst, rtype, alpha, beta)`,
|
|
72
|
+
* limited to the pixel-math aspect. `rtype` is not applicable here — canvas ImageData
|
|
73
|
+
* is always 8-bit RGBA (`Uint8ClampedArray`), so type conversion has no meaning.
|
|
74
|
+
*
|
|
75
|
+
* Useful for brightness/contrast adjustment:
|
|
76
|
+
* - `alpha > 1` increases contrast
|
|
77
|
+
* - `beta > 0` increases brightness
|
|
78
|
+
* - `alpha = 0.5, beta = 0` halves contrast
|
|
79
|
+
*/
|
|
80
|
+
convert(options?: {
|
|
81
|
+
alpha?: number;
|
|
82
|
+
beta?: number;
|
|
83
|
+
}): this;
|
|
84
|
+
/**
|
|
85
|
+
* Invert all RGB channels: `dst = 255 - src`.
|
|
86
|
+
* Alpha channel is preserved unchanged.
|
|
87
|
+
* Equivalent to OpenCV's `cv.bitwise_not`.
|
|
88
|
+
*/
|
|
89
|
+
invert(): this;
|
|
90
|
+
/**
|
|
91
|
+
* Apply binary threshold: pixels with luma above `thresh` become `maxValue`,
|
|
92
|
+
* all others become 0.
|
|
93
|
+
*
|
|
94
|
+
* Equivalent to OpenCV's `cv.threshold(src, dst, thresh, maxval, THRESH_BINARY)`.
|
|
95
|
+
* Operates on the luma of each pixel (R channel is used directly when the
|
|
96
|
+
* image is already grayscale, i.e. R === G === B).
|
|
97
|
+
*
|
|
98
|
+
* Note: Otsu's automatic threshold (`THRESH_OTSU`) is not supported
|
|
99
|
+
* canvas-natively — use a fixed `thresh` value instead.
|
|
100
|
+
*/
|
|
101
|
+
threshold(options?: {
|
|
102
|
+
thresh?: number;
|
|
103
|
+
maxValue?: number;
|
|
104
|
+
}): this;
|
|
105
|
+
/**
|
|
106
|
+
* Add a uniform border around the canvas.
|
|
107
|
+
* Equivalent to OpenCV's `cv.copyMakeBorder` with `BORDER_CONSTANT`.
|
|
108
|
+
*
|
|
109
|
+
* @param options.size Border width in pixels (default 10)
|
|
110
|
+
* @param options.color CSS color string for the border fill (default "white")
|
|
111
|
+
*/
|
|
112
|
+
border(options?: {
|
|
113
|
+
size?: number;
|
|
114
|
+
color?: string;
|
|
115
|
+
}): this;
|
|
116
|
+
/**
|
|
117
|
+
* Rotate the canvas around its centre (or a custom pivot) while keeping the
|
|
118
|
+
* original canvas dimensions. Pixels that fall outside the bounds after
|
|
119
|
+
* rotation are clipped (transparent).
|
|
120
|
+
*
|
|
121
|
+
* Positive `angle` rotates counter-clockwise, matching the convention used
|
|
122
|
+
* by OpenCV's `getRotationMatrix2D`.
|
|
123
|
+
*
|
|
124
|
+
* @param options.angle Rotation angle in degrees
|
|
125
|
+
* @param options.cx Pivot X (default: canvas centre)
|
|
126
|
+
* @param options.cy Pivot Y (default: canvas centre)
|
|
127
|
+
*/
|
|
128
|
+
rotate(options: {
|
|
129
|
+
angle: number;
|
|
130
|
+
cx?: number;
|
|
131
|
+
cy?: number;
|
|
132
|
+
}): this;
|
|
133
|
+
/**
|
|
134
|
+
* Detect connected regions on a binary (black-and-white) canvas and return
|
|
135
|
+
* their bounding boxes and pixel areas.
|
|
136
|
+
*
|
|
137
|
+
* Uses an 8-connected DFS flood-fill — equivalent to OpenCV's
|
|
138
|
+
* `findContours` with `RETR_LIST + CHAIN_APPROX_SIMPLE` on a binary image,
|
|
139
|
+
* returning bounding-box level information.
|
|
140
|
+
*
|
|
141
|
+
* Best called after `.grayscale().threshold()` to ensure a clean binary input.
|
|
142
|
+
*
|
|
143
|
+
* @param options.foreground Which pixel tone is the foreground to detect:
|
|
144
|
+
* `"light"` (white regions, default) or `"dark"` (black regions).
|
|
145
|
+
* @param options.thresh Luma threshold that separates foreground from background (default 127).
|
|
146
|
+
* For `foreground: "light"`: pixel is foreground when `r > thresh`.
|
|
147
|
+
* For `foreground: "dark"`: pixel is foreground when `r <= thresh`.
|
|
148
|
+
* **Use `thresh: 0` when working on a resized binary image** — resizing
|
|
149
|
+
* introduces anti-aliased gray border pixels (values 1–127) that the
|
|
150
|
+
* default threshold would miss, matching OpenCV's behaviour of treating
|
|
151
|
+
* any non-zero pixel as contour-adjacent.
|
|
152
|
+
* @param options.minArea Ignore regions smaller than this many pixels (default 1).
|
|
153
|
+
* @param options.maxArea Ignore regions larger than this many pixels (default unlimited).
|
|
154
|
+
* @param options.padding Expand each detected bbox by a fraction of its **height**.
|
|
155
|
+
* Mirrors `extractBoxesFromContours` padding logic:
|
|
156
|
+
* `vertical` and `horizontal` are both applied as
|
|
157
|
+
* `Math.round(bboxHeight × factor)` and clamped to the canvas bounds.
|
|
158
|
+
* Default: no padding.
|
|
159
|
+
* @param options.scale Multiply all bbox coordinates by this factor after padding.
|
|
160
|
+
* Use `originalWidth / processedWidth` (i.e. `1 / resizeRatio`)
|
|
161
|
+
* to convert from a resized canvas back to original image coordinates.
|
|
162
|
+
* Default: 1 (no scaling).
|
|
163
|
+
*
|
|
164
|
+
* @example
|
|
165
|
+
* ```ts
|
|
166
|
+
* // Direct equivalent of extractBoxesFromContours with default padding:
|
|
167
|
+
* const regions = new CanvasProcessor(binaryCanvas).findRegions({
|
|
168
|
+
* foreground: "light",
|
|
169
|
+
* minArea: 20,
|
|
170
|
+
* padding: { vertical: 0.4, horizontal: 0.6 },
|
|
171
|
+
* scale: originalWidth / processedWidth,
|
|
172
|
+
* });
|
|
173
|
+
* ```
|
|
174
|
+
*/
|
|
175
|
+
findRegions(options?: {
|
|
176
|
+
foreground?: "light" | "dark";
|
|
177
|
+
thresh?: number;
|
|
178
|
+
minArea?: number;
|
|
179
|
+
maxArea?: number;
|
|
180
|
+
padding?: {
|
|
181
|
+
vertical?: number;
|
|
182
|
+
horizontal?: number;
|
|
183
|
+
};
|
|
184
|
+
scale?: number;
|
|
185
|
+
}): DetectedRegion[];
|
|
186
|
+
/**
|
|
187
|
+
* Return the current canvas state.
|
|
188
|
+
*/
|
|
189
|
+
toCanvas(): CanvasLike;
|
|
8
190
|
/**
|
|
9
191
|
* Convert an ArrayBuffer (image file bytes) to a CanvasLike.
|
|
10
192
|
* If the value is already a CanvasLike it is returned as-is.
|
package/canvas-processor.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export class CanvasProcessor{static async prepareCanvas(file){if(getPlatform().isCanvas(file))return file;return getPlatform().loadImage(file)}static async prepareBuffer(canvas){if(canvas instanceof ArrayBuffer)return canvas;if(typeof canvas.toBuffer==="function"){let buffer=canvas.toBuffer("image/png");let arrayBuffer=new ArrayBuffer(buffer.byteLength);new Uint8Array(arrayBuffer).set(new Uint8Array(buffer));return arrayBuffer}if(typeof canvas.toDataURL==="function"){let dataURL=canvas.toDataURL("image/png");let base64Data=dataURL.replace(/^data:image\/png;base64,/,"");let binaryString=atob(base64Data);let arrayBuffer=new ArrayBuffer(binaryString.length);let bytes=new Uint8Array(arrayBuffer);for(let i=0;i<binaryString.length;i++){bytes[i]=binaryString.charCodeAt(i)}return arrayBuffer}let ctx=canvas.getContext("2d");let imageData=ctx.getImageData(0,0,canvas.width,canvas.height);let canvasBuffer=new ArrayBuffer(imageData.data.byteLength);new Uint8Array(canvasBuffer).set(new Uint8Array(imageData.data.buffer,imageData.data.byteOffset,imageData.data.byteLength));return canvasBuffer}}import{getPlatform}from"./canvas-factory.js";
|
|
1
|
+
export class CanvasProcessor{_canvas;constructor(source){this._canvas=source}get width(){return this._canvas.width}get height(){return this._canvas.height}resize(options){const{width,height}=options;let dst=getPlatform().createCanvas(width,height);dst.getContext("2d").drawImage(this._canvas,0,0,width,height);this._canvas=dst;return this}grayscale(){const{width,height}=this._canvas;let imageData=this._canvas.getContext("2d").getImageData(0,0,width,height);let d=imageData.data;for(let i=0;i<d.length;i+=4){let luma=Math.round(0.299*d[i]+0.587*d[i+1]+0.114*d[i+2]);d[i]=luma;d[i+1]=luma;d[i+2]=luma}let dst=getPlatform().createCanvas(width,height);dst.getContext("2d").putImageData(imageData,0,0);this._canvas=dst;return this}convert(options={}){const{alpha=1,beta=0}=options;if(alpha===1&&beta===0)return this;const{width,height}=this._canvas;let imageData=this._canvas.getContext("2d").getImageData(0,0,width,height);let d=imageData.data;for(let i=0;i<d.length;i+=4){d[i]=Math.round(d[i]*alpha+beta);d[i+1]=Math.round(d[i+1]*alpha+beta);d[i+2]=Math.round(d[i+2]*alpha+beta)}let dst=getPlatform().createCanvas(width,height);dst.getContext("2d").putImageData(imageData,0,0);this._canvas=dst;return this}invert(){const{width,height}=this._canvas;let imageData=this._canvas.getContext("2d").getImageData(0,0,width,height);let d=imageData.data;for(let i=0;i<d.length;i+=4){d[i]=255-d[i];d[i+1]=255-d[i+1];d[i+2]=255-d[i+2]}let dst=getPlatform().createCanvas(width,height);dst.getContext("2d").putImageData(imageData,0,0);this._canvas=dst;return this}threshold(options={}){const{thresh=127,maxValue=255}=options;const{width,height}=this._canvas;let imageData=this._canvas.getContext("2d").getImageData(0,0,width,height);let d=imageData.data;for(let i=0;i<d.length;i+=4){let luma=d[i]===d[i+1]&&d[i+1]===d[i+2]?d[i]:Math.round(0.299*d[i]+0.587*d[i+1]+0.114*d[i+2]);let val=luma>thresh?maxValue:0;d[i]=val;d[i+1]=val;d[i+2]=val}let dst=getPlatform().createCanvas(width,height);dst.getContext("2d").putImageData(imageData,0,0);this._canvas=dst;return this}border(options={}){const{size=10,color="white"}=options;const{width,height}=this._canvas;let dst=getPlatform().createCanvas(width+size*2,height+size*2);let ctx=dst.getContext("2d");ctx.fillStyle=color;ctx.fillRect(0,0,dst.width,dst.height);ctx.drawImage(this._canvas,size,size);this._canvas=dst;return this}rotate(options){const{angle,cx=this._canvas.width/2,cy=this._canvas.height/2}=options;if(angle===0)return this;const{width,height}=this._canvas;let dst=getPlatform().createCanvas(width,height);let ctx=dst.getContext("2d");ctx.save();ctx.translate(cx,cy);ctx.rotate(-angle*Math.PI/180);ctx.drawImage(this._canvas,-cx,-cy);ctx.restore();this._canvas=dst;return this}findRegions(options={}){const{foreground="light",thresh=127,minArea=1,maxArea=1/0,padding,scale=1}=options;const{width,height}=this._canvas;let data=this._canvas.getContext("2d").getImageData(0,0,width,height).data;let visited=new Uint8Array(width*height);let regions=[];let neighbours=[[-1,-1],[0,-1],[1,-1],[-1,0],[1,0],[-1,1],[0,1],[1,1]];let isForeground=(pixelIdx)=>{let r=data[pixelIdx];return foreground==="light"?r>thresh:r<=thresh};for(let startY=0;startY<height;startY++){for(let startX=0;startX<width;startX++){let startFlat=startY*width+startX;if(visited[startFlat])continue;visited[startFlat]=1;if(!isForeground(startFlat*4))continue;let stack=[startFlat];let minX=startX,maxX=startX;let minY=startY,maxY=startY;let area=0;while(stack.length>0){let flat=stack.pop();area++;let x=flat%width;let y=(flat-x)/width;if(x<minX)minX=x;else if(x>maxX)maxX=x;if(y<minY)minY=y;else if(y>maxY)maxY=y;for(const[dx,dy]of neighbours){let nx=x+dx;let ny=y+dy;if(nx<0||nx>=width||ny<0||ny>=height)continue;let nFlat=ny*width+nx;if(visited[nFlat])continue;visited[nFlat]=1;if(isForeground(nFlat*4))stack.push(nFlat)}}if(area>=minArea&&area<=maxArea){let x0=minX;let y0=minY;let x1=maxX+1;let y1=maxY+1;if(padding){let bboxH=y1-y0;let vPad=Math.round(bboxH*(padding.vertical??0));let hPad=Math.round(bboxH*(padding.horizontal??0));x0=Math.max(0,x0-hPad);y0=Math.max(0,y0-vPad);x1=Math.min(width,x1+hPad);y1=Math.min(height,y1+vPad)}if(scale!==1){x0=Math.max(0,Math.round(x0*scale));y0=Math.max(0,Math.round(y0*scale));x1=Math.round(x1*scale);y1=Math.round(y1*scale)}regions.push({bbox:{x0,y0,x1,y1},area})}}}return regions}toCanvas(){return this._canvas}static async prepareCanvas(file){if(getPlatform().isCanvas(file))return file;return getPlatform().loadImage(file)}static async prepareBuffer(canvas){if(canvas instanceof ArrayBuffer)return canvas;if(typeof canvas.toBuffer==="function"){let buffer=canvas.toBuffer("image/png");let arrayBuffer=new ArrayBuffer(buffer.byteLength);new Uint8Array(arrayBuffer).set(new Uint8Array(buffer));return arrayBuffer}if(typeof canvas.toDataURL==="function"){let dataURL=canvas.toDataURL("image/png");let base64Data=dataURL.replace(/^data:image\/png;base64,/,"");let binaryString=atob(base64Data);let arrayBuffer=new ArrayBuffer(binaryString.length);let bytes=new Uint8Array(arrayBuffer);for(let i=0;i<binaryString.length;i++){bytes[i]=binaryString.charCodeAt(i)}return arrayBuffer}let ctx=canvas.getContext("2d");let imageData=ctx.getImageData(0,0,canvas.width,canvas.height);let canvasBuffer=new ArrayBuffer(imageData.data.byteLength);new Uint8Array(canvasBuffer).set(new Uint8Array(imageData.data.buffer,imageData.data.byteOffset,imageData.data.byteLength));return canvasBuffer}}import{getPlatform}from"./canvas-factory.js";
|
package/index.canvas-web.d.ts
CHANGED
|
@@ -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
|
|
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
|
-
export { CanvasProcessor } from "./canvas-processor.js";
|
|
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
|
|
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
|
-
export { CanvasProcessor } from "./canvas-processor.js";
|
|
8
|
+
export { CanvasProcessor, type DetectedRegion } from "./canvas-processor.js";
|
package/index.d.ts
CHANGED
|
@@ -3,12 +3,12 @@ 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
|
|
6
|
+
export { executeOperation, OperationRegistry, registry } from "./pipeline/index.js";
|
|
7
7
|
export { getPlatform, setPlatform } from "./canvas-factory.js";
|
|
8
|
-
export type { CanvasLike, CanvasPlatform, Context2DLike
|
|
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
|
-
export { CanvasProcessor } from "./canvas-processor.js";
|
|
11
|
+
export { CanvasProcessor, type DetectedRegion } from "./canvas-processor.js";
|
|
12
12
|
export { Contours } from "./contours.js";
|
|
13
13
|
export { calculateMeanGrayscaleValue, calculateMeanNormalizedLabLightness, type CalculateMeanLightnessOptions, } from "./image-analysis.js";
|
|
14
14
|
export { ImageProcessor } from "./image-processor.js";
|
package/index.web.d.ts
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
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
|
|
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
|
|
7
|
+
export { executeOperation, OperationRegistry, registry } from "./pipeline/index.js";
|
|
8
8
|
export { CanvasToolkitBase as CanvasToolkit, CanvasToolkitBase, type ContourLike, } from "./canvas-toolkit.base.js";
|
|
9
|
-
export { CanvasProcessor } from "./canvas-processor.js";
|
|
9
|
+
export { CanvasProcessor, type DetectedRegion } from "./canvas-processor.js";
|
|
10
10
|
export { Contours } from "./contours.js";
|
|
11
11
|
export { calculateMeanGrayscaleValue, calculateMeanNormalizedLabLightness, type CalculateMeanLightnessOptions, } from "./image-analysis.js";
|
|
12
12
|
export { ImageProcessor } from "./image-processor.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;
|
package/operations/blur.d.ts
CHANGED
|
@@ -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];
|
package/operations/border.d.ts
CHANGED
|
@@ -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;
|
package/operations/canny.d.ts
CHANGED
|
@@ -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;
|
package/operations/convert.d.ts
CHANGED
|
@@ -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;
|
package/operations/dilate.d.ts
CHANGED
|
@@ -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];
|
package/operations/erode.d.ts
CHANGED
|
@@ -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;
|
package/operations/invert.d.ts
CHANGED
|
@@ -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];
|
package/operations/resize.d.ts
CHANGED
|
@@ -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;
|
package/operations/rotate.d.ts
CHANGED
|
@@ -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;
|
package/operations/warp.d.ts
CHANGED
|
@@ -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.
|
|
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
|
-
"
|
|
51
|
-
"
|
|
52
|
-
"
|
|
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
|
-
"
|
|
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": {
|
package/pipeline/registry.d.ts
CHANGED
package/pipeline/types.d.ts
CHANGED
|
@@ -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
|
-
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
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];
|