ocr-photo-v2
v0.4.0
Published
Document scanning component for Angular. Scan different types of ID documentation to obtain the following outputs: - Fake documentation validation - Data extraction - Photo capture
Downloads
25
Readme
OcrPhoto by TalentoMOBILE
Document scanning component for Angular. Scan different types of ID documentation to obtain the following outputs:
- Fake documentation validation
- Data extraction
- Photo capture
To contact TalentoMobile: [email protected]
INSTALATION
npm install ocr-photo-V2
COMPONENT INPUTS
| INPUT | | |---|---| | [env] | Setting the environment. dev, pre, pro as parameters. | | [token] | Validation token. | | [docType] | Type of document to be analyze. |
COMPONENT OUTPUTS
| INPUT | | |---|---| | (photoTaken) | Receive the auto photo taken | | (photoMessage) | Receive the message to guide the user fot the automatic photo taken | | (error)| Receive any possible error. |
MESSAGE TYPES
SHOW_DOCUMENT HOLD_DOCUMENT CENTER_DOCUMENT BRING_DOCUMENT_CLOSER
ERROR TYPES
DEVICES_NOT_FOUND_ERROR TRACK_START_ERROR CONSTRAINT_NOT_SATISFIED_ERROR PERMISSION_DENIED_ERROR TYPE_ERROR GENERIC_ERROR
METHODS ACCESSIBLE FROM TS
| OUTPUT | | input | output |
|---|---|---|---|
| takeframe() | Capture Photo manually | | Blob image |
| sendFront() | Send the front capture to be analyze. | Blob image | FRONT_RESPONSE |
| sendBackPhoto() | Send the back capture to be analyze. | Blob image | BACK_RESPONSE |
| stopVideo() | Close the camera ones the whole capture have finished | |
| reset() | Refresh teh photo taken to take a new photo | |
| compressImg() | Compress the image file | Blob image | Blob image |
FRONT_RESPONSE
This response will be optimize and resented in the Back response as a global response for the whole process
{
"data":{
"country": {
"value": "string", "confidence": 0
},
"date_of_birth": {
"value": "string", "confidence": 0
},
"doc_expire_date": {
"value": "string", "confidence": 0
},
"doc_number": {
"value": "string", "confidence": 0
},
"doc_type": {
"value": "string", "confidence": 0
},
"name": {
"value": "string", "confidence": 0
},
"nationality":{
"value": "string", "confidence": 0
},
"opt_cli": {
"value": "string", "confidence": 0
},
"opt_laser_grab": {
"value": "string", "confidence": 0
},
"opt_number_can": {
"value": "string", "confidence": 0
},
"opt_support_number": {
"value": "string", "confidence": 0
},
"sex": {
"value": "string", "confidence": 0
},
"surname1": {
"value": "string", "confidence": 0
},
"surname2": {
"value": "string", "confidence": 0
}
},
"docType": "string",
"frontVal":{
"document_aspect_ratio": "boolean",
"document_aspect_ratio_value": "number",
"document_backwhite_check": "boolean",
"document_typography": "boolean"
},
"uuid": "string"
}
BACK_RESPONSE
{
"result":
{
"doc_type": {
"value": "string",
"confidence": 0
},
"doc_number": {
"value": "string",
"confidence": 0
},
"personal_number": {
"value": "string",
"confidence": 0
},
"doc_expire_date": {
"value": "string",
"confidence": 0
},
"issue_date": {
"value": "string",
"confidence": 0
},
"surname1": {
"value": "string",
"confidence": 0
},
"surname2": {
"value": "string",
"confidence": 0
},
"name": {
"value": "string",
"confidence": 0
},
"sex": {
"value": "string",
"confidence": 0
},
"date_of_birth": {
"value": "string",
"confidence": 0
},
"nationality": {
"value": "string",
"confidence": 0
},
"nationality_code": {
"value": "string",
"confidence": 0
},
"country_expedition": {
"value": "string",
"confidence": 0
},
"country": {
"value": "string",
"confidence": 0
},
"address": {
"value": "string",
"confidence": 0
},
"mrz": {
"value": "string",
"confidence": 0
},
"mrz_data": {
"doc_type": "string",
"serial_number": "string",
"nation_code": "string",
"reserve_number": "string",
"date_of_birth": "string",
"doc_expire_date": "string",
"sex": "string",
"nationality_code": "string",
"name": "string",
"surname1": "string",
"surname2": "string",
"checkdigits": {}
},
"validationChecks": {
"sidesMatch": "boolean", // ambos lados pertenecen al mismo documento
"validMRZ": "boolean", // Validación algorítmica del mrz
"expired": "boolean", // valida si los documentos están vencidos
"legalAge": "boolean", // valida si el titular del documento es mayor de edad por ley
"quality": "boolean", // valida que los campos hayan sido capturados. 3 campos vacios o mas y la fallta del doc_number y mrz deverán un false.
"front":{
"face_photo": "boolean", // Verifica que hay foto de la cara en el documento
"document_aspect_ratio" : "boolena", // Verifica la proporción de las mediciones (alt x anch)
"document_aspect_ratio_value": "number", // Valor de las proporciones (alt x anch)
"document_backwhite_check": "boolean" // Verifica que el documento esta en blanco y negro
},
"back":{
"char_distance" : "boolean", // Verifica la distancia entre caracteres
"char_distance_value" : "number", // Valor de distancia entre caracteres
"document_aspect_ratio": "boolean", // Verifica la proporción de las mediciones (alt x anch)
"document_aspect_ratio_value": "number", // Valor de las proporciones (alt x anch)
"document_backwhite_check": "boolean", // Verifica que el documento esta en blanco y negro
"mrz_aspect_ratio": "boolean", // Verifica la proporción de las mediciones (alt x anch) del mrz
"mrz_aspect_ratio_value": "number", // Valor de las proporciones (alt x anch) del mrz
"mrz_characters_count_value": "number", // Cantidad de caracteres encontrados en el mrz
"mrz_typography": "number" // Porcentaje de valided en similitud con la fuente ocrb
}
},
"opt_data": {},
"global_confidence": "boolean" // Media de todos los confidence obtenidos
}
},
"error":{
"code": "string",
"message": "string"
}
}
USAGE EXAMPLE
app.module.ts (Import in your app module)
import {BrowserModule} from '@angular/platform-browser';
import {NgModule} from '@angular/core';
import {AppComponent} from './app.component';
import {OcrPhotoModule} from 'ocr-photo';
@NgModule({
declarations: [AppComponent],
imports: [BrowserModule],
providers: [OcrPhotoModule],
bootstrap: [AppComponent]
})
export class AppModule {}
app.component.ts
import { ChangeDetectorRef, Component, OnInit } from '@angular/core';
import { DomSanitizer } from '@angular/platform-browser';
import { CaptureFrameService } from 'ocr-photo';
enum MessageType {
SHOW_DOCUMENT = 'Muestra tu documento de identidad',
HOLD_DOCUMENT = 'No muevas el documento',
CENTER_DOCUMENT = 'Centra el documento entre los márgenes',
BRING_DOCUMENT_CLOSER = 'Acerca el documento ajustándolo a los márgenes'
}
enum ErrorOcr {
DEVICES_NOT_FOUND_ERROR = '',
TRACK_START_ERROR = '',
CONSTRAINT_NOT_SATISFIED_ERROR = '',
PERMISSION_DENIED_ERROR = '',
TYPE_ERROR = '',
GENERIC_ERROR = '',
}
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.scss']
})
export class AppComponent implements OnInit {
public imageTaken: string;
public imageToShow
public processID : string;
public frontFinish = false;
public loading = false;
public error = false; // Show error
public photoMessage = '';
constructor(
private captureFrameService:CaptureFrameService,
private cdRef:ChangeDetectorRef,
private sanitizer: DomSanitizer){}
ngOnInit() {
return null;
}
// TAKE A PHOTO MANUALY AND RETRIVE THE CAPTURE IN BASE64. (we save the photo in a variable )
async flashFoto(){
this.imageTaken = await this.captureFrameService.takeFrame() as any
let objectURL = URL.createObjectURL(this.imageTaken);
this.imageToShow = this.sanitizer.bypassSecurityTrustUrl(objectURL);
console.log(this.imageTaken);
}
// SENDING THE PHOTO (FRONT) TO BE ANALYSE. The response can be the end response in case is a ONE-SIDE-DOCUMENT otherwise we will capture the uuid to continue with
sendFrontPhoto(){
this.loading = true;
this.cdRef.detectChanges();
this.captureFrameService.sendFront(this.imageTaken).then((res:any) => {
console.log(res);
if (res.uuid) this.processID = res.uuid
this.frontFinish = true;
this.imageTaken = '';
this.loading = false;
this.error = false;
this.cdRef.detectChanges();
}).catch((err)=>{
this.loading = false;
this.error = err;
this.imageTaken = '';
this.cdRef.detectChanges();
console.warn(err)
})
}
// SENDING THE PHOTO (BACK) TO BE ANALYSE. OBTAINING THE END RESPONSE WITH THE RESULTS
sendBackPhoto(){
this.loading = true;
this.cdRef.detectChanges();
this.captureFrameService.sendBack(this.imageTaken, this.processID).then((res) => {
console.log(res);
this.loading = false;
this.error = false;
this.cdRef.detectChanges();
}).catch((err)=> {
this.loading = false;
this.error = err;
this.imageTaken = '';
this.cdRef.detectChanges();
console.warn(err)
})
}
// CAPTURE THE PHOTO FROM EVENT
photoTaken(event){
console.log('CAPTURING PHOTO', event);
this.imageTaken = event;
let objectURL = URL.createObjectURL(this.imageTaken);
this.imageToShow = this.sanitizer.bypassSecurityTrustUrl(objectURL);
this.cdRef.detectChanges();
}
handleError(error){
console.log('ERROR');
console.log(error);
}
//CAPTURE THE MESSAGE FROM EVENT
public changeMessage(event){
this.photoMessage = MessageType[event];;
this.cdRef.detectChanges();
}
// IMAGE RESIDE BLOB OUTPUT
async onChangeFile(event){
const file = event.target.files[0];
const newBlobImg = await new Promise((resolve, reject) => {
let reader = new FileReader();
reader.onload = async (e:any) => {
let blob = new Blob([new Uint8Array(e.target.result)], {type: file.type });
const newBlob = await this.captureFrameService.compressImg(blob)
resolve(newBlob)
};
reader.readAsArrayBuffer(file);
})
console.log(newBlobImg);
}
}
app.component.html
<div class="main_container">
<div>{{frontFinish ? 'Mostrar trasera' : 'Mostrar delantera'}}</div>
<div class="video_box">
<!-- // FOR THE COMPONENT TO WORK ALL THE THREE PARAMETERS MUST BE PASSED CORRECTLY -->
<tm-ocr-photo
#taken
[env]="'local'"
(photoTaken)="photoTaken($event)"
(photoMessage)="changeMessage($event)"
[token]="'c23...'"
(error)="handleError($event)"
[docType]="'ESP_DNI3'">
</tm-ocr-photo>
</div>
<div>{{photoMessage}}</div>
<button (click)="flashFoto()" style="margin-bottom:20px">Capturar imagen</button>
<div *ngIf="imageTaken" class="photoShow">
<img [src]="imageToShow" alt="">
<button *ngIf="!frontFinish" (click)="taken.reset(); sendFrontPhoto()">Confirmar Front</button>
<button *ngIf="frontFinish" (click)="sendBackPhoto()">Confirmar Back</button>
</div>
<div>{{error ? 'Calidad insuficiente' + error : ''}}</div>
</div>
<div *ngIf="loading" class="spinner_box">
<div class="lds-ring"><div></div><div></div><div></div><div></div></div>
</div>
app.component.scss
.main_container{
margin: 40px;
display: flex;
flex-direction: column;
align-items: center;
.video_box{
width: 350px;
height: auto;
}
.photoShow{
width: 250px;
height: 250px;
display: flex;
flex-direction: column;
align-items: center;
img{
width: 100%;
}
}
}
button{
margin-top: 20px;
cursor: pointer;
border-radius: 12px;
font-size: 14px;
font-weight: 500;
line-height: 25px;
letter-spacing: 0.5px;
width: 100%;
max-width: 200px;
height: 40px;
border: none;
background-color: #005db4d8 !important;
color: white;
&:focus{
outline: none;
}
transition: all 200ms ;
}
////////SPINNER///
.lds-ring {
display: inline-block;
position: relative;
width: 80px;
height: 80px;
}
.lds-ring div {
box-sizing: border-box;
display: block;
position: absolute;
width: 64px;
height: 64px;
margin: 8px;
border: 8px solid #005db4d8;
border-radius: 50%;
animation: lds-ring 1.2s cubic-bezier(0.5, 0, 0.5, 1) infinite;
border-color: #005db4d8 transparent transparent transparent;
}
.lds-ring div:nth-child(1) {
animation-delay: -0.45s;
}
.lds-ring div:nth-child(2) {
animation-delay: -0.3s;
}
.lds-ring div:nth-child(3) {
animation-delay: -0.15s;
}
@keyframes lds-ring {
0% {
transform: rotate(0deg);
}
100% {
transform: rotate(360deg);
}
}
.spinner_box{
width: 100%;
height: 100%;
background: rgba(10, 10, 10, 0.2);;
right: 0;
top: 0;
display: flex;
justify-content: center;
align-items: center;
position: fixed;
}