From 7638bc41eb2a947aaa28be1b060ae66198a73d11 Mon Sep 17 00:00:00 2001 From: Christoph Oberhofer Date: Sun, 1 Mar 2015 22:48:00 +0100 Subject: [PATCH 1/4] Added basic support for Code 39 barcodes --- dist/quagga.js | 218 ++++++++++++++++++++++++++++++++++-- example/live_w_locator.html | 2 + example/static_images.html | 4 +- example/static_images.js | 4 +- src/array_helper.js | 10 ++ src/barcode_decoder.js | 5 +- src/barcode_reader.js | 7 +- src/code_39_reader.js | 186 ++++++++++++++++++++++++++++++ src/config.js | 2 +- 9 files changed, 422 insertions(+), 16 deletions(-) create mode 100644 src/code_39_reader.js diff --git a/dist/quagga.js b/dist/quagga.js index d51693e..bf35b5e 100644 --- a/dist/quagga.js +++ b/dist/quagga.js @@ -470,10 +470,11 @@ define( return error; }; - BarcodeReader.prototype._nextSet = function(line) { + BarcodeReader.prototype._nextSet = function(line, offset) { var i; - - for (i = 0; i < line.length; i++) { + + offset = offset || 0; + for (i = offset; i < line.length; i++) { if (line[i]) { return i; } @@ -4180,6 +4181,16 @@ define('array_helper',[],function() { } } return max; + }, + + sum: function(arr) { + var length = arr.length, + sum = 0; + + while(length--) { + sum += arr[length]; + } + return sum; } }; }); @@ -6416,12 +6427,199 @@ define('bresenham',[],function() { /* jshint undef: true, unused: true, browser:true, devel: true */ /* global define */ -define('barcode_decoder',["bresenham", "image_debug", 'code_128_reader', 'ean_reader'], function(Bresenham, ImageDebug, Code128Reader, EANReader) { +define( + 'code_39_reader',[ + "./barcode_reader", + "./array_helper" + ], + function(BarcodeReader, ArrayHelper) { + + + function Code39Reader() { + BarcodeReader.call(this); + } + + var properties = { + ALPHABETH_STRING: {value: "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ-. *$/+%"}, + ALPHABET: {value: [48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 45, 46, 32, 42, 36, 47, 43, 37]}, + CHARACTER_ENCODINGS: {value: [0x034, 0x121, 0x061, 0x160, 0x031, 0x130, 0x070, 0x025, 0x124, 0x064, 0x109, 0x049, 0x148, 0x019, 0x118, 0x058, 0x00D, 0x10C, 0x04C, 0x01C, 0x103, 0x043, 0x142, 0x013, 0x112, 0x052, 0x007, 0x106, 0x046, 0x016, 0x181, 0x0C1, 0x1C0, 0x091, 0x190, 0x0D0, 0x085, 0x184, 0x0C4, 0x094, 0x0A8, 0x0A2, 0x08A, 0x02A]}, + ASTERISK: {value: 0x094} + }; + + Code39Reader.prototype = Object.create(BarcodeReader.prototype, properties); + Code39Reader.prototype.constructor = Code39Reader; + + Code39Reader.prototype._toCounters = function(start, counter) { + var self = this, + numCounters = counter.length, + end = self._row.length, + isWhite = !self._row[start], + i, + counterPos = 0; + + ArrayHelper.init(counter, 0); + + for ( i = start; i < end; i++) { + if (self._row[i] ^ isWhite) { + counter[counterPos]++; + } else { + counterPos++; + if (counterPos === numCounters) { + break; + } else { + counter[counterPos] = 1; + isWhite = !isWhite; + } + } + } + + return counter; + }; + + Code39Reader.prototype._decode = function() { + var self = this, + counters = [0,0,0,0,0,0,0,0,0], + result = [], + start = self._findStart(), + decodedChar, + lastStart, + pattern, + nextStart; + + if (!start) { + return null; + } + nextStart = self._nextSet(self._row, start.end); + + do { + counters = self._toCounters(nextStart, counters); + pattern = self._toPattern(counters); + if (pattern < 0) { + return null; + } + decodedChar = self._patternToChar(pattern); + result.push(decodedChar); + lastStart = nextStart; + nextStart += ArrayHelper.sum(counters); + nextStart = self._nextSet(self._row, nextStart); + } while(decodedChar !== '*'); + result.pop(); + + return { + code : result.join(""), + start : start.start, + end : nextStart, + startInfo : start, + decodedCodes : result + }; + }; + + Code39Reader.prototype._patternToChar = function(pattern) { + var i, + self = this; + + for (i = 0; i < self.CHARACTER_ENCODINGS.length; i++) { + if (self.CHARACTER_ENCODINGS[i] === pattern) { + return String.fromCharCode(self.ALPHABET[i]); + } + } + }; + + Code39Reader.prototype._findNextWidth = function(counters, current) { + var i, + minWidth = Number.MAX_VALUE; + + for (i = 0; i < counters.length; i++) { + if (counters[i] < minWidth && counters[i] > current) { + minWidth = counters[i]; + } + } + + return minWidth; + }; + + Code39Reader.prototype._toPattern = function(counters) { + var numCounters = counters.length, + maxNarrowWidth = 0, + numWideBars = numCounters, + self = this, + pattern, + i; + + while(numWideBars > 3) { + maxNarrowWidth = self._findNextWidth(counters, maxNarrowWidth); + numWideBars = 0; + pattern = 0; + for (i = 0; i < numCounters; i++) { + if (counters[i] > maxNarrowWidth) { + pattern |= 1 << (numCounters - 1 - i); + numWideBars++; + } + } + + if (numWideBars === 3) { + return pattern; + } + } + return -1; + }; + Code39Reader.prototype._findEnd = function() { + + }; + Code39Reader.prototype._findStart = function() { + var self = this, + offset = self._nextSet(self._row), + patternStart = offset, + counter = [0,0,0,0,0,0,0,0,0], + counterPos = 0, + isWhite = false, + i, + j; + + for ( i = offset; i < self._row.length; i++) { + if (self._row[i] ^ isWhite) { + counter[counterPos]++; + } else { + if (counterPos === counter.length - 1) { + + // find start pattern + if (self._toPattern(counter) === self.ASTERISK) { + return { + start: patternStart, + end: i + }; + } + + patternStart += counter[0] + counter[1]; + for ( j = 0; j < 7; j++) { + counter[j] = counter[j + 2]; + } + counter[7] = 0; + counter[8] = 0; + counterPos--; + } else { + counterPos++; + } + counter[counterPos] = 1; + isWhite = !isWhite; + } + } + return null; + }; + + return (Code39Reader); + } +); +/* jshint undef: true, unused: true, browser:true, devel: true */ +/* global define */ + +define('barcode_decoder',["bresenham", "image_debug", 'code_128_reader', 'ean_reader', 'code_39_reader'], function(Bresenham, ImageDebug, Code128Reader, EANReader, Code39Reader) { var readers = { code_128_reader: Code128Reader, - ean_reader: EANReader + ean_reader: EANReader, + code_39_reader: Code39Reader }; var BarcodeDecoder = { create : function(config, inputImageWrapper) { @@ -6821,7 +7019,7 @@ define('config',[],function(){ debug: false, controls: false, locate: true, - numOfWorkers: 4, + numOfWorkers: 0, scriptName: 'quagga.js', visual: { show: true @@ -6967,7 +7165,7 @@ define('camera_access',["html_utils"], function(HtmlUtils) { } /** - * Tries to attach the camer-stream to a given video-element + * Tries to attach the camera-stream to a given video-element * and calls the callback function when the content is ready * @param {Object} constraints * @param {Object} video @@ -7001,6 +7199,12 @@ define('camera_access',["html_utils"], function(HtmlUtils) { }); } + /** + * Normalizes the incoming constraints to satisfy the current browser + * @param config + * @param cb Callback which is called whenever constraints are created + * @returns {*} + */ function normalizeConstraints(config, cb) { var constraints = { audio: false, diff --git a/example/live_w_locator.html b/example/live_w_locator.html index 9849539..f9b1f6b 100644 --- a/example/live_w_locator.html +++ b/example/live_w_locator.html @@ -33,6 +33,8 @@ + +
diff --git a/example/static_images.html b/example/static_images.html index bbbce6d..713b3de 100644 --- a/example/static_images.html +++ b/example/static_images.html @@ -31,8 +31,10 @@
+ + - +
diff --git a/example/static_images.js b/example/static_images.js index b292474..656f5c4 100644 --- a/example/static_images.js +++ b/example/static_images.js @@ -16,8 +16,8 @@ $(function() { }); }, config: { - reader: "code_128", - length: 10 + reader: "code_39", + length: 4 }, attachListeners: function() { $(".controls").on("click", "button.next", function(e) { diff --git a/src/array_helper.js b/src/array_helper.js index 223b9af..398830d 100644 --- a/src/array_helper.js +++ b/src/array_helper.js @@ -71,6 +71,16 @@ define(function() { } } return max; + }, + + sum: function(arr) { + var length = arr.length, + sum = 0; + + while(length--) { + sum += arr[length]; + } + return sum; } }; }); \ No newline at end of file diff --git a/src/barcode_decoder.js b/src/barcode_decoder.js index 8f32460..d6b3aaa 100644 --- a/src/barcode_decoder.js +++ b/src/barcode_decoder.js @@ -1,12 +1,13 @@ /* jshint undef: true, unused: true, browser:true, devel: true */ /* global define */ -define(["bresenham", "image_debug", 'code_128_reader', 'ean_reader'], function(Bresenham, ImageDebug, Code128Reader, EANReader) { +define(["bresenham", "image_debug", 'code_128_reader', 'ean_reader', 'code_39_reader'], function(Bresenham, ImageDebug, Code128Reader, EANReader, Code39Reader) { "use strict"; var readers = { code_128_reader: Code128Reader, - ean_reader: EANReader + ean_reader: EANReader, + code_39_reader: Code39Reader }; var BarcodeDecoder = { create : function(config, inputImageWrapper) { diff --git a/src/barcode_reader.js b/src/barcode_reader.js index 1147468..1466e78 100644 --- a/src/barcode_reader.js +++ b/src/barcode_reader.js @@ -34,10 +34,11 @@ define( return error; }; - BarcodeReader.prototype._nextSet = function(line) { + BarcodeReader.prototype._nextSet = function(line, offset) { var i; - - for (i = 0; i < line.length; i++) { + + offset = offset || 0; + for (i = offset; i < line.length; i++) { if (line[i]) { return i; } diff --git a/src/code_39_reader.js b/src/code_39_reader.js new file mode 100644 index 0000000..1837373 --- /dev/null +++ b/src/code_39_reader.js @@ -0,0 +1,186 @@ +/* jshint undef: true, unused: true, browser:true, devel: true */ +/* global define */ + +define( + [ + "./barcode_reader", + "./array_helper" + ], + function(BarcodeReader, ArrayHelper) { + "use strict"; + + function Code39Reader() { + BarcodeReader.call(this); + } + + var properties = { + ALPHABETH_STRING: {value: "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ-. *$/+%"}, + ALPHABET: {value: [48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 45, 46, 32, 42, 36, 47, 43, 37]}, + CHARACTER_ENCODINGS: {value: [0x034, 0x121, 0x061, 0x160, 0x031, 0x130, 0x070, 0x025, 0x124, 0x064, 0x109, 0x049, 0x148, 0x019, 0x118, 0x058, 0x00D, 0x10C, 0x04C, 0x01C, 0x103, 0x043, 0x142, 0x013, 0x112, 0x052, 0x007, 0x106, 0x046, 0x016, 0x181, 0x0C1, 0x1C0, 0x091, 0x190, 0x0D0, 0x085, 0x184, 0x0C4, 0x094, 0x0A8, 0x0A2, 0x08A, 0x02A]}, + ASTERISK: {value: 0x094} + }; + + Code39Reader.prototype = Object.create(BarcodeReader.prototype, properties); + Code39Reader.prototype.constructor = Code39Reader; + + Code39Reader.prototype._toCounters = function(start, counter) { + var self = this, + numCounters = counter.length, + end = self._row.length, + isWhite = !self._row[start], + i, + counterPos = 0; + + ArrayHelper.init(counter, 0); + + for ( i = start; i < end; i++) { + if (self._row[i] ^ isWhite) { + counter[counterPos]++; + } else { + counterPos++; + if (counterPos === numCounters) { + break; + } else { + counter[counterPos] = 1; + isWhite = !isWhite; + } + } + } + + return counter; + }; + + Code39Reader.prototype._decode = function() { + var self = this, + counters = [0,0,0,0,0,0,0,0,0], + result = [], + start = self._findStart(), + decodedChar, + lastStart, + pattern, + nextStart; + + if (!start) { + return null; + } + nextStart = self._nextSet(self._row, start.end); + + do { + counters = self._toCounters(nextStart, counters); + pattern = self._toPattern(counters); + if (pattern < 0) { + return null; + } + decodedChar = self._patternToChar(pattern); + result.push(decodedChar); + lastStart = nextStart; + nextStart += ArrayHelper.sum(counters); + nextStart = self._nextSet(self._row, nextStart); + } while(decodedChar !== '*'); + result.pop(); + + return { + code : result.join(""), + start : start.start, + end : nextStart, + startInfo : start, + decodedCodes : result + }; + }; + + Code39Reader.prototype._patternToChar = function(pattern) { + var i, + self = this; + + for (i = 0; i < self.CHARACTER_ENCODINGS.length; i++) { + if (self.CHARACTER_ENCODINGS[i] === pattern) { + return String.fromCharCode(self.ALPHABET[i]); + } + } + }; + + Code39Reader.prototype._findNextWidth = function(counters, current) { + var i, + minWidth = Number.MAX_VALUE; + + for (i = 0; i < counters.length; i++) { + if (counters[i] < minWidth && counters[i] > current) { + minWidth = counters[i]; + } + } + + return minWidth; + }; + + Code39Reader.prototype._toPattern = function(counters) { + var numCounters = counters.length, + maxNarrowWidth = 0, + numWideBars = numCounters, + self = this, + pattern, + i; + + while(numWideBars > 3) { + maxNarrowWidth = self._findNextWidth(counters, maxNarrowWidth); + numWideBars = 0; + pattern = 0; + for (i = 0; i < numCounters; i++) { + if (counters[i] > maxNarrowWidth) { + pattern |= 1 << (numCounters - 1 - i); + numWideBars++; + } + } + + if (numWideBars === 3) { + return pattern; + } + } + return -1; + }; + Code39Reader.prototype._findEnd = function() { + + }; + Code39Reader.prototype._findStart = function() { + var self = this, + offset = self._nextSet(self._row), + patternStart = offset, + counter = [0,0,0,0,0,0,0,0,0], + counterPos = 0, + isWhite = false, + i, + j; + + for ( i = offset; i < self._row.length; i++) { + if (self._row[i] ^ isWhite) { + counter[counterPos]++; + } else { + if (counterPos === counter.length - 1) { + + // find start pattern + if (self._toPattern(counter) === self.ASTERISK) { + return { + start: patternStart, + end: i + }; + } + + patternStart += counter[0] + counter[1]; + for ( j = 0; j < 7; j++) { + counter[j] = counter[j + 2]; + } + counter[7] = 0; + counter[8] = 0; + counterPos--; + } else { + counterPos++; + } + counter[counterPos] = 1; + isWhite = !isWhite; + } + } + return null; + }; + + return (Code39Reader); + } +); \ No newline at end of file diff --git a/src/config.js b/src/config.js index 6cf894e..eb929a9 100644 --- a/src/config.js +++ b/src/config.js @@ -16,7 +16,7 @@ define(function(){ debug: false, controls: false, locate: true, - numOfWorkers: 4, + numOfWorkers: 0, scriptName: 'quagga.js', visual: { show: true From b91246c6990e41d665ce286c183330d121b8d064 Mon Sep 17 00:00:00 2001 From: Christoph Oberhofer Date: Wed, 4 Mar 2015 22:23:59 +0100 Subject: [PATCH 2/4] Improved support on Code 39 barcodes --- dist/quagga.js | 46 ++++++++++++++++++++++------ example/static_images.html | 2 +- example/static_images.js | 2 +- src/barcode_reader.js | 12 ++++++++ src/code_39_reader.js | 28 ++++++++++++----- src/config.js | 2 +- test/fixtures/code_39/image-001.jpg | Bin 0 -> 85859 bytes test/fixtures/code_39/image-002.jpg | Bin 0 -> 84301 bytes test/fixtures/code_39/image-003.jpg | Bin 0 -> 71742 bytes test/fixtures/code_39/image-004.jpg | Bin 0 -> 68838 bytes test/fixtures/code_39/image-005.jpg | Bin 0 -> 75154 bytes test/fixtures/code_39/image-006.jpg | Bin 0 -> 85025 bytes test/fixtures/code_39/image-007.jpg | Bin 0 -> 84227 bytes test/fixtures/code_39/image-008.jpg | Bin 0 -> 65317 bytes test/fixtures/code_39/image-009.jpg | Bin 0 -> 80514 bytes test/fixtures/code_39/image-010.jpg | Bin 0 -> 63980 bytes 16 files changed, 72 insertions(+), 20 deletions(-) create mode 100644 test/fixtures/code_39/image-001.jpg create mode 100644 test/fixtures/code_39/image-002.jpg create mode 100644 test/fixtures/code_39/image-003.jpg create mode 100644 test/fixtures/code_39/image-004.jpg create mode 100644 test/fixtures/code_39/image-005.jpg create mode 100644 test/fixtures/code_39/image-006.jpg create mode 100644 test/fixtures/code_39/image-007.jpg create mode 100644 test/fixtures/code_39/image-008.jpg create mode 100644 test/fixtures/code_39/image-009.jpg create mode 100644 test/fixtures/code_39/image-010.jpg diff --git a/dist/quagga.js b/dist/quagga.js index bf35b5e..522e58a 100644 --- a/dist/quagga.js +++ b/dist/quagga.js @@ -597,6 +597,18 @@ define( } return result; }; + + BarcodeReader.prototype._matchRange = function(start, end, value) { + var i; + + start = start < 0 ? 0 : start; + for (i = start; i < end; i++) { + if (this._row[i] !== value) { + return false; + } + } + return true; + }; BarcodeReader.DIRECTION = { FORWARD : 1, @@ -6505,6 +6517,8 @@ define( } while(decodedChar !== '*'); result.pop(); + + return { code : result.join(""), start : start.start, @@ -6542,6 +6556,7 @@ define( var numCounters = counters.length, maxNarrowWidth = 0, numWideBars = numCounters, + wideBarWidth = 0, self = this, pattern, i; @@ -6554,18 +6569,25 @@ define( if (counters[i] > maxNarrowWidth) { pattern |= 1 << (numCounters - 1 - i); numWideBars++; + wideBarWidth += counters[i]; } } if (numWideBars === 3) { + for (i = 0; i < numCounters && numWideBars > 0; i++) { + if (counters[i] > maxNarrowWidth) { + numWideBars--; + if ((counters[i] * 3) >= wideBarWidth) { + return -1; + } + } + } return pattern; } } return -1; }; - Code39Reader.prototype._findEnd = function() { - }; Code39Reader.prototype._findStart = function() { var self = this, offset = self._nextSet(self._row), @@ -6574,7 +6596,8 @@ define( counterPos = 0, isWhite = false, i, - j; + j, + whiteSpaceMustStart; for ( i = offset; i < self._row.length; i++) { if (self._row[i] ^ isWhite) { @@ -6584,10 +6607,13 @@ define( // find start pattern if (self._toPattern(counter) === self.ASTERISK) { - return { - start: patternStart, - end: i - }; + whiteSpaceMustStart = Math.floor(Math.max(0, patternStart - ((i - patternStart) / 4))); + if (self._matchRange(whiteSpaceMustStart, patternStart, 0)) { + return { + start: patternStart, + end: i + }; + } } patternStart += counter[0] + counter[1]; @@ -7016,7 +7042,7 @@ define('config',[],function(){ } }, tracking: false, - debug: false, + debug: true, controls: false, locate: true, numOfWorkers: 0, @@ -7026,9 +7052,9 @@ define('config',[],function(){ }, decoder:{ drawBoundingBox: false, - showFrequency: false, + showFrequency: true, drawScanline: false, - showPattern: false, + showPattern: true, readers: [ 'code_128_reader' ] diff --git a/example/static_images.html b/example/static_images.html index 713b3de..e95d159 100644 --- a/example/static_images.html +++ b/example/static_images.html @@ -43,7 +43,7 @@
    -
    +