In the digital era, extracting information from documents and vehicle IDs has become increasingly important for web-based applications. Machine Readable Zones (MRZ) and Vehicle Identification Numbers (VIN) can now be scanned directly in the browser using modern web technologies. In this tutorial, you'll learn how to build a web-based MRZ and VIN scanner using JavaScript, HTML5 and Dynamsoft Capture Vision SDK.
Web MRZ/VIN Scanner Demo Video
Prerequisites
- Free Trial License for Dynamsoft Capture Vision.
Online Demo
Installation
The dynamsoft-capture-vision-bundle is the JavaScript version of Dynamsoft Capture Vision, available via npm or CDN. To use it, include the library in your index.html
:
<script src="https://cdn.jsdelivr.net/npm/dynamsoft-capture-vision-bundle@2.6.1000/dist/dcv.bundle.min.js"></script>
Setting Up the HTML Structure
The target HTML layout for the MRZ/VIN scanner consists of three main sections:
-
A
div
element for license key setup, input source selection (File or Camera), and scanning mode toggle (MRZ or VIN).
<div class="container"> <div class="row"> <div> <label> Get a License key from <a href="https://www.dynamsoft.com/customer/license/trialLicense/?product=dcv&package=cross-platform" target="_blank">here</a> </label> <input type="text" id="license_key" value="LICENSE-KEY" placeholder="LICENSE-KEY"> <button onclick="activate()">Activate SDK</button> </div> </div> <div class="row"> <div> <select onchange="selectChanged()" id="dropdown"> <option value="file">File</option> <option value="camera">Camera</option> </select> <form id="modeSelector"> <label> <input type="radio" name="scanMode" value="mrz" checked> MRZ </label> <label> <input type="radio" name="scanMode" value="vin"> VIN </label> </form> </div> </div> </div>
-
A
div
for displaying the uploaded image and its scanning result.
<div class="container" id="file_container"> <div> <input type="file" id="pick_file" accept="image/*" /> </div> <div class="row"> <div class="imageview"> <img id="image_file" src="default.png" /> <canvas id="overlay_canvas" class="overlay"></canvas> </div> </div> <div class="row"> <div> <textarea id="detection_result"></textarea> </div> </div> </div>
-
A
dev
for showing the live camera stream along with real-time scanning results.
<div class="container" id="camera_container"> <div> <select onchange="cameraChanged()" id="camera_source"> </select> <button onclick="scan()" id="scan_button">Start</button> <div id="videoview"> <div id="camera_view"></div> </div> <div class="row"> <div> <textarea id="scan_result"></textarea> </div> </div> </div> </div>
Initializing MRZ and VIN Recognition Engines
The recognition engines for MRZ and VIN are initialized in the activate()
function, which is triggered when the user clicks the Activate SDK button. This function sets up the license key, loads the required models and code parsers, and registers result receivers for both MRZ and VIN.
async function activate() {
toggleLoading(true);
let divElement = document.getElementById("license_key");
let licenseKey = divElement.value == "" ? divElement.placeholder : divElement.value;
try {
await Dynamsoft.License.LicenseManager.initLicense(
licenseKey,
true
);
Dynamsoft.Core.CoreModule.loadWasm(["DLR"]);
parser = await Dynamsoft.DCP.CodeParser.createInstance();
// Load VIN and MRZ models
await Dynamsoft.DCP.CodeParserModule.loadSpec("VIN");
await Dynamsoft.DLR.LabelRecognizerModule.loadRecognitionData("VIN");
await Dynamsoft.DCP.CodeParserModule.loadSpec("MRTD_TD1_ID");
await Dynamsoft.DCP.CodeParserModule.loadSpec("MRTD_TD2_FRENCH_ID");
await Dynamsoft.DCP.CodeParserModule.loadSpec("MRTD_TD2_ID");
await Dynamsoft.DCP.CodeParserModule.loadSpec("MRTD_TD2_VISA");
await Dynamsoft.DCP.CodeParserModule.loadSpec("MRTD_TD3_PASSPORT");
await Dynamsoft.DCP.CodeParserModule.loadSpec("MRTD_TD3_VISA");
await Dynamsoft.DLR.LabelRecognizerModule.loadRecognitionData("MRZ");
mrzRouter = await Dynamsoft.CVR.CaptureVisionRouter.createInstance();
await mrzRouter.initSettings("./mrz.json");
mrzRouter.addResultReceiver({
onCapturedResultReceived: (result) => {
// TODO: Handle MRZ result
},
});
vinRouter = await Dynamsoft.CVR.CaptureVisionRouter.createInstance();
await vinRouter.initSettings("./vin.json");
vinRouter.addResultReceiver({
onCapturedResultReceived: (result) => {
// TODO: Handle MRZ result
},
});
isSDKReady = true;
}
catch (ex) {
console.error(ex);
}
toggleLoading(false);
}
Accessing the Camera
Dynamsoft Capture Vision SDK provides the CameraEnhancer
and CameraView
classes for managing camera access and display. CameraEnhancer
wraps the getUserMedia()
method, while CameraView
class adds a live video view to the DOM.
async function openCamera(cameraEnhancer, cameraInfo) {
if (!Dynamsoft) return;
try {
await cameraEnhancer.selectCamera(cameraInfo);
cameraEnhancer.on("played", function () {
resolution = cameraEnhancer.getResolution();
});
await cameraEnhancer.open();
}
catch (ex) {
console.error(ex);
}
}
async function closeCamera(cameraEnhancer) {
if (!Dynamsoft) return;
try {
await cameraEnhancer.close();
}
catch (ex) {
console.error(ex);
}
}
async function setResolution(cameraEnhancer, width, height) {
if (!Dynamsoft) return;
try {
await cameraEnhancer.setResolution(width, height);
}
catch (ex) {
console.error(ex);
}
}
async function initCamera() {
if (!Dynamsoft) return;
try {
cameraView = await Dynamsoft.DCE.CameraView.createInstance();
cameraEnhancer = await Dynamsoft.DCE.CameraEnhancer.createInstance(cameraView);
let scanRegion = {
x: 10,
y: 30,
width: 80,
height: 40,
isMeasuredInPercentage: true
};
cameraEnhancer.setScanRegion(scanRegion);
cameras = await cameraEnhancer.getAllCameras();
if (cameras != null && cameras.length > 0) {
for (let i = 0; i < cameras.length; i++) {
let option = document.createElement("option");
option.text = cameras[i].label;
cameraSource.add(option);
}
try {
let uiElement = document.getElementById("camera_view");
uiElement.append(cameraView.getUIElement());
cameraView.getUIElement().shadowRoot?.querySelector('.dce-sel-camera')?.setAttribute('style', 'display: none');
cameraView.getUIElement().shadowRoot?.querySelector('.dce-sel-resolution')?.setAttribute('style', 'display: none');
}
catch (ex) {
console.error(ex);
}
}
else {
alert("No camera found.");
}
}
catch (ex) {
console.error(ex);
}
}
async function cameraChanged() {
if (cameras != null && cameras.length > 0) {
let index = cameraSource.selectedIndex;
await openCamera(cameraEnhancer, cameras[index]);
}
}
Implementing the MRZ/VIN Scanner Logic
To recognize MRZ and VIN from images or camera streams, use the capture()
and startCapturing()
methods respectively. The capture()
method returns the recognition results directly, while the startCapturing()
method starts a continuous capturing process and returns the results through the onCapturedResultReceived
callback.
-
Recognizing MRZ/VIN from Image Files
function loadImage2Canvas(base64Image) { imageFile.src = base64Image; img.src = base64Image; img.onload = function () { let width = img.width; let height = img.height; overlayCanvas.width = width; overlayCanvas.height = height; if (!isSDKReady) { alert("Please activate the SDK first."); return; } toggleLoading(true); let selectedMode = document.querySelector('input[name="scanMode"]:checked').value; let context = overlayCanvas.getContext('2d'); context.clearRect(0, 0, overlayCanvas.width, overlayCanvas.height); try { if (selectedMode == "mrz") { mrzRouter.capture(img.src, "ReadMRZ").then((result) => { showFileResult(selectedMode, context, result); }); } else if (selectedMode == "vin") { vinRouter.capture(img.src, "ReadVINText").then((result) => { showFileResult(selectedMode, context, result); }); } } catch (ex) { console.error(ex); } toggleLoading(false); }; }
-
Recognizing MRZ/VIN from Camera Stream
async function scan() { if (!isSDKReady) { alert("Please activate the SDK first."); return; } let selectedMode = document.querySelector('input[name="scanMode"]:checked').value; if (!isDetecting) { scanButton.innerHTML = "Stop"; isDetecting = true; if (selectedMode == "mrz") { mrzRouter.setInput(cameraEnhancer); mrzRouter.startCapturing("ReadMRZ"); } else if (selectedMode == "vin") { vinRouter.setInput(cameraEnhancer); vinRouter.startCapturing("ReadVINText"); } } else { scanButton.innerHTML = "Scan"; isDetecting = false; if (selectedMode == "mrz") { mrzRouter.stopCapturing(); } else if (selectedMode == "vin") { vinRouter.stopCapturing(); } cameraView.clearAllInnerDrawingItems(); } }
Drawing Overlays and Results
You can draw overlays on the image or video stream to highlight recognized areas and display the parsed results in a text area.
-
On Image Files
async function showFileResult(selectedMode, context, result) { let parseResults = ''; let detection_result = document.getElementById('detection_result'); detection_result.innerHTML = ""; let txts = []; let items = result.items; if (items.length > 0) { for (var i = 0; i < items.length; ++i) { if (items[i].type !== Dynamsoft.Core.EnumCapturedResultItemType.CRIT_TEXT_LINE) { continue; } let item = items[i]; parseResults = await parser.parse(item.text); txts.push(item.text); localization = item.location; context.strokeStyle = '#ff0000'; context.lineWidth = 2; let points = localization.points; context.beginPath(); context.moveTo(points[0].x, points[0].y); context.lineTo(points[1].x, points[1].y); context.lineTo(points[2].x, points[2].y); context.lineTo(points[3].x, points[3].y); context.closePath(); context.stroke(); } } if (txts.length > 0) { detection_result.innerHTML += txts.join('\n') + '\n\n'; if (selectedMode == "mrz") { detection_result.innerHTML += JSON.stringify(extractMrzInfo(parseResults)); } else if (selectedMode == "vin") { detection_result.innerHTML += JSON.stringify(extractVinInfo(parseResults)); } } else { detection_result.innerHTML += "Recognition Failed\n"; } }
-
On Camera Stream
async function showCameraResult(result) { let selectedMode = document.querySelector('input[name="scanMode"]:checked').value; let items = result.items; let scan_result = document.getElementById('scan_result'); if (items != null && items.length > 0) { let item = items[0]; let parseResults = await parser.parse(item.text); if (selectedMode == "mrz") { scan_result.innerHTML = JSON.stringify(extractMrzInfo(parseResults)); } else if (selectedMode == "vin") { scan_result.innerHTML = JSON.stringify(extractVinInfo(parseResults)); } } }
Running the Web MRZ/VIN Scanner
- Open your terminal and navigate to the project directory.
-
Start a local server using Python:
python -m http.server 8000
-
Open your web browser and navigate to
http://localhost:8000
.
Source Code
https://github.com/yushulx/javascript-barcode-qr-code-scanner/tree/main/examples/mrz-vin-scanner