scanic 0.1.5 → 0.1.7

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.
@@ -1 +1 @@
1
- {"version":3,"file":"scanic.js","sources":["../src/constants.js","../src/contourDetection.js","../src/cornerDetection.js","../wasm_blur/pkg/wasm_blur.js","../src/edgeDetection.js","../src/liveScanner.js","../src/index.js"],"sourcesContent":["/**\r\n * Constants used throughout the library\r\n */\r\n\r\nexport const VERSION = '1.0.0';\r\n\r\nexport const DEFAULTS = {\r\n // Edge detection params\r\n GAUSSIAN_SIGMA: 1.1,\r\n LOW_THRESHOLD: 50,\r\n HIGH_THRESHOLD: 150,\r\n \r\n // Contour detection params\r\n MIN_CONTOUR_AREA: 1000,\r\n MIN_CONTOUR_POINTS: 10,\r\n \r\n // Debug options\r\n DEBUG_OVERLAY_OPACITY: 0.7\r\n};","/**\r\n * Pure JavaScript implementation of contour detection algorithms\r\n * Based on Suzuki, S. and Abe, K. (1985). Topological structural analysis of digitized binary images by border following.\r\n * Replaces the previous flood-fill based connected components analysis.\r\n */\r\n\r\nimport { DEFAULTS } from './constants.js';\r\n\r\n// Constants for different retrieval modes (subset of OpenCV)\r\nconst RETR_EXTERNAL = 0;\r\nconst RETR_LIST = 1;\r\n// Constants for different approximation methods (subset of OpenCV)\r\nconst CHAIN_APPROX_NONE = 1;\r\nconst CHAIN_APPROX_SIMPLE = 2;\r\n\r\n// Deltas for 8-connectivity neighborhood checks (0-7 clockwise from top)\r\n// Corresponds to OpenCV's chain code directions\r\nconst deltas = [\r\n { dx: 0, dy: -1 }, // 0: Top\r\n { dx: 1, dy: -1 }, // 1: Top-right\r\n { dx: 1, dy: 0 }, // 2: Right\r\n { dx: 1, dy: 1 }, // 3: Bottom-right\r\n { dx: 0, dy: 1 }, // 4: Bottom\r\n { dx: -1, dy: 1 }, // 5: Bottom-left\r\n { dx: -1, dy: 0 }, // 6: Left\r\n { dx: -1, dy: -1 } // 7: Top-left\r\n];\r\n\r\n/**\r\n * Detects contours in a binary edge image using Suzuki's border following algorithm.\r\n * @param {Uint8ClampedArray} edges - Binary edge image (pixels > 0 are foreground)\r\n * @param {Object} options - Configuration options\r\n * @param {number} [options.width] - Image width (required if not square)\r\n * @param {number} [options.height] - Image height (required if not square)\r\n * @param {number} [options.mode=RETR_LIST] - Contour retrieval mode (RETR_EXTERNAL or RETR_LIST)\r\n * @param {number} [options.method=CHAIN_APPROX_SIMPLE] - Contour approximation method (CHAIN_APPROX_NONE or CHAIN_APPROX_SIMPLE)\r\n * @param {number} [options.minArea=DEFAULTS.MIN_CONTOUR_AREA] - Minimum contour area filter (applied after detection)\r\n * @param {Object} [options.debug] - Optional debug object to store intermediate results\r\n * @returns {Array} Array of contours, each contour is an array of points {x, y}. Sorted by area (largest first).\r\n */\r\nexport function detectDocumentContour(edges, options = {}) {\r\n const width = options.width || Math.sqrt(edges.length);\r\n const height = options.height || edges.length / width;\r\n const mode = options.mode !== undefined ? options.mode : RETR_LIST;\r\n const method = options.method !== undefined ? options.method : CHAIN_APPROX_SIMPLE;\r\n const minArea = options.minArea || DEFAULTS.MIN_CONTOUR_AREA;\r\n\r\n // Create a padded label map to simplify boundary checks.\r\n // 0: background\r\n // 1: foreground (unlabeled)\r\n // >= 2: contour ID (2, 3, ...)\r\n const paddedWidth = width + 2;\r\n const paddedHeight = height + 2;\r\n const labels = new Int32Array(paddedWidth * paddedHeight); // Initialized to 0\r\n\r\n // Copy edges data to the label map, mapping foreground pixels to 1\r\n for (let y = 0; y < height; y++) {\r\n for (let x = 0; x < width; x++) {\r\n if (edges[y * width + x] > 0) {\r\n labels[(y + 1) * paddedWidth + (x + 1)] = 1;\r\n }\r\n }\r\n }\r\n\r\n const contours = [];\r\n let nextContourId = 2; // Start labeling contours from 2\r\n\r\n // Raster scan\r\n for (let y = 1; y <= height; y++) {\r\n for (let x = 1; x <= width; x++) {\r\n const currentPixelLabel = labels[y * paddedWidth + x];\r\n const leftPixelLabel = labels[y * paddedWidth + (x - 1)];\r\n\r\n let startPoint = null;\r\n let isOuter = false;\r\n let initialDirection = -1;\r\n\r\n if (currentPixelLabel === 1 && leftPixelLabel === 0) {\r\n // Found the start of an outer contour boundary (NBD = 1 in Suzuki's terms)\r\n isOuter = true;\r\n startPoint = { x: x, y: y };\r\n initialDirection = 2; // Start searching right\r\n // if (options.debug) console.log(`Outer contour start at (${x-1}, ${y-1})`);\r\n } else if (currentPixelLabel === 0 && leftPixelLabel >= 1 && leftPixelLabel !== -1) {\r\n // Found the start of a hole contour boundary (NBD >= 2 in Suzuki's terms)\r\n // Check if the left pixel is already part of a traced contour border\r\n // If leftPixelLabel is > 1, it might be already traced. If it's 1, it's an unlabeled foreground pixel.\r\n // We only start tracing if the left pixel is unlabeled foreground (1).\r\n if (leftPixelLabel === 1) {\r\n isOuter = false;\r\n startPoint = { x: x - 1, y: y };\r\n initialDirection = 6; // Start searching left\r\n // if (options.debug) console.log(`Hole contour start at (${x-1-1}, ${y-1})`);\r\n }\r\n }\r\n\r\n\r\n if (startPoint) {\r\n // If mode is RETR_EXTERNAL, only process outer contours\r\n if (mode === RETR_EXTERNAL && !isOuter) {\r\n // Mark the starting pixel of the hole so we don't process it again\r\n // Use a special marker (-1) to distinguish from contour IDs\r\n labels[startPoint.y * paddedWidth + startPoint.x] = -1;\r\n continue;\r\n }\r\n\r\n const contourId = nextContourId++;\r\n const points = traceContour(labels, paddedWidth, paddedHeight, startPoint, initialDirection, contourId);\r\n\r\n if (points && points.length > 0) {\r\n let finalPoints = points;\r\n if (method === CHAIN_APPROX_SIMPLE) {\r\n finalPoints = simplifyChainApproxSimple(points);\r\n }\r\n\r\n // Adjust points to original image coordinates (remove padding offset)\r\n const adjustedPoints = finalPoints.map(p => ({ x: p.x - 1, y: p.y - 1 }));\r\n\r\n if (adjustedPoints.length >= (method === CHAIN_APPROX_SIMPLE ? 4 : DEFAULTS.MIN_CONTOUR_POINTS)) { // Need at least 4 points for a simple polygon approx\r\n const contour = {\r\n id: contourId,\r\n points: adjustedPoints,\r\n isOuter: isOuter,\r\n // Calculate area and bounding box later if needed for filtering/sorting\r\n };\r\n contours.push(contour);\r\n }\r\n } else {\r\n // Handle single point contours or errors if necessary\r\n // Mark the start point if trace failed or resulted in no points\r\n if (labels[startPoint.y * paddedWidth + startPoint.x] === 1) {\r\n labels[startPoint.y * paddedWidth + startPoint.x] = contourId; // Mark as visited\r\n }\r\n }\r\n } else if (currentPixelLabel >= 1 && leftPixelLabel >= 1 && currentPixelLabel !== leftPixelLabel) {\r\n // Handle merging contours or complex topology if needed (not implemented for RETR_LIST/EXTERNAL)\r\n }\r\n }\r\n }\r\n\r\n // Calculate area and bounding box for filtering and sorting\r\n contours.forEach(contour => {\r\n contour.area = calculateContourArea(contour.points);\r\n contour.boundingBox = calculateBoundingBox(contour.points);\r\n });\r\n\r\n // Filter by minimum area\r\n const filteredContours = contours.filter(contour => contour.area >= minArea);\r\n\r\n // Sort contours by area (largest first)\r\n filteredContours.sort((a, b) => b.area - a.area);\r\n\r\n // console.log(`Found ${contours.length} contours before filtering, ${filteredContours.length} after filtering.`);\r\n\r\n // Store debug info if requested\r\n if (options.debug) {\r\n options.debug.labels = labels; // Store the final label map\r\n options.debug.rawContours = contours; // Store contours before filtering/sorting\r\n options.debug.finalContours = filteredContours;\r\n // console.log('Contour detection debug info stored');\r\n }\r\n return filteredContours // Return only the points array per contour\r\n}\r\n\r\n/**\r\n * Traces a contour boundary using border following.\r\n * @param {Int32Array} labels - The label map (modified during tracing)\r\n * @param {number} width - Padded width of the label map\r\n * @param {number} height - Padded height of the label map\r\n * @param {Object} startPoint - Starting point {x, y} in padded coordinates\r\n * @param {number} initialDirection - Initial search direction (0-7)\r\n * @param {number} contourId - The ID to label this contour with\r\n * @returns {Array} Array of points {x, y} in padded coordinates, or null if error\r\n */\r\nfunction traceContour(labels, width, height, startPoint, initialDirection, contourId) {\r\n const points = [];\r\n const visitedPoints = new Set(); // Use a Set for efficient duplicate checking\r\n let currentPoint = { ...startPoint };\r\n let prevDirection = -1; // Store the direction from which we arrived at currentPoint\r\n\r\n // Mark the starting pixel with the contour ID\r\n labels[startPoint.y * width + startPoint.x] = contourId;\r\n\r\n let count = 0; // Safety break\r\n const maxSteps = width * height; // Max possible steps\r\n\r\n while (count++ < maxSteps) {\r\n // Determine the direction to start searching from (relative to the direction we came from)\r\n // In Suzuki's paper, this is based on the chain code of the previous step.\r\n // Simplified: Start searching from the direction after the one that led us here.\r\n // If we arrived from direction `d`, the next pixel must be in `(d+1)%8` to `(d+7)%8`.\r\n // Let's adapt OpenCV's logic: search starts from (prevDirection + 2) % 8 clockwise.\r\n // If it's the first step, prevDirection is unknown, use initialDirection logic.\r\n\r\n let searchDirection;\r\n if (prevDirection === -1) {\r\n // First step: Use initialDirection logic (e.g., start right for outer, left for inner)\r\n // The initial search should find the *first* pixel of the contour boundary clockwise.\r\n // Let's refine the initial search based on OpenCV's approach:\r\n // Find the first non-zero pixel starting from `initialDirection` clockwise.\r\n let found = false;\r\n for (let i = 0; i < 8; i++) {\r\n searchDirection = (initialDirection + i) % 8;\r\n const nextX = currentPoint.x + deltas[searchDirection].dx;\r\n const nextY = currentPoint.y + deltas[searchDirection].dy;\r\n if (nextX >= 0 && nextX < width && nextY >= 0 && nextY < height && labels[nextY * width + nextX] > 0) {\r\n found = true;\r\n break;\r\n }\r\n }\r\n if (!found) return null; // Should not happen if startPoint is valid\r\n\r\n } else {\r\n // Subsequent steps: Start search from (prevDirection + 2) % 8 clockwise\r\n searchDirection = (prevDirection + 2) % 8;\r\n }\r\n\r\n\r\n let nextPoint = null;\r\n let nextDirection = -1;\r\n\r\n // Search clockwise for the next boundary pixel\r\n for (let i = 0; i < 8; i++) {\r\n const checkDirection = (searchDirection + i) % 8;\r\n const checkX = currentPoint.x + deltas[checkDirection].dx;\r\n const checkY = currentPoint.y + deltas[checkDirection].dy;\r\n\r\n // Check bounds (should be within padded area)\r\n if (checkX >= 0 && checkX < width && checkY >= 0 && checkY < height) {\r\n const pixelLabel = labels[checkY * width + checkX];\r\n if (pixelLabel > 0) { // Found a foreground pixel (labeled or unlabeled)\r\n nextPoint = { x: checkX, y: checkY };\r\n // The direction *from* currentPoint *to* nextPoint is checkDirection\r\n nextDirection = checkDirection;\r\n // The direction *from* which we will arrive *at* nextPoint is (checkDirection + 4) % 8\r\n prevDirection = (checkDirection + 4) % 8;\r\n break;\r\n }\r\n }\r\n }\r\n\r\n if (!nextPoint) {\r\n // Should not happen in a well-formed contour, maybe isolated pixel?\r\n if (points.length === 0) { // If it's just the start point\r\n points.push({ ...currentPoint }); // Add the single point\r\n }\r\n console.warn(`Contour tracing stopped unexpectedly at (${currentPoint.x-1}, ${currentPoint.y-1}) for contour ${contourId}`);\r\n break;\r\n }\r\n\r\n // Add the *current* point to the list before moving\r\n const pointKey = `${currentPoint.x},${currentPoint.y}`;\r\n if (visitedPoints.has(pointKey)) {\r\n // console.warn(`Duplicate point detected at (${currentPoint.x}, ${currentPoint.y}) for contour ${contourId}`);\r\n // console.warn(points)\r\n // console.warn(filtered)\r\n return points; // Avoid infinite loops on duplicate points\r\n }\r\n points.push({ ...currentPoint });\r\n visitedPoints.add(pointKey);\r\n \r\n\r\n // Mark the next pixel if it's unlabeled\r\n if (labels[nextPoint.y * width + nextPoint.x] === 1) {\r\n labels[nextPoint.y * width + nextPoint.x] = contourId;\r\n }\r\n\r\n // Move to the next point\r\n currentPoint = nextPoint;\r\n\r\n // Check if we returned to the start point\r\n if (currentPoint.x === startPoint.x && currentPoint.y === startPoint.y) {\r\n // Check if we came from the same direction as the initial step search ended.\r\n // This is complex, let's use a simpler check: if we are back at start, we are done.\r\n // OpenCV has more sophisticated checks involving i4 == i0 && i3 == i1.\r\n break;\r\n }\r\n }\r\n\r\n if (count >= maxSteps) {\r\n console.warn(`Contour tracing exceeded max steps for contour ${contourId}`);\r\n return null; // Indicate potential error\r\n }\r\n\r\n return points;\r\n}\r\n\r\n/**\r\n * Simplifies a contour polygon using CHAIN_APPROX_SIMPLE.\r\n * Removes intermediate points that lie on the straight line segment between their neighbors.\r\n * @param {Array} points - Array of contour points {x, y}\r\n * @returns {Array} Simplified array of points\r\n */\r\nfunction simplifyChainApproxSimple(points) {\r\n if (points.length <= 2) {\r\n return points;\r\n }\r\n\r\n const simplifiedPoints = [];\r\n const n = points.length;\r\n\r\n for (let i = 0; i < n; i++) {\r\n const prevPoint = points[(i + n - 1) % n]; // Handle wrap around\r\n const currentPoint = points[i];\r\n const nextPoint = points[(i + 1) % n]; // Handle wrap around\r\n\r\n // Check for collinearity: (y2-y1)*(x3-x2) == (y3-y2)*(x2-x1)\r\n const dx1 = currentPoint.x - prevPoint.x;\r\n const dy1 = currentPoint.y - prevPoint.y;\r\n const dx2 = nextPoint.x - currentPoint.x;\r\n const dy2 = nextPoint.y - currentPoint.y;\r\n\r\n // If points are not collinear, keep the current point\r\n if (dx1 * dy2 !== dy1 * dx2) {\r\n simplifiedPoints.push(currentPoint);\r\n }\r\n }\r\n\r\n // Handle cases where all points are collinear (e.g., straight line)\r\n // In this case, the above loop might remove all points. Keep first and last?\r\n // OpenCV keeps the two endpoints of the line segment.\r\n if (simplifiedPoints.length === 0 && n > 0) {\r\n // If all points were collinear, return the start and end points of the original sequence\r\n // This requires knowing the original start/end, which isn't trivial with wrap-around.\r\n // Let's return the first and the point furthest from the first.\r\n if (n === 1) return [points[0]];\r\n if (n === 2) return points;\r\n\r\n // Find the point most distant from the first point to represent the line segment\r\n let maxDistSq = 0;\r\n let farthestIdx = 1;\r\n const p0 = points[0];\r\n for(let i = 1; i < n; i++) {\r\n const pi = points[i];\r\n const distSq = (pi.x - p0.x)**2 + (pi.y - p0.y)**2;\r\n if (distSq > maxDistSq) {\r\n maxDistSq = distSq;\r\n farthestIdx = i;\r\n }\r\n }\r\n // Ensure order if needed, but for simple approx, just two points is fine.\r\n return [points[0], points[farthestIdx]];\r\n }\r\n\r\n\r\n return simplifiedPoints;\r\n}\r\n\r\n\r\n// --- Helper functions (keep or adapt from original) ---\r\n\r\n/**\r\n * Calculates the area of a contour using the shoelace formula\r\n * @param {Array} points - Array of point coordinates {x, y}\r\n * @returns {number} Contour area\r\n */\r\nfunction calculateContourArea(points) {\r\n let area = 0;\r\n const n = points.length;\r\n\r\n if (n < 3) return 0;\r\n\r\n for (let i = 0; i < n; i++) {\r\n const j = (i + 1) % n;\r\n area += points[i].x * points[j].y;\r\n area -= points[j].x * points[i].y;\r\n }\r\n\r\n return Math.abs(area) / 2;\r\n}\r\n\r\n/**\r\n * Calculates the bounding box of a contour\r\n * @param {Array} points - Array of point coordinates\r\n * @returns {Object} Bounding box with minX, minY, maxX, maxY properties\r\n */\r\nfunction calculateBoundingBox(points) {\r\n if (points.length === 0) {\r\n return { minX: 0, minY: 0, maxX: 0, maxY: 0 };\r\n }\r\n let minX = points[0].x;\r\n let minY = points[0].y;\r\n let maxX = points[0].x;\r\n let maxY = points[0].y;\r\n\r\n for (let i = 1; i < points.length; i++) {\r\n const point = points[i];\r\n minX = Math.min(minX, point.x);\r\n minY = Math.min(minY, point.y);\r\n maxX = Math.max(maxX, point.x);\r\n maxY = Math.max(maxY, point.y);\r\n }\r\n\r\n return { minX, minY, maxX, maxY };\r\n}\r\n\r\n\r\n// --- Functions below are no longer directly used by detectDocumentContour ---\r\n// --- but might be useful elsewhere or can be removed ---\r\n\r\n/**\r\n * Simplifies a contour using the Ramer-Douglas-Peucker algorithm\r\n * (No longer used by default contour detection, kept for potential external use)\r\n * @param {Array} points - Array of point coordinates\r\n * @param {number} epsilon - Epsilon value for simplification\r\n * @returns {Array} Simplified contour points\r\n */\r\nexport function simplifyContour(points, epsilon = 1.0) {\r\n // ... (keep existing implementation if needed elsewhere) ...\r\n if (points.length <= 2) {\r\n return points;\r\n }\r\n\r\n // Find point with the maximum distance\r\n let maxDistance = 0;\r\n let index = 0;\r\n\r\n const firstPoint = points[0];\r\n const lastPoint = points[points.length - 1];\r\n\r\n for (let i = 1; i < points.length - 1; i++) {\r\n const distance = perpendicularDistance(points[i], firstPoint, lastPoint);\r\n\r\n if (distance > maxDistance) {\r\n maxDistance = distance;\r\n index = i;\r\n }\r\n }\r\n\r\n // If max distance is greater than epsilon, recursively simplify\r\n if (maxDistance > epsilon) {\r\n // Recursive simplification\r\n const firstSegment = simplifyContour(points.slice(0, index + 1), epsilon);\r\n const secondSegment = simplifyContour(points.slice(index), epsilon);\r\n\r\n // Concatenate the two segments\r\n return firstSegment.slice(0, -1).concat(secondSegment);\r\n } else {\r\n // Return just the endpoints\r\n return [firstPoint, lastPoint];\r\n }\r\n}\r\n\r\n/**\r\n * Calculates the perpendicular distance from a point to a line\r\n * (Helper for RDP simplifyContour, keep if that function is kept)\r\n * @param {Object} point - Point to measure from\r\n * @param {Object} lineStart - Start point of the line\r\n * @param {Object} lineEnd - End point of the line\r\n * @returns {number} Perpendicular distance\r\n */\r\nfunction perpendicularDistance(point, lineStart, lineEnd) {\r\n // ... (keep existing implementation if needed elsewhere) ...\r\n const dx = lineEnd.x - lineStart.x;\r\n const dy = lineEnd.y - lineStart.y;\r\n\r\n // Line length squared\r\n const lineLengthSq = dx * dx + dy * dy;\r\n\r\n if (lineLengthSq === 0) {\r\n // Point to point distance if the line has zero length\r\n return Math.sqrt(\r\n Math.pow(point.x - lineStart.x, 2) +\r\n Math.pow(point.y - lineStart.y, 2)\r\n );\r\n }\r\n\r\n // Calculate the projection parameter t\r\n const t = ((point.x - lineStart.x) * dx + (point.y - lineStart.y) * dy) / lineLengthSq;\r\n\r\n let closestPointX, closestPointY;\r\n\r\n if (t < 0) {\r\n closestPointX = lineStart.x;\r\n closestPointY = lineStart.y;\r\n } else if (t > 1) {\r\n closestPointX = lineEnd.x;\r\n closestPointY = lineEnd.y;\r\n } else {\r\n closestPointX = lineStart.x + t * dx;\r\n closestPointY = lineStart.y + t * dy;\r\n }\r\n\r\n // Calculate the distance from the point to the closest point on the line segment\r\n const distDx = point.x - closestPointX;\r\n const distDy = point.y - closestPointY;\r\n return Math.sqrt(distDx * distDx + distDy * distDy);\r\n\r\n /* // Original implementation using area formula (distance to infinite line)\r\n const lineLength = Math.sqrt(lineLengthSq);\r\n const area = Math.abs(dy * point.x - dx * point.y + lineEnd.x * lineStart.y - lineEnd.y * lineStart.x);\r\n return area / lineLength;\r\n */\r\n}\r\n\r\n/**\r\n * Creates a polygon approximation of a contour using RDP.\r\n * (No longer used by default contour detection, kept for potential external use)\r\n * @param {Array} contourPoints - Array of points {x, y}\r\n * @param {number} epsilon - Epsilon for polygon approximation (relative to perimeter)\r\n * @returns {Array} Array of polygon points\r\n */\r\nexport function approximatePolygon(contourPoints, epsilon = 0.02) {\r\n // Calculate contour perimeter\r\n const perimeter = calculateContourPerimeter(contourPoints);\r\n\r\n // Calculate epsilon based on perimeter\r\n const actualEpsilon = epsilon * perimeter;\r\n\r\n // Simplify the contour using RDP\r\n const simplifiedPoints = simplifyContour(contourPoints, actualEpsilon);\r\n\r\n return simplifiedPoints;\r\n}\r\n\r\n/**\r\n * Calculates the perimeter of a contour\r\n * (Helper for RDP approximatePolygon, keep if that function is kept)\r\n * @param {Array} points - Array of point coordinates\r\n * @returns {number} Contour perimeter\r\n */\r\nfunction calculateContourPerimeter(points) {\r\n // ... (keep existing implementation if needed elsewhere) ...\r\n let perimeter = 0;\r\n const n = points.length;\r\n\r\n if (n < 2) return 0;\r\n\r\n for (let i = 0; i < n; i++) {\r\n const j = (i + 1) % n; // Wrap around for the last segment\r\n const dx = points[i].x - points[j].x;\r\n const dy = points[i].y - points[j].y;\r\n perimeter += Math.sqrt(dx * dx + dy * dy);\r\n }\r\n\r\n return perimeter;\r\n}\r\n\r\n// Flood fill is no longer used for contour detection\r\n/*\r\nfunction floodFill(edges, labels, width, height, startX, startY, label) {\r\n // ... (original floodFill implementation removed) ...\r\n}\r\n*/","/**\r\n * Pure JavaScript implementation for detecting corners of a document\r\n * Replaces OpenCV's corner detection and point finding logic\r\n */\r\n\r\nimport { approximatePolygon } from './contourDetection.js';\r\n\r\n/**\r\n * Calculate distance between two points\r\n * @param {Object} p1 - First point {x, y}\r\n * @param {Object} p2 - Second point {x, y}\r\n * @returns {number} Distance between points\r\n */\r\nexport function distance(p1, p2) {\r\n return Math.hypot(p2.x - p1.x, p2.y - p1.y);\r\n}\r\n\r\n/**\r\n * Find the center point of a contour\r\n * @param {Array} points - Array of contour points\r\n * @returns {Object} Center point {x, y}\r\n */\r\nfunction findCenter(points) {\r\n let sumX = 0;\r\n let sumY = 0;\r\n \r\n for (const point of points) {\r\n sumX += point.x;\r\n sumY += point.y;\r\n }\r\n \r\n return {\r\n x: sumX / points.length,\r\n y: sumY / points.length\r\n };\r\n}\r\n\r\n/**\r\n * Find the four corners of a document contour\r\n * @param {Object} contour - Contour object with points property\r\n * @param {Object} options - Configuration options\r\n * @returns {Object} Object with topLeft, topRight, bottomRight, bottomLeft corners\r\n */\r\nexport function findCornerPoints(contour, options = {}) {\r\n if (!contour || !contour.points || contour.points.length < 4) {\r\n console.warn('Contour does not have enough points for corner detection');\r\n return null;\r\n }\r\n \r\n // Try to find a quadrilateral approximation of the contour\r\n const epsilon = options.epsilon || 0.02;\r\n const approximation = approximatePolygon(contour, epsilon);\r\n \r\n let corners;\r\n \r\n // If we get exactly 4 points, we can use them as corners\r\n if (approximation && approximation.length === 4) {\r\n // console.log('Found 4-point approximation, using as corners');\r\n corners = orderCornerPoints(approximation);\r\n } else {\r\n // console.log(`Polygon approximation gave ${approximation ? approximation.length : 'null'} points, using coordinate extremes method`);\r\n // Fallback: Use the coordinate extremes method on the original contour points\r\n corners = findCornersByCoordinateExtremes(contour.points); \r\n }\r\n \r\n // Ensure all corners were found\r\n if (!corners || !corners.topLeft || !corners.topRight || !corners.bottomRight || !corners.bottomLeft) {\r\n console.warn('Failed to find all four corners.', corners);\r\n // Return null or partial corners? Returning null might be safer downstream.\r\n return null; \r\n }\r\n\r\n // Debug info\r\n console.log('Corner points:', corners);\r\n return corners;\r\n}\r\n\r\n/**\r\n * Find corners by finding points with min/max coordinate sums/differences.\r\n * This is an alternative heuristic for finding corners.\r\n * @param {Array} points - Array of contour points\r\n * @returns {Object} Object with topLeft, topRight, bottomRight, bottomLeft corners\r\n */\r\nfunction findCornersByCoordinateExtremes(points) {\r\n if (!points || points.length === 0) return null;\r\n\r\n let topLeft = points[0]; // Min sum x + y\r\n let topRight = points[0]; // Max diff x - y\r\n let bottomRight = points[0]; // Max sum x + y\r\n let bottomLeft = points[0]; // Min diff x - y\r\n\r\n let minSum = topLeft.x + topLeft.y;\r\n let maxDiff = topRight.x - topRight.y;\r\n let maxSum = bottomRight.x + bottomRight.y;\r\n let minDiff = bottomLeft.x - bottomLeft.y;\r\n\r\n for (let i = 1; i < points.length; i++) {\r\n const point = points[i];\r\n const sum = point.x + point.y;\r\n const diff = point.x - point.y;\r\n\r\n // Top-Left (min sum)\r\n if (sum < minSum) {\r\n minSum = sum;\r\n topLeft = point;\r\n }\r\n // Bottom-Right (max sum)\r\n if (sum > maxSum) {\r\n maxSum = sum;\r\n bottomRight = point;\r\n }\r\n // Top-Right (max diff)\r\n if (diff > maxDiff) {\r\n maxDiff = diff;\r\n topRight = point;\r\n }\r\n // Bottom-Left (min diff)\r\n if (diff < minDiff) {\r\n minDiff = diff;\r\n bottomLeft = point;\r\n }\r\n }\r\n\r\n return {\r\n topLeft,\r\n topRight,\r\n bottomRight,\r\n bottomLeft\r\n };\r\n}\r\n\r\n/**\r\n * Orders 4 points in clockwise order starting from top-left\r\n * @param {Array} points - Array of 4 points to order\r\n * @returns {Object} Object with ordered points\r\n */\r\nfunction orderCornerPoints(points) {\r\n if (points.length !== 4) {\r\n console.warn(`Expected 4 points, got ${points.length}`);\r\n return null;\r\n }\r\n \r\n // Calculate centroid\r\n const center = findCenter(points);\r\n \r\n // Sort the points by their angles relative to the center\r\n const sortedPoints = [...points].sort((a, b) => {\r\n const angleA = Math.atan2(a.y - center.y, a.x - center.x);\r\n const angleB = Math.atan2(b.y - center.y, b.x - center.x);\r\n return angleA - angleB;\r\n });\r\n \r\n // Now find the top-left point (minimum sum of x and y)\r\n let minSum = Infinity;\r\n let minIndex = 0;\r\n \r\n for (let i = 0; i < 4; i++) {\r\n const sum = sortedPoints[i].x + sortedPoints[i].y;\r\n if (sum < minSum) {\r\n minSum = sum;\r\n minIndex = i;\r\n }\r\n }\r\n \r\n // Reorder array to start with the top-left point\r\n const orderedPoints = [\r\n sortedPoints[minIndex],\r\n sortedPoints[(minIndex + 1) % 4],\r\n sortedPoints[(minIndex + 2) % 4],\r\n sortedPoints[(minIndex + 3) % 4]\r\n ];\r\n \r\n // Return as named corners\r\n return {\r\n topLeft: orderedPoints[0],\r\n topRight: orderedPoints[1],\r\n bottomRight: orderedPoints[2],\r\n bottomLeft: orderedPoints[3]\r\n };\r\n}","let wasm;\n\nlet cachedUint8ArrayMemory0 = null;\n\nfunction getUint8ArrayMemory0() {\n if (cachedUint8ArrayMemory0 === null || cachedUint8ArrayMemory0.byteLength === 0) {\n cachedUint8ArrayMemory0 = new Uint8Array(wasm.memory.buffer);\n }\n return cachedUint8ArrayMemory0;\n}\n\nlet WASM_VECTOR_LEN = 0;\n\nfunction passArray8ToWasm0(arg, malloc) {\n const ptr = malloc(arg.length * 1, 1) >>> 0;\n getUint8ArrayMemory0().set(arg, ptr / 1);\n WASM_VECTOR_LEN = arg.length;\n return ptr;\n}\n\nfunction getArrayU8FromWasm0(ptr, len) {\n ptr = ptr >>> 0;\n return getUint8ArrayMemory0().subarray(ptr / 1, ptr / 1 + len);\n}\n/**\n * @param {Uint8Array} edges\n * @param {number} width\n * @param {number} height\n * @param {number} kernel_size\n * @returns {Uint8Array}\n */\nexport function dilate(edges, width, height, kernel_size) {\n const ptr0 = passArray8ToWasm0(edges, wasm.__wbindgen_malloc);\n const len0 = WASM_VECTOR_LEN;\n const ret = wasm.dilate(ptr0, len0, width, height, kernel_size);\n var v2 = getArrayU8FromWasm0(ret[0], ret[1]).slice();\n wasm.__wbindgen_free(ret[0], ret[1] * 1, 1);\n return v2;\n}\n\n/**\n * @param {Uint8Array} grayscale\n * @param {number} width\n * @param {number} height\n * @param {number} low_threshold\n * @param {number} high_threshold\n * @param {number} kernel_size\n * @param {number} sigma\n * @param {boolean} l2_gradient\n * @param {boolean} apply_dilation\n * @param {number} dilation_kernel_size\n * @returns {Uint8Array}\n */\nexport function canny_edge_detector_full(grayscale, width, height, low_threshold, high_threshold, kernel_size, sigma, l2_gradient, apply_dilation, dilation_kernel_size) {\n const ptr0 = passArray8ToWasm0(grayscale, wasm.__wbindgen_malloc);\n const len0 = WASM_VECTOR_LEN;\n 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);\n var v2 = getArrayU8FromWasm0(ret[0], ret[1]).slice();\n wasm.__wbindgen_free(ret[0], ret[1] * 1, 1);\n return v2;\n}\n\nlet cachedFloat32ArrayMemory0 = null;\n\nfunction getFloat32ArrayMemory0() {\n if (cachedFloat32ArrayMemory0 === null || cachedFloat32ArrayMemory0.byteLength === 0) {\n cachedFloat32ArrayMemory0 = new Float32Array(wasm.memory.buffer);\n }\n return cachedFloat32ArrayMemory0;\n}\n\nfunction passArrayF32ToWasm0(arg, malloc) {\n const ptr = malloc(arg.length * 4, 4) >>> 0;\n getFloat32ArrayMemory0().set(arg, ptr / 4);\n WASM_VECTOR_LEN = arg.length;\n return ptr;\n}\n/**\n * Applies double thresholding and hysteresis using a stack-based approach.\n * Optimized version with SIMD for threshold comparisons and better memory access patterns.\n * Follows OpenCV's logic more closely.\n *\n * # Arguments\n * * `suppressed` - Suppressed magnitude values (Float32Array from JavaScript)\n * * `width` - Image width\n * * `height` - Image height\n * * `low_threshold` - Low threshold value\n * * `high_threshold` - High threshold value\n *\n * # Returns\n * Edge map as Vec<u8> (0: weak edge/potential, 1: non-edge, 2: strong edge)\n * @param {Float32Array} suppressed\n * @param {number} width\n * @param {number} height\n * @param {number} low_threshold\n * @param {number} high_threshold\n * @returns {Uint8Array}\n */\nexport function hysteresis_thresholding(suppressed, width, height, low_threshold, high_threshold) {\n const ptr0 = passArrayF32ToWasm0(suppressed, wasm.__wbindgen_malloc);\n const len0 = WASM_VECTOR_LEN;\n const ret = wasm.hysteresis_thresholding(ptr0, len0, width, height, low_threshold, high_threshold);\n var v2 = getArrayU8FromWasm0(ret[0], ret[1]).slice();\n wasm.__wbindgen_free(ret[0], ret[1] * 1, 1);\n return v2;\n}\n\n/**\n * Creates a binary edge image from the hysteresis edge map\n * SIMD-optimized version for converting edge map to binary\n *\n * # Arguments\n * * `edge_map` - Edge map from hysteresis thresholding (0, 1, 2 values)\n *\n * # Returns\n * Binary edge image as Vec<u8> (0 or 255)\n * @param {Uint8Array} edge_map\n * @returns {Uint8Array}\n */\nexport function edge_map_to_binary(edge_map) {\n const ptr0 = passArray8ToWasm0(edge_map, wasm.__wbindgen_malloc);\n const len0 = WASM_VECTOR_LEN;\n const ret = wasm.edge_map_to_binary(ptr0, len0);\n var v2 = getArrayU8FromWasm0(ret[0], ret[1]).slice();\n wasm.__wbindgen_free(ret[0], ret[1] * 1, 1);\n return v2;\n}\n\n/**\n * Combined hysteresis thresholding and binary conversion\n * This is a convenience function that combines both steps for efficiency\n * Optimized to avoid intermediate allocations where possible\n *\n * # Arguments\n * * `suppressed` - Suppressed magnitude values (Float32Array from JavaScript)\n * * `width` - Image width\n * * `height` - Image height\n * * `low_threshold` - Low threshold value\n * * `high_threshold` - High threshold value\n *\n * # Returns\n * Binary edge image as Vec<u8> (0 or 255)\n * @param {Float32Array} suppressed\n * @param {number} width\n * @param {number} height\n * @param {number} low_threshold\n * @param {number} high_threshold\n * @returns {Uint8Array}\n */\nexport function hysteresis_thresholding_binary(suppressed, width, height, low_threshold, high_threshold) {\n const ptr0 = passArrayF32ToWasm0(suppressed, wasm.__wbindgen_malloc);\n const len0 = WASM_VECTOR_LEN;\n const ret = wasm.hysteresis_thresholding_binary(ptr0, len0, width, height, low_threshold, high_threshold);\n var v2 = getArrayU8FromWasm0(ret[0], ret[1]).slice();\n wasm.__wbindgen_free(ret[0], ret[1] * 1, 1);\n return v2;\n}\n\n/**\n * @param {Uint8Array} grayscale\n * @param {number} width\n * @param {number} height\n * @param {number} kernel_size\n * @param {number} sigma\n * @returns {Uint8Array}\n */\nexport function blur(grayscale, width, height, kernel_size, sigma) {\n const ptr0 = passArray8ToWasm0(grayscale, wasm.__wbindgen_malloc);\n const len0 = WASM_VECTOR_LEN;\n const ret = wasm.blur(ptr0, len0, width, height, kernel_size, sigma);\n var v2 = getArrayU8FromWasm0(ret[0], ret[1]).slice();\n wasm.__wbindgen_free(ret[0], ret[1] * 1, 1);\n return v2;\n}\n\nlet cachedUint16ArrayMemory0 = null;\n\nfunction getUint16ArrayMemory0() {\n if (cachedUint16ArrayMemory0 === null || cachedUint16ArrayMemory0.byteLength === 0) {\n cachedUint16ArrayMemory0 = new Uint16Array(wasm.memory.buffer);\n }\n return cachedUint16ArrayMemory0;\n}\n\nfunction passArray16ToWasm0(arg, malloc) {\n const ptr = malloc(arg.length * 2, 2) >>> 0;\n getUint16ArrayMemory0().set(arg, ptr / 2);\n WASM_VECTOR_LEN = arg.length;\n return ptr;\n}\n\nfunction getArrayF32FromWasm0(ptr, len) {\n ptr = ptr >>> 0;\n return getFloat32ArrayMemory0().subarray(ptr / 4, ptr / 4 + len);\n}\n/**\n * @param {Int16Array} dx\n * @param {Int16Array} dy\n * @param {number} width\n * @param {number} height\n * @param {boolean} l2_gradient\n * @returns {Float32Array}\n */\nexport function non_maximum_suppression(dx, dy, width, height, l2_gradient) {\n const ptr0 = passArray16ToWasm0(dx, wasm.__wbindgen_malloc);\n const len0 = WASM_VECTOR_LEN;\n const ptr1 = passArray16ToWasm0(dy, wasm.__wbindgen_malloc);\n const len1 = WASM_VECTOR_LEN;\n const ret = wasm.non_maximum_suppression(ptr0, len0, ptr1, len1, width, height, l2_gradient);\n var v3 = getArrayF32FromWasm0(ret[0], ret[1]).slice();\n wasm.__wbindgen_free(ret[0], ret[1] * 4, 4);\n return v3;\n}\n\nlet cachedInt16ArrayMemory0 = null;\n\nfunction getInt16ArrayMemory0() {\n if (cachedInt16ArrayMemory0 === null || cachedInt16ArrayMemory0.byteLength === 0) {\n cachedInt16ArrayMemory0 = new Int16Array(wasm.memory.buffer);\n }\n return cachedInt16ArrayMemory0;\n}\n\nfunction getArrayI16FromWasm0(ptr, len) {\n ptr = ptr >>> 0;\n return getInt16ArrayMemory0().subarray(ptr / 2, ptr / 2 + len);\n}\n/**\n * @param {Uint8Array} blurred\n * @param {number} width\n * @param {number} height\n * @returns {Int16Array}\n */\nexport function calculate_gradients(blurred, width, height) {\n const ptr0 = passArray8ToWasm0(blurred, wasm.__wbindgen_malloc);\n const len0 = WASM_VECTOR_LEN;\n const ret = wasm.calculate_gradients(ptr0, len0, width, height);\n var v2 = getArrayI16FromWasm0(ret[0], ret[1]).slice();\n wasm.__wbindgen_free(ret[0], ret[1] * 2, 2);\n return v2;\n}\n\nasync function __wbg_load(module, imports) {\n if (typeof Response === 'function' && module instanceof Response) {\n if (typeof WebAssembly.instantiateStreaming === 'function') {\n try {\n return await WebAssembly.instantiateStreaming(module, imports);\n\n } catch (e) {\n if (module.headers.get('Content-Type') != 'application/wasm') {\n 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);\n\n } else {\n throw e;\n }\n }\n }\n\n const bytes = await module.arrayBuffer();\n return await WebAssembly.instantiate(bytes, imports);\n\n } else {\n const instance = await WebAssembly.instantiate(module, imports);\n\n if (instance instanceof WebAssembly.Instance) {\n return { instance, module };\n\n } else {\n return instance;\n }\n }\n}\n\nfunction __wbg_get_imports() {\n const imports = {};\n imports.wbg = {};\n imports.wbg.__wbindgen_init_externref_table = function() {\n const table = wasm.__wbindgen_export_0;\n const offset = table.grow(4);\n table.set(0, undefined);\n table.set(offset + 0, undefined);\n table.set(offset + 1, null);\n table.set(offset + 2, true);\n table.set(offset + 3, false);\n ;\n };\n\n return imports;\n}\n\nfunction __wbg_init_memory(imports, memory) {\n\n}\n\nfunction __wbg_finalize_init(instance, module) {\n wasm = instance.exports;\n __wbg_init.__wbindgen_wasm_module = module;\n cachedFloat32ArrayMemory0 = null;\n cachedInt16ArrayMemory0 = null;\n cachedUint16ArrayMemory0 = null;\n cachedUint8ArrayMemory0 = null;\n\n\n wasm.__wbindgen_start();\n return wasm;\n}\n\nfunction initSync(module) {\n if (wasm !== undefined) return wasm;\n\n\n if (typeof module !== 'undefined') {\n if (Object.getPrototypeOf(module) === Object.prototype) {\n ({module} = module)\n } else {\n console.warn('using deprecated parameters for `initSync()`; pass a single object instead')\n }\n }\n\n const imports = __wbg_get_imports();\n\n __wbg_init_memory(imports);\n\n if (!(module instanceof WebAssembly.Module)) {\n module = new WebAssembly.Module(module);\n }\n\n const instance = new WebAssembly.Instance(module, imports);\n\n return __wbg_finalize_init(instance, module);\n}\n\nasync function __wbg_init(module_or_path) {\n if (wasm !== undefined) return wasm;\n\n\n if (typeof module_or_path !== 'undefined') {\n if (Object.getPrototypeOf(module_or_path) === Object.prototype) {\n ({module_or_path} = module_or_path)\n } else {\n console.warn('using deprecated parameters for the initialization function; pass a single object instead')\n }\n }\n\n if (typeof module_or_path === 'undefined') {\n module_or_path = new URL('wasm_blur_bg.wasm', import.meta.url);\n }\n const imports = __wbg_get_imports();\n\n if (typeof module_or_path === 'string' || (typeof Request === 'function' && module_or_path instanceof Request) || (typeof URL === 'function' && module_or_path instanceof URL)) {\n module_or_path = fetch(module_or_path);\n }\n\n __wbg_init_memory(imports);\n\n const { instance, module } = await __wbg_load(await module_or_path, imports);\n\n return __wbg_finalize_init(instance, module);\n}\n\nexport { initSync };\nexport default __wbg_init;\n","/**\r\n * Pure JavaScript implementation of edge detection algorithms\r\n * Inspired by OpenCV's Canny edge detector\r\n */\r\n\r\nimport { DEFAULTS } from './constants.js';\r\nimport init, { \r\n blur as wasmBlur, \r\n calculate_gradients as wasmGradients, \r\n dilate as wasmDilate, \r\n non_maximum_suppression as wasmMaximumSuppression, \r\n canny_edge_detector_full as wasmFullCanny,\r\n hysteresis_thresholding as wasmHysteresis,\r\n hysteresis_thresholding_binary as wasmHysteresisBinary\r\n} from '../wasm_blur/pkg/wasm_blur.js';\r\n\r\n// Initialize the wasm module\r\nconst wasmReady = init();\r\n\r\n/**\r\n * Converts ImageData to grayscale (separate from blur for consistency with jscanify)\r\n * @param {ImageData} imageData - Original image data\r\n * @returns {Uint8ClampedArray} Grayscale image data (1 channel)\r\n */\r\nexport function convertToGrayscale(imageData) {\r\n const { width, height, data } = imageData;\r\n const grayscale = new Uint8ClampedArray(width * height);\r\n \r\n // Convert to grayscale with integer math (faster than floating point)\r\n // Use bit shifting for multiplication (>>8 is equivalent to /256)\r\n for (let i = 0, j = 0; i < data.length; i += 4, j++) {\r\n // 54 (~0.2126*256), 183 (~0.7152*256), 19 (~0.0722*256)\r\n grayscale[j] = (data[i] * 54 + data[i+1] * 183 + data[i+2] * 19) >> 8;\r\n }\r\n \r\n return grayscale;\r\n}\r\n\r\n/**\r\n * Applies Gaussian blur to a grayscale image (matching jscanify's approach)\r\n * @param {Uint8ClampedArray} grayscale - Grayscale image data\r\n * @param {number} width - Image width\r\n * @param {number} height - Image height\r\n * @param {number} kernelSize - Kernel size (should be 5 to match jscanify)\r\n * @param {number} sigma - Gaussian sigma parameter\r\n * @returns {Uint8ClampedArray} Blurred grayscale image data\r\n */\r\nexport function gaussianBlurGrayscale(grayscale, width, height, kernelSize = 5, sigma = 0) {\r\n // If sigma is 0, calculate it from kernel size (OpenCV default)\r\n if (sigma === 0) {\r\n sigma = 0.3 * ((kernelSize - 1) * 0.5 - 1) + 0.8;\r\n }\r\n \r\n const halfKernel = Math.floor(kernelSize / 2);\r\n \r\n // Create and normalize Gaussian kernel once\r\n const kernel = createGaussianKernel(kernelSize, sigma);\r\n \r\n // Preallocate arrays\r\n const tempArray = new Uint8ClampedArray(width * height);\r\n const blurred = new Uint8ClampedArray(width * height);\r\n \r\n // Horizontal pass - process rows in a single loop to improve cache locality\r\n for (let y = 0; y < height; y++) {\r\n const rowOffset = y * width;\r\n \r\n for (let x = 0; x < width; x++) {\r\n let sum = 0;\r\n \r\n // Apply kernel horizontally with bounds checking\r\n for (let k = -halfKernel; k <= halfKernel; k++) {\r\n const xOffset = Math.min(width - 1, Math.max(0, x + k));\r\n sum += grayscale[rowOffset + xOffset] * kernel[halfKernel + k];\r\n }\r\n \r\n tempArray[rowOffset + x] = sum;\r\n }\r\n }\r\n \r\n // Vertical pass - process columns with better memory access pattern\r\n for (let x = 0; x < width; x++) {\r\n for (let y = 0; y < height; y++) {\r\n let sum = 0;\r\n \r\n // Apply kernel vertically with bounds checking\r\n for (let k = -halfKernel; k <= halfKernel; k++) {\r\n const yOffset = Math.min(height - 1, Math.max(0, y + k));\r\n sum += tempArray[yOffset * width + x] * kernel[halfKernel + k];\r\n }\r\n \r\n blurred[y * width + x] = Math.round(sum);\r\n }\r\n }\r\n \r\n return blurred;\r\n}\r\n\r\n/**\r\n * Legacy wrapper for backwards compatibility\r\n * @param {ImageData} imageData - Original image data\r\n * @param {number} sigma - Gaussian sigma parameter (standard deviation)\r\n * @returns {Uint8ClampedArray} Blurred grayscale image data (1 channel)\r\n */\r\nexport function gaussianBlur(imageData, sigma = DEFAULTS.GAUSSIAN_SIGMA, forcedKernelSize = null) {\r\n const grayscale = convertToGrayscale(imageData);\r\n const kernelSize = forcedKernelSize || 5; // Default to 5 like jscanify\r\n return gaussianBlurGrayscale(grayscale, imageData.width, imageData.height, kernelSize, sigma);\r\n}\r\n\r\n/**\r\n * Creates a 1D Gaussian kernel\r\n * @param {number} size - Kernel size (odd number)\r\n * @param {number} sigma - Gaussian sigma parameter\r\n * @returns {Float32Array} Gaussian kernel\r\n */\r\nfunction createGaussianKernel(size, sigma) {\r\n const kernel = new Float32Array(size);\r\n const halfSize = Math.floor(size / 2);\r\n \r\n let sum = 0;\r\n for (let i = 0; i < size; i++) {\r\n const x = i - halfSize;\r\n // Gaussian function: (1/(sigma*sqrt(2*PI))) * e^(-(x^2)/(2*sigma^2))\r\n kernel[i] = Math.exp(-(x * x) / (2 * sigma * sigma));\r\n sum += kernel[i];\r\n }\r\n \r\n // Normalize kernel\r\n for (let i = 0; i < size; i++) {\r\n kernel[i] /= sum;\r\n }\r\n \r\n return kernel;\r\n}\r\n\r\n/**\r\n * Calculates the gradients (dx, dy) using Sobel operators\r\n * @param {Uint8ClampedArray} blurred - Blurred grayscale image\r\n * @param {number} width - Image width\r\n * @param {number} height - Image height\r\n * @returns {{dx: Int16Array, dy: Int16Array}} Object containing gradient arrays\r\n */\r\nfunction calculateGradients(blurred, width, height) {\r\n // Use Int16Array to store gradients, allowing negative values\r\n const dx = new Int16Array(width * height);\r\n const dy = new Int16Array(width * height);\r\n \r\n // Find gradients by unrolling the Sobel operator loops\r\n for (let y = 1; y < height - 1; y++) {\r\n const rowOffset = y * width;\r\n const prevRowOffset = (y - 1) * width;\r\n const nextRowOffset = (y + 1) * width;\r\n\r\n for (let x = 1; x < width - 1; x++) {\r\n const currentIdx = rowOffset + x;\r\n\r\n // Get neighborhood pixels\r\n const p0 = blurred[prevRowOffset + x - 1];\r\n const p1 = blurred[prevRowOffset + x];\r\n const p2 = blurred[prevRowOffset + x + 1];\r\n const p3 = blurred[rowOffset + x - 1];\r\n const p5 = blurred[rowOffset + x + 1];\r\n const p6 = blurred[nextRowOffset + x - 1];\r\n const p7 = blurred[nextRowOffset + x];\r\n const p8 = blurred[nextRowOffset + x + 1];\r\n \r\n // Calculate Sobel gradients\r\n const gx = (p2 - p0) + 2 * (p5 - p3) + (p8 - p6);\r\n const gy = (p6 + 2 * p7 + p8) - (p0 + 2 * p1 + p2);\r\n \r\n dx[currentIdx] = gx;\r\n dy[currentIdx] = gy;\r\n }\r\n }\r\n \r\n return { dx, dy };\r\n}\r\n\r\n\r\n/**\r\n * Applies non-maximum suppression to the gradient magnitude\r\n * @param {Int16Array} dx - Gradient in x-direction\r\n * @param {Int16Array} dy - Gradient in y-direction\r\n * @param {number} width - Image width\r\n * @param {number} height - Image height\r\n * @param {boolean} L2gradient - Whether to use L2 norm for magnitude\r\n * @returns {Float32Array} Suppressed magnitude (using Float32 for precision)\r\n */\r\nfunction nonMaximumSuppression(dx, dy, width, height, L2gradient) {\r\n // Use Float32Array for magnitude to preserve precision before thresholding\r\n const magnitude = new Float32Array(width * height);\r\n const suppressed = new Float32Array(width * height);\r\n \r\n // Calculate magnitude for all pixels first\r\n for (let i = 0; i < dx.length; i++) {\r\n const gx = dx[i];\r\n const gy = dy[i];\r\n if (L2gradient) {\r\n magnitude[i] = Math.sqrt(gx * gx + gy * gy);\r\n } else {\r\n magnitude[i] = Math.abs(gx) + Math.abs(gy); // L1 norm\r\n }\r\n }\r\n \r\n // Perform non-maximum suppression\r\n for (let y = 1; y < height - 1; y++) {\r\n for (let x = 1; x < width - 1; x++) {\r\n const idx = y * width + x;\r\n const mag = magnitude[idx];\r\n \r\n // Skip pixels with zero magnitude\r\n if (mag === 0) {\r\n suppressed[idx] = 0;\r\n continue;\r\n }\r\n \r\n const gx = dx[idx];\r\n const gy = dy[idx];\r\n \r\n let neighbor1 = 0, neighbor2 = 0;\r\n \r\n // Determine neighbors based on gradient direction\r\n // Use absolute values to determine dominant direction\r\n const absGx = Math.abs(gx);\r\n const absGy = Math.abs(gy);\r\n \r\n if (absGy > absGx * 2.4142) { // Vertical edge (angle near 90 or 270)\r\n neighbor1 = magnitude[idx - width]; // top\r\n neighbor2 = magnitude[idx + width]; // bottom\r\n } else if (absGx > absGy * 2.4142) { // Horizontal edge (angle near 0 or 180)\r\n neighbor1 = magnitude[idx - 1]; // left\r\n neighbor2 = magnitude[idx + 1]; // right\r\n } else { // Diagonal edge\r\n // Determine diagonal direction based on signs of gx and gy\r\n const s = (gx ^ gy) < 0 ? -1 : 1; // Check if signs are different\r\n if (gy > 0) { // Gradient points down\r\n neighbor1 = magnitude[(y - 1) * width + (x - s)]; // top-left/right\r\n neighbor2 = magnitude[(y + 1) * width + (x + s)]; // bottom-right/left\r\n } else { // Gradient points up\r\n neighbor1 = magnitude[(y + 1) * width + (x - s)]; // bottom-left/right\r\n neighbor2 = magnitude[(y - 1) * width + (x + s)]; // top-right/left\r\n }\r\n // Refined diagonal check (approximating OpenCV's logic)\r\n // Check 45 degrees (top-right / bottom-left)\r\n if ((gx > 0 && gy > 0) || (gx < 0 && gy < 0)) { // Quadrants 1 & 3\r\n neighbor1 = magnitude[(y - 1) * width + (x + 1)]; // top-right\r\n neighbor2 = magnitude[(y + 1) * width + (x - 1)]; // bottom-left\r\n } else { // Quadrants 2 & 4 (135 degrees)\r\n neighbor1 = magnitude[(y - 1) * width + (x - 1)]; // top-left\r\n neighbor2 = magnitude[(y + 1) * width + (x + 1)]; // bottom-right\r\n }\r\n }\r\n \r\n // If the pixel's magnitude is greater than or equal to its neighbors\r\n // along the gradient direction, keep it. Otherwise, suppress it.\r\n if (mag >= neighbor1 && mag >= neighbor2) {\r\n suppressed[idx] = mag;\r\n } else {\r\n suppressed[idx] = 0;\r\n }\r\n }\r\n }\r\n return suppressed;\r\n}\r\n\r\n\r\n/**\r\n * Applies double thresholding and hysteresis using a stack-based approach.\r\n * Follows OpenCV's logic more closely.\r\n * @param {Float32Array} suppressed - Suppressed magnitude (Float32Array)\r\n * @param {number} width - Image width\r\n * @param {number} height - Image height\r\n * @param {number} lowThreshold - Low threshold value\r\n * @param {number} highThreshold - High threshold value\r\n * @returns {Uint8Array} Edge map (0: non-edge, 2: edge pixel)\r\n */\r\nfunction hysteresisThresholding(suppressed, width, height, lowThreshold, highThreshold) {\r\n // Map values: 0 = weak edge (potential), 1 = non-edge, 2 = strong edge\r\n const edgeMap = new Uint8Array(width * height);\r\n const stack = [];\r\n \r\n // First pass: Identify strong edges and potential weak edges\r\n for (let y = 1; y < height - 1; y++) { // Iterate excluding borders\r\n for (let x = 1; x < width - 1; x++) {\r\n const idx = y * width + x;\r\n const mag = suppressed[idx];\r\n \r\n if (mag >= highThreshold) {\r\n // Strong edge pixel\r\n edgeMap[idx] = 2;\r\n stack.push({ x, y });\r\n } else if (mag >= lowThreshold) {\r\n // Weak edge pixel (potential edge)\r\n edgeMap[idx] = 0; // Mark as potential\r\n } else {\r\n // Non-edge pixel\r\n edgeMap[idx] = 1; // Mark as non-edge\r\n }\r\n }\r\n }\r\n // Initialize borders as non-edge (value 1)\r\n for (let x = 0; x < width; x++) {\r\n edgeMap[x] = 1; // Top row\r\n edgeMap[(height - 1) * width + x] = 1; // Bottom row\r\n }\r\n for (let y = 1; y < height - 1; y++) {\r\n edgeMap[y * width] = 1; // Left column\r\n edgeMap[y * width + width - 1] = 1; // Right column\r\n }\r\n\r\n\r\n // Second pass: Hysteresis - connect weak edges to strong edges\r\n const dxNeighbors = [-1, 0, 1, -1, 1, -1, 0, 1];\r\n const dyNeighbors = [-1, -1, -1, 0, 0, 1, 1, 1];\r\n \r\n while (stack.length > 0) {\r\n const { x, y } = stack.pop();\r\n \r\n // Check all 8 neighbors\r\n for (let i = 0; i < 8; i++) {\r\n const nx = x + dxNeighbors[i];\r\n const ny = y + dyNeighbors[i];\r\n const nidx = ny * width + nx;\r\n \r\n // Check bounds (already handled by border initialization)\r\n // If neighbor is a weak edge (value 0), promote it to strong (value 2) and add to stack\r\n if (edgeMap[nidx] === 0) {\r\n edgeMap[nidx] = 2; // Promote to strong edge\r\n stack.push({ x: nx, y: ny });\r\n }\r\n }\r\n }\r\n \r\n // Note: Pixels that were initially weak (0) but not connected remain 0.\r\n // Pixels below lowThreshold remain 1. Only pixels marked 2 are considered final edges.\r\n \r\n return edgeMap; // Return the map with 0, 1, 2 values\r\n}\r\n\r\n/**\r\n * Applies morphological dilation to binary image using a separable (two-pass) approach.\r\n * This is much faster than a 2D kernel for square structuring elements.\r\n * @param {Uint8ClampedArray} edges - Binary edge image (0 or 255)\r\n * @param {number} width - Image width\r\n * @param {number} height - Image height\r\n * @param {number} kernelSize - Kernel size (default 5 to match jscanify)\r\n * @returns {Uint8ClampedArray} Dilated edge image\r\n */\r\nexport function dilateEdges(edges, width, height, kernelSize = 5) {\r\n const halfKernel = Math.floor(kernelSize / 2);\r\n const temp = new Uint8ClampedArray(width * height);\r\n const dilated = new Uint8ClampedArray(width * height);\r\n\r\n // Horizontal pass\r\n for (let y = 0; y < height; y++) {\r\n const rowOffset = y * width;\r\n for (let x = 0; x < width; x++) {\r\n let maxVal = 0;\r\n // Find max in horizontal neighborhood\r\n for (let k = -halfKernel; k <= halfKernel; k++) {\r\n const nx = x + k;\r\n if (nx >= 0 && nx < width) {\r\n const val = edges[rowOffset + nx];\r\n if (val > maxVal) {\r\n maxVal = val;\r\n }\r\n }\r\n }\r\n temp[rowOffset + x] = maxVal;\r\n }\r\n }\r\n\r\n // Vertical pass\r\n for (let x = 0; x < width; x++) {\r\n for (let y = 0; y < height; y++) {\r\n let maxVal = 0;\r\n // Find max in vertical neighborhood from temp array\r\n for (let k = -halfKernel; k <= halfKernel; k++) {\r\n const ny = y + k;\r\n if (ny >= 0 && ny < height) {\r\n const val = temp[ny * width + x];\r\n if (val > maxVal) {\r\n maxVal = val;\r\n }\r\n }\r\n }\r\n dilated[y * width + x] = maxVal;\r\n }\r\n }\r\n \r\n return dilated;\r\n}\r\n\r\n/**\r\n * Full Canny edge detector implementation matching jscanify's approach\r\n * @param {ImageData} imageData - Original image data\r\n * @param {Object} options - Configuration options\r\n * @param {number} [options.lowThreshold=75] - Low threshold for hysteresis (matching jscanify)\r\n * @param {number} [options.highThreshold=200] - High threshold for hysteresis (matching jscanify)\r\n * @param {number} [options.sigma=0] - Gaussian blur sigma (0 means auto-calculate from kernel size)\r\n * @param {number} [options.kernelSize=5] - Gaussian kernel size (matching jscanify)\r\n * @param {boolean} [options.L2gradient=false] - Use L2 norm for gradient magnitude (like OpenCV default)\r\n * @param {boolean} [options.applyDilation=true] - Apply dilation after Canny (matching jscanify)\r\n * @param {number} [options.dilationKernelSize=5] - Dilation kernel size\r\n * @param {boolean} [options.useWasmBlur=false] - Use WASM for Gaussian blur\r\n * @param {boolean} [options.useWasmGradients=false] - Use WASM for gradient calculation\r\n * @param {boolean} [options.useWasmDilation=false] - Use WASM for dilation\r\n * @param {boolean} [options.useWasmNMS=false] - Use WASM for non-maximum suppression\r\n * @param {boolean} [options.useWasmHysteresis=false] - Use WASM for hysteresis thresholding\r\n * @param {boolean} [options.useWasmFullCanny=false] - Use the full WASM Canny implementation\r\n * @param {object} [options.debug={}] - Object to store intermediate results if provided\r\n * @returns {Promise<Uint8ClampedArray>} Binary edge image (0 or 255)\r\n */\r\nexport async function cannyEdgeDetector(imageData, options = {}) {\r\n // Timing table setup\r\n const timings = [];\r\n const tStart = performance.now();\r\n\r\n const { width, height } = imageData;\r\n let lowThreshold = options.lowThreshold !== undefined ? options.lowThreshold : 75;\r\n let highThreshold = options.highThreshold !== undefined ? options.highThreshold : 200;\r\n const kernelSize = options.kernelSize || 5; // Match jscanify's 5x5 kernel\r\n const sigma = options.sigma || 0; // Let the blur function calculate sigma\r\n const L2gradient = options.L2gradient === undefined ? false : options.L2gradient;\r\n const applyDilation = options.applyDilation !== undefined ? options.applyDilation : true;\r\n const dilationKernelSize = options.dilationKernelSize || 5;\r\n const useWasmBlur = true;\r\n const useWasmGradients = false; \r\n const useWasmDilation = true;\r\n const useWasmNMS = true;\r\n const useWasmHysteresis = options.useWasmHysteresis !== undefined ? options.useWasmHysteresis : false;\r\n const useWasmFullCanny = false;\r\n\r\n // Ensure high threshold is greater than low threshold\r\n if (lowThreshold >= highThreshold) {\r\n console.warn(`Canny Edge Detector: lowThreshold (${lowThreshold}) should be lower than highThreshold (${highThreshold}). Swapping them.`);\r\n [lowThreshold, highThreshold] = [highThreshold, lowThreshold];\r\n }\r\n\r\n // Step 1: Convert to grayscale\r\n let t0 = performance.now();\r\n const grayscale = convertToGrayscale(imageData);\r\n let t1 = performance.now();\r\n timings.push({ step: 'Grayscale', ms: (t1 - t0).toFixed(2) });\r\n if (options.debug) options.debug.grayscale = grayscale;\r\n\r\n // Step 2: Apply Gaussian blur (JS or WASM)\r\n let blurred;\r\n t0 = performance.now();\r\n if (useWasmBlur) {\r\n try {\r\n await wasmReady; // Ensure wasm is initialized\r\n blurred = wasmBlur(grayscale, width, height, kernelSize, sigma);\r\n } catch (e) {\r\n blurred = gaussianBlurGrayscale(grayscale, width, height, kernelSize, sigma);\r\n }\r\n } else {\r\n blurred = gaussianBlurGrayscale(grayscale, width, height, kernelSize, sigma);\r\n }\r\n t1 = performance.now();\r\n timings.push({ step: 'Gaussian Blur', ms: (t1 - t0).toFixed(2) });\r\n if (options.debug) {\r\n options.debug.blurred = blurred;\r\n }\r\n\r\n // Step 3: Compute gradients (dx, dy)\r\n t0 = performance.now();\r\n let dx, dy;\r\n if (useWasmGradients) {\r\n try {\r\n await wasmReady; // Ensure wasm is initialized\r\n const gradientResult = wasmGradients(blurred, width, height);\r\n dx = new Int16Array(gradientResult.gx);\r\n dy = new Int16Array(gradientResult.gy);\r\n } catch (e) {\r\n const gradients = calculateGradients(blurred, width, height);\r\n dx = gradients.dx;\r\n dy = gradients.dy;\r\n }\r\n } else {\r\n const gradients = calculateGradients(blurred, width, height);\r\n dx = gradients.dx;\r\n dy = gradients.dy;\r\n }\r\n t1 = performance.now();\r\n timings.push({ step: 'Gradients', ms: (t1 - t0).toFixed(2) });\r\n\r\n // Step 4: Apply non-maximum suppression\r\n t0 = performance.now();\r\n let suppressed;\r\n if (useWasmNMS) {\r\n try {\r\n await wasmReady;\r\n suppressed = await wasmMaximumSuppression(dx, dy, width, height, L2gradient);\r\n } catch (e) {\r\n suppressed = nonMaximumSuppression(dx, dy, width, height, L2gradient);\r\n }\r\n } else {\r\n suppressed = nonMaximumSuppression(dx, dy, width, height, L2gradient);\r\n }\r\n t1 = performance.now();\r\n timings.push({ step: 'Non-Max Suppression', ms: (t1 - t0).toFixed(2) });\r\n\r\n // Step 5: Apply double thresholding and hysteresis\r\n t0 = performance.now();\r\n const finalLowThreshold = L2gradient ? lowThreshold * lowThreshold : lowThreshold;\r\n const finalHighThreshold = L2gradient ? highThreshold * highThreshold : highThreshold;\r\n \r\n let edgeMap;\r\n if (useWasmHysteresis) {\r\n try {\r\n await wasmReady;\r\n edgeMap = wasmHysteresis(suppressed, width, height, finalLowThreshold, finalHighThreshold);\r\n } catch (e) {\r\n console.warn(\"WASM hysteresis failed, falling back to JS:\", e);\r\n edgeMap = hysteresisThresholding(suppressed, width, height, finalLowThreshold, finalHighThreshold);\r\n }\r\n } else {\r\n edgeMap = hysteresisThresholding(suppressed, width, height, finalLowThreshold, finalHighThreshold);\r\n }\r\n \r\n t1 = performance.now();\r\n timings.push({ step: 'Hysteresis', ms: (t1 - t0).toFixed(2) });\r\n\r\n // Step 6: Create binary image (0 or 255)\r\n t0 = performance.now();\r\n const cannyEdges = new Uint8ClampedArray(width * height);\r\n for (let i = 0; i < edgeMap.length; i++) {\r\n cannyEdges[i] = edgeMap[i] === 2 ? 255 : 0;\r\n }\r\n t1 = performance.now();\r\n timings.push({ step: 'Binary Image', ms: (t1 - t0).toFixed(2) });\r\n\r\n // Step 7: Apply dilation if requested (matching jscanify)\r\n t0 = performance.now();\r\n let finalEdges = cannyEdges;\r\n if (applyDilation) {\r\n if (useWasmDilation) {\r\n try {\r\n await wasmReady; // Ensure wasm is initialized\r\n finalEdges = wasmDilate(cannyEdges, width, height, dilationKernelSize);\r\n } catch (e) {\r\n finalEdges = dilateEdges(cannyEdges, width, height, dilationKernelSize);\r\n }\r\n } else {\r\n finalEdges = dilateEdges(cannyEdges, width, height, dilationKernelSize);\r\n }\r\n }\r\n t1 = performance.now();\r\n timings.push({ step: 'Dilation', ms: (t1 - t0).toFixed(2) });\r\n\r\n // Store debug info if requested\r\n if (options.debug) {\r\n options.debug.dx = dx; // Int16Array\r\n options.debug.dy = dy; // Int16Array\r\n // Calculate magnitude separately for debugging if needed\r\n const magnitude = new Float32Array(width * height);\r\n for (let i = 0; i < dx.length; i++) {\r\n const gx = dx[i]; const gy = dy[i];\r\n magnitude[i] = L2gradient ? Math.sqrt(gx * gx + gy * gy) : Math.abs(gx) + Math.abs(gy);\r\n }\r\n options.debug.magnitude = magnitude; // Float32Array (raw magnitude)\r\n options.debug.suppressed = suppressed; // Float32Array (after NMS)\r\n options.debug.edgeMap = edgeMap; // Uint8Array (0, 1, 2 values from hysteresis)\r\n options.debug.cannyEdges = cannyEdges; // Uint8ClampedArray (0 or 255, before dilation)\r\n options.debug.finalEdges = finalEdges; // Uint8ClampedArray (0 or 255, after dilation if applied)\r\n options.debug.timings = timings;\r\n }\r\n\r\n const tEnd = performance.now();\r\n timings.unshift({ step: 'Total', ms: (tEnd - tStart).toFixed(2) });\r\n // Print timing table\r\n console.table(timings);\r\n\r\n return finalEdges; // Return the final binary edge image\r\n}\r\n\r\n/**\r\n * Full Canny edge detector implementation using WASM, for comparison or direct use\r\n * This function is intended to match the performance and output of the JS cannyEdgeDetector,\r\n * but runs entirely in WASM for potentially faster execution.\r\n * @param {ImageData} imageData - Original image data\r\n * @param {Object} options - Configuration options (same as cannyEdgeDetector)\r\n * @returns {Promise<Uint8ClampedArray>} Binary edge image (0 or 255)\r\n */\r\nexport async function cannyEdgeDetectorWasm(imageData, options = {}) {\r\n // Directly call the WASM canny_edge_detector_full function\r\n let result;\r\n try {\r\n await wasmReady; // Ensure wasm is initialized\r\n console.log('Using WASM Full Canny');\r\n result = wasmFullCanny(imageData.data, imageData.width, imageData.height, options.lowThreshold, options.highThreshold, options.sigma, options.kernelSize, options.L2gradient, options.applyDilation, options.dilationKernelSize);\r\n } catch (e) {\r\n console.error(\"WASM full Canny failed:\", e);\r\n throw e; // Rethrow to let the caller handle the error\r\n }\r\n \r\n // Convert result to Uint8ClampedArray (if not already)\r\n const edges = new Uint8ClampedArray(result);\r\n \r\n return edges;\r\n}","/**\r\n * Live document scanner for webcam integration\r\n * Provides efficient real-time document detection with frame rate optimization\r\n */\r\n\r\nimport { scanDocument } from './index.js';\r\n\r\nexport class LiveScanner {\r\n constructor(options = {}) {\r\n this.options = {\r\n targetFPS: options.targetFPS || 10, // Limit FPS for performance\r\n detectionInterval: options.detectionInterval || 150, // ms between detections\r\n confidenceThreshold: options.confidenceThreshold || 0.7,\r\n stabilizationFrames: options.stabilizationFrames || 3,\r\n maxProcessingDimension: options.maxProcessingDimension || 500, // Lower for live processing\r\n ...options\r\n };\r\n \r\n this.isRunning = false;\r\n this.stream = null;\r\n this.video = null;\r\n this.canvas = null;\r\n this.ctx = null;\r\n this.outputCanvas = null;\r\n this.outputCtx = null;\r\n \r\n // Performance tracking\r\n this.lastDetectionTime = 0;\r\n this.frameCount = 0;\r\n this.detectionCount = 0;\r\n this.lastFPSUpdate = 0;\r\n this.currentFPS = 0;\r\n \r\n // Detection state\r\n this.lastResult = null;\r\n this.stableResults = [];\r\n this.currentCorners = null;\r\n \r\n // Callbacks\r\n this.onDetection = null;\r\n this.onFPSUpdate = null;\r\n this.onError = null;\r\n }\r\n \r\n /**\r\n * Initialize webcam access and start live scanning\r\n * @param {HTMLElement} outputElement - Canvas element to render results to\r\n * @param {Object} constraints - MediaStream constraints\r\n */\r\n async init(outputElement, constraints = {}) {\r\n try {\r\n this.outputCanvas = outputElement;\r\n this.outputCtx = this.outputCanvas.getContext('2d');\r\n \r\n // Create hidden video element for webcam stream\r\n this.video = document.createElement('video');\r\n this.video.style.display = 'none';\r\n this.video.autoplay = true;\r\n this.video.muted = true;\r\n this.video.playsInline = true;\r\n document.body.appendChild(this.video);\r\n \r\n // Create hidden canvas for processing\r\n this.canvas = document.createElement('canvas');\r\n this.ctx = this.canvas.getContext('2d');\r\n \r\n // Get webcam stream\r\n const defaultConstraints = {\r\n video: {\r\n width: { ideal: 1280, max: 1920 },\r\n height: { ideal: 720, max: 1080 },\r\n facingMode: 'environment' // Use back camera on mobile\r\n },\r\n audio: false\r\n };\r\n \r\n const finalConstraints = { ...defaultConstraints, ...constraints };\r\n this.stream = await navigator.mediaDevices.getUserMedia(finalConstraints);\r\n this.video.srcObject = this.stream;\r\n \r\n // Wait for video to be ready\r\n await new Promise((resolve) => {\r\n this.video.addEventListener('loadedmetadata', resolve, { once: true });\r\n });\r\n \r\n // Set canvas sizes\r\n this.canvas.width = this.video.videoWidth;\r\n this.canvas.height = this.video.videoHeight;\r\n this.outputCanvas.width = this.video.videoWidth;\r\n this.outputCanvas.height = this.video.videoHeight;\r\n \r\n console.log(`Live scanner initialized: ${this.video.videoWidth}x${this.video.videoHeight}`);\r\n \r\n } catch (error) {\r\n console.error('Failed to initialize live scanner:', error);\r\n if (this.onError) this.onError(error);\r\n throw error;\r\n }\r\n }\r\n \r\n /**\r\n * Start the live scanning loop\r\n */\r\n start() {\r\n if (this.isRunning || !this.video) {\r\n console.warn('Scanner already running or not initialized');\r\n return;\r\n }\r\n \r\n this.isRunning = true;\r\n this.lastDetectionTime = Date.now();\r\n this.lastFPSUpdate = Date.now();\r\n this.frameCount = 0;\r\n this.detectionCount = 0;\r\n \r\n console.log('Live scanner started');\r\n this.processFrame();\r\n }\r\n \r\n /**\r\n * Stop the live scanning\r\n */\r\n stop() {\r\n this.isRunning = false;\r\n \r\n if (this.stream) {\r\n this.stream.getTracks().forEach(track => track.stop());\r\n this.stream = null;\r\n }\r\n \r\n if (this.video) {\r\n this.video.remove();\r\n this.video = null;\r\n }\r\n \r\n console.log('Live scanner stopped');\r\n }\r\n \r\n /**\r\n * Main processing loop - optimized for performance\r\n */\r\n async processFrame() {\r\n if (!this.isRunning) return;\r\n \r\n const now = Date.now();\r\n this.frameCount++;\r\n \r\n try {\r\n // Draw current video frame to output canvas\r\n this.outputCtx.drawImage(this.video, 0, 0, this.outputCanvas.width, this.outputCanvas.height);\r\n \r\n // Only run detection at specified intervals\r\n const timeSinceLastDetection = now - this.lastDetectionTime;\r\n if (timeSinceLastDetection >= this.options.detectionInterval) {\r\n this.lastDetectionTime = now;\r\n this.detectionCount++;\r\n \r\n // Capture frame for processing\r\n this.ctx.drawImage(this.video, 0, 0, this.canvas.width, this.canvas.height);\r\n const imageData = this.ctx.getImageData(0, 0, this.canvas.width, this.canvas.height);\r\n \r\n // Run detection asynchronously to avoid blocking\r\n this.detectDocumentAsync(imageData).catch(error => {\r\n console.error('Detection error:', error);\r\n if (this.onError) this.onError(error);\r\n });\r\n }\r\n \r\n // Draw current overlay if we have stable corners\r\n if (this.currentCorners) {\r\n this.drawDocumentOverlay(this.currentCorners);\r\n }\r\n \r\n // Update FPS counter\r\n if (now - this.lastFPSUpdate >= 1000) {\r\n this.currentFPS = Math.round(this.frameCount * 1000 / (now - this.lastFPSUpdate));\r\n this.frameCount = 0;\r\n this.lastFPSUpdate = now;\r\n \r\n if (this.onFPSUpdate) {\r\n this.onFPSUpdate({\r\n renderFPS: this.currentFPS,\r\n detectionFPS: Math.round(this.detectionCount * 1000 / 1000),\r\n lastDetectionTime: timeSinceLastDetection\r\n });\r\n }\r\n this.detectionCount = 0;\r\n }\r\n \r\n } catch (error) {\r\n console.error('Frame processing error:', error);\r\n if (this.onError) this.onError(error);\r\n }\r\n \r\n // Schedule next frame\r\n requestAnimationFrame(() => this.processFrame());\r\n }\r\n \r\n /**\r\n * Run document detection asynchronously\r\n */\r\n async detectDocumentAsync(imageData) {\r\n try {\r\n const result = await scanDocument(imageData, {\r\n ...this.options,\r\n mode: 'detect', // Only detect, no image processing\r\n debug: false // Disable debug for performance\r\n });\r\n \r\n if (result.success && result.corners) {\r\n this.updateStableCorners(result.corners);\r\n \r\n if (this.onDetection) {\r\n this.onDetection({\r\n corners: result.corners,\r\n confidence: this.calculateConfidence(result),\r\n isStable: this.stableResults.length >= this.options.stabilizationFrames\r\n });\r\n }\r\n } else {\r\n // Gradually fade out corners if no detection\r\n if (this.stableResults.length > 0) {\r\n this.stableResults.pop();\r\n if (this.stableResults.length === 0) {\r\n this.currentCorners = null;\r\n }\r\n }\r\n }\r\n \r\n } catch (error) {\r\n console.error('Document detection failed:', error);\r\n throw error;\r\n }\r\n }\r\n \r\n /**\r\n * Update stable corner detection with smoothing\r\n */\r\n updateStableCorners(newCorners) {\r\n this.stableResults.push(newCorners);\r\n \r\n // Keep only recent results\r\n if (this.stableResults.length > this.options.stabilizationFrames) {\r\n this.stableResults.shift();\r\n }\r\n \r\n // Calculate average corners for stability\r\n if (this.stableResults.length >= this.options.stabilizationFrames) {\r\n this.currentCorners = this.averageCorners(this.stableResults);\r\n }\r\n }\r\n \r\n /**\r\n * Calculate average corners from multiple detections for smoothing\r\n */\r\n averageCorners(cornersList) {\r\n const avg = {\r\n topLeft: { x: 0, y: 0 },\r\n topRight: { x: 0, y: 0 },\r\n bottomRight: { x: 0, y: 0 },\r\n bottomLeft: { x: 0, y: 0 }\r\n };\r\n \r\n cornersList.forEach(corners => {\r\n Object.keys(avg).forEach(key => {\r\n avg[key].x += corners[key].x;\r\n avg[key].y += corners[key].y;\r\n });\r\n });\r\n \r\n const count = cornersList.length;\r\n Object.keys(avg).forEach(key => {\r\n avg[key].x = Math.round(avg[key].x / count);\r\n avg[key].y = Math.round(avg[key].y / count);\r\n });\r\n \r\n return avg;\r\n }\r\n \r\n /**\r\n * Draw document overlay on output canvas\r\n */\r\n drawDocumentOverlay(corners, ctx = null, scaleX = 1, scaleY = 1) {\r\n const targetCtx = ctx || this.outputCtx;\r\n \r\n // Save context\r\n targetCtx.save();\r\n \r\n // Scale corners if needed\r\n const scaledCorners = {\r\n topLeft: { x: corners.topLeft.x * scaleX, y: corners.topLeft.y * scaleY },\r\n topRight: { x: corners.topRight.x * scaleX, y: corners.topRight.y * scaleY },\r\n bottomRight: { x: corners.bottomRight.x * scaleX, y: corners.bottomRight.y * scaleY },\r\n bottomLeft: { x: corners.bottomLeft.x * scaleX, y: corners.bottomLeft.y * scaleY }\r\n };\r\n \r\n // Draw document border\r\n targetCtx.strokeStyle = '#00FF00';\r\n targetCtx.lineWidth = 3;\r\n targetCtx.setLineDash([5, 5]);\r\n \r\n targetCtx.beginPath();\r\n targetCtx.moveTo(scaledCorners.topLeft.x, scaledCorners.topLeft.y);\r\n targetCtx.lineTo(scaledCorners.topRight.x, scaledCorners.topRight.y);\r\n targetCtx.lineTo(scaledCorners.bottomRight.x, scaledCorners.bottomRight.y);\r\n targetCtx.lineTo(scaledCorners.bottomLeft.x, scaledCorners.bottomLeft.y);\r\n targetCtx.closePath();\r\n targetCtx.stroke();\r\n \r\n // Draw corners\r\n targetCtx.fillStyle = '#00FF00';\r\n targetCtx.setLineDash([]);\r\n const cornerSize = 8 * Math.max(scaleX, scaleY); // Scale corner size too\r\n \r\n Object.values(scaledCorners).forEach(corner => {\r\n targetCtx.beginPath();\r\n targetCtx.arc(corner.x, corner.y, cornerSize, 0, 2 * Math.PI);\r\n targetCtx.fill();\r\n });\r\n \r\n // Restore context\r\n targetCtx.restore();\r\n }\r\n \r\n /**\r\n * Calculate detection confidence (placeholder - can be enhanced)\r\n */\r\n calculateConfidence(result) {\r\n // Simple confidence based on contour area and corner detection\r\n // This could be enhanced with more sophisticated metrics\r\n return 0.8; // Placeholder\r\n }\r\n \r\n /**\r\n * Capture current frame as document with perspective transform\r\n */\r\n async captureDocument() {\r\n if (!this.currentCorners || !this.video) {\r\n throw new Error('No stable document detected');\r\n }\r\n \r\n // Import extractDocument function dynamically to avoid circular dependency\r\n const { extractDocument } = await import('./index.js');\r\n \r\n // Create a high-quality capture\r\n const captureCanvas = document.createElement('canvas');\r\n captureCanvas.width = this.video.videoWidth;\r\n captureCanvas.height = this.video.videoHeight;\r\n const captureCtx = captureCanvas.getContext('2d');\r\n captureCtx.drawImage(this.video, 0, 0);\r\n \r\n // Scale corners to match the capture resolution\r\n const scaleX = this.video.videoWidth / this.outputCanvas.width;\r\n const scaleY = this.video.videoHeight / this.outputCanvas.height;\r\n \r\n const scaledCorners = {\r\n topLeft: { \r\n x: this.currentCorners.topLeft.x * scaleX, \r\n y: this.currentCorners.topLeft.y * scaleY \r\n },\r\n topRight: { \r\n x: this.currentCorners.topRight.x * scaleX, \r\n y: this.currentCorners.topRight.y * scaleY \r\n },\r\n bottomRight: { \r\n x: this.currentCorners.bottomRight.x * scaleX, \r\n y: this.currentCorners.bottomRight.y * scaleY \r\n },\r\n bottomLeft: { \r\n x: this.currentCorners.bottomLeft.x * scaleX, \r\n y: this.currentCorners.bottomLeft.y * scaleY \r\n }\r\n };\r\n \r\n // Apply perspective transform and return the corrected document\r\n return extractDocument(captureCanvas, scaledCorners);\r\n }\r\n \r\n /**\r\n * Get current scanner statistics\r\n */\r\n getStats() {\r\n return {\r\n isRunning: this.isRunning,\r\n currentFPS: this.currentFPS,\r\n videoResolution: this.video ? `${this.video.videoWidth}x${this.video.videoHeight}` : null,\r\n hasStableDetection: this.currentCorners !== null,\r\n stabilizationProgress: `${this.stableResults.length}/${this.options.stabilizationFrames}`\r\n };\r\n }\r\n}\r\n\r\n// Helper function to check webcam availability\r\nexport async function checkWebcamAvailability() {\r\n try {\r\n const devices = await navigator.mediaDevices.enumerateDevices();\r\n const videoDevices = devices.filter(device => device.kind === 'videoinput');\r\n return {\r\n available: videoDevices.length > 0,\r\n deviceCount: videoDevices.length,\r\n devices: videoDevices\r\n };\r\n } catch (error) {\r\n console.error('Error checking webcam availability:', error);\r\n return { available: false, error: error.message };\r\n }\r\n}\r\n","/**\r\n * scanic\r\n * JavaScript document scanner without OpenCV dependency\r\n * MIT License\r\n */\r\n\r\n\r\nimport { detectDocumentContour } from './contourDetection.js';\r\nimport { findCornerPoints } from './cornerDetection.js';\r\nimport { cannyEdgeDetector } from './edgeDetection.js';\r\n\r\n// Only import LiveScanner and checkWebcamAvailability for export, do not use them internally here\r\nimport { LiveScanner, checkWebcamAvailability } from './liveScanner.js';\r\n\r\n\r\n// Helper function to calculate smart adaptive downscale factor\r\nfunction calculateAdaptiveDownscale(imageData, maxDimension = 800) {\r\n const { width, height } = imageData;\r\n const maxCurrentDimension = Math.max(width, height);\r\n \r\n // If image is already smaller than target, no scaling needed\r\n if (maxCurrentDimension <= maxDimension) {\r\n return {\r\n scaledImageData: imageData,\r\n scaleFactor: 1,\r\n originalDimensions: { width, height },\r\n scaledDimensions: { width, height }\r\n };\r\n }\r\n \r\n // Calculate scale factor to fit within maxDimension\r\n const scaleFactor = maxDimension / maxCurrentDimension;\r\n const scaledWidth = Math.round(width * scaleFactor);\r\n const scaledHeight = Math.round(height * scaleFactor);\r\n \r\n // Create scaled image data\r\n const tempCanvas = document.createElement('canvas');\r\n tempCanvas.width = width;\r\n tempCanvas.height = height;\r\n const tempCtx = tempCanvas.getContext('2d');\r\n tempCtx.putImageData(imageData, 0, 0);\r\n\r\n const scaledCanvas = document.createElement('canvas');\r\n scaledCanvas.width = scaledWidth;\r\n scaledCanvas.height = scaledHeight;\r\n const scaledCtx = scaledCanvas.getContext('2d');\r\n \r\n // Use high-quality scaling\r\n scaledCtx.imageSmoothingEnabled = true;\r\n scaledCtx.imageSmoothingQuality = 'high';\r\n scaledCtx.drawImage(tempCanvas, 0, 0, width, height, 0, 0, scaledWidth, scaledHeight);\r\n \r\n const scaledImageData = scaledCtx.getImageData(0, 0, scaledWidth, scaledHeight);\r\n \r\n return {\r\n scaledImageData,\r\n scaleFactor: 1 / scaleFactor, // Return inverse for compatibility with existing code\r\n originalDimensions: { width, height },\r\n scaledDimensions: { width: scaledWidth, height: scaledHeight }\r\n };\r\n}\r\n\r\n// Internal function to detect document in image\r\nasync function detectDocumentInternal(imageData, options = {}) {\r\n const debugInfo = options.debug ? {} : null;\r\n \r\n // Smart adaptive downscaling - ensure largest dimension doesn't exceed maxProcessingDimension\r\n const maxProcessingDimension = options.maxProcessingDimension || 800;\r\n const { scaledImageData, scaleFactor, originalDimensions, scaledDimensions } = \r\n calculateAdaptiveDownscale(imageData, maxProcessingDimension);\r\n \r\n if (debugInfo) {\r\n debugInfo.preprocessing = {\r\n originalDimensions,\r\n scaledDimensions,\r\n scaleFactor,\r\n maxProcessingDimension\r\n };\r\n }\r\n \r\n const { width, height } = scaledImageData; // Use scaled dimensions\r\n \r\n // Run edge detection on the adaptively scaled image\r\n const edges = await cannyEdgeDetector(scaledImageData, {\r\n lowThreshold: options.lowThreshold || 75, // Match OpenCV values\r\n highThreshold: options.highThreshold || 200, // Match OpenCV values\r\n dilationKernelSize: options.dilationKernelSize || 3, // Match OpenCV value \r\n dilationIterations: options.dilationIterations || 1,\r\n debug: debugInfo,\r\n skipNMS: false, // options.skipNMS // Optional flag to skip non-max suppression\r\n useWasmBlur: true, // option to use wasm blur\r\n });\r\n \r\n // Detect contours from edges\r\n const contours = detectDocumentContour(edges, {\r\n minArea: (options.minArea || 1000) / (scaleFactor * scaleFactor), // Adjust minArea for scaled image\r\n debug: debugInfo,\r\n width: width, \r\n height: height \r\n });\r\n\r\n if (!contours || contours.length === 0) {\r\n console.log('No document detected');\r\n return {\r\n success: false,\r\n message: 'No document detected',\r\n debug: debugInfo\r\n };\r\n }\r\n \r\n // Get the largest contour which is likely the document\r\n const documentContour = contours[0]; \r\n \r\n // Find corner points on the scaled image\r\n const cornerPoints = findCornerPoints(documentContour, { \r\n epsilon: options.epsilon // Pass epsilon for approximation\r\n });\r\n \r\n // Scale corner points back to original image size\r\n let finalCorners = cornerPoints;\r\n if (scaleFactor !== 1) {\r\n finalCorners = {\r\n topLeft: { x: cornerPoints.topLeft.x * scaleFactor, y: cornerPoints.topLeft.y * scaleFactor },\r\n topRight: { x: cornerPoints.topRight.x * scaleFactor, y: cornerPoints.topRight.y * scaleFactor },\r\n bottomRight: { x: cornerPoints.bottomRight.x * scaleFactor, y: cornerPoints.bottomRight.y * scaleFactor },\r\n bottomLeft: { x: cornerPoints.bottomLeft.x * scaleFactor, y: cornerPoints.bottomLeft.y * scaleFactor },\r\n };\r\n }\r\n \r\n // Return the result, scaling the contour points back up as well\r\n return {\r\n success: true,\r\n contour: documentContour,\r\n corners: finalCorners,\r\n debug: debugInfo\r\n };\r\n}\r\n\r\n// --- Perspective transform helpers (internal use only) ---\r\nfunction getPerspectiveTransform(srcPoints, dstPoints) {\r\n // Helper to build the system of equations\r\n function buildMatrix(points) {\r\n const matrix = [];\r\n for (let i = 0; i < 4; i++) {\r\n const [x, y] = points[i];\r\n matrix.push([x, y, 1, 0, 0, 0, -x * dstPoints[i][0], -y * dstPoints[i][0]]);\r\n matrix.push([0, 0, 0, x, y, 1, -x * dstPoints[i][1], -y * dstPoints[i][1]]);\r\n }\r\n return matrix;\r\n }\r\n\r\n const A = buildMatrix(srcPoints);\r\n const b = [\r\n dstPoints[0][0], dstPoints[0][1],\r\n dstPoints[1][0], dstPoints[1][1],\r\n dstPoints[2][0], dstPoints[2][1],\r\n dstPoints[3][0], dstPoints[3][1]\r\n ];\r\n\r\n // Solve Ah = b for h (h is 8x1, last element is 1)\r\n // Use Gaussian elimination or Cramer's rule for 8x8\r\n // For simplicity, use numeric.js if available, else implement basic solver\r\n function solve(A, b) {\r\n // Gaussian elimination for 8x8\r\n const m = A.length;\r\n const n = A[0].length;\r\n const M = A.map(row => row.slice());\r\n const B = b.slice();\r\n\r\n for (let i = 0; i < n; i++) {\r\n // Find max row\r\n let maxRow = i;\r\n for (let k = i + 1; k < m; k++) {\r\n if (Math.abs(M[k][i]) > Math.abs(M[maxRow][i])) maxRow = k;\r\n }\r\n // Swap rows\r\n [M[i], M[maxRow]] = [M[maxRow], M[i]];\r\n [B[i], B[maxRow]] = [B[maxRow], B[i]];\r\n\r\n // Eliminate\r\n for (let k = i + 1; k < m; k++) {\r\n const c = M[k][i] / M[i][i];\r\n for (let j = i; j < n; j++) {\r\n M[k][j] -= c * M[i][j];\r\n }\r\n B[k] -= c * B[i];\r\n }\r\n }\r\n\r\n // Back substitution\r\n const x = new Array(n);\r\n for (let i = n - 1; i >= 0; i--) {\r\n let sum = B[i];\r\n for (let j = i + 1; j < n; j++) {\r\n sum -= M[i][j] * x[j];\r\n }\r\n x[i] = sum / M[i][i];\r\n }\r\n return x;\r\n }\r\n\r\n const h = solve(A, b);\r\n // h is [h0,h1,h2,h3,h4,h5,h6,h7], h8 = 1\r\n const matrix = [\r\n [h[0], h[1], h[2]],\r\n [h[3], h[4], h[5]],\r\n [h[6], h[7], 1]\r\n ];\r\n return matrix;\r\n}\r\n\r\n\r\n\r\n\r\nfunction unwarpImage(ctx, image, corners) {\r\n // Get perspective transform matrix\r\n const { topLeft, topRight, bottomRight, bottomLeft } = corners;\r\n // Compute output rectangle size\r\n const widthA = Math.hypot(bottomRight.x - bottomLeft.x, bottomRight.y - bottomLeft.y);\r\n const widthB = Math.hypot(topRight.x - topLeft.x, topRight.y - topLeft.y);\r\n const maxWidth = Math.round(Math.max(widthA, widthB));\r\n const heightA = Math.hypot(topRight.x - bottomRight.x, topRight.y - bottomRight.y);\r\n const heightB = Math.hypot(topLeft.x - bottomLeft.x, topLeft.y - bottomLeft.y);\r\n const maxHeight = Math.round(Math.max(heightA, heightB));\r\n\r\n // Set output canvas size\r\n ctx.canvas.width = maxWidth;\r\n ctx.canvas.height = maxHeight;\r\n\r\n const srcPoints = [\r\n [topLeft.x, topLeft.y],\r\n [topRight.x, topRight.y],\r\n [bottomRight.x, bottomRight.y],\r\n [bottomLeft.x, bottomLeft.y]\r\n ];\r\n const dstPoints = [\r\n [0, 0],\r\n [maxWidth - 1, 0],\r\n [maxWidth - 1, maxHeight - 1],\r\n [0, maxHeight - 1]\r\n ];\r\n const perspectiveMatrix = getPerspectiveTransform(srcPoints, dstPoints);\r\n warpTransform(ctx, image, perspectiveMatrix, maxWidth, maxHeight);\r\n}\r\n\r\nfunction invert3x3(m) {\r\n // Invert a 3x3 matrix\r\n const a = m[0][0], b = m[0][1], c = m[0][2];\r\n const d = m[1][0], e = m[1][1], f = m[1][2];\r\n const g = m[2][0], h = m[2][1], i = m[2][2];\r\n const A = e * i - f * h;\r\n const B = -(d * i - f * g);\r\n const C = d * h - e * g;\r\n const D = -(b * i - c * h);\r\n const E = a * i - c * g;\r\n const F = -(a * h - b * g);\r\n const G = b * f - c * e;\r\n const H = -(a * f - c * d);\r\n const I = a * e - b * d;\r\n const det = a * A + b * B + c * C;\r\n if (det === 0) throw new Error('Singular matrix');\r\n return [\r\n [A / det, D / det, G / det],\r\n [B / det, E / det, H / det],\r\n [C / det, F / det, I / det]\r\n ];\r\n}\r\n\r\nfunction warpTransform(ctx, image, matrix, outWidth, outHeight) {\r\n // Inverse matrix for mapping output to input\r\n const inv = invert3x3(matrix);\r\n // Get source image data\r\n const srcCanvas = document.createElement('canvas');\r\n srcCanvas.width = image.width || image.naturalWidth;\r\n srcCanvas.height = image.height || image.naturalHeight;\r\n const srcCtx = srcCanvas.getContext('2d');\r\n srcCtx.drawImage(image, 0, 0, srcCanvas.width, srcCanvas.height);\r\n const srcData = srcCtx.getImageData(0, 0, srcCanvas.width, srcCanvas.height);\r\n const out = ctx.createImageData(outWidth, outHeight);\r\n for (let y = 0; y < outHeight; y++) {\r\n for (let x = 0; x < outWidth; x++) {\r\n // Map (x, y) in output to (srcX, srcY) in input\r\n const denom = inv[2][0] * x + inv[2][1] * y + inv[2][2];\r\n const srcX = (inv[0][0] * x + inv[0][1] * y + inv[0][2]) / denom;\r\n const srcY = (inv[1][0] * x + inv[1][1] * y + inv[1][2]) / denom;\r\n // Bilinear sample\r\n const sx = Math.max(0, Math.min(srcCanvas.width - 2, srcX));\r\n const sy = Math.max(0, Math.min(srcCanvas.height - 2, srcY));\r\n const ix = Math.floor(sx), iy = Math.floor(sy);\r\n const dx = sx - ix, dy = sy - iy;\r\n for (let c = 0; c < 4; c++) {\r\n // Bilinear interpolation\r\n const i00 = srcData.data[(iy * srcCanvas.width + ix) * 4 + c];\r\n const i10 = srcData.data[(iy * srcCanvas.width + (ix + 1)) * 4 + c];\r\n const i01 = srcData.data[((iy + 1) * srcCanvas.width + ix) * 4 + c];\r\n const i11 = srcData.data[((iy + 1) * srcCanvas.width + (ix + 1)) * 4 + c];\r\n out.data[(y * outWidth + x) * 4 + c] =\r\n (1 - dx) * (1 - dy) * i00 +\r\n dx * (1 - dy) * i10 +\r\n (1 - dx) * dy * i01 +\r\n dx * dy * i11;\r\n }\r\n }\r\n }\r\n ctx.putImageData(out, 0, 0);\r\n}\r\n\r\n\r\n/**\r\n * Main entry point for document scanning.\r\n * @param {HTMLImageElement|HTMLCanvasElement|ImageData} image\r\n * @param {Object} options\r\n * - mode: 'detect' | 'extract' (default: 'detect')\r\n * - output: 'canvas' | 'imagedata' | 'dataurl' (default: 'canvas')\r\n * - debug: boolean\r\n * - ...other detection options\r\n * @returns {Promise<{output, corners, contour, debug, success, message}>}\r\n */\r\nexport async function scanDocument(image, options = {}) {\r\n const mode = options.mode || 'detect';\r\n const outputType = options.output || 'canvas';\r\n const debug = !!options.debug;\r\n\r\n // Prepare input image data\r\n let imageData, width, height;\r\n if (image instanceof ImageData) {\r\n imageData = image;\r\n width = image.width;\r\n height = image.height;\r\n } else {\r\n // HTMLImageElement or HTMLCanvasElement\r\n const tempCanvas = document.createElement('canvas');\r\n tempCanvas.width = image.width || image.naturalWidth;\r\n tempCanvas.height = image.height || image.naturalHeight;\r\n const tempCtx = tempCanvas.getContext('2d');\r\n tempCtx.drawImage(image, 0, 0, tempCanvas.width, tempCanvas.height);\r\n imageData = tempCtx.getImageData(0, 0, tempCanvas.width, tempCanvas.height);\r\n width = tempCanvas.width;\r\n height = tempCanvas.height;\r\n }\r\n\r\n // Detect document\r\n const detection = await detectDocumentInternal(imageData, options);\r\n if (!detection.success) {\r\n return {\r\n output: null,\r\n corners: null,\r\n contour: null,\r\n debug: detection.debug,\r\n success: false,\r\n message: detection.message || 'No document detected'\r\n };\r\n }\r\n\r\n let resultCanvas;\r\n let output;\r\n\r\n if (mode === 'detect') {\r\n // Just return detection info, no image processing\r\n output = null;\r\n } else if (mode === 'extract') {\r\n // Return only the cropped/warped document\r\n resultCanvas = document.createElement('canvas');\r\n const ctx = resultCanvas.getContext('2d');\r\n unwarpImage(ctx, image, detection.corners);\r\n }\r\n\r\n // Prepare output in requested format (only if not detect mode)\r\n if (mode !== 'detect' && resultCanvas) {\r\n if (outputType === 'canvas') {\r\n output = resultCanvas;\r\n } else if (outputType === 'imagedata') {\r\n output = resultCanvas.getContext('2d').getImageData(0, 0, resultCanvas.width, resultCanvas.height);\r\n } else if (outputType === 'dataurl') {\r\n output = resultCanvas.toDataURL();\r\n } else {\r\n output = resultCanvas;\r\n }\r\n }\r\n\r\n return {\r\n output,\r\n corners: detection.corners,\r\n contour: detection.contour,\r\n debug: detection.debug,\r\n success: true,\r\n message: 'Document detected'\r\n };\r\n}\r\n\r\n// Export only the main API and live scanner utilities\r\nexport { LiveScanner, checkWebcamAvailability };"],"names":["DEFAULTS","RETR_EXTERNAL","RETR_LIST","CHAIN_APPROX_SIMPLE","deltas","detectDocumentContour","edges","options","width","height","mode","method","minArea","paddedWidth","paddedHeight","labels","y","x","contours","nextContourId","currentPixelLabel","leftPixelLabel","startPoint","isOuter","initialDirection","contourId","points","traceContour","finalPoints","simplifyChainApproxSimple","adjustedPoints","p","contour","calculateContourArea","calculateBoundingBox","filteredContours","b","visitedPoints","currentPoint","prevDirection","count","maxSteps","searchDirection","found","i","nextX","nextY","nextPoint","checkDirection","checkX","checkY","pointKey","simplifiedPoints","n","prevPoint","dx1","dy1","dx2","dy2","maxDistSq","farthestIdx","p0","pi","distSq","area","j","minX","minY","maxX","maxY","point","simplifyContour","epsilon","maxDistance","index","firstPoint","lastPoint","distance","perpendicularDistance","firstSegment","secondSegment","lineStart","lineEnd","dx","dy","lineLengthSq","t","closestPointX","closestPointY","distDx","distDy","approximatePolygon","contourPoints","perimeter","calculateContourPerimeter","actualEpsilon","findCenter","sumX","sumY","findCornerPoints","approximation","corners","orderCornerPoints","findCornersByCoordinateExtremes","topLeft","topRight","bottomRight","bottomLeft","minSum","maxDiff","maxSum","minDiff","sum","diff","center","sortedPoints","a","angleA","angleB","minIndex","orderedPoints","wasm","cachedUint8ArrayMemory0","getUint8ArrayMemory0","WASM_VECTOR_LEN","passArray8ToWasm0","arg","malloc","ptr","getArrayU8FromWasm0","len","dilate","kernel_size","ptr0","len0","ret","v2","cachedFloat32ArrayMemory0","getFloat32ArrayMemory0","passArrayF32ToWasm0","hysteresis_thresholding","suppressed","low_threshold","high_threshold","blur","grayscale","sigma","cachedUint16ArrayMemory0","getUint16ArrayMemory0","passArray16ToWasm0","getArrayF32FromWasm0","non_maximum_suppression","l2_gradient","ptr1","len1","v3","__wbg_load","module","imports","e","bytes","instance","__wbg_get_imports","table","offset","__wbg_finalize_init","__wbg_init","module_or_path","wasmReady","init","convertToGrayscale","imageData","data","gaussianBlurGrayscale","kernelSize","halfKernel","kernel","createGaussianKernel","tempArray","blurred","rowOffset","k","xOffset","yOffset","size","halfSize","calculateGradients","prevRowOffset","nextRowOffset","currentIdx","p1","p2","p3","p5","p6","p7","p8","gx","gy","nonMaximumSuppression","L2gradient","magnitude","idx","mag","neighbor1","neighbor2","absGx","absGy","s","hysteresisThresholding","lowThreshold","highThreshold","edgeMap","stack","dxNeighbors","dyNeighbors","nx","ny","nidx","dilateEdges","temp","dilated","maxVal","val","cannyEdgeDetector","timings","tStart","applyDilation","dilationKernelSize","useWasmHysteresis","t0","t1","wasmBlur","gradients","wasmMaximumSuppression","finalLowThreshold","finalHighThreshold","wasmHysteresis","cannyEdges","finalEdges","wasmDilate","tEnd","LiveScanner","outputElement","constraints","finalConstraints","resolve","error","track","now","timeSinceLastDetection","result","scanDocument","newCorners","cornersList","avg","key","ctx","scaleX","scaleY","targetCtx","scaledCorners","cornerSize","corner","extractDocument","captureCanvas","checkWebcamAvailability","videoDevices","device","calculateAdaptiveDownscale","maxDimension","maxCurrentDimension","scaleFactor","scaledWidth","scaledHeight","tempCanvas","scaledCanvas","scaledCtx","detectDocumentInternal","debugInfo","maxProcessingDimension","scaledImageData","originalDimensions","scaledDimensions","documentContour","cornerPoints","finalCorners","getPerspectiveTransform","srcPoints","dstPoints","buildMatrix","matrix","A","solve","m","M","row","B","maxRow","c","h","unwarpImage","image","widthA","widthB","maxWidth","heightA","heightB","maxHeight","perspectiveMatrix","warpTransform","invert3x3","d","f","g","C","D","E","F","G","H","I","det","outWidth","outHeight","inv","srcCanvas","srcCtx","srcData","out","denom","srcX","srcY","sx","sy","ix","iy","i00","i10","i01","i11","outputType","tempCtx","detection","resultCanvas","output"],"mappings":"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"}
1
+ {"version":3,"file":"scanic.js","sources":["../src/constants.js","../src/contourDetection.js","../src/cornerDetection.js","../wasm_blur/pkg/wasm_blur.js","../src/edgeDetection.js","../src/index.js"],"sourcesContent":["/**\r\n * Constants used throughout the library\r\n */\r\n\r\nexport const VERSION = '1.0.0';\r\n\r\nexport const DEFAULTS = {\r\n // Edge detection params\r\n GAUSSIAN_SIGMA: 1.1,\r\n LOW_THRESHOLD: 50,\r\n HIGH_THRESHOLD: 150,\r\n \r\n // Contour detection params\r\n MIN_CONTOUR_AREA: 1000,\r\n MIN_CONTOUR_POINTS: 10,\r\n \r\n // Debug options\r\n DEBUG_OVERLAY_OPACITY: 0.7\r\n};","/**\r\n * Pure JavaScript implementation of contour detection algorithms\r\n * Based on Suzuki, S. and Abe, K. (1985). Topological structural analysis of digitized binary images by border following.\r\n * Replaces the previous flood-fill based connected components analysis.\r\n */\r\n\r\nimport { DEFAULTS } from './constants.js';\r\n\r\n// Constants for different retrieval modes (subset of OpenCV)\r\nconst RETR_EXTERNAL = 0;\r\nconst RETR_LIST = 1;\r\n// Constants for different approximation methods (subset of OpenCV)\r\nconst CHAIN_APPROX_NONE = 1;\r\nconst CHAIN_APPROX_SIMPLE = 2;\r\n\r\n// Deltas for 8-connectivity neighborhood checks (0-7 clockwise from top)\r\n// Corresponds to OpenCV's chain code directions\r\nconst deltas = [\r\n { dx: 0, dy: -1 }, // 0: Top\r\n { dx: 1, dy: -1 }, // 1: Top-right\r\n { dx: 1, dy: 0 }, // 2: Right\r\n { dx: 1, dy: 1 }, // 3: Bottom-right\r\n { dx: 0, dy: 1 }, // 4: Bottom\r\n { dx: -1, dy: 1 }, // 5: Bottom-left\r\n { dx: -1, dy: 0 }, // 6: Left\r\n { dx: -1, dy: -1 } // 7: Top-left\r\n];\r\n\r\n/**\r\n * Detects contours in a binary edge image using Suzuki's border following algorithm.\r\n * @param {Uint8ClampedArray} edges - Binary edge image (pixels > 0 are foreground)\r\n * @param {Object} options - Configuration options\r\n * @param {number} [options.width] - Image width (required if not square)\r\n * @param {number} [options.height] - Image height (required if not square)\r\n * @param {number} [options.mode=RETR_LIST] - Contour retrieval mode (RETR_EXTERNAL or RETR_LIST)\r\n * @param {number} [options.method=CHAIN_APPROX_SIMPLE] - Contour approximation method (CHAIN_APPROX_NONE or CHAIN_APPROX_SIMPLE)\r\n * @param {number} [options.minArea=DEFAULTS.MIN_CONTOUR_AREA] - Minimum contour area filter (applied after detection)\r\n * @param {Object} [options.debug] - Optional debug object to store intermediate results\r\n * @returns {Array} Array of contours, each contour is an array of points {x, y}. Sorted by area (largest first).\r\n */\r\nexport function detectDocumentContour(edges, options = {}) {\r\n const width = options.width || Math.sqrt(edges.length);\r\n const height = options.height || edges.length / width;\r\n const mode = options.mode !== undefined ? options.mode : RETR_LIST;\r\n const method = options.method !== undefined ? options.method : CHAIN_APPROX_SIMPLE;\r\n const minArea = options.minArea || DEFAULTS.MIN_CONTOUR_AREA;\r\n\r\n // Create a padded label map to simplify boundary checks.\r\n // 0: background\r\n // 1: foreground (unlabeled)\r\n // >= 2: contour ID (2, 3, ...)\r\n const paddedWidth = width + 2;\r\n const paddedHeight = height + 2;\r\n const labels = new Int32Array(paddedWidth * paddedHeight); // Initialized to 0\r\n\r\n // Copy edges data to the label map, mapping foreground pixels to 1\r\n for (let y = 0; y < height; y++) {\r\n for (let x = 0; x < width; x++) {\r\n if (edges[y * width + x] > 0) {\r\n labels[(y + 1) * paddedWidth + (x + 1)] = 1;\r\n }\r\n }\r\n }\r\n\r\n const contours = [];\r\n let nextContourId = 2; // Start labeling contours from 2\r\n\r\n // Raster scan\r\n for (let y = 1; y <= height; y++) {\r\n for (let x = 1; x <= width; x++) {\r\n const currentPixelLabel = labels[y * paddedWidth + x];\r\n const leftPixelLabel = labels[y * paddedWidth + (x - 1)];\r\n\r\n let startPoint = null;\r\n let isOuter = false;\r\n let initialDirection = -1;\r\n\r\n if (currentPixelLabel === 1 && leftPixelLabel === 0) {\r\n // Found the start of an outer contour boundary (NBD = 1 in Suzuki's terms)\r\n isOuter = true;\r\n startPoint = { x: x, y: y };\r\n initialDirection = 2; // Start searching right\r\n // if (options.debug) console.log(`Outer contour start at (${x-1}, ${y-1})`);\r\n } else if (currentPixelLabel === 0 && leftPixelLabel >= 1 && leftPixelLabel !== -1) {\r\n // Found the start of a hole contour boundary (NBD >= 2 in Suzuki's terms)\r\n // Check if the left pixel is already part of a traced contour border\r\n // If leftPixelLabel is > 1, it might be already traced. If it's 1, it's an unlabeled foreground pixel.\r\n // We only start tracing if the left pixel is unlabeled foreground (1).\r\n if (leftPixelLabel === 1) {\r\n isOuter = false;\r\n startPoint = { x: x - 1, y: y };\r\n initialDirection = 6; // Start searching left\r\n // if (options.debug) console.log(`Hole contour start at (${x-1-1}, ${y-1})`);\r\n }\r\n }\r\n\r\n\r\n if (startPoint) {\r\n // If mode is RETR_EXTERNAL, only process outer contours\r\n if (mode === RETR_EXTERNAL && !isOuter) {\r\n // Mark the starting pixel of the hole so we don't process it again\r\n // Use a special marker (-1) to distinguish from contour IDs\r\n labels[startPoint.y * paddedWidth + startPoint.x] = -1;\r\n continue;\r\n }\r\n\r\n const contourId = nextContourId++;\r\n const points = traceContour(labels, paddedWidth, paddedHeight, startPoint, initialDirection, contourId);\r\n\r\n if (points && points.length > 0) {\r\n let finalPoints = points;\r\n if (method === CHAIN_APPROX_SIMPLE) {\r\n finalPoints = simplifyChainApproxSimple(points);\r\n }\r\n\r\n // Adjust points to original image coordinates (remove padding offset)\r\n const adjustedPoints = finalPoints.map(p => ({ x: p.x - 1, y: p.y - 1 }));\r\n\r\n if (adjustedPoints.length >= (method === CHAIN_APPROX_SIMPLE ? 4 : DEFAULTS.MIN_CONTOUR_POINTS)) { // Need at least 4 points for a simple polygon approx\r\n const contour = {\r\n id: contourId,\r\n points: adjustedPoints,\r\n isOuter: isOuter,\r\n // Calculate area and bounding box later if needed for filtering/sorting\r\n };\r\n contours.push(contour);\r\n }\r\n } else {\r\n // Handle single point contours or errors if necessary\r\n // Mark the start point if trace failed or resulted in no points\r\n if (labels[startPoint.y * paddedWidth + startPoint.x] === 1) {\r\n labels[startPoint.y * paddedWidth + startPoint.x] = contourId; // Mark as visited\r\n }\r\n }\r\n } else if (currentPixelLabel >= 1 && leftPixelLabel >= 1 && currentPixelLabel !== leftPixelLabel) {\r\n // Handle merging contours or complex topology if needed (not implemented for RETR_LIST/EXTERNAL)\r\n }\r\n }\r\n }\r\n\r\n // Calculate area and bounding box for filtering and sorting\r\n contours.forEach(contour => {\r\n contour.area = calculateContourArea(contour.points);\r\n contour.boundingBox = calculateBoundingBox(contour.points);\r\n });\r\n\r\n // Filter by minimum area\r\n const filteredContours = contours.filter(contour => contour.area >= minArea);\r\n\r\n // Sort contours by area (largest first)\r\n filteredContours.sort((a, b) => b.area - a.area);\r\n\r\n // console.log(`Found ${contours.length} contours before filtering, ${filteredContours.length} after filtering.`);\r\n\r\n // Store debug info if requested\r\n if (options.debug) {\r\n options.debug.labels = labels; // Store the final label map\r\n options.debug.rawContours = contours; // Store contours before filtering/sorting\r\n options.debug.finalContours = filteredContours;\r\n // console.log('Contour detection debug info stored');\r\n }\r\n return filteredContours // Return only the points array per contour\r\n}\r\n\r\n/**\r\n * Traces a contour boundary using border following.\r\n * @param {Int32Array} labels - The label map (modified during tracing)\r\n * @param {number} width - Padded width of the label map\r\n * @param {number} height - Padded height of the label map\r\n * @param {Object} startPoint - Starting point {x, y} in padded coordinates\r\n * @param {number} initialDirection - Initial search direction (0-7)\r\n * @param {number} contourId - The ID to label this contour with\r\n * @returns {Array} Array of points {x, y} in padded coordinates, or null if error\r\n */\r\nfunction traceContour(labels, width, height, startPoint, initialDirection, contourId) {\r\n const points = [];\r\n const visitedPoints = new Set(); // Use a Set for efficient duplicate checking\r\n let currentPoint = { ...startPoint };\r\n let prevDirection = -1; // Store the direction from which we arrived at currentPoint\r\n\r\n // Mark the starting pixel with the contour ID\r\n labels[startPoint.y * width + startPoint.x] = contourId;\r\n\r\n let count = 0; // Safety break\r\n const maxSteps = width * height; // Max possible steps\r\n\r\n while (count++ < maxSteps) {\r\n // Determine the direction to start searching from (relative to the direction we came from)\r\n // In Suzuki's paper, this is based on the chain code of the previous step.\r\n // Simplified: Start searching from the direction after the one that led us here.\r\n // If we arrived from direction `d`, the next pixel must be in `(d+1)%8` to `(d+7)%8`.\r\n // Let's adapt OpenCV's logic: search starts from (prevDirection + 2) % 8 clockwise.\r\n // If it's the first step, prevDirection is unknown, use initialDirection logic.\r\n\r\n let searchDirection;\r\n if (prevDirection === -1) {\r\n // First step: Use initialDirection logic (e.g., start right for outer, left for inner)\r\n // The initial search should find the *first* pixel of the contour boundary clockwise.\r\n // Let's refine the initial search based on OpenCV's approach:\r\n // Find the first non-zero pixel starting from `initialDirection` clockwise.\r\n let found = false;\r\n for (let i = 0; i < 8; i++) {\r\n searchDirection = (initialDirection + i) % 8;\r\n const nextX = currentPoint.x + deltas[searchDirection].dx;\r\n const nextY = currentPoint.y + deltas[searchDirection].dy;\r\n if (nextX >= 0 && nextX < width && nextY >= 0 && nextY < height && labels[nextY * width + nextX] > 0) {\r\n found = true;\r\n break;\r\n }\r\n }\r\n if (!found) return null; // Should not happen if startPoint is valid\r\n\r\n } else {\r\n // Subsequent steps: Start search from (prevDirection + 2) % 8 clockwise\r\n searchDirection = (prevDirection + 2) % 8;\r\n }\r\n\r\n\r\n let nextPoint = null;\r\n let nextDirection = -1;\r\n\r\n // Search clockwise for the next boundary pixel\r\n for (let i = 0; i < 8; i++) {\r\n const checkDirection = (searchDirection + i) % 8;\r\n const checkX = currentPoint.x + deltas[checkDirection].dx;\r\n const checkY = currentPoint.y + deltas[checkDirection].dy;\r\n\r\n // Check bounds (should be within padded area)\r\n if (checkX >= 0 && checkX < width && checkY >= 0 && checkY < height) {\r\n const pixelLabel = labels[checkY * width + checkX];\r\n if (pixelLabel > 0) { // Found a foreground pixel (labeled or unlabeled)\r\n nextPoint = { x: checkX, y: checkY };\r\n // The direction *from* currentPoint *to* nextPoint is checkDirection\r\n nextDirection = checkDirection;\r\n // The direction *from* which we will arrive *at* nextPoint is (checkDirection + 4) % 8\r\n prevDirection = (checkDirection + 4) % 8;\r\n break;\r\n }\r\n }\r\n }\r\n\r\n if (!nextPoint) {\r\n // Should not happen in a well-formed contour, maybe isolated pixel?\r\n if (points.length === 0) { // If it's just the start point\r\n points.push({ ...currentPoint }); // Add the single point\r\n }\r\n console.warn(`Contour tracing stopped unexpectedly at (${currentPoint.x-1}, ${currentPoint.y-1}) for contour ${contourId}`);\r\n break;\r\n }\r\n\r\n // Add the *current* point to the list before moving\r\n const pointKey = `${currentPoint.x},${currentPoint.y}`;\r\n if (visitedPoints.has(pointKey)) {\r\n // console.warn(`Duplicate point detected at (${currentPoint.x}, ${currentPoint.y}) for contour ${contourId}`);\r\n // console.warn(points)\r\n // console.warn(filtered)\r\n return points; // Avoid infinite loops on duplicate points\r\n }\r\n points.push({ ...currentPoint });\r\n visitedPoints.add(pointKey);\r\n \r\n\r\n // Mark the next pixel if it's unlabeled\r\n if (labels[nextPoint.y * width + nextPoint.x] === 1) {\r\n labels[nextPoint.y * width + nextPoint.x] = contourId;\r\n }\r\n\r\n // Move to the next point\r\n currentPoint = nextPoint;\r\n\r\n // Check if we returned to the start point\r\n if (currentPoint.x === startPoint.x && currentPoint.y === startPoint.y) {\r\n // Check if we came from the same direction as the initial step search ended.\r\n // This is complex, let's use a simpler check: if we are back at start, we are done.\r\n // OpenCV has more sophisticated checks involving i4 == i0 && i3 == i1.\r\n break;\r\n }\r\n }\r\n\r\n if (count >= maxSteps) {\r\n console.warn(`Contour tracing exceeded max steps for contour ${contourId}`);\r\n return null; // Indicate potential error\r\n }\r\n\r\n return points;\r\n}\r\n\r\n/**\r\n * Simplifies a contour polygon using CHAIN_APPROX_SIMPLE.\r\n * Removes intermediate points that lie on the straight line segment between their neighbors.\r\n * @param {Array} points - Array of contour points {x, y}\r\n * @returns {Array} Simplified array of points\r\n */\r\nfunction simplifyChainApproxSimple(points) {\r\n if (points.length <= 2) {\r\n return points;\r\n }\r\n\r\n const simplifiedPoints = [];\r\n const n = points.length;\r\n\r\n for (let i = 0; i < n; i++) {\r\n const prevPoint = points[(i + n - 1) % n]; // Handle wrap around\r\n const currentPoint = points[i];\r\n const nextPoint = points[(i + 1) % n]; // Handle wrap around\r\n\r\n // Check for collinearity: (y2-y1)*(x3-x2) == (y3-y2)*(x2-x1)\r\n const dx1 = currentPoint.x - prevPoint.x;\r\n const dy1 = currentPoint.y - prevPoint.y;\r\n const dx2 = nextPoint.x - currentPoint.x;\r\n const dy2 = nextPoint.y - currentPoint.y;\r\n\r\n // If points are not collinear, keep the current point\r\n if (dx1 * dy2 !== dy1 * dx2) {\r\n simplifiedPoints.push(currentPoint);\r\n }\r\n }\r\n\r\n // Handle cases where all points are collinear (e.g., straight line)\r\n // In this case, the above loop might remove all points. Keep first and last?\r\n // OpenCV keeps the two endpoints of the line segment.\r\n if (simplifiedPoints.length === 0 && n > 0) {\r\n // If all points were collinear, return the start and end points of the original sequence\r\n // This requires knowing the original start/end, which isn't trivial with wrap-around.\r\n // Let's return the first and the point furthest from the first.\r\n if (n === 1) return [points[0]];\r\n if (n === 2) return points;\r\n\r\n // Find the point most distant from the first point to represent the line segment\r\n let maxDistSq = 0;\r\n let farthestIdx = 1;\r\n const p0 = points[0];\r\n for(let i = 1; i < n; i++) {\r\n const pi = points[i];\r\n const distSq = (pi.x - p0.x)**2 + (pi.y - p0.y)**2;\r\n if (distSq > maxDistSq) {\r\n maxDistSq = distSq;\r\n farthestIdx = i;\r\n }\r\n }\r\n // Ensure order if needed, but for simple approx, just two points is fine.\r\n return [points[0], points[farthestIdx]];\r\n }\r\n\r\n\r\n return simplifiedPoints;\r\n}\r\n\r\n\r\n// --- Helper functions (keep or adapt from original) ---\r\n\r\n/**\r\n * Calculates the area of a contour using the shoelace formula\r\n * @param {Array} points - Array of point coordinates {x, y}\r\n * @returns {number} Contour area\r\n */\r\nfunction calculateContourArea(points) {\r\n let area = 0;\r\n const n = points.length;\r\n\r\n if (n < 3) return 0;\r\n\r\n for (let i = 0; i < n; i++) {\r\n const j = (i + 1) % n;\r\n area += points[i].x * points[j].y;\r\n area -= points[j].x * points[i].y;\r\n }\r\n\r\n return Math.abs(area) / 2;\r\n}\r\n\r\n/**\r\n * Calculates the bounding box of a contour\r\n * @param {Array} points - Array of point coordinates\r\n * @returns {Object} Bounding box with minX, minY, maxX, maxY properties\r\n */\r\nfunction calculateBoundingBox(points) {\r\n if (points.length === 0) {\r\n return { minX: 0, minY: 0, maxX: 0, maxY: 0 };\r\n }\r\n let minX = points[0].x;\r\n let minY = points[0].y;\r\n let maxX = points[0].x;\r\n let maxY = points[0].y;\r\n\r\n for (let i = 1; i < points.length; i++) {\r\n const point = points[i];\r\n minX = Math.min(minX, point.x);\r\n minY = Math.min(minY, point.y);\r\n maxX = Math.max(maxX, point.x);\r\n maxY = Math.max(maxY, point.y);\r\n }\r\n\r\n return { minX, minY, maxX, maxY };\r\n}\r\n\r\n\r\n// --- Functions below are no longer directly used by detectDocumentContour ---\r\n// --- but might be useful elsewhere or can be removed ---\r\n\r\n/**\r\n * Simplifies a contour using the Ramer-Douglas-Peucker algorithm\r\n * (No longer used by default contour detection, kept for potential external use)\r\n * @param {Array} points - Array of point coordinates\r\n * @param {number} epsilon - Epsilon value for simplification\r\n * @returns {Array} Simplified contour points\r\n */\r\nexport function simplifyContour(points, epsilon = 1.0) {\r\n // ... (keep existing implementation if needed elsewhere) ...\r\n if (points.length <= 2) {\r\n return points;\r\n }\r\n\r\n // Find point with the maximum distance\r\n let maxDistance = 0;\r\n let index = 0;\r\n\r\n const firstPoint = points[0];\r\n const lastPoint = points[points.length - 1];\r\n\r\n for (let i = 1; i < points.length - 1; i++) {\r\n const distance = perpendicularDistance(points[i], firstPoint, lastPoint);\r\n\r\n if (distance > maxDistance) {\r\n maxDistance = distance;\r\n index = i;\r\n }\r\n }\r\n\r\n // If max distance is greater than epsilon, recursively simplify\r\n if (maxDistance > epsilon) {\r\n // Recursive simplification\r\n const firstSegment = simplifyContour(points.slice(0, index + 1), epsilon);\r\n const secondSegment = simplifyContour(points.slice(index), epsilon);\r\n\r\n // Concatenate the two segments\r\n return firstSegment.slice(0, -1).concat(secondSegment);\r\n } else {\r\n // Return just the endpoints\r\n return [firstPoint, lastPoint];\r\n }\r\n}\r\n\r\n/**\r\n * Calculates the perpendicular distance from a point to a line\r\n * (Helper for RDP simplifyContour, keep if that function is kept)\r\n * @param {Object} point - Point to measure from\r\n * @param {Object} lineStart - Start point of the line\r\n * @param {Object} lineEnd - End point of the line\r\n * @returns {number} Perpendicular distance\r\n */\r\nfunction perpendicularDistance(point, lineStart, lineEnd) {\r\n // ... (keep existing implementation if needed elsewhere) ...\r\n const dx = lineEnd.x - lineStart.x;\r\n const dy = lineEnd.y - lineStart.y;\r\n\r\n // Line length squared\r\n const lineLengthSq = dx * dx + dy * dy;\r\n\r\n if (lineLengthSq === 0) {\r\n // Point to point distance if the line has zero length\r\n return Math.sqrt(\r\n Math.pow(point.x - lineStart.x, 2) +\r\n Math.pow(point.y - lineStart.y, 2)\r\n );\r\n }\r\n\r\n // Calculate the projection parameter t\r\n const t = ((point.x - lineStart.x) * dx + (point.y - lineStart.y) * dy) / lineLengthSq;\r\n\r\n let closestPointX, closestPointY;\r\n\r\n if (t < 0) {\r\n closestPointX = lineStart.x;\r\n closestPointY = lineStart.y;\r\n } else if (t > 1) {\r\n closestPointX = lineEnd.x;\r\n closestPointY = lineEnd.y;\r\n } else {\r\n closestPointX = lineStart.x + t * dx;\r\n closestPointY = lineStart.y + t * dy;\r\n }\r\n\r\n // Calculate the distance from the point to the closest point on the line segment\r\n const distDx = point.x - closestPointX;\r\n const distDy = point.y - closestPointY;\r\n return Math.sqrt(distDx * distDx + distDy * distDy);\r\n\r\n /* // Original implementation using area formula (distance to infinite line)\r\n const lineLength = Math.sqrt(lineLengthSq);\r\n const area = Math.abs(dy * point.x - dx * point.y + lineEnd.x * lineStart.y - lineEnd.y * lineStart.x);\r\n return area / lineLength;\r\n */\r\n}\r\n\r\n/**\r\n * Creates a polygon approximation of a contour using RDP.\r\n * (No longer used by default contour detection, kept for potential external use)\r\n * @param {Array} contourPoints - Array of points {x, y}\r\n * @param {number} epsilon - Epsilon for polygon approximation (relative to perimeter)\r\n * @returns {Array} Array of polygon points\r\n */\r\nexport function approximatePolygon(contourPoints, epsilon = 0.02) {\r\n // Calculate contour perimeter\r\n const perimeter = calculateContourPerimeter(contourPoints);\r\n\r\n // Calculate epsilon based on perimeter\r\n const actualEpsilon = epsilon * perimeter;\r\n\r\n // Simplify the contour using RDP\r\n const simplifiedPoints = simplifyContour(contourPoints, actualEpsilon);\r\n\r\n return simplifiedPoints;\r\n}\r\n\r\n/**\r\n * Calculates the perimeter of a contour\r\n * (Helper for RDP approximatePolygon, keep if that function is kept)\r\n * @param {Array} points - Array of point coordinates\r\n * @returns {number} Contour perimeter\r\n */\r\nfunction calculateContourPerimeter(points) {\r\n // ... (keep existing implementation if needed elsewhere) ...\r\n let perimeter = 0;\r\n const n = points.length;\r\n\r\n if (n < 2) return 0;\r\n\r\n for (let i = 0; i < n; i++) {\r\n const j = (i + 1) % n; // Wrap around for the last segment\r\n const dx = points[i].x - points[j].x;\r\n const dy = points[i].y - points[j].y;\r\n perimeter += Math.sqrt(dx * dx + dy * dy);\r\n }\r\n\r\n return perimeter;\r\n}\r\n\r\n// Flood fill is no longer used for contour detection\r\n/*\r\nfunction floodFill(edges, labels, width, height, startX, startY, label) {\r\n // ... (original floodFill implementation removed) ...\r\n}\r\n*/","/**\r\n * Pure JavaScript implementation for detecting corners of a document\r\n * Replaces OpenCV's corner detection and point finding logic\r\n */\r\n\r\nimport { approximatePolygon } from './contourDetection.js';\r\n\r\n/**\r\n * Calculate distance between two points\r\n * @param {Object} p1 - First point {x, y}\r\n * @param {Object} p2 - Second point {x, y}\r\n * @returns {number} Distance between points\r\n */\r\nexport function distance(p1, p2) {\r\n return Math.hypot(p2.x - p1.x, p2.y - p1.y);\r\n}\r\n\r\n/**\r\n * Find the center point of a contour\r\n * @param {Array} points - Array of contour points\r\n * @returns {Object} Center point {x, y}\r\n */\r\nfunction findCenter(points) {\r\n let sumX = 0;\r\n let sumY = 0;\r\n \r\n for (const point of points) {\r\n sumX += point.x;\r\n sumY += point.y;\r\n }\r\n \r\n return {\r\n x: sumX / points.length,\r\n y: sumY / points.length\r\n };\r\n}\r\n\r\n/**\r\n * Find the four corners of a document contour\r\n * @param {Object} contour - Contour object with points property\r\n * @param {Object} options - Configuration options\r\n * @returns {Object} Object with topLeft, topRight, bottomRight, bottomLeft corners\r\n */\r\nexport function findCornerPoints(contour, options = {}) {\r\n if (!contour || !contour.points || contour.points.length < 4) {\r\n console.warn('Contour does not have enough points for corner detection');\r\n return null;\r\n }\r\n \r\n // Try to find a quadrilateral approximation of the contour\r\n const epsilon = options.epsilon || 0.02;\r\n const approximation = approximatePolygon(contour, epsilon);\r\n \r\n let corners;\r\n \r\n // If we get exactly 4 points, we can use them as corners\r\n if (approximation && approximation.length === 4) {\r\n // console.log('Found 4-point approximation, using as corners');\r\n corners = orderCornerPoints(approximation);\r\n } else {\r\n // console.log(`Polygon approximation gave ${approximation ? approximation.length : 'null'} points, using coordinate extremes method`);\r\n // Fallback: Use the coordinate extremes method on the original contour points\r\n corners = findCornersByCoordinateExtremes(contour.points); \r\n }\r\n \r\n // Ensure all corners were found\r\n if (!corners || !corners.topLeft || !corners.topRight || !corners.bottomRight || !corners.bottomLeft) {\r\n console.warn('Failed to find all four corners.', corners);\r\n // Return null or partial corners? Returning null might be safer downstream.\r\n return null; \r\n }\r\n\r\n // Debug info\r\n console.log('Corner points:', corners);\r\n return corners;\r\n}\r\n\r\n/**\r\n * Find corners by finding points with min/max coordinate sums/differences.\r\n * This is an alternative heuristic for finding corners.\r\n * @param {Array} points - Array of contour points\r\n * @returns {Object} Object with topLeft, topRight, bottomRight, bottomLeft corners\r\n */\r\nfunction findCornersByCoordinateExtremes(points) {\r\n if (!points || points.length === 0) return null;\r\n\r\n let topLeft = points[0]; // Min sum x + y\r\n let topRight = points[0]; // Max diff x - y\r\n let bottomRight = points[0]; // Max sum x + y\r\n let bottomLeft = points[0]; // Min diff x - y\r\n\r\n let minSum = topLeft.x + topLeft.y;\r\n let maxDiff = topRight.x - topRight.y;\r\n let maxSum = bottomRight.x + bottomRight.y;\r\n let minDiff = bottomLeft.x - bottomLeft.y;\r\n\r\n for (let i = 1; i < points.length; i++) {\r\n const point = points[i];\r\n const sum = point.x + point.y;\r\n const diff = point.x - point.y;\r\n\r\n // Top-Left (min sum)\r\n if (sum < minSum) {\r\n minSum = sum;\r\n topLeft = point;\r\n }\r\n // Bottom-Right (max sum)\r\n if (sum > maxSum) {\r\n maxSum = sum;\r\n bottomRight = point;\r\n }\r\n // Top-Right (max diff)\r\n if (diff > maxDiff) {\r\n maxDiff = diff;\r\n topRight = point;\r\n }\r\n // Bottom-Left (min diff)\r\n if (diff < minDiff) {\r\n minDiff = diff;\r\n bottomLeft = point;\r\n }\r\n }\r\n\r\n return {\r\n topLeft,\r\n topRight,\r\n bottomRight,\r\n bottomLeft\r\n };\r\n}\r\n\r\n/**\r\n * Orders 4 points in clockwise order starting from top-left\r\n * @param {Array} points - Array of 4 points to order\r\n * @returns {Object} Object with ordered points\r\n */\r\nfunction orderCornerPoints(points) {\r\n if (points.length !== 4) {\r\n console.warn(`Expected 4 points, got ${points.length}`);\r\n return null;\r\n }\r\n \r\n // Calculate centroid\r\n const center = findCenter(points);\r\n \r\n // Sort the points by their angles relative to the center\r\n const sortedPoints = [...points].sort((a, b) => {\r\n const angleA = Math.atan2(a.y - center.y, a.x - center.x);\r\n const angleB = Math.atan2(b.y - center.y, b.x - center.x);\r\n return angleA - angleB;\r\n });\r\n \r\n // Now find the top-left point (minimum sum of x and y)\r\n let minSum = Infinity;\r\n let minIndex = 0;\r\n \r\n for (let i = 0; i < 4; i++) {\r\n const sum = sortedPoints[i].x + sortedPoints[i].y;\r\n if (sum < minSum) {\r\n minSum = sum;\r\n minIndex = i;\r\n }\r\n }\r\n \r\n // Reorder array to start with the top-left point\r\n const orderedPoints = [\r\n sortedPoints[minIndex],\r\n sortedPoints[(minIndex + 1) % 4],\r\n sortedPoints[(minIndex + 2) % 4],\r\n sortedPoints[(minIndex + 3) % 4]\r\n ];\r\n \r\n // Return as named corners\r\n return {\r\n topLeft: orderedPoints[0],\r\n topRight: orderedPoints[1],\r\n bottomRight: orderedPoints[2],\r\n bottomLeft: orderedPoints[3]\r\n };\r\n}","let wasm;\n\nlet cachedUint8ArrayMemory0 = null;\n\nfunction getUint8ArrayMemory0() {\n if (cachedUint8ArrayMemory0 === null || cachedUint8ArrayMemory0.byteLength === 0) {\n cachedUint8ArrayMemory0 = new Uint8Array(wasm.memory.buffer);\n }\n return cachedUint8ArrayMemory0;\n}\n\nlet WASM_VECTOR_LEN = 0;\n\nfunction passArray8ToWasm0(arg, malloc) {\n const ptr = malloc(arg.length * 1, 1) >>> 0;\n getUint8ArrayMemory0().set(arg, ptr / 1);\n WASM_VECTOR_LEN = arg.length;\n return ptr;\n}\n\nfunction getArrayU8FromWasm0(ptr, len) {\n ptr = ptr >>> 0;\n return getUint8ArrayMemory0().subarray(ptr / 1, ptr / 1 + len);\n}\n/**\n * @param {Uint8Array} edges\n * @param {number} width\n * @param {number} height\n * @param {number} kernel_size\n * @returns {Uint8Array}\n */\nexport function dilate(edges, width, height, kernel_size) {\n const ptr0 = passArray8ToWasm0(edges, wasm.__wbindgen_malloc);\n const len0 = WASM_VECTOR_LEN;\n const ret = wasm.dilate(ptr0, len0, width, height, kernel_size);\n var v2 = getArrayU8FromWasm0(ret[0], ret[1]).slice();\n wasm.__wbindgen_free(ret[0], ret[1] * 1, 1);\n return v2;\n}\n\n/**\n * @param {Uint8Array} grayscale\n * @param {number} width\n * @param {number} height\n * @param {number} low_threshold\n * @param {number} high_threshold\n * @param {number} kernel_size\n * @param {number} sigma\n * @param {boolean} l2_gradient\n * @param {boolean} apply_dilation\n * @param {number} dilation_kernel_size\n * @returns {Uint8Array}\n */\nexport function canny_edge_detector_full(grayscale, width, height, low_threshold, high_threshold, kernel_size, sigma, l2_gradient, apply_dilation, dilation_kernel_size) {\n const ptr0 = passArray8ToWasm0(grayscale, wasm.__wbindgen_malloc);\n const len0 = WASM_VECTOR_LEN;\n 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);\n var v2 = getArrayU8FromWasm0(ret[0], ret[1]).slice();\n wasm.__wbindgen_free(ret[0], ret[1] * 1, 1);\n return v2;\n}\n\nlet cachedFloat32ArrayMemory0 = null;\n\nfunction getFloat32ArrayMemory0() {\n if (cachedFloat32ArrayMemory0 === null || cachedFloat32ArrayMemory0.byteLength === 0) {\n cachedFloat32ArrayMemory0 = new Float32Array(wasm.memory.buffer);\n }\n return cachedFloat32ArrayMemory0;\n}\n\nfunction passArrayF32ToWasm0(arg, malloc) {\n const ptr = malloc(arg.length * 4, 4) >>> 0;\n getFloat32ArrayMemory0().set(arg, ptr / 4);\n WASM_VECTOR_LEN = arg.length;\n return ptr;\n}\n/**\n * Applies double thresholding and hysteresis using a stack-based approach.\n * Optimized version with SIMD for threshold comparisons and better memory access patterns.\n * Follows OpenCV's logic more closely.\n *\n * # Arguments\n * * `suppressed` - Suppressed magnitude values (Float32Array from JavaScript)\n * * `width` - Image width\n * * `height` - Image height\n * * `low_threshold` - Low threshold value\n * * `high_threshold` - High threshold value\n *\n * # Returns\n * Edge map as Vec<u8> (0: weak edge/potential, 1: non-edge, 2: strong edge)\n * @param {Float32Array} suppressed\n * @param {number} width\n * @param {number} height\n * @param {number} low_threshold\n * @param {number} high_threshold\n * @returns {Uint8Array}\n */\nexport function hysteresis_thresholding(suppressed, width, height, low_threshold, high_threshold) {\n const ptr0 = passArrayF32ToWasm0(suppressed, wasm.__wbindgen_malloc);\n const len0 = WASM_VECTOR_LEN;\n const ret = wasm.hysteresis_thresholding(ptr0, len0, width, height, low_threshold, high_threshold);\n var v2 = getArrayU8FromWasm0(ret[0], ret[1]).slice();\n wasm.__wbindgen_free(ret[0], ret[1] * 1, 1);\n return v2;\n}\n\n/**\n * Creates a binary edge image from the hysteresis edge map\n * SIMD-optimized version for converting edge map to binary\n *\n * # Arguments\n * * `edge_map` - Edge map from hysteresis thresholding (0, 1, 2 values)\n *\n * # Returns\n * Binary edge image as Vec<u8> (0 or 255)\n * @param {Uint8Array} edge_map\n * @returns {Uint8Array}\n */\nexport function edge_map_to_binary(edge_map) {\n const ptr0 = passArray8ToWasm0(edge_map, wasm.__wbindgen_malloc);\n const len0 = WASM_VECTOR_LEN;\n const ret = wasm.edge_map_to_binary(ptr0, len0);\n var v2 = getArrayU8FromWasm0(ret[0], ret[1]).slice();\n wasm.__wbindgen_free(ret[0], ret[1] * 1, 1);\n return v2;\n}\n\n/**\n * Combined hysteresis thresholding and binary conversion\n * This is a convenience function that combines both steps for efficiency\n * Optimized to avoid intermediate allocations where possible\n *\n * # Arguments\n * * `suppressed` - Suppressed magnitude values (Float32Array from JavaScript)\n * * `width` - Image width\n * * `height` - Image height\n * * `low_threshold` - Low threshold value\n * * `high_threshold` - High threshold value\n *\n * # Returns\n * Binary edge image as Vec<u8> (0 or 255)\n * @param {Float32Array} suppressed\n * @param {number} width\n * @param {number} height\n * @param {number} low_threshold\n * @param {number} high_threshold\n * @returns {Uint8Array}\n */\nexport function hysteresis_thresholding_binary(suppressed, width, height, low_threshold, high_threshold) {\n const ptr0 = passArrayF32ToWasm0(suppressed, wasm.__wbindgen_malloc);\n const len0 = WASM_VECTOR_LEN;\n const ret = wasm.hysteresis_thresholding_binary(ptr0, len0, width, height, low_threshold, high_threshold);\n var v2 = getArrayU8FromWasm0(ret[0], ret[1]).slice();\n wasm.__wbindgen_free(ret[0], ret[1] * 1, 1);\n return v2;\n}\n\n/**\n * @param {Uint8Array} grayscale\n * @param {number} width\n * @param {number} height\n * @param {number} kernel_size\n * @param {number} sigma\n * @returns {Uint8Array}\n */\nexport function blur(grayscale, width, height, kernel_size, sigma) {\n const ptr0 = passArray8ToWasm0(grayscale, wasm.__wbindgen_malloc);\n const len0 = WASM_VECTOR_LEN;\n const ret = wasm.blur(ptr0, len0, width, height, kernel_size, sigma);\n var v2 = getArrayU8FromWasm0(ret[0], ret[1]).slice();\n wasm.__wbindgen_free(ret[0], ret[1] * 1, 1);\n return v2;\n}\n\nlet cachedUint16ArrayMemory0 = null;\n\nfunction getUint16ArrayMemory0() {\n if (cachedUint16ArrayMemory0 === null || cachedUint16ArrayMemory0.byteLength === 0) {\n cachedUint16ArrayMemory0 = new Uint16Array(wasm.memory.buffer);\n }\n return cachedUint16ArrayMemory0;\n}\n\nfunction passArray16ToWasm0(arg, malloc) {\n const ptr = malloc(arg.length * 2, 2) >>> 0;\n getUint16ArrayMemory0().set(arg, ptr / 2);\n WASM_VECTOR_LEN = arg.length;\n return ptr;\n}\n\nfunction getArrayF32FromWasm0(ptr, len) {\n ptr = ptr >>> 0;\n return getFloat32ArrayMemory0().subarray(ptr / 4, ptr / 4 + len);\n}\n/**\n * @param {Int16Array} dx\n * @param {Int16Array} dy\n * @param {number} width\n * @param {number} height\n * @param {boolean} l2_gradient\n * @returns {Float32Array}\n */\nexport function non_maximum_suppression(dx, dy, width, height, l2_gradient) {\n const ptr0 = passArray16ToWasm0(dx, wasm.__wbindgen_malloc);\n const len0 = WASM_VECTOR_LEN;\n const ptr1 = passArray16ToWasm0(dy, wasm.__wbindgen_malloc);\n const len1 = WASM_VECTOR_LEN;\n const ret = wasm.non_maximum_suppression(ptr0, len0, ptr1, len1, width, height, l2_gradient);\n var v3 = getArrayF32FromWasm0(ret[0], ret[1]).slice();\n wasm.__wbindgen_free(ret[0], ret[1] * 4, 4);\n return v3;\n}\n\nlet cachedInt16ArrayMemory0 = null;\n\nfunction getInt16ArrayMemory0() {\n if (cachedInt16ArrayMemory0 === null || cachedInt16ArrayMemory0.byteLength === 0) {\n cachedInt16ArrayMemory0 = new Int16Array(wasm.memory.buffer);\n }\n return cachedInt16ArrayMemory0;\n}\n\nfunction getArrayI16FromWasm0(ptr, len) {\n ptr = ptr >>> 0;\n return getInt16ArrayMemory0().subarray(ptr / 2, ptr / 2 + len);\n}\n/**\n * @param {Uint8Array} blurred\n * @param {number} width\n * @param {number} height\n * @returns {Int16Array}\n */\nexport function calculate_gradients(blurred, width, height) {\n const ptr0 = passArray8ToWasm0(blurred, wasm.__wbindgen_malloc);\n const len0 = WASM_VECTOR_LEN;\n const ret = wasm.calculate_gradients(ptr0, len0, width, height);\n var v2 = getArrayI16FromWasm0(ret[0], ret[1]).slice();\n wasm.__wbindgen_free(ret[0], ret[1] * 2, 2);\n return v2;\n}\n\nasync function __wbg_load(module, imports) {\n if (typeof Response === 'function' && module instanceof Response) {\n if (typeof WebAssembly.instantiateStreaming === 'function') {\n try {\n return await WebAssembly.instantiateStreaming(module, imports);\n\n } catch (e) {\n if (module.headers.get('Content-Type') != 'application/wasm') {\n 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);\n\n } else {\n throw e;\n }\n }\n }\n\n const bytes = await module.arrayBuffer();\n return await WebAssembly.instantiate(bytes, imports);\n\n } else {\n const instance = await WebAssembly.instantiate(module, imports);\n\n if (instance instanceof WebAssembly.Instance) {\n return { instance, module };\n\n } else {\n return instance;\n }\n }\n}\n\nfunction __wbg_get_imports() {\n const imports = {};\n imports.wbg = {};\n imports.wbg.__wbindgen_init_externref_table = function() {\n const table = wasm.__wbindgen_export_0;\n const offset = table.grow(4);\n table.set(0, undefined);\n table.set(offset + 0, undefined);\n table.set(offset + 1, null);\n table.set(offset + 2, true);\n table.set(offset + 3, false);\n ;\n };\n\n return imports;\n}\n\nfunction __wbg_init_memory(imports, memory) {\n\n}\n\nfunction __wbg_finalize_init(instance, module) {\n wasm = instance.exports;\n __wbg_init.__wbindgen_wasm_module = module;\n cachedFloat32ArrayMemory0 = null;\n cachedInt16ArrayMemory0 = null;\n cachedUint16ArrayMemory0 = null;\n cachedUint8ArrayMemory0 = null;\n\n\n wasm.__wbindgen_start();\n return wasm;\n}\n\nfunction initSync(module) {\n if (wasm !== undefined) return wasm;\n\n\n if (typeof module !== 'undefined') {\n if (Object.getPrototypeOf(module) === Object.prototype) {\n ({module} = module)\n } else {\n console.warn('using deprecated parameters for `initSync()`; pass a single object instead')\n }\n }\n\n const imports = __wbg_get_imports();\n\n __wbg_init_memory(imports);\n\n if (!(module instanceof WebAssembly.Module)) {\n module = new WebAssembly.Module(module);\n }\n\n const instance = new WebAssembly.Instance(module, imports);\n\n return __wbg_finalize_init(instance, module);\n}\n\nasync function __wbg_init(module_or_path) {\n if (wasm !== undefined) return wasm;\n\n\n if (typeof module_or_path !== 'undefined') {\n if (Object.getPrototypeOf(module_or_path) === Object.prototype) {\n ({module_or_path} = module_or_path)\n } else {\n console.warn('using deprecated parameters for the initialization function; pass a single object instead')\n }\n }\n\n if (typeof module_or_path === 'undefined') {\n module_or_path = new URL('wasm_blur_bg.wasm', import.meta.url);\n }\n const imports = __wbg_get_imports();\n\n if (typeof module_or_path === 'string' || (typeof Request === 'function' && module_or_path instanceof Request) || (typeof URL === 'function' && module_or_path instanceof URL)) {\n module_or_path = fetch(module_or_path);\n }\n\n __wbg_init_memory(imports);\n\n const { instance, module } = await __wbg_load(await module_or_path, imports);\n\n return __wbg_finalize_init(instance, module);\n}\n\nexport { initSync };\nexport default __wbg_init;\n","/**\r\n * Pure JavaScript implementation of edge detection algorithms\r\n * Inspired by OpenCV's Canny edge detector\r\n */\r\n\r\nimport { DEFAULTS } from './constants.js';\r\nimport init, { \r\n blur as wasmBlur, \r\n calculate_gradients as wasmGradients, \r\n dilate as wasmDilate, \r\n non_maximum_suppression as wasmMaximumSuppression, \r\n canny_edge_detector_full as wasmFullCanny,\r\n hysteresis_thresholding as wasmHysteresis,\r\n hysteresis_thresholding_binary as wasmHysteresisBinary\r\n} from '../wasm_blur/pkg/wasm_blur.js';\r\n\r\n// Initialize the wasm module\r\nconst wasmReady = init();\r\n\r\n/**\r\n * Converts ImageData to grayscale (separate from blur for consistency with jscanify)\r\n * @param {ImageData} imageData - Original image data\r\n * @returns {Uint8ClampedArray} Grayscale image data (1 channel)\r\n */\r\nexport function convertToGrayscale(imageData) {\r\n const { width, height, data } = imageData;\r\n const grayscale = new Uint8ClampedArray(width * height);\r\n \r\n // Convert to grayscale with integer math (faster than floating point)\r\n // Use bit shifting for multiplication (>>8 is equivalent to /256)\r\n for (let i = 0, j = 0; i < data.length; i += 4, j++) {\r\n // 54 (~0.2126*256), 183 (~0.7152*256), 19 (~0.0722*256)\r\n grayscale[j] = (data[i] * 54 + data[i+1] * 183 + data[i+2] * 19) >> 8;\r\n }\r\n \r\n return grayscale;\r\n}\r\n\r\n/**\r\n * Applies Gaussian blur to a grayscale image (matching jscanify's approach)\r\n * @param {Uint8ClampedArray} grayscale - Grayscale image data\r\n * @param {number} width - Image width\r\n * @param {number} height - Image height\r\n * @param {number} kernelSize - Kernel size (should be 5 to match jscanify)\r\n * @param {number} sigma - Gaussian sigma parameter\r\n * @returns {Uint8ClampedArray} Blurred grayscale image data\r\n */\r\nexport function gaussianBlurGrayscale(grayscale, width, height, kernelSize = 5, sigma = 0) {\r\n // If sigma is 0, calculate it from kernel size (OpenCV default)\r\n if (sigma === 0) {\r\n sigma = 0.3 * ((kernelSize - 1) * 0.5 - 1) + 0.8;\r\n }\r\n \r\n const halfKernel = Math.floor(kernelSize / 2);\r\n \r\n // Create and normalize Gaussian kernel once\r\n const kernel = createGaussianKernel(kernelSize, sigma);\r\n \r\n // Preallocate arrays\r\n const tempArray = new Uint8ClampedArray(width * height);\r\n const blurred = new Uint8ClampedArray(width * height);\r\n \r\n // Horizontal pass - process rows in a single loop to improve cache locality\r\n for (let y = 0; y < height; y++) {\r\n const rowOffset = y * width;\r\n \r\n for (let x = 0; x < width; x++) {\r\n let sum = 0;\r\n \r\n // Apply kernel horizontally with bounds checking\r\n for (let k = -halfKernel; k <= halfKernel; k++) {\r\n const xOffset = Math.min(width - 1, Math.max(0, x + k));\r\n sum += grayscale[rowOffset + xOffset] * kernel[halfKernel + k];\r\n }\r\n \r\n tempArray[rowOffset + x] = sum;\r\n }\r\n }\r\n \r\n // Vertical pass - process columns with better memory access pattern\r\n for (let x = 0; x < width; x++) {\r\n for (let y = 0; y < height; y++) {\r\n let sum = 0;\r\n \r\n // Apply kernel vertically with bounds checking\r\n for (let k = -halfKernel; k <= halfKernel; k++) {\r\n const yOffset = Math.min(height - 1, Math.max(0, y + k));\r\n sum += tempArray[yOffset * width + x] * kernel[halfKernel + k];\r\n }\r\n \r\n blurred[y * width + x] = Math.round(sum);\r\n }\r\n }\r\n \r\n return blurred;\r\n}\r\n\r\n/**\r\n * Legacy wrapper for backwards compatibility\r\n * @param {ImageData} imageData - Original image data\r\n * @param {number} sigma - Gaussian sigma parameter (standard deviation)\r\n * @returns {Uint8ClampedArray} Blurred grayscale image data (1 channel)\r\n */\r\nexport function gaussianBlur(imageData, sigma = DEFAULTS.GAUSSIAN_SIGMA, forcedKernelSize = null) {\r\n const grayscale = convertToGrayscale(imageData);\r\n const kernelSize = forcedKernelSize || 5; // Default to 5 like jscanify\r\n return gaussianBlurGrayscale(grayscale, imageData.width, imageData.height, kernelSize, sigma);\r\n}\r\n\r\n/**\r\n * Creates a 1D Gaussian kernel\r\n * @param {number} size - Kernel size (odd number)\r\n * @param {number} sigma - Gaussian sigma parameter\r\n * @returns {Float32Array} Gaussian kernel\r\n */\r\nfunction createGaussianKernel(size, sigma) {\r\n const kernel = new Float32Array(size);\r\n const halfSize = Math.floor(size / 2);\r\n \r\n let sum = 0;\r\n for (let i = 0; i < size; i++) {\r\n const x = i - halfSize;\r\n // Gaussian function: (1/(sigma*sqrt(2*PI))) * e^(-(x^2)/(2*sigma^2))\r\n kernel[i] = Math.exp(-(x * x) / (2 * sigma * sigma));\r\n sum += kernel[i];\r\n }\r\n \r\n // Normalize kernel\r\n for (let i = 0; i < size; i++) {\r\n kernel[i] /= sum;\r\n }\r\n \r\n return kernel;\r\n}\r\n\r\n/**\r\n * Calculates the gradients (dx, dy) using Sobel operators\r\n * @param {Uint8ClampedArray} blurred - Blurred grayscale image\r\n * @param {number} width - Image width\r\n * @param {number} height - Image height\r\n * @returns {{dx: Int16Array, dy: Int16Array}} Object containing gradient arrays\r\n */\r\nfunction calculateGradients(blurred, width, height) {\r\n // Use Int16Array to store gradients, allowing negative values\r\n const dx = new Int16Array(width * height);\r\n const dy = new Int16Array(width * height);\r\n \r\n // Find gradients by unrolling the Sobel operator loops\r\n for (let y = 1; y < height - 1; y++) {\r\n const rowOffset = y * width;\r\n const prevRowOffset = (y - 1) * width;\r\n const nextRowOffset = (y + 1) * width;\r\n\r\n for (let x = 1; x < width - 1; x++) {\r\n const currentIdx = rowOffset + x;\r\n\r\n // Get neighborhood pixels\r\n const p0 = blurred[prevRowOffset + x - 1];\r\n const p1 = blurred[prevRowOffset + x];\r\n const p2 = blurred[prevRowOffset + x + 1];\r\n const p3 = blurred[rowOffset + x - 1];\r\n const p5 = blurred[rowOffset + x + 1];\r\n const p6 = blurred[nextRowOffset + x - 1];\r\n const p7 = blurred[nextRowOffset + x];\r\n const p8 = blurred[nextRowOffset + x + 1];\r\n \r\n // Calculate Sobel gradients\r\n const gx = (p2 - p0) + 2 * (p5 - p3) + (p8 - p6);\r\n const gy = (p6 + 2 * p7 + p8) - (p0 + 2 * p1 + p2);\r\n \r\n dx[currentIdx] = gx;\r\n dy[currentIdx] = gy;\r\n }\r\n }\r\n \r\n return { dx, dy };\r\n}\r\n\r\n\r\n/**\r\n * Applies non-maximum suppression to the gradient magnitude\r\n * @param {Int16Array} dx - Gradient in x-direction\r\n * @param {Int16Array} dy - Gradient in y-direction\r\n * @param {number} width - Image width\r\n * @param {number} height - Image height\r\n * @param {boolean} L2gradient - Whether to use L2 norm for magnitude\r\n * @returns {Float32Array} Suppressed magnitude (using Float32 for precision)\r\n */\r\nfunction nonMaximumSuppression(dx, dy, width, height, L2gradient) {\r\n // Use Float32Array for magnitude to preserve precision before thresholding\r\n const magnitude = new Float32Array(width * height);\r\n const suppressed = new Float32Array(width * height);\r\n \r\n // Calculate magnitude for all pixels first\r\n for (let i = 0; i < dx.length; i++) {\r\n const gx = dx[i];\r\n const gy = dy[i];\r\n if (L2gradient) {\r\n magnitude[i] = Math.sqrt(gx * gx + gy * gy);\r\n } else {\r\n magnitude[i] = Math.abs(gx) + Math.abs(gy); // L1 norm\r\n }\r\n }\r\n \r\n // Perform non-maximum suppression\r\n for (let y = 1; y < height - 1; y++) {\r\n for (let x = 1; x < width - 1; x++) {\r\n const idx = y * width + x;\r\n const mag = magnitude[idx];\r\n \r\n // Skip pixels with zero magnitude\r\n if (mag === 0) {\r\n suppressed[idx] = 0;\r\n continue;\r\n }\r\n \r\n const gx = dx[idx];\r\n const gy = dy[idx];\r\n \r\n let neighbor1 = 0, neighbor2 = 0;\r\n \r\n // Determine neighbors based on gradient direction\r\n // Use absolute values to determine dominant direction\r\n const absGx = Math.abs(gx);\r\n const absGy = Math.abs(gy);\r\n \r\n if (absGy > absGx * 2.4142) { // Vertical edge (angle near 90 or 270)\r\n neighbor1 = magnitude[idx - width]; // top\r\n neighbor2 = magnitude[idx + width]; // bottom\r\n } else if (absGx > absGy * 2.4142) { // Horizontal edge (angle near 0 or 180)\r\n neighbor1 = magnitude[idx - 1]; // left\r\n neighbor2 = magnitude[idx + 1]; // right\r\n } else { // Diagonal edge\r\n // Determine diagonal direction based on signs of gx and gy\r\n const s = (gx ^ gy) < 0 ? -1 : 1; // Check if signs are different\r\n if (gy > 0) { // Gradient points down\r\n neighbor1 = magnitude[(y - 1) * width + (x - s)]; // top-left/right\r\n neighbor2 = magnitude[(y + 1) * width + (x + s)]; // bottom-right/left\r\n } else { // Gradient points up\r\n neighbor1 = magnitude[(y + 1) * width + (x - s)]; // bottom-left/right\r\n neighbor2 = magnitude[(y - 1) * width + (x + s)]; // top-right/left\r\n }\r\n // Refined diagonal check (approximating OpenCV's logic)\r\n // Check 45 degrees (top-right / bottom-left)\r\n if ((gx > 0 && gy > 0) || (gx < 0 && gy < 0)) { // Quadrants 1 & 3\r\n neighbor1 = magnitude[(y - 1) * width + (x + 1)]; // top-right\r\n neighbor2 = magnitude[(y + 1) * width + (x - 1)]; // bottom-left\r\n } else { // Quadrants 2 & 4 (135 degrees)\r\n neighbor1 = magnitude[(y - 1) * width + (x - 1)]; // top-left\r\n neighbor2 = magnitude[(y + 1) * width + (x + 1)]; // bottom-right\r\n }\r\n }\r\n \r\n // If the pixel's magnitude is greater than or equal to its neighbors\r\n // along the gradient direction, keep it. Otherwise, suppress it.\r\n if (mag >= neighbor1 && mag >= neighbor2) {\r\n suppressed[idx] = mag;\r\n } else {\r\n suppressed[idx] = 0;\r\n }\r\n }\r\n }\r\n return suppressed;\r\n}\r\n\r\n\r\n/**\r\n * Applies double thresholding and hysteresis using a stack-based approach.\r\n * Follows OpenCV's logic more closely.\r\n * @param {Float32Array} suppressed - Suppressed magnitude (Float32Array)\r\n * @param {number} width - Image width\r\n * @param {number} height - Image height\r\n * @param {number} lowThreshold - Low threshold value\r\n * @param {number} highThreshold - High threshold value\r\n * @returns {Uint8Array} Edge map (0: non-edge, 2: edge pixel)\r\n */\r\nfunction hysteresisThresholding(suppressed, width, height, lowThreshold, highThreshold) {\r\n // Map values: 0 = weak edge (potential), 1 = non-edge, 2 = strong edge\r\n const edgeMap = new Uint8Array(width * height);\r\n const stack = [];\r\n \r\n // First pass: Identify strong edges and potential weak edges\r\n for (let y = 1; y < height - 1; y++) { // Iterate excluding borders\r\n for (let x = 1; x < width - 1; x++) {\r\n const idx = y * width + x;\r\n const mag = suppressed[idx];\r\n \r\n if (mag >= highThreshold) {\r\n // Strong edge pixel\r\n edgeMap[idx] = 2;\r\n stack.push({ x, y });\r\n } else if (mag >= lowThreshold) {\r\n // Weak edge pixel (potential edge)\r\n edgeMap[idx] = 0; // Mark as potential\r\n } else {\r\n // Non-edge pixel\r\n edgeMap[idx] = 1; // Mark as non-edge\r\n }\r\n }\r\n }\r\n // Initialize borders as non-edge (value 1)\r\n for (let x = 0; x < width; x++) {\r\n edgeMap[x] = 1; // Top row\r\n edgeMap[(height - 1) * width + x] = 1; // Bottom row\r\n }\r\n for (let y = 1; y < height - 1; y++) {\r\n edgeMap[y * width] = 1; // Left column\r\n edgeMap[y * width + width - 1] = 1; // Right column\r\n }\r\n\r\n\r\n // Second pass: Hysteresis - connect weak edges to strong edges\r\n const dxNeighbors = [-1, 0, 1, -1, 1, -1, 0, 1];\r\n const dyNeighbors = [-1, -1, -1, 0, 0, 1, 1, 1];\r\n \r\n while (stack.length > 0) {\r\n const { x, y } = stack.pop();\r\n \r\n // Check all 8 neighbors\r\n for (let i = 0; i < 8; i++) {\r\n const nx = x + dxNeighbors[i];\r\n const ny = y + dyNeighbors[i];\r\n const nidx = ny * width + nx;\r\n \r\n // Check bounds (already handled by border initialization)\r\n // If neighbor is a weak edge (value 0), promote it to strong (value 2) and add to stack\r\n if (edgeMap[nidx] === 0) {\r\n edgeMap[nidx] = 2; // Promote to strong edge\r\n stack.push({ x: nx, y: ny });\r\n }\r\n }\r\n }\r\n \r\n // Note: Pixels that were initially weak (0) but not connected remain 0.\r\n // Pixels below lowThreshold remain 1. Only pixels marked 2 are considered final edges.\r\n \r\n return edgeMap; // Return the map with 0, 1, 2 values\r\n}\r\n\r\n/**\r\n * Applies morphological dilation to binary image using a separable (two-pass) approach.\r\n * This is much faster than a 2D kernel for square structuring elements.\r\n * @param {Uint8ClampedArray} edges - Binary edge image (0 or 255)\r\n * @param {number} width - Image width\r\n * @param {number} height - Image height\r\n * @param {number} kernelSize - Kernel size (default 5 to match jscanify)\r\n * @returns {Uint8ClampedArray} Dilated edge image\r\n */\r\nexport function dilateEdges(edges, width, height, kernelSize = 5) {\r\n const halfKernel = Math.floor(kernelSize / 2);\r\n const temp = new Uint8ClampedArray(width * height);\r\n const dilated = new Uint8ClampedArray(width * height);\r\n\r\n // Horizontal pass\r\n for (let y = 0; y < height; y++) {\r\n const rowOffset = y * width;\r\n for (let x = 0; x < width; x++) {\r\n let maxVal = 0;\r\n // Find max in horizontal neighborhood\r\n for (let k = -halfKernel; k <= halfKernel; k++) {\r\n const nx = x + k;\r\n if (nx >= 0 && nx < width) {\r\n const val = edges[rowOffset + nx];\r\n if (val > maxVal) {\r\n maxVal = val;\r\n }\r\n }\r\n }\r\n temp[rowOffset + x] = maxVal;\r\n }\r\n }\r\n\r\n // Vertical pass\r\n for (let x = 0; x < width; x++) {\r\n for (let y = 0; y < height; y++) {\r\n let maxVal = 0;\r\n // Find max in vertical neighborhood from temp array\r\n for (let k = -halfKernel; k <= halfKernel; k++) {\r\n const ny = y + k;\r\n if (ny >= 0 && ny < height) {\r\n const val = temp[ny * width + x];\r\n if (val > maxVal) {\r\n maxVal = val;\r\n }\r\n }\r\n }\r\n dilated[y * width + x] = maxVal;\r\n }\r\n }\r\n \r\n return dilated;\r\n}\r\n\r\n/**\r\n * Full Canny edge detector implementation matching jscanify's approach\r\n * @param {ImageData} imageData - Original image data\r\n * @param {Object} options - Configuration options\r\n * @param {number} [options.lowThreshold=75] - Low threshold for hysteresis (matching jscanify)\r\n * @param {number} [options.highThreshold=200] - High threshold for hysteresis (matching jscanify)\r\n * @param {number} [options.sigma=0] - Gaussian blur sigma (0 means auto-calculate from kernel size)\r\n * @param {number} [options.kernelSize=5] - Gaussian kernel size (matching jscanify)\r\n * @param {boolean} [options.L2gradient=false] - Use L2 norm for gradient magnitude (like OpenCV default)\r\n * @param {boolean} [options.applyDilation=true] - Apply dilation after Canny (matching jscanify)\r\n * @param {number} [options.dilationKernelSize=5] - Dilation kernel size\r\n * @param {boolean} [options.useWasmBlur=false] - Use WASM for Gaussian blur\r\n * @param {boolean} [options.useWasmGradients=false] - Use WASM for gradient calculation\r\n * @param {boolean} [options.useWasmDilation=false] - Use WASM for dilation\r\n * @param {boolean} [options.useWasmNMS=false] - Use WASM for non-maximum suppression\r\n * @param {boolean} [options.useWasmHysteresis=false] - Use WASM for hysteresis thresholding\r\n * @param {boolean} [options.useWasmFullCanny=false] - Use the full WASM Canny implementation\r\n * @param {object} [options.debug={}] - Object to store intermediate results if provided\r\n * @returns {Promise<Uint8ClampedArray>} Binary edge image (0 or 255)\r\n */\r\nexport async function cannyEdgeDetector(imageData, options = {}) {\r\n // Timing table setup\r\n const timings = [];\r\n const tStart = performance.now();\r\n\r\n const { width, height } = imageData;\r\n let lowThreshold = options.lowThreshold !== undefined ? options.lowThreshold : 75;\r\n let highThreshold = options.highThreshold !== undefined ? options.highThreshold : 200;\r\n const kernelSize = options.kernelSize || 5; // Match jscanify's 5x5 kernel\r\n const sigma = options.sigma || 0; // Let the blur function calculate sigma\r\n const L2gradient = options.L2gradient === undefined ? false : options.L2gradient;\r\n const applyDilation = options.applyDilation !== undefined ? options.applyDilation : true;\r\n const dilationKernelSize = options.dilationKernelSize || 5;\r\n const useWasmBlur = true;\r\n const useWasmGradients = false; \r\n const useWasmDilation = true;\r\n const useWasmNMS = true;\r\n const useWasmHysteresis = options.useWasmHysteresis !== undefined ? options.useWasmHysteresis : false;\r\n const useWasmFullCanny = false;\r\n\r\n // Ensure high threshold is greater than low threshold\r\n if (lowThreshold >= highThreshold) {\r\n console.warn(`Canny Edge Detector: lowThreshold (${lowThreshold}) should be lower than highThreshold (${highThreshold}). Swapping them.`);\r\n [lowThreshold, highThreshold] = [highThreshold, lowThreshold];\r\n }\r\n\r\n // Step 1: Convert to grayscale\r\n let t0 = performance.now();\r\n const grayscale = convertToGrayscale(imageData);\r\n let t1 = performance.now();\r\n timings.push({ step: 'Grayscale', ms: (t1 - t0).toFixed(2) });\r\n if (options.debug) options.debug.grayscale = grayscale;\r\n\r\n // Step 2: Apply Gaussian blur (JS or WASM)\r\n let blurred;\r\n t0 = performance.now();\r\n if (useWasmBlur) {\r\n try {\r\n await wasmReady; // Ensure wasm is initialized\r\n blurred = wasmBlur(grayscale, width, height, kernelSize, sigma);\r\n } catch (e) {\r\n blurred = gaussianBlurGrayscale(grayscale, width, height, kernelSize, sigma);\r\n }\r\n } else {\r\n blurred = gaussianBlurGrayscale(grayscale, width, height, kernelSize, sigma);\r\n }\r\n t1 = performance.now();\r\n timings.push({ step: 'Gaussian Blur', ms: (t1 - t0).toFixed(2) });\r\n if (options.debug) {\r\n options.debug.blurred = blurred;\r\n }\r\n\r\n // Step 3: Compute gradients (dx, dy)\r\n t0 = performance.now();\r\n let dx, dy;\r\n if (useWasmGradients) {\r\n try {\r\n await wasmReady; // Ensure wasm is initialized\r\n const gradientResult = wasmGradients(blurred, width, height);\r\n dx = new Int16Array(gradientResult.gx);\r\n dy = new Int16Array(gradientResult.gy);\r\n } catch (e) {\r\n const gradients = calculateGradients(blurred, width, height);\r\n dx = gradients.dx;\r\n dy = gradients.dy;\r\n }\r\n } else {\r\n const gradients = calculateGradients(blurred, width, height);\r\n dx = gradients.dx;\r\n dy = gradients.dy;\r\n }\r\n t1 = performance.now();\r\n timings.push({ step: 'Gradients', ms: (t1 - t0).toFixed(2) });\r\n\r\n // Step 4: Apply non-maximum suppression\r\n t0 = performance.now();\r\n let suppressed;\r\n if (useWasmNMS) {\r\n try {\r\n await wasmReady;\r\n suppressed = await wasmMaximumSuppression(dx, dy, width, height, L2gradient);\r\n } catch (e) {\r\n suppressed = nonMaximumSuppression(dx, dy, width, height, L2gradient);\r\n }\r\n } else {\r\n suppressed = nonMaximumSuppression(dx, dy, width, height, L2gradient);\r\n }\r\n t1 = performance.now();\r\n timings.push({ step: 'Non-Max Suppression', ms: (t1 - t0).toFixed(2) });\r\n\r\n // Step 5: Apply double thresholding and hysteresis\r\n t0 = performance.now();\r\n const finalLowThreshold = L2gradient ? lowThreshold * lowThreshold : lowThreshold;\r\n const finalHighThreshold = L2gradient ? highThreshold * highThreshold : highThreshold;\r\n \r\n let edgeMap;\r\n if (useWasmHysteresis) {\r\n try {\r\n await wasmReady;\r\n edgeMap = wasmHysteresis(suppressed, width, height, finalLowThreshold, finalHighThreshold);\r\n } catch (e) {\r\n console.warn(\"WASM hysteresis failed, falling back to JS:\", e);\r\n edgeMap = hysteresisThresholding(suppressed, width, height, finalLowThreshold, finalHighThreshold);\r\n }\r\n } else {\r\n edgeMap = hysteresisThresholding(suppressed, width, height, finalLowThreshold, finalHighThreshold);\r\n }\r\n \r\n t1 = performance.now();\r\n timings.push({ step: 'Hysteresis', ms: (t1 - t0).toFixed(2) });\r\n\r\n // Step 6: Create binary image (0 or 255)\r\n t0 = performance.now();\r\n const cannyEdges = new Uint8ClampedArray(width * height);\r\n for (let i = 0; i < edgeMap.length; i++) {\r\n cannyEdges[i] = edgeMap[i] === 2 ? 255 : 0;\r\n }\r\n t1 = performance.now();\r\n timings.push({ step: 'Binary Image', ms: (t1 - t0).toFixed(2) });\r\n\r\n // Step 7: Apply dilation if requested (matching jscanify)\r\n t0 = performance.now();\r\n let finalEdges = cannyEdges;\r\n if (applyDilation) {\r\n if (useWasmDilation) {\r\n try {\r\n await wasmReady; // Ensure wasm is initialized\r\n finalEdges = wasmDilate(cannyEdges, width, height, dilationKernelSize);\r\n } catch (e) {\r\n finalEdges = dilateEdges(cannyEdges, width, height, dilationKernelSize);\r\n }\r\n } else {\r\n finalEdges = dilateEdges(cannyEdges, width, height, dilationKernelSize);\r\n }\r\n }\r\n t1 = performance.now();\r\n timings.push({ step: 'Dilation', ms: (t1 - t0).toFixed(2) });\r\n\r\n // Store debug info if requested\r\n if (options.debug) {\r\n options.debug.dx = dx; // Int16Array\r\n options.debug.dy = dy; // Int16Array\r\n // Calculate magnitude separately for debugging if needed\r\n const magnitude = new Float32Array(width * height);\r\n for (let i = 0; i < dx.length; i++) {\r\n const gx = dx[i]; const gy = dy[i];\r\n magnitude[i] = L2gradient ? Math.sqrt(gx * gx + gy * gy) : Math.abs(gx) + Math.abs(gy);\r\n }\r\n options.debug.magnitude = magnitude; // Float32Array (raw magnitude)\r\n options.debug.suppressed = suppressed; // Float32Array (after NMS)\r\n options.debug.edgeMap = edgeMap; // Uint8Array (0, 1, 2 values from hysteresis)\r\n options.debug.cannyEdges = cannyEdges; // Uint8ClampedArray (0 or 255, before dilation)\r\n options.debug.finalEdges = finalEdges; // Uint8ClampedArray (0 or 255, after dilation if applied)\r\n options.debug.timings = timings;\r\n }\r\n\r\n const tEnd = performance.now();\r\n timings.unshift({ step: 'Total', ms: (tEnd - tStart).toFixed(2) });\r\n // Print timing table\r\n console.table(timings);\r\n\r\n return finalEdges; // Return the final binary edge image\r\n}\r\n\r\n/**\r\n * Full Canny edge detector implementation using WASM, for comparison or direct use\r\n * This function is intended to match the performance and output of the JS cannyEdgeDetector,\r\n * but runs entirely in WASM for potentially faster execution.\r\n * @param {ImageData} imageData - Original image data\r\n * @param {Object} options - Configuration options (same as cannyEdgeDetector)\r\n * @returns {Promise<Uint8ClampedArray>} Binary edge image (0 or 255)\r\n */\r\nexport async function cannyEdgeDetectorWasm(imageData, options = {}) {\r\n // Directly call the WASM canny_edge_detector_full function\r\n let result;\r\n try {\r\n await wasmReady; // Ensure wasm is initialized\r\n console.log('Using WASM Full Canny');\r\n result = wasmFullCanny(imageData.data, imageData.width, imageData.height, options.lowThreshold, options.highThreshold, options.sigma, options.kernelSize, options.L2gradient, options.applyDilation, options.dilationKernelSize);\r\n } catch (e) {\r\n console.error(\"WASM full Canny failed:\", e);\r\n throw e; // Rethrow to let the caller handle the error\r\n }\r\n \r\n // Convert result to Uint8ClampedArray (if not already)\r\n const edges = new Uint8ClampedArray(result);\r\n \r\n return edges;\r\n}","/**\r\n * scanic\r\n * JavaScript document scanner without OpenCV dependency\r\n * MIT License\r\n */\r\n\r\n\r\nimport { detectDocumentContour } from './contourDetection.js';\r\nimport { findCornerPoints } from './cornerDetection.js';\r\nimport { cannyEdgeDetector } from './edgeDetection.js';\r\n\r\n\r\n// Helper function to calculate smart adaptive downscale factor\r\nfunction calculateAdaptiveDownscale(imageData, maxDimension = 800) {\r\n const { width, height } = imageData;\r\n const maxCurrentDimension = Math.max(width, height);\r\n \r\n // If image is already smaller than target, no scaling needed\r\n if (maxCurrentDimension <= maxDimension) {\r\n return {\r\n scaledImageData: imageData,\r\n scaleFactor: 1,\r\n originalDimensions: { width, height },\r\n scaledDimensions: { width, height }\r\n };\r\n }\r\n \r\n // Calculate scale factor to fit within maxDimension\r\n const scaleFactor = maxDimension / maxCurrentDimension;\r\n const scaledWidth = Math.round(width * scaleFactor);\r\n const scaledHeight = Math.round(height * scaleFactor);\r\n \r\n // Create scaled image data\r\n const tempCanvas = document.createElement('canvas');\r\n tempCanvas.width = width;\r\n tempCanvas.height = height;\r\n const tempCtx = tempCanvas.getContext('2d');\r\n tempCtx.putImageData(imageData, 0, 0);\r\n\r\n const scaledCanvas = document.createElement('canvas');\r\n scaledCanvas.width = scaledWidth;\r\n scaledCanvas.height = scaledHeight;\r\n const scaledCtx = scaledCanvas.getContext('2d');\r\n \r\n // Use high-quality scaling\r\n scaledCtx.imageSmoothingEnabled = true;\r\n scaledCtx.imageSmoothingQuality = 'high';\r\n scaledCtx.drawImage(tempCanvas, 0, 0, width, height, 0, 0, scaledWidth, scaledHeight);\r\n \r\n const scaledImageData = scaledCtx.getImageData(0, 0, scaledWidth, scaledHeight);\r\n \r\n return {\r\n scaledImageData,\r\n scaleFactor: 1 / scaleFactor, // Return inverse for compatibility with existing code\r\n originalDimensions: { width, height },\r\n scaledDimensions: { width: scaledWidth, height: scaledHeight }\r\n };\r\n}\r\n\r\n// Internal function to detect document in image\r\nasync function detectDocumentInternal(imageData, options = {}) {\r\n const debugInfo = options.debug ? {} : null;\r\n \r\n // Smart adaptive downscaling - ensure largest dimension doesn't exceed maxProcessingDimension\r\n const maxProcessingDimension = options.maxProcessingDimension || 800;\r\n const { scaledImageData, scaleFactor, originalDimensions, scaledDimensions } = \r\n calculateAdaptiveDownscale(imageData, maxProcessingDimension);\r\n \r\n if (debugInfo) {\r\n debugInfo.preprocessing = {\r\n originalDimensions,\r\n scaledDimensions,\r\n scaleFactor,\r\n maxProcessingDimension\r\n };\r\n }\r\n \r\n const { width, height } = scaledImageData; // Use scaled dimensions\r\n \r\n // Run edge detection on the adaptively scaled image\r\n const edges = await cannyEdgeDetector(scaledImageData, {\r\n lowThreshold: options.lowThreshold || 75, // Match OpenCV values\r\n highThreshold: options.highThreshold || 200, // Match OpenCV values\r\n dilationKernelSize: options.dilationKernelSize || 3, // Match OpenCV value \r\n dilationIterations: options.dilationIterations || 1,\r\n debug: debugInfo,\r\n skipNMS: false, // options.skipNMS // Optional flag to skip non-max suppression\r\n useWasmBlur: true, // option to use wasm blur\r\n });\r\n \r\n // Detect contours from edges\r\n const contours = detectDocumentContour(edges, {\r\n minArea: (options.minArea || 1000) / (scaleFactor * scaleFactor), // Adjust minArea for scaled image\r\n debug: debugInfo,\r\n width: width, \r\n height: height \r\n });\r\n\r\n if (!contours || contours.length === 0) {\r\n console.log('No document detected');\r\n return {\r\n success: false,\r\n message: 'No document detected',\r\n debug: debugInfo\r\n };\r\n }\r\n \r\n // Get the largest contour which is likely the document\r\n const documentContour = contours[0]; \r\n \r\n // Find corner points on the scaled image\r\n const cornerPoints = findCornerPoints(documentContour, { \r\n epsilon: options.epsilon // Pass epsilon for approximation\r\n });\r\n \r\n // Scale corner points back to original image size\r\n let finalCorners = cornerPoints;\r\n if (scaleFactor !== 1) {\r\n finalCorners = {\r\n topLeft: { x: cornerPoints.topLeft.x * scaleFactor, y: cornerPoints.topLeft.y * scaleFactor },\r\n topRight: { x: cornerPoints.topRight.x * scaleFactor, y: cornerPoints.topRight.y * scaleFactor },\r\n bottomRight: { x: cornerPoints.bottomRight.x * scaleFactor, y: cornerPoints.bottomRight.y * scaleFactor },\r\n bottomLeft: { x: cornerPoints.bottomLeft.x * scaleFactor, y: cornerPoints.bottomLeft.y * scaleFactor },\r\n };\r\n }\r\n \r\n // Return the result, scaling the contour points back up as well\r\n return {\r\n success: true,\r\n contour: documentContour,\r\n corners: finalCorners,\r\n debug: debugInfo\r\n };\r\n}\r\n\r\n// --- Perspective transform helpers (internal use only) ---\r\nfunction getPerspectiveTransform(srcPoints, dstPoints) {\r\n // Helper to build the system of equations\r\n function buildMatrix(points) {\r\n const matrix = [];\r\n for (let i = 0; i < 4; i++) {\r\n const [x, y] = points[i];\r\n matrix.push([x, y, 1, 0, 0, 0, -x * dstPoints[i][0], -y * dstPoints[i][0]]);\r\n matrix.push([0, 0, 0, x, y, 1, -x * dstPoints[i][1], -y * dstPoints[i][1]]);\r\n }\r\n return matrix;\r\n }\r\n\r\n const A = buildMatrix(srcPoints);\r\n const b = [\r\n dstPoints[0][0], dstPoints[0][1],\r\n dstPoints[1][0], dstPoints[1][1],\r\n dstPoints[2][0], dstPoints[2][1],\r\n dstPoints[3][0], dstPoints[3][1]\r\n ];\r\n\r\n // Solve Ah = b for h (h is 8x1, last element is 1)\r\n // Use Gaussian elimination or Cramer's rule for 8x8\r\n // For simplicity, use numeric.js if available, else implement basic solver\r\n function solve(A, b) {\r\n // Gaussian elimination for 8x8\r\n const m = A.length;\r\n const n = A[0].length;\r\n const M = A.map(row => row.slice());\r\n const B = b.slice();\r\n\r\n for (let i = 0; i < n; i++) {\r\n // Find max row\r\n let maxRow = i;\r\n for (let k = i + 1; k < m; k++) {\r\n if (Math.abs(M[k][i]) > Math.abs(M[maxRow][i])) maxRow = k;\r\n }\r\n // Swap rows\r\n [M[i], M[maxRow]] = [M[maxRow], M[i]];\r\n [B[i], B[maxRow]] = [B[maxRow], B[i]];\r\n\r\n // Eliminate\r\n for (let k = i + 1; k < m; k++) {\r\n const c = M[k][i] / M[i][i];\r\n for (let j = i; j < n; j++) {\r\n M[k][j] -= c * M[i][j];\r\n }\r\n B[k] -= c * B[i];\r\n }\r\n }\r\n\r\n // Back substitution\r\n const x = new Array(n);\r\n for (let i = n - 1; i >= 0; i--) {\r\n let sum = B[i];\r\n for (let j = i + 1; j < n; j++) {\r\n sum -= M[i][j] * x[j];\r\n }\r\n x[i] = sum / M[i][i];\r\n }\r\n return x;\r\n }\r\n\r\n const h = solve(A, b);\r\n // h is [h0,h1,h2,h3,h4,h5,h6,h7], h8 = 1\r\n const matrix = [\r\n [h[0], h[1], h[2]],\r\n [h[3], h[4], h[5]],\r\n [h[6], h[7], 1]\r\n ];\r\n return matrix;\r\n}\r\n\r\n\r\n\r\n\r\nfunction unwarpImage(ctx, image, corners) {\r\n // Get perspective transform matrix\r\n const { topLeft, topRight, bottomRight, bottomLeft } = corners;\r\n // Compute output rectangle size\r\n const widthA = Math.hypot(bottomRight.x - bottomLeft.x, bottomRight.y - bottomLeft.y);\r\n const widthB = Math.hypot(topRight.x - topLeft.x, topRight.y - topLeft.y);\r\n const maxWidth = Math.round(Math.max(widthA, widthB));\r\n const heightA = Math.hypot(topRight.x - bottomRight.x, topRight.y - bottomRight.y);\r\n const heightB = Math.hypot(topLeft.x - bottomLeft.x, topLeft.y - bottomLeft.y);\r\n const maxHeight = Math.round(Math.max(heightA, heightB));\r\n\r\n // Set output canvas size\r\n ctx.canvas.width = maxWidth;\r\n ctx.canvas.height = maxHeight;\r\n\r\n const srcPoints = [\r\n [topLeft.x, topLeft.y],\r\n [topRight.x, topRight.y],\r\n [bottomRight.x, bottomRight.y],\r\n [bottomLeft.x, bottomLeft.y]\r\n ];\r\n const dstPoints = [\r\n [0, 0],\r\n [maxWidth - 1, 0],\r\n [maxWidth - 1, maxHeight - 1],\r\n [0, maxHeight - 1]\r\n ];\r\n const perspectiveMatrix = getPerspectiveTransform(srcPoints, dstPoints);\r\n warpTransform(ctx, image, perspectiveMatrix, maxWidth, maxHeight);\r\n}\r\n\r\nfunction invert3x3(m) {\r\n // Invert a 3x3 matrix\r\n const a = m[0][0], b = m[0][1], c = m[0][2];\r\n const d = m[1][0], e = m[1][1], f = m[1][2];\r\n const g = m[2][0], h = m[2][1], i = m[2][2];\r\n const A = e * i - f * h;\r\n const B = -(d * i - f * g);\r\n const C = d * h - e * g;\r\n const D = -(b * i - c * h);\r\n const E = a * i - c * g;\r\n const F = -(a * h - b * g);\r\n const G = b * f - c * e;\r\n const H = -(a * f - c * d);\r\n const I = a * e - b * d;\r\n const det = a * A + b * B + c * C;\r\n if (det === 0) throw new Error('Singular matrix');\r\n return [\r\n [A / det, D / det, G / det],\r\n [B / det, E / det, H / det],\r\n [C / det, F / det, I / det]\r\n ];\r\n}\r\n\r\nfunction warpTransform(ctx, image, matrix, outWidth, outHeight) {\r\n // Inverse matrix for mapping output to input\r\n const inv = invert3x3(matrix);\r\n // Get source image data\r\n const srcCanvas = document.createElement('canvas');\r\n srcCanvas.width = image.width || image.naturalWidth;\r\n srcCanvas.height = image.height || image.naturalHeight;\r\n const srcCtx = srcCanvas.getContext('2d');\r\n srcCtx.drawImage(image, 0, 0, srcCanvas.width, srcCanvas.height);\r\n const srcData = srcCtx.getImageData(0, 0, srcCanvas.width, srcCanvas.height);\r\n const out = ctx.createImageData(outWidth, outHeight);\r\n for (let y = 0; y < outHeight; y++) {\r\n for (let x = 0; x < outWidth; x++) {\r\n // Map (x, y) in output to (srcX, srcY) in input\r\n const denom = inv[2][0] * x + inv[2][1] * y + inv[2][2];\r\n const srcX = (inv[0][0] * x + inv[0][1] * y + inv[0][2]) / denom;\r\n const srcY = (inv[1][0] * x + inv[1][1] * y + inv[1][2]) / denom;\r\n // Bilinear sample\r\n const sx = Math.max(0, Math.min(srcCanvas.width - 2, srcX));\r\n const sy = Math.max(0, Math.min(srcCanvas.height - 2, srcY));\r\n const ix = Math.floor(sx), iy = Math.floor(sy);\r\n const dx = sx - ix, dy = sy - iy;\r\n for (let c = 0; c < 4; c++) {\r\n // Bilinear interpolation\r\n const i00 = srcData.data[(iy * srcCanvas.width + ix) * 4 + c];\r\n const i10 = srcData.data[(iy * srcCanvas.width + (ix + 1)) * 4 + c];\r\n const i01 = srcData.data[((iy + 1) * srcCanvas.width + ix) * 4 + c];\r\n const i11 = srcData.data[((iy + 1) * srcCanvas.width + (ix + 1)) * 4 + c];\r\n out.data[(y * outWidth + x) * 4 + c] =\r\n (1 - dx) * (1 - dy) * i00 +\r\n dx * (1 - dy) * i10 +\r\n (1 - dx) * dy * i01 +\r\n dx * dy * i11;\r\n }\r\n }\r\n }\r\n ctx.putImageData(out, 0, 0);\r\n}\r\n\r\n\r\n/**\r\n * Extract document with manual corner points (no detection).\r\n * @param {HTMLImageElement|HTMLCanvasElement|ImageData} image\r\n * @param {Object} corners - Corner points object with topLeft, topRight, bottomRight, bottomLeft\r\n * @param {Object} options\r\n * - output: 'canvas' | 'imagedata' | 'dataurl' (default: 'canvas')\r\n * @returns {Promise<{output, corners, success, message}>}\r\n */\r\nexport async function extractDocument(image, corners, options = {}) {\r\n const outputType = options.output || 'canvas';\r\n\r\n if (!corners || !corners.topLeft || !corners.topRight || !corners.bottomRight || !corners.bottomLeft) {\r\n return {\r\n output: null,\r\n corners: null,\r\n success: false,\r\n message: 'Invalid corner points provided'\r\n };\r\n }\r\n\r\n try {\r\n // Create result canvas and extract document\r\n const resultCanvas = document.createElement('canvas');\r\n const ctx = resultCanvas.getContext('2d');\r\n unwarpImage(ctx, image, corners);\r\n\r\n let output;\r\n // Prepare output in requested format\r\n if (outputType === 'canvas') {\r\n output = resultCanvas;\r\n } else if (outputType === 'imagedata') {\r\n output = resultCanvas.getContext('2d').getImageData(0, 0, resultCanvas.width, resultCanvas.height);\r\n } else if (outputType === 'dataurl') {\r\n output = resultCanvas.toDataURL();\r\n } else {\r\n output = resultCanvas;\r\n }\r\n\r\n return {\r\n output,\r\n corners,\r\n success: true,\r\n message: 'Document extracted successfully'\r\n };\r\n } catch (error) {\r\n return {\r\n output: null,\r\n corners,\r\n success: false,\r\n message: `Extraction failed: ${error.message}`\r\n };\r\n }\r\n}\r\n\r\n/**\r\n * Main entry point for document scanning.\r\n * @param {HTMLImageElement|HTMLCanvasElement|ImageData} image\r\n * @param {Object} options\r\n * - mode: 'detect' | 'extract' (default: 'detect')\r\n * - output: 'canvas' | 'imagedata' | 'dataurl' (default: 'canvas')\r\n * - debug: boolean\r\n * - ...other detection options\r\n * @returns {Promise<{output, corners, contour, debug, success, message}>}\r\n */\r\nexport async function scanDocument(image, options = {}) {\r\n const mode = options.mode || 'detect';\r\n const outputType = options.output || 'canvas';\r\n const debug = !!options.debug;\r\n\r\n // Prepare input image data\r\n let imageData, width, height;\r\n if (image instanceof ImageData) {\r\n imageData = image;\r\n width = image.width;\r\n height = image.height;\r\n } else {\r\n // HTMLImageElement or HTMLCanvasElement\r\n const tempCanvas = document.createElement('canvas');\r\n tempCanvas.width = image.width || image.naturalWidth;\r\n tempCanvas.height = image.height || image.naturalHeight;\r\n const tempCtx = tempCanvas.getContext('2d');\r\n tempCtx.drawImage(image, 0, 0, tempCanvas.width, tempCanvas.height);\r\n imageData = tempCtx.getImageData(0, 0, tempCanvas.width, tempCanvas.height);\r\n width = tempCanvas.width;\r\n height = tempCanvas.height;\r\n }\r\n\r\n // Detect document\r\n const detection = await detectDocumentInternal(imageData, options);\r\n if (!detection.success) {\r\n return {\r\n output: null,\r\n corners: null,\r\n contour: null,\r\n debug: detection.debug,\r\n success: false,\r\n message: detection.message || 'No document detected'\r\n };\r\n }\r\n\r\n let resultCanvas;\r\n let output;\r\n\r\n if (mode === 'detect') {\r\n // Just return detection info, no image processing\r\n output = null;\r\n } else if (mode === 'extract') {\r\n // Return only the cropped/warped document\r\n resultCanvas = document.createElement('canvas');\r\n const ctx = resultCanvas.getContext('2d');\r\n unwarpImage(ctx, image, detection.corners);\r\n }\r\n\r\n // Prepare output in requested format (only if not detect mode)\r\n if (mode !== 'detect' && resultCanvas) {\r\n if (outputType === 'canvas') {\r\n output = resultCanvas;\r\n } else if (outputType === 'imagedata') {\r\n output = resultCanvas.getContext('2d').getImageData(0, 0, resultCanvas.width, resultCanvas.height);\r\n } else if (outputType === 'dataurl') {\r\n output = resultCanvas.toDataURL();\r\n } else {\r\n output = resultCanvas;\r\n }\r\n }\r\n\r\n return {\r\n output,\r\n corners: detection.corners,\r\n contour: detection.contour,\r\n debug: detection.debug,\r\n success: true,\r\n message: 'Document detected'\r\n };\r\n}"],"names":["DEFAULTS","RETR_EXTERNAL","RETR_LIST","CHAIN_APPROX_SIMPLE","deltas","detectDocumentContour","edges","options","width","height","mode","method","minArea","paddedWidth","paddedHeight","labels","y","x","contours","nextContourId","currentPixelLabel","leftPixelLabel","startPoint","isOuter","initialDirection","contourId","points","traceContour","finalPoints","simplifyChainApproxSimple","adjustedPoints","p","contour","calculateContourArea","calculateBoundingBox","filteredContours","a","b","visitedPoints","currentPoint","prevDirection","count","maxSteps","searchDirection","found","i","nextX","nextY","nextPoint","checkDirection","checkX","checkY","pointKey","simplifiedPoints","n","prevPoint","dx1","dy1","dx2","dy2","maxDistSq","farthestIdx","p0","pi","distSq","area","j","minX","minY","maxX","maxY","point","simplifyContour","epsilon","maxDistance","index","firstPoint","lastPoint","distance","perpendicularDistance","firstSegment","secondSegment","lineStart","lineEnd","dx","dy","lineLengthSq","t","closestPointX","closestPointY","distDx","distDy","approximatePolygon","contourPoints","perimeter","calculateContourPerimeter","actualEpsilon","findCenter","sumX","sumY","findCornerPoints","approximation","corners","orderCornerPoints","findCornersByCoordinateExtremes","topLeft","topRight","bottomRight","bottomLeft","minSum","maxDiff","maxSum","minDiff","sum","diff","center","sortedPoints","angleA","angleB","minIndex","orderedPoints","wasm","cachedUint8ArrayMemory0","getUint8ArrayMemory0","WASM_VECTOR_LEN","passArray8ToWasm0","arg","malloc","ptr","getArrayU8FromWasm0","len","dilate","kernel_size","ptr0","len0","ret","v2","cachedFloat32ArrayMemory0","getFloat32ArrayMemory0","passArrayF32ToWasm0","hysteresis_thresholding","suppressed","low_threshold","high_threshold","blur","grayscale","sigma","cachedUint16ArrayMemory0","getUint16ArrayMemory0","passArray16ToWasm0","getArrayF32FromWasm0","non_maximum_suppression","l2_gradient","ptr1","len1","v3","__wbg_load","module","imports","e","bytes","instance","__wbg_get_imports","table","offset","__wbg_finalize_init","__wbg_init","module_or_path","wasmReady","init","convertToGrayscale","imageData","data","gaussianBlurGrayscale","kernelSize","halfKernel","kernel","createGaussianKernel","tempArray","blurred","rowOffset","k","xOffset","yOffset","size","halfSize","calculateGradients","prevRowOffset","nextRowOffset","currentIdx","p1","p2","p3","p5","p6","p7","p8","gx","gy","nonMaximumSuppression","L2gradient","magnitude","idx","mag","neighbor1","neighbor2","absGx","absGy","s","hysteresisThresholding","lowThreshold","highThreshold","edgeMap","stack","dxNeighbors","dyNeighbors","nx","ny","nidx","dilateEdges","temp","dilated","maxVal","val","cannyEdgeDetector","timings","tStart","applyDilation","dilationKernelSize","useWasmHysteresis","t0","t1","wasmBlur","gradients","wasmMaximumSuppression","finalLowThreshold","finalHighThreshold","wasmHysteresis","cannyEdges","finalEdges","wasmDilate","tEnd","calculateAdaptiveDownscale","maxDimension","maxCurrentDimension","scaleFactor","scaledWidth","scaledHeight","tempCanvas","scaledCanvas","scaledCtx","detectDocumentInternal","debugInfo","maxProcessingDimension","scaledImageData","originalDimensions","scaledDimensions","documentContour","cornerPoints","finalCorners","getPerspectiveTransform","srcPoints","dstPoints","buildMatrix","matrix","A","solve","m","M","row","B","maxRow","c","h","unwarpImage","ctx","image","widthA","widthB","maxWidth","heightA","heightB","maxHeight","perspectiveMatrix","warpTransform","invert3x3","d","f","g","C","E","F","G","H","I","det","outWidth","outHeight","inv","srcCanvas","srcCtx","srcData","out","denom","srcX","srcY","sx","sy","ix","iy","i00","i10","i01","i11","extractDocument","outputType","resultCanvas","output","error","scanDocument","tempCtx","detection"],"mappings":"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"}