scanic 1.1.1 → 1.3.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/dist/scanic.js CHANGED
@@ -1,2670 +1 @@
1
- const DEFAULTS = {
2
- // Contour detection params
3
- MIN_CONTOUR_AREA: 1e3,
4
- MIN_CONTOUR_POINTS: 10
5
- };
6
- const RETR_EXTERNAL = 0;
7
- const RETR_LIST = 1;
8
- const CHAIN_APPROX_SIMPLE = 2;
9
- function detectDocumentContour(edges, options = {}) {
10
- const width = options.width || Math.sqrt(edges.length);
11
- const height = options.height || edges.length / width;
12
- const mode = options.mode !== void 0 ? options.mode : RETR_LIST;
13
- const method = options.method !== void 0 ? options.method : CHAIN_APPROX_SIMPLE;
14
- const minArea = options.minArea || DEFAULTS.MIN_CONTOUR_AREA;
15
- const paddedWidth = width + 2;
16
- const paddedHeight = height + 2;
17
- const labels = new Int32Array(paddedWidth * paddedHeight);
18
- for (let y = 0; y < height; y++) {
19
- for (let x = 0; x < width; x++) {
20
- if (edges[y * width + x] > 0) {
21
- labels[(y + 1) * paddedWidth + (x + 1)] = 1;
22
- }
23
- }
24
- }
25
- const contours = [];
26
- let nextContourId = 2;
27
- for (let y = 1; y <= height; y++) {
28
- for (let x = 1; x <= width; x++) {
29
- const currentPixelLabel = labels[y * paddedWidth + x];
30
- const leftPixelLabel = labels[y * paddedWidth + (x - 1)];
31
- let startPoint = null;
32
- let isOuter = false;
33
- let initialDirection = -1;
34
- if (currentPixelLabel === 1 && leftPixelLabel === 0) {
35
- isOuter = true;
36
- startPoint = { x, y };
37
- initialDirection = 2;
38
- } else if (currentPixelLabel === 0 && leftPixelLabel >= 1 && leftPixelLabel !== -1) {
39
- if (leftPixelLabel === 1) {
40
- isOuter = false;
41
- startPoint = { x: x - 1, y };
42
- initialDirection = 6;
43
- }
44
- }
45
- if (startPoint) {
46
- if (mode === RETR_EXTERNAL && !isOuter) {
47
- labels[startPoint.y * paddedWidth + startPoint.x] = -1;
48
- continue;
49
- }
50
- const contourId = nextContourId++;
51
- const points = traceContour(labels, paddedWidth, paddedHeight, startPoint, initialDirection, contourId);
52
- if (points && points.length > 0) {
53
- let finalPoints = points;
54
- if (method === CHAIN_APPROX_SIMPLE) {
55
- finalPoints = simplifyChainApproxSimple(points);
56
- }
57
- const adjustedPoints = finalPoints.map((p) => ({ x: p.x - 1, y: p.y - 1 }));
58
- if (adjustedPoints.length >= (method === CHAIN_APPROX_SIMPLE ? 4 : DEFAULTS.MIN_CONTOUR_POINTS)) {
59
- const contour = {
60
- id: contourId,
61
- points: adjustedPoints,
62
- isOuter
63
- // Calculate area and bounding box later if needed for filtering/sorting
64
- };
65
- contours.push(contour);
66
- }
67
- } else {
68
- if (labels[startPoint.y * paddedWidth + startPoint.x] === 1) {
69
- labels[startPoint.y * paddedWidth + startPoint.x] = contourId;
70
- }
71
- }
72
- }
73
- }
74
- }
75
- contours.forEach((contour) => {
76
- contour.area = calculateContourArea(contour.points);
77
- contour.boundingBox = calculateBoundingBox(contour.points);
78
- });
79
- const filteredContours = contours.filter((contour) => contour.area >= minArea);
80
- filteredContours.sort((a, b) => b.area - a.area);
81
- if (options.debug) {
82
- options.debug.labels = labels;
83
- options.debug.rawContours = contours;
84
- options.debug.finalContours = filteredContours;
85
- }
86
- return filteredContours;
87
- }
88
- function traceContour(labels, width, height, startPoint, initialDirection, contourId) {
89
- const points = [];
90
- const visited = /* @__PURE__ */ new Set();
91
- let currentX = startPoint.x;
92
- let currentY = startPoint.y;
93
- const startX = currentX;
94
- const startY = currentY;
95
- let prevDirection = -1;
96
- labels[startY * width + startX] = contourId;
97
- let count = 0;
98
- const maxSteps = width * height;
99
- const dx = [0, 1, 1, 1, 0, -1, -1, -1];
100
- const dy = [-1, -1, 0, 1, 1, 1, 0, -1];
101
- while (count++ < maxSteps) {
102
- let searchDirection;
103
- if (prevDirection === -1) {
104
- let found = false;
105
- for (let i = 0; i < 8; i++) {
106
- searchDirection = initialDirection + i & 7;
107
- const nextX2 = currentX + dx[searchDirection];
108
- const nextY2 = currentY + dy[searchDirection];
109
- if (nextX2 >= 0 && nextX2 < width && nextY2 >= 0 && nextY2 < height && labels[nextY2 * width + nextX2] > 0) {
110
- found = true;
111
- break;
112
- }
113
- }
114
- if (!found) return null;
115
- } else {
116
- searchDirection = prevDirection + 2 & 7;
117
- }
118
- let nextX = -1;
119
- let nextY = -1;
120
- for (let i = 0; i < 8; i++) {
121
- const checkDirection = searchDirection + i & 7;
122
- const checkX = currentX + dx[checkDirection];
123
- const checkY = currentY + dy[checkDirection];
124
- if (checkX >= 0 && checkX < width && checkY >= 0 && checkY < height) {
125
- if (labels[checkY * width + checkX] > 0) {
126
- nextX = checkX;
127
- nextY = checkY;
128
- prevDirection = checkDirection + 4 & 7;
129
- break;
130
- }
131
- }
132
- }
133
- if (nextX === -1) {
134
- if (points.length === 0) {
135
- points.push({ x: currentX, y: currentY });
136
- }
137
- console.warn(`Contour tracing stopped unexpectedly at (${currentX - 1}, ${currentY - 1}) for contour ${contourId}`);
138
- break;
139
- }
140
- const visitedKey = currentY * width + currentX;
141
- if (visited.has(visitedKey)) {
142
- return points;
143
- }
144
- points.push({ x: currentX, y: currentY });
145
- visited.add(visitedKey);
146
- const nextIdx = nextY * width + nextX;
147
- if (labels[nextIdx] === 1) {
148
- labels[nextIdx] = contourId;
149
- }
150
- currentX = nextX;
151
- currentY = nextY;
152
- if (currentX === startX && currentY === startY) {
153
- break;
154
- }
155
- }
156
- if (count >= maxSteps) {
157
- console.warn(`Contour tracing exceeded max steps for contour ${contourId}`);
158
- return null;
159
- }
160
- return points;
161
- }
162
- function simplifyChainApproxSimple(points) {
163
- const n = points.length;
164
- if (n <= 2) {
165
- return points;
166
- }
167
- const simplifiedPoints = [];
168
- const lastPoint = points[n - 1];
169
- const firstPoint = points[0];
170
- let prevPoint = lastPoint;
171
- let currentPoint = firstPoint;
172
- let nextPoint = points[1];
173
- let dx1 = currentPoint.x - prevPoint.x;
174
- let dy1 = currentPoint.y - prevPoint.y;
175
- let dx2 = nextPoint.x - currentPoint.x;
176
- let dy2 = nextPoint.y - currentPoint.y;
177
- if (dx1 * dy2 !== dy1 * dx2) {
178
- simplifiedPoints.push(currentPoint);
179
- }
180
- for (let i = 1; i < n - 1; i++) {
181
- prevPoint = points[i - 1];
182
- currentPoint = points[i];
183
- nextPoint = points[i + 1];
184
- dx1 = currentPoint.x - prevPoint.x;
185
- dy1 = currentPoint.y - prevPoint.y;
186
- dx2 = nextPoint.x - currentPoint.x;
187
- dy2 = nextPoint.y - currentPoint.y;
188
- if (dx1 * dy2 !== dy1 * dx2) {
189
- simplifiedPoints.push(currentPoint);
190
- }
191
- }
192
- prevPoint = points[n - 2];
193
- currentPoint = lastPoint;
194
- nextPoint = firstPoint;
195
- dx1 = currentPoint.x - prevPoint.x;
196
- dy1 = currentPoint.y - prevPoint.y;
197
- dx2 = nextPoint.x - currentPoint.x;
198
- dy2 = nextPoint.y - currentPoint.y;
199
- if (dx1 * dy2 !== dy1 * dx2) {
200
- simplifiedPoints.push(currentPoint);
201
- }
202
- if (simplifiedPoints.length === 0) {
203
- if (n === 1) return [points[0]];
204
- if (n === 2) return points;
205
- let maxDistSq = 0;
206
- let farthestIdx = 1;
207
- const p0x = firstPoint.x;
208
- const p0y = firstPoint.y;
209
- for (let i = 1; i < n; i++) {
210
- const pi = points[i];
211
- const dx = pi.x - p0x;
212
- const dy = pi.y - p0y;
213
- const distSq = dx * dx + dy * dy;
214
- if (distSq > maxDistSq) {
215
- maxDistSq = distSq;
216
- farthestIdx = i;
217
- }
218
- }
219
- return [firstPoint, points[farthestIdx]];
220
- }
221
- return simplifiedPoints;
222
- }
223
- function calculateContourArea(points) {
224
- let area = 0;
225
- const n = points.length;
226
- if (n < 3) return 0;
227
- for (let i = 0; i < n; i++) {
228
- const j = (i + 1) % n;
229
- area += points[i].x * points[j].y;
230
- area -= points[j].x * points[i].y;
231
- }
232
- return Math.abs(area) / 2;
233
- }
234
- function calculateBoundingBox(points) {
235
- if (points.length === 0) {
236
- return { minX: 0, minY: 0, maxX: 0, maxY: 0 };
237
- }
238
- let minX = points[0].x;
239
- let minY = points[0].y;
240
- let maxX = points[0].x;
241
- let maxY = points[0].y;
242
- for (let i = 1; i < points.length; i++) {
243
- const point = points[i];
244
- minX = Math.min(minX, point.x);
245
- minY = Math.min(minY, point.y);
246
- maxX = Math.max(maxX, point.x);
247
- maxY = Math.max(maxY, point.y);
248
- }
249
- return { minX, minY, maxX, maxY };
250
- }
251
- function simplifyContour(points, epsilon = 1) {
252
- if (points.length <= 2) {
253
- return points;
254
- }
255
- let maxDistance = 0;
256
- let index = 0;
257
- const firstPoint = points[0];
258
- const lastPoint = points[points.length - 1];
259
- for (let i = 1; i < points.length - 1; i++) {
260
- const distance2 = perpendicularDistance(points[i], firstPoint, lastPoint);
261
- if (distance2 > maxDistance) {
262
- maxDistance = distance2;
263
- index = i;
264
- }
265
- }
266
- if (maxDistance > epsilon) {
267
- const firstSegment = simplifyContour(points.slice(0, index + 1), epsilon);
268
- const secondSegment = simplifyContour(points.slice(index), epsilon);
269
- return firstSegment.slice(0, -1).concat(secondSegment);
270
- } else {
271
- return [firstPoint, lastPoint];
272
- }
273
- }
274
- function perpendicularDistance(point, lineStart, lineEnd) {
275
- const dx = lineEnd.x - lineStart.x;
276
- const dy = lineEnd.y - lineStart.y;
277
- const lineLengthSq = dx * dx + dy * dy;
278
- if (lineLengthSq === 0) {
279
- return Math.sqrt(
280
- Math.pow(point.x - lineStart.x, 2) + Math.pow(point.y - lineStart.y, 2)
281
- );
282
- }
283
- const t = ((point.x - lineStart.x) * dx + (point.y - lineStart.y) * dy) / lineLengthSq;
284
- let closestPointX, closestPointY;
285
- if (t < 0) {
286
- closestPointX = lineStart.x;
287
- closestPointY = lineStart.y;
288
- } else if (t > 1) {
289
- closestPointX = lineEnd.x;
290
- closestPointY = lineEnd.y;
291
- } else {
292
- closestPointX = lineStart.x + t * dx;
293
- closestPointY = lineStart.y + t * dy;
294
- }
295
- const distDx = point.x - closestPointX;
296
- const distDy = point.y - closestPointY;
297
- return Math.sqrt(distDx * distDx + distDy * distDy);
298
- }
299
- function approximatePolygon(contourPoints, epsilon = 0.02) {
300
- const perimeter = calculateContourPerimeter(contourPoints);
301
- const actualEpsilon = epsilon * perimeter;
302
- const simplifiedPoints = simplifyContour(contourPoints, actualEpsilon);
303
- return simplifiedPoints;
304
- }
305
- function calculateContourPerimeter(points) {
306
- let perimeter = 0;
307
- const n = points.length;
308
- if (n < 2) return 0;
309
- for (let i = 0; i < n; i++) {
310
- const j = (i + 1) % n;
311
- const dx = points[i].x - points[j].x;
312
- const dy = points[i].y - points[j].y;
313
- perimeter += Math.sqrt(dx * dx + dy * dy);
314
- }
315
- return perimeter;
316
- }
317
- function distance(p1, p2) {
318
- return Math.hypot(p2.x - p1.x, p2.y - p1.y);
319
- }
320
- function findCenter(points) {
321
- let sumX = 0;
322
- let sumY = 0;
323
- for (const point of points) {
324
- sumX += point.x;
325
- sumY += point.y;
326
- }
327
- return {
328
- x: sumX / points.length,
329
- y: sumY / points.length
330
- };
331
- }
332
- function findCornerPoints(contour, options = {}) {
333
- if (!contour || !contour.points || contour.points.length < 4) {
334
- console.warn("Contour does not have enough points for corner detection");
335
- return null;
336
- }
337
- const epsilon = options.epsilon || 0.02;
338
- const approximation = findBestQuadrilateralApproximation(contour.points, epsilon);
339
- let corners;
340
- if (approximation && approximation.length === 4) {
341
- corners = orderCornerPoints(approximation);
342
- } else {
343
- corners = findCornersByQuadrants(contour.points);
344
- if (!corners || !corners.topLeft || !corners.topRight || !corners.bottomRight || !corners.bottomLeft) {
345
- corners = findCornersByCoordinateExtremes(contour.points);
346
- }
347
- }
348
- if (!corners || !corners.topLeft || !corners.topRight || !corners.bottomRight || !corners.bottomLeft) {
349
- console.warn("Failed to find all four corners.", corners);
350
- return null;
351
- }
352
- return corners;
353
- }
354
- function findBestQuadrilateralApproximation(points, baseEpsilon) {
355
- if (!points || points.length < 4) return null;
356
- const epsilonCandidates = [
357
- baseEpsilon,
358
- baseEpsilon * 1.3,
359
- baseEpsilon * 1.6,
360
- baseEpsilon * 2
361
- ];
362
- const seen = /* @__PURE__ */ new Set();
363
- for (const candidate of epsilonCandidates) {
364
- const epsilon = Math.min(0.12, candidate);
365
- const key = epsilon.toFixed(4);
366
- if (seen.has(key)) continue;
367
- seen.add(key);
368
- const approximation = approximatePolygon(points, epsilon);
369
- const quad = reduceApproximationToQuadrilateral(approximation);
370
- if (quad && quad.length === 4) {
371
- return quad;
372
- }
373
- }
374
- return null;
375
- }
376
- function reduceApproximationToQuadrilateral(approximation) {
377
- if (!approximation || approximation.length < 4) return null;
378
- if (approximation.length === 4) return approximation;
379
- if (approximation.length > 8) return null;
380
- let points = removeNearDuplicateVertices(approximation, 6);
381
- if (points.length === 4) return points;
382
- if (points.length < 4) return null;
383
- return null;
384
- }
385
- function removeNearDuplicateVertices(points, tolerance) {
386
- if (!points || points.length === 0) return [];
387
- const filtered = [];
388
- for (const point of points) {
389
- if (filtered.length === 0 || distance(point, filtered[filtered.length - 1]) > tolerance) {
390
- filtered.push(point);
391
- }
392
- }
393
- if (filtered.length > 2 && distance(filtered[0], filtered[filtered.length - 1]) <= tolerance) {
394
- filtered.pop();
395
- }
396
- return filtered;
397
- }
398
- function findCornersByQuadrants(points) {
399
- if (!points || points.length < 4) return null;
400
- const center = findCenter(points);
401
- let topLeft = null;
402
- let topRight = null;
403
- let bottomRight = null;
404
- let bottomLeft = null;
405
- let topLeftDist = 0;
406
- let topRightDist = 0;
407
- let bottomRightDist = 0;
408
- let bottomLeftDist = 0;
409
- for (const point of points) {
410
- const dist = distance(point, center);
411
- if (point.x < center.x && point.y < center.y) {
412
- if (dist > topLeftDist) {
413
- topLeft = point;
414
- topLeftDist = dist;
415
- }
416
- } else if (point.x > center.x && point.y < center.y) {
417
- if (dist > topRightDist) {
418
- topRight = point;
419
- topRightDist = dist;
420
- }
421
- } else if (point.x > center.x && point.y > center.y) {
422
- if (dist > bottomRightDist) {
423
- bottomRight = point;
424
- bottomRightDist = dist;
425
- }
426
- } else if (point.x < center.x && point.y > center.y) {
427
- if (dist > bottomLeftDist) {
428
- bottomLeft = point;
429
- bottomLeftDist = dist;
430
- }
431
- }
432
- }
433
- if (!topLeft || !topRight || !bottomRight || !bottomLeft) {
434
- return null;
435
- }
436
- return {
437
- topLeft,
438
- topRight,
439
- bottomRight,
440
- bottomLeft
441
- };
442
- }
443
- function findCornersByCoordinateExtremes(points) {
444
- if (!points || points.length === 0) return null;
445
- let topLeft = points[0];
446
- let topRight = points[0];
447
- let bottomRight = points[0];
448
- let bottomLeft = points[0];
449
- let minSum = topLeft.x + topLeft.y;
450
- let maxDiff = topRight.x - topRight.y;
451
- let maxSum = bottomRight.x + bottomRight.y;
452
- let minDiff = bottomLeft.x - bottomLeft.y;
453
- for (let i = 1; i < points.length; i++) {
454
- const point = points[i];
455
- const sum = point.x + point.y;
456
- const diff = point.x - point.y;
457
- if (sum < minSum) {
458
- minSum = sum;
459
- topLeft = point;
460
- }
461
- if (sum > maxSum) {
462
- maxSum = sum;
463
- bottomRight = point;
464
- }
465
- if (diff > maxDiff) {
466
- maxDiff = diff;
467
- topRight = point;
468
- }
469
- if (diff < minDiff) {
470
- minDiff = diff;
471
- bottomLeft = point;
472
- }
473
- }
474
- return {
475
- topLeft,
476
- topRight,
477
- bottomRight,
478
- bottomLeft
479
- };
480
- }
481
- function orderCornerPoints(points) {
482
- if (points.length !== 4) {
483
- console.warn(`Expected 4 points, got ${points.length}`);
484
- return null;
485
- }
486
- const center = findCenter(points);
487
- const sortedPoints = [...points].sort((a, b) => {
488
- const angleA = Math.atan2(a.y - center.y, a.x - center.x);
489
- const angleB = Math.atan2(b.y - center.y, b.x - center.x);
490
- return angleA - angleB;
491
- });
492
- let minSum = Infinity;
493
- let minIndex = 0;
494
- for (let i = 0; i < 4; i++) {
495
- const sum = sortedPoints[i].x + sortedPoints[i].y;
496
- if (sum < minSum) {
497
- minSum = sum;
498
- minIndex = i;
499
- }
500
- }
501
- const orderedPoints = [
502
- sortedPoints[minIndex],
503
- sortedPoints[(minIndex + 1) % 4],
504
- sortedPoints[(minIndex + 2) % 4],
505
- sortedPoints[(minIndex + 3) % 4]
506
- ];
507
- return {
508
- topLeft: orderedPoints[0],
509
- topRight: orderedPoints[1],
510
- bottomRight: orderedPoints[2],
511
- bottomLeft: orderedPoints[3]
512
- };
513
- }
514
- let wasm;
515
- let cachedUint8ArrayMemory0 = null;
516
- function getUint8ArrayMemory0() {
517
- if (cachedUint8ArrayMemory0 === null || cachedUint8ArrayMemory0.byteLength === 0) {
518
- cachedUint8ArrayMemory0 = new Uint8Array(wasm.memory.buffer);
519
- }
520
- return cachedUint8ArrayMemory0;
521
- }
522
- let WASM_VECTOR_LEN = 0;
523
- function passArray8ToWasm0(arg, malloc) {
524
- const ptr = malloc(arg.length * 1, 1) >>> 0;
525
- getUint8ArrayMemory0().set(arg, ptr / 1);
526
- WASM_VECTOR_LEN = arg.length;
527
- return ptr;
528
- }
529
- function getArrayU8FromWasm0(ptr, len) {
530
- ptr = ptr >>> 0;
531
- return getUint8ArrayMemory0().subarray(ptr / 1, ptr / 1 + len);
532
- }
533
- function blur(grayscale, width, height, kernel_size, sigma) {
534
- const ptr0 = passArray8ToWasm0(grayscale, wasm.__wbindgen_malloc);
535
- const len0 = WASM_VECTOR_LEN;
536
- const ret = wasm.blur(ptr0, len0, width, height, kernel_size, sigma);
537
- var v2 = getArrayU8FromWasm0(ret[0], ret[1]).slice();
538
- wasm.__wbindgen_free(ret[0], ret[1] * 1, 1);
539
- return v2;
540
- }
541
- let cachedFloat32ArrayMemory0 = null;
542
- function getFloat32ArrayMemory0() {
543
- if (cachedFloat32ArrayMemory0 === null || cachedFloat32ArrayMemory0.byteLength === 0) {
544
- cachedFloat32ArrayMemory0 = new Float32Array(wasm.memory.buffer);
545
- }
546
- return cachedFloat32ArrayMemory0;
547
- }
548
- function passArrayF32ToWasm0(arg, malloc) {
549
- const ptr = malloc(arg.length * 4, 4) >>> 0;
550
- getFloat32ArrayMemory0().set(arg, ptr / 4);
551
- WASM_VECTOR_LEN = arg.length;
552
- return ptr;
553
- }
554
- function hysteresis_thresholding(suppressed, width, height, low_threshold, high_threshold) {
555
- const ptr0 = passArrayF32ToWasm0(suppressed, wasm.__wbindgen_malloc);
556
- const len0 = WASM_VECTOR_LEN;
557
- const ret = wasm.hysteresis_thresholding(ptr0, len0, width, height, low_threshold, high_threshold);
558
- var v2 = getArrayU8FromWasm0(ret[0], ret[1]).slice();
559
- wasm.__wbindgen_free(ret[0], ret[1] * 1, 1);
560
- return v2;
561
- }
562
- function dilate(edges, width, height, kernel_size) {
563
- const ptr0 = passArray8ToWasm0(edges, wasm.__wbindgen_malloc);
564
- const len0 = WASM_VECTOR_LEN;
565
- const ret = wasm.dilate(ptr0, len0, width, height, kernel_size);
566
- var v2 = getArrayU8FromWasm0(ret[0], ret[1]).slice();
567
- wasm.__wbindgen_free(ret[0], ret[1] * 1, 1);
568
- return v2;
569
- }
570
- let cachedUint16ArrayMemory0 = null;
571
- function getUint16ArrayMemory0() {
572
- if (cachedUint16ArrayMemory0 === null || cachedUint16ArrayMemory0.byteLength === 0) {
573
- cachedUint16ArrayMemory0 = new Uint16Array(wasm.memory.buffer);
574
- }
575
- return cachedUint16ArrayMemory0;
576
- }
577
- function passArray16ToWasm0(arg, malloc) {
578
- const ptr = malloc(arg.length * 2, 2) >>> 0;
579
- getUint16ArrayMemory0().set(arg, ptr / 2);
580
- WASM_VECTOR_LEN = arg.length;
581
- return ptr;
582
- }
583
- function getArrayF32FromWasm0(ptr, len) {
584
- ptr = ptr >>> 0;
585
- return getFloat32ArrayMemory0().subarray(ptr / 4, ptr / 4 + len);
586
- }
587
- function non_maximum_suppression(dx, dy, width, height, l2_gradient) {
588
- const ptr0 = passArray16ToWasm0(dx, wasm.__wbindgen_malloc);
589
- const len0 = WASM_VECTOR_LEN;
590
- const ptr1 = passArray16ToWasm0(dy, wasm.__wbindgen_malloc);
591
- const len1 = WASM_VECTOR_LEN;
592
- const ret = wasm.non_maximum_suppression(ptr0, len0, ptr1, len1, width, height, l2_gradient);
593
- var v3 = getArrayF32FromWasm0(ret[0], ret[1]).slice();
594
- wasm.__wbindgen_free(ret[0], ret[1] * 4, 4);
595
- return v3;
596
- }
597
- function canny_edge_detector_full(grayscale, width, height, low_threshold, high_threshold, kernel_size, sigma, l2_gradient, apply_dilation, dilation_kernel_size) {
598
- const ptr0 = passArray8ToWasm0(grayscale, wasm.__wbindgen_malloc);
599
- const len0 = WASM_VECTOR_LEN;
600
- const ret = wasm.canny_edge_detector_full(ptr0, len0, width, height, low_threshold, high_threshold, kernel_size, sigma, l2_gradient, apply_dilation, dilation_kernel_size);
601
- var v2 = getArrayU8FromWasm0(ret[0], ret[1]).slice();
602
- wasm.__wbindgen_free(ret[0], ret[1] * 1, 1);
603
- return v2;
604
- }
605
- async function __wbg_load(module, imports) {
606
- if (typeof Response === "function" && module instanceof Response) {
607
- if (typeof WebAssembly.instantiateStreaming === "function") {
608
- try {
609
- return await WebAssembly.instantiateStreaming(module, imports);
610
- } catch (e) {
611
- if (module.headers.get("Content-Type") != "application/wasm") {
612
- console.warn("`WebAssembly.instantiateStreaming` failed because your server does not serve Wasm with `application/wasm` MIME type. Falling back to `WebAssembly.instantiate` which is slower. Original error:\n", e);
613
- } else {
614
- throw e;
615
- }
616
- }
617
- }
618
- const bytes = await module.arrayBuffer();
619
- return await WebAssembly.instantiate(bytes, imports);
620
- } else {
621
- const instance = await WebAssembly.instantiate(module, imports);
622
- if (instance instanceof WebAssembly.Instance) {
623
- return { instance, module };
624
- } else {
625
- return instance;
626
- }
627
- }
628
- }
629
- function __wbg_get_imports() {
630
- const imports = {};
631
- imports.wbg = {};
632
- imports.wbg.__wbindgen_init_externref_table = function() {
633
- const table = wasm.__wbindgen_export_0;
634
- const offset = table.grow(4);
635
- table.set(0, void 0);
636
- table.set(offset + 0, void 0);
637
- table.set(offset + 1, null);
638
- table.set(offset + 2, true);
639
- table.set(offset + 3, false);
640
- };
641
- return imports;
642
- }
643
- function __wbg_finalize_init(instance, module) {
644
- wasm = instance.exports;
645
- __wbg_init.__wbindgen_wasm_module = module;
646
- cachedFloat32ArrayMemory0 = null;
647
- cachedUint16ArrayMemory0 = null;
648
- cachedUint8ArrayMemory0 = null;
649
- wasm.__wbindgen_start();
650
- return wasm;
651
- }
652
- async function __wbg_init(module_or_path) {
653
- if (wasm !== void 0) return wasm;
654
- if (typeof module_or_path !== "undefined") {
655
- if (Object.getPrototypeOf(module_or_path) === Object.prototype) {
656
- ({ module_or_path } = module_or_path);
657
- } else {
658
- console.warn("using deprecated parameters for the initialization function; pass a single object instead");
659
- }
660
- }
661
- if (typeof module_or_path === "undefined") {
662
- throw new Error("scanic: WASM module bytes must be passed to init().");
663
- }
664
- const imports = __wbg_get_imports();
665
- if (typeof module_or_path === "string" || typeof Request === "function" && module_or_path instanceof Request || typeof URL === "function" && module_or_path instanceof URL) {
666
- module_or_path = fetch(module_or_path);
667
- }
668
- const { instance, module } = await __wbg_load(await module_or_path, imports);
669
- return __wbg_finalize_init(instance, module);
670
- }
671
- const wasmBase64 = "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JAAgAiABIAJqIgFLBEBBAEEAEC0ACyADQQRqIAAoAgAiAiAAKAIEQQggASACQQF0IgIgASACSxsiASABQQhNGyIBQQFBARARIAMoAgRBAUYEQCADKAIIIAMoAgwQLQALIAMoAgghAiAAIAE2AgAgACACNgIEIANBEGokAAt5AQF/IwBBIGsiAiQAAn8gACgCAEGAgICAeEcEQCABIAAoAgQgACgCCBAwDAELIAJBEGogACgCDCgCACIAQQhqKQIANwMAIAJBGGogAEEQaikCADcDACACIAApAgA3AwggASgCACABKAIEIAJBCGoQBwsgAkEgaiQAC2kBA38jAEEQayIBJAAgAUEEaiAAKAIAIgIgACgCBEEEIAJBAXQiAiACQQRNGyICQQRBBBARIAEoAgRBAUYEQCABKAIIIAEoAgwQLQALIAEoAgghAyAAIAI2AgAgACADNgIEIAFBEGokAAtpAQN/IwBBEGsiASQAIAFBBGogACgCACICIAAoAgRBBCACQQF0IgIgAkEETRsiAkEEQQgQESABKAIEQQFGBEAgASgCCCABKAIMEC0ACyABKAIIIQMgACACNgIAIAAgAzYCBCABQRBqJAALaQEDfyMAQRBrIgEkACABQQRqIAAoAgAiAiAAKAIEQQQgAkEBdCICIAJBBE0bIgJBAkECEBEgASgCBEEBRgRAIAEoAgggASgCDBAtAAsgASgCCCEDIAAgAjYCACAAIAM2AgQgAUEQaiQACxIAIwBBMGsiACQAIABBMGokAAtoAgF/AX4jAEEwayIDJAAgAyABNgIEIAMgADYCACADQQI2AgwgA0Hkl8AANgIIIANCAjcCFCADQoCAgIAwIgQgA62ENwMoIAMgBCADQQRqrYQ3AyAgAyADQSBqNgIQIANBCGogAhAmAAtHAQF/IAAoAgAgACgCCCIDayACSQRAIAAgAyACEBIgACgCCCEDCyACBEAgACgCBCADaiABIAL8CgAACyAAIAIgA2o2AghBAAtEAQJ/IAEoAgQhAiABKAIAIQNBCEEEEDUiAUUEQEEEQQgQOQALIAEgAjYCBCABIAM2AgAgAEHcksAANgIEIAAgATYCAAvGAgACQCAAIAJNBEAgACABTSABIAJLcg0BIwBBMGsiAiQAIAIgATYCBCACIAA2AgAgAkECNgIMIAJB2JbAADYCCCACQgI3AhQgAiACQQRqrUKAgICAMIQ3AyggAiACrUKAgICAMIQ3AyAgAiACQSBqNgIQIAJBCGogAxAmAAsjAEEwayIBJAAgASACNgIEIAEgADYCACABQQI2AgwgAUH8lsAANgIIIAFCAjcCFCABIAFBBGqtQoCAgIAwhDcDKCABIAGtQoCAgIAwhDcDICABIAFBIGo2AhAgAUEIaiADECYACyMAQTBrIgAkACAAIAI2AgQgACABNgIAIABBAjYCDCAAQaSWwAA2AgggAEICNwIUIAAgAEEEaq1CgICAgDCENwMoIAAgAK1CgICAgDCENwMgIAAgAEEgajYCECAAQQhqIAMQJgALQQEBfyMAQSBrIgMkACADQQA2AhAgA0EBNgIEIANCBDcCCCADIAE2AhwgAyAANgIYIAMgA0EYajYCACADIAIQJgALywwBD38jAEEQayIOJAAgCCEMIAkhFiAKIRdBACEIQQAhCSMAQdAAayILJAAgC0EIaiAAIhggASITIAIiCiADIAYgBxAEIAtBFGogCygCDCIZIAsoAhAgAiADEAUgAiADbCIBQQF0IQACfwJAAkAgAUEASCAAQf7///8HS3JFBEAgAEUNAUECIQggAEECEDUiAg0CCyAIIAAQLQALIAtBADYCKCALQoCAgIAgNwIgIAtBADYCNCALQoCAgIAgNwIsQQIhCEEBDAELIAtBADYCKCALIAI2AiQgCyABNgIgAkACQCAAQQIQNSIIBEAgC0EANgI0IAsgCDYCMCALIAE2AixBASABRQ0DGkEAIQAgCygCGCEIIAsoAhwhBiABIQIDQCAAIAZJBEAgCC8BACENIAsoAigiCSALKAIgRgRAIAtBIGoQFgsgCygCJCAJQQF0aiANOwEAIAsgCUEBajYCKCAAQQFqIAZPDQMgCEECai8BACENIAsoAjQiCSALKAIsRgRAIAtBLGoQFgsgCygCMCAJQQF0aiANOwEAIAsgCUEBaiIJNgI0IAhBBGohCCAAQQJqIQAgAkEBayICDQEMBAsLIAAgBkGckMAAEBgAC0ECIAAQLQALIABBAWogBkGskMAAEBgACyALKAIwIQhBAAshECALQThqIAsoAiQgCygCKCAIIAkgCiADIAxBAEcQAyALKAJAIQ8gCygCPCEUAkACQAJ/IBAEQEEBIQ1BAAwBCyABQQEQNiINRQ0BIAELIRUgC0EANgJMIAtCgICAgMAANwJEAkACQAJAAkACQCAKQQNrQX1LDQAgA0EBayIRQQJJDQAgBSAFlCAFIAwbIQUgBCAElCAEIAwbIQRBAiEAQQEhCQNAIAAhAiAJIApsIQxBAiEAQQEhCANAIAghBiAAIQggBiAMaiIAIA9PDQUCQCAFIBQgAEECdGoqAgAiB18EQCAAIAFPDQUgACANakECOgAAIAsoAkwiACALKAJERgRAIAtBxABqEBULIAsoAkggAEEDdGoiEiAJNgIEIBIgBjYCACALIABBAWo2AkwMAQsgBCAHX0UNACAAIAFPDQUgACANakEBOgAACyAIQQFqIgAgCkcNAAsgAiACIBFJIgZqIQAgAiEJIAYNAAsgCygCTCICRQ0AA0BBfyEJIAsgAkEBayICNgJMIAsoAkggAkEDdGoiACgCBCEPIAAoAgAhEQNAAkAgCSAPaiIGRSADIAZNcg0AIAYgCmwhEkF/IQADQAJAIAAgCXJFDQAgACARaiIIRSAIIApPcg0AIAEgCCASaiIMSwRAIAwgDWoiDC0AAEEBRw0BIAxBAjoAACALKAJMIgIgCygCREYEQCALQcQAahAVCyALKAJIIAJBA3RqIgwgBjYCBCAMIAg2AgAgCyACQQFqIgI2AkwMAQsgDCABQdyPwAAQGAALIABBAUYiCA0BQQEgAEEBaiAIGyIAQQFMDQALCyAJQQFGIgBFBEBBASAJQQFqIAAbIglBAUwNAQsLIAINAAsLIBAEQEEAIQhBASEJDAQLIAFBARA2IgkEQEEAIQAgAUEBRwRAIAFB/v///wdxIQIDQCAAIA1qIgYtAABBAkYEQCAAIAlqQf8BOgAACyAGQQFqLQAAQQJGBEAgACAJakEBakH/AToAAAsgAiAAQQJqIgBHDQALCwJAIAFBAXFFDQAgACANai0AAEECRw0AIAAgCWpB/wE6AAALIAEhCAwECwwECyAAIAFBjJDAABAYAAsgACABQfyPwAAQGAALIAAgD0Hsj8AAEBgACyALKAJEIgAEQCALKAJIIABBA3QQNAsgFQRAIA0gFRA0CwJAAkAgFkUEQCAJIQAMAQtBASEAIBBFBEAgAUEBEDYiAEUNAgsgCSABIAogAyAXIAAgARACIAhFDQAgCSAIEDQLIAsoAjgiAgRAIBQgAkECdBA0CyALKAIsIgIEQCALKAIwIAJBAXQQNAsgCygCICICBEAgCygCJCACQQF0EDQLIAsoAhQiAgRAIAsoAhggAkEBdBA0CyALKAIIIgIEQCAZIAIQNAsgEwRAIBggExA0CyAOIAE2AgQgDiAANgIAIAtB0ABqJAAMAgsLQQEgARAtAAsgDigCACAOKAIEIA5BEGokAAs4AAJAIAJBgIDEAEYNACAAIAIgASgCEBECAEUNAEEBDwsgA0UEQEEADwsgACADQQAgASgCDBEDAAs2AQF/IwBBIGsiASQAIAFBADYCGCABQQE2AgwgAUGol8AANgIIIAFCBDcCECABQQhqIAAQJgALyAEBAn8jAEEQayIIJAAjAEEQayIHJAAgB0EEaiAAIAEgAiADIAQgBSAGQQBHEAMgAwRAIAIgA0EBdBA0CyABBEAgACABQQF0EDQLAkAgBygCBCIBIAcoAgwiAE0EQCAHKAIIIQEMAQsgAUECdCECIAcoAgghAyAARQRAQQQhASADIAIQNAwBCyADIAJBBCAAQQJ0IgIQMSIBDQBBBCACEC0ACyAIIAA2AgQgCCABNgIAIAdBEGokACAIKAIAIAgoAgQgCEEQaiQAC6YBAQJ/IwBBEGsiByQAIwBBEGsiBiQAIAZBBGogACABIAIgAyAEIAUQBCABBEAgACABEDQLAkAgBigCBCICIAYoAgwiAE0EQCAGKAIIIQEMAQsgBigCCCEDIABFBEBBASEBIAMgAhA0DAELIAMgAkEBIAAQMSIBDQBBASAAEC0ACyAHIAA2AgQgByABNgIAIAZBEGokACAHKAIAIAcoAgQgB0EQaiQAC9YVAhZ/AX0jAEEQayITJAAgACEWIAEhEkEAIQAjAEEQayIGJAACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQCADIAIiCGwiCUEASA0AQQEhCiAJBEBBASEAIAlBARA1IgpFDQELIAkEQCAKQQEgCfwLAAtBgMAAQQQQNSIRBEAgBkEANgIMIAYgETYCCCAGQYAINgIEIANBAWsiGEECSQ0LIAhBAWshFyAIQQJrIg5BAnYhGSAOQXxxIhtBAXIhFCAOQQRPBEAgCEECdCIaIBlBBHRqIBZqQQRqIQ8gCCAKaiELIBYgGmohDEEAIQJBAiEAQQEhBwNAIAAhECAHIAhsIgEgF2oiACABQQFqIgNJIAAgEktyDQsgACAJSw0JQQAhAEEBIQEgDCEDA0AgACAOTw0IIA4gAGsiDUEAIA0gDk0bQQFqIQ0CQCAFIANBBGoqAgAiHF8EQCAAIAtqQQFqQQI6AAAgBigCBCACRgRAIAZBBGoQFSAGKAIIIRELIBEgAkEDdGoiFSAHNgIEIBUgAEEBajYCACAGIAJBAWoiAjYCDAwBCyAEIBxfRQ0AIAAgC2pBAWpBADoAAAsgDUECRg0HAkAgBSADQQhqKgIAIhxfBEAgACALakECakECOgAAIAYoAgQgAkYEQCAGQQRqEBULIAYoAggiESACQQN0aiIVIAc2AgQgFSAAQQJqNgIAIAYgAkEBaiICNgIMDAELIAQgHF9FDQAgACALakECakEAOgAACyANQQNGDQUCQCAFIANBDGoqAgAiHF8EQCAAIAtqQQNqQQI6AAAgBigCBCACRgRAIAZBBGoQFQsgBigCCCIRIAJBA3RqIhUgBzYCBCAVIABBA2o2AgAgBiACQQFqIgI2AgwMAQsgBCAcX0UNACAAIAtqQQNqQQA6AAALIA1BBEYNBiABIQ0CQCAFIANBEGoiAyoCACIcXwRAIAAgC2pBBGpBAjoAACAGKAIEIAJGBEAgBkEEahAVCyAGKAIIIhEgAkEDdGoiASAHNgIEIAEgAEEEajYCACAGIAJBAWoiAjYCDAwBCyAEIBxfRQ0AIAAgC2pBBGpBADoAAAsgDUEBaiEBIABBBGohACANIBlJDQALIA8hAyAUIQEgDiAbRwRAA0AgASEAAkAgBSADKgIAIhxfBEAgACALakECOgAAIAYoAgQgAkYEQCAGQQRqEBULIAYoAggiESACQQN0aiIBIAc2AgQgASAANgIAIAYgAkEBaiICNgIMDAELIAQgHF9FDQAgACALakEAOgAACyADQQRqIQMgAEEBaiEBIAAgDkkNAAsLIA8gGmohDyAIIAtqIQsgDCAaaiEMIBAgECAYSSIBaiEAIBAhByABDQALDAsLAkAgDiAbRwRAIAhBAnQiDCAZQQR0aiAWakEEaiEHIAggCmohC0EAIQJBAiEAQQEhDwwBCyAIQQFqIQMgCEEBdEEBayEHQQAhAkECIQADQCACIAdqIgEgAiADakkgASASS3INCiABIAlLDQggAiAIaiECIAAgGEkgAEEBaiEADQALDAwLA0AgACEQIAggD2wiASAXaiIAIAFBAWoiA0kgACASS3INCiAAIAlLDQggByEDIBQhAQNAIAEhAAJAIAMqAgAiHCAFYEUEQCAEIBxfRQ0BIAAgC2pBADoAAAwBCyAAIAtqQQI6AAAgBigCBCACRgRAIAZBBGoQFSAGKAIIIRELIBEgAkEDdGoiASAPNgIEIAEgADYCACAGIAJBAWoiAjYCDAsgA0EEaiEDIABBAWohASAAIA5JDQALIAcgDGohByAIIAtqIQsgECAQIBhJIgFqIQAgECEPIAENAAsMCgtBBEGAwAAQLQALIAAgCRAtAAsgAEECaiEADAILIABBA2ohAAwBCyAAQQFqIQALIAAgDkHQicAAEBgACyACIAhqQQFqIQMgCEEBdCACakEBayEACyADIAAgCUHAicAAEBsACyACIAhqQQFqIQMgCEEBdCACakEBayEACyADIAAgEkHgicAAEBsACyACRQ0AIAhBAWohD0EBIAhrIRAgCEF/cyEUIAgEQCAGKAIIIQEDQCAGIAJBAWsiADYCDCAGKAIEIQwCQCABIABBA3QiDWoiAygCBCAIbCADKAIAaiIDIBRqIgcgCU8NACAHIApqIgstAAANACALQQI6AAAgASANaiIAIAcgCG4iDTYCBCAAIAcgCCANbGs2AgAgBiACNgIMIAIhAAsCQCADIAhrIgIgCU8NACACIApqIgctAAANACAHQQI6AAAgAiACIAhuIgIgCGxrIQcgACAMRgRAIAZBBGoQFSAGKAIIIQELIAEgAEEDdGoiDCACNgIEIAwgBzYCACAGIABBAWoiADYCDAsCQCADIBBqIgIgCU8NACACIApqIgctAAANACAHQQI6AAAgAiACIAhuIgIgCGxrIQcgBigCBCAARgRAIAZBBGoQFQsgBigCCCIBIABBA3RqIgwgAjYCBCAMIAc2AgAgBiAAQQFqIgA2AgwLAkAgA0EBayICIAlPDQAgAiAKaiIHLQAADQAgB0ECOgAAIAIgAiAIbiICIAhsayEHIAYoAgQgAEYEQCAGQQRqEBULIAYoAggiASAAQQN0aiIMIAI2AgQgDCAHNgIAIAYgAEEBaiIANgIMCwJAIANBAWoiAiAJTw0AIAIgCmoiBy0AAA0AIAdBAjoAACACIAIgCG4iAiAIbGshByAGKAIEIABGBEAgBkEEahAVCyAGKAIIIgEgAEEDdGoiDCACNgIEIAwgBzYCACAGIABBAWoiADYCDAsCQCADIBdqIgIgCU8NACACIApqIgctAAANACAHQQI6AAAgAiACIAhuIgIgCGxrIQcgBigCBCAARgRAIAZBBGoQFQsgBigCCCIBIABBA3RqIgwgAjYCBCAMIAc2AgAgBiAAQQFqIgA2AgwLAkAgAyAIaiICIAlPDQAgAiAKaiIHLQAADQAgB0ECOgAAIAIgAiAIbiICIAhsayEHIAYoAgQgAEYEQCAGQQRqEBULIAYoAggiASAAQQN0aiIMIAI2AgQgDCAHNgIAIAYgAEEBaiIANgIMCyAJIAMgD2oiAk0EQCAAIgINAQwDCyACIApqIgMtAAAEQCAAIgINAQwDCyADQQI6AAAgAiACIAhuIgIgCGxrIQMgBigCBCAARgRAIAZBBGoQFQsgBigCCCIBIABBA3RqIgcgAjYCBCAHIAM2AgAgBiAAQQFqIgI2AgwgAg0ACwwBCyAGKAIIIAJBA3RqQQhrIQEDQCAJIAEoAgAiACAUaiIDSwRAIAMgCmoiAy0AAEUNAwsgACAJTyIHRQRAIAAgCmoiAy0AAEUNAwsgCSAAIBBqIgNLBEAgAyAKaiIDLQAARQ0DCyAJIABBAWsiA0sEQCADIApqIgMtAABFDQMLIAkgAEEBaiIDSwRAIAMgCmoiAy0AAEUNAwsgCSAAIBdqIgNLBEAgAyAKaiIDLQAARQ0DCyAHRQRAIAAgCmoiAy0AAEUNAwsgCSAAIA9qIgBLBEAgACAKaiIDLQAARQ0DCyABQQhrIQEgAkEBayICDQALCyAGKAIEIgAEQCAGKAIIIABBA3QQNAsgEgRAIBYgEkECdBA0CyATIAk2AgQgEyAKNgIAIAZBEGokAAwBCyADQQI6AABBsInAABAfAAsgEygCACATKAIEIBNBEGokAAvoDgIUfwF9IwBBEGsiEiQAIAAhFSABIRNBACEBIwBBEGsiByQAAkACQAJAAkACQAJAAkACQAJAIAIgA2wiCEEASA0AQQEhDEEBIQkCQCAIBEBBASENIAhBARA2IgxFDQIgCEEBEDUiCUUNAQsgCARAIAlBASAI/AsAC0GAwABBBBA1IgsEQCAHQQA2AgwgByALNgIIIAdBgAg2AgQgAkEBayIWQQJJDQcgA0EBayIXQQJJDQcgAiAMaiEUIAIgCWohD0ECIAJrIRggAkECdCIZIBVqQQRqIRAgAiERQQIhAEEBIQoDQCAAIQMgECENQQEhAANAIAAgEWoiDiATTw0HAkAgBSANKgIAIhpfBEAgCCAOTQ0HIAAgD2pBAjoAACAAIBRqQf8BOgAAIAcoAgQgBkYEQCAHQQRqEBUgBygCCCELCyALIAZBA3RqIgEgCjYCBCABIAA2AgAgByAGQQFqIgY2AgwgBiEBDAELIAQgGl9FDQAgCCAOTQ0HIAAgD2pBADoAAAsgDUEEaiENIBggAEEBaiIAakEBRw0ACyAQIBlqIRAgAiAUaiEUIAIgD2ohDyACIBFqIREgAyADIBdJIg1qIQAgAyEKIA0NAAsgAUUNByACQQFqIQ1BASACayEQIAJBf3MhESACRQ0GA0AgByABQQFrIgA2AgwgBygCBCEKAkAgCyAAQQN0Ig5qIgMoAgQgAmwgAygCAGoiAyARaiIGIAhPDQAgBiAJaiIPLQAADQAgD0ECOgAAIAYgDGpB/wE6AAAgBygCCCILIA5qIgAgBiACbiIONgIEIAAgBiACIA5sazYCACAHIAE2AgwgASEACwJAIAMgAmsiASAITw0AIAEgCWoiBi0AAA0AIAZBAjoAACABIAxqQf8BOgAAIAEgASACbiIBIAJsayEGIAAgCkYEQCAHQQRqEBULIAcoAggiCyAAQQN0aiIKIAE2AgQgCiAGNgIAIAcgAEEBaiIANgIMCwJAIAMgEGoiASAITw0AIAEgCWoiBi0AAA0AIAZBAjoAACABIAxqQf8BOgAAIAEgASACbiIBIAJsayEGIAcoAgQgAEYEQCAHQQRqEBULIAcoAggiCyAAQQN0aiIKIAE2AgQgCiAGNgIAIAcgAEEBaiIANgIMCwJAIANBAWsiASAITw0AIAEgCWoiBi0AAA0AIAZBAjoAACABIAxqQf8BOgAAIAEgASACbiIBIAJsayEGIAcoAgQgAEYEQCAHQQRqEBULIAcoAggiCyAAQQN0aiIKIAE2AgQgCiAGNgIAIAcgAEEBaiIANgIMCwJAIANBAWoiASAITw0AIAEgCWoiBi0AAA0AIAZBAjoAACABIAxqQf8BOgAAIAEgASACbiIBIAJsayEGIAcoAgQgAEYEQCAHQQRqEBULIAcoAggiCyAAQQN0aiIKIAE2AgQgCiAGNgIAIAcgAEEBaiIANgIMCwJAIAMgFmoiASAITw0AIAEgCWoiBi0AAA0AIAZBAjoAACABIAxqQf8BOgAAIAEgASACbiIBIAJsayEGIAcoAgQgAEYEQCAHQQRqEBULIAcoAggiCyAAQQN0aiIKIAE2AgQgCiAGNgIAIAcgAEEBaiIANgIMCwJAIAIgA2oiASAITw0AIAEgCWoiBi0AAA0AIAZBAjoAACABIAxqQf8BOgAAIAEgASACbiIBIAJsayEGIAcoAgQgAEYEQCAHQQRqEBULIAcoAggiCyAAQQN0aiIKIAE2AgQgCiAGNgIAIAcgAEEBaiIANgIMCyAIIAMgDWoiAU0EQCAAIgENAQwJCyABIAlqIgMtAAAEQCAAIgENAQwJCyADQQI6AAAgASAMakH/AToAACABIAEgAm4iASACbGshAyAHKAIEIABGBEAgB0EEahAVCyAHKAIIIgsgAEEDdGoiBiABNgIEIAYgAzYCACAHIABBAWoiATYCDCABDQALDAcLQQRBgMAAEC0AC0EBIAgQLQALIA0gCBAtAAsgDiAIQaCKwAAQGAALIA4gCEGQisAAEBgACyAOIBNBgIrAABAYAAsgCyABQQN0akEIayEKA0AgByABQQFrIgE2AgwgCCAKKAIAIgIgEWoiAEsEQCAAIAlqIgYtAABFDQQLIAIgCE8iA0UEQCACIAlqIgYtAABFDQMLIAggAiAQaiIASwRAIAAgCWoiBi0AAEUNBAsgCCACQQFrIgBLBEAgACAJaiIGLQAARQ0ECyAIIAJBAWoiAEsEQCAAIAlqIgYtAABFDQQLIAggAiAWaiIASwRAIAAgCWoiBi0AAEUNBAsgA0UEQCACIAlqIgYtAABFDQMLIAggAiANaiIASwRAIAAgCWoiBi0AAEUNBAsgCkEIayEKIAENAAsLIAcoAgQiAARAIAcoAgggAEEDdBA0CyAIBEAgCSAIEDQLIBMEQCAVIBNBAnQQNAsgEiAINgIEIBIgDDYCACAHQRBqJAAMAgsgAiEACyAGQQI6AAAgACAMakH/AToAAEHwicAAEB8ACyASKAIAIBIoAgQgEkEQaiQACy8AAkAgAWlBAUYgAEGAgICAeCABa01xRQ0AIAAEQCAAIAEQNSIBRQ0BCyABDwsAC4ABAQR/IwBBEGsiBSQAAkACQCACIANsIgZBAEgNAAJAIAZFBEBBASEHDAELQQEhCCAGQQEQNiIHRQ0BCyAAIAEgAiADIAQgByAGEAIgAQRAIAAgARA0CyAFIAY2AgQgBSAHNgIADAELIAggBhAtAAsgBSgCACAFKAIEIAVBEGokAAv6AQICfwF+IwBBEGsiAiQAIAJBATsBDCACIAE2AgggAiAANgIEIwBBEGsiASQAIAJBBGoiACkCACEEIAEgADYCDCABIAQ3AgQjAEEQayIAJAAgAUEEaiIBKAIAIgIoAgwhAwJAAkACQAJAIAIoAgQOAgABAgsgAw0BQQEhAkEAIQMMAgsgAw0AIAIoAgAiAigCBCEDIAIoAgAhAgwBCyAAQYCAgIB4NgIAIAAgATYCDCAAQcCSwAAgASgCBCABKAIIIgAtAAggAC0ACRAPAAsgACADNgIEIAAgAjYCACAAQaSSwAAgASgCBCABKAIIIgAtAAggAC0ACRAPAAuuAQECfyMAQRBrIgUkACMAQRBrIgQkACAEQQRqIAAgASACIAMQBSABBEAgACABEDQLAkAgBCgCBCIBIAQoAgwiAE0EQCAEKAIIIQEMAQsgAUEBdCECIAQoAgghAyAARQRAQQIhASADIAIQNAwBCyADIAJBAiAAQQF0IgIQMSIBDQBBAiACEC0ACyAFIAA2AgQgBSABNgIAIARBEGokACAFKAIAIAUoAgQgBUEQaiQAC6AEAQd/IwBBEGsiBCQAIAAhBkEAIQACQCABRQRAQQEhBQwBCyABQQEQNiIFBEAgAUEISQ0BIAFBA3YhBwNAAkACQCAAIAFPDQAgACAFaiICQX9BACAAIAZqIgMtAABBAkYbOgAAIAEgAEEBaksEQCACQQFqQX9BACADQQFqLQAAQQJGGzoAACABIABBAmpLBEAgAkECakF/QQAgA0ECai0AAEECRhs6AAAgASAAQQNqSwRAIAJBA2pBf0EAIANBA2otAABBAkYbOgAAIAEgAEEEaksEQCACQQRqQX9BACADQQRqLQAAQQJGGzoAACABIABBBWpLBEAgAkEFakF/QQAgA0EFai0AAEECRhs6AAAgASAAQQZqSwRAIAJBBmpBf0EAIANBBmotAABBAkYbOgAAIABBB2oiCCABSQ0HIAghAAwGCyAAQQZqIQAMBQsgAEEFaiEADAQLIABBBGohAAwDCyAAQQNqIQAMAgsgAEECaiEADAELIABBAWohAAsgACABQaCJwAAQGAALIAJBB2pBf0EAIANBB2otAABBAkYbOgAAIABBCGohACAHQQFrIgcNAAsMAQtBASABEC0ACyABIAFB+P///wdxIgBHBEADQCAAIAVqQX9BACAAIAZqLQAAQQJGGzoAACAAQQFqIgAgAUkNAAsLIAEEQCAGIAEQNAsgBCABNgIEIAQgBTYCACAEKAIAIAQoAgQgBEEQaiQACyUBAX8gACgCACIBQYCAgIB4ckGAgICAeEcEQCAAKAIEIAEQNAsLFwEBfyAAKAIAIgEEQCAAKAIEIAEQNAsLHwAgAEEIakGwkcAAKQIANwIAIABBqJHAACkCADcCAAsfACAAQQhqQcCRwAApAgA3AgAgAEG4kcAAKQIANwIAC0MAIAAEQCAAIAEQOQALIwBBIGsiACQAIABBADYCGCAAQQE2AgwgAEGQlMAANgIIIABCBDcCECAAQQhqQZiUwAAQJgALHAAgAEEANgIQIABCADcCCCAAQoCAgIDAADcCAAsNACABBEAgACABEDQLCxYAIAAoAgAgASACIAAoAgQoAgwRAwAL5wYBBX8CfwJAAkACQAJAAkACQAJAIABBBGsiBygCACIIQXhxIgRBBEEIIAhBA3EiBRsgAWpPBEAgBUEAIAFBJ2oiBiAESRsNAQJAIAJBCU8EQCACIAMQCSICDQFBAAwKC0EAIQIgA0HM/3tLDQhBECADQQtqQXhxIANBC0kbIQEgAEEIayEGIAVFBEAgBkUgAUGAAklyIAQgAWtBgIAISyABIARPcnINByAADAoLIAQgBmohBQJAIAEgBEsEQCAFQcybwAAoAgBGDQFByJvAACgCACAFRwRAIAUoAgQiCEECcQ0JIAhBeHEiCCAEaiIEIAFJDQkgBSAIEAogBCABayIFQRBPBEAgByABIAcoAgBBAXFyQQJyNgIAIAEgBmoiASAFQQNyNgIEIAQgBmoiBCAEKAIEQQFyNgIEIAEgBRAIDAkLIAcgBCAHKAIAQQFxckECcjYCACAEIAZqIgEgASgCBEEBcjYCBAwIC0HAm8AAKAIAIARqIgQgAUkNCAJAIAQgAWsiBUEPTQRAIAcgCEEBcSAEckECcjYCACAEIAZqIgEgASgCBEEBcjYCBEEAIQVBACEBDAELIAcgASAIQQFxckECcjYCACABIAZqIgEgBUEBcjYCBCAEIAZqIgQgBTYCACAEIAQoAgRBfnE2AgQLQcibwAAgATYCAEHAm8AAIAU2AgAMBwsgBCABayIEQQ9NDQYgByABIAhBAXFyQQJyNgIAIAEgBmoiASAEQQNyNgIEIAUgBSgCBEEBcjYCBCABIAQQCAwGC0HEm8AAKAIAIARqIgQgAUsNBAwGCyADIAEgASADSxsiAwRAIAIgACAD/AoAAAsgBygCACIDQXhxIgcgAUEEQQggA0EDcSIDG2pJDQIgA0UgBiAHT3INBkGsk8AAQS5B3JPAABAcAAtB7JLAAEEuQZyTwAAQHAALQayTwABBLkHck8AAEBwAC0HsksAAQS5BnJPAABAcAAsgByABIAhBAXFyQQJyNgIAIAEgBmoiBSAEIAFrIgFBAXI2AgRBxJvAACABNgIAQcybwAAgBTYCAAsgBkUNACAADAMLIAMQASIBRQ0BIANBfEF4IAcoAgAiAkEDcRsgAkF4cWoiAiACIANLGyICBEAgASAAIAL8CgAACyABIQILIAAQBgsgAgsLEAAgASAAKAIAIAAoAgQQMAsTACAAQdySwAA2AgQgACABNgIAC18BAn8CQAJAIABBBGsoAgAiAkF4cSIDQQRBCCACQQNxIgIbIAFqTwRAIAJBACADIAFBJ2pLGw0BIAAQBgwCC0HsksAAQS5BnJPAABAcAAtBrJPAAEEuQdyTwAAQHAALCxkAAn8gAUEJTwRAIAEgABAJDAELIAAQAQsLPgACQAJ/IAFBCU8EQCABIAAQCQwBCyAAEAELIgFFDQAgAUEEay0AAEEDcUUgAEVyDQAgAUEAIAD8CwALIAELDQAgAEGMksAAIAEQBwsMACAAIAEpAgA3AwALGQAgACABQeSbwAAoAgAiAEECIAAbEQAAAAsJACAAQQA2AgALC48YAgBBgIDAAAv8F2Fzc2VydGlvbiBmYWlsZWQ6IG1pbiA8PSBtYXhzcmMvY2FubnkucnMAc3JjL2h5c3RlcmVzaXMucnMAc3JjL2dhdXNzaWFuX2JsdXIucnMAL3J1c3RjL2RlZDVjMDZjZjIxZDJiOTNiZmZkNWQ4ODRhYTZlOTY5MzRlZTQyMzQvbGlicmFyeS9jb3JlL3NyYy9jbXAucnMAc3JjL2dyYWRpZW50X2NhbGN1bGF0aW9uLnJzAHNyYy9kaWxhdGlvbi5ycwBzcmMvbm9uX21heGltdW1fc3VwcHJlc3Npb24ucnMAbGlicmFyeS9hbGxvYy9zcmMvcmF3X3ZlYy9tb2QucnMAL3J1c3QvZGVwcy9kbG1hbGxvYy0wLjIuMTAvc3JjL2RsbWFsbG9jLnJzAGxpYnJhcnkvc3RkL3NyYy9hbGxvYy5ycwAvdXNyL2xvY2FsL2NhcmdvL3JlZ2lzdHJ5L3NyYy9pbmRleC5jcmF0ZXMuaW8tMTk0OWNmOGM2YjViNTU3Zi9vbmNlX2NlbGwtMS4yMS4zL3NyYy9saWIucnMAAFAAEABHAAAAQgQAAAkAAAA7ABAAFAAAAPsAAAAfAAAAOwAQABQAAAAtAQAAGQAAADsAEAAUAAAAHwEAAA0AAAA7ABAAFAAAACABAAANAAAAOwAQABQAAAAhAQAADQAAADsAEAAUAAAAIgEAAA0AAAA7ABAAFAAAAE8AAAAbAAAAOwAQABQAAABQAAAAHwAAADsAEAAUAAAAeAAAABkAAAA7ABAAFAAAAGIAAAAhAAAAOwAQABQAAABFAQAAHwAAADsAEAAUAAAAfAEAAB8AAAA7ABAAFAAAAH4BAAAdAAAAOwAQABQAAAB/AQAAHAAAADsAEAAUAAAAUAEAAB8AAAA7ABAAFAAAAHIBAAAdAAAAOwAQABQAAABzAQAAHAAAADsAEAAUAAAAdAEAABwAAAA7ABAAFAAAAGgBAAANAAAAOwAQABQAAABpAQAADQAAADsAEAAUAAAAagEAAA0AAAA7ABAAFAAAAGsBAAANAAAAOwAQABQAAABHAQAAHQAAADsAEAAUAAAASQEAABwAAAA7ABAAFAAAAI8AAAAbAAAAOwAQABQAAACQAAAAHwAAADsAEAAUAAAAkwAAACIAAAA7ABAAFAAAAJUAAAAiAAAAOwAQABQAAAC9AAAALwAAADsAEAAUAAAAvgAAAC0AAAA7ABAAFAAAAL8AAAAtAAAAOwAQABQAAACfAAAAEQAAADsAEAAUAAAAoAAAABEAAAA7ABAAFAAAAKEAAAARAAAAOwAQABQAAACnAAAAEQAAADsAEAAUAAAA2AAAABsAAAA7ABAAFAAAANkAAAAfAAAAOwAQABQAAADgAAAAGQAAAEtlcm5lbCBzaXplIG11c3QgYmUgb2RkIGFuZCBncmVhdGVyIHRoYW4gMAAAFAQQACoAAAA7ABAAFAAAAJQBAAAJAAAASW5wdXQgYXJyYXkgc2l6ZSBkb2Vzbid0IG1hdGNoIHdpZHRoICogaGVpZ2h0AAAAWAQQAC0AAAA7ABAAFAAAAJEBAAAJAAAAKQAQABEAAACHAAAAHgAAACkAEAARAAAAaAAAABoAAAApABAAEQAAACQAAAAnAAAAKQAQABEAAAAtAAAAGwAAACkAEAARAAAAIwAAACQAAAApABAAEQAAANkAAAAaAAAAKQAQABEAAACyAAAAFwAAACkAEAARAAAAuwAAABkAAAApABAAEQAAALYAAAAZAAAAYXNzZXJ0aW9uIGZhaWxlZDogbWluIDw9IG1heFAAEABHAAAAQgQAAAkAAACYABAAGwAAAA8AAAAWAAAAmAAQABsAAAAQAAAAFgAAAJgAEAAbAAAAEQAAABYAAACYABAAGwAAABIAAAAWAAAAmAAQABsAAAATAAAAFgAAAJgAEAAbAAAAFAAAABYAAACYABAAGwAAABUAAAAWAAAAmAAQABsAAAAWAAAAFgAAAJgAEAAbAAAAHAAAABMAAACYABAAGwAAAB0AAAATAAAAtAAQAA8AAABkAAAADQAAALQAEAAPAAAAXwAAAB8AAAC0ABAADwAAAFQAAAANAAAAtAAQAA8AAABPAAAAHwAAALQAEAAPAAAAMwAAAA0AAAC0ABAADwAAAC4AAAAfAAAAtAAQAA8AAAAeAAAAEQAAALQAEAAPAAAAGQAAABsAAADEABAAHgAAAFoAAAAgAAAAxAAQAB4AAABhAAAAFgAAAMQAEAAeAAAAYgAAABYAAADEABAAHgAAAHkAAAAqAAAAxAAQAB4AAAB6AAAAKgAAAMQAEAAeAAAAdgAAACoAAADEABAAHgAAAHcAAAAqAAAAxAAQAB4AAABxAAAAJgAAAMQAEAAeAAAAcgAAACYAAADEABAAHgAAAG4AAAAmAAAAxAAQAB4AAABvAAAAJgAAAMQAEAAeAAAALgAAABIAAADEABAAHgAAADIAAAANAAAAxAAQAB4AAAAwAAAADQAAAMQAEAAeAAAAEwAAABMAAADEABAAHgAAABQAAAATAAAAxAAQAB4AAAAVAAAAEwAAAMQAEAAeAAAAFgAAABMAAADEABAAHgAAABkAAAATAAAAxAAQAB4AAAAaAAAAEwAAAMQAEAAeAAAAGwAAABMAAADEABAAHgAAABwAAAATAAAAHAAQAAwAAAAoAAAAIAAAABwAEAAMAAAAEgAAABcAAAAcABAADAAAABcAAAAZAAAAHAAQAAwAAAAUAAAAGQAAABwAEAAMAAAAUQAAAB4AAAAcABAADAAAAFIAAAAeAAAATGF6eSBpbnN0YW5jZSBoYXMgcHJldmlvdXNseSBiZWVuIHBvaXNvbmVkAAA8CBAAKgAAAEgBEABaAAAACAMAABkAAAByZWVudHJhbnQgaW5pdAAAgAgQAA4AAABIARAAWgAAAHoCAAANAAAAfP2LMlfmV/kC30S/40jnr21dy9YsUOtjeEGmV3Ebi7ltZW1vcnkgYWxsb2NhdGlvbiBvZiAgYnl0ZXMgZmFpbGVkAADICBAAFQAAAN0IEAANAAAALwEQABgAAABkAQAACQAAAAQAAAAMAAAABAAAAAUAAAAGAAAABwAAAAAAAAAIAAAABAAAAAgAAAAJAAAACgAAAAsAAAAMAAAAEAAAAAQAAAANAAAADgAAAA8AAAAQAAAAAAAAAAgAAAAEAAAAEQAAAGFzc2VydGlvbiBmYWlsZWQ6IHBzaXplID49IHNpemUgKyBtaW5fb3ZlcmhlYWQAAAQBEAAqAAAAsQQAAAkAAABhc3NlcnRpb24gZmFpbGVkOiBwc2l6ZSA8PSBzaXplICsgbWF4X292ZXJoZWFkAAAEARAAKgAAALcEAAANAAAABAAAAAwAAAAEAAAAEgAAAGNhcGFjaXR5IG92ZXJmbG93AAAA/AkQABEAAADjABAAIAAAABwAAAAFAAAAMDAwMTAyMDMwNDA1MDYwNzA4MDkxMDExMTIxMzE0MTUxNjE3MTgxOTIwMjEyMjIzMjQyNTI2MjcyODI5MzAzMTMyMzMzNDM1MzYzNzM4Mzk0MDQxNDI0MzQ0NDU0NjQ3NDg0OTUwNTE1MjUzNTQ1NTU2NTc1ODU5NjA2MTYyNjM2NDY1NjY2NzY4Njk3MDcxNzI3Mzc0NzU3Njc3Nzg3OTgwODE4MjgzODQ4NTg2ODc4ODg5OTA5MTkyOTM5NDk1OTY5Nzk4OTlyYW5nZSBlbmQgaW5kZXggIG91dCBvZiByYW5nZSBmb3Igc2xpY2Ugb2YgbGVuZ3RoIAAA8AoQABAAAAAACxAAIgAAAHNsaWNlIGluZGV4IHN0YXJ0cyBhdCAgYnV0IGVuZHMgYXQgADQLEAAWAAAASgsQAA0AAAByYW5nZSBzdGFydCBpbmRleCAAAGgLEAASAAAAAAsQACIAAABhdHRlbXB0IHRvIGRpdmlkZSBieSB6ZXJvAAAAjAsQABkAAABpbmRleCBvdXQgb2YgYm91bmRzOiB0aGUgbGVuIGlzICBidXQgdGhlIGluZGV4IGlzIAAAsAsQACAAAADQCxAAEgAAAAAAAD8AAAC/AEGUmMAACwEBAHwJcHJvZHVjZXJzAghsYW5ndWFnZQEEUnVzdAAMcHJvY2Vzc2VkLWJ5AwVydXN0Yx0xLjkyLjAgKGRlZDVjMDZjZiAyMDI1LTEyLTA4KQZ3YWxydXMGMC4yMy4zDHdhc20tYmluZGdlbhMwLjIuMTAwICgyNDA1ZWMyYjQpAHQPdGFyZ2V0X2ZlYXR1cmVzBysPbXV0YWJsZS1nbG9iYWxzKxNub250cmFwcGluZy1mcHRvaW50KwdzaW1kMTI4KwtidWxrLW1lbW9yeSsIc2lnbi1leHQrD3JlZmVyZW5jZS10eXBlcysKbXVsdGl2YWx1ZQ==";
672
- let wasmReadyPromise = null;
673
- let hasLoggedFullCannyFallback = false;
674
- let wasmSupportCache = null;
675
- function wasmBytesFromBase64(base64) {
676
- if (typeof atob === "function") {
677
- const binary = atob(base64);
678
- const bytes = new Uint8Array(binary.length);
679
- for (let i = 0; i < binary.length; i++) bytes[i] = binary.charCodeAt(i);
680
- return bytes;
681
- }
682
- return Uint8Array.from(Buffer.from(base64, "base64"));
683
- }
684
- function wasmModuleSupported() {
685
- if (wasmSupportCache !== null) return wasmSupportCache;
686
- let supported = false;
687
- if (typeof WebAssembly === "object" && typeof WebAssembly.validate === "function") {
688
- const simd = new Uint8Array([0, 97, 115, 109, 1, 0, 0, 0, 1, 5, 1, 96, 0, 1, 123, 3, 2, 1, 0, 10, 10, 1, 8, 0, 65, 0, 253, 15, 253, 98, 11]);
689
- const referenceTypes = new Uint8Array([0, 97, 115, 109, 1, 0, 0, 0, 1, 5, 1, 96, 0, 1, 111, 3, 2, 1, 0, 10, 6, 1, 4, 0, 208, 111, 11]);
690
- try {
691
- supported = WebAssembly.validate(simd) && WebAssembly.validate(referenceTypes);
692
- } catch {
693
- supported = false;
694
- }
695
- }
696
- wasmSupportCache = supported;
697
- return supported;
698
- }
699
- async function initializeWasmInternal() {
700
- if (!wasmModuleSupported()) {
701
- throw new Error("scanic: WebAssembly features required by the WASM module (reference-types, SIMD) are unavailable in this engine; using the JavaScript fallback.");
702
- }
703
- return await __wbg_init({ module_or_path: wasmBytesFromBase64(wasmBase64) });
704
- }
705
- function initializeWasm() {
706
- if (!wasmReadyPromise) {
707
- wasmReadyPromise = initializeWasmInternal().catch((error) => {
708
- wasmReadyPromise = null;
709
- throw error;
710
- });
711
- }
712
- return wasmReadyPromise;
713
- }
714
- function convertToGrayscale(imageData) {
715
- const { width, height, data } = imageData;
716
- const grayscale = new Uint8ClampedArray(width * height);
717
- for (let i = 0, j = 0; i < data.length; i += 4, j++) {
718
- grayscale[j] = data[i] * 54 + data[i + 1] * 183 + data[i + 2] * 19 >> 8;
719
- }
720
- return grayscale;
721
- }
722
- function gaussianBlurGrayscale(grayscale, width, height, kernelSize = 5, sigma = 0) {
723
- if (sigma === 0) {
724
- sigma = 0.3 * ((kernelSize - 1) * 0.5 - 1) + 0.8;
725
- }
726
- const halfKernel = Math.floor(kernelSize / 2);
727
- const kernel = createGaussianKernel(kernelSize, sigma);
728
- const tempArray = new Uint8ClampedArray(width * height);
729
- const blurred = new Uint8ClampedArray(width * height);
730
- for (let y = 0; y < height; y++) {
731
- const rowOffset = y * width;
732
- for (let x = 0; x < width; x++) {
733
- let sum = 0;
734
- for (let k = -halfKernel; k <= halfKernel; k++) {
735
- const xOffset = Math.min(width - 1, Math.max(0, x + k));
736
- sum += grayscale[rowOffset + xOffset] * kernel[halfKernel + k];
737
- }
738
- tempArray[rowOffset + x] = sum;
739
- }
740
- }
741
- for (let x = 0; x < width; x++) {
742
- for (let y = 0; y < height; y++) {
743
- let sum = 0;
744
- for (let k = -halfKernel; k <= halfKernel; k++) {
745
- const yOffset = Math.min(height - 1, Math.max(0, y + k));
746
- sum += tempArray[yOffset * width + x] * kernel[halfKernel + k];
747
- }
748
- blurred[y * width + x] = Math.round(sum);
749
- }
750
- }
751
- return blurred;
752
- }
753
- function createGaussianKernel(size, sigma) {
754
- const kernel = new Float32Array(size);
755
- const halfSize = Math.floor(size / 2);
756
- let sum = 0;
757
- for (let i = 0; i < size; i++) {
758
- const x = i - halfSize;
759
- kernel[i] = Math.exp(-(x * x) / (2 * sigma * sigma));
760
- sum += kernel[i];
761
- }
762
- for (let i = 0; i < size; i++) {
763
- kernel[i] /= sum;
764
- }
765
- return kernel;
766
- }
767
- function calculateGradients(blurred, width, height) {
768
- const dx = new Int16Array(width * height);
769
- const dy = new Int16Array(width * height);
770
- for (let y = 1; y < height - 1; y++) {
771
- const rowOffset = y * width;
772
- const prevRowOffset = (y - 1) * width;
773
- const nextRowOffset = (y + 1) * width;
774
- for (let x = 1; x < width - 1; x++) {
775
- const currentIdx = rowOffset + x;
776
- const p0 = blurred[prevRowOffset + x - 1];
777
- const p1 = blurred[prevRowOffset + x];
778
- const p2 = blurred[prevRowOffset + x + 1];
779
- const p3 = blurred[rowOffset + x - 1];
780
- const p5 = blurred[rowOffset + x + 1];
781
- const p6 = blurred[nextRowOffset + x - 1];
782
- const p7 = blurred[nextRowOffset + x];
783
- const p8 = blurred[nextRowOffset + x + 1];
784
- const gx = p2 - p0 + 2 * (p5 - p3) + (p8 - p6);
785
- const gy = p6 + 2 * p7 + p8 - (p0 + 2 * p1 + p2);
786
- dx[currentIdx] = gx;
787
- dy[currentIdx] = gy;
788
- }
789
- }
790
- return { dx, dy };
791
- }
792
- function nonMaximumSuppression(dx, dy, width, height, L2gradient) {
793
- const magnitude = new Float32Array(width * height);
794
- const suppressed = new Float32Array(width * height);
795
- for (let i = 0; i < dx.length; i++) {
796
- const gx = dx[i];
797
- const gy = dy[i];
798
- if (L2gradient) {
799
- magnitude[i] = Math.sqrt(gx * gx + gy * gy);
800
- } else {
801
- magnitude[i] = Math.abs(gx) + Math.abs(gy);
802
- }
803
- }
804
- for (let y = 1; y < height - 1; y++) {
805
- for (let x = 1; x < width - 1; x++) {
806
- const idx = y * width + x;
807
- const mag = magnitude[idx];
808
- if (mag === 0) {
809
- suppressed[idx] = 0;
810
- continue;
811
- }
812
- const gx = dx[idx];
813
- const gy = dy[idx];
814
- let neighbor1 = 0, neighbor2 = 0;
815
- const absGx = Math.abs(gx);
816
- const absGy = Math.abs(gy);
817
- if (absGy > absGx * 2.4142) {
818
- neighbor1 = magnitude[idx - width];
819
- neighbor2 = magnitude[idx + width];
820
- } else if (absGx > absGy * 2.4142) {
821
- neighbor1 = magnitude[idx - 1];
822
- neighbor2 = magnitude[idx + 1];
823
- } else {
824
- const s = (gx ^ gy) < 0 ? -1 : 1;
825
- if (gy > 0) {
826
- neighbor1 = magnitude[(y - 1) * width + (x - s)];
827
- neighbor2 = magnitude[(y + 1) * width + (x + s)];
828
- } else {
829
- neighbor1 = magnitude[(y + 1) * width + (x - s)];
830
- neighbor2 = magnitude[(y - 1) * width + (x + s)];
831
- }
832
- if (gx > 0 && gy > 0 || gx < 0 && gy < 0) {
833
- neighbor1 = magnitude[(y - 1) * width + (x + 1)];
834
- neighbor2 = magnitude[(y + 1) * width + (x - 1)];
835
- } else {
836
- neighbor1 = magnitude[(y - 1) * width + (x - 1)];
837
- neighbor2 = magnitude[(y + 1) * width + (x + 1)];
838
- }
839
- }
840
- if (mag >= neighbor1 && mag >= neighbor2) {
841
- suppressed[idx] = mag;
842
- } else {
843
- suppressed[idx] = 0;
844
- }
845
- }
846
- }
847
- return suppressed;
848
- }
849
- function hysteresisThresholding(suppressed, width, height, lowThreshold, highThreshold) {
850
- const edgeMap = new Uint8Array(width * height);
851
- const stack = [];
852
- for (let y = 1; y < height - 1; y++) {
853
- for (let x = 1; x < width - 1; x++) {
854
- const idx = y * width + x;
855
- const mag = suppressed[idx];
856
- if (mag >= highThreshold) {
857
- edgeMap[idx] = 2;
858
- stack.push({ x, y });
859
- } else if (mag >= lowThreshold) {
860
- edgeMap[idx] = 0;
861
- } else {
862
- edgeMap[idx] = 1;
863
- }
864
- }
865
- }
866
- for (let x = 0; x < width; x++) {
867
- edgeMap[x] = 1;
868
- edgeMap[(height - 1) * width + x] = 1;
869
- }
870
- for (let y = 1; y < height - 1; y++) {
871
- edgeMap[y * width] = 1;
872
- edgeMap[y * width + width - 1] = 1;
873
- }
874
- const dxNeighbors = [-1, 0, 1, -1, 1, -1, 0, 1];
875
- const dyNeighbors = [-1, -1, -1, 0, 0, 1, 1, 1];
876
- while (stack.length > 0) {
877
- const { x, y } = stack.pop();
878
- for (let i = 0; i < 8; i++) {
879
- const nx = x + dxNeighbors[i];
880
- const ny = y + dyNeighbors[i];
881
- const nidx = ny * width + nx;
882
- if (edgeMap[nidx] === 0) {
883
- edgeMap[nidx] = 2;
884
- stack.push({ x: nx, y: ny });
885
- }
886
- }
887
- }
888
- return edgeMap;
889
- }
890
- function dilateEdges(edges, width, height, kernelSize = 5) {
891
- const halfKernel = Math.floor(kernelSize / 2);
892
- const temp = new Uint8ClampedArray(width * height);
893
- const dilated = new Uint8ClampedArray(width * height);
894
- for (let y = 0; y < height; y++) {
895
- const rowOffset = y * width;
896
- for (let x = 0; x < width; x++) {
897
- let maxVal = 0;
898
- for (let k = -halfKernel; k <= halfKernel; k++) {
899
- const nx = x + k;
900
- if (nx >= 0 && nx < width) {
901
- const val = edges[rowOffset + nx];
902
- if (val > maxVal) {
903
- maxVal = val;
904
- }
905
- }
906
- }
907
- temp[rowOffset + x] = maxVal;
908
- }
909
- }
910
- for (let x = 0; x < width; x++) {
911
- for (let y = 0; y < height; y++) {
912
- let maxVal = 0;
913
- for (let k = -halfKernel; k <= halfKernel; k++) {
914
- const ny = y + k;
915
- if (ny >= 0 && ny < height) {
916
- const val = temp[ny * width + x];
917
- if (val > maxVal) {
918
- maxVal = val;
919
- }
920
- }
921
- }
922
- dilated[y * width + x] = maxVal;
923
- }
924
- }
925
- return dilated;
926
- }
927
- async function cannyEdgeDetector(input, options = {}) {
928
- const storeFullDebug = !!(options.debug && !options.debug._timingsOnly);
929
- const timings = [];
930
- const tStart = performance.now();
931
- const skipGrayscale = options.skipGrayscale || false;
932
- let width, height, grayscale;
933
- if (skipGrayscale) {
934
- width = options.width;
935
- height = options.height;
936
- grayscale = input;
937
- if (storeFullDebug) options.debug.grayscale = grayscale;
938
- } else {
939
- width = input.width;
940
- height = input.height;
941
- let t02 = performance.now();
942
- grayscale = convertToGrayscale(input);
943
- let t12 = performance.now();
944
- timings.push({ step: "Grayscale", ms: (t12 - t02).toFixed(2) });
945
- if (storeFullDebug) options.debug.grayscale = grayscale;
946
- }
947
- let lowThreshold = options.lowThreshold !== void 0 ? options.lowThreshold : null;
948
- let highThreshold = options.highThreshold !== void 0 ? options.highThreshold : null;
949
- if (lowThreshold === null || highThreshold === null) {
950
- const HIST_SIZE = 1024;
951
- const hist = new Uint32Array(HIST_SIZE);
952
- let nonZeroCount = 0;
953
- for (let y = 1; y < height - 1; y++) {
954
- for (let x = 1; x < width - 1; x++) {
955
- const idx = y * width + x;
956
- const gx = -grayscale[idx - width - 1] + grayscale[idx - width + 1] - 2 * grayscale[idx - 1] + 2 * grayscale[idx + 1] - grayscale[idx + width - 1] + grayscale[idx + width + 1];
957
- const gy = -grayscale[idx - width - 1] - 2 * grayscale[idx - width] - grayscale[idx - width + 1] + grayscale[idx + width - 1] + 2 * grayscale[idx + width] + grayscale[idx + width + 1];
958
- let mag = Math.abs(gx) + Math.abs(gy);
959
- if (mag >= HIST_SIZE) mag = HIST_SIZE - 1;
960
- if (mag > 0) {
961
- hist[mag]++;
962
- nonZeroCount++;
963
- }
964
- }
965
- }
966
- const targetCount = Math.round(nonZeroCount * 0.7);
967
- let cumulative = 0;
968
- let pivot = 75;
969
- for (let i = 1; i < HIST_SIZE; i++) {
970
- cumulative += hist[i];
971
- if (cumulative >= targetCount) {
972
- pivot = i;
973
- break;
974
- }
975
- }
976
- if (lowThreshold === null) lowThreshold = Math.max(5, Math.round(pivot * 0.5));
977
- if (highThreshold === null) highThreshold = Math.min(500, Math.round(pivot * 1.5));
978
- }
979
- const kernelSize = options.kernelSize || 5;
980
- const sigma = options.sigma || 0;
981
- const L2gradient = options.L2gradient === void 0 ? false : options.L2gradient;
982
- const applyDilation = options.applyDilation !== void 0 ? options.applyDilation : true;
983
- const dilationKernelSize = options.dilationKernelSize || 5;
984
- const useWasmHysteresis = options.useWasmHysteresis !== void 0 ? options.useWasmHysteresis : false;
985
- const useWasmFullCanny = options.useWasmFullCanny !== void 0 ? options.useWasmFullCanny : false;
986
- if (lowThreshold >= highThreshold) {
987
- console.warn(`Canny Edge Detector: lowThreshold (${lowThreshold}) should be lower than highThreshold (${highThreshold}). Swapping them.`);
988
- [lowThreshold, highThreshold] = [highThreshold, lowThreshold];
989
- }
990
- if (useWasmFullCanny) {
991
- try {
992
- await initializeWasm();
993
- const t0w = performance.now();
994
- const finalEdges2 = new Uint8ClampedArray(
995
- canny_edge_detector_full(
996
- grayscale,
997
- width,
998
- height,
999
- lowThreshold,
1000
- highThreshold,
1001
- kernelSize,
1002
- sigma,
1003
- L2gradient,
1004
- applyDilation,
1005
- dilationKernelSize
1006
- )
1007
- );
1008
- const t1w = performance.now();
1009
- const wasmMs = (t1w - t0w).toFixed(2);
1010
- timings.push({ step: "Edge Processing (WASM)", ms: wasmMs });
1011
- if (storeFullDebug) {
1012
- options.debug.finalEdges = finalEdges2;
1013
- }
1014
- if (options.debug) options.debug.timings = timings;
1015
- else options.debug = { timings };
1016
- const tEnd2 = performance.now();
1017
- timings.unshift({ step: "Edge Detection Total", ms: (tEnd2 - tStart).toFixed(2) });
1018
- return finalEdges2;
1019
- } catch (error) {
1020
- if (!hasLoggedFullCannyFallback) {
1021
- hasLoggedFullCannyFallback = true;
1022
- const reason = error instanceof Error ? error.message : String(error);
1023
- console.warn(`Full WASM Canny unavailable, using step-by-step path: ${reason}`);
1024
- }
1025
- }
1026
- }
1027
- let t0, t1;
1028
- let blurred;
1029
- t0 = performance.now();
1030
- {
1031
- try {
1032
- await initializeWasm();
1033
- blurred = blur(grayscale, width, height, kernelSize, sigma);
1034
- } catch (e) {
1035
- blurred = gaussianBlurGrayscale(grayscale, width, height, kernelSize, sigma);
1036
- }
1037
- }
1038
- t1 = performance.now();
1039
- timings.push({ step: "Gaussian Blur", ms: (t1 - t0).toFixed(2) });
1040
- if (storeFullDebug) {
1041
- options.debug.blurred = blurred;
1042
- }
1043
- t0 = performance.now();
1044
- let dx, dy;
1045
- {
1046
- const gradients = calculateGradients(blurred, width, height);
1047
- dx = gradients.dx;
1048
- dy = gradients.dy;
1049
- }
1050
- t1 = performance.now();
1051
- timings.push({ step: "Gradients", ms: (t1 - t0).toFixed(2) });
1052
- t0 = performance.now();
1053
- let suppressed;
1054
- {
1055
- try {
1056
- await initializeWasm();
1057
- suppressed = await non_maximum_suppression(dx, dy, width, height, L2gradient);
1058
- } catch (e) {
1059
- suppressed = nonMaximumSuppression(dx, dy, width, height, L2gradient);
1060
- }
1061
- }
1062
- t1 = performance.now();
1063
- timings.push({ step: "Non-Max Suppression", ms: (t1 - t0).toFixed(2) });
1064
- t0 = performance.now();
1065
- const finalLowThreshold = L2gradient ? lowThreshold * lowThreshold : lowThreshold;
1066
- const finalHighThreshold = L2gradient ? highThreshold * highThreshold : highThreshold;
1067
- let edgeMap;
1068
- if (useWasmHysteresis) {
1069
- try {
1070
- await initializeWasm();
1071
- edgeMap = hysteresis_thresholding(suppressed, width, height, finalLowThreshold, finalHighThreshold);
1072
- } catch (e) {
1073
- console.warn("WASM hysteresis failed, falling back to JS:", e);
1074
- edgeMap = hysteresisThresholding(suppressed, width, height, finalLowThreshold, finalHighThreshold);
1075
- }
1076
- } else {
1077
- edgeMap = hysteresisThresholding(suppressed, width, height, finalLowThreshold, finalHighThreshold);
1078
- }
1079
- t1 = performance.now();
1080
- timings.push({ step: "Hysteresis", ms: (t1 - t0).toFixed(2) });
1081
- t0 = performance.now();
1082
- const cannyEdges = new Uint8ClampedArray(width * height);
1083
- for (let i = 0; i < edgeMap.length; i++) {
1084
- cannyEdges[i] = edgeMap[i] === 2 ? 255 : 0;
1085
- }
1086
- t1 = performance.now();
1087
- timings.push({ step: "Binary Image", ms: (t1 - t0).toFixed(2) });
1088
- t0 = performance.now();
1089
- let finalEdges = cannyEdges;
1090
- if (applyDilation) {
1091
- {
1092
- try {
1093
- await initializeWasm();
1094
- finalEdges = dilate(cannyEdges, width, height, dilationKernelSize);
1095
- } catch (e) {
1096
- finalEdges = dilateEdges(cannyEdges, width, height, dilationKernelSize);
1097
- }
1098
- }
1099
- }
1100
- t1 = performance.now();
1101
- timings.push({ step: "Dilation", ms: (t1 - t0).toFixed(2) });
1102
- if (storeFullDebug) {
1103
- options.debug.dx = dx;
1104
- options.debug.dy = dy;
1105
- const magnitude = new Float32Array(width * height);
1106
- for (let i = 0; i < dx.length; i++) {
1107
- const gx = dx[i];
1108
- const gy = dy[i];
1109
- magnitude[i] = L2gradient ? Math.sqrt(gx * gx + gy * gy) : Math.abs(gx) + Math.abs(gy);
1110
- }
1111
- options.debug.magnitude = magnitude;
1112
- options.debug.suppressed = suppressed;
1113
- options.debug.edgeMap = edgeMap;
1114
- options.debug.cannyEdges = cannyEdges;
1115
- options.debug.finalEdges = finalEdges;
1116
- }
1117
- if (options.debug) {
1118
- options.debug.timings = timings;
1119
- } else if (!options.debug) {
1120
- options.debug = { timings };
1121
- }
1122
- const tEnd = performance.now();
1123
- timings.unshift({ step: "Edge Detection Total", ms: (tEnd - tStart).toFixed(2) });
1124
- return finalEdges;
1125
- }
1126
- function cloneCorners(corners) {
1127
- return {
1128
- topLeft: { x: corners.topLeft.x, y: corners.topLeft.y },
1129
- topRight: { x: corners.topRight.x, y: corners.topRight.y },
1130
- bottomRight: { x: corners.bottomRight.x, y: corners.bottomRight.y },
1131
- bottomLeft: { x: corners.bottomLeft.x, y: corners.bottomLeft.y }
1132
- };
1133
- }
1134
- function pointDistance$1(a, b) {
1135
- return Math.hypot(a.x - b.x, a.y - b.y);
1136
- }
1137
- function cornersAreFiniteAndDistinct$1(corners, minDistance = 4) {
1138
- const points = [corners == null ? void 0 : corners.topLeft, corners == null ? void 0 : corners.topRight, corners == null ? void 0 : corners.bottomRight, corners == null ? void 0 : corners.bottomLeft];
1139
- if (points.some((p) => !p || !Number.isFinite(p.x) || !Number.isFinite(p.y))) {
1140
- return false;
1141
- }
1142
- for (let i = 0; i < points.length; i++) {
1143
- for (let j = i + 1; j < points.length; j++) {
1144
- if (pointDistance$1(points[i], points[j]) < minDistance) {
1145
- return false;
1146
- }
1147
- }
1148
- }
1149
- return true;
1150
- }
1151
- function isConvexQuadrilateral$1(corners) {
1152
- const points = [corners.topLeft, corners.topRight, corners.bottomRight, corners.bottomLeft];
1153
- const crossSigns = [];
1154
- for (let i = 0; i < points.length; i++) {
1155
- const p0 = points[i];
1156
- const p1 = points[(i + 1) % points.length];
1157
- const p2 = points[(i + 2) % points.length];
1158
- const cross = (p1.x - p0.x) * (p2.y - p1.y) - (p1.y - p0.y) * (p2.x - p1.x);
1159
- if (Math.abs(cross) < 1e-6) continue;
1160
- crossSigns.push(Math.sign(cross));
1161
- }
1162
- if (crossSigns.length < 3) {
1163
- return false;
1164
- }
1165
- const firstSign = crossSigns[0];
1166
- return crossSigns.every((s) => s === firstSign);
1167
- }
1168
- function createDefaultCorners(imageWidth, imageHeight, insetRatio = 0.08) {
1169
- const inset = Math.max(8, Math.min(imageWidth, imageHeight) * insetRatio);
1170
- return {
1171
- topLeft: { x: inset, y: inset },
1172
- topRight: { x: imageWidth - inset, y: inset },
1173
- bottomRight: { x: imageWidth - inset, y: imageHeight - inset },
1174
- bottomLeft: { x: inset, y: imageHeight - inset }
1175
- };
1176
- }
1177
- function drawHandle(ctx, point, radius, color, stroke, lineWidth) {
1178
- ctx.beginPath();
1179
- ctx.arc(point.x, point.y, radius, 0, Math.PI * 2);
1180
- ctx.fillStyle = color;
1181
- ctx.fill();
1182
- ctx.lineWidth = lineWidth;
1183
- ctx.strokeStyle = stroke;
1184
- ctx.stroke();
1185
- }
1186
- function normalizeImageToCanvas(image) {
1187
- if (!image) {
1188
- throw new Error("No image provided");
1189
- }
1190
- const isImageData = image && typeof image.width === "number" && typeof image.height === "number" && image.data;
1191
- let width = 0;
1192
- let height = 0;
1193
- if (isImageData) {
1194
- width = image.width;
1195
- height = image.height;
1196
- } else {
1197
- width = image.width || image.naturalWidth;
1198
- height = image.height || image.naturalHeight;
1199
- }
1200
- if (!width || !height) {
1201
- throw new Error("Image must be loaded before creating the corner editor");
1202
- }
1203
- const canvas = document.createElement("canvas");
1204
- canvas.width = width;
1205
- canvas.height = height;
1206
- const ctx = canvas.getContext("2d", { willReadFrequently: true });
1207
- if (!ctx) {
1208
- throw new Error("Failed to create 2D canvas context for source image");
1209
- }
1210
- if (isImageData) {
1211
- ctx.putImageData(image, 0, 0);
1212
- } else {
1213
- ctx.drawImage(image, 0, 0, width, height);
1214
- }
1215
- return { canvas, width, height };
1216
- }
1217
- function createCornerEditor(options = {}) {
1218
- var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j;
1219
- const container = options.container;
1220
- if (!container || typeof container.appendChild !== "function") {
1221
- throw new Error("createCornerEditor requires a valid container element");
1222
- }
1223
- const { canvas: sourceCanvas, width: imageWidth, height: imageHeight } = normalizeImageToCanvas(options.image);
1224
- const editorCanvas = document.createElement("canvas");
1225
- editorCanvas.style.position = "absolute";
1226
- editorCanvas.style.top = "0";
1227
- editorCanvas.style.left = "0";
1228
- editorCanvas.style.display = "block";
1229
- editorCanvas.style.boxSizing = "border-box";
1230
- editorCanvas.style.touchAction = "none";
1231
- editorCanvas.style.userSelect = "none";
1232
- editorCanvas.style.webkitUserSelect = "none";
1233
- editorCanvas.style.cursor = "crosshair";
1234
- editorCanvas.style.outline = "none";
1235
- const keyboardEnabled = options.keyboard !== false;
1236
- if (keyboardEnabled) {
1237
- editorCanvas.tabIndex = 0;
1238
- editorCanvas.setAttribute("role", "application");
1239
- editorCanvas.setAttribute("aria-label", "Document corner editor. Use arrow keys to adjust the selected corner.");
1240
- }
1241
- const restoreContainerStyle = {
1242
- position: container.style.position,
1243
- minHeight: container.style.minHeight
1244
- };
1245
- let changedContainerPosition = false;
1246
- let changedContainerMinHeight = false;
1247
- if (getComputedStyle(container).position === "static") {
1248
- container.style.position = "relative";
1249
- changedContainerPosition = true;
1250
- }
1251
- container.appendChild(editorCanvas);
1252
- const ctx = editorCanvas.getContext("2d");
1253
- if (!ctx) {
1254
- throw new Error("Failed to create 2D canvas context for corner editor");
1255
- }
1256
- const magnifier = {
1257
- enabled: ((_a = options.magnifier) == null ? void 0 : _a.enabled) !== false,
1258
- size: ((_b = options.magnifier) == null ? void 0 : _b.size) || 110,
1259
- zoom: ((_c = options.magnifier) == null ? void 0 : _c.zoom) || 2,
1260
- margin: ((_d = options.magnifier) == null ? void 0 : _d.margin) || 16,
1261
- borderColor: ((_e = options.magnifier) == null ? void 0 : _e.borderColor) || "#ffffff",
1262
- borderWidth: ((_f = options.magnifier) == null ? void 0 : _f.borderWidth) || 2,
1263
- crosshairColor: ((_g = options.magnifier) == null ? void 0 : _g.crosshairColor) || "#ffffff",
1264
- crosshairSize: ((_h = options.magnifier) == null ? void 0 : _h.crosshairSize) || 18
1265
- };
1266
- const nudges = {
1267
- enabled: !!((_i = options.nudges) == null ? void 0 : _i.enabled),
1268
- steps: (((_j = options.nudges) == null ? void 0 : _j.steps) && options.nudges.steps.length ? options.nudges.steps : [1, 5]).map((v) => Math.max(1, Math.round(v)))
1269
- };
1270
- const defaultCorners = createDefaultCorners(imageWidth, imageHeight);
1271
- const requestedCorners = options.corners ? cloneCorners(options.corners) : defaultCorners;
1272
- let corners = cornersAreFiniteAndDistinct$1(requestedCorners) && isConvexQuadrilateral$1(requestedCorners) ? requestedCorners : defaultCorners;
1273
- const initialCorners = cloneCorners(corners);
1274
- let isDestroyed = false;
1275
- let activeCornerKey = null;
1276
- let dragPointerId = null;
1277
- let lastPointerPosition = null;
1278
- const handleHitArea = Math.max(24, options.handleHitArea || 48);
1279
- const handleRadius = Math.max(8, Math.min(16, handleHitArea * 0.3));
1280
- const cornerOrder = ["topLeft", "topRight", "bottomRight", "bottomLeft"];
1281
- let view = { scale: 1, offsetX: 0, offsetY: 0, width: 1, height: 1 };
1282
- let nudgeControls = null;
1283
- let activeNudgeCorner = "topLeft";
1284
- const runtimeGlobal = typeof window !== "undefined" ? window : globalThis;
1285
- function emitChange() {
1286
- if (typeof options.onChange === "function") {
1287
- options.onChange(cloneCorners(corners));
1288
- }
1289
- }
1290
- let lastDisplayWidth = 0;
1291
- let lastDisplayHeight = 0;
1292
- function computeDisplaySize() {
1293
- const width = Math.max(1, Math.round(container.clientWidth));
1294
- let height = Math.round(container.clientHeight);
1295
- if (height < 80) {
1296
- const aspect = imageHeight / Math.max(1, imageWidth);
1297
- const viewportCap = (runtimeGlobal.innerHeight || 800) * 0.7;
1298
- height = Math.max(240, Math.round(Math.min(width * aspect, viewportCap)));
1299
- container.style.minHeight = height + "px";
1300
- changedContainerMinHeight = true;
1301
- }
1302
- return { width, height: Math.max(1, height) };
1303
- }
1304
- function updateCanvasSize() {
1305
- const { width, height } = computeDisplaySize();
1306
- const dpr = runtimeGlobal.devicePixelRatio || 1;
1307
- lastDisplayWidth = width;
1308
- lastDisplayHeight = height;
1309
- editorCanvas.style.width = width + "px";
1310
- editorCanvas.style.height = height + "px";
1311
- const bufferWidth = Math.round(width * dpr);
1312
- const bufferHeight = Math.round(height * dpr);
1313
- if (editorCanvas.width !== bufferWidth) editorCanvas.width = bufferWidth;
1314
- if (editorCanvas.height !== bufferHeight) editorCanvas.height = bufferHeight;
1315
- ctx.setTransform(dpr, 0, 0, dpr, 0, 0);
1316
- const padding = 12;
1317
- const availableWidth = Math.max(1, width - padding * 2);
1318
- const availableHeight = Math.max(1, height - padding * 2);
1319
- const scale = Math.min(availableWidth / imageWidth, availableHeight / imageHeight);
1320
- const drawWidth = imageWidth * scale;
1321
- const drawHeight = imageHeight * scale;
1322
- view = {
1323
- scale,
1324
- offsetX: Math.round((width - drawWidth) / 2),
1325
- offsetY: Math.round((height - drawHeight) / 2),
1326
- width,
1327
- height
1328
- };
1329
- }
1330
- function imageToView(point) {
1331
- return {
1332
- x: view.offsetX + point.x * view.scale,
1333
- y: view.offsetY + point.y * view.scale
1334
- };
1335
- }
1336
- function viewToImage(x, y) {
1337
- const px = (x - view.offsetX) / view.scale;
1338
- const py = (y - view.offsetY) / view.scale;
1339
- return {
1340
- x: Math.max(0, Math.min(imageWidth, px)),
1341
- y: Math.max(0, Math.min(imageHeight, py))
1342
- };
1343
- }
1344
- function cornersValid(nextCorners) {
1345
- return cornersAreFiniteAndDistinct$1(nextCorners) && isConvexQuadrilateral$1(nextCorners);
1346
- }
1347
- function getEventCanvasPoint(event) {
1348
- const rect = editorCanvas.getBoundingClientRect();
1349
- return {
1350
- x: event.clientX - rect.left,
1351
- y: event.clientY - rect.top
1352
- };
1353
- }
1354
- function hitTestCorner(canvasX, canvasY) {
1355
- let hit = null;
1356
- let bestDistance = Infinity;
1357
- for (const key of cornerOrder) {
1358
- const p = imageToView(corners[key]);
1359
- const d = Math.hypot(canvasX - p.x, canvasY - p.y);
1360
- if (d <= handleHitArea && d < bestDistance) {
1361
- bestDistance = d;
1362
- hit = key;
1363
- }
1364
- }
1365
- return hit;
1366
- }
1367
- function drawPolygonOverlay() {
1368
- const points = cornerOrder.map((key) => imageToView(corners[key]));
1369
- ctx.save();
1370
- ctx.fillStyle = "rgba(15, 23, 42, 0.40)";
1371
- ctx.beginPath();
1372
- ctx.rect(0, 0, view.width, view.height);
1373
- ctx.moveTo(points[0].x, points[0].y);
1374
- for (let i = 1; i < points.length; i++) {
1375
- ctx.lineTo(points[i].x, points[i].y);
1376
- }
1377
- ctx.closePath();
1378
- ctx.fill("evenodd");
1379
- ctx.restore();
1380
- ctx.save();
1381
- ctx.strokeStyle = "#22c55e";
1382
- ctx.lineWidth = 3;
1383
- ctx.beginPath();
1384
- ctx.moveTo(points[0].x, points[0].y);
1385
- for (let i = 1; i < points.length; i++) {
1386
- ctx.lineTo(points[i].x, points[i].y);
1387
- }
1388
- ctx.closePath();
1389
- ctx.stroke();
1390
- ctx.restore();
1391
- for (const key of cornerOrder) {
1392
- const p = imageToView(corners[key]);
1393
- const isActive = key === activeCornerKey;
1394
- drawHandle(
1395
- ctx,
1396
- p,
1397
- isActive ? handleRadius + 1 : handleRadius,
1398
- isActive ? "#f59e0b" : "#ffffff",
1399
- isActive ? "#7c2d12" : "#0f172a",
1400
- 2
1401
- );
1402
- }
1403
- }
1404
- function drawMagnifier() {
1405
- if (!magnifier.enabled || !activeCornerKey || !lastPointerPosition) {
1406
- return;
1407
- }
1408
- const active = corners[activeCornerKey];
1409
- const size = magnifier.size;
1410
- const radius = size / 2;
1411
- const zoom = Math.max(1.1, magnifier.zoom);
1412
- let lensX = lastPointerPosition.x + radius + magnifier.margin;
1413
- let lensY = lastPointerPosition.y - radius - magnifier.margin;
1414
- if (lensX + radius > view.width) {
1415
- lensX = lastPointerPosition.x - radius - magnifier.margin;
1416
- }
1417
- if (lensY - radius < 0) {
1418
- lensY = lastPointerPosition.y + radius + magnifier.margin;
1419
- }
1420
- lensX = Math.max(radius + 2, Math.min(view.width - radius - 2, lensX));
1421
- lensY = Math.max(radius + 2, Math.min(view.height - radius - 2, lensY));
1422
- const sampleSize = size / zoom;
1423
- const sx = Math.max(0, Math.min(imageWidth - sampleSize, active.x - sampleSize / 2));
1424
- const sy = Math.max(0, Math.min(imageHeight - sampleSize, active.y - sampleSize / 2));
1425
- ctx.save();
1426
- ctx.beginPath();
1427
- ctx.arc(lensX, lensY, radius, 0, Math.PI * 2);
1428
- ctx.clip();
1429
- ctx.drawImage(sourceCanvas, sx, sy, sampleSize, sampleSize, lensX - radius, lensY - radius, size, size);
1430
- ctx.restore();
1431
- ctx.save();
1432
- ctx.beginPath();
1433
- ctx.arc(lensX, lensY, radius, 0, Math.PI * 2);
1434
- ctx.lineWidth = magnifier.borderWidth;
1435
- ctx.strokeStyle = magnifier.borderColor;
1436
- ctx.stroke();
1437
- const ch = magnifier.crosshairSize / 2;
1438
- ctx.strokeStyle = magnifier.crosshairColor;
1439
- ctx.lineWidth = 1.5;
1440
- ctx.beginPath();
1441
- ctx.moveTo(lensX - ch, lensY);
1442
- ctx.lineTo(lensX + ch, lensY);
1443
- ctx.moveTo(lensX, lensY - ch);
1444
- ctx.lineTo(lensX, lensY + ch);
1445
- ctx.stroke();
1446
- ctx.restore();
1447
- }
1448
- function render() {
1449
- if (isDestroyed) return;
1450
- ctx.clearRect(0, 0, view.width, view.height);
1451
- ctx.drawImage(
1452
- sourceCanvas,
1453
- 0,
1454
- 0,
1455
- imageWidth,
1456
- imageHeight,
1457
- view.offsetX,
1458
- view.offsetY,
1459
- imageWidth * view.scale,
1460
- imageHeight * view.scale
1461
- );
1462
- drawPolygonOverlay();
1463
- drawMagnifier();
1464
- }
1465
- const raf = typeof runtimeGlobal.requestAnimationFrame === "function" ? runtimeGlobal.requestAnimationFrame.bind(runtimeGlobal) : null;
1466
- const caf = typeof runtimeGlobal.cancelAnimationFrame === "function" ? runtimeGlobal.cancelAnimationFrame.bind(runtimeGlobal) : null;
1467
- let rafId = null;
1468
- function scheduleRender() {
1469
- if (isDestroyed) return;
1470
- if (!raf) {
1471
- render();
1472
- return;
1473
- }
1474
- if (rafId !== null) return;
1475
- rafId = raf(() => {
1476
- rafId = null;
1477
- render();
1478
- });
1479
- }
1480
- function setCorner(nextCornerKey, nextPoint) {
1481
- const candidate = cloneCorners(corners);
1482
- candidate[nextCornerKey] = {
1483
- x: Math.max(0, Math.min(imageWidth, nextPoint.x)),
1484
- y: Math.max(0, Math.min(imageHeight, nextPoint.y))
1485
- };
1486
- if (!cornersValid(candidate)) {
1487
- return false;
1488
- }
1489
- corners = candidate;
1490
- activeNudgeCorner = nextCornerKey;
1491
- emitChange();
1492
- scheduleRender();
1493
- return true;
1494
- }
1495
- function buildNudgeControls() {
1496
- if (!nudges.enabled) {
1497
- return;
1498
- }
1499
- nudgeControls = document.createElement("div");
1500
- nudgeControls.style.position = "absolute";
1501
- nudgeControls.style.right = "8px";
1502
- nudgeControls.style.bottom = "8px";
1503
- nudgeControls.style.background = "rgba(15, 23, 42, 0.9)";
1504
- nudgeControls.style.border = "1px solid rgba(148, 163, 184, 0.5)";
1505
- nudgeControls.style.borderRadius = "10px";
1506
- nudgeControls.style.padding = "8px";
1507
- nudgeControls.style.display = "grid";
1508
- nudgeControls.style.gridTemplateColumns = "repeat(4, auto)";
1509
- nudgeControls.style.gap = "6px";
1510
- nudgeControls.style.zIndex = "2";
1511
- const makeButton = (glyph, ariaLabel, dx, dy, step) => {
1512
- const btn = document.createElement("button");
1513
- btn.type = "button";
1514
- btn.textContent = glyph + (step > 1 ? " " + step : "");
1515
- btn.setAttribute("aria-label", ariaLabel);
1516
- btn.style.border = "1px solid #475569";
1517
- btn.style.background = "#0f172a";
1518
- btn.style.color = "#e2e8f0";
1519
- btn.style.borderRadius = "6px";
1520
- btn.style.padding = "4px 8px";
1521
- btn.style.fontSize = "13px";
1522
- btn.style.lineHeight = "1";
1523
- btn.style.cursor = "pointer";
1524
- btn.addEventListener("click", () => {
1525
- const current = corners[activeNudgeCorner];
1526
- setCorner(activeNudgeCorner, {
1527
- x: current.x + dx * step,
1528
- y: current.y + dy * step
1529
- });
1530
- });
1531
- return btn;
1532
- };
1533
- for (const step of nudges.steps) {
1534
- nudgeControls.appendChild(makeButton("←", `Move left ${step}px`, -1, 0, step));
1535
- nudgeControls.appendChild(makeButton("→", `Move right ${step}px`, 1, 0, step));
1536
- nudgeControls.appendChild(makeButton("↑", `Move up ${step}px`, 0, -1, step));
1537
- nudgeControls.appendChild(makeButton("↓", `Move down ${step}px`, 0, 1, step));
1538
- }
1539
- container.appendChild(nudgeControls);
1540
- }
1541
- function handlePointerDown(event) {
1542
- if (isDestroyed) return;
1543
- const point = getEventCanvasPoint(event);
1544
- const hitCorner = hitTestCorner(point.x, point.y);
1545
- if (!hitCorner) return;
1546
- if (typeof event.preventDefault === "function") {
1547
- event.preventDefault();
1548
- }
1549
- activeCornerKey = hitCorner;
1550
- activeNudgeCorner = hitCorner;
1551
- dragPointerId = event.pointerId;
1552
- lastPointerPosition = point;
1553
- editorCanvas.style.cursor = "grabbing";
1554
- if (keyboardEnabled && typeof editorCanvas.focus === "function") {
1555
- try {
1556
- editorCanvas.focus({ preventScroll: true });
1557
- } catch (_) {
1558
- editorCanvas.focus();
1559
- }
1560
- }
1561
- if (editorCanvas.setPointerCapture && dragPointerId !== void 0) {
1562
- editorCanvas.setPointerCapture(dragPointerId);
1563
- }
1564
- scheduleRender();
1565
- }
1566
- function handlePointerMove(event) {
1567
- if (isDestroyed) return;
1568
- const point = getEventCanvasPoint(event);
1569
- if (!activeCornerKey) {
1570
- editorCanvas.style.cursor = hitTestCorner(point.x, point.y) ? "grab" : "crosshair";
1571
- return;
1572
- }
1573
- if (dragPointerId !== null && event.pointerId !== dragPointerId) return;
1574
- lastPointerPosition = point;
1575
- const imagePoint = viewToImage(point.x, point.y);
1576
- setCorner(activeCornerKey, imagePoint);
1577
- }
1578
- function handlePointerUp(event) {
1579
- if (!activeCornerKey) return;
1580
- if (dragPointerId !== null && event.pointerId !== dragPointerId) return;
1581
- if (editorCanvas.releasePointerCapture && dragPointerId !== null && dragPointerId !== void 0) {
1582
- try {
1583
- editorCanvas.releasePointerCapture(dragPointerId);
1584
- } catch (_) {
1585
- }
1586
- }
1587
- activeCornerKey = null;
1588
- dragPointerId = null;
1589
- lastPointerPosition = null;
1590
- editorCanvas.style.cursor = "crosshair";
1591
- scheduleRender();
1592
- }
1593
- function handleKeyDown(event) {
1594
- if (isDestroyed || !keyboardEnabled) return;
1595
- if (event.key === "Enter") {
1596
- event.preventDefault();
1597
- confirmEditor();
1598
- return;
1599
- }
1600
- if (event.key === "Escape") {
1601
- event.preventDefault();
1602
- cancelEditor();
1603
- return;
1604
- }
1605
- let dx = 0;
1606
- let dy = 0;
1607
- if (event.key === "ArrowLeft") dx = -1;
1608
- else if (event.key === "ArrowRight") dx = 1;
1609
- else if (event.key === "ArrowUp") dy = -1;
1610
- else if (event.key === "ArrowDown") dy = 1;
1611
- else return;
1612
- event.preventDefault();
1613
- const step = event.shiftKey ? nudges.steps[nudges.steps.length - 1] || 10 : 1;
1614
- const current = corners[activeNudgeCorner];
1615
- setCorner(activeNudgeCorner, {
1616
- x: current.x + dx * step,
1617
- y: current.y + dy * step
1618
- });
1619
- }
1620
- function handleMouseDown(event) {
1621
- handlePointerDown({
1622
- clientX: event.clientX,
1623
- clientY: event.clientY,
1624
- pointerId: 1
1625
- });
1626
- }
1627
- function handleMouseMove(event) {
1628
- handlePointerMove({
1629
- clientX: event.clientX,
1630
- clientY: event.clientY,
1631
- pointerId: 1
1632
- });
1633
- }
1634
- function handleTouchStart(event) {
1635
- const touch = event.touches[0];
1636
- if (!touch) return;
1637
- const point = getEventCanvasPoint(touch);
1638
- if (hitTestCorner(point.x, point.y)) {
1639
- event.preventDefault();
1640
- }
1641
- handlePointerDown({
1642
- clientX: touch.clientX,
1643
- clientY: touch.clientY,
1644
- pointerId: 2
1645
- });
1646
- }
1647
- function handleTouchMove(event) {
1648
- const touch = event.touches[0];
1649
- if (!touch) return;
1650
- event.preventDefault();
1651
- handlePointerMove({
1652
- clientX: touch.clientX,
1653
- clientY: touch.clientY,
1654
- pointerId: 2
1655
- });
1656
- }
1657
- function attachEvents() {
1658
- if (typeof runtimeGlobal.PointerEvent !== "undefined") {
1659
- editorCanvas.addEventListener("pointerdown", handlePointerDown);
1660
- editorCanvas.addEventListener("pointermove", handlePointerMove);
1661
- editorCanvas.addEventListener("pointerup", handlePointerUp);
1662
- editorCanvas.addEventListener("pointercancel", handlePointerUp);
1663
- } else {
1664
- editorCanvas.addEventListener("mousedown", handleMouseDown);
1665
- if (typeof runtimeGlobal.addEventListener === "function") {
1666
- runtimeGlobal.addEventListener("mousemove", handleMouseMove);
1667
- runtimeGlobal.addEventListener("mouseup", handlePointerUp);
1668
- }
1669
- editorCanvas.addEventListener("touchstart", handleTouchStart, { passive: false });
1670
- editorCanvas.addEventListener("touchmove", handleTouchMove, { passive: false });
1671
- editorCanvas.addEventListener("touchend", handlePointerUp);
1672
- editorCanvas.addEventListener("touchcancel", handlePointerUp);
1673
- }
1674
- if (keyboardEnabled) {
1675
- editorCanvas.addEventListener("keydown", handleKeyDown);
1676
- }
1677
- }
1678
- function detachEvents() {
1679
- editorCanvas.removeEventListener("pointerdown", handlePointerDown);
1680
- editorCanvas.removeEventListener("pointermove", handlePointerMove);
1681
- editorCanvas.removeEventListener("pointerup", handlePointerUp);
1682
- editorCanvas.removeEventListener("pointercancel", handlePointerUp);
1683
- editorCanvas.removeEventListener("mousedown", handleMouseDown);
1684
- if (typeof runtimeGlobal.removeEventListener === "function") {
1685
- runtimeGlobal.removeEventListener("mousemove", handleMouseMove);
1686
- runtimeGlobal.removeEventListener("mouseup", handlePointerUp);
1687
- }
1688
- editorCanvas.removeEventListener("touchstart", handleTouchStart);
1689
- editorCanvas.removeEventListener("touchmove", handleTouchMove);
1690
- editorCanvas.removeEventListener("touchend", handlePointerUp);
1691
- editorCanvas.removeEventListener("touchcancel", handlePointerUp);
1692
- editorCanvas.removeEventListener("keydown", handleKeyDown);
1693
- }
1694
- let dprCleanup = null;
1695
- function watchDevicePixelRatio() {
1696
- if (typeof runtimeGlobal.matchMedia !== "function") return;
1697
- if (dprCleanup) dprCleanup();
1698
- const dpr = runtimeGlobal.devicePixelRatio || 1;
1699
- const mq = runtimeGlobal.matchMedia(`(resolution: ${dpr}dppx)`);
1700
- const onChange = () => {
1701
- if (isDestroyed) return;
1702
- updateCanvasSize();
1703
- scheduleRender();
1704
- watchDevicePixelRatio();
1705
- };
1706
- if (typeof mq.addEventListener === "function") {
1707
- mq.addEventListener("change", onChange);
1708
- dprCleanup = () => mq.removeEventListener("change", onChange);
1709
- } else if (typeof mq.addListener === "function") {
1710
- mq.addListener(onChange);
1711
- dprCleanup = () => mq.removeListener(onChange);
1712
- } else {
1713
- dprCleanup = null;
1714
- }
1715
- }
1716
- function confirmEditor() {
1717
- const output = cloneCorners(corners);
1718
- if (typeof options.onConfirm === "function") {
1719
- options.onConfirm(output);
1720
- }
1721
- return output;
1722
- }
1723
- function cancelEditor() {
1724
- if (typeof options.onCancel === "function") {
1725
- options.onCancel();
1726
- }
1727
- }
1728
- const resizeObserver = typeof ResizeObserver !== "undefined" ? new ResizeObserver(() => {
1729
- if (isDestroyed) return;
1730
- const next = computeDisplaySize();
1731
- if (next.width === lastDisplayWidth && next.height === lastDisplayHeight) {
1732
- return;
1733
- }
1734
- updateCanvasSize();
1735
- scheduleRender();
1736
- }) : null;
1737
- updateCanvasSize();
1738
- buildNudgeControls();
1739
- attachEvents();
1740
- watchDevicePixelRatio();
1741
- if (resizeObserver) {
1742
- resizeObserver.observe(container);
1743
- }
1744
- render();
1745
- return {
1746
- getCorners() {
1747
- return cloneCorners(corners);
1748
- },
1749
- setCorners(nextCorners) {
1750
- if (!nextCorners || !cornersValid(nextCorners)) {
1751
- return false;
1752
- }
1753
- corners = cloneCorners(nextCorners);
1754
- emitChange();
1755
- scheduleRender();
1756
- return true;
1757
- },
1758
- reset() {
1759
- corners = cloneCorners(initialCorners);
1760
- emitChange();
1761
- scheduleRender();
1762
- },
1763
- nudge(cornerKey, dx, dy, step = 1) {
1764
- if (!cornerOrder.includes(cornerKey)) {
1765
- return false;
1766
- }
1767
- return setCorner(cornerKey, {
1768
- x: corners[cornerKey].x + dx * step,
1769
- y: corners[cornerKey].y + dy * step
1770
- });
1771
- },
1772
- confirm() {
1773
- return confirmEditor();
1774
- },
1775
- cancel() {
1776
- cancelEditor();
1777
- },
1778
- destroy() {
1779
- if (isDestroyed) return;
1780
- isDestroyed = true;
1781
- if (rafId !== null && caf) {
1782
- caf(rafId);
1783
- rafId = null;
1784
- }
1785
- if (dprCleanup) {
1786
- dprCleanup();
1787
- dprCleanup = null;
1788
- }
1789
- if (resizeObserver) {
1790
- resizeObserver.disconnect();
1791
- }
1792
- detachEvents();
1793
- if (nudgeControls && nudgeControls.parentNode) {
1794
- nudgeControls.parentNode.removeChild(nudgeControls);
1795
- }
1796
- if (editorCanvas.parentNode) {
1797
- editorCanvas.parentNode.removeChild(editorCanvas);
1798
- }
1799
- if (changedContainerPosition) {
1800
- container.style.position = restoreContainerStyle.position;
1801
- }
1802
- if (changedContainerMinHeight) {
1803
- container.style.minHeight = restoreContainerStyle.minHeight;
1804
- }
1805
- }
1806
- };
1807
- }
1808
- async function initialize() {
1809
- try {
1810
- return await initializeWasm();
1811
- } catch {
1812
- return null;
1813
- }
1814
- }
1815
- class Scanner {
1816
- constructor(options = {}) {
1817
- this.defaultOptions = {
1818
- maxProcessingDimension: 800,
1819
- mode: "detect",
1820
- output: "canvas",
1821
- ...options
1822
- };
1823
- this.initialized = false;
1824
- }
1825
- /**
1826
- * Warm up the scanner (load WASM, etc.)
1827
- */
1828
- async initialize() {
1829
- if (this.initialized) return;
1830
- try {
1831
- await initializeWasm();
1832
- } catch {
1833
- }
1834
- this.initialized = true;
1835
- }
1836
- /**
1837
- * Scan an image for a document.
1838
- * @param {HTMLImageElement|HTMLCanvasElement|ImageData} image
1839
- * @param {Object} options Override default options
1840
- */
1841
- async scan(image, options = {}) {
1842
- if (!this.initialized) await this.initialize();
1843
- const combinedOptions = { ...this.defaultOptions, ...options };
1844
- return await scanDocument(image, combinedOptions);
1845
- }
1846
- /**
1847
- * Extract a document from an image using manual corners.
1848
- * @param {HTMLImageElement|HTMLCanvasElement|ImageData} image
1849
- * @param {Object} corners
1850
- * @param {Object} options
1851
- */
1852
- async extract(image, corners, options = {}) {
1853
- if (!this.initialized) await this.initialize();
1854
- const combinedOptions = { ...this.defaultOptions, ...options };
1855
- return await extractDocument(image, corners, combinedOptions);
1856
- }
1857
- }
1858
- async function prepareScaleAndGrayscale(image, maxDimension = 800) {
1859
- let originalWidth, originalHeight;
1860
- const isImageData = image && typeof image.width === "number" && typeof image.height === "number" && image.data;
1861
- if (isImageData) {
1862
- originalWidth = image.width;
1863
- originalHeight = image.height;
1864
- } else if (image) {
1865
- originalWidth = image.width || image.naturalWidth;
1866
- originalHeight = image.height || image.naturalHeight;
1867
- } else {
1868
- throw new Error("No image provided");
1869
- }
1870
- const maxCurrentDimension = Math.max(originalWidth, originalHeight);
1871
- let targetWidth, targetHeight, scaleFactor;
1872
- if (maxCurrentDimension <= maxDimension) {
1873
- targetWidth = originalWidth;
1874
- targetHeight = originalHeight;
1875
- scaleFactor = 1;
1876
- } else {
1877
- const scale = maxDimension / maxCurrentDimension;
1878
- targetWidth = Math.round(originalWidth * scale);
1879
- targetHeight = Math.round(originalHeight * scale);
1880
- scaleFactor = 1 / scale;
1881
- }
1882
- const useOffscreen = typeof OffscreenCanvas !== "undefined";
1883
- const canvas = useOffscreen ? new OffscreenCanvas(targetWidth, targetHeight) : document.createElement("canvas");
1884
- if (!useOffscreen) {
1885
- canvas.width = targetWidth;
1886
- canvas.height = targetHeight;
1887
- }
1888
- const ctx = canvas.getContext("2d", { willReadFrequently: true });
1889
- ctx.imageSmoothingEnabled = true;
1890
- ctx.imageSmoothingQuality = "medium";
1891
- if (isImageData) {
1892
- const tempCanvas = useOffscreen ? new OffscreenCanvas(originalWidth, originalHeight) : document.createElement("canvas");
1893
- if (!useOffscreen) {
1894
- tempCanvas.width = originalWidth;
1895
- tempCanvas.height = originalHeight;
1896
- }
1897
- const tempCtx = tempCanvas.getContext("2d");
1898
- tempCtx.putImageData(image, 0, 0);
1899
- ctx.drawImage(tempCanvas, 0, 0, originalWidth, originalHeight, 0, 0, targetWidth, targetHeight);
1900
- } else {
1901
- ctx.drawImage(image, 0, 0, originalWidth, originalHeight, 0, 0, targetWidth, targetHeight);
1902
- }
1903
- const imageData = ctx.getImageData(0, 0, targetWidth, targetHeight);
1904
- const data = imageData.data;
1905
- const pixelCount = targetWidth * targetHeight;
1906
- const grayscaleData = new Uint8ClampedArray(pixelCount);
1907
- for (let i = 0, j = 0; i < data.length; i += 4, j++) {
1908
- const gray = data[i] * 54 + data[i + 1] * 183 + data[i + 2] * 19 >> 8;
1909
- grayscaleData[j] = gray;
1910
- data[i] = data[i + 1] = data[i + 2] = gray;
1911
- }
1912
- return {
1913
- grayscaleData,
1914
- imageData,
1915
- // Keep full RGBA for debug visualization
1916
- scaleFactor,
1917
- originalDimensions: { width: originalWidth, height: originalHeight },
1918
- scaledDimensions: { width: targetWidth, height: targetHeight }
1919
- };
1920
- }
1921
- function clamp01(value) {
1922
- if (value <= 0) return 0;
1923
- if (value >= 1) return 1;
1924
- return value;
1925
- }
1926
- function pointDistance(a, b) {
1927
- return Math.hypot(a.x - b.x, a.y - b.y);
1928
- }
1929
- function polygonAreaFromCorners(corners) {
1930
- if (!corners) return 0;
1931
- const points = [corners.topLeft, corners.topRight, corners.bottomRight, corners.bottomLeft];
1932
- let area = 0;
1933
- for (let i = 0; i < points.length; i++) {
1934
- const j = (i + 1) % points.length;
1935
- area += points[i].x * points[j].y - points[j].x * points[i].y;
1936
- }
1937
- return Math.abs(area) / 2;
1938
- }
1939
- function sideLengthsFromCorners(corners) {
1940
- return [
1941
- pointDistance(corners.topLeft, corners.topRight),
1942
- pointDistance(corners.topRight, corners.bottomRight),
1943
- pointDistance(corners.bottomRight, corners.bottomLeft),
1944
- pointDistance(corners.bottomLeft, corners.topLeft)
1945
- ];
1946
- }
1947
- function cornerAnglesDegrees(corners) {
1948
- const points = [corners.topLeft, corners.topRight, corners.bottomRight, corners.bottomLeft];
1949
- const angles = [];
1950
- for (let i = 0; i < points.length; i++) {
1951
- const prev = points[(i + points.length - 1) % points.length];
1952
- const curr = points[i];
1953
- const next = points[(i + 1) % points.length];
1954
- const v1x = prev.x - curr.x;
1955
- const v1y = prev.y - curr.y;
1956
- const v2x = next.x - curr.x;
1957
- const v2y = next.y - curr.y;
1958
- const dot = v1x * v2x + v1y * v2y;
1959
- const mag1 = Math.hypot(v1x, v1y);
1960
- const mag2 = Math.hypot(v2x, v2y);
1961
- const denom = Math.max(1e-6, mag1 * mag2);
1962
- const cosTheta = Math.max(-1, Math.min(1, dot / denom));
1963
- angles.push(Math.acos(cosTheta) * 180 / Math.PI);
1964
- }
1965
- return angles;
1966
- }
1967
- function computeRightAngleScore(corners) {
1968
- const angles = cornerAnglesDegrees(corners);
1969
- let total = 0;
1970
- for (const angle of angles) {
1971
- const deviation = Math.abs(angle - 90);
1972
- total += clamp01(1 - deviation / 55);
1973
- }
1974
- return total / Math.max(1, angles.length);
1975
- }
1976
- function computeOppositeSideConsistency(corners) {
1977
- const sides = sideLengthsFromCorners(corners);
1978
- const widthConsistency = Math.min(sides[0], sides[2]) / Math.max(1e-6, Math.max(sides[0], sides[2]));
1979
- const heightConsistency = Math.min(sides[1], sides[3]) / Math.max(1e-6, Math.max(sides[1], sides[3]));
1980
- return (widthConsistency + heightConsistency) / 2;
1981
- }
1982
- function compareCandidates(a, b) {
1983
- const validDelta = Number(b.isValid) - Number(a.isValid);
1984
- if (validDelta !== 0) return validDelta;
1985
- const confidenceDelta = b.confidence - a.confidence;
1986
- const nearTie = Math.abs(confidenceDelta) < 0.015;
1987
- if (!nearTie && confidenceDelta !== 0) return confidenceDelta;
1988
- const complexityA = Math.abs((a.approxCount ?? 99) - 4);
1989
- const complexityB = Math.abs((b.approxCount ?? 99) - 4);
1990
- const complexityDelta = complexityA - complexityB;
1991
- if (complexityDelta !== 0) return complexityDelta;
1992
- const angleDelta = (b.rightAngleScore ?? 0) - (a.rightAngleScore ?? 0);
1993
- if (Math.abs(angleDelta) > 1e-6) return angleDelta;
1994
- const fitErrorA = Math.abs(1 - (a.contourFitRatio ?? 1));
1995
- const fitErrorB = Math.abs(1 - (b.contourFitRatio ?? 1));
1996
- const fitDelta = fitErrorA - fitErrorB;
1997
- if (Math.abs(fitDelta) > 1e-6) return fitDelta;
1998
- return confidenceDelta || b.score - a.score || b.coverageRatio - a.coverageRatio || b.area - a.area;
1999
- }
2000
- function cornersAreFiniteAndDistinct(corners, minDistance = 6) {
2001
- const points = [corners == null ? void 0 : corners.topLeft, corners == null ? void 0 : corners.topRight, corners == null ? void 0 : corners.bottomRight, corners == null ? void 0 : corners.bottomLeft];
2002
- if (points.some((p) => !p || !Number.isFinite(p.x) || !Number.isFinite(p.y))) {
2003
- return false;
2004
- }
2005
- for (let i = 0; i < points.length; i++) {
2006
- for (let j = i + 1; j < points.length; j++) {
2007
- if (pointDistance(points[i], points[j]) < minDistance) {
2008
- return false;
2009
- }
2010
- }
2011
- }
2012
- return true;
2013
- }
2014
- function isConvexQuadrilateral(corners) {
2015
- const points = [corners.topLeft, corners.topRight, corners.bottomRight, corners.bottomLeft];
2016
- const crossSigns = [];
2017
- for (let i = 0; i < points.length; i++) {
2018
- const p0 = points[i];
2019
- const p1 = points[(i + 1) % points.length];
2020
- const p2 = points[(i + 2) % points.length];
2021
- const cross = (p1.x - p0.x) * (p2.y - p1.y) - (p1.y - p0.y) * (p2.x - p1.x);
2022
- if (Math.abs(cross) < 1e-6) {
2023
- continue;
2024
- }
2025
- crossSigns.push(Math.sign(cross));
2026
- }
2027
- if (crossSigns.length < 3) {
2028
- return false;
2029
- }
2030
- const firstSign = crossSigns[0];
2031
- return crossSigns.every((s) => s === firstSign);
2032
- }
2033
- function computeEdgeSupportScore(contour, edges, width, height) {
2034
- if (!(contour == null ? void 0 : contour.points) || contour.points.length === 0) {
2035
- return 0;
2036
- }
2037
- const sampleStep = Math.max(1, Math.floor(contour.points.length / 240));
2038
- let samples = 0;
2039
- let supported = 0;
2040
- for (let i = 0; i < contour.points.length; i += sampleStep) {
2041
- const p = contour.points[i];
2042
- const x = Math.max(0, Math.min(width - 1, p.x | 0));
2043
- const y = Math.max(0, Math.min(height - 1, p.y | 0));
2044
- samples++;
2045
- let localHit = false;
2046
- for (let oy = -1; oy <= 1 && !localHit; oy++) {
2047
- const ny = y + oy;
2048
- if (ny < 0 || ny >= height) continue;
2049
- for (let ox = -1; ox <= 1; ox++) {
2050
- const nx = x + ox;
2051
- if (nx < 0 || nx >= width) continue;
2052
- if (edges[ny * width + nx] > 0) {
2053
- localHit = true;
2054
- break;
2055
- }
2056
- }
2057
- }
2058
- if (localHit) supported++;
2059
- }
2060
- if (samples === 0) return 0;
2061
- return supported / samples;
2062
- }
2063
- function evaluateContourCandidate(contour, edges, width, height, options = {}) {
2064
- const epsilon = options.epsilon || 0.02;
2065
- const approx = approximatePolygon(contour.points, epsilon);
2066
- const approxCount = approx.length;
2067
- const corners = findCornerPoints(contour, { epsilon });
2068
- if (!corners) {
2069
- return {
2070
- contour,
2071
- corners: null,
2072
- score: 0,
2073
- confidence: 0,
2074
- isValid: false,
2075
- area: contour.area || 0,
2076
- fillRatio: 0,
2077
- coverageRatio: 0,
2078
- cornersArea: 0,
2079
- approxCount,
2080
- convex: false,
2081
- edgeSupport: 0,
2082
- aspectRatio: Infinity,
2083
- minSide: 0,
2084
- rightAngleScore: 0,
2085
- oppositeSideConsistency: 0,
2086
- contourFitRatio: 0,
2087
- contourFitScore: 0
2088
- };
2089
- }
2090
- const imageArea = width * height;
2091
- const area = contour.area || 0;
2092
- const box = contour.boundingBox || { minX: 0, minY: 0, maxX: 0, maxY: 0 };
2093
- const boxArea = Math.max(1, (box.maxX - box.minX + 1) * (box.maxY - box.minY + 1));
2094
- const fillRatio = area / boxArea;
2095
- const cornersArea = polygonAreaFromCorners(corners);
2096
- const coverageRatio = cornersArea / Math.max(1, imageArea);
2097
- const sides = sideLengthsFromCorners(corners);
2098
- const minSide = Math.min(...sides);
2099
- const avgWidth = (sides[0] + sides[2]) / 2;
2100
- const avgHeight = (sides[1] + sides[3]) / 2;
2101
- const aspectRatio = avgWidth > avgHeight ? avgWidth / Math.max(1e-6, avgHeight) : avgHeight / Math.max(1e-6, avgWidth);
2102
- const convex = isConvexQuadrilateral(corners);
2103
- const edgeSupport = computeEdgeSupportScore(contour, edges, width, height);
2104
- const rightAngleScore = computeRightAngleScore(corners);
2105
- const oppositeSideConsistency = computeOppositeSideConsistency(corners);
2106
- const contourFitRatio = area / Math.max(1, cornersArea);
2107
- const contourFitScore = clamp01(1 - Math.abs(contourFitRatio - 1) / 0.55);
2108
- const quadLikeness = approxCount === 4 ? 1 : approxCount === 5 ? 0.9 : approxCount === 6 ? 0.7 : approxCount <= 8 ? 0.5 : 0.28;
2109
- const areaScore = clamp01(area / Math.max(1, imageArea * 0.4));
2110
- const fillScore = clamp01((fillRatio - 0.08) / 0.72);
2111
- const coverageScore = clamp01((coverageRatio - 0.03) / 0.82);
2112
- const minSideRatio = options.minDocumentSideRatio !== void 0 ? options.minDocumentSideRatio : 0.06;
2113
- const minCoverage = options.minDocumentCoverageRatio !== void 0 ? options.minDocumentCoverageRatio : 0.04;
2114
- const maxAspect = options.maxDocumentAspectRatio !== void 0 ? options.maxDocumentAspectRatio : 8;
2115
- const minFillRatio = options.minDocumentFillRatio !== void 0 ? options.minDocumentFillRatio : 0.07;
2116
- const minContourFitRatio = options.minContourFitRatio !== void 0 ? options.minContourFitRatio : 0.11;
2117
- const maxContourFitRatio = options.maxContourFitRatio !== void 0 ? options.maxContourFitRatio : 1.2;
2118
- const minRightAngleScore = options.minRightAngleScore !== void 0 ? options.minRightAngleScore : 0.42;
2119
- const minOppositeSideConsistency = options.minOppositeSideConsistency !== void 0 ? options.minOppositeSideConsistency : 0.3;
2120
- const minSidePx = Math.min(width, height) * minSideRatio;
2121
- const geometryValid = cornersAreFiniteAndDistinct(corners) && convex && minSide >= minSidePx && coverageRatio >= minCoverage && aspectRatio <= maxAspect && fillRatio >= minFillRatio && contourFitRatio >= minContourFitRatio && contourFitRatio <= maxContourFitRatio && rightAngleScore >= minRightAngleScore && oppositeSideConsistency >= minOppositeSideConsistency;
2122
- const score = areaScore * 0.22 + fillScore * 0.14 + quadLikeness * 0.15 + (convex ? 1 : 0) * 0.08 + edgeSupport * 0.08 + coverageScore * 0.13 + rightAngleScore * 0.1 + oppositeSideConsistency * 0.05 + contourFitScore * 0.05;
2123
- const confidence = geometryValid ? score : score * 0.33;
2124
- return {
2125
- contour,
2126
- corners,
2127
- score,
2128
- confidence,
2129
- isValid: geometryValid,
2130
- area,
2131
- fillRatio,
2132
- coverageRatio,
2133
- cornersArea,
2134
- approxCount,
2135
- convex,
2136
- edgeSupport,
2137
- aspectRatio,
2138
- minSide,
2139
- rightAngleScore,
2140
- oppositeSideConsistency,
2141
- contourFitRatio,
2142
- contourFitScore
2143
- };
2144
- }
2145
- function selectBestContourCandidate(contours, edges, width, height, options = {}) {
2146
- const maxCandidateContours = options.maxCandidateContours || 12;
2147
- const candidates = contours.slice(0, maxCandidateContours).map((contour, index) => ({
2148
- rankByArea: index,
2149
- ...evaluateContourCandidate(contour, edges, width, height, options)
2150
- })).sort(compareCandidates);
2151
- return {
2152
- best: candidates[0] || null,
2153
- candidates
2154
- };
2155
- }
2156
- function shouldRunDetectionCascade(bestCandidate, options = {}) {
2157
- if (options.enableDetectionCascade === false) return false;
2158
- if (!bestCandidate || !bestCandidate.corners) return true;
2159
- const minConfidenceForSinglePass = options.minCascadeTriggerConfidence !== void 0 ? options.minCascadeTriggerConfidence : 0.68;
2160
- if (!bestCandidate.isValid) return true;
2161
- if (bestCandidate.confidence < minConfidenceForSinglePass) return true;
2162
- if (bestCandidate.approxCount > 5) return true;
2163
- if (bestCandidate.rightAngleScore < 0.55) return true;
2164
- if (bestCandidate.contourFitRatio < 0.14) return true;
2165
- return false;
2166
- }
2167
- function buildDetectionPassProfiles(options = {}) {
2168
- const baseKernel = options.dilationKernelSize || 3;
2169
- const baseIterations = options.dilationIterations || 1;
2170
- const baseApplyDilation = options.applyDilation !== void 0 ? options.applyDilation : true;
2171
- const profiles = [
2172
- {
2173
- name: "default",
2174
- lowThreshold: options.lowThreshold,
2175
- highThreshold: options.highThreshold,
2176
- dilationKernelSize: baseKernel,
2177
- dilationIterations: baseIterations,
2178
- applyDilation: baseApplyDilation
2179
- }
2180
- ];
2181
- if (options.enableDetectionCascade === false) {
2182
- return profiles;
2183
- }
2184
- profiles.push({
2185
- name: "connect-edges",
2186
- lowThreshold: options.lowThreshold,
2187
- highThreshold: options.highThreshold,
2188
- dilationKernelSize: Math.max(baseKernel, 5),
2189
- dilationIterations: Math.max(baseIterations, 2),
2190
- applyDilation: true
2191
- });
2192
- profiles.push({
2193
- name: "no-dilation",
2194
- lowThreshold: options.lowThreshold,
2195
- highThreshold: options.highThreshold,
2196
- dilationKernelSize: baseKernel,
2197
- dilationIterations: baseIterations,
2198
- applyDilation: false
2199
- });
2200
- if (options.lowThreshold === void 0 && options.highThreshold === void 0) {
2201
- profiles.push({
2202
- name: "fixed-mid-thresholds",
2203
- lowThreshold: 60,
2204
- highThreshold: 180,
2205
- dilationKernelSize: baseKernel,
2206
- dilationIterations: baseIterations,
2207
- applyDilation: baseApplyDilation
2208
- });
2209
- }
2210
- const deduped = [];
2211
- const seen = /* @__PURE__ */ new Set();
2212
- for (const profile of profiles) {
2213
- const key = [
2214
- profile.lowThreshold,
2215
- profile.highThreshold,
2216
- profile.dilationKernelSize,
2217
- profile.dilationIterations,
2218
- profile.applyDilation
2219
- ].join(":");
2220
- if (seen.has(key)) continue;
2221
- seen.add(key);
2222
- deduped.push(profile);
2223
- }
2224
- return deduped;
2225
- }
2226
- async function detectDocumentInternal(grayscaleData, width, height, scaleFactor, options = {}) {
2227
- const debugInfo = options.debug ? {} : { _timingsOnly: true };
2228
- const timings = [];
2229
- if (debugInfo && !debugInfo._timingsOnly) {
2230
- debugInfo.preprocessing = {
2231
- scaledDimensions: { width, height },
2232
- scaleFactor,
2233
- maxProcessingDimension: options.maxProcessingDimension || 800
2234
- };
2235
- }
2236
- const passProfiles = buildDetectionPassProfiles(options);
2237
- const passResults = [];
2238
- const runDetectionPass = async (profile, passIndex) => {
2239
- const passLabel = profile.name || `pass-${passIndex + 1}`;
2240
- const passSuffix = passIndex === 0 ? "" : ` (${passLabel})`;
2241
- const passDebug = options.debug ? {} : { _timingsOnly: true };
2242
- const edges = await cannyEdgeDetector(grayscaleData, {
2243
- width,
2244
- height,
2245
- lowThreshold: profile.lowThreshold,
2246
- highThreshold: profile.highThreshold,
2247
- dilationKernelSize: profile.dilationKernelSize,
2248
- dilationIterations: profile.dilationIterations,
2249
- applyDilation: profile.applyDilation,
2250
- debug: passDebug,
2251
- skipGrayscale: true,
2252
- useWasmBlur: true,
2253
- useWasmHysteresis: options.useWasmHysteresis,
2254
- useWasmFullCanny: options.useWasmFullCanny
2255
- });
2256
- if (passDebug.timings) {
2257
- passDebug.timings.forEach((timing) => {
2258
- if (timing.step === "Edge Detection Total") return;
2259
- timings.push({ step: `${timing.step}${passSuffix}`, ms: timing.ms });
2260
- });
2261
- }
2262
- let t0 = performance.now();
2263
- const contours = detectDocumentContour(edges, {
2264
- minArea: (options.minArea || 1e3) / (scaleFactor * scaleFactor),
2265
- width,
2266
- height
2267
- });
2268
- timings.push({ step: `Find Contours${passSuffix}`, ms: (performance.now() - t0).toFixed(2) });
2269
- t0 = performance.now();
2270
- const { best: best2, candidates: candidates2 } = selectBestContourCandidate(contours, edges, width, height, options);
2271
- timings.push({ step: `Corner Detection${passSuffix}`, ms: (performance.now() - t0).toFixed(2) });
2272
- return {
2273
- name: passLabel,
2274
- params: {
2275
- lowThreshold: profile.lowThreshold,
2276
- highThreshold: profile.highThreshold,
2277
- dilationKernelSize: profile.dilationKernelSize,
2278
- dilationIterations: profile.dilationIterations,
2279
- applyDilation: profile.applyDilation
2280
- },
2281
- contours,
2282
- best: best2,
2283
- candidates: candidates2
2284
- };
2285
- };
2286
- const primaryPass = await runDetectionPass(passProfiles[0], 0);
2287
- passResults.push(primaryPass);
2288
- if (shouldRunDetectionCascade(primaryPass.best, options)) {
2289
- for (let i = 1; i < passProfiles.length; i++) {
2290
- passResults.push(await runDetectionPass(passProfiles[i], i));
2291
- }
2292
- }
2293
- const allCandidates = [];
2294
- for (const pass of passResults) {
2295
- for (const candidate of pass.candidates) {
2296
- allCandidates.push({
2297
- ...candidate,
2298
- passName: pass.name,
2299
- passParams: pass.params
2300
- });
2301
- }
2302
- }
2303
- allCandidates.sort(compareCandidates);
2304
- const best = allCandidates[0] || null;
2305
- const candidates = allCandidates;
2306
- if (!best || !best.corners) {
2307
- console.log("No document detected");
2308
- return {
2309
- success: false,
2310
- message: "No document detected",
2311
- debug: debugInfo._timingsOnly ? null : debugInfo,
2312
- timings
2313
- };
2314
- }
2315
- if (debugInfo && !debugInfo._timingsOnly) {
2316
- debugInfo.passes = passResults.map((pass) => ({
2317
- name: pass.name,
2318
- params: pass.params,
2319
- contourCount: pass.contours.length,
2320
- bestCandidate: pass.best ? {
2321
- score: pass.best.score,
2322
- confidence: pass.best.confidence,
2323
- isValid: pass.best.isValid,
2324
- approxCount: pass.best.approxCount,
2325
- coverageRatio: pass.best.coverageRatio,
2326
- fillRatio: pass.best.fillRatio,
2327
- rightAngleScore: pass.best.rightAngleScore,
2328
- contourFitRatio: pass.best.contourFitRatio
2329
- } : null
2330
- }));
2331
- debugInfo.candidates = candidates.map((candidate) => ({
2332
- passName: candidate.passName,
2333
- rankByArea: candidate.rankByArea,
2334
- area: candidate.area,
2335
- fillRatio: candidate.fillRatio,
2336
- coverageRatio: candidate.coverageRatio,
2337
- cornersArea: candidate.cornersArea,
2338
- approxCount: candidate.approxCount,
2339
- convex: candidate.convex,
2340
- edgeSupport: candidate.edgeSupport,
2341
- aspectRatio: candidate.aspectRatio,
2342
- minSide: candidate.minSide,
2343
- rightAngleScore: candidate.rightAngleScore,
2344
- oppositeSideConsistency: candidate.oppositeSideConsistency,
2345
- contourFitRatio: candidate.contourFitRatio,
2346
- contourFitScore: candidate.contourFitScore,
2347
- score: candidate.score,
2348
- confidence: candidate.confidence,
2349
- isValid: candidate.isValid
2350
- }));
2351
- debugInfo.selectedCandidate = candidates[0] ? {
2352
- passName: candidates[0].passName,
2353
- rankByArea: candidates[0].rankByArea,
2354
- area: candidates[0].area,
2355
- fillRatio: candidates[0].fillRatio,
2356
- coverageRatio: candidates[0].coverageRatio,
2357
- approxCount: candidates[0].approxCount,
2358
- edgeSupport: candidates[0].edgeSupport,
2359
- aspectRatio: candidates[0].aspectRatio,
2360
- minSide: candidates[0].minSide,
2361
- rightAngleScore: candidates[0].rightAngleScore,
2362
- oppositeSideConsistency: candidates[0].oppositeSideConsistency,
2363
- contourFitRatio: candidates[0].contourFitRatio,
2364
- score: candidates[0].score,
2365
- confidence: candidates[0].confidence,
2366
- isValid: candidates[0].isValid
2367
- } : null;
2368
- }
2369
- const cornerPoints = best.corners;
2370
- const documentContour = best.contour;
2371
- let finalCorners = cornerPoints;
2372
- if (scaleFactor !== 1) {
2373
- finalCorners = {
2374
- topLeft: { x: cornerPoints.topLeft.x * scaleFactor, y: cornerPoints.topLeft.y * scaleFactor },
2375
- topRight: { x: cornerPoints.topRight.x * scaleFactor, y: cornerPoints.topRight.y * scaleFactor },
2376
- bottomRight: { x: cornerPoints.bottomRight.x * scaleFactor, y: cornerPoints.bottomRight.y * scaleFactor },
2377
- bottomLeft: { x: cornerPoints.bottomLeft.x * scaleFactor, y: cornerPoints.bottomLeft.y * scaleFactor }
2378
- };
2379
- }
2380
- return {
2381
- success: true,
2382
- contour: documentContour,
2383
- corners: finalCorners,
2384
- confidence: best.confidence,
2385
- debug: debugInfo._timingsOnly ? null : debugInfo,
2386
- timings
2387
- };
2388
- }
2389
- function getPerspectiveTransform(srcPoints, dstPoints) {
2390
- function buildMatrix(points) {
2391
- const matrix2 = [];
2392
- for (let i = 0; i < 4; i++) {
2393
- const [x, y] = points[i];
2394
- matrix2.push([x, y, 1, 0, 0, 0, -x * dstPoints[i][0], -y * dstPoints[i][0]]);
2395
- matrix2.push([0, 0, 0, x, y, 1, -x * dstPoints[i][1], -y * dstPoints[i][1]]);
2396
- }
2397
- return matrix2;
2398
- }
2399
- const A = buildMatrix(srcPoints);
2400
- const b = [
2401
- dstPoints[0][0],
2402
- dstPoints[0][1],
2403
- dstPoints[1][0],
2404
- dstPoints[1][1],
2405
- dstPoints[2][0],
2406
- dstPoints[2][1],
2407
- dstPoints[3][0],
2408
- dstPoints[3][1]
2409
- ];
2410
- function solve(A2, b2) {
2411
- const m = A2.length;
2412
- const n = A2[0].length;
2413
- const M = A2.map((row) => row.slice());
2414
- const B = b2.slice();
2415
- for (let i = 0; i < n; i++) {
2416
- let maxRow = i;
2417
- for (let k = i + 1; k < m; k++) {
2418
- if (Math.abs(M[k][i]) > Math.abs(M[maxRow][i])) maxRow = k;
2419
- }
2420
- [M[i], M[maxRow]] = [M[maxRow], M[i]];
2421
- [B[i], B[maxRow]] = [B[maxRow], B[i]];
2422
- for (let k = i + 1; k < m; k++) {
2423
- const c = M[k][i] / M[i][i];
2424
- for (let j = i; j < n; j++) {
2425
- M[k][j] -= c * M[i][j];
2426
- }
2427
- B[k] -= c * B[i];
2428
- }
2429
- }
2430
- const x = new Array(n);
2431
- for (let i = n - 1; i >= 0; i--) {
2432
- let sum = B[i];
2433
- for (let j = i + 1; j < n; j++) {
2434
- sum -= M[i][j] * x[j];
2435
- }
2436
- x[i] = sum / M[i][i];
2437
- }
2438
- return x;
2439
- }
2440
- const h = solve(A, b);
2441
- const matrix = [
2442
- [h[0], h[1], h[2]],
2443
- [h[3], h[4], h[5]],
2444
- [h[6], h[7], 1]
2445
- ];
2446
- return matrix;
2447
- }
2448
- function unwarpImage(ctx, image, corners) {
2449
- const { topLeft, topRight, bottomRight, bottomLeft } = corners;
2450
- const widthA = Math.hypot(bottomRight.x - bottomLeft.x, bottomRight.y - bottomLeft.y);
2451
- const widthB = Math.hypot(topRight.x - topLeft.x, topRight.y - topLeft.y);
2452
- const maxWidth = Math.round(Math.max(widthA, widthB));
2453
- const heightA = Math.hypot(topRight.x - bottomRight.x, topRight.y - bottomRight.y);
2454
- const heightB = Math.hypot(topLeft.x - bottomLeft.x, topLeft.y - bottomLeft.y);
2455
- const maxHeight = Math.round(Math.max(heightA, heightB));
2456
- ctx.canvas.width = maxWidth;
2457
- ctx.canvas.height = maxHeight;
2458
- const srcPoints = [
2459
- [topLeft.x, topLeft.y],
2460
- [topRight.x, topRight.y],
2461
- [bottomRight.x, bottomRight.y],
2462
- [bottomLeft.x, bottomLeft.y]
2463
- ];
2464
- const dstPoints = [
2465
- [0, 0],
2466
- [maxWidth - 1, 0],
2467
- [maxWidth - 1, maxHeight - 1],
2468
- [0, maxHeight - 1]
2469
- ];
2470
- const perspectiveMatrix = getPerspectiveTransform(srcPoints, dstPoints);
2471
- warpTransform(ctx, image, perspectiveMatrix, maxWidth, maxHeight);
2472
- }
2473
- function invert3x3(m) {
2474
- const a = m[0][0], b = m[0][1], c = m[0][2];
2475
- const d = m[1][0], e = m[1][1], f = m[1][2];
2476
- const g = m[2][0], h = m[2][1], i = m[2][2];
2477
- const A = e * i - f * h;
2478
- const B = -(d * i - f * g);
2479
- const C = d * h - e * g;
2480
- const D = -(b * i - c * h);
2481
- const E = a * i - c * g;
2482
- const F = -(a * h - b * g);
2483
- const G = b * f - c * e;
2484
- const H = -(a * f - c * d);
2485
- const I = a * e - b * d;
2486
- const det = a * A + b * B + c * C;
2487
- if (det === 0) throw new Error("Singular matrix");
2488
- return [
2489
- [A / det, D / det, G / det],
2490
- [B / det, E / det, H / det],
2491
- [C / det, F / det, I / det]
2492
- ];
2493
- }
2494
- function warpTransform(ctx, image, matrix, outWidth, outHeight) {
2495
- const isImageData = image && typeof image.width === "number" && typeof image.height === "number" && image.data;
2496
- const srcWidth = image.width || image.naturalWidth;
2497
- const srcHeight = image.height || image.naturalHeight;
2498
- const srcCanvas = document.createElement("canvas");
2499
- srcCanvas.width = srcWidth;
2500
- srcCanvas.height = srcHeight;
2501
- const srcCtx = srcCanvas.getContext("2d", { willReadFrequently: true });
2502
- if (isImageData) {
2503
- srcCtx.putImageData(image, 0, 0);
2504
- } else {
2505
- srcCtx.drawImage(image, 0, 0, srcWidth, srcHeight);
2506
- }
2507
- const srcData = srcCtx.getImageData(0, 0, srcWidth, srcHeight).data;
2508
- const inv = invert3x3(matrix);
2509
- const i00 = inv[0][0], i01 = inv[0][1], i02 = inv[0][2];
2510
- const i10 = inv[1][0], i11 = inv[1][1], i12 = inv[1][2];
2511
- const i20 = inv[2][0], i21 = inv[2][1], i22 = inv[2][2];
2512
- const outData = ctx.createImageData(outWidth, outHeight);
2513
- const dst = outData.data;
2514
- const maxSrcX = srcWidth - 1;
2515
- const maxSrcY = srcHeight - 1;
2516
- for (let oy = 0; oy < outHeight; oy++) {
2517
- const iy1 = i01 * oy + i02;
2518
- const iy2 = i11 * oy + i12;
2519
- const iy3 = i21 * oy + i22;
2520
- for (let ox = 0; ox < outWidth; ox++) {
2521
- const w = i20 * ox + iy3;
2522
- const invW = 1 / w;
2523
- const sx = (i00 * ox + iy1) * invW;
2524
- const sy = (i10 * ox + iy2) * invW;
2525
- const csx = sx < 0 ? 0 : sx > maxSrcX ? maxSrcX : sx;
2526
- const csy = sy < 0 ? 0 : sy > maxSrcY ? maxSrcY : sy;
2527
- const x0 = csx | 0;
2528
- const y0 = csy | 0;
2529
- const x1 = x0 < maxSrcX ? x0 + 1 : x0;
2530
- const y1 = y0 < maxSrcY ? y0 + 1 : y0;
2531
- const fx = csx - x0;
2532
- const fy = csy - y0;
2533
- const fx1 = 1 - fx;
2534
- const fy1 = 1 - fy;
2535
- const w00 = fx1 * fy1;
2536
- const w10 = fx * fy1;
2537
- const w01 = fx1 * fy;
2538
- const w11 = fx * fy;
2539
- const idx00 = y0 * srcWidth + x0 << 2;
2540
- const idx10 = y0 * srcWidth + x1 << 2;
2541
- const idx01 = y1 * srcWidth + x0 << 2;
2542
- const idx11 = y1 * srcWidth + x1 << 2;
2543
- const di = oy * outWidth + ox << 2;
2544
- dst[di] = srcData[idx00] * w00 + srcData[idx10] * w10 + srcData[idx01] * w01 + srcData[idx11] * w11 + 0.5 | 0;
2545
- dst[di + 1] = srcData[idx00 + 1] * w00 + srcData[idx10 + 1] * w10 + srcData[idx01 + 1] * w01 + srcData[idx11 + 1] * w11 + 0.5 | 0;
2546
- dst[di + 2] = srcData[idx00 + 2] * w00 + srcData[idx10 + 2] * w10 + srcData[idx01 + 2] * w01 + srcData[idx11 + 2] * w11 + 0.5 | 0;
2547
- dst[di + 3] = 255;
2548
- }
2549
- }
2550
- ctx.putImageData(outData, 0, 0);
2551
- }
2552
- async function extractDocument(image, corners, options = {}) {
2553
- const outputType = options.output || "canvas";
2554
- if (!corners || !corners.topLeft || !corners.topRight || !corners.bottomRight || !corners.bottomLeft) {
2555
- return {
2556
- output: null,
2557
- corners: null,
2558
- success: false,
2559
- message: "Invalid corner points provided"
2560
- };
2561
- }
2562
- try {
2563
- const resultCanvas = document.createElement("canvas");
2564
- const ctx = resultCanvas.getContext("2d");
2565
- unwarpImage(ctx, image, corners);
2566
- let output;
2567
- if (outputType === "canvas") {
2568
- output = resultCanvas;
2569
- } else if (outputType === "imagedata") {
2570
- output = resultCanvas.getContext("2d").getImageData(0, 0, resultCanvas.width, resultCanvas.height);
2571
- } else if (outputType === "dataurl") {
2572
- output = resultCanvas.toDataURL();
2573
- } else {
2574
- output = resultCanvas;
2575
- }
2576
- return {
2577
- output,
2578
- corners,
2579
- success: true,
2580
- message: "Document extracted successfully"
2581
- };
2582
- } catch (error) {
2583
- return {
2584
- output: null,
2585
- corners,
2586
- success: false,
2587
- message: `Extraction failed: ${error.message}`
2588
- };
2589
- }
2590
- }
2591
- async function scanDocument(image, options = {}) {
2592
- const timings = [];
2593
- const totalStart = performance.now();
2594
- const mode = options.mode || "detect";
2595
- const outputType = options.output || "canvas";
2596
- !!options.debug;
2597
- const maxProcessingDimension = options.maxProcessingDimension || 800;
2598
- let t0 = performance.now();
2599
- const { grayscaleData, imageData, scaleFactor, originalDimensions, scaledDimensions } = await prepareScaleAndGrayscale(image, maxProcessingDimension);
2600
- timings.push({ step: "Image Prep + Scale + Gray", ms: (performance.now() - t0).toFixed(2) });
2601
- const detection = await detectDocumentInternal(
2602
- grayscaleData,
2603
- scaledDimensions.width,
2604
- scaledDimensions.height,
2605
- scaleFactor,
2606
- options
2607
- );
2608
- if (detection.timings) {
2609
- detection.timings.forEach((t) => timings.push(t));
2610
- }
2611
- if (!detection.success) {
2612
- const totalEnd2 = performance.now();
2613
- timings.unshift({ step: "Total", ms: (totalEnd2 - totalStart).toFixed(2) });
2614
- console.table(timings);
2615
- return {
2616
- output: null,
2617
- corners: null,
2618
- contour: null,
2619
- confidence: detection.confidence || null,
2620
- debug: detection.debug,
2621
- success: false,
2622
- message: detection.message || "No document detected",
2623
- timings
2624
- };
2625
- }
2626
- let resultCanvas;
2627
- let output;
2628
- if (mode === "detect") {
2629
- output = null;
2630
- } else if (mode === "extract") {
2631
- t0 = performance.now();
2632
- resultCanvas = document.createElement("canvas");
2633
- const ctx = resultCanvas.getContext("2d");
2634
- unwarpImage(ctx, image, detection.corners);
2635
- timings.push({ step: "Perspective Transform", ms: (performance.now() - t0).toFixed(2) });
2636
- }
2637
- if (mode !== "detect" && resultCanvas) {
2638
- t0 = performance.now();
2639
- if (outputType === "canvas") {
2640
- output = resultCanvas;
2641
- } else if (outputType === "imagedata") {
2642
- output = resultCanvas.getContext("2d").getImageData(0, 0, resultCanvas.width, resultCanvas.height);
2643
- } else if (outputType === "dataurl") {
2644
- output = resultCanvas.toDataURL();
2645
- } else {
2646
- output = resultCanvas;
2647
- }
2648
- timings.push({ step: "Output Conversion", ms: (performance.now() - t0).toFixed(2) });
2649
- }
2650
- const totalEnd = performance.now();
2651
- timings.unshift({ step: "Total", ms: (totalEnd - totalStart).toFixed(2) });
2652
- console.table(timings);
2653
- return {
2654
- output,
2655
- corners: detection.corners,
2656
- contour: detection.contour,
2657
- confidence: detection.confidence || null,
2658
- debug: detection.debug,
2659
- success: true,
2660
- message: "Document detected",
2661
- timings
2662
- };
2663
- }
2664
- export {
2665
- Scanner,
2666
- createCornerEditor,
2667
- extractDocument,
2668
- initialize,
2669
- scanDocument
2670
- };
1
+ var A,I=1e3,g=10;function B(A,B={}){const t=B.width||Math.sqrt(A.length),i=B.height||A.length/t,E=void 0!==B.mode?B.mode:1,e=void 0!==B.method?B.method:2,o=B.minArea||I,n=t+2,a=i+2,s=new Int32Array(n*a);for(let I=0;I<i;I++)for(let g=0;g<t;g++)A[I*t+g]>0&&(s[(I+1)*n+(g+1)]=1);const c=[];let r=2;for(let I=1;I<=i;I++)for(let A=1;A<=t;A++){const B=s[I*n+A],t=s[I*n+(A-1)];let i=null,o=!1,h=-1;if(1===B&&0===t?(o=!0,i={x:A,y:I},h=2):0===B&&t>=1&&-1!==t&&1===t&&(o=!1,i={x:A-1,y:I},h=6),i){if(0===E&&!o){s[i.y*n+i.x]=-1;continue}const A=r++,I=C(s,n,a,i,h,A);if(I&&I.length>0){let B=I;2===e&&(B=Q(I));const C=B.map(A=>({x:A.x-1,y:A.y-1}));if(C.length>=(2===e?4:g)){const I={id:A,points:C,isOuter:o};c.push(I)}}else 1===s[i.y*n+i.x]&&(s[i.y*n+i.x]=A)}}c.forEach(A=>{A.area=function(A){let I=0;const g=A.length;if(g<3)return 0;for(let B=0;B<g;B++){const C=(B+1)%g;I+=A[B].x*A[C].y,I-=A[C].x*A[B].y}return Math.abs(I)/2}(A.points),A.boundingBox=function(A){if(0===A.length)return{minX:0,minY:0,maxX:0,maxY:0};let I=A[0].x,g=A[0].y,B=A[0].x,C=A[0].y;for(let Q=1;Q<A.length;Q++){const t=A[Q];I=Math.min(I,t.x),g=Math.min(g,t.y),B=Math.max(B,t.x),C=Math.max(C,t.y)}return{minX:I,minY:g,maxX:B,maxY:C}}(A.points)});const h=c.filter(A=>A.area>=o);return h.sort((A,I)=>I.area-A.area),B.debug&&(B.debug.labels=s,B.debug.rawContours=c,B.debug.finalContours=h),h}function C(A,I,g,B,C,Q){const t=[],i=/* @__PURE__ */new Set;let E=B.x,e=B.y;const o=E,n=e;let a=-1;A[n*I+o]=Q;let s=0;const c=I*g,r=[0,1,1,1,0,-1,-1,-1],h=[-1,-1,0,1,1,1,0,-1];for(;s++<c;){let B;if(-1===a){let Q=!1;for(let t=0;t<8;t++){B=C+t&7;const i=E+r[B],o=e+h[B];if(i>=0&&i<I&&o>=0&&o<g&&A[o*I+i]>0){Q=!0;break}}if(!Q)return null}else B=a+2&7;let s=-1,c=-1;for(let C=0;C<8;C++){const Q=B+C&7,t=E+r[Q],i=e+h[Q];if(t>=0&&t<I&&i>=0&&i<g&&A[i*I+t]>0){s=t,c=i,a=Q+4&7;break}}if(-1===s){0===t.length&&t.push({x:E,y:e});break}const l=e*I+E;if(i.has(l))return t;t.push({x:E,y:e}),i.add(l);const y=c*I+s;if(1===A[y]&&(A[y]=Q),E=s,e=c,E===o&&e===n)break}return s>=c?null:t}function Q(A){const I=A.length;if(I<=2)return A;const g=[],B=A[I-1],C=A[0];let Q=B,t=C,i=A[1],E=t.x-Q.x,e=t.y-Q.y,o=i.x-t.x,n=i.y-t.y;E*n!==e*o&&g.push(t);for(let a=1;a<I-1;a++)Q=A[a-1],t=A[a],i=A[a+1],E=t.x-Q.x,e=t.y-Q.y,o=i.x-t.x,n=i.y-t.y,E*n!==e*o&&g.push(t);if(Q=A[I-2],t=B,i=C,E=t.x-Q.x,e=t.y-Q.y,o=i.x-t.x,n=i.y-t.y,E*n!==e*o&&g.push(t),0===g.length){if(1===I)return[A[0]];if(2===I)return A;let g=0,B=1;const Q=C.x,t=C.y;for(let C=1;C<I;C++){const I=A[C],i=I.x-Q,E=I.y-t,e=i*i+E*E;e>g&&(g=e,B=C)}return[C,A[B]]}return g}function t(A,I=1){if(A.length<=2)return A;let g=0,B=0;const C=A[0],Q=A[A.length-1];for(let t=1;t<A.length-1;t++){const I=i(A[t],C,Q);I>g&&(g=I,B=t)}if(g>I){const g=t(A.slice(0,B+1),I),C=t(A.slice(B),I);return g.slice(0,-1).concat(C)}return[C,Q]}function i(A,I,g){const B=g.x-I.x,C=g.y-I.y,Q=B*B+C*C;if(0===Q)return Math.sqrt(Math.pow(A.x-I.x,2)+Math.pow(A.y-I.y,2));const t=((A.x-I.x)*B+(A.y-I.y)*C)/Q;let i,E;t<0?(i=I.x,E=I.y):t>1?(i=g.x,E=g.y):(i=I.x+t*B,E=I.y+t*C);const e=A.x-i,o=A.y-E;return Math.sqrt(e*e+o*o)}function E(A,I=.02){return t(A,I*function(A){let I=0;const g=A.length;if(g<2)return 0;for(let B=0;B<g;B++){const C=(B+1)%g,Q=A[B].x-A[C].x,t=A[B].y-A[C].y;I+=Math.sqrt(Q*Q+t*t)}return I}(A))}function e(A,I){return Math.hypot(I.x-A.x,I.y-A.y)}function o(A){let I=0,g=0;for(const B of A)I+=B.x,g+=B.y;return{x:I/A.length,y:g/A.length}}function n(A,I={}){if(!A||!A.points||A.points.length<4)return null;const g=I.epsilon||.02,B=function(A,I){if(!A||A.length<4)return null;const g=[I,1.3*I,1.6*I,2*I],B=/* @__PURE__ */new Set;for(const C of g){const I=Math.min(.12,C),g=I.toFixed(4);if(B.has(g))continue;B.add(g);const Q=a(E(A,I));if(Q&&4===Q.length)return Q}return null}(A.points,g);let C;return B&&4===B.length?C=function(A){if(4!==A.length)return null;const I=o(A),g=[...A].sort((A,g)=>Math.atan2(A.y-I.y,A.x-I.x)-Math.atan2(g.y-I.y,g.x-I.x));let B=1/0,C=0;for(let t=0;t<4;t++){const A=g[t].x+g[t].y;A<B&&(B=A,C=t)}const Q=[g[C],g[(C+1)%4],g[(C+2)%4],g[(C+3)%4]];return{topLeft:Q[0],topRight:Q[1],bottomRight:Q[2],bottomLeft:Q[3]}}(B):(C=function(A){if(!A||A.length<4)return null;const I=o(A);let g=null,B=null,C=null,Q=null,t=0,i=0,E=0,n=0;for(const o of A){const A=e(o,I);o.x<I.x&&o.y<I.y?A>t&&(g=o,t=A):o.x>I.x&&o.y<I.y?A>i&&(B=o,i=A):o.x>I.x&&o.y>I.y?A>E&&(C=o,E=A):o.x<I.x&&o.y>I.y&&A>n&&(Q=o,n=A)}return g&&B&&C&&Q?{topLeft:g,topRight:B,bottomRight:C,bottomLeft:Q}:null}(A.points),C&&C.topLeft&&C.topRight&&C.bottomRight&&C.bottomLeft||(C=function(A){if(!A||0===A.length)return null;let I=A[0],g=A[0],B=A[0],C=A[0],Q=I.x+I.y,t=g.x-g.y,i=B.x+B.y,E=C.x-C.y;for(let e=1;e<A.length;e++){const o=A[e],n=o.x+o.y,a=o.x-o.y;n<Q&&(Q=n,I=o),n>i&&(i=n,B=o),a>t&&(t=a,g=o),a<E&&(E=a,C=o)}return{topLeft:I,topRight:g,bottomRight:B,bottomLeft:C}}(A.points))),C&&C.topLeft&&C.topRight&&C.bottomRight&&C.bottomLeft?C:null}function a(A){if(!A||A.length<4)return null;if(4===A.length)return A;if(A.length>8)return null;let I=function(A,I){if(!A||0===A.length)return[];const g=[];for(const B of A)(0===g.length||e(B,g[g.length-1])>I)&&g.push(B);g.length>2&&e(g[0],g[g.length-1])<=I&&g.pop();return g}(A,6);return 4===I.length?I:(I.length,null)}var s=null;function c(){return null!==s&&0!==s.byteLength||(s=new Uint8Array(A.memory.buffer)),s}var r=0;function h(A,I){const g=I(1*A.length,1)>>>0;return c().set(A,g/1),r=A.length,g}function l(A,I){return A>>>=0,c().subarray(A/1,A/1+I)}var y=null;function D(){return null!==y&&0!==y.byteLength||(y=new Float32Array(A.memory.buffer)),y}function w(I,g,B,C,Q){const t=function(A,I){const g=I(4*A.length,4)>>>0;return D().set(A,g/4),r=A.length,g}(I,A.__wbindgen_malloc),i=r,E=A.hysteresis_thresholding(t,i,g,B,C,Q);var e=l(E[0],E[1]).slice();return A.__wbindgen_free(E[0],1*E[1],1),e}var d=null;function f(I,g){const B=g(2*I.length,2)>>>0;return(null!==d&&0!==d.byteLength||(d=new Uint16Array(A.memory.buffer)),d).set(I,B/2),r=I.length,B}function u(I,g,B,C,Q){const t=f(I,A.__wbindgen_malloc),i=r,E=f(g,A.__wbindgen_malloc),e=r,o=A.non_maximum_suppression(t,i,E,e,B,C,Q);var n,a,s=(n=o[0],a=o[1],n>>>=0,D().subarray(n/4,n/4+a)).slice();return A.__wbindgen_free(o[0],4*o[1],4),s}async function m(I){if(void 0!==A)return A;if(void 0!==I&&Object.getPrototypeOf(I)===Object.prototype&&({module_or_path:I}=I),void 0===I)throw new Error("scanic: WASM module bytes must be passed to init().");const g=function(){const I={wbg:{}};return I.wbg.__wbindgen_init_externref_table=function(){const I=A.__wbindgen_export_0,g=I.grow(4);I.set(0,void 0),I.set(g+0,void 0),I.set(g+1,null),I.set(g+2,!0),I.set(g+3,!1)},I}();("string"==typeof I||"function"==typeof Request&&I instanceof Request||"function"==typeof URL&&I instanceof URL)&&(I=fetch(I));const{instance:B,module:C}=await async function(A,I){if("function"==typeof Response&&A instanceof Response){if("function"==typeof WebAssembly.instantiateStreaming)try{return await WebAssembly.instantiateStreaming(A,I)}catch(g){if("application/wasm"==A.headers.get("Content-Type"))throw g}const B=await A.arrayBuffer();return await WebAssembly.instantiate(B,I)}{const g=await WebAssembly.instantiate(A,I);return g instanceof WebAssembly.Instance?{instance:g,module:A}:g}}(await I,g);return function(I,g){return A=I.exports,m.__wbindgen_wasm_module=g,y=null,d=null,s=null,A.__wbindgen_start(),A}(B,C)}var N=null,R=!1,F=null;function p(A){if("function"==typeof atob){const I=atob(A),g=new Uint8Array(I.length);for(let A=0;A<I.length;A++)g[A]=I.charCodeAt(A);return g}return Uint8Array.from(Buffer.from(A,"base64"))}async function J(){if(!function(){if(null!==F)return F;let A=!1;if("object"==typeof WebAssembly&&"function"==typeof WebAssembly.validate){const I=new Uint8Array([0,97,115,109,1,0,0,0,1,5,1,96,0,1,123,3,2,1,0,10,10,1,8,0,65,0,253,15,253,98,11]),g=new Uint8Array([0,97,115,109,1,0,0,0,1,5,1,96,0,1,111,3,2,1,0,10,6,1,4,0,208,111,11]);try{A=WebAssembly.validate(I)&&WebAssembly.validate(g)}catch{A=!1}}return F=A,A}())throw new Error("scanic: WebAssembly features required by the WASM module (reference-types, SIMD) are unavailable in this engine; using the JavaScript fallback.");return await m({module_or_path:p("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JAAgAiABIAJqIgFLBEBBAEEAEC0ACyADQQRqIAAoAgAiAiAAKAIEQQggASACQQF0IgIgASACSxsiASABQQhNGyIBQQFBARARIAMoAgRBAUYEQCADKAIIIAMoAgwQLQALIAMoAgghAiAAIAE2AgAgACACNgIEIANBEGokAAt5AQF/IwBBIGsiAiQAAn8gACgCAEGAgICAeEcEQCABIAAoAgQgACgCCBAwDAELIAJBEGogACgCDCgCACIAQQhqKQIANwMAIAJBGGogAEEQaikCADcDACACIAApAgA3AwggASgCACABKAIEIAJBCGoQBwsgAkEgaiQAC2kBA38jAEEQayIBJAAgAUEEaiAAKAIAIgIgACgCBEEEIAJBAXQiAiACQQRNGyICQQRBBBARIAEoAgRBAUYEQCABKAIIIAEoAgwQLQALIAEoAgghAyAAIAI2AgAgACADNgIEIAFBEGokAAtpAQN/IwBBEGsiASQAIAFBBGogACgCACICIAAoAgRBBCACQQF0IgIgAkEETRsiAkEEQQgQESABKAIEQQFGBEAgASgCCCABKAIMEC0ACyABKAIIIQMgACACNgIAIAAgAzYCBCABQRBqJAALaQEDfyMAQRBrIgEkACABQQRqIAAoAgAiAiAAKAIEQQQgAkEBdCICIAJBBE0bIgJBAkECEBEgASgCBEEBRgRAIAEoAgggASgCDBAtAAsgASgCCCEDIAAgAjYCACAAIAM2AgQgAUEQaiQACxIAIwBBMGsiACQAIABBMGokAAtoAgF/AX4jAEEwayIDJAAgAyABNgIEIAMgADYCACADQQI2AgwgA0Hkl8AANgIIIANCAjcCFCADQoCAgIAwIgQgA62ENwMoIAMgBCADQQRqrYQ3AyAgAyADQSBqNgIQIANBCGogAhAmAAtHAQF/IAAoAgAgACgCCCIDayACSQRAIAAgAyACEBIgACgCCCEDCyACBEAgACgCBCADaiABIAL8CgAACyAAIAIgA2o2AghBAAtEAQJ/IAEoAgQhAiABKAIAIQNBCEEEEDUiAUUEQEEEQQgQOQALIAEgAjYCBCABIAM2AgAgAEHcksAANgIEIAAgATYCAAvGAgACQCAAIAJNBEAgACABTSABIAJLcg0BIwBBMGsiAiQAIAIgATYCBCACIAA2AgAgAkECNgIMIAJB2JbAADYCCCACQgI3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IAAAABwAEAAMAAAAEgAAABcAAAAcABAADAAAABcAAAAZAAAAHAAQAAwAAAAUAAAAGQAAABwAEAAMAAAAUQAAAB4AAAAcABAADAAAAFIAAAAeAAAATGF6eSBpbnN0YW5jZSBoYXMgcHJldmlvdXNseSBiZWVuIHBvaXNvbmVkAAA8CBAAKgAAAEgBEABaAAAACAMAABkAAAByZWVudHJhbnQgaW5pdAAAgAgQAA4AAABIARAAWgAAAHoCAAANAAAAfP2LMlfmV/kC30S/40jnr21dy9YsUOtjeEGmV3Ebi7ltZW1vcnkgYWxsb2NhdGlvbiBvZiAgYnl0ZXMgZmFpbGVkAADICBAAFQAAAN0IEAANAAAALwEQABgAAABkAQAACQAAAAQAAAAMAAAABAAAAAUAAAAGAAAABwAAAAAAAAAIAAAABAAAAAgAAAAJAAAACgAAAAsAAAAMAAAAEAAAAAQAAAANAAAADgAAAA8AAAAQAAAAAAAAAAgAAAAEAAAAEQAAAGFzc2VydGlvbiBmYWlsZWQ6IHBzaXplID49IHNpemUgKyBtaW5fb3ZlcmhlYWQAAAQBEAAqAAAAsQQAAAkAAABhc3NlcnRpb24gZmFpbGVkOiBwc2l6ZSA8PSBzaXplICsgbWF4X292ZXJoZWFkAAAEARAAKgAAALcEAAANAAAABAAAAAwAAAAEAAAAEgAAAGNhcGFjaXR5IG92ZXJmbG93AAAA/AkQABEAAADjABAAIAAAABwAAAAFAAAAMDAwMTAyMDMwNDA1MDYwNzA4MDkxMDExMTIxMzE0MTUxNjE3MTgxOTIwMjEyMjIzMjQyNTI2MjcyODI5MzAzMTMyMzMzNDM1MzYzNzM4Mzk0MDQxNDI0MzQ0NDU0NjQ3NDg0OTUwNTE1MjUzNTQ1NTU2NTc1ODU5NjA2MTYyNjM2NDY1NjY2NzY4Njk3MDcxNzI3Mzc0NzU3Njc3Nzg3OTgwODE4MjgzODQ4NTg2ODc4ODg5OTA5MTkyOTM5NDk1OTY5Nzk4OTlyYW5nZSBlbmQgaW5kZXggIG91dCBvZiByYW5nZSBmb3Igc2xpY2Ugb2YgbGVuZ3RoIAAA8AoQABAAAAAACxAAIgAAAHNsaWNlIGluZGV4IHN0YXJ0cyBhdCAgYnV0IGVuZHMgYXQgADQLEAAWAAAASgsQAA0AAAByYW5nZSBzdGFydCBpbmRleCAAAGgLEAASAAAAAAsQACIAAABhdHRlbXB0IHRvIGRpdmlkZSBieSB6ZXJvAAAAjAsQABkAAABpbmRleCBvdXQgb2YgYm91bmRzOiB0aGUgbGVuIGlzICBidXQgdGhlIGluZGV4IGlzIAAAsAsQACAAAADQCxAAEgAAAAAAAD8AAAC/AEGUmMAACwEBAHwJcHJvZHVjZXJzAghsYW5ndWFnZQEEUnVzdAAMcHJvY2Vzc2VkLWJ5AwVydXN0Yx0xLjkyLjAgKGRlZDVjMDZjZiAyMDI1LTEyLTA4KQZ3YWxydXMGMC4yMy4zDHdhc20tYmluZGdlbhMwLjIuMTAwICgyNDA1ZWMyYjQpAHQPdGFyZ2V0X2ZlYXR1cmVzBysPbXV0YWJsZS1nbG9iYWxzKxNub250cmFwcGluZy1mcHRvaW50KwdzaW1kMTI4KwtidWxrLW1lbW9yeSsIc2lnbi1leHQrD3JlZmVyZW5jZS10eXBlcysKbXVsdGl2YWx1ZQ==")})}function G(){return N||(N=J().catch(A=>{throw N=null,A})),N}function k(A,I,g,B=5,C=0){0===C&&(C=.3*(.5*(B-1)-1)+.8);const Q=Math.floor(B/2),t=function(A,I){const g=new Float32Array(A),B=Math.floor(A/2);let C=0;for(let Q=0;Q<A;Q++){const A=Q-B;g[Q]=Math.exp(-A*A/(2*I*I)),C+=g[Q]}for(let Q=0;Q<A;Q++)g[Q]/=C;return g}(B,C),i=new Uint8ClampedArray(I*g),E=new Uint8ClampedArray(I*g);for(let e=0;e<g;e++){const g=e*I;for(let B=0;B<I;B++){let C=0;for(let i=-Q;i<=Q;i++){C+=A[g+Math.min(I-1,Math.max(0,B+i))]*t[Q+i]}i[g+B]=C}}for(let e=0;e<I;e++)for(let A=0;A<g;A++){let B=0;for(let C=-Q;C<=Q;C++){B+=i[Math.min(g-1,Math.max(0,A+C))*I+e]*t[Q+C]}E[A*I+e]=Math.round(B)}return E}function S(A,I,g,B,C){const Q=new Uint8Array(I*g),t=[];for(let e=1;e<g-1;e++)for(let g=1;g<I-1;g++){const i=e*I+g,E=A[i];E>=C?(Q[i]=2,t.push({x:g,y:e})):Q[i]=E>=B?0:1}for(let e=0;e<I;e++)Q[e]=1,Q[(g-1)*I+e]=1;for(let e=1;e<g-1;e++)Q[e*I]=1,Q[e*I+I-1]=1;const i=[-1,0,1,-1,1,-1,0,1],E=[-1,-1,-1,0,0,1,1,1];for(;t.length>0;){const{x:A,y:g}=t.pop();for(let B=0;B<8;B++){const C=A+i[B],e=g+E[B],o=e*I+C;0===Q[o]&&(Q[o]=2,t.push({x:C,y:e}))}}return Q}async function M(I,g={}){const B=!(!g.debug||g.debug._timingsOnly),C=[],Q=performance.now();let t,i,E;if(g.skipGrayscale||!1)t=g.width,i=g.height,E=I,B&&(g.debug.grayscale=E);else{t=I.width,i=I.height;let A=performance.now();E=function(A){const{width:I,height:g,data:B}=A,C=new Uint8ClampedArray(I*g);for(let Q=0,t=0;Q<B.length;Q+=4,t++)C[t]=54*B[Q]+183*B[Q+1]+19*B[Q+2]>>8;return C}(I);let Q=performance.now();C.push({step:"Grayscale",ms:(Q-A).toFixed(2)}),B&&(g.debug.grayscale=E)}let e=void 0!==g.lowThreshold?g.lowThreshold:null,o=void 0!==g.highThreshold?g.highThreshold:null;if(null===e||null===o){const A=1024,I=new Uint32Array(A);let g=0;for(let e=1;e<i-1;e++)for(let B=1;B<t-1;B++){const C=e*t+B,Q=-E[C-t-1]+E[C-t+1]-2*E[C-1]+2*E[C+1]-E[C+t-1]+E[C+t+1],i=-E[C-t-1]-2*E[C-t]-E[C-t+1]+E[C+t-1]+2*E[C+t]+E[C+t+1];let o=Math.abs(Q)+Math.abs(i);o>=A&&(o=A-1),o>0&&(I[o]++,g++)}const B=Math.round(.7*g);let C=0,Q=75;for(let t=1;t<A;t++)if(C+=I[t],C>=B){Q=t;break}null===e&&(e=Math.max(5,Math.round(.5*Q))),null===o&&(o=Math.min(500,Math.round(1.5*Q)))}const n=g.kernelSize||5,a=g.sigma||0,s=void 0!==g.L2gradient&&g.L2gradient,c=void 0===g.applyDilation||g.applyDilation,y=g.dilationKernelSize||5,D=void 0!==g.useWasmHysteresis&&g.useWasmHysteresis,d=void 0!==g.useWasmFullCanny&&g.useWasmFullCanny;if(e>=o&&([e,o]=[o,e]),d)try{await G();const I=performance.now(),D=new Uint8ClampedArray(function(I,g,B,C,Q,t,i,E,e,o){const n=h(I,A.__wbindgen_malloc),a=r,s=A.canny_edge_detector_full(n,a,g,B,C,Q,t,i,E,e,o);var c=l(s[0],s[1]).slice();return A.__wbindgen_free(s[0],1*s[1],1),c}(E,t,i,e,o,n,a,s,c,y)),w=(performance.now()-I).toFixed(2);C.push({step:"Edge Processing (WASM)",ms:w}),B&&(g.debug.finalEdges=D),g.debug?g.debug.timings=C:g.debug={timings:C};const d=performance.now();return C.unshift({step:"Edge Detection Total",ms:(d-Q).toFixed(2)}),D}catch(Y){if(!R){R=!0;Y instanceof Error?Y.message:String(Y)}}let f,m,N,F,p,J;f=performance.now();try{await G(),N=function(I,g,B,C,Q){const t=h(I,A.__wbindgen_malloc),i=r,E=A.blur(t,i,g,B,C,Q);var e=l(E[0],E[1]).slice();return A.__wbindgen_free(E[0],1*E[1],1),e}(E,t,i,n,a)}catch(q){N=k(E,t,i,n,a)}m=performance.now(),C.push({step:"Gaussian Blur",ms:(m-f).toFixed(2)}),B&&(g.debug.blurred=N),f=performance.now();{const A=function(A,I,g){const B=new Int16Array(I*g),C=new Int16Array(I*g);for(let Q=1;Q<g-1;Q++){const g=Q*I,t=(Q-1)*I,i=(Q+1)*I;for(let Q=1;Q<I-1;Q++){const I=g+Q,E=A[t+Q-1],e=A[t+Q],o=A[t+Q+1],n=A[g+Q-1],a=A[g+Q+1],s=A[i+Q-1],c=A[i+Q],r=A[i+Q+1],h=o-E+2*(a-n)+(r-s),l=s+2*c+r-(E+2*e+o);B[I]=h,C[I]=l}}return{dx:B,dy:C}}(N,t,i);F=A.dx,p=A.dy}m=performance.now(),C.push({step:"Gradients",ms:(m-f).toFixed(2)}),f=performance.now();try{await G(),J=await u(F,p,t,i,s)}catch(q){J=function(A,I,g,B,C){const Q=new Float32Array(g*B),t=new Float32Array(g*B);for(let i=0;i<A.length;i++){const g=A[i],B=I[i];Q[i]=C?Math.sqrt(g*g+B*B):Math.abs(g)+Math.abs(B)}for(let i=1;i<B-1;i++)for(let B=1;B<g-1;B++){const C=i*g+B,E=Q[C];if(0===E){t[C]=0;continue}const e=A[C],o=I[C];let n=0,a=0;const s=Math.abs(e),c=Math.abs(o);if(c>2.4142*s)n=Q[C-g],a=Q[C+g];else if(s>2.4142*c)n=Q[C-1],a=Q[C+1];else{const A=(e^o)<0?-1:1;o>0?(n=Q[(i-1)*g+(B-A)],a=Q[(i+1)*g+(B+A)]):(n=Q[(i+1)*g+(B-A)],a=Q[(i-1)*g+(B+A)]),e>0&&o>0||e<0&&o<0?(n=Q[(i-1)*g+(B+1)],a=Q[(i+1)*g+(B-1)]):(n=Q[(i-1)*g+(B-1)],a=Q[(i+1)*g+(B+1)])}t[C]=E>=n&&E>=a?E:0}return t}(F,p,t,i,s)}m=performance.now(),C.push({step:"Non-Max Suppression",ms:(m-f).toFixed(2)}),f=performance.now();const M=s?e*e:e,b=s?o*o:o;let L;if(D)try{await G(),L=w(J,t,i,M,b)}catch(q){L=S(J,t,i,M,b)}else L=S(J,t,i,M,b);m=performance.now(),C.push({step:"Hysteresis",ms:(m-f).toFixed(2)}),f=performance.now();const x=new Uint8ClampedArray(t*i);for(let A=0;A<L.length;A++)x[A]=2===L[A]?255:0;m=performance.now(),C.push({step:"Binary Image",ms:(m-f).toFixed(2)}),f=performance.now();let U=x;if(c)try{await G(),U=function(I,g,B,C){const Q=h(I,A.__wbindgen_malloc),t=r,i=A.dilate(Q,t,g,B,C);var E=l(i[0],i[1]).slice();return A.__wbindgen_free(i[0],1*i[1],1),E}(x,t,i,y)}catch(q){U=function(A,I,g,B=5){const C=Math.floor(B/2),Q=new Uint8ClampedArray(I*g),t=new Uint8ClampedArray(I*g);for(let i=0;i<g;i++){const g=i*I;for(let B=0;B<I;B++){let t=0;for(let Q=-C;Q<=C;Q++){const C=B+Q;if(C>=0&&C<I){const I=A[g+C];I>t&&(t=I)}}Q[g+B]=t}}for(let i=0;i<I;i++)for(let A=0;A<g;A++){let B=0;for(let t=-C;t<=C;t++){const C=A+t;if(C>=0&&C<g){const A=Q[C*I+i];A>B&&(B=A)}}t[A*I+i]=B}return t}(x,t,i,y)}if(m=performance.now(),C.push({step:"Dilation",ms:(m-f).toFixed(2)}),B){g.debug.dx=F,g.debug.dy=p;const A=new Float32Array(t*i);for(let I=0;I<F.length;I++){const g=F[I],B=p[I];A[I]=s?Math.sqrt(g*g+B*B):Math.abs(g)+Math.abs(B)}g.debug.magnitude=A,g.debug.suppressed=J,g.debug.edgeMap=L,g.debug.cannyEdges=x,g.debug.finalEdges=U}g.debug?g.debug.timings=C:g.debug||(g.debug={timings:C});const H=performance.now();return C.unshift({step:"Edge Detection Total",ms:(H-Q).toFixed(2)}),U}function b(A){return{topLeft:{x:A.topLeft.x,y:A.topLeft.y},topRight:{x:A.topRight.x,y:A.topRight.y},bottomRight:{x:A.bottomRight.x,y:A.bottomRight.y},bottomLeft:{x:A.bottomLeft.x,y:A.bottomLeft.y}}}function L(A,I){return Math.hypot(A.x-I.x,A.y-I.y)}function x(A,I=4){const g=[A?.topLeft,A?.topRight,A?.bottomRight,A?.bottomLeft];if(g.some(A=>!A||!Number.isFinite(A.x)||!Number.isFinite(A.y)))return!1;for(let B=0;B<g.length;B++)for(let A=B+1;A<g.length;A++)if(L(g[B],g[A])<I)return!1;return!0}function U(A){const I=[A.topLeft,A.topRight,A.bottomRight,A.bottomLeft],g=[];for(let C=0;C<I.length;C++){const A=I[C],B=I[(C+1)%I.length],Q=I[(C+2)%I.length],t=(B.x-A.x)*(Q.y-B.y)-(B.y-A.y)*(Q.x-B.x);Math.abs(t)<1e-6||g.push(Math.sign(t))}if(g.length<3)return!1;const B=g[0];return g.every(A=>A===B)}var H="scanic-corner-editor-styles",Y='<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="3 4 3 10 9 10"/><path d="M3.5 14a8.5 8.5 0 1 0 2-7.4L3 10"/></svg>',q='<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><line x1="18" y1="6" x2="6" y2="18"/><line x1="6" y1="6" x2="18" y2="18"/></svg>',v='<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.4" stroke-linecap="round" stroke-linejoin="round"><polyline points="20 6 9 17 4 12"/></svg>',K='<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><line x1="4" y1="21" x2="4" y2="14"/><line x1="4" y1="10" x2="4" y2="3"/><line x1="12" y1="21" x2="12" y2="12"/><line x1="12" y1="8" x2="12" y2="3"/><line x1="20" y1="21" x2="20" y2="16"/><line x1="20" y1="12" x2="20" y2="3"/><line x1="1" y1="14" x2="7" y2="14"/><line x1="9" y1="8" x2="15" y2="8"/><line x1="17" y1="16" x2="23" y2="16"/></svg>';function T(A={}){const I=A.container;if(!I||"function"!=typeof I.appendChild)throw new Error("createCornerEditor requires a valid container element");const g=I.ownerDocument||("undefined"!=typeof document?document:null),B="undefined"!=typeof window?window:globalThis;!1!==A.injectStyles&&function(A){if(!A||"function"!=typeof A.getElementById)return;if(A.getElementById(H))return;const I=A.createElement("style");I.id=H,I.textContent="\n.scanic-corner-editor {\n --scanic-accent: #6366f1;\n --scanic-mask: rgba(15, 23, 42, 0.45);\n --scanic-edge-color: var(--scanic-accent);\n --scanic-edge-width: 2.5;\n --scanic-handle-size: 20px;\n --scanic-handle-hit: 44px;\n --scanic-handle-color: #ffffff;\n --scanic-handle-ring: 2px;\n --scanic-handle-ring-color: var(--scanic-accent);\n --scanic-handle-shadow: 0 1px 4px rgba(15, 23, 42, 0.45);\n --scanic-handle-active-shadow: 0 8px 22px rgba(15, 23, 42, 0.5);\n --scanic-handle-active-scale: 1.28;\n --scanic-surface: rgba(15, 23, 42, 0.92);\n --scanic-surface-fg: #e2e8f0;\n --scanic-surface-radius: 12px;\n}\n.scanic-handle {\n position: absolute;\n box-sizing: border-box;\n width: var(--scanic-handle-size);\n height: var(--scanic-handle-size);\n margin: 0;\n padding: 0;\n border: var(--scanic-handle-ring) solid var(--scanic-handle-ring-color);\n border-radius: 50%;\n background: var(--scanic-handle-color);\n box-shadow: var(--scanic-handle-shadow);\n transform: translate(-50%, -50%);\n cursor: grab;\n touch-action: none;\n z-index: 2;\n transition: transform 0.12s ease, box-shadow 0.12s ease,\n background 0.12s ease, border-color 0.12s ease;\n}\n/* Enlarges the pointer/touch target without changing the visual size. */\n.scanic-handle::after {\n content: '';\n position: absolute;\n left: 50%;\n top: 50%;\n width: var(--scanic-handle-hit);\n height: var(--scanic-handle-hit);\n transform: translate(-50%, -50%);\n border-radius: 50%;\n}\n/* Soft accent halo that blooms when a handle is grabbed — the \"depth\" layer. */\n.scanic-handle::before {\n content: '';\n position: absolute;\n left: 50%;\n top: 50%;\n width: 0;\n height: 0;\n border-radius: 50%;\n background: var(--scanic-accent);\n opacity: 0;\n transform: translate(-50%, -50%);\n transition: opacity 0.15s ease, width 0.15s ease, height 0.15s ease;\n pointer-events: none;\n}\n.scanic-handle:hover {\n transform: translate(-50%, -50%) scale(1.12);\n}\n.scanic-handle:focus-visible {\n outline: none;\n box-shadow: 0 0 0 3px color-mix(in srgb, var(--scanic-accent) 45%, transparent),\n var(--scanic-handle-shadow);\n}\n/* The currently selected corner — the target of nudges / keyboard. */\n.scanic-handle.is-selected {\n border-color: var(--scanic-accent);\n transform: translate(-50%, -50%) scale(1.12);\n}\n.scanic-handle.is-selected::before {\n opacity: 0.16;\n width: calc(var(--scanic-handle-size) * 2);\n height: calc(var(--scanic-handle-size) * 2);\n}\n.scanic-handle.is-active {\n cursor: grabbing;\n background: var(--scanic-accent);\n border-color: #ffffff;\n transform: translate(-50%, -50%) scale(var(--scanic-handle-active-scale));\n box-shadow: var(--scanic-handle-active-shadow);\n}\n.scanic-handle.is-active::before {\n opacity: 0.22;\n width: calc(var(--scanic-handle-size) * 2.8);\n height: calc(var(--scanic-handle-size) * 2.8);\n}\n.scanic-toolbar,\n.scanic-nudges {\n position: absolute;\n z-index: 3;\n display: flex;\n gap: 4px;\n padding: 5px;\n background: var(--scanic-surface);\n border-radius: var(--scanic-surface-radius);\n -webkit-backdrop-filter: blur(8px);\n backdrop-filter: blur(8px);\n box-shadow: 0 6px 20px rgba(15, 23, 42, 0.35);\n}\n.scanic-toolbar {\n left: 50%;\n bottom: 12px;\n transform: translateX(-50%);\n}\n.scanic-nudges {\n top: 10px;\n right: 10px;\n display: grid;\n grid-template-columns: repeat(4, auto);\n gap: 3px;\n}\n.scanic-toolbar button,\n.scanic-nudges button {\n display: grid;\n place-items: center;\n margin: 0;\n padding: 0;\n font-family: inherit;\n font-size: 15px;\n font-weight: 600;\n line-height: 1;\n color: var(--scanic-surface-fg);\n background: transparent;\n border: 0;\n border-radius: 8px;\n cursor: pointer;\n transition: background 0.12s ease, color 0.12s ease, filter 0.12s ease;\n}\n.scanic-toolbar button {\n width: 34px;\n height: 34px;\n}\n.scanic-nudges button {\n width: 30px;\n height: 30px;\n}\n.scanic-toolbar button svg {\n width: 18px;\n height: 18px;\n display: block;\n}\n.scanic-toolbar button:hover,\n.scanic-nudges button:hover {\n background: rgba(255, 255, 255, 0.14);\n}\n.scanic-toolbar button:focus-visible,\n.scanic-nudges button:focus-visible {\n outline: 2px solid var(--scanic-accent);\n outline-offset: 1px;\n}\n.scanic-toolbar .scanic-btn-apply {\n background: var(--scanic-accent);\n color: #ffffff;\n}\n.scanic-toolbar .scanic-btn-apply:hover {\n background: var(--scanic-accent);\n filter: brightness(1.1);\n}\n.scanic-toolbar .scanic-btn-expert.is-on {\n background: color-mix(in srgb, var(--scanic-accent) 32%, transparent);\n color: #ffffff;\n}\n",(A.head||A.documentElement).appendChild(I)}(g);const{canvas:C,width:Q,height:t}=function(A){if(!A)throw new Error("No image provided");const I=A&&"number"==typeof A.width&&"number"==typeof A.height&&A.data;let g=0,B=0;if(I?(g=A.width,B=A.height):(g=A.width||A.naturalWidth,B=A.height||A.naturalHeight),!g||!B)throw new Error("Image must be loaded before creating the corner editor");const C=document.createElement("canvas");C.width=g,C.height=B;const Q=C.getContext("2d",{willReadFrequently:!0});if(!Q)throw new Error("Failed to create 2D canvas context for source image");return I?Q.putImageData(A,0,0):Q.drawImage(A,0,0,g,B),{canvas:C,width:g,height:B}}(A.image),i=!I.classList.contains("scanic-corner-editor");I.classList.add("scanic-corner-editor"),A.classNames?.root&&I.classList.add(A.classNames.root);const E={position:I.style.position,minHeight:I.style.minHeight};let e=!1,o=!1;"static"===getComputedStyle(I).position&&(I.style.position="relative",e=!0),L(A.theme);const n=g.createElement("canvas");n.style.position="absolute",n.style.top="0",n.style.left="0",n.style.display="block",n.style.touchAction="none",n.style.userSelect="none",n.style.webkitUserSelect="none",n.style.cursor="default",I.appendChild(n);const a=n.getContext("2d");if(!a)throw new Error("Failed to create 2D canvas context for corner editor");const s={enabled:!1!==A.magnifier?.enabled,size:A.magnifier?.size||120,zoom:A.magnifier?.zoom||2,margin:A.magnifier?.margin||8,borderColor:A.magnifier?.borderColor||"#ffffff",borderWidth:A.magnifier?.borderWidth||2,crosshairColor:A.magnifier?.crosshairColor||"#ffffff",crosshairSize:A.magnifier?.crosshairSize||18},c={enabled:!!A.nudges?.enabled,steps:(A.nudges?.steps&&A.nudges.steps.length?A.nudges.steps:[1,10]).map(A=>Math.max(1,Math.round(A)))},r={enabled:!1!==A.toolbar?.enabled,reset:!1!==A.toolbar?.reset,cancel:!1!==A.toolbar?.cancel,apply:!1!==A.toolbar?.apply,labels:{reset:A.toolbar?.labels?.reset||"Reset",cancel:A.toolbar?.labels?.cancel||"Cancel",apply:A.toolbar?.labels?.apply||"Apply"}},h=!1!==A.keyboard,l=function(A,I,g=.08){const B=Math.max(8,Math.min(A,I)*g);return{topLeft:{x:B,y:B},topRight:{x:A-B,y:B},bottomRight:{x:A-B,y:I-B},bottomLeft:{x:B,y:I-B}}}(Q,t),y=A.corners?b(A.corners):l;let D=x(y)&&U(y)?y:l;const w=b(D);let d=!1,f=null,u="topLeft",m=null,N=null;const R=Math.max(24,A.handleHitArea||44);A.handleHitArea&&I.style.setProperty("--scanic-handle-hit",R+"px");const F=["topLeft","topRight","bottomRight","bottomLeft"],p={topLeft:"Top-left corner",topRight:"Top-right corner",bottomRight:"Bottom-right corner",bottomLeft:"Bottom-left corner"};let J={scale:1,offsetX:0,offsetY:0,width:1,height:1};const G={mask:"rgba(15,23,42,0.45)",edgeColor:"#6366f1",edgeWidth:2.5},k={};for(const b of F){const B=g.createElement("button");B.type="button",B.className="scanic-handle"+(A.classNames?.handle?" "+A.classNames.handle:""),B.dataset.corner=b,B.setAttribute("aria-label",p[b]),h||(B.tabIndex=-1),B.addEventListener("pointerdown",A=>CA(b,A)),B.addEventListener("pointermove",A=>QA(b,A)),B.addEventListener("pointerup",A=>tA(b,A)),B.addEventListener("pointercancel",A=>tA(b,A)),B.addEventListener("focus",()=>{u=b,z()}),h&&B.addEventListener("keydown",A=>iA(b,A)),I.appendChild(B),k[b]=B}function S(A,I){const g=n.style.color;n.style.color=`var(${A}, ${I})`;const B=getComputedStyle(n).color||I;return n.style.color=g,B}function M(){G.mask=S("--scanic-mask","rgba(15, 23, 42, 0.45)"),G.edgeColor=S("--scanic-edge-color","#6366f1");const A=getComputedStyle(I).getPropertyValue("--scanic-edge-width"),g=parseFloat(A);G.edgeWidth=Number.isFinite(g)&&g>0?g:2.5}function L(A){if(!A||"object"!=typeof A)return;const g=new Set(["handleSize","handleHit"]);for(const[B,C]of Object.entries({accent:"--scanic-accent",mask:"--scanic-mask",edgeColor:"--scanic-edge-color",edgeWidth:"--scanic-edge-width",handleSize:"--scanic-handle-size",handleHit:"--scanic-handle-hit",handleColor:"--scanic-handle-color",handleRingColor:"--scanic-handle-ring-color",surface:"--scanic-surface",surfaceColor:"--scanic-surface-fg",radius:"--scanic-surface-radius"})){const Q=A[B];if(null==Q)continue;const t="number"==typeof Q&&g.has(B)?`${Q}px`:String(Q);I.style.setProperty(C,t)}}function T(){"function"==typeof A.onChange&&A.onChange(b(D))}let W=0,P=0;function Z(){const A=Math.max(1,Math.round(I.clientWidth));let g=Math.round(I.clientHeight);if(g<80){const C=t/Math.max(1,Q),i=.7*(B.innerHeight||800);g=Math.max(240,Math.round(Math.min(A*C,i))),I.style.minHeight=g+"px",o=!0}return{width:A,height:Math.max(1,g)}}function X(){const{width:A,height:I}=Z(),g=B.devicePixelRatio||1;W=A,P=I,n.style.width=A+"px",n.style.height=I+"px";const C=Math.round(A*g),i=Math.round(I*g);n.width!==C&&(n.width=C),n.height!==i&&(n.height=i),a.setTransform(g,0,0,g,0,0);const E=Math.max(1,A-24),e=Math.max(1,I-24),o=Math.min(E/Q,e/t),s=Q*o,c=t*o;J={scale:o,offsetX:Math.round((A-s)/2),offsetY:Math.round((I-c)/2),width:A,height:I},M()}function V(A){return{x:J.offsetX+A.x*J.scale,y:J.offsetY+A.y*J.scale}}function j(A){return x(A)&&U(A)}function O(A){const I=n.getBoundingClientRect();return{x:A.clientX-I.left,y:A.clientY-I.top}}function z(){for(const A of F)k[A].classList.toggle("is-selected",A===u)}function _(){d||(a.clearRect(0,0,J.width,J.height),a.drawImage(C,0,0,Q,t,J.offsetX,J.offsetY,Q*J.scale,t*J.scale),function(){const A=F.map(A=>V(D[A]));a.save(),a.fillStyle=G.mask,a.beginPath(),a.rect(0,0,J.width,J.height),a.moveTo(A[0].x,A[0].y);for(let I=1;I<A.length;I++)a.lineTo(A[I].x,A[I].y);a.closePath(),a.fill("evenodd"),a.restore(),a.save(),a.strokeStyle=G.edgeColor,a.lineWidth=G.edgeWidth,a.lineJoin="round",a.beginPath(),a.moveTo(A[0].x,A[0].y);for(let I=1;I<A.length;I++)a.lineTo(A[I].x,A[I].y);a.closePath(),a.stroke(),a.restore()}(),function(){if(!s.enabled||!f||!N)return;const A=D[f],I=s.size,g=I/2,B=Math.max(1.1,s.zoom);let i=N.x+g+s.margin,E=N.y-g-s.margin;i+g>J.width&&(i=N.x-g-s.margin),E-g<0&&(E=N.y+g+s.margin),i=Math.max(g+2,Math.min(J.width-g-2,i)),E=Math.max(g+2,Math.min(J.height-g-2,E));const e=I/B,o=Math.max(0,Math.min(Q-e,A.x-e/2)),n=Math.max(0,Math.min(t-e,A.y-e/2));a.save(),a.beginPath(),a.arc(i,E,g,0,2*Math.PI),a.clip(),a.drawImage(C,o,n,e,e,i-g,E-g,I,I),a.restore(),a.save(),a.beginPath(),a.arc(i,E,g,0,2*Math.PI),a.lineWidth=s.borderWidth,a.strokeStyle=s.borderColor,a.stroke();const c=s.crosshairSize/2;a.strokeStyle=s.crosshairColor,a.lineWidth=1.5,a.beginPath(),a.moveTo(i-c,E),a.lineTo(i+c,E),a.moveTo(i,E-c),a.lineTo(i,E+c),a.stroke(),a.restore()}(),function(){for(const A of F){const I=V(D[A]),g=k[A];g.style.left=I.x+"px",g.style.top=I.y+"px"}}())}const $="function"==typeof B.requestAnimationFrame?B.requestAnimationFrame.bind(B):null,AA="function"==typeof B.cancelAnimationFrame?B.cancelAnimationFrame.bind(B):null;let IA=null;function gA(){d||($?null===IA&&(IA=$(()=>{IA=null,_()})):_())}function BA(A,I){const g=b(D);return g[A]={x:Math.max(0,Math.min(Q,I.x)),y:Math.max(0,Math.min(t,I.y))},!!j(g)&&(D=g,u=A,z(),T(),gA(),!0)}function CA(A,I){if(!d){if("function"==typeof I.preventDefault&&I.preventDefault(),f=A,u=A,z(),m=I.pointerId,N=O(I),k[A].classList.add("is-active"),h&&"function"==typeof k[A].focus)try{k[A].focus({preventScroll:!0})}catch(g){k[A].focus()}if(k[A].setPointerCapture&&null!=I.pointerId)try{k[A].setPointerCapture(I.pointerId)}catch(g){}gA()}}function QA(A,I){d||f!==A||null!==m&&I.pointerId!==m||(N=O(I),BA(A,function(A,I){const g=(A-J.offsetX)/J.scale,B=(I-J.offsetY)/J.scale;return{x:Math.max(0,Math.min(Q,g)),y:Math.max(0,Math.min(t,B))}}(N.x,N.y)))}function tA(A,I){if(f===A&&(null===m||I.pointerId===m)){if(k[A].releasePointerCapture&&null!=m)try{k[A].releasePointerCapture(m)}catch(g){}k[A].classList.remove("is-active"),f=null,m=null,N=null,gA()}}function iA(A,I){if(d||!h)return;if("Enter"===I.key)return I.preventDefault(),void rA();if("Escape"===I.key)return I.preventDefault(),void hA();let g=0,B=0;if("ArrowLeft"===I.key)g=-1;else if("ArrowRight"===I.key)g=1;else if("ArrowUp"===I.key)B=-1;else{if("ArrowDown"!==I.key)return;B=1}I.preventDefault();const C=I.shiftKey?c.steps[c.steps.length-1]||10:1;BA(A,{x:D[A].x+g*C,y:D[A].y+B*C})}let EA=null,eA=null,oA=null,nA=!1;const aA=r.enabled&&c.enabled;function sA({html:A,text:I,title:B,className:C,onClick:Q}){const t=g.createElement("button");return t.type="button",null!=A?t.innerHTML=A:t.textContent=I,B&&(t.title=B,t.setAttribute("aria-label",B)),C&&(t.className=C),t.addEventListener("click",Q),t}let cA=null;function rA(){const I=b(D);return"function"==typeof A.onConfirm&&A.onConfirm(I),I}function hA(){"function"==typeof A.onCancel&&A.onCancel()}function lA(){D=b(w),T(),gA()}const yA="undefined"!=typeof ResizeObserver?new ResizeObserver(()=>{if(d)return;const A=Z();A.width===W&&A.height===P||(X(),gA())}):null;return X(),r.enabled&&(r.reset||r.cancel||r.apply||aA)&&(EA=g.createElement("div"),EA.className="scanic-toolbar"+(A.classNames?.toolbar?" "+A.classNames.toolbar:""),r.reset&&EA.appendChild(sA({html:Y,title:r.labels.reset,className:"scanic-btn-reset",onClick:()=>lA()})),aA&&(oA=sA({html:K,title:"Precision nudge (expert)",className:"scanic-btn-expert",onClick:()=>{return nA=A=!nA,eA&&(eA.style.display=A?"":"none"),void(oA&&(oA.classList.toggle("is-on",A),oA.setAttribute("aria-pressed",String(A))));var A}}),oA.setAttribute("aria-pressed","false"),EA.appendChild(oA)),r.cancel&&EA.appendChild(sA({html:q,title:r.labels.cancel,className:"scanic-btn-cancel",onClick:()=>hA()})),r.apply&&EA.appendChild(sA({html:v,title:r.labels.apply,className:"scanic-btn-apply",onClick:()=>rA()})),I.appendChild(EA)),function(){if(!c.enabled)return;eA=g.createElement("div"),eA.className="scanic-nudges"+(A.classNames?.nudges?" "+A.classNames.nudges:"");const B=(A,I,g,B,C)=>sA({text:A,title:I,className:"scanic-btn-nudge",onClick:()=>{const A=D[u];BA(u,{x:A.x+g*C,y:A.y+B*C})}});for(const A of c.steps)eA.appendChild(B("←",`Move left ${A}px`,-1,0,A)),eA.appendChild(B("↑",`Move up ${A}px`,0,-1,A)),eA.appendChild(B("↓",`Move down ${A}px`,0,1,A)),eA.appendChild(B("→",`Move right ${A}px`,1,0,A));I.appendChild(eA),aA&&(eA.style.display="none")}(),function A(){if("function"!=typeof B.matchMedia)return;cA&&cA();const I=B.devicePixelRatio||1,g=B.matchMedia(`(resolution: ${I}dppx)`),C=()=>{d||(X(),gA(),A())};"function"==typeof g.addEventListener?(g.addEventListener("change",C),cA=()=>g.removeEventListener("change",C)):"function"==typeof g.addListener?(g.addListener(C),cA=()=>g.removeListener(C)):cA=null}(),yA&&yA.observe(I),z(),_(),{getCorners:()=>b(D),setCorners:A=>!(!A||!j(A))&&(D=b(A),T(),gA(),!0),reset(){lA()},nudge:(A,I,g,B=1)=>!!F.includes(A)&&BA(A,{x:D[A].x+I*B,y:D[A].y+g*B}),refreshTheme(A){L(A),M(),gA()},confirm:()=>rA(),cancel(){hA()},destroy(){if(!d){d=!0,null!==IA&&AA&&(AA(IA),IA=null),cA&&(cA(),cA=null),yA&&yA.disconnect();for(const A of F){const I=k[A];I&&I.parentNode&&I.parentNode.removeChild(I)}EA&&EA.parentNode&&EA.parentNode.removeChild(EA),eA&&eA.parentNode&&eA.parentNode.removeChild(eA),n.parentNode&&n.parentNode.removeChild(n),i&&I.classList.remove("scanic-corner-editor"),A.classNames?.root&&I.classList.remove(A.classNames.root),e&&(I.style.position=E.position),o&&(I.style.minHeight=E.minHeight)}}}}async function W(){try{return await G()}catch{return null}}var P=class{constructor(A={}){this.defaultOptions={maxProcessingDimension:800,mode:"detect",output:"canvas",...A},this.initialized=!1}async initialize(){if(!this.initialized){if("ml"===this.defaultOptions.detector)try{const{initializeMl:A}=await import("./mlDetector-DV0KqH2X.js");await A(this.defaultOptions.ml||{})}catch{}else try{await G()}catch{}this.initialized=!0}}async scan(A,I={}){return this.initialized||await this.initialize(),await IA(A,{...this.defaultOptions,...I})}async extract(A,I,g={}){return this.initialized||await this.initialize(),await AA(A,I,{...this.defaultOptions,...g})}};function Z(A){return A<=0?0:A>=1?1:A}function X(A,I){return Math.hypot(A.x-I.x,A.y-I.y)}function V(A){return[X(A.topLeft,A.topRight),X(A.topRight,A.bottomRight),X(A.bottomRight,A.bottomLeft),X(A.bottomLeft,A.topLeft)]}function j(A){const I=function(A){const I=[A.topLeft,A.topRight,A.bottomRight,A.bottomLeft],g=[];for(let B=0;B<I.length;B++){const A=I[(B+I.length-1)%I.length],C=I[B],Q=I[(B+1)%I.length],t=A.x-C.x,i=A.y-C.y,E=Q.x-C.x,e=Q.y-C.y,o=t*E+i*e,n=Math.max(1e-6,Math.hypot(t,i)*Math.hypot(E,e)),a=Math.max(-1,Math.min(1,o/n));g.push(180*Math.acos(a)/Math.PI)}return g}(A);let g=0;for(const B of I){g+=Z(1-Math.abs(B-90)/55)}return g/Math.max(1,I.length)}function O(A,I){const g=Number(I.isValid)-Number(A.isValid);if(0!==g)return g;const B=I.confidence-A.confidence;if(!(Math.abs(B)<.015)&&0!==B)return B;const C=Math.abs((A.approxCount??99)-4)-Math.abs((I.approxCount??99)-4);if(0!==C)return C;const Q=(I.rightAngleScore??0)-(A.rightAngleScore??0);if(Math.abs(Q)>1e-6)return Q;const t=Math.abs(1-(A.contourFitRatio??1))-Math.abs(1-(I.contourFitRatio??1));return Math.abs(t)>1e-6?t:B||I.score-A.score||I.coverageRatio-A.coverageRatio||I.area-A.area}function z(A,I,g,B,C={}){const Q=C.epsilon||.02,t=E(A.points,Q).length,i=n(A,{epsilon:Q});if(!i)return{contour:A,corners:null,score:0,confidence:0,isValid:!1,area:A.area||0,fillRatio:0,coverageRatio:0,cornersArea:0,approxCount:t,convex:!1,edgeSupport:0,aspectRatio:1/0,minSide:0,rightAngleScore:0,oppositeSideConsistency:0,contourFitRatio:0,contourFitScore:0};const e=g*B,o=A.area||0,a=A.boundingBox||{minX:0,minY:0,maxX:0,maxY:0},s=o/Math.max(1,(a.maxX-a.minX+1)*(a.maxY-a.minY+1)),c=function(A){if(!A)return 0;const I=[A.topLeft,A.topRight,A.bottomRight,A.bottomLeft];let g=0;for(let B=0;B<I.length;B++){const A=(B+1)%I.length;g+=I[B].x*I[A].y-I[A].x*I[B].y}return Math.abs(g)/2}(i),r=c/Math.max(1,e),h=V(i),l=Math.min(...h),y=(h[0]+h[2])/2,D=(h[1]+h[3])/2,w=y>D?y/Math.max(1e-6,D):D/Math.max(1e-6,y),d=function(A){const I=[A.topLeft,A.topRight,A.bottomRight,A.bottomLeft],g=[];for(let C=0;C<I.length;C++){const A=I[C],B=I[(C+1)%I.length],Q=I[(C+2)%I.length],t=(B.x-A.x)*(Q.y-B.y)-(B.y-A.y)*(Q.x-B.x);Math.abs(t)<1e-6||g.push(Math.sign(t))}if(g.length<3)return!1;const B=g[0];return g.every(A=>A===B)}(i),f=function(A,I,g,B){if(!A?.points||0===A.points.length)return 0;const C=Math.max(1,Math.floor(A.points.length/240));let Q=0,t=0;for(let i=0;i<A.points.length;i+=C){const C=A.points[i],E=Math.max(0,Math.min(g-1,0|C.x)),e=Math.max(0,Math.min(B-1,0|C.y));Q++;let o=!1;for(let A=-1;A<=1&&!o;A++){const C=e+A;if(!(C<0||C>=B))for(let A=-1;A<=1;A++){const B=E+A;if(!(B<0||B>=g)&&I[C*g+B]>0){o=!0;break}}}o&&t++}return 0===Q?0:t/Q}(A,I,g,B),u=j(i),m=function(A){const I=V(A);return(Math.min(I[0],I[2])/Math.max(1e-6,Math.max(I[0],I[2]))+Math.min(I[1],I[3])/Math.max(1e-6,Math.max(I[1],I[3])))/2}(i),N=o/Math.max(1,c),R=Z(1-Math.abs(N-1)/.55),F=4===t?1:5===t?.9:6===t?.7:t<=8?.5:.28,p=Z(o/Math.max(1,.4*e)),J=Z((s-.08)/.72),G=Z((r-.03)/.82),k=void 0!==C.minDocumentSideRatio?C.minDocumentSideRatio:.06,S=void 0!==C.minDocumentCoverageRatio?C.minDocumentCoverageRatio:.04,M=void 0!==C.maxDocumentAspectRatio?C.maxDocumentAspectRatio:8,b=void 0!==C.minDocumentFillRatio?C.minDocumentFillRatio:.07,L=void 0!==C.minContourFitRatio?C.minContourFitRatio:.11,x=void 0!==C.maxContourFitRatio?C.maxContourFitRatio:1.2,U=void 0!==C.minRightAngleScore?C.minRightAngleScore:.42,H=void 0!==C.minOppositeSideConsistency?C.minOppositeSideConsistency:.3,Y=Math.min(g,B)*k,q=function(A,I=6){const g=[A?.topLeft,A?.topRight,A?.bottomRight,A?.bottomLeft];if(g.some(A=>!A||!Number.isFinite(A.x)||!Number.isFinite(A.y)))return!1;for(let B=0;B<g.length;B++)for(let A=B+1;A<g.length;A++)if(X(g[B],g[A])<I)return!1;return!0}(i)&&d&&l>=Y&&r>=S&&w<=M&&s>=b&&N>=L&&N<=x&&u>=U&&m>=H,v=.22*p+.14*J+.15*F+.08*(d?1:0)+.08*f+.13*G+.1*u+.05*m+.05*R;return{contour:A,corners:i,score:v,confidence:q?v:.33*v,isValid:q,area:o,fillRatio:s,coverageRatio:r,cornersArea:c,approxCount:t,convex:d,edgeSupport:f,aspectRatio:w,minSide:l,rightAngleScore:u,oppositeSideConsistency:m,contourFitRatio:N,contourFitScore:R}}async function _(A,I,g,C,Q={}){const t=Q.debug?{}:{_timingsOnly:!0},i=[];t&&!t._timingsOnly&&(t.preprocessing={scaledDimensions:{width:I,height:g},scaleFactor:C,maxProcessingDimension:Q.maxProcessingDimension||800});const E=function(A={}){const I=A.dilationKernelSize||3,g=A.dilationIterations||1,B=void 0===A.applyDilation||A.applyDilation,C=[{name:"default",lowThreshold:A.lowThreshold,highThreshold:A.highThreshold,dilationKernelSize:I,dilationIterations:g,applyDilation:B}];if(!1===A.enableDetectionCascade)return C;C.push({name:"connect-edges",lowThreshold:A.lowThreshold,highThreshold:A.highThreshold,dilationKernelSize:Math.max(I,5),dilationIterations:Math.max(g,2),applyDilation:!0}),C.push({name:"no-dilation",lowThreshold:A.lowThreshold,highThreshold:A.highThreshold,dilationKernelSize:I,dilationIterations:g,applyDilation:!1}),void 0===A.lowThreshold&&void 0===A.highThreshold&&C.push({name:"fixed-mid-thresholds",lowThreshold:60,highThreshold:180,dilationKernelSize:I,dilationIterations:g,applyDilation:B});const Q=[],t=/* @__PURE__ */new Set;for(const i of C){const A=[i.lowThreshold,i.highThreshold,i.dilationKernelSize,i.dilationIterations,i.applyDilation].join(":");t.has(A)||(t.add(A),Q.push(i))}return Q}(Q),e=[],o=async(t,E)=>{const e=t.name||`pass-${E+1}`,o=0===E?"":` (${e})`,n=Q.debug?{}:{_timingsOnly:!0},a=await M(A,{width:I,height:g,lowThreshold:t.lowThreshold,highThreshold:t.highThreshold,dilationKernelSize:t.dilationKernelSize,dilationIterations:t.dilationIterations,applyDilation:t.applyDilation,debug:n,skipGrayscale:!0,useWasmBlur:!0,useWasmHysteresis:Q.useWasmHysteresis,useWasmFullCanny:Q.useWasmFullCanny});n.timings&&n.timings.forEach(A=>{"Edge Detection Total"!==A.step&&i.push({step:`${A.step}${o}`,ms:A.ms})});let s=performance.now();const c=B(a,{minArea:(Q.minArea||1e3)/(C*C),width:I,height:g});i.push({step:`Find Contours${o}`,ms:(performance.now()-s).toFixed(2)}),s=performance.now();const{best:r,candidates:h}=function(A,I,g,B,C={}){const Q=C.maxCandidateContours||12,t=A.slice(0,Q).map((A,Q)=>({rankByArea:Q,...z(A,I,g,B,C)})).sort(O);return{best:t[0]||null,candidates:t}}(c,a,I,g,Q);return i.push({step:`Corner Detection${o}`,ms:(performance.now()-s).toFixed(2)}),{name:e,params:{lowThreshold:t.lowThreshold,highThreshold:t.highThreshold,dilationKernelSize:t.dilationKernelSize,dilationIterations:t.dilationIterations,applyDilation:t.applyDilation},contours:c,best:r,candidates:h}},n=await o(E[0],0);if(e.push(n),function(A,I={}){if(!1===I.enableDetectionCascade)return!1;if(!A||!A.corners)return!0;const g=void 0!==I.minCascadeTriggerConfidence?I.minCascadeTriggerConfidence:.68;return!A.isValid||A.confidence<g||A.approxCount>5||A.rightAngleScore<.55||A.contourFitRatio<.14}(n.best,Q))for(let B=1;B<E.length;B++)e.push(await o(E[B],B));const a=[];for(const B of e)for(const A of B.candidates)a.push({...A,passName:B.name,passParams:B.params});a.sort(O);const s=a[0]||null,c=a;if(!s||!s.corners)return{success:!1,message:"No document detected",debug:t._timingsOnly?null:t,timings:i};t&&!t._timingsOnly&&(t.passes=e.map(A=>({name:A.name,params:A.params,contourCount:A.contours.length,bestCandidate:A.best?{score:A.best.score,confidence:A.best.confidence,isValid:A.best.isValid,approxCount:A.best.approxCount,coverageRatio:A.best.coverageRatio,fillRatio:A.best.fillRatio,rightAngleScore:A.best.rightAngleScore,contourFitRatio:A.best.contourFitRatio}:null})),t.candidates=c.map(A=>({passName:A.passName,rankByArea:A.rankByArea,area:A.area,fillRatio:A.fillRatio,coverageRatio:A.coverageRatio,cornersArea:A.cornersArea,approxCount:A.approxCount,convex:A.convex,edgeSupport:A.edgeSupport,aspectRatio:A.aspectRatio,minSide:A.minSide,rightAngleScore:A.rightAngleScore,oppositeSideConsistency:A.oppositeSideConsistency,contourFitRatio:A.contourFitRatio,contourFitScore:A.contourFitScore,score:A.score,confidence:A.confidence,isValid:A.isValid})),t.selectedCandidate=c[0]?{passName:c[0].passName,rankByArea:c[0].rankByArea,area:c[0].area,fillRatio:c[0].fillRatio,coverageRatio:c[0].coverageRatio,approxCount:c[0].approxCount,edgeSupport:c[0].edgeSupport,aspectRatio:c[0].aspectRatio,minSide:c[0].minSide,rightAngleScore:c[0].rightAngleScore,oppositeSideConsistency:c[0].oppositeSideConsistency,contourFitRatio:c[0].contourFitRatio,score:c[0].score,confidence:c[0].confidence,isValid:c[0].isValid}:null);const r=s.corners,h=s.contour;let l=r;return 1!==C&&(l={topLeft:{x:r.topLeft.x*C,y:r.topLeft.y*C},topRight:{x:r.topRight.x*C,y:r.topRight.y*C},bottomRight:{x:r.bottomRight.x*C,y:r.bottomRight.y*C},bottomLeft:{x:r.bottomLeft.x*C,y:r.bottomLeft.y*C}}),{success:!0,contour:h,corners:l,confidence:s.confidence,debug:t._timingsOnly?null:t,timings:i}}function $(A,I,g){const{topLeft:B,topRight:C,bottomRight:Q,bottomLeft:t}=g,i=Math.hypot(Q.x-t.x,Q.y-t.y),E=Math.hypot(C.x-B.x,C.y-B.y),e=Math.round(Math.max(i,E)),o=Math.hypot(C.x-Q.x,C.y-Q.y),n=Math.hypot(B.x-t.x,B.y-t.y),a=Math.round(Math.max(o,n));A.canvas.width=e,A.canvas.height=a,function(A,I,g,B,C){const Q=I&&"number"==typeof I.width&&"number"==typeof I.height&&I.data,t=I.width||I.naturalWidth,i=I.height||I.naturalHeight,E=document.createElement("canvas");E.width=t,E.height=i;const e=E.getContext("2d",{willReadFrequently:!0});Q?e.putImageData(I,0,0):e.drawImage(I,0,0,t,i);const o=e.getImageData(0,0,t,i).data,n=function(A){const I=A[0][0],g=A[0][1],B=A[0][2],C=A[1][0],Q=A[1][1],t=A[1][2],i=A[2][0],E=A[2][1],e=A[2][2],o=Q*e-t*E,n=-(C*e-t*i),a=C*E-Q*i,s=-(g*e-B*E),c=I*e-B*i,r=-(I*E-g*i),h=g*t-B*Q,l=-(I*t-B*C),y=I*Q-g*C,D=I*o+g*n+B*a;if(0===D)throw new Error("Singular matrix");return[[o/D,s/D,h/D],[n/D,c/D,l/D],[a/D,r/D,y/D]]}(g),a=n[0][0],s=n[0][1],c=n[0][2],r=n[1][0],h=n[1][1],l=n[1][2],y=n[2][0],D=n[2][1],w=n[2][2],d=A.createImageData(B,C),f=d.data,u=t-1,m=i-1;for(let N=0;N<C;N++){const A=s*N+c,I=h*N+l,g=D*N+w;for(let C=0;C<B;C++){const Q=1/(y*C+g),i=(a*C+A)*Q,E=(r*C+I)*Q,e=i<0?0:i>u?u:i,n=E<0?0:E>m?m:E,s=0|e,c=0|n,h=s<u?s+1:s,l=c<m?c+1:c,D=e-s,w=n-c,d=1-D,R=1-w,F=d*R,p=D*R,J=d*w,G=D*w,k=c*t+s<<2,S=c*t+h<<2,M=l*t+s<<2,b=l*t+h<<2,L=N*B+C<<2;f[L]=o[k]*F+o[S]*p+o[M]*J+o[b]*G+.5|0,f[L+1]=o[k+1]*F+o[S+1]*p+o[M+1]*J+o[b+1]*G+.5|0,f[L+2]=o[k+2]*F+o[S+2]*p+o[M+2]*J+o[b+2]*G+.5|0,f[L+3]=255}}A.putImageData(d,0,0)}(A,I,function(A,I){const g=function(A,I){const g=A.length,B=A[0].length,C=A.map(A=>A.slice()),Q=I.slice();for(let i=0;i<B;i++){let A=i;for(let I=i+1;I<g;I++)Math.abs(C[I][i])>Math.abs(C[A][i])&&(A=I);[C[i],C[A]]=[C[A],C[i]],[Q[i],Q[A]]=[Q[A],Q[i]];for(let I=i+1;I<g;I++){const A=C[I][i]/C[i][i];for(let g=i;g<B;g++)C[I][g]-=A*C[i][g];Q[I]-=A*Q[i]}}const t=new Array(B);for(let i=B-1;i>=0;i--){let A=Q[i];for(let I=i+1;I<B;I++)A-=C[i][I]*t[I];t[i]=A/C[i][i]}return t}(function(A){const g=[];for(let B=0;B<4;B++){const[C,Q]=A[B];g.push([C,Q,1,0,0,0,-C*I[B][0],-Q*I[B][0]]),g.push([0,0,0,C,Q,1,-C*I[B][1],-Q*I[B][1]])}return g}(A),[I[0][0],I[0][1],I[1][0],I[1][1],I[2][0],I[2][1],I[3][0],I[3][1]]);return[[g[0],g[1],g[2]],[g[3],g[4],g[5]],[g[6],g[7],1]]}([[B.x,B.y],[C.x,C.y],[Q.x,Q.y],[t.x,t.y]],[[0,0],[e-1,0],[e-1,a-1],[0,a-1]]),e,a)}async function AA(A,I,g={}){const B=g.output||"canvas";if(!(I&&I.topLeft&&I.topRight&&I.bottomRight&&I.bottomLeft))return{output:null,corners:null,success:!1,message:"Invalid corner points provided"};try{const g=document.createElement("canvas");let C;return $(g.getContext("2d"),A,I),C="canvas"===B?g:"imagedata"===B?g.getContext("2d").getImageData(0,0,g.width,g.height):"dataurl"===B?g.toDataURL():g,{output:C,corners:I,success:!0,message:"Document extracted successfully"}}catch(C){return{output:null,corners:I,success:!1,message:`Extraction failed: ${C.message}`}}}async function IA(A,I={}){const g=[],B=performance.now(),C=I.mode||"detect",Q=I.output||"canvas";I.debug;const t=I.maxProcessingDimension||800;let i,E,e,o;if("ml"===(I.detector||"classical")){E=performance.now();const{detectDocumentMl:B}=await import("./mlDetector-DV0KqH2X.js");i=await B(A,I.ml||{}),g.push({step:"ML Detection Total",ms:(performance.now()-E).toFixed(2)}),i.timings&&i.timings.forEach(A=>g.push(A))}else{E=performance.now();const{grayscaleData:B,scaleFactor:C,scaledDimensions:Q}=await async function(A,I=800){let g,B;const C=A&&"number"==typeof A.width&&"number"==typeof A.height&&A.data;if(C)g=A.width,B=A.height;else{if(!A)throw new Error("No image provided");g=A.width||A.naturalWidth,B=A.height||A.naturalHeight}const Q=Math.max(g,B);let t,i,E;if(Q<=I)t=g,i=B,E=1;else{const A=I/Q;t=Math.round(g*A),i=Math.round(B*A),E=1/A}const e="undefined"!=typeof OffscreenCanvas,o=e?new OffscreenCanvas(t,i):document.createElement("canvas");e||(o.width=t,o.height=i);const n=o.getContext("2d",{willReadFrequently:!0});if(n.imageSmoothingEnabled=!0,n.imageSmoothingQuality="medium",C){const I=e?new OffscreenCanvas(g,B):document.createElement("canvas");e||(I.width=g,I.height=B),I.getContext("2d").putImageData(A,0,0),n.drawImage(I,0,0,g,B,0,0,t,i)}else n.drawImage(A,0,0,g,B,0,0,t,i);const a=n.getImageData(0,0,t,i),s=a.data,c=new Uint8ClampedArray(t*i);for(let r=0,h=0;r<s.length;r+=4,h++){const A=54*s[r]+183*s[r+1]+19*s[r+2]>>8;c[h]=A,s[r]=s[r+1]=s[r+2]=A}return{grayscaleData:c,imageData:a,scaleFactor:E,originalDimensions:{width:g,height:B},scaledDimensions:{width:t,height:i}}}(A,t);g.push({step:"Image Prep + Scale + Gray",ms:(performance.now()-E).toFixed(2)}),i=await _(B,Q.width,Q.height,C,I),i.timings&&i.timings.forEach(A=>g.push(A))}if(!i.success){const A=performance.now();return g.unshift({step:"Total",ms:(A-B).toFixed(2)}),{output:null,corners:null,contour:null,confidence:i.confidence||null,score:i.score??null,debug:i.debug,success:!1,message:i.message||"No document detected",timings:g}}"detect"===C?o=null:"extract"===C&&(E=performance.now(),e=document.createElement("canvas"),$(e.getContext("2d"),A,i.corners),g.push({step:"Perspective Transform",ms:(performance.now()-E).toFixed(2)})),"detect"!==C&&e&&(E=performance.now(),o="canvas"===Q?e:"imagedata"===Q?e.getContext("2d").getImageData(0,0,e.width,e.height):"dataurl"===Q?e.toDataURL():e,g.push({step:"Output Conversion",ms:(performance.now()-E).toFixed(2)}));const n=performance.now();return g.unshift({step:"Total",ms:(n-B).toFixed(2)}),{output:o,corners:i.corners,contour:i.contour??null,confidence:i.confidence||null,score:i.score??null,debug:i.debug,success:!0,message:"Document detected",timings:g}}export{P as Scanner,T as createCornerEditor,AA as extractDocument,W as initialize,IA as scanDocument};