-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathformat_2of5.py
126 lines (107 loc) · 3.73 KB
/
format_2of5.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
#!/usr/bin/env python
#
# debarkoder - a simple barcode recognition utility
# http://github.com/lkraider/debarkoder
#
# Copyright 2010, Paul Eipper
# Licensed under BSD style license
# There are no warranties, expressed or implied. Use at your own risk.
# Share, understand, and enjoy.
"""
I25 barcode format recognizer.
call the process method with run-length encoded barcode data to decode it.
"""
# I25 barcode (interleaved 2 of 5)
# digit encoding format:
# five bars for each digit
code_length = 5
# there's always two 1's and three 0's for each code
# in the barcode, a narrow bar means 0 and a wide bar means 1
wide_count = 2
narrow_count = 3
# the wide bars are three times larger than the narrow bars
widebar_mult = 3
# there is a header made of 4 bars and a tail made of 3 bars
extra_bars = 7
head_data = '0000'
tail_data = '100'
# the bit weights are 1, 2, 4, 7, 0
# the code for 0 has a value of 11
table = {
'00110': 0,
'10001': 1,
'01001': 2,
'11000': 3,
'00101': 4,
'10100': 5,
'01100': 6,
'00011': 7,
'10010': 8,
'01010': 9,
}
# the smaller I25 barcode has 17 bars (counting head, tail, and 2 encoded digits)
minimum_size = 17
def group(data, size):
""" Return a batch of requested size from the supplied data at each iteration. """
for i in range(0, len(data), size):
yield data[i:i+size]
def convert(encoded):
""" Convert data encoded in tuple format to string buffer format. """
return str.join('', (str(v) for v,c in encoded))
def deinterleave(encoded):
"""
Return the encoded data in the correct order and format for decoding.
Parse the encoded data into 5 black bars, followed by 5 white bars and so on,
verifying first the header and tail and skipping them on conversion.
>>> deinterleave([(0, 'b'), (0, 'w'), (0, 'b'), (0, 'w'), \
(0, 'b'), (0, 'w'), (0, 'b'), (0, 'w'), (1, 'b'), \
(0, 'w'), (1, 'b'), (0, 'w'), (0, 'b'), (1, 'w'), \
(1, 'b'), (0, 'w'), (0, 'b')])
'0011000001'
"""
head = convert(encoded[:4])
tail = convert(encoded[-3:])
if head != head_data or tail != tail_data:
return None
black = encoded[4:-3:2]
white = encoded[5:-3:2]
data = ''
for bline, wline in zip(group(black, 5), group(white, 5)):
data += convert(bline) + convert(wline)
return data
def decode(data):
"""
Return a list of the numeric codes decoded from a string buffer of 1's and 0's.
Each code is 5 chars long and converts to a single digit.
If an invalid or incomplete code is found in the data it is decoded as None.
>>> decode('00110100010100111000001011010001100000111001001010')
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> decode('001101')
[0, None]
"""
result = []
for i in group(data, 5):
result.append(table.get(i))
return result
def threshold(rle):
""" Calculate the cutoff size to identify a bar as narrow or wide. """
# mean_len = ((min(rle)[0] + max(rle)[0]) / 2) - 1
# I was using the mean pixel length of the bars,
# but the calc below should be more accurate
n = (len(rle) - extra_bars) / code_length # number of digits in data
pxlen = sum(map(lambda x: x[0], rle[4:-3])) # sum of pixel length of all digits
pxmult = narrow_count + (wide_count * widebar_mult)
cutoff = 2 * (pxlen / (pxmult * n))
return cutoff
def process(rle):
""" Full I25 processing. Return None for invalid input. """
if len(rle) < minimum_size: return None
result = []
cutoff = threshold(rle)
encoded = [(int(l > cutoff), c) for l,c in rle]
data = deinterleave(encoded)
if data: result = decode(data)
return result or None
if __name__ == '__main__':
import doctest
doctest.testmod()