mirror of
https://github.com/serratus/quaggaJS.git
synced 2026-08-13 05:31:37 +08:00
Moved files into meaningful folders
This commit is contained in:
@@ -0,0 +1,299 @@
|
||||
import Bresenham from './bresenham';
|
||||
import ImageDebug from '../common/image_debug';
|
||||
import Code128Reader from '../reader/code_128_reader';
|
||||
import EANReader from '../reader/ean_reader';
|
||||
import Code39Reader from '../reader/code_39_reader';
|
||||
import Code39VINReader from '../reader/code_39_vin_reader';
|
||||
import CodabarReader from '../reader/codabar_reader';
|
||||
import UPCReader from '../reader/upc_reader';
|
||||
import EAN8Reader from '../reader/ean_8_reader';
|
||||
import UPCEReader from '../reader/upc_e_reader';
|
||||
import I2of5Reader from '../reader/i2of5_reader';
|
||||
|
||||
const READERS = {
|
||||
code_128_reader: Code128Reader,
|
||||
ean_reader: EANReader,
|
||||
ean_8_reader: EAN8Reader,
|
||||
code_39_reader: Code39Reader,
|
||||
code_39_vin_reader: Code39VINReader,
|
||||
codabar_reader: CodabarReader,
|
||||
upc_reader: UPCReader,
|
||||
upc_e_reader: UPCEReader,
|
||||
i2of5_reader: I2of5Reader
|
||||
};
|
||||
export default {
|
||||
create: function(config, inputImageWrapper) {
|
||||
var _canvas = {
|
||||
ctx: {
|
||||
frequency: null,
|
||||
pattern: null,
|
||||
overlay: null
|
||||
},
|
||||
dom: {
|
||||
frequency: null,
|
||||
pattern: null,
|
||||
overlay: null
|
||||
}
|
||||
},
|
||||
_barcodeReaders = [];
|
||||
|
||||
initCanvas();
|
||||
initReaders();
|
||||
initConfig();
|
||||
|
||||
function initCanvas() {
|
||||
if (typeof document !== 'undefined') {
|
||||
var $debug = document.querySelector("#debug.detection");
|
||||
_canvas.dom.frequency = document.querySelector("canvas.frequency");
|
||||
if (!_canvas.dom.frequency) {
|
||||
_canvas.dom.frequency = document.createElement("canvas");
|
||||
_canvas.dom.frequency.className = "frequency";
|
||||
if ($debug) {
|
||||
$debug.appendChild(_canvas.dom.frequency);
|
||||
}
|
||||
}
|
||||
_canvas.ctx.frequency = _canvas.dom.frequency.getContext("2d");
|
||||
|
||||
_canvas.dom.pattern = document.querySelector("canvas.patternBuffer");
|
||||
if (!_canvas.dom.pattern) {
|
||||
_canvas.dom.pattern = document.createElement("canvas");
|
||||
_canvas.dom.pattern.className = "patternBuffer";
|
||||
if ($debug) {
|
||||
$debug.appendChild(_canvas.dom.pattern);
|
||||
}
|
||||
}
|
||||
_canvas.ctx.pattern = _canvas.dom.pattern.getContext("2d");
|
||||
|
||||
_canvas.dom.overlay = document.querySelector("canvas.drawingBuffer");
|
||||
if (_canvas.dom.overlay) {
|
||||
_canvas.ctx.overlay = _canvas.dom.overlay.getContext("2d");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function initReaders() {
|
||||
config.readers.forEach(function(readerConfig) {
|
||||
var reader,
|
||||
configuration = {};
|
||||
|
||||
if (typeof readerConfig === 'object') {
|
||||
reader = readerConfig.format;
|
||||
configuration = readerConfig.config;
|
||||
} else if (typeof readerConfig === 'string') {
|
||||
reader = readerConfig;
|
||||
}
|
||||
console.log("Before registering reader: ", reader);
|
||||
_barcodeReaders.push(new READERS[reader](configuration));
|
||||
});
|
||||
console.log("Registered Readers: " + _barcodeReaders
|
||||
.map((reader) => JSON.stringify({format: reader.FORMAT, config: reader.config}))
|
||||
.join(', '));
|
||||
}
|
||||
|
||||
function initConfig() {
|
||||
if (typeof document !== 'undefined') {
|
||||
var i,
|
||||
vis = [{
|
||||
node: _canvas.dom.frequency,
|
||||
prop: config.showFrequency
|
||||
}, {
|
||||
node: _canvas.dom.pattern,
|
||||
prop: config.showPattern
|
||||
}];
|
||||
|
||||
for (i = 0; i < vis.length; i++) {
|
||||
if (vis[i].prop === true) {
|
||||
vis[i].node.style.display = "block";
|
||||
} else {
|
||||
vis[i].node.style.display = "none";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* extend the line on both ends
|
||||
* @param {Array} line
|
||||
* @param {Number} angle
|
||||
*/
|
||||
function getExtendedLine(line, angle, ext) {
|
||||
function extendLine(amount) {
|
||||
var extension = {
|
||||
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
|
||||
extendLine(ext);
|
||||
while (ext > 1 && (!inputImageWrapper.inImageWithBorder(line[0], 0)
|
||||
|| !inputImageWrapper.inImageWithBorder(line[1], 0))) {
|
||||
ext -= Math.ceil(ext / 2);
|
||||
extendLine(-ext);
|
||||
}
|
||||
return line;
|
||||
}
|
||||
|
||||
function getLine(box) {
|
||||
return [{
|
||||
x: (box[1][0] - box[0][0]) / 2 + box[0][0],
|
||||
y: (box[1][1] - box[0][1]) / 2 + box[0][1]
|
||||
}, {
|
||||
x: (box[3][0] - box[2][0]) / 2 + box[2][0],
|
||||
y: (box[3][1] - box[2][1]) / 2 + box[2][1]
|
||||
}];
|
||||
}
|
||||
|
||||
function tryDecode(line) {
|
||||
var result = null,
|
||||
i,
|
||||
barcodeLine = Bresenham.getBarcodeLine(inputImageWrapper, line[0], line[1]);
|
||||
|
||||
if (config.showFrequency) {
|
||||
ImageDebug.drawPath(line, {x: 'x', y: 'y'}, _canvas.ctx.overlay, {color: 'red', lineWidth: 3});
|
||||
Bresenham.debug.printFrequency(barcodeLine.line, _canvas.dom.frequency);
|
||||
}
|
||||
Bresenham.toBinaryLine(barcodeLine);
|
||||
if (config.showPattern) {
|
||||
Bresenham.debug.printPattern(barcodeLine.line, _canvas.dom.pattern);
|
||||
}
|
||||
|
||||
for ( i = 0; i < _barcodeReaders.length && result === null; i++) {
|
||||
result = _barcodeReaders[i].decodePattern(barcodeLine.line);
|
||||
}
|
||||
if (result === null){
|
||||
return null;
|
||||
}
|
||||
return {
|
||||
codeResult: result,
|
||||
barcodeLine: barcodeLine
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* This method slices the given area apart and tries to detect a barcode-pattern
|
||||
* for each slice. It returns the decoded barcode, or null if nothing was found
|
||||
* @param {Array} box
|
||||
* @param {Array} line
|
||||
* @param {Number} lineAngle
|
||||
*/
|
||||
function tryDecodeBruteForce(box, line, lineAngle) {
|
||||
var sideLength = Math.sqrt(Math.pow(box[1][0] - box[0][0], 2) + Math.pow((box[1][1] - box[0][1]), 2)),
|
||||
i,
|
||||
slices = 16,
|
||||
result = null,
|
||||
dir,
|
||||
extension,
|
||||
xdir = Math.sin(lineAngle),
|
||||
ydir = Math.cos(lineAngle);
|
||||
|
||||
for ( i = 1; i < slices && result === null; i++) {
|
||||
// move line perpendicular to angle
|
||||
dir = sideLength / slices * i * (i % 2 === 0 ? -1 : 1);
|
||||
extension = {
|
||||
y: dir * xdir,
|
||||
x: dir * ydir
|
||||
};
|
||||
line[0].y += extension.x;
|
||||
line[0].x -= extension.y;
|
||||
line[1].y += extension.x;
|
||||
line[1].x -= extension.y;
|
||||
|
||||
result = tryDecode(line);
|
||||
}
|
||||
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.
|
||||
* @param {Object} box The area to search in
|
||||
* @returns {Object} the result {codeResult, line, angle, pattern, threshold}
|
||||
*/
|
||||
function decodeFromBoundingBox(box) {
|
||||
var line,
|
||||
lineAngle,
|
||||
ctx = _canvas.ctx.overlay,
|
||||
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, Math.floor(lineLength * 0.1));
|
||||
if (line === null){
|
||||
return null;
|
||||
}
|
||||
|
||||
result = tryDecode(line);
|
||||
if (result === null) {
|
||||
result = tryDecodeBruteForce(box, line, lineAngle);
|
||||
}
|
||||
|
||||
if (result === null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (result && config.drawScanline && ctx) {
|
||||
ImageDebug.drawPath(line, {x: 'x', y: 'y'}, ctx, {color: 'red', lineWidth: 3});
|
||||
}
|
||||
|
||||
return {
|
||||
codeResult: result.codeResult,
|
||||
line: line,
|
||||
angle: lineAngle,
|
||||
pattern: result.barcodeLine.line,
|
||||
threshold: result.barcodeLine.threshold
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
decodeFromBoundingBox: function(box) {
|
||||
return decodeFromBoundingBox(box);
|
||||
},
|
||||
decodeFromBoundingBoxes: function(boxes) {
|
||||
var i, result,
|
||||
barcodes = [],
|
||||
multiple = config.multiple;
|
||||
|
||||
for ( i = 0; i < boxes.length; i++) {
|
||||
const box = boxes[i];
|
||||
result = decodeFromBoundingBox(box) || {};
|
||||
result.box = box;
|
||||
|
||||
if (multiple) {
|
||||
barcodes.push(result);
|
||||
} else if (result.codeResult) {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
if (multiple) {
|
||||
return {
|
||||
barcodes
|
||||
};
|
||||
}
|
||||
},
|
||||
setReaders: function(readers) {
|
||||
config.readers = readers;
|
||||
_barcodeReaders.length = 0;
|
||||
initReaders();
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,213 @@
|
||||
import CVUtils from '../common/cv_utils';
|
||||
import ImageWrapper from '../common/image_wrapper';
|
||||
|
||||
var Bresenham = {};
|
||||
|
||||
var Slope = {
|
||||
DIR: {
|
||||
UP: 1,
|
||||
DOWN: -1
|
||||
}
|
||||
};
|
||||
/**
|
||||
* Scans a line of the given image from point p1 to p2 and returns a result object containing
|
||||
* gray-scale values (0-255) of the underlying pixels in addition to the min
|
||||
* and max values.
|
||||
* @param {Object} imageWrapper
|
||||
* @param {Object} p1 The start point {x,y}
|
||||
* @param {Object} p2 The end point {x,y}
|
||||
* @returns {line, min, max}
|
||||
*/
|
||||
Bresenham.getBarcodeLine = function(imageWrapper, p1, p2) {
|
||||
var x0 = p1.x | 0,
|
||||
y0 = p1.y | 0,
|
||||
x1 = p2.x | 0,
|
||||
y1 = p2.y | 0,
|
||||
steep = Math.abs(y1 - y0) > Math.abs(x1 - x0),
|
||||
deltax,
|
||||
deltay,
|
||||
error,
|
||||
ystep,
|
||||
y,
|
||||
tmp,
|
||||
x,
|
||||
line = [],
|
||||
imageData = imageWrapper.data,
|
||||
width = imageWrapper.size.x,
|
||||
sum = 0,
|
||||
val,
|
||||
min = 255,
|
||||
max = 0;
|
||||
|
||||
function read(a, b) {
|
||||
val = imageData[b * width + a];
|
||||
sum += val;
|
||||
min = val < min ? val : min;
|
||||
max = val > max ? val : max;
|
||||
line.push(val);
|
||||
}
|
||||
|
||||
if (steep) {
|
||||
tmp = x0;
|
||||
x0 = y0;
|
||||
y0 = tmp;
|
||||
|
||||
tmp = x1;
|
||||
x1 = y1;
|
||||
y1 = tmp;
|
||||
}
|
||||
if (x0 > x1) {
|
||||
tmp = x0;
|
||||
x0 = x1;
|
||||
x1 = tmp;
|
||||
|
||||
tmp = y0;
|
||||
y0 = y1;
|
||||
y1 = tmp;
|
||||
}
|
||||
deltax = x1 - x0;
|
||||
deltay = Math.abs(y1 - y0);
|
||||
error = (deltax / 2) | 0;
|
||||
y = y0;
|
||||
ystep = y0 < y1 ? 1 : -1;
|
||||
for ( x = x0; x < x1; x++) {
|
||||
if (steep){
|
||||
read(y, x);
|
||||
} else {
|
||||
read(x, y);
|
||||
}
|
||||
error = error - deltay;
|
||||
if (error < 0) {
|
||||
y = y + ystep;
|
||||
error = error + deltax;
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
line: line,
|
||||
min: min,
|
||||
max: max
|
||||
};
|
||||
};
|
||||
|
||||
Bresenham.toOtsuBinaryLine = function(result) {
|
||||
var line = result.line,
|
||||
image = new ImageWrapper({x: line.length - 1, y: 1}, line),
|
||||
threshold = CVUtils.determineOtsuThreshold(image, 5);
|
||||
|
||||
line = CVUtils.sharpenLine(line);
|
||||
CVUtils.thresholdImage(image, threshold);
|
||||
|
||||
return {
|
||||
line: line,
|
||||
threshold: threshold
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Converts the result from getBarcodeLine into a binary representation
|
||||
* also considering the frequency and slope of the signal for more robust results
|
||||
* @param {Object} result {line, min, max}
|
||||
*/
|
||||
Bresenham.toBinaryLine = function(result) {
|
||||
var min = result.min,
|
||||
max = result.max,
|
||||
line = result.line,
|
||||
slope,
|
||||
slope2,
|
||||
center = min + (max - min) / 2,
|
||||
extrema = [],
|
||||
currentDir,
|
||||
dir,
|
||||
threshold = (max - min) / 12,
|
||||
rThreshold = -threshold,
|
||||
i,
|
||||
j;
|
||||
|
||||
// 1. find extrema
|
||||
currentDir = line[0] > center ? Slope.DIR.UP : Slope.DIR.DOWN;
|
||||
extrema.push({
|
||||
pos: 0,
|
||||
val: line[0]
|
||||
});
|
||||
for ( i = 0; i < line.length - 2; i++) {
|
||||
slope = (line[i + 1] - line[i]);
|
||||
slope2 = (line[i + 2] - line[i + 1]);
|
||||
if ((slope + slope2) < rThreshold && line[i + 1] < (center * 1.5)) {
|
||||
dir = Slope.DIR.DOWN;
|
||||
} else if ((slope + slope2) > threshold && line[i + 1] > (center * 0.5)) {
|
||||
dir = Slope.DIR.UP;
|
||||
} else {
|
||||
dir = currentDir;
|
||||
}
|
||||
|
||||
if (currentDir !== dir) {
|
||||
extrema.push({
|
||||
pos: i,
|
||||
val: line[i]
|
||||
});
|
||||
currentDir = dir;
|
||||
}
|
||||
}
|
||||
extrema.push({
|
||||
pos: line.length,
|
||||
val: line[line.length - 1]
|
||||
});
|
||||
|
||||
for ( j = extrema[0].pos; j < extrema[1].pos; j++) {
|
||||
line[j] = line[j] > center ? 0 : 1;
|
||||
}
|
||||
|
||||
// iterate over extrema and convert to binary based on avg between minmax
|
||||
for ( i = 1; i < extrema.length - 1; i++) {
|
||||
if (extrema[i + 1].val > extrema[i].val) {
|
||||
threshold = (extrema[i].val + ((extrema[i + 1].val - extrema[i].val) / 3) * 2) | 0;
|
||||
} else {
|
||||
threshold = (extrema[i + 1].val + ((extrema[i].val - extrema[i + 1].val) / 3)) | 0;
|
||||
}
|
||||
|
||||
for ( j = extrema[i].pos; j < extrema[i + 1].pos; j++) {
|
||||
line[j] = line[j] > threshold ? 0 : 1;
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
line: line,
|
||||
threshold: threshold
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Used for development only
|
||||
*/
|
||||
Bresenham.debug = {
|
||||
printFrequency: function(line, canvas) {
|
||||
var i,
|
||||
ctx = canvas.getContext("2d");
|
||||
canvas.width = line.length;
|
||||
canvas.height = 256;
|
||||
|
||||
ctx.beginPath();
|
||||
ctx.strokeStyle = "blue";
|
||||
for ( i = 0; i < line.length; i++) {
|
||||
ctx.moveTo(i, 255);
|
||||
ctx.lineTo(i, 255 - line[i]);
|
||||
}
|
||||
ctx.stroke();
|
||||
ctx.closePath();
|
||||
},
|
||||
|
||||
printPattern: function(line, canvas) {
|
||||
var ctx = canvas.getContext("2d"), i;
|
||||
|
||||
canvas.width = line.length;
|
||||
ctx.fillColor = "black";
|
||||
for ( i = 0; i < line.length; i++) {
|
||||
if (line[i] === 1) {
|
||||
ctx.fillRect(i, 0, 1, 100);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export default Bresenham;
|
||||
Reference in New Issue
Block a user