scanic 1.2.0 → 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,2860 +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/IAEoAgQhAiABKAIAIQNBCEEEEDUiAUUEQEEEQQgQOQALIAEgAjYCBCABIAM2AgAgAEHcksAANgIEIAAgATYCAAvGAgACQCAAIAJNBEAgACABTSABIAJLcg0BIwBBMGsiAiQAIAIgATYCBCACIAA2AgAgAkECNgIMIAJB2JbAADYCCCACQgI3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IAAAABwAEAAMAAAAEgAAABcAAAAcABAADAAAABcAAAAZAAAAHAAQAAwAAAAUAAAAGQAAABwAEAAMAAAAUQAAAB4AAAAcABAADAAAAFIAAAAeAAAATGF6eSBpbnN0YW5jZSBoYXMgcHJldmlvdXNseSBiZWVuIHBvaXNvbmVkAAA8CBAAKgAAAEgBEABaAAAACAMAABkAAAByZWVudHJhbnQgaW5pdAAAgAgQAA4AAABIARAAWgAAAHoCAAANAAAAfP2LMlfmV/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 normalizeImageToCanvas(image) {
1178
- if (!image) {
1179
- throw new Error("No image provided");
1180
- }
1181
- const isImageData = image && typeof image.width === "number" && typeof image.height === "number" && image.data;
1182
- let width = 0;
1183
- let height = 0;
1184
- if (isImageData) {
1185
- width = image.width;
1186
- height = image.height;
1187
- } else {
1188
- width = image.width || image.naturalWidth;
1189
- height = image.height || image.naturalHeight;
1190
- }
1191
- if (!width || !height) {
1192
- throw new Error("Image must be loaded before creating the corner editor");
1193
- }
1194
- const canvas = document.createElement("canvas");
1195
- canvas.width = width;
1196
- canvas.height = height;
1197
- const ctx = canvas.getContext("2d", { willReadFrequently: true });
1198
- if (!ctx) {
1199
- throw new Error("Failed to create 2D canvas context for source image");
1200
- }
1201
- if (isImageData) {
1202
- ctx.putImageData(image, 0, 0);
1203
- } else {
1204
- ctx.drawImage(image, 0, 0, width, height);
1205
- }
1206
- return { canvas, width, height };
1207
- }
1208
- const STYLE_ID = "scanic-corner-editor-styles";
1209
- const EDITOR_CSS = `
1210
- .scanic-corner-editor {
1211
- --scanic-accent: #6366f1;
1212
- --scanic-mask: rgba(15, 23, 42, 0.45);
1213
- --scanic-edge-color: var(--scanic-accent);
1214
- --scanic-edge-width: 2.5;
1215
- --scanic-handle-size: 20px;
1216
- --scanic-handle-hit: 44px;
1217
- --scanic-handle-color: #ffffff;
1218
- --scanic-handle-ring: 2px;
1219
- --scanic-handle-ring-color: var(--scanic-accent);
1220
- --scanic-handle-shadow: 0 1px 4px rgba(15, 23, 42, 0.45);
1221
- --scanic-handle-active-shadow: 0 8px 22px rgba(15, 23, 42, 0.5);
1222
- --scanic-handle-active-scale: 1.28;
1223
- --scanic-surface: rgba(15, 23, 42, 0.92);
1224
- --scanic-surface-fg: #e2e8f0;
1225
- --scanic-surface-radius: 12px;
1226
- }
1227
- .scanic-handle {
1228
- position: absolute;
1229
- box-sizing: border-box;
1230
- width: var(--scanic-handle-size);
1231
- height: var(--scanic-handle-size);
1232
- margin: 0;
1233
- padding: 0;
1234
- border: var(--scanic-handle-ring) solid var(--scanic-handle-ring-color);
1235
- border-radius: 50%;
1236
- background: var(--scanic-handle-color);
1237
- box-shadow: var(--scanic-handle-shadow);
1238
- transform: translate(-50%, -50%);
1239
- cursor: grab;
1240
- touch-action: none;
1241
- z-index: 2;
1242
- transition: transform 0.12s ease, box-shadow 0.12s ease,
1243
- background 0.12s ease, border-color 0.12s ease;
1244
- }
1245
- /* Enlarges the pointer/touch target without changing the visual size. */
1246
- .scanic-handle::after {
1247
- content: '';
1248
- position: absolute;
1249
- left: 50%;
1250
- top: 50%;
1251
- width: var(--scanic-handle-hit);
1252
- height: var(--scanic-handle-hit);
1253
- transform: translate(-50%, -50%);
1254
- border-radius: 50%;
1255
- }
1256
- /* Soft accent halo that blooms when a handle is grabbed — the "depth" layer. */
1257
- .scanic-handle::before {
1258
- content: '';
1259
- position: absolute;
1260
- left: 50%;
1261
- top: 50%;
1262
- width: 0;
1263
- height: 0;
1264
- border-radius: 50%;
1265
- background: var(--scanic-accent);
1266
- opacity: 0;
1267
- transform: translate(-50%, -50%);
1268
- transition: opacity 0.15s ease, width 0.15s ease, height 0.15s ease;
1269
- pointer-events: none;
1270
- }
1271
- .scanic-handle:hover {
1272
- transform: translate(-50%, -50%) scale(1.12);
1273
- }
1274
- .scanic-handle:focus-visible {
1275
- outline: none;
1276
- box-shadow: 0 0 0 3px color-mix(in srgb, var(--scanic-accent) 45%, transparent),
1277
- var(--scanic-handle-shadow);
1278
- }
1279
- /* The currently selected corner — the target of nudges / keyboard. */
1280
- .scanic-handle.is-selected {
1281
- border-color: var(--scanic-accent);
1282
- transform: translate(-50%, -50%) scale(1.12);
1283
- }
1284
- .scanic-handle.is-selected::before {
1285
- opacity: 0.16;
1286
- width: calc(var(--scanic-handle-size) * 2);
1287
- height: calc(var(--scanic-handle-size) * 2);
1288
- }
1289
- .scanic-handle.is-active {
1290
- cursor: grabbing;
1291
- background: var(--scanic-accent);
1292
- border-color: #ffffff;
1293
- transform: translate(-50%, -50%) scale(var(--scanic-handle-active-scale));
1294
- box-shadow: var(--scanic-handle-active-shadow);
1295
- }
1296
- .scanic-handle.is-active::before {
1297
- opacity: 0.22;
1298
- width: calc(var(--scanic-handle-size) * 2.8);
1299
- height: calc(var(--scanic-handle-size) * 2.8);
1300
- }
1301
- .scanic-toolbar,
1302
- .scanic-nudges {
1303
- position: absolute;
1304
- z-index: 3;
1305
- display: flex;
1306
- gap: 4px;
1307
- padding: 5px;
1308
- background: var(--scanic-surface);
1309
- border-radius: var(--scanic-surface-radius);
1310
- -webkit-backdrop-filter: blur(8px);
1311
- backdrop-filter: blur(8px);
1312
- box-shadow: 0 6px 20px rgba(15, 23, 42, 0.35);
1313
- }
1314
- .scanic-toolbar {
1315
- left: 50%;
1316
- bottom: 12px;
1317
- transform: translateX(-50%);
1318
- }
1319
- .scanic-nudges {
1320
- top: 10px;
1321
- right: 10px;
1322
- display: grid;
1323
- grid-template-columns: repeat(4, auto);
1324
- gap: 3px;
1325
- }
1326
- .scanic-toolbar button,
1327
- .scanic-nudges button {
1328
- display: grid;
1329
- place-items: center;
1330
- margin: 0;
1331
- padding: 0;
1332
- font-family: inherit;
1333
- font-size: 15px;
1334
- font-weight: 600;
1335
- line-height: 1;
1336
- color: var(--scanic-surface-fg);
1337
- background: transparent;
1338
- border: 0;
1339
- border-radius: 8px;
1340
- cursor: pointer;
1341
- transition: background 0.12s ease, color 0.12s ease, filter 0.12s ease;
1342
- }
1343
- .scanic-toolbar button {
1344
- width: 34px;
1345
- height: 34px;
1346
- }
1347
- .scanic-nudges button {
1348
- width: 30px;
1349
- height: 30px;
1350
- }
1351
- .scanic-toolbar button svg {
1352
- width: 18px;
1353
- height: 18px;
1354
- display: block;
1355
- }
1356
- .scanic-toolbar button:hover,
1357
- .scanic-nudges button:hover {
1358
- background: rgba(255, 255, 255, 0.14);
1359
- }
1360
- .scanic-toolbar button:focus-visible,
1361
- .scanic-nudges button:focus-visible {
1362
- outline: 2px solid var(--scanic-accent);
1363
- outline-offset: 1px;
1364
- }
1365
- .scanic-toolbar .scanic-btn-apply {
1366
- background: var(--scanic-accent);
1367
- color: #ffffff;
1368
- }
1369
- .scanic-toolbar .scanic-btn-apply:hover {
1370
- background: var(--scanic-accent);
1371
- filter: brightness(1.1);
1372
- }
1373
- .scanic-toolbar .scanic-btn-expert.is-on {
1374
- background: color-mix(in srgb, var(--scanic-accent) 32%, transparent);
1375
- color: #ffffff;
1376
- }
1377
- `;
1378
- const ICONS = {
1379
- reset: '<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>',
1380
- cancel: '<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>',
1381
- apply: '<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>',
1382
- expert: '<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>'
1383
- };
1384
- function injectStyles(doc) {
1385
- if (!doc || typeof doc.getElementById !== "function") return;
1386
- if (doc.getElementById(STYLE_ID)) return;
1387
- const style = doc.createElement("style");
1388
- style.id = STYLE_ID;
1389
- style.textContent = EDITOR_CSS;
1390
- (doc.head || doc.documentElement).appendChild(style);
1391
- }
1392
- function createCornerEditor(options = {}) {
1393
- var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o, _p, _q, _r, _s, _t, _u, _v;
1394
- const container = options.container;
1395
- if (!container || typeof container.appendChild !== "function") {
1396
- throw new Error("createCornerEditor requires a valid container element");
1397
- }
1398
- const doc = container.ownerDocument || (typeof document !== "undefined" ? document : null);
1399
- const runtimeGlobal = typeof window !== "undefined" ? window : globalThis;
1400
- if (options.injectStyles !== false) {
1401
- injectStyles(doc);
1402
- }
1403
- const { canvas: sourceCanvas, width: imageWidth, height: imageHeight } = normalizeImageToCanvas(options.image);
1404
- const addedRootClass = !container.classList.contains("scanic-corner-editor");
1405
- container.classList.add("scanic-corner-editor");
1406
- if ((_a = options.classNames) == null ? void 0 : _a.root) container.classList.add(options.classNames.root);
1407
- const restoreContainerStyle = {
1408
- position: container.style.position,
1409
- minHeight: container.style.minHeight
1410
- };
1411
- let changedContainerPosition = false;
1412
- let changedContainerMinHeight = false;
1413
- if (getComputedStyle(container).position === "static") {
1414
- container.style.position = "relative";
1415
- changedContainerPosition = true;
1416
- }
1417
- applyThemeOption(options.theme);
1418
- const editorCanvas = doc.createElement("canvas");
1419
- editorCanvas.style.position = "absolute";
1420
- editorCanvas.style.top = "0";
1421
- editorCanvas.style.left = "0";
1422
- editorCanvas.style.display = "block";
1423
- editorCanvas.style.touchAction = "none";
1424
- editorCanvas.style.userSelect = "none";
1425
- editorCanvas.style.webkitUserSelect = "none";
1426
- editorCanvas.style.cursor = "default";
1427
- container.appendChild(editorCanvas);
1428
- const ctx = editorCanvas.getContext("2d");
1429
- if (!ctx) {
1430
- throw new Error("Failed to create 2D canvas context for corner editor");
1431
- }
1432
- const magnifier = {
1433
- enabled: ((_b = options.magnifier) == null ? void 0 : _b.enabled) !== false,
1434
- size: ((_c = options.magnifier) == null ? void 0 : _c.size) || 120,
1435
- zoom: ((_d = options.magnifier) == null ? void 0 : _d.zoom) || 2,
1436
- margin: ((_e = options.magnifier) == null ? void 0 : _e.margin) || 8,
1437
- borderColor: ((_f = options.magnifier) == null ? void 0 : _f.borderColor) || "#ffffff",
1438
- borderWidth: ((_g = options.magnifier) == null ? void 0 : _g.borderWidth) || 2,
1439
- crosshairColor: ((_h = options.magnifier) == null ? void 0 : _h.crosshairColor) || "#ffffff",
1440
- crosshairSize: ((_i = options.magnifier) == null ? void 0 : _i.crosshairSize) || 18
1441
- };
1442
- const nudges = {
1443
- enabled: !!((_j = options.nudges) == null ? void 0 : _j.enabled),
1444
- steps: (((_k = options.nudges) == null ? void 0 : _k.steps) && options.nudges.steps.length ? options.nudges.steps : [1, 10]).map((v) => Math.max(1, Math.round(v)))
1445
- };
1446
- const toolbar = {
1447
- enabled: ((_l = options.toolbar) == null ? void 0 : _l.enabled) !== false,
1448
- reset: ((_m = options.toolbar) == null ? void 0 : _m.reset) !== false,
1449
- cancel: ((_n = options.toolbar) == null ? void 0 : _n.cancel) !== false,
1450
- apply: ((_o = options.toolbar) == null ? void 0 : _o.apply) !== false,
1451
- labels: {
1452
- reset: ((_q = (_p = options.toolbar) == null ? void 0 : _p.labels) == null ? void 0 : _q.reset) || "Reset",
1453
- cancel: ((_s = (_r = options.toolbar) == null ? void 0 : _r.labels) == null ? void 0 : _s.cancel) || "Cancel",
1454
- apply: ((_u = (_t = options.toolbar) == null ? void 0 : _t.labels) == null ? void 0 : _u.apply) || "Apply"
1455
- }
1456
- };
1457
- const keyboardEnabled = options.keyboard !== false;
1458
- const defaultCorners = createDefaultCorners(imageWidth, imageHeight);
1459
- const requestedCorners = options.corners ? cloneCorners(options.corners) : defaultCorners;
1460
- let corners = cornersAreFiniteAndDistinct$1(requestedCorners) && isConvexQuadrilateral$1(requestedCorners) ? requestedCorners : defaultCorners;
1461
- const initialCorners = cloneCorners(corners);
1462
- let isDestroyed = false;
1463
- let activeCornerKey = null;
1464
- let focusedCornerKey = "topLeft";
1465
- let dragPointerId = null;
1466
- let lastPointerPosition = null;
1467
- Math.max(24, options.handleHitArea || 44);
1468
- const cornerOrder = ["topLeft", "topRight", "bottomRight", "bottomLeft"];
1469
- const cornerLabels = {
1470
- topLeft: "Top-left corner",
1471
- topRight: "Top-right corner",
1472
- bottomRight: "Bottom-right corner",
1473
- bottomLeft: "Bottom-left corner"
1474
- };
1475
- let view = { scale: 1, offsetX: 0, offsetY: 0, width: 1, height: 1 };
1476
- const resolved = { mask: "rgba(15,23,42,0.45)", edgeColor: "#6366f1", edgeWidth: 2.5 };
1477
- const handleEls = {};
1478
- for (const key of cornerOrder) {
1479
- const el = doc.createElement("button");
1480
- el.type = "button";
1481
- el.className = "scanic-handle" + (((_v = options.classNames) == null ? void 0 : _v.handle) ? " " + options.classNames.handle : "");
1482
- el.dataset.corner = key;
1483
- el.setAttribute("aria-label", cornerLabels[key]);
1484
- if (!keyboardEnabled) el.tabIndex = -1;
1485
- el.addEventListener("pointerdown", (e) => onHandlePointerDown(key, e));
1486
- el.addEventListener("pointermove", (e) => onHandlePointerMove(key, e));
1487
- el.addEventListener("pointerup", (e) => onHandlePointerUp(key, e));
1488
- el.addEventListener("pointercancel", (e) => onHandlePointerUp(key, e));
1489
- el.addEventListener("focus", () => {
1490
- focusedCornerKey = key;
1491
- updateSelected();
1492
- });
1493
- if (keyboardEnabled) el.addEventListener("keydown", (e) => onHandleKeyDown(key, e));
1494
- container.appendChild(el);
1495
- handleEls[key] = el;
1496
- }
1497
- function resolveColor(expr, fallback) {
1498
- const prev = editorCanvas.style.color;
1499
- editorCanvas.style.color = `var(${expr}, ${fallback})`;
1500
- const value = getComputedStyle(editorCanvas).color || fallback;
1501
- editorCanvas.style.color = prev;
1502
- return value;
1503
- }
1504
- function resolveTheme() {
1505
- resolved.mask = resolveColor("--scanic-mask", "rgba(15, 23, 42, 0.45)");
1506
- resolved.edgeColor = resolveColor("--scanic-edge-color", "#6366f1");
1507
- const widthRaw = getComputedStyle(container).getPropertyValue("--scanic-edge-width");
1508
- const parsed = parseFloat(widthRaw);
1509
- resolved.edgeWidth = Number.isFinite(parsed) && parsed > 0 ? parsed : 2.5;
1510
- }
1511
- function applyThemeOption(theme) {
1512
- if (!theme || typeof theme !== "object") return;
1513
- const pxKeys = /* @__PURE__ */ new Set(["handleSize", "handleHit"]);
1514
- const map = {
1515
- accent: "--scanic-accent",
1516
- mask: "--scanic-mask",
1517
- edgeColor: "--scanic-edge-color",
1518
- edgeWidth: "--scanic-edge-width",
1519
- handleSize: "--scanic-handle-size",
1520
- handleHit: "--scanic-handle-hit",
1521
- handleColor: "--scanic-handle-color",
1522
- handleRingColor: "--scanic-handle-ring-color",
1523
- surface: "--scanic-surface",
1524
- surfaceColor: "--scanic-surface-fg",
1525
- radius: "--scanic-surface-radius"
1526
- };
1527
- for (const [key, cssVar] of Object.entries(map)) {
1528
- const value = theme[key];
1529
- if (value == null) continue;
1530
- const cssValue = typeof value === "number" && pxKeys.has(key) ? `${value}px` : String(value);
1531
- container.style.setProperty(cssVar, cssValue);
1532
- }
1533
- }
1534
- function emitChange() {
1535
- if (typeof options.onChange === "function") {
1536
- options.onChange(cloneCorners(corners));
1537
- }
1538
- }
1539
- let lastDisplayWidth = 0;
1540
- let lastDisplayHeight = 0;
1541
- function computeDisplaySize() {
1542
- const width = Math.max(1, Math.round(container.clientWidth));
1543
- let height = Math.round(container.clientHeight);
1544
- if (height < 80) {
1545
- const aspect = imageHeight / Math.max(1, imageWidth);
1546
- const viewportCap = (runtimeGlobal.innerHeight || 800) * 0.7;
1547
- height = Math.max(240, Math.round(Math.min(width * aspect, viewportCap)));
1548
- container.style.minHeight = height + "px";
1549
- changedContainerMinHeight = true;
1550
- }
1551
- return { width, height: Math.max(1, height) };
1552
- }
1553
- function updateCanvasSize() {
1554
- const { width, height } = computeDisplaySize();
1555
- const dpr = runtimeGlobal.devicePixelRatio || 1;
1556
- lastDisplayWidth = width;
1557
- lastDisplayHeight = height;
1558
- editorCanvas.style.width = width + "px";
1559
- editorCanvas.style.height = height + "px";
1560
- const bufferWidth = Math.round(width * dpr);
1561
- const bufferHeight = Math.round(height * dpr);
1562
- if (editorCanvas.width !== bufferWidth) editorCanvas.width = bufferWidth;
1563
- if (editorCanvas.height !== bufferHeight) editorCanvas.height = bufferHeight;
1564
- ctx.setTransform(dpr, 0, 0, dpr, 0, 0);
1565
- const padding = 12;
1566
- const availableWidth = Math.max(1, width - padding * 2);
1567
- const availableHeight = Math.max(1, height - padding * 2);
1568
- const scale = Math.min(availableWidth / imageWidth, availableHeight / imageHeight);
1569
- const drawWidth = imageWidth * scale;
1570
- const drawHeight = imageHeight * scale;
1571
- view = {
1572
- scale,
1573
- offsetX: Math.round((width - drawWidth) / 2),
1574
- offsetY: Math.round((height - drawHeight) / 2),
1575
- width,
1576
- height
1577
- };
1578
- resolveTheme();
1579
- }
1580
- function imageToView(point) {
1581
- return {
1582
- x: view.offsetX + point.x * view.scale,
1583
- y: view.offsetY + point.y * view.scale
1584
- };
1585
- }
1586
- function viewToImage(x, y) {
1587
- const px = (x - view.offsetX) / view.scale;
1588
- const py = (y - view.offsetY) / view.scale;
1589
- return {
1590
- x: Math.max(0, Math.min(imageWidth, px)),
1591
- y: Math.max(0, Math.min(imageHeight, py))
1592
- };
1593
- }
1594
- function cornersValid(nextCorners) {
1595
- return cornersAreFiniteAndDistinct$1(nextCorners) && isConvexQuadrilateral$1(nextCorners);
1596
- }
1597
- function getEventCanvasPoint(event) {
1598
- const rect = editorCanvas.getBoundingClientRect();
1599
- return {
1600
- x: event.clientX - rect.left,
1601
- y: event.clientY - rect.top
1602
- };
1603
- }
1604
- function drawOverlay() {
1605
- const points = cornerOrder.map((key) => imageToView(corners[key]));
1606
- ctx.save();
1607
- ctx.fillStyle = resolved.mask;
1608
- ctx.beginPath();
1609
- ctx.rect(0, 0, view.width, view.height);
1610
- ctx.moveTo(points[0].x, points[0].y);
1611
- for (let i = 1; i < points.length; i++) ctx.lineTo(points[i].x, points[i].y);
1612
- ctx.closePath();
1613
- ctx.fill("evenodd");
1614
- ctx.restore();
1615
- ctx.save();
1616
- ctx.strokeStyle = resolved.edgeColor;
1617
- ctx.lineWidth = resolved.edgeWidth;
1618
- ctx.lineJoin = "round";
1619
- ctx.beginPath();
1620
- ctx.moveTo(points[0].x, points[0].y);
1621
- for (let i = 1; i < points.length; i++) ctx.lineTo(points[i].x, points[i].y);
1622
- ctx.closePath();
1623
- ctx.stroke();
1624
- ctx.restore();
1625
- }
1626
- function drawMagnifier() {
1627
- if (!magnifier.enabled || !activeCornerKey || !lastPointerPosition) {
1628
- return;
1629
- }
1630
- const active = corners[activeCornerKey];
1631
- const size = magnifier.size;
1632
- const radius = size / 2;
1633
- const zoom = Math.max(1.1, magnifier.zoom);
1634
- let lensX = lastPointerPosition.x + radius + magnifier.margin;
1635
- let lensY = lastPointerPosition.y - radius - magnifier.margin;
1636
- if (lensX + radius > view.width) {
1637
- lensX = lastPointerPosition.x - radius - magnifier.margin;
1638
- }
1639
- if (lensY - radius < 0) {
1640
- lensY = lastPointerPosition.y + radius + magnifier.margin;
1641
- }
1642
- lensX = Math.max(radius + 2, Math.min(view.width - radius - 2, lensX));
1643
- lensY = Math.max(radius + 2, Math.min(view.height - radius - 2, lensY));
1644
- const sampleSize = size / zoom;
1645
- const sx = Math.max(0, Math.min(imageWidth - sampleSize, active.x - sampleSize / 2));
1646
- const sy = Math.max(0, Math.min(imageHeight - sampleSize, active.y - sampleSize / 2));
1647
- ctx.save();
1648
- ctx.beginPath();
1649
- ctx.arc(lensX, lensY, radius, 0, Math.PI * 2);
1650
- ctx.clip();
1651
- ctx.drawImage(sourceCanvas, sx, sy, sampleSize, sampleSize, lensX - radius, lensY - radius, size, size);
1652
- ctx.restore();
1653
- ctx.save();
1654
- ctx.beginPath();
1655
- ctx.arc(lensX, lensY, radius, 0, Math.PI * 2);
1656
- ctx.lineWidth = magnifier.borderWidth;
1657
- ctx.strokeStyle = magnifier.borderColor;
1658
- ctx.stroke();
1659
- const ch = magnifier.crosshairSize / 2;
1660
- ctx.strokeStyle = magnifier.crosshairColor;
1661
- ctx.lineWidth = 1.5;
1662
- ctx.beginPath();
1663
- ctx.moveTo(lensX - ch, lensY);
1664
- ctx.lineTo(lensX + ch, lensY);
1665
- ctx.moveTo(lensX, lensY - ch);
1666
- ctx.lineTo(lensX, lensY + ch);
1667
- ctx.stroke();
1668
- ctx.restore();
1669
- }
1670
- function positionHandles() {
1671
- for (const key of cornerOrder) {
1672
- const p = imageToView(corners[key]);
1673
- const el = handleEls[key];
1674
- el.style.left = p.x + "px";
1675
- el.style.top = p.y + "px";
1676
- }
1677
- }
1678
- function updateSelected() {
1679
- for (const key of cornerOrder) {
1680
- handleEls[key].classList.toggle("is-selected", key === focusedCornerKey);
1681
- }
1682
- }
1683
- function render() {
1684
- if (isDestroyed) return;
1685
- ctx.clearRect(0, 0, view.width, view.height);
1686
- ctx.drawImage(
1687
- sourceCanvas,
1688
- 0,
1689
- 0,
1690
- imageWidth,
1691
- imageHeight,
1692
- view.offsetX,
1693
- view.offsetY,
1694
- imageWidth * view.scale,
1695
- imageHeight * view.scale
1696
- );
1697
- drawOverlay();
1698
- drawMagnifier();
1699
- positionHandles();
1700
- }
1701
- const raf = typeof runtimeGlobal.requestAnimationFrame === "function" ? runtimeGlobal.requestAnimationFrame.bind(runtimeGlobal) : null;
1702
- const caf = typeof runtimeGlobal.cancelAnimationFrame === "function" ? runtimeGlobal.cancelAnimationFrame.bind(runtimeGlobal) : null;
1703
- let rafId = null;
1704
- function scheduleRender() {
1705
- if (isDestroyed) return;
1706
- if (!raf) {
1707
- render();
1708
- return;
1709
- }
1710
- if (rafId !== null) return;
1711
- rafId = raf(() => {
1712
- rafId = null;
1713
- render();
1714
- });
1715
- }
1716
- function setCorner(nextCornerKey, nextPoint) {
1717
- const candidate = cloneCorners(corners);
1718
- candidate[nextCornerKey] = {
1719
- x: Math.max(0, Math.min(imageWidth, nextPoint.x)),
1720
- y: Math.max(0, Math.min(imageHeight, nextPoint.y))
1721
- };
1722
- if (!cornersValid(candidate)) {
1723
- return false;
1724
- }
1725
- corners = candidate;
1726
- focusedCornerKey = nextCornerKey;
1727
- updateSelected();
1728
- emitChange();
1729
- scheduleRender();
1730
- return true;
1731
- }
1732
- function onHandlePointerDown(key, event) {
1733
- if (isDestroyed) return;
1734
- if (typeof event.preventDefault === "function") event.preventDefault();
1735
- activeCornerKey = key;
1736
- focusedCornerKey = key;
1737
- updateSelected();
1738
- dragPointerId = event.pointerId;
1739
- lastPointerPosition = getEventCanvasPoint(event);
1740
- handleEls[key].classList.add("is-active");
1741
- if (keyboardEnabled && typeof handleEls[key].focus === "function") {
1742
- try {
1743
- handleEls[key].focus({ preventScroll: true });
1744
- } catch (_) {
1745
- handleEls[key].focus();
1746
- }
1747
- }
1748
- if (handleEls[key].setPointerCapture && event.pointerId != null) {
1749
- try {
1750
- handleEls[key].setPointerCapture(event.pointerId);
1751
- } catch (_) {
1752
- }
1753
- }
1754
- scheduleRender();
1755
- }
1756
- function onHandlePointerMove(key, event) {
1757
- if (isDestroyed || activeCornerKey !== key) return;
1758
- if (dragPointerId !== null && event.pointerId !== dragPointerId) return;
1759
- lastPointerPosition = getEventCanvasPoint(event);
1760
- setCorner(key, viewToImage(lastPointerPosition.x, lastPointerPosition.y));
1761
- }
1762
- function onHandlePointerUp(key, event) {
1763
- if (activeCornerKey !== key) return;
1764
- if (dragPointerId !== null && event.pointerId !== dragPointerId) return;
1765
- if (handleEls[key].releasePointerCapture && dragPointerId != null) {
1766
- try {
1767
- handleEls[key].releasePointerCapture(dragPointerId);
1768
- } catch (_) {
1769
- }
1770
- }
1771
- handleEls[key].classList.remove("is-active");
1772
- activeCornerKey = null;
1773
- dragPointerId = null;
1774
- lastPointerPosition = null;
1775
- scheduleRender();
1776
- }
1777
- function onHandleKeyDown(key, event) {
1778
- if (isDestroyed || !keyboardEnabled) return;
1779
- if (event.key === "Enter") {
1780
- event.preventDefault();
1781
- confirmEditor();
1782
- return;
1783
- }
1784
- if (event.key === "Escape") {
1785
- event.preventDefault();
1786
- cancelEditor();
1787
- return;
1788
- }
1789
- let dx = 0;
1790
- let dy = 0;
1791
- if (event.key === "ArrowLeft") dx = -1;
1792
- else if (event.key === "ArrowRight") dx = 1;
1793
- else if (event.key === "ArrowUp") dy = -1;
1794
- else if (event.key === "ArrowDown") dy = 1;
1795
- else return;
1796
- event.preventDefault();
1797
- const step = event.shiftKey ? nudges.steps[nudges.steps.length - 1] || 10 : 1;
1798
- setCorner(key, { x: corners[key].x + dx * step, y: corners[key].y + dy * step });
1799
- }
1800
- let toolbarEl = null;
1801
- let nudgeControls = null;
1802
- let expertBtn = null;
1803
- let expertVisible = false;
1804
- const hasExpertToggle = toolbar.enabled && nudges.enabled;
1805
- function makeButton({ html, text, title, className, onClick }) {
1806
- const btn = doc.createElement("button");
1807
- btn.type = "button";
1808
- if (html != null) btn.innerHTML = html;
1809
- else btn.textContent = text;
1810
- if (title) {
1811
- btn.title = title;
1812
- btn.setAttribute("aria-label", title);
1813
- }
1814
- if (className) btn.className = className;
1815
- btn.addEventListener("click", onClick);
1816
- return btn;
1817
- }
1818
- function setExpert(on) {
1819
- expertVisible = on;
1820
- if (nudgeControls) nudgeControls.style.display = on ? "" : "none";
1821
- if (expertBtn) {
1822
- expertBtn.classList.toggle("is-on", on);
1823
- expertBtn.setAttribute("aria-pressed", String(on));
1824
- }
1825
- }
1826
- function buildToolbar() {
1827
- var _a2;
1828
- if (!toolbar.enabled) return;
1829
- if (!toolbar.reset && !toolbar.cancel && !toolbar.apply && !hasExpertToggle) return;
1830
- toolbarEl = doc.createElement("div");
1831
- toolbarEl.className = "scanic-toolbar" + (((_a2 = options.classNames) == null ? void 0 : _a2.toolbar) ? " " + options.classNames.toolbar : "");
1832
- if (toolbar.reset) {
1833
- toolbarEl.appendChild(makeButton({ html: ICONS.reset, title: toolbar.labels.reset, className: "scanic-btn-reset", onClick: () => publicReset() }));
1834
- }
1835
- if (hasExpertToggle) {
1836
- expertBtn = makeButton({ html: ICONS.expert, title: "Precision nudge (expert)", className: "scanic-btn-expert", onClick: () => setExpert(!expertVisible) });
1837
- expertBtn.setAttribute("aria-pressed", "false");
1838
- toolbarEl.appendChild(expertBtn);
1839
- }
1840
- if (toolbar.cancel) {
1841
- toolbarEl.appendChild(makeButton({ html: ICONS.cancel, title: toolbar.labels.cancel, className: "scanic-btn-cancel", onClick: () => cancelEditor() }));
1842
- }
1843
- if (toolbar.apply) {
1844
- toolbarEl.appendChild(makeButton({ html: ICONS.apply, title: toolbar.labels.apply, className: "scanic-btn-apply", onClick: () => confirmEditor() }));
1845
- }
1846
- container.appendChild(toolbarEl);
1847
- }
1848
- function buildNudgeControls() {
1849
- var _a2;
1850
- if (!nudges.enabled) return;
1851
- nudgeControls = doc.createElement("div");
1852
- nudgeControls.className = "scanic-nudges" + (((_a2 = options.classNames) == null ? void 0 : _a2.nudges) ? " " + options.classNames.nudges : "");
1853
- const makeNudge = (glyph, label, dx, dy, step) => makeButton({
1854
- text: glyph,
1855
- title: label,
1856
- className: "scanic-btn-nudge",
1857
- onClick: () => {
1858
- const current = corners[focusedCornerKey];
1859
- setCorner(focusedCornerKey, { x: current.x + dx * step, y: current.y + dy * step });
1860
- }
1861
- });
1862
- for (const step of nudges.steps) {
1863
- nudgeControls.appendChild(makeNudge("←", `Move left ${step}px`, -1, 0, step));
1864
- nudgeControls.appendChild(makeNudge("↑", `Move up ${step}px`, 0, -1, step));
1865
- nudgeControls.appendChild(makeNudge("↓", `Move down ${step}px`, 0, 1, step));
1866
- nudgeControls.appendChild(makeNudge("→", `Move right ${step}px`, 1, 0, step));
1867
- }
1868
- container.appendChild(nudgeControls);
1869
- if (hasExpertToggle) nudgeControls.style.display = "none";
1870
- }
1871
- let dprCleanup = null;
1872
- function watchDevicePixelRatio() {
1873
- if (typeof runtimeGlobal.matchMedia !== "function") return;
1874
- if (dprCleanup) dprCleanup();
1875
- const dpr = runtimeGlobal.devicePixelRatio || 1;
1876
- const mq = runtimeGlobal.matchMedia(`(resolution: ${dpr}dppx)`);
1877
- const onChange = () => {
1878
- if (isDestroyed) return;
1879
- updateCanvasSize();
1880
- scheduleRender();
1881
- watchDevicePixelRatio();
1882
- };
1883
- if (typeof mq.addEventListener === "function") {
1884
- mq.addEventListener("change", onChange);
1885
- dprCleanup = () => mq.removeEventListener("change", onChange);
1886
- } else if (typeof mq.addListener === "function") {
1887
- mq.addListener(onChange);
1888
- dprCleanup = () => mq.removeListener(onChange);
1889
- } else {
1890
- dprCleanup = null;
1891
- }
1892
- }
1893
- function confirmEditor() {
1894
- const output = cloneCorners(corners);
1895
- if (typeof options.onConfirm === "function") {
1896
- options.onConfirm(output);
1897
- }
1898
- return output;
1899
- }
1900
- function cancelEditor() {
1901
- if (typeof options.onCancel === "function") {
1902
- options.onCancel();
1903
- }
1904
- }
1905
- function publicReset() {
1906
- corners = cloneCorners(initialCorners);
1907
- emitChange();
1908
- scheduleRender();
1909
- }
1910
- const resizeObserver = typeof ResizeObserver !== "undefined" ? new ResizeObserver(() => {
1911
- if (isDestroyed) return;
1912
- const next = computeDisplaySize();
1913
- if (next.width === lastDisplayWidth && next.height === lastDisplayHeight) {
1914
- return;
1915
- }
1916
- updateCanvasSize();
1917
- scheduleRender();
1918
- }) : null;
1919
- updateCanvasSize();
1920
- buildToolbar();
1921
- buildNudgeControls();
1922
- watchDevicePixelRatio();
1923
- if (resizeObserver) {
1924
- resizeObserver.observe(container);
1925
- }
1926
- updateSelected();
1927
- render();
1928
- return {
1929
- getCorners() {
1930
- return cloneCorners(corners);
1931
- },
1932
- setCorners(nextCorners) {
1933
- if (!nextCorners || !cornersValid(nextCorners)) {
1934
- return false;
1935
- }
1936
- corners = cloneCorners(nextCorners);
1937
- emitChange();
1938
- scheduleRender();
1939
- return true;
1940
- },
1941
- reset() {
1942
- publicReset();
1943
- },
1944
- nudge(cornerKey, dx, dy, step = 1) {
1945
- if (!cornerOrder.includes(cornerKey)) {
1946
- return false;
1947
- }
1948
- return setCorner(cornerKey, {
1949
- x: corners[cornerKey].x + dx * step,
1950
- y: corners[cornerKey].y + dy * step
1951
- });
1952
- },
1953
- /** Re-read CSS variables into the canvas layer after a runtime theme change. */
1954
- refreshTheme(theme) {
1955
- applyThemeOption(theme);
1956
- resolveTheme();
1957
- scheduleRender();
1958
- },
1959
- confirm() {
1960
- return confirmEditor();
1961
- },
1962
- cancel() {
1963
- cancelEditor();
1964
- },
1965
- destroy() {
1966
- var _a2;
1967
- if (isDestroyed) return;
1968
- isDestroyed = true;
1969
- if (rafId !== null && caf) {
1970
- caf(rafId);
1971
- rafId = null;
1972
- }
1973
- if (dprCleanup) {
1974
- dprCleanup();
1975
- dprCleanup = null;
1976
- }
1977
- if (resizeObserver) {
1978
- resizeObserver.disconnect();
1979
- }
1980
- for (const key of cornerOrder) {
1981
- const el = handleEls[key];
1982
- if (el && el.parentNode) el.parentNode.removeChild(el);
1983
- }
1984
- if (toolbarEl && toolbarEl.parentNode) toolbarEl.parentNode.removeChild(toolbarEl);
1985
- if (nudgeControls && nudgeControls.parentNode) nudgeControls.parentNode.removeChild(nudgeControls);
1986
- if (editorCanvas.parentNode) editorCanvas.parentNode.removeChild(editorCanvas);
1987
- if (addedRootClass) container.classList.remove("scanic-corner-editor");
1988
- if ((_a2 = options.classNames) == null ? void 0 : _a2.root) container.classList.remove(options.classNames.root);
1989
- if (changedContainerPosition) {
1990
- container.style.position = restoreContainerStyle.position;
1991
- }
1992
- if (changedContainerMinHeight) {
1993
- container.style.minHeight = restoreContainerStyle.minHeight;
1994
- }
1995
- }
1996
- };
1997
- }
1998
- async function initialize() {
1999
- try {
2000
- return await initializeWasm();
2001
- } catch {
2002
- return null;
2003
- }
2004
- }
2005
- class Scanner {
2006
- constructor(options = {}) {
2007
- this.defaultOptions = {
2008
- maxProcessingDimension: 800,
2009
- mode: "detect",
2010
- output: "canvas",
2011
- ...options
2012
- };
2013
- this.initialized = false;
2014
- }
2015
- /**
2016
- * Warm up the scanner (load WASM, etc.)
2017
- */
2018
- async initialize() {
2019
- if (this.initialized) return;
2020
- try {
2021
- await initializeWasm();
2022
- } catch {
2023
- }
2024
- this.initialized = true;
2025
- }
2026
- /**
2027
- * Scan an image for a document.
2028
- * @param {HTMLImageElement|HTMLCanvasElement|ImageData} image
2029
- * @param {Object} options Override default options
2030
- */
2031
- async scan(image, options = {}) {
2032
- if (!this.initialized) await this.initialize();
2033
- const combinedOptions = { ...this.defaultOptions, ...options };
2034
- return await scanDocument(image, combinedOptions);
2035
- }
2036
- /**
2037
- * Extract a document from an image using manual corners.
2038
- * @param {HTMLImageElement|HTMLCanvasElement|ImageData} image
2039
- * @param {Object} corners
2040
- * @param {Object} options
2041
- */
2042
- async extract(image, corners, options = {}) {
2043
- if (!this.initialized) await this.initialize();
2044
- const combinedOptions = { ...this.defaultOptions, ...options };
2045
- return await extractDocument(image, corners, combinedOptions);
2046
- }
2047
- }
2048
- async function prepareScaleAndGrayscale(image, maxDimension = 800) {
2049
- let originalWidth, originalHeight;
2050
- const isImageData = image && typeof image.width === "number" && typeof image.height === "number" && image.data;
2051
- if (isImageData) {
2052
- originalWidth = image.width;
2053
- originalHeight = image.height;
2054
- } else if (image) {
2055
- originalWidth = image.width || image.naturalWidth;
2056
- originalHeight = image.height || image.naturalHeight;
2057
- } else {
2058
- throw new Error("No image provided");
2059
- }
2060
- const maxCurrentDimension = Math.max(originalWidth, originalHeight);
2061
- let targetWidth, targetHeight, scaleFactor;
2062
- if (maxCurrentDimension <= maxDimension) {
2063
- targetWidth = originalWidth;
2064
- targetHeight = originalHeight;
2065
- scaleFactor = 1;
2066
- } else {
2067
- const scale = maxDimension / maxCurrentDimension;
2068
- targetWidth = Math.round(originalWidth * scale);
2069
- targetHeight = Math.round(originalHeight * scale);
2070
- scaleFactor = 1 / scale;
2071
- }
2072
- const useOffscreen = typeof OffscreenCanvas !== "undefined";
2073
- const canvas = useOffscreen ? new OffscreenCanvas(targetWidth, targetHeight) : document.createElement("canvas");
2074
- if (!useOffscreen) {
2075
- canvas.width = targetWidth;
2076
- canvas.height = targetHeight;
2077
- }
2078
- const ctx = canvas.getContext("2d", { willReadFrequently: true });
2079
- ctx.imageSmoothingEnabled = true;
2080
- ctx.imageSmoothingQuality = "medium";
2081
- if (isImageData) {
2082
- const tempCanvas = useOffscreen ? new OffscreenCanvas(originalWidth, originalHeight) : document.createElement("canvas");
2083
- if (!useOffscreen) {
2084
- tempCanvas.width = originalWidth;
2085
- tempCanvas.height = originalHeight;
2086
- }
2087
- const tempCtx = tempCanvas.getContext("2d");
2088
- tempCtx.putImageData(image, 0, 0);
2089
- ctx.drawImage(tempCanvas, 0, 0, originalWidth, originalHeight, 0, 0, targetWidth, targetHeight);
2090
- } else {
2091
- ctx.drawImage(image, 0, 0, originalWidth, originalHeight, 0, 0, targetWidth, targetHeight);
2092
- }
2093
- const imageData = ctx.getImageData(0, 0, targetWidth, targetHeight);
2094
- const data = imageData.data;
2095
- const pixelCount = targetWidth * targetHeight;
2096
- const grayscaleData = new Uint8ClampedArray(pixelCount);
2097
- for (let i = 0, j = 0; i < data.length; i += 4, j++) {
2098
- const gray = data[i] * 54 + data[i + 1] * 183 + data[i + 2] * 19 >> 8;
2099
- grayscaleData[j] = gray;
2100
- data[i] = data[i + 1] = data[i + 2] = gray;
2101
- }
2102
- return {
2103
- grayscaleData,
2104
- imageData,
2105
- // Keep full RGBA for debug visualization
2106
- scaleFactor,
2107
- originalDimensions: { width: originalWidth, height: originalHeight },
2108
- scaledDimensions: { width: targetWidth, height: targetHeight }
2109
- };
2110
- }
2111
- function clamp01(value) {
2112
- if (value <= 0) return 0;
2113
- if (value >= 1) return 1;
2114
- return value;
2115
- }
2116
- function pointDistance(a, b) {
2117
- return Math.hypot(a.x - b.x, a.y - b.y);
2118
- }
2119
- function polygonAreaFromCorners(corners) {
2120
- if (!corners) return 0;
2121
- const points = [corners.topLeft, corners.topRight, corners.bottomRight, corners.bottomLeft];
2122
- let area = 0;
2123
- for (let i = 0; i < points.length; i++) {
2124
- const j = (i + 1) % points.length;
2125
- area += points[i].x * points[j].y - points[j].x * points[i].y;
2126
- }
2127
- return Math.abs(area) / 2;
2128
- }
2129
- function sideLengthsFromCorners(corners) {
2130
- return [
2131
- pointDistance(corners.topLeft, corners.topRight),
2132
- pointDistance(corners.topRight, corners.bottomRight),
2133
- pointDistance(corners.bottomRight, corners.bottomLeft),
2134
- pointDistance(corners.bottomLeft, corners.topLeft)
2135
- ];
2136
- }
2137
- function cornerAnglesDegrees(corners) {
2138
- const points = [corners.topLeft, corners.topRight, corners.bottomRight, corners.bottomLeft];
2139
- const angles = [];
2140
- for (let i = 0; i < points.length; i++) {
2141
- const prev = points[(i + points.length - 1) % points.length];
2142
- const curr = points[i];
2143
- const next = points[(i + 1) % points.length];
2144
- const v1x = prev.x - curr.x;
2145
- const v1y = prev.y - curr.y;
2146
- const v2x = next.x - curr.x;
2147
- const v2y = next.y - curr.y;
2148
- const dot = v1x * v2x + v1y * v2y;
2149
- const mag1 = Math.hypot(v1x, v1y);
2150
- const mag2 = Math.hypot(v2x, v2y);
2151
- const denom = Math.max(1e-6, mag1 * mag2);
2152
- const cosTheta = Math.max(-1, Math.min(1, dot / denom));
2153
- angles.push(Math.acos(cosTheta) * 180 / Math.PI);
2154
- }
2155
- return angles;
2156
- }
2157
- function computeRightAngleScore(corners) {
2158
- const angles = cornerAnglesDegrees(corners);
2159
- let total = 0;
2160
- for (const angle of angles) {
2161
- const deviation = Math.abs(angle - 90);
2162
- total += clamp01(1 - deviation / 55);
2163
- }
2164
- return total / Math.max(1, angles.length);
2165
- }
2166
- function computeOppositeSideConsistency(corners) {
2167
- const sides = sideLengthsFromCorners(corners);
2168
- const widthConsistency = Math.min(sides[0], sides[2]) / Math.max(1e-6, Math.max(sides[0], sides[2]));
2169
- const heightConsistency = Math.min(sides[1], sides[3]) / Math.max(1e-6, Math.max(sides[1], sides[3]));
2170
- return (widthConsistency + heightConsistency) / 2;
2171
- }
2172
- function compareCandidates(a, b) {
2173
- const validDelta = Number(b.isValid) - Number(a.isValid);
2174
- if (validDelta !== 0) return validDelta;
2175
- const confidenceDelta = b.confidence - a.confidence;
2176
- const nearTie = Math.abs(confidenceDelta) < 0.015;
2177
- if (!nearTie && confidenceDelta !== 0) return confidenceDelta;
2178
- const complexityA = Math.abs((a.approxCount ?? 99) - 4);
2179
- const complexityB = Math.abs((b.approxCount ?? 99) - 4);
2180
- const complexityDelta = complexityA - complexityB;
2181
- if (complexityDelta !== 0) return complexityDelta;
2182
- const angleDelta = (b.rightAngleScore ?? 0) - (a.rightAngleScore ?? 0);
2183
- if (Math.abs(angleDelta) > 1e-6) return angleDelta;
2184
- const fitErrorA = Math.abs(1 - (a.contourFitRatio ?? 1));
2185
- const fitErrorB = Math.abs(1 - (b.contourFitRatio ?? 1));
2186
- const fitDelta = fitErrorA - fitErrorB;
2187
- if (Math.abs(fitDelta) > 1e-6) return fitDelta;
2188
- return confidenceDelta || b.score - a.score || b.coverageRatio - a.coverageRatio || b.area - a.area;
2189
- }
2190
- function cornersAreFiniteAndDistinct(corners, minDistance = 6) {
2191
- 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];
2192
- if (points.some((p) => !p || !Number.isFinite(p.x) || !Number.isFinite(p.y))) {
2193
- return false;
2194
- }
2195
- for (let i = 0; i < points.length; i++) {
2196
- for (let j = i + 1; j < points.length; j++) {
2197
- if (pointDistance(points[i], points[j]) < minDistance) {
2198
- return false;
2199
- }
2200
- }
2201
- }
2202
- return true;
2203
- }
2204
- function isConvexQuadrilateral(corners) {
2205
- const points = [corners.topLeft, corners.topRight, corners.bottomRight, corners.bottomLeft];
2206
- const crossSigns = [];
2207
- for (let i = 0; i < points.length; i++) {
2208
- const p0 = points[i];
2209
- const p1 = points[(i + 1) % points.length];
2210
- const p2 = points[(i + 2) % points.length];
2211
- const cross = (p1.x - p0.x) * (p2.y - p1.y) - (p1.y - p0.y) * (p2.x - p1.x);
2212
- if (Math.abs(cross) < 1e-6) {
2213
- continue;
2214
- }
2215
- crossSigns.push(Math.sign(cross));
2216
- }
2217
- if (crossSigns.length < 3) {
2218
- return false;
2219
- }
2220
- const firstSign = crossSigns[0];
2221
- return crossSigns.every((s) => s === firstSign);
2222
- }
2223
- function computeEdgeSupportScore(contour, edges, width, height) {
2224
- if (!(contour == null ? void 0 : contour.points) || contour.points.length === 0) {
2225
- return 0;
2226
- }
2227
- const sampleStep = Math.max(1, Math.floor(contour.points.length / 240));
2228
- let samples = 0;
2229
- let supported = 0;
2230
- for (let i = 0; i < contour.points.length; i += sampleStep) {
2231
- const p = contour.points[i];
2232
- const x = Math.max(0, Math.min(width - 1, p.x | 0));
2233
- const y = Math.max(0, Math.min(height - 1, p.y | 0));
2234
- samples++;
2235
- let localHit = false;
2236
- for (let oy = -1; oy <= 1 && !localHit; oy++) {
2237
- const ny = y + oy;
2238
- if (ny < 0 || ny >= height) continue;
2239
- for (let ox = -1; ox <= 1; ox++) {
2240
- const nx = x + ox;
2241
- if (nx < 0 || nx >= width) continue;
2242
- if (edges[ny * width + nx] > 0) {
2243
- localHit = true;
2244
- break;
2245
- }
2246
- }
2247
- }
2248
- if (localHit) supported++;
2249
- }
2250
- if (samples === 0) return 0;
2251
- return supported / samples;
2252
- }
2253
- function evaluateContourCandidate(contour, edges, width, height, options = {}) {
2254
- const epsilon = options.epsilon || 0.02;
2255
- const approx = approximatePolygon(contour.points, epsilon);
2256
- const approxCount = approx.length;
2257
- const corners = findCornerPoints(contour, { epsilon });
2258
- if (!corners) {
2259
- return {
2260
- contour,
2261
- corners: null,
2262
- score: 0,
2263
- confidence: 0,
2264
- isValid: false,
2265
- area: contour.area || 0,
2266
- fillRatio: 0,
2267
- coverageRatio: 0,
2268
- cornersArea: 0,
2269
- approxCount,
2270
- convex: false,
2271
- edgeSupport: 0,
2272
- aspectRatio: Infinity,
2273
- minSide: 0,
2274
- rightAngleScore: 0,
2275
- oppositeSideConsistency: 0,
2276
- contourFitRatio: 0,
2277
- contourFitScore: 0
2278
- };
2279
- }
2280
- const imageArea = width * height;
2281
- const area = contour.area || 0;
2282
- const box = contour.boundingBox || { minX: 0, minY: 0, maxX: 0, maxY: 0 };
2283
- const boxArea = Math.max(1, (box.maxX - box.minX + 1) * (box.maxY - box.minY + 1));
2284
- const fillRatio = area / boxArea;
2285
- const cornersArea = polygonAreaFromCorners(corners);
2286
- const coverageRatio = cornersArea / Math.max(1, imageArea);
2287
- const sides = sideLengthsFromCorners(corners);
2288
- const minSide = Math.min(...sides);
2289
- const avgWidth = (sides[0] + sides[2]) / 2;
2290
- const avgHeight = (sides[1] + sides[3]) / 2;
2291
- const aspectRatio = avgWidth > avgHeight ? avgWidth / Math.max(1e-6, avgHeight) : avgHeight / Math.max(1e-6, avgWidth);
2292
- const convex = isConvexQuadrilateral(corners);
2293
- const edgeSupport = computeEdgeSupportScore(contour, edges, width, height);
2294
- const rightAngleScore = computeRightAngleScore(corners);
2295
- const oppositeSideConsistency = computeOppositeSideConsistency(corners);
2296
- const contourFitRatio = area / Math.max(1, cornersArea);
2297
- const contourFitScore = clamp01(1 - Math.abs(contourFitRatio - 1) / 0.55);
2298
- const quadLikeness = approxCount === 4 ? 1 : approxCount === 5 ? 0.9 : approxCount === 6 ? 0.7 : approxCount <= 8 ? 0.5 : 0.28;
2299
- const areaScore = clamp01(area / Math.max(1, imageArea * 0.4));
2300
- const fillScore = clamp01((fillRatio - 0.08) / 0.72);
2301
- const coverageScore = clamp01((coverageRatio - 0.03) / 0.82);
2302
- const minSideRatio = options.minDocumentSideRatio !== void 0 ? options.minDocumentSideRatio : 0.06;
2303
- const minCoverage = options.minDocumentCoverageRatio !== void 0 ? options.minDocumentCoverageRatio : 0.04;
2304
- const maxAspect = options.maxDocumentAspectRatio !== void 0 ? options.maxDocumentAspectRatio : 8;
2305
- const minFillRatio = options.minDocumentFillRatio !== void 0 ? options.minDocumentFillRatio : 0.07;
2306
- const minContourFitRatio = options.minContourFitRatio !== void 0 ? options.minContourFitRatio : 0.11;
2307
- const maxContourFitRatio = options.maxContourFitRatio !== void 0 ? options.maxContourFitRatio : 1.2;
2308
- const minRightAngleScore = options.minRightAngleScore !== void 0 ? options.minRightAngleScore : 0.42;
2309
- const minOppositeSideConsistency = options.minOppositeSideConsistency !== void 0 ? options.minOppositeSideConsistency : 0.3;
2310
- const minSidePx = Math.min(width, height) * minSideRatio;
2311
- const geometryValid = cornersAreFiniteAndDistinct(corners) && convex && minSide >= minSidePx && coverageRatio >= minCoverage && aspectRatio <= maxAspect && fillRatio >= minFillRatio && contourFitRatio >= minContourFitRatio && contourFitRatio <= maxContourFitRatio && rightAngleScore >= minRightAngleScore && oppositeSideConsistency >= minOppositeSideConsistency;
2312
- 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;
2313
- const confidence = geometryValid ? score : score * 0.33;
2314
- return {
2315
- contour,
2316
- corners,
2317
- score,
2318
- confidence,
2319
- isValid: geometryValid,
2320
- area,
2321
- fillRatio,
2322
- coverageRatio,
2323
- cornersArea,
2324
- approxCount,
2325
- convex,
2326
- edgeSupport,
2327
- aspectRatio,
2328
- minSide,
2329
- rightAngleScore,
2330
- oppositeSideConsistency,
2331
- contourFitRatio,
2332
- contourFitScore
2333
- };
2334
- }
2335
- function selectBestContourCandidate(contours, edges, width, height, options = {}) {
2336
- const maxCandidateContours = options.maxCandidateContours || 12;
2337
- const candidates = contours.slice(0, maxCandidateContours).map((contour, index) => ({
2338
- rankByArea: index,
2339
- ...evaluateContourCandidate(contour, edges, width, height, options)
2340
- })).sort(compareCandidates);
2341
- return {
2342
- best: candidates[0] || null,
2343
- candidates
2344
- };
2345
- }
2346
- function shouldRunDetectionCascade(bestCandidate, options = {}) {
2347
- if (options.enableDetectionCascade === false) return false;
2348
- if (!bestCandidate || !bestCandidate.corners) return true;
2349
- const minConfidenceForSinglePass = options.minCascadeTriggerConfidence !== void 0 ? options.minCascadeTriggerConfidence : 0.68;
2350
- if (!bestCandidate.isValid) return true;
2351
- if (bestCandidate.confidence < minConfidenceForSinglePass) return true;
2352
- if (bestCandidate.approxCount > 5) return true;
2353
- if (bestCandidate.rightAngleScore < 0.55) return true;
2354
- if (bestCandidate.contourFitRatio < 0.14) return true;
2355
- return false;
2356
- }
2357
- function buildDetectionPassProfiles(options = {}) {
2358
- const baseKernel = options.dilationKernelSize || 3;
2359
- const baseIterations = options.dilationIterations || 1;
2360
- const baseApplyDilation = options.applyDilation !== void 0 ? options.applyDilation : true;
2361
- const profiles = [
2362
- {
2363
- name: "default",
2364
- lowThreshold: options.lowThreshold,
2365
- highThreshold: options.highThreshold,
2366
- dilationKernelSize: baseKernel,
2367
- dilationIterations: baseIterations,
2368
- applyDilation: baseApplyDilation
2369
- }
2370
- ];
2371
- if (options.enableDetectionCascade === false) {
2372
- return profiles;
2373
- }
2374
- profiles.push({
2375
- name: "connect-edges",
2376
- lowThreshold: options.lowThreshold,
2377
- highThreshold: options.highThreshold,
2378
- dilationKernelSize: Math.max(baseKernel, 5),
2379
- dilationIterations: Math.max(baseIterations, 2),
2380
- applyDilation: true
2381
- });
2382
- profiles.push({
2383
- name: "no-dilation",
2384
- lowThreshold: options.lowThreshold,
2385
- highThreshold: options.highThreshold,
2386
- dilationKernelSize: baseKernel,
2387
- dilationIterations: baseIterations,
2388
- applyDilation: false
2389
- });
2390
- if (options.lowThreshold === void 0 && options.highThreshold === void 0) {
2391
- profiles.push({
2392
- name: "fixed-mid-thresholds",
2393
- lowThreshold: 60,
2394
- highThreshold: 180,
2395
- dilationKernelSize: baseKernel,
2396
- dilationIterations: baseIterations,
2397
- applyDilation: baseApplyDilation
2398
- });
2399
- }
2400
- const deduped = [];
2401
- const seen = /* @__PURE__ */ new Set();
2402
- for (const profile of profiles) {
2403
- const key = [
2404
- profile.lowThreshold,
2405
- profile.highThreshold,
2406
- profile.dilationKernelSize,
2407
- profile.dilationIterations,
2408
- profile.applyDilation
2409
- ].join(":");
2410
- if (seen.has(key)) continue;
2411
- seen.add(key);
2412
- deduped.push(profile);
2413
- }
2414
- return deduped;
2415
- }
2416
- async function detectDocumentInternal(grayscaleData, width, height, scaleFactor, options = {}) {
2417
- const debugInfo = options.debug ? {} : { _timingsOnly: true };
2418
- const timings = [];
2419
- if (debugInfo && !debugInfo._timingsOnly) {
2420
- debugInfo.preprocessing = {
2421
- scaledDimensions: { width, height },
2422
- scaleFactor,
2423
- maxProcessingDimension: options.maxProcessingDimension || 800
2424
- };
2425
- }
2426
- const passProfiles = buildDetectionPassProfiles(options);
2427
- const passResults = [];
2428
- const runDetectionPass = async (profile, passIndex) => {
2429
- const passLabel = profile.name || `pass-${passIndex + 1}`;
2430
- const passSuffix = passIndex === 0 ? "" : ` (${passLabel})`;
2431
- const passDebug = options.debug ? {} : { _timingsOnly: true };
2432
- const edges = await cannyEdgeDetector(grayscaleData, {
2433
- width,
2434
- height,
2435
- lowThreshold: profile.lowThreshold,
2436
- highThreshold: profile.highThreshold,
2437
- dilationKernelSize: profile.dilationKernelSize,
2438
- dilationIterations: profile.dilationIterations,
2439
- applyDilation: profile.applyDilation,
2440
- debug: passDebug,
2441
- skipGrayscale: true,
2442
- useWasmBlur: true,
2443
- useWasmHysteresis: options.useWasmHysteresis,
2444
- useWasmFullCanny: options.useWasmFullCanny
2445
- });
2446
- if (passDebug.timings) {
2447
- passDebug.timings.forEach((timing) => {
2448
- if (timing.step === "Edge Detection Total") return;
2449
- timings.push({ step: `${timing.step}${passSuffix}`, ms: timing.ms });
2450
- });
2451
- }
2452
- let t0 = performance.now();
2453
- const contours = detectDocumentContour(edges, {
2454
- minArea: (options.minArea || 1e3) / (scaleFactor * scaleFactor),
2455
- width,
2456
- height
2457
- });
2458
- timings.push({ step: `Find Contours${passSuffix}`, ms: (performance.now() - t0).toFixed(2) });
2459
- t0 = performance.now();
2460
- const { best: best2, candidates: candidates2 } = selectBestContourCandidate(contours, edges, width, height, options);
2461
- timings.push({ step: `Corner Detection${passSuffix}`, ms: (performance.now() - t0).toFixed(2) });
2462
- return {
2463
- name: passLabel,
2464
- params: {
2465
- lowThreshold: profile.lowThreshold,
2466
- highThreshold: profile.highThreshold,
2467
- dilationKernelSize: profile.dilationKernelSize,
2468
- dilationIterations: profile.dilationIterations,
2469
- applyDilation: profile.applyDilation
2470
- },
2471
- contours,
2472
- best: best2,
2473
- candidates: candidates2
2474
- };
2475
- };
2476
- const primaryPass = await runDetectionPass(passProfiles[0], 0);
2477
- passResults.push(primaryPass);
2478
- if (shouldRunDetectionCascade(primaryPass.best, options)) {
2479
- for (let i = 1; i < passProfiles.length; i++) {
2480
- passResults.push(await runDetectionPass(passProfiles[i], i));
2481
- }
2482
- }
2483
- const allCandidates = [];
2484
- for (const pass of passResults) {
2485
- for (const candidate of pass.candidates) {
2486
- allCandidates.push({
2487
- ...candidate,
2488
- passName: pass.name,
2489
- passParams: pass.params
2490
- });
2491
- }
2492
- }
2493
- allCandidates.sort(compareCandidates);
2494
- const best = allCandidates[0] || null;
2495
- const candidates = allCandidates;
2496
- if (!best || !best.corners) {
2497
- console.log("No document detected");
2498
- return {
2499
- success: false,
2500
- message: "No document detected",
2501
- debug: debugInfo._timingsOnly ? null : debugInfo,
2502
- timings
2503
- };
2504
- }
2505
- if (debugInfo && !debugInfo._timingsOnly) {
2506
- debugInfo.passes = passResults.map((pass) => ({
2507
- name: pass.name,
2508
- params: pass.params,
2509
- contourCount: pass.contours.length,
2510
- bestCandidate: pass.best ? {
2511
- score: pass.best.score,
2512
- confidence: pass.best.confidence,
2513
- isValid: pass.best.isValid,
2514
- approxCount: pass.best.approxCount,
2515
- coverageRatio: pass.best.coverageRatio,
2516
- fillRatio: pass.best.fillRatio,
2517
- rightAngleScore: pass.best.rightAngleScore,
2518
- contourFitRatio: pass.best.contourFitRatio
2519
- } : null
2520
- }));
2521
- debugInfo.candidates = candidates.map((candidate) => ({
2522
- passName: candidate.passName,
2523
- rankByArea: candidate.rankByArea,
2524
- area: candidate.area,
2525
- fillRatio: candidate.fillRatio,
2526
- coverageRatio: candidate.coverageRatio,
2527
- cornersArea: candidate.cornersArea,
2528
- approxCount: candidate.approxCount,
2529
- convex: candidate.convex,
2530
- edgeSupport: candidate.edgeSupport,
2531
- aspectRatio: candidate.aspectRatio,
2532
- minSide: candidate.minSide,
2533
- rightAngleScore: candidate.rightAngleScore,
2534
- oppositeSideConsistency: candidate.oppositeSideConsistency,
2535
- contourFitRatio: candidate.contourFitRatio,
2536
- contourFitScore: candidate.contourFitScore,
2537
- score: candidate.score,
2538
- confidence: candidate.confidence,
2539
- isValid: candidate.isValid
2540
- }));
2541
- debugInfo.selectedCandidate = candidates[0] ? {
2542
- passName: candidates[0].passName,
2543
- rankByArea: candidates[0].rankByArea,
2544
- area: candidates[0].area,
2545
- fillRatio: candidates[0].fillRatio,
2546
- coverageRatio: candidates[0].coverageRatio,
2547
- approxCount: candidates[0].approxCount,
2548
- edgeSupport: candidates[0].edgeSupport,
2549
- aspectRatio: candidates[0].aspectRatio,
2550
- minSide: candidates[0].minSide,
2551
- rightAngleScore: candidates[0].rightAngleScore,
2552
- oppositeSideConsistency: candidates[0].oppositeSideConsistency,
2553
- contourFitRatio: candidates[0].contourFitRatio,
2554
- score: candidates[0].score,
2555
- confidence: candidates[0].confidence,
2556
- isValid: candidates[0].isValid
2557
- } : null;
2558
- }
2559
- const cornerPoints = best.corners;
2560
- const documentContour = best.contour;
2561
- let finalCorners = cornerPoints;
2562
- if (scaleFactor !== 1) {
2563
- finalCorners = {
2564
- topLeft: { x: cornerPoints.topLeft.x * scaleFactor, y: cornerPoints.topLeft.y * scaleFactor },
2565
- topRight: { x: cornerPoints.topRight.x * scaleFactor, y: cornerPoints.topRight.y * scaleFactor },
2566
- bottomRight: { x: cornerPoints.bottomRight.x * scaleFactor, y: cornerPoints.bottomRight.y * scaleFactor },
2567
- bottomLeft: { x: cornerPoints.bottomLeft.x * scaleFactor, y: cornerPoints.bottomLeft.y * scaleFactor }
2568
- };
2569
- }
2570
- return {
2571
- success: true,
2572
- contour: documentContour,
2573
- corners: finalCorners,
2574
- confidence: best.confidence,
2575
- debug: debugInfo._timingsOnly ? null : debugInfo,
2576
- timings
2577
- };
2578
- }
2579
- function getPerspectiveTransform(srcPoints, dstPoints) {
2580
- function buildMatrix(points) {
2581
- const matrix2 = [];
2582
- for (let i = 0; i < 4; i++) {
2583
- const [x, y] = points[i];
2584
- matrix2.push([x, y, 1, 0, 0, 0, -x * dstPoints[i][0], -y * dstPoints[i][0]]);
2585
- matrix2.push([0, 0, 0, x, y, 1, -x * dstPoints[i][1], -y * dstPoints[i][1]]);
2586
- }
2587
- return matrix2;
2588
- }
2589
- const A = buildMatrix(srcPoints);
2590
- const b = [
2591
- dstPoints[0][0],
2592
- dstPoints[0][1],
2593
- dstPoints[1][0],
2594
- dstPoints[1][1],
2595
- dstPoints[2][0],
2596
- dstPoints[2][1],
2597
- dstPoints[3][0],
2598
- dstPoints[3][1]
2599
- ];
2600
- function solve(A2, b2) {
2601
- const m = A2.length;
2602
- const n = A2[0].length;
2603
- const M = A2.map((row) => row.slice());
2604
- const B = b2.slice();
2605
- for (let i = 0; i < n; i++) {
2606
- let maxRow = i;
2607
- for (let k = i + 1; k < m; k++) {
2608
- if (Math.abs(M[k][i]) > Math.abs(M[maxRow][i])) maxRow = k;
2609
- }
2610
- [M[i], M[maxRow]] = [M[maxRow], M[i]];
2611
- [B[i], B[maxRow]] = [B[maxRow], B[i]];
2612
- for (let k = i + 1; k < m; k++) {
2613
- const c = M[k][i] / M[i][i];
2614
- for (let j = i; j < n; j++) {
2615
- M[k][j] -= c * M[i][j];
2616
- }
2617
- B[k] -= c * B[i];
2618
- }
2619
- }
2620
- const x = new Array(n);
2621
- for (let i = n - 1; i >= 0; i--) {
2622
- let sum = B[i];
2623
- for (let j = i + 1; j < n; j++) {
2624
- sum -= M[i][j] * x[j];
2625
- }
2626
- x[i] = sum / M[i][i];
2627
- }
2628
- return x;
2629
- }
2630
- const h = solve(A, b);
2631
- const matrix = [
2632
- [h[0], h[1], h[2]],
2633
- [h[3], h[4], h[5]],
2634
- [h[6], h[7], 1]
2635
- ];
2636
- return matrix;
2637
- }
2638
- function unwarpImage(ctx, image, corners) {
2639
- const { topLeft, topRight, bottomRight, bottomLeft } = corners;
2640
- const widthA = Math.hypot(bottomRight.x - bottomLeft.x, bottomRight.y - bottomLeft.y);
2641
- const widthB = Math.hypot(topRight.x - topLeft.x, topRight.y - topLeft.y);
2642
- const maxWidth = Math.round(Math.max(widthA, widthB));
2643
- const heightA = Math.hypot(topRight.x - bottomRight.x, topRight.y - bottomRight.y);
2644
- const heightB = Math.hypot(topLeft.x - bottomLeft.x, topLeft.y - bottomLeft.y);
2645
- const maxHeight = Math.round(Math.max(heightA, heightB));
2646
- ctx.canvas.width = maxWidth;
2647
- ctx.canvas.height = maxHeight;
2648
- const srcPoints = [
2649
- [topLeft.x, topLeft.y],
2650
- [topRight.x, topRight.y],
2651
- [bottomRight.x, bottomRight.y],
2652
- [bottomLeft.x, bottomLeft.y]
2653
- ];
2654
- const dstPoints = [
2655
- [0, 0],
2656
- [maxWidth - 1, 0],
2657
- [maxWidth - 1, maxHeight - 1],
2658
- [0, maxHeight - 1]
2659
- ];
2660
- const perspectiveMatrix = getPerspectiveTransform(srcPoints, dstPoints);
2661
- warpTransform(ctx, image, perspectiveMatrix, maxWidth, maxHeight);
2662
- }
2663
- function invert3x3(m) {
2664
- const a = m[0][0], b = m[0][1], c = m[0][2];
2665
- const d = m[1][0], e = m[1][1], f = m[1][2];
2666
- const g = m[2][0], h = m[2][1], i = m[2][2];
2667
- const A = e * i - f * h;
2668
- const B = -(d * i - f * g);
2669
- const C = d * h - e * g;
2670
- const D = -(b * i - c * h);
2671
- const E = a * i - c * g;
2672
- const F = -(a * h - b * g);
2673
- const G = b * f - c * e;
2674
- const H = -(a * f - c * d);
2675
- const I = a * e - b * d;
2676
- const det = a * A + b * B + c * C;
2677
- if (det === 0) throw new Error("Singular matrix");
2678
- return [
2679
- [A / det, D / det, G / det],
2680
- [B / det, E / det, H / det],
2681
- [C / det, F / det, I / det]
2682
- ];
2683
- }
2684
- function warpTransform(ctx, image, matrix, outWidth, outHeight) {
2685
- const isImageData = image && typeof image.width === "number" && typeof image.height === "number" && image.data;
2686
- const srcWidth = image.width || image.naturalWidth;
2687
- const srcHeight = image.height || image.naturalHeight;
2688
- const srcCanvas = document.createElement("canvas");
2689
- srcCanvas.width = srcWidth;
2690
- srcCanvas.height = srcHeight;
2691
- const srcCtx = srcCanvas.getContext("2d", { willReadFrequently: true });
2692
- if (isImageData) {
2693
- srcCtx.putImageData(image, 0, 0);
2694
- } else {
2695
- srcCtx.drawImage(image, 0, 0, srcWidth, srcHeight);
2696
- }
2697
- const srcData = srcCtx.getImageData(0, 0, srcWidth, srcHeight).data;
2698
- const inv = invert3x3(matrix);
2699
- const i00 = inv[0][0], i01 = inv[0][1], i02 = inv[0][2];
2700
- const i10 = inv[1][0], i11 = inv[1][1], i12 = inv[1][2];
2701
- const i20 = inv[2][0], i21 = inv[2][1], i22 = inv[2][2];
2702
- const outData = ctx.createImageData(outWidth, outHeight);
2703
- const dst = outData.data;
2704
- const maxSrcX = srcWidth - 1;
2705
- const maxSrcY = srcHeight - 1;
2706
- for (let oy = 0; oy < outHeight; oy++) {
2707
- const iy1 = i01 * oy + i02;
2708
- const iy2 = i11 * oy + i12;
2709
- const iy3 = i21 * oy + i22;
2710
- for (let ox = 0; ox < outWidth; ox++) {
2711
- const w = i20 * ox + iy3;
2712
- const invW = 1 / w;
2713
- const sx = (i00 * ox + iy1) * invW;
2714
- const sy = (i10 * ox + iy2) * invW;
2715
- const csx = sx < 0 ? 0 : sx > maxSrcX ? maxSrcX : sx;
2716
- const csy = sy < 0 ? 0 : sy > maxSrcY ? maxSrcY : sy;
2717
- const x0 = csx | 0;
2718
- const y0 = csy | 0;
2719
- const x1 = x0 < maxSrcX ? x0 + 1 : x0;
2720
- const y1 = y0 < maxSrcY ? y0 + 1 : y0;
2721
- const fx = csx - x0;
2722
- const fy = csy - y0;
2723
- const fx1 = 1 - fx;
2724
- const fy1 = 1 - fy;
2725
- const w00 = fx1 * fy1;
2726
- const w10 = fx * fy1;
2727
- const w01 = fx1 * fy;
2728
- const w11 = fx * fy;
2729
- const idx00 = y0 * srcWidth + x0 << 2;
2730
- const idx10 = y0 * srcWidth + x1 << 2;
2731
- const idx01 = y1 * srcWidth + x0 << 2;
2732
- const idx11 = y1 * srcWidth + x1 << 2;
2733
- const di = oy * outWidth + ox << 2;
2734
- dst[di] = srcData[idx00] * w00 + srcData[idx10] * w10 + srcData[idx01] * w01 + srcData[idx11] * w11 + 0.5 | 0;
2735
- dst[di + 1] = srcData[idx00 + 1] * w00 + srcData[idx10 + 1] * w10 + srcData[idx01 + 1] * w01 + srcData[idx11 + 1] * w11 + 0.5 | 0;
2736
- dst[di + 2] = srcData[idx00 + 2] * w00 + srcData[idx10 + 2] * w10 + srcData[idx01 + 2] * w01 + srcData[idx11 + 2] * w11 + 0.5 | 0;
2737
- dst[di + 3] = 255;
2738
- }
2739
- }
2740
- ctx.putImageData(outData, 0, 0);
2741
- }
2742
- async function extractDocument(image, corners, options = {}) {
2743
- const outputType = options.output || "canvas";
2744
- if (!corners || !corners.topLeft || !corners.topRight || !corners.bottomRight || !corners.bottomLeft) {
2745
- return {
2746
- output: null,
2747
- corners: null,
2748
- success: false,
2749
- message: "Invalid corner points provided"
2750
- };
2751
- }
2752
- try {
2753
- const resultCanvas = document.createElement("canvas");
2754
- const ctx = resultCanvas.getContext("2d");
2755
- unwarpImage(ctx, image, corners);
2756
- let output;
2757
- if (outputType === "canvas") {
2758
- output = resultCanvas;
2759
- } else if (outputType === "imagedata") {
2760
- output = resultCanvas.getContext("2d").getImageData(0, 0, resultCanvas.width, resultCanvas.height);
2761
- } else if (outputType === "dataurl") {
2762
- output = resultCanvas.toDataURL();
2763
- } else {
2764
- output = resultCanvas;
2765
- }
2766
- return {
2767
- output,
2768
- corners,
2769
- success: true,
2770
- message: "Document extracted successfully"
2771
- };
2772
- } catch (error) {
2773
- return {
2774
- output: null,
2775
- corners,
2776
- success: false,
2777
- message: `Extraction failed: ${error.message}`
2778
- };
2779
- }
2780
- }
2781
- async function scanDocument(image, options = {}) {
2782
- const timings = [];
2783
- const totalStart = performance.now();
2784
- const mode = options.mode || "detect";
2785
- const outputType = options.output || "canvas";
2786
- !!options.debug;
2787
- const maxProcessingDimension = options.maxProcessingDimension || 800;
2788
- let t0 = performance.now();
2789
- const { grayscaleData, imageData, scaleFactor, originalDimensions, scaledDimensions } = await prepareScaleAndGrayscale(image, maxProcessingDimension);
2790
- timings.push({ step: "Image Prep + Scale + Gray", ms: (performance.now() - t0).toFixed(2) });
2791
- const detection = await detectDocumentInternal(
2792
- grayscaleData,
2793
- scaledDimensions.width,
2794
- scaledDimensions.height,
2795
- scaleFactor,
2796
- options
2797
- );
2798
- if (detection.timings) {
2799
- detection.timings.forEach((t) => timings.push(t));
2800
- }
2801
- if (!detection.success) {
2802
- const totalEnd2 = performance.now();
2803
- timings.unshift({ step: "Total", ms: (totalEnd2 - totalStart).toFixed(2) });
2804
- console.table(timings);
2805
- return {
2806
- output: null,
2807
- corners: null,
2808
- contour: null,
2809
- confidence: detection.confidence || null,
2810
- debug: detection.debug,
2811
- success: false,
2812
- message: detection.message || "No document detected",
2813
- timings
2814
- };
2815
- }
2816
- let resultCanvas;
2817
- let output;
2818
- if (mode === "detect") {
2819
- output = null;
2820
- } else if (mode === "extract") {
2821
- t0 = performance.now();
2822
- resultCanvas = document.createElement("canvas");
2823
- const ctx = resultCanvas.getContext("2d");
2824
- unwarpImage(ctx, image, detection.corners);
2825
- timings.push({ step: "Perspective Transform", ms: (performance.now() - t0).toFixed(2) });
2826
- }
2827
- if (mode !== "detect" && resultCanvas) {
2828
- t0 = performance.now();
2829
- if (outputType === "canvas") {
2830
- output = resultCanvas;
2831
- } else if (outputType === "imagedata") {
2832
- output = resultCanvas.getContext("2d").getImageData(0, 0, resultCanvas.width, resultCanvas.height);
2833
- } else if (outputType === "dataurl") {
2834
- output = resultCanvas.toDataURL();
2835
- } else {
2836
- output = resultCanvas;
2837
- }
2838
- timings.push({ step: "Output Conversion", ms: (performance.now() - t0).toFixed(2) });
2839
- }
2840
- const totalEnd = performance.now();
2841
- timings.unshift({ step: "Total", ms: (totalEnd - totalStart).toFixed(2) });
2842
- console.table(timings);
2843
- return {
2844
- output,
2845
- corners: detection.corners,
2846
- contour: detection.contour,
2847
- confidence: detection.confidence || null,
2848
- debug: detection.debug,
2849
- success: true,
2850
- message: "Document detected",
2851
- timings
2852
- };
2853
- }
2854
- export {
2855
- Scanner,
2856
- createCornerEditor,
2857
- extractDocument,
2858
- initialize,
2859
- scanDocument
2860
- };
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};