def __init__(self, debug_barcodes): if not gouda: raise InselectError('Barcode decoding not available') elif not DATAMATRIX_ENGINE: raise InselectError('No datamatrix engine available') else: self.engine = DATAMATRIX_ENGINE() gouda.util.DEBUG_PRINT = debug_barcodes
def __init__(self, path): path = Path(path) if not path.is_file(): raise InselectError('Image file [{0}] does not exist'.format(str(path))) else: self._path = path self._array = None
def __call__(self, progress): """ """ debug_print('BarcodePlugin.__call__') if InliteEngine.available(): engine = InliteEngine(datamatrix=True) elif LibDMTXEngine.available(): engine = LibDMTXEngine() else: raise InselectError('No barcode decoding engine available') progress('Loading full-res image') image_array = self.document.scanned.array items = self.document.items for index, item, crop in izip(count(), items, self.document.crops): msg = u'Reading barcodes in box {0} of {1}'.format(1 + index, len(items)) progress(msg) barcodes = self._decode_barcodes(engine, crop, progress) if barcodes: debug_print('Crop [{0}] - found [{1}]'.format(index, barcodes)) # TODO LH This mapping to come from metadata config? item['fields']['catalogNumber'] = barcodes else: debug_print('Crop [{0}] - no barcodes'.format(index)) self.items = items debug_print('BarcodePlugin.__call__ exiting. [{0}] boxes'.format(len(items)))
def open_file(self, path=None): """Opens path, which can be None, the path to an inselect document or the path to an image file. If None, the user is prompted to select a file. * If a .inselect file, the file is opened * If an image file for which a .inselect document already exists, the .inselect file is opened * If a _thumbnail.jpg file corresponding to an existing .inselect file, the .inselect file is opened * If an image file, a new .inselect file is created and opened """ debug_print(u'MainWindow.open_file [{0}]'.format(path)) if not path: folder = QSettings().value( 'working_directory', QDesktopServices.storageLocation( QDesktopServices.DocumentsLocation)) filter = u'Inselect documents (*{0});;Images ({1})' filter = filter.format(InselectDocument.EXTENSION, u' '.join(IMAGE_PATTERNS)) path, selectedFilter = QtGui.QFileDialog.getOpenFileName( self, "Open", folder, filter) if path: # Will be None if user cancelled getOpenFileName if not self.close_document(): # User does not want to close the existing document pass else: path = Path(path) if path.suffix in IMAGE_SUFFIXES: # Compute the path to the inselect document (which may or # may not already exist) of the image file doc_of_image = path.name.replace( InselectDocument.THUMBNAIL_SUFFIX, u'') doc_of_image = path.parent / doc_of_image doc_of_image = doc_of_image.with_suffix( InselectDocument.EXTENSION) else: doc_of_image = None if InselectDocument.EXTENSION == path.suffix: # Open the .inselect document debug_print('Opening inselect document [{0}]'.format(path)) self.open_document(path) elif doc_of_image and doc_of_image.is_file(): # An image file corresponding to an existing .inselect file msg = u'Opening inselect document [{0}] of thumbnail [{1}]' debug_print(msg.format(doc_of_image, path)) self.open_document(doc_of_image) elif path.suffix in IMAGE_SUFFIXES: msg = u'Creating new inselect document for image [{0}]' debug_print(msg.format(path)) self.new_document(path) else: raise InselectError('Unknown file type [{0}]'.format(path))
def scene_box_added(self, rect): """The user added a box """ m = self.model() row = len(self._rows) if not m.insertRow(row): raise InselectError('Could not insert row') else: # Cumbersome conversion to ints rect = QRect(rect.left(), rect.top(), rect.width(), rect.height()) if not m.setData(m.index(row, 0), rect, RectRole): raise InselectError('Could not set rect') else: # Select the new box self.scene.clearSelection() item = next(self.items_of_rows([row])) item.setSelected(True) item.update()
def __init__(self, document, parent): super(BarcodePlugin, self).__init__() try: import gouda.strategies.roi.roi # noqa import gouda.strategies.resize # noqa except ImportError: raise InselectError('Barcode decoding is not available') else: self.document = document self.parent = parent
def main(args=None): if args is None: args = sys.argv[1:] # Private import to avoid top-level import of cv2 (via gouda), which # would make the script slower to start and potentially break if # frozen on Windows (see call to fix_frozen_dll_path below). try: import gouda except ImportError: gouda = engine_options = None else: from gouda.engines.options import engine_options from gouda.strategies.resize import resize from gouda.strategies.roi.roi import roi if not gouda: raise InselectError('Barcode decoding not available') options = engine_options() if not options: raise InselectError('No barcode reading engines are available') parser = argparse.ArgumentParser( description='Reads barcodes within boxes, overwriting existing values') parser.add_argument('--debug', action='store_true') parser.add_argument('--debug-barcodes', action='store_true') parser.add_argument('-v', '--version', action='version', version='%(prog)s ' + inselect.__version__) parser.add_argument("dir", type=Path, help='Directory containing inselect documents') parser.add_argument('engine', choices=sorted(options.keys()), help='The barcode reading engine to use') args = parser.parse_args(args) inselect.lib.utils.DEBUG_PRINT = args.debug gouda.util.DEBUG_PRINT = args.debug_barcodes engine = options[args.engine]() read_barcodes(engine, args.dir, strategies=(resize, roi))
def save_crops(self, normalised, paths, progress=None): "Saves crops given in normalised to paths." # TODO Copy EXIF tags? # TODO Make read-only? for index, crop, path in izip(count(), self.crops(normalised), paths): if progress: progress('Writing crop {0}'.format(1 + index)) if not cv2.imwrite(str(path), crop): raise InselectError('Unable to write crop [{0}]'.format(path)) else: debug_print('Wrote crop [{0}]'.format(path))
def array(self): """ Lazy-load np.array of the image """ if self._array is None: p = str(self._path) debug_print('Reading from image file [{0}]'.format(p)) image = cv2.imread(p) if image is None: raise InselectError('[{0}] could not be read as an image'.format(p)) else: self._array = image return self._array
def load_engine(): """Returns an instance of the user's choice of barcode reading engine """ try: from gouda.engines import InliteEngine, LibDMTXEngine, ZbarEngine except ImportError: raise InselectError('Barcode decoding is not available') else: settings = current_settings() engine = settings['engine'] if 'libdmtx' == engine: return LibDMTXEngine() elif 'zbar' == engine: return ZbarEngine() elif 'inlite' == engine: return InliteEngine(settings['inlite-format']) else: raise ValueError( 'Unrecognised barcode reader [{0}]'.format(engine))
def ingest_from_directory(inbox, docs): """Ingest images from the directory given by inbox to the directory given by docs """ inbox, docs = Path(inbox), Path(docs) if not inbox.is_dir(): raise InselectError( 'Inbox directory [{0}] does not exist'.format(inbox)) if not docs.is_dir(): print('Create document directory [{0}]'.format(docs)) docs.mkdir(parents=True) # TODO LH Case insensitive matching for source in apply(chain, [inbox.glob(p) for p in IMAGE_PATTERNS]): try: ingest_image(source, docs) except Exception: print('Error ingesting [{0}]'.format(source)) traceback.print_exc()
def new_document(self, path): """Creates and opens a new inselect document for the scanned image given in path """ debug_print('MainWindow.new_document [{0}]'.format(path)) path = Path(path) if not path.is_file(): raise InselectError( u'Image file [{0}] does not exist'.format(path)) else: # Callable for worker thread class NewDoc(object): def __init__(self, image): self.image = image def __call__(self, progress): progress('Creating thumbnail of scanned image') doc = ingest_image(self.image, self.image.parent) self.document_path = doc.document_path self.run_in_worker(NewDoc(path), 'New document', self.new_document_finished)
def ingest_from_directory( inbox, docs, thumbnail_width_pixels=InselectDocument.THUMBNAIL_DEFAULT_WIDTH, cookie_cutter=None): """Ingest images from the directory given by inbox to the directory given by docs """ inbox, docs = Path(inbox), Path(docs) cookie_cutter = Path(cookie_cutter) if cookie_cutter else None if not inbox.is_dir(): raise InselectError( 'Inbox directory [{0}] does not exist'.format(inbox)) if not docs.is_dir(): print('Create document directory [{0}]'.format(docs)) docs.mkdir(parents=True) if cookie_cutter: cookie_cutter = CookieCutter.load(cookie_cutter) for source in (p for p in inbox.iterdir() if IMAGE_SUFFIXES_RE.match(p.name)): print('Ingesting [{0}]'.format(source)) try: ingest_image(source, docs, thumbnail_width_pixels=thumbnail_width_pixels, cookie_cutter=cookie_cutter) except KeyboardInterrupt: raise except Exception: print('Error ingesting [{0}]'.format(source)) traceback.print_exc() else: print('Ingested [{0}]'.format(source))
def __new__(cls, left, top, width, height): if left < 0 or top < 0 or width <= 0 or height <= 0: raise InselectError('Bad rectangle') else: return super(Rect, cls).__new__(cls, left, top, width, height)
def __init__(self, document, parent): if not gouda: raise InselectError('Barcode decoding is not available') else: self.document = document
def validate_in_bounds(self, boxes): h, w = self.array.shape[:2] for left, top, width, height in boxes: if not (left>=0 and top>=0 and left<w and top<h and width>0 and left+width<=w and height>0 and top+height<=h): raise InselectError('One or more boxes are not in bounds')
def validate_normalised(boxes): for l, t, w, h in boxes: if not (l >= 0 and t >= 0 and l <= 1 and t <= 1 and w > 0 and l + w <= 1 and h > 0 and t + h <= 1): raise InselectError('One or more boxes are not normalised')