@exige/web-scanner-sdk 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +288 -0
- package/dist/assets/document-worker-QjcJjl9-.js +2 -0
- package/dist/assets/document-worker-QjcJjl9-.js.map +1 -0
- package/dist/assets/ort.min-CA5AZ7mL.js +2937 -0
- package/dist/assets/ort.min-CA5AZ7mL.js.map +1 -0
- package/dist/components/ScannerCameraOverlay.d.ts +36 -0
- package/dist/components/scanner-camera-overlay.css +2 -0
- package/dist/finder-Clo7f2gN.js +1447 -0
- package/dist/finder-Clo7f2gN.js.map +1 -0
- package/dist/index.d.ts +20 -0
- package/dist/live-overlay-geometry.d.ts +11 -0
- package/dist/react.d.ts +2 -0
- package/dist/react.js +448 -0
- package/dist/react.js.map +1 -0
- package/dist/scanner-sdk.js +921 -0
- package/dist/scanner-sdk.js.map +1 -0
- package/dist/sdk/camera.d.ts +142 -0
- package/dist/sdk/detect.d.ts +3 -0
- package/dist/sdk/display-quad.d.ts +23 -0
- package/dist/sdk/document-analysis.d.ts +14 -0
- package/dist/sdk/document-geometry.d.ts +21 -0
- package/dist/sdk/field-model.d.ts +4 -0
- package/dist/sdk/finder-simulator.d.ts +17 -0
- package/dist/sdk/finder.d.ts +166 -0
- package/dist/sdk/frame-geometry.d.ts +6 -0
- package/dist/sdk/image.d.ts +18 -0
- package/dist/sdk/license-codec.d.ts +2 -0
- package/dist/sdk/license-public-key.d.ts +9 -0
- package/dist/sdk/license-runtime.d.ts +2 -0
- package/dist/sdk/license.d.ts +76 -0
- package/dist/sdk/lighting.d.ts +22 -0
- package/dist/sdk/ocr.d.ts +75 -0
- package/dist/sdk/programmatic-redaction.d.ts +102 -0
- package/dist/sdk/redact.d.ts +12 -0
- package/dist/sdk/scan.d.ts +32 -0
- package/dist/sdk/types.d.ts +92 -0
- package/package.json +74 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scanner-sdk.js","names":[],"sources":["../src/sdk/frame-geometry.ts","../src/sdk/finder-simulator.ts","../src/sdk/ocr.ts","../src/sdk/detect.ts","../src/sdk/redact.ts","../src/sdk/field-model.ts","../src/sdk/programmatic-redaction.ts"],"sourcesContent":["import type { DocumentCorners, Point } from './types'\n\nexport interface FrameSize {\n width: number\n height: number\n}\n\nfunction mapPointFromVisibleSourceToTarget(\n point: Point,\n source: FrameSize,\n target: FrameSize,\n): Point {\n const sourceWidth = Math.max(1, source.width)\n const sourceHeight = Math.max(1, source.height)\n const targetWidth = Math.max(1, target.width)\n const targetHeight = Math.max(1, target.height)\n // The analysis frame is the center-cropped view of a differently-shaped\n // still. Invert object-fit: cover so source pixels map into the full still.\n const scale = Math.max(sourceWidth / targetWidth, sourceHeight / targetHeight)\n const offsetX = (sourceWidth - targetWidth * scale) / 2\n const offsetY = (sourceHeight - targetHeight * scale) / 2\n return {\n x: (point.x - offsetX) / scale,\n y: (point.y - offsetY) / scale,\n }\n}\n\nexport function mapDocumentCornersToFrame(\n corners: DocumentCorners,\n source: FrameSize,\n target: FrameSize,\n): DocumentCorners {\n return {\n topLeft: mapPointFromVisibleSourceToTarget(corners.topLeft, source, target),\n topRight: mapPointFromVisibleSourceToTarget(corners.topRight, source, target),\n bottomRight: mapPointFromVisibleSourceToTarget(corners.bottomRight, source, target),\n bottomLeft: mapPointFromVisibleSourceToTarget(corners.bottomLeft, source, target),\n }\n}\n","import type { DocumentQuad, Point } from './types'\n\nexport interface SimulatorPose {\n centerX: number\n centerY: number\n rotation: number\n width: number\n height: number\n phase: 'entering' | 'jittering' | 'settled'\n}\n\nexport interface FinderSimulator {\n start: () => void\n stop: () => void\n reset: () => void\n}\n\nexport function simulatorPose(elapsedMs: number, canvasWidth = 960, canvasHeight = 600): SimulatorPose {\n // Settle comfortably above the finder's default 72% limiting-axis fill target so\n // the simulator demonstrates Move closer before becoming eligible for auto-capture.\n const width = canvasWidth * 0.86\n const height = canvasHeight * 0.86\n if (elapsedMs < 1200) {\n const progress = elapsedMs / 1200\n return {\n centerX: -width / 2 + (canvasWidth / 2 + width / 2) * (1 - Math.pow(1 - progress, 3)),\n centerY: canvasHeight / 2,\n rotation: -0.14 + progress * 0.08,\n width,\n height,\n phase: 'entering',\n }\n }\n if (elapsedMs < 3600) {\n const progress = (elapsedMs - 1200) / 2400\n const amplitude = (1 - progress) * 18\n return {\n centerX: canvasWidth / 2 + Math.sin(elapsedMs / 85) * amplitude,\n centerY: canvasHeight / 2 + Math.cos(elapsedMs / 110) * amplitude * 0.55,\n rotation: -0.055 + Math.sin(elapsedMs / 125) * (1 - progress) * 0.045,\n width,\n height,\n phase: 'jittering',\n }\n }\n return {\n centerX: canvasWidth / 2,\n centerY: canvasHeight / 2,\n rotation: 0,\n width,\n height,\n phase: 'settled',\n }\n}\n\nfunction rotatePoint(x: number, y: number, pose: SimulatorPose): Point {\n const cosine = Math.cos(pose.rotation)\n const sine = Math.sin(pose.rotation)\n return {\n x: pose.centerX + x * cosine - y * sine,\n y: pose.centerY + x * sine + y * cosine,\n }\n}\n\nexport function simulatorQuad(pose: SimulatorPose): DocumentQuad {\n const topLeft = rotatePoint(-pose.width / 2, -pose.height / 2, pose)\n const topRight = rotatePoint(pose.width / 2, -pose.height / 2, pose)\n const bottomRight = rotatePoint(pose.width / 2, pose.height / 2, pose)\n const bottomLeft = rotatePoint(-pose.width / 2, pose.height / 2, pose)\n return {\n topLeft,\n topRight,\n bottomRight,\n bottomLeft,\n edges: {\n top: { start: topLeft, end: topRight },\n right: { start: topRight, end: bottomRight },\n bottom: { start: bottomRight, end: bottomLeft },\n left: { start: bottomLeft, end: topLeft },\n },\n }\n}\n\nfunction drawFrame(canvas: HTMLCanvasElement, pose: SimulatorPose) {\n const context = canvas.getContext('2d')\n if (!context) throw new Error('Canvas 2D is unavailable in this browser.')\n const background = context.createLinearGradient(0, 0, canvas.width, canvas.height)\n background.addColorStop(0, '#16231c')\n background.addColorStop(1, '#32443a')\n context.fillStyle = background\n context.fillRect(0, 0, canvas.width, canvas.height)\n context.save()\n context.translate(pose.centerX, pose.centerY)\n context.rotate(pose.rotation)\n context.shadowColor = 'rgba(0,0,0,.45)'\n context.shadowBlur = 24\n context.fillStyle = '#fffdf4'\n context.strokeStyle = '#111914'\n context.lineWidth = 7\n context.fillRect(-pose.width / 2, -pose.height / 2, pose.width, pose.height)\n context.strokeRect(-pose.width / 2, -pose.height / 2, pose.width, pose.height)\n context.shadowColor = 'transparent'\n const left = -pose.width / 2\n const top = -pose.height / 2\n context.fillStyle = '#d7ded8'\n context.fillRect(left + 38, top + 84, 145, 190)\n context.fillStyle = '#617269'\n context.beginPath()\n context.arc(left + 110, top + 140, 38, 0, Math.PI * 2)\n context.fill()\n context.fillRect(left + 70, top + 185, 80, 65)\n context.fillStyle = '#17231c'\n context.font = '700 25px Arial'\n context.fillText('IDENTITY CARD', left + 38, top + 48)\n context.font = '700 31px Arial'\n context.fillText('TAYLOR MORGAN', left + 220, top + 118)\n context.font = '23px Arial'\n context.fillText('01/02/1990', left + 220, top + 166)\n context.fillText('A1234567', left + 220, top + 208)\n context.fillText('123 MAIN ST', left + 220, top + 270)\n context.fillText('AUSTIN TX 78701', left + 220, top + 307)\n context.restore()\n}\n\nexport function createFinderSimulator(canvas: HTMLCanvasElement): FinderSimulator {\n if (!canvas.width) canvas.width = 960\n if (!canvas.height) canvas.height = 600\n let running = false\n let startedAt = 0\n let animationFrame = 0\n const render = (time: number) => {\n if (!running) return\n if (!startedAt) startedAt = time\n drawFrame(canvas, simulatorPose(time - startedAt, canvas.width, canvas.height))\n animationFrame = requestAnimationFrame(render)\n }\n const reset = () => {\n startedAt = 0\n drawFrame(canvas, simulatorPose(0, canvas.width, canvas.height))\n }\n reset()\n return {\n start() {\n if (running) return\n running = true\n animationFrame = requestAnimationFrame(render)\n },\n stop() {\n running = false\n cancelAnimationFrame(animationFrame)\n },\n reset,\n }\n}\n","import { sourceToCanvas } from './image'\nimport { assertLicense } from './license'\nimport {\n analyzeCastShadowLuminance,\n flattenIlluminationLuminance,\n type CastShadowAnalysis,\n type CastShadowRegion,\n} from './lighting'\nimport type { ImageSource, OcrBox, OcrResult } from './types'\n\nexport interface OcrOptions {\n language?: string\n /**\n * Minimum normalized confidence accepted for OCR tokens, from 0 to 1.\n * Defaults to 0.5. Words and characters are filtered independently.\n */\n minConfidence?: number\n /**\n * Re-reads one low-yield phone/hand cast-shadow tile after local\n * illumination normalization. Defaults to true.\n */\n shadowPass?: boolean\n includeWords?: boolean\n includeCharacters?: boolean\n onProgress?: (progress: number, status: string) => void\n}\n\ninterface TesseractBbox {\n x0: number\n y0: number\n x1: number\n y1: number\n}\n\ninterface TesseractSymbol {\n text: string\n confidence: number\n bbox: TesseractBbox\n}\n\ninterface TesseractWord {\n text: string\n confidence: number\n bbox: TesseractBbox\n symbols: TesseractSymbol[]\n}\n\ninterface TesseractBlock {\n paragraphs: Array<{\n lines: Array<{\n words: TesseractWord[]\n }>\n }>\n}\n\nexport interface OcrPass {\n blocks: TesseractBlock[] | null\n /** Maps pass coordinates back into the original input image. */\n scaleX?: number\n scaleY?: number\n offsetX?: number\n offsetY?: number\n}\n\nfunction toBox(\n item: { text: string; confidence: number; bbox: TesseractBbox },\n scaleX = 1,\n scaleY = 1,\n offsetX = 0,\n offsetY = 0,\n): OcrBox {\n return {\n text: item.text,\n confidence: Math.max(0, Math.min(1, item.confidence / 100)),\n bbox: {\n x: item.bbox.x0 * scaleX + offsetX,\n y: item.bbox.y0 * scaleY + offsetY,\n width: Math.max(0, item.bbox.x1 - item.bbox.x0) * scaleX,\n height: Math.max(0, item.bbox.y1 - item.bbox.y0) * scaleY,\n },\n }\n}\n\nfunction resolveMinConfidence(value = 0.5) {\n if (!Number.isFinite(value) || value < 0 || value > 1) {\n throw new RangeError('OCR minConfidence must be between 0 and 1.')\n }\n return value\n}\n\nfunction overlapOverSmaller(first: OcrBox, second: OcrBox) {\n const left = Math.max(first.bbox.x, second.bbox.x)\n const top = Math.max(first.bbox.y, second.bbox.y)\n const right = Math.min(\n first.bbox.x + first.bbox.width,\n second.bbox.x + second.bbox.width,\n )\n const bottom = Math.min(\n first.bbox.y + first.bbox.height,\n second.bbox.y + second.bbox.height,\n )\n const intersection = Math.max(0, right - left) * Math.max(0, bottom - top)\n const smaller = Math.min(\n first.bbox.width * first.bbox.height,\n second.bbox.width * second.bbox.height,\n )\n return smaller > 0 ? intersection / smaller : 0\n}\n\nfunction dedupeBoxes(boxes: OcrBox[]) {\n const ranked = [...boxes].sort((first, second) =>\n second.confidence - first.confidence ||\n first.bbox.y - second.bbox.y ||\n first.bbox.x - second.bbox.x ||\n first.text.localeCompare(second.text),\n )\n const retained: OcrBox[] = []\n for (const candidate of ranked) {\n if (!retained.some((existing) => overlapOverSmaller(existing, candidate) >= 0.65)) {\n retained.push(candidate)\n }\n }\n return retained\n}\n\nfunction isLikelyText(\n item: OcrBox,\n kind: 'word' | 'character',\n imageWidth: number,\n imageHeight: number,\n inkDensity?: (box: OcrBox['bbox']) => number,\n) {\n const text = item.text.trim()\n if (!text || !/[\\p{L}\\p{N}]/u.test(text)) return false\n const area = item.bbox.width * item.bbox.height\n const areaRatio = area / Math.max(1, imageWidth * imageHeight)\n const aspect = item.bbox.width / Math.max(1, item.bbox.height)\n const minimumArea = kind === 'word' ? 0.000015 : 0.0000015\n const maximumAspect = kind === 'word' ? 50 : 8\n if (areaRatio < minimumArea || aspect < 0.05 || aspect > maximumAspect) return false\n if (inkDensity) {\n const density = inkDensity(item.bbox)\n if (density < 0.01 || density > 0.90) return false\n }\n return true\n}\n\nfunction rebuildText(words: OcrBox[]) {\n const sorted = [...words].sort((first, second) =>\n first.bbox.y + first.bbox.height / 2 - (second.bbox.y + second.bbox.height / 2) ||\n first.bbox.x - second.bbox.x,\n )\n const lines: Array<{ centerY: number; height: number; words: OcrBox[] }> = []\n for (const word of sorted) {\n const centerY = word.bbox.y + word.bbox.height / 2\n const line = lines.find((candidate) =>\n Math.abs(candidate.centerY - centerY) <=\n Math.max(candidate.height, word.bbox.height) * 0.6,\n )\n if (line) {\n line.words.push(word)\n const count = line.words.length\n line.centerY = (line.centerY * (count - 1) + centerY) / count\n line.height = Math.max(line.height, word.bbox.height)\n } else {\n lines.push({ centerY, height: word.bbox.height, words: [word] })\n }\n }\n return lines\n .sort((first, second) => first.centerY - second.centerY)\n .map((line) => line.words\n .sort((first, second) => first.bbox.x - second.bbox.x)\n .map((word) => word.text.trim())\n .join(' '))\n .join('\\n')\n}\n\nexport function mergeOcrPasses(\n passes: OcrPass[],\n options: Pick<OcrOptions, 'minConfidence' | 'includeWords' | 'includeCharacters'>,\n imageSize?: { width: number; height: number },\n inkDensity?: (box: OcrBox['bbox']) => number,\n) {\n const minConfidence = resolveMinConfidence(options.minConfidence)\n const allWords: OcrBox[] = []\n const allCharacters: OcrBox[] = []\n let inferredWidth = 1\n let inferredHeight = 1\n\n for (const pass of passes) {\n const scaleX = pass.scaleX ?? 1\n const scaleY = pass.scaleY ?? 1\n const offsetX = pass.offsetX ?? 0\n const offsetY = pass.offsetY ?? 0\n for (const block of pass.blocks ?? []) {\n for (const paragraph of block.paragraphs) {\n for (const line of paragraph.lines) {\n for (const word of line.words) {\n const wordBox = toBox(word, scaleX, scaleY, offsetX, offsetY)\n inferredWidth = Math.max(inferredWidth, wordBox.bbox.x + wordBox.bbox.width)\n inferredHeight = Math.max(inferredHeight, wordBox.bbox.y + wordBox.bbox.height)\n if (wordBox.confidence >= minConfidence) allWords.push(wordBox)\n for (const symbol of word.symbols) {\n const character = toBox(symbol, scaleX, scaleY, offsetX, offsetY)\n if (character.confidence >= minConfidence) allCharacters.push(character)\n }\n }\n }\n }\n }\n }\n\n const width = imageSize?.width ?? inferredWidth\n const height = imageSize?.height ?? inferredHeight\n const words = dedupeBoxes(allWords)\n .filter((word) => isLikelyText(word, 'word', width, height, inkDensity))\n const characters = dedupeBoxes(allCharacters)\n .filter((character) => isLikelyText(character, 'character', width, height, inkDensity))\n const text = rebuildText(words)\n\n return {\n text,\n confidence: words.length\n ? words.reduce((total, word) => total + word.confidence, 0) / words.length\n : 0,\n words: options.includeWords ? words : [],\n characters: options.includeCharacters ? characters : [],\n }\n}\n\nexport function extractOcrLayout(\n blocks: TesseractBlock[] | null,\n options: Pick<OcrOptions, 'minConfidence' | 'includeWords' | 'includeCharacters'>,\n) {\n return mergeOcrPasses([{ blocks }], options)\n}\n\nexport function extractOcrBoxes(\n blocks: TesseractBlock[] | null,\n options: Pick<OcrOptions, 'minConfidence' | 'includeWords' | 'includeCharacters'>,\n) {\n const { words, characters } = extractOcrLayout(blocks, options)\n return { words, characters }\n}\n\nfunction otsuThreshold(luminance: Uint8Array) {\n const histogram = new Uint32Array(256)\n for (const value of luminance) histogram[value] += 1\n let totalWeighted = 0\n for (let value = 0; value < 256; value += 1) totalWeighted += value * histogram[value]\n let backgroundWeight = 0\n let backgroundWeighted = 0\n let bestVariance = -1\n let threshold = 127\n for (let value = 0; value < 256; value += 1) {\n backgroundWeight += histogram[value]\n if (!backgroundWeight) continue\n const foregroundWeight = luminance.length - backgroundWeight\n if (!foregroundWeight) break\n backgroundWeighted += value * histogram[value]\n const backgroundMean = backgroundWeighted / backgroundWeight\n const foregroundMean = (totalWeighted - backgroundWeighted) / foregroundWeight\n const variance = backgroundWeight * foregroundWeight *\n Math.pow(backgroundMean - foregroundMean, 2)\n if (variance > bestVariance) {\n bestVariance = variance\n threshold = value\n }\n }\n return threshold\n}\n\nfunction canvasLuminance(canvas: HTMLCanvasElement) {\n const context = canvas.getContext('2d', { willReadFrequently: true })\n if (!context) throw new Error('Canvas 2D is unavailable in this browser.')\n const image = context.getImageData(0, 0, canvas.width, canvas.height)\n const luminance = new Uint8Array(canvas.width * canvas.height)\n for (let pixel = 0, offset = 0; pixel < luminance.length; pixel += 1, offset += 4) {\n luminance[pixel] = Math.round(\n image.data[offset] * 0.299 +\n image.data[offset + 1] * 0.587 +\n image.data[offset + 2] * 0.114,\n )\n }\n return { context, image, luminance }\n}\n\nfunction createAdaptiveShadowCanvas(source: HTMLCanvasElement) {\n const maxDimension = 1800\n const sourceMax = Math.max(source.width, source.height)\n const scale = sourceMax > maxDimension\n ? maxDimension / sourceMax\n : Math.min(2, maxDimension / Math.max(1, sourceMax))\n const canvas = document.createElement('canvas')\n canvas.width = Math.max(1, Math.round(source.width * scale))\n canvas.height = Math.max(1, Math.round(source.height * scale))\n const context = canvas.getContext('2d', { willReadFrequently: true })\n if (!context) throw new Error('Canvas 2D is unavailable in this browser.')\n context.drawImage(source, 0, 0, canvas.width, canvas.height)\n const { image, luminance } = canvasLuminance(canvas)\n const flattened = flattenIlluminationLuminance(\n luminance,\n canvas.width,\n canvas.height,\n )\n for (let pixel = 0, offset = 0; pixel < flattened.length; pixel += 1, offset += 4) {\n image.data[offset] = flattened[pixel]\n image.data[offset + 1] = flattened[pixel]\n image.data[offset + 2] = flattened[pixel]\n image.data[offset + 3] = 255\n }\n context.putImageData(image, 0, 0)\n return canvas\n}\n\nfunction sampleCastShadow(source: HTMLCanvasElement) {\n const sample = document.createElement('canvas')\n sample.width = 72\n sample.height = 72\n const context = sample.getContext('2d', { willReadFrequently: true })\n if (!context) return { strong: false, strength: 0, darkness: 0, areaRatio: 0 }\n context.drawImage(source, 0, 0, sample.width, sample.height)\n const { luminance } = canvasLuminance(sample)\n return analyzeCastShadowLuminance(luminance, sample.width, sample.height)\n}\n\nfunction wordCenterInRegion(\n word: OcrBox,\n region: CastShadowRegion,\n width: number,\n height: number,\n) {\n const x = (word.bbox.x + word.bbox.width / 2) / width\n const y = (word.bbox.y + word.bbox.height / 2) / height\n return (\n x >= region.x &&\n x <= region.x + region.width &&\n y >= region.y &&\n y <= region.y + region.height\n )\n}\n\nexport function shadowRegionNeedsOcr(\n analysis: CastShadowAnalysis,\n words: OcrBox[],\n imageSize: { width: number; height: number },\n) {\n if (!analysis.strong || !analysis.region) return false\n const inside = words.filter((word) =>\n wordCenterInRegion(word, analysis.region!, imageSize.width, imageSize.height)\n ).length\n const outside = words.length - inside\n const regionArea = analysis.region.width * analysis.region.height\n const expectedInside = outside > 0 && regionArea < 0.95\n ? outside / (1 - regionArea) * regionArea\n : 3\n // A modest deficit matters on dense receipts/menus: decorative false\n // positives can otherwise make a badly read shadow region look populated.\n return inside < Math.max(3, expectedInside * 0.85)\n}\n\nfunction cropShadowTile(\n adaptive: HTMLCanvasElement,\n region: CastShadowRegion,\n originalWidth: number,\n originalHeight: number,\n) {\n const left = Math.max(0, Math.floor(region.x * adaptive.width))\n const top = Math.max(0, Math.floor(region.y * adaptive.height))\n const right = Math.min(\n adaptive.width,\n Math.ceil((region.x + region.width) * adaptive.width),\n )\n const bottom = Math.min(\n adaptive.height,\n Math.ceil((region.y + region.height) * adaptive.height),\n )\n const tile = document.createElement('canvas')\n tile.width = Math.max(1, right - left)\n tile.height = Math.max(1, bottom - top)\n tile.getContext('2d')?.drawImage(\n adaptive,\n left,\n top,\n tile.width,\n tile.height,\n 0,\n 0,\n tile.width,\n tile.height,\n )\n const scaleX = originalWidth / adaptive.width\n const scaleY = originalHeight / adaptive.height\n return {\n tile,\n passMapping: {\n scaleX,\n scaleY,\n offsetX: left * scaleX,\n offsetY: top * scaleY,\n },\n }\n}\n\nfunction createSparseTextCanvas(source: HTMLCanvasElement) {\n const maxDimension = 2200\n const sourceMax = Math.max(source.width, source.height)\n const scale = sourceMax > maxDimension\n ? maxDimension / sourceMax\n : Math.min(1.5, maxDimension / Math.max(1, sourceMax))\n const canvas = document.createElement('canvas')\n canvas.width = Math.max(1, Math.round(source.width * scale))\n canvas.height = Math.max(1, Math.round(source.height * scale))\n const context = canvas.getContext('2d', { willReadFrequently: true })\n if (!context) throw new Error('Canvas 2D is unavailable in this browser.')\n context.drawImage(source, 0, 0, canvas.width, canvas.height)\n const image = context.getImageData(0, 0, canvas.width, canvas.height)\n const luminance = new Uint8Array(canvas.width * canvas.height)\n for (let pixel = 0, offset = 0; pixel < luminance.length; pixel += 1, offset += 4) {\n luminance[pixel] = Math.round(\n image.data[offset] * 0.299 +\n image.data[offset + 1] * 0.587 +\n image.data[offset + 2] * 0.114,\n )\n }\n const threshold = otsuThreshold(luminance)\n for (let pixel = 0, offset = 0; pixel < luminance.length; pixel += 1, offset += 4) {\n const value = luminance[pixel] <= threshold ? 0 : 255\n image.data[offset] = value\n image.data[offset + 1] = value\n image.data[offset + 2] = value\n image.data[offset + 3] = 255\n }\n context.putImageData(image, 0, 0)\n return canvas\n}\n\nfunction createInkDensitySampler(source: HTMLCanvasElement) {\n const sample = document.createElement('canvas')\n sample.width = 24\n sample.height = 24\n const context = sample.getContext('2d', { willReadFrequently: true })\n return (box: OcrBox['bbox']) => {\n if (!context || box.width <= 0 || box.height <= 0) return 0\n context.clearRect(0, 0, sample.width, sample.height)\n context.drawImage(\n source,\n box.x,\n box.y,\n box.width,\n box.height,\n 0,\n 0,\n sample.width,\n sample.height,\n )\n const pixels = context.getImageData(0, 0, sample.width, sample.height).data\n let minimum = 255\n let maximum = 0\n const values = new Uint8Array(sample.width * sample.height)\n for (let pixel = 0, offset = 0; pixel < values.length; pixel += 1, offset += 4) {\n const value = Math.round(\n pixels[offset] * 0.299 +\n pixels[offset + 1] * 0.587 +\n pixels[offset + 2] * 0.114,\n )\n values[pixel] = value\n minimum = Math.min(minimum, value)\n maximum = Math.max(maximum, value)\n }\n if (maximum - minimum < 12) return 0\n const threshold = minimum + (maximum - minimum) * 0.55\n let ink = 0\n for (const value of values) if (value < threshold) ink += 1\n return ink / values.length\n }\n}\n\nexport async function recognizeText(\n source: ImageSource,\n options: OcrOptions = {},\n): Promise<OcrResult> {\n await assertLicense('ocr')\n const language = options.language ?? 'eng'\n resolveMinConfidence(options.minConfidence)\n const canvas = await sourceToCanvas(source, { cloneCanvas: false })\n const sparseCanvas = createSparseTextCanvas(canvas)\n const shadow: CastShadowAnalysis = options.shadowPass === false\n ? { strong: false, strength: 0, darkness: 0, areaRatio: 0 }\n : sampleCastShadow(canvas)\n const { createWorker, PSM } = await import('tesseract.js')\n let passIndex = 0\n const passCount = shadow.strong ? 3 : 2\n const worker = await createWorker(language, undefined, {\n logger(message) {\n if (typeof message.progress === 'number') {\n options.onProgress?.(\n Math.min(1, (passIndex + message.progress) / passCount),\n message.status,\n )\n }\n },\n })\n try {\n await worker.setParameters({ tessedit_pageseg_mode: PSM.AUTO })\n const automatic = await worker.recognize(\n canvas,\n {},\n { text: true, blocks: true },\n )\n passIndex = 1\n await worker.setParameters({\n tessedit_pageseg_mode: PSM.SPARSE_TEXT,\n preserve_interword_spaces: '1',\n })\n const sparse = await worker.recognize(\n sparseCanvas,\n {},\n { text: true, blocks: true },\n )\n const passes: OcrPass[] = [\n { blocks: automatic.data.blocks as TesseractBlock[] | null },\n {\n blocks: sparse.data.blocks as TesseractBlock[] | null,\n scaleX: canvas.width / sparseCanvas.width,\n scaleY: canvas.height / sparseCanvas.height,\n },\n ]\n const mergeOptions = { ...options, includeWords: true }\n const inkDensity = createInkDensitySampler(canvas)\n const baseLayout = mergeOcrPasses(\n passes,\n mergeOptions,\n { width: canvas.width, height: canvas.height },\n inkDensity,\n )\n const useShadowPass =\n options.shadowPass !== false &&\n shadowRegionNeedsOcr(\n shadow,\n baseLayout.words,\n { width: canvas.width, height: canvas.height },\n )\n if (useShadowPass && shadow.region) {\n passIndex = 2\n const adaptiveCanvas = createAdaptiveShadowCanvas(canvas)\n const { tile, passMapping } = cropShadowTile(\n adaptiveCanvas,\n shadow.region,\n canvas.width,\n canvas.height,\n )\n await worker.setParameters({\n tessedit_pageseg_mode: PSM.SPARSE_TEXT,\n preserve_interword_spaces: '1',\n })\n const adaptive = await worker.recognize(\n tile,\n {},\n { text: true, blocks: true },\n )\n passes.push({\n blocks: adaptive.data.blocks as TesseractBlock[] | null,\n ...passMapping,\n })\n }\n const layout = passes.length === 2\n ? baseLayout\n : mergeOcrPasses(\n passes,\n mergeOptions,\n { width: canvas.width, height: canvas.height },\n inkDensity,\n )\n options.onProgress?.(1, 'recognizing text')\n return {\n text: layout.text,\n confidence: layout.confidence,\n language,\n imageWidth: canvas.width,\n imageHeight: canvas.height,\n diagnostics: {\n shadowPassUsed: useShadowPass,\n castShadowStrength: shadow.strength,\n shadowRegion: shadow.region\n ? {\n x: shadow.region.x * canvas.width,\n y: shadow.region.y * canvas.height,\n width: shadow.region.width * canvas.width,\n height: shadow.region.height * canvas.height,\n }\n : undefined,\n },\n ...(options.includeWords ? { words: layout.words } : {}),\n ...(options.includeCharacters ? { characters: layout.characters } : {}),\n }\n } finally {\n await worker.terminate()\n }\n}\n","import { sourceToCanvas } from './image'\nimport { assertLicense } from './license'\nimport type { DetectOptions, Detection, ImageSource } from './types'\n\ntype DetectorKind = 'vehicle' | 'plate'\nconst sessions = new Map<string, Promise<import('onnxruntime-web').InferenceSession>>()\n\nfunction overlap(a: Detection, b: Detection) {\n const intersection =\n Math.max(0, Math.min(a.x + a.width, b.x + b.width) - Math.max(a.x, b.x)) *\n Math.max(0, Math.min(a.y + a.height, b.y + b.height) - Math.max(a.y, b.y))\n return intersection / (a.width * a.height + b.width * b.height - intersection)\n}\n\nfunction suppress(boxes: Detection[]) {\n return boxes\n .sort((a, b) => b.confidence - a.confidence)\n .filter((candidate, index, all) =>\n all.slice(0, index).every((accepted) => overlap(candidate, accepted) < 0.45),\n )\n .slice(0, 20)\n}\n\nfunction heuristic(canvas: HTMLCanvasElement, kind: DetectorKind): Detection[] {\n const isPlate = kind === 'plate'\n if (canvas.width < 320 || canvas.height < 180) return []\n const aspect = canvas.width / canvas.height\n if (isPlate && aspect < 1.15) return []\n const width = canvas.width * (isPlate ? 0.24 : 0.58)\n const height = canvas.height * (isPlate ? 0.1 : 0.35)\n return [\n {\n id: `${kind}-fallback-1`,\n label: isPlate ? 'possible plate' : 'possible vehicle',\n confidence: isPlate ? 0.42 : 0.48,\n source: 'heuristic',\n x: (canvas.width - width) / 2,\n y: canvas.height * (isPlate ? 0.58 : 0.43),\n width,\n height,\n },\n ]\n}\n\nasync function infer(\n canvas: HTMLCanvasElement,\n kind: DetectorKind,\n modelUrl: string,\n confidence: number,\n) {\n const ort = await import('onnxruntime-web')\n const size = 640\n const input = document.createElement('canvas')\n input.width = size\n input.height = size\n const context = input.getContext('2d', { willReadFrequently: true })\n if (!context) throw new Error('Canvas 2D is unavailable in this browser.')\n context.drawImage(canvas, 0, 0, size, size)\n const pixels = context.getImageData(0, 0, size, size).data\n const channels = size * size\n const data = new Float32Array(channels * 3)\n for (let index = 0; index < channels; index += 1) {\n data[index] = pixels[index * 4] / 255\n data[channels + index] = pixels[index * 4 + 1] / 255\n data[channels * 2 + index] = pixels[index * 4 + 2] / 255\n }\n let sessionPromise = sessions.get(modelUrl)\n if (!sessionPromise) {\n sessionPromise = ort.InferenceSession.create(modelUrl, {\n executionProviders: ['wasm'],\n graphOptimizationLevel: 'all',\n })\n sessions.set(modelUrl, sessionPromise)\n }\n const session = await sessionPromise\n const inputName = session.inputNames[0]\n const output = await session.run({ [inputName]: new ort.Tensor('float32', data, [1, 3, size, size]) })\n const tensor = output[session.outputNames[0]]\n const dimensions = tensor.dims.map(Number)\n const values = tensor.data as Float32Array\n const features = dimensions.length === 3 ? dimensions[1] : 0\n const candidates = dimensions.length === 3 ? dimensions[2] : 0\n if (features < 5 || candidates < 1) throw new Error('Unsupported YOLO output shape.')\n\n const vehicleClasses = new Set([2, 3, 5, 7])\n const boxes: Detection[] = []\n for (let index = 0; index < candidates; index += 1) {\n let bestScore = 0\n let bestClass = 0\n for (let classIndex = 4; classIndex < features; classIndex += 1) {\n const score = values[classIndex * candidates + index]\n if (score > bestScore) {\n bestScore = score\n bestClass = classIndex - 4\n }\n }\n if (bestScore < confidence || (kind === 'vehicle' && !vehicleClasses.has(bestClass))) continue\n const centerX = values[index]\n const centerY = values[candidates + index]\n const width = values[candidates * 2 + index]\n const height = values[candidates * 3 + index]\n boxes.push({\n id: `${kind}-${index}`,\n label: kind === 'plate' ? 'license plate' : ['car', 'motorcycle', 'bus', 'truck'][[2, 3, 5, 7].indexOf(bestClass)] ?? 'vehicle',\n confidence: bestScore,\n source: 'model',\n x: ((centerX - width / 2) / size) * canvas.width,\n y: ((centerY - height / 2) / size) * canvas.height,\n width: (width / size) * canvas.width,\n height: (height / size) * canvas.height,\n })\n }\n return suppress(boxes)\n}\n\nasync function detect(source: ImageSource, kind: DetectorKind, options: DetectOptions) {\n const canvas = await sourceToCanvas(source)\n if (!options.modelUrl) return options.allowFallback === false ? [] : heuristic(canvas, kind)\n try {\n const detections = await infer(canvas, kind, options.modelUrl, options.confidence ?? 0.35)\n return detections.length || options.allowFallback === false ? detections : heuristic(canvas, kind)\n } catch (error) {\n if (options.allowFallback === false) throw error\n return heuristic(canvas, kind)\n }\n}\n\nexport async function detectVehicles(source: ImageSource, options: DetectOptions = {}) {\n await assertLicense('vehicles')\n return detect(source, 'vehicle', options)\n}\n\nexport async function detectPlates(source: ImageSource, options: DetectOptions = {}) {\n await assertLicense('vehicles')\n return detect(source, 'plate', options)\n}\n","import { canvasToBlob, clampBox, sourceToCanvas } from './image'\nimport { assertLicense } from './license'\nimport type { ImageSource, RedactionRegion, RedactOptions } from './types'\n\nexport interface RedactResult {\n canvas: HTMLCanvasElement\n blob: Blob\n}\n\n/**\n * Applies redaction regions to a copy of an image and encodes the result.\n *\n * The source is not mutated. Regions use source-image pixel coordinates and\n * are clipped to the image bounds.\n */\nexport async function applyRedactions(\n source: ImageSource,\n regions: RedactionRegion[],\n options: RedactOptions = {},\n): Promise<RedactResult> {\n await assertLicense('redaction')\n const canvas = await sourceToCanvas(source)\n const context = canvas.getContext('2d')\n if (!context) throw new Error('Canvas 2D is unavailable in this browser.')\n\n for (const region of regions) {\n const box = clampBox(region, canvas.width, canvas.height)\n if (box.width < 1 || box.height < 1) continue\n const mode = region.mode ?? options.mode ?? 'blackout'\n if (mode === 'blackout') {\n context.fillStyle = '#050706'\n context.fillRect(box.x, box.y, box.width, box.height)\n continue\n }\n const buffer = document.createElement('canvas')\n buffer.width = Math.ceil(box.width)\n buffer.height = Math.ceil(box.height)\n const bufferContext = buffer.getContext('2d')\n if (!bufferContext) continue\n bufferContext.filter = `blur(${options.blurRadius ?? 18}px)`\n const bleed = Math.max(12, options.blurRadius ?? 18)\n bufferContext.drawImage(\n canvas,\n box.x - bleed,\n box.y - bleed,\n box.width + bleed * 2,\n box.height + bleed * 2,\n -bleed,\n -bleed,\n box.width + bleed * 2,\n box.height + bleed * 2,\n )\n context.drawImage(buffer, box.x, box.y, box.width, box.height)\n }\n\n return {\n canvas,\n blob: await canvasToBlob(canvas, options.mimeType ?? 'image/png', options.quality),\n }\n}\n","import { sourceToCanvas } from './image'\nimport type { ImageSource } from './types'\nimport type { SemanticField, SemanticModelDetection } from './programmatic-redaction'\n\nconst sessions = new Map<string, Promise<import('onnxruntime-web').InferenceSession>>()\n\nexport const defaultFieldClasses: SemanticField[] = [\n 'person_name',\n 'date_of_birth',\n 'address',\n 'document_number',\n 'expiration_date',\n 'postal_code',\n 'state',\n]\n\nfunction overlap(a: SemanticModelDetection, b: SemanticModelDetection) {\n const intersection =\n Math.max(0, Math.min(a.bbox.x + a.bbox.width, b.bbox.x + b.bbox.width) - Math.max(a.bbox.x, b.bbox.x)) *\n Math.max(0, Math.min(a.bbox.y + a.bbox.height, b.bbox.y + b.bbox.height) - Math.max(a.bbox.y, b.bbox.y))\n return intersection / Math.max(1, a.bbox.width * a.bbox.height + b.bbox.width * b.bbox.height - intersection)\n}\n\nexport async function detectFieldModel(\n source: ImageSource,\n modelUrl: string,\n classes: SemanticField[] = defaultFieldClasses,\n confidence = 0.35,\n): Promise<SemanticModelDetection[]> {\n const canvas = await sourceToCanvas(source, { cloneCanvas: false })\n const ort = await import('onnxruntime-web')\n const size = 640\n const input = document.createElement('canvas')\n input.width = size\n input.height = size\n const context = input.getContext('2d', { willReadFrequently: true })\n if (!context) throw new Error('Canvas 2D is unavailable in this browser.')\n context.drawImage(canvas, 0, 0, size, size)\n const pixels = context.getImageData(0, 0, size, size).data\n const channelSize = size * size\n const data = new Float32Array(channelSize * 3)\n for (let index = 0; index < channelSize; index += 1) {\n data[index] = pixels[index * 4] / 255\n data[channelSize + index] = pixels[index * 4 + 1] / 255\n data[channelSize * 2 + index] = pixels[index * 4 + 2] / 255\n }\n let sessionPromise = sessions.get(modelUrl)\n if (!sessionPromise) {\n sessionPromise = ort.InferenceSession.create(modelUrl, {\n executionProviders: ['wasm'],\n graphOptimizationLevel: 'all',\n })\n sessions.set(modelUrl, sessionPromise)\n }\n const session = await sessionPromise\n const output = await session.run({\n [session.inputNames[0]]: new ort.Tensor('float32', data, [1, 3, size, size]),\n })\n const tensor = output[session.outputNames[0]]\n const dimensions = tensor.dims.map(Number)\n const values = tensor.data as Float32Array\n const features = dimensions.length === 3 ? dimensions[1] : 0\n const candidates = dimensions.length === 3 ? dimensions[2] : 0\n if (features < 5 || candidates < 1) throw new Error('Unsupported field-model YOLO output shape.')\n const detections: SemanticModelDetection[] = []\n for (let index = 0; index < candidates; index += 1) {\n let score = 0\n let classIndex = 0\n for (let feature = 4; feature < Math.min(features, classes.length + 4); feature += 1) {\n const candidateScore = values[feature * candidates + index]\n if (candidateScore > score) {\n score = candidateScore\n classIndex = feature - 4\n }\n }\n const field = classes[classIndex]\n if (!field || score < confidence) continue\n const centerX = values[index]\n const centerY = values[candidates + index]\n const width = values[candidates * 2 + index]\n const height = values[candidates * 3 + index]\n detections.push({\n field,\n confidence: score,\n bbox: {\n x: ((centerX - width / 2) / size) * canvas.width,\n y: ((centerY - height / 2) / size) * canvas.height,\n width: (width / size) * canvas.width,\n height: (height / size) * canvas.height,\n },\n })\n }\n return detections\n .sort((a, b) => b.confidence - a.confidence)\n .filter((candidate, index, all) =>\n all.slice(0, index).every((accepted) =>\n accepted.field !== candidate.field || overlap(candidate, accepted) < 0.45,\n ),\n )\n .slice(0, 30)\n}\n","import { sourceToCanvas } from './image'\nimport { assertLicense } from './license'\nimport { detectFieldModel } from './field-model'\nimport { recognizeText } from './ocr'\nimport { applyRedactions, type RedactResult } from './redact'\nimport type { Box, ImageSource, OcrBox, OcrResult, RedactOptions } from './types'\n\nexport type StringMatchMode = 'exact' | 'case-insensitive' | 'regex'\nexport type LabelDirection = 'above' | 'below' | 'left' | 'right' | 'any'\n\nexport interface StringRedactionRule {\n id: string\n strategy: 'string'\n mode: StringMatchMode\n values?: string[]\n pattern?: string\n flags?: string\n scope?: 'word' | 'line'\n}\n\nexport interface LabelRedactionRule {\n id: string\n strategy: 'label'\n label: string\n direction: LabelDirection\n labelMode?: 'exact' | 'case-insensitive'\n valuePattern?: string\n patternFlags?: string\n window?: {\n distance?: number\n crossAxis?: number\n }\n redactLabel?: boolean\n}\n\nexport type SemanticField =\n | 'person_name'\n | 'date_of_birth'\n | 'address'\n | 'document_number'\n | 'expiration_date'\n | 'postal_code'\n | 'state'\n\nexport interface SemanticRedactionRule {\n id: string\n strategy: 'semantic'\n field: SemanticField\n}\n\nexport interface SemanticModelDetection {\n field: SemanticField\n bbox: Box\n confidence: number\n}\n\nexport type ProgrammaticRedactionRule =\n | StringRedactionRule\n | LabelRedactionRule\n | SemanticRedactionRule\n\nexport interface NormalizedRect {\n x: number\n y: number\n width: number\n height: number\n}\n\nexport interface TemplateRectField {\n id: string\n strategy: 'rect'\n rect: NormalizedRect\n}\n\nexport interface TemplateAnchorField extends Omit<LabelRedactionRule, 'id' | 'strategy'> {\n id: string\n strategy: 'anchor'\n}\n\nexport type RedactionTemplateField = TemplateRectField | TemplateAnchorField\n\nexport interface RedactionTemplate {\n id: string\n fields: RedactionTemplateField[]\n}\n\nexport interface MatchRedactionOptions {\n rules: ProgrammaticRedactionRule[]\n template?: RedactionTemplate\n imageWidth?: number\n imageHeight?: number\n semanticDetections?: SemanticModelDetection[]\n modelRequested?: boolean\n}\n\nexport interface RedactionMatch {\n ruleId: string\n strategy: 'string' | 'label' | 'template-rect' | 'template-anchor'\n | 'semantic'\n source?: 'string' | 'label' | 'template' | 'alias' | 'mrz' | 'layout' | 'model'\n bbox: Box\n text?: string\n confidence: number\n}\n\nexport interface RedactionMatchResult {\n matches: RedactionMatch[]\n unmatchedRuleIds: string[]\n complete: boolean\n confidence: number\n}\n\nexport interface ProgrammaticRedactionOptions extends MatchRedactionOptions {\n ocr?: OcrResult\n style?: RedactOptions\n requireAll?: boolean\n models?: {\n fields?: string\n fieldClasses?: SemanticField[]\n confidence?: number\n allowHeuristicFallback?: boolean\n }\n}\n\nexport interface ProgrammaticRedactionResult extends RedactionMatchResult {\n status: 'success' | 'partial' | 'unmatched'\n ocr?: OcrResult\n canvas?: HTMLCanvasElement\n blob?: Blob\n modelStatus: 'not-requested' | 'loaded' | 'fallback' | 'failed'\n modelError?: string\n}\n\ninterface OcrLine {\n words: OcrBox[]\n text: string\n bbox: Box\n}\n\nfunction unionBoxes(items: Array<{ bbox: Box }>): Box {\n const left = Math.min(...items.map((item) => item.bbox.x))\n const top = Math.min(...items.map((item) => item.bbox.y))\n const right = Math.max(...items.map((item) => item.bbox.x + item.bbox.width))\n const bottom = Math.max(...items.map((item) => item.bbox.y + item.bbox.height))\n return { x: left, y: top, width: right - left, height: bottom - top }\n}\n\nfunction groupLines(words: OcrBox[]) {\n const lines: OcrBox[][] = []\n const sorted = [...words].sort((a, b) =>\n a.bbox.y + a.bbox.height / 2 - (b.bbox.y + b.bbox.height / 2) || a.bbox.x - b.bbox.x,\n )\n for (const word of sorted) {\n const center = word.bbox.y + word.bbox.height / 2\n const line = lines.find((candidate) => {\n const box = unionBoxes(candidate)\n const lineCenter = box.y + box.height / 2\n return Math.abs(center - lineCenter) <= Math.max(word.bbox.height, box.height) * 0.65\n })\n if (line) line.push(word)\n else lines.push([word])\n }\n return lines.map<OcrLine>((lineWords) => {\n lineWords.sort((a, b) => a.bbox.x - b.bbox.x)\n return {\n words: lineWords,\n text: lineWords.map((word) => word.text).join(' '),\n bbox: unionBoxes(lineWords),\n }\n })\n}\n\nfunction normalized(value: string, insensitive: boolean, label = false) {\n const trimmed = value.trim().replace(/\\s+/g, ' ')\n const cleaned = label ? trimmed.replace(/[::]\\s*$/, '') : trimmed\n return insensitive ? cleaned.toLocaleLowerCase() : cleaned\n}\n\nfunction averageConfidence(words: OcrBox[]) {\n return words.reduce((total, word) => total + word.confidence, 0) / Math.max(1, words.length)\n}\n\nfunction matchStringRule(rule: StringRedactionRule, lines: OcrLine[]): RedactionMatch[] {\n const mode = rule.mode\n const insensitive = mode === 'case-insensitive'\n const scope = rule.scope ?? 'line'\n let expression: RegExp | undefined\n if (mode === 'regex') {\n const pattern = rule.pattern ?? rule.values?.[0]\n if (!pattern) return []\n expression = new RegExp(pattern, rule.flags?.replace('g', '') ?? '')\n }\n const values = (rule.values ?? []).filter(Boolean)\n const matches: RedactionMatch[] = []\n const add = (words: OcrBox[]) => matches.push({\n ruleId: rule.id,\n strategy: 'string',\n source: 'string',\n bbox: unionBoxes(words),\n text: words.map((word) => word.text).join(' '),\n confidence: averageConfidence(words),\n })\n if (scope === 'word') {\n for (const line of lines) {\n for (const word of line.words) {\n const candidate = normalized(word.text, insensitive)\n const matched = expression\n ? expression.test(word.text)\n : values.some((value) => candidate === normalized(value, insensitive))\n if (matched) add([word])\n }\n }\n return matches\n }\n for (const line of lines) {\n if (expression) {\n if (expression.test(line.text)) add(line.words)\n continue\n }\n for (const value of values) {\n const expected = normalized(value, insensitive).split(' ')\n for (let index = 0; index <= line.words.length - expected.length; index += 1) {\n const candidate = line.words.slice(index, index + expected.length)\n if (candidate.every((word, offset) =>\n normalized(word.text, insensitive) === expected[offset],\n )) add(candidate)\n }\n }\n }\n return matches\n}\n\nfunction findLabelWords(rule: LabelRedactionRule, lines: OcrLine[]) {\n const insensitive = rule.labelMode !== 'exact'\n const expected = normalized(rule.label, insensitive, true).split(' ')\n for (const line of lines) {\n for (let index = 0; index <= line.words.length - expected.length; index += 1) {\n const candidate = line.words.slice(index, index + expected.length)\n if (candidate.every((word, offset) =>\n normalized(word.text, insensitive, true) === expected[offset],\n )) return candidate\n }\n }\n return undefined\n}\n\nfunction directionalWords(\n words: OcrBox[],\n label: Box,\n direction: Exclude<LabelDirection, 'any'>,\n width: number,\n height: number,\n rule: LabelRedactionRule,\n) {\n const distance = rule.window?.distance ?? 0.28\n const crossAxis = rule.window?.crossAxis ?? 0.12\n const labelCenterX = label.x + label.width / 2\n const labelCenterY = label.y + label.height / 2\n return words.filter((word) => {\n const centerX = word.bbox.x + word.bbox.width / 2\n const centerY = word.bbox.y + word.bbox.height / 2\n if (direction === 'right') {\n return word.bbox.x >= label.x + label.width &&\n word.bbox.x - label.x - label.width <= width * distance &&\n Math.abs(centerY - labelCenterY) <= height * crossAxis\n }\n if (direction === 'left') {\n return word.bbox.x + word.bbox.width <= label.x &&\n label.x - word.bbox.x - word.bbox.width <= width * distance &&\n Math.abs(centerY - labelCenterY) <= height * crossAxis\n }\n if (direction === 'below') {\n return word.bbox.y >= label.y + label.height &&\n word.bbox.y - label.y - label.height <= height * distance &&\n Math.abs(centerX - labelCenterX) <= width * crossAxis\n }\n return word.bbox.y + word.bbox.height <= label.y &&\n label.y - word.bbox.y - word.bbox.height <= height * distance &&\n Math.abs(centerX - labelCenterX) <= width * crossAxis\n })\n}\n\nfunction matchLabelRule(\n rule: LabelRedactionRule,\n words: OcrBox[],\n lines: OcrLine[],\n width: number,\n height: number,\n strategy: RedactionMatch['strategy'] = 'label',\n): RedactionMatch[] {\n const labelWords = findLabelWords(rule, lines)\n if (!labelWords) return []\n const labelBox = unionBoxes(labelWords)\n const labelSet = new Set(labelWords)\n const available = words.filter((word) => !labelSet.has(word))\n const directions: Array<Exclude<LabelDirection, 'any'>> =\n rule.direction === 'any' ? ['right', 'below', 'left', 'above'] : [rule.direction]\n let best: { words: OcrBox[]; distance: number } | undefined\n for (const direction of directions) {\n const candidates = directionalWords(available, labelBox, direction, width, height, rule)\n const candidateLines = groupLines(candidates)\n for (const line of candidateLines) {\n if (rule.valuePattern && !new RegExp(rule.valuePattern, rule.patternFlags).test(line.text)) continue\n const centerX = line.bbox.x + line.bbox.width / 2\n const centerY = line.bbox.y + line.bbox.height / 2\n const labelCenterX = labelBox.x + labelBox.width / 2\n const labelCenterY = labelBox.y + labelBox.height / 2\n const distance = Math.hypot((centerX - labelCenterX) / width, (centerY - labelCenterY) / height)\n if (!best || distance < best.distance) best = { words: line.words, distance }\n }\n }\n if (!best) return []\n const matchedWords = rule.redactLabel ? [...labelWords, ...best.words] : best.words\n return [{\n ruleId: rule.id,\n strategy,\n source: strategy === 'template-anchor' ? 'template' : 'label',\n bbox: unionBoxes(matchedWords),\n text: best.words.map((word) => word.text).join(' '),\n confidence: Math.max(0, averageConfidence(best.words) * (1 - Math.min(0.5, best.distance))),\n }]\n}\n\nconst fieldAliases: Record<SemanticField, string[]> = {\n person_name: ['LN', 'FN', 'DCS', 'DAC', 'DAD', 'NAME'],\n date_of_birth: ['DOB', 'DBB', 'BIRTH'],\n address: ['ADDRESS', 'ADDR', 'DAG', 'DAI'],\n document_number: ['DLN', 'DAQ', 'IDNO', 'LICNO'],\n expiration_date: ['EXP', 'DBA', 'EXPIRES'],\n postal_code: ['ZIP', 'DAK', 'POSTAL'],\n state: ['STATE', 'DAJ'],\n}\n\nconst allAliases = new Set(Object.values(fieldAliases).flat())\nconst dateToken = /^(?:\\d{2}[/-]\\d{2}[/-]\\d{2,4}|\\d{4}[/-]\\d{2}[/-]\\d{2}|\\d{6}|\\d{8})$/\nconst streetToken = /\\b(?:ST|STREET|AVE|AVENUE|RD|ROAD|BLVD|BOULEVARD|LN|LANE|DR|DRIVE|WAY|CT|COURT)\\b/i\nconst headerToken = /\\b(?:DRIVER|LICENSE|IDENTIFICATION|CARD|CLASS|SEX|EYES|HEIGHT|RESTRICTIONS)\\b/i\n\nfunction semanticTextIsValid(field: SemanticField, text: string) {\n const value = text.trim()\n if (!value) return false\n if (field === 'date_of_birth' || field === 'expiration_date') return dateToken.test(value.replace(/\\s/g, ''))\n if (field === 'postal_code') return /^(?:\\d{5}(?:-\\d{4})?|[A-Z]\\d[A-Z]\\s?\\d[A-Z]\\d)$/i.test(value)\n if (field === 'state') return /^[A-Z]{2}$/i.test(value)\n if (field === 'document_number') return /^(?=.*\\d)[A-Z0-9-]{5,20}$/i.test(value)\n if (field === 'person_name') {\n return !/\\d/.test(value) && /^[A-ZÀ-ÖØ-öø-ÿ' -]{3,}$/i.test(value) && !headerToken.test(value)\n }\n return /\\d/.test(value) && /[A-Z]/i.test(value)\n}\n\nfunction semanticMatch(\n ruleId: string,\n source: NonNullable<RedactionMatch['source']>,\n words: OcrBox[],\n text?: string,\n confidenceScale = 1,\n): RedactionMatch {\n return {\n ruleId,\n strategy: 'semantic',\n source,\n bbox: unionBoxes(words),\n text: text ?? words.map((word) => word.text).join(' '),\n confidence: Math.max(0, Math.min(1, averageConfidence(words) * confidenceScale)),\n }\n}\n\nfunction aliasSemanticMatches(field: SemanticField, ruleId: string, lines: OcrLine[]) {\n const aliases = fieldAliases[field]\n const matches: RedactionMatch[] = []\n for (const line of lines) {\n for (let index = 0; index < line.words.length; index += 1) {\n const token = line.words[index].text.toUpperCase().replace(/[^A-Z0-9]/g, '')\n const alias = aliases.find((candidate) => token === candidate || token.startsWith(candidate))\n if (!alias) continue\n let valueWords: OcrBox[]\n let valueText: string\n if (token !== alias) {\n valueWords = [line.words[index]]\n valueText = line.words[index].text.replace(new RegExp(`^${alias}[:\\\\s-]*`, 'i'), '')\n } else {\n valueWords = line.words.slice(index + 1).filter((word) =>\n !allAliases.has(word.text.toUpperCase().replace(/[^A-Z0-9]/g, '')),\n )\n valueText = valueWords.map((word) => word.text).join(' ')\n }\n if (!valueWords.length || !semanticTextIsValid(field, valueText)) continue\n matches.push(semanticMatch(ruleId, 'alias', valueWords, valueText, 0.98))\n }\n }\n return matches\n}\n\nfunction mrzSemanticMatches(field: SemanticField, ruleId: string, lines: OcrLine[]) {\n const mrzLines = lines.filter((line) => {\n const compact = line.text.replace(/\\s/g, '')\n return compact.includes('<<') || /^[A-Z0-9<]{25,}$/.test(compact)\n })\n if (!mrzLines.length) return []\n if (field === 'person_name') {\n const line = mrzLines.find((candidate) => candidate.text.includes('<<'))\n if (!line) return []\n const compact = line.text.replace(/\\s/g, '')\n const nameStart = /^[PIV]<[A-Z]{3}/.test(compact) ? 5 : compact.indexOf('<') + 1\n const names = compact.slice(nameStart).replace(/<+/g, ' ').trim()\n return names ? [semanticMatch(ruleId, 'mrz', line.words, names, 0.96)] : []\n }\n if (field === 'date_of_birth') {\n for (const line of mrzLines) {\n const compact = line.text.replace(/\\s/g, '')\n const value = /^\\d{6}/.exec(compact)?.[0] ?? /^.{13}(\\d{6})/.exec(compact)?.[1]\n if (value) return [semanticMatch(ruleId, 'mrz', line.words, value, 0.96)]\n }\n }\n if (field === 'document_number') {\n const line = mrzLines[0]\n return [semanticMatch(ruleId, 'mrz', line.words, line.text, 0.9)]\n }\n return []\n}\n\nfunction yearFromDate(value: string) {\n const compact = value.replace(/\\s/g, '')\n const fourDigit = compact.match(/\\d{4}/)?.[0]\n if (fourDigit) return Number(fourDigit)\n const six = compact.match(/^\\d{2}/)?.[0]\n if (!six) return undefined\n const year = Number(six)\n return year > new Date().getFullYear() % 100 ? 1900 + year : 2000 + year\n}\n\nfunction layoutSemanticMatches(\n field: SemanticField,\n ruleId: string,\n words: OcrBox[],\n lines: OcrLine[],\n height: number,\n) {\n if (field === 'address') {\n const index = lines.findIndex((line) => /\\d/.test(line.text) && streetToken.test(line.text))\n if (index < 0) return []\n const selected = [...lines[index].words]\n const next = lines[index + 1]\n if (next && /(?:\\b[A-Z]{2}\\b.*\\d{5}|\\d{5})/i.test(next.text)) selected.push(...next.words)\n return [semanticMatch(ruleId, 'layout', selected, undefined, 0.76)]\n }\n if (field === 'person_name') {\n const candidates = lines.map((line) => {\n const alphaWords = line.words.filter((word) => /^[A-ZÀ-ÖØ-öø-ÿ'-]+$/i.test(word.text))\n const y = line.bbox.y / Math.max(1, height)\n const valid = alphaWords.length >= 2 && alphaWords.length <= 5 &&\n alphaWords.length === line.words.length && !headerToken.test(line.text) &&\n !streetToken.test(line.text) && !line.text.includes('<')\n const uppercase = line.text.replace(/[^A-Z]/g, '').length / Math.max(1, line.text.replace(/[^A-Za-z]/g, '').length)\n return { line, score: valid ? 0.45 + averageConfidence(line.words) * 0.3 + uppercase * 0.15 + (y < 0.65 ? 0.1 : 0) : 0 }\n }).filter((candidate) => candidate.score >= 0.68).sort((a, b) => b.score - a.score)\n if (!candidates.length || (candidates[1] && candidates[0].score - candidates[1].score < 0.04)) return []\n return [semanticMatch(ruleId, 'layout', candidates[0].line.words, undefined, candidates[0].score)]\n }\n if (field === 'date_of_birth' || field === 'expiration_date') {\n const currentYear = new Date().getFullYear()\n const candidates = words.filter((word) => dateToken.test(word.text.replace(/\\s/g, '')))\n .map((word) => ({ word, year: yearFromDate(word.text) }))\n .filter((candidate) => candidate.year !== undefined)\n const filtered = field === 'date_of_birth'\n ? candidates.filter((candidate) => candidate.year! <= currentYear - 14 && candidate.year! >= currentYear - 120)\n : candidates.filter((candidate) => candidate.year! >= currentYear - 1)\n filtered.sort((a, b) => field === 'date_of_birth' ? a.year! - b.year! : b.year! - a.year!)\n if (!filtered.length) return []\n return [semanticMatch(ruleId, 'layout', [filtered[0].word], undefined, 0.72)]\n }\n if (field === 'postal_code') {\n const candidate = words.find((word) => semanticTextIsValid(field, word.text))\n return candidate ? [semanticMatch(ruleId, 'layout', [candidate], undefined, 0.78)] : []\n }\n if (field === 'state') {\n const candidates = words.filter((word) => semanticTextIsValid(field, word.text))\n return candidates.length === 1 ? [semanticMatch(ruleId, 'layout', candidates, undefined, 0.7)] : []\n }\n if (field === 'document_number') {\n const candidates = words.filter((word) => semanticTextIsValid(field, word.text))\n candidates.sort((a, b) => b.text.length - a.text.length)\n return candidates.length ? [semanticMatch(ruleId, 'layout', [candidates[0]], undefined, 0.7)] : []\n }\n return []\n}\n\nfunction intersectionRatio(box: Box, word: Box) {\n const area =\n Math.max(0, Math.min(box.x + box.width, word.x + word.width) - Math.max(box.x, word.x)) *\n Math.max(0, Math.min(box.y + box.height, word.y + word.height) - Math.max(box.y, word.y))\n return area / Math.max(1, word.width * word.height)\n}\n\nfunction boxesOverlap(a: Box, b: Box) {\n const area =\n Math.max(0, Math.min(a.x + a.width, b.x + b.width) - Math.max(a.x, b.x)) *\n Math.max(0, Math.min(a.y + a.height, b.y + b.height) - Math.max(a.y, b.y))\n return area / Math.max(1, Math.min(a.width * a.height, b.width * b.height))\n}\n\nfunction matchSemanticRule(\n rule: SemanticRedactionRule,\n words: OcrBox[],\n lines: OcrLine[],\n height: number,\n detections: SemanticModelDetection[] | undefined,\n modelRequested: boolean,\n) {\n const aliases = aliasSemanticMatches(rule.field, rule.id, lines)\n const mrz = mrzSemanticMatches(rule.field, rule.id, lines)\n const deterministic = [...aliases, ...mrz]\n const fallback = deterministic.length\n ? deterministic\n : layoutSemanticMatches(rule.field, rule.id, words, lines, height)\n if (!modelRequested) return fallback\n const modelCandidates = (detections ?? [])\n .filter((detection) => detection.field === rule.field)\n .sort((a, b) => b.confidence - a.confidence)\n for (const detection of modelCandidates) {\n const selected = words.filter((word) => intersectionRatio(detection.bbox, word.bbox) >= 0.3)\n if (!selected.length) continue\n const text = selected.map((word) => word.text).join(' ')\n if (!semanticTextIsValid(rule.field, text)) continue\n const modelMatch = semanticMatch(rule.id, 'model', selected, text, detection.confidence)\n if (fallback.length && !fallback.some((candidate) => boxesOverlap(candidate.bbox, modelMatch.bbox) >= 0.25)) {\n continue\n }\n return [modelMatch]\n }\n return []\n}\n\nfunction templateRect(field: TemplateRectField, templateId: string, width: number, height: number): RedactionMatch {\n const clamp = (value: number) => Math.max(0, Math.min(1, value))\n const x = clamp(field.rect.x)\n const y = clamp(field.rect.y)\n return {\n ruleId: `${templateId}.${field.id}`,\n strategy: 'template-rect',\n source: 'template',\n bbox: {\n x: x * width,\n y: y * height,\n width: Math.max(0, Math.min(1 - x, field.rect.width)) * width,\n height: Math.max(0, Math.min(1 - y, field.rect.height)) * height,\n },\n confidence: 1,\n }\n}\n\nexport function matchRedactionRules(\n ocr: OcrResult | undefined,\n options: MatchRedactionOptions,\n): RedactionMatchResult {\n const width = options.imageWidth ?? ocr?.imageWidth ?? 0\n const height = options.imageHeight ?? ocr?.imageHeight ?? 0\n const words = ocr?.words ?? []\n const lines = groupLines(words)\n const matches: RedactionMatch[] = []\n const unmatchedRuleIds: string[] = []\n for (const rule of options.rules) {\n const ruleMatches = rule.strategy === 'string'\n ? matchStringRule(rule, lines)\n : rule.strategy === 'label'\n ? matchLabelRule(rule, words, lines, width, height)\n : matchSemanticRule(\n rule,\n words,\n lines,\n height,\n options.semanticDetections,\n options.modelRequested === true,\n )\n if (ruleMatches.length) matches.push(...ruleMatches)\n else unmatchedRuleIds.push(rule.id)\n }\n for (const field of options.template?.fields ?? []) {\n const ruleId = `${options.template!.id}.${field.id}`\n if (field.strategy === 'rect') {\n if (width && height) matches.push(templateRect(field, options.template!.id, width, height))\n else unmatchedRuleIds.push(ruleId)\n continue\n }\n const anchorRule: LabelRedactionRule = {\n ...field,\n id: ruleId,\n strategy: 'label',\n }\n const fieldMatches = matchLabelRule(anchorRule, words, lines, width, height, 'template-anchor')\n if (fieldMatches.length) matches.push(...fieldMatches)\n else unmatchedRuleIds.push(ruleId)\n }\n const expected = options.rules.length + (options.template?.fields.length ?? 0)\n const matchedIds = new Set(matches.map((match) => match.ruleId))\n const confidence = expected\n ? [...matchedIds].reduce((total, id) => {\n const items = matches.filter((match) => match.ruleId === id)\n return total + Math.max(...items.map((match) => match.confidence))\n }, 0) / expected\n : 1\n return {\n matches,\n unmatchedRuleIds,\n complete: unmatchedRuleIds.length === 0,\n confidence,\n }\n}\n\nfunction needsOcr(options: ProgrammaticRedactionOptions) {\n return options.rules.length > 0 ||\n options.template?.fields.some((field) => field.strategy === 'anchor') === true\n}\n\nexport function redactionStatus(\n summary: RedactionMatchResult,\n requireAll = false,\n): ProgrammaticRedactionResult['status'] {\n if (requireAll && !summary.complete) return 'unmatched'\n return summary.complete ? 'success' : 'partial'\n}\n\nexport async function redactProgrammatically(\n image: ImageSource,\n options: ProgrammaticRedactionOptions,\n): Promise<ProgrammaticRedactionResult> {\n await assertLicense('redaction')\n const canvas = await sourceToCanvas(image, { cloneCanvas: false })\n let ocr = options.ocr\n if (needsOcr(options) && !ocr?.words) {\n ocr = await recognizeText(canvas, { includeWords: true })\n }\n let semanticDetections = options.semanticDetections\n let modelRequested = options.modelRequested === true\n let modelStatus: ProgrammaticRedactionResult['modelStatus'] = 'not-requested'\n let modelError: string | undefined\n if (options.models?.fields) {\n try {\n semanticDetections = await detectFieldModel(\n canvas,\n options.models.fields,\n options.models.fieldClasses,\n options.models.confidence,\n )\n modelRequested = true\n modelStatus = 'loaded'\n } catch (error) {\n modelError = error instanceof Error ? error.message : 'The field model could not load.'\n const allowFallback = options.models.allowHeuristicFallback !== false\n modelRequested = !allowFallback\n modelStatus = allowFallback ? 'fallback' : 'failed'\n }\n }\n const summary = matchRedactionRules(ocr, {\n rules: options.rules,\n template: options.template,\n imageWidth: canvas.width,\n imageHeight: canvas.height,\n semanticDetections,\n modelRequested,\n })\n const status = redactionStatus(summary, options.requireAll)\n if (status === 'unmatched') {\n return { ...summary, status, ocr, modelStatus, modelError }\n }\n const output: RedactResult = await applyRedactions(\n canvas,\n summary.matches.map((match) => ({ ...match.bbox, mode: options.style?.mode })),\n options.style,\n )\n return {\n ...summary,\n status,\n ocr,\n canvas: output.canvas,\n blob: output.blob,\n modelStatus,\n modelError,\n }\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
export interface CameraOptions {
|
|
2
|
+
facingMode?: 'environment' | 'user';
|
|
3
|
+
idealWidth?: number;
|
|
4
|
+
idealHeight?: number;
|
|
5
|
+
maxWidth?: number;
|
|
6
|
+
maxHeight?: number;
|
|
7
|
+
maxFrameRate?: number;
|
|
8
|
+
/** Rear-camera torch policy. Defaults to `alwaysOff`. */
|
|
9
|
+
torch?: TorchMode;
|
|
10
|
+
}
|
|
11
|
+
export type TorchMode = 'defaultOn' | 'defaultOff' | 'alwaysOn' | 'alwaysOff' | 'auto';
|
|
12
|
+
export interface TorchStatus {
|
|
13
|
+
mode: TorchMode;
|
|
14
|
+
supported: boolean;
|
|
15
|
+
on: boolean;
|
|
16
|
+
/** Whether the camera UI should show a torch control. */
|
|
17
|
+
visible: boolean;
|
|
18
|
+
/** Whether an end user can change the control with `setTorch()`. */
|
|
19
|
+
mutable: boolean;
|
|
20
|
+
/** True when luminance hysteresis, rather than an end user, owns the state. */
|
|
21
|
+
automatic: boolean;
|
|
22
|
+
}
|
|
23
|
+
export interface CameraSession {
|
|
24
|
+
stream: MediaStream;
|
|
25
|
+
readonly torchMode: TorchMode;
|
|
26
|
+
readonly torchSupported: boolean;
|
|
27
|
+
readonly torchOn: boolean;
|
|
28
|
+
capture: (maxDimension?: number) => HTMLCanvasElement;
|
|
29
|
+
capturePhoto: () => Promise<CameraPhoto>;
|
|
30
|
+
getSettings: () => MediaTrackSettings;
|
|
31
|
+
getDiagnostics: () => CameraDiagnostics;
|
|
32
|
+
getTorchStatus: () => TorchStatus;
|
|
33
|
+
/**
|
|
34
|
+
* Changes torch state only for `defaultOn` and `defaultOff`.
|
|
35
|
+
* Returns false, without throwing, for unsupported or immutable modes.
|
|
36
|
+
*/
|
|
37
|
+
setTorch: (enabled: boolean) => Promise<boolean>;
|
|
38
|
+
stop: () => void;
|
|
39
|
+
}
|
|
40
|
+
export type CapturePath = 'video-frame' | 'live-mirror';
|
|
41
|
+
export interface CameraPhoto {
|
|
42
|
+
frame: HTMLCanvasElement;
|
|
43
|
+
method: CapturePath;
|
|
44
|
+
settings: MediaTrackSettings;
|
|
45
|
+
fallbackReason?: 'video-frame-empty';
|
|
46
|
+
luminance: number;
|
|
47
|
+
diagnostics: CameraDiagnostics;
|
|
48
|
+
}
|
|
49
|
+
export interface CameraDiagnostics {
|
|
50
|
+
videoWidth: number;
|
|
51
|
+
videoHeight: number;
|
|
52
|
+
readyState: number;
|
|
53
|
+
paused: boolean;
|
|
54
|
+
connected: boolean;
|
|
55
|
+
rendered: boolean;
|
|
56
|
+
trackReadyState: MediaStreamTrackState | 'missing';
|
|
57
|
+
trackEnabled: boolean;
|
|
58
|
+
trackMuted: boolean;
|
|
59
|
+
liveLuminance: number;
|
|
60
|
+
mirrorLuminance: number;
|
|
61
|
+
mirrorAgeMs: number;
|
|
62
|
+
/**
|
|
63
|
+
* Whether the browser exposes `ImageCapture`. Reported for support triage
|
|
64
|
+
* only: capture never calls `takePhoto()`. WebKit exposes the constructor
|
|
65
|
+
* but returns an all-black still from it.
|
|
66
|
+
*/
|
|
67
|
+
hasImageCapture: boolean;
|
|
68
|
+
torchMode: TorchMode;
|
|
69
|
+
torchSupported: boolean;
|
|
70
|
+
torchOn: boolean;
|
|
71
|
+
torch: TorchStatus;
|
|
72
|
+
engine: BrowserEngine;
|
|
73
|
+
}
|
|
74
|
+
export interface CaptureDiagnosticsReport {
|
|
75
|
+
capturedAt?: string;
|
|
76
|
+
error?: string;
|
|
77
|
+
captureMethod?: CapturePath;
|
|
78
|
+
luminance?: number;
|
|
79
|
+
fallbackReason?: CameraPhoto['fallbackReason'];
|
|
80
|
+
shutter?: CameraDiagnostics;
|
|
81
|
+
live?: CameraDiagnostics;
|
|
82
|
+
finder?: {
|
|
83
|
+
state?: string;
|
|
84
|
+
confidence?: number;
|
|
85
|
+
stableFrames?: number;
|
|
86
|
+
requiredStableFrames?: number;
|
|
87
|
+
detector?: string;
|
|
88
|
+
modelStatus?: string;
|
|
89
|
+
sourceWidth?: number;
|
|
90
|
+
sourceHeight?: number;
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
/** Plain-text dump safe to paste into chat or a ticket. */
|
|
94
|
+
export declare function formatCaptureDiagnosticsReport(report: CaptureDiagnosticsReport): string;
|
|
95
|
+
export interface BrowserEngine {
|
|
96
|
+
/** Engine actually executing, not the vendor shell around it. */
|
|
97
|
+
engine: 'webkit' | 'blink' | 'gecko' | 'unknown';
|
|
98
|
+
/** Vendor shell, which on iOS is only a wrapper around WebKit. */
|
|
99
|
+
browser: string;
|
|
100
|
+
iosWebKit: boolean;
|
|
101
|
+
userAgent: string;
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Identifies the rendering engine by capability plus the vendor shell by name.
|
|
105
|
+
* Every iOS browser is WebKit, so behavior must never be gated on Safari alone.
|
|
106
|
+
*/
|
|
107
|
+
export declare function detectBrowserEngine(userAgent?: string, maxTouchPoints?: number): BrowserEngine;
|
|
108
|
+
/** Mean luminance below this is treated as an empty/black frame. */
|
|
109
|
+
export declare const NEAR_BLACK_LUMINANCE = 10;
|
|
110
|
+
export interface AutoTorchOptions {
|
|
111
|
+
/** Luminance at or below which dark samples accumulate. */
|
|
112
|
+
turnOnLuminance?: number;
|
|
113
|
+
/** Luminance at or above which bright samples accumulate. */
|
|
114
|
+
turnOffLuminance?: number;
|
|
115
|
+
/** Consecutive dark samples required before enabling the torch. */
|
|
116
|
+
darkSamples?: number;
|
|
117
|
+
/** Consecutive bright samples required before disabling the torch. */
|
|
118
|
+
brightSamples?: number;
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* Stateful automatic torch policy. The wide threshold gap and consecutive
|
|
122
|
+
* sample counts prevent exposure changes and hand movement from flickering the
|
|
123
|
+
* LED. `update()` returns a new requested state only when the state changes.
|
|
124
|
+
*/
|
|
125
|
+
export declare class AutoTorchController {
|
|
126
|
+
private on;
|
|
127
|
+
private darkCount;
|
|
128
|
+
private brightCount;
|
|
129
|
+
private readonly turnOnLuminance;
|
|
130
|
+
private readonly turnOffLuminance;
|
|
131
|
+
private readonly darkSamples;
|
|
132
|
+
private readonly brightSamples;
|
|
133
|
+
constructor(options?: AutoTorchOptions);
|
|
134
|
+
update(luminance: number): boolean | undefined;
|
|
135
|
+
getState(): boolean;
|
|
136
|
+
}
|
|
137
|
+
export declare function torchControl(mode: TorchMode, supported: boolean): TorchStatus;
|
|
138
|
+
export declare function meanFrameLuminance(pixels: Uint8ClampedArray): number;
|
|
139
|
+
export declare function isNearBlackFrame(pixels: Uint8ClampedArray, blackThreshold?: number): boolean;
|
|
140
|
+
export declare function mobileCameraConstraints(options?: CameraOptions): MediaStreamConstraints;
|
|
141
|
+
export declare function captureVideoFrame(video: HTMLVideoElement, maxDimension?: number): HTMLCanvasElement;
|
|
142
|
+
export declare function startCamera(video: HTMLVideoElement, options?: CameraOptions): Promise<CameraSession>;
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import type { DetectOptions, Detection, ImageSource } from './types';
|
|
2
|
+
export declare function detectVehicles(source: ImageSource, options?: DetectOptions): Promise<Detection[]>;
|
|
3
|
+
export declare function detectPlates(source: ImageSource, options?: DetectOptions): Promise<Detection[]>;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { DocumentQuad } from './types';
|
|
2
|
+
export interface QuadDisplaySmootherOptions {
|
|
3
|
+
response?: number;
|
|
4
|
+
snapDistance?: number;
|
|
5
|
+
}
|
|
6
|
+
/**
|
|
7
|
+
* Smooths only the displayed quad. Detector stability must continue to use raw
|
|
8
|
+
* detections and must never consume this animated output.
|
|
9
|
+
*/
|
|
10
|
+
export declare class QuadDisplaySmoother {
|
|
11
|
+
private current?;
|
|
12
|
+
private target?;
|
|
13
|
+
private readonly response;
|
|
14
|
+
private readonly snapDistance;
|
|
15
|
+
constructor(options?: QuadDisplaySmootherOptions);
|
|
16
|
+
setTarget(quad: DocumentQuad, frameWidth: number, frameHeight: number): {
|
|
17
|
+
quad: DocumentQuad | undefined;
|
|
18
|
+
snapped: boolean;
|
|
19
|
+
};
|
|
20
|
+
step(deltaMs: number): DocumentQuad | undefined;
|
|
21
|
+
clear(): void;
|
|
22
|
+
getCurrent(): DocumentQuad | undefined;
|
|
23
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { type QuadCandidate } from './document-geometry';
|
|
2
|
+
import type { DocumentDetectionResult, DocumentModelStatus } from './types';
|
|
3
|
+
export interface PixelAnalysis {
|
|
4
|
+
sharpness: number;
|
|
5
|
+
candidate?: QuadCandidate;
|
|
6
|
+
edgeMap?: Uint8Array;
|
|
7
|
+
detector?: NonNullable<DocumentDetectionResult['detector']>;
|
|
8
|
+
modelStatus?: DocumentModelStatus;
|
|
9
|
+
modelProvider?: NonNullable<DocumentDetectionResult['modelProvider']>;
|
|
10
|
+
modelError?: string;
|
|
11
|
+
inferenceMs?: number;
|
|
12
|
+
}
|
|
13
|
+
export declare const MIN_DOCUMENT_SHARPNESS = 3;
|
|
14
|
+
export declare function analyzeDocumentPixels(pixels: Uint8ClampedArray, width: number, height: number, requestedThreshold?: number, priorEdgeMaps?: Uint8Array[], lineFallback?: boolean): PixelAnalysis;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { DocumentCorners, Point } from './types';
|
|
2
|
+
export interface QuadCandidate {
|
|
3
|
+
corners: DocumentCorners;
|
|
4
|
+
confidence: number;
|
|
5
|
+
areaRatio: number;
|
|
6
|
+
}
|
|
7
|
+
export interface PerspectiveQuadScore {
|
|
8
|
+
total: number;
|
|
9
|
+
areaRatio: number;
|
|
10
|
+
angleScore: number;
|
|
11
|
+
oppositeAngleScore: number;
|
|
12
|
+
parallelScore: number;
|
|
13
|
+
homographyScore: number;
|
|
14
|
+
aspectBoost: number;
|
|
15
|
+
}
|
|
16
|
+
export declare function polygonArea(points: Point[]): number;
|
|
17
|
+
export declare function orderCorners(points: Point[]): DocumentCorners | undefined;
|
|
18
|
+
export declare function scorePerspectiveQuad(corners: DocumentCorners, frameWidth: number, frameHeight: number, hull?: Point[]): PerspectiveQuadScore;
|
|
19
|
+
export declare function detectQuadFromEdges(edgeMap: Uint8Array, width: number, height: number, options?: {
|
|
20
|
+
lineFallback?: boolean;
|
|
21
|
+
}): QuadCandidate | undefined;
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { ImageSource } from './types';
|
|
2
|
+
import type { SemanticField, SemanticModelDetection } from './programmatic-redaction';
|
|
3
|
+
export declare const defaultFieldClasses: SemanticField[];
|
|
4
|
+
export declare function detectFieldModel(source: ImageSource, modelUrl: string, classes?: SemanticField[], confidence?: number): Promise<SemanticModelDetection[]>;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { DocumentQuad } from './types';
|
|
2
|
+
export interface SimulatorPose {
|
|
3
|
+
centerX: number;
|
|
4
|
+
centerY: number;
|
|
5
|
+
rotation: number;
|
|
6
|
+
width: number;
|
|
7
|
+
height: number;
|
|
8
|
+
phase: 'entering' | 'jittering' | 'settled';
|
|
9
|
+
}
|
|
10
|
+
export interface FinderSimulator {
|
|
11
|
+
start: () => void;
|
|
12
|
+
stop: () => void;
|
|
13
|
+
reset: () => void;
|
|
14
|
+
}
|
|
15
|
+
export declare function simulatorPose(elapsedMs: number, canvasWidth?: number, canvasHeight?: number): SimulatorPose;
|
|
16
|
+
export declare function simulatorQuad(pose: SimulatorPose): DocumentQuad;
|
|
17
|
+
export declare function createFinderSimulator(canvas: HTMLCanvasElement): FinderSimulator;
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
import { type ScanOptions } from './scan';
|
|
2
|
+
import type { DocumentDetectionResult, DocumentModelStatus, DocumentQuad } from './types';
|
|
3
|
+
export type AutoCaptureMode = 'defaultOn' | 'defaultOff' | 'alwaysOn' | 'alwaysOff';
|
|
4
|
+
export type FinderSource = HTMLVideoElement | MediaStream | HTMLCanvasElement;
|
|
5
|
+
export type FinderState = 'seeking' | 'detecting' | 'unstable' | 'ready' | 'captured';
|
|
6
|
+
export type FinderGuidanceReason = 'place-document' | 'move-closer' | 'move-farther-away' | 'straighten-camera' | 'hold-still' | 'ready';
|
|
7
|
+
export interface QuadFill {
|
|
8
|
+
/** Raw quad bounds width divided by the analysis frame width. */
|
|
9
|
+
widthFill: number;
|
|
10
|
+
/** Raw quad bounds height divided by the analysis frame height. */
|
|
11
|
+
heightFill: number;
|
|
12
|
+
/** Fill on the limiting axis: `max(widthFill, heightFill)`. */
|
|
13
|
+
limitingFill: number;
|
|
14
|
+
/** Shoelace polygon area divided by the analysis frame area. Diagnostic only. */
|
|
15
|
+
areaFill: number;
|
|
16
|
+
/** Long side divided by short side of the raw quad. */
|
|
17
|
+
aspectRatio: number;
|
|
18
|
+
}
|
|
19
|
+
export interface FinderGuidance {
|
|
20
|
+
hint: string;
|
|
21
|
+
reason: FinderGuidanceReason;
|
|
22
|
+
/**
|
|
23
|
+
* Fill on the limiting axis of the raw quad: the larger of its width and
|
|
24
|
+
* height fill. A tall receipt that fills the frame height is fully framed
|
|
25
|
+
* even though its bounding box covers little frame area.
|
|
26
|
+
*/
|
|
27
|
+
fillRatio: number;
|
|
28
|
+
/** Component fills, polygon area fill, and aspect ratio behind `fillRatio`. */
|
|
29
|
+
fill: QuadFill;
|
|
30
|
+
readyForAutoCapture: boolean;
|
|
31
|
+
}
|
|
32
|
+
export interface FinderLighting {
|
|
33
|
+
castShadow: boolean;
|
|
34
|
+
strength: number;
|
|
35
|
+
hint?: string;
|
|
36
|
+
}
|
|
37
|
+
export declare function selectFinderHint(guidance: FinderGuidance, lighting: FinderLighting): string;
|
|
38
|
+
export interface AutoCaptureControl {
|
|
39
|
+
visible: boolean;
|
|
40
|
+
checked: boolean;
|
|
41
|
+
mutable: boolean;
|
|
42
|
+
}
|
|
43
|
+
export interface FinderStatus {
|
|
44
|
+
state: FinderState;
|
|
45
|
+
confidence: number;
|
|
46
|
+
quad?: DocumentQuad;
|
|
47
|
+
guidance: FinderGuidance;
|
|
48
|
+
lighting: FinderLighting;
|
|
49
|
+
stableFrames: number;
|
|
50
|
+
requiredStableFrames: number;
|
|
51
|
+
autoCapture: AutoCaptureControl;
|
|
52
|
+
sourceWidth?: number;
|
|
53
|
+
sourceHeight?: number;
|
|
54
|
+
detector?: DocumentDetectionResult['detector'];
|
|
55
|
+
modelStatus?: DocumentModelStatus;
|
|
56
|
+
modelProvider?: DocumentDetectionResult['modelProvider'];
|
|
57
|
+
modelError?: string;
|
|
58
|
+
inferenceMs?: number;
|
|
59
|
+
}
|
|
60
|
+
export interface FinderCapture {
|
|
61
|
+
frame: HTMLCanvasElement;
|
|
62
|
+
detection?: DocumentDetectionResult;
|
|
63
|
+
reason: 'auto' | 'manual';
|
|
64
|
+
}
|
|
65
|
+
export interface FinderOptions {
|
|
66
|
+
source: FinderSource;
|
|
67
|
+
autoCapture: AutoCaptureMode;
|
|
68
|
+
/** Supplies the final full-resolution frame while finder analysis stays bounded. */
|
|
69
|
+
captureFrame?: () => HTMLCanvasElement | Promise<HTMLCanvasElement>;
|
|
70
|
+
onStatus?: (status: FinderStatus) => void;
|
|
71
|
+
onCapture?: (capture: FinderCapture) => void | Promise<void>;
|
|
72
|
+
/** Reports capture failures, including automatic captures, instead of swallowing them. */
|
|
73
|
+
onCaptureError?: (error: Error) => void;
|
|
74
|
+
/** Consecutive stable raw detections required. Defaults to 4. */
|
|
75
|
+
stableFrames?: number;
|
|
76
|
+
/** Minimum detector confidence on the normalized 0–1 scale. Defaults to 0.72. */
|
|
77
|
+
confidenceThreshold?: number;
|
|
78
|
+
/** Maximum normalized corner movement while entering ready. Defaults to 0.024. */
|
|
79
|
+
cornerTolerance?: number;
|
|
80
|
+
/** Movement allowed after ready before dropping out. Defaults to 0.036. */
|
|
81
|
+
exitCornerTolerance?: number;
|
|
82
|
+
intervalMs?: number;
|
|
83
|
+
maxFrameDimension?: number;
|
|
84
|
+
guidance?: FinderGuidanceOptions;
|
|
85
|
+
detection?: ScanOptions;
|
|
86
|
+
playShutterSound?: boolean;
|
|
87
|
+
}
|
|
88
|
+
export interface FinderSession {
|
|
89
|
+
stop: () => void;
|
|
90
|
+
capture: () => Promise<FinderCapture>;
|
|
91
|
+
setAutoCapture: (enabled: boolean) => boolean;
|
|
92
|
+
getStatus: () => FinderStatus;
|
|
93
|
+
}
|
|
94
|
+
export declare function snapshotBeforeCaptureFeedback<T>(snapshot: () => T | Promise<T>, feedback: () => void): Promise<T>;
|
|
95
|
+
export declare function autoCaptureControl(mode: AutoCaptureMode): AutoCaptureControl;
|
|
96
|
+
export interface StabilityOptions {
|
|
97
|
+
stableFrames: number;
|
|
98
|
+
confidenceThreshold: number;
|
|
99
|
+
cornerTolerance: number;
|
|
100
|
+
exitCornerTolerance?: number;
|
|
101
|
+
}
|
|
102
|
+
export interface FinderGuidanceOptions {
|
|
103
|
+
confidenceThreshold?: number;
|
|
104
|
+
/** Limiting-axis fill required to enter ready. Defaults to 0.72. */
|
|
105
|
+
minFillRatio?: number;
|
|
106
|
+
/** Limiting-axis fill allowed after ready before exiting. Defaults to 0.64. */
|
|
107
|
+
exitFillRatio?: number;
|
|
108
|
+
/** Long-to-short side ratio that makes a document high-aspect. Defaults to 2.2. */
|
|
109
|
+
highAspectRatio?: number;
|
|
110
|
+
/** Long-side fill required to enter ready on a high-aspect document. Defaults to 0.8. */
|
|
111
|
+
highAspectFillRatio?: number;
|
|
112
|
+
/** Long-side fill allowed after ready on a high-aspect document. Defaults to 0.72. */
|
|
113
|
+
highAspectExitFillRatio?: number;
|
|
114
|
+
/** Edge clearance required to enter ready. Defaults to 0.015. */
|
|
115
|
+
edgeMarginRatio?: number;
|
|
116
|
+
/** Edge clearance allowed after ready. Defaults to 0.005. */
|
|
117
|
+
exitEdgeMarginRatio?: number;
|
|
118
|
+
/** Opposite-edge ratio required to enter ready. Defaults to 0.48. */
|
|
119
|
+
minPerspectiveRatio?: number;
|
|
120
|
+
/** Opposite-edge ratio allowed after ready. Defaults to 0.4. */
|
|
121
|
+
exitMinPerspectiveRatio?: number;
|
|
122
|
+
}
|
|
123
|
+
export declare const DEFAULT_FINDER_STABLE_FRAMES = 4;
|
|
124
|
+
export declare const DEFAULT_FINDER_CONFIDENCE_THRESHOLD = 0.72;
|
|
125
|
+
export declare const DEFAULT_FINDER_CORNER_TOLERANCE = 0.024;
|
|
126
|
+
export declare const DEFAULT_FINDER_EXIT_CORNER_TOLERANCE = 0.036;
|
|
127
|
+
export declare const DEFAULT_FINDER_MIN_FILL_RATIO = 0.72;
|
|
128
|
+
export declare const DEFAULT_FINDER_EXIT_FILL_RATIO = 0.64;
|
|
129
|
+
export declare const DEFAULT_FINDER_HIGH_ASPECT_RATIO = 2.2;
|
|
130
|
+
/** Extra long-side fill asked of a high-aspect document, above the base target. */
|
|
131
|
+
export declare const DEFAULT_FINDER_HIGH_ASPECT_FILL_MARGIN = 0.08;
|
|
132
|
+
export declare const DEFAULT_FINDER_EDGE_MARGIN_RATIO = 0.015;
|
|
133
|
+
export declare const DEFAULT_FINDER_EXIT_EDGE_MARGIN_RATIO = 0.005;
|
|
134
|
+
export declare const DEFAULT_FINDER_MIN_PERSPECTIVE_RATIO = 0.48;
|
|
135
|
+
export declare const DEFAULT_FINDER_EXIT_MIN_PERSPECTIVE_RATIO = 0.4;
|
|
136
|
+
/**
|
|
137
|
+
* Measures how much of each frame axis the raw quad occupies. A document can
|
|
138
|
+
* only grow until its limiting axis reaches a frame edge, so the larger of the
|
|
139
|
+
* two axis fills — not the bounding-box area — describes how well it is framed.
|
|
140
|
+
*/
|
|
141
|
+
export declare function measureQuadFill(quad: DocumentQuad, frameWidth: number, frameHeight: number): QuadFill;
|
|
142
|
+
/**
|
|
143
|
+
* Fill the limiting axis must reach. High-aspect documents are asked for a
|
|
144
|
+
* little more of the long side, because their short side can never fill and
|
|
145
|
+
* the long side is the only axis carrying resolution. The target never exceeds
|
|
146
|
+
* what the edge margin physically allows.
|
|
147
|
+
*/
|
|
148
|
+
export declare function limitingFillTarget(fill: QuadFill, previouslyReady: boolean, edgeMarginRatio: number, options?: FinderGuidanceOptions): number;
|
|
149
|
+
export declare function selectFinderGuidance(detection: DocumentDetectionResult, stability: {
|
|
150
|
+
ready: boolean;
|
|
151
|
+
previouslyReady?: boolean;
|
|
152
|
+
}, options?: FinderGuidanceOptions): FinderGuidance;
|
|
153
|
+
export declare class QuadStabilityTracker {
|
|
154
|
+
private previous?;
|
|
155
|
+
private count;
|
|
156
|
+
private ready;
|
|
157
|
+
private readonly options;
|
|
158
|
+
constructor(options: StabilityOptions);
|
|
159
|
+
reset(): void;
|
|
160
|
+
update(detection: DocumentDetectionResult): {
|
|
161
|
+
stableFrames: number;
|
|
162
|
+
ready: boolean;
|
|
163
|
+
movement: number;
|
|
164
|
+
};
|
|
165
|
+
}
|
|
166
|
+
export declare function startFinder(options: FinderOptions): Promise<FinderSession>;
|