jscanify 1.1.0 → 1.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +24 -13
- package/docs/images/test/test3.jpg +0 -0
- package/docs/images/test/test4.jpg +0 -0
- package/docs/images/test/test5.jpg +0 -0
- package/docs/images/test/test6.jpg +0 -0
- package/docs/images/test/test7.jpg +0 -0
- package/docs/images/test/test8.jpg +0 -0
- package/docs/images/test/test9.jpg +0 -0
- package/docs/index.css +141 -141
- package/docs/index.html +123 -124
- package/docs/script.js +41 -42
- package/docs/tester.html +148 -0
- package/package.json +32 -32
- package/src/jscanify-node.js +11 -15
- package/src/jscanify.js +255 -271
- package/src/opencv.js +47 -47
- package/test/tests.js +102 -39
- package/install.md +0 -5
package/docs/script.js
CHANGED
|
@@ -1,43 +1,42 @@
|
|
|
1
|
-
let loadedOpenCV = false
|
|
2
|
-
|
|
3
|
-
const openCvURL = "https://docs.opencv.org/4.7.0/opencv.js"
|
|
4
|
-
|
|
5
|
-
function loadOpenCV(onComplete) {
|
|
6
|
-
if (loadedOpenCV) {
|
|
7
|
-
onComplete()
|
|
8
|
-
} else {
|
|
9
|
-
$('#demo-result').text('Loading OpenCV...')
|
|
10
|
-
const script = document.createElement("script")
|
|
11
|
-
script.src = openCvURL
|
|
12
|
-
|
|
13
|
-
script.onload = function () {
|
|
14
|
-
setTimeout(function () {
|
|
15
|
-
onComplete()
|
|
16
|
-
}, 1000)
|
|
17
|
-
loadedOpenCV = true
|
|
18
|
-
}
|
|
19
|
-
document.body.appendChild(script)
|
|
20
|
-
}
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
const scanner = new jscanify()
|
|
24
|
-
$('#demo-images .image-container').click(function () {
|
|
25
|
-
$('.image-container.selected').removeClass('selected')
|
|
26
|
-
$(this).addClass('selected')
|
|
27
|
-
const imageSrc = $(this).find('img').data('url')
|
|
28
|
-
loadOpenCV(function () {
|
|
29
|
-
$('#demo-result').empty()
|
|
30
|
-
|
|
31
|
-
const newImg = document.createElement("img")
|
|
32
|
-
newImg.src = imageSrc
|
|
33
|
-
|
|
34
|
-
newImg.onload = function(){
|
|
35
|
-
scanner.extractPaper(newImg, 386, 500
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
})
|
|
1
|
+
let loadedOpenCV = false
|
|
2
|
+
|
|
3
|
+
const openCvURL = "https://docs.opencv.org/4.7.0/opencv.js"
|
|
4
|
+
|
|
5
|
+
function loadOpenCV(onComplete) {
|
|
6
|
+
if (loadedOpenCV) {
|
|
7
|
+
onComplete()
|
|
8
|
+
} else {
|
|
9
|
+
$('#demo-result').text('Loading OpenCV...')
|
|
10
|
+
const script = document.createElement("script")
|
|
11
|
+
script.src = openCvURL
|
|
12
|
+
|
|
13
|
+
script.onload = function () {
|
|
14
|
+
setTimeout(function () {
|
|
15
|
+
onComplete()
|
|
16
|
+
}, 1000)
|
|
17
|
+
loadedOpenCV = true
|
|
18
|
+
}
|
|
19
|
+
document.body.appendChild(script)
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const scanner = new jscanify()
|
|
24
|
+
$('#demo-images .image-container').click(function () {
|
|
25
|
+
$('.image-container.selected').removeClass('selected')
|
|
26
|
+
$(this).addClass('selected')
|
|
27
|
+
const imageSrc = $(this).find('img').data('url')
|
|
28
|
+
loadOpenCV(function () {
|
|
29
|
+
$('#demo-result').empty()
|
|
30
|
+
|
|
31
|
+
const newImg = document.createElement("img")
|
|
32
|
+
newImg.src = imageSrc
|
|
33
|
+
|
|
34
|
+
newImg.onload = function(){
|
|
35
|
+
const resultCanvas = scanner.extractPaper(newImg, 386, 500);
|
|
36
|
+
$('#demo-result').append(resultCanvas);
|
|
37
|
+
|
|
38
|
+
const highlightedCanvas = scanner.highlightPaper(newImg)
|
|
39
|
+
$('#demo-result').append(highlightedCanvas);
|
|
40
|
+
}
|
|
41
|
+
})
|
|
43
42
|
})
|
package/docs/tester.html
ADDED
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html>
|
|
3
|
+
<head>
|
|
4
|
+
<script
|
|
5
|
+
async
|
|
6
|
+
src="https://www.googletagmanager.com/gtag/js?id=G-32CWY3SB1G"
|
|
7
|
+
></script>
|
|
8
|
+
<script>
|
|
9
|
+
function gtag() {
|
|
10
|
+
dataLayer.push(arguments);
|
|
11
|
+
}
|
|
12
|
+
(window.dataLayer = window.dataLayer || []),
|
|
13
|
+
gtag("js", new Date()),
|
|
14
|
+
gtag("config", "G-32CWY3SB1G");
|
|
15
|
+
</script>
|
|
16
|
+
<meta charset="UTF-8" />
|
|
17
|
+
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
|
|
18
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
19
|
+
<title>jscanify debugger tool</title>
|
|
20
|
+
<meta
|
|
21
|
+
name="description"
|
|
22
|
+
content="A debugging tool for developers to test jscanify."
|
|
23
|
+
/>
|
|
24
|
+
<meta property="og:title" content="jscanify debugger tool" />
|
|
25
|
+
<meta
|
|
26
|
+
property="og:description"
|
|
27
|
+
content="A debugging tool for developers to test jscanify."
|
|
28
|
+
/>
|
|
29
|
+
<meta
|
|
30
|
+
property="og:url"
|
|
31
|
+
content="https://colonelparrot.github.io/jscanify/tester.html"
|
|
32
|
+
/>
|
|
33
|
+
<meta
|
|
34
|
+
property="og:image"
|
|
35
|
+
content="https://colonelparrot.github.io/jscanify/images/logo.png"
|
|
36
|
+
/>
|
|
37
|
+
<meta property="og:locale" content="en_US" />
|
|
38
|
+
<link rel="icon" type="image/x-icon" href="favicon.ico" />
|
|
39
|
+
<style>
|
|
40
|
+
html,
|
|
41
|
+
body {
|
|
42
|
+
margin: 0;
|
|
43
|
+
}
|
|
44
|
+
* {
|
|
45
|
+
font-family: system-ui;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
h1,
|
|
49
|
+
h2,
|
|
50
|
+
h3 {
|
|
51
|
+
font-weight: 400;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
img,
|
|
55
|
+
canvas {
|
|
56
|
+
max-height: 400px;
|
|
57
|
+
max-width: 400px;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
#results > div {
|
|
61
|
+
margin: 20px;
|
|
62
|
+
margin-top: 0;
|
|
63
|
+
}
|
|
64
|
+
</style>
|
|
65
|
+
</head>
|
|
66
|
+
<body>
|
|
67
|
+
<div style="padding: 30px">
|
|
68
|
+
<h1 style="margin: 0">jscanify debugging tool</h1>
|
|
69
|
+
<h2 style="margin-top: 10px">
|
|
70
|
+
A tool for developers to test jscanify on test images
|
|
71
|
+
</h2>
|
|
72
|
+
|
|
73
|
+
<h3 style="margin-bottom: 10px">Upload your image:</h3>
|
|
74
|
+
<input
|
|
75
|
+
type="file"
|
|
76
|
+
id="fileInput"
|
|
77
|
+
accept="image/png, image/gif, image/jpeg"
|
|
78
|
+
/>
|
|
79
|
+
|
|
80
|
+
<div id="result" style="margin-top: 50px; display: none">
|
|
81
|
+
<h2 style="margin-bottom: 0; margin-left: 20px">Result</h2>
|
|
82
|
+
<div style="display: flex; flex-wrap: wrap" id="results">
|
|
83
|
+
<div>
|
|
84
|
+
<h3>Original image</h3>
|
|
85
|
+
<img id="orig" />
|
|
86
|
+
</div>
|
|
87
|
+
<div id="highlighted">
|
|
88
|
+
<h3>Highlighted Paper</h3>
|
|
89
|
+
</div>
|
|
90
|
+
<div id="extracted">
|
|
91
|
+
<h3>Extracted Paper</h3>
|
|
92
|
+
</div>
|
|
93
|
+
<div id="cornerPts">
|
|
94
|
+
<h3>Corner Points</h3>
|
|
95
|
+
<pre style="font-family: monospace"></pre>
|
|
96
|
+
</div>
|
|
97
|
+
</div>
|
|
98
|
+
</div>
|
|
99
|
+
<div style="margin-top: 50px">
|
|
100
|
+
Check out the code for this page
|
|
101
|
+
<a
|
|
102
|
+
href="https://github.com/ColonelParrot/jscanify/blob/master/docs/tester.html"
|
|
103
|
+
target="_blank"
|
|
104
|
+
>here.</a
|
|
105
|
+
>
|
|
106
|
+
</div>
|
|
107
|
+
</div>
|
|
108
|
+
|
|
109
|
+
<script src="https://docs.opencv.org/4.7.0/opencv.js" async></script>
|
|
110
|
+
<!-- warning: loading OpenCV can take some time. Load asynchronously -->
|
|
111
|
+
<script src="https://cdn.jsdelivr.net/gh/ColonelParrot/jscanify@master/src/jscanify.min.js"></script>
|
|
112
|
+
<script>
|
|
113
|
+
window.addEventListener("load", function () {
|
|
114
|
+
const scanner = new jscanify();
|
|
115
|
+
fileInput.addEventListener("change", function (e) {
|
|
116
|
+
if (e.target.files.length) {
|
|
117
|
+
const image = e.target.files[0];
|
|
118
|
+
orig.src = URL.createObjectURL(image);
|
|
119
|
+
clearData();
|
|
120
|
+
result.style.display = "block";
|
|
121
|
+
|
|
122
|
+
orig.onload = function () {
|
|
123
|
+
const highlightedCanvas = scanner.highlightPaper(orig);
|
|
124
|
+
highlighted.appendChild(highlightedCanvas);
|
|
125
|
+
|
|
126
|
+
const extractedCanvas = scanner.extractPaper(orig, 772, 1000);
|
|
127
|
+
extracted.appendChild(extractedCanvas);
|
|
128
|
+
|
|
129
|
+
const contour = scanner.findPaperContour(cv.imread(orig));
|
|
130
|
+
const cornerPoints = scanner.getCornerPoints(contour);
|
|
131
|
+
cornerPts.querySelector("pre").textContent = JSON.stringify(
|
|
132
|
+
cornerPoints,
|
|
133
|
+
null,
|
|
134
|
+
4
|
|
135
|
+
);
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
});
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
function clearData() {
|
|
142
|
+
highlighted.querySelector("canvas")?.remove();
|
|
143
|
+
extracted.querySelector("canvas")?.remove();
|
|
144
|
+
cornerPts.querySelector("pre").textContent = "";
|
|
145
|
+
}
|
|
146
|
+
</script>
|
|
147
|
+
</body>
|
|
148
|
+
</html>
|
package/package.json
CHANGED
|
@@ -1,32 +1,32 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "jscanify",
|
|
3
|
-
"version": "1.
|
|
4
|
-
"description": "Open-source Javascript mobile document scanner.",
|
|
5
|
-
"main": "src/jscanify-node.js",
|
|
6
|
-
"directories": {
|
|
7
|
-
"doc": "docs"
|
|
8
|
-
},
|
|
9
|
-
"scripts": {
|
|
10
|
-
"test": "mocha"
|
|
11
|
-
},
|
|
12
|
-
"repository": {
|
|
13
|
-
"type": "git",
|
|
14
|
-
"url": "https://github.com/
|
|
15
|
-
},
|
|
16
|
-
"keywords": [
|
|
17
|
-
"js",
|
|
18
|
-
"scanner",
|
|
19
|
-
"document-scanner"
|
|
20
|
-
],
|
|
21
|
-
"author": "ColonelParrot",
|
|
22
|
-
"license": "MIT",
|
|
23
|
-
"bugs": {
|
|
24
|
-
"url": "https://github.com/
|
|
25
|
-
},
|
|
26
|
-
"homepage": "https://colonelparrot.github.io/jscanify/",
|
|
27
|
-
"dependencies": {
|
|
28
|
-
"canvas": "^2.11.2",
|
|
29
|
-
"jsdom": "^22.0.0",
|
|
30
|
-
"mocha": "^10.2.0"
|
|
31
|
-
}
|
|
32
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"name": "jscanify",
|
|
3
|
+
"version": "1.3.0",
|
|
4
|
+
"description": "Open-source Javascript mobile document scanner.",
|
|
5
|
+
"main": "src/jscanify-node.js",
|
|
6
|
+
"directories": {
|
|
7
|
+
"doc": "docs"
|
|
8
|
+
},
|
|
9
|
+
"scripts": {
|
|
10
|
+
"test": "mocha --trace-uncaught"
|
|
11
|
+
},
|
|
12
|
+
"repository": {
|
|
13
|
+
"type": "git",
|
|
14
|
+
"url": "https://github.com/puffinsoft/jscanify.git"
|
|
15
|
+
},
|
|
16
|
+
"keywords": [
|
|
17
|
+
"js",
|
|
18
|
+
"scanner",
|
|
19
|
+
"document-scanner"
|
|
20
|
+
],
|
|
21
|
+
"author": "ColonelParrot",
|
|
22
|
+
"license": "MIT",
|
|
23
|
+
"bugs": {
|
|
24
|
+
"url": "https://github.com/puffinsoft/jscanify/issues"
|
|
25
|
+
},
|
|
26
|
+
"homepage": "https://colonelparrot.github.io/jscanify/",
|
|
27
|
+
"dependencies": {
|
|
28
|
+
"canvas": "^2.11.2",
|
|
29
|
+
"jsdom": "^22.0.0",
|
|
30
|
+
"mocha": "^10.2.0"
|
|
31
|
+
}
|
|
32
|
+
}
|
package/src/jscanify-node.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/*! jscanify v1.
|
|
1
|
+
/*! jscanify v1.3.0 | (c) ColonelParrot and other contributors | MIT License */
|
|
2
2
|
|
|
3
3
|
const { Canvas, createCanvas, Image, ImageData } = require("canvas");
|
|
4
4
|
const { JSDOM } = require("jsdom");
|
|
@@ -22,7 +22,7 @@ let cv;
|
|
|
22
22
|
* @returns distance between two points
|
|
23
23
|
*/
|
|
24
24
|
function distance(p1, p2) {
|
|
25
|
-
return Math.
|
|
25
|
+
return Math.hypot(p1.x - p2.x, p1.y - p2.y);
|
|
26
26
|
}
|
|
27
27
|
|
|
28
28
|
class jscanify {
|
|
@@ -44,7 +44,7 @@ class jscanify {
|
|
|
44
44
|
*/
|
|
45
45
|
findPaperContour(img) {
|
|
46
46
|
const imgGray = new cv.Mat();
|
|
47
|
-
cv.
|
|
47
|
+
cv.Canny(img, imgGray, 50, 200);
|
|
48
48
|
|
|
49
49
|
const imgBlur = new cv.Mat();
|
|
50
50
|
cv.GaussianBlur(
|
|
@@ -69,6 +69,7 @@ class jscanify {
|
|
|
69
69
|
cv.RETR_CCOMP,
|
|
70
70
|
cv.CHAIN_APPROX_SIMPLE
|
|
71
71
|
);
|
|
72
|
+
|
|
72
73
|
let maxArea = 0;
|
|
73
74
|
let maxContourIndex = -1;
|
|
74
75
|
for (let i = 0; i < contours.size(); ++i) {
|
|
@@ -140,10 +141,10 @@ class jscanify {
|
|
|
140
141
|
* @param {*} image image to process
|
|
141
142
|
* @param {*} resultWidth desired result paper width
|
|
142
143
|
* @param {*} resultHeight desired result paper height
|
|
143
|
-
* @param {*} onComplete callback with `HTMLCanvasElement` passed - the unwarped paper
|
|
144
144
|
* @param {*} cornerPoints optional custom corner points, in case automatic corner points are incorrect
|
|
145
|
+
* @returns `HTMLCanvasElement` containing undistorted image
|
|
145
146
|
*/
|
|
146
|
-
extractPaper(image, resultWidth, resultHeight,
|
|
147
|
+
extractPaper(image, resultWidth, resultHeight, cornerPoints) {
|
|
147
148
|
const canvas = createCanvas();
|
|
148
149
|
const img = cv.imread(image);
|
|
149
150
|
const maxContour = this.findPaperContour(img);
|
|
@@ -187,15 +188,10 @@ class jscanify {
|
|
|
187
188
|
);
|
|
188
189
|
|
|
189
190
|
cv.imshow(canvas, warpedDst);
|
|
190
|
-
const canvasContext = canvas.getContext("2d");
|
|
191
|
-
canvasContext.save();
|
|
192
|
-
canvasContext.scale(1, -1);
|
|
193
|
-
canvasContext.drawImage(canvas, 0, -resultHeight);
|
|
194
|
-
canvasContext.restore();
|
|
195
191
|
|
|
196
192
|
img.delete();
|
|
197
193
|
warpedDst.delete();
|
|
198
|
-
|
|
194
|
+
return canvas;
|
|
199
195
|
}
|
|
200
196
|
|
|
201
197
|
/**
|
|
@@ -222,25 +218,25 @@ class jscanify {
|
|
|
222
218
|
for (let i = 0; i < contour.data32S.length; i += 2) {
|
|
223
219
|
const point = { x: contour.data32S[i], y: contour.data32S[i + 1] };
|
|
224
220
|
const dist = distance(point, center);
|
|
225
|
-
if (point.x < center.x && point.y
|
|
221
|
+
if (point.x < center.x && point.y < center.y) {
|
|
226
222
|
// top left
|
|
227
223
|
if (dist > topLeftCornerDist) {
|
|
228
224
|
topLeftCorner = point;
|
|
229
225
|
topLeftCornerDist = dist;
|
|
230
226
|
}
|
|
231
|
-
} else if (point.x > center.x && point.y
|
|
227
|
+
} else if (point.x > center.x && point.y < center.y) {
|
|
232
228
|
// top right
|
|
233
229
|
if (dist > topRightCornerDist) {
|
|
234
230
|
topRightCorner = point;
|
|
235
231
|
topRightCornerDist = dist;
|
|
236
232
|
}
|
|
237
|
-
} else if (point.x < center.x && point.y
|
|
233
|
+
} else if (point.x < center.x && point.y > center.y) {
|
|
238
234
|
// bottom left
|
|
239
235
|
if (dist > bottomLeftCornerDist) {
|
|
240
236
|
bottomLeftCorner = point;
|
|
241
237
|
bottomLeftCornerDist = dist;
|
|
242
238
|
}
|
|
243
|
-
} else if (point.x > center.x && point.y
|
|
239
|
+
} else if (point.x > center.x && point.y > center.y) {
|
|
244
240
|
// bottom right
|
|
245
241
|
if (dist > bottomRightCornerDist) {
|
|
246
242
|
bottomRightCorner = point;
|