realtime-capture-nid
v0.1.3
Published
Real-time national ID card detection and auto-capture using YOLO ONNX in the browser. Provides a drop-in React component and low-level utilities for edge detection, perspective correction, and quality scoring.
Maintainers
Readme
realtime-capture-nid
Real-time national ID card detection and auto-capture in the browser, powered by YOLO ONNX inference via onnxruntime-web.
Provides a drop-in React component (<NidCapture />) and low-level utilities for edge detection, perspective correction, and quality scoring.
Features
- Browser-only — runs entirely client-side using WebAssembly (no server needed)
- YOLO ONNX inference for real-time ID card detection
- Edge refinement — Sobel gradients + RANSAC line fitting to find precise card corners
- Perspective unwarp — homography-based rectification produces a clean rectangular image
- Quality gating — blur, glare, brightness, tilt, skew, and motion checks with automatic countdown capture
- Fully configurable — custom model URL, locale strings, thresholds, and callbacks
Installation
npm install realtime-capture-nid onnxruntime-web
# or
pnpm add realtime-capture-nid onnxruntime-web
# or
yarn add realtime-capture-nid onnxruntime-webQuick Start
1. Place your ONNX model
Put your YOLO ONNX model file in your app's public/weights/ directory (or any path accessible via URL):
public/
weights/
yolo26n.onnx2. Configure Next.js
onnxruntime-web requires WASM files to be served and proper webpack/header config. Here's a full next.config.ts:
import type { NextConfig } from "next";
import { copyFileSync, mkdirSync, existsSync } from "fs";
import { join, dirname } from "path";
// Copy WASM files from onnxruntime-web to public/ so the browser can load them
function copyWasmFiles() {
const wasmDir = join(process.cwd(), "node_modules", "onnxruntime-web", "dist");
const publicDir = join(process.cwd(), "public");
if (!existsSync(wasmDir)) return;
const wasmFiles = [
"ort-wasm-simd-threaded.wasm",
"ort-wasm-simd.wasm",
"ort-wasm.wasm",
"ort-wasm-threaded.wasm",
];
for (const file of wasmFiles) {
const src = join(wasmDir, file);
const dest = join(publicDir, file);
if (existsSync(src) && !existsSync(dest)) {
mkdirSync(dirname(dest), { recursive: true });
copyFileSync(src, dest);
}
}
}
copyWasmFiles();
const nextConfig: NextConfig = {
transpilePackages: ["onnxruntime-web"],
webpack: (config, { isServer }) => {
if (!isServer) {
config.resolve.fallback = {
...config.resolve.fallback,
fs: false,
path: false,
crypto: false,
};
}
return config;
},
// Required only if you want multi-threaded WASM (wasmNumThreads > 1)
async headers() {
return [
{
source: "/(.*)",
headers: [
{ key: "Cross-Origin-Opener-Policy", value: "same-origin" },
{ key: "Cross-Origin-Embedder-Policy", value: "require-corp" },
],
},
];
},
};
export default nextConfig;3. Import the styles and use the component
"use client"; // if using Next.js App Router
import { NidCapture } from "realtime-capture-nid";
import "realtime-capture-nid/styles.css";
export default function CaptureIdPage() {
return (
<NidCapture
modelUrl="/weights/yolo26n.onnx"
onCapture={(result) => {
console.log("Captured!", result.imageDataUrl);
console.log("Quality:", result.quality.score);
// Upload result.imageDataUrl to your server, etc.
}}
/>
);
}Note: The CSS import (
realtime-capture-nid/styles.css) includes all styles the component needs. You can import it in your root layout instead if you prefer.
Props
| Prop | Type | Default | Description |
|------|------|---------|-------------|
| modelUrl | string | "/weights/yolo26n.onnx" | URL to the ONNX model file |
| onCapture | (result: CaptureResult) => void | — | Called when a card is successfully captured |
| defaultConfidenceThreshold | number | 0.25 | Initial YOLO confidence threshold (0–1) |
| maxOutputSide | number | 1280 | Max dimension (px) of the output JPEG |
| jpegQuality | number | 0.92 | JPEG quality (0–1) |
| locale | Partial<NidCaptureLocale> | Khmer defaults | Override any user-facing string |
| className | string | — | Additional CSS class on the root container |
| showHeader | boolean | true | Show the title and subtitle |
| showConfidenceSlider | boolean | true | Show the confidence threshold slider |
| showDetections | boolean | true | Show the detection list |
| wasmNumThreads | number | 1 | WASM threads (1 = works everywhere; >1 requires COOP/COEP headers) |
CaptureResult
type CaptureResult = {
imageDataUrl: string; // JPEG data URL of the rectified card
quality: QualityResult; // detailed quality metrics
timestamp: Date;
};Locale / i18n
Override any string by passing a partial locale object. The defaults are in Khmer:
<NidCapture
locale={{
title: "ID Card Scanner",
subtitle: "Point your camera at the ID card.",
openCamera: "Open Camera",
closeCamera: "Close Camera",
screenshot: "Screenshot",
searchingCard: "Searching for ID card...",
capturedDone: "Captured!",
// ... see NidCaptureLocale type for all keys
}}
/>Low-Level Utilities
If you want to build your own UI, you can import the processing utilities directly:
import {
// YOLO pre/post-processing
preprocessImage,
processOutput,
setModelInputSize,
getModelInputSize,
// Edge detection & quad refinement
refineToQuad,
quadBBox,
quadDimensions,
// Perspective correction
unwarpQuad,
// Quality assessment
evaluateQuality,
// Constants
AUTO_CAPTURE_THRESHOLD,
CLASS_NAMES,
} from "realtime-capture-nid";Example: Custom detection pipeline
import {
preprocessImage,
processOutput,
setModelInputSize,
refineToQuad,
evaluateQuality,
unwarpQuad,
} from "realtime-capture-nid";
// Set model input size (default 640)
setModelInputSize(640);
// Preprocess a video frame for YOLO
const { tensor, scale, offsetX, offsetY } = preprocessImage(videoElement);
// Run inference with onnxruntime-web (you manage the session)
const results = await session.run({ images: inputTensor });
const output = results[session.outputNames[0]];
// Post-process detections
const detections = processOutput(output, videoWidth, videoHeight, scale, offsetX, offsetY, 0.25);
// Refine the best detection to a precise quad
const { quad, refined } = refineToQuad(videoElement, {
x: det.x, y: det.y, width: det.width, height: det.height,
});
// Check quality
const quality = evaluateQuality(videoElement, quad, bbox, prevBBox);
// Unwarp to a clean rectangular image
if (quality.ok) {
const canvas = unwarpQuad(videoElement, quad, { maxSide: 1280 });
const dataUrl = canvas?.toDataURL("image/jpeg", 0.92);
}Types
All TypeScript types are exported:
import type {
Detection,
Point,
Quad,
BBox,
QualityIssue,
QualityResult,
CaptureResult,
NidCaptureProps,
NidCaptureLocale,
} from "realtime-capture-nid";Requirements
- React >= 18
- onnxruntime-web >= 1.17
- Tailwind CSS (for the
<NidCapture />component styles) - A YOLO ONNX model trained to detect
nationalidclass - Browser with WebAssembly support
Development
# Install dependencies
pnpm install
# Run the demo app (Next.js)
pnpm dev
# Build the library
pnpm build
# Type-check
pnpm typecheckLicense
MIT
