|
|
|
@ -1,8 +1,8 @@
|
|
|
|
|
/* jshint undef: true, unused: true, browser:true, devel: true */
|
|
|
|
|
/* global define, vec2 */
|
|
|
|
|
/* global define, vec2, importScripts */
|
|
|
|
|
|
|
|
|
|
define(["code_128_reader", "ean_reader", "input_stream", "image_wrapper", "barcode_locator", "barcode_decoder", "frame_grabber", "html_utils", "config", "events", "camera_access"],
|
|
|
|
|
function(Code128Reader, EANReader, InputStream, ImageWrapper, BarcodeLocator, BarcodeDecoder, FrameGrabber, HtmlUtils, _config, Events, CameraAccess) {
|
|
|
|
|
define(["code_128_reader", "ean_reader", "input_stream", "image_wrapper", "barcode_locator", "barcode_decoder", "frame_grabber", "html_utils", "config", "events", "camera_access", "async"],
|
|
|
|
|
function(Code128Reader, EANReader, InputStream, ImageWrapper, BarcodeLocator, BarcodeDecoder, FrameGrabber, HtmlUtils, _config, Events, CameraAccess, async) {
|
|
|
|
|
"use strict";
|
|
|
|
|
|
|
|
|
|
var _inputStream,
|
|
|
|
@ -21,11 +21,12 @@ function(Code128Reader, EANReader, InputStream, ImageWrapper, BarcodeLocator, Ba
|
|
|
|
|
_inputImageWrapper,
|
|
|
|
|
_boxSize,
|
|
|
|
|
_decoder,
|
|
|
|
|
_initialized = false;
|
|
|
|
|
_workerPool,
|
|
|
|
|
_onUIThread = true;
|
|
|
|
|
|
|
|
|
|
function initialize(config) {
|
|
|
|
|
_config = HtmlUtils.mergeObjects(_config, config);
|
|
|
|
|
initInputStream();
|
|
|
|
|
function initializeData(imageWrapper) {
|
|
|
|
|
initBuffers(imageWrapper);
|
|
|
|
|
_decoder = BarcodeDecoder.create(_config.decoder, _inputImageWrapper);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function initConfig() {
|
|
|
|
@ -48,7 +49,7 @@ function(Code128Reader, EANReader, InputStream, ImageWrapper, BarcodeLocator, Ba
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function initInputStream() {
|
|
|
|
|
function initInputStream(cb) {
|
|
|
|
|
var video;
|
|
|
|
|
if (_config.inputStream.type == "VideoStream") {
|
|
|
|
|
video = document.createElement("video");
|
|
|
|
@ -74,23 +75,31 @@ function(Code128Reader, EANReader, InputStream, ImageWrapper, BarcodeLocator, Ba
|
|
|
|
|
_inputStream.setAttribute("preload", "auto");
|
|
|
|
|
_inputStream.setAttribute("autoplay", true);
|
|
|
|
|
_inputStream.setInputStream(_config.inputStream);
|
|
|
|
|
_inputStream.addEventListener("canrecord", canRecord);
|
|
|
|
|
_inputStream.addEventListener("canrecord", canRecord.bind(undefined, cb));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function canRecord() {
|
|
|
|
|
initBuffers(function() {
|
|
|
|
|
function canRecord(cb) {
|
|
|
|
|
initCanvas();
|
|
|
|
|
_decoder = BarcodeDecoder.create(_config.decoder, _inputImageWrapper);
|
|
|
|
|
_framegrabber = FrameGrabber.create(_inputStream, _canvasContainer.dom.image);
|
|
|
|
|
_framegrabber.attachData(_inputImageWrapper.data);
|
|
|
|
|
|
|
|
|
|
initConfig();
|
|
|
|
|
_inputStream.play();
|
|
|
|
|
_initialized = true;
|
|
|
|
|
if (_config.readyFunc) {
|
|
|
|
|
_config.readyFunc.apply();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (_config.numOfWorkers > 0) {
|
|
|
|
|
initWorkers(function() {
|
|
|
|
|
console.log("Workers created");
|
|
|
|
|
_workerPool.forEach(function(workerThread) {
|
|
|
|
|
console.log(workerThread.busy);
|
|
|
|
|
});
|
|
|
|
|
ready(cb);
|
|
|
|
|
});
|
|
|
|
|
} else {
|
|
|
|
|
initializeData();
|
|
|
|
|
ready(cb);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function ready(cb){
|
|
|
|
|
_inputStream.play();
|
|
|
|
|
cb();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function initCanvas() {
|
|
|
|
@ -104,8 +113,8 @@ function(Code128Reader, EANReader, InputStream, ImageWrapper, BarcodeLocator, Ba
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
_canvasContainer.ctx.image = _canvasContainer.dom.image.getContext("2d");
|
|
|
|
|
_canvasContainer.dom.image.width = _inputImageWrapper.size.x;
|
|
|
|
|
_canvasContainer.dom.image.height = _inputImageWrapper.size.y;
|
|
|
|
|
_canvasContainer.dom.image.width = _inputStream.getWidth();
|
|
|
|
|
_canvasContainer.dom.image.height = _inputStream.getHeight();
|
|
|
|
|
|
|
|
|
|
_canvasContainer.dom.overlay = document.querySelector("canvas.drawingBuffer");
|
|
|
|
|
if (!_canvasContainer.dom.overlay) {
|
|
|
|
@ -121,50 +130,85 @@ function(Code128Reader, EANReader, InputStream, ImageWrapper, BarcodeLocator, Ba
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
_canvasContainer.ctx.overlay = _canvasContainer.dom.overlay.getContext("2d");
|
|
|
|
|
_canvasContainer.dom.overlay.width = _inputImageWrapper.size.x;
|
|
|
|
|
_canvasContainer.dom.overlay.height = _inputImageWrapper.size.y;
|
|
|
|
|
_canvasContainer.dom.overlay.width = _inputStream.getWidth();
|
|
|
|
|
_canvasContainer.dom.overlay.height = _inputStream.getHeight();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function initBuffers(cb) {
|
|
|
|
|
function initBuffers(imageWrapper) {
|
|
|
|
|
if (imageWrapper) {
|
|
|
|
|
_inputImageWrapper = imageWrapper;
|
|
|
|
|
} else {
|
|
|
|
|
_inputImageWrapper = new ImageWrapper({
|
|
|
|
|
x : _inputStream.getWidth(),
|
|
|
|
|
y : _inputStream.getHeight()
|
|
|
|
|
});
|
|
|
|
|
console.log(_inputStream.getWidth());
|
|
|
|
|
console.log(_inputStream.getHeight());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
console.log(_inputImageWrapper.size);
|
|
|
|
|
_boxSize = [
|
|
|
|
|
vec2.create([20, _inputStream.getHeight() / 2 - 100]),
|
|
|
|
|
vec2.create([20, _inputStream.getHeight() / 2 + 100]),
|
|
|
|
|
vec2.create([_inputStream.getWidth() - 20, _inputStream.getHeight() / 2 + 100]),
|
|
|
|
|
vec2.create([_inputStream.getWidth() - 20, _inputStream.getHeight() / 2 - 100])
|
|
|
|
|
vec2.create([20, _inputImageWrapper.size.y / 2 - 100]),
|
|
|
|
|
vec2.create([20, _inputImageWrapper.size.y / 2 + 100]),
|
|
|
|
|
vec2.create([_inputImageWrapper.size.x - 20, _inputImageWrapper.size.y / 2 + 100]),
|
|
|
|
|
vec2.create([_inputImageWrapper.size.x - 20, _inputImageWrapper.size.y / 2 - 100])
|
|
|
|
|
];
|
|
|
|
|
BarcodeLocator.init(_inputImageWrapper, _config.locator, cb);
|
|
|
|
|
BarcodeLocator.init(_inputImageWrapper, _config.locator);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function getBoundingBoxes(cb) {
|
|
|
|
|
function getBoundingBoxes() {
|
|
|
|
|
if (_config.locate) {
|
|
|
|
|
BarcodeLocator.locate(cb);
|
|
|
|
|
return BarcodeLocator.locate();
|
|
|
|
|
} else {
|
|
|
|
|
cb([_boxSize]);
|
|
|
|
|
return [_boxSize];
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function update(cb) {
|
|
|
|
|
var result;
|
|
|
|
|
function locateAndDecode() {
|
|
|
|
|
var result,
|
|
|
|
|
boxes;
|
|
|
|
|
|
|
|
|
|
if (_framegrabber.grab()) {
|
|
|
|
|
_canvasContainer.ctx.overlay.clearRect(0, 0, _inputImageWrapper.size.x, _inputImageWrapper.size.y);
|
|
|
|
|
getBoundingBoxes(function(boxes) {
|
|
|
|
|
// attach data back to grabber
|
|
|
|
|
_framegrabber.attachData(_inputImageWrapper.data);
|
|
|
|
|
boxes = getBoundingBoxes();
|
|
|
|
|
if (boxes) {
|
|
|
|
|
result = _decoder.decodeFromBoundingBoxes(boxes);
|
|
|
|
|
Events.publish("processed", result);
|
|
|
|
|
if (result && result.codeResult) {
|
|
|
|
|
Events.publish("detected", result.codeResult.code);
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
Events.publish("processed");
|
|
|
|
|
}
|
|
|
|
|
return cb();
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function update() {
|
|
|
|
|
var availableWorker;
|
|
|
|
|
|
|
|
|
|
if (_onUIThread) {
|
|
|
|
|
if (_workerPool.length > 0) {
|
|
|
|
|
availableWorker = _workerPool.filter(function(workerThread) {
|
|
|
|
|
return !workerThread.busy;
|
|
|
|
|
})[0];
|
|
|
|
|
if (availableWorker) {
|
|
|
|
|
_framegrabber.attachData(availableWorker.imageData);
|
|
|
|
|
} else {
|
|
|
|
|
return; // all workers are busy
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
_framegrabber.attachData(_inputImageWrapper.data);
|
|
|
|
|
}
|
|
|
|
|
if (_framegrabber.grab()) {
|
|
|
|
|
_canvasContainer.ctx.overlay.clearRect(0, 0, _inputStream.getWidth(), _inputStream.getHeight());
|
|
|
|
|
if (availableWorker) {
|
|
|
|
|
availableWorker.busy = true;
|
|
|
|
|
availableWorker.worker.postMessage({
|
|
|
|
|
cmd: 'process',
|
|
|
|
|
imageData: availableWorker.imageData
|
|
|
|
|
}, [availableWorker.imageData.buffer]);
|
|
|
|
|
} else {
|
|
|
|
|
locateAndDecode();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
locateAndDecode();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -172,21 +216,123 @@ function(Code128Reader, EANReader, InputStream, ImageWrapper, BarcodeLocator, Ba
|
|
|
|
|
_stopped = false;
|
|
|
|
|
( function frame() {
|
|
|
|
|
if (!_stopped) {
|
|
|
|
|
update(function() {
|
|
|
|
|
if (_config.inputStream.type == "LiveStream") {
|
|
|
|
|
update();
|
|
|
|
|
if (_onUIThread && _config.inputStream.type == "LiveStream") {
|
|
|
|
|
window.requestAnimFrame(frame);
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
}());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function initWorkers(cb) {
|
|
|
|
|
_workerPool = [];
|
|
|
|
|
|
|
|
|
|
async.times(_config.numOfWorkers, function(n, next) {
|
|
|
|
|
initWorker(function(workerThread) {
|
|
|
|
|
_workerPool.push(workerThread);
|
|
|
|
|
next(null);
|
|
|
|
|
});
|
|
|
|
|
}, cb);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function initWorker(cb) {
|
|
|
|
|
var blobURL,
|
|
|
|
|
workerThread = {
|
|
|
|
|
worker: null,
|
|
|
|
|
imageData: new Uint8Array(_inputStream.getWidth() * _inputStream.getHeight()),
|
|
|
|
|
busy: true
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
blobURL = generateWorkerBlob();
|
|
|
|
|
workerThread.worker = new Worker(blobURL);
|
|
|
|
|
URL.revokeObjectURL(blobURL);
|
|
|
|
|
|
|
|
|
|
workerThread.worker.onmessage = function(e) {
|
|
|
|
|
if (e.data.event === 'initialized') {
|
|
|
|
|
workerThread.busy = false;
|
|
|
|
|
workerThread.imageData = new Uint8Array(e.data.imageData);
|
|
|
|
|
console.log("Worker initialized");
|
|
|
|
|
return cb(workerThread);
|
|
|
|
|
} else if (e.data.event === 'processed') {
|
|
|
|
|
workerThread.imageData = new Uint8Array(e.data.imageData);
|
|
|
|
|
workerThread.busy = false;
|
|
|
|
|
if (e.data.result && e.data.result.codeResult) {
|
|
|
|
|
Events.publish("detected", e.data.result.codeResult.code);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
workerThread.worker.postMessage({
|
|
|
|
|
cmd: 'init',
|
|
|
|
|
size: {x: _inputStream.getWidth(), y: _inputStream.getHeight()},
|
|
|
|
|
imageData: workerThread.imageData,
|
|
|
|
|
config: _config
|
|
|
|
|
}, [workerThread.imageData.buffer]);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
function workerInterface(scriptUrl) {
|
|
|
|
|
importScripts(scriptUrl);
|
|
|
|
|
/* jshint ignore:start */
|
|
|
|
|
var imageWrapper;
|
|
|
|
|
|
|
|
|
|
self.onmessage = function(e) {
|
|
|
|
|
if (e.data.cmd === 'init') {
|
|
|
|
|
var config = e.data.config;
|
|
|
|
|
config.numOfWorkers = 0;
|
|
|
|
|
imageWrapper = new Quagga.ImageWrapper({
|
|
|
|
|
x : e.data.size.x,
|
|
|
|
|
y : e.data.size.y
|
|
|
|
|
}, new Uint8Array(e.data.imageData));
|
|
|
|
|
Quagga.init(config, ready, imageWrapper);
|
|
|
|
|
Quagga.onProcessed(onProcessed);
|
|
|
|
|
} else if (e.data.cmd === 'process') {
|
|
|
|
|
imageWrapper.data = new Uint8Array(e.data.imageData);
|
|
|
|
|
Quagga.start();
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
function onProcessed(result) {
|
|
|
|
|
self.postMessage({'event': 'processed', imageData: imageWrapper.data, result: result}, [imageWrapper.data.buffer]);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function ready() {
|
|
|
|
|
self.postMessage({'event': 'initialized', imageData: imageWrapper.data}, [imageWrapper.data.buffer]);
|
|
|
|
|
}
|
|
|
|
|
/* jshint ignore:end */
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function generateWorkerBlob() {
|
|
|
|
|
var blob,
|
|
|
|
|
quaggaAbsoluteUrl,
|
|
|
|
|
scripts = document.getElementsByTagName('script'),
|
|
|
|
|
regex = new RegExp('\/' + _config.scriptName + '$');
|
|
|
|
|
|
|
|
|
|
quaggaAbsoluteUrl = Array.prototype.slice.apply(scripts).filter(function(script) {
|
|
|
|
|
return script.src && script.src.match(regex);
|
|
|
|
|
}).map(function(script) {
|
|
|
|
|
return script.src;
|
|
|
|
|
})[0];
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
blob = new Blob(['(' + workerInterface.toString() + ')("' + quaggaAbsoluteUrl + '");'],
|
|
|
|
|
{type : 'text/javascript'});
|
|
|
|
|
|
|
|
|
|
return window.URL.createObjectURL(blob);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return {
|
|
|
|
|
init : function(config) {
|
|
|
|
|
initialize(config);
|
|
|
|
|
init : function(config, cb, imageWrapper) {
|
|
|
|
|
_config = HtmlUtils.mergeObjects(_config, config);
|
|
|
|
|
if (imageWrapper) {
|
|
|
|
|
_onUIThread = false;
|
|
|
|
|
initializeData(imageWrapper);
|
|
|
|
|
return cb();
|
|
|
|
|
} else {
|
|
|
|
|
initInputStream(cb);
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
start : function() {
|
|
|
|
|
console.log("Start!");
|
|
|
|
|
start();
|
|
|
|
|
},
|
|
|
|
|
stop : function() {
|
|
|
|
@ -198,8 +344,8 @@ function(Code128Reader, EANReader, InputStream, ImageWrapper, BarcodeLocator, Ba
|
|
|
|
|
onDetected : function(callback) {
|
|
|
|
|
Events.subscribe("detected", callback);
|
|
|
|
|
},
|
|
|
|
|
isInitialized : function() {
|
|
|
|
|
return _initialized;
|
|
|
|
|
onProcessed: function(callback) {
|
|
|
|
|
Events.subscribe("processed", callback);
|
|
|
|
|
},
|
|
|
|
|
setReaders: function(readers) {
|
|
|
|
|
_decoder.setReaders(readers);
|
|
|
|
@ -212,19 +358,19 @@ function(Code128Reader, EANReader, InputStream, ImageWrapper, BarcodeLocator, Ba
|
|
|
|
|
sequence : false,
|
|
|
|
|
size: 800
|
|
|
|
|
};
|
|
|
|
|
config.readyFunc = function() {
|
|
|
|
|
config.numOfWorkers = 1;
|
|
|
|
|
this.init(config, function() {
|
|
|
|
|
Events.once("detected", function(result) {
|
|
|
|
|
_stopped = true;
|
|
|
|
|
resultCallback.call(null, result);
|
|
|
|
|
}, true);
|
|
|
|
|
start();
|
|
|
|
|
};
|
|
|
|
|
initialize(config);
|
|
|
|
|
});
|
|
|
|
|
},
|
|
|
|
|
Reader: {
|
|
|
|
|
EANReader : EANReader,
|
|
|
|
|
Code128Reader : Code128Reader
|
|
|
|
|
},
|
|
|
|
|
Locator: BarcodeLocator
|
|
|
|
|
ImageWrapper: ImageWrapper
|
|
|
|
|
};
|
|
|
|
|
});
|
|
|
|
|