def run(input_image_path: Path, output_directory: Path, identfier: str, num_lines: int, start_column: int = 0, show: bool = True): """""" trace_image = read_image(input_image_path) if len(trace_image.image.shape) > 2: trace_image.bgr_to_gray() trace_image = trace_image.image new_image = np.zeros(trace_image.shape) # point_list = [] x_list: tp.List[float] = [] y_list: tp.List[tp.List[float]] = [] for i, new_y in find_datapoints(image=trace_image, start_column=start_column, num_lines=num_lines, show=show): y_list.append(new_y) for y in new_y: new_image[int(new_y), i] = 255 x_list.append(i) y_list.append(int(new_y)) x_arr = np.asarray(x_list, dtype=np.float_) y_arr = np.asarray(y_list, dtype=np.float_) y_arr -= y_arr.mean() # mean zero y_arr *= -1 # flip if show: fig, (ax1, ax2) = plt.subplots(2) ax1.imshow(new_image) ax2.plot(x_arr, y_arr) plt.show() out_array = np.zeros((x_arr.size, 2)) out_array[:, 0] = x_arr out_array[:, 1] = y_arr output_directory.mkdir(exist_ok=True, parents=True) np.save(output_directory / f"{identfier}_trace", out_array)
def run(input_image_path: Path, output_directory: Path, name: str, *, debug: bool = False, compute_scale: bool = True, kernel_length: int = 9, num_iterations: int = 4, blur_size: int = 9, thresh_val: float = 175, dx: float = 2.5e-4, dy: float = 2.5e-4): image = read_image(input_image_path) image_list = split_image(image, kernel_length=kernel_length, blur_kernel_size=blur_size, threshold_value=thresh_val, num_iterations=num_iterations) if debug: debug_path = get_debug_path("split_image") save(image.image, debug_path, "input") scale_dict = {} for i, image in enumerate(image_list): image.bgr_to_gray() # TODO: debug each split rectangles = markers(image, kernel_length=kernel_length, blur_kernel_size=blur_size, threshold_value=thresh_val, num_iterations=num_iterations, debug=debug) if len(rectangles) >= 2: axis, scale = get_axis(image, rectangles) image.set_axis(axis) image.set_scale(scale) image.reset_image() resample(image, step_x=dx, step_y=dy) image.checkpoint() else: print(f"skipping split {i}") continue if compute_scale: # Recompute scale image.bgr_to_gray() rectangles = markers(image, kernel_length=kernel_length, blur_kernel_size=blur_size, threshold_value=thresh_val, num_iterations=num_iterations, debug=debug) if len(rectangles) < 2: logger.info( f"Cannot compute scale of split {i} -- using average of previous scales" ) if len(scale_dict) > 0: scale_dict[i] = sum(scale_dict.values()) / len(scale_dict) else: axis, scale = get_axis(image, rectangles) image.set_axis(axis) image.set_scale(scale) scale_dict[i] = scale output_directory.mkdir(exist_ok=True, parents=True) for i, image in enumerate(image_list): logger.info(f"Scale: {image.scale}") save_image(output_directory / f"{name}_split{i}.png", image) # dump_image(output_directory / f"split{i}_{identifier}.pkl", image) with open(output_directory / "scales.txt", "a") as output_handle: for case, scale in scale_dict.items(): output_handle.write(f"Case: {case}, Scale: {scale}\n")
def run( input_image_path: Path, output_directory: Path, identifier: str, scale: float = None, start_column: int = 0, show: bool = False, show_step: bool = False ): """Digitise *a single* eeg trace. input_image_path: Path to segmented EEG trace. output_directory: Path to directory where time series is stored. identifier: 'Name' of the trace. start_column: The column from which to start digitising. show: Display peaks selection for each column and the final result. """ trace_image = read_image(input_image_path) # Make sure image is grayscale if len(trace_image.image.shape) > 2: trace_image.bgr_to_gray() trace_image = trace_image.image new_image = np.zeros(trace_image.shape) x_list: tp.List[int] = [] y_list: tp.List[float] = [] for i, new_y in find_datapoints( image=trace_image, start_column=start_column, show=show_step ): y_list.append(new_y) x_list.append(i) try: new_image[int(new_y), i] = 255 # For quality control except IndexError as e: logging.info(f"{new_y}, {i}") logging.info(f"{new_image.shape}") import sys; sys.exit(1) x_arr = np.asarray(x_list, dtype=np.float_) y_arr = np.asarray(y_list, dtype=np.float_) # TODO: Why do I do this? This can be done later I think # y_arr -= y_arr.mean() # mean zero # y_arr *= -1 # flip # TODO: Save image for later review fig, (ax1, ax2) = plt.subplots(2) ax2.plot(x_arr, y_arr) output_directory.mkdir(exist_ok=True, parents=True) # fig.savefig(output_directory / f"trace{identifier}_imageQC.png") if show: plt.show() plt.close(fig) out_array = np.zeros((x_arr.size, 2)) out_array[:, 0] = x_arr out_array[:, 1] = y_arr if scale is not None: out_array *= 15/scale output_directory.mkdir(exist_ok=True, parents=True) np.save(output_directory / f"trace{identifier}", out_array)
def run( *, input_image_path: Path, output_directory: Path, identifier: str, scale: float = None, debug: bool = False, ): """Segment the contours from a black and white image and save the segmented lines.""" image = read_image(input_image_path) if debug: debug_path = get_debug_path("remove_background") save(image.image, debug_path, "input") image.bgr_to_gray() if debug: save(image.iamge, debug_path, "match_contours") contours = get_contours(image=image) features = match_contours(matcher=get_graph_matcher(), contours=contours) if debug: debug_path = get_debug_path("extract_contours_bw") save(image.image, debug_path, "input") output_directory.mkdir(exist_ok=True, parents=True) for i, c in enumerate(features): tmp_image = image.copy_image() filter_contours(image_array=tmp_image, contours=[c]) clipped_contour = tmp_image[~np.all(tmp_image == 0, axis=1)] save_image(output_directory / f"{identifier}_trace{i}.png", Image(clipped_contour)) ############################ ### Make annotated image ### ############################ fig, ax = plt.subplots(1, figsize=(15, 10), dpi=500) tmp_image = image.image_orig color_iterator = itertools.cycle(mtableau_brg()) for i, c in enumerate(features): color = next(color_iterator) tmp_image = cv2.drawContours(tmp_image, features, i, color_to_256_RGB(color), cv2.FILLED) polygon = Polygon(c.reshape(-1, 2)) x0, y0, x1, y1 = polygon.bounds ann = ax.annotate( f"Contour {i}", xy=(x1, y1), # (x0, y1) xycoords="data", xytext=(0, 35), textcoords="offset points", size=10, bbox=dict( boxstyle="round", fc=color # normalised color )) ax.imshow(tmp_image) ax.set_title("A digitised paper strip") if scale is not None: # multiply by distance between black sqaures? ax.set_xticklabels( ["{:.1f} cm".format(15 * i / scale) for i in ax.get_xticks()]) ax.set_yticklabels( ["{:.1f} cm".format(15 * i / scale) for i in ax.get_yticks()]) ax.set_ylabel("Voltage") ax.set_xlabel("Time") fig.savefig(output_directory / f"{identifier}_annotated.png", dpi=500)
plot_img = image.image.copy() plot_img[:, 399:402] = 0 print(plot_img) ax1.imshow(plot_img) values = image.image[:, 400] x = np.linspace(0, 1, values.size) t, c, k = interpolate.splrep(x, values, s=10, k=3) xnew = np.linspace(0, 1, 10000) splev = interpolate.BSpline(t, c, k) ax2.plot(x, values, "--", alpha=0.75) ax2.plot(xnew, splev(xnew)) plt.show() if __name__ == "__main__": filepath = Path("../data/scan4.png") image = read_image(filepath) # plt.imshow(image.image) # image.show() preprocess(image) markers(image) graphs(image) # extract(image)
def run( input_image_path: Path, output_directory: Path, identifier: str, scale: float = None, debug:bool ): """Remove the background, segment the contours and save the segmented lines as pngs.""" image = read_image(input_image_path) if debug: debug_path = get_debug_path("prepare_lines") save(np.ndarray, debug_path, "input") image.bgr_to_gray() image.checkpoint("resampled") remove_background(image=image, smooth_kernel_size=3) features = extract_contours(image=image, blur_kernel_size=3) if debug: save(np.ndarray, debug_path, "remove_background") image.invert() image.reset_image("resampled") image.invert() output_directory.mkdir(exist_ok=True, parents=True) for i, c in enumerate(features): tmp_image = image.copy_image() filter_contours(image_array=tmp_image, contours=[c]) clipped_contour = tmp_image[~np.all(tmp_image == 0, axis=1)] save_image(output_directory / f"{identifier}_trace{i}.png", Image(clipped_contour)) ################## ### Make image ### ################## tmp_image = np.ones((*image.image.shape, 3), dtype=np.uint8) tmp_image[:] = (255, 255, 255) # White fig, ax = plt.subplots(1) ax.imshow(tmp_image) color_iterator = itertools.cycle(mtableau_brg()) for i, c in enumerate(features): color = next(color_iterator) tmp_image = cv2.drawContours(tmp_image, features, i, color_to_256_RGB(color), cv2.FILLED) polygon = Polygon(c.reshape(-1, 2)) x0, y0, x1, y1 = polygon.bounds tmp_image2 = np.zeros(tuple(map(math.ceil, (y1, x1))), dtype=np.uint8) tmp_image2 = cv2.drawContours(tmp_image2, features, i, 255, cv2.FILLED) ann = ax.annotate( f"Contour {i}", xy=(x0, y1), xycoords="data", xytext=(0, 35), textcoords="offset points", size=10, bbox=dict( boxstyle="round", fc=color # normalised color ) ) ax.imshow(tmp_image) ax.set_title("A digitised paper strip") if scale is not None: # multiply by distance between black sqaures? ax.set_xticklabels(["{:.1f} cm".format(15*i/scale) for i in ax.get_xticks()]) ax.set_yticklabels(["{:.1f} cm".format(15*i/scale) for i in ax.get_yticks()]) ax.set_ylabel("Voltage") ax.set_xlabel("Time") fig.savefig(output_directory / f"{identifier}_annotated.png")
def run(*, input_image_path: Path, output_directory: Path, identifier: str, smooth_kernel_size: int = 3, threshold_value: int = 100, background_kernel_size=5, scale: float = None, debug: bool = False, blue_color_filter: tp.Sequence[int] = None, red_color_filter: tp.Sequence[int] = None, horisontal_kernel_length: int = None, x_interval: tp.Tuple[int, int] = None): """Remove the background, segment the contours and save the segmented lines as pngs.""" image = read_image(input_image_path) if debug: debug_path = get_debug_path("prepare_lines") save(image.image, debug_path, "input") if blue_color_filter is not None: lower = tuple(map(int, blue_color_filter[:3])) upper = tuple(map(int, blue_color_filter[3:])) print(lower, upper) blue_mask = cv2.inRange(image.image, lower, upper) image.image[blue_mask == 255] = 255 if red_color_filter is not None: lower = tuple(map(int, red_color_filter[:3])) upper = tuple(map(int, red_color_filter[3:])) red_mask = cv2.inRange(image.image, lower, upper) image.image[red_mask == 255] = 255 image.invert() image.bgr_to_gray() image.checkpoint("resampled") if horisontal_kernel_length is not None: horisontal_kernel = cv2.getStructuringElement( cv2.MORPH_RECT, (horisontal_kernel_length, 1)) x0, x1 = x_interval if x1 == -1: # Set -1 to be inclusive last element x1 = None detected_lines = cv2.morphologyEx(image.image[x0:x1, :], cv2.MORPH_OPEN, horisontal_kernel, iterations=1) # findContours returns (contours, hierarchy) contours = cv2.findContours(detected_lines, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)[0] for c in contours: cv2.drawContours(image.image[x0:x1, :], [c], -1, 0, -10) remove_background(image=image, smooth_kernel_size=smooth_kernel_size, threshold_value=threshold_value, background_kernel_size=background_kernel_size, debug=debug) image.checkpoint("remove_background") if debug: save(image.image, debug_path, "match_contours") contours = get_contours(image=image) features = match_contours(matcher=get_graph_matcher(), contours=contours) if features is None: # In case of a blank image return quality_control_image = image.draw(features, show=False) cv2.imwrite(str(output_directory / f"QC_{identifier}.png"), quality_control_image) if debug: save(image.image, debug_path, "remove_background") # image.reset_image("resampled") image.reset_image("remove_background") # image.invert() output_directory.mkdir(exist_ok=True, parents=True) for i, c in enumerate(features): tmp_image = image.copy_image() filter_contours(image_array=tmp_image, contours=[c]) clipped_contour = tmp_image[~np.all(tmp_image == 0, axis=1)] save_image(output_directory / f"{identifier}_trace{i}.png", Image(clipped_contour)) ############################ ### Make annotated image ### ############################ fig, ax = plt.subplots(1, figsize=(15, 10), dpi=500) tmp_image = image.image_orig color_iterator = itertools.cycle(mtableau_brg()) for i, c in enumerate(features): color = next(color_iterator) tmp_image = cv2.drawContours(tmp_image, features, i, color_to_256_RGB(color), cv2.FILLED) polygon = Polygon(c.reshape(-1, 2)) x0, y0, x1, y1 = polygon.bounds ann = ax.annotate( f"Contour {i}", xy=((x0 + x1) / 2, y1), # (x0, y1) size=5, color=color, arrowprops=dict(arrowstyle='->', connectionstyle="arc3,rad=-0.5", color=color)) ax.imshow(tmp_image) ax.set_title("A digitised paper strip") if scale is not None: # multiply by distance between black sqaures? ax.set_xticklabels( ["{:.1f} cm".format(15 * i / scale) for i in ax.get_xticks()]) ax.set_yticklabels( ["{:.1f} cm".format(15 * i / scale) for i in ax.get_yticks()]) ax.set_ylabel("Voltage") ax.set_xlabel("Time") fig.savefig(output_directory / f"{identifier}_annotated.png", dpi=500)