Skip to content

Commit 9d78fd9

Browse files
committed
Streaming decoder update. Usable now.
1 parent 08eb81c commit 9d78fd9

7 files changed

Lines changed: 69 additions & 53 deletions

File tree

docs/source/conf.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@
4848
# built documents.
4949
#
5050
# The short X.Y version.
51-
version = release = '0.7.0'
51+
version = release = '0.8.0'
5252
# The full version, including alpha/beta/rc tags.
5353

5454
# The language for content autogenerated by Sphinx. Refer to documentation

examples/decode_opencv.py

Lines changed: 10 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,9 @@
11
# -*- coding: utf-8 -*-
22

33
import ctypes
4-
import time
54
import sys
65

76
import cv
8-
import array
97
import quirc
108

119
# Create window for image showing
@@ -23,12 +21,8 @@
2321
# And the font for text drawing
2422
font = cv.InitFont(cv.CV_FONT_HERSHEY_COMPLEX_SMALL, 2, 2, 0, 1, 8)
2523

26-
# Initialize all required quirc structures
27-
obj = quirc.api.new()
28-
quirc.api.resize(obj, *size)
29-
30-
code = quirc.api.structures.Code()
31-
data = quirc.api.structures.Data()
24+
# Initialize QR decoder
25+
decoder = quirc.Decoder(*size)
3226

3327
while True:
3428
# Query new frame
@@ -37,40 +31,23 @@
3731
# Make a grayscale copy
3832
cv.CvtColor(frame, grayscale, cv.CV_BGR2GRAY)
3933

40-
# Create a buffer for recognition
41-
buffer = quirc.api.begin(obj, *size)
42-
43-
# Fill it with a pixels data
44-
buf = array.array('B', grayscale.tostring())
45-
ctypes.memmove(buffer, buf.buffer_info()[0], size[0]*size[1])
46-
47-
quirc.api.end(obj)
48-
49-
for i in range(quirc.api.count(obj)):
50-
# Extract all QR codes
51-
quirc.api.extract(obj, i, code)
52-
try:
53-
quirc.api.decode(code, data)
54-
except quirc.DecodeException:
55-
continue
34+
for code in decoder.decode(grayscale.tostring()):
5635

5736
# Draw a countours for each QR code
58-
cv.Line(frame, (code.corners[0].x, code.corners[0].y), (code.corners[1].x, code.corners[1].y), (0, 255, 0))
59-
cv.Line(frame, (code.corners[1].x, code.corners[1].y), (code.corners[2].x, code.corners[2].y), (0, 255, 0))
60-
cv.Line(frame, (code.corners[2].x, code.corners[2].y), (code.corners[3].x, code.corners[3].y), (0, 255, 0))
61-
cv.Line(frame, (code.corners[3].x, code.corners[3].y), (code.corners[0].x, code.corners[0].y), (0, 255, 0))
37+
# TODO: replace with a cv.PolyLine
38+
cv.Line(frame, (code.corners[0][0], code.corners[0][1]), (code.corners[1][0], code.corners[1][1]), (0, 255, 0))
39+
cv.Line(frame, (code.corners[1][0], code.corners[1][1]), (code.corners[2][0], code.corners[2][1]), (0, 255, 0))
40+
cv.Line(frame, (code.corners[2][0], code.corners[2][1]), (code.corners[3][0], code.corners[3][1]), (0, 255, 0))
41+
cv.Line(frame, (code.corners[3][0], code.corners[3][1]), (code.corners[0][0], code.corners[0][1]), (0, 255, 0))
6242

6343
# And a decoded text for each one
64-
cv.PutText(frame, ctypes.string_at(data.payload, data.payload_len), (code.corners[3].x, code.corners[3].y+14), font, (0, 0, 255))
44+
cv.PutText(frame, code.text, (code.corners[3][0], code.corners[3][1]+14), font, (0, 0, 255))
6545

6646
# Show this image in the window
6747
cv.ShowImage('window', frame)
6848

6949
# Wait for key pressing
7050
key = cv.WaitKey(5)
7151
if key > 0:
72-
# And if there any key, do not forgeting to clean up memory!
73-
quirc.api.destroy(obj)
74-
75-
# Cya!
52+
# If any key presses, exit
7653
sys.exit()

quirc/__init__.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,9 @@
33

44
"""Bindings to QR code decoding library `quirc`"""
55

6-
__version__ = '0.7.0'
6+
__version__ = '0.8.0'
77

88
import api
9+
import converters
910
from base import decode, Decoder
1011
from api.exceptions import DecodeException

quirc/base.py

Lines changed: 28 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -83,32 +83,45 @@ def __init__(self, width, height):
8383
self._width = width
8484
self._height = height
8585

86-
self._width_iter = compat.range(self._width)
87-
self._height_iter = compat.range(self._height)
88-
8986
self._obj = api.new()
9087
api.resize(self._obj, self._width, self._height)
9188

9289
self._code = api.structures.Code()
9390
self._data = api.structures.Data()
9491

95-
def decode(self, image):
96-
buffer = api.begin(self._obj, self._width, self._height)
92+
def decode(self, data):
93+
"""Fill buffer with a raw binary data
94+
95+
Each byte already must represent one pixel
9796
98-
if image.mode not in ('1', 'L'):
99-
image = image.convert('L')
100-
pixels = image.load()
97+
Parameters::
10198
102-
idx = 0
103-
for i in self._width_iter:
104-
for j in self._height_iter:
105-
buffer[idx] = ctypes.c_uint8(pixels[j, i])
106-
idx += 1
99+
data : image binary data
100+
"""
107101

108-
del idx
102+
buffer = api.begin(self._obj, self._width, self._height)
103+
104+
converters.raw(buffer, data)
109105

110-
# Finish codes identification
111106
api.end(self._obj)
112107

108+
for i in range(api.count(self._obj)):
109+
110+
# Extract first code
111+
api.extract(self._obj, i, self._code)
112+
try:
113+
api.decode(self._code, self._data)
114+
except api.exceptions.DecodeException:
115+
continue
116+
117+
yield Code(
118+
tuple([(corner.x, corner.y) for corner in self._code.corners]),
119+
self._code.size,
120+
self._data.version,
121+
self._data.ecc_level,
122+
self._data.data_type,
123+
ctypes.string_at(self._data.payload, self._data.payload_len),
124+
)
125+
113126
def __del__(self):
114127
api.destroy(self._obj)

quirc/converters.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
"""
77

88
import ctypes
9+
import array
910

1011
from compat import range
1112

@@ -29,3 +30,14 @@ def pil(image):
2930
for i in width_iter:
3031
for j in height_iter:
3132
yield ctypes.c_uint8(pixels[j, i])
33+
34+
def raw(buffer, data):
35+
"""Fill the buffer with a raw binary data
36+
37+
Parameters::
38+
buffer : returned by the `quirc.api.begin` function object
39+
data : binary data
40+
"""
41+
42+
buf = array.array('B', data)
43+
ctypes.memmove(buffer, buf.buffer_info()[0], len(data))

setup.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020

2121
setup(
2222
name='quirc',
23-
version='0.7.0',
23+
version='0.8.0',
2424
author='SvartalF',
2525
author_email='self@svartalf.info',
2626
url='https://github.com/svartalf/python-quirc',

tests/test_quirc.py

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,22 @@ class TestQuircCase(unittest.TestCase):
2121
def setUp(self):
2222
self._folder = os.path.abspath(os.path.dirname(__file__))
2323

24+
@unittest.skipIf(not compat.have_pil, 'PIL is unaccessible')
2425
def test_decoder(self):
2526
decoder = quirc.Decoder(148, 148)
26-
decoder.decode(Image.open(os.path.join(self._folder, 'images', 'link.gif')))
27+
image = Image.open(os.path.join(self._folder, 'images', 'link.gif')).convert('L')
28+
result = list(decoder.decode(image.tostring()))
29+
30+
self.assertEqual(len(result), 1)
31+
32+
code = result[0]
33+
34+
self.assertEqual(code.data_type, 4)
35+
self.assertEqual(code.ecc_level, 0)
36+
self.assertEqual(code.size, 29)
37+
38+
self.assertTupleEqual(code.corners, ((16, 16), (132, 16), (132, 132), (16, 132)))
39+
self.assertEqual(code.text, 'https://github.com/svartalf/python-quirc')
2740

2841
@unittest.skipIf(not compat.have_pil, 'PIL is unaccessible')
2942
def test_pil(self):
@@ -45,4 +58,4 @@ def test_pil(self):
4558
self.assertEqual(code.size, 29)
4659

4760
self.assertTupleEqual(code.corners, ((16, 16), (132, 16), (132, 132), (16, 132)))
48-
self.assertEqual(code.text, str(code), 'https://github.com/svartalf/python-quirc')
61+
self.assertEqual(code.text, 'https://github.com/svartalf/python-quirc')

0 commit comments

Comments
 (0)