Skip to content

Commit

Permalink
Merge pull request #12 from globophobe/master
Browse files Browse the repository at this point in the history
Add location/position support
  • Loading branch information
quicklizard99 committed Mar 11, 2018
2 parents 372003b + 4d0d092 commit 1aa81f7
Show file tree
Hide file tree
Showing 4 changed files with 75 additions and 15 deletions.
18 changes: 9 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,8 +49,8 @@ The `decode` function accepts instances of `PIL.Image`.
>>> from pyzbar.pyzbar import decode
>>> from PIL import Image
>>> decode(Image.open('pyzbar/tests/code128.png'))
[Decoded(data=b'Foramenifera', type='CODE128'),
Decoded(data=b'Rana temporaria', type='CODE128')]
[Decoded(data=b'Foramenifera', type='CODE128', rect=Rect(left=37, top=550, width=324, height=76)),
Decoded(data=b'Rana temporaria', type='CODE128', rect=Rect(left=4, top=0, width=390, height=76))]
```

It also accepts instances of `numpy.ndarray`, which might come from loading
Expand All @@ -59,8 +59,8 @@ images using [OpenCV](http://opencv.org/).
```
>>> import cv2
>>> decode(cv2.imread('pyzbar/tests/code128.png'))
[Decoded(data=b'Foramenifera', type='CODE128'),
Decoded(data=b'Rana temporaria', type='CODE128')]
[Decoded(data=b'Foramenifera', type='CODE128', rect=Rect(left=37, top=550, width=324, height=76)),
Decoded(data=b'Rana temporaria', type='CODE128', rect=Rect(left=4, top=0, width=390, height=76))]
```

You can also provide a tuple `(pixels, width, height)`, where the image data
Expand All @@ -72,14 +72,14 @@ is eight bits-per-pixel.
>>> # 8 bpp by considering just the blue channel
>>> decode((image[:, :, 0].astype('uint8').tobytes(), width, height))
[Decoded(data=b'Foramenifera', type='CODE128'),
Decoded(data=b'Rana temporaria', type='CODE128')]
[Decoded(data=b'Foramenifera', type='CODE128', rect=Rect(left=37, top=550, width=324, height=76)),
Decoded(data=b'Rana temporaria', type='CODE128', rect=Rect(left=4, top=0, width=390, height=76))]
>>> # 8 bpp by converting image to greyscale
>>> grey = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
>>> decode((grey.tobytes(), width, height))
[Decoded(data=b'Foramenifera', type='CODE128'),
Decoded(data=b'Rana temporaria', type='CODE128')]
[Decoded(data=b'Foramenifera', type='CODE128', rect=Rect(left=37, top=550, width=324, height=76)),
Decoded(data=b'Rana temporaria', type='CODE128', rect=Rect(left=4, top=0, width=390, height=76))]
>>> # If you don't provide 8 bpp
>>> decode((image.tobytes(), width, height))
Expand All @@ -98,7 +98,7 @@ effect of this on performance)
>>> from pyzbar.pyzbar import ZBarSymbol
>>> # Look for just qrcode
>>> decode(Image.open('pyzbar/tests/qrcode.png'), symbols=[ZBarSymbol.QRCODE])
[Decoded(data=b'Thalassiodracon', type='QRCODE')]
[Decoded(data=b'Thalassiodracon', type='QRCODE', rect=Rect(left=27, top=27, width=145, height=145))]
>>> # If we look for just code128, the qrcodes in the image will not be detected
>>> decode(Image.open('pyzbar/tests/qrcode.png'), symbols=[ZBarSymbol.CODE128])
Expand Down
38 changes: 36 additions & 2 deletions pyzbar/pyzbar.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from __future__ import print_function

from operator import itemgetter
from collections import namedtuple
from contextlib import contextmanager
from ctypes import cast, c_void_p, string_at
Expand All @@ -11,14 +12,19 @@
zbar_image_create, zbar_image_destroy, zbar_image_set_format,
zbar_image_set_size, zbar_image_set_data, zbar_scan_image,
zbar_image_first_symbol, zbar_symbol_get_data,
zbar_symbol_get_loc_size, zbar_symbol_get_loc_x, zbar_symbol_get_loc_y,
zbar_symbol_next, ZBarConfig, ZBarSymbol, EXTERNAL_DEPENDENCIES
)

__all__ = ['decode', 'EXTERNAL_DEPENDENCIES']

RANGEFN = getattr(globals(), 'xrange', range)

# A rectangle
Rect = namedtuple('Rect', ['left', 'top', 'width', 'height'])

# Results of reading a barcode
Decoded = namedtuple('Decoded', ['data', 'type'])
Decoded = namedtuple('Decoded', ['data', 'type', 'rect'])

# ZBar's magic 'fourcc' numbers that represent image formats
FOURCC = {
Expand Down Expand Up @@ -73,6 +79,23 @@ def zbar_image_scanner():
zbar_image_scanner_destroy(scanner)


def bounding_box_of_locations(locations):
"""Computes bounding boxes from scan locations.
Args:
locations: :obj:`list` of tuple (x, y) values.
Returns:
:obj:`list` of :obj:`Rect`: Coordinates of the bounding boxes.
"""
x_values = list(map(itemgetter(0), locations))
x_min, x_max = min(x_values), max(x_values)
y_values = list(map(itemgetter(1), locations))
y_min, y_max = min(y_values), max(y_values)
return (Rect(x_min, y_min, x_max - x_min, y_max - y_min))


def decode(image, symbols=None):
"""Decodes datamatrix barcodes in `image`.
Expand Down Expand Up @@ -144,9 +167,20 @@ def decode(image, symbols=None):
while symbol:
data = string_at(zbar_symbol_get_data(symbol))
symbol_type = ZBarSymbol(symbol.contents.value).name

loc = zbar_symbol_get_loc_size(symbol)
locations = [
(
zbar_symbol_get_loc_x(symbol, l),
zbar_symbol_get_loc_y(symbol, l)
)
for l in RANGEFN(loc)
]

results.append(Decoded(
data=data,
type=symbol_type
type=symbol_type,
rect=bounding_box_of_locations(locations)
))

symbol = zbar_symbol_next(symbol)
Expand Down
14 changes: 10 additions & 4 deletions pyzbar/tests/test_decode.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,9 @@
cv2 = None


from pyzbar.pyzbar import decode, Decoded, ZBarSymbol, EXTERNAL_DEPENDENCIES
from pyzbar.pyzbar import (
decode, Rect, Decoded, ZBarSymbol, EXTERNAL_DEPENDENCIES
)
from pyzbar.pyzbar_error import PyZbarError


Expand All @@ -24,20 +26,24 @@ class TestDecode(unittest.TestCase):
EXPECTED_CODE128 = [
Decoded(
data=b'Foramenifera',
type='CODE128'
type='CODE128',
rect=Rect(left=37, top=550, width=324, height=76)
),
Decoded(
data=b'Rana temporaria',
type='CODE128'
type='CODE128',
rect=Rect(left=4, top=0, width=390, height=76)
)
]

EXPECTED_QRCODE = [
Decoded(
b'Thalassiodracon',
type='QRCODE'
type='QRCODE',
rect=Rect(left=27, top=27, width=145, height=145)
)
]

def setUp(self):
self.code128 = Image.open(str(TESTDATA.joinpath('code128.png')))
self.qrcode = Image.open(str(TESTDATA.joinpath('qrcode.png')))
Expand Down
20 changes: 20 additions & 0 deletions pyzbar/wrapper.py
Original file line number Diff line number Diff line change
Expand Up @@ -261,6 +261,26 @@ def zbar_function(fname, restype, *args):
POINTER(c_int) # values in ZBarSymbol
)

zbar_symbol_get_loc_size = zbar_function(
'zbar_symbol_get_loc_size',
c_uint,
POINTER(c_int) # values in ZBARSymbol
)

zbar_symbol_get_loc_x = zbar_function(
'zbar_symbol_get_loc_x',
c_int,
POINTER(c_int), # values in ZBARSymbol
c_uint
)

zbar_symbol_get_loc_y = zbar_function(
'zbar_symbol_get_loc_y',
c_int,
POINTER(c_int), # values in ZBARSymbol
c_uint
)

zbar_symbol_next = zbar_function(
'zbar_symbol_next',
POINTER(c_int),
Expand Down

0 comments on commit 1aa81f7

Please sign in to comment.