ppu-ocv 1.6.0 → 2.0.0
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 +77 -9
- package/canvas-factory.d.ts +42 -0
- package/canvas-factory.js +1 -0
- package/canvas-toolkit.base.d.ts +49 -0
- package/canvas-toolkit.base.js +1 -0
- package/canvas-toolkit.d.ts +8 -87
- package/canvas-toolkit.js +1 -1
- package/contours.d.ts +4 -3
- package/contours.js +1 -1
- package/cv-provider.d.ts +45 -0
- package/cv-provider.js +1 -0
- package/image-analysis.d.ts +3 -3
- package/image-analysis.js +1 -1
- package/image-processor.d.ts +53 -53
- package/image-processor.js +1 -1
- package/index.d.ts +11 -8
- package/index.js +1 -1
- package/index.web.d.ts +12 -0
- package/index.web.js +1 -0
- package/operations/adaptive-threshold.d.ts +3 -3
- package/operations/adaptive-threshold.js +1 -1
- package/operations/blur.d.ts +3 -3
- package/operations/blur.js +1 -1
- package/operations/border.d.ts +3 -3
- package/operations/border.js +1 -1
- package/operations/canny.d.ts +3 -3
- package/operations/canny.js +1 -1
- package/operations/convert.d.ts +3 -3
- package/operations/convert.js +1 -1
- package/operations/dilate.d.ts +3 -3
- package/operations/dilate.js +1 -1
- package/operations/erode.d.ts +3 -3
- package/operations/erode.js +1 -1
- package/operations/grayscale.d.ts +3 -3
- package/operations/grayscale.js +1 -1
- package/operations/invert.d.ts +3 -3
- package/operations/invert.js +1 -1
- package/operations/morphological-gradient.d.ts +3 -3
- package/operations/morphological-gradient.js +1 -1
- package/operations/resize.d.ts +3 -3
- package/operations/resize.js +1 -1
- package/operations/rotate.d.ts +3 -3
- package/operations/rotate.js +1 -1
- package/operations/threshold.d.ts +3 -3
- package/operations/threshold.js +1 -1
- package/operations/warp.d.ts +4 -3
- package/operations/warp.js +1 -1
- package/package.json +17 -7
- package/pipeline/index.d.ts +30 -30
- package/pipeline/index.js +1 -1
- package/pipeline/registry.d.ts +2 -2
- package/pipeline/types.d.ts +1 -1
- package/platform/node.d.ts +3 -0
- package/platform/node.js +1 -0
- package/platform/web.d.ts +3 -0
- package/platform/web.js +1 -0
package/README.md
CHANGED
|
@@ -11,7 +11,7 @@ const processor = new ImageProcessor(canvas);
|
|
|
11
11
|
|
|
12
12
|
const result = processor
|
|
13
13
|
.grayscale()
|
|
14
|
-
.blur({ size: 5 })
|
|
14
|
+
.blur({ size: [5, 5] })
|
|
15
15
|
.threshold()
|
|
16
16
|
.invert()
|
|
17
17
|
.dilate({ size: [20, 20], iter: 5 })
|
|
@@ -43,13 +43,7 @@ yarn add ppu-ocv
|
|
|
43
43
|
bun add ppu-ocv
|
|
44
44
|
```
|
|
45
45
|
|
|
46
|
-
|
|
47
|
-
> This project is developed and tested primarily with Bun.
|
|
48
|
-
> Support for Node.js, Deno, or browser environments is **not guaranteed**.
|
|
49
|
-
> If you choose to use it outside of Bun and encounter any issues, feel free to report them.
|
|
50
|
-
> I'm open to fixing bugs for other runtimes with community help.
|
|
51
|
-
|
|
52
|
-
## Usage
|
|
46
|
+
## Usage (Node.js / Bun)
|
|
53
47
|
|
|
54
48
|
Note that Operation order is matter, you should atleast know some basic in using OpenCV. See the operations table below this.
|
|
55
49
|
|
|
@@ -63,7 +57,7 @@ const canvas = await ImageProcessor.prepareCanvas(image);
|
|
|
63
57
|
await ImageProcessor.initRuntime();
|
|
64
58
|
|
|
65
59
|
const processor = new ImageProcessor(canvas);
|
|
66
|
-
processor.grayscale().blur({ size: 5 }).threshold();
|
|
60
|
+
processor.grayscale().blur({ size: [5, 5] }).threshold();
|
|
67
61
|
|
|
68
62
|
const resultCanvas = processor.toCanvas();
|
|
69
63
|
processor.destroy();
|
|
@@ -101,6 +95,80 @@ await canvasToolkit.saveImage({
|
|
|
101
95
|
|
|
102
96
|
For more advanced usage, see: [Example usage of ppu-ocv](./examples)
|
|
103
97
|
|
|
98
|
+
## Web / Browser Support
|
|
99
|
+
|
|
100
|
+
Starting from v2.0.0, ppu-ocv supports running in the browser. Import from `ppu-ocv/web` instead of `ppu-ocv` to use the browser-native canvas APIs (`HTMLCanvasElement` / `OffscreenCanvas`) instead of `@napi-rs/canvas`.
|
|
101
|
+
|
|
102
|
+
### With a bundler (Vite, webpack, etc.)
|
|
103
|
+
|
|
104
|
+
```ts
|
|
105
|
+
import { ImageProcessor, cv } from "ppu-ocv/web";
|
|
106
|
+
|
|
107
|
+
await ImageProcessor.initRuntime();
|
|
108
|
+
|
|
109
|
+
// From a file input or fetch
|
|
110
|
+
const response = await fetch("/my-image.jpg");
|
|
111
|
+
const buffer = await response.arrayBuffer();
|
|
112
|
+
|
|
113
|
+
const canvas = await ImageProcessor.prepareCanvas(buffer);
|
|
114
|
+
const processor = new ImageProcessor(canvas);
|
|
115
|
+
|
|
116
|
+
processor.grayscale().blur({ size: [5, 5] }).threshold();
|
|
117
|
+
|
|
118
|
+
const result = processor.toCanvas(); // returns HTMLCanvasElement
|
|
119
|
+
document.body.appendChild(result);
|
|
120
|
+
|
|
121
|
+
processor.destroy();
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
### Vanilla HTML (no bundler)
|
|
125
|
+
|
|
126
|
+
`initRuntime()` automatically loads `@techstark/opencv-js` from the npm CDN if it's not already available. No extra script tags or import maps needed:
|
|
127
|
+
|
|
128
|
+
```html
|
|
129
|
+
<script type="module">
|
|
130
|
+
import { ImageProcessor } from "https://cdn.jsdelivr.net/npm/ppu-ocv@2/index.web.js";
|
|
131
|
+
await ImageProcessor.initRuntime();
|
|
132
|
+
|
|
133
|
+
const processor = new ImageProcessor(canvas);
|
|
134
|
+
processor.grayscale().blur({ size: [5, 5] }).threshold();
|
|
135
|
+
|
|
136
|
+
const result = processor.toCanvas();
|
|
137
|
+
processor.destroy();
|
|
138
|
+
</script>
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
> **Note:** ES modules require HTTP/HTTPS — use a local server (`npx serve .`) for dev, or deploy to GitHub Pages.
|
|
142
|
+
|
|
143
|
+
See the [interactive demo](./index.html) for a full working example.
|
|
144
|
+
|
|
145
|
+
### Node vs Web differences
|
|
146
|
+
|
|
147
|
+
| Feature | `ppu-ocv` (Node) | `ppu-ocv/web` (Browser) |
|
|
148
|
+
| --- | --- | --- |
|
|
149
|
+
| Canvas backend | `@napi-rs/canvas` | `HTMLCanvasElement` / `OffscreenCanvas` |
|
|
150
|
+
| `CanvasToolkit` | Full (includes `saveImage`, `clearOutput`) | Base only (`crop`, `isDirty`, `drawLine`, `drawContour`) |
|
|
151
|
+
| File I/O | ✅ `saveImage`, `clearOutput` | ❌ Not available (use download links or `toDataURL`) |
|
|
152
|
+
| `ImageProcessor` | ✅ | ✅ Same API |
|
|
153
|
+
| `Contours` | ✅ | ✅ Same API |
|
|
154
|
+
| Image analysis | ✅ | ✅ Same API |
|
|
155
|
+
|
|
156
|
+
### Platform abstraction
|
|
157
|
+
|
|
158
|
+
Under the hood, ppu-ocv uses a platform abstraction layer. The `ppu-ocv` entry point auto-registers the Node platform, and `ppu-ocv/web` auto-registers the browser platform. You can also set a custom platform:
|
|
159
|
+
|
|
160
|
+
```ts
|
|
161
|
+
import { setPlatform, type CanvasPlatform } from "ppu-ocv/web";
|
|
162
|
+
|
|
163
|
+
const myPlatform: CanvasPlatform = {
|
|
164
|
+
createCanvas(width, height) { /* ... */ },
|
|
165
|
+
loadImage(source) { /* ... */ },
|
|
166
|
+
isCanvas(value) { /* ... */ },
|
|
167
|
+
};
|
|
168
|
+
|
|
169
|
+
setPlatform(myPlatform);
|
|
170
|
+
```
|
|
171
|
+
|
|
104
172
|
## Built-in pipeline operations
|
|
105
173
|
|
|
106
174
|
To avoid bloat, we only ship essential operations for chaining. Currently shipped operations are:
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Platform abstraction layer for canvas operations.
|
|
3
|
+
* Allows ppu-ocv to work with both @napi-rs/canvas (Node) and browser canvas APIs.
|
|
4
|
+
*/
|
|
5
|
+
/** Structural type satisfied by both @napi-rs/canvas Canvas and HTMLCanvasElement/OffscreenCanvas */
|
|
6
|
+
export interface CanvasLike {
|
|
7
|
+
width: number;
|
|
8
|
+
height: number;
|
|
9
|
+
getContext(contextId: "2d"): any;
|
|
10
|
+
toBuffer?: (...args: any[]) => Buffer;
|
|
11
|
+
toDataURL?: (...args: any[]) => string;
|
|
12
|
+
}
|
|
13
|
+
/** Structural type for 2D rendering context */
|
|
14
|
+
export interface Context2DLike {
|
|
15
|
+
canvas: any;
|
|
16
|
+
drawImage(...args: any[]): void;
|
|
17
|
+
getImageData(sx: number, sy: number, sw: number, sh: number): {
|
|
18
|
+
data: Uint8ClampedArray;
|
|
19
|
+
width: number;
|
|
20
|
+
height: number;
|
|
21
|
+
};
|
|
22
|
+
putImageData(imageData: any, dx: number, dy: number): void;
|
|
23
|
+
createImageData(width: number, height: number): any;
|
|
24
|
+
beginPath(): void;
|
|
25
|
+
closePath(): void;
|
|
26
|
+
moveTo(x: number, y: number): void;
|
|
27
|
+
lineTo(x: number, y: number): void;
|
|
28
|
+
stroke(): void;
|
|
29
|
+
strokeRect(x: number, y: number, w: number, h: number): void;
|
|
30
|
+
strokeStyle: string | CanvasGradient | CanvasPattern;
|
|
31
|
+
lineWidth: number;
|
|
32
|
+
}
|
|
33
|
+
/** Platform-specific canvas operations */
|
|
34
|
+
export interface CanvasPlatform {
|
|
35
|
+
createCanvas(width: number, height: number): CanvasLike;
|
|
36
|
+
loadImage(source: ArrayBuffer | string): Promise<CanvasLike>;
|
|
37
|
+
isCanvas(value: unknown): value is CanvasLike;
|
|
38
|
+
}
|
|
39
|
+
/** Register the platform-specific canvas implementation */
|
|
40
|
+
export declare function setPlatform(platform: CanvasPlatform): void;
|
|
41
|
+
/** Get the registered platform. Throws if none has been set. */
|
|
42
|
+
export declare function getPlatform(): CanvasPlatform;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
let _platform=null;export function setPlatform(platform){_platform=platform}export function getPlatform(){if(!_platform){throw new Error("No canvas platform registered. "+'Import "ppu-ocv" (Node) or "ppu-ocv/web" (browser) to auto-register.')}return _platform}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import type { BoundingBox } from "./index.interface.js";
|
|
2
|
+
import type { CanvasLike, Context2DLike } from "./canvas-factory.js";
|
|
3
|
+
import { cv } from "./cv-provider.js";
|
|
4
|
+
/**
|
|
5
|
+
* Cross-platform base class for canvas manipulation utilities.
|
|
6
|
+
* Contains only methods that work in both Node and browser environments.
|
|
7
|
+
*/
|
|
8
|
+
export declare class CanvasToolkitBase {
|
|
9
|
+
protected static _baseInstance: CanvasToolkitBase | null;
|
|
10
|
+
protected step: number;
|
|
11
|
+
protected constructor();
|
|
12
|
+
static getInstance(): CanvasToolkitBase;
|
|
13
|
+
/**
|
|
14
|
+
* Crop a part of source canvas and return a new canvas of the cropped part
|
|
15
|
+
*/
|
|
16
|
+
crop(options: {
|
|
17
|
+
bbox: BoundingBox;
|
|
18
|
+
canvas: CanvasLike;
|
|
19
|
+
}): CanvasLike;
|
|
20
|
+
/**
|
|
21
|
+
* Check whether a binary canvas is dirty (full of major color either black or white) or not
|
|
22
|
+
*/
|
|
23
|
+
isDirty(options: {
|
|
24
|
+
canvas: CanvasLike;
|
|
25
|
+
threshold?: number;
|
|
26
|
+
majorColorThreshold?: number;
|
|
27
|
+
}): boolean;
|
|
28
|
+
/**
|
|
29
|
+
* Draw a non-filled rectangle on the canvas
|
|
30
|
+
*/
|
|
31
|
+
drawLine(options: {
|
|
32
|
+
ctx: Context2DLike;
|
|
33
|
+
x: number;
|
|
34
|
+
y: number;
|
|
35
|
+
width: number;
|
|
36
|
+
height: number;
|
|
37
|
+
lineWidth?: number;
|
|
38
|
+
color?: string;
|
|
39
|
+
}): void;
|
|
40
|
+
/**
|
|
41
|
+
* Draw a contour on the canvas
|
|
42
|
+
*/
|
|
43
|
+
drawContour(options: {
|
|
44
|
+
ctx: Context2DLike;
|
|
45
|
+
contour: cv.Mat;
|
|
46
|
+
strokeStyle?: string;
|
|
47
|
+
lineWidth?: number;
|
|
48
|
+
}): void;
|
|
49
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export class CanvasToolkitBase{static _baseInstance=null;step=0;constructor(){}static getInstance(){if(!CanvasToolkitBase._baseInstance){CanvasToolkitBase._baseInstance=new CanvasToolkitBase}return CanvasToolkitBase._baseInstance}crop(options){const{bbox,canvas}=options;let croppedCanvas=getPlatform().createCanvas(bbox.x1-bbox.x0,bbox.y1-bbox.y0);let croppedCtx=croppedCanvas.getContext("2d");croppedCtx.drawImage(canvas,bbox.x0,bbox.y0,bbox.x1-bbox.x0,bbox.y1-bbox.y0,0,0,croppedCanvas.width,croppedCanvas.height);return croppedCanvas}isDirty(options){const{canvas,threshold=127.5,majorColorThreshold=0.97}=options;let whiteCount=0;let blackCount=0;let borderlessCanvas=this.crop({bbox:{x0:canvas.width*0.1,y0:canvas.height*0.1,x1:canvas.width*0.9,y1:canvas.height*0.9},canvas});let ctx=borderlessCanvas.getContext("2d");let colorData=ctx.getImageData(0,0,borderlessCanvas.width,borderlessCanvas.height).data;for(let i=0;i<colorData.length;i+=4){let red=colorData[i];let green=colorData[i+1];let blue=colorData[i+2];if(red>=threshold&&green>=threshold&&blue>=threshold){whiteCount++}else{blackCount++}}let majorColorRatio=Math.max(whiteCount,blackCount)/(blackCount+whiteCount);return majorColorRatio<majorColorThreshold}drawLine(options){const{ctx,x,y,width,height,lineWidth=2,color="blue"}=options;ctx.beginPath();ctx.strokeStyle=color;ctx.lineWidth=lineWidth;ctx.strokeRect(x,y,width,height);ctx.closePath()}drawContour(options){const{ctx,contour,strokeStyle="red",lineWidth=2}=options;let pts=contour.data32S;if(pts.length<4)return;ctx.strokeStyle=strokeStyle;ctx.lineWidth=lineWidth;ctx.beginPath();ctx.moveTo(pts[0],pts[1]);for(let i=2;i<pts.length;i+=2){ctx.lineTo(pts[i],pts[i+1])}ctx.closePath();ctx.stroke()}}import{getPlatform}from"./canvas-factory.js";
|
package/canvas-toolkit.d.ts
CHANGED
|
@@ -1,58 +1,13 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
import {
|
|
1
|
+
import type { CanvasLike } from "./canvas-factory.js";
|
|
2
|
+
import { CanvasToolkitBase } from "./canvas-toolkit.base.js";
|
|
3
3
|
/**
|
|
4
|
-
*
|
|
4
|
+
* Node.js canvas toolkit with file-system operations.
|
|
5
|
+
* Extends CanvasToolkitBase with saveImage() and clearOutput().
|
|
5
6
|
*/
|
|
6
|
-
export declare class CanvasToolkit {
|
|
7
|
-
private static
|
|
8
|
-
|
|
9
|
-
/**
|
|
10
|
-
* Private constructor to prevent direct instantiation
|
|
11
|
-
*/
|
|
12
|
-
private constructor();
|
|
13
|
-
/**
|
|
14
|
-
* Get the singleton instance of CanvasToolkit
|
|
15
|
-
* @returns The singleton instance
|
|
16
|
-
* @example
|
|
17
|
-
* const canvasToolkit = CanvasToolkit.getInstance();
|
|
18
|
-
*/
|
|
7
|
+
export declare class CanvasToolkit extends CanvasToolkitBase {
|
|
8
|
+
private static _nodeInstance;
|
|
9
|
+
protected constructor();
|
|
19
10
|
static getInstance(): CanvasToolkit;
|
|
20
|
-
/**
|
|
21
|
-
* Crop a part of source canvas and return a new canvas of the cropped part
|
|
22
|
-
* @param options
|
|
23
|
-
* @param options.bbox Bounding box of the cropped part
|
|
24
|
-
* @param options.canvas Source canvas
|
|
25
|
-
* @returns A new canvas of the cropped part
|
|
26
|
-
* @example
|
|
27
|
-
* const croppedCanvas = CanvasToolkit.getInstance().crop({
|
|
28
|
-
* bbox: { x0: 10, y0: 10, x1: 100, y1: 100 },
|
|
29
|
-
* canvas: sourceCanvas,
|
|
30
|
-
* });
|
|
31
|
-
*/
|
|
32
|
-
crop(options: {
|
|
33
|
-
bbox: BoundingBox;
|
|
34
|
-
canvas: Canvas;
|
|
35
|
-
}): Canvas;
|
|
36
|
-
/**
|
|
37
|
-
* Check whether a binary canvas is dirty (full of major color either black or white) or not
|
|
38
|
-
* @param options
|
|
39
|
-
* @param options.canvas Source canvas
|
|
40
|
-
* @param options.threshold Threshold for color detection (default: 127.5)
|
|
41
|
-
* @param options.majorColorThreshold Major color threshold (default: 0.97)
|
|
42
|
-
* @returns true if the canvas is dirty, false otherwise
|
|
43
|
-
* @example
|
|
44
|
-
* const isDirty = CanvasToolkit.getInstance().isDirty({
|
|
45
|
-
* canvas: sourceCanvas,
|
|
46
|
-
* threshold: 127.5,
|
|
47
|
-
* majorColorThreshold: 0.97,
|
|
48
|
-
* });
|
|
49
|
-
* console.log(isDirty); // true or false
|
|
50
|
-
*/
|
|
51
|
-
isDirty(options: {
|
|
52
|
-
canvas: Canvas;
|
|
53
|
-
threshold?: number;
|
|
54
|
-
majorColorThreshold?: number;
|
|
55
|
-
}): boolean;
|
|
56
11
|
/**
|
|
57
12
|
* Save a canvas to an image file
|
|
58
13
|
* @param options
|
|
@@ -67,7 +22,7 @@ export declare class CanvasToolkit {
|
|
|
67
22
|
* });
|
|
68
23
|
*/
|
|
69
24
|
saveImage(options: {
|
|
70
|
-
canvas:
|
|
25
|
+
canvas: CanvasLike;
|
|
71
26
|
filename: string;
|
|
72
27
|
path: string;
|
|
73
28
|
}): Promise<void>;
|
|
@@ -76,38 +31,4 @@ export declare class CanvasToolkit {
|
|
|
76
31
|
* @param path Path to the output folder (default: "out")
|
|
77
32
|
*/
|
|
78
33
|
clearOutput(path?: string): void;
|
|
79
|
-
/**
|
|
80
|
-
* Draw a non-filled rectangle on the canvas
|
|
81
|
-
* @param options
|
|
82
|
-
* @param options.ctx Canvas rendering context
|
|
83
|
-
* @param options.x X coordinate of the top-left corner
|
|
84
|
-
* @param options.y Y coordinate of the top-left corner
|
|
85
|
-
* @param options.width Width of the rectangle
|
|
86
|
-
* @param options.height Height of the rectangle
|
|
87
|
-
* @param options.lineWidth Line width (default: 2)
|
|
88
|
-
* @param options.color Color of the rectangle (default: "blue")
|
|
89
|
-
*/
|
|
90
|
-
drawLine(options: {
|
|
91
|
-
ctx: SKRSContext2D;
|
|
92
|
-
x: number;
|
|
93
|
-
y: number;
|
|
94
|
-
width: number;
|
|
95
|
-
height: number;
|
|
96
|
-
lineWidth?: number;
|
|
97
|
-
color?: string;
|
|
98
|
-
}): void;
|
|
99
|
-
/**
|
|
100
|
-
* Draw a contour on the canvas
|
|
101
|
-
* @param options
|
|
102
|
-
* @param options.ctx Canvas rendering context
|
|
103
|
-
* @param options.contour Contour to be drawn
|
|
104
|
-
* @param options.strokeStyle Stroke color (default: "red")
|
|
105
|
-
* @param options.lineWidth Line width (default: 2)
|
|
106
|
-
*/
|
|
107
|
-
drawContour(options: {
|
|
108
|
-
ctx: SKRSContext2D;
|
|
109
|
-
contour: cv.Mat;
|
|
110
|
-
strokeStyle?: string;
|
|
111
|
-
lineWidth?: number;
|
|
112
|
-
}): void;
|
|
113
34
|
}
|
package/canvas-toolkit.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export class CanvasToolkit{static
|
|
1
|
+
import{CanvasToolkitBase}from"./canvas-toolkit.base.js";import{createWriteStream,existsSync,mkdirSync,readdirSync,unlinkSync}from"fs";import{join}from"path";export class CanvasToolkit extends CanvasToolkitBase{static _nodeInstance=null;constructor(){super()}static getInstance(){if(!CanvasToolkit._nodeInstance){CanvasToolkit._nodeInstance=new CanvasToolkit}return CanvasToolkit._nodeInstance}saveImage(options){const{canvas,filename,path="out"}=options;let folderPath=join(process.cwd(),path);if(!existsSync(folderPath)){mkdirSync(folderPath,{recursive:true})}let filePath=join(folderPath,`${this.step++}. ${filename}.png`);let out=createWriteStream(filePath);let buffer=canvas.toBuffer("image/png");return new Promise((res,rej)=>{out.write(buffer,(err)=>{if(err){rej(err)}else{res()}})})}clearOutput(path="out"){let folderPath=join(process.cwd(),path);if(existsSync(folderPath)){let files=readdirSync(folderPath);for(let file of files){if(file===".gitignore")continue;let filePath=join(folderPath,file);unlinkSync(filePath)}}}}
|
package/contours.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
import { cv } from "./
|
|
1
|
+
import type { CanvasLike } from "./canvas-factory.js";
|
|
2
|
+
import { cv } from "./cv-provider.js";
|
|
3
|
+
import type { BoundingBox, Points } from "./index.interface.js";
|
|
3
4
|
export interface ContoursOptions {
|
|
4
5
|
/** The contour retrieval mode. (cv.RETR_...) */
|
|
5
6
|
mode: cv.RetrievalModes;
|
|
@@ -62,7 +63,7 @@ export declare class Contours {
|
|
|
62
63
|
* @returns The four corner points of the contour (topLeft, topRight, bottomLeft, bottomRight) and the bounding box.
|
|
63
64
|
*/
|
|
64
65
|
getCornerPoints(options: {
|
|
65
|
-
canvas:
|
|
66
|
+
canvas: CanvasLike;
|
|
66
67
|
contour?: cv.Mat;
|
|
67
68
|
}): {
|
|
68
69
|
points: Points;
|
package/contours.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export class Contours{contours;constructor(img,options={}){let opts={...defaultOptions(),...options};if(img instanceof cv.Mat){let contours=new cv.MatVector;let hierarchy=new cv.Mat;try{cv.findContours(img,contours,hierarchy,opts.mode,opts.method)}catch(error){throw error}hierarchy.delete();this.contours=contours}else{throw new Error("Invalid img type. Must be cv.Mat.")}}getAll(){return this.contours}getSize(){return this.contours.size()}getFromIndex(index){if(index<this.contours.size()){return this.contours.get(index)}return new cv.Mat}getRect(contour){return cv.boundingRect(contour)}iterate(callback){for(let i=0,len=this.contours.size();i<len;i++){let contour=this.contours.get(i);callback(contour)}return this}getLargestContourArea(){let maxArea=0;let largestContour=null;this.iterate((contour)=>{let area=cv.contourArea(contour);if(area>maxArea){maxArea=area;largestContour=contour}});return largestContour}getCornerPoints(options){const{canvas,contour=this.getLargestContourArea()}=options;let bbox={x0:0,y0:0,x1:canvas.width,y1:canvas.height};if(!contour){return{points:{topLeft:{x:bbox.x0,y:bbox.y0},topRight:{x:bbox.x1,y:bbox.y0},bottomLeft:{x:bbox.x0,y:bbox.y1},bottomRight:{x:bbox.x1,y:bbox.y1}},bbox}}let rect=cv.minAreaRect(contour);let vertices=cv.RotatedRect.points(rect);let points={topLeft:{x:0,y:0},topRight:{x:0,y:0},bottomRight:{x:0,y:0},bottomLeft:{x:0,y:0}};let sums=vertices.map((pt)=>pt.x+pt.y);let diffs=vertices.map((pt)=>pt.y-pt.x);let topLeftIdx=sums.indexOf(Math.min(...sums));let topRightIdx=diffs.indexOf(Math.min(...diffs));let bottomRightIdx=sums.indexOf(Math.max(...sums));let bottomLeftIdx=diffs.indexOf(Math.max(...diffs));if(!vertices[topLeftIdx]||!vertices[topRightIdx]||!vertices[bottomRightIdx]||!vertices[bottomLeftIdx]){return{points:{topLeft:{x:bbox.x0,y:bbox.y0},topRight:{x:bbox.x1,y:bbox.y0},bottomLeft:{x:bbox.x0,y:bbox.y1},bottomRight:{x:bbox.x1,y:bbox.y1}},bbox}}points.topLeft={x:vertices[topLeftIdx].x,y:vertices[topLeftIdx].y};points.topRight={x:vertices[topRightIdx].x,y:vertices[topRightIdx].y};points.bottomRight={x:vertices[bottomRightIdx].x,y:vertices[bottomRightIdx].y};points.bottomLeft={x:vertices[bottomLeftIdx].x,y:vertices[bottomLeftIdx].y};contour.delete();let ensureInBounds=(p)=>{p.x=Math.max(0,Math.min(canvas.width,p.x));p.y=Math.max(0,Math.min(canvas.height,p.y));return p};points.topLeft=ensureInBounds(points.topLeft);points.topRight=ensureInBounds(points.topRight);points.bottomLeft=ensureInBounds(points.bottomLeft);points.bottomRight=ensureInBounds(points.bottomRight);return{points,bbox}}getApproximateRectangleContour(options){const{threshold=0.02,contour=this.getLargestContourArea()}=options??{};if(!contour)return;let epsilon=threshold*cv.arcLength(contour,true);let approxContour=new cv.Mat;cv.approxPolyDP(contour,approxContour,epsilon,true);contour.delete();return approxContour}destroy(){try{this.contours.delete()}catch(error){}}}import{cv}from"./
|
|
1
|
+
export class Contours{contours;constructor(img,options={}){let opts={...defaultOptions(),...options};if(img instanceof cv.Mat){let contours=new cv.MatVector;let hierarchy=new cv.Mat;try{cv.findContours(img,contours,hierarchy,opts.mode,opts.method)}catch(error){throw error}hierarchy.delete();this.contours=contours}else{throw new Error("Invalid img type. Must be cv.Mat.")}}getAll(){return this.contours}getSize(){return this.contours.size()}getFromIndex(index){if(index<this.contours.size()){return this.contours.get(index)}return new cv.Mat}getRect(contour){return cv.boundingRect(contour)}iterate(callback){for(let i=0,len=this.contours.size();i<len;i++){let contour=this.contours.get(i);callback(contour)}return this}getLargestContourArea(){let maxArea=0;let largestContour=null;this.iterate((contour)=>{let area=cv.contourArea(contour);if(area>maxArea){maxArea=area;largestContour=contour}});return largestContour}getCornerPoints(options){const{canvas,contour=this.getLargestContourArea()}=options;let bbox={x0:0,y0:0,x1:canvas.width,y1:canvas.height};if(!contour){return{points:{topLeft:{x:bbox.x0,y:bbox.y0},topRight:{x:bbox.x1,y:bbox.y0},bottomLeft:{x:bbox.x0,y:bbox.y1},bottomRight:{x:bbox.x1,y:bbox.y1}},bbox}}let rect=cv.minAreaRect(contour);let vertices=cv.RotatedRect.points(rect);let points={topLeft:{x:0,y:0},topRight:{x:0,y:0},bottomRight:{x:0,y:0},bottomLeft:{x:0,y:0}};let sums=vertices.map((pt)=>pt.x+pt.y);let diffs=vertices.map((pt)=>pt.y-pt.x);let topLeftIdx=sums.indexOf(Math.min(...sums));let topRightIdx=diffs.indexOf(Math.min(...diffs));let bottomRightIdx=sums.indexOf(Math.max(...sums));let bottomLeftIdx=diffs.indexOf(Math.max(...diffs));if(!vertices[topLeftIdx]||!vertices[topRightIdx]||!vertices[bottomRightIdx]||!vertices[bottomLeftIdx]){return{points:{topLeft:{x:bbox.x0,y:bbox.y0},topRight:{x:bbox.x1,y:bbox.y0},bottomLeft:{x:bbox.x0,y:bbox.y1},bottomRight:{x:bbox.x1,y:bbox.y1}},bbox}}points.topLeft={x:vertices[topLeftIdx].x,y:vertices[topLeftIdx].y};points.topRight={x:vertices[topRightIdx].x,y:vertices[topRightIdx].y};points.bottomRight={x:vertices[bottomRightIdx].x,y:vertices[bottomRightIdx].y};points.bottomLeft={x:vertices[bottomLeftIdx].x,y:vertices[bottomLeftIdx].y};contour.delete();let ensureInBounds=(p)=>{p.x=Math.max(0,Math.min(canvas.width,p.x));p.y=Math.max(0,Math.min(canvas.height,p.y));return p};points.topLeft=ensureInBounds(points.topLeft);points.topRight=ensureInBounds(points.topRight);points.bottomLeft=ensureInBounds(points.bottomLeft);points.bottomRight=ensureInBounds(points.bottomRight);return{points,bbox}}getApproximateRectangleContour(options){const{threshold=0.02,contour=this.getLargestContourArea()}=options??{};if(!contour)return;let epsilon=threshold*cv.arcLength(contour,true);let approxContour=new cv.Mat;cv.approxPolyDP(contour,approxContour,epsilon,true);contour.delete();return approxContour}destroy(){try{this.contours.delete()}catch(error){}}}import{cv}from"./cv-provider.js";function defaultOptions(){return{mode:cv.RETR_EXTERNAL,method:cv.CHAIN_APPROX_SIMPLE}}
|
package/cv-provider.d.ts
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Lazy OpenCV accessor.
|
|
3
|
+
*
|
|
4
|
+
* In Node (with @techstark/opencv-js installed), `cv` is available after
|
|
5
|
+
* `import cv from "@techstark/opencv-js"`.
|
|
6
|
+
*
|
|
7
|
+
* In the browser, `cv` is set on `globalThis` after OpenCV.js is loaded
|
|
8
|
+
* (either via a <script> tag or dynamically by `initRuntime()`).
|
|
9
|
+
*
|
|
10
|
+
* This module re-exports `cv` as a lazy proxy so that static `import`
|
|
11
|
+
* resolution does NOT require `@techstark/opencv-js` to be present
|
|
12
|
+
* as a resolvable bare specifier at module-load time.
|
|
13
|
+
*/
|
|
14
|
+
import type _cvType from "@techstark/opencv-js";
|
|
15
|
+
type CV = typeof _cvType;
|
|
16
|
+
/**
|
|
17
|
+
* Set the cv instance (called by platform entry points).
|
|
18
|
+
*/
|
|
19
|
+
export declare function setCv(instance: CV): void;
|
|
20
|
+
/**
|
|
21
|
+
* TypeScript Declaration Merging:
|
|
22
|
+
* By exporting both a `namespace cv` and a `const cv`, consumers importing `{ cv }`
|
|
23
|
+
* get BOTH the types (e.g. `cv.Mat`) AND the runtime Proxy object.
|
|
24
|
+
*/
|
|
25
|
+
export declare namespace cv {
|
|
26
|
+
type Mat = _cvType.Mat;
|
|
27
|
+
type MatVector = _cvType.MatVector;
|
|
28
|
+
type Point = _cvType.Point;
|
|
29
|
+
type Rect = _cvType.Rect;
|
|
30
|
+
type Size = _cvType.Size;
|
|
31
|
+
type Scalar = _cvType.Scalar;
|
|
32
|
+
type AdaptiveThresholdTypes = _cvType.AdaptiveThresholdTypes;
|
|
33
|
+
type ThresholdTypes = _cvType.ThresholdTypes;
|
|
34
|
+
type LineTypes = _cvType.LineTypes;
|
|
35
|
+
type RetrievalModes = _cvType.RetrievalModes;
|
|
36
|
+
type ContourApproximationModes = _cvType.ContourApproximationModes;
|
|
37
|
+
type BorderTypes = _cvType.BorderTypes;
|
|
38
|
+
type InterpolationFlags = _cvType.InterpolationFlags;
|
|
39
|
+
type ColorConversionCodes = _cvType.ColorConversionCodes;
|
|
40
|
+
type MorphShapes = _cvType.MorphShapes;
|
|
41
|
+
type MorphTypes = _cvType.MorphTypes;
|
|
42
|
+
type int = number;
|
|
43
|
+
}
|
|
44
|
+
export declare const cv: CV;
|
|
45
|
+
export {};
|
package/cv-provider.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
let _cv=null;function getCv(){if(_cv)return _cv;if(typeof globalThis!=="undefined"&&globalThis.cv){_cv=globalThis.cv||null;return _cv}throw new Error("OpenCV is not loaded. Call ImageProcessor.initRuntime() first.")}export function setCv(instance){_cv=instance;if(typeof globalThis!=="undefined"){globalThis.cv=instance}}export let cv=new Proxy({},{get(_target,prop){if(prop===Symbol.toPrimitive||prop===Symbol.toStringTag){return}return getCv()[prop]},set(_target,prop,value){getCv()[prop]=value;return true},has(_target,prop){try{return prop in getCv()}catch{return false}}});
|
package/image-analysis.d.ts
CHANGED
|
@@ -3,13 +3,13 @@
|
|
|
3
3
|
* !IMPORTANT: Ensure ImageProcessor.initRuntime() has been called successfully
|
|
4
4
|
* once before using any functions from this module.
|
|
5
5
|
*/
|
|
6
|
-
import type {
|
|
6
|
+
import type { CanvasLike } from "./canvas-factory.js";
|
|
7
7
|
/**
|
|
8
8
|
* Options for calculating mean Lab lightness.
|
|
9
9
|
*/
|
|
10
10
|
export interface CalculateMeanLightnessOptions {
|
|
11
11
|
/** The canvas containing the image to be processed. */
|
|
12
|
-
canvas:
|
|
12
|
+
canvas: CanvasLike;
|
|
13
13
|
/** The target dimensions for analysis (resizes internally). */
|
|
14
14
|
dimension: {
|
|
15
15
|
width: number;
|
|
@@ -32,4 +32,4 @@ export declare function calculateMeanNormalizedLabLightness(options: CalculateMe
|
|
|
32
32
|
* @returns Mean grayscale value (typically 0-255).
|
|
33
33
|
* @throws Error if OpenCV operations fail.
|
|
34
34
|
*/
|
|
35
|
-
export declare function calculateMeanGrayscaleValue(canvas:
|
|
35
|
+
export declare function calculateMeanGrayscaleValue(canvas: CanvasLike): number;
|
package/image-analysis.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{
|
|
1
|
+
import{cv}from"./cv-provider.js";import{ImageProcessor}from"./image-processor.js";export function calculateMeanNormalizedLabLightness(options){const{canvas,dimension}=options;let processor=null;let resized=null;let labImg=null;let channels=null;let L=null;let mask=null;let scalarMat=null;try{processor=new ImageProcessor(canvas);resized=processor.execute("resize",{width:dimension.width,height:dimension.height}).toMat();labImg=new cv.Mat;cv.cvtColor(resized,labImg,cv.COLOR_BGR2Lab);channels=new cv.MatVector;cv.split(labImg,channels);L=channels.get(0);mask=new cv.Mat;let maxLocResult=cv.minMaxLoc(L,mask);let maxPixelValue=maxLocResult.maxVal;if(maxPixelValue===0){return 0}scalarMat=new cv.Mat(L.rows,L.cols,L.type(),new cv.Scalar(maxPixelValue));cv.divide(L,scalarMat,L,1,-1);let meanL=cv.mean(L)[0];return meanL??0}finally{processor?.destroy();labImg?.delete();channels?.delete();L?.delete();mask?.delete();scalarMat?.delete()}}export function calculateMeanGrayscaleValue(canvas){let processor=null;let grayscaleImg=null;try{processor=new ImageProcessor(canvas);grayscaleImg=processor.blur().grayscale().toMat();let mean=cv.mean(grayscaleImg)[0];return mean??0}finally{processor?.destroy()}}
|
package/image-processor.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import
|
|
3
|
-
import type { ConvertOptions } from "./pipeline";
|
|
1
|
+
import type { CanvasLike } from "./canvas-factory.js";
|
|
2
|
+
import { cv } from "./cv-provider.js";
|
|
3
|
+
import type { AdaptiveThresholdOptions, BlurOptions, BorderOptions, CannyOptions, ConvertOptions, DilateOptions, ErodeOptions, GrayscaleOptions, InvertOptions, MorphologicalGradientOptions, OperationName, OperationOptions, RequiredOptions, ResizeOptions, RotateOptions, ThresholdOptions, WarpOptions } from "./pipeline/index.js";
|
|
4
4
|
type NameWithRequiredOptions = {
|
|
5
5
|
[N in OperationName]: OperationOptions<N> extends RequiredOptions ? N : never;
|
|
6
6
|
}[OperationName];
|
|
@@ -11,19 +11,23 @@ export declare class ImageProcessor {
|
|
|
11
11
|
height: number;
|
|
12
12
|
/**
|
|
13
13
|
* Create an ImageProcessor instance from a Canvas or cv.Mat
|
|
14
|
-
* @param source Source image as
|
|
14
|
+
* @param source Source image as CanvasLike or cv.Mat
|
|
15
15
|
*/
|
|
16
|
-
constructor(source:
|
|
16
|
+
constructor(source: CanvasLike | cv.Mat);
|
|
17
17
|
/**
|
|
18
18
|
* Convert array buffer to canvas
|
|
19
19
|
*/
|
|
20
|
-
static prepareCanvas(file: ArrayBuffer): Promise<
|
|
20
|
+
static prepareCanvas(file: ArrayBuffer): Promise<CanvasLike>;
|
|
21
21
|
/**
|
|
22
22
|
* Convert canvas to array buffer
|
|
23
23
|
*/
|
|
24
|
-
static prepareBuffer(canvas:
|
|
24
|
+
static prepareBuffer(canvas: CanvasLike): Promise<ArrayBuffer>;
|
|
25
25
|
/**
|
|
26
|
-
* Initialize OpenCV runtime
|
|
26
|
+
* Initialize OpenCV runtime. Must be called before any image processing.
|
|
27
|
+
*
|
|
28
|
+
* - **Node.js**: Uses `@techstark/opencv-js` from node_modules (loaded by entry point).
|
|
29
|
+
* - **Browser with bundler**: Resolves `@techstark/opencv-js` via the bundler.
|
|
30
|
+
* - **Browser without bundler**: Falls back to loading `@techstark/opencv-js` from npm CDN.
|
|
27
31
|
*/
|
|
28
32
|
static initRuntime(): Promise<void>;
|
|
29
33
|
/**
|
|
@@ -31,47 +35,43 @@ export declare class ImageProcessor {
|
|
|
31
35
|
* @param operationName Name of the operation (e.g., "resize")
|
|
32
36
|
* @param options Required options for the operation
|
|
33
37
|
*/
|
|
34
|
-
execute<
|
|
38
|
+
execute<N extends NameWithRequiredOptions>(operationName: N, options: OperationOptions<N>): this;
|
|
35
39
|
/**
|
|
36
|
-
* Execute a registered pipeline operation
|
|
37
|
-
* @param operationName Name of the operation (e.g., "blur"
|
|
38
|
-
* @param options Optional
|
|
40
|
+
* Execute a registered pipeline operation that has default options.
|
|
41
|
+
* @param operationName Name of the operation (e.g., "blur")
|
|
42
|
+
* @param options Optional override of the default options
|
|
39
43
|
*/
|
|
40
|
-
execute<
|
|
44
|
+
execute<N extends NameWithOptionalOptions>(operationName: N, options?: Partial<OperationOptions<N>>): this;
|
|
41
45
|
/**
|
|
42
46
|
* Convert image to grayscale
|
|
43
47
|
* @description Usage order: independent
|
|
44
48
|
* @param options Optional configuration for grayscale conversion
|
|
45
49
|
*/
|
|
46
50
|
grayscale(options?: Partial<GrayscaleOptions>): this;
|
|
47
|
-
/**
|
|
48
|
-
* Invert image colors
|
|
49
|
-
* @description Usage order: ideally (after) threshold or adaptiveThreshold
|
|
50
|
-
* @param options Optional configuration for inversion
|
|
51
|
-
*/
|
|
52
|
-
invert(options?: Partial<InvertOptions>): this;
|
|
53
|
-
/**
|
|
54
|
-
* Add border to image
|
|
55
|
-
* @description Usage order: independent
|
|
56
|
-
* @param options Border configuration options
|
|
57
|
-
*/
|
|
58
|
-
border(options?: Partial<BorderOptions>): this;
|
|
59
51
|
/**
|
|
60
52
|
* Bluring image to reduce noise using Gaussian Blur
|
|
61
53
|
* @description Usage order: (ideally after) grayscale
|
|
62
54
|
* @param options Blur configuration options
|
|
63
55
|
*/
|
|
64
56
|
blur(options?: Partial<BlurOptions>): this;
|
|
65
|
-
/**
|
|
57
|
+
/**
|
|
58
|
+
* Thresholding to convert image to binary
|
|
66
59
|
* @description Usage order: (after) grayscale (and optionally blur)
|
|
67
60
|
* @param options Thresholding configuration options
|
|
68
61
|
*/
|
|
69
62
|
threshold(options?: Partial<ThresholdOptions>): this;
|
|
70
|
-
/**
|
|
63
|
+
/**
|
|
64
|
+
* Adaptive thresholding to convert image to binary
|
|
71
65
|
* @description Usage order: (after) grayscale (and optionally blur)
|
|
72
66
|
* @param options Adaptive thresholding configuration options
|
|
73
67
|
*/
|
|
74
68
|
adaptiveThreshold(options?: Partial<AdaptiveThresholdOptions>): this;
|
|
69
|
+
/**
|
|
70
|
+
* Invert image colors
|
|
71
|
+
* @description Usage order: ideally (after) threshold or adaptiveThreshold
|
|
72
|
+
* @param options Optional configuration for inversion
|
|
73
|
+
*/
|
|
74
|
+
invert(options?: Partial<InvertOptions>): this;
|
|
75
75
|
/**
|
|
76
76
|
* Canny edge detection to detect edges in the image
|
|
77
77
|
* @description Usage order: (after) grayscale + blur
|
|
@@ -79,11 +79,11 @@ export declare class ImageProcessor {
|
|
|
79
79
|
*/
|
|
80
80
|
canny(options?: Partial<CannyOptions>): this;
|
|
81
81
|
/**
|
|
82
|
-
*
|
|
83
|
-
* @description Usage order: (after)
|
|
84
|
-
* @param options
|
|
82
|
+
* Dilate image to increase the size of the foreground object
|
|
83
|
+
* @description Usage order: (after) threshold or edge detection
|
|
84
|
+
* @param options Dilation configuration options
|
|
85
85
|
*/
|
|
86
|
-
|
|
86
|
+
dilate(options?: Partial<DilateOptions>): this;
|
|
87
87
|
/**
|
|
88
88
|
* Erode image to reduce noise
|
|
89
89
|
* @description Usage order: (after) threshold or edge detection
|
|
@@ -91,29 +91,29 @@ export declare class ImageProcessor {
|
|
|
91
91
|
*/
|
|
92
92
|
erode(options?: Partial<ErodeOptions>): this;
|
|
93
93
|
/**
|
|
94
|
-
*
|
|
95
|
-
* @description Usage order:
|
|
96
|
-
* @param options
|
|
94
|
+
* Add border to image
|
|
95
|
+
* @description Usage order: independent
|
|
96
|
+
* @param options Border configuration options
|
|
97
97
|
*/
|
|
98
|
-
|
|
98
|
+
border(options?: Partial<BorderOptions>): this;
|
|
99
99
|
/**
|
|
100
100
|
* Resize image to a new width and height
|
|
101
101
|
* @description Usage order: independent
|
|
102
|
-
*
|
|
102
|
+
* @param options Resize configuration options
|
|
103
103
|
*/
|
|
104
104
|
resize(options: ResizeOptions): this;
|
|
105
|
-
/**
|
|
106
|
-
* Warp image to a new perspective
|
|
107
|
-
* @description Usage order: independent
|
|
108
|
-
* @param options Warp configuration options
|
|
109
|
-
*/
|
|
110
|
-
warp(options: WarpOptions): this;
|
|
111
105
|
/**
|
|
112
106
|
* Rotate image by a given angle
|
|
113
107
|
* @description Usage order: independent
|
|
114
108
|
* @param options Rotate configuration options
|
|
115
109
|
*/
|
|
116
110
|
rotate(options: RotateOptions): this;
|
|
111
|
+
/**
|
|
112
|
+
* Warp image to a new perspective
|
|
113
|
+
* @description Usage order: independent
|
|
114
|
+
* @param options Warp configuration options
|
|
115
|
+
*/
|
|
116
|
+
warp(options: WarpOptions): this;
|
|
117
117
|
/**
|
|
118
118
|
* Convert image matrix into new matrix type
|
|
119
119
|
* @description Usage order: independent
|
|
@@ -121,22 +121,22 @@ export declare class ImageProcessor {
|
|
|
121
121
|
*/
|
|
122
122
|
convert(options: ConvertOptions): this;
|
|
123
123
|
/**
|
|
124
|
-
*
|
|
125
|
-
* @
|
|
126
|
-
* @
|
|
124
|
+
* Morphological gradient to highlight the edges in the image
|
|
125
|
+
* @description Usage order: (after) dilation + erosion (or threshold)
|
|
126
|
+
* @param options Morphological gradient configuration options
|
|
127
127
|
*/
|
|
128
|
-
|
|
128
|
+
morphologicalGradient(options?: Partial<MorphologicalGradientOptions>): this;
|
|
129
129
|
/**
|
|
130
|
-
*
|
|
131
|
-
* @kind non-chainable
|
|
132
|
-
* @returns cv.Mat
|
|
130
|
+
* Get the result as a cv.Mat
|
|
133
131
|
*/
|
|
134
132
|
toMat(): cv.Mat;
|
|
135
133
|
/**
|
|
136
|
-
*
|
|
137
|
-
* @kind non-chainable
|
|
138
|
-
* @returns Canvas
|
|
134
|
+
* Get the result canvas
|
|
139
135
|
*/
|
|
140
|
-
toCanvas():
|
|
136
|
+
toCanvas(): CanvasLike;
|
|
137
|
+
/**
|
|
138
|
+
* Clean up cv.Mat to free memory
|
|
139
|
+
*/
|
|
140
|
+
destroy(): void;
|
|
141
141
|
}
|
|
142
142
|
export {};
|
package/image-processor.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export class ImageProcessor{img;width;height;constructor(source){if(source
|
|
1
|
+
export class ImageProcessor{img;width;height;constructor(source){if(getPlatform().isCanvas(source)){let ctx=source.getContext("2d");let imageData=ctx.getImageData(0,0,source.width,source.height);this.img=cv.matFromImageData(imageData);this.width=source.width;this.height=source.height}else if(source instanceof cv.Mat){this.img=source;this.width=source.cols;this.height=source.rows}else{throw new Error("Invalid source type. Must be either Canvas or cv.Mat.")}}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}static async initRuntime(){if(globalThis.cv?.Mat){setCv(globalThis.cv);return}try{let mod=await import("@techstark/opencv-js");let _cv=mod.default||mod;setCv(_cv);if(!_cv.Mat){await new Promise((res)=>{_cv["onRuntimeInitialized"]=()=>res()})}return}catch{}if(typeof document!=="undefined"){await new Promise((resolve,reject)=>{let script=document.createElement("script");script.src="https://cdn.jsdelivr.net/npm/@techstark/opencv-js@4.10.0-release.1/dist/opencv.js";script.async=true;script.onload=()=>{let g=globalThis;if(g.cv?.Mat){setCv(g.cv);resolve()}else if(g.cv){g.cv["onRuntimeInitialized"]=()=>{setCv(g.cv);resolve()}}else{reject(new Error("OpenCV.js loaded but cv not found on globalThis"))}};script.onerror=()=>reject(new Error("Failed to load @techstark/opencv-js from CDN"));document.head.appendChild(script)});return}throw new Error("Cannot initialize OpenCV runtime. Install @techstark/opencv-js or run in a browser.")}execute(operationName,options){let result=executeOperation(operationName,this.img,options);this.img=result.img;this.width=result.width;this.height=result.height;return this}grayscale(options){return this.execute("grayscale",options)}blur(options){return this.execute("blur",options)}threshold(options){return this.execute("threshold",options)}adaptiveThreshold(options){return this.execute("adaptiveThreshold",options)}invert(options){return this.execute("invert",options)}canny(options){return this.execute("canny",options)}dilate(options){return this.execute("dilate",options)}erode(options){return this.execute("erode",options)}border(options){return this.execute("border",options)}resize(options){return this.execute("resize",options)}rotate(options){return this.execute("rotate",options)}warp(options){return this.execute("warp",options)}convert(options){return this.execute("convert",options)}morphologicalGradient(options){return this.execute("morphologicalGradient",options)}toMat(){return this.img}toCanvas(){let platform=getPlatform();let canvas=platform.createCanvas(this.width,this.height);try{cv.imshow(canvas,this.img)}catch(e){let ctx=canvas.getContext("2d");if(!ctx)throw new Error("Could not get 2d context from canvas");let imgData=ctx.createImageData(this.width,this.height);if(this.img.channels()===1){let data=imgData.data;let gray=new Uint8Array(this.img.data);for(let i=0;i<gray.length;i++){data[i*4]=gray[i];data[i*4+1]=gray[i];data[i*4+2]=gray[i];data[i*4+3]=255}}else{imgData.data.set(new Uint8ClampedArray(this.img.data))}ctx.putImageData(imgData,0,0)}return canvas}destroy(){try{this.img.delete()}catch{}}}import{getPlatform}from"./canvas-factory.js";import{cv,setCv}from"./cv-provider.js";import{executeOperation}from"./pipeline/index.js";
|
package/index.d.ts
CHANGED
|
@@ -1,11 +1,14 @@
|
|
|
1
|
-
import cv from "
|
|
1
|
+
import { cv } from "./cv-provider.js";
|
|
2
2
|
export { cv };
|
|
3
3
|
export { Canvas, createCanvas, ImageData, loadImage } from "@napi-rs/canvas";
|
|
4
4
|
export type { SKRSContext2D } from "@napi-rs/canvas";
|
|
5
|
-
export type { BoundingBox, Coordinate, Points } from "./index.interface";
|
|
6
|
-
export { executeOperation, OperationRegistry, registry } from "./pipeline";
|
|
7
|
-
export {
|
|
8
|
-
export {
|
|
9
|
-
export {
|
|
10
|
-
export {
|
|
11
|
-
export
|
|
5
|
+
export type { BoundingBox, Coordinate, Points } from "./index.interface.js";
|
|
6
|
+
export { executeOperation, OperationRegistry, registry, } from "./pipeline/index.js";
|
|
7
|
+
export { getPlatform, setPlatform } from "./canvas-factory.js";
|
|
8
|
+
export type { CanvasLike, CanvasPlatform, Context2DLike, } from "./canvas-factory.js";
|
|
9
|
+
export { CanvasToolkitBase } from "./canvas-toolkit.base.js";
|
|
10
|
+
export { CanvasToolkit } from "./canvas-toolkit.js";
|
|
11
|
+
export { Contours } from "./contours.js";
|
|
12
|
+
export { calculateMeanGrayscaleValue, calculateMeanNormalizedLabLightness, type CalculateMeanLightnessOptions, } from "./image-analysis.js";
|
|
13
|
+
export { ImageProcessor } from "./image-processor.js";
|
|
14
|
+
export type { AdaptiveThresholdOptions, BlurOptions, BorderOptions, CannyOptions, DilateOptions, ErodeOptions, GrayscaleOptions, InvertOptions, MorphologicalGradientOptions, OperationFunction, OperationName, OperationOptions, OperationResult, PartialOptions, RegisteredOperations, RequiredOptions, ResizeOptions, RotateOptions, ThresholdOptions, WarpOptions, } from "./pipeline/index.js";
|
package/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import
|
|
1
|
+
import _cv from"@techstark/opencv-js";import{cv,setCv}from"./cv-provider.js";setCv(_cv);export{cv};import{setPlatform}from"./canvas-factory.js";import{nodePlatform}from"./platform/node.js";setPlatform(nodePlatform);export{Canvas,createCanvas,ImageData,loadImage}from"@napi-rs/canvas";export{executeOperation,OperationRegistry,registry}from"./pipeline/index.js";export{getPlatform,setPlatform}from"./canvas-factory.js";export{CanvasToolkitBase}from"./canvas-toolkit.base.js";export{CanvasToolkit}from"./canvas-toolkit.js";export{Contours}from"./contours.js";export{calculateMeanGrayscaleValue,calculateMeanNormalizedLabLightness}from"./image-analysis.js";export{ImageProcessor}from"./image-processor.js";
|
package/index.web.d.ts
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { cv } from "./cv-provider.js";
|
|
2
|
+
export { cv };
|
|
3
|
+
export { getPlatform, setPlatform } from "./canvas-factory.js";
|
|
4
|
+
export type { CanvasLike, CanvasPlatform, Context2DLike, } from "./canvas-factory.js";
|
|
5
|
+
export { webPlatform } from "./platform/web.js";
|
|
6
|
+
export type { BoundingBox, Coordinate, Points } from "./index.interface.js";
|
|
7
|
+
export { executeOperation, OperationRegistry, registry, } from "./pipeline/index.js";
|
|
8
|
+
export { CanvasToolkitBase as CanvasToolkit, CanvasToolkitBase, } from "./canvas-toolkit.base.js";
|
|
9
|
+
export { Contours } from "./contours.js";
|
|
10
|
+
export { calculateMeanGrayscaleValue, calculateMeanNormalizedLabLightness, type CalculateMeanLightnessOptions, } from "./image-analysis.js";
|
|
11
|
+
export { ImageProcessor } from "./image-processor.js";
|
|
12
|
+
export type { AdaptiveThresholdOptions, BlurOptions, BorderOptions, CannyOptions, DilateOptions, ErodeOptions, GrayscaleOptions, InvertOptions, MorphologicalGradientOptions, OperationFunction, OperationName, OperationOptions, OperationResult, PartialOptions, RegisteredOperations, RequiredOptions, ResizeOptions, RotateOptions, ThresholdOptions, WarpOptions, } from "./pipeline/index.js";
|
package/index.web.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{cv}from"./cv-provider.js";export{cv};import{setPlatform}from"./canvas-factory.js";import{webPlatform}from"./platform/web.js";setPlatform(webPlatform);export{getPlatform,setPlatform}from"./canvas-factory.js";export{webPlatform}from"./platform/web.js";export{executeOperation,OperationRegistry,registry}from"./pipeline/index.js";export{CanvasToolkitBase as CanvasToolkit,CanvasToolkitBase}from"./canvas-toolkit.base.js";export{Contours}from"./contours.js";export{calculateMeanGrayscaleValue,calculateMeanNormalizedLabLightness}from"./image-analysis.js";export{ImageProcessor}from"./image-processor.js";
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import
|
|
2
|
-
import {
|
|
3
|
-
declare module "../
|
|
1
|
+
import { cv } from "../cv-provider.js";
|
|
2
|
+
import type { OperationResult, PartialOptions } from "../pipeline/types.js";
|
|
3
|
+
declare module "../pipeline/types" {
|
|
4
4
|
interface RegisteredOperations {
|
|
5
5
|
adaptiveThreshold: AdaptiveThresholdOptions;
|
|
6
6
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{cv
|
|
1
|
+
import{cv}from"../cv-provider.js";import{registry}from"../pipeline/registry.js";function defaultOptions(){return{upper:255,method:cv.ADAPTIVE_THRESH_GAUSSIAN_C,type:cv.THRESH_BINARY_INV,size:7,constant:2}}export function adaptiveThreshold(img,options){let imgAdaptiveThreshold=new cv.Mat;cv.adaptiveThreshold(img,imgAdaptiveThreshold,options.upper,options.method,options.type,options.size,options.constant);img.delete();return{img:imgAdaptiveThreshold,width:imgAdaptiveThreshold.cols,height:imgAdaptiveThreshold.rows}}registry.register("adaptiveThreshold",adaptiveThreshold,defaultOptions);
|
package/operations/blur.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import
|
|
2
|
-
import {
|
|
3
|
-
declare module "../
|
|
1
|
+
import { cv } from "../cv-provider.js";
|
|
2
|
+
import type { OperationResult, PartialOptions } from "../pipeline/types.js";
|
|
3
|
+
declare module "../pipeline/types" {
|
|
4
4
|
interface RegisteredOperations {
|
|
5
5
|
blur: BlurOptions;
|
|
6
6
|
}
|
package/operations/blur.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{cv
|
|
1
|
+
import{cv}from"../cv-provider.js";import{registry}from"../pipeline/registry.js";function defaultOptions(){return{size:[5,5],sigma:0}}export function blur(img,options){let imgBlur=new cv.Mat;cv.GaussianBlur(img,imgBlur,new cv.Size(options.size[0],options.size[1]),options.sigma);img.delete();return{img:imgBlur,width:imgBlur.cols,height:imgBlur.rows}}registry.register("blur",blur,defaultOptions);
|
package/operations/border.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import type { OperationResult, PartialOptions } from "../
|
|
2
|
-
import { cv } from "../
|
|
3
|
-
declare module "../
|
|
1
|
+
import type { OperationResult, PartialOptions } from "../pipeline/types.js";
|
|
2
|
+
import { cv } from "../cv-provider.js";
|
|
3
|
+
declare module "../pipeline/types" {
|
|
4
4
|
interface RegisteredOperations {
|
|
5
5
|
border: BorderOptions;
|
|
6
6
|
}
|
package/operations/border.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{cv
|
|
1
|
+
import{cv}from"../cv-provider.js";import{registry}from"../pipeline/registry.js";function defaultOptions(){return{size:10,borderType:cv.BORDER_CONSTANT,borderColor:[255,255,255,255]}}export function border(img,options){let imgBorder=new cv.Mat;cv.copyMakeBorder(img,imgBorder,options.size,options.size,options.size,options.size,options.borderType,options.borderColor);img.delete();return{img:imgBorder,width:imgBorder.cols,height:imgBorder.rows}}registry.register("border",border,defaultOptions);
|
package/operations/canny.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import type { OperationResult, PartialOptions } from "../
|
|
2
|
-
import { cv } from "../
|
|
3
|
-
declare module "../
|
|
1
|
+
import type { OperationResult, PartialOptions } from "../pipeline/types.js";
|
|
2
|
+
import { cv } from "../cv-provider.js";
|
|
3
|
+
declare module "../pipeline/types" {
|
|
4
4
|
interface RegisteredOperations {
|
|
5
5
|
canny: CannyOptions;
|
|
6
6
|
}
|
package/operations/canny.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{cv
|
|
1
|
+
import{cv}from"../cv-provider.js";import{registry}from"../pipeline/registry.js";function defaultOptions(){return{lower:50,upper:150}}export function canny(img,options){let imgCanny=new cv.Mat;cv.Canny(img,imgCanny,options.lower,options.upper);img.delete();return{img:imgCanny,width:imgCanny.cols,height:imgCanny.rows}}registry.register("canny",canny,defaultOptions);
|
package/operations/convert.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import type { OperationResult, RequiredOptions } from "../
|
|
2
|
-
import { cv } from "../
|
|
3
|
-
declare module "../
|
|
1
|
+
import type { OperationResult, RequiredOptions } from "../pipeline/types.js";
|
|
2
|
+
import { cv } from "../cv-provider.js";
|
|
3
|
+
declare module "../pipeline/types" {
|
|
4
4
|
interface RegisteredOperations {
|
|
5
5
|
convert: ConvertOptions;
|
|
6
6
|
}
|
package/operations/convert.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{cv
|
|
1
|
+
import{cv}from"../cv-provider.js";import{registry}from"../pipeline/registry.js";export function convert(img,options){if(options.rtype===undefined){throw new Error("Invalid options: rtype is required")}let imgConvert=new cv.Mat;img.convertTo(imgConvert,options.rtype);img.delete();return{img:imgConvert,width:imgConvert.cols,height:imgConvert.rows}}registry.register("convert",convert);
|
package/operations/dilate.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import type { OperationResult, PartialOptions } from "../
|
|
2
|
-
import { cv } from "../
|
|
3
|
-
declare module "../
|
|
1
|
+
import type { OperationResult, PartialOptions } from "../pipeline/types.js";
|
|
2
|
+
import { cv } from "../cv-provider.js";
|
|
3
|
+
declare module "../pipeline/types" {
|
|
4
4
|
interface RegisteredOperations {
|
|
5
5
|
dilate: DilateOptions;
|
|
6
6
|
}
|
package/operations/dilate.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{cv
|
|
1
|
+
import{cv}from"../cv-provider.js";import{registry}from"../pipeline/registry.js";function defaultOptions(){return{size:[5,5],iter:1}}export function dilate(img,options){let imgDilate=new cv.Mat;let kernel=cv.getStructuringElement(cv.MORPH_RECT,new cv.Size(options.size[0],options.size[1]));cv.dilate(img,imgDilate,kernel,new cv.Point(-1,-1),options.iter);img.delete();return{img:imgDilate,width:imgDilate.cols,height:imgDilate.rows}}registry.register("dilate",dilate,defaultOptions);
|
package/operations/erode.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import type { OperationResult, PartialOptions } from "../
|
|
2
|
-
import { cv } from "../
|
|
3
|
-
declare module "../
|
|
1
|
+
import type { OperationResult, PartialOptions } from "../pipeline/types.js";
|
|
2
|
+
import { cv } from "../cv-provider.js";
|
|
3
|
+
declare module "../pipeline/types" {
|
|
4
4
|
interface RegisteredOperations {
|
|
5
5
|
erode: ErodeOptions;
|
|
6
6
|
}
|
package/operations/erode.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{cv
|
|
1
|
+
import{cv}from"../cv-provider.js";import{registry}from"../pipeline/registry.js";function defaultOptions(){return{size:[5,5],iter:1}}export function erode(img,options){let imgErode=new cv.Mat;let kernel=cv.getStructuringElement(cv.MORPH_RECT,new cv.Size(options.size[0],options.size[1]));cv.erode(img,imgErode,kernel,new cv.Point(-1,-1),options.iter);img.delete();return{img:imgErode,width:imgErode.cols,height:imgErode.rows}}registry.register("erode",erode,defaultOptions);
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import type { OperationResult, PartialOptions } from "../
|
|
2
|
-
import { cv } from "../
|
|
3
|
-
declare module "../
|
|
1
|
+
import type { OperationResult, PartialOptions } from "../pipeline/types.js";
|
|
2
|
+
import { cv } from "../cv-provider.js";
|
|
3
|
+
declare module "../pipeline/types" {
|
|
4
4
|
interface RegisteredOperations {
|
|
5
5
|
grayscale: GrayscaleOptions;
|
|
6
6
|
}
|
package/operations/grayscale.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{cv
|
|
1
|
+
import{cv}from"../cv-provider.js";import{registry}from"../pipeline/registry.js";function defaultOptions(){return{}}export function grayscale(img,options){let imgGrayscale=new cv.Mat;cv.cvtColor(img,imgGrayscale,cv.COLOR_RGBA2GRAY);img.delete();return{img:imgGrayscale,width:imgGrayscale.cols,height:imgGrayscale.rows}}registry.register("grayscale",grayscale,defaultOptions);
|
package/operations/invert.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import type { OperationResult, PartialOptions } from "../
|
|
2
|
-
import { cv } from "../
|
|
3
|
-
declare module "../
|
|
1
|
+
import type { OperationResult, PartialOptions } from "../pipeline/types.js";
|
|
2
|
+
import { cv } from "../cv-provider.js";
|
|
3
|
+
declare module "../pipeline/types" {
|
|
4
4
|
interface RegisteredOperations {
|
|
5
5
|
invert: InvertOptions;
|
|
6
6
|
}
|
package/operations/invert.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{cv
|
|
1
|
+
import{cv}from"../cv-provider.js";import{registry}from"../pipeline/registry.js";function defaultOptions(){return{}}export function invert(img,options){let imgInvert=new cv.Mat;cv.bitwise_not(img,imgInvert);img.delete();return{img:imgInvert,width:imgInvert.cols,height:imgInvert.rows}}registry.register("invert",invert,defaultOptions);
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import type { OperationResult, PartialOptions } from "../
|
|
2
|
-
import { cv } from "../
|
|
3
|
-
declare module "../
|
|
1
|
+
import type { OperationResult, PartialOptions } from "../pipeline/types.js";
|
|
2
|
+
import { cv } from "../cv-provider.js";
|
|
3
|
+
declare module "../pipeline/types" {
|
|
4
4
|
interface RegisteredOperations {
|
|
5
5
|
morphologicalGradient: MorphologicalGradientOptions;
|
|
6
6
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{cv
|
|
1
|
+
import{cv}from"../cv-provider.js";import{registry}from"../pipeline/registry.js";function defaultOptions(){return{size:[3,3]}}export function morphologicalGradient(img,options){let imgMorphologicalGradient=new cv.Mat;let kernel=cv.getStructuringElement(cv.MORPH_RECT,new cv.Size(options.size[0],options.size[1]));cv.morphologyEx(img,imgMorphologicalGradient,cv.MORPH_GRADIENT,kernel);img.delete();return{img:imgMorphologicalGradient,width:imgMorphologicalGradient.cols,height:imgMorphologicalGradient.rows}}registry.register("morphologicalGradient",morphologicalGradient,defaultOptions);
|
package/operations/resize.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import type { OperationResult, RequiredOptions } from "../
|
|
2
|
-
import { cv } from "../
|
|
3
|
-
declare module "../
|
|
1
|
+
import type { OperationResult, RequiredOptions } from "../pipeline/types.js";
|
|
2
|
+
import { cv } from "../cv-provider.js";
|
|
3
|
+
declare module "../pipeline/types" {
|
|
4
4
|
interface RegisteredOperations {
|
|
5
5
|
resize: ResizeOptions;
|
|
6
6
|
}
|
package/operations/resize.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{cv
|
|
1
|
+
import{cv}from"../cv-provider.js";import{registry}from"../pipeline/registry.js";export function resize(img,options){if(!options.width||!options.height){throw new Error("Invalid options: width and height are required")}let imgResize=new cv.Mat;cv.resize(img,imgResize,new cv.Size(options.width,options.height));img.delete();return{img:imgResize,width:imgResize.cols,height:imgResize.rows}}registry.register("resize",resize);
|
package/operations/rotate.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import type { OperationResult, RequiredOptions } from "../
|
|
2
|
-
import { cv } from "../
|
|
3
|
-
declare module "../
|
|
1
|
+
import type { OperationResult, RequiredOptions } from "../pipeline/types.js";
|
|
2
|
+
import { cv } from "../cv-provider.js";
|
|
3
|
+
declare module "../pipeline/types" {
|
|
4
4
|
interface RegisteredOperations {
|
|
5
5
|
rotate: RotateOptions;
|
|
6
6
|
}
|
package/operations/rotate.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{cv
|
|
1
|
+
import{cv}from"../cv-provider.js";import{registry}from"../pipeline/registry.js";export function rotate(img,options){let center=options.center||new cv.Point(img.cols/2,img.rows/2);let M=cv.getRotationMatrix2D(center,options.angle,1);let dsize=new cv.Size(img.cols,img.rows);let rotatedImg=new cv.Mat;cv.warpAffine(img,rotatedImg,M,dsize,cv.INTER_LINEAR,cv.BORDER_CONSTANT,new cv.Scalar);img.delete();M.delete();return{img:rotatedImg,width:rotatedImg.cols,height:rotatedImg.rows}}registry.register("rotate",rotate);
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import type { OperationResult, PartialOptions } from "../
|
|
2
|
-
import { cv } from "../
|
|
3
|
-
declare module "../
|
|
1
|
+
import type { OperationResult, PartialOptions } from "../pipeline/types.js";
|
|
2
|
+
import { cv } from "../cv-provider.js";
|
|
3
|
+
declare module "../pipeline/types" {
|
|
4
4
|
interface RegisteredOperations {
|
|
5
5
|
threshold: ThresholdOptions;
|
|
6
6
|
}
|
package/operations/threshold.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{cv
|
|
1
|
+
import{cv}from"../cv-provider.js";import{registry}from"../pipeline/registry.js";function defaultOptions(){return{lower:0,upper:255,type:cv.THRESH_BINARY_INV+cv.THRESH_OTSU}}export function threshold(img,options){let imgThreshold=new cv.Mat;cv.threshold(img,imgThreshold,options.lower,options.upper,options.type);img.delete();return{img:imgThreshold,width:imgThreshold.cols,height:imgThreshold.rows}}registry.register("threshold",threshold,defaultOptions);
|
package/operations/warp.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import
|
|
2
|
-
import {
|
|
3
|
-
|
|
1
|
+
import { cv } from "../cv-provider.js";
|
|
2
|
+
import type { BoundingBox, Points } from "../index.interface.js";
|
|
3
|
+
import type { OperationResult, RequiredOptions } from "../pipeline/types.js";
|
|
4
|
+
declare module "../pipeline/types" {
|
|
4
5
|
interface RegisteredOperations {
|
|
5
6
|
warp: WarpOptions;
|
|
6
7
|
}
|
package/operations/warp.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{cv
|
|
1
|
+
import{cv}from"../cv-provider.js";import{registry}from"../pipeline/registry.js";export function warp(img,options){if(!options.points||!options.bbox){throw new Error("Invalid options: points and bbox are required")}const{points,bbox}=options;let imgWarp=new cv.Mat;let targetWidth=bbox.x1-bbox.x0;let targetHeight=bbox.y1-bbox.y0;let destArray=[0,0,targetWidth-1,0,targetWidth-1,targetHeight-1,0,targetHeight-1];let srcArray=[points.topLeft.x,points.topLeft.y,points.topRight.x,points.topRight.y,points.bottomRight.x,points.bottomRight.y,points.bottomLeft.x,points.bottomLeft.y];let dest=cv.matFromArray(4,1,cv.CV_32FC2,destArray);let src=cv.matFromArray(4,1,cv.CV_32FC2,srcArray);let M=cv.getPerspectiveTransform(src,dest);let dsize=new cv.Size(targetWidth,targetHeight);cv.warpPerspective(img,imgWarp,M,dsize);M.delete();src.delete();dest.delete();img.delete();return{img:imgWarp,width:imgWarp.cols,height:imgWarp.rows}}registry.register("warp",warp);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ppu-ocv",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "2.0.0",
|
|
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",
|
|
@@ -16,28 +16,38 @@
|
|
|
16
16
|
"type-safe",
|
|
17
17
|
"modular",
|
|
18
18
|
"chainable",
|
|
19
|
-
"bun"
|
|
19
|
+
"bun",
|
|
20
|
+
"browser",
|
|
21
|
+
"web"
|
|
20
22
|
],
|
|
21
23
|
"author": "snowfluke",
|
|
22
24
|
"license": "MIT",
|
|
23
25
|
"type": "module",
|
|
24
26
|
"main": "./index.js",
|
|
25
27
|
"types": "./index.d.ts",
|
|
28
|
+
"exports": {
|
|
29
|
+
".": {
|
|
30
|
+
"types": "./index.d.ts",
|
|
31
|
+
"default": "./index.js"
|
|
32
|
+
},
|
|
33
|
+
"./web": {
|
|
34
|
+
"types": "./index.web.d.ts",
|
|
35
|
+
"default": "./index.web.js"
|
|
36
|
+
}
|
|
37
|
+
},
|
|
26
38
|
"scripts": {
|
|
27
39
|
"task": "bun scripts/task.ts",
|
|
28
40
|
"build:test": "bun task build && bun test",
|
|
29
41
|
"build:publish": "bun task build && bun task report-size && bun task publish",
|
|
30
|
-
"lint": "
|
|
31
|
-
"lint:fix": "
|
|
42
|
+
"lint": "prettier --check ./src",
|
|
43
|
+
"lint:fix": "prettier --write ./src"
|
|
32
44
|
},
|
|
33
45
|
"devDependencies": {
|
|
34
46
|
"@stylistic/eslint-plugin": "latest",
|
|
35
47
|
"@types/bun": "latest",
|
|
36
48
|
"@types/uglify-js": "latest",
|
|
37
|
-
"eslint": "latest",
|
|
38
|
-
"eslint-plugin-jsdoc": "latest",
|
|
39
49
|
"mitata": "latest",
|
|
40
|
-
"prettier": "^3.
|
|
50
|
+
"prettier": "^3.8.1",
|
|
41
51
|
"tsx": "latest",
|
|
42
52
|
"typescript": "latest",
|
|
43
53
|
"typescript-eslint": "latest",
|
package/pipeline/index.d.ts
CHANGED
|
@@ -1,30 +1,30 @@
|
|
|
1
|
-
export { executeOperation, OperationRegistry, registry } from "./registry";
|
|
2
|
-
export type { OperationFunction, OperationName, OperationOptions, OperationResult, PartialOptions, RegisteredOperations, RequiredOptions, } from "./types";
|
|
3
|
-
import "../operations/adaptive-threshold";
|
|
4
|
-
import "../operations/blur";
|
|
5
|
-
import "../operations/border";
|
|
6
|
-
import "../operations/canny";
|
|
7
|
-
import "../operations/convert";
|
|
8
|
-
import "../operations/dilate";
|
|
9
|
-
import "../operations/erode";
|
|
10
|
-
import "../operations/grayscale";
|
|
11
|
-
import "../operations/invert";
|
|
12
|
-
import "../operations/morphological-gradient";
|
|
13
|
-
import "../operations/resize";
|
|
14
|
-
import "../operations/rotate";
|
|
15
|
-
import "../operations/threshold";
|
|
16
|
-
import "../operations/warp";
|
|
17
|
-
export type { AdaptiveThresholdOptions } from "../operations/adaptive-threshold";
|
|
18
|
-
export type { BlurOptions } from "../operations/blur";
|
|
19
|
-
export type { BorderOptions } from "../operations/border";
|
|
20
|
-
export type { CannyOptions } from "../operations/canny";
|
|
21
|
-
export type { ConvertOptions } from "../operations/convert";
|
|
22
|
-
export type { DilateOptions } from "../operations/dilate";
|
|
23
|
-
export type { ErodeOptions } from "../operations/erode";
|
|
24
|
-
export type { GrayscaleOptions } from "../operations/grayscale";
|
|
25
|
-
export type { InvertOptions } from "../operations/invert";
|
|
26
|
-
export type { MorphologicalGradientOptions } from "../operations/morphological-gradient";
|
|
27
|
-
export type { ResizeOptions } from "../operations/resize";
|
|
28
|
-
export type { RotateOptions } from "../operations/rotate";
|
|
29
|
-
export type { ThresholdOptions } from "../operations/threshold";
|
|
30
|
-
export type { WarpOptions } from "../operations/warp";
|
|
1
|
+
export { executeOperation, OperationRegistry, registry } from "./registry.js";
|
|
2
|
+
export type { OperationFunction, OperationName, OperationOptions, OperationResult, PartialOptions, RegisteredOperations, RequiredOptions, } from "./types.js";
|
|
3
|
+
import "../operations/adaptive-threshold.js";
|
|
4
|
+
import "../operations/blur.js";
|
|
5
|
+
import "../operations/border.js";
|
|
6
|
+
import "../operations/canny.js";
|
|
7
|
+
import "../operations/convert.js";
|
|
8
|
+
import "../operations/dilate.js";
|
|
9
|
+
import "../operations/erode.js";
|
|
10
|
+
import "../operations/grayscale.js";
|
|
11
|
+
import "../operations/invert.js";
|
|
12
|
+
import "../operations/morphological-gradient.js";
|
|
13
|
+
import "../operations/resize.js";
|
|
14
|
+
import "../operations/rotate.js";
|
|
15
|
+
import "../operations/threshold.js";
|
|
16
|
+
import "../operations/warp.js";
|
|
17
|
+
export type { AdaptiveThresholdOptions } from "../operations/adaptive-threshold.js";
|
|
18
|
+
export type { BlurOptions } from "../operations/blur.js";
|
|
19
|
+
export type { BorderOptions } from "../operations/border.js";
|
|
20
|
+
export type { CannyOptions } from "../operations/canny.js";
|
|
21
|
+
export type { ConvertOptions } from "../operations/convert.js";
|
|
22
|
+
export type { DilateOptions } from "../operations/dilate.js";
|
|
23
|
+
export type { ErodeOptions } from "../operations/erode.js";
|
|
24
|
+
export type { GrayscaleOptions } from "../operations/grayscale.js";
|
|
25
|
+
export type { InvertOptions } from "../operations/invert.js";
|
|
26
|
+
export type { MorphologicalGradientOptions } from "../operations/morphological-gradient.js";
|
|
27
|
+
export type { ResizeOptions } from "../operations/resize.js";
|
|
28
|
+
export type { RotateOptions } from "../operations/rotate.js";
|
|
29
|
+
export type { ThresholdOptions } from "../operations/threshold.js";
|
|
30
|
+
export type { WarpOptions } from "../operations/warp.js";
|
package/pipeline/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export{executeOperation,OperationRegistry,registry}from"./registry";import"../operations/adaptive-threshold";import"../operations/blur";import"../operations/border";import"../operations/canny";import"../operations/convert";import"../operations/dilate";import"../operations/erode";import"../operations/grayscale";import"../operations/invert";import"../operations/morphological-gradient";import"../operations/resize";import"../operations/rotate";import"../operations/threshold";import"../operations/warp";
|
|
1
|
+
export{executeOperation,OperationRegistry,registry}from"./registry.js";import"../operations/adaptive-threshold.js";import"../operations/blur.js";import"../operations/border.js";import"../operations/canny.js";import"../operations/convert.js";import"../operations/dilate.js";import"../operations/erode.js";import"../operations/grayscale.js";import"../operations/invert.js";import"../operations/morphological-gradient.js";import"../operations/resize.js";import"../operations/rotate.js";import"../operations/threshold.js";import"../operations/warp.js";
|
package/pipeline/registry.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { cv } from "../
|
|
2
|
-
import type { OperationFunction, OperationName, OperationOptions, OperationResult } from "./index";
|
|
1
|
+
import { cv } from "../cv-provider.js";
|
|
2
|
+
import type { OperationFunction, OperationName, OperationOptions, OperationResult } from "./index.js";
|
|
3
3
|
export declare class OperationRegistry {
|
|
4
4
|
private operations;
|
|
5
5
|
private defaultOptions;
|
package/pipeline/types.d.ts
CHANGED
package/platform/node.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{Canvas,createCanvas as napiCreateCanvas,loadImage as napiLoadImage}from"@napi-rs/canvas";export let nodePlatform={createCanvas(width,height){return napiCreateCanvas(width,height)},async loadImage(source){let img=await napiLoadImage(source);let canvas=napiCreateCanvas(img.width,img.height);let ctx=canvas.getContext("2d");ctx.drawImage(img,0,0);return canvas},isCanvas(value){return value instanceof Canvas}};
|
package/platform/web.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export let webPlatform={createCanvas(width,height){if(typeof OffscreenCanvas!=="undefined"){return new OffscreenCanvas(width,height)}if(typeof document!=="undefined"){let c=document.createElement("canvas");c.width=width;c.height=height;return c}throw new Error("No canvas implementation available in this environment.")},async loadImage(source){let blob;if(source instanceof ArrayBuffer){blob=new Blob([source])}else if(typeof source==="string"){let res=await fetch(source);blob=await res.blob()}else{throw new Error("loadImage: unsupported source type")}let bitmap=await createImageBitmap(blob);let canvas=webPlatform.createCanvas(bitmap.width,bitmap.height);let ctx=canvas.getContext("2d");ctx.drawImage(bitmap,0,0);bitmap.close();return canvas},isCanvas(value){if(typeof HTMLCanvasElement!=="undefined"&&value instanceof HTMLCanvasElement){return true}if(typeof OffscreenCanvas!=="undefined"&&value instanceof OffscreenCanvas){return true}return false}};
|