From caabcfd6679b67f9a2852ae1971516f1a4998f92 Mon Sep 17 00:00:00 2001 From: Jefff Nelson Date: Wed, 22 Apr 2020 03:54:14 -0700 Subject: [PATCH 1/5] Add fallback locator point recenter --- src/index.ts | 47 ++++++++++++++++++++-------------------- src/locator/index.ts | 51 +++++++++++++++++++++++++++++++++++++------- src/locator/test.ts | 12 +++++------ 3 files changed, 73 insertions(+), 37 deletions(-) diff --git a/src/index.ts b/src/index.ts index 65116049..c09f8cea 100644 --- a/src/index.ts +++ b/src/index.ts @@ -24,34 +24,35 @@ export interface QRCode { } function scan(matrix: BitMatrix): QRCode | null { - const location = locate(matrix); - if (!location) { + const locations = locate(matrix); + if (!locations) { return null; } - const extracted = extract(matrix, location); - const decoded = decode(extracted.matrix); - if (!decoded) { - return null; - } - - return { - binaryData: decoded.bytes, - data: decoded.text, - chunks: decoded.chunks, - location: { - topRightCorner: extracted.mappingFunction(location.dimension, 0), - topLeftCorner: extracted.mappingFunction(0, 0), - bottomRightCorner: extracted.mappingFunction(location.dimension, location.dimension), - bottomLeftCorner: extracted.mappingFunction(0, location.dimension), + for (const location of locations) { + const extracted = extract(matrix, location); + const decoded = decode(extracted.matrix); + if (decoded) { + return { + binaryData: decoded.bytes, + data: decoded.text, + chunks: decoded.chunks, + location: { + topRightCorner: extracted.mappingFunction(location.dimension, 0), + topLeftCorner: extracted.mappingFunction(0, 0), + bottomRightCorner: extracted.mappingFunction(location.dimension, location.dimension), + bottomLeftCorner: extracted.mappingFunction(0, location.dimension), - topRightFinderPattern: location.topRight, - topLeftFinderPattern: location.topLeft, - bottomLeftFinderPattern: location.bottomLeft, + topRightFinderPattern: location.topRight, + topLeftFinderPattern: location.topLeft, + bottomLeftFinderPattern: location.bottomLeft, - bottomRightAlignmentPattern: location.alignmentPattern, - }, - }; + bottomRightAlignmentPattern: location.alignmentPattern, + }, + }; + } + } + return null; } export interface Options { diff --git a/src/locator/index.ts b/src/locator/index.ts index 6d6ad146..a33aa691 100644 --- a/src/locator/index.ts +++ b/src/locator/index.ts @@ -212,6 +212,20 @@ function scorePattern(point: Point, ratios: number[], matrix: BitMatrix) { } } +function recenterLocation(matrix: BitMatrix, p: Point): Point { + let leftX = Math.round(p.x); + while (matrix.get(leftX, Math.round(p.y))) { + leftX--; + } + + let rightX = Math.round(p.x); + while (matrix.get(rightX, Math.round(p.y))) { + rightX++; + } + + return { x: (leftX + rightX) / 2, y: p.y }; +} + interface Quad { top: { startX: number; @@ -225,7 +239,7 @@ interface Quad { }; } -export function locate(matrix: BitMatrix): QRLocation { +export function locate(matrix: BitMatrix): QRLocation[] { const finderPatternQuads: Quad[] = []; let activeFinderPatternQuads: Quad[] = []; const alignmentPatternQuads: Quad[] = []; @@ -361,7 +375,34 @@ export function locate(matrix: BitMatrix): QRLocation { const { topRight, topLeft, bottomLeft } = reorderFinderPatterns( finderPatternGroups[0].points[0], finderPatternGroups[0].points[1], finderPatternGroups[0].points[2], ); + const { alignmentPattern, dimension } = findAlignmentPattern(matrix, alignmentPatternQuads, topRight, topLeft, bottomLeft); + + // We normally use the center of the quads as the location of the tracking points, which is optimal for most cases and will account + // for a skew in the image. However, In some cases, a slight skew might not be real and instead be caused by image compression + // errors and/or low resolution. For those cases, we'd be better off centering the point exactly in the middle of the black area. We + // compute and return the location data for the naively centered points as it is little additional work and allows for multiple + // attempts at decoding harder images. + const midTopRight = recenterLocation(matrix, topRight); + const midTopLeft = recenterLocation(matrix, topLeft); + const midBottomLeft = recenterLocation(matrix, bottomLeft); + const centeredAlignment = findAlignmentPattern(matrix, alignmentPatternQuads, midTopRight, midTopLeft, midBottomLeft); + + return [{ + alignmentPattern: { x: alignmentPattern.x, y: alignmentPattern.y }, + bottomLeft: {x: bottomLeft.x, y: bottomLeft.y }, + dimension, + topLeft: {x: topLeft.x, y: topLeft.y }, + topRight: {x: topRight.x, y: topRight.y }, + }, { + alignmentPattern: { x: centeredAlignment.alignmentPattern.x, y: centeredAlignment.alignmentPattern.y }, + bottomLeft: { x: midBottomLeft.x, y: midBottomLeft. y }, + topLeft: { x: midTopLeft.x, y: midTopLeft. y }, + topRight: { x: midTopRight.x, y: midTopRight. y }, + dimension: centeredAlignment.dimension, + }]; +} +function findAlignmentPattern(matrix: BitMatrix, alignmentPatternQuads: Quad[], topRight: Point, topLeft: Point, bottomLeft: Point) { // Now that we've found the three finder patterns we can determine the blockSize and the size of the QR code. // We'll use these to help find the alignment pattern but also later when we do the extraction. let dimension: number; @@ -405,11 +446,5 @@ export function locate(matrix: BitMatrix): QRLocation { // so we can only use our best guess. const alignmentPattern = modulesBetweenFinderPatterns >= 15 && alignmentPatterns.length ? alignmentPatterns[0] : expectedAlignmentPattern; - return { - alignmentPattern: { x: alignmentPattern.x, y: alignmentPattern.y }, - bottomLeft: {x: bottomLeft.x, y: bottomLeft.y }, - dimension, - topLeft: {x: topLeft.x, y: topLeft.y }, - topRight: {x: topRight.x, y: topRight.y }, - }; + return { alignmentPattern, dimension }; } diff --git a/src/locator/test.ts b/src/locator/test.ts index 11c4fea1..cc055c92 100644 --- a/src/locator/test.ts +++ b/src/locator/test.ts @@ -10,7 +10,7 @@ describe("locate", () => { it('locates a "perfect" image', async () => { const binarized = await loadBinarized("./src/locator/test-data/perfect.png"); - expect(locate(binarized)).toEqual({ + expect(locate(binarized)[0]).toEqual({ alignmentPattern: {x: 170.5, y: 170.5}, bottomLeft: {x: 3.5, y: 173.5}, dimension: 177, @@ -21,7 +21,7 @@ describe("locate", () => { it("locates a QR in a real world image", async () => { const binarized = await loadBinarized("./src/locator/test-data/real-world.png"); - expect(locate(binarized)).toEqual({ + expect(locate(binarized)[0]).toEqual({ alignmentPattern: { x: 264.25, y: 177 }, bottomLeft: { x: 195.5, y: 191.5 }, dimension: 33, @@ -32,7 +32,7 @@ describe("locate", () => { it("locates a small QR code in real world photo", async () => { const binarized = await loadBinarized("./src/locator/test-data/small-photo.png"); - expect(locate(binarized)).toEqual({ + expect(locate(binarized)[0]).toEqual({ alignmentPattern: { x: 103, y: 147.5 }, bottomLeft: { x: 73.5, y: 152 }, dimension: 29, @@ -43,7 +43,7 @@ describe("locate", () => { it("locates a extremely distored QR code", async () => { const binarized = await loadBinarized("./src/locator/test-data/distorted-extreme.png"); - expect(locate(binarized)).toEqual({ + expect(locate(binarized)[0]).toEqual({ alignmentPattern: { x: 164.5, y: 39 }, bottomLeft: { x: 221.5, y: 18.5 }, dimension: 25, @@ -54,7 +54,7 @@ describe("locate", () => { it("locates a damaged QR code and guesses the finder pattern location", async () => { const binarized = await loadBinarized("./src/locator/test-data/damaged.png"); - expect(locate(binarized)).toEqual({ + expect(locate(binarized)[0]).toEqual({ alignmentPattern: { x: 219.75, y: 221 }, bottomLeft: { x: 81.5, y: 215.5 }, dimension: 29, @@ -65,7 +65,7 @@ describe("locate", () => { it("locates a damaged QR code and guesses the finder pattern location", async () => { const binarized = await loadBinarized("./src/locator/test-data/damaged.png"); - expect(locate(binarized)).toEqual({ + expect(locate(binarized)[0]).toEqual({ alignmentPattern: { x: 219.75, y: 221 }, bottomLeft: { x: 81.5, y: 215.5 }, dimension: 29, From 738d4b99c295f9da31f59e22e3c698e3bde52a83 Mon Sep 17 00:00:00 2001 From: Jefff Nelson Date: Wed, 22 Apr 2020 04:04:55 -0700 Subject: [PATCH 2/5] Update end-to-end tests --- tests/end-to-end/148/output.json | 56 ++++++- tests/end-to-end/61/output.json | 144 ++++++++++++++++- tests/end-to-end/94/output.json | 204 ++++++++++++++++++++++++- tests/end-to-end/96/output.json | 204 ++++++++++++++++++++++++- tests/end-to-end/cupcake-2/output.json | 86 ++++++++++- tests/end-to-end/report.json | 14 +- 6 files changed, 696 insertions(+), 12 deletions(-) diff --git a/tests/end-to-end/148/output.json b/tests/end-to-end/148/output.json index ec747fa4..6922d0df 100644 --- a/tests/end-to-end/148/output.json +++ b/tests/end-to-end/148/output.json @@ -1 +1,55 @@ -null \ No newline at end of file +{ + "binaryData": [ + 49, + 50, + 51, + 52, + 53, + 54, + 55, + 56, + 57, + 48 + ], + "data": "1234567890", + "chunks": [ + { + "type": "numeric", + "text": "1234567890" + } + ], + "location": { + "topRightCorner": { + "x": 263.49999999999994, + "y": 292.49999999999994 + }, + "topLeftCorner": { + "x": 290.6357772807236, + "y": 234.69908351972865 + }, + "bottomRightCorner": { + "x": 207.16596877093724, + "y": 266.00026171089854 + }, + "bottomLeftCorner": { + "x": 233.5, + "y": 208.49999999999994 + }, + "topRightFinderPattern": { + "x": 258.5, + "y": 278.5 + }, + "topLeftFinderPattern": { + "x": 276.5, + "y": 240 + }, + "bottomLeftFinderPattern": { + "x": 238.5, + "y": 222.5 + }, + "bottomRightAlignmentPattern": { + "x": 232.6604148394045, + "y": 256.43984443522334 + } + } +} \ No newline at end of file diff --git a/tests/end-to-end/61/output.json b/tests/end-to-end/61/output.json index ec747fa4..71a079da 100644 --- a/tests/end-to-end/61/output.json +++ b/tests/end-to-end/61/output.json @@ -1 +1,143 @@ -null \ No newline at end of file +{ + "binaryData": [ + 71, + 111, + 111, + 103, + 108, + 101, + 32, + 80, + 114, + 105, + 110, + 116, + 32, + 65, + 100, + 115, + 32, + 45, + 32, + 84, + 46, + 71, + 46, + 73, + 46, + 65, + 46, + 70, + 46, + 32, + 45, + 32, + 74, + 97, + 110, + 117, + 97, + 114, + 121, + 32, + 51, + 49, + 44, + 32, + 50, + 48, + 48, + 56 + ], + "data": "Google Print Ads - T.G.I.A.F. - January 31, 2008", + "chunks": [ + { + "type": "byte", + "bytes": [ + 71, + 111, + 111, + 103, + 108, + 101, + 32, + 80, + 114, + 105, + 110, + 116, + 32, + 65, + 100, + 115, + 32, + 45, + 32, + 84, + 46, + 71, + 46, + 73, + 46, + 65, + 46, + 70, + 46, + 32, + 45, + 32, + 74, + 97, + 110, + 117, + 97, + 114, + 121, + 32, + 51, + 49, + 44, + 32, + 50, + 48, + 48, + 56 + ], + "text": "Google Print Ads - T.G.I.A.F. - January 31, 2008" + } + ], + "location": { + "topRightCorner": { + "x": 207.74905277460078, + "y": 35.92342050272172 + }, + "topLeftCorner": { + "x": 37.28418186638301, + "y": 40.82136923413848 + }, + "bottomRightCorner": { + "x": 199.730516256019, + "y": 207.08099785827298 + }, + "bottomLeftCorner": { + "x": 37.55287125013931, + "y": 204.8002517828075 + }, + "topRightFinderPattern": { + "x": 185.5, + "y": 58 + }, + "topLeftFinderPattern": { + "x": 57, + "y": 61 + }, + "bottomLeftFinderPattern": { + "x": 56.5, + "y": 186 + }, + "bottomRightAlignmentPattern": { + "x": 163.5, + "y": 170 + } + } +} \ No newline at end of file diff --git a/tests/end-to-end/94/output.json b/tests/end-to-end/94/output.json index ec747fa4..345292e1 100644 --- a/tests/end-to-end/94/output.json +++ b/tests/end-to-end/94/output.json @@ -1 +1,203 @@ -null \ No newline at end of file +{ + "binaryData": [ + 85, + 73, + 32, + 111, + 102, + 102, + 105, + 99, + 101, + 32, + 104, + 111, + 117, + 114, + 115, + 32, + 115, + 105, + 103, + 110, + 117, + 112, + 13, + 10, + 104, + 116, + 116, + 112, + 58, + 47, + 47, + 119, + 119, + 119, + 46, + 99, + 111, + 114, + 112, + 46, + 103, + 111, + 111, + 103, + 108, + 101, + 46, + 99, + 111, + 109, + 47, + 115, + 112, + 97, + 114, + 114, + 111, + 119, + 47, + 117, + 105, + 95, + 111, + 102, + 102, + 105, + 99, + 101, + 95, + 104, + 111, + 117, + 114, + 115, + 47, + 32, + 13, + 10 + ], + "data": "UI office hours signup\r\nhttp://www.corp.google.com/sparrow/ui_office_hours/ \r\n", + "chunks": [ + { + "type": "byte", + "bytes": [ + 85, + 73, + 32, + 111, + 102, + 102, + 105, + 99, + 101, + 32, + 104, + 111, + 117, + 114, + 115, + 32, + 115, + 105, + 103, + 110, + 117, + 112, + 13, + 10, + 104, + 116, + 116, + 112, + 58, + 47, + 47, + 119, + 119, + 119, + 46, + 99, + 111, + 114, + 112, + 46, + 103, + 111, + 111, + 103, + 108, + 101, + 46, + 99, + 111, + 109, + 47, + 115, + 112, + 97, + 114, + 114, + 111, + 119, + 47, + 117, + 105, + 95, + 111, + 102, + 102, + 105, + 99, + 101, + 95, + 104, + 111, + 117, + 114, + 115, + 47, + 32, + 13, + 10 + ], + "text": "UI office hours signup\r\nhttp://www.corp.google.com/sparrow/ui_office_hours/ \r\n" + } + ], + "location": { + "topRightCorner": { + "x": 209.43221529461573, + "y": 29.431156910287495 + }, + "topLeftCorner": { + "x": 29.145842438461127, + "y": 26.288965769230416 + }, + "bottomRightCorner": { + "x": 195.7418483157054, + "y": 169.63160944945972 + }, + "bottomLeftCorner": { + "x": 44.50153747551136, + "y": 168.25769867169186 + }, + "topRightFinderPattern": { + "x": 194.5, + "y": 42 + }, + "topLeftFinderPattern": { + "x": 44.5, + "y": 39.5 + }, + "bottomLeftFinderPattern": { + "x": 55.5, + "y": 159 + }, + "bottomRightAlignmentPattern": { + "x": 175.25, + "y": 152 + } + } +} \ No newline at end of file diff --git a/tests/end-to-end/96/output.json b/tests/end-to-end/96/output.json index ec747fa4..0b9f6805 100644 --- a/tests/end-to-end/96/output.json +++ b/tests/end-to-end/96/output.json @@ -1 +1,203 @@ -null \ No newline at end of file +{ + "binaryData": [ + 85, + 73, + 32, + 111, + 102, + 102, + 105, + 99, + 101, + 32, + 104, + 111, + 117, + 114, + 115, + 32, + 115, + 105, + 103, + 110, + 117, + 112, + 13, + 10, + 104, + 116, + 116, + 112, + 58, + 47, + 47, + 119, + 119, + 119, + 46, + 99, + 111, + 114, + 112, + 46, + 103, + 111, + 111, + 103, + 108, + 101, + 46, + 99, + 111, + 109, + 47, + 115, + 112, + 97, + 114, + 114, + 111, + 119, + 47, + 117, + 105, + 95, + 111, + 102, + 102, + 105, + 99, + 101, + 95, + 104, + 111, + 117, + 114, + 115, + 47, + 32, + 13, + 10 + ], + "data": "UI office hours signup\r\nhttp://www.corp.google.com/sparrow/ui_office_hours/ \r\n", + "chunks": [ + { + "type": "byte", + "bytes": [ + 85, + 73, + 32, + 111, + 102, + 102, + 105, + 99, + 101, + 32, + 104, + 111, + 117, + 114, + 115, + 32, + 115, + 105, + 103, + 110, + 117, + 112, + 13, + 10, + 104, + 116, + 116, + 112, + 58, + 47, + 47, + 119, + 119, + 119, + 46, + 99, + 111, + 114, + 112, + 46, + 103, + 111, + 111, + 103, + 108, + 101, + 46, + 99, + 111, + 109, + 47, + 115, + 112, + 97, + 114, + 114, + 111, + 119, + 47, + 117, + 105, + 95, + 111, + 102, + 102, + 105, + 99, + 101, + 95, + 104, + 111, + 117, + 114, + 115, + 47, + 32, + 13, + 10 + ], + "text": "UI office hours signup\r\nhttp://www.corp.google.com/sparrow/ui_office_hours/ \r\n" + } + ], + "location": { + "topRightCorner": { + "x": 172.95121886567557, + "y": 56.88733241612363 + }, + "topLeftCorner": { + "x": 62.51835881359606, + "y": 41.665152482731365 + }, + "bottomRightCorner": { + "x": 163.76170764727038, + "y": 189.3912966928725 + }, + "bottomLeftCorner": { + "x": 53.702422326956096, + "y": 201.05378793308296 + }, + "topRightFinderPattern": { + "x": 165, + "y": 66.5 + }, + "topLeftFinderPattern": { + "x": 72, + "y": 55.5 + }, + "bottomLeftFinderPattern": { + "x": 64.5, + "y": 188 + }, + "bottomRightAlignmentPattern": { + "x": 151.5, + "y": 171.5 + } + } +} \ No newline at end of file diff --git a/tests/end-to-end/cupcake-2/output.json b/tests/end-to-end/cupcake-2/output.json index ec747fa4..850d3630 100644 --- a/tests/end-to-end/cupcake-2/output.json +++ b/tests/end-to-end/cupcake-2/output.json @@ -1 +1,85 @@ -null \ No newline at end of file +{ + "binaryData": [ + 104, + 116, + 116, + 112, + 58, + 47, + 47, + 103, + 101, + 111, + 108, + 111, + 113, + 105, + 46, + 99, + 111, + 109, + 47 + ], + "data": "http://geoloqi.com/", + "chunks": [ + { + "type": "byte", + "bytes": [ + 104, + 116, + 116, + 112, + 58, + 47, + 47, + 103, + 101, + 111, + 108, + 111, + 113, + 105, + 46, + 99, + 111, + 109, + 47 + ], + "text": "http://geoloqi.com/" + } + ], + "location": { + "topRightCorner": { + "x": 127.46105750170044, + "y": 34.76208030378532 + }, + "topLeftCorner": { + "x": 41.94779823920208, + "y": 105.74971229104122 + }, + "bottomRightCorner": { + "x": 189.69872798105325, + "y": 96.54143904933944 + }, + "bottomLeftCorner": { + "x": 99.62052210326115, + "y": 177.4448242207865 + }, + "topRightFinderPattern": { + "x": 124, + "y": 52.5 + }, + "topLeftFinderPattern": { + "x": 62, + "y": 104.5 + }, + "bottomLeftFinderPattern": { + "x": 104, + "y": 155 + }, + "bottomRightAlignmentPattern": { + "x": 150.25, + "y": 99 + } + } +} \ No newline at end of file diff --git a/tests/end-to-end/report.json b/tests/end-to-end/report.json index 24fb4f5b..9e99945a 100644 --- a/tests/end-to-end/report.json +++ b/tests/end-to-end/report.json @@ -1,7 +1,7 @@ { "counts": { - "failed": 47, - "successful": 207 + "failed": 42, + "successful": 212 }, "tests": { "0": true, @@ -64,7 +64,7 @@ "58": true, "59": true, "60": true, - "61": false, + "61": true, "62": true, "63": true, "64": true, @@ -97,9 +97,9 @@ "91": true, "92": false, "93": true, - "94": false, + "94": true, "95": true, - "96": false, + "96": true, "97": true, "98": true, "99": true, @@ -151,7 +151,7 @@ "145": true, "146": true, "147": true, - "148": false, + "148": true, "149": true, "150": false, "151": true, @@ -207,7 +207,7 @@ "chevy-ad": true, "cupake-5": false, "cupcake-1": true, - "cupcake-2": false, + "cupcake-2": true, "cupcake-3": true, "cupcake-4": true, "damaged": false, From a52cda51666afa1478682e27d79f192c443d3ddb Mon Sep 17 00:00:00 2001 From: Jefff Nelson Date: Wed, 22 Apr 2020 20:25:36 -0700 Subject: [PATCH 3/5] Add vertical centering also --- src/locator/index.ts | 14 +++- tests/end-to-end/148/output.json | 24 +++--- tests/end-to-end/61/output.json | 22 ++--- tests/end-to-end/82/output.json | 110 ++++++++++++++++++++++++- tests/end-to-end/92/output.json | 56 ++++++++++++- tests/end-to-end/94/output.json | 22 ++--- tests/end-to-end/96/output.json | 20 ++--- tests/end-to-end/cupcake-2/output.json | 20 ++--- tests/end-to-end/report.json | 8 +- 9 files changed, 234 insertions(+), 62 deletions(-) diff --git a/src/locator/index.ts b/src/locator/index.ts index a33aa691..b22c4442 100644 --- a/src/locator/index.ts +++ b/src/locator/index.ts @@ -217,13 +217,23 @@ function recenterLocation(matrix: BitMatrix, p: Point): Point { while (matrix.get(leftX, Math.round(p.y))) { leftX--; } - let rightX = Math.round(p.x); while (matrix.get(rightX, Math.round(p.y))) { rightX++; } + const x = (leftX + rightX) / 2; + + let topY = Math.round(p.y); + while (matrix.get(Math.round(x), topY)) { + topY--; + } + let bottomY = Math.round(p.y); + while (matrix.get(Math.round(x), bottomY)) { + bottomY++; + } + const y = (topY + bottomY) / 2; - return { x: (leftX + rightX) / 2, y: p.y }; + return { x, y }; } interface Quad { diff --git a/tests/end-to-end/148/output.json b/tests/end-to-end/148/output.json index 6922d0df..4df0ed05 100644 --- a/tests/end-to-end/148/output.json +++ b/tests/end-to-end/148/output.json @@ -21,35 +21,35 @@ "location": { "topRightCorner": { "x": 263.49999999999994, - "y": 292.49999999999994 + "y": 291.375 }, "topLeftCorner": { - "x": 290.6357772807236, - "y": 234.69908351972865 + "x": 290.79762807324306, + "y": 234.26604686604492 }, "bottomRightCorner": { - "x": 207.16596877093724, - "y": 266.00026171089854 + "x": 207.9274477212262, + "y": 264.60245217347966 }, "bottomLeftCorner": { - "x": 233.5, - "y": 208.49999999999994 + "x": 233.49999999999997, + "y": 208.125 }, "topRightFinderPattern": { "x": 258.5, - "y": 278.5 + "y": 277.5 }, "topLeftFinderPattern": { "x": 276.5, - "y": 240 + "y": 239.5 }, "bottomLeftFinderPattern": { "x": 238.5, - "y": 222.5 + "y": 222 }, "bottomRightAlignmentPattern": { - "x": 232.6604148394045, - "y": 256.43984443522334 + "x": 232.84618068755663, + "y": 255.48041599830515 } } } \ No newline at end of file diff --git a/tests/end-to-end/61/output.json b/tests/end-to-end/61/output.json index 71a079da..5808bccc 100644 --- a/tests/end-to-end/61/output.json +++ b/tests/end-to-end/61/output.json @@ -108,32 +108,32 @@ ], "location": { "topRightCorner": { - "x": 207.74905277460078, - "y": 35.92342050272172 + "x": 207.7810586662029, + "y": 35.39166271880641 }, "topLeftCorner": { - "x": 37.28418186638301, - "y": 40.82136923413848 + "x": 37.31970355694221, + "y": 39.672933251301814 }, "bottomRightCorner": { - "x": 199.730516256019, - "y": 207.08099785827298 + "x": 199.80450482609717, + "y": 207.49761061850413 }, "bottomLeftCorner": { - "x": 37.55287125013931, - "y": 204.8002517828075 + "x": 37.57948911702004, + "y": 204.27384025598016 }, "topRightFinderPattern": { "x": 185.5, - "y": 58 + "y": 57.5 }, "topLeftFinderPattern": { "x": 57, - "y": 61 + "y": 60 }, "bottomLeftFinderPattern": { "x": 56.5, - "y": 186 + "y": 185.5 }, "bottomRightAlignmentPattern": { "x": 163.5, diff --git a/tests/end-to-end/82/output.json b/tests/end-to-end/82/output.json index ec747fa4..942fd023 100644 --- a/tests/end-to-end/82/output.json +++ b/tests/end-to-end/82/output.json @@ -1 +1,109 @@ -null \ No newline at end of file +{ + "binaryData": [ + 104, + 116, + 116, + 112, + 58, + 47, + 47, + 99, + 111, + 100, + 101, + 46, + 103, + 111, + 111, + 103, + 108, + 101, + 46, + 99, + 111, + 109, + 47, + 112, + 47, + 122, + 120, + 105, + 110, + 103, + 47 + ], + "data": "http://code.google.com/p/zxing/", + "chunks": [ + { + "type": "byte", + "bytes": [ + 104, + 116, + 116, + 112, + 58, + 47, + 47, + 99, + 111, + 100, + 101, + 46, + 103, + 111, + 111, + 103, + 108, + 101, + 46, + 99, + 111, + 109, + 47, + 112, + 47, + 122, + 120, + 105, + 110, + 103, + 47 + ], + "text": "http://code.google.com/p/zxing/" + } + ], + "location": { + "topRightCorner": { + "x": 161.6951269176281, + "y": 61.745332923199555 + }, + "topLeftCorner": { + "x": 77.03759523249865, + "y": 51.8409703690389 + }, + "bottomRightCorner": { + "x": 147.85112998168927, + "y": 146.5334877662124 + }, + "bottomLeftCorner": { + "x": 67.25061775575521, + "y": 142.30094367891465 + }, + "topRightFinderPattern": { + "x": 148.5, + "y": 73 + }, + "topLeftFinderPattern": { + "x": 88, + "y": 66.5 + }, + "bottomLeftFinderPattern": { + "x": 80.5, + "y": 131 + }, + "bottomRightAlignmentPattern": { + "x": 131, + "y": 124 + } + } +} \ No newline at end of file diff --git a/tests/end-to-end/92/output.json b/tests/end-to-end/92/output.json index ec747fa4..e0930425 100644 --- a/tests/end-to-end/92/output.json +++ b/tests/end-to-end/92/output.json @@ -1 +1,55 @@ -null \ No newline at end of file +{ + "binaryData": [ + 50, + 48, + 50, + 49, + 50, + 48, + 48, + 48, + 48, + 48 + ], + "data": "2021200000", + "chunks": [ + { + "type": "numeric", + "text": "2021200000" + } + ], + "location": { + "topRightCorner": { + "x": 190.37500000000003, + "y": 123.24999999999999 + }, + "topLeftCorner": { + "x": 140.82437472806706, + "y": 120.1954804763799 + }, + "bottomRightCorner": { + "x": 189.37498917656237, + "y": 172.48075757475945 + }, + "bottomLeftCorner": { + "x": 140.125, + "y": 169.74999999999997 + }, + "topRightFinderPattern": { + "x": 182, + "y": 131 + }, + "topLeftFinderPattern": { + "x": 149, + "y": 129 + }, + "bottomLeftFinderPattern": { + "x": 148.5, + "y": 162 + }, + "bottomRightAlignmentPattern": { + "x": 174.47561880331884, + "y": 156.4352817881895 + } + } +} \ No newline at end of file diff --git a/tests/end-to-end/94/output.json b/tests/end-to-end/94/output.json index 345292e1..4190200c 100644 --- a/tests/end-to-end/94/output.json +++ b/tests/end-to-end/94/output.json @@ -168,32 +168,32 @@ ], "location": { "topRightCorner": { - "x": 209.43221529461573, - "y": 29.431156910287495 + "x": 209.51355745730348, + "y": 28.862689046730146 }, "topLeftCorner": { - "x": 29.145842438461127, - "y": 26.288965769230416 + "x": 29.23676041336614, + "y": 25.808825443291582 }, "bottomRightCorner": { - "x": 195.7418483157054, - "y": 169.63160944945972 + "x": 195.8683092978863, + "y": 169.81926539702602 }, "bottomLeftCorner": { - "x": 44.50153747551136, - "y": 168.25769867169186 + "x": 44.55958093441372, + "y": 167.70884194729203 }, "topRightFinderPattern": { "x": 194.5, - "y": 42 + "y": 41.5 }, "topLeftFinderPattern": { "x": 44.5, - "y": 39.5 + "y": 39 }, "bottomLeftFinderPattern": { "x": 55.5, - "y": 159 + "y": 158.5 }, "bottomRightAlignmentPattern": { "x": 175.25, diff --git a/tests/end-to-end/96/output.json b/tests/end-to-end/96/output.json index 0b9f6805..dfae6abe 100644 --- a/tests/end-to-end/96/output.json +++ b/tests/end-to-end/96/output.json @@ -168,20 +168,20 @@ ], "location": { "topRightCorner": { - "x": 172.95121886567557, - "y": 56.88733241612363 + "x": 172.95423865294515, + "y": 56.92325495516057 }, "topLeftCorner": { - "x": 62.51835881359606, - "y": 41.665152482731365 + "x": 62.52318449791687, + "y": 41.112591119588885 }, "bottomRightCorner": { - "x": 163.76170764727038, - "y": 189.3912966928725 + "x": 163.7681469521871, + "y": 189.47785056515463 }, "bottomLeftCorner": { - "x": 53.702422326956096, - "y": 201.05378793308296 + "x": 53.70662900165528, + "y": 200.49500388855432 }, "topRightFinderPattern": { "x": 165, @@ -189,11 +189,11 @@ }, "topLeftFinderPattern": { "x": 72, - "y": 55.5 + "y": 55 }, "bottomLeftFinderPattern": { "x": 64.5, - "y": 188 + "y": 187.5 }, "bottomRightAlignmentPattern": { "x": 151.5, diff --git a/tests/end-to-end/cupcake-2/output.json b/tests/end-to-end/cupcake-2/output.json index 850d3630..cb0c1c4b 100644 --- a/tests/end-to-end/cupcake-2/output.json +++ b/tests/end-to-end/cupcake-2/output.json @@ -50,24 +50,24 @@ ], "location": { "topRightCorner": { - "x": 127.46105750170044, - "y": 34.76208030378532 + "x": 127.46497636162992, + "y": 34.06874732856506 }, "topLeftCorner": { - "x": 41.94779823920208, - "y": 105.74971229104122 + "x": 41.953364086855075, + "y": 105.74936541101754 }, "bottomRightCorner": { - "x": 189.69872798105325, - "y": 96.54143904933944 + "x": 189.71164362381936, + "y": 96.54063410843052 }, "bottomLeftCorner": { - "x": 99.62052210326115, - "y": 177.4448242207865 + "x": 99.62566806959562, + "y": 178.13716773984265 }, "topRightFinderPattern": { "x": 124, - "y": 52.5 + "y": 52 }, "topLeftFinderPattern": { "x": 62, @@ -75,7 +75,7 @@ }, "bottomLeftFinderPattern": { "x": 104, - "y": 155 + "y": 155.5 }, "bottomRightAlignmentPattern": { "x": 150.25, diff --git a/tests/end-to-end/report.json b/tests/end-to-end/report.json index 9e99945a..c8cfddc5 100644 --- a/tests/end-to-end/report.json +++ b/tests/end-to-end/report.json @@ -1,7 +1,7 @@ { "counts": { - "failed": 42, - "successful": 212 + "failed": 40, + "successful": 214 }, "tests": { "0": true, @@ -85,7 +85,7 @@ "79": true, "80": false, "81": false, - "82": false, + "82": true, "83": true, "84": true, "85": false, @@ -95,7 +95,7 @@ "89": true, "90": true, "91": true, - "92": false, + "92": true, "93": true, "94": true, "95": true, From 49eca1af2ccea5226e080d2d1ada85fb81b1e2a9 Mon Sep 17 00:00:00 2001 From: Jefff Nelson Date: Wed, 22 Apr 2020 22:34:48 -0700 Subject: [PATCH 4/5] Add a test for locator fallback --- src/locator/index.ts | 39 ++++++++++++++++++----------- src/locator/test-data/odd-skew.png | Bin 0 -> 5963 bytes src/locator/test.ts | 11 ++++++++ 3 files changed, 36 insertions(+), 14 deletions(-) create mode 100644 src/locator/test-data/odd-skew.png diff --git a/src/locator/index.ts b/src/locator/index.ts index b22c4442..d837fed1 100644 --- a/src/locator/index.ts +++ b/src/locator/index.ts @@ -385,7 +385,17 @@ export function locate(matrix: BitMatrix): QRLocation[] { const { topRight, topLeft, bottomLeft } = reorderFinderPatterns( finderPatternGroups[0].points[0], finderPatternGroups[0].points[1], finderPatternGroups[0].points[2], ); - const { alignmentPattern, dimension } = findAlignmentPattern(matrix, alignmentPatternQuads, topRight, topLeft, bottomLeft); + const alignment = findAlignmentPattern(matrix, alignmentPatternQuads, topRight, topLeft, bottomLeft); + const result: QRLocation[] = []; + if (alignment) { + result.push({ + alignmentPattern: { x: alignment.alignmentPattern.x, y: alignment.alignmentPattern.y }, + bottomLeft: {x: bottomLeft.x, y: bottomLeft.y }, + dimension: alignment.dimension, + topLeft: {x: topLeft.x, y: topLeft.y }, + topRight: {x: topRight.x, y: topRight.y }, + }); + } // We normally use the center of the quads as the location of the tracking points, which is optimal for most cases and will account // for a skew in the image. However, In some cases, a slight skew might not be real and instead be caused by image compression @@ -396,20 +406,21 @@ export function locate(matrix: BitMatrix): QRLocation[] { const midTopLeft = recenterLocation(matrix, topLeft); const midBottomLeft = recenterLocation(matrix, bottomLeft); const centeredAlignment = findAlignmentPattern(matrix, alignmentPatternQuads, midTopRight, midTopLeft, midBottomLeft); + if (centeredAlignment) { + result.push({ + alignmentPattern: { x: centeredAlignment.alignmentPattern.x, y: centeredAlignment.alignmentPattern.y }, + bottomLeft: { x: midBottomLeft.x, y: midBottomLeft. y }, + topLeft: { x: midTopLeft.x, y: midTopLeft. y }, + topRight: { x: midTopRight.x, y: midTopRight. y }, + dimension: centeredAlignment.dimension, + }); + } + + if (result.length === 0) { + return null; + } - return [{ - alignmentPattern: { x: alignmentPattern.x, y: alignmentPattern.y }, - bottomLeft: {x: bottomLeft.x, y: bottomLeft.y }, - dimension, - topLeft: {x: topLeft.x, y: topLeft.y }, - topRight: {x: topRight.x, y: topRight.y }, - }, { - alignmentPattern: { x: centeredAlignment.alignmentPattern.x, y: centeredAlignment.alignmentPattern.y }, - bottomLeft: { x: midBottomLeft.x, y: midBottomLeft. y }, - topLeft: { x: midTopLeft.x, y: midTopLeft. y }, - topRight: { x: midTopRight.x, y: midTopRight. y }, - dimension: centeredAlignment.dimension, - }]; + return result; } function findAlignmentPattern(matrix: BitMatrix, alignmentPatternQuads: Quad[], topRight: Point, topLeft: Point, bottomLeft: Point) { diff --git a/src/locator/test-data/odd-skew.png b/src/locator/test-data/odd-skew.png new file mode 100644 index 0000000000000000000000000000000000000000..ce0ccd5cc998b0bbe694497b54004d983b76f6ec GIT binary patch literal 5963 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 Xa_GrC{4IC^uH)u<=%Xr^|4jKW8<>q; literal 0 HcmV?d00001 diff --git a/src/locator/test.ts b/src/locator/test.ts index cc055c92..cf610350 100644 --- a/src/locator/test.ts +++ b/src/locator/test.ts @@ -79,4 +79,15 @@ describe("locate", () => { const binarized = await loadBinarized("./src/locator/test-data/malformed-infinity.png"); expect(locate(binarized)).toEqual(null); }); + + it("returns a centered alignment as a fallback", async () => { + const binarized = await loadBinarized("./src/locator/test-data/odd-skew.png"); + expect(locate(binarized)[1]).toEqual({ + alignmentPattern: { x: 163.5, y: 170 }, + bottomLeft: { x: 56.5, y: 185.5 }, + dimension: 29, + topLeft: { x: 57, y: 60 }, + topRight: { x: 185.5, y: 57.5 }, + }); + }); }); From 29aa08631c5fb7a2547b2b0fb4e206deb7afd651 Mon Sep 17 00:00:00 2001 From: Jefff Nelson Date: Tue, 28 Apr 2020 19:58:38 -0700 Subject: [PATCH 5/5] Bump to 1.3.0 and rebuild --- dist/decoder/decodeData/index.d.ts | 2 +- dist/jsQR.js | 148 ++++++++++++++++++++--------- dist/locator/index.d.ts | 2 +- docs/jsQR.js | 148 ++++++++++++++++++++--------- package.json | 2 +- 5 files changed, 209 insertions(+), 93 deletions(-) diff --git a/dist/decoder/decodeData/index.d.ts b/dist/decoder/decodeData/index.d.ts index 732b0f9b..83fb8935 100644 --- a/dist/decoder/decodeData/index.d.ts +++ b/dist/decoder/decodeData/index.d.ts @@ -21,6 +21,6 @@ export declare enum Mode { Alphanumeric = "alphanumeric", Byte = "byte", Kanji = "kanji", - ECI = "eci", + ECI = "eci" } export declare function decode(data: Uint8ClampedArray, version: number): DecodedQR; diff --git a/dist/jsQR.js b/dist/jsQR.js index f749098d..2e756b2c 100644 --- a/dist/jsQR.js +++ b/dist/jsQR.js @@ -225,6 +225,7 @@ var GenericGFPoly = /** @class */ (function () { return this.coefficients[this.coefficients.length - 1 - degree]; }; GenericGFPoly.prototype.addOrSubtract = function (other) { + var _a; if (this.isZero()) { return other; } @@ -245,7 +246,6 @@ var GenericGFPoly = /** @class */ (function () { sumDiff[i] = GenericGF_1.addOrSubtractGF(smallerCoefficients[i - lengthDiff], largerCoefficients[i]); } return new GenericGFPoly(this.field, sumDiff); - var _a; }; GenericGFPoly.prototype.multiply = function (scalar) { if (scalar === 0) { @@ -329,30 +329,33 @@ var decoder_1 = __webpack_require__(5); var extractor_1 = __webpack_require__(11); var locator_1 = __webpack_require__(12); function scan(matrix) { - var location = locator_1.locate(matrix); - if (!location) { + var locations = locator_1.locate(matrix); + if (!locations) { return null; } - var extracted = extractor_1.extract(matrix, location); - var decoded = decoder_1.decode(extracted.matrix); - if (!decoded) { - return null; + for (var _i = 0, locations_1 = locations; _i < locations_1.length; _i++) { + var location_1 = locations_1[_i]; + var extracted = extractor_1.extract(matrix, location_1); + var decoded = decoder_1.decode(extracted.matrix); + if (decoded) { + return { + binaryData: decoded.bytes, + data: decoded.text, + chunks: decoded.chunks, + location: { + topRightCorner: extracted.mappingFunction(location_1.dimension, 0), + topLeftCorner: extracted.mappingFunction(0, 0), + bottomRightCorner: extracted.mappingFunction(location_1.dimension, location_1.dimension), + bottomLeftCorner: extracted.mappingFunction(0, location_1.dimension), + topRightFinderPattern: location_1.topRight, + topLeftFinderPattern: location_1.topLeft, + bottomLeftFinderPattern: location_1.bottomLeft, + bottomRightAlignmentPattern: location_1.alignmentPattern, + }, + }; + } } - return { - binaryData: decoded.bytes, - data: decoded.text, - chunks: decoded.chunks, - location: { - topRightCorner: extracted.mappingFunction(location.dimension, 0), - topLeftCorner: extracted.mappingFunction(0, 0), - bottomRightCorner: extracted.mappingFunction(location.dimension, location.dimension), - bottomLeftCorner: extracted.mappingFunction(0, location.dimension), - topRightFinderPattern: location.topRight, - topLeftFinderPattern: location.topLeft, - bottomLeftFinderPattern: location.bottomLeft, - bottomRightAlignmentPattern: location.alignmentPattern, - }, - }; + return null; } var defaultOptions = { inversionAttempts: "attemptBoth", @@ -599,7 +602,7 @@ function readCodewords(matrix, version, formatInfo) { // Read columns in pairs, from right to left var readingUp = true; for (var columnIndex = dimension - 1; columnIndex > 0; columnIndex -= 2) { - if (columnIndex === 6) { + if (columnIndex === 6) { // Skip whole column with vertical alignment pattern; columnIndex--; } for (var i = 0; i < dimension; i++) { @@ -613,7 +616,7 @@ function readCodewords(matrix, version, formatInfo) { bit = !bit; } currentByte = pushBit(bit, currentByte); - if (bitsRead === 8) { + if (bitsRead === 8) { // Whole bytes codewords.push(currentByte); bitsRead = 0; currentByte = 0; @@ -628,7 +631,7 @@ function readCodewords(matrix, version, formatInfo) { function readVersion(matrix) { var dimension = matrix.height; var provisionalVersion = Math.floor((dimension - 17) / 4); - if (provisionalVersion <= 6) { + if (provisionalVersion <= 6) { // 6 and under dont have version info in the QR code return version_1.VERSIONS[provisionalVersion - 1]; } var topRightVersionBits = 0; @@ -670,21 +673,21 @@ function readVersion(matrix) { function readFormatInformation(matrix) { var topLeftFormatInfoBits = 0; for (var x = 0; x <= 8; x++) { - if (x !== 6) { + if (x !== 6) { // Skip timing pattern bit topLeftFormatInfoBits = pushBit(matrix.get(x, 8), topLeftFormatInfoBits); } } for (var y = 7; y >= 0; y--) { - if (y !== 6) { + if (y !== 6) { // Skip timing pattern bit topLeftFormatInfoBits = pushBit(matrix.get(8, y), topLeftFormatInfoBits); } } var dimension = matrix.height; var topRightBottomRightFormatInfoBits = 0; - for (var y = dimension - 1; y >= dimension - 7; y--) { + for (var y = dimension - 1; y >= dimension - 7; y--) { // bottom left topRightBottomRightFormatInfoBits = pushBit(matrix.get(8, y), topRightBottomRightFormatInfoBits); } - for (var x = dimension - 8; x < dimension; x++) { + for (var x = dimension - 8; x < dimension; x++) { // top right topRightBottomRightFormatInfoBits = pushBit(matrix.get(x, 8), topRightBottomRightFormatInfoBits); } var bestDifference = Infinity; @@ -699,7 +702,7 @@ function readFormatInformation(matrix) { bestFormatInfo = formatInfo; bestDifference = difference; } - if (topLeftFormatInfoBits !== topRightBottomRightFormatInfoBits) { + if (topLeftFormatInfoBits !== topRightBottomRightFormatInfoBits) { // also try the other option difference = numBitsDiffering(topRightBottomRightFormatInfoBits, bits); if (difference < bestDifference) { bestFormatInfo = formatInfo; @@ -945,6 +948,7 @@ function decodeKanji(stream, size) { return { bytes: bytes, text: text }; } function decode(data, version) { + var _a, _b, _c, _d; var stream = new BitStream_1.BitStream(data); // There are 3 'sizes' based on the version. 1-9 is small (0), 10-26 is medium (1) and 27-40 is large (2). var size = version <= 9 ? 0 : version <= 26 ? 1 : 2; @@ -1024,7 +1028,10 @@ function decode(data, version) { }); } } - var _a, _b, _c, _d; + // If there is no data left, or the remaining bits are all 0, then that counts as a termination marker + if (stream.available() === 0 || stream.readBits(stream.available()) === 0) { + return result; + } } exports.decode = decode; @@ -8145,6 +8152,7 @@ Object.defineProperty(exports, "__esModule", { value: true }); var GenericGF_1 = __webpack_require__(1); var GenericGFPoly_1 = __webpack_require__(2); function runEuclideanAlgorithm(field, a, b, R) { + var _a; // Assume a's degree is >= b's if (a.degree() < b.degree()) { _a = [b, a], a = _a[0], b = _a[1]; @@ -8185,7 +8193,6 @@ function runEuclideanAlgorithm(field, a, b, R) { } var inverse = field.inverse(sigmaTildeAtZero); return [t.multiply(inverse), r.multiply(inverse)]; - var _a; } function findErrorLocations(field, errorLocator) { // This is a direct application of Chien's search @@ -9590,7 +9597,7 @@ var BitMatrix_1 = __webpack_require__(0); function squareToQuadrilateral(p1, p2, p3, p4) { var dx3 = p1.x - p2.x + p3.x - p4.x; var dy3 = p1.y - p2.y + p3.y - p4.y; - if (dx3 === 0 && dy3 === 0) { + if (dx3 === 0 && dy3 === 0) { // Affine return { a11: p2.x - p1.x, a12: p2.y - p1.y, @@ -9696,6 +9703,7 @@ function sum(values) { } // Takes three finder patterns and organizes them into topLeft, topRight, etc function reorderFinderPatterns(pattern1, pattern2, pattern3) { + var _a, _b, _c, _d; // Find distances between pattern centers var oneTwoDistance = distance(pattern1, pattern2); var twoThreeDistance = distance(pattern2, pattern3); @@ -9720,7 +9728,6 @@ function reorderFinderPatterns(pattern1, pattern2, pattern3) { _d = [topRight, bottomLeft], bottomLeft = _d[0], topRight = _d[1]; } return { bottomLeft: bottomLeft, topLeft: topLeft, topRight: topRight }; - var _a, _b, _c, _d; } // Computes the dimension (number of modules on a side) of the QR Code based on the position of the finder patterns function computeDimension(topLeft, topRight, bottomLeft, matrix) { @@ -9810,13 +9817,13 @@ function countBlackWhiteRunTowardsPoint(origin, end, matrix, length) { // along the line that intersects with the end point. Returns an array of elements, representing the pixel sizes // of the black white run. Takes a length which represents the number of switches from black to white to look for. function countBlackWhiteRun(origin, end, matrix, length) { + var _a; var rise = end.y - origin.y; var run = end.x - origin.x; var towardsEnd = countBlackWhiteRunTowardsPoint(origin, end, matrix, Math.ceil(length / 2)); var awayFromEnd = countBlackWhiteRunTowardsPoint(origin, { x: origin.x - run, y: origin.y - rise }, matrix, Math.ceil(length / 2)); var middleValue = towardsEnd.shift() + awayFromEnd.shift() - 1; // Substract one so we don't double count a pixel return (_a = awayFromEnd.concat(middleValue)).concat.apply(_a, towardsEnd); - var _a; } // Takes in a black white run and an array of expected ratios. Returns the average size of the run as well as the "error" - // that is the amount the run diverges from the expected ratio @@ -9864,6 +9871,27 @@ function scorePattern(point, ratios, matrix) { return Infinity; } } +function recenterLocation(matrix, p) { + var leftX = Math.round(p.x); + while (matrix.get(leftX, Math.round(p.y))) { + leftX--; + } + var rightX = Math.round(p.x); + while (matrix.get(rightX, Math.round(p.y))) { + rightX++; + } + var x = (leftX + rightX) / 2; + var topY = Math.round(p.y); + while (matrix.get(Math.round(x), topY)) { + topY--; + } + var bottomY = Math.round(p.y); + while (matrix.get(Math.round(x), bottomY)) { + bottomY++; + } + var y = (topY + bottomY) / 2; + return { x: x, y: y }; +} function locate(matrix) { var finderPatternQuads = []; var activeFinderPatternQuads = []; @@ -9966,6 +9994,7 @@ function locate(matrix) { }) .filter(function (q) { return !!q; }) // Filter out any rejected quads from above .sort(function (a, b) { return a.score - b.score; }) + // Now take the top finder pattern options and try to find 2 other options with a similar size. .map(function (point, i, finderPatterns) { if (i > MAX_FINDERPATTERNS_TO_SEARCH) { return null; @@ -9986,12 +10015,49 @@ function locate(matrix) { return null; } var _a = reorderFinderPatterns(finderPatternGroups[0].points[0], finderPatternGroups[0].points[1], finderPatternGroups[0].points[2]), topRight = _a.topRight, topLeft = _a.topLeft, bottomLeft = _a.bottomLeft; + var alignment = findAlignmentPattern(matrix, alignmentPatternQuads, topRight, topLeft, bottomLeft); + var result = []; + if (alignment) { + result.push({ + alignmentPattern: { x: alignment.alignmentPattern.x, y: alignment.alignmentPattern.y }, + bottomLeft: { x: bottomLeft.x, y: bottomLeft.y }, + dimension: alignment.dimension, + topLeft: { x: topLeft.x, y: topLeft.y }, + topRight: { x: topRight.x, y: topRight.y }, + }); + } + // We normally use the center of the quads as the location of the tracking points, which is optimal for most cases and will account + // for a skew in the image. However, In some cases, a slight skew might not be real and instead be caused by image compression + // errors and/or low resolution. For those cases, we'd be better off centering the point exactly in the middle of the black area. We + // compute and return the location data for the naively centered points as it is little additional work and allows for multiple + // attempts at decoding harder images. + var midTopRight = recenterLocation(matrix, topRight); + var midTopLeft = recenterLocation(matrix, topLeft); + var midBottomLeft = recenterLocation(matrix, bottomLeft); + var centeredAlignment = findAlignmentPattern(matrix, alignmentPatternQuads, midTopRight, midTopLeft, midBottomLeft); + if (centeredAlignment) { + result.push({ + alignmentPattern: { x: centeredAlignment.alignmentPattern.x, y: centeredAlignment.alignmentPattern.y }, + bottomLeft: { x: midBottomLeft.x, y: midBottomLeft.y }, + topLeft: { x: midTopLeft.x, y: midTopLeft.y }, + topRight: { x: midTopRight.x, y: midTopRight.y }, + dimension: centeredAlignment.dimension, + }); + } + if (result.length === 0) { + return null; + } + return result; +} +exports.locate = locate; +function findAlignmentPattern(matrix, alignmentPatternQuads, topRight, topLeft, bottomLeft) { + var _a; // Now that we've found the three finder patterns we can determine the blockSize and the size of the QR code. // We'll use these to help find the alignment pattern but also later when we do the extraction. var dimension; var moduleSize; try { - (_b = computeDimension(topLeft, topRight, bottomLeft, matrix), dimension = _b.dimension, moduleSize = _b.moduleSize); + (_a = computeDimension(topLeft, topRight, bottomLeft, matrix), dimension = _a.dimension, moduleSize = _a.moduleSize); } catch (e) { return null; @@ -10025,16 +10091,8 @@ function locate(matrix) { // If there are less than 15 modules between finder patterns it's a version 1 QR code and as such has no alignmemnt pattern // so we can only use our best guess. var alignmentPattern = modulesBetweenFinderPatterns >= 15 && alignmentPatterns.length ? alignmentPatterns[0] : expectedAlignmentPattern; - return { - alignmentPattern: { x: alignmentPattern.x, y: alignmentPattern.y }, - bottomLeft: { x: bottomLeft.x, y: bottomLeft.y }, - dimension: dimension, - topLeft: { x: topLeft.x, y: topLeft.y }, - topRight: { x: topRight.x, y: topRight.y }, - }; - var _b; + return { alignmentPattern: alignmentPattern, dimension: dimension }; } -exports.locate = locate; /***/ }) diff --git a/dist/locator/index.d.ts b/dist/locator/index.d.ts index 5698fb5f..11316062 100644 --- a/dist/locator/index.d.ts +++ b/dist/locator/index.d.ts @@ -10,4 +10,4 @@ export interface QRLocation { alignmentPattern: Point; dimension: number; } -export declare function locate(matrix: BitMatrix): QRLocation; +export declare function locate(matrix: BitMatrix): QRLocation[]; diff --git a/docs/jsQR.js b/docs/jsQR.js index f749098d..2e756b2c 100644 --- a/docs/jsQR.js +++ b/docs/jsQR.js @@ -225,6 +225,7 @@ var GenericGFPoly = /** @class */ (function () { return this.coefficients[this.coefficients.length - 1 - degree]; }; GenericGFPoly.prototype.addOrSubtract = function (other) { + var _a; if (this.isZero()) { return other; } @@ -245,7 +246,6 @@ var GenericGFPoly = /** @class */ (function () { sumDiff[i] = GenericGF_1.addOrSubtractGF(smallerCoefficients[i - lengthDiff], largerCoefficients[i]); } return new GenericGFPoly(this.field, sumDiff); - var _a; }; GenericGFPoly.prototype.multiply = function (scalar) { if (scalar === 0) { @@ -329,30 +329,33 @@ var decoder_1 = __webpack_require__(5); var extractor_1 = __webpack_require__(11); var locator_1 = __webpack_require__(12); function scan(matrix) { - var location = locator_1.locate(matrix); - if (!location) { + var locations = locator_1.locate(matrix); + if (!locations) { return null; } - var extracted = extractor_1.extract(matrix, location); - var decoded = decoder_1.decode(extracted.matrix); - if (!decoded) { - return null; + for (var _i = 0, locations_1 = locations; _i < locations_1.length; _i++) { + var location_1 = locations_1[_i]; + var extracted = extractor_1.extract(matrix, location_1); + var decoded = decoder_1.decode(extracted.matrix); + if (decoded) { + return { + binaryData: decoded.bytes, + data: decoded.text, + chunks: decoded.chunks, + location: { + topRightCorner: extracted.mappingFunction(location_1.dimension, 0), + topLeftCorner: extracted.mappingFunction(0, 0), + bottomRightCorner: extracted.mappingFunction(location_1.dimension, location_1.dimension), + bottomLeftCorner: extracted.mappingFunction(0, location_1.dimension), + topRightFinderPattern: location_1.topRight, + topLeftFinderPattern: location_1.topLeft, + bottomLeftFinderPattern: location_1.bottomLeft, + bottomRightAlignmentPattern: location_1.alignmentPattern, + }, + }; + } } - return { - binaryData: decoded.bytes, - data: decoded.text, - chunks: decoded.chunks, - location: { - topRightCorner: extracted.mappingFunction(location.dimension, 0), - topLeftCorner: extracted.mappingFunction(0, 0), - bottomRightCorner: extracted.mappingFunction(location.dimension, location.dimension), - bottomLeftCorner: extracted.mappingFunction(0, location.dimension), - topRightFinderPattern: location.topRight, - topLeftFinderPattern: location.topLeft, - bottomLeftFinderPattern: location.bottomLeft, - bottomRightAlignmentPattern: location.alignmentPattern, - }, - }; + return null; } var defaultOptions = { inversionAttempts: "attemptBoth", @@ -599,7 +602,7 @@ function readCodewords(matrix, version, formatInfo) { // Read columns in pairs, from right to left var readingUp = true; for (var columnIndex = dimension - 1; columnIndex > 0; columnIndex -= 2) { - if (columnIndex === 6) { + if (columnIndex === 6) { // Skip whole column with vertical alignment pattern; columnIndex--; } for (var i = 0; i < dimension; i++) { @@ -613,7 +616,7 @@ function readCodewords(matrix, version, formatInfo) { bit = !bit; } currentByte = pushBit(bit, currentByte); - if (bitsRead === 8) { + if (bitsRead === 8) { // Whole bytes codewords.push(currentByte); bitsRead = 0; currentByte = 0; @@ -628,7 +631,7 @@ function readCodewords(matrix, version, formatInfo) { function readVersion(matrix) { var dimension = matrix.height; var provisionalVersion = Math.floor((dimension - 17) / 4); - if (provisionalVersion <= 6) { + if (provisionalVersion <= 6) { // 6 and under dont have version info in the QR code return version_1.VERSIONS[provisionalVersion - 1]; } var topRightVersionBits = 0; @@ -670,21 +673,21 @@ function readVersion(matrix) { function readFormatInformation(matrix) { var topLeftFormatInfoBits = 0; for (var x = 0; x <= 8; x++) { - if (x !== 6) { + if (x !== 6) { // Skip timing pattern bit topLeftFormatInfoBits = pushBit(matrix.get(x, 8), topLeftFormatInfoBits); } } for (var y = 7; y >= 0; y--) { - if (y !== 6) { + if (y !== 6) { // Skip timing pattern bit topLeftFormatInfoBits = pushBit(matrix.get(8, y), topLeftFormatInfoBits); } } var dimension = matrix.height; var topRightBottomRightFormatInfoBits = 0; - for (var y = dimension - 1; y >= dimension - 7; y--) { + for (var y = dimension - 1; y >= dimension - 7; y--) { // bottom left topRightBottomRightFormatInfoBits = pushBit(matrix.get(8, y), topRightBottomRightFormatInfoBits); } - for (var x = dimension - 8; x < dimension; x++) { + for (var x = dimension - 8; x < dimension; x++) { // top right topRightBottomRightFormatInfoBits = pushBit(matrix.get(x, 8), topRightBottomRightFormatInfoBits); } var bestDifference = Infinity; @@ -699,7 +702,7 @@ function readFormatInformation(matrix) { bestFormatInfo = formatInfo; bestDifference = difference; } - if (topLeftFormatInfoBits !== topRightBottomRightFormatInfoBits) { + if (topLeftFormatInfoBits !== topRightBottomRightFormatInfoBits) { // also try the other option difference = numBitsDiffering(topRightBottomRightFormatInfoBits, bits); if (difference < bestDifference) { bestFormatInfo = formatInfo; @@ -945,6 +948,7 @@ function decodeKanji(stream, size) { return { bytes: bytes, text: text }; } function decode(data, version) { + var _a, _b, _c, _d; var stream = new BitStream_1.BitStream(data); // There are 3 'sizes' based on the version. 1-9 is small (0), 10-26 is medium (1) and 27-40 is large (2). var size = version <= 9 ? 0 : version <= 26 ? 1 : 2; @@ -1024,7 +1028,10 @@ function decode(data, version) { }); } } - var _a, _b, _c, _d; + // If there is no data left, or the remaining bits are all 0, then that counts as a termination marker + if (stream.available() === 0 || stream.readBits(stream.available()) === 0) { + return result; + } } exports.decode = decode; @@ -8145,6 +8152,7 @@ Object.defineProperty(exports, "__esModule", { value: true }); var GenericGF_1 = __webpack_require__(1); var GenericGFPoly_1 = __webpack_require__(2); function runEuclideanAlgorithm(field, a, b, R) { + var _a; // Assume a's degree is >= b's if (a.degree() < b.degree()) { _a = [b, a], a = _a[0], b = _a[1]; @@ -8185,7 +8193,6 @@ function runEuclideanAlgorithm(field, a, b, R) { } var inverse = field.inverse(sigmaTildeAtZero); return [t.multiply(inverse), r.multiply(inverse)]; - var _a; } function findErrorLocations(field, errorLocator) { // This is a direct application of Chien's search @@ -9590,7 +9597,7 @@ var BitMatrix_1 = __webpack_require__(0); function squareToQuadrilateral(p1, p2, p3, p4) { var dx3 = p1.x - p2.x + p3.x - p4.x; var dy3 = p1.y - p2.y + p3.y - p4.y; - if (dx3 === 0 && dy3 === 0) { + if (dx3 === 0 && dy3 === 0) { // Affine return { a11: p2.x - p1.x, a12: p2.y - p1.y, @@ -9696,6 +9703,7 @@ function sum(values) { } // Takes three finder patterns and organizes them into topLeft, topRight, etc function reorderFinderPatterns(pattern1, pattern2, pattern3) { + var _a, _b, _c, _d; // Find distances between pattern centers var oneTwoDistance = distance(pattern1, pattern2); var twoThreeDistance = distance(pattern2, pattern3); @@ -9720,7 +9728,6 @@ function reorderFinderPatterns(pattern1, pattern2, pattern3) { _d = [topRight, bottomLeft], bottomLeft = _d[0], topRight = _d[1]; } return { bottomLeft: bottomLeft, topLeft: topLeft, topRight: topRight }; - var _a, _b, _c, _d; } // Computes the dimension (number of modules on a side) of the QR Code based on the position of the finder patterns function computeDimension(topLeft, topRight, bottomLeft, matrix) { @@ -9810,13 +9817,13 @@ function countBlackWhiteRunTowardsPoint(origin, end, matrix, length) { // along the line that intersects with the end point. Returns an array of elements, representing the pixel sizes // of the black white run. Takes a length which represents the number of switches from black to white to look for. function countBlackWhiteRun(origin, end, matrix, length) { + var _a; var rise = end.y - origin.y; var run = end.x - origin.x; var towardsEnd = countBlackWhiteRunTowardsPoint(origin, end, matrix, Math.ceil(length / 2)); var awayFromEnd = countBlackWhiteRunTowardsPoint(origin, { x: origin.x - run, y: origin.y - rise }, matrix, Math.ceil(length / 2)); var middleValue = towardsEnd.shift() + awayFromEnd.shift() - 1; // Substract one so we don't double count a pixel return (_a = awayFromEnd.concat(middleValue)).concat.apply(_a, towardsEnd); - var _a; } // Takes in a black white run and an array of expected ratios. Returns the average size of the run as well as the "error" - // that is the amount the run diverges from the expected ratio @@ -9864,6 +9871,27 @@ function scorePattern(point, ratios, matrix) { return Infinity; } } +function recenterLocation(matrix, p) { + var leftX = Math.round(p.x); + while (matrix.get(leftX, Math.round(p.y))) { + leftX--; + } + var rightX = Math.round(p.x); + while (matrix.get(rightX, Math.round(p.y))) { + rightX++; + } + var x = (leftX + rightX) / 2; + var topY = Math.round(p.y); + while (matrix.get(Math.round(x), topY)) { + topY--; + } + var bottomY = Math.round(p.y); + while (matrix.get(Math.round(x), bottomY)) { + bottomY++; + } + var y = (topY + bottomY) / 2; + return { x: x, y: y }; +} function locate(matrix) { var finderPatternQuads = []; var activeFinderPatternQuads = []; @@ -9966,6 +9994,7 @@ function locate(matrix) { }) .filter(function (q) { return !!q; }) // Filter out any rejected quads from above .sort(function (a, b) { return a.score - b.score; }) + // Now take the top finder pattern options and try to find 2 other options with a similar size. .map(function (point, i, finderPatterns) { if (i > MAX_FINDERPATTERNS_TO_SEARCH) { return null; @@ -9986,12 +10015,49 @@ function locate(matrix) { return null; } var _a = reorderFinderPatterns(finderPatternGroups[0].points[0], finderPatternGroups[0].points[1], finderPatternGroups[0].points[2]), topRight = _a.topRight, topLeft = _a.topLeft, bottomLeft = _a.bottomLeft; + var alignment = findAlignmentPattern(matrix, alignmentPatternQuads, topRight, topLeft, bottomLeft); + var result = []; + if (alignment) { + result.push({ + alignmentPattern: { x: alignment.alignmentPattern.x, y: alignment.alignmentPattern.y }, + bottomLeft: { x: bottomLeft.x, y: bottomLeft.y }, + dimension: alignment.dimension, + topLeft: { x: topLeft.x, y: topLeft.y }, + topRight: { x: topRight.x, y: topRight.y }, + }); + } + // We normally use the center of the quads as the location of the tracking points, which is optimal for most cases and will account + // for a skew in the image. However, In some cases, a slight skew might not be real and instead be caused by image compression + // errors and/or low resolution. For those cases, we'd be better off centering the point exactly in the middle of the black area. We + // compute and return the location data for the naively centered points as it is little additional work and allows for multiple + // attempts at decoding harder images. + var midTopRight = recenterLocation(matrix, topRight); + var midTopLeft = recenterLocation(matrix, topLeft); + var midBottomLeft = recenterLocation(matrix, bottomLeft); + var centeredAlignment = findAlignmentPattern(matrix, alignmentPatternQuads, midTopRight, midTopLeft, midBottomLeft); + if (centeredAlignment) { + result.push({ + alignmentPattern: { x: centeredAlignment.alignmentPattern.x, y: centeredAlignment.alignmentPattern.y }, + bottomLeft: { x: midBottomLeft.x, y: midBottomLeft.y }, + topLeft: { x: midTopLeft.x, y: midTopLeft.y }, + topRight: { x: midTopRight.x, y: midTopRight.y }, + dimension: centeredAlignment.dimension, + }); + } + if (result.length === 0) { + return null; + } + return result; +} +exports.locate = locate; +function findAlignmentPattern(matrix, alignmentPatternQuads, topRight, topLeft, bottomLeft) { + var _a; // Now that we've found the three finder patterns we can determine the blockSize and the size of the QR code. // We'll use these to help find the alignment pattern but also later when we do the extraction. var dimension; var moduleSize; try { - (_b = computeDimension(topLeft, topRight, bottomLeft, matrix), dimension = _b.dimension, moduleSize = _b.moduleSize); + (_a = computeDimension(topLeft, topRight, bottomLeft, matrix), dimension = _a.dimension, moduleSize = _a.moduleSize); } catch (e) { return null; @@ -10025,16 +10091,8 @@ function locate(matrix) { // If there are less than 15 modules between finder patterns it's a version 1 QR code and as such has no alignmemnt pattern // so we can only use our best guess. var alignmentPattern = modulesBetweenFinderPatterns >= 15 && alignmentPatterns.length ? alignmentPatterns[0] : expectedAlignmentPattern; - return { - alignmentPattern: { x: alignmentPattern.x, y: alignmentPattern.y }, - bottomLeft: { x: bottomLeft.x, y: bottomLeft.y }, - dimension: dimension, - topLeft: { x: topLeft.x, y: topLeft.y }, - topRight: { x: topRight.x, y: topRight.y }, - }; - var _b; + return { alignmentPattern: alignmentPattern, dimension: dimension }; } -exports.locate = locate; /***/ }) diff --git a/package.json b/package.json index 25706e01..68bd6256 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "jsqr", - "version": "1.2.0", + "version": "1.3.0", "description": "A pure javascript QR code reading library that takes in raw images and will locate, extract and parse any QR code found within.", "repository": "https://github.com/cozmo/jsQR", "main": "./dist/jsQR.js",