summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--big.pngbin0 -> 13062 bytes
-rw-r--r--example.jpgbin0 -> 82294 bytes
-rw-r--r--package.json2
-rw-r--r--scan.js202
4 files changed, 203 insertions, 1 deletions
diff --git a/big.png b/big.png
new file mode 100644
index 0000000..98e97de
--- /dev/null
+++ b/big.png
Binary files differ
diff --git a/example.jpg b/example.jpg
new file mode 100644
index 0000000..7900382
--- /dev/null
+++ b/example.jpg
Binary files differ
diff --git a/package.json b/package.json
index 6000771..b791717 100644
--- a/package.json
+++ b/package.json
@@ -9,6 +9,6 @@
"author": "Marvin Borner",
"license": "BSD",
"dependencies": {
- "opencv": "^6.2.0"
+ "opencv4nodejs": "^5.2.0"
}
}
diff --git a/scan.js b/scan.js
index e69de29..b01a65a 100644
--- a/scan.js
+++ b/scan.js
@@ -0,0 +1,202 @@
+const cv = require("opencv4nodejs");
+
+class FinderPattern {
+ constructor(a, b, c) {
+ this.topLeft = new cv.Point(a);
+ this.topRight = new cv.Point(b);
+ this.bottomLeft = new cv.Point(c);
+ }
+}
+
+function compareContourAreas(contour1, contour2) {
+ return contour1.area > contour2.area;
+}
+
+function getContourCenter(vec) {
+ let x = 0.0;
+ let y = 0.0;
+ for (let i = 0; i < vec.size(); i++) {
+ x += vec[i].x;
+ y += vec[i].y;
+ }
+ return new cv.Point(x / vec.size(), y / vec.size())
+}
+
+function isContourInsideContour(cont_in, cont_out) {
+ for (let i = 0; i < cont_in.size(); i++) {
+ if (cont_out.pointPolygonTest(cont_in[i]) <= 0) return false;
+ }
+ return true;
+}
+
+function findLimitedContours(contour, minPix, maxPix) {
+ const contours = contour.findContours(cv.RETR_TREE, cv.CHAIN_APPROX_SIMPLE);
+ console.log(contours.size());
+ let m = 0;
+ while (m < contours.size()) {
+ if (contours[m].area <= minPix) {
+ contours.erase(contours.begin())
+ } else if (contours[m].area > maxPix) {
+ contours.erase(contours.begin())
+ } else ++m;
+ }
+ console.log(contours.size());
+ return contours;
+}
+
+function getContourPair(contours) {
+ let vectorPair = [];
+ let flags = Array(contours.size()).fill(false);
+ for (let i = 0; i < contours.size() - 1; i++) {
+ if (flags[i]) continue;
+ let temp = new cv.Contour();
+ temp.push_back(contours[i]);
+ for (let j = i + 1; j < contours.size(); j++) {
+ if (isContourInsideContour(contours[j], contours[i])) {
+ temp.push_back(contours[j]);
+ flags[j] = true;
+ }
+ }
+ if (temp.size() > 1) {
+ vectorPair.push(temp)
+ }
+ }
+ flags.clear();
+ for (let i = 0; i < vectorPair.length; i++) {
+ vectorPair[i] = vectorPair[i].sort((a, b) => compareContourAreas(a, b))
+ }
+ return vectorPair;
+}
+
+function eliminatePairs(vectorPair, minRatio, maxRatio) {
+ console.log(maxRatio);
+ let flag = false;
+ let m = 0;
+ while (m < vectorPair.size()) {
+ flag = false;
+ if (vectorPair[m].size() < 3) {
+ vectorPair.erase(vectorPair.begin() + m);
+ continue;
+ }
+ for (let i = 0; i < vectorPair[m].size() - 1; i++) {
+ const area1 = vectorPair[m][i].area;
+ const area2 = vectorPair[m][i + 1].area;
+ if (area1 / area2 < minRatio || area1 / area2 > maxRatio) {
+ vectorPair.erase(vectorPair.begin() + m);
+ flag = true;
+ break;
+ }
+ }
+ if (!flag) {
+ ++m;
+ }
+ }
+ if (vectorPair.size() > 3) {
+ eliminatePairs(vectorPair, minRatio, maxRatio * 0.9)
+ }
+}
+
+function getDistance(a, b) {
+ return Math.sqrt(((a.x - b.x) ** 2) + (a.y - b.y) ** 2);
+}
+
+function getFinderPattern(vectorPair) {
+ const point1 = getContourCenter(vectorPair[0][vectorPair[0].size() - 1]);
+ const point2 = getContourCenter(vectorPair[1][vectorPair[1].size() - 1]);
+ const point3 = getContourCenter(vectorPair[2][vectorPair[2].size() - 1]);
+ const distance1 = getDistance(point1, point2);
+ const distance2 = getDistance(point1, point3);
+ const distance3 = getDistance(point2, point3);
+ let x1, y1, x2, y2, x3, y3, p1, p2, p3;
+ const Max = Math.max.apply(Math, [distance1, distance2, distance3]);
+ if (Max === distance1) {
+ p1 = point1;
+ p2 = point2;
+ p3 = point3;
+ } else if (Max === distance2) {
+ p1 = point1;
+ p2 = point3;
+ p3 = point2;
+ } else {
+ p1 = point2;
+ p2 = point3;
+ p3 = point1;
+ }
+ x1 = p1.x;
+ y1 = p1.y;
+ x2 = p2.x;
+ y2 = p2.y;
+ x3 = p3.x;
+ y3 = p3.y;
+ if(x1 === x2){
+ if(y1 > y2){
+ if(x3 < x1){
+ return FinderPattern(p3, p2, p1);
+ }else{
+ return FinderPattern(p3, p1, p2);
+ }
+ }else{
+ if(x3 < x1){
+ return FinderPattern(p3, p1, p2);
+ }else{
+ return FinderPattern(p3, p2, p1);
+ }
+ }
+ }else{
+ let newY = (y2 - y1) / (x2 - x1) * x3 + y1 - (y2 - y1) / (x2 - x1) * x1;
+ if(x1 > x2){
+ if(newY < y3){
+ return FinderPattern(p3, p2, p1);
+ }else{
+ return FinderPattern(p3, p1, p2);
+ }
+ }else{
+ if(newY < y3){
+ return FinderPattern(p3, p1, p2);
+ }else{
+ return FinderPattern(p3, p2, p1);
+ }
+ }
+ }
+}
+
+const ori = cv.imread("./example.jpg");
+const gray = ori.cvtColor(cv.COLOR_GRAY2BGR);
+let canny = new cv.Mat;
+gray.copyTo(canny);
+const bin = gray.threshold(0, 255, cv.THRESH_OTSU);
+let contour = new cv.Mat;
+bin.copyTo(contour);
+let contours = findLimitedContours(contour, 8.00, 0.2 * ori.cols * ori.rows);
+
+
+if(!contours.empty()) contours = contours.sort((a, b) => compareContourAreas(a, b));
+const vectorPair = getContourPair(contours);
+eliminatePairs(vectorPair, 1.0, 10.0);
+fPattern = getFinderPattern(vectorPair);
+let drawing = new cv.Mat;
+ori.copyTo(drawing);
+
+drawing.drawCircle(fPattern.topLeft, 3, new cv.Vec3(255, 0, 0), 2, 8, 0);
+drawing.drawCircle(fPattern.topRight, 3, new cv.Vec3(0,255,0), 2, 8, 0);
+drawing.drawCircle(fPattern.bottomLeft, 3, new cv.Vec3(0,0,255), 2, 8, 0);
+
+let vectorSource = [new cv.Point2(0, 0)];
+let vectorDestination = [new cv.Point2(0, 0)];
+vectorSource.push(fPattern.topLeft);
+vectorSource.push(fPattern.topRight);
+vectorSource.push(fPattern.bottomLeft);
+vectorDestination.push(new cv.Point2(20, 20));
+vectorDestination.push(new cv.Point2(120, 20));
+vectorDestination.push(new cv.Point2(20, 120));
+const affineTrans = cv.getAffineTransform(vectorSource, vectorDestination);
+const warped = ori.warpAffine(affineTrans, ori.size());
+const qrColor = warped(new cv.Rect(0, 0, 140, 140));
+const qrColorGray = qrColor.cvtColor(cv.COLOR_GRAY2RGB);
+const qrBin = qrColorGray.threshold(0, 255, cv.THRESH_OTSU);
+
+cv.imshow("binary", bin);
+cv.imshow("canny", canny);
+cv.imshow("finder patterns", drawing);
+cv.imshow("binaried qr code", qrBin);
+cv.waitKey();