1# The MIT License (MIT) 2# 3# Copyright (c) 2014-2015 WUSTL ZPLAB 4# 5# Permission is hereby granted, free of charge, to any person obtaining a copy 6# of this software and associated documentation files (the "Software"), to deal 7# in the Software without restriction, including without limitation the rights 8# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9# copies of the Software, and to permit persons to whom the Software is 10# furnished to do so, subject to the following conditions: 11# 12# The above copyright notice and this permission notice shall be included in all 13# copies or substantial portions of the Software. 14# 15# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21# SOFTWARE. 22# 23# Authors: Zach Pincus 24 25import ctypes 26import numpy 27import sys 28import os.path 29import glob 30import collections 31 32__all__ = ['ZBAR_SYMBOLS', 'ZBAR_CONFIGS', 'Scanner', 'Symbol'] 33 34 35def load_zbar(): 36 if sys.platform == 'win32': 37 loader = ctypes.windll 38 functype = ctypes.WINFUNCTYPE 39 else: 40 loader = ctypes.cdll 41 functype = ctypes.CFUNCTYPE 42 43 zbar = None 44 errors = [] 45 possible_zbar_libs = glob.glob(os.path.join(os.path.dirname(__file__), '_zbar.*')) 46 for lib in possible_zbar_libs: 47 try: 48 zbar = loader.LoadLibrary(lib) 49 break 50 except Exception: 51 # Get exception instance in Python 2.x/3.x compatible manner 52 e_type, e_value, e_tb = sys.exc_info() 53 del e_tb 54 errors.append((lib, e_value)) 55 56 if zbar is None: 57 if errors: 58 # No zbar library loaded, and load-errors reported for some 59 # candidate libs 60 err_txt = ['%s:\n%s' % (l, str(e.args[0])) for l, e in errors] 61 raise RuntimeError('One or more zbar libraries were found, but ' 62 'could not be loaded due to the following errors:\n' 63 '\n\n'.join(err_txt)) 64 else: 65 # No errors, because no potential libraries found at all! 66 raise RuntimeError('Could not find a zbar library in ' + __file__) 67 68 return zbar 69 70_ZB = load_zbar() 71 72API = { 73 'zbar_image_scanner_create': (ctypes.c_void_p, ()), 74 'zbar_image_scanner_destroy': (None, (ctypes.c_void_p,)), 75 'zbar_image_scanner_set_config': (None, (ctypes.c_void_p, ctypes.c_uint, ctypes.c_uint, ctypes.c_int)), 76 'zbar_scan_image': (ctypes.c_int, (ctypes.c_void_p, ctypes.c_uint, ctypes.c_uint, ctypes.c_void_p)), 77 'zbar_image_scanner_first_symbol': (ctypes.c_void_p, (ctypes.c_void_p,)), 78 'zbar_symbol_next': (ctypes.c_void_p, (ctypes.c_void_p,)), 79 'zbar_symbol_get_type': (ctypes.c_uint, (ctypes.c_void_p,)), 80 'zbar_get_symbol_name': (ctypes.c_char_p, (ctypes.c_uint,)), 81 'zbar_symbol_get_data': (ctypes.c_void_p, (ctypes.c_void_p,)), 82 'zbar_symbol_get_data_length': (ctypes.c_uint, (ctypes.c_void_p,)), 83 'zbar_symbol_get_quality': (ctypes.c_int, (ctypes.c_void_p,)), 84 'zbar_symbol_get_loc_size': (ctypes.c_uint, (ctypes.c_void_p,)), 85 'zbar_symbol_get_loc_x': (ctypes.c_int, (ctypes.c_void_p, ctypes.c_uint)), 86 'zbar_symbol_get_loc_y': (ctypes.c_int, (ctypes.c_void_p,ctypes.c_uint)), 87 } 88 89def register_api(lib, api): 90 for f, (restype, argtypes) in api.items(): 91 func = getattr(lib, f) 92 func.restype = restype 93 func.argtypes = argtypes 94 95register_api(_ZB, API) 96 97ZBAR_SYMBOLS = { 98 'ZBAR_NONE' : 0, # /**< no symbol decoded */ 99 'ZBAR_PARTIAL' : 1, # /**< intermediate status */ 100 'ZBAR_EAN8' : 8, # /**< EAN-8 */ 101 'ZBAR_UPCE' : 9, # /**< UPC-E */ 102 'ZBAR_ISBN10' : 10, # /**< ISBN-10 (from EAN-13). @since 0.4 */ 103 'ZBAR_UPCA' : 12, # /**< UPC-A */ 104 'ZBAR_EAN13' : 13, # /**< EAN-13 */ 105 'ZBAR_ISBN13' : 14, # /**< ISBN-13 (from EAN-13). @since 0.4 */ 106 'ZBAR_I25' : 25, # /**< Interleaved 2 of 5. @since 0.4 */ 107 'ZBAR_CODE39' : 39, # /**< Code 39. @since 0.4 */ 108 'ZBAR_PDF417' : 57, # /**< PDF417. @since 0.6 */ 109 'ZBAR_QRCODE' : 64, # /**< QR Code. @since 0.10 */ 110 'ZBAR_CODE128' : 128, # /**< Code 128 */ 111 'ZBAR_SYMBOL' : 0x00ff, # /**< mask for base symbol type */ 112 'ZBAR_ADDON2' : 0x0200, # /**< 2-digit add-on flag */ 113 'ZBAR_ADDON5' : 0x0500, # /**< 5-digit add-on flag */ 114 'ZBAR_ADDON' : 0x0700, # /**< add-on flag mask */ 115} 116 117ZBAR_CONFIGS = { 118 'ZBAR_CFG_ENABLE': 0, #/**< enable symbology/feature */ 119 'ZBAR_CFG_ADD_CHECK': 1, #/**< enable check digit when optional */ 120 'ZBAR_CFG_EMIT_CHECK': 2, #/**< return check digit when present */ 121 'ZBAR_CFG_ASCII': 3, #/**< enable full ASCII character set */ 122 'ZBAR_CFG_NUM': 4, #/**< number of boolean decoder configs */ 123 'ZBAR_CFG_MIN_LEN': 0x20, #/**< minimum data length for valid decode */ 124 'ZBAR_CFG_MAX_LEN': 0x21, #/**< maximum data length for valid decode */ 125 'ZBAR_CFG_POSITION': 0x80, #/**< enable scanner to collect position data */ 126 'ZBAR_CFG_X_DENSITY':0x100, #/**< image scanner vertical scan density */ 127 'ZBAR_CFG_Y_DENSITY':0x101, #/**< image scanner horizontal scan density */ 128} 129 130 131Symbol = collections.namedtuple('Symbol', ['type', 'data', 'quality', 'position']) 132 133class Scanner(object): 134 def __init__(self, config=None): 135 """Create a barcode-scanner object. 136 137 By default, scanning for all barcode types is enabled, and reporting of 138 their locations is enabled. This can be controlled by the config parameter. 139 140 Parameters: 141 config: None or a list of (symbol_type, config_type, value) triples. 142 * symbol_type must be one of ZBAR_SYMBOLS, which refers to a 143 class of barcodes. ZBAR_NONE will cause the configuration 144 option to apply to all barcode types. 145 * config_type must be one of ZBAR_CONFIGS, defined in zbar.h. 146 Of particular interest are ZBAR_CFG_ENABLE (enable specific 147 symbol type), ZBAR_CFG_ADD_CHECK (enable check-digit 148 verification) and ZBAR_CFG_MIN_LEN and ZBAR_CFG_MAX_LEN (only 149 return decoded barcodes with the specified data length). 150 NB: Enabling/disabling specific barcode types is complex and 151 not particularly well supported by zbar (some barcode types 152 will be scanned-for by default unless disabled; some require 153 specific enablement; some types like ISBN and UPC that are 154 subclasses of EAN barcodes require EAN to also be enabled). 155 Thus is is STRONGLY recommended to use the default config 156 and filter for barcode types after the fact. 157 * value should be 1 for boolean options, or an integer for the 158 other options. 159 """ 160 self._scanner = _ZB.zbar_image_scanner_create() 161 if config is None: 162 config = [('ZBAR_NONE', 'ZBAR_CFG_ENABLE', 1), ('ZBAR_NONE', 'ZBAR_CFG_POSITION', 1)] 163 164 for symbol_type, config_type, value in config: 165 _ZB.zbar_image_scanner_set_config(self._scanner, ZBAR_SYMBOLS[symbol_type], ZBAR_CONFIGS[config_type], value) 166 167 def __del__(self): 168 _ZB.zbar_image_scanner_destroy(self._scanner) 169 del self._scanner 170 171 def scan(self, image): 172 """Scan an image and return a list of barcodes identified. 173 174 Parameters: 175 image: must be a 2-dimensional numpy array of dtype uint8. 176 177 Returns: list of Symbol namedtuples. 178 179 Each Symbol has 'type', 'data', 'quality', and 'position' attributes. 180 * 'type' refers to the barcode's type (e.g. 'QR-Code') 181 * 'data' is a bytes instance containing the barcode payload 182 * 'quality' is a numerical score 183 * 'position' is either an empty list (if position recording was 184 disabled), or a list of (x, y) indices into the image that define 185 the barcode's location. 186 """ 187 image = numpy.asarray(image) 188 if not image.dtype == numpy.uint8 and image.ndim == 2: 189 raise ValueError('Image must be 2D uint8 type') 190 if image.flags.c_contiguous: 191 height, width = image.shape 192 else: 193 image = numpy.asfortranarray(image) 194 width, height = image.shape 195 num_symbols = _ZB.zbar_scan_image(self._scanner, width, height, image.ctypes.data) 196 symbols = [] 197 symbol = _ZB.zbar_image_scanner_first_symbol(self._scanner) 198 while(symbol): 199 sym_type = _ZB.zbar_symbol_get_type(symbol) 200 sym_name = _ZB.zbar_get_symbol_name(sym_type).decode('ascii') 201 sym_data_ptr = _ZB.zbar_symbol_get_data(symbol) 202 sym_data_len = _ZB.zbar_symbol_get_data_length(symbol) 203 sym_data = ctypes.string_at(sym_data_ptr, sym_data_len) 204 sym_quality = _ZB.zbar_symbol_get_quality(symbol) 205 sym_loc = [] 206 for i in range(_ZB.zbar_symbol_get_loc_size(symbol)): 207 x = _ZB.zbar_symbol_get_loc_x(symbol, i) 208 y = _ZB.zbar_symbol_get_loc_y(symbol, i) 209 sym_loc.append((x, y)) 210 symbols.append(Symbol(sym_name, sym_data, sym_quality, sym_loc)) 211 symbol = _ZB.zbar_symbol_next(symbol) 212 assert len(symbols) == num_symbols 213 return symbols 214