-
Notifications
You must be signed in to change notification settings - Fork 0
/
11.3.html
272 lines (254 loc) · 11.9 KB
/
11.3.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta name="description"
content="Quickly detect document boundaries from a live camera stream and crop the document out before normalizing it further through perspective correction, deskewing, and more." />
<meta name="keywords" content="camera based quadrilateral detection and normalization" />
<title>Detect the boundary of a document and normalize it</title>
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/dcv.bundle.js"></script>
</head>
<body>
<h1 style="font-size: 1.5em">
Detect the boundary of a document and normalize it
</h1>
<button id="start-detecting" onclick="startDetecting()">
Start Detecting
</button>
<button id="restart-detecting" onclick="restartDetecting()" style="display: none">
Restart Detecting
</button>
<button id="confirm-quad-for-normalization">Confirm the Boundary</button>
<button id="normalize-with-confirmed-quad" disabled>Normalize</button><br />
<input type="checkbox" style="vertical-align: middle" id="auto-normalize" /><label style="vertical-align: middle"
for="auto-normalize">Normalize Automatically</label>
<div id="div-ui-container" style="margin-top: 10px; height: 450px"></div>
<div id="div-image-container" style="display: none; width: 100%; height: 70vh"></div>
<div id="normalized-result"></div>
<script>
let quads = [];
let cameraEnhancer = null;
let router = null;
let items;
let layer;
let originalImage;
let imageEditorView;
let promiseCVRReady;
let frameCount = 0;
const btnStart = document.querySelector("#start-detecting");
const btnRestart = document.querySelector("#restart-detecting");
const cameraViewContainer = document.querySelector("#div-ui-container");
const normalizedImageContainer = document.querySelector("#normalized-result");
const btnEdit = document.querySelector("#confirm-quad-for-normalization");
const btnNormalize = document.querySelector("#normalize-with-confirmed-quad");
const imageEditorViewContainer = document.querySelector("#div-image-container");
const autoNormalize = document.querySelector("#auto-normalize");
/** LICENSE ALERT - README
* To use the library, you need to first call the method initLicense() to initialize the license using a license key string.
*/
Dynamsoft.License.LicenseManager.initLicense(
"DLS2eyJvcmdhbml6YXRpb25JRCI6IjIwMDAwMSJ9"
);
/**
* The license "DLS2eyJvcmdhbml6YXRpb25JRCI6IjIwMDAwMSJ9" is a temporary license for testing good for 24 hours.
* You can visit https://www.dynamsoft.com/customer/license/trialLicense?utm_source=github&architecture=dcv&product=ddn&package=js to get your own trial license good for 30 days.
* LICENSE ALERT - THE END
*/
/**
* Preloads the `DocumentNormalizer` module, saving time in preparing for document border detection and image normalization.
*/
Dynamsoft.Core.CoreModule.loadWasm(["DDN"]);
/**
* Creates a CameraEnhancer instance and prepares an ImageEditorView instance for later use.
*/
async function initDCE() {
const view = await Dynamsoft.DCE.CameraView.createInstance();
cameraEnhancer = await Dynamsoft.DCE.CameraEnhancer.createInstance(view);
imageEditorView = await Dynamsoft.DCE.ImageEditorView.createInstance(imageEditorViewContainer);
/* Creates an image editing layer for drawing found document boundaries. */
layer = imageEditorView.createDrawingLayer();
cameraViewContainer.append(view.getUIElement());
}
/**
* Creates a CaptureVisionRouter instance and configure the task to detect document boundaries.
* Also, make sure the original image is returned after it has been processed.
*/
let cvrReady = (async function initCVR() {
await initDCE();
router = await Dynamsoft.CVR.CaptureVisionRouter.createInstance();
router.setInput(cameraEnhancer);
/**
* Sets the result types to be returned.
* Because we need to normalize the original image later, here we set the return result type to
* include both the quadrilateral and original image data.
*/
let newSettings = await router.getSimplifiedSettings("DetectDocumentBoundaries_Default");
newSettings.capturedResultItemTypes = Dynamsoft.Core.EnumCapturedResultItemType.CRIT_DETECTED_QUAD | Dynamsoft.Core.EnumCapturedResultItemType.CRIT_ORIGINAL_IMAGE;
await router.updateSettings("DetectDocumentBoundaries_Default", newSettings);
/* Defines the result receiver for the task.*/
const resultReceiver = new Dynamsoft.CVR.CapturedResultReceiver();
resultReceiver.onCapturedResultReceived = handleCapturedResult;
router.addResultReceiver(resultReceiver);
})();
/**
* Defines the callback function that is executed after each image has been processed.
*/
async function handleCapturedResult(result) {
/* Do something with the result */
/* Saves the image data of the current frame for subsequent image editing. */
const originalImage = result.items.filter((item) => item.type === 1);
originalImageData = originalImage.length && originalImage[0].imageData;
if (!autoNormalize.checked) {
/* why > 1? Because the "result items" always contain a result of the original image. */
if (result.items.length > 1) {
items = result.items;
}
} else if (originalImageData) {
/** If "Normalize Automatically" is checked, the library uses the document boundaries found in consecutive
* image frames to decide whether conditions are suitable for automatic normalization.
*/
if (result.items.length <= 1) {
frameCount = 0;
return;
}
frameCount++;
/**
* In our case, we determine a good condition for "automatic normalization" to be
* "getting document boundary detected for 30 consecutive frames".
*
* NOTE that this condition will not be valid should you add a CapturedResultFilter
* with ResultDeduplication enabled.
*/
if (frameCount === 30) {
frameCount = 0;
normalizedImageContainer.innerHTML = "";
/**
* When the condition is met, we use the document boundary found in this image frame
* to normalize the document by setting the coordinates of the ROI (region of interest)
* in the built-in template "NormalizeDocument_Default".
*/
let settings = await router.getSimplifiedSettings("NormalizeDocument_Default");
settings.roiMeasuredInPercentage = 0;
settings.roi.points = result.items[1].location.points;
await router.updateSettings("NormalizeDocument_Default", settings);
/**
* After that, executes the normalization and shows the result on the page.
*/
let normalizeResult = await router.capture(originalImageData, "NormalizeDocument_Default");
normalizedImageContainer.append(normalizeResult.items[0].toCanvas());
cameraViewContainer.style.display = "none";
btnStart.style.display = "none";
btnRestart.style.display = "inline";
btnEdit.disabled = true;
await router.stopCapturing();
}
}
}
async function startDetecting() {
try {
await (promiseCVRReady = promiseCVRReady || (async () => {
await cvrReady;
/* Starts streaming the video. */
await cameraEnhancer.open();
/* Uses the built-in template "DetectDocumentBoundaries_Default" to start a continuous boundary detection task. */
await router.startCapturing("DetectDocumentBoundaries_Default");
})());
} catch (ex) {
let errMsg;
if (ex.message.includes("network connection error")) {
errMsg = "Failed to connect to Dynamsoft License Server: network connection error. Check your Internet connection or contact Dynamsoft Support ([email protected]) to acquire an offline license.";
} else {
errMsg = ex.message || ex;
}
console.error(errMsg);
alert(errMsg);
}
}
async function restartDetecting() {
/* Reset the UI elements and restart the detection task. */
imageEditorViewContainer.style.display = "none";
normalizedImageContainer.innerHTML = "";
cameraViewContainer.style.display = "block";
btnStart.style.display = "inline";
btnRestart.style.display = "none";
btnNormalize.disabled = true;
btnEdit.disabled = false;
layer.clearDrawingItems();
await router.startCapturing("DetectDocumentBoundaries_Default");
}
autoNormalize.addEventListener("change", () => {
btnEdit.style.display = autoNormalize.checked ? "none" : "inline";
btnNormalize.style.display = autoNormalize.checked ? "none" : "inline";
});
btnEdit.addEventListener("click", async () => {
if (!cameraEnhancer.isOpen() || items.length <= 1) return;
/* Stops the detection task since we assume we have found a good boundary. */
router.stopCapturing();
/* Hides the cameraView and shows the imageEditorView. */
cameraViewContainer.style.display = "none";
imageEditorViewContainer.style.display = "block";
/* Draws the image on the imageEditorView first. */
imageEditorView.setOriginalImage(originalImageData);
quads = [];
/* Draws the document boundary (quad) over the image. */
for (let i = 0; i < items.length; i++) {
if (items[i].type === Dynamsoft.Core.EnumCapturedResultItemType.CRIT_ORIGINAL_IMAGE) continue;
const points = items[i].location.points;
const quad = new Dynamsoft.DCE.QuadDrawingItem({ points });
quads.push(quad);
layer.addDrawingItems(quads);
}
btnStart.style.display = "none";
btnRestart.style.display = "inline";
btnNormalize.disabled = false;
btnEdit.disabled = true;
});
btnNormalize.addEventListener("click", async () => {
/* Gets the selected quadrilateral. */
let seletedItems = imageEditorView.getSelectedDrawingItems();
let quad;
if (seletedItems.length) {
quad = seletedItems[0].getQuad();
} else {
quad = items[1].location;
}
const isPointOverBoundary = (point) => {
if (point.x < 0 ||
point.x > originalImageData.width ||
point.y < 0 ||
point.y > originalImageData.height) {
return true;
} else {
return false;
}
};
/* Check if the points beyond the boundaries of the image. */
if (quad.points.some(point => isPointOverBoundary(point))) {
alert("The document boundaries extend beyond the boundaries of the image and cannot be used to normalize the document.");
return;
}
/* Hides the imageEditorView. */
imageEditorViewContainer.style.display = "none";
/* Removes the old normalized image if any. */
normalizedImageContainer.innerHTML = "";
/**
* Sets the coordinates of the ROI (region of interest)
* in the built-in template "NormalizeDocument_Default".
*/
let newSettings = await router.getSimplifiedSettings("NormalizeDocument_Default");
newSettings.roiMeasuredInPercentage = 0;
newSettings.roi.points = quad.points;
await router.updateSettings("NormalizeDocument_Default", newSettings);
/* Executes the normalization and shows the result on the page. */
let normalizeResult = await router.capture(originalImageData, "NormalizeDocument_Default");
if (normalizeResult.items[0]) {
normalizedImageContainer.append(normalizeResult.items[0].toCanvas());
}
layer.clearDrawingItems();
btnNormalize.disabled = true;
btnEdit.disabled = true;
});
</script>
</body>
</html>