diff options
-rw-r--r-- | big.png | bin | 0 -> 13062 bytes | |||
-rw-r--r-- | example.jpg | bin | 0 -> 82294 bytes | |||
-rw-r--r-- | package.json | 2 | ||||
-rw-r--r-- | scan.js | 202 |
4 files changed, 203 insertions, 1 deletions
Binary files differ diff --git a/example.jpg b/example.jpg Binary files differnew file mode 100644 index 0000000..7900382 --- /dev/null +++ b/example.jpg 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" } } @@ -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(); |