jscanify 1.1.0 → 1.2.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 +17 -9
- package/docs/index.html +1 -1
- package/docs/script.js +4 -5
- package/package.json +1 -1
- package/src/jscanify-node.js +7 -12
- package/src/jscanify.js +15 -32
- package/test/tests.js +58 -12
- package/install.md +0 -5
package/README.md
CHANGED
|
@@ -3,6 +3,10 @@
|
|
|
3
3
|
</p>
|
|
4
4
|
|
|
5
5
|
<p align="center">
|
|
6
|
+
<a href="https://www.jsdelivr.com/package/gh/ColonelParrot/jscanify"><img src="https://data.jsdelivr.com/v1/package/gh/ColonelParrot/jscanify/badge"></a>
|
|
7
|
+
<a href="https://cdnjs.com/libraries/jscanify"><img src="https://img.shields.io/cdnjs/v/jscanify"></a>
|
|
8
|
+
<a href="https://npmjs.com/package/jscanify"><img src="https://badgen.net/npm/dw/jscanify"></a>
|
|
9
|
+
<br />
|
|
6
10
|
<a href="https://github.com/ColonelParrot/jscanify/blob/master/LICENSE"><img src="https://img.shields.io/github/license/ColonelParrot/jscanify.svg"></a>
|
|
7
11
|
<a href="https://GitHub.com/ColonelParrot/jscanify/releases/"><img src="https://img.shields.io/github/release/ColonelParrot/jscanify.svg"></a>
|
|
8
12
|
<a href="https://npmjs.com/package/jscanify"><img src="https://badgen.net/npm/v/jscanify"></a>
|
|
@@ -22,8 +26,8 @@ Available on <a href="https://www.npmjs.com/package/jscanify">npm</a> or via <a
|
|
|
22
26
|
- paper detection & highlighting
|
|
23
27
|
- paper scanning with distortion correction
|
|
24
28
|
|
|
25
|
-
| Image Highlighting
|
|
26
|
-
|
|
|
29
|
+
| Image Highlighting | Scanned Result |
|
|
30
|
+
| -------------------------------------------- | ------------------------------------------ |
|
|
27
31
|
| <img src="docs/images/highlight-paper1.png"> | <img src="docs/images/scanned-paper1.png"> |
|
|
28
32
|
| <img src="docs/images/highlight-paper2.png"> | <img src="docs/images/scanned-paper2.png"> |
|
|
29
33
|
|
|
@@ -46,6 +50,8 @@ cdn:
|
|
|
46
50
|
<script src="https://cdn.jsdelivr.net/gh/ColonelParrot/jscanify@master/src/jscanify.min.js"></script>
|
|
47
51
|
```
|
|
48
52
|
|
|
53
|
+
> **Note**: jscanify on NodeJS is slightly different. See [wiki: use on NodeJS](https://github.com/ColonelParrot/jscanify/wiki#use-on-nodejs).
|
|
54
|
+
|
|
49
55
|
### Highlight Paper in Image
|
|
50
56
|
|
|
51
57
|
```html
|
|
@@ -67,9 +73,8 @@ const scanner = new jscanify();
|
|
|
67
73
|
const paperWidth = 500;
|
|
68
74
|
const paperHeight = 1000;
|
|
69
75
|
image.onload = function () {
|
|
70
|
-
scanner.extractPaper(image, paperWidth, paperHeight
|
|
71
|
-
|
|
72
|
-
});
|
|
76
|
+
const resultCanvas = scanner.extractPaper(image, paperWidth, paperHeight);
|
|
77
|
+
document.body.appendChild(resultCanvas);
|
|
73
78
|
};
|
|
74
79
|
```
|
|
75
80
|
|
|
@@ -78,9 +83,10 @@ image.onload = function () {
|
|
|
78
83
|
The following code continuously reads from the user's camera and highlights the paper:
|
|
79
84
|
|
|
80
85
|
```html
|
|
81
|
-
<video id="video"></video>
|
|
82
|
-
|
|
83
|
-
<canvas id="result"></canvas>
|
|
86
|
+
<video id="video"></video> <canvas id="canvas"></canvas>
|
|
87
|
+
<!-- original video -->
|
|
88
|
+
<canvas id="result"></canvas>
|
|
89
|
+
<!-- highlighted video -->
|
|
84
90
|
```
|
|
85
91
|
|
|
86
92
|
```js
|
|
@@ -102,6 +108,8 @@ navigator.mediaDevices.getUserMedia({ video: true }).then((stream) => {
|
|
|
102
108
|
```
|
|
103
109
|
|
|
104
110
|
To export the paper to a PDF, see [here](https://stackoverflow.com/questions/23681325/convert-canvas-to-pdf)
|
|
111
|
+
|
|
105
112
|
### Notes
|
|
113
|
+
|
|
106
114
|
- for optimal paper detection, the paper should be placed on a flat surface with a solid background color
|
|
107
|
-
- we recommend wrapping your code using `jscanify` in a window `load` event listener to ensure OpenCV is loaded
|
|
115
|
+
- we recommend wrapping your code using `jscanify` in a window `load` event listener to ensure OpenCV is loaded
|
package/docs/index.html
CHANGED
|
@@ -103,7 +103,7 @@ image.onload = function () {
|
|
|
103
103
|
It's that easy! Come check out the <a href="https://github.com/ColonelParrot/jscanify/wiki"
|
|
104
104
|
target="_blank">documentation</a>!
|
|
105
105
|
</div>
|
|
106
|
-
<script src="
|
|
106
|
+
<script src="../src/jscanify.js"></script>
|
|
107
107
|
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
|
|
108
108
|
<script src="script.js"></script>
|
|
109
109
|
|
package/docs/script.js
CHANGED
|
@@ -32,12 +32,11 @@ $('#demo-images .image-container').click(function () {
|
|
|
32
32
|
newImg.src = imageSrc
|
|
33
33
|
|
|
34
34
|
newImg.onload = function(){
|
|
35
|
-
scanner.extractPaper(newImg, 386, 500
|
|
36
|
-
|
|
35
|
+
const resultCanvas = scanner.extractPaper(newImg, 386, 500);
|
|
36
|
+
$('#demo-result').append(resultCanvas);
|
|
37
37
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
});
|
|
38
|
+
const highlightedCanvas = scanner.highlightPaper(newImg)
|
|
39
|
+
$('#demo-result').append(highlightedCanvas);
|
|
41
40
|
}
|
|
42
41
|
})
|
|
43
42
|
})
|
package/package.json
CHANGED
package/src/jscanify-node.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/*! jscanify v1.
|
|
1
|
+
/*! jscanify v1.2.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");
|
|
@@ -143,7 +143,7 @@ class jscanify {
|
|
|
143
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
145
|
*/
|
|
146
|
-
extractPaper(image, resultWidth, resultHeight,
|
|
146
|
+
extractPaper(image, resultWidth, resultHeight, cornerPoints) {
|
|
147
147
|
const canvas = createCanvas();
|
|
148
148
|
const img = cv.imread(image);
|
|
149
149
|
const maxContour = this.findPaperContour(img);
|
|
@@ -187,15 +187,10 @@ class jscanify {
|
|
|
187
187
|
);
|
|
188
188
|
|
|
189
189
|
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
190
|
|
|
196
191
|
img.delete();
|
|
197
192
|
warpedDst.delete();
|
|
198
|
-
|
|
193
|
+
return canvas;
|
|
199
194
|
}
|
|
200
195
|
|
|
201
196
|
/**
|
|
@@ -222,25 +217,25 @@ class jscanify {
|
|
|
222
217
|
for (let i = 0; i < contour.data32S.length; i += 2) {
|
|
223
218
|
const point = { x: contour.data32S[i], y: contour.data32S[i + 1] };
|
|
224
219
|
const dist = distance(point, center);
|
|
225
|
-
if (point.x < center.x && point.y
|
|
220
|
+
if (point.x < center.x && point.y < center.y) {
|
|
226
221
|
// top left
|
|
227
222
|
if (dist > topLeftCornerDist) {
|
|
228
223
|
topLeftCorner = point;
|
|
229
224
|
topLeftCornerDist = dist;
|
|
230
225
|
}
|
|
231
|
-
} else if (point.x > center.x && point.y
|
|
226
|
+
} else if (point.x > center.x && point.y < center.y) {
|
|
232
227
|
// top right
|
|
233
228
|
if (dist > topRightCornerDist) {
|
|
234
229
|
topRightCorner = point;
|
|
235
230
|
topRightCornerDist = dist;
|
|
236
231
|
}
|
|
237
|
-
} else if (point.x < center.x && point.y
|
|
232
|
+
} else if (point.x < center.x && point.y > center.y) {
|
|
238
233
|
// bottom left
|
|
239
234
|
if (dist > bottomLeftCornerDist) {
|
|
240
235
|
bottomLeftCorner = point;
|
|
241
236
|
bottomLeftCornerDist = dist;
|
|
242
237
|
}
|
|
243
|
-
} else if (point.x > center.x && point.y
|
|
238
|
+
} else if (point.x > center.x && point.y > center.y) {
|
|
244
239
|
// bottom right
|
|
245
240
|
if (dist > bottomRightCornerDist) {
|
|
246
241
|
bottomRightCorner = point;
|
package/src/jscanify.js
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
/*! jscanify v1.
|
|
1
|
+
/*! jscanify v1.2.0 | (c) ColonelParrot and other contributors | MIT License */
|
|
2
2
|
|
|
3
3
|
(function (global, factory) {
|
|
4
4
|
typeof exports === "object" && typeof module !== "undefined"
|
|
5
5
|
? (module.exports = factory())
|
|
6
6
|
: typeof define === "function" && define.amd
|
|
7
|
-
|
|
8
|
-
|
|
7
|
+
? define(factory)
|
|
8
|
+
: (global.jscanify = factory());
|
|
9
9
|
})(this, function () {
|
|
10
10
|
"use strict";
|
|
11
11
|
|
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
}
|
|
21
21
|
|
|
22
22
|
class jscanify {
|
|
23
|
-
constructor() {
|
|
23
|
+
constructor() {}
|
|
24
24
|
|
|
25
25
|
/**
|
|
26
26
|
* Finds the contour of the paper within the image
|
|
@@ -134,7 +134,7 @@
|
|
|
134
134
|
* @param {*} onComplete callback with `HTMLCanvasElement` passed - the unwarped paper
|
|
135
135
|
* @param {*} cornerPoints optional custom corner points, in case automatic corner points are incorrect
|
|
136
136
|
*/
|
|
137
|
-
extractPaper(image, resultWidth, resultHeight,
|
|
137
|
+
extractPaper(image, resultWidth, resultHeight, cornerPoints) {
|
|
138
138
|
const canvas = document.createElement("canvas");
|
|
139
139
|
|
|
140
140
|
const img = cv.imread(image);
|
|
@@ -185,33 +185,17 @@
|
|
|
185
185
|
|
|
186
186
|
cv.imshow(canvas, warpedDst);
|
|
187
187
|
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
// flip unwarped image
|
|
192
|
-
|
|
193
|
-
let ctx = canvas.getContext("2d");
|
|
194
|
-
ctx.clearRect(0, 0, canvas.width, canvas.height);
|
|
195
|
-
canvas.width = resultWidth;
|
|
196
|
-
canvas.height = resultHeight;
|
|
197
|
-
ctx.setTransform(1, 0, 0, -1, 0, canvas.height);
|
|
198
|
-
|
|
199
|
-
ctx.drawImage(newImg, 0, 0);
|
|
200
|
-
|
|
201
|
-
ctx.setTransform(1, 0, 0, 1, 0, 0);
|
|
202
|
-
|
|
203
|
-
img.delete();
|
|
204
|
-
warpedDst.delete();
|
|
205
|
-
onComplete(canvas);
|
|
206
|
-
};
|
|
188
|
+
img.delete()
|
|
189
|
+
warpedDst.delete()
|
|
190
|
+
return canvas;
|
|
207
191
|
}
|
|
208
|
-
|
|
192
|
+
|
|
209
193
|
/**
|
|
210
194
|
* Calculates the corner points of a contour.
|
|
211
195
|
* @param {*} contour contour from {@link findPaperContour}
|
|
212
196
|
* @returns object with properties `topLeftCorner`, `topRightCorner`, `bottomLeftCorner`, `bottomRightCorner`, each with `x` and `y` property
|
|
213
197
|
*/
|
|
214
|
-
|
|
198
|
+
getCornerPoints(contour) {
|
|
215
199
|
let rect = cv.minAreaRect(contour);
|
|
216
200
|
const center = rect.center;
|
|
217
201
|
|
|
@@ -230,25 +214,25 @@
|
|
|
230
214
|
for (let i = 0; i < contour.data32S.length; i += 2) {
|
|
231
215
|
const point = { x: contour.data32S[i], y: contour.data32S[i + 1] };
|
|
232
216
|
const dist = distance(point, center);
|
|
233
|
-
if (point.x < center.x && point.y
|
|
217
|
+
if (point.x < center.x && point.y < center.y) {
|
|
234
218
|
// top left
|
|
235
219
|
if (dist > topLeftCornerDist) {
|
|
236
220
|
topLeftCorner = point;
|
|
237
221
|
topLeftCornerDist = dist;
|
|
238
222
|
}
|
|
239
|
-
} else if (point.x > center.x && point.y
|
|
223
|
+
} else if (point.x > center.x && point.y < center.y) {
|
|
240
224
|
// top right
|
|
241
225
|
if (dist > topRightCornerDist) {
|
|
242
226
|
topRightCorner = point;
|
|
243
227
|
topRightCornerDist = dist;
|
|
244
228
|
}
|
|
245
|
-
} else if (point.x < center.x && point.y
|
|
229
|
+
} else if (point.x < center.x && point.y > center.y) {
|
|
246
230
|
// bottom left
|
|
247
231
|
if (dist > bottomLeftCornerDist) {
|
|
248
232
|
bottomLeftCorner = point;
|
|
249
233
|
bottomLeftCornerDist = dist;
|
|
250
234
|
}
|
|
251
|
-
} else if (point.x > center.x && point.y
|
|
235
|
+
} else if (point.x > center.x && point.y > center.y) {
|
|
252
236
|
// bottom right
|
|
253
237
|
if (dist > bottomRightCornerDist) {
|
|
254
238
|
bottomRightCorner = point;
|
|
@@ -264,8 +248,7 @@
|
|
|
264
248
|
bottomRightCorner,
|
|
265
249
|
};
|
|
266
250
|
}
|
|
267
|
-
|
|
268
251
|
}
|
|
269
252
|
|
|
270
253
|
return jscanify;
|
|
271
|
-
});
|
|
254
|
+
});
|
package/test/tests.js
CHANGED
|
@@ -1,8 +1,12 @@
|
|
|
1
|
+
/*
|
|
2
|
+
run tests with: npm test
|
|
3
|
+
*/
|
|
4
|
+
|
|
1
5
|
console.log("RUNNING JSCANIFY TESTS");
|
|
2
6
|
console.log("Warning: This may take a bit");
|
|
3
7
|
|
|
4
|
-
const {
|
|
5
|
-
const { writeFileSync, unlinkSync, existsSync } = require("fs");
|
|
8
|
+
const { loadImage, createCanvas } = require("canvas");
|
|
9
|
+
const { mkdirSync, writeFileSync, unlinkSync, existsSync } = require("fs");
|
|
6
10
|
const assert = require("assert");
|
|
7
11
|
|
|
8
12
|
const jscanify = require("../src/jscanify-node");
|
|
@@ -11,8 +15,11 @@ const path = require("path");
|
|
|
11
15
|
const outputPaths = {
|
|
12
16
|
highlight: __dirname + "/output/highlighted.jpg",
|
|
13
17
|
extracted: __dirname + "/output/extracted.jpg",
|
|
18
|
+
cornerPoints: __dirname + "/output/corner_points.jpg",
|
|
14
19
|
};
|
|
15
20
|
|
|
21
|
+
const baseFolder = __dirname.replaceAll("\\", "/") + "/output/";
|
|
22
|
+
|
|
16
23
|
const TEST_IMAGE_PATH = path.join(
|
|
17
24
|
__dirname,
|
|
18
25
|
"..",
|
|
@@ -30,6 +37,10 @@ function setup() {
|
|
|
30
37
|
unlinkSync(path);
|
|
31
38
|
}
|
|
32
39
|
});
|
|
40
|
+
|
|
41
|
+
if (!existsSync(baseFolder)) {
|
|
42
|
+
mkdirSync(baseFolder);
|
|
43
|
+
}
|
|
33
44
|
}
|
|
34
45
|
|
|
35
46
|
function test() {
|
|
@@ -39,7 +50,8 @@ function test() {
|
|
|
39
50
|
console.log("loading OpenCV.js...");
|
|
40
51
|
scanner.loadOpenCV(function (cv) {
|
|
41
52
|
console.log("Finished loading OpenCV.js");
|
|
42
|
-
|
|
53
|
+
console.log("Writing test images to: " + baseFolder);
|
|
54
|
+
describe("feature tests", function () {
|
|
43
55
|
it("should highlight paper", function (done) {
|
|
44
56
|
const highlighted = scanner.highlightPaper(testImage);
|
|
45
57
|
writeFileSync(
|
|
@@ -48,19 +60,53 @@ function test() {
|
|
|
48
60
|
);
|
|
49
61
|
|
|
50
62
|
assert.ok(existsSync(outputPaths.highlight));
|
|
51
|
-
done()
|
|
63
|
+
done();
|
|
52
64
|
});
|
|
53
65
|
|
|
54
66
|
it("should extract paper", function (done) {
|
|
55
|
-
scanner.extractPaper(testImage, 386, 500
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
67
|
+
const extracted = scanner.extractPaper(testImage, 386, 500);
|
|
68
|
+
writeFileSync(
|
|
69
|
+
outputPaths.extracted,
|
|
70
|
+
extracted.toBuffer("image/jpeg")
|
|
71
|
+
);
|
|
72
|
+
|
|
73
|
+
assert.ok(existsSync(outputPaths.extracted));
|
|
74
|
+
done();
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
it("should label corner points", function (done) {
|
|
78
|
+
const parsedImage = cv.imread(testImage);
|
|
79
|
+
const paperContour = scanner.findPaperContour(parsedImage);
|
|
80
|
+
const {
|
|
81
|
+
topLeftCorner,
|
|
82
|
+
topRightCorner,
|
|
83
|
+
bottomLeftCorner,
|
|
84
|
+
bottomRightCorner,
|
|
85
|
+
} = scanner.getCornerPoints(paperContour, testImage);
|
|
86
|
+
|
|
87
|
+
const canvas = createCanvas();
|
|
88
|
+
|
|
89
|
+
cv.imshow(canvas, parsedImage);
|
|
90
|
+
const ctx = canvas.getContext("2d");
|
|
91
|
+
const points = [
|
|
92
|
+
{ p: topLeftCorner, text: "top left corner" },
|
|
93
|
+
{ p: topRightCorner, text: "top right corner" },
|
|
94
|
+
{ p: bottomLeftCorner, text: "bottom left corner" },
|
|
95
|
+
{ p: bottomRightCorner, text: "bottom right corner" },
|
|
96
|
+
];
|
|
97
|
+
ctx.fillStyle = "cyan";
|
|
98
|
+
ctx.font = "25px serif";
|
|
99
|
+
points.forEach(({ p: point, text }) => {
|
|
100
|
+
ctx.beginPath();
|
|
101
|
+
ctx.arc(point.x, point.y, 15, 0, 2 * Math.PI, false);
|
|
102
|
+
ctx.fillText(text, point.x + 30, point.y)
|
|
103
|
+
ctx.fill();
|
|
63
104
|
});
|
|
105
|
+
|
|
106
|
+
writeFileSync(outputPaths.cornerPoints, canvas.toBuffer("image/jpeg"));
|
|
107
|
+
|
|
108
|
+
assert.ok(existsSync(outputPaths.cornerPoints));
|
|
109
|
+
done();
|
|
64
110
|
});
|
|
65
111
|
});
|
|
66
112
|
});
|