Skip to content
Open
Show file tree
Hide file tree
Changes from 8 commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
fc690ea
WIP code
Dec 1, 2020
09c4ea3
WIP: commit meant to save the code
odahcam Dec 2, 2020
9d0fae6
dependecies: updated sharp
odahcam Dec 2, 2020
9a7527c
tests(multi qrcode): fix array equals assertion
odahcam Dec 2, 2020
abeafd7
image loading fix
odahcam Dec 2, 2020
7e7c13f
tests corrections
odahcam Dec 4, 2020
abcff1a
new finder pattern finder
odahcam Dec 4, 2020
e0ae737
added some notes
odahcam Dec 4, 2020
9f073f9
working on overloads
odahcam Dec 7, 2020
f2a398d
Merge branch 'feature/multi-qr-code-reader' of https://github.com/zxi…
odahcam Dec 7, 2020
384c84a
test: fix overload usage
odahcam Dec 7, 2020
562277e
Merge branch 'master' into feature/multi-qr-code-reader
odahcam Dec 9, 2020
f5212ac
fix implementation and overload names
odahcam Dec 22, 2020
ebd9756
fix spacing in func declarations
odahcam Dec 22, 2020
6efcdca
implemented overloading pattern
odahcam Dec 23, 2020
9e92e55
fix general build/test errors
odahcam Dec 23, 2020
e3c15b6
Merge branch 'master' into feature/multi-qr-code-reader
odahcam Apr 11, 2021
e5aac7c
updates and added info about porting overloads
odahcam Apr 11, 2021
49a0363
fixed tests build
odahcam Apr 11, 2021
07ec4e7
Merge branch 'master' into feature/multi-qr-code-reader
odahcam Apr 11, 2021
3599b38
ts-node launches
odahcam May 29, 2021
7c0beab
Remove duplicate import and add type to assertion to allow tests to run
shahrin-shahrulzaman-rakuten Aug 11, 2021
fa890ef
Remove duplicate import and add type to assertion to allow tests to run
shahrin-shahrulzaman-rakuten Aug 11, 2021
9f32092
Merge pull request #464 from shahrin014/fix/feature/multi-qr-code-reader
werthdavid Aug 11, 2021
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions .vscode/launch.json
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,26 @@
"./src/test/core/aztec/**/*.spec.ts"
],
"internalConsoleOptions": "openOnSessionStart"
},
{
"type": "node",
"request": "launch",
"name": "Multi-reader Tests - ts-node",
"program": "${workspaceFolder}/node_modules/mocha/bin/_mocha",
"args": [
"--require",
"ts-node/register",
"--require",
"tsconfig-paths/register",
"-u",
"tdd",
"--timeout",
"999999",
"--colors",
"--recursive",
"./src/test/core/multi/**/*.spec.ts"
],
"internalConsoleOptions": "openOnSessionStart"
}
]
}
6 changes: 4 additions & 2 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,8 @@ https://docs.oracle.com/javase/tutorial/java/nutsandbolts/datatypes.html
- `int` has 32 bits, signed, so `int[]` transforms to `Int32Array`.
- `char` has 2 bytes, so `char[]` transforms to `Uint16Array`.
- `long` has 64 bit two's complement `integer`, can be signed or unsigned.
- `float[]` can be ported to `Float32Array`.
- `double[]` can be ported to `Float64Array`.

### JavaScript's TypedArray

Expand All @@ -71,8 +73,8 @@ https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects

- Take care of `int` -> `number` (integer to number) port when doing bitwise transformation especially `<<`. Do a `& 0xFFFFFFFF` for ints, a &0xFF for bytes.
- Take care of array initialization, in Java `new Array(N)` initializes capacity NOT size/length.
- Use `Math.floor` for any division of ints otherwise the `number` type is a floating point and keeps the numbers after the dot.
- For `float` to `int` casting use `Math.trunc`, to replicate the same effect as Java casting does.
- Use `Math.floor` for any division of `int`s otherwise the `number` type is a floating point and keeps the numbers after the dot.
- For `float`/`number` to `int` casting use `Math.trunc`, to replicate the same effect as Java casting does.

## Encoding

Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@
"nyc": "^15.1.0",
"rollup": "^2.8.2",
"seedrandom": "^2.4.4",
"sharp": "^0.22.1",
"sharp": "^0.26.3",
"shx": "0.3.2",
"sinon": "^7.2.7",
"terser": "^5.3.7",
Expand Down
3 changes: 2 additions & 1 deletion src/core/Reader.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,8 +46,9 @@ interface Reader {
* @throws NotFoundException if no potential barcode is found
* @throws ChecksumException if a potential barcode is found but does not pass its checksum
* @throws FormatException if a potential barcode is found but format is invalid
* @override decode
*/
// decode(image: BinaryBitmap): Result /*throws NotFoundException, ChecksumException, FormatException*/
// decodeWithoutHints(image: BinaryBitmap): Result;

/**
* Locates and decodes a barcode in some format within an image. This method also accepts
Expand Down
34 changes: 19 additions & 15 deletions src/core/Result.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,21 +32,25 @@ export default class Result {

private resultMetadata: Map<ResultMetadataType, Object>;

// public constructor(private text: string,
// Uint8Array rawBytes,
// ResultPoconst resultPoints: Int32Array,
// BarcodeFormat format) {
// this(text, rawBytes, resultPoints, format, System.currentTimeMillis())
// }

// public constructor(text: string,
// Uint8Array rawBytes,
// ResultPoconst resultPoints: Int32Array,
// BarcodeFormat format,
// long timestamp) {
// this(text, rawBytes, rawBytes == null ? 0 : 8 * rawBytes.length,
// resultPoints, format, timestamp)
// }
public static constructor4Args(
text: string,
rawBytes: Uint8Array,
resultPoints: ResultPoint[],
format: BarcodeFormat,
) {
return Result.constructor5Args(text, rawBytes, resultPoints, format, System.currentTimeMillis());
}

public static constructor5Args(
text: string,
rawBytes: Uint8Array,
resultPoints: ResultPoint[],
format: BarcodeFormat,
timestamp: number /* long */,
) {
return new Result(text, rawBytes, rawBytes == null ? 0 : 8 * rawBytes.length,
resultPoints, format, timestamp);
}

public constructor(private text: string,
private rawBytes: Uint8Array,
Expand Down
3 changes: 2 additions & 1 deletion src/core/multi/MultipleBarcodeReader.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,9 @@ export default /*public*/ interface MultipleBarcodeReader {

/**
* @throws NotFoundException
* @override decodeMultiple
*/
decodeMultiple(image: BinaryBitmap): Result[];
decodeMultipleWithoutHints(image: BinaryBitmap): Result[];

/**
* @throws NotFoundException
Expand Down
181 changes: 181 additions & 0 deletions src/core/multi/qrcode/QRCodeMultiReader.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,181 @@
/*
* Copyright 2009 ZXing authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

import { int, List } from 'src/customTypings';
import BarcodeFormat from '../../BarcodeFormat';
import BinaryBitmap from '../../BinaryBitmap';
import DecoderResult from '../../common/DecoderResult';
import DetectorResult from '../../common/DetectorResult';
import DecodeHintType from '../../DecodeHintType';
import QRCodeDecoderMetaData from '../../qrcode/decoder/QRCodeDecoderMetaData';
import QRCodeReader from '../../qrcode/QRCodeReader';
import ReaderException from '../../ReaderException';
import Result from '../../Result';
import ResultMetadataType from '../../ResultMetadataType';
import ResultPoint from '../../ResultPoint';
import ByteArrayOutputStream from '../../util/ByteArrayOutputStream';
import Collections from '../../util/Collections';
import Comparator from '../../util/Comparator';
import Integer from '../../util/Integer';
import StringBuilder from '../../util/StringBuilder';
import MultipleBarcodeReader from '../MultipleBarcodeReader';
import MultiDetector from './detector/MultiDetector';

// package com.google.zxing.multi.qrcode;

// import com.google.zxing.BarcodeFormat;
// import com.google.zxing.BinaryBitmap;
// import com.google.zxing.DecodeHintType;
// import com.google.zxing.NotFoundException;
// import com.google.zxing.ReaderException;
// import com.google.zxing.Result;
// import com.google.zxing.ResultMetadataType;
// import com.google.zxing.ResultPoint;
// import com.google.zxing.common.DecoderResult;
// import com.google.zxing.common.DetectorResult;
// import com.google.zxing.multi.MultipleBarcodeReader;
// import com.google.zxing.multi.qrcode.detector.MultiDetector;
// import com.google.zxing.qrcode.QRCodeReader;
// import com.google.zxing.qrcode.decoder.QRCodeDecoderMetaData;

// import java.io.ByteArrayOutputStream;
// import java.io.Serializable;
// import java.util.ArrayList;
// import java.util.List;
// import java.util.Map;
// import java.util.Collections;
// import java.util.Comparator;

/**
* This implementation can detect and decode multiple QR Codes in an image.
*
* @author Sean Owen
* @author Hannes Erven
*/
export default /*public final*/ class QRCodeMultiReader extends QRCodeReader implements MultipleBarcodeReader {

private static /* final */ EMPTY_RESULT_ARRAY: Result[] = [];
protected static /* final */ NO_POINTS = new Array<ResultPoint>();

/**
* @throws NotFoundException
* @override decodeMultiple
*/
public decodeMultipleWithoutHints(image: BinaryBitmap): Result[] {
return this.decodeMultiple(image, null);
}

/**
* @override
* @throws NotFoundException
*/
public decodeMultiple(image: BinaryBitmap, hints: Map<DecodeHintType, any>): Result[] {
let results: List<Result> = [];
const detectorResults: DetectorResult[] = new MultiDetector(image.getBlackMatrix()).detectMulti(hints);
for (const detectorResult of detectorResults) {
try {
const decoderResult: DecoderResult = this.getDecoder().decodeBitMatrix(detectorResult.getBits(), hints);
const points: ResultPoint[] = detectorResult.getPoints();
// If the code was mirrored: swap the bottom-left and the top-right points.
if (decoderResult.getOther() instanceof QRCodeDecoderMetaData) {
(<QRCodeDecoderMetaData> decoderResult.getOther()).applyMirroredCorrection(points);
}
const result: Result = Result.constructor4Args(decoderResult.getText(), decoderResult.getRawBytes(), points,
BarcodeFormat.QR_CODE);
const byteSegments: List<Uint8Array> = decoderResult.getByteSegments();
if (byteSegments != null) {
result.putMetadata(ResultMetadataType.BYTE_SEGMENTS, byteSegments);
}
const ecLevel: string = decoderResult.getECLevel();
if (ecLevel != null) {
result.putMetadata(ResultMetadataType.ERROR_CORRECTION_LEVEL, ecLevel);
}
if (decoderResult.hasStructuredAppend()) {
result.putMetadata(ResultMetadataType.STRUCTURED_APPEND_SEQUENCE,
decoderResult.getStructuredAppendSequenceNumber());
result.putMetadata(ResultMetadataType.STRUCTURED_APPEND_PARITY,
decoderResult.getStructuredAppendParity());
}
results.push(result);
} catch (re) {
if (re instanceof ReaderException) {
// ignore and continue
} else {
throw re;
}
}
}
if (results.length === 0) {
return QRCodeMultiReader.EMPTY_RESULT_ARRAY;
} else {
results = QRCodeMultiReader.processStructuredAppend(results);
return results/* .toArray(QRCodeMultiReader.EMPTY_RESULT_ARRAY) */;
}
}

static processStructuredAppend( results: List<Result>): List<Result> {
const newResults: List<Result> = [];
const saResults: List<Result> = [];
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We need to create multiple saResults: List<Result> that grouped by ResultMetadataType.STRUCTURED_APPEND_PARITY to handle multi parity structured append qr codes in one document.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the tip!! Can you help me with some examples or a PR?

for (const result of results) {
if (result.getResultMetadata().has(ResultMetadataType.STRUCTURED_APPEND_SEQUENCE)) {
saResults.push(result);
} else {
newResults.push(result);
}
}
if (saResults.length === 0) {
return results;
}

// sort and concatenate the SA list items
Collections.sort(saResults, new SAComparator());
const newText: StringBuilder = new StringBuilder();
const newRawBytes: ByteArrayOutputStream = new ByteArrayOutputStream();
const newByteSegment: ByteArrayOutputStream = new ByteArrayOutputStream();
for (const saResult of saResults) {
newText.append(saResult.getText());
const saBytes: Uint8Array = saResult.getRawBytes();
newRawBytes.writeBytesOffset(saBytes, 0, saBytes.length);
// @SuppressWarnings("unchecked")
const byteSegments: Iterable<Uint8Array> =
<Iterable<Uint8Array>> saResult.getResultMetadata().get(ResultMetadataType.BYTE_SEGMENTS);
if (byteSegments != null) {
for (const segment of byteSegments) {
newByteSegment.writeBytesOffset(segment, 0, segment.length);
}
}
}

const newResult: Result = Result.constructor4Args(newText.toString(), newRawBytes.toByteArray(), QRCodeMultiReader.NO_POINTS, BarcodeFormat.QR_CODE);
if (newByteSegment.size() > 0) {
newResult.putMetadata(ResultMetadataType.BYTE_SEGMENTS, Collections.singletonList(newByteSegment.toByteArray()));
}
newResults.push(newResult); // TYPESCRIPTPORT: inserted element at the start of the array because it seems the Java version does that as well.
return newResults;
}

}

/* private static final*/ class SAComparator implements Comparator<Result>/*, Serializable*/ {
/**
* @override
*/
public compare(a: Result, b: Result): int {
const aNumber: int = <int> a.getResultMetadata().get(ResultMetadataType.STRUCTURED_APPEND_SEQUENCE);
const bNumber: int = <int> b.getResultMetadata().get(ResultMetadataType.STRUCTURED_APPEND_SEQUENCE);
return Integer.compare(aNumber, bNumber);
}
}
89 changes: 89 additions & 0 deletions src/core/multi/qrcode/detector/MultiDetector.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
/*
* Copyright 2009 ZXing authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

import BitMatrix from "src/core/common/BitMatrix";
import DetectorResult from "src/core/common/DetectorResult";
import DecodeHintType from "src/core/DecodeHintType";
import NotFoundException from "src/core/NotFoundException";
import Detector from "src/core/qrcode/detector/Detector";
import FinderPatternInfo from "src/core/qrcode/detector/FinderPatternInfo";
import ReaderException from "src/core/ReaderException";
import ResultPointCallback from "src/core/ResultPointCallback";
import { List } from "src/customTypings";
import MultiFinderPatternFinder from "./MultiFinderPatternFinder";

// package com.google.zxing.multi.qrcode.detector;

// import com.google.zxing.DecodeHintType;
// import com.google.zxing.NotFoundException;
// import com.google.zxing.ReaderException;
// import com.google.zxing.ResultPointCallback;
// import com.google.zxing.common.BitMatrix;
// import com.google.zxing.common.DetectorResult;
// import com.google.zxing.qrcode.detector.Detector;
// import com.google.zxing.qrcode.detector.FinderPatternInfo;

// import java.util.ArrayList;
// import java.util.List;
// import java.util.Map;

/**
* <p>Encapsulates logic that can detect one or more QR Codes in an image, even if the QR Code
* is rotated or skewed, or partially obscured.</p>
*
* @author Sean Owen
* @author Hannes Erven
*/
export default /* public final */ class MultiDetector extends Detector {

private static /* final */ EMPTY_DETECTOR_RESULTS: DetectorResult[] = [];

public constructor( image: BitMatrix) {
super(image);
}

/** @throws NotFoundException */
public detectMulti( hints: Map<DecodeHintType, any>): DetectorResult[] {
const image: BitMatrix = this.getImage();
const resultPointCallback: ResultPointCallback =
hints == null ? null : <ResultPointCallback> hints.get(DecodeHintType.NEED_RESULT_POINT_CALLBACK);
const finder: MultiFinderPatternFinder = new MultiFinderPatternFinder(image, resultPointCallback);
const infos: FinderPatternInfo[] = finder.findMulti(hints);

if (infos.length === 0) {
throw NotFoundException.getNotFoundInstance();
}

const result: List<DetectorResult> = [];
for (const info of infos) {
try {
result.push(this.processFinderPatternInfo(info));
} catch (e) {
if (e instanceof ReaderException) {
// ignore
} else {
throw e;
}
}
}
if (result.length === 0) {
return MultiDetector.EMPTY_DETECTOR_RESULTS;
} else {
return result/* .toArray(EMPTY_DETECTOR_RESULTS) */;
}
}

}
Loading