Spawning multiple workers for entire pipeline; API change for Quagga.init; Added async-library

pull/12/head
Christoph Oberhofer 11 years ago
parent b7b4711a7e
commit 2d5fa058d5

@ -48,13 +48,18 @@ module.exports = function(grunt) {
"glMatrixAddon" : {
"deps" : ["glMatrix"],
"exports" : "glMatrixAddon"
},
"async": {
"deps": [],
"exports": "async"
}
},
"paths" : {
"typedefs" : "typedefs",
"glMatrix" : "vendor/glMatrix",
"glMatrixAddon" : "glMatrixAddon"
"glMatrixAddon" : "glMatrixAddon",
"async": "vendor/async"
}
}
}

1555
dist/quagga.js vendored

File diff suppressed because it is too large Load Diff

@ -8,11 +8,10 @@ $(function() {
},
decoder : {
readers : ["code_128_reader"]
},
readyFunc : function() {
App.attachListeners();
Quagga.start();
}
}, function() {
App.attachListeners();
Quagga.start();
});
},
attachListeners : function() {

@ -9,11 +9,10 @@ $(function() {
},
decoder : {
readers : [App.config.reader + "_reader"]
},
readyFunc : function() {
App.attachListeners();
Quagga.start();
}
}, function() {
App.attachListeners();
Quagga.start();
});
},
config: {

@ -30,7 +30,7 @@ define(["bresenham", "image_debug", 'code_128_reader', 'ean_reader'], function(B
initConfig();
function initCanvas() {
var $debug = document.querySelector("#debug.detection");
/* var $debug = document.querySelector("#debug.detection");
_canvas.dom.frequency = document.querySelector("canvas.frequency");
if (!_canvas.dom.frequency) {
_canvas.dom.frequency = document.createElement("canvas");
@ -54,7 +54,7 @@ define(["bresenham", "image_debug", 'code_128_reader', 'ean_reader'], function(B
_canvas.dom.overlay = document.querySelector("canvas.drawingBuffer");
if (_canvas.dom.overlay) {
_canvas.ctx.overlay = _canvas.dom.overlay.getContext("2d");
}
} */
}
function initReaders() {
@ -66,7 +66,7 @@ define(["bresenham", "image_debug", 'code_128_reader', 'ean_reader'], function(B
}
function initConfig() {
var i,
/* var i,
vis = [{
node : _canvas.dom.frequency,
prop : config.showFrequency
@ -81,7 +81,7 @@ define(["bresenham", "image_debug", 'code_128_reader', 'ean_reader'], function(B
} else {
vis[i].node.style.display = "none";
}
}
} */
}
/**

@ -26,10 +26,7 @@ function(ImageWrapper, CVUtils, Rasterizer, Tracer, skeletonizer, ArrayHelper, I
_numPatches = {x: 0, y: 0},
_inputImageWrapper,
_skeletonizer,
self = this,
_worker,
_locatedCb,
_initialized;
self = this;
function initBuffers() {
var skeletonImageData;
@ -477,122 +474,44 @@ function(ImageWrapper, CVUtils, Rasterizer, Tracer, skeletonizer, ArrayHelper, I
return label;
}
function initWorker(cb) {
var tmpData,
blobURL;
blobURL = generateWorkerBlob();
_worker = new Worker(blobURL);
URL.revokeObjectURL(blobURL);
tmpData = _inputImageWrapper.data;
_inputImageWrapper.data = null; // do not send the data along
_worker.postMessage({cmd: 'init', inputImageWrapper: _inputImageWrapper, config: _config});
_inputImageWrapper.data = tmpData;
_worker.onmessage = function(e) {
if (e.data.event === 'initialized') {
_initialized = true;
cb();
} else if (e.data.event === 'located') {
_inputImageWrapper.data = new Uint8Array(e.data.buffer);
_locatedCb(e.data.result);
}
};
}
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];
/* jshint ignore:start */
blob = new Blob(['(' +
(function(scriptUrl){
importScripts(scriptUrl);
var inputImageWrapper,
config,
Locator = Quagga.Locator;
self.onmessage = function(e) {
if (e.data.cmd === 'init') {
inputImageWrapper = e.data.inputImageWrapper;
config = e.data.config;
config.useWorker = false;
Locator.init(inputImageWrapper, config, function() {
self.postMessage({'event': 'initialized'});
});
} else if (e.data.cmd === 'locate') {
inputImageWrapper.data = new Uint8Array(e.data.buffer);
Locator.locate(function(result) {
self.postMessage({'event': 'located', result: result, buffer : inputImageWrapper.data}, [inputImageWrapper.data.buffer]);
});
}
};
}).toString() + ')("' + quaggaAbsoluteUrl + '");'
], {type : 'text/javascript'});
/* jshint ignore:end */
return window.URL.createObjectURL(blob);
}
return {
init : function(inputImageWrapper, config, cb) {
init : function(inputImageWrapper, config) {
_config = config;
_inputImageWrapper = inputImageWrapper;
// 1. check config for web-worker
if (_config.useWorker) {
initWorker(cb);
} else {
initBuffers();
initCanvas();
cb();
}
initBuffers();
initCanvas();
},
locate : function(cb) {
locate : function() {
var patchesFound,
topLabels = [],
boxes = [];
if (_config.useWorker) {
_locatedCb = cb;
_worker.postMessage({cmd: 'locate', buffer: _inputImageWrapper.data}, [_inputImageWrapper.data.buffer]);
} else {
if (_config.halfSample) {
CVUtils.halfSample(_inputImageWrapper, _currentImageWrapper);
}
binarizeImage();
patchesFound = findPatches();
// return unless 5% or more patches are found
if (patchesFound.length < _numPatches.x * _numPatches.y * 0.05) {
return cb(null);
}
if (_config.halfSample) {
CVUtils.halfSample(_inputImageWrapper, _currentImageWrapper);
}
// rasterrize area by comparing angular similarity;
var maxLabel = rasterizeAngularSimilarity(patchesFound);
if (maxLabel <= 1) {
return cb(null);
}
binarizeImage();
patchesFound = findPatches();
// return unless 5% or more patches are found
if (patchesFound.length < _numPatches.x * _numPatches.y * 0.05) {
return null;
}
// search for area with the most patches (biggest connected area)
topLabels = findBiggestConnectedAreas(maxLabel);
if (topLabels.length === 0) {
return cb(null);
}
// rasterrize area by comparing angular similarity;
var maxLabel = rasterizeAngularSimilarity(patchesFound);
if (maxLabel <= 1) {
return null;
}
boxes = findBoxes(topLabels, maxLabel);
cb(boxes);
// search for area with the most patches (biggest connected area)
topLabels = findBiggestConnectedAreas(maxLabel);
if (topLabels.length === 0) {
return null;
}
boxes = findBoxes(topLabels, maxLabel);
return boxes;
}
};
});

@ -11,13 +11,15 @@ define(function(){
debug: false,
controls: false,
locate: true,
numOfWorkers: 4,
scriptName: 'quagga.js',
visual: {
show: true
},
decoder:{
drawBoundingBox: true,
drawBoundingBox: false,
showFrequency: false,
drawScanline: true,
drawScanline: false,
showPattern: false,
readers: [
'code_128_reader'
@ -25,8 +27,6 @@ define(function(){
},
locator: {
halfSample: true,
useWorker: true,
scriptName: 'quagga.js',
showCanvas: false,
showPatches: false,
showFoundPatches: false,

@ -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() {
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();
}
});
function canRecord(cb) {
initCanvas();
_framegrabber = FrameGrabber.create(_inputStream, _canvasContainer.dom.image);
initConfig();
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) {
_inputImageWrapper = new ImageWrapper({
x : _inputStream.getWidth(),
y : _inputStream.getHeight()
});
console.log(_inputStream.getWidth());
console.log(_inputStream.getHeight());
function initBuffers(imageWrapper) {
if (imageWrapper) {
_inputImageWrapper = imageWrapper;
} else {
_inputImageWrapper = new ImageWrapper({
x : _inputStream.getWidth(),
y : _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 {
return [_boxSize];
}
}
function locateAndDecode() {
var result,
boxes;
boxes = getBoundingBoxes();
if (boxes) {
result = _decoder.decodeFromBoundingBoxes(boxes);
Events.publish("processed", result);
if (result && result.codeResult) {
Events.publish("detected", result.codeResult.code);
}
} else {
cb([_boxSize]);
Events.publish("processed");
}
}
function update(cb) {
var result;
function update() {
var availableWorker;
if (_framegrabber.grab()) {
_canvasContainer.ctx.overlay.clearRect(0, 0, _inputImageWrapper.size.x, _inputImageWrapper.size.y);
getBoundingBoxes(function(boxes) {
// attach data back to grabber
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 (boxes) {
result = _decoder.decodeFromBoundingBoxes(boxes);
if (result && result.codeResult) {
Events.publish("detected", result.codeResult.code);
}
}
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();
}
return cb();
});
}
} 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") {
window.requestAnimFrame(frame);
}
});
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
};
});

1123
src/vendor/async.js vendored

File diff suppressed because it is too large Load Diff
Loading…
Cancel
Save