def locate_thumbnail(thumbnail_filename, source_filename, display=False, save_visualization=False, save_reconstruction=False, reconstruction_format="jpg"): thumbnail_basename, thumbnail_image = open_image(thumbnail_filename) source_basename, source_image = open_image(source_filename) logging.info("Attempting to locate %s within %s", thumbnail_filename, source_filename) kp_pairs = match_images(thumbnail_image, source_image) if len(kp_pairs) >= 4: title = "Found %d matches" % len(kp_pairs) logging.info(title) H, mask = find_homography(kp_pairs) new_thumbnail, corners, rotation = reconstruct_thumbnail(thumbnail_image, source_image, kp_pairs, H) print(json.dumps({ "master": { "source": source_filename, "dimensions": { "height": source_image.shape[0], "width": source_image.shape[1], } }, "thumbnail": { "source": thumbnail_filename, "dimensions": { "height": thumbnail_image.shape[0], "width": thumbnail_image.shape[1], } }, "bounding_box": { "height": corners[0][1] - corners[0][0], "width": corners[1][1] - corners[1][0], "x": corners[1][0], "y": corners[0][0], }, "rotation_degrees": rotation })) if save_reconstruction: new_filename = "%s.reconstructed.%s" % (thumbnail_basename, reconstruction_format) cv2.imwrite(new_filename, new_thumbnail) logging.info("Saved reconstructed thumbnail %s", new_filename) else: logging.warning("Found only %d matches; skipping reconstruction", len(kp_pairs)) new_thumbnail = corners = H = mask = None if display or save_visualization: vis_image = visualize_matches(source_image, thumbnail_image, new_thumbnail, corners, kp_pairs, mask) if save_visualization: vis_filename = "%s.visualized%s" % os.path.splitext(thumbnail_filename) cv2.imwrite(vis_filename, vis_image) logging.info("Saved match visualization %s", vis_filename) if display: cv2.imshow(title, vis_image) cv2.waitKey() cv2.destroyAllWindows()
def display_images(extractor, files): window_name = "Controls" images = [] for f in files: print("Loading %s" % f) try: images.append(open_image(f)) except Exception as exc: print(exc, file=sys.stderr) continue def update_display(*args): extractor.canny_threshold = cv2.getTrackbarPos("Canny Threshold", window_name) extractor.erosion_element = cv2.getTrackbarPos("Erosion Element", window_name) extractor.erosion_size = cv2.getTrackbarPos("Erosion Size", window_name) extractor.dilation_element = cv2.getTrackbarPos( "Dilation Element", window_name) extractor.dilation_size = cv2.getTrackbarPos("Dilation Size", window_name) # TODO: tame configuration hideousness: labels = [ "Canny Threshold: %s" % extractor.canny_threshold, "Erosion Element: %s" % FigureExtractor.MORPH_TYPE_KEYS[extractor.erosion_element], "Erosion Size: %s" % extractor.erosion_size, "Dilation Element: %s" % FigureExtractor.MORPH_TYPE_KEYS[extractor.dilation_element], "Dilation Size: %s" % extractor.dilation_size ] labels_img = numpy.zeros((30 * (len(labels) + 1), 600, 3), numpy.uint8) for i, label in enumerate(labels, 1): cv2.putText(labels_img, label, (0, i * 30), cv2.FONT_HERSHEY_SIMPLEX, 1.0, (192, 192, 192)) cv2.imshow("Controls", labels_img) print("Settings:\n\t", "\n\t".join(labels)) print() for name, image in images: filtered_image = extractor.filter_image(image) contours, hierarchy = extractor.find_contours(filtered_image) # The filtered image will be heavily processed down to 1-bit depth. We'll convert it to RGB # so we can display the effects of the filters with full-color overlays for detected figures: output = cv2.cvtColor(filtered_image, cv2.COLOR_GRAY2RGB) print("Processing %s" % name) for bbox in extractor.get_bounding_boxes_from_contours( contours, filtered_image): print("\tExtract: %s" % bbox) output[bbox.image_slice] = image[bbox.image_slice] cv2.polylines(output, bbox.poly, True, (32, 192, 32), thickness=3) cv2.drawContours(output, contours, bbox.contour_index, (32, 192, 32), hierarchy=hierarchy, maxLevel=0) cv2.rectangle(output, (bbox.x1, bbox.y1), (bbox.x2, bbox.y2), color=(32, 192, 192)) cv2.imshow(name, output) cv2.namedWindow(window_name) cv2.resizeWindow(window_name, 600, 340) cv2.createTrackbar("Canny Threshold", window_name, extractor.canny_threshold, 255, update_display) cv2.createTrackbar("Erosion Element", window_name, extractor.erosion_element, len(extractor.MORPH_TYPES) - 1, update_display) cv2.createTrackbar("Erosion Size", window_name, extractor.erosion_size, 64, update_display) cv2.createTrackbar("Dilation Element", window_name, extractor.dilation_element, len(extractor.MORPH_TYPES) - 1, update_display) cv2.createTrackbar("Dilation Size", window_name, extractor.dilation_size, 64, update_display) update_display() if args.interactive: while cv2.waitKey() not in (13, 27): continue cv2.destroyAllWindows()
except ImportError: import pdb # FIXME: we should have a way to enumerate this from FigureExtractor and feed argparse that way: param_names = [action.dest for action in extraction_params._group_actions] params = {k: v for (k, v) in args._get_kwargs() if k in param_names} try: extractor = FigureExtractor(**params) if args.interactive: display_images(extractor, args.files) else: for f in args.files: try: base_name, source_image = open_image(f) except Exception as exc: print(exc, file=sys.stderr) continue output_base = os.path.join(output_dir, base_name) print("Processing %s" % f) boxes = [] for i, bbox in enumerate(extractor.find_figures(source_image), 1): extracted = source_image[bbox.image_slice] extract_filename = os.path.join( output_dir, "%s-%d.jpg" % (output_base, i))
def display_images(extractor, files): window_name = "Controls" images = [] for f in files: print "Loading %s" % f try: images.append(open_image(f)) except StandardError as exc: print >>sys.stderr, exc continue def update_display(*args): extractor.canny_threshold = cv2.getTrackbarPos("Canny Threshold", window_name) extractor.erosion_element = cv2.getTrackbarPos("Erosion Element", window_name) extractor.erosion_size = cv2.getTrackbarPos("Erosion Size", window_name) extractor.dilation_element = cv2.getTrackbarPos("Dilation Element", window_name) extractor.dilation_size = cv2.getTrackbarPos("Dilation Size", window_name) # TODO: tame configuration hideousness: labels = ["Canny Threshold: %s" % extractor.canny_threshold, "Erosion Element: %s" % FigureExtractor.MORPH_TYPE_KEYS[extractor.erosion_element], "Erosion Size: %s" % extractor.erosion_size, "Dilation Element: %s" % FigureExtractor.MORPH_TYPE_KEYS[extractor.dilation_element], "Dilation Size: %s" % extractor.dilation_size] labels_img = numpy.zeros((30 * (len(labels) + 1), 600, 3), numpy.uint8) for i, label in enumerate(labels, 1): cv2.putText(labels_img, label, (0, i * 30), cv2.FONT_HERSHEY_SIMPLEX, 1.0, (192, 192, 192)) cv2.imshow("Controls", labels_img) print "Settings:\n\t", "\n\t".join(labels) print for name, image in images: filtered_image = extractor.filter_image(image) contours, hierarchy = extractor.find_contours(filtered_image) # The filtered image will be heavily processed down to 1-bit depth. We'll convert it to RGB # so we can display the effects of the filters with full-color overlays for detected figures: output = cv2.cvtColor(filtered_image, cv2.COLOR_GRAY2RGB) print "Processing %s" % name for bbox in extractor.get_bounding_boxes_from_contours(contours, filtered_image): print "\tExtract: %s" % bbox output[bbox.image_slice] = image[bbox.image_slice] cv2.polylines(output, bbox.poly, True, (32, 192, 32), thickness=3) cv2.drawContours(output, contours, bbox.contour_index, (32, 192, 32), hierarchy=hierarchy, maxLevel=0) cv2.rectangle(output, (bbox.x1, bbox.y1), (bbox.x2, bbox.y2), color=(32, 192, 192)) cv2.imshow(name, output) cv2.namedWindow(window_name) cv2.resizeWindow(window_name, 600, 340) cv2.createTrackbar("Canny Threshold", window_name, extractor.canny_threshold, 255, update_display) cv2.createTrackbar("Erosion Element", window_name, extractor.erosion_element, len(extractor.MORPH_TYPES) - 1, update_display) cv2.createTrackbar("Erosion Size", window_name, extractor.erosion_size, 64, update_display) cv2.createTrackbar("Dilation Element", window_name, extractor.dilation_element, len(extractor.MORPH_TYPES) - 1, update_display) cv2.createTrackbar("Dilation Size", window_name, extractor.dilation_size, 64, update_display) update_display() if args.interactive: while cv2.waitKey() not in (13, 27): continue cv2.destroyAllWindows()
import pdb # FIXME: we should have a way to enumerate this from FigureExtractor and feed argparse that way: param_names = [action.dest for action in extraction_params._group_actions] params = {k: v for (k, v) in args._get_kwargs() if k in param_names} try: extractor = FigureExtractor(**params) print("MADE EXTRACTOR") if args.interactive: display_images(extractor, args.files) else: for f in args.files: try: base_name, source_image = open_image(f) except StandardError as exc: print >>sys.stderr, exc continue output_base = os.path.join(output_dir, base_name) print "Processing %s" % f boxes = [] for i, bbox in enumerate(extractor.find_figures(source_image), 1): extracted = source_image[bbox.image_slice] extract_filename = os.path.join(output_dir, "%s-%d.jpg" % (output_base, i)) print "\tSaving %s" % extract_filename cv2.imwrite(extract_filename, extracted)
def locate_thumbnail(thumbnail_filename, source_filename, display=False, save_visualization=False, save_reconstruction=False, reconstruction_format="jpg", max_aspect_ratio_delta=0.1, match_aspect_ratio=False, minimum_matches=10, json_output_filename=None, max_master_edge=4096, max_output_edge=2048): thumbnail_basename, thumbnail_image = open_image(thumbnail_filename) source_basename, source_image = open_image(source_filename) if (((source_image.shape[0] <= thumbnail_image.shape[0]) or (source_image.shape[1] <= thumbnail_image.shape[1]))): raise RuntimeError("Master file wasn't larger than the thumbnail: %r vs %r" % (source_image.shape, thumbnail_image.shape)) logging.info("Attempting to locate %s within %s", thumbnail_filename, source_filename) full_source_image = source_image if max_master_edge and any(i for i in source_image.shape if i > max_master_edge): logging.info("Resizing master to fit within %d pixels", max_master_edge) source_image = fit_image_within(source_image, max_master_edge, max_master_edge) logging.info('Finding common features') kp_pairs = match_images(thumbnail_image, source_image) if len(kp_pairs) >= minimum_matches: title = "Found %d matches" % len(kp_pairs) logging.info(title) H, mask = find_homography(kp_pairs) corners = get_scaled_corners(thumbnail_image, source_image, full_source_image, kp_pairs, H) new_thumbnail, corners, rotation = reconstruct_thumbnail(thumbnail_image, full_source_image, corners, match_aspect_ratio=match_aspect_ratio, max_aspect_ratio_delta=max_aspect_ratio_delta) if json_output_filename: with open(json_output_filename, mode='wb') as json_file: json.dump({ "master": { "source": source_filename, "dimensions": { "height": full_source_image.shape[0], "width": full_source_image.shape[1], } }, "thumbnail": { "source": thumbnail_filename, "dimensions": { "height": thumbnail_image.shape[0], "width": thumbnail_image.shape[1], } }, "bounding_box": { "height": corners[0][1] - corners[0][0], "width": corners[1][1] - corners[1][0], "x": corners[1][0], "y": corners[0][0], }, "rotation_degrees": rotation }, json_file, indent=4) if save_reconstruction: new_filename = "%s.reconstructed.%s" % (thumbnail_basename, reconstruction_format) new_thumb_img = fit_image_within(new_thumbnail, max_output_edge, max_output_edge) cv2.imwrite(new_filename, new_thumb_img) logging.info("Saved reconstructed %s thumbnail %s", new_thumb_img.shape[:2], new_filename) else: logging.warning("Found only %d matches; skipping reconstruction", len(kp_pairs)) title = "MATCH FAILED: %d pairs" % len(kp_pairs) new_thumbnail = corners = H = mask = None if display or save_visualization: vis_image = visualize_matches(source_image, thumbnail_image, new_thumbnail, corners, kp_pairs, mask) if save_visualization: vis_filename = "%s.visualized%s" % os.path.splitext(thumbnail_filename) cv2.imwrite(vis_filename, vis_image) logging.info("Saved match visualization %s", vis_filename) if display: # This may or may not exist depending on whether OpenCV was compiled using the QT backend: window_flags = getattr(cv, 'CV_WINDOW_NORMAL', cv.CV_WINDOW_AUTOSIZE) window_title = '%s - %s' % (thumbnail_basename, title) cv2.namedWindow(window_title, flags=window_flags) cv2.imshow(window_title, vis_image) cv2.waitKey() cv2.destroyAllWindows()
def locate_thumbnail(thumbnail_filename, source_filename, display=False, save_visualization=False, save_reconstruction=False, reconstruction_format="jpg", max_aspect_ratio_delta=0.1, match_aspect_ratio=False, minimum_matches=10, json_output_filename=None, max_master_edge=4096, max_output_edge=2048): thumbnail_basename, thumbnail_image = open_image(thumbnail_filename) source_basename, source_image = open_image(source_filename) if (((source_image.shape[0] <= thumbnail_image.shape[0]) or (source_image.shape[1] <= thumbnail_image.shape[1]))): raise RuntimeError( "Master file wasn't larger than the thumbnail: %r vs %r" % (source_image.shape, thumbnail_image.shape)) logging.info("Attempting to locate %s within %s", thumbnail_filename, source_filename) full_source_image = source_image if max_master_edge and any( i for i in source_image.shape if i > max_master_edge): logging.info("Resizing master to fit within %d pixels", max_master_edge) source_image = fit_image_within(source_image, max_master_edge, max_master_edge) logging.info('Finding common features') kp_pairs = match_images(thumbnail_image, source_image) if len(kp_pairs) >= minimum_matches: title = "Found %d matches" % len(kp_pairs) logging.info(title) H, mask = find_homography(kp_pairs) corners = get_scaled_corners(thumbnail_image, source_image, full_source_image, kp_pairs, H) new_thumbnail, corners, rotation = reconstruct_thumbnail( thumbnail_image, full_source_image, corners, match_aspect_ratio=match_aspect_ratio, max_aspect_ratio_delta=max_aspect_ratio_delta) if json_output_filename: with open(json_output_filename, mode='wb') as json_file: json.dump( { "master": { "source": source_filename, "dimensions": { "height": full_source_image.shape[0], "width": full_source_image.shape[1], } }, "thumbnail": { "source": thumbnail_filename, "dimensions": { "height": thumbnail_image.shape[0], "width": thumbnail_image.shape[1], } }, "bounding_box": { "height": corners[0][1] - corners[0][0], "width": corners[1][1] - corners[1][0], "x": corners[1][0], "y": corners[0][0], }, "rotation_degrees": rotation }, json_file, indent=4) if save_reconstruction: new_filename = "%s.reconstructed.%s" % (thumbnail_basename, reconstruction_format) new_thumb_img = fit_image_within(new_thumbnail, max_output_edge, max_output_edge) cv2.imwrite(new_filename, new_thumb_img) logging.info("Saved reconstructed %s thumbnail %s", new_thumb_img.shape[:2], new_filename) else: logging.warning("Found only %d matches; skipping reconstruction", len(kp_pairs)) title = "MATCH FAILED: %d pairs" % len(kp_pairs) new_thumbnail = corners = H = mask = None if display or save_visualization: vis_image = visualize_matches(source_image, thumbnail_image, new_thumbnail, corners, kp_pairs, mask) if save_visualization: vis_filename = "%s.visualized%s" % os.path.splitext(thumbnail_filename) cv2.imwrite(vis_filename, vis_image) logging.info("Saved match visualization %s", vis_filename) if display: # This may or may not exist depending on whether OpenCV was compiled using the QT backend: window_flags = getattr(cv, 'CV_WINDOW_NORMAL', cv.CV_WINDOW_AUTOSIZE) window_title = '%s - %s' % (thumbnail_basename, title) cv2.namedWindow(window_title, flags=window_flags) cv2.imshow(window_title, vis_image) cv2.waitKey() cv2.destroyAllWindows()