Merge pull request #29 from serratus/upc-barcode
UPC-A, UPC-E and EAN-8 support and further improving overall robustness. Closes #24
@@ -7,10 +7,11 @@ quaggaJS
|
||||
|
||||
QuaggaJS is a barcode-scanner entirely written in JavaScript supporting real-
|
||||
time localization and decoding of various types of barcodes such as __EAN__,
|
||||
__CODE128__, __CODE39__ and __CODABAR__. The library is also capable of using `getUserMedia`
|
||||
to get direct access to the user's camera stream. Although the code relies on
|
||||
heavy image-processing even recent smartphones are capable of locating and
|
||||
decoding barcodes in real-time.
|
||||
__CODE 128__, __CODE 39__, __EAN 8__, __UPC-A__, __UPC-C__ and __CODABAR__.
|
||||
The library is also capable of using `getUserMedia` to get direct access to
|
||||
the user's camera stream. Although the code relies on heavy image-processing
|
||||
even recent smartphones are capable of locating and decoding barcodes in
|
||||
real-time.
|
||||
|
||||
Try some [examples](http://serratus.github.io/quaggaJS/examples) and check out
|
||||
the blog post ([How barcode-localization works in QuaggaJS][oberhofer_co_how])
|
||||
@@ -292,6 +293,14 @@ work.
|
||||
|
||||
## <a name="changelog">Changelog</a>
|
||||
|
||||
### 2015-04-30
|
||||
- Features
|
||||
- Added support for [UPC-A and UPC-E][upc_wiki] barcodes
|
||||
- Added support for [EAN-8][ean_8_wiki] barcodes
|
||||
- Improvements
|
||||
- Added extended configuration to the live-video example
|
||||
- Releasing resources when calling ``Quagga.stop()``
|
||||
|
||||
### 2015-04-25
|
||||
- Improvements
|
||||
- Added extended configuration to the file-input example
|
||||
|
||||
@@ -1240,23 +1240,50 @@ define(
|
||||
throw BarcodeReader.PatternNotFoundException;
|
||||
};
|
||||
|
||||
EANReader.prototype._decode = function() {
|
||||
var startInfo,
|
||||
self = this,
|
||||
code = null,
|
||||
result = [],
|
||||
i,
|
||||
codeFrequency = 0x0,
|
||||
decodedCodes = [];
|
||||
EANReader.prototype._findStart = function() {
|
||||
var self = this,
|
||||
leadingWhitespaceStart,
|
||||
offset = self._nextSet(self._row),
|
||||
startInfo;
|
||||
|
||||
try {
|
||||
startInfo = self._findPattern(self.START_PATTERN);
|
||||
code = {
|
||||
code : startInfo.code,
|
||||
start : startInfo.start,
|
||||
end : startInfo.end
|
||||
while(!startInfo) {
|
||||
startInfo = self._findPattern(self.START_PATTERN, offset);
|
||||
leadingWhitespaceStart = startInfo.start - (startInfo.end - startInfo.start);
|
||||
if (leadingWhitespaceStart >= 0) {
|
||||
if (self._matchRange(leadingWhitespaceStart, startInfo.start, 0)) {
|
||||
return startInfo;
|
||||
}
|
||||
}
|
||||
offset = startInfo.end;
|
||||
startInfo = null;
|
||||
}
|
||||
};
|
||||
decodedCodes.push(code);
|
||||
|
||||
EANReader.prototype._verifyTrailingWhitespace = function(endInfo) {
|
||||
var self = this,
|
||||
trailingWhitespaceEnd;
|
||||
|
||||
trailingWhitespaceEnd = endInfo.end + (endInfo.end - endInfo.start);
|
||||
if (trailingWhitespaceEnd < self._row.length) {
|
||||
if (self._matchRange(endInfo.end, trailingWhitespaceEnd, 0)) {
|
||||
return endInfo;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
EANReader.prototype._findEnd = function(offset, isWhite) {
|
||||
var self = this,
|
||||
endInfo = self._findPattern(self.STOP_PATTERN, offset, isWhite, false);
|
||||
|
||||
return self._verifyTrailingWhitespace(endInfo);
|
||||
};
|
||||
|
||||
EANReader.prototype._decodePayload = function(code, result, decodedCodes) {
|
||||
var i,
|
||||
self = this,
|
||||
codeFrequency = 0x0;
|
||||
|
||||
for ( i = 0; i < 6; i++) {
|
||||
code = self._decodeCode(code.end);
|
||||
if (code.code >= self.CODE_G_START) {
|
||||
@@ -1276,7 +1303,7 @@ define(
|
||||
}
|
||||
}
|
||||
|
||||
code = self._findPattern(self.MIDDLE_PATTERN, code.end, true);
|
||||
code = self._findPattern(self.MIDDLE_PATTERN, code.end, true, false);
|
||||
if (code === null) {
|
||||
return null;
|
||||
}
|
||||
@@ -1288,7 +1315,30 @@ define(
|
||||
result.push(code.code);
|
||||
}
|
||||
|
||||
code = self._findPattern(self.STOP_PATTERN, code.end);
|
||||
return code;
|
||||
};
|
||||
|
||||
EANReader.prototype._decode = function() {
|
||||
var startInfo,
|
||||
self = this,
|
||||
code = null,
|
||||
result = [],
|
||||
decodedCodes = [];
|
||||
|
||||
try {
|
||||
startInfo = self._findStart();
|
||||
code = {
|
||||
code : startInfo.code,
|
||||
start : startInfo.start,
|
||||
end : startInfo.end
|
||||
};
|
||||
decodedCodes.push(code);
|
||||
code = self._decodePayload(code, result, decodedCodes);
|
||||
code = self._findEnd(code.end, false);
|
||||
if (!code){
|
||||
return null;
|
||||
}
|
||||
|
||||
decodedCodes.push(code);
|
||||
|
||||
// Checksum
|
||||
@@ -1459,6 +1509,17 @@ define('input_stream',["image_loader"], function(ImageLoader) {
|
||||
}
|
||||
};
|
||||
|
||||
that.clearEventHandlers = function() {
|
||||
_eventNames.forEach(function(eventName) {
|
||||
var handlers = _eventHandlers[eventName];
|
||||
if (handlers && handlers.length > 0) {
|
||||
handlers.forEach(function(handler) {
|
||||
video.removeEventListener(eventName, handler);
|
||||
});
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
that.trigger = function(eventName, args) {
|
||||
var j,
|
||||
handlers = _eventHandlers[eventName];
|
||||
@@ -6384,23 +6445,23 @@ define('bresenham',[],function() {
|
||||
extrema = [],
|
||||
currentDir,
|
||||
dir,
|
||||
threshold = (max - min) / 8,
|
||||
threshold = (max - min) / 12,
|
||||
rThreshold = -threshold,
|
||||
i,
|
||||
j;
|
||||
|
||||
// 1. find extrema
|
||||
currentDir = line[0] > center ? Slope.DIR.DOWN : Slope.DIR.UP;
|
||||
currentDir = line[0] > center ? Slope.DIR.UP : Slope.DIR.DOWN;
|
||||
extrema.push({
|
||||
pos : 0,
|
||||
val : line[0]
|
||||
});
|
||||
for ( i = 0; i < line.length - 1; i++) {
|
||||
slope = (line[i + 1] - line[i]);
|
||||
if (slope < rThreshold) {
|
||||
dir = Slope.DIR.UP;
|
||||
} else if (slope > threshold) {
|
||||
if (slope < rThreshold && line[i + 1] < (center*1.5)) {
|
||||
dir = Slope.DIR.DOWN;
|
||||
} else if (slope > threshold && line[i + 1] > (center*0.5)) {
|
||||
dir = Slope.DIR.UP;
|
||||
} else {
|
||||
dir = currentDir;
|
||||
}
|
||||
@@ -6995,14 +7056,221 @@ define(
|
||||
/* 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', 'codabar_reader'], function(Bresenham, ImageDebug, Code128Reader, EANReader, Code39Reader, CodabarReader) {
|
||||
define(
|
||||
'upc_reader',[
|
||||
"./ean_reader"
|
||||
],
|
||||
function(EANReader) {
|
||||
|
||||
|
||||
function UPCReader() {
|
||||
EANReader.call(this);
|
||||
}
|
||||
|
||||
UPCReader.prototype = Object.create(EANReader.prototype);
|
||||
UPCReader.prototype.constructor = UPCReader;
|
||||
|
||||
UPCReader.prototype._decode = function() {
|
||||
var result = EANReader.prototype._decode.call(this);
|
||||
|
||||
if (result && result.code && result.code.length === 13 && result.code.charAt(0) === "0") {
|
||||
|
||||
result.code = result.code.substring(1);
|
||||
return result;
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
return (UPCReader);
|
||||
}
|
||||
);
|
||||
/* jshint undef: true, unused: true, browser:true, devel: true */
|
||||
/* global define */
|
||||
|
||||
define(
|
||||
'ean_8_reader',[
|
||||
"./ean_reader"
|
||||
],
|
||||
function(EANReader) {
|
||||
|
||||
|
||||
function EAN8Reader() {
|
||||
EANReader.call(this);
|
||||
}
|
||||
|
||||
EAN8Reader.prototype = Object.create(EANReader.prototype);
|
||||
EAN8Reader.prototype.constructor = EAN8Reader;
|
||||
|
||||
EAN8Reader.prototype._decodePayload = function(code, result, decodedCodes) {
|
||||
var i,
|
||||
self = this;
|
||||
|
||||
for ( i = 0; i < 4; i++) {
|
||||
code = self._decodeCode(code.end);
|
||||
result.push(code.code);
|
||||
decodedCodes.push(code);
|
||||
}
|
||||
|
||||
code = self._findPattern(self.MIDDLE_PATTERN, code.end, true);
|
||||
if (code === null) {
|
||||
return null;
|
||||
}
|
||||
decodedCodes.push(code);
|
||||
|
||||
for ( i = 0; i < 4; i++) {
|
||||
code = self._decodeCode(code.end, self.CODE_G_START);
|
||||
decodedCodes.push(code);
|
||||
result.push(code.code);
|
||||
}
|
||||
|
||||
return code;
|
||||
};
|
||||
|
||||
return (EAN8Reader);
|
||||
}
|
||||
);
|
||||
/* jshint undef: true, unused: true, browser:true, devel: true */
|
||||
/* global define */
|
||||
|
||||
define(
|
||||
'upc_e_reader',[
|
||||
"./ean_reader"
|
||||
],
|
||||
function(EANReader) {
|
||||
|
||||
|
||||
function UPCEReader() {
|
||||
EANReader.call(this);
|
||||
}
|
||||
|
||||
var properties = {
|
||||
CODE_FREQUENCY : {value: [
|
||||
[ 56, 52, 50, 49, 44, 38, 35, 42, 41, 37 ],
|
||||
[7, 11, 13, 14, 19, 25, 28, 21, 22, 26]]},
|
||||
STOP_PATTERN: { value: [1 / 6 * 7, 1 / 6 * 7, 1 / 6 * 7, 1 / 6 * 7, 1 / 6 * 7, 1 / 6 * 7]}
|
||||
};
|
||||
|
||||
UPCEReader.prototype = Object.create(EANReader.prototype, properties);
|
||||
UPCEReader.prototype.constructor = UPCEReader;
|
||||
|
||||
UPCEReader.prototype._decodePayload = function(code, result, decodedCodes) {
|
||||
var i,
|
||||
self = this,
|
||||
codeFrequency = 0x0;
|
||||
|
||||
for ( i = 0; i < 6; i++) {
|
||||
code = self._decodeCode(code.end);
|
||||
if (code.code >= self.CODE_G_START) {
|
||||
code.code = code.code - self.CODE_G_START;
|
||||
codeFrequency |= 1 << (5 - i);
|
||||
} else {
|
||||
codeFrequency |= 0 << (5 - i);
|
||||
}
|
||||
result.push(code.code);
|
||||
decodedCodes.push(code);
|
||||
}
|
||||
self._determineParity(codeFrequency, result);
|
||||
|
||||
return code;
|
||||
};
|
||||
|
||||
UPCEReader.prototype._determineParity = function(codeFrequency, result) {
|
||||
var self =this,
|
||||
i,
|
||||
nrSystem;
|
||||
|
||||
for (nrSystem = 0; nrSystem < self.CODE_FREQUENCY.length; nrSystem++){
|
||||
for ( i = 0; i < self.CODE_FREQUENCY[nrSystem].length; i++) {
|
||||
if (codeFrequency === self.CODE_FREQUENCY[nrSystem][i]) {
|
||||
result.unshift(nrSystem);
|
||||
result.push(i);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
UPCEReader.prototype._convertToUPCA = function(result) {
|
||||
var upca = [result[0]],
|
||||
lastDigit = result[result.length - 2];
|
||||
|
||||
if (lastDigit <= 2) {
|
||||
upca = upca.concat(result.slice(1, 3))
|
||||
.concat([lastDigit, 0, 0, 0, 0])
|
||||
.concat(result.slice(3, 6));
|
||||
} else if (lastDigit === 3) {
|
||||
upca = upca.concat(result.slice(1, 4))
|
||||
.concat([0 ,0, 0, 0, 0])
|
||||
.concat(result.slice(4,6));
|
||||
} else if (lastDigit === 4) {
|
||||
upca = upca.concat(result.slice(1, 5))
|
||||
.concat([0, 0, 0, 0, 0, result[5]]);
|
||||
} else {
|
||||
upca = upca.concat(result.slice(1, 6))
|
||||
.concat([0, 0, 0, 0, lastDigit]);
|
||||
}
|
||||
|
||||
upca.push(result[result.length - 1]);
|
||||
return upca;
|
||||
};
|
||||
|
||||
UPCEReader.prototype._checksum = function(result) {
|
||||
return EANReader.prototype._checksum.call(this, this._convertToUPCA(result));
|
||||
};
|
||||
|
||||
UPCEReader.prototype._findEnd = function(offset, isWhite) {
|
||||
isWhite = true;
|
||||
return EANReader.prototype._findEnd.call(this, offset, isWhite);
|
||||
};
|
||||
|
||||
UPCEReader.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 (UPCEReader);
|
||||
}
|
||||
);
|
||||
/* 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',
|
||||
'codabar_reader',
|
||||
'upc_reader',
|
||||
'ean_8_reader',
|
||||
'upc_e_reader'
|
||||
], function(
|
||||
Bresenham,
|
||||
ImageDebug,
|
||||
Code128Reader,
|
||||
EANReader,
|
||||
Code39Reader,
|
||||
CodabarReader,
|
||||
UPCReader,
|
||||
EAN8Reader,
|
||||
UPCEReader) {
|
||||
|
||||
|
||||
var readers = {
|
||||
code_128_reader: Code128Reader,
|
||||
ean_reader: EANReader,
|
||||
ean_8_reader: EAN8Reader,
|
||||
code_39_reader: Code39Reader,
|
||||
codabar_reader: CodabarReader
|
||||
codabar_reader: CodabarReader,
|
||||
upc_reader: UPCReader,
|
||||
upc_e_reader: UPCEReader
|
||||
};
|
||||
var BarcodeDecoder = {
|
||||
create : function(config, inputImageWrapper) {
|
||||
@@ -7090,18 +7358,25 @@ define('barcode_decoder',["bresenham", "image_debug", 'code_128_reader', 'ean_re
|
||||
* @param {Number} angle
|
||||
*/
|
||||
function getExtendedLine(line, angle, ext) {
|
||||
function extendLine(amount) {
|
||||
var extension = {
|
||||
y : ext * Math.sin(angle),
|
||||
x : ext * Math.cos(angle)
|
||||
y : amount * Math.sin(angle),
|
||||
x : amount * Math.cos(angle)
|
||||
};
|
||||
|
||||
line[0].y -= extension.y;
|
||||
line[0].x -= extension.x;
|
||||
line[1].y += extension.y;
|
||||
line[1].x += extension.x;
|
||||
}
|
||||
|
||||
// check if inside image
|
||||
if (!inputImageWrapper.inImageWithBorder(line[0], 0) || !inputImageWrapper.inImageWithBorder(line[1], 0)) {
|
||||
extendLine(ext);
|
||||
while (ext > 1 && !inputImageWrapper.inImageWithBorder(line[0], 0) || !inputImageWrapper.inImageWithBorder(line[1], 0)) {
|
||||
ext -= Math.floor(ext/2);
|
||||
extendLine(-ext);
|
||||
}
|
||||
if (ext <= 1) {
|
||||
return null;
|
||||
}
|
||||
return line;
|
||||
@@ -7181,6 +7456,12 @@ define('barcode_decoder',["bresenham", "image_debug", 'code_128_reader', 'ean_re
|
||||
return result;
|
||||
}
|
||||
|
||||
function getLineLength(line) {
|
||||
return Math.sqrt(
|
||||
Math.pow(Math.abs(line[1].y - line[0].y), 2) +
|
||||
Math.pow(Math.abs(line[1].x - line[0].x), 2));
|
||||
}
|
||||
|
||||
/**
|
||||
* With the help of the configured readers (Code128 or EAN) this function tries to detect a
|
||||
* valid barcode pattern within the given area.
|
||||
@@ -7191,15 +7472,17 @@ define('barcode_decoder',["bresenham", "image_debug", 'code_128_reader', 'ean_re
|
||||
var line,
|
||||
lineAngle,
|
||||
ctx = _canvas.ctx.overlay,
|
||||
result;
|
||||
result,
|
||||
lineLength;
|
||||
|
||||
if (config.drawBoundingBox && ctx) {
|
||||
ImageDebug.drawPath(box, {x: 0, y: 1}, ctx, {color: "blue", lineWidth: 2});
|
||||
}
|
||||
|
||||
line = getLine(box);
|
||||
lineLength = getLineLength(line);
|
||||
lineAngle = Math.atan2(line[1].y - line[0].y, line[1].x - line[0].x);
|
||||
line = getExtendedLine(line, lineAngle, 10);
|
||||
line = getExtendedLine(line, lineAngle, Math.floor(lineLength*0.1));
|
||||
if(line === null){
|
||||
return null;
|
||||
}
|
||||
@@ -7531,7 +7814,8 @@ define('events',[],function() {
|
||||
|
||||
define('camera_access',["html_utils"], function(HtmlUtils) {
|
||||
|
||||
var streamRef;
|
||||
var streamRef,
|
||||
loadedDataHandler;
|
||||
|
||||
/**
|
||||
* Wraps browser-specific getUserMedia
|
||||
@@ -7547,17 +7831,7 @@ define('camera_access',["html_utils"], function(HtmlUtils) {
|
||||
}, failure);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
* @param {Object} callback
|
||||
*/
|
||||
function initCamera(constraints, video, callback) {
|
||||
getUserMedia(constraints, function(src) {
|
||||
video.src = src;
|
||||
video.addEventListener('loadeddata', function() {
|
||||
function loadedData(video, callback) {
|
||||
var attempts = 10;
|
||||
|
||||
function checkVideo() {
|
||||
@@ -7573,9 +7847,24 @@ define('camera_access',["html_utils"], function(HtmlUtils) {
|
||||
}
|
||||
attempts--;
|
||||
}
|
||||
|
||||
checkVideo();
|
||||
}, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
* @param {Object} callback
|
||||
*/
|
||||
function initCamera(constraints, video, callback) {
|
||||
getUserMedia(constraints, function(src) {
|
||||
video.src = src;
|
||||
if (loadedDataHandler) {
|
||||
video.removeEventListener("loadeddata", loadedDataHandler, false);
|
||||
}
|
||||
loadedDataHandler = loadedData.bind(null, video, callback);
|
||||
video.addEventListener('loadeddata', loadedDataHandler, false);
|
||||
video.play();
|
||||
}, function(e) {
|
||||
console.log(e);
|
||||
@@ -8064,8 +8353,14 @@ function(Code128Reader,
|
||||
},
|
||||
stop : function() {
|
||||
_stopped = true;
|
||||
_workerPool.forEach(function(workerThread) {
|
||||
workerThread.worker.terminate();
|
||||
console.log("Worker terminated!");
|
||||
});
|
||||
_workerPool.length = 0;
|
||||
if (_config.inputStream.type === "LiveStream") {
|
||||
CameraAccess.release();
|
||||
_inputStream.clearEventHandlers();
|
||||
}
|
||||
},
|
||||
pause: function() {
|
||||
|
||||
@@ -0,0 +1,83 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
|
||||
|
||||
<title>Camera</title>
|
||||
<script type="text/javascript">
|
||||
navigator.getUserMedia = navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia || navigator.msGetUserMedia;
|
||||
window.URL = window.URL || window.webkitURL || window.mozURL || window.msURL;
|
||||
|
||||
function getUserMedia(constraints, success, failure) {
|
||||
navigator.getUserMedia(constraints, function(stream) {
|
||||
var videoSrc = (window.URL && window.URL.createObjectURL(stream)) || stream;
|
||||
success.apply(null, [videoSrc]);
|
||||
}, failure);
|
||||
}
|
||||
|
||||
|
||||
function initCamera(constraints, video, callback) {
|
||||
getUserMedia(constraints, function (src) {
|
||||
video.src = src;
|
||||
video.addEventListener('loadeddata', function() {
|
||||
var attempts = 10;
|
||||
|
||||
function checkVideo() {
|
||||
if (attempts > 0) {
|
||||
if (video.videoWidth > 0 && video.videoHeight > 0) {
|
||||
console.log(video.videoWidth + "px x " + video.videoHeight + "px");
|
||||
video.play();
|
||||
callback();
|
||||
} else {
|
||||
window.setTimeout(checkVideo, 100);
|
||||
}
|
||||
} else {
|
||||
callback('Unable to play video stream.');
|
||||
}
|
||||
attempts--;
|
||||
}
|
||||
|
||||
checkVideo();
|
||||
}, false);
|
||||
}, function(e) {
|
||||
console.log(e);
|
||||
});
|
||||
}
|
||||
|
||||
function copyToCanvas(video, ctx) {
|
||||
( function frame() {
|
||||
ctx.drawImage(video, 0, 0);
|
||||
window.requestAnimationFrame(frame);
|
||||
}());
|
||||
}
|
||||
|
||||
window.addEventListener('load', function() {
|
||||
var constraints = {
|
||||
video: {
|
||||
mandatory: {
|
||||
minWidth: 1280,
|
||||
minHeight: 720
|
||||
}
|
||||
}
|
||||
},
|
||||
video = document.createElement('video'),
|
||||
canvas = document.createElement('canvas');
|
||||
|
||||
document.body.appendChild(video);
|
||||
document.body.appendChild(canvas);
|
||||
|
||||
initCamera(constraints, video, function() {
|
||||
canvas.setAttribute('width', video.videoWidth);
|
||||
canvas.setAttribute('height', video.videoHeight);
|
||||
copyToCanvas(video, canvas.getContext('2d'));
|
||||
});
|
||||
}, false);
|
||||
|
||||
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
@@ -45,6 +45,9 @@
|
||||
<option value="code_128" selected="selected">Code 128</option>
|
||||
<option value="code_39">Code 39</option>
|
||||
<option value="ean">EAN</option>
|
||||
<option value="ean_8">EAN-8</option>
|
||||
<option value="upc">UPC</option>
|
||||
<option value="upc_e">UPC-E</option>
|
||||
<option value="codabar">Codabar</option>
|
||||
</select>
|
||||
</label>
|
||||
@@ -52,8 +55,8 @@
|
||||
<span>Resolution (long side)</span>
|
||||
<select name="input-stream_size">
|
||||
<option value="320">320px</option>
|
||||
<option value="640">640px</option>
|
||||
<option selected="selected" value="800">800px</option>
|
||||
<option selected="selected" value="640">640px</option>
|
||||
<option value="800">800px</option>
|
||||
<option value="1280">1280px</option>
|
||||
<option value="1600">1600px</option>
|
||||
<option value="1920">1920px</option>
|
||||
@@ -64,8 +67,8 @@
|
||||
<select name="locator_patch-size">
|
||||
<option value="x-small">x-small</option>
|
||||
<option value="small">small</option>
|
||||
<option selected="selected" value="medium">medium</option>
|
||||
<option value="large">large</option>
|
||||
<option value="medium">medium</option>
|
||||
<option selected="selected" value="large">large</option>
|
||||
<option value="x-large">x-large</option>
|
||||
</select>
|
||||
</label>
|
||||
@@ -76,8 +79,8 @@
|
||||
<label>
|
||||
<span>Workers</span>
|
||||
<select name="numOfWorkers">
|
||||
<option value="0">0</option>
|
||||
<option value="1" selected="selected">1</option>
|
||||
<option selected="selected" value="0">0</option>
|
||||
<option value="1">1</option>
|
||||
<option value="2">2</option>
|
||||
<option value="4">4</option>
|
||||
<option value="8">8</option>
|
||||
|
||||
@@ -3,10 +3,6 @@ $(function() {
|
||||
init: function() {
|
||||
App.attachListeners();
|
||||
},
|
||||
config: {
|
||||
reader: "code_128",
|
||||
length: 10
|
||||
},
|
||||
attachListeners: function() {
|
||||
var self = this;
|
||||
|
||||
@@ -92,15 +88,17 @@ $(function() {
|
||||
},
|
||||
state: {
|
||||
inputStream: {
|
||||
size: 800
|
||||
size: 640
|
||||
},
|
||||
locator: {
|
||||
patchSize: "medium",
|
||||
patchSize: "large",
|
||||
halfSample: false
|
||||
},
|
||||
numOfWorkers: 1,
|
||||
numOfWorkers: 0,
|
||||
decoder: {
|
||||
readers: ["code_128_reader"]
|
||||
readers: ["code_128_reader"],
|
||||
showFrequency: true,
|
||||
showPattern: true
|
||||
},
|
||||
locate: true,
|
||||
src: null
|
||||
|
||||
@@ -27,16 +27,58 @@
|
||||
It works best if your camera has built-in auto-focus.
|
||||
</p>
|
||||
<div class="controls">
|
||||
<fieldset class="input-group">
|
||||
<button class="stop">Stop</button>
|
||||
<fieldset class="reader-group">
|
||||
<label>Code128</label>
|
||||
<input type="radio" name="reader" value="code_128" checked />
|
||||
<label>EAN</label>
|
||||
<input type="radio" name="reader" value="ean" />
|
||||
<label>Code39</label>
|
||||
<input type="radio" name="reader" value="code_39" />
|
||||
</fieldset>
|
||||
<br clear="all" />
|
||||
<fieldset class="reader-config-group">
|
||||
<label>
|
||||
<span>Barcode-Type</span>
|
||||
<select name="decoder_readers">
|
||||
<option value="code_128" selected="selected">Code 128</option>
|
||||
<option value="code_39">Code 39</option>
|
||||
<option value="ean">EAN</option>
|
||||
<option value="ean_8">EAN-8</option>
|
||||
<option value="upc">UPC</option>
|
||||
<option value="upc_e">UPC-E</option>
|
||||
<option value="codabar">Codabar</option>
|
||||
</select>
|
||||
</label>
|
||||
<label>
|
||||
<span>Resolution (long side)</span>
|
||||
<select name="input-stream_constraints">
|
||||
<option value="320x240">320px</option>
|
||||
<option selected="selected" value="640x480">640px</option>
|
||||
<option value="800x600">800px</option>
|
||||
<option value="1280x720">1280px</option>
|
||||
<option value="1600x960">1600px</option>
|
||||
<option value="1920x1080">1920px</option>
|
||||
</select>
|
||||
</label>
|
||||
<label>
|
||||
<span>Patch-Size</span>
|
||||
<select name="locator_patch-size">
|
||||
<option value="x-small">x-small</option>
|
||||
<option value="small">small</option>
|
||||
<option selected="selected" value="medium">medium</option>
|
||||
<option value="large">large</option>
|
||||
<option value="x-large">x-large</option>
|
||||
</select>
|
||||
</label>
|
||||
<label>
|
||||
<span>Half-Sample</span>
|
||||
<input type="checkbox" checked="checked" name="locator_half-sample" />
|
||||
</label>
|
||||
<label>
|
||||
<span>Workers</span>
|
||||
<select name="numOfWorkers">
|
||||
<option value="0">0</option>
|
||||
<option value="1">1</option>
|
||||
<option value="2">2</option>
|
||||
<option selected="selected" value="4">4</option>
|
||||
<option value="8">8</option>
|
||||
</select>
|
||||
</label>
|
||||
</fieldset>
|
||||
</div>
|
||||
<div id="result_strip">
|
||||
<ul class="thumbnails"></ul>
|
||||
@@ -50,7 +92,7 @@
|
||||
</footer>
|
||||
|
||||
<script src="../src/vendor/jquery-1.9.0.min.js" type="text/javascript"></script>
|
||||
<script src="../dist/quagga.min.js" type="text/javascript"></script>
|
||||
<script src="../dist/quagga.js" type="text/javascript"></script>
|
||||
<script src="live_w_locator.js" type="text/javascript"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -1,33 +1,103 @@
|
||||
$(function() {
|
||||
var App = {
|
||||
init : function() {
|
||||
Quagga.init({
|
||||
inputStream : {
|
||||
name : "Live",
|
||||
type : "LiveStream"
|
||||
},
|
||||
decoder : {
|
||||
readers : ["code_128_reader"]
|
||||
}
|
||||
}, function() {
|
||||
Quagga.init(this.state, function() {
|
||||
App.attachListeners();
|
||||
Quagga.start();
|
||||
});
|
||||
},
|
||||
attachListeners: function() {
|
||||
$(".controls .reader-group").on("change", "input", function(e) {
|
||||
e.preventDefault();
|
||||
Quagga.setReaders([e.target.value + "_reader"]);
|
||||
});
|
||||
var self = this;
|
||||
|
||||
$(".controls").on("click", "button.stop", function(e) {
|
||||
e.preventDefault();
|
||||
Quagga.stop();
|
||||
});
|
||||
|
||||
$(".controls .reader-config-group").on("change", "input, select", function(e) {
|
||||
e.preventDefault();
|
||||
var $target = $(e.target),
|
||||
value = $target.attr("type") === "checkbox" ? $target.prop("checked") : $target.val(),
|
||||
name = $target.attr("name"),
|
||||
state = self._convertNameToState(name);
|
||||
|
||||
console.log("Value of "+ state + " changed to " + value);
|
||||
self.setState(state, value);
|
||||
});
|
||||
},
|
||||
_accessByPath: function(obj, path, val) {
|
||||
var parts = path.split('.'),
|
||||
depth = parts.length,
|
||||
setter = (typeof val !== "undefined") ? true : false;
|
||||
|
||||
return parts.reduce(function(o, key, i) {
|
||||
if (setter && (i + 1) === depth) {
|
||||
o[key] = val;
|
||||
}
|
||||
return key in o ? o[key] : {};
|
||||
}, obj);
|
||||
},
|
||||
_convertNameToState: function(name) {
|
||||
return name.replace("_", ".").split("-").reduce(function(result, value) {
|
||||
return result + value.charAt(0).toUpperCase() + value.substring(1);
|
||||
});
|
||||
},
|
||||
detachListeners: function() {
|
||||
$(".controls .reader-group").off("change", "input");
|
||||
$(".controls").off("click", "button.stop");
|
||||
$(".controls .reader-config-group").off("change", "input, select");
|
||||
},
|
||||
setState: function(path, value) {
|
||||
var self = this;
|
||||
|
||||
if (typeof self._accessByPath(self.inputMapper, path) === "function") {
|
||||
value = self._accessByPath(self.inputMapper, path)(value);
|
||||
}
|
||||
|
||||
self._accessByPath(self.state, path, value);
|
||||
|
||||
console.log(JSON.stringify(self.state));
|
||||
App.detachListeners();
|
||||
Quagga.stop();
|
||||
App.init();
|
||||
},
|
||||
inputMapper: {
|
||||
inputStream: {
|
||||
constraints: function(value){
|
||||
var values = value.split('x');
|
||||
return {
|
||||
width: parseInt(values[0]),
|
||||
height: parseInt(values[1]),
|
||||
facing: "environment"
|
||||
}
|
||||
}
|
||||
},
|
||||
numOfWorkers: function(value) {
|
||||
return parseInt(value);
|
||||
},
|
||||
decoder: {
|
||||
readers: function(value) {
|
||||
return [value + "_reader"];
|
||||
}
|
||||
}
|
||||
},
|
||||
state: {
|
||||
inputStream: {
|
||||
type : "LiveStream",
|
||||
constraints: {
|
||||
width: 640,
|
||||
height: 480,
|
||||
facing: "environment" // or user
|
||||
}
|
||||
},
|
||||
locator: {
|
||||
patchSize: "medium",
|
||||
halfSample: true
|
||||
},
|
||||
numOfWorkers: 4,
|
||||
decoder: {
|
||||
readers : ["code_128_reader"]
|
||||
},
|
||||
locate: true
|
||||
},
|
||||
lastResult : null
|
||||
};
|
||||
|
||||
@@ -29,16 +29,20 @@
|
||||
<strong>Code128</strong> and <strong>EAN</strong> encoded barcodes.
|
||||
</p>
|
||||
<div class="controls">
|
||||
<fieldset class="input-group">
|
||||
<button class="next">Next</button>
|
||||
<fieldset class="reader-group">
|
||||
<label>Codabar</label>
|
||||
<input type="radio" name="reader" value="codabar" checked />
|
||||
<label>Code39</label>
|
||||
<input type="radio" name="reader" value="code_39" />
|
||||
<label>Code128</label>
|
||||
<input type="radio" name="reader" value="code_128" />
|
||||
<label>EAN</label>
|
||||
<input type="radio" name="reader" value="ean" />
|
||||
</fieldset>
|
||||
<fieldset class="reader-config-group">
|
||||
<span>Barcode-Type</span>
|
||||
<select name="decoder_readers;input-stream_src">
|
||||
<option value="code_128" selected="selected">Code 128</option>
|
||||
<option value="code_39">Code 39</option>
|
||||
<option value="ean">EAN</option>
|
||||
<option value="ean_8">EAN-8</option>
|
||||
<option value="upc">UPC</option>
|
||||
<option value="upc_e">UPC-E</option>
|
||||
<option value="codabar">Codabar</option>
|
||||
</select>
|
||||
</fieldset>
|
||||
</div>
|
||||
<div id="result_strip">
|
||||
|
||||
@@ -1,41 +1,95 @@
|
||||
$(function() {
|
||||
var App = {
|
||||
init: function() {
|
||||
Quagga.init({
|
||||
inputStream: { name: "Test",
|
||||
type: "ImageStream",
|
||||
src: "../test/fixtures/" + App.config.reader + "/",
|
||||
length: App.config.length
|
||||
},
|
||||
decoder : {
|
||||
readers : [App.config.reader + "_reader"]
|
||||
}
|
||||
}, function() {
|
||||
Quagga.init(this.state, function() {
|
||||
App.attachListeners();
|
||||
Quagga.start();
|
||||
});
|
||||
},
|
||||
config: {
|
||||
reader: "codabar",
|
||||
reader: "code_128",
|
||||
length: 10
|
||||
},
|
||||
attachListeners: function() {
|
||||
var self = this;
|
||||
|
||||
$(".controls").on("click", "button.next", function(e) {
|
||||
e.preventDefault();
|
||||
Quagga.start();
|
||||
});
|
||||
|
||||
$(".controls .reader-group").on("change", "input", function(e) {
|
||||
$(".controls .reader-config-group").on("change", "input, select", function(e) {
|
||||
e.preventDefault();
|
||||
App.detachListeners();
|
||||
Quagga.stop();
|
||||
App.config.reader = e.target.value;
|
||||
App.init();
|
||||
var $target = $(e.target),
|
||||
value = $target.attr("type") === "checkbox" ? $target.prop("checked") : $target.val(),
|
||||
name = $target.attr("name"),
|
||||
states = self._convertNameToStates(name);
|
||||
|
||||
console.log("Value of "+ states + " changed to " + value);
|
||||
self.setState(states, value);
|
||||
});
|
||||
},
|
||||
detachListeners: function() {
|
||||
$(".controls").off("click", "button.next");
|
||||
$(".controls .reader-group").off("change", "input");
|
||||
$(".controls .reader-config-group").off("change", "input, select");
|
||||
},
|
||||
_accessByPath: function(obj, path, val) {
|
||||
var parts = path.split('.'),
|
||||
depth = parts.length,
|
||||
setter = (typeof val !== "undefined") ? true : false;
|
||||
|
||||
return parts.reduce(function(o, key, i) {
|
||||
if (setter && (i + 1) === depth) {
|
||||
o[key] = val;
|
||||
}
|
||||
return key in o ? o[key] : {};
|
||||
}, obj);
|
||||
},
|
||||
_convertNameToStates: function(names) {
|
||||
return names.split(";").map(this._convertNameToState.bind(this));
|
||||
},
|
||||
_convertNameToState: function(name) {
|
||||
return name.replace("_", ".").split("-").reduce(function(result, value) {
|
||||
return result + value.charAt(0).toUpperCase() + value.substring(1);
|
||||
});
|
||||
},
|
||||
setState: function(paths, value) {
|
||||
var self = this;
|
||||
|
||||
paths.forEach(function(path) {
|
||||
var mappedValue;
|
||||
if (typeof self._accessByPath(self.inputMapper, path) === "function") {
|
||||
mappedValue = self._accessByPath(self.inputMapper, path)(value);
|
||||
}
|
||||
self._accessByPath(self.state, path, mappedValue);
|
||||
});
|
||||
|
||||
console.log(JSON.stringify(self.state));
|
||||
App.detachListeners();
|
||||
Quagga.stop();
|
||||
App.init();
|
||||
},
|
||||
inputMapper: {
|
||||
decoder: {
|
||||
readers: function(value) {
|
||||
return [value + "_reader"];
|
||||
}
|
||||
},
|
||||
inputStream: {
|
||||
src: function(value) {
|
||||
return "../test/fixtures/" + value + "/"
|
||||
}
|
||||
}
|
||||
},
|
||||
state: {
|
||||
inputStream: { name: "Test",
|
||||
type: "ImageStream",
|
||||
src: "../test/fixtures/code_128/",
|
||||
length: 10
|
||||
},
|
||||
decoder : {
|
||||
readers : ["code_128_reader"]
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "quagga",
|
||||
"version": "0.5.4",
|
||||
"version": "0.6.0",
|
||||
"description": "An advanced barcode-scanner written in JavaScript",
|
||||
"main": "dist/quagga.js",
|
||||
"devDependencies": {
|
||||
|
||||
@@ -1,14 +1,36 @@
|
||||
/* jshint undef: true, unused: true, browser:true, devel: true */
|
||||
/* global define */
|
||||
|
||||
define(["bresenham", "image_debug", 'code_128_reader', 'ean_reader', 'code_39_reader', 'codabar_reader'], function(Bresenham, ImageDebug, Code128Reader, EANReader, Code39Reader, CodabarReader) {
|
||||
define([
|
||||
"bresenham",
|
||||
"image_debug",
|
||||
'code_128_reader',
|
||||
'ean_reader',
|
||||
'code_39_reader',
|
||||
'codabar_reader',
|
||||
'upc_reader',
|
||||
'ean_8_reader',
|
||||
'upc_e_reader'
|
||||
], function(
|
||||
Bresenham,
|
||||
ImageDebug,
|
||||
Code128Reader,
|
||||
EANReader,
|
||||
Code39Reader,
|
||||
CodabarReader,
|
||||
UPCReader,
|
||||
EAN8Reader,
|
||||
UPCEReader) {
|
||||
"use strict";
|
||||
|
||||
var readers = {
|
||||
code_128_reader: Code128Reader,
|
||||
ean_reader: EANReader,
|
||||
ean_8_reader: EAN8Reader,
|
||||
code_39_reader: Code39Reader,
|
||||
codabar_reader: CodabarReader
|
||||
codabar_reader: CodabarReader,
|
||||
upc_reader: UPCReader,
|
||||
upc_e_reader: UPCEReader
|
||||
};
|
||||
var BarcodeDecoder = {
|
||||
create : function(config, inputImageWrapper) {
|
||||
@@ -96,18 +118,25 @@ define(["bresenham", "image_debug", 'code_128_reader', 'ean_reader', 'code_39_re
|
||||
* @param {Number} angle
|
||||
*/
|
||||
function getExtendedLine(line, angle, ext) {
|
||||
function extendLine(amount) {
|
||||
var extension = {
|
||||
y : ext * Math.sin(angle),
|
||||
x : ext * Math.cos(angle)
|
||||
y : amount * Math.sin(angle),
|
||||
x : amount * Math.cos(angle)
|
||||
};
|
||||
|
||||
line[0].y -= extension.y;
|
||||
line[0].x -= extension.x;
|
||||
line[1].y += extension.y;
|
||||
line[1].x += extension.x;
|
||||
}
|
||||
|
||||
// check if inside image
|
||||
if (!inputImageWrapper.inImageWithBorder(line[0], 0) || !inputImageWrapper.inImageWithBorder(line[1], 0)) {
|
||||
extendLine(ext);
|
||||
while (ext > 1 && !inputImageWrapper.inImageWithBorder(line[0], 0) || !inputImageWrapper.inImageWithBorder(line[1], 0)) {
|
||||
ext -= Math.floor(ext/2);
|
||||
extendLine(-ext);
|
||||
}
|
||||
if (ext <= 1) {
|
||||
return null;
|
||||
}
|
||||
return line;
|
||||
@@ -187,6 +216,12 @@ define(["bresenham", "image_debug", 'code_128_reader', 'ean_reader', 'code_39_re
|
||||
return result;
|
||||
}
|
||||
|
||||
function getLineLength(line) {
|
||||
return Math.sqrt(
|
||||
Math.pow(Math.abs(line[1].y - line[0].y), 2) +
|
||||
Math.pow(Math.abs(line[1].x - line[0].x), 2));
|
||||
}
|
||||
|
||||
/**
|
||||
* With the help of the configured readers (Code128 or EAN) this function tries to detect a
|
||||
* valid barcode pattern within the given area.
|
||||
@@ -197,15 +232,17 @@ define(["bresenham", "image_debug", 'code_128_reader', 'ean_reader', 'code_39_re
|
||||
var line,
|
||||
lineAngle,
|
||||
ctx = _canvas.ctx.overlay,
|
||||
result;
|
||||
result,
|
||||
lineLength;
|
||||
|
||||
if (config.drawBoundingBox && ctx) {
|
||||
ImageDebug.drawPath(box, {x: 0, y: 1}, ctx, {color: "blue", lineWidth: 2});
|
||||
}
|
||||
|
||||
line = getLine(box);
|
||||
lineLength = getLineLength(line);
|
||||
lineAngle = Math.atan2(line[1].y - line[0].y, line[1].x - line[0].x);
|
||||
line = getExtendedLine(line, lineAngle, 10);
|
||||
line = getExtendedLine(line, lineAngle, Math.floor(lineLength*0.1));
|
||||
if(line === null){
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -107,23 +107,23 @@ define(function() {
|
||||
extrema = [],
|
||||
currentDir,
|
||||
dir,
|
||||
threshold = (max - min) / 8,
|
||||
threshold = (max - min) / 12,
|
||||
rThreshold = -threshold,
|
||||
i,
|
||||
j;
|
||||
|
||||
// 1. find extrema
|
||||
currentDir = line[0] > center ? Slope.DIR.DOWN : Slope.DIR.UP;
|
||||
currentDir = line[0] > center ? Slope.DIR.UP : Slope.DIR.DOWN;
|
||||
extrema.push({
|
||||
pos : 0,
|
||||
val : line[0]
|
||||
});
|
||||
for ( i = 0; i < line.length - 1; i++) {
|
||||
slope = (line[i + 1] - line[i]);
|
||||
if (slope < rThreshold) {
|
||||
dir = Slope.DIR.UP;
|
||||
} else if (slope > threshold) {
|
||||
if (slope < rThreshold && line[i + 1] < (center*1.5)) {
|
||||
dir = Slope.DIR.DOWN;
|
||||
} else if (slope > threshold && line[i + 1] > (center*0.5)) {
|
||||
dir = Slope.DIR.UP;
|
||||
} else {
|
||||
dir = currentDir;
|
||||
}
|
||||
|
||||
@@ -3,7 +3,8 @@
|
||||
|
||||
define(["html_utils"], function(HtmlUtils) {
|
||||
"use strict";
|
||||
var streamRef;
|
||||
var streamRef,
|
||||
loadedDataHandler;
|
||||
|
||||
/**
|
||||
* Wraps browser-specific getUserMedia
|
||||
@@ -19,17 +20,7 @@ define(["html_utils"], function(HtmlUtils) {
|
||||
}, failure);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
* @param {Object} callback
|
||||
*/
|
||||
function initCamera(constraints, video, callback) {
|
||||
getUserMedia(constraints, function(src) {
|
||||
video.src = src;
|
||||
video.addEventListener('loadeddata', function() {
|
||||
function loadedData(video, callback) {
|
||||
var attempts = 10;
|
||||
|
||||
function checkVideo() {
|
||||
@@ -45,9 +36,24 @@ define(["html_utils"], function(HtmlUtils) {
|
||||
}
|
||||
attempts--;
|
||||
}
|
||||
|
||||
checkVideo();
|
||||
}, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
* @param {Object} callback
|
||||
*/
|
||||
function initCamera(constraints, video, callback) {
|
||||
getUserMedia(constraints, function(src) {
|
||||
video.src = src;
|
||||
if (loadedDataHandler) {
|
||||
video.removeEventListener("loadeddata", loadedDataHandler, false);
|
||||
}
|
||||
loadedDataHandler = loadedData.bind(null, video, callback);
|
||||
video.addEventListener('loadeddata', loadedDataHandler, false);
|
||||
video.play();
|
||||
}, function(e) {
|
||||
console.log(e);
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
/* jshint undef: true, unused: true, browser:true, devel: true */
|
||||
/* global define */
|
||||
|
||||
define(
|
||||
[
|
||||
"./ean_reader"
|
||||
],
|
||||
function(EANReader) {
|
||||
"use strict";
|
||||
|
||||
function EAN8Reader() {
|
||||
EANReader.call(this);
|
||||
}
|
||||
|
||||
EAN8Reader.prototype = Object.create(EANReader.prototype);
|
||||
EAN8Reader.prototype.constructor = EAN8Reader;
|
||||
|
||||
EAN8Reader.prototype._decodePayload = function(code, result, decodedCodes) {
|
||||
var i,
|
||||
self = this;
|
||||
|
||||
for ( i = 0; i < 4; i++) {
|
||||
code = self._decodeCode(code.end);
|
||||
result.push(code.code);
|
||||
decodedCodes.push(code);
|
||||
}
|
||||
|
||||
code = self._findPattern(self.MIDDLE_PATTERN, code.end, true);
|
||||
if (code === null) {
|
||||
return null;
|
||||
}
|
||||
decodedCodes.push(code);
|
||||
|
||||
for ( i = 0; i < 4; i++) {
|
||||
code = self._decodeCode(code.end, self.CODE_G_START);
|
||||
decodedCodes.push(code);
|
||||
result.push(code.code);
|
||||
}
|
||||
|
||||
return code;
|
||||
};
|
||||
|
||||
return (EAN8Reader);
|
||||
}
|
||||
);
|
||||
@@ -167,23 +167,50 @@ define(
|
||||
throw BarcodeReader.PatternNotFoundException;
|
||||
};
|
||||
|
||||
EANReader.prototype._decode = function() {
|
||||
var startInfo,
|
||||
self = this,
|
||||
code = null,
|
||||
result = [],
|
||||
i,
|
||||
codeFrequency = 0x0,
|
||||
decodedCodes = [];
|
||||
EANReader.prototype._findStart = function() {
|
||||
var self = this,
|
||||
leadingWhitespaceStart,
|
||||
offset = self._nextSet(self._row),
|
||||
startInfo;
|
||||
|
||||
try {
|
||||
startInfo = self._findPattern(self.START_PATTERN);
|
||||
code = {
|
||||
code : startInfo.code,
|
||||
start : startInfo.start,
|
||||
end : startInfo.end
|
||||
while(!startInfo) {
|
||||
startInfo = self._findPattern(self.START_PATTERN, offset);
|
||||
leadingWhitespaceStart = startInfo.start - (startInfo.end - startInfo.start);
|
||||
if (leadingWhitespaceStart >= 0) {
|
||||
if (self._matchRange(leadingWhitespaceStart, startInfo.start, 0)) {
|
||||
return startInfo;
|
||||
}
|
||||
}
|
||||
offset = startInfo.end;
|
||||
startInfo = null;
|
||||
}
|
||||
};
|
||||
decodedCodes.push(code);
|
||||
|
||||
EANReader.prototype._verifyTrailingWhitespace = function(endInfo) {
|
||||
var self = this,
|
||||
trailingWhitespaceEnd;
|
||||
|
||||
trailingWhitespaceEnd = endInfo.end + (endInfo.end - endInfo.start);
|
||||
if (trailingWhitespaceEnd < self._row.length) {
|
||||
if (self._matchRange(endInfo.end, trailingWhitespaceEnd, 0)) {
|
||||
return endInfo;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
EANReader.prototype._findEnd = function(offset, isWhite) {
|
||||
var self = this,
|
||||
endInfo = self._findPattern(self.STOP_PATTERN, offset, isWhite, false);
|
||||
|
||||
return self._verifyTrailingWhitespace(endInfo);
|
||||
};
|
||||
|
||||
EANReader.prototype._decodePayload = function(code, result, decodedCodes) {
|
||||
var i,
|
||||
self = this,
|
||||
codeFrequency = 0x0;
|
||||
|
||||
for ( i = 0; i < 6; i++) {
|
||||
code = self._decodeCode(code.end);
|
||||
if (code.code >= self.CODE_G_START) {
|
||||
@@ -203,7 +230,7 @@ define(
|
||||
}
|
||||
}
|
||||
|
||||
code = self._findPattern(self.MIDDLE_PATTERN, code.end, true);
|
||||
code = self._findPattern(self.MIDDLE_PATTERN, code.end, true, false);
|
||||
if (code === null) {
|
||||
return null;
|
||||
}
|
||||
@@ -215,7 +242,30 @@ define(
|
||||
result.push(code.code);
|
||||
}
|
||||
|
||||
code = self._findPattern(self.STOP_PATTERN, code.end);
|
||||
return code;
|
||||
};
|
||||
|
||||
EANReader.prototype._decode = function() {
|
||||
var startInfo,
|
||||
self = this,
|
||||
code = null,
|
||||
result = [],
|
||||
decodedCodes = [];
|
||||
|
||||
try {
|
||||
startInfo = self._findStart();
|
||||
code = {
|
||||
code : startInfo.code,
|
||||
start : startInfo.start,
|
||||
end : startInfo.end
|
||||
};
|
||||
decodedCodes.push(code);
|
||||
code = self._decodePayload(code, result, decodedCodes);
|
||||
code = self._findEnd(code.end, false);
|
||||
if (!code){
|
||||
return null;
|
||||
}
|
||||
|
||||
decodedCodes.push(code);
|
||||
|
||||
// Checksum
|
||||
|
||||
@@ -68,6 +68,17 @@ define(["image_loader"], function(ImageLoader) {
|
||||
}
|
||||
};
|
||||
|
||||
that.clearEventHandlers = function() {
|
||||
_eventNames.forEach(function(eventName) {
|
||||
var handlers = _eventHandlers[eventName];
|
||||
if (handlers && handlers.length > 0) {
|
||||
handlers.forEach(function(handler) {
|
||||
video.removeEventListener(eventName, handler);
|
||||
});
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
that.trigger = function(eventName, args) {
|
||||
var j,
|
||||
handlers = _eventHandlers[eventName];
|
||||
|
||||
@@ -407,8 +407,14 @@ function(Code128Reader,
|
||||
},
|
||||
stop : function() {
|
||||
_stopped = true;
|
||||
_workerPool.forEach(function(workerThread) {
|
||||
workerThread.worker.terminate();
|
||||
console.log("Worker terminated!");
|
||||
});
|
||||
_workerPool.length = 0;
|
||||
if (_config.inputStream.type === "LiveStream") {
|
||||
CameraAccess.release();
|
||||
_inputStream.clearEventHandlers();
|
||||
}
|
||||
},
|
||||
pause: function() {
|
||||
|
||||
@@ -0,0 +1,109 @@
|
||||
/* jshint undef: true, unused: true, browser:true, devel: true */
|
||||
/* global define */
|
||||
|
||||
define(
|
||||
[
|
||||
"./ean_reader"
|
||||
],
|
||||
function(EANReader) {
|
||||
"use strict";
|
||||
|
||||
function UPCEReader() {
|
||||
EANReader.call(this);
|
||||
}
|
||||
|
||||
var properties = {
|
||||
CODE_FREQUENCY : {value: [
|
||||
[ 56, 52, 50, 49, 44, 38, 35, 42, 41, 37 ],
|
||||
[7, 11, 13, 14, 19, 25, 28, 21, 22, 26]]},
|
||||
STOP_PATTERN: { value: [1 / 6 * 7, 1 / 6 * 7, 1 / 6 * 7, 1 / 6 * 7, 1 / 6 * 7, 1 / 6 * 7]}
|
||||
};
|
||||
|
||||
UPCEReader.prototype = Object.create(EANReader.prototype, properties);
|
||||
UPCEReader.prototype.constructor = UPCEReader;
|
||||
|
||||
UPCEReader.prototype._decodePayload = function(code, result, decodedCodes) {
|
||||
var i,
|
||||
self = this,
|
||||
codeFrequency = 0x0;
|
||||
|
||||
for ( i = 0; i < 6; i++) {
|
||||
code = self._decodeCode(code.end);
|
||||
if (code.code >= self.CODE_G_START) {
|
||||
code.code = code.code - self.CODE_G_START;
|
||||
codeFrequency |= 1 << (5 - i);
|
||||
} else {
|
||||
codeFrequency |= 0 << (5 - i);
|
||||
}
|
||||
result.push(code.code);
|
||||
decodedCodes.push(code);
|
||||
}
|
||||
self._determineParity(codeFrequency, result);
|
||||
|
||||
return code;
|
||||
};
|
||||
|
||||
UPCEReader.prototype._determineParity = function(codeFrequency, result) {
|
||||
var self =this,
|
||||
i,
|
||||
nrSystem;
|
||||
|
||||
for (nrSystem = 0; nrSystem < self.CODE_FREQUENCY.length; nrSystem++){
|
||||
for ( i = 0; i < self.CODE_FREQUENCY[nrSystem].length; i++) {
|
||||
if (codeFrequency === self.CODE_FREQUENCY[nrSystem][i]) {
|
||||
result.unshift(nrSystem);
|
||||
result.push(i);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
UPCEReader.prototype._convertToUPCA = function(result) {
|
||||
var upca = [result[0]],
|
||||
lastDigit = result[result.length - 2];
|
||||
|
||||
if (lastDigit <= 2) {
|
||||
upca = upca.concat(result.slice(1, 3))
|
||||
.concat([lastDigit, 0, 0, 0, 0])
|
||||
.concat(result.slice(3, 6));
|
||||
} else if (lastDigit === 3) {
|
||||
upca = upca.concat(result.slice(1, 4))
|
||||
.concat([0 ,0, 0, 0, 0])
|
||||
.concat(result.slice(4,6));
|
||||
} else if (lastDigit === 4) {
|
||||
upca = upca.concat(result.slice(1, 5))
|
||||
.concat([0, 0, 0, 0, 0, result[5]]);
|
||||
} else {
|
||||
upca = upca.concat(result.slice(1, 6))
|
||||
.concat([0, 0, 0, 0, lastDigit]);
|
||||
}
|
||||
|
||||
upca.push(result[result.length - 1]);
|
||||
return upca;
|
||||
};
|
||||
|
||||
UPCEReader.prototype._checksum = function(result) {
|
||||
return EANReader.prototype._checksum.call(this, this._convertToUPCA(result));
|
||||
};
|
||||
|
||||
UPCEReader.prototype._findEnd = function(offset, isWhite) {
|
||||
isWhite = true;
|
||||
return EANReader.prototype._findEnd.call(this, offset, isWhite);
|
||||
};
|
||||
|
||||
UPCEReader.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 (UPCEReader);
|
||||
}
|
||||
);
|
||||
@@ -0,0 +1,31 @@
|
||||
/* jshint undef: true, unused: true, browser:true, devel: true */
|
||||
/* global define */
|
||||
|
||||
define(
|
||||
[
|
||||
"./ean_reader"
|
||||
],
|
||||
function(EANReader) {
|
||||
"use strict";
|
||||
|
||||
function UPCReader() {
|
||||
EANReader.call(this);
|
||||
}
|
||||
|
||||
UPCReader.prototype = Object.create(EANReader.prototype);
|
||||
UPCReader.prototype.constructor = UPCReader;
|
||||
|
||||
UPCReader.prototype._decode = function() {
|
||||
var result = EANReader.prototype._decode.call(this);
|
||||
|
||||
if (result && result.code && result.code.length === 13 && result.code.charAt(0) === "0") {
|
||||
|
||||
result.code = result.code.substring(1);
|
||||
return result;
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
return (UPCReader);
|
||||
}
|
||||
);
|
||||
|
Before Width: | Height: | Size: 570 KiB After Width: | Height: | Size: 48 KiB |
|
Before Width: | Height: | Size: 710 KiB After Width: | Height: | Size: 84 KiB |
|
Before Width: | Height: | Size: 674 KiB After Width: | Height: | Size: 69 KiB |
|
Before Width: | Height: | Size: 797 KiB After Width: | Height: | Size: 93 KiB |
|
Before Width: | Height: | Size: 736 KiB After Width: | Height: | Size: 60 KiB |
|
Before Width: | Height: | Size: 774 KiB After Width: | Height: | Size: 88 KiB |
|
Before Width: | Height: | Size: 748 KiB After Width: | Height: | Size: 72 KiB |
|
Before Width: | Height: | Size: 741 KiB After Width: | Height: | Size: 136 KiB |
|
Before Width: | Height: | Size: 570 KiB After Width: | Height: | Size: 60 KiB |
|
Before Width: | Height: | Size: 562 KiB After Width: | Height: | Size: 115 KiB |
|
Before Width: | Height: | Size: 678 KiB After Width: | Height: | Size: 68 KiB |
|
Before Width: | Height: | Size: 595 KiB After Width: | Height: | Size: 84 KiB |
|
Before Width: | Height: | Size: 678 KiB After Width: | Height: | Size: 63 KiB |
|
Before Width: | Height: | Size: 570 KiB After Width: | Height: | Size: 118 KiB |
|
Before Width: | Height: | Size: 774 KiB After Width: | Height: | Size: 111 KiB |
|
Before Width: | Height: | Size: 687 KiB After Width: | Height: | Size: 89 KiB |
|
Before Width: | Height: | Size: 738 KiB After Width: | Height: | Size: 125 KiB |
|
Before Width: | Height: | Size: 662 KiB After Width: | Height: | Size: 60 KiB |
|
Before Width: | Height: | Size: 664 KiB After Width: | Height: | Size: 85 KiB |
|
Before Width: | Height: | Size: 757 KiB After Width: | Height: | Size: 98 KiB |
|
After Width: | Height: | Size: 48 KiB |
|
After Width: | Height: | Size: 50 KiB |
|
After Width: | Height: | Size: 64 KiB |
|
After Width: | Height: | Size: 50 KiB |
|
After Width: | Height: | Size: 76 KiB |
|
After Width: | Height: | Size: 88 KiB |
|
After Width: | Height: | Size: 47 KiB |
|
After Width: | Height: | Size: 36 KiB |
|
After Width: | Height: | Size: 50 KiB |
|
After Width: | Height: | Size: 52 KiB |
|
After Width: | Height: | Size: 48 KiB |
|
After Width: | Height: | Size: 53 KiB |
|
After Width: | Height: | Size: 84 KiB |
|
After Width: | Height: | Size: 66 KiB |
|
After Width: | Height: | Size: 78 KiB |
|
After Width: | Height: | Size: 46 KiB |
|
After Width: | Height: | Size: 80 KiB |
|
After Width: | Height: | Size: 62 KiB |
|
After Width: | Height: | Size: 82 KiB |
|
After Width: | Height: | Size: 60 KiB |
|
After Width: | Height: | Size: 73 KiB |
|
After Width: | Height: | Size: 60 KiB |
|
After Width: | Height: | Size: 69 KiB |
|
After Width: | Height: | Size: 63 KiB |
|
After Width: | Height: | Size: 66 KiB |
|
After Width: | Height: | Size: 78 KiB |
|
After Width: | Height: | Size: 57 KiB |
|
After Width: | Height: | Size: 71 KiB |
|
After Width: | Height: | Size: 65 KiB |
|
After Width: | Height: | Size: 78 KiB |