@datachecker/autocapture
v4.1.1
Published
This project contains Datachecker's AutoCapture tool, that captures images of identity documents (ID/Passport/Driver license). The tool only takes a capture once a document is detected and it passes the quality control.
Downloads
227
Readme
AutoCapture
This project contains Datachecker's AutoCapture tool, that captures images of identity documents (ID/Passport/Driver license). The tool only takes a capture once a document is detected and it passes the quality control.
The tool will be run in the browser and is therefore written in JavaScript.
Trigger mechanism
The tool performs the following checks:
- Is the environment not too dark (under exposure)?
- Is there a document?
- Is the detected document not too far?
- Is the detected document not too close?
- Is the image sharp?
Prerequisites
Please visit Datachecker API documentation.
- Datachecker OAuth Token
- Datachecker SDK Token
Compatibility
The SDK requires a browser that supports at least ECMAScript 12 (ES12). It is highly recommended to use the latest version of your preferred browser to ensure compatibility and access to the latest features and security updates.
Using the latest browser versions will ensure that all modern JavaScript features required by the SDK are supported.
Steps
- Request OAUTH Token
- Put OAuth in header
- SDK configuration (add SDK token)
- Run SDK
OAuth Token
Datachecker uses OAuth authorization. In order to request the SDK token you will need to provide a valid OAuth token in the header.
Example header:
header = {'Authorization': `Bearer ${response.accessToken}`}
This OAuth token can be retrieved with the Datachecker OAuth Token API. The scope "productapi.sdk.read"
needs to be present to make use of the SDK token. If this scope is missing you will not be able to retrieve an SDK token.
Example OAuth:
fetch(<BASE_ENDPOINT>+"/oauth/token", {
method: 'POST',
body: JSON.stringify({
"clientId": <CLIENTID>,
"clientSecret": <CLIENTSECRET>,
"scopes": [
"productapi.sdk.read",
]
})
})
.then(response => response.json())
Note: Contact Datachecker for client_id and client_secret.
SDK Token
The SDK is locked. In order to use the SDK in production a token is required. The application can only be started with a valid token. This token is a base64
string. The token can be generated by calling the Datachecker SDK Token API.
Example:
fetch(<BASE_ENDPOINT>+"/sdk/token?customer_reference=<CUSTOMER>&services=AUTO_CAPTURE", {
method: 'GET',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json',
'Authorization': `Bearer <ACCESSTOKEN>`
}
})
.then(response => response.json())
Configuration
To run this tool, you will need initialise with the following variables.
| ATTRIBUTE | FORMAT | DEFAULT VALUE | EXAMPLE | NOTES |
| ------------------- | ----------------------- | --------------------------------------------------- | --------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| ALLOWED_DOCUMENTS
| object | see ALLOWED DOCUMENTS | see ALLOWED DOCUMENTS | optional Enable or disable flipping of certain documents. |
| ALWAYS_FLIP
| bool | false
| true
| optional Always perform flipping even when MRZ
scanning is false
. |
| APPROVAL
| bool | false
| false
| optional Approval screen after capture as an extra quality check. |
| ASSETS_FOLDER
| string | ""
| "../"
| optional Specifies location of locally hosted assets folder. (see Asset Fetching Configuration) |
| ASSETS_MODE
| string | "CDN"
| "LOCAL"
| optional Specifies mode of asset fetching, either through CDN or locally hosted assets. (see Asset Fetching Configuration) |
| BACKGROUND_COLOR
| string (Hex color code) | "#1d3461"
| "#1d3461"
| optional Specifies the background color using a hex color code. |
| CONTAINER_ID
| string | | "AC_mount"
| required div id to mount tool on. If the div
does not exist it will be created and placed in <body>
. |
| CROP_FACE
| bool | true
| true
| optional Enable cropping of face as output. |
| DEBUG
| bool | false
| false
| optional When debug is true
more detailed logs will be visible. |
| LANGUAGE
| string | "nl"
| "nl"
| required Notifications in specific language. |
| MRZ_SETTINGS
| object | see MRZ_SETTINGS | see MRZ_SETTINGS | optional Settings of MRZ scanning. |
| MRZ
| bool | false
| false
| optional Enable MRZ scanning. |
| onComplete
| javascript function | | function(data) {console.log(data)}
| required Callback function on complete. |
| onError
| javascript function | function(error) {console.log(error)}
| function(error) {console.log(error)}
| required Callback function on error. |
| onImage
| javascript function | function(data) {console.log(data)}
| function(data) {console.log(data)}
| optional Callback function on image. |
| onUserExit
| javascript function | function(error) {console.log(error)}
| function(error) {window.history.back()}
| required Callback function on user exit. |
| ROI_MODE
| string | "landscape-landscape"
| portrait-landscape
| optional Frame orientation options: "portrait-landscape"
, "landscape-landscape"
|
| TOKEN
| string | | see SDK Token | required Datachecker SDK token. |
Asset fetching Configuration
AutoCapture requires fetching assets, which can be done either through a CDN or by hosting them locally. Configure this in the tool settings as follows:
CDN Configuration
// configuration
{
ASSETS_MODE: "CDN",
// other configurations
}
Locally Hosting Configuration
To host assets locally, first copy them to your desired location:
cp -r dist/assets/ path/to/hosted/assets/
Then, configure the tool to use these local assets:
// configuration
{
ASSETS_MODE: "LOCAL",
ASSETS_FOLDER: "path/to/hosted/assets/",
// other configurations
}
For comphrehensive integration examples, please refer to our Integration Examples.
Version Control
To ensure compatibility:
- Separate Asset Versioning: The assets directory contains a version file, separate from the main file's version.
- Compatibility Check: The main file will perform a version check and throw an error if the versions are incompatible.
Handling callbacks
Within the application, you can take advantage of four callback functions to enhance the user experience and manage the flow of your process.
Note: When integrating the application into Native Apps using web views, it's essential to adapt and utilize these callback functions according to the conventions and requirements of the native platforms (e.g., iOS, Android). Native app development environments may have specific ways of handling JavaScript callbacks, and you should ensure seamless communication between the web view and the native code.
Example Web (JS):
let AC = new AutoCapture();
AC.init({
CONTAINER_ID: 'AC_mount',
LANGUAGE: 'en',
TOKEN: "<SDK_TOKEN>",
onComplete: function(data) {
console.log(data);
},
onImage: function(data) {
console.log(data);
},
onError: function(error) {
console.log(error)
},
onUserExit: function(error) {
console.log(error);
window.history.back();
}
});
| ATTRIBUTE | FORMAT | DEFAULT VALUE | EXAMPLE | NOTES |
| ------------- | ------------------- | -------------------------------------- | ----------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| onComplete
| javascript function | | function(data) {console.log(data)}
| required Callback that fires when all interactive tasks in the workflow have been completed. |
| onError
| javascript function | function(error) {console.log(error)}
| function(error) {console.log(error)}
| required Callback that fires when an error occurs. |
| onImage
| javascript function | function(data) {console.log(data)}
| function(data) {console.log(data)}
| optional Callback that fires when frame succesfully passes quality controls. This callback can be used when you want to process or analyze live frames. (See External-MRZ) |
| onUserExit
| javascript function | function(error) {console.log(error)}
| function(error) {window.history.back()}
| required Callback that fires when the user exits the flow without completing it. |
onComplete
This callback function will be called once all the tasks within the workflow succesfully have been completed. This callback function is required. The data
parameter within the function represents the output of the completed process. You can customize this function to handle and display the data as needed.
Example Web (JS):
Within the example below we are logging the output (data
) to console.
let AC = new AutoCapture();
AC.init({
...,
onComplete: function(data) {
console.log(data);
}
});
onImage
This callback function will be called when a frame succesfully passes quality controls. This callback can be used when you want to process or analyze live frames (See External-MRZ). This callback function is optional. The data
parameter within the function represents the frame that succesfully passed the quality controls. It's format is a base64
image string where the Data URI ("data:image/png;base64,"
) has been taken off.
Example Web (JS):
Within the example below we are logging the output (data
) to console.
let AC = new AutoCapture();
AC.init({
...,
onImage: function(data) {
// data = "iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADg....."
console.log(data)
}
});
onError
This callback can be used to alert users when something goes wrong during the process. This callback function is required. The error
parameter within the function contains information about the specific error encountered, allowing you to log or display error messages for debugging or user guidance. The errors that are thrown are either known or unknown. The known errors can be found within the Languages dictionary. On the other hand, the unknown errors will be thrown as is.
Example Web (JS):
Within the example below we are logging the output (error
) to console.
let AC = new AutoCapture();
AC.init({
...,
onError: function(error) {
console.log(error)
}
});
onUserExit
This callback can be used to implement actions like returning users to the previous page or prompting them for confirmation before exiting to ensure they don't lose any unsaved data or work. This callback function is required. The error
parameter within the function contains information about the specific error encountered, allowing you to log or display error messages for debugging or user guidance. The error that is thrown is "exit"
.
Example Web (JS):
Within the example below we are logging the output (error
) to console. Finally, we move back one page in the session history with window.history.back()
.
let AC = new AutoCapture();
AC.init({
...,
onUserExit: function(error) {
console.log(error);
window.history.back()
}
});
Usage/Examples
The tool first needs to be initialised to load all the models. Once its initialised, it will be started.
let AC = new AutoCapture();
AC.init({
CONTAINER_ID: ...,
LANGUAGE: ...,
TOKEN: ...,
onComplete: ...,
onError: ...,
onUserExit: ...
});
To stop the camera and empty the container with its contents the stop
function can be called. This function will automatically be called within onComplete
, onError
and onUserExit
thus do not have to be called within your own custom versions of these functions.
AC.stop();
If you wish to completely remove the container (identified by CONTAINER_ID
), use the remove command:
AC.remove();
Example below:
let AC = new AutoCapture();
AC.init({
CONTAINER_ID: 'AC_mount',
LANGUAGE: 'nl',
TOKEN: "<SDK_TOKEN>",
onComplete: function(data) {
console.log(data);
},
onError: function(error) {
console.log(error)
},
onUserExit: function(error) {
console.log(error);
window.history.back()
}
});
Importing SDK
Import the SDK with one of the three methods: Script tag, ES6 or CommonJS.
Script Tag
Easily add AutoCapture to your HTML files using the Script Tag method.
<!-- Add AutoCapture directly in your HTML -->
<script src="dist/autocapture.obf.js"></script>
NPM
For projects using NPM and a module bundler like Webpack or Rollup, you can import AutoCapture as an ES6 module or with CommonJS require syntax.
// Import AutoCapture in your JavaScript file
// ES6 style import
import AutoCapture from '@datachecker/autocapture';
// CommonJS style require
let AutoCapture = require('@datachecker/autocapture')
Demo
<!DOCTYPE html>
<html>
<head>
<title>AutoCapture</title>
</head>
<body>
<div id="AC_mount"></div>
</body>
<script src="autocapture.obf.js" type="text/javascript"></script>
<script>
let AC = new AutoCapture();
AC.init({
CONTAINER_ID: 'AC_mount',
LANGUAGE: 'nl',
TOKEN: "<SDK_TOKEN>",
onComplete: function (data) {
console.log(data)
},
onError: function(error) {
console.log(error)
},
onUserExit: function(error) {
console.log(error);
window.history.back()
}
});
</script>
</html>
For comphrehensive integration examples, please refer to our Integration Examples.
Languages
There are two ways in which notifications can be loaded: from file, from object (json).
File
The languages can be found in assets/language/
. The current support languages are en
and nl
. More languages could be created.
The notifications can be loaded in configuration
like the following:
let AC = new AutoCapture();
AC.init({
LANGUAGE: 'en',
...
To create support for a new language, a js file needs to be created with specific keys.
The keys can be derived from the current language js files (assets/language/en.js
).
Example:
var LANGUAGE = {
"start_prompt": "Tap to start",
"flip": "Flip the document",
"flip_frontside": "Flip the document to the frontside",
"flip_backside": "Flip the document to the backside",
"std_msg_0": "Place your document",
"exp_dark": "Environment is too dark",
"exp_bright": "Environment is too bright",
"blur": "Hold still...",
"glare": "Glare detected",
"size": "Move closer",
"focus": "Focus on document.",
"approval_prompt": "Is the image right?",
"retry": "Try again",
"confirm": "Accept",
"capture_error": "We were unable to capture an image. Camera access is required.",
"mrz_search": "Searching MRZ...",
"continue": "Continue",
"tutorial": "Follow the instructions",
"rotate_phone": "Please rotate your phone upright",
"camera_selection": "Please select the back camera"
}
Object (json)
Notifications can also be loaded as a json object like the following:
let AC = new AutoCapture();
AC.init({
LANGUAGE: JSON.stringify(
{
"start_prompt": "Tap to start",
"flip": "Flip the document",
"flip_frontside": "Flip the document to the frontside",
"flip_backside": "Flip the document to the backside",
"std_msg_0": "Place your document in the frame",
"exp_dark": "Environment is too dark",
"exp_bright": "Environment is too bright",
"blur": "Hold still...",
"glare": "Glare detected",
"size": "Move closer",
"focus": "Focus on document.",
"approval_prompt": "Is the image right?",
"retry": "Try again",
"confirm": "Accept",
"capture_error": "We were unable to capture an image. Camera access is required.",
"mrz_search": "Searching MRZ...",
"continue": "Continue",
"tutorial": "Follow the instructions",
"rotate_phone": "Please rotate your phone upright",
}
),
...
Models
The tool uses a collection of neural networks. Make sure that you host the full directory so the models can be accessed. The models path can be configured. (see Configuration)
The models are located under models/
.
Face
There is a face detector within the SDK. This detector will try to find the portrait picture on the document. If it is found, this face will be present in the output. Else, the output will not have any mention of face
. When more faces are found only one face is being returned. The face with the highest score will be returned.
Example:
{
"image": ["...base64_img"],
"face": {
"score": 0.9,
"data": "...base64_img"
},
}
Allowed documents
The ALLOWED_DOCUMENTS
setting lets you specify which documents should permit flipping and which should not. This allows you to control how the application handles different types of documents, ensuring that the capture process meets your requirements.
Example:
let AC = new AutoCapture();
AC.init({
CONTAINER_ID: 'AC_mount',
ALLOWED_DOCUMENTS: {
ID: ['FRONT', 'BACK'], // Two entries, so flipping is needed
PASSPORT: ['FRONT', 'BACK'], // Two entries, so flipping is needed
DUTCH_PASSPORT: undefined, // Dutch passport will be copied from PASSPORT settings if undefined
RESIDENCE_PERMIT: ['FRONT', 'BACK'], // Two entries, so flipping is needed
DRIVING_LICENSE: ['FRONT', 'BACK'], // Two entries, so flipping is needed
},
TOKEN: "<SDK_TOKEN>",
onComplete: function (data) {
console.log(data)
},
onError: function(error) {
console.log(error)
},
onUserExit: function (error) {
console.log(error)
window.history.back()
}
})
MRZ
The application supports Machine Readable Zone (MRZ) scanning, enabling it to extract information from documents with MRZ data using Optical Character Recognition (OCR) techniques. The internal OCR engine previously used for this purpose has been removed, so now only an external OCR engine is available for MRZ scanning. Users can integrate an external OCR engine (see External MRZ) to handle MRZ scanning.
Supported Document Types
The MRZ scanning tool supports various types of documents, including:
- TD1: Travel Document Type 1
- TD2: Travel Document Type 2
- TD3: Travel Document Type 3
- Dutch eDL: Dutch electronic driver's license
MRZ Scanning Process
The MRZ scanning process involves the following steps:
Quality Checks: MRZ scanning initiates only after passing all necessary quality checks to ensure the document's readability.
Perspective Correction: When using an external OCR, the application will crop the region of interest (ROI) from the image and rotate it based on the detected card's orientation. For more details, see External MRZ.
Retries: If the initial scan does not yield the desired results, the tool offers the option to retry scanning. In that case, the tool will try to use the next frame. Only frames that pass all the quality checks will be used. The number of retries can be configured using the following setting: (see Configuration).
MRZ_RETRIES: 5
To enable infinite retries, use:
MRZ_RETRIES: -1
Enabling MRZ Scanning
To enable MRZ scanning use MRZ: true
(see Configuration).
MRZ Configuration (MRZ_SETTINGS)
| ATTRIBUTE | FORMAT | DEFAULT VALUE | EXAMPLE | NOTES |
| ----------------- | ---------- | ----------------- | ----------- | ---------------------------------------------------------------------------------------------------------------------------- |
| MIN_VALID_SCORE
| int | 50
| 50
| optional Minimum valid score of MRZ. The valid score is a range between 0-100 indicating how valid the found MRZ is. |
| MRZ_RETRIES
| int | 5
| 5
| optional Amount of retries for MRZ scanning before continuing. Use -1
for infinite retries. |
Example:
let AC = new AutoCapture();
AC.init({
CONTAINER_ID: 'AC_mount',
MRZ: true,
MRZ_SETTINGS: {
MRZ_RETRIES: -1,
MIN_VALID_SCORE: 90,
},
TOKEN: "<SDK_TOKEN>",
onComplete: function (data) {
console.log(data)
},
onError: function(error) {
console.log(error)
},
onUserExit: function (error) {
console.log(error)
window.history.back()
}
})
Example output:
{
"angle": "...",
"type": "PASSPORT",
"subtype": "<",
"country": "NLD",
"lname": "DE",
"lname2": "BRUIJN",
"spacing": "",
"fname": "WILLEKE",
"mname1": "LISELOTTE",
"name_complement": "",
"number": "SPECI2014",
"check_digit_document_number": "2",
"nationality": "NLD",
"date_of_birth": "1965-03-10",
"check_digit_date_of_birth": "1",
"sex": "F",
"expiration_date": "2024-03-09",
"check_digit_expiration_date": "6",
"complement": "999999990<<<<<84",
"mrz_type": "td3",
"raw_mrz": [
"P<NLDDE<BRUIJN<<WILLEKE<LISELOTTE<<<<<<<<<<<",
"SPECI20142NLD6503101F2403096999999990<<<<<84"
],
"check_digit_composite": "4",
"personal_number": "999999990",
"check_digit_personal_number": "8",
"valid_number": true,
"valid_date_of_birth": true,
"valid_expiration_date": true,
"valid_personal_number": true,
"valid_composite": true,
"valid_misc": true,
"valid_score": 100,
"misc": "P",
"names": "WILLEKE LISELOTTE",
"surname": "DE BRUIJN"
}
External MRZ
It is possible to retrieve the MRZ externally and then send it to the SDK using the following approach:
Using the onImage Callback
Utilize the
onImage
callback to trigger an external process with the current frames.If this external process successfully retrieves the MRZ information, it can then be sent to the SDK using the
parse_mrz
function.The
parse_mrz
function expects the MRZ lines as an argument, provided as a single string with line breaks ("\n"
) separating the lines.
Here's an example of how this can be implemented in JavaScript:
// Initialize the AutoCapture SDK
let AC = new AutoCapture();
AC.init({
// ... other configurations
onImage: function (data) {
// External OCR process retrieves MRZ information
let MRZ_text = EXTERNAL_OCR_FUNCTION(data); // Example MRZ text: "P<NLDDE<BRUIJN<<WILLEKE<LISELOTTE<<<<<<<<<<<\nSPECI20142NLD6503101F2403096999999990<<<<<84"
// Send the retrieved MRZ information to the SDK for parsing
AC.parse_mrz(MRZ_text);
},
});
Output
The SDK will output in the following structure:
{
"image": ["...base64_img"],
"face": {
"score": "...",
"data": "...base64_img"
},
"meta": [
{
"angle": "...",
"coordinates": [
["...", "..."],
["...", "..."],
["...", "..."],
["...", "..."]
]
}
],
"token": "sdk_token"
}
With MRZ:
{
"image": ["...base64_img"],
"face": {
"score": "...",
"data": "...base64_img"
},
"meta": [
{
"angle": "...",
"coordinates": [
["...", "..."],
["...", "..."],
["...", "..."],
["...", "..."]
]
}
],
"mrz": {
"angle": "...",
"type": "...",
"subtype": "...",
"country": "...",
"lname": "...",
"lname2": "...",
"spacing": "...",
"fname": "...",
"mname1": "...",
"name_complement": "...",
"number": "...",
"check_digit_document_number": "...",
"nationality": "...",
"date_of_birth": "...",
"check_digit_date_of_birth": "...",
"sex": "...",
"expiration_date": "...",
"check_digit_expiration_date": "...",
"complement": "...",
"mrz_type": "...",
"raw_mrz": [
"...",
"..."
],
"check_digit_composite": "...",
"personal_number": "...",
"check_digit_personal_number": "...",
"valid_number": true,
"valid_date_of_birth": true,
"valid_expiration_date": true,
"valid_personal_number": true,
"valid_composite": true,
"valid_misc": true,
"valid_score": true,
"misc": "...",
"names": "...",
"surname": "..."
},
"token": "sdk_token"
}
Example:
{
"image": ["iVBORw0KGgoAAAANSUhEUgAAAysAAAS..."],
"face": {
"score": 0.9,
"data": "iVBORw0KGgoAAAANSUhEUgAAAysAAAS..."
},
"meta": [
{
"angle": 0,
"coordinates": [
[0, 0],
[0, 100],
[150, 100],
[150, 0]
]
}
],
"mrz": {
"angle": "...",
"type": "PASSPORT",
"subtype": "<",
"country": "NLD",
"lname": "DE",
"lname2": "BRUIJN",
"spacing": "",
"fname": "WILLEKE",
"mname1": "LISELOTTE",
"name_complement": "",
"number": "SPECI2014",
"check_digit_document_number": "2",
"nationality": "NLD",
"date_of_birth": "1965-03-10",
"check_digit_date_of_birth": "1",
"sex": "F",
"expiration_date": "2024-03-09",
"check_digit_expiration_date": "6",
"complement": "999999990<<<<<84",
"mrz_type": "td3",
"raw_mrz": [
"P<NLDDE<BRUIJN<<WILLEKE<LISELOTTE<<<<<<<<<<<",
"SPECI20142NLD6503101F2403096999999990<<<<<84"
],
"check_digit_composite": "4",
"personal_number": "999999990",
"check_digit_personal_number": "8",
"valid_number": true,
"valid_date_of_birth": true,
"valid_expiration_date": true,
"valid_personal_number": true,
"valid_composite": true,
"valid_misc": true,
"valid_score": 100,
"misc": "P",
"names": "WILLEKE LISELOTTE",
"surname": "DE BRUIJN"
},
"token": "sdk_token"
}