@areb0s/ocr-browser 1.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.
@@ -0,0 +1,4 @@
1
+ import { FileUtilsBase } from '@areb0s/ocr-common';
2
+ export declare class FileUtils extends FileUtilsBase {
3
+ static read(url: string): Promise<string>;
4
+ }
@@ -0,0 +1,8 @@
1
+ import { FileUtilsBase } from '@areb0s/ocr-common';
2
+ export class FileUtils extends FileUtilsBase {
3
+ static async read(url) {
4
+ const res = await fetch(url);
5
+ return await res.text();
6
+ }
7
+ }
8
+ //# sourceMappingURL=FileUtils.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"FileUtils.js","sourceRoot":"","sources":["../src/FileUtils.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAA;AAElD,MAAM,OAAO,SAAU,SAAQ,aAAa;IAC1C,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,GAAW;QAC3B,MAAM,GAAG,GAAG,MAAM,KAAK,CAAC,GAAG,CAAC,CAAA;QAC5B,OAAO,MAAM,GAAG,CAAC,IAAI,EAAE,CAAA;IACzB,CAAC;CACF"}
@@ -0,0 +1,65 @@
1
+ import { ImageRawBase } from '@areb0s/ocr-common';
2
+ import type { ImageRawData, LineImage, SizeOption, BrowserImageInput } from '@areb0s/ocr-common';
3
+ export declare class ImageRaw extends ImageRawBase {
4
+ #private;
5
+ data: Uint8ClampedArray;
6
+ /**
7
+ * Check if the input is an ImageBitmap
8
+ */
9
+ static isImageBitmap(input: unknown): input is ImageBitmap;
10
+ /**
11
+ * Check if the input is an HTMLImageElement
12
+ */
13
+ static isHTMLImageElement(input: unknown): input is HTMLImageElement;
14
+ /**
15
+ * Check if the input is an HTMLCanvasElement
16
+ */
17
+ static isHTMLCanvasElement(input: unknown): input is HTMLCanvasElement;
18
+ /**
19
+ * Check if the input is an HTMLVideoElement
20
+ */
21
+ static isHTMLVideoElement(input: unknown): input is HTMLVideoElement;
22
+ /**
23
+ * Check if the input is ImageRawData
24
+ */
25
+ static isImageRawData(input: unknown): input is ImageRawData;
26
+ /**
27
+ * Universal factory method that accepts all browser image input types
28
+ * @param input - URL string, ImageRawData, ImageBitmap, HTMLImageElement, HTMLCanvasElement, or HTMLVideoElement
29
+ * @returns Promise<ImageRaw>
30
+ */
31
+ static from(input: BrowserImageInput): Promise<ImageRaw>;
32
+ /**
33
+ * Create ImageRaw from a URL (existing method)
34
+ */
35
+ static open(url: string): Promise<ImageRaw>;
36
+ /**
37
+ * Create ImageRaw from an ImageBitmap
38
+ * Note: This method will close the ImageBitmap after conversion to free memory
39
+ * @param bitmap - ImageBitmap to convert
40
+ * @returns Promise<ImageRaw>
41
+ */
42
+ static fromImageBitmap(bitmap: ImageBitmap): Promise<ImageRaw>;
43
+ /**
44
+ * Create ImageRaw from an HTMLImageElement
45
+ * @param img - HTMLImageElement to convert (must be loaded/decoded)
46
+ * @returns Promise<ImageRaw>
47
+ */
48
+ static fromHTMLImageElement(img: HTMLImageElement): Promise<ImageRaw>;
49
+ /**
50
+ * Create ImageRaw from an HTMLCanvasElement
51
+ * @param canvas - HTMLCanvasElement to convert
52
+ * @returns Promise<ImageRaw>
53
+ */
54
+ static fromHTMLCanvasElement(canvas: HTMLCanvasElement): Promise<ImageRaw>;
55
+ /**
56
+ * Create ImageRaw from an HTMLVideoElement (captures current frame)
57
+ * @param video - HTMLVideoElement to capture from
58
+ * @returns Promise<ImageRaw>
59
+ */
60
+ static fromHTMLVideoElement(video: HTMLVideoElement): Promise<ImageRaw>;
61
+ constructor({ data, width, height }: ImageRawData);
62
+ write(path: string): Promise<void>;
63
+ resize({ width, height }: SizeOption): Promise<this>;
64
+ drawBox(lineImages: LineImage[]): Promise<this>;
65
+ }
@@ -0,0 +1,283 @@
1
+ import { ImageRawBase } from '@areb0s/ocr-common';
2
+ import invariant from 'tiny-invariant';
3
+ export class ImageRaw extends ImageRawBase {
4
+ data;
5
+ #imageData;
6
+ #canvas;
7
+ // ===========================================
8
+ // Type Guards
9
+ // ===========================================
10
+ /**
11
+ * Check if the input is an ImageBitmap
12
+ */
13
+ static isImageBitmap(input) {
14
+ return typeof ImageBitmap !== 'undefined' && input instanceof ImageBitmap;
15
+ }
16
+ /**
17
+ * Check if the input is an HTMLImageElement
18
+ */
19
+ static isHTMLImageElement(input) {
20
+ return typeof HTMLImageElement !== 'undefined' && input instanceof HTMLImageElement;
21
+ }
22
+ /**
23
+ * Check if the input is an HTMLCanvasElement
24
+ */
25
+ static isHTMLCanvasElement(input) {
26
+ return typeof HTMLCanvasElement !== 'undefined' && input instanceof HTMLCanvasElement;
27
+ }
28
+ /**
29
+ * Check if the input is an HTMLVideoElement
30
+ */
31
+ static isHTMLVideoElement(input) {
32
+ return typeof HTMLVideoElement !== 'undefined' && input instanceof HTMLVideoElement;
33
+ }
34
+ /**
35
+ * Check if the input is ImageRawData
36
+ */
37
+ static isImageRawData(input) {
38
+ return (typeof input === 'object' &&
39
+ input !== null &&
40
+ 'data' in input &&
41
+ 'width' in input &&
42
+ 'height' in input &&
43
+ (input.data instanceof Uint8Array || input.data instanceof Uint8ClampedArray));
44
+ }
45
+ // ===========================================
46
+ // Factory Methods
47
+ // ===========================================
48
+ /**
49
+ * Universal factory method that accepts all browser image input types
50
+ * @param input - URL string, ImageRawData, ImageBitmap, HTMLImageElement, HTMLCanvasElement, or HTMLVideoElement
51
+ * @returns Promise<ImageRaw>
52
+ */
53
+ static async from(input) {
54
+ // String (URL or data URL)
55
+ if (typeof input === 'string') {
56
+ return ImageRaw.open(input);
57
+ }
58
+ // ImageBitmap
59
+ if (ImageRaw.isImageBitmap(input)) {
60
+ return ImageRaw.fromImageBitmap(input);
61
+ }
62
+ // HTMLImageElement
63
+ if (ImageRaw.isHTMLImageElement(input)) {
64
+ return ImageRaw.fromHTMLImageElement(input);
65
+ }
66
+ // HTMLCanvasElement
67
+ if (ImageRaw.isHTMLCanvasElement(input)) {
68
+ return ImageRaw.fromHTMLCanvasElement(input);
69
+ }
70
+ // HTMLVideoElement
71
+ if (ImageRaw.isHTMLVideoElement(input)) {
72
+ return ImageRaw.fromHTMLVideoElement(input);
73
+ }
74
+ // ImageRawData (fallback)
75
+ if (ImageRaw.isImageRawData(input)) {
76
+ return new ImageRaw(input);
77
+ }
78
+ throw new Error('Unsupported image input type');
79
+ }
80
+ /**
81
+ * Create ImageRaw from a URL (existing method)
82
+ */
83
+ static async open(url) {
84
+ const image = await imageFromUrl(url);
85
+ const canvas = document.createElement('canvas');
86
+ canvasDrawImage(canvas, image, image.naturalWidth, image.naturalHeight);
87
+ const imageData = canvasGetImageData(canvas);
88
+ return new ImageRaw({
89
+ data: imageData.data,
90
+ width: imageData.width,
91
+ height: imageData.height,
92
+ });
93
+ }
94
+ /**
95
+ * Create ImageRaw from an ImageBitmap
96
+ * Note: This method will close the ImageBitmap after conversion to free memory
97
+ * @param bitmap - ImageBitmap to convert
98
+ * @returns Promise<ImageRaw>
99
+ */
100
+ static async fromImageBitmap(bitmap) {
101
+ // Validate bitmap
102
+ if (bitmap.width === 0 || bitmap.height === 0) {
103
+ throw new Error('Invalid ImageBitmap: dimensions are zero (bitmap may be closed or neutered)');
104
+ }
105
+ const canvas = document.createElement('canvas');
106
+ canvas.width = bitmap.width;
107
+ canvas.height = bitmap.height;
108
+ const ctx = canvas.getContext('2d', { willReadFrequently: true });
109
+ if (!ctx) {
110
+ // Cleanup before throwing
111
+ try {
112
+ bitmap.close();
113
+ }
114
+ catch {
115
+ // Ignore if already closed
116
+ }
117
+ throw new Error('Failed to create canvas 2D context');
118
+ }
119
+ try {
120
+ ctx.drawImage(bitmap, 0, 0);
121
+ const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
122
+ return new ImageRaw({
123
+ data: imageData.data,
124
+ width: imageData.width,
125
+ height: imageData.height,
126
+ });
127
+ }
128
+ finally {
129
+ // Always close the bitmap to free memory
130
+ try {
131
+ bitmap.close();
132
+ }
133
+ catch {
134
+ // Ignore if already closed or neutered
135
+ }
136
+ }
137
+ }
138
+ /**
139
+ * Create ImageRaw from an HTMLImageElement
140
+ * @param img - HTMLImageElement to convert (must be loaded/decoded)
141
+ * @returns Promise<ImageRaw>
142
+ */
143
+ static async fromHTMLImageElement(img) {
144
+ // Ensure image is loaded
145
+ if (!img.complete || img.naturalWidth === 0) {
146
+ await img.decode();
147
+ }
148
+ const canvas = document.createElement('canvas');
149
+ canvasDrawImage(canvas, img, img.naturalWidth, img.naturalHeight);
150
+ const imageData = canvasGetImageData(canvas);
151
+ return new ImageRaw({
152
+ data: imageData.data,
153
+ width: imageData.width,
154
+ height: imageData.height,
155
+ });
156
+ }
157
+ /**
158
+ * Create ImageRaw from an HTMLCanvasElement
159
+ * @param canvas - HTMLCanvasElement to convert
160
+ * @returns Promise<ImageRaw>
161
+ */
162
+ static async fromHTMLCanvasElement(canvas) {
163
+ const imageData = canvasGetImageData(canvas);
164
+ return new ImageRaw({
165
+ data: imageData.data,
166
+ width: imageData.width,
167
+ height: imageData.height,
168
+ });
169
+ }
170
+ /**
171
+ * Create ImageRaw from an HTMLVideoElement (captures current frame)
172
+ * @param video - HTMLVideoElement to capture from
173
+ * @returns Promise<ImageRaw>
174
+ */
175
+ static async fromHTMLVideoElement(video) {
176
+ if (video.readyState < 2) {
177
+ throw new Error('Video is not ready. Ensure video has loaded metadata and data.');
178
+ }
179
+ const canvas = document.createElement('canvas');
180
+ canvas.width = video.videoWidth;
181
+ canvas.height = video.videoHeight;
182
+ const ctx = canvas.getContext('2d', { willReadFrequently: true });
183
+ if (!ctx) {
184
+ throw new Error('Failed to create canvas 2D context');
185
+ }
186
+ ctx.drawImage(video, 0, 0);
187
+ const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
188
+ return new ImageRaw({
189
+ data: imageData.data,
190
+ width: imageData.width,
191
+ height: imageData.height,
192
+ });
193
+ }
194
+ // ===========================================
195
+ // Constructor & Instance Methods
196
+ // ===========================================
197
+ constructor({ data, width, height }) {
198
+ const newData = Uint8ClampedArray.from(data);
199
+ super({
200
+ data: newData,
201
+ width,
202
+ height,
203
+ });
204
+ const canvas = document.createElement('canvas');
205
+ const imageData = new ImageData(newData, width, height);
206
+ canvasPutImageData(canvas, imageData);
207
+ this.#canvas = canvas;
208
+ this.#imageData = imageData;
209
+ this.data = newData; // this.data is undefined without this line
210
+ }
211
+ async write(path) {
212
+ document.body.append(this.#canvas);
213
+ }
214
+ async resize({ width, height }) {
215
+ invariant(width !== undefined || height !== undefined, 'both width and height are undefined');
216
+ const newWidth = width || Math.round((this.width / this.height) * height);
217
+ const newHeight = height || Math.round((this.height / this.width) * width);
218
+ const newCanvas = document.createElement('canvas');
219
+ canvasDrawImage(newCanvas, this.#canvas, newWidth, newHeight);
220
+ const newImageData = canvasGetImageData(newCanvas);
221
+ return this.#apply(newImageData);
222
+ }
223
+ async drawBox(lineImages) {
224
+ this.#ctx.strokeStyle = 'red';
225
+ for (const lineImage of lineImages) {
226
+ const [first, ...rests] = lineImage.box;
227
+ this.#ctx.beginPath();
228
+ this.#ctx.moveTo(first[0], first[1]);
229
+ for (const rest of rests) {
230
+ this.#ctx.lineTo(rest[0], rest[1]);
231
+ }
232
+ this.#ctx.closePath();
233
+ this.#ctx.stroke();
234
+ }
235
+ return this;
236
+ }
237
+ get #ctx() {
238
+ return this.#canvas.getContext('2d');
239
+ }
240
+ #apply(imageData) {
241
+ canvasPutImageData(this.#canvas, imageData);
242
+ this.#imageData = imageData;
243
+ this.data = imageData.data;
244
+ this.width = imageData.width;
245
+ this.height = imageData.height;
246
+ return this;
247
+ }
248
+ #putImageData() {
249
+ this.#canvas.width = this.width;
250
+ this.#canvas.height = this.height;
251
+ this.#ctx.putImageData(this.#imageData, 0, 0);
252
+ return this;
253
+ }
254
+ #drawImage(image, width, height) {
255
+ canvasDrawImage(this.#canvas, image, width, height);
256
+ return this;
257
+ }
258
+ }
259
+ // ===========================================
260
+ // Helper Functions
261
+ // ===========================================
262
+ function canvasDrawImage(canvas, image, width, height) {
263
+ canvas.width = width || image.width;
264
+ canvas.height = height || image.height;
265
+ const ctx = canvas.getContext('2d');
266
+ ctx.drawImage(image, 0, 0, canvas.width, canvas.height);
267
+ }
268
+ function canvasPutImageData(canvas, imageData, width, height) {
269
+ const ctx = canvas.getContext('2d');
270
+ canvas.width = width || imageData.width;
271
+ canvas.height = height || imageData.height;
272
+ ctx.putImageData(imageData, 0, 0);
273
+ }
274
+ function canvasGetImageData(canvas) {
275
+ return canvas.getContext('2d').getImageData(0, 0, canvas.width, canvas.height);
276
+ }
277
+ async function imageFromUrl(url) {
278
+ const image = new Image();
279
+ image.src = url;
280
+ await image.decode();
281
+ return image;
282
+ }
283
+ //# sourceMappingURL=ImageRaw.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ImageRaw.js","sourceRoot":"","sources":["../src/ImageRaw.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,5 @@
1
+ import Ocr from '@areb0s/ocr-common';
2
+ import { ImageRaw } from './ImageRaw.js';
3
+ export * from '@areb0s/ocr-common';
4
+ export { ImageRaw };
5
+ export default Ocr;
package/build/index.js ADDED
@@ -0,0 +1,11 @@
1
+ import Ocr, { registerBackend } from '@areb0s/ocr-common';
2
+ import { splitIntoLineImages } from '@areb0s/ocr-common/splitIntoLineImages';
3
+ import { InferenceSession } from 'onnxruntime-web';
4
+ import { FileUtils } from './FileUtils.js';
5
+ import { ImageRaw } from './ImageRaw.js';
6
+ // Browser doesn't have default models - user must provide model paths via Ocr.create({ models: {...} })
7
+ registerBackend({ FileUtils, ImageRaw, InferenceSession, splitIntoLineImages, defaultModels: undefined });
8
+ export * from '@areb0s/ocr-common';
9
+ export { ImageRaw }; // Export ImageRaw for direct access to static methods (from, fromImageBitmap, etc.)
10
+ export default Ocr;
11
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,GAAG,EAAE,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAA;AACzD,OAAO,EAAE,mBAAmB,EAAE,MAAM,wCAAwC,CAAA;AAC5E,OAAO,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAA;AAClD,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAA;AACvC,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAA;AAErC,wGAAwG;AACxG,eAAe,CAAC,EAAE,SAAS,EAAE,QAAQ,EAAE,gBAAgB,EAAE,mBAAmB,EAAE,aAAa,EAAE,SAAS,EAAE,CAAC,CAAA;AAEzG,cAAc,oBAAoB,CAAA;AAClC,OAAO,EAAE,QAAQ,EAAE,CAAA,CAAC,oFAAoF;AACxG,eAAe,GAAG,CAAA"}
package/package.json ADDED
@@ -0,0 +1,41 @@
1
+ {
2
+ "name": "@areb0s/ocr-browser",
3
+ "version": "1.0.0",
4
+ "description": "Fork of @gutenye/ocr-browser with ImageBitmap support. High accurate OCR library for Browser based on PaddleOCR and ONNX runtime. Accepts ImageBitmap, HTMLImageElement, HTMLCanvasElement, HTMLVideoElement as input.",
5
+ "keywords": [
6
+ "ocr",
7
+ "paddleocr",
8
+ "typescript",
9
+ "onnxruntime",
10
+ "web",
11
+ "browser",
12
+ "imagebitmap"
13
+ ],
14
+ "type": "module",
15
+ "license": "MIT",
16
+ "repository": {
17
+ "type": "git",
18
+ "url": "https://github.com/areb0s/ocr.git",
19
+ "directory": "packages/browser"
20
+ },
21
+ "exports": {
22
+ "bun": "./src/index.ts",
23
+ "node": "./build/index.js",
24
+ "react-native": "./src/index.ts",
25
+ "default": "./build/index.js"
26
+ },
27
+ "files": [
28
+ "src",
29
+ "build",
30
+ "!**/__tests__"
31
+ ],
32
+ "scripts": {
33
+ "build": "npx tsc --project tsconfig.build.json && npx tsc-alias --project tsconfig.build.json",
34
+ "prepublishOnly": "npm run build"
35
+ },
36
+ "dependencies": {
37
+ "@areb0s/ocr-common": "1.0.0",
38
+ "@gutenye/ocr-models": "^1.2.2",
39
+ "onnxruntime-web": "^1.17.3"
40
+ }
41
+ }
@@ -0,0 +1,8 @@
1
+ import { FileUtilsBase } from '@areb0s/ocr-common'
2
+
3
+ export class FileUtils extends FileUtilsBase {
4
+ static async read(url: string) {
5
+ const res = await fetch(url)
6
+ return await res.text()
7
+ }
8
+ }
@@ -0,0 +1,327 @@
1
+ import { ImageRawBase } from '@areb0s/ocr-common'
2
+ import type { ImageRawData, LineImage, SizeOption, BrowserImageInput } from '@areb0s/ocr-common'
3
+ import invariant from 'tiny-invariant'
4
+
5
+ export class ImageRaw extends ImageRawBase {
6
+ data: Uint8ClampedArray
7
+ #imageData: ImageData
8
+ #canvas: HTMLCanvasElement
9
+
10
+ // ===========================================
11
+ // Type Guards
12
+ // ===========================================
13
+
14
+ /**
15
+ * Check if the input is an ImageBitmap
16
+ */
17
+ static isImageBitmap(input: unknown): input is ImageBitmap {
18
+ return typeof ImageBitmap !== 'undefined' && input instanceof ImageBitmap
19
+ }
20
+
21
+ /**
22
+ * Check if the input is an HTMLImageElement
23
+ */
24
+ static isHTMLImageElement(input: unknown): input is HTMLImageElement {
25
+ return typeof HTMLImageElement !== 'undefined' && input instanceof HTMLImageElement
26
+ }
27
+
28
+ /**
29
+ * Check if the input is an HTMLCanvasElement
30
+ */
31
+ static isHTMLCanvasElement(input: unknown): input is HTMLCanvasElement {
32
+ return typeof HTMLCanvasElement !== 'undefined' && input instanceof HTMLCanvasElement
33
+ }
34
+
35
+ /**
36
+ * Check if the input is an HTMLVideoElement
37
+ */
38
+ static isHTMLVideoElement(input: unknown): input is HTMLVideoElement {
39
+ return typeof HTMLVideoElement !== 'undefined' && input instanceof HTMLVideoElement
40
+ }
41
+
42
+ /**
43
+ * Check if the input is ImageRawData
44
+ */
45
+ static isImageRawData(input: unknown): input is ImageRawData {
46
+ return (
47
+ typeof input === 'object' &&
48
+ input !== null &&
49
+ 'data' in input &&
50
+ 'width' in input &&
51
+ 'height' in input &&
52
+ (input.data instanceof Uint8Array || input.data instanceof Uint8ClampedArray)
53
+ )
54
+ }
55
+
56
+ // ===========================================
57
+ // Factory Methods
58
+ // ===========================================
59
+
60
+ /**
61
+ * Universal factory method that accepts all browser image input types
62
+ * @param input - URL string, ImageRawData, ImageBitmap, HTMLImageElement, HTMLCanvasElement, or HTMLVideoElement
63
+ * @returns Promise<ImageRaw>
64
+ */
65
+ static async from(input: BrowserImageInput): Promise<ImageRaw> {
66
+ // String (URL or data URL)
67
+ if (typeof input === 'string') {
68
+ return ImageRaw.open(input)
69
+ }
70
+
71
+ // ImageBitmap
72
+ if (ImageRaw.isImageBitmap(input)) {
73
+ return ImageRaw.fromImageBitmap(input)
74
+ }
75
+
76
+ // HTMLImageElement
77
+ if (ImageRaw.isHTMLImageElement(input)) {
78
+ return ImageRaw.fromHTMLImageElement(input)
79
+ }
80
+
81
+ // HTMLCanvasElement
82
+ if (ImageRaw.isHTMLCanvasElement(input)) {
83
+ return ImageRaw.fromHTMLCanvasElement(input)
84
+ }
85
+
86
+ // HTMLVideoElement
87
+ if (ImageRaw.isHTMLVideoElement(input)) {
88
+ return ImageRaw.fromHTMLVideoElement(input)
89
+ }
90
+
91
+ // ImageRawData (fallback)
92
+ if (ImageRaw.isImageRawData(input)) {
93
+ return new ImageRaw(input)
94
+ }
95
+
96
+ throw new Error('Unsupported image input type')
97
+ }
98
+
99
+ /**
100
+ * Create ImageRaw from a URL (existing method)
101
+ */
102
+ static async open(url: string): Promise<ImageRaw> {
103
+ const image = await imageFromUrl(url)
104
+ const canvas = document.createElement('canvas')
105
+ canvasDrawImage(canvas, image, image.naturalWidth, image.naturalHeight)
106
+ const imageData = canvasGetImageData(canvas)
107
+ return new ImageRaw({
108
+ data: imageData.data,
109
+ width: imageData.width,
110
+ height: imageData.height,
111
+ })
112
+ }
113
+
114
+ /**
115
+ * Create ImageRaw from an ImageBitmap
116
+ * Note: This method will close the ImageBitmap after conversion to free memory
117
+ * @param bitmap - ImageBitmap to convert
118
+ * @returns Promise<ImageRaw>
119
+ */
120
+ static async fromImageBitmap(bitmap: ImageBitmap): Promise<ImageRaw> {
121
+ // Validate bitmap
122
+ if (bitmap.width === 0 || bitmap.height === 0) {
123
+ throw new Error('Invalid ImageBitmap: dimensions are zero (bitmap may be closed or neutered)')
124
+ }
125
+
126
+ const canvas = document.createElement('canvas')
127
+ canvas.width = bitmap.width
128
+ canvas.height = bitmap.height
129
+
130
+ const ctx = canvas.getContext('2d', { willReadFrequently: true })
131
+ if (!ctx) {
132
+ // Cleanup before throwing
133
+ try {
134
+ bitmap.close()
135
+ } catch {
136
+ // Ignore if already closed
137
+ }
138
+ throw new Error('Failed to create canvas 2D context')
139
+ }
140
+
141
+ try {
142
+ ctx.drawImage(bitmap, 0, 0)
143
+ const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height)
144
+
145
+ return new ImageRaw({
146
+ data: imageData.data,
147
+ width: imageData.width,
148
+ height: imageData.height,
149
+ })
150
+ } finally {
151
+ // Always close the bitmap to free memory
152
+ try {
153
+ bitmap.close()
154
+ } catch {
155
+ // Ignore if already closed or neutered
156
+ }
157
+ }
158
+ }
159
+
160
+ /**
161
+ * Create ImageRaw from an HTMLImageElement
162
+ * @param img - HTMLImageElement to convert (must be loaded/decoded)
163
+ * @returns Promise<ImageRaw>
164
+ */
165
+ static async fromHTMLImageElement(img: HTMLImageElement): Promise<ImageRaw> {
166
+ // Ensure image is loaded
167
+ if (!img.complete || img.naturalWidth === 0) {
168
+ await img.decode()
169
+ }
170
+
171
+ const canvas = document.createElement('canvas')
172
+ canvasDrawImage(canvas, img, img.naturalWidth, img.naturalHeight)
173
+ const imageData = canvasGetImageData(canvas)
174
+
175
+ return new ImageRaw({
176
+ data: imageData.data,
177
+ width: imageData.width,
178
+ height: imageData.height,
179
+ })
180
+ }
181
+
182
+ /**
183
+ * Create ImageRaw from an HTMLCanvasElement
184
+ * @param canvas - HTMLCanvasElement to convert
185
+ * @returns Promise<ImageRaw>
186
+ */
187
+ static async fromHTMLCanvasElement(canvas: HTMLCanvasElement): Promise<ImageRaw> {
188
+ const imageData = canvasGetImageData(canvas)
189
+
190
+ return new ImageRaw({
191
+ data: imageData.data,
192
+ width: imageData.width,
193
+ height: imageData.height,
194
+ })
195
+ }
196
+
197
+ /**
198
+ * Create ImageRaw from an HTMLVideoElement (captures current frame)
199
+ * @param video - HTMLVideoElement to capture from
200
+ * @returns Promise<ImageRaw>
201
+ */
202
+ static async fromHTMLVideoElement(video: HTMLVideoElement): Promise<ImageRaw> {
203
+ if (video.readyState < 2) {
204
+ throw new Error('Video is not ready. Ensure video has loaded metadata and data.')
205
+ }
206
+
207
+ const canvas = document.createElement('canvas')
208
+ canvas.width = video.videoWidth
209
+ canvas.height = video.videoHeight
210
+
211
+ const ctx = canvas.getContext('2d', { willReadFrequently: true })
212
+ if (!ctx) {
213
+ throw new Error('Failed to create canvas 2D context')
214
+ }
215
+
216
+ ctx.drawImage(video, 0, 0)
217
+ const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height)
218
+
219
+ return new ImageRaw({
220
+ data: imageData.data,
221
+ width: imageData.width,
222
+ height: imageData.height,
223
+ })
224
+ }
225
+
226
+ // ===========================================
227
+ // Constructor & Instance Methods
228
+ // ===========================================
229
+
230
+ constructor({ data, width, height }: ImageRawData) {
231
+ const newData = Uint8ClampedArray.from(data)
232
+ super({
233
+ data: newData,
234
+ width,
235
+ height,
236
+ })
237
+ const canvas = document.createElement('canvas')
238
+ const imageData = new ImageData(newData, width, height)
239
+ canvasPutImageData(canvas, imageData)
240
+ this.#canvas = canvas
241
+ this.#imageData = imageData
242
+ this.data = newData // this.data is undefined without this line
243
+ }
244
+
245
+ async write(path: string) {
246
+ document.body.append(this.#canvas)
247
+ }
248
+
249
+ async resize({ width, height }: SizeOption) {
250
+ invariant(width !== undefined || height !== undefined, 'both width and height are undefined')
251
+ const newWidth = width || Math.round((this.width / this.height) * height!)
252
+ const newHeight = height || Math.round((this.height / this.width) * width!)
253
+ const newCanvas = document.createElement('canvas')
254
+ canvasDrawImage(newCanvas, this.#canvas, newWidth, newHeight)
255
+ const newImageData = canvasGetImageData(newCanvas)
256
+ return this.#apply(newImageData)
257
+ }
258
+
259
+ async drawBox(lineImages: LineImage[]) {
260
+ this.#ctx.strokeStyle = 'red'
261
+ for (const lineImage of lineImages) {
262
+ const [first, ...rests] = lineImage.box
263
+ this.#ctx.beginPath()
264
+ this.#ctx.moveTo(first[0], first[1])
265
+ for (const rest of rests) {
266
+ this.#ctx.lineTo(rest[0], rest[1])
267
+ }
268
+ this.#ctx.closePath()
269
+ this.#ctx.stroke()
270
+ }
271
+ return this
272
+ }
273
+
274
+ get #ctx() {
275
+ return this.#canvas.getContext('2d')!
276
+ }
277
+
278
+ #apply(imageData: ImageData) {
279
+ canvasPutImageData(this.#canvas, imageData)
280
+ this.#imageData = imageData
281
+ this.data = imageData.data
282
+ this.width = imageData.width
283
+ this.height = imageData.height
284
+ return this
285
+ }
286
+
287
+ #putImageData() {
288
+ this.#canvas.width = this.width
289
+ this.#canvas.height = this.height
290
+ this.#ctx.putImageData(this.#imageData, 0, 0)
291
+ return this
292
+ }
293
+
294
+ #drawImage(image: CanvasImageSource, width?: number, height?: number) {
295
+ canvasDrawImage(this.#canvas, image, width, height)
296
+ return this
297
+ }
298
+ }
299
+
300
+ // ===========================================
301
+ // Helper Functions
302
+ // ===========================================
303
+
304
+ function canvasDrawImage(canvas: HTMLCanvasElement, image: CanvasImageSource, width?: number, height?: number) {
305
+ canvas.width = width || (image as any).width
306
+ canvas.height = height || (image as any).height
307
+ const ctx = canvas.getContext('2d')!
308
+ ctx.drawImage(image, 0, 0, canvas.width, canvas.height)
309
+ }
310
+
311
+ function canvasPutImageData(canvas: HTMLCanvasElement, imageData: ImageData, width?: number, height?: number) {
312
+ const ctx = canvas.getContext('2d')!
313
+ canvas.width = width || imageData.width
314
+ canvas.height = height || imageData.height
315
+ ctx.putImageData(imageData, 0, 0)
316
+ }
317
+
318
+ function canvasGetImageData(canvas: HTMLCanvasElement) {
319
+ return canvas.getContext('2d')!.getImageData(0, 0, canvas.width, canvas.height)
320
+ }
321
+
322
+ async function imageFromUrl(url: string) {
323
+ const image = new Image()
324
+ image.src = url
325
+ await image.decode()
326
+ return image
327
+ }
package/src/index.ts ADDED
@@ -0,0 +1,12 @@
1
+ import Ocr, { registerBackend } from '@areb0s/ocr-common'
2
+ import { splitIntoLineImages } from '@areb0s/ocr-common/splitIntoLineImages'
3
+ import { InferenceSession } from 'onnxruntime-web'
4
+ import { FileUtils } from './FileUtils'
5
+ import { ImageRaw } from './ImageRaw'
6
+
7
+ // Browser doesn't have default models - user must provide model paths via Ocr.create({ models: {...} })
8
+ registerBackend({ FileUtils, ImageRaw, InferenceSession, splitIntoLineImages, defaultModels: undefined })
9
+
10
+ export * from '@areb0s/ocr-common'
11
+ export { ImageRaw } // Export ImageRaw for direct access to static methods (from, fromImageBitmap, etc.)
12
+ export default Ocr