Preparing pdf417 decoding

feature/#70
Christoph Oberhofer 9 years ago
parent 7ec6eb28d3
commit a8b2d3ed9a

@ -19,14 +19,12 @@
/* line 1, ../sass/_viewport.scss */
#interactive.viewport {
width: 640px;
height: 480px;
}
/* line 6, ../sass/_viewport.scss */
#interactive.viewport canvas, video {
float: left;
width: 640px;
height: 480px;
}
/* line 10, ../sass/_viewport.scss */
#interactive.viewport canvas.drawingBuffer, video.drawingBuffer {

@ -88,16 +88,17 @@ $(function() {
},
state: {
inputStream: {
size: 640,
size: 800,
singleChannel: false
},
locator: {
patchSize: "large",
halfSample: false
halfSample: true
},
decoder: {
readers: ["code_128_reader"]
readers: ["pdf_417_reader"]
},
debug: true,
locate: true,
src: null
}
@ -131,7 +132,7 @@ $(function() {
drawingCanvas = Quagga.canvas.dom.overlay,
area;
if (result) {
if (false) {
if (result.boxes) {
drawingCtx.clearRect(0, 0, parseInt(drawingCanvas.getAttribute("width")), parseInt(drawingCanvas.getAttribute("height")));
result.boxes.filter(function (box) {

@ -0,0 +1,43 @@
export function getCenterLineFromBox(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]
}];
}
export 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));
}
export function getLineAngle(line) {
return Math.atan2(line[1].y - line[0].y, line[1].x - line[0].x);
}
function extendLine(line, angle, ext) {
const extension = {
y: ext * Math.sin(angle),
x: ext * Math.cos(angle)
};
line[0].y -= extension.y;
line[0].x -= extension.x;
line[1].y += extension.y;
line[1].x += extension.x;
return line;
}
export function getExtendedLine(inputImageWrapper, line, angle, ext) {
line = extendLine(line, angle, ext);
while (ext > 1 && (!inputImageWrapper.inImageWithBorder(line[0], 0)
|| !inputImageWrapper.inImageWithBorder(line[1], 0))) {
ext -= Math.ceil(ext / 2);
line = extendLine(line, angle, -ext);
}
return line;
}

@ -15,9 +15,24 @@ export default {
for (var j = 1; j < path.length; j++) {
ctx.lineTo(path[j][def.x], path[j][def.y]);
}
ctx.closePath();
ctx.stroke();
},
drawVertex: function(vertex, def, ctx) {
ctx.beginPath();
ctx.moveTo(vertex[def.x]-2, vertex[def.y]-2);
ctx.lineTo(vertex[def.x]+2, vertex[def.y]+2);
ctx.moveTo(vertex[def.x]-2, vertex[def.y]+2);
ctx.lineTo(vertex[def.x]+2, vertex[def.y]-2);
ctx.stroke();
},
drawVertices: function(vertices, def, ctx, style) {
ctx.strokeStyle = style.color;
ctx.fillStyle = style.color;
ctx.lineWidth = style.lineWidth;
vertices.forEach((vertex) => {
this.drawVertex(vertex, def, ctx);
});
},
drawImage: function(imageData, size, ctx) {
var canvasData = ctx.getImageData(0, 0, size.x, size.y),
data = canvasData.data,

@ -9,6 +9,13 @@ 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';
import Pdf417Reader from '../reader/pdf_417_reader';
import {
getCenterLineFromBox as getLine,
getLineLength,
getLineAngle,
getExtendedLine
} from '../common/geometric';
const READERS = {
code_128_reader: Code128Reader,
@ -19,7 +26,8 @@ const READERS = {
codabar_reader: CodabarReader,
upc_reader: UPCReader,
upc_e_reader: UPCEReader,
i2of5_reader: I2of5Reader
i2of5_reader: I2of5Reader,
pdf_417_reader: Pdf417Reader
};
export default {
create: function(config, inputImageWrapper) {
@ -115,44 +123,6 @@ export default {
}
}
/**
* 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,
@ -215,12 +185,6 @@ export default {
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.
@ -240,10 +204,17 @@ export default {
}
}
if (_barcodeReaders.some((reader) => (reader.FORMAT === 'pdf147'))) {
const reader = _barcodeReaders
.filter((reader) => (reader.FORMAT === 'pdf147'))[0];
reader.decode(inputImageWrapper, box, ctx);
return null;
}
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));
lineAngle = getLineAngle(line);
line = getExtendedLine(inputImageWrapper, line, lineAngle, Math.floor(lineLength * 0.1));
if (line === null){
return null;
}
@ -279,6 +250,7 @@ export default {
barcodes = [],
multiple = config.multiple;
console.log(boxes);
for ( i = 0; i < boxes.length; i++) {
const box = boxes[i];
result = decodeFromBoundingBox(box) || {};

@ -0,0 +1,48 @@
import {
getCenterLineFromBox as getLine,
getLineLength,
getLineAngle,
getExtendedLine
} from '../common/geometric';
import ImageDebug from '../common/image_debug';
import Bresenham from '../decoder/bresenham';
import {findPattern} from '../reader/common';
const vec2 = {
clone: require('gl-vec2/clone'),
dot: require('gl-vec2/dot')
}
const START_PATTERN = [8, 1, 1, 1, 1, 1, 1, 3],
STOP_PATTERN = [7, 1, 1, 3, 1, 1, 1, 2, 1],
MODULO = START_PATTERN.reduce((sum, i) => (sum + i),0);
export default function detect(inputImageWrapper, box, ctx) {
let line = getLine(box),
lineLength = getLineLength(line),
lineAngle = getLineAngle(line),
extendedLine = getExtendedLine(inputImageWrapper, line, lineAngle, Math.floor(lineLength * 0.15)),
barcodeLine = Bresenham.getBarcodeLine(inputImageWrapper, extendedLine[0], extendedLine[1]);
if (ENV.development) {
if (ctx) {
ImageDebug.drawPath(extendedLine, {x: 'x', y: 'y'}, ctx, {color: 'red', lineWidth: 1});
}
}
Bresenham.toBinaryLine(barcodeLine);
const match = findPattern(barcodeLine.line, START_PATTERN, {modulo: MODULO});
console.log(match);
return [
vec2.clone([199, 84]),
vec2.clone([210, 303]),
vec2.clone([634, 88]),
vec2.clone([660, 271]),
vec2.clone([295, 83]),
vec2.clone([310, 295]),
vec2.clone([554, 83]),
vec2.clone([580, 277])
]
};

@ -0,0 +1,126 @@
export function nextSet(line, offset) {
var i;
offset = offset || 0;
for (i = offset; i < line.length; i++) {
if (line[i]) {
return i;
}
}
return line.length;
};
export function normalize(counter, modulo) {
var i,
sum = 0,
ratio,
numOnes = 0,
normalized = [],
norm = 0;
for (i = 0; i < counter.length; i++) {
if (counter[i] === 1) {
numOnes++;
} else {
sum += counter[i];
}
}
ratio = sum / (modulo - numOnes);
if (ratio > 1.0) {
for (i = 0; i < counter.length; i++) {
norm = counter[i] === 1 ? counter[i] : counter[i] / ratio;
normalized.push(norm);
}
} else {
ratio = (sum + numOnes) / modulo;
for (i = 0; i < counter.length; i++) {
norm = counter[i] / ratio;
normalized.push(norm);
}
}
return normalized;
}
export function matchPattern(counter, code, modulo, maxSingleError=1) {
var i,
error = 0,
singleError = 0;
for (i = 0; i < counter.length; i++) {
singleError = Math.abs(code[i] - counter[i]);
if (singleError > maxSingleError) {
return Number.MAX_VALUE;
}
error += singleError;
}
return error / modulo;
}
export function findPattern(row, pattern, {
offset,
isWhite=false,
tryHarder=true,
epsilon=0.5,
maxSingleError=0.5,
modulo}) {
var counter = [],
i,
counterPos = 0,
bestMatch = {
error: Number.MAX_VALUE,
code: -1,
start: 0,
end: 0
},
error,
j,
sum,
normalized;
if (!offset) {
offset = nextSet(row);
}
for ( i = 0; i < pattern.length; i++) {
counter[i] = 0;
}
for ( i = offset; i < row.length; i++) {
if (row[i] ^ isWhite) {
counter[counterPos]++;
} else {
if (counterPos === counter.length - 1) {
sum = 0;
for ( j = 0; j < counter.length; j++) {
sum += counter[j];
}
normalized = normalize(counter, modulo);
if (normalized) {
error = matchPattern(normalized, pattern, modulo, maxSingleError);
if (error < epsilon) {
bestMatch.error = error;
bestMatch.start = i - sum;
bestMatch.end = i;
return bestMatch;
}
}
if (tryHarder) {
for ( j = 0; j < counter.length - 2; j++) {
counter[j] = counter[j + 2];
}
counter[counter.length - 2] = 0;
counter[counter.length - 1] = 0;
counterPos--;
} else {
return null;
}
} else {
counterPos++;
}
counter[counterPos] = 1;
isWhite = !isWhite;
}
}
return null;
}

@ -0,0 +1,30 @@
import BarcodeReader from './barcode_reader';
import detect from '../detector/pdf_147_detector';
import ImageDebug from '../common/image_debug';
function Pdf147Reader() {
BarcodeReader.call(this);
}
const properties = {
SINGLE_CODE_ERROR: {value: 1},
AVG_CODE_ERROR: {value: 0.5},
FORMAT: {value: "pdf147", writeable: false}
};
Pdf147Reader.prototype = Object.create(BarcodeReader.prototype, properties);
Pdf147Reader.prototype.constructor = Pdf147Reader;
Pdf147Reader.prototype.decode = function(inputImageWrapper, box, ctx) {
console.log("Pdf147Reader...");
const detectionInfo = detect(inputImageWrapper, box, ctx);
console.log(detectionInfo);
if (ENV.development) {
if (ctx) {
ImageDebug.drawVertices(detectionInfo, {x: 0, y: 1}, ctx, {color: "red", lineWidth: 1});
}
}
console.log("Pdf147Reader... END")
};
export default Pdf147Reader;
Loading…
Cancel
Save