diff --git a/example/file_input.html b/example/file_input.html index 3d25fb1..4465baf 100644 --- a/example/file_input.html +++ b/example/file_input.html @@ -52,6 +52,7 @@ UPC-E Codabar ITF + 2of5 diff --git a/example/live_w_locator.html b/example/live_w_locator.html index 046509a..289f268 100644 --- a/example/live_w_locator.html +++ b/example/live_w_locator.html @@ -43,7 +43,8 @@ UPC UPC-E Codabar - ITF + Interleaved 2 of 5 + Standard 2 of 5 diff --git a/example/live_w_locator.js b/example/live_w_locator.js index 412a14e..cd49f37 100644 --- a/example/live_w_locator.js +++ b/example/live_w_locator.js @@ -2,7 +2,19 @@ $(function() { var resultCollector = Quagga.ResultCollector.create({ capture: true, capacity: 20, - blacklist: [{code: "2167361334", format: "i2of5"}], + blacklist: [{ + code: "9577149002", format: "2of5" + }, { + code: "5776158811", format: "2of5" + }, { + code: "0463381455", format: "2of5" + }, { + code: "3261594101", format: "2of5" + }, { + code: "6730705801", format: "2of5" + }, { + code: "8568166929", format: "2of5" + }], filter: function(codeResult) { // only store results which match this constraint // e.g.: codeResult @@ -169,7 +181,8 @@ $(function() { patchSize: "medium", halfSample: true }, - numOfWorkers: 4, + numOfWorkers: 2, + frequency: 10, decoder: { readers : [{ format: "code_128_reader", diff --git a/example/static_images.html b/example/static_images.html index 307c168..23fd49f 100644 --- a/example/static_images.html +++ b/example/static_images.html @@ -45,6 +45,8 @@ UPC-E Codabar I2of5 + Interleaved 2 of 5 + Standard 2 of 5 diff --git a/karma-integration.conf.js b/karma-integration.conf.js index 60a983d..1d7fe3a 100644 --- a/karma-integration.conf.js +++ b/karma-integration.conf.js @@ -14,6 +14,9 @@ module.exports = function(config) { 'test/test-main-integration.js': ['webpack'] }, webpack: { + entry: [ + './src/quagga.js' + ], module: { loaders: [{ test: /\.jsx?$/, @@ -45,7 +48,7 @@ module.exports = function(config) { reporters: ['progress'], port: 9876, colors: true, - logLevel: config.LOG_INFO, + logLevel: config.LOG_INFO, // LOG_DEBUG autoWatch: true, browsers: ['Chrome'], singleRun: false diff --git a/karma.conf.js b/karma.conf.js index ce177aa..0601cd4 100644 --- a/karma.conf.js +++ b/karma.conf.js @@ -13,6 +13,9 @@ module.exports = function(config) { 'test/test-main.js': ['webpack'] }, webpack: { + entry: [ + './src/quagga.js' + ], module: { loaders: [{ test: /\.jsx?$/, diff --git a/src/decoder/barcode_decoder.js b/src/decoder/barcode_decoder.js index 4c68ee6..8bbcc02 100644 --- a/src/decoder/barcode_decoder.js +++ b/src/decoder/barcode_decoder.js @@ -11,6 +11,7 @@ import EAN2Reader from '../reader/ean_2_reader'; import EAN5Reader from '../reader/ean_5_reader'; import UPCEReader from '../reader/upc_e_reader'; import I2of5Reader from '../reader/i2of5_reader'; +import TwoOfFiveReader from '../reader/2of5_reader'; const READERS = { code_128_reader: Code128Reader, @@ -23,7 +24,8 @@ const READERS = { codabar_reader: CodabarReader, upc_reader: UPCReader, upc_e_reader: UPCEReader, - i2of5_reader: I2of5Reader + i2of5_reader: I2of5Reader, + '2of5_reader': TwoOfFiveReader, }; export default { create: function(config, inputImageWrapper) { diff --git a/src/input/camera_access.js b/src/input/camera_access.js index 54f8e1b..f29ac9b 100644 --- a/src/input/camera_access.js +++ b/src/input/camera_access.js @@ -14,7 +14,7 @@ function waitForVideo(video) { function checkVideo() { if (attempts > 0) { - if (video.videoWidth > 0 && video.videoHeight > 0) { + if (video.videoWidth > 10 && video.videoHeight > 10) { if (ENV.development) { console.log(video.videoWidth + "px x " + video.videoHeight + "px"); } diff --git a/src/reader/2of5_reader.js b/src/reader/2of5_reader.js new file mode 100644 index 0000000..77cab5c --- /dev/null +++ b/src/reader/2of5_reader.js @@ -0,0 +1,260 @@ +import BarcodeReader from './barcode_reader'; + +function TwoOfFiveReader(opts) { + BarcodeReader.call(this, opts); + this.barSpaceRatio = [1, 1]; +} + +var N = 1, + W = 3, + properties = { + START_PATTERN: {value: [W, N, W, N, N, N]}, + STOP_PATTERN: {value: [W, N, N, N, W]}, + CODE_PATTERN: {value: [ + [N, N, W, W, N], + [W, N, N, N, W], + [N, W, N, N, W], + [W, W, N, N, N], + [N, N, W, N, W], + [W, N, W, N, N], + [N, W, W, N, N], + [N, N, N, W, W], + [W, N, N, W, N], + [N, W, N, W, N] + ]}, + SINGLE_CODE_ERROR: {value: 0.78, writable: true}, + AVG_CODE_ERROR: {value: 0.30, writable: true}, + MAX_CORRECTION_FACTOR: {value: 5}, + FORMAT: {value: "2of5"} + }; + +const startPatternLength = properties.START_PATTERN.value.reduce((sum, val) => sum + val, 0); + +TwoOfFiveReader.prototype = Object.create(BarcodeReader.prototype, properties); +TwoOfFiveReader.prototype.constructor = TwoOfFiveReader; + +TwoOfFiveReader.prototype._findPattern = function(pattern, offset, isWhite, tryHarder) { + var counter = [], + self = this, + i, + counterPos = 0, + bestMatch = { + error: Number.MAX_VALUE, + code: -1, + start: 0, + end: 0 + }, + error, + j, + sum, + normalized, + epsilon = self.AVG_CODE_ERROR; + + isWhite = isWhite || false; + tryHarder = tryHarder || false; + + if (!offset) { + offset = self._nextSet(self._row); + } + + for ( i = 0; i < pattern.length; i++) { + counter[i] = 0; + } + + for ( i = offset; i < self._row.length; i++) { + if (self._row[i] ^ isWhite) { + counter[counterPos]++; + } else { + if (counterPos === counter.length - 1) { + sum = 0; + for ( j = 0; j < counter.length; j++) { + sum += counter[j]; + } + error = self._matchPattern(counter, pattern); + if (error < epsilon) { + bestMatch.error = error; + bestMatch.start = i - sum; + bestMatch.end = i; + return bestMatch; + } + if (tryHarder) { + for (j = 0; j < counter.length - 2; j++) { + counter[j] = counter[j + 2]; + } + counter[counter.length - 2] = 0; + counter[counter.length - 1] = 0; + counterPos--; + } else { + return null; + } + } else { + counterPos++; + } + counter[counterPos] = 1; + isWhite = !isWhite; + } + } + return null; +}; + +TwoOfFiveReader.prototype._findStart = function() { + var self = this, + leadingWhitespaceStart, + offset = self._nextSet(self._row), + startInfo, + narrowBarWidth = 1; + + while (!startInfo) { + startInfo = self._findPattern(self.START_PATTERN, offset, false, true); + if (!startInfo) { + return null; + } + narrowBarWidth = Math.floor((startInfo.end - startInfo.start) / startPatternLength); + leadingWhitespaceStart = startInfo.start - narrowBarWidth * 5; + if (leadingWhitespaceStart >= 0) { + if (self._matchRange(leadingWhitespaceStart, startInfo.start, 0)) { + return startInfo; + } + } + offset = startInfo.end; + startInfo = null; + } +}; + +TwoOfFiveReader.prototype._verifyTrailingWhitespace = function(endInfo) { + var self = this, + trailingWhitespaceEnd; + + trailingWhitespaceEnd = endInfo.end + ((endInfo.end - endInfo.start) / 2); + if (trailingWhitespaceEnd < self._row.length) { + if (self._matchRange(endInfo.end, trailingWhitespaceEnd, 0)) { + return endInfo; + } + } + return null; +}; + +TwoOfFiveReader.prototype._findEnd = function() { + var self = this, + endInfo, + tmp, + offset; + + self._row.reverse(); + offset = self._nextSet(self._row); + endInfo = self._findPattern(self.STOP_PATTERN, offset, false, true); + self._row.reverse(); + + if (endInfo === null) { + return null; + } + + // reverse numbers + tmp = endInfo.start; + endInfo.start = self._row.length - endInfo.end; + endInfo.end = self._row.length - tmp; + + return endInfo !== null ? self._verifyTrailingWhitespace(endInfo) : null; +}; + +TwoOfFiveReader.prototype._decodeCode = function(counter) { + var j, + self = this, + sum = 0, + normalized, + error, + epsilon = self.AVG_CODE_ERROR, + code, + bestMatch = { + error: Number.MAX_VALUE, + code: -1, + start: 0, + end: 0 + }; + + for ( j = 0; j < counter.length; j++) { + sum += counter[j]; + } + for (code = 0; code < self.CODE_PATTERN.length; code++) { + error = self._matchPattern(counter, self.CODE_PATTERN[code]); + if (error < bestMatch.error) { + bestMatch.code = code; + bestMatch.error = error; + } + } + if (bestMatch.error < epsilon) { + return bestMatch; + } +}; + +TwoOfFiveReader.prototype._decodePayload = function(counters, result, decodedCodes) { + var i, + self = this, + pos = 0, + counterLength = counters.length, + counter = [0, 0, 0, 0, 0], + code; + + while (pos < counterLength) { + for (i = 0; i < 5; i++) { + counter[i] = counters[pos] * this.barSpaceRatio[0]; + pos += 2; + } + code = self._decodeCode(counter); + if (!code) { + return null; + } + result.push(code.code + ""); + decodedCodes.push(code); + } + return code; +}; + +TwoOfFiveReader.prototype._verifyCounterLength = function(counters) { + return (counters.length % 10 === 0); +}; + +TwoOfFiveReader.prototype._decode = function() { + var startInfo, + endInfo, + self = this, + code, + result = [], + decodedCodes = [], + counters; + + startInfo = self._findStart(); + if (!startInfo) { + return null; + } + decodedCodes.push(startInfo); + + endInfo = self._findEnd(); + if (!endInfo) { + return null; + } + + counters = self._fillCounters(startInfo.end, endInfo.start, false); + if (!self._verifyCounterLength(counters)) { + return null; + } + code = self._decodePayload(counters, result, decodedCodes); + if (!code) { + return null; + } + if (result.length % 2 !== 0 || + result.length < 6) { + return null; + } + + decodedCodes.push(endInfo); + return { + code: result.join(""), + start: startInfo.start, + end: endInfo.end, + startInfo: startInfo, + decodedCodes: decodedCodes + }; +}; + +export default TwoOfFiveReader; diff --git a/test/fixtures/2of5/image-001.jpg b/test/fixtures/2of5/image-001.jpg new file mode 100644 index 0000000..77744b9 Binary files /dev/null and b/test/fixtures/2of5/image-001.jpg differ diff --git a/test/fixtures/2of5/image-002.jpg b/test/fixtures/2of5/image-002.jpg new file mode 100644 index 0000000..eea372a Binary files /dev/null and b/test/fixtures/2of5/image-002.jpg differ diff --git a/test/fixtures/2of5/image-003.jpg b/test/fixtures/2of5/image-003.jpg new file mode 100644 index 0000000..bd60e84 Binary files /dev/null and b/test/fixtures/2of5/image-003.jpg differ diff --git a/test/fixtures/2of5/image-004.jpg b/test/fixtures/2of5/image-004.jpg new file mode 100644 index 0000000..b7e58fe Binary files /dev/null and b/test/fixtures/2of5/image-004.jpg differ diff --git a/test/fixtures/2of5/image-005.jpg b/test/fixtures/2of5/image-005.jpg new file mode 100644 index 0000000..06e379e Binary files /dev/null and b/test/fixtures/2of5/image-005.jpg differ diff --git a/test/fixtures/2of5/image-006.jpg b/test/fixtures/2of5/image-006.jpg new file mode 100644 index 0000000..74f4ba5 Binary files /dev/null and b/test/fixtures/2of5/image-006.jpg differ diff --git a/test/fixtures/2of5/image-007.jpg b/test/fixtures/2of5/image-007.jpg new file mode 100644 index 0000000..70677d1 Binary files /dev/null and b/test/fixtures/2of5/image-007.jpg differ diff --git a/test/fixtures/2of5/image-008.jpg b/test/fixtures/2of5/image-008.jpg new file mode 100644 index 0000000..5eecc3f Binary files /dev/null and b/test/fixtures/2of5/image-008.jpg differ diff --git a/test/fixtures/2of5/image-009.jpg b/test/fixtures/2of5/image-009.jpg new file mode 100644 index 0000000..c7c88cb Binary files /dev/null and b/test/fixtures/2of5/image-009.jpg differ diff --git a/test/fixtures/2of5/image-010.jpg b/test/fixtures/2of5/image-010.jpg new file mode 100644 index 0000000..d419d71 Binary files /dev/null and b/test/fixtures/2of5/image-010.jpg differ diff --git a/test/fixtures/2of5/image-012.jpg b/test/fixtures/2of5/image-012.jpg new file mode 100644 index 0000000..f704037 Binary files /dev/null and b/test/fixtures/2of5/image-012.jpg differ diff --git a/test/fixtures/2of5/image-015.jpg b/test/fixtures/2of5/image-015.jpg new file mode 100644 index 0000000..496f6b2 Binary files /dev/null and b/test/fixtures/2of5/image-015.jpg differ diff --git a/test/fixtures/2of5/image-016.jpg b/test/fixtures/2of5/image-016.jpg new file mode 100644 index 0000000..c30e78a Binary files /dev/null and b/test/fixtures/2of5/image-016.jpg differ diff --git a/test/fixtures/2of5/image-017.jpg b/test/fixtures/2of5/image-017.jpg new file mode 100644 index 0000000..9ce5d9f Binary files /dev/null and b/test/fixtures/2of5/image-017.jpg differ diff --git a/test/integration/integration.spec.js b/test/integration/integration.spec.js index 4ff125d..9153e8a 100644 --- a/test/integration/integration.spec.js +++ b/test/integration/integration.spec.js @@ -304,4 +304,41 @@ describe('decodeSingle', function () { }); _runTestSet(testSet, config); }); + + describe("2of5", function() { + var config = config = { + inputStream: { + size: 800, + singleChannel: false + }, + locator: { + patchSize: "medium", + halfSample: true + }, + numOfWorkers: 0, + decoder: { + readers: ["2of5_reader"] + }, + locate: true, + src: null + }, + testSet = [ + {"name": "image-001.jpg", "result": "9577149002"}, + {"name": "image-002.jpg", "result": "9577149002"}, + {"name": "image-003.jpg", "result": "5776158811"}, + {"name": "image-004.jpg", "result": "0463381455"}, + {"name": "image-005.jpg", "result": "3261594101"}, + {"name": "image-006.jpg", "result": "3261594101"}, + {"name": "image-007.jpg", "result": "3261594101"}, + {"name": "image-008.jpg", "result": "6730705801"}, + {"name": "image-009.jpg", "result": "5776158811"}, + {"name": "image-010.jpg", "result": "5776158811"} + ]; + + testSet.forEach(function(sample) { + sample.format = "2of5"; + }); + + _runTestSet(testSet, config); + }); }); diff --git a/test/spec/camera_access.spec.js b/test/spec/camera_access.spec.js index 18bf11b..54d5f00 100644 --- a/test/spec/camera_access.spec.js +++ b/test/spec/camera_access.spec.js @@ -153,21 +153,6 @@ describe("camera_access", () => { done(); }); }); - - it("should set deviceId if facingMode is set to environment", (done) => { - setDevices([{deviceId: "front", kind: "videoinput", label: "front Facing"}, - {deviceId: "back", label: "back Facing", kind: "videoinput"}]); - const givenConstraints = {width: 180, facingMode: "environment"}; - return pickConstraints(givenConstraints).then((actualConstraints) => { - expect(actualConstraints.video).to.deep.equal({width: 180, deviceId: "back"}); - done(); - }) - .catch((err) => { - console.log(err); - expect(err).to.equal(null); - done(); - }); - }); }); }); });