def TRIANGLE_DEMO(): """ Draw a set of axes and a triangle. Perform a series of random transformations on the triangle and display the results. """ A = Point(143, 52) B = Point(17, 96.5) C = Point(0, 0) for i in range(10): # Create random transformation angle = random.random() * 2 * math.pi scale = random.random() * 3 delX = (random.random() - 0.5) * 200 delY = (random.random() - 0.5) * 200 translate = Point(delX, delY) transform = Transform(translate, angle, scale) # Transform the triangle A_ = transform.transform(A) B_ = transform.transform(B) C_ = transform.transform(C) # From the line A-B and the transformed line A'-B', determine what the transformation was # This should be the same as the original transformation trans_calc = Transform.line_mapping(A, B, A_, B_) print("Angle: {:.2f}; {:.2f}".format(rad_to_deg(angle), rad_to_deg(trans_calc.rot))) print("Trans: ({}); ({})".format(translate, trans_calc.trans)) print("Zoom: {:.2f}; {:.2f}".format(scale, trans_calc.zoom)) # Display on image image = Image.blank(1000, 800) image.draw_offset = IMG_CENTER draw_axes(image, 300) # Draw original triangle image.draw_line(A, B, Color.Red(), 5) image.draw_line(C, B, Color.Red(), 5) image.draw_line(A, C, Color.Red(), 5) #Draw transformed triangle image.draw_line(A_, B_, Color.Green()) image.draw_line(A_, C_, Color.Green()) image.draw_line(C_, B_, Color.Green()) # Check that the reverse transformation works properly A__ = transform.reverse(A_) B__ = transform.reverse(B_) C__ = transform.reverse(C_) # Draw the reverse transformation - this should overlap the origianl triangle image.draw_line(A__, B__, Color.Green(), 1) image.draw_line(A__, C__, Color.Green(), 1) image.draw_line(C__, B__, Color.Green(), 1) # Write the transformation on the image image.draw_text(transform.__str__(), Point(-450, 350), Color.White(), centered=False, scale=0.5, thickness=1) # Show the image image.popup()
def __init__(self, file): Config.__init__(self, file) add = self.add if IS_BUNDLED: default_store = "./store/" else: default_store = "../store/" self.color_ok = add(ColorConfigItem, "Read Color", Color.Green()) self.color_unreadable = add(ColorConfigItem, "Not Read Color", Color.Red()) self.color_empty = add(ColorConfigItem, "Empty Color", Color.Grey()) self.camera_number = add(IntConfigItem, "Camera Number", default=0) self.camera_width = add(IntConfigItem, "Camera Width", default=1920) self.camera_height = add(IntConfigItem, "Camera Height", default=1080) self.plate_type = add(EnumConfigItem, "Sample Plate Type", default=Geometry.UNIPUCK, extra_arg=Geometry.TYPES) self.barcode_size = add(EnumConfigItem, "Datamatrix Size", default=DataMatrix.DEFAULT_SIZE, extra_arg=DatamatrixSizeTable.valid_sizes()) self.scan_beep = add(BoolConfigItem, "Beep While Scanning", default=True) self.scan_clipboard = add(BoolConfigItem, "Results to Clipboard", default=True) self.image_puck = add(BoolConfigItem, "Draw Puck", default=True) self.image_pins = add(BoolConfigItem, "Draw Slot Highlights", default=True) self.image_crop = add(BoolConfigItem, "Crop to Puck", default=True) self.store_directory = add(DirectoryConfigItem, "Store Directory", default=default_store) self.store_capacity = add(IntConfigItem, "Results History Size", default=50) self.console_frame = add(BoolConfigItem, "Print Frame Summary", default=False) self.slot_images = add(BoolConfigItem, "Save Debug Images", default=False) self.slot_image_directory = add(DirectoryConfigItem, "Debug Directory", default="../debug-output/") self.initialize_from_file()
def CIRCLES_DEMO(): """ Draw a set of axes and a random set of circles. Perform a series of random transformations on the circles. """ # Create a set of random circles points = [] for i in range(10): X = (random.random()) * 200 Y = (random.random()) * 200 points.append(Point(X, Y)) for i in range(10): # Create random transformation angle = random.random() * 2 * math.pi scale = random.random() * 3 delX = (random.random() - 0.5) * 200 delY = (random.random() - 0.5) * 200 translate = Point(delX, delY) trs = Transform(translate, angle, scale) # Display on image image = Image.blank(1000, 800) image.draw_offset = IMG_CENTER draw_axes(image, 300) # Draw the circles and transformed circles on the image radius = 10 for p in points: circle = Circle(p, radius) trans_circle = Circle(trs.transform(p), radius * trs.zoom) image.draw_circle(circle, Color.Red()) image.draw_circle(trans_circle, Color.Blue()) # Write the transformation on the image image.draw_text(trs.__str__(), Point(-450, 350), Color.White(), centered=False, scale=0.5, thickness=1) # Show the image image.popup()
def __init__(self, file, file_manager): Config.__init__(self, file, file_manager) add = self.add if IS_BUNDLED: default_store = "./store/" else: default_store = "../store/" self.color_ok = add(ColorConfigItem, "Pin/Puck Read", Color.Green()) self.color_accept = add(ColorConfigItem, "Puck Partially Read", Color.Yellow()) self.color_unreadable = add(ColorConfigItem, "Pin/Puck Not Read", Color.Red()) self.color_empty = add(ColorConfigItem, "Pin Empty", Color.Grey()) self.plate_type = add(EnumConfigItem, "Sample Plate Type", default=Geometry.UNIPUCK, extra_arg=Geometry.TYPES) self.top_barcode_size = add( EnumConfigItem, "Datamatrix Size", default=DataMatrix.DEFAULT_SIZE, extra_arg=DatamatrixSizeTable.valid_sizes()) self.top_camera_timeout = add(IntConfigItem, "Scan Timeout", default=15, extra_arg="s") self.scan_beep = add(BoolConfigItem, "Beep While Scanning", default=True) self.scan_clipboard = add(BoolConfigItem, "Results to Clipboard", default=True) self.image_puck = add(BoolConfigItem, "Puck Highlight", default=True) self.image_pins = add(BoolConfigItem, "Slots Highlight", default=True) self.image_crop = add(BoolConfigItem, "Crop to Puck", default=True) self.store_directory = add(DirectoryConfigItem, "Store Directory", default=default_store) self.store_capacity = add(IntConfigItem, "Results History Size", default=50) self.console_frame = add(BoolConfigItem, "Print Frame Summary", default=False) self.slot_images = add(BoolConfigItem, "Save Debug Images", default=False) self.slot_image_directory = add(DirectoryConfigItem, "Debug Directory", default="../debug-output/") self.top_camera_number = add(IntConfigItem, "Top Camera Number", default=1) self.top_camera_width = add(IntConfigItem, "Top Camera Width", default=1600) self.top_camera_height = add(IntConfigItem, "Top Camera Height", default=1200) self.side_camera_number = add(IntConfigItem, "Side Camera Number", default=2) self.side_camera_width = add(IntConfigItem, "Side Camera Width", default=1600) self.side_camera_height = add(IntConfigItem, "Side Camera Height", default=1200) self.initialize_from_file()
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 draw_circles(img, circles, avg_barcode_radius): for circle in circles: img.draw_circle(circle, Color.Red(), thickness=3) text = "{:.2f}".format(circle.radius() / avg_barcode_radius) img.draw_text(text, circle.center(), Color.Red(), True, 1, 1)