Dynamsoft Document Normalizer JavaScript SDK позволяет обнаруживать и исправлять документы. Его можно легко интегрировать в веб-приложения с помощью нескольких строк кода. В этой статье мы создадим веб-приложение Angular, чтобы продемонстрировать, как использовать SDK для обнаружения и исправления документов.
Предпосылки
Angular CLI: инструмент интерфейса командной строки для разработки на Angular.
npm install -g @angular/cli
Инициализировать проект Angular и установить зависимости
В терминале выполните следующую команду, чтобы создать новый проект Angular.
ng new angular-document-edge-detection
Затем перейдите в папку проекта и установите зависимости.
- Dynamsoft Document Normalizer: JavaScript SDK для обнаружения и исправления документов.
npm i dynamsoft-document-normalizer
- Dynamsoft Camera Enhancer: JavaScript SDK для управления камерой, захвата изображений и потоковой передачи видео.
npm i dynamsoft-camera-enhancer
Затем откройте файл angular.json
, чтобы настроить путь к ресурсу для Dynamsoft Document Normalizer:
"assets": [ "src/favicon.ico", "src/assets", { "glob": "**/*", "input": "./node_modules/dynamsoft-document-normalizer/dist", "output": "assets/dynamsoft-document-normalizer" } ],
Выходной путь должен быть таким же, как указанный в коде. Мы создаем файл dynamsoft.service.ts
для установки лицензионного ключа и пути к ресурсу:
import { Injectable, Optional } from '@angular/core'; import { DocumentNormalizer} from 'dynamsoft-document-normalizer'; @Injectable({ providedIn: 'root' }) export class DynamsoftService { constructor() { DocumentNormalizer.license = "DLS2eyJoYW5kc2hha2VDb2RlIjoiMjAwMDAxLTE2NDk4Mjk3OTI2MzUiLCJvcmdhbml6YXRpb25JRCI6IjIwMDAwMSIsInNlc3Npb25QYXNzd29yZCI6IndTcGR6Vm05WDJrcEQ5YUoifQ=="; DocumentNormalizer.engineResourcePath = "assets/dynamsoft-document-normalizer"; } }
Параметры алгоритма обнаружения и исправления документов
Dynamsoft Document Normalizer предоставляет набор параметров для управления процессом обнаружения и исправления. Вы можете обратиться к онлайн-документации для более подробной информации.
Здесь мы создаем несколько простых шаблонов параметров с многострочными строками TypeScript:
export class Template { static binary = ` { "GlobalParameter":{ "Name":"GP" }, "ImageParameterArray":[ { "Name":"IP-1", "NormalizerParameterName":"NP-1", "BinarizationModes":[{"Mode":"BM_LOCAL_BLOCK", "ThresholdCompensation":9}], "ScaleDownThreshold":2300 } ], "NormalizerParameterArray":[ { "Name":"NP-1", "ColourMode": "ICM_BINARY" } ] } `; static color = ` { "GlobalParameter":{ "Name":"GP" }, "ImageParameterArray":[ { "Name":"IP-1", "NormalizerParameterName":"NP-1", "BinarizationModes":[{"Mode":"BM_LOCAL_BLOCK", "ThresholdCompensation":9}], "ScaleDownThreshold":2300 } ], "NormalizerParameterArray":[ { "Name":"NP-1", "ColourMode": "ICM_COLOUR" } ] } `; static grayscale = ` { "GlobalParameter":{ "Name":"GP" }, "ImageParameterArray":[ { "Name":"IP-1", "NormalizerParameterName":"NP-1", "BinarizationModes":[{"Mode":"BM_LOCAL_BLOCK", "ThresholdCompensation":9}], "ScaleDownThreshold":2300 } ], "NormalizerParameterArray":[ { "Name":"NP-1", "ColourMode": "ICM_GRAYSCALE" } ] } `; }
Единственная разница между этими тремя шаблонами — это параметр ColourMode
. Параметр ColourMode
управляет цветовым режимом выходного изображения. Он может быть установлен на ICM_BINARY
, ICM_COLOUR
или ICM_GRAYSCALE
.
Компоненты Angular для обнаружения и исправления документов
Мы собираемся создать два компонента Angular: file
и camera
. Компонент file
отвечает за обнаружение и исправление документов из файлов изображений. Компонент camera
отвечает за обнаружение и исправление документов из потока камеры.
ng generate component file-detection ng generate component camera-detection
Компонент обнаружения файлов
Страница обнаружения файлов состоит из пяти частей: группы переключателей, кнопки ввода файла, элемента изображения, холста и элемента div.
<span id="loading-status" style="font-size:x-large" [hidden]="isLoaded">Loading Library...</span> <br /> <div class="row"> <label for="binary"> <input type="radio" name="templates" value="binary" (change)="onRadioChange($event)" />Black & White </label> <label for="grayscale"><input type="radio" name="templates" value="grayscale" (change)="onRadioChange($event)" /> Grayscale </label> <label for="color"><input type="radio" name="templates" value="color" [checked]="true" (change)="onRadioChange($event)" /> Color </label> </div> <input type="file" title="file" id="file" accept="image/*" (change)="onChange($event)" /> <div class="container"> <div id="imageview"> <img id="image" alt="" /> <canvas id="overlay"></canvas> </div> <div id="resultview"> <canvas id="normalizedImage"></canvas> </div> </div>
В TypeScript мы сначала внедряем DynamsoftService
в конструктор и инициализируем объект Dynamsoft Document Normalizer в методе ngOnInit
.
import { Component, OnInit } from '@angular/core'; import { DocumentNormalizer } from 'dynamsoft-document-normalizer'; import { DynamsoftService } from '../dynamsoft.service'; import { OverlayManager } from '../overlay'; import { Template } from '../template'; @Component({ selector: 'app-file-detection', templateUrl: './file-detection.component.html', styleUrls: ['./file-detection.component.css'] }) export class FileDetectionComponent implements OnInit { isLoaded = false; overlay: HTMLCanvasElement | undefined; context: CanvasRenderingContext2D | undefined; normalizer: DocumentNormalizer | undefined; overlayManager: OverlayManager; points: any[] = []; currentFile: File | undefined; constructor(private dynamsoftService: DynamsoftService) { this.overlayManager = new OverlayManager(); } ngOnInit(): void { this.overlayManager.initOverlay(document.getElementById('overlay') as HTMLCanvasElement); (async () => { this.normalizer = await DocumentNormalizer.createInstance(); this.isLoaded = true; await this.normalizer.setRuntimeSettings(Template.color); })(); } }
Затем добавьте реализации для элементов пользовательского интерфейса.
- Группа переключателей используется для переключения шаблона параметра.
onRadioChange(event: Event) { if (!this.normalizer) { return; } let target = event.target as HTMLInputElement; let template = Template.binary; if (target.value === 'binary') { template = Template.binary; } else if (target.value === 'grayscale') { template = Template.grayscale; } else if (target.value === 'color') { template = Template.color; } (async () => { await this.normalizer!.setRuntimeSettings(template); this.normalize(this.currentFile!, this. Points); })(); }
- Кнопка ввода файла используется для выбора файла изображения.
onChange(event: Event) { const element = event.currentTarget as HTMLInputElement; let fileList: FileList | null = element.files; if (fileList) { let file = fileList.item(0) as any; } }
- Элемент изображения используется для отображения загруженного файла изображения.
if (file) { this.currentFile = file; let fr = new FileReader(); fr.onload = (event: any) => { let image = document.getElementById('image') as HTMLImageElement; if (image) { image.src = event.target.result; const img = new Image(); img.onload = (event: any) => { }; img.src = event.target.result; } }; fr.readAsDataURL(file);
Когда изображение загружено, вызовите метод detectQuad
для обнаружения краев документа.
this.normalizer.detectQuad(file).then((results: any) => { try { if (results.length > 0) { } } catch (e) { alert(e); } });
- Холст используется для отображения обнаруженных краев документа.
try { if (results.length > 0) { let result = results[0]; this.points = result['location']['points']; this.overlayManager.drawOverlay( this.points, ); } } catch (e) { alert(e); }
- Элемент div используется для отображения исправленного документа.
this.normalize(file, this.points); normalize(file: File, points: any) { if (this.normalizer) { this.normalizer.normalize(file, points).then((result: any) => { let image = document.getElementById('normalizedImage') as HTMLCanvasElement; if (image) { image.width = result.image.width; image.height = result.image.height; let ctx = image.getContext('2d') as CanvasRenderingContext2D; var imgdata = ctx.createImageData(image.width, image.height); var imgdatalen = result.image.data.length; for(var i=0; i<imgdatalen; i++) { imgdata.data[i] = result.image.data[i]; } ctx.putImageData(imgdata, 0, 0); } }); } }
Компонент обнаружения камеры
Пользовательский интерфейс компонента обнаружения камеры аналогичен компоненту обнаружения файлов. Разница в том, что элемент изображения заменяется элементом видео.
<div id="document-scanner"> <span id="loading-status" style="font-size:x-large" [hidden]="isLoaded">Loading Library...</span> <br /> <div class="row"> <label for="binary"> <input type="radio" name="templates" value="binary" (change)="onRadioChange($event)" />Black & White </label> <label for="grayscale"><input type="radio" name="templates" value="grayscale" (change)="onRadioChange($event)" /> Grayscale </label> <label for="color"><input type="radio" name="templates" value="color" [checked]="true" (change)="onRadioChange($event)" /> Color </label> </div> <label for="threshold"><input id="thredshold" (change)="updateThresholdCompensation($event)" type="range" min="0" max="10" value="9" step="1">Edge Detection Threshold: <span id="ThresholdCompensationval" style="width: 12px;" [textContent]="9"></span></label> <div> <label for="videoSource">Video Source: <select id="videoSource" (change)="openCamera()"></select></label> <button id="detectButton" (click)="detectDocument()">Start Detection</button> <button id="captureButton" (click)="captureDocument()">Capture Document</button> </div> <div id="videoview"> <div class="dce-video-container" id="videoContainer"></div> <canvas id="overlay"></canvas> </div> <div class="container"> <div id="resultview"> <canvas id="normalizedImage"></canvas> </div> </div> </div>
Известно, что метод getUserMedia
— единственный API, который может получить доступ к потоку камеры. С методом getUserMedia
у нас еще много работы по программированию камеры. Вместо этого для упрощения кодирования мы используем Dynamsoft Camera Enhancer. SDK камеры JavaScript инкапсулирует метод getUserMedia
и предоставляет несколько полезных API.
Вот код инициализации компонента обнаружения камеры.
import { Component, OnInit } from '@angular/core'; import { DocumentNormalizer } from 'dynamsoft-document-normalizer'; import { CameraEnhancer } from 'dynamsoft-camera-enhancer'; import { DynamsoftService } from '../dynamsoft.service'; import { OverlayManager } from '../overlay'; import { Template } from '../template'; @Component({ selector: 'app-camera-detection', templateUrl: './camera-detection.component.html', styleUrls: ['./camera-detection.component.css'] }) export class CameraDetectionComponent implements OnInit { isLoaded = false; overlay: HTMLCanvasElement | undefined; context: CanvasRenderingContext2D | undefined; normalizer: DocumentNormalizer | undefined; overlayManager: OverlayManager; currentData: any; cameraInfo: any = {}; videoSelect: HTMLSelectElement | undefined; enhancer: CameraEnhancer | undefined; isDetecting = false; captured: any[] = []; constructor(private dynamsoftService: DynamsoftService) { this.overlayManager = new OverlayManager(); } ngOnDestroy() { this.normalizer?.dispose(); this.normalizer = undefined; this.enhancer?.dispose(true); this.enhancer = undefined; } ngOnInit(): void { this.videoSelect = document.querySelector('select#videoSource') as HTMLSelectElement; this.overlayManager.initOverlay(document.getElementById('overlay') as HTMLCanvasElement); (async () => { this.normalizer = await DocumentNormalizer.createInstance(); this.enhancer = await CameraEnhancer.createInstance(); this.enhancer.on("cameraOpen", (playCallBackInfo: any) => { this.overlayManager.updateOverlay(playCallBackInfo.width, playCallBackInfo.height); }); this.enhancer.on("cameraClose", (playCallBackInfo: any) => { console.log(playCallBackInfo.deviceId); }); this.isLoaded = true; await this.normalizer.setRuntimeSettings(Template.color); })(); } }
После создания экземпляра Dynamsoft Camera Enhancer нам нужно привязать его к элементу div для отображения потока камеры.
let uiElement = document.getElementById('videoContainer'); if (uiElement) { await this.enhancer.setUIElement(uiElement); }
Если у вас несколько камер, вы можете вызвать метод getAllCameras
, чтобы получить список камер:
let cameras = await this.enhancer.getAllCameras(); this.listCameras(cameras);
Затем выберите камеру и откройте ее:
async openCamera(): Promise<void> { if (this.videoSelect) { let deviceId = this.videoSelect.value; if (this.enhancer) { await this.enhancer.selectCamera(this.cameraInfo[deviceId]); await this.enhancer.open() } } }
Метод detectQuad
поддерживает различные типы ввода. В приведенном выше разделе мы использовали метод detectQuad
для обнаружения документа из файла изображения. Теперь мы используем его для обнаружения документа из кадров потока камеры.
detect(): void { if (this.normalizer && this.enhancer && this.isDetecting) { let data = this.enhancer.getFrame().toCanvas(); this.normalizer.detectQuad(data).then((results: any) => { this.overlayManager.clearOverlay(); if (!this.isDetecting) return; try { if (results.length > 0) { if (this.captured.length > 0) { this.captured.pop(); } let result = results[0]; let points = result['location']['points']; this.captured.push({ 'image': data, 'points': points }); this.overlayManager.drawOverlay( points, ); } } catch (e) { alert(e); } this. Detect(); }); } }
Задание обнаружения документов выполняется в веб-воркере. Таким образом, не вызывайте его постоянно. Вы должны дождаться завершения предыдущего задания обнаружения, прежде чем вызывать его снова.
Запустите ng serve
, чтобы повеселиться с приложением для обнаружения и проверки веб-документов.
Исходный код
https://github.com/yushulx/angular-document-edge-detection
Первоначально опубликовано на https://www.dynamsoft.com 5 января 2023 г.