Added result-collector; Relates to #48

pull/62/head
Christoph Oberhofer 10 years ago
parent e142350863
commit b648014a5e

@ -34,7 +34,7 @@ module.exports = function(grunt) {
}, },
"baseUrl" : "src", "baseUrl" : "src",
"name" : "quagga", "name" : "quagga",
"useStrict": false, "useStrict": true,
"out" : "dist/quagga.js", "out" : "dist/quagga.js",
"include" : ['quagga'], "include" : ['quagga'],
"optimize" : "none", "optimize" : "none",

@ -1,7 +1,7 @@
quaggaJS quaggaJS
======== ========
- [Changelog](#changelog) (2015-06-14) - [Changelog](#changelog) (2015-06-21)
## What is QuaggaJS? ## What is QuaggaJS?
@ -224,7 +224,8 @@ The default `config` object is set as followed:
right: "0%", // right offset right: "0%", // right offset
left: "0%", // left offset left: "0%", // left offset
bottom: "0%" // bottom offset bottom: "0%" // bottom offset
} },
singleChannel: false // true: only the red color-channel is read
}, },
tracking: false, tracking: false,
debug: false, debug: false,
@ -298,8 +299,71 @@ web-workers, and their restriction not to have access to the DOM, the
configuration must be explicitly set to `config.numOfWorkers = 0` in order to configuration must be explicitly set to `config.numOfWorkers = 0` in order to
work. work.
## <a name="resultcollector">ResultCollector</a>
Quagga is not perfect by any means and may produce false positives from time
to time. In order to find out which images produced those false positives,
the built-in ``ResultCollector`` will support you and me helping squashing
bugs in the implementation.
### Creating a ``ResultCollector``
You can easily create a new ``ResultCollector`` by calling its ``create``
method with a configuration.
```javascript
var resultCollector = Quagga.ResultCollector.create({
capture: true, // keep track of the image producing this result
capacity: 20, // maximum number of results to store
blacklist: [ // list containing codes which should not be recorded
{code: "3574660239843", format: "ean_13"}],
filter: function(codeResult) {
// only store results which match this constraint
// returns true/false
// e.g.: return codeResult.format === "ean_13";
return true;
}
});
```
### Using a ``ResultCollector``
After creating a ``ResultCollector`` you have to attach it to Quagga by
calling ``Quagga.registerResultCollector(resultCollector)``.
### Reading results
After a test/recording session, you can now print the collected results which
do not fit into a certain schema. Calling ``getResults`` on the
``resultCollector`` returns an ``Array`` containing objects with:
```javascript
{
codeResult: {}, // same as in onDetected event
frame: "data:image/png;base64,iVBOR..." // dataURL of the gray-scaled image
}
```
The ``frame`` property is an internal representation of the image and
therefore only available in gray-scale. The dataURL representation allows
easy saving/rendering of the image.
### Comparing results
Now, having the frames available on disk, you can load each single image by
calling ``decodeSingle`` with the same configuration as used during recording
. In order to reproduce the exact same result, you have to make sure to turn
on the ``singleChannel`` flag in the configuration when using ``decodeSingle``.
## <a name="changelog">Changelog</a> ## <a name="changelog">Changelog</a>
### 2015-06-21
- Features
- Added ``singleChannel`` configuration to ``inputStream`` (in [config]
(#configobject))
- Added ``ResultCollector`` functionality (see [ResultCollector]
(#resultcollector))
### 2015-06-13 ### 2015-06-13
- Improvements - Improvements
- Added ``format`` property to ``codeResult`` (in [result](#resultobject)) - Added ``format`` property to ``codeResult`` (in [result](#resultobject))

226
dist/quagga.js vendored

@ -4815,13 +4815,19 @@ define('cv_utils',['cluster', 'glMatrixAddon', "array_helper"], function(Cluster
}; };
CVUtils.computeGray = function(imageData, outArray) { CVUtils.computeGray = function(imageData, outArray, config) {
var l = imageData.length / 4; var l = (imageData.length / 4) | 0,
var i = 0; i,
for ( i = 0; i < l; i++) { singleChannel = config && config.singleChannel === true;
//outArray[i] = (0.299*imageData[i*4+0] + 0.587*imageData[i*4+1] + 0.114*imageData[i*4+2]);
outArray[i] = Math.floor(0.299 * imageData[i * 4 + 0] + 0.587 * imageData[i * 4 + 1] + 0.114 * imageData[i * 4 + 2]); if (singleChannel) {
for (i = 0; i < l; i++) {
outArray[i] = imageData[i * 4 + 0];
}
} else {
for (i = 0; i < l; i++) {
outArray[i] = Math.floor(0.299 * imageData[i * 4 + 0] + 0.587 * imageData[i * 4 + 1] + 0.114 * imageData[i * 4 + 2]);
}
} }
}; };
@ -6003,6 +6009,26 @@ define('image_debug',[],function() {
} }
ctx.closePath(); ctx.closePath();
ctx.stroke(); ctx.stroke();
},
drawImage: function(imageData, size, ctx) {
var canvasData = ctx.getImageData(0, 0, size.x, size.y),
data = canvasData.data,
imageDataPos = imageData.length,
canvasDataPos = data.length,
value;
if (canvasDataPos/imageDataPos !== 4) {
return false;
}
while(imageDataPos--){
value = imageData[imageDataPos];
data[--canvasDataPos] = 255;
data[--canvasDataPos] = value;
data[--canvasDataPos] = value;
data[--canvasDataPos] = value;
}
ctx.putImageData(canvasData, 0, 0);
return true;
} }
}; };
@ -7939,7 +7965,7 @@ define('frame_grabber',["cv_utils"], function(CVUtils) {
if(doHalfSample){ if(doHalfSample){
CVUtils.grayAndHalfSampleFromCanvasData(ctxData, _size, _data); CVUtils.grayAndHalfSampleFromCanvasData(ctxData, _size, _data);
} else { } else {
CVUtils.computeGray(ctxData, _data); CVUtils.computeGray(ctxData, _data, _streamConfig);
} }
return true; return true;
} else { } else {
@ -8017,7 +8043,8 @@ define('config',[],function(){
right: "0%", right: "0%",
left: "0%", left: "0%",
bottom: "0%" bottom: "0%"
} },
singleChannel: false // true: only the red color-channel is read
}, },
tracking: false, tracking: false,
debug: false, debug: false,
@ -8288,8 +8315,68 @@ define('camera_access',["html_utils"], function(HtmlUtils) {
}; };
}); });
/* jshint undef: true, unused: true, browser:true, devel: true */
/* global define */
define('result_collector',["image_debug"], function(ImageDebug) {
"use strict";
function contains(codeResult, list) {
if (list) {
return list.some(function (item) {
return Object.keys(item).every(function (key) {
return item[key] === codeResult[key];
});
});
}
return false;
}
function passesFilter(codeResult, filter) {
if (typeof filter === 'function') {
return filter(codeResult);
}
return true;
}
return {
create: function(config) {
var canvas = document.createElement("canvas"),
ctx = canvas.getContext("2d"),
results = [],
capacity = config.capacity || 20,
capture = config.capture === true;
function matchesConstraints(codeResult) {
return capacity && codeResult && !contains(codeResult, config.blacklist) && passesFilter(codeResult, config.filter);
}
return {
addResult: function(data, imageSize, codeResult) {
var result = {};
if (matchesConstraints(codeResult)) {
capacity--;
result.codeResult = codeResult;
if (capture) {
canvas.width = imageSize.x;
canvas.height = imageSize.y;
ImageDebug.drawImage(data, imageSize, ctx);
result.frame = canvas.toDataURL();
}
results.push(result);
}
},
getResults: function() {
return results;
}
};
}
};
});
/* jshint undef: true, unused: true, browser:true, devel: true, evil: true */ /* jshint undef: true, unused: true, browser:true, devel: true, evil: true */
/* global define, vec2 */ /* global define, vec2 */
define('quagga',[ define('quagga',[
@ -8304,7 +8391,8 @@ define('quagga',[
"config", "config",
"events", "events",
"camera_access", "camera_access",
"image_debug"], "image_debug",
"result_collector"],
function(Code128Reader, function(Code128Reader,
EANReader, EANReader,
InputStream, InputStream,
@ -8316,7 +8404,8 @@ function(Code128Reader,
_config, _config,
Events, Events,
CameraAccess, CameraAccess,
ImageDebug) { ImageDebug,
ResultCollector) {
"use strict"; "use strict";
var _inputStream, var _inputStream,
@ -8336,7 +8425,8 @@ function(Code128Reader,
_boxSize, _boxSize,
_decoder, _decoder,
_workerPool = [], _workerPool = [],
_onUIThread = true; _onUIThread = true,
_resultCollector;
function initializeData(imageWrapper) { function initializeData(imageWrapper) {
initBuffers(imageWrapper); initBuffers(imageWrapper);
@ -8344,20 +8434,22 @@ function(Code128Reader,
} }
function initConfig() { function initConfig() {
var vis = [{ if (typeof document !== "undefined") {
node : document.querySelector("div[data-controls]"), var vis = [{
prop : _config.controls node: document.querySelector("div[data-controls]"),
}, { prop: _config.controls
node : _canvasContainer.dom.overlay, }, {
prop : _config.visual.show node: _canvasContainer.dom.overlay,
}]; prop: _config.visual.show
}];
for (var i = 0; i < vis.length; i++) {
if (vis[i].node) { for (var i = 0; i < vis.length; i++) {
if (vis[i].prop === true) { if (vis[i].node) {
vis[i].node.style.display = "block"; if (vis[i].prop === true) {
} else { vis[i].node.style.display = "block";
vis[i].node.style.display = "none"; } else {
vis[i].node.style.display = "none";
}
} }
} }
} }
@ -8420,35 +8512,37 @@ function(Code128Reader,
} }
function initCanvas() { function initCanvas() {
var $viewport = document.querySelector("#interactive.viewport"); if (typeof document !== "undefined") {
_canvasContainer.dom.image = document.querySelector("canvas.imgBuffer"); var $viewport = document.querySelector("#interactive.viewport");
if (!_canvasContainer.dom.image) { _canvasContainer.dom.image = document.querySelector("canvas.imgBuffer");
_canvasContainer.dom.image = document.createElement("canvas"); if (!_canvasContainer.dom.image) {
_canvasContainer.dom.image.className = "imgBuffer"; _canvasContainer.dom.image = document.createElement("canvas");
if($viewport && _config.inputStream.type == "ImageStream") { _canvasContainer.dom.image.className = "imgBuffer";
$viewport.appendChild(_canvasContainer.dom.image); if ($viewport && _config.inputStream.type == "ImageStream") {
} $viewport.appendChild(_canvasContainer.dom.image);
} }
_canvasContainer.ctx.image = _canvasContainer.dom.image.getContext("2d"); }
_canvasContainer.dom.image.width = _inputStream.getCanvasSize().x; _canvasContainer.ctx.image = _canvasContainer.dom.image.getContext("2d");
_canvasContainer.dom.image.height = _inputStream.getCanvasSize().y; _canvasContainer.dom.image.width = _inputStream.getCanvasSize().x;
_canvasContainer.dom.image.height = _inputStream.getCanvasSize().y;
_canvasContainer.dom.overlay = document.querySelector("canvas.drawingBuffer");
if (!_canvasContainer.dom.overlay) { _canvasContainer.dom.overlay = document.querySelector("canvas.drawingBuffer");
_canvasContainer.dom.overlay = document.createElement("canvas"); if (!_canvasContainer.dom.overlay) {
_canvasContainer.dom.overlay.className = "drawingBuffer"; _canvasContainer.dom.overlay = document.createElement("canvas");
if($viewport) { _canvasContainer.dom.overlay.className = "drawingBuffer";
$viewport.appendChild(_canvasContainer.dom.overlay); if ($viewport) {
} $viewport.appendChild(_canvasContainer.dom.overlay);
var clearFix = document.createElement("br"); }
clearFix.setAttribute("clear", "all"); var clearFix = document.createElement("br");
if($viewport) { clearFix.setAttribute("clear", "all");
$viewport.appendChild(clearFix); if ($viewport) {
} $viewport.appendChild(clearFix);
} }
_canvasContainer.ctx.overlay = _canvasContainer.dom.overlay.getContext("2d"); }
_canvasContainer.dom.overlay.width = _inputStream.getCanvasSize().x; _canvasContainer.ctx.overlay = _canvasContainer.dom.overlay.getContext("2d");
_canvasContainer.dom.overlay.height = _inputStream.getCanvasSize().y; _canvasContainer.dom.overlay.width = _inputStream.getCanvasSize().x;
_canvasContainer.dom.overlay.height = _inputStream.getCanvasSize().y;
}
} }
function initBuffers(imageWrapper) { function initBuffers(imageWrapper) {
@ -8516,10 +8610,16 @@ function(Code128Reader,
} }
} }
function publishResult(result) { function publishResult(result, imageData) {
if (_onUIThread) { if (_onUIThread) {
transformResult(result); transformResult(result);
if (imageData && result && result.codeResult) {
if (_resultCollector) {
_resultCollector.addResult(imageData, _inputStream.getCanvasSize(), result.codeResult);
}
}
} }
Events.publish("processed", result); Events.publish("processed", result);
if (result && result.codeResult) { if (result && result.codeResult) {
Events.publish("detected", result); Events.publish("detected", result);
@ -8535,7 +8635,7 @@ function(Code128Reader,
result = _decoder.decodeFromBoundingBoxes(boxes); result = _decoder.decodeFromBoundingBoxes(boxes);
result = result || {}; result = result || {};
result.boxes = boxes; result.boxes = boxes;
publishResult(result); publishResult(result, _inputImageWrapper.data);
} else { } else {
publishResult(); publishResult();
} }
@ -8604,7 +8704,7 @@ function(Code128Reader,
function initWorker(cb) { function initWorker(cb) {
var blobURL, var blobURL,
workerThread = { workerThread = {
worker: null, worker: undefined,
imageData: new Uint8Array(_inputStream.getWidth() * _inputStream.getHeight()), imageData: new Uint8Array(_inputStream.getWidth() * _inputStream.getHeight()),
busy: true busy: true
}; };
@ -8622,7 +8722,7 @@ function(Code128Reader,
} else if (e.data.event === 'processed') { } else if (e.data.event === 'processed') {
workerThread.imageData = new Uint8Array(e.data.imageData); workerThread.imageData = new Uint8Array(e.data.imageData);
workerThread.busy = false; workerThread.busy = false;
publishResult(e.data.result); publishResult(e.data.result, workerThread.imageData);
} }
}; };
@ -8737,6 +8837,11 @@ function(Code128Reader,
setReaders: function(readers) { setReaders: function(readers) {
setReaders(readers); setReaders(readers);
}, },
registerResultCollector: function(resultCollector) {
if (resultCollector && typeof resultCollector.addResult === 'function') {
_resultCollector = resultCollector;
}
},
canvas : _canvasContainer, canvas : _canvasContainer,
decodeSingle : function(config, resultCallback) { decodeSingle : function(config, resultCallback) {
config = HtmlUtils.mergeObjects({ config = HtmlUtils.mergeObjects({
@ -8764,7 +8869,8 @@ function(Code128Reader,
Code128Reader : Code128Reader Code128Reader : Code128Reader
}, },
ImageWrapper: ImageWrapper, ImageWrapper: ImageWrapper,
ImageDebug: ImageDebug ImageDebug: ImageDebug,
ResultCollector: ResultCollector
}; };
}); });

File diff suppressed because one or more lines are too long

@ -74,7 +74,7 @@
padding: 10px 0; padding: 10px 0;
} }
/* line 50, ../sass/_viewport.scss */ /* line 50, ../sass/_viewport.scss */
#result_strip ul.thumbnails { #result_strip > ul {
padding: 0; padding: 0;
margin: 0; margin: 0;
list-style-type: none; list-style-type: none;
@ -84,34 +84,34 @@
white-space: nowrap; white-space: nowrap;
} }
/* line 59, ../sass/_viewport.scss */ /* line 59, ../sass/_viewport.scss */
#result_strip ul.thumbnails > li { #result_strip > ul > li {
display: inline-block; display: inline-block;
vertical-align: middle; vertical-align: middle;
width: 160px; width: 160px;
} }
/* line 63, ../sass/_viewport.scss */ /* line 63, ../sass/_viewport.scss */
#result_strip ul.thumbnails > li .thumbnail { #result_strip > ul > li .thumbnail {
padding: 5px; padding: 5px;
margin: 4px; margin: 4px;
border: 1px dashed #CCC; border: 1px dashed #CCC;
} }
/* line 68, ../sass/_viewport.scss */ /* line 68, ../sass/_viewport.scss */
#result_strip ul.thumbnails > li .thumbnail img { #result_strip > ul > li .thumbnail img {
max-width: 140px; max-width: 140px;
} }
/* line 71, ../sass/_viewport.scss */ /* line 71, ../sass/_viewport.scss */
#result_strip ul.thumbnails > li .thumbnail .caption { #result_strip > ul > li .thumbnail .caption {
white-space: normal; white-space: normal;
} }
/* line 73, ../sass/_viewport.scss */ /* line 73, ../sass/_viewport.scss */
#result_strip ul.thumbnails > li .thumbnail .caption h4 { #result_strip > ul > li .thumbnail .caption h4 {
text-align: center; text-align: center;
word-wrap: break-word; word-wrap: break-word;
height: 40px; height: 40px;
margin: 0px; margin: 0px;
} }
/* line 83, ../sass/_viewport.scss */ /* line 83, ../sass/_viewport.scss */
#result_strip ul.thumbnails:after { #result_strip > ul:after {
content: ""; content: "";
display: table; display: table;
clear: both; clear: both;

@ -77,6 +77,10 @@
<span>Half-Sample</span> <span>Half-Sample</span>
<input type="checkbox" name="locator_half-sample" /> <input type="checkbox" name="locator_half-sample" />
</label> </label>
<label>
<span>Single Channel</span>
<input type="checkbox" name="input-stream_single-channel" />
</label>
<label> <label>
<span>Workers</span> <span>Workers</span>
<select name="numOfWorkers"> <select name="numOfWorkers">

@ -88,11 +88,12 @@ $(function() {
}, },
state: { state: {
inputStream: { inputStream: {
size: 640 size: 640,
singleChannel: false
}, },
locator: { locator: {
patchSize: "large", patchSize: "medium",
halfSample: false halfSample: true
}, },
numOfWorkers: 0, numOfWorkers: 0,
decoder: { decoder: {

@ -77,6 +77,10 @@
<span>Half-Sample</span> <span>Half-Sample</span>
<input type="checkbox" name="locator_half-sample" /> <input type="checkbox" name="locator_half-sample" />
</label> </label>
<label>
<span>Single Channel</span>
<input type="checkbox" name="input-stream_single-channel" />
</label>
<label> <label>
<span>Workers</span> <span>Workers</span>
<select name="numOfWorkers"> <select name="numOfWorkers">

@ -112,11 +112,12 @@ define(['quagga'], function(Quagga) {
}, },
state: { state: {
inputStream: { inputStream: {
size: 640 size: 640,
singleChannel: false
}, },
locator: { locator: {
patchSize: "large", patchSize: "medium",
halfSample: false halfSample: true
}, },
numOfWorkers: 0, numOfWorkers: 0,
decoder: { decoder: {

@ -82,6 +82,7 @@
</div> </div>
<div id="result_strip"> <div id="result_strip">
<ul class="thumbnails"></ul> <ul class="thumbnails"></ul>
<ul class="collector"></ul>
</div> </div>
<div id="interactive" class="viewport"></div> <div id="interactive" class="viewport"></div>
</section> </section>

@ -1,7 +1,18 @@
$(function() { $(function() {
var resultCollector = Quagga.ResultCollector.create({
capture: true,
capacity: 20,
blacklist: [{code: "3574660239843", format: "ean_13"}],
filter: function(codeResult) {
// only store results which match this constraint
// e.g.: codeResult
return true;
}
});
var App = { var App = {
init : function() { init : function() {
Quagga.init(this.state, function() { Quagga.init(this.state, function() {
Quagga.registerResultCollector(resultCollector);
App.attachListeners(); App.attachListeners();
Quagga.start(); Quagga.start();
}); });
@ -12,6 +23,7 @@ $(function() {
$(".controls").on("click", "button.stop", function(e) { $(".controls").on("click", "button.stop", function(e) {
e.preventDefault(); e.preventDefault();
Quagga.stop(); Quagga.stop();
self._printCollectedResults();
}); });
$(".controls .reader-config-group").on("change", "input, select", function(e) { $(".controls .reader-config-group").on("change", "input, select", function(e) {
@ -25,6 +37,18 @@ $(function() {
self.setState(state, value); self.setState(state, value);
}); });
}, },
_printCollectedResults: function() {
var results = resultCollector.getResults(),
$ul = $("#result_strip ul.collector");
results.forEach(function(result) {
var $li = $('<li><div class="thumbnail"><div class="imgWrapper"><img /></div><div class="caption"><h4 class="code"></h4></div></div></li>');
$li.find("img").attr("src", result.frame);
$li.find("h4.code").html(result.codeResult.code + " (" + result.codeResult.format + ")");
$ul.prepend($li);
});
},
_accessByPath: function(obj, path, val) { _accessByPath: function(obj, path, val) {
var parts = path.split('.'), var parts = path.split('.'),
depth = parts.length, depth = parts.length,
@ -93,9 +117,9 @@ $(function() {
patchSize: "medium", patchSize: "medium",
halfSample: true halfSample: true
}, },
numOfWorkers: 4, numOfWorkers: 1,
decoder: { decoder: {
readers : ["code_128_reader"] readers : ["ean_reader"]
}, },
locate: true locate: true
}, },

@ -47,7 +47,7 @@
border-bottom: 1px solid #EEE; border-bottom: 1px solid #EEE;
padding: 10px 0; padding: 10px 0;
ul.thumbnails{ & > ul {
padding: 0; padding: 0;
margin: 0; margin: 0;
list-style-type: none; list-style-type: none;

@ -1,6 +1,6 @@
{ {
"name": "quagga", "name": "quagga",
"version": "0.6.9", "version": "0.6.10",
"description": "An advanced barcode-scanner written in JavaScript", "description": "An advanced barcode-scanner written in JavaScript",
"main": "dist/quagga.js", "main": "dist/quagga.js",
"devDependencies": { "devDependencies": {

@ -71,9 +71,9 @@ define(['quagga', 'async'], function(Quagga, async) {
var config = generateConfig(), var config = generateConfig(),
testSet = [ testSet = [
{"name": "image-001.jpg", "result": "0001285112001000040801"}, {"name": "image-001.jpg", "result": "0001285112001000040801"},
// {"name": "image-002.jpg", "result": "FANAVF1461710"}, {"name": "image-002.jpg", "result": "FANAVF1461710"},
// {"name": "image-003.jpg", "result": "673023"}, // {"name": "image-003.jpg", "result": "673023"},
{"name": "image-004.jpg", "result": "010210150301625334"}, // {"name": "image-004.jpg", "result": "010210150301625334"},
{"name": "image-005.jpg", "result": "419055603900009001012999"}, {"name": "image-005.jpg", "result": "419055603900009001012999"},
{"name": "image-006.jpg", "result": "419055603900009001012999"}, {"name": "image-006.jpg", "result": "419055603900009001012999"},
{"name": "image-007.jpg", "result": "T 000003552345"}, {"name": "image-007.jpg", "result": "T 000003552345"},

@ -0,0 +1,103 @@
define(['result_collector', 'image_debug'], function(ResultCollector, ImageDebug) {
var canvasMock,
imageSize,
config;
beforeEach(function() {
imageSize = {x: 320, y: 240};
config = {
capture: true,
capacity: 20,
blacklist: [{code: "3574660239843", format: "ean_13"}],
filter: function(codeResult) {
return true;
}
};
canvasMock = {
getContext: function() {
return {};
},
toDataURL: sinon.spy(),
width: 0,
height: 0
};
sinon.stub(document, "createElement", function(type) {
if (type === "canvas") {
return canvasMock;
}
});
});
afterEach(function() {
document.createElement.restore();
});
describe('create', function () {
it("should return a new collector", function() {
ResultCollector.create(config);
expect(document.createElement.calledOnce).to.be.equal(true);
expect(document.createElement.getCall(0).args[0]).to.equal("canvas");
});
});
describe('addResult', function() {
beforeEach(function() {
sinon.stub(ImageDebug, "drawImage", function() {});
});
afterEach(function() {
ImageDebug.drawImage.restore();
});
it("should not add result if capacity is full", function(){
config.capacity = 1;
var collector = ResultCollector.create(config);
collector.addResult([], imageSize, {});
collector.addResult([], imageSize, {});
collector.addResult([], imageSize, {});
expect(collector.getResults()).to.have.length(1);
});
it("should only add results which match constraints", function(){
var collector = ResultCollector.create(config),
results;
collector.addResult([], imageSize, {code: "423423443", format: "ean_13"});
collector.addResult([], imageSize, {code: "3574660239843", format: "ean_13"});
collector.addResult([], imageSize, {code: "3574660239843", format: "code_128"});
results = collector.getResults();
expect(results).to.have.length(2);
results.forEach(function(result) {
expect(result).not.to.deep.equal(config.blacklist[0]);
});
});
it("should add result if no filter is set", function() {
delete config.filter;
var collector = ResultCollector.create(config);
collector.addResult([], imageSize, {code: "423423443", format: "ean_13"});
expect(collector.getResults()).to.have.length(1);
});
it("should not add results if filter returns false", function() {
config.filter = function(){ return false };
var collector = ResultCollector.create(config);
collector.addResult([], imageSize, {code: "423423443", format: "ean_13"});
expect(collector.getResults()).to.have.length(0);
});
it("should add result if no blacklist is set", function() {
delete config.blacklist;
var collector = ResultCollector.create(config);
collector.addResult([], imageSize, {code: "3574660239843", format: "ean_13"});
expect(collector.getResults()).to.have.length(1);
});
});
});

@ -18,7 +18,8 @@ define(function(){
right: "0%", right: "0%",
left: "0%", left: "0%",
bottom: "0%" bottom: "0%"
} },
singleChannel: false // true: only the red color-channel is read
}, },
tracking: false, tracking: false,
debug: false, debug: false,

@ -484,13 +484,19 @@ define(['cluster', 'glMatrixAddon', "array_helper"], function(Cluster2, glMatrix
}; };
CVUtils.computeGray = function(imageData, outArray) { CVUtils.computeGray = function(imageData, outArray, config) {
var l = imageData.length / 4; var l = (imageData.length / 4) | 0,
var i = 0; i,
for ( i = 0; i < l; i++) { singleChannel = config && config.singleChannel === true;
//outArray[i] = (0.299*imageData[i*4+0] + 0.587*imageData[i*4+1] + 0.114*imageData[i*4+2]);
if (singleChannel) {
outArray[i] = Math.floor(0.299 * imageData[i * 4 + 0] + 0.587 * imageData[i * 4 + 1] + 0.114 * imageData[i * 4 + 2]); for (i = 0; i < l; i++) {
outArray[i] = imageData[i * 4 + 0];
}
} else {
for (i = 0; i < l; i++) {
outArray[i] = Math.floor(0.299 * imageData[i * 4 + 0] + 0.587 * imageData[i * 4 + 1] + 0.114 * imageData[i * 4 + 2]);
}
} }
}; };

@ -59,7 +59,7 @@ define(["cv_utils"], function(CVUtils) {
if(doHalfSample){ if(doHalfSample){
CVUtils.grayAndHalfSampleFromCanvasData(ctxData, _size, _data); CVUtils.grayAndHalfSampleFromCanvasData(ctxData, _size, _data);
} else { } else {
CVUtils.computeGray(ctxData, _data); CVUtils.computeGray(ctxData, _data, _streamConfig);
} }
return true; return true;
} else { } else {

@ -23,6 +23,26 @@ define(function() {
} }
ctx.closePath(); ctx.closePath();
ctx.stroke(); ctx.stroke();
},
drawImage: function(imageData, size, ctx) {
var canvasData = ctx.getImageData(0, 0, size.x, size.y),
data = canvasData.data,
imageDataPos = imageData.length,
canvasDataPos = data.length,
value;
if (canvasDataPos/imageDataPos !== 4) {
return false;
}
while(imageDataPos--){
value = imageData[imageDataPos];
data[--canvasDataPos] = 255;
data[--canvasDataPos] = value;
data[--canvasDataPos] = value;
data[--canvasDataPos] = value;
}
ctx.putImageData(canvasData, 0, 0);
return true;
} }
}; };

@ -1,5 +1,5 @@
/* jshint undef: true, unused: true, browser:true, devel: true, evil: true */ /* jshint undef: true, unused: true, browser:true, devel: true, evil: true */
/* global define, vec2 */ /* global define, vec2 */
define([ define([
@ -14,7 +14,8 @@ define([
"config", "config",
"events", "events",
"camera_access", "camera_access",
"image_debug"], "image_debug",
"result_collector"],
function(Code128Reader, function(Code128Reader,
EANReader, EANReader,
InputStream, InputStream,
@ -26,7 +27,8 @@ function(Code128Reader,
_config, _config,
Events, Events,
CameraAccess, CameraAccess,
ImageDebug) { ImageDebug,
ResultCollector) {
"use strict"; "use strict";
var _inputStream, var _inputStream,
@ -46,7 +48,8 @@ function(Code128Reader,
_boxSize, _boxSize,
_decoder, _decoder,
_workerPool = [], _workerPool = [],
_onUIThread = true; _onUIThread = true,
_resultCollector;
function initializeData(imageWrapper) { function initializeData(imageWrapper) {
initBuffers(imageWrapper); initBuffers(imageWrapper);
@ -54,20 +57,22 @@ function(Code128Reader,
} }
function initConfig() { function initConfig() {
var vis = [{ if (typeof document !== "undefined") {
node : document.querySelector("div[data-controls]"), var vis = [{
prop : _config.controls node: document.querySelector("div[data-controls]"),
}, { prop: _config.controls
node : _canvasContainer.dom.overlay, }, {
prop : _config.visual.show node: _canvasContainer.dom.overlay,
}]; prop: _config.visual.show
}];
for (var i = 0; i < vis.length; i++) {
if (vis[i].node) { for (var i = 0; i < vis.length; i++) {
if (vis[i].prop === true) { if (vis[i].node) {
vis[i].node.style.display = "block"; if (vis[i].prop === true) {
} else { vis[i].node.style.display = "block";
vis[i].node.style.display = "none"; } else {
vis[i].node.style.display = "none";
}
} }
} }
} }
@ -130,35 +135,37 @@ function(Code128Reader,
} }
function initCanvas() { function initCanvas() {
var $viewport = document.querySelector("#interactive.viewport"); if (typeof document !== "undefined") {
_canvasContainer.dom.image = document.querySelector("canvas.imgBuffer"); var $viewport = document.querySelector("#interactive.viewport");
if (!_canvasContainer.dom.image) { _canvasContainer.dom.image = document.querySelector("canvas.imgBuffer");
_canvasContainer.dom.image = document.createElement("canvas"); if (!_canvasContainer.dom.image) {
_canvasContainer.dom.image.className = "imgBuffer"; _canvasContainer.dom.image = document.createElement("canvas");
if($viewport && _config.inputStream.type == "ImageStream") { _canvasContainer.dom.image.className = "imgBuffer";
$viewport.appendChild(_canvasContainer.dom.image); if ($viewport && _config.inputStream.type == "ImageStream") {
} $viewport.appendChild(_canvasContainer.dom.image);
} }
_canvasContainer.ctx.image = _canvasContainer.dom.image.getContext("2d");
_canvasContainer.dom.image.width = _inputStream.getCanvasSize().x;
_canvasContainer.dom.image.height = _inputStream.getCanvasSize().y;
_canvasContainer.dom.overlay = document.querySelector("canvas.drawingBuffer");
if (!_canvasContainer.dom.overlay) {
_canvasContainer.dom.overlay = document.createElement("canvas");
_canvasContainer.dom.overlay.className = "drawingBuffer";
if($viewport) {
$viewport.appendChild(_canvasContainer.dom.overlay);
} }
var clearFix = document.createElement("br"); _canvasContainer.ctx.image = _canvasContainer.dom.image.getContext("2d");
clearFix.setAttribute("clear", "all"); _canvasContainer.dom.image.width = _inputStream.getCanvasSize().x;
if($viewport) { _canvasContainer.dom.image.height = _inputStream.getCanvasSize().y;
$viewport.appendChild(clearFix);
_canvasContainer.dom.overlay = document.querySelector("canvas.drawingBuffer");
if (!_canvasContainer.dom.overlay) {
_canvasContainer.dom.overlay = document.createElement("canvas");
_canvasContainer.dom.overlay.className = "drawingBuffer";
if ($viewport) {
$viewport.appendChild(_canvasContainer.dom.overlay);
}
var clearFix = document.createElement("br");
clearFix.setAttribute("clear", "all");
if ($viewport) {
$viewport.appendChild(clearFix);
}
} }
_canvasContainer.ctx.overlay = _canvasContainer.dom.overlay.getContext("2d");
_canvasContainer.dom.overlay.width = _inputStream.getCanvasSize().x;
_canvasContainer.dom.overlay.height = _inputStream.getCanvasSize().y;
} }
_canvasContainer.ctx.overlay = _canvasContainer.dom.overlay.getContext("2d");
_canvasContainer.dom.overlay.width = _inputStream.getCanvasSize().x;
_canvasContainer.dom.overlay.height = _inputStream.getCanvasSize().y;
} }
function initBuffers(imageWrapper) { function initBuffers(imageWrapper) {
@ -226,10 +233,16 @@ function(Code128Reader,
} }
} }
function publishResult(result) { function publishResult(result, imageData) {
if (_onUIThread) { if (_onUIThread) {
transformResult(result); transformResult(result);
if (imageData && result && result.codeResult) {
if (_resultCollector) {
_resultCollector.addResult(imageData, _inputStream.getCanvasSize(), result.codeResult);
}
}
} }
Events.publish("processed", result); Events.publish("processed", result);
if (result && result.codeResult) { if (result && result.codeResult) {
Events.publish("detected", result); Events.publish("detected", result);
@ -245,7 +258,7 @@ function(Code128Reader,
result = _decoder.decodeFromBoundingBoxes(boxes); result = _decoder.decodeFromBoundingBoxes(boxes);
result = result || {}; result = result || {};
result.boxes = boxes; result.boxes = boxes;
publishResult(result); publishResult(result, _inputImageWrapper.data);
} else { } else {
publishResult(); publishResult();
} }
@ -314,7 +327,7 @@ function(Code128Reader,
function initWorker(cb) { function initWorker(cb) {
var blobURL, var blobURL,
workerThread = { workerThread = {
worker: null, worker: undefined,
imageData: new Uint8Array(_inputStream.getWidth() * _inputStream.getHeight()), imageData: new Uint8Array(_inputStream.getWidth() * _inputStream.getHeight()),
busy: true busy: true
}; };
@ -332,7 +345,7 @@ function(Code128Reader,
} else if (e.data.event === 'processed') { } else if (e.data.event === 'processed') {
workerThread.imageData = new Uint8Array(e.data.imageData); workerThread.imageData = new Uint8Array(e.data.imageData);
workerThread.busy = false; workerThread.busy = false;
publishResult(e.data.result); publishResult(e.data.result, workerThread.imageData);
} }
}; };
@ -447,6 +460,11 @@ function(Code128Reader,
setReaders: function(readers) { setReaders: function(readers) {
setReaders(readers); setReaders(readers);
}, },
registerResultCollector: function(resultCollector) {
if (resultCollector && typeof resultCollector.addResult === 'function') {
_resultCollector = resultCollector;
}
},
canvas : _canvasContainer, canvas : _canvasContainer,
decodeSingle : function(config, resultCallback) { decodeSingle : function(config, resultCallback) {
config = HtmlUtils.mergeObjects({ config = HtmlUtils.mergeObjects({
@ -474,6 +492,7 @@ function(Code128Reader,
Code128Reader : Code128Reader Code128Reader : Code128Reader
}, },
ImageWrapper: ImageWrapper, ImageWrapper: ImageWrapper,
ImageDebug: ImageDebug ImageDebug: ImageDebug,
ResultCollector: ResultCollector
}; };
}); });

@ -0,0 +1,59 @@
/* jshint undef: true, unused: true, browser:true, devel: true */
/* global define */
define(["image_debug"], function(ImageDebug) {
"use strict";
function contains(codeResult, list) {
if (list) {
return list.some(function (item) {
return Object.keys(item).every(function (key) {
return item[key] === codeResult[key];
});
});
}
return false;
}
function passesFilter(codeResult, filter) {
if (typeof filter === 'function') {
return filter(codeResult);
}
return true;
}
return {
create: function(config) {
var canvas = document.createElement("canvas"),
ctx = canvas.getContext("2d"),
results = [],
capacity = config.capacity || 20,
capture = config.capture === true;
function matchesConstraints(codeResult) {
return capacity && codeResult && !contains(codeResult, config.blacklist) && passesFilter(codeResult, config.filter);
}
return {
addResult: function(data, imageSize, codeResult) {
var result = {};
if (matchesConstraints(codeResult)) {
capacity--;
result.codeResult = codeResult;
if (capture) {
canvas.width = imageSize.x;
canvas.height = imageSize.y;
ImageDebug.drawImage(data, imageSize, ctx);
result.frame = canvas.toDataURL();
}
results.push(result);
}
},
getResults: function() {
return results;
}
};
}
};
});

@ -46,7 +46,8 @@ require.config({
'tracer': 'src/tracer', 'tracer': 'src/tracer',
'upc_e_reader': 'src/upc_e_reader', 'upc_e_reader': 'src/upc_e_reader',
'upc_reader': 'src/upc_reader', 'upc_reader': 'src/upc_reader',
'async': 'node_modules/async/lib/async' 'async': 'node_modules/async/lib/async',
'result_collector': 'src/result_collector'
}, },
deps: allTestFiles, deps: allTestFiles,
callback: window.__karma__.start callback: window.__karma__.start

Loading…
Cancel
Save