def test_paste_when_location_is_inside_image_4_channels_no_blending_when_source_alpha_is_255( self): image_target = Image(img=np.full((10, 10, 4), 30.0, np.uint8)) image_src = Image(img=np.full((1, 1, 4), 255.0, np.uint8)) a = np.sum(image_src.img[0:1, 0:1, 0:3]) image_target.paste(image_src, 0, 0) b = np.sum(image_target.img[0:1, 0:1, 0:3]) self.assertEquals(a, b) #target changed into source
def test_paste_when_location_is_inside_image_4_channels_blending_works( self): image_target = Image(img=np.full((10, 10, 4), 10, np.uint8)) image_src = Image(img=np.full((1, 1, 4), 25, np.uint8)) image_target.paste(image_src, 0, 0) a = np.sum(image_src.img[0:1, 0:1, 0:3]) b = np.sum(image_target.img[0:1, 0:1, 0:3]) self.assertNotEquals(a, b) self.assertEquals(b, 33)
def target_image_alpha_becomes_255_after_paste(self): image_target = Image(img=np.full((5, 5, 4), 10, np.uint8)) max_alpha_before_paste = np.max(image_target.img[:, :, 3]) image_src = Image(img=np.full((1, 1, 4), 25, np.uint8)) image_target.paste(image_src, 0, 0) max_alpha_after_paste = np.max(image_target.img[:, :, 3]) self.assertEquals(max_alpha_before_paste, 10) self.assertEquals(max_alpha_after_paste, 255) #I expected it to be as it was before
def test_paste_when_location_is_outside_image_negative_coordinates_3_channels( self): image_target = Image.blank(10, 10) image_src = Image(img=np.full((2, 2, 3), 2, np.uint8)) image_target.paste(image_src, -10, 0) a = np.sum(image_target.img) self.assertEquals(a, 0)
def _adaptive_threshold(image, block_size, c): """ Perform an adaptive threshold operation on the image, reducing it to a binary image. """ thresh = cv2.adaptiveThreshold(image, 255.0, cv2.ADAPTIVE_THRESH_MEAN_C, cv2.THRESH_BINARY, block_size, c) return Image(thresh)
def test_paste_when_location_is_inside_image_4_target_alpha_does_not_matter( self): image_target = Image(img=np.full((10, 10, 4), 10, np.uint8)) image_src = Image(img=np.full((1, 1, 4), 25, np.uint8)) image_target.paste(image_src, 0, 0) b = np.sum(image_target.img[0:1, 0:1, 0:3]) self.assertEquals(b, 33) array = np.full((10, 10, 4), 10, np.uint8) array[:, :, 3] = np.ones((10, 10), np.uint8) image_target_new = Image(img=array) image_src_new = Image(img=np.full((1, 1, 4), 25, np.uint8)) image_target_new.paste(image_src_new, 0, 0) c = np.sum(image_target_new.img[0:1, 0:1, 0:3]) self.assertEquals(c, 33) self.assertEquals(b, c)
def _process_frame(self, frame, config, overlay_queue, result_queue, message_queue): image = Image(frame) gray_image = image.to_grayscale() # If we have an existing partial plate, merge the new plate with it and only try to read the # barcodes which haven't already been read. This significantly increases efficiency because # barcode read is expensive. scan_result = self._scanner.scan_next_frame(gray_image) if config.console_frame.value(): scan_result.print_summary() if scan_result.success(): # Record the time so we can see how long its been since we last saw a puck self._last_puck_time = time.time() plate = scan_result.plate() if scan_result.any_valid_barcodes(): overlay_queue.put(PlateOverlay(plate, config)) self._plate_beep(plate, config.scan_beep.value()) if scan_result.any_new_barcodes(): result_queue.put((plate, image)) elif scan_result.any_valid_barcodes(): # We have read valid barcodes but they are not new, so the scanner didn't even output a plate self._last_puck_time = time.time() message_queue.put(NoNewBarcodeMessage()) elif scan_result.error() is not None and ( time.time() - self._last_puck_time > NO_PUCK_TIME): message_queue.put(ScanErrorMessage(scan_result.error()))
def test_paste_when_location_is_inside_image_3_channels(self): image_target = Image.blank(10, 10) image_src = Image(img=np.full((1, 1, 3), 2, np.uint8)) image_target.paste(image_src, 0, 0) a = np.sum(image_target.img) self.assertEquals(a, 6) self.assertEquals(image_target.img[0, 0, 1], 2) self.assertEquals(image_target.img[0, 1, 1], 0)
def draw_on_image(self, img): """ Draw the status message to the image. """ image = Image(img) # If the overlay has not expired, draw on the plate highlight and/or the status message if not self.has_expired(): image.draw_text(self._text, image.center(), self._color, centered=True, scale=2, thickness=3)
def _do_close_morph(threshold_image, morph_size): """ Perform a generic morphological operation on an image. """ element = cv2.getStructuringElement(cv2.MORPH_RECT, (morph_size, morph_size)) closed = cv2.morphologyEx(threshold_image.img, cv2.MORPH_CLOSE, element, iterations=1) return Image(closed)
def draw_on_image(self, img): """ Draw the plate highlight to the image. """ image = Image(img) # If the overlay has not expired, draw on the plate highlight and/or the status message if not self.has_expired(): self._plate.draw_plate(image, Color.Blue()) self._plate.draw_pins(image, self._options)
def _run_capture(self, stream, camera_positon, task_queue, view_queue, overlay_queue, stop_queue, message_queue): # Store the latest image overlay which highlights the puck latest_overlay = Overlay(0) last_time = time.time() display = True while stop_queue.empty(): if display: print("--- capture inside loop") display = False # Capture the next frame from the camera frame = stream.get_frame() if frame is None: message_queue.put(CameraErrorMessage(camera_positon)) return # Add the frame to the task queue to be processed # NOTE: the rate at which frames are pushed to the task queue is lower than the rate at which frames are acquired if task_queue.qsize() < Q_LIMIT and (time.time() - last_time >= INTERVAL): # Make a copy of image so the overlay doesn't overwrite it task_queue.put(frame.copy()) last_time = time.time() # All frames (scanned or not) are pushed to the view queue for display # Get the latest overlay - it won't be generated from the current frame but it doesn't matter while not overlay_queue.empty(): try: latest_overlay = overlay_queue.get(False) except queue.Empty: # Race condition where the scanner worker was stopped and the overlay queue cleared between # our calls to queue.empty() and queue.get(False) latest_overlay = Overlay(0) # Draw the overlay on the frame latest_overlay.draw_on_image(frame) view_queue.put(Image(frame)) print("CAPTURE stop & flush queues") self._flush_queue(task_queue) print("--- capture task Q flushed") self._flush_queue(view_queue) print("--- capture view Q flushed")
def _DEBUG_WIGGLES_READ(self, barcode, locate_type, side_length): if not self.DEBUG: return if barcode.is_valid(): print("DEBUG - WIGGLES SUCCESSFUL - " + locate_type) result = "_SUCCESS" else: result = "_FAIL" slot_img = Image(barcode._image).to_alpha() self._DEBUG_SAVE_IMAGE(slot_img, locate_type + result, side_length - 1) fp = barcode._finder_pattern fp.draw_to_image(slot_img, Color.Green()) self._DEBUG_SAVE_IMAGE(slot_img, locate_type + result, side_length - 1)
def __init__(self, original_frame): self._frame = original_frame self._image = Image(self._frame)
def polygon_image(lines): blank = Image.blank(image_original.width, image_original.height, 3, 255) img = cv2.drawContours(blank.img, lines, -1, (0, 255, 0), 1) return Image(img)
def _scanner_worker(task_queue, overlay_queue, result_queue, options): """ Function used as the main loop of a worker process. Scan images for barcodes, combining partial scans until a full puck is reached. Keep the record of the last scan which was at least partially successful (aligned geometry and some barcodes scanned). For each new frame, we can attempt to merge the results with this previous plates so that we don't have to re-read any of the previously captured barcodes (because this is a relatively expensive operation). """ last_plate_time = time.time() SlotScanner.DEBUG = options.slot_images.value() SlotScanner.DEBUG_DIR = options.slot_image_directory.value() plate_type = options.plate_type.value() barcode_size = options.barcode_size.value() if plate_type == "None": scanner = OpenScanner(barcode_size) else: scanner = GeometryScanner(plate_type, barcode_size) while True: # Get next image from queue (terminate if a queue contains a 'None' sentinel) frame = task_queue.get(True) if frame is None: break # Make grayscale version of image image = Image(frame) gray_image = image.to_grayscale() # If we have an existing partial plate, merge the new plate with it and only try to read the # barcodes which haven't already been read. This significantly increases efficiency because # barcode read is expensive. scan_result = scanner.scan_next_frame(gray_image) if options.console_frame.value(): scan_result.print_summary() if scan_result.success(): # Record the time so we can see how long its been since we last saw a plate last_plate_time = time.time() plate = scan_result.plate() if scan_result.already_scanned(): overlay_queue.put(TextOverlay(SCANNED_TAG, Color.Green())) elif scan_result.any_valid_barcodes(): overlay_queue.put(PlateOverlay(plate, options)) _plate_beep(plate, options) if scan_result.any_new_barcodes(): result_queue.put((plate, image)) else: time_since_plate = time.time() - last_plate_time if time_since_plate > NO_PUCK_TIME: overlay_queue.put(TextOverlay(scan_result.error(), Color.Red()))
def test_paste_when_location_is_outside_image_x_off(self): image_target = Image.blank(10, 10) image_src = Image(img=np.full((5, 5, 3), 2, np.uint8)) image_target.paste(image_src, 20, 0) a = np.sum(image_target.img) self.assertEquals(a, 0)
def _do_threshold(gray_image, block_size, c): """ Perform an adaptive threshold operation on the image. """ raw = gray_image.img thresh = cv2.adaptiveThreshold(raw, 255.0, cv2.ADAPTIVE_THRESH_MEAN_C, cv2.THRESH_BINARY, block_size, c) return Image(thresh)