Skip to content

Commit 06bcda3

Browse files
committed
194: Added basic support for 2of5 barcodes; fixed integration-tests
1 parent f9a19e0 commit 06bcda3

25 files changed

+328
-21
lines changed

example/file_input.html

+1
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ <h3>Working with file-input</h3>
5252
<option value="upc_e">UPC-E</option>
5353
<option value="codabar">Codabar</option>
5454
<option value="i2of5">ITF</option>
55+
<option value="2of5">2of5</option>
5556
</select>
5657
</label>
5758
<label>

example/live_w_locator.html

+2-1
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,8 @@ <h3>The user's camera</h3>
4343
<option value="upc">UPC</option>
4444
<option value="upc_e">UPC-E</option>
4545
<option value="codabar">Codabar</option>
46-
<option value="i2of5">ITF</option>
46+
<option value="i2of5">Interleaved 2 of 5</option>
47+
<option value="2of5">Standard 2 of 5</option>
4748
</select>
4849
</label>
4950
<label>

example/live_w_locator.js

+15-2
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,19 @@ $(function() {
22
var resultCollector = Quagga.ResultCollector.create({
33
capture: true,
44
capacity: 20,
5-
blacklist: [{code: "2167361334", format: "i2of5"}],
5+
blacklist: [{
6+
code: "9577149002", format: "2of5"
7+
}, {
8+
code: "5776158811", format: "2of5"
9+
}, {
10+
code: "0463381455", format: "2of5"
11+
}, {
12+
code: "3261594101", format: "2of5"
13+
}, {
14+
code: "6730705801", format: "2of5"
15+
}, {
16+
code: "8568166929", format: "2of5"
17+
}],
618
filter: function(codeResult) {
719
// only store results which match this constraint
820
// e.g.: codeResult
@@ -169,7 +181,8 @@ $(function() {
169181
patchSize: "medium",
170182
halfSample: true
171183
},
172-
numOfWorkers: 4,
184+
numOfWorkers: 2,
185+
frequency: 10,
173186
decoder: {
174187
readers : [{
175188
format: "code_128_reader",

example/static_images.html

+2
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,8 @@ <h3>Working with static images</h3>
4545
<option value="upc_e">UPC-E</option>
4646
<option value="codabar">Codabar</option>
4747
<option value="i2of5">I2of5</option>
48+
<option value="i2of5">Interleaved 2 of 5</option>
49+
<option value="2of5">Standard 2 of 5</option>
4850
</select>
4951
</fieldset>
5052
</div>

karma-integration.conf.js

+4-1
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,9 @@ module.exports = function(config) {
1414
'test/test-main-integration.js': ['webpack']
1515
},
1616
webpack: {
17+
entry: [
18+
'./src/quagga.js'
19+
],
1720
module: {
1821
loaders: [{
1922
test: /\.jsx?$/,
@@ -45,7 +48,7 @@ module.exports = function(config) {
4548
reporters: ['progress'],
4649
port: 9876,
4750
colors: true,
48-
logLevel: config.LOG_INFO,
51+
logLevel: config.LOG_INFO, // LOG_DEBUG
4952
autoWatch: true,
5053
browsers: ['Chrome'],
5154
singleRun: false

karma.conf.js

+3
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,9 @@ module.exports = function(config) {
1313
'test/test-main.js': ['webpack']
1414
},
1515
webpack: {
16+
entry: [
17+
'./src/quagga.js'
18+
],
1619
module: {
1720
loaders: [{
1821
test: /\.jsx?$/,

src/decoder/barcode_decoder.js

+3-1
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import EAN2Reader from '../reader/ean_2_reader';
1111
import EAN5Reader from '../reader/ean_5_reader';
1212
import UPCEReader from '../reader/upc_e_reader';
1313
import I2of5Reader from '../reader/i2of5_reader';
14+
import TwoOfFiveReader from '../reader/2of5_reader';
1415

1516
const READERS = {
1617
code_128_reader: Code128Reader,
@@ -23,7 +24,8 @@ const READERS = {
2324
codabar_reader: CodabarReader,
2425
upc_reader: UPCReader,
2526
upc_e_reader: UPCEReader,
26-
i2of5_reader: I2of5Reader
27+
i2of5_reader: I2of5Reader,
28+
'2of5_reader': TwoOfFiveReader,
2729
};
2830
export default {
2931
create: function(config, inputImageWrapper) {

src/input/camera_access.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ function waitForVideo(video) {
1414

1515
function checkVideo() {
1616
if (attempts > 0) {
17-
if (video.videoWidth > 0 && video.videoHeight > 0) {
17+
if (video.videoWidth > 10 && video.videoHeight > 10) {
1818
if (ENV.development) {
1919
console.log(video.videoWidth + "px x " + video.videoHeight + "px");
2020
}

src/reader/2of5_reader.js

+260
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,260 @@
1+
import BarcodeReader from './barcode_reader';
2+
3+
function TwoOfFiveReader(opts) {
4+
BarcodeReader.call(this, opts);
5+
this.barSpaceRatio = [1, 1];
6+
}
7+
8+
var N = 1,
9+
W = 3,
10+
properties = {
11+
START_PATTERN: {value: [W, N, W, N, N, N]},
12+
STOP_PATTERN: {value: [W, N, N, N, W]},
13+
CODE_PATTERN: {value: [
14+
[N, N, W, W, N],
15+
[W, N, N, N, W],
16+
[N, W, N, N, W],
17+
[W, W, N, N, N],
18+
[N, N, W, N, W],
19+
[W, N, W, N, N],
20+
[N, W, W, N, N],
21+
[N, N, N, W, W],
22+
[W, N, N, W, N],
23+
[N, W, N, W, N]
24+
]},
25+
SINGLE_CODE_ERROR: {value: 0.78, writable: true},
26+
AVG_CODE_ERROR: {value: 0.30, writable: true},
27+
MAX_CORRECTION_FACTOR: {value: 5},
28+
FORMAT: {value: "2of5"}
29+
};
30+
31+
const startPatternLength = properties.START_PATTERN.value.reduce((sum, val) => sum + val, 0);
32+
33+
TwoOfFiveReader.prototype = Object.create(BarcodeReader.prototype, properties);
34+
TwoOfFiveReader.prototype.constructor = TwoOfFiveReader;
35+
36+
TwoOfFiveReader.prototype._findPattern = function(pattern, offset, isWhite, tryHarder) {
37+
var counter = [],
38+
self = this,
39+
i,
40+
counterPos = 0,
41+
bestMatch = {
42+
error: Number.MAX_VALUE,
43+
code: -1,
44+
start: 0,
45+
end: 0
46+
},
47+
error,
48+
j,
49+
sum,
50+
normalized,
51+
epsilon = self.AVG_CODE_ERROR;
52+
53+
isWhite = isWhite || false;
54+
tryHarder = tryHarder || false;
55+
56+
if (!offset) {
57+
offset = self._nextSet(self._row);
58+
}
59+
60+
for ( i = 0; i < pattern.length; i++) {
61+
counter[i] = 0;
62+
}
63+
64+
for ( i = offset; i < self._row.length; i++) {
65+
if (self._row[i] ^ isWhite) {
66+
counter[counterPos]++;
67+
} else {
68+
if (counterPos === counter.length - 1) {
69+
sum = 0;
70+
for ( j = 0; j < counter.length; j++) {
71+
sum += counter[j];
72+
}
73+
error = self._matchPattern(counter, pattern);
74+
if (error < epsilon) {
75+
bestMatch.error = error;
76+
bestMatch.start = i - sum;
77+
bestMatch.end = i;
78+
return bestMatch;
79+
}
80+
if (tryHarder) {
81+
for (j = 0; j < counter.length - 2; j++) {
82+
counter[j] = counter[j + 2];
83+
}
84+
counter[counter.length - 2] = 0;
85+
counter[counter.length - 1] = 0;
86+
counterPos--;
87+
} else {
88+
return null;
89+
}
90+
} else {
91+
counterPos++;
92+
}
93+
counter[counterPos] = 1;
94+
isWhite = !isWhite;
95+
}
96+
}
97+
return null;
98+
};
99+
100+
TwoOfFiveReader.prototype._findStart = function() {
101+
var self = this,
102+
leadingWhitespaceStart,
103+
offset = self._nextSet(self._row),
104+
startInfo,
105+
narrowBarWidth = 1;
106+
107+
while (!startInfo) {
108+
startInfo = self._findPattern(self.START_PATTERN, offset, false, true);
109+
if (!startInfo) {
110+
return null;
111+
}
112+
narrowBarWidth = Math.floor((startInfo.end - startInfo.start) / startPatternLength);
113+
leadingWhitespaceStart = startInfo.start - narrowBarWidth * 5;
114+
if (leadingWhitespaceStart >= 0) {
115+
if (self._matchRange(leadingWhitespaceStart, startInfo.start, 0)) {
116+
return startInfo;
117+
}
118+
}
119+
offset = startInfo.end;
120+
startInfo = null;
121+
}
122+
};
123+
124+
TwoOfFiveReader.prototype._verifyTrailingWhitespace = function(endInfo) {
125+
var self = this,
126+
trailingWhitespaceEnd;
127+
128+
trailingWhitespaceEnd = endInfo.end + ((endInfo.end - endInfo.start) / 2);
129+
if (trailingWhitespaceEnd < self._row.length) {
130+
if (self._matchRange(endInfo.end, trailingWhitespaceEnd, 0)) {
131+
return endInfo;
132+
}
133+
}
134+
return null;
135+
};
136+
137+
TwoOfFiveReader.prototype._findEnd = function() {
138+
var self = this,
139+
endInfo,
140+
tmp,
141+
offset;
142+
143+
self._row.reverse();
144+
offset = self._nextSet(self._row);
145+
endInfo = self._findPattern(self.STOP_PATTERN, offset, false, true);
146+
self._row.reverse();
147+
148+
if (endInfo === null) {
149+
return null;
150+
}
151+
152+
// reverse numbers
153+
tmp = endInfo.start;
154+
endInfo.start = self._row.length - endInfo.end;
155+
endInfo.end = self._row.length - tmp;
156+
157+
return endInfo !== null ? self._verifyTrailingWhitespace(endInfo) : null;
158+
};
159+
160+
TwoOfFiveReader.prototype._decodeCode = function(counter) {
161+
var j,
162+
self = this,
163+
sum = 0,
164+
normalized,
165+
error,
166+
epsilon = self.AVG_CODE_ERROR,
167+
code,
168+
bestMatch = {
169+
error: Number.MAX_VALUE,
170+
code: -1,
171+
start: 0,
172+
end: 0
173+
};
174+
175+
for ( j = 0; j < counter.length; j++) {
176+
sum += counter[j];
177+
}
178+
for (code = 0; code < self.CODE_PATTERN.length; code++) {
179+
error = self._matchPattern(counter, self.CODE_PATTERN[code]);
180+
if (error < bestMatch.error) {
181+
bestMatch.code = code;
182+
bestMatch.error = error;
183+
}
184+
}
185+
if (bestMatch.error < epsilon) {
186+
return bestMatch;
187+
}
188+
};
189+
190+
TwoOfFiveReader.prototype._decodePayload = function(counters, result, decodedCodes) {
191+
var i,
192+
self = this,
193+
pos = 0,
194+
counterLength = counters.length,
195+
counter = [0, 0, 0, 0, 0],
196+
code;
197+
198+
while (pos < counterLength) {
199+
for (i = 0; i < 5; i++) {
200+
counter[i] = counters[pos] * this.barSpaceRatio[0];
201+
pos += 2;
202+
}
203+
code = self._decodeCode(counter);
204+
if (!code) {
205+
return null;
206+
}
207+
result.push(code.code + "");
208+
decodedCodes.push(code);
209+
}
210+
return code;
211+
};
212+
213+
TwoOfFiveReader.prototype._verifyCounterLength = function(counters) {
214+
return (counters.length % 10 === 0);
215+
};
216+
217+
TwoOfFiveReader.prototype._decode = function() {
218+
var startInfo,
219+
endInfo,
220+
self = this,
221+
code,
222+
result = [],
223+
decodedCodes = [],
224+
counters;
225+
226+
startInfo = self._findStart();
227+
if (!startInfo) {
228+
return null;
229+
}
230+
decodedCodes.push(startInfo);
231+
232+
endInfo = self._findEnd();
233+
if (!endInfo) {
234+
return null;
235+
}
236+
237+
counters = self._fillCounters(startInfo.end, endInfo.start, false);
238+
if (!self._verifyCounterLength(counters)) {
239+
return null;
240+
}
241+
code = self._decodePayload(counters, result, decodedCodes);
242+
if (!code) {
243+
return null;
244+
}
245+
if (result.length % 2 !== 0 ||
246+
result.length < 6) {
247+
return null;
248+
}
249+
250+
decodedCodes.push(endInfo);
251+
return {
252+
code: result.join(""),
253+
start: startInfo.start,
254+
end: endInfo.end,
255+
startInfo: startInfo,
256+
decodedCodes: decodedCodes
257+
};
258+
};
259+
260+
export default TwoOfFiveReader;

test/fixtures/2of5/image-001.jpg

174 KB
Loading

test/fixtures/2of5/image-002.jpg

178 KB
Loading

test/fixtures/2of5/image-003.jpg

171 KB
Loading

test/fixtures/2of5/image-004.jpg

162 KB
Loading

test/fixtures/2of5/image-005.jpg

164 KB
Loading

test/fixtures/2of5/image-006.jpg

145 KB
Loading

test/fixtures/2of5/image-007.jpg

122 KB
Loading

test/fixtures/2of5/image-008.jpg

168 KB
Loading

test/fixtures/2of5/image-009.jpg

156 KB
Loading

test/fixtures/2of5/image-010.jpg

183 KB
Loading

test/fixtures/2of5/image-012.jpg

173 KB
Loading

test/fixtures/2of5/image-015.jpg

169 KB
Loading

test/fixtures/2of5/image-016.jpg

147 KB
Loading

test/fixtures/2of5/image-017.jpg

187 KB
Loading

0 commit comments

Comments
 (0)