ppu-ocv 3.0.0 → 3.1.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 +53 -1
- 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 +1 -1
- package/index.canvas.d.ts +1 -1
- package/index.d.ts +1 -1
- package/index.web.d.ts +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -251,13 +251,65 @@ See: [How to extend ppu-ocv operations](./docs/how-to-extend-ppu-ocv-operations.
|
|
|
251
251
|
|
|
252
252
|
#### `CanvasProcessor`
|
|
253
253
|
|
|
254
|
-
Canvas
|
|
254
|
+
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.
|
|
255
|
+
|
|
256
|
+
```ts
|
|
257
|
+
const result = new CanvasProcessor(canvas)
|
|
258
|
+
.resize({ width: 360, height: 640 })
|
|
259
|
+
.grayscale()
|
|
260
|
+
.threshold({ thresh: 127 })
|
|
261
|
+
.invert()
|
|
262
|
+
.border({ size: 10, color: "white" })
|
|
263
|
+
.toCanvas();
|
|
264
|
+
|
|
265
|
+
// Detect connected white regions on a binary image
|
|
266
|
+
const regions = new CanvasProcessor(binaryCanvas).findRegions({
|
|
267
|
+
foreground: "light",
|
|
268
|
+
minArea: 20,
|
|
269
|
+
// thresh: 0 ← use on resized binary images to match OpenCV (any non-zero pixel = foreground)
|
|
270
|
+
// padding: { vertical: 0.4, horizontal: 0.6 } ← expand bbox by fraction of height
|
|
271
|
+
// scale: 1 / resizeRatio ← map coords back to original image space
|
|
272
|
+
});
|
|
273
|
+
regions.sort((a, b) => b.area - a.area); // largest first
|
|
274
|
+
// regions[0] → { bbox: { x0, y0, x1, y1 }, area }
|
|
275
|
+
```
|
|
276
|
+
|
|
277
|
+
**Static I/O**
|
|
255
278
|
|
|
256
279
|
| Method | Args | Description |
|
|
257
280
|
| ---------------------- | ----------- | ----------------------------------------------------- |
|
|
258
281
|
| static `prepareCanvas` | ArrayBuffer | Load image bytes into a `CanvasLike` |
|
|
259
282
|
| static `prepareBuffer` | CanvasLike | Export a `CanvasLike` to an `ArrayBuffer` (PNG bytes) |
|
|
260
283
|
|
|
284
|
+
**Instance operations** (chainable, return `this`)
|
|
285
|
+
|
|
286
|
+
| Method | Options | OpenCV equivalent | Fidelity |
|
|
287
|
+
| ----------- | ---------------------------------- | ------------------------- | -------------- |
|
|
288
|
+
| `resize` | `width`, `height` | `cv.resize` INTER_LINEAR | 1:1 (↓), ≈ (↑) |
|
|
289
|
+
| `grayscale` | — | `COLOR_RGBA2GRAY` | **1:1** |
|
|
290
|
+
| `convert` | `alpha?`, `beta?` | `Mat.convertTo` (α·x + β) | **1:1** |
|
|
291
|
+
| `invert` | — | `cv.bitwise_not` | **1:1** ¹ |
|
|
292
|
+
| `threshold` | `thresh?` (127), `maxValue?` (255) | `THRESH_BINARY` | **1:1** |
|
|
293
|
+
| `border` | `size?` (10), `color?` (CSS) | `BORDER_CONSTANT` | **1:1** |
|
|
294
|
+
| `rotate` | `angle`, `cx?`, `cy?` | `warpAffine` | ≈ (±6 px) ² |
|
|
295
|
+
| `toCanvas` | — | — | — |
|
|
296
|
+
|
|
297
|
+
**Region detection** (returns data, does not mutate)
|
|
298
|
+
|
|
299
|
+
| Method | Options | Description |
|
|
300
|
+
| ------------- | ------------------------------------------------ | ------------------------------------------------------------ |
|
|
301
|
+
| `findRegions` | `foreground?` (`"light"`), `thresh?` (127), `minArea?`, `maxArea?`, `padding?`, `scale?` | 8-connected flood-fill on a binary canvas → `DetectedRegion[]` |
|
|
302
|
+
|
|
303
|
+
`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. ³
|
|
304
|
+
|
|
305
|
+
**`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).
|
|
306
|
+
|
|
307
|
+
> ¹ Canvas `invert` preserves the alpha channel; OpenCV `bitwise_not` also inverts alpha. Results are identical when the source is opaque (alpha=255).
|
|
308
|
+
>
|
|
309
|
+
> ² Canvas uses anti-aliased bilinear interpolation; OpenCV uses plain bilinear. Difference is visually imperceptible and has no impact on OCR quality.
|
|
310
|
+
>
|
|
311
|
+
> ³ `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.
|
|
312
|
+
|
|
261
313
|
#### `ImageProcessor`
|
|
262
314
|
|
|
263
315
|
Requires OpenCV. Available from `ppu-ocv` and `ppu-ocv/web`.
|
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
|
@@ -3,4 +3,4 @@ export { getPlatform, setPlatform } from "./canvas-factory.js";
|
|
|
3
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
|
@@ -5,4 +5,4 @@ export { getPlatform, setPlatform } from "./canvas-factory.js";
|
|
|
5
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
|
@@ -8,7 +8,7 @@ export { getPlatform, setPlatform } from "./canvas-factory.js";
|
|
|
8
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
|
@@ -6,7 +6,7 @@ export { webPlatform } from "./platform/web.js";
|
|
|
6
6
|
export type { BoundingBox, Coordinate, Points } from "./index.interface.js";
|
|
7
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";
|
package/package.json
CHANGED