Skip to content

Commit 2c1fa05

Browse files
committed
High-level API for decoding images via PIL
1 parent 900ed2d commit 2c1fa05

4 files changed

Lines changed: 122 additions & 2 deletions

File tree

README.md

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,19 @@ When version 1.0.0 will be released the API will be frozen, and any changes whic
1616

1717
## Usage
1818

19+
### High-level API
20+
21+
Just call a `quirc.decode()` function with a PIL.Image object as a parameter, like this:
22+
23+
import Image
24+
25+
image = Image.open('tests/images/test1.png')
26+
for code in quirc.decode(image):
27+
print code['text']
28+
# >>> test1
29+
30+
Currently only PIL is supported.
31+
1932
### Low-level API
2033

2134
Low-level API directly corresponds to the C API:

quirc/__init__.py

Lines changed: 70 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,75 @@
33

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

6+
import ctypes
7+
68
from quirc import api
79

8-
__version__ = '0.5.0'
9-
__all__ = ('api',)
10+
USING_PIL = True
11+
try:
12+
from PIL import Image
13+
except ImportError:
14+
try:
15+
import Image
16+
except ImportError:
17+
USING_PIL = False
18+
19+
__version__ = '0.6.0'
20+
__all__ = ('api', 'decode')
21+
22+
23+
def decode(image):
24+
"""Recognize image and return generator with all the available QR codes
25+
26+
Currently supports only PIL Image object as an parameter
27+
"""
28+
29+
if not (USING_PIL and isinstance(image, Image.Image)):
30+
raise TypeError('Unknown image object type: %s' % type(image))
31+
32+
# Convert to grayscale mode
33+
if image.mode not in ('1', 'L'):
34+
image = image.convert('L')
35+
36+
width, height = image.size
37+
pixels = image.load()
38+
39+
obj = api.new()
40+
api.resize(obj, width, height)
41+
buffer = api.begin(obj, width, height)
42+
43+
# Fill buffer with a image pixels. One cell, one pixel.
44+
# TODO: looks like a very slow operation
45+
idx = 0
46+
for i in range(width):
47+
for j in range(height):
48+
buffer[idx] = ctypes.c_uint8(pixels[j, i])
49+
idx += 1
50+
51+
del idx
52+
53+
# Finish codes identification
54+
api.end(obj)
55+
56+
num_codes = api.count(obj)
57+
58+
code = api.structures.Code()
59+
data = api.structures.Data()
60+
61+
for i in range(num_codes):
62+
63+
# Extract first code
64+
api.extract(obj, i, code)
65+
api.decode(code, data)
66+
67+
yield {
68+
'corners': tuple([(corner.x, corner.y) for corner in code.corners]),
69+
'size': code.size,
70+
'version': data.version,
71+
'ecc_level': data.ecc_level,
72+
'mask': data.mask,
73+
'data_type': data.data_type,
74+
'text': ctypes.string_at(data.payload, data.payload_len),
75+
}
76+
77+
api.destroy(obj)

tests/images/link.gif

1.27 KB
Loading

tests/test_quirc.py

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
# -*- coding: utf-8 -*-
2+
3+
import os.path
4+
import unittest
5+
6+
import quirc
7+
8+
USING_PIL = True
9+
try:
10+
import Image
11+
except ImportError:
12+
try:
13+
from PIL import Image
14+
except ImportError:
15+
USING_PIL = False
16+
17+
18+
class TestQuircCase(unittest.TestCase):
19+
20+
def setUp(self):
21+
self._folder = os.path.abspath(os.path.dirname(__file__))
22+
23+
@unittest.skipIf(not USING_PIL, 'PIL is unaccessible')
24+
def test_pil(self):
25+
image = Image.open(os.path.join(self._folder, 'images', 'link.gif'))
26+
27+
result = list(quirc.decode(image))
28+
29+
self.assertEqual(len(result), 1)
30+
31+
code = result[0]
32+
33+
self.assertEqual(code['data_type'], 4)
34+
self.assertEqual(code['mask'], 6)
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')

0 commit comments

Comments
 (0)