def filter_binary_dilation(np_img, disk_size=5, iterations=1, output_type="uint8"): """ Dilate a binary object (bool, float, or uint8). Args: np_img: Binary image as a NumPy array. disk_size: Radius of the disk structuring element used for dilation. iterations: How many times to repeat the dilation. output_type: Type of array to return (bool, float, or uint8). Returns: NumPy array (bool, float, or uint8) where edges have been dilated. """ t = Time() if np_img.dtype == "uint8": np_img = np_img / 255 result = sc_morph.binary_dilation(np_img, sk_morphology.disk(disk_size), iterations=iterations) if output_type == "bool": pass elif output_type == "float": result = result.astype(float) else: result = result.astype("uint8") * 255 util.np_info(result, "Binary Dilation", t.elapsed()) return result
def filter_binary_closing(np_img, disk_size=3, iterations=1, output_type="uint8"): """ Close a binary object (bool, float, or uint8). Closing is a dilation followed by an erosion. Closing can be used to remove small holes. Args: np_img: Binary image as a NumPy array. disk_size: Radius of the disk structuring element used for closing. iterations: How many times to repeat. output_type: Type of array to return (bool, float, or uint8). Returns: NumPy array (bool, float, or uint8) following binary closing. """ t = Time() if np_img.dtype == "uint8": np_img = np_img / 255 result = sc_morph.binary_closing(np_img, sk_morphology.disk(disk_size), iterations=iterations) if output_type == "bool": pass elif output_type == "float": result = result.astype(float) else: result = result.astype("uint8") * 255 util.np_info(result, "Binary Closing", t.elapsed()) return result
def filter_remove_small_holes(np_img, min_size=3000, output_type="uint8"): """ Filter image to remove small holes less than a particular size. Args: np_img: Image as a NumPy array of type bool. min_size: Remove small holes below this size. output_type: Type of array to return (bool, float, or uint8). Returns: NumPy array (bool, float, or uint8). """ t = Time() rem_sm = sk_morphology.remove_small_holes(np_img, min_size=min_size) if output_type == "bool": pass elif output_type == "float": rem_sm = rem_sm.astype(float) else: rem_sm = rem_sm.astype("uint8") * 255 util.np_info(rem_sm, "Remove Small Holes", t.elapsed()) return rem_sm
def filter_red_pen(rgb, output_type="bool"): """ Create a mask to filter out red pen marks from a slide. Args: rgb: RGB image as a NumPy array. output_type: Type of array to return (bool, float, or uint8). Returns: NumPy array representing the mask. """ t = Time() result = filter_red(rgb, red_lower_thresh=150, green_upper_thresh=80, blue_upper_thresh=90) & \ filter_red(rgb, red_lower_thresh=110, green_upper_thresh=20, blue_upper_thresh=30) & \ filter_red(rgb, red_lower_thresh=185, green_upper_thresh=65, blue_upper_thresh=105) & \ filter_red(rgb, red_lower_thresh=195, green_upper_thresh=85, blue_upper_thresh=125) & \ filter_red(rgb, red_lower_thresh=220, green_upper_thresh=115, blue_upper_thresh=145) & \ filter_red(rgb, red_lower_thresh=125, green_upper_thresh=40, blue_upper_thresh=70) & \ filter_red(rgb, red_lower_thresh=200, green_upper_thresh=120, blue_upper_thresh=150) & \ filter_red(rgb, red_lower_thresh=100, green_upper_thresh=50, blue_upper_thresh=65) & \ filter_red(rgb, red_lower_thresh=85, green_upper_thresh=25, blue_upper_thresh=45) if output_type == "bool": pass elif output_type == "float": result = result.astype(float) else: result = result.astype("uint8") * 255 util.np_info(result, "Filter Red Pen", t.elapsed()) return result
def filter_blue(rgb, red_upper_thresh, green_upper_thresh, blue_lower_thresh, output_type="bool", display_np_info=False): """ Create a mask to filter out blueish colors, where the mask is based on a pixel being below a red channel threshold value, below a green channel threshold value, and above a blue channel threshold value. Args: rgb: RGB image as a NumPy array. red_upper_thresh: Red channel upper threshold value. green_upper_thresh: Green channel upper threshold value. blue_lower_thresh: Blue channel lower threshold value. output_type: Type of array to return (bool, float, or uint8). display_np_info: If True, display NumPy array info and filter time. Returns: NumPy array representing the mask. """ if display_np_info: t = Time() r = rgb[:, :, 0] < red_upper_thresh g = rgb[:, :, 1] < green_upper_thresh b = rgb[:, :, 2] > blue_lower_thresh result = ~(r & g & b) if output_type == "bool": pass elif output_type == "float": result = result.astype(float) else: result = result.astype("uint8") * 255 if display_np_info: util.np_info(result, "Filter Blue", t.elapsed()) return result
def filter_blue_pen(rgb, output_type="bool"): """ Create a mask to filter out blue pen marks from a slide. Args: rgb: RGB image as a NumPy array. output_type: Type of array to return (bool, float, or uint8). Returns: NumPy array representing the mask. """ t = Time() result = filter_blue(rgb, red_upper_thresh=60, green_upper_thresh=120, blue_lower_thresh=190) & \ filter_blue(rgb, red_upper_thresh=120, green_upper_thresh=170, blue_lower_thresh=200) & \ filter_blue(rgb, red_upper_thresh=175, green_upper_thresh=210, blue_lower_thresh=230) & \ filter_blue(rgb, red_upper_thresh=145, green_upper_thresh=180, blue_lower_thresh=210) & \ filter_blue(rgb, red_upper_thresh=37, green_upper_thresh=95, blue_lower_thresh=160) & \ filter_blue(rgb, red_upper_thresh=30, green_upper_thresh=65, blue_lower_thresh=130) & \ filter_blue(rgb, red_upper_thresh=130, green_upper_thresh=155, blue_lower_thresh=180) & \ filter_blue(rgb, red_upper_thresh=40, green_upper_thresh=35, blue_lower_thresh=85) & \ filter_blue(rgb, red_upper_thresh=30, green_upper_thresh=20, blue_lower_thresh=65) & \ filter_blue(rgb, red_upper_thresh=90, green_upper_thresh=90, blue_lower_thresh=140) & \ filter_blue(rgb, red_upper_thresh=60, green_upper_thresh=60, blue_lower_thresh=120) & \ filter_blue(rgb, red_upper_thresh=110, green_upper_thresh=110, blue_lower_thresh=175) if output_type == "bool": pass elif output_type == "float": result = result.astype(float) else: result = result.astype("uint8") * 255 util.np_info(result, "Filter Blue Pen", t.elapsed()) return result
def filter_grays(rgb, tolerance=15, output_type="bool"): """ Create a mask to filter out pixels where the red, green, and blue channel values are similar. Args: np_img: RGB image as a NumPy array. tolerance: Tolerance value to determine how similar the values must be in order to be filtered out output_type: Type of array to return (bool, float, or uint8). Returns: NumPy array representing a mask where pixels with similar red, green, and blue values have been masked out. """ t = Time() (h, w, c) = rgb.shape rgb = rgb.astype(np.int) rg_diff = abs(rgb[:, :, 0] - rgb[:, :, 1]) <= tolerance rb_diff = abs(rgb[:, :, 0] - rgb[:, :, 2]) <= tolerance gb_diff = abs(rgb[:, :, 1] - rgb[:, :, 2]) <= tolerance result = ~(rg_diff & rb_diff & gb_diff) if output_type == "bool": pass elif output_type == "float": result = result.astype(float) else: result = result.astype("uint8") * 255 util.np_info(result, "Filter Grays", t.elapsed()) return result
def filter_local_equalization(np_img, disk_size=50): """ Filter image (gray) using local equalization, which uses local histograms based on the disk structuring element. Args: np_img: Image as a NumPy array. disk_size: Radius of the disk structuring element used for the local histograms Returns: NumPy array with contrast enhanced using local equalization. """ t = Time() local_equ = sk_filters.rank.equalize(np_img, selem=sk_morphology.disk(disk_size)) util.np_info(local_equ, "Local Equalization", t.elapsed()) return local_equ
def filter_rgb_to_hsv(np_img, display_np_info=True): """ Filter RGB channels to HSV (Hue, Saturation, Value). Args: np_img: RGB image as a NumPy array. display_np_info: If True, display NumPy array info and filter time. Returns: Image as NumPy array in HSV representation. """ if display_np_info: t = Time() hsv = sk_color.rgb2hsv(np_img) if display_np_info: util.np_info(hsv, "RGB to HSV", t.elapsed()) return hsv
def filter_contrast_stretch(np_img, low=40, high=60): """ Filter image (gray or RGB) using contrast stretching to increase contrast in image based on the intensities in a specified range. Args: np_img: Image as a NumPy array (gray or RGB). low: Range low value (0 to 255). high: Range high value (0 to 255). Returns: Image as NumPy array with contrast enhanced. """ t = Time() low_p, high_p = np.percentile(np_img, (low * 100 / 255, high * 100 / 255)) contrast_stretch = sk_exposure.rescale_intensity(np_img, in_range=(low_p, high_p)) util.np_info(contrast_stretch, "Contrast Stretch", t.elapsed()) return contrast_stretch
def filter_complement(np_img, output_type="uint8"): """ Obtain the complement of an image as a NumPy array. Args: np_img: Image as a NumPy array. type: Type of array to return (float or uint8). Returns: Complement image as Numpy array. """ t = Time() if output_type == "float": complement = 1.0 - np_img else: complement = 255 - np_img util.np_info(complement, "Complement", t.elapsed()) return complement
def filter_kmeans_segmentation(np_img, compactness=10, n_segments=800): """ Use K-means segmentation (color/space proximity) to segment RGB image where each segment is colored based on the average color for that segment. Args: np_img: Binary image as a NumPy array. compactness: Color proximity versus space proximity factor. n_segments: The number of segments. Returns: NumPy array (uint8) representing 3-channel RGB image where each segment has been colored based on the average color for that segment. """ t = Time() labels = sk_segmentation.slic(np_img, compactness=compactness, n_segments=n_segments) result = sk_color.label2rgb(labels, np_img, kind='avg') util.np_info(result, "K-Means Segmentation", t.elapsed()) return result
def filter_rgb_to_hed(np_img, output_type="uint8"): """ Filter RGB channels to HED (Hematoxylin - Eosin - Diaminobenzidine) channels. Args: np_img: RGB image as a NumPy array. output_type: Type of array to return (float or uint8). Returns: NumPy array (float or uint8) with HED channels. """ t = Time() hed = sk_color.rgb2hed(np_img) if output_type == "float": hed = sk_exposure.rescale_intensity(hed, out_range=(0.0, 1.0)) else: hed = (sk_exposure.rescale_intensity(hed, out_range=(0, 255))).astype("uint8") util.np_info(hed, "RGB to HED", t.elapsed()) return hed
def filter_rgb_to_grayscale(np_img, output_type="uint8"): """ Convert an RGB NumPy array to a grayscale NumPy array. Shape (h, w, c) to (h, w). Args: np_img: RGB Image as a NumPy array. output_type: Type of array to return (float or uint8) Returns: Grayscale image as NumPy array with shape (h, w). """ t = Time() # Another common RGB ratio possibility: [0.299, 0.587, 0.114] grayscale = np.dot(np_img[..., :3], [0.2125, 0.7154, 0.0721]) if output_type != "float": grayscale = grayscale.astype("uint8") util.np_info(grayscale, "Gray", t.elapsed()) return grayscale
def filter_hed_to_eosin(np_img, output_type="uint8"): """ Obtain Eosin channel from HED NumPy array and rescale it (for example, to 0 to 255 for uint8) for increased contrast. Args: np_img: HED image as a NumPy array. output_type: Type of array to return (float or uint8). Returns: NumPy array for Eosin channel. """ t = Time() eosin = np_img[:, :, 1] if output_type == "float": eosin = sk_exposure.rescale_intensity(eosin, out_range=(0.0, 1.0)) else: eosin = (sk_exposure.rescale_intensity(eosin, out_range=(0, 255))).astype("uint8") util.np_info(eosin, "HED to Eosin", t.elapsed()) return eosin
def filter_rag_threshold(np_img, compactness=10, n_segments=800, threshold=9): """ Use K-means segmentation to segment RGB image, build region adjacency graph based on the segments, combine similar regions based on threshold value, and then output these resulting region segments. Args: np_img: Binary image as a NumPy array. compactness: Color proximity versus space proximity factor. n_segments: The number of segments. threshold: Threshold value for combining regions. Returns: NumPy array (uint8) representing 3-channel RGB image where each segment has been colored based on the average color for that segment (and similar segments have been combined). """ t = Time() labels = sk_segmentation.slic(np_img, compactness=compactness, n_segments=n_segments) g = sk_future.graph.rag_mean_color(np_img, labels) labels2 = sk_future.graph.cut_threshold(labels, g, threshold) result = sk_color.label2rgb(labels2, np_img, kind='avg') util.np_info(result, "RAG Threshold", t.elapsed()) return result
def filter_otsu_threshold(np_img, output_type="uint8"): """ Compute Otsu threshold on image as a NumPy array and return binary image based on pixels above threshold. Args: np_img: Image as a NumPy array. output_type: Type of array to return (bool, float, or uint8). Returns: NumPy array (bool, float, or uint8) where True, 1.0, and 255 represent a pixel above Otsu threshold. """ t = Time() otsu_thresh_value = sk_filters.threshold_otsu(np_img) otsu = (np_img > otsu_thresh_value) if output_type == "bool": pass elif output_type == "float": otsu = otsu.astype(float) else: otsu = otsu.astype("uint8") * 255 util.np_info(otsu, "Otsu Threshold", t.elapsed()) return otsu
def filter_adaptive_equalization(np_img, nbins=256, clip_limit=0.01, output_type="uint8"): """ Filter image (gray or RGB) using adaptive equalization to increase contrast in image, where contrast in local regions is enhanced. Args: np_img: Image as a NumPy array (gray or RGB). nbins: Number of histogram bins. clip_limit: Clipping limit where higher value increases contrast. output_type: Type of array to return (float or uint8). Returns: NumPy array (float or uint8) with contrast enhanced by adaptive equalization. """ t = Time() adapt_equ = sk_exposure.equalize_adapthist(np_img, nbins=nbins, clip_limit=clip_limit) if output_type == "float": pass else: adapt_equ = (adapt_equ * 255).astype("uint8") util.np_info(adapt_equ, "Adapt Equalization", t.elapsed()) return adapt_equ
def filter_entropy(np_img, neighborhood=9, threshold=5, output_type="uint8"): """ Filter image based on entropy (complexity). Args: np_img: Image as a NumPy array. neighborhood: Neighborhood size (defines height and width of 2D array of 1's). threshold: Threshold value. output_type: Type of array to return (bool, float, or uint8). Returns: NumPy array (bool, float, or uint8) where True, 1.0, and 255 represent a measure of complexity. """ t = Time() entr = sk_filters.rank.entropy(np_img, np.ones((neighborhood, neighborhood))) > threshold if output_type == "bool": pass elif output_type == "float": entr = entr.astype(float) else: entr = entr.astype("uint8") * 255 util.np_info(entr, "Entropy", t.elapsed()) return entr
def filter_hysteresis_threshold(np_img, low=50, high=100, output_type="uint8"): """ Apply two-level (hysteresis) threshold to an image as a NumPy array, returning a binary image. Args: np_img: Image as a NumPy array. low: Low threshold. high: High threshold. output_type: Type of array to return (bool, float, or uint8). Returns: NumPy array (bool, float, or uint8) where True, 1.0, and 255 represent a pixel above hysteresis threshold. """ t = Time() hyst = sk_filters.apply_hysteresis_threshold(np_img, low, high) if output_type == "bool": pass elif output_type == "float": hyst = hyst.astype(float) else: hyst = (255 * hyst).astype("uint8") util.np_info(hyst, "Hysteresis Threshold", t.elapsed()) return hyst
def filter_threshold(np_img, threshold, output_type="bool"): """ Return mask where a pixel has a value if it exceeds the threshold value. Args: np_img: Binary image as a NumPy array. threshold: The threshold value to exceed. output_type: Type of array to return (bool, float, or uint8). Returns: NumPy array representing a mask where a pixel has a value (T, 1.0, or 255) if the corresponding input array pixel exceeds the threshold value. """ t = Time() result = (np_img > threshold) if output_type == "bool": pass elif output_type == "float": result = result.astype(float) else: result = result.astype("uint8") * 255 util.np_info(result, "Threshold", t.elapsed()) return result
def filter_histogram_equalization(np_img, nbins=256, output_type="uint8"): """ Filter image (gray or RGB) using histogram equalization to increase contrast in image. Args: np_img: Image as a NumPy array (gray or RGB). nbins: Number of histogram bins. output_type: Type of array to return (float or uint8). Returns: NumPy array (float or uint8) with contrast enhanced by histogram equalization. """ t = Time() # if uint8 type and nbins is specified, convert to float so that nbins can be a value besides 256 if np_img.dtype == "uint8" and nbins != 256: np_img = np_img / 255 hist_equ = sk_exposure.equalize_hist(np_img, nbins=nbins) if output_type == "float": pass else: hist_equ = (hist_equ * 255).astype("uint8") util.np_info(hist_equ, "Hist Equalization", t.elapsed()) return hist_equ
def filter_local_otsu_threshold(np_img, disk_size=3, output_type="uint8"): """ Compute local Otsu threshold for each pixel and return binary image based on pixels being less than the local Otsu threshold. Args: np_img: Image as a NumPy array. disk_size: Radius of the disk structuring element used to compute the Otsu threshold for each pixel. output_type: Type of array to return (bool, float, or uint8). Returns: NumPy array (bool, float, or uint8) where local Otsu threshold values have been applied to original image. """ t = Time() local_otsu = sk_filters.rank.otsu(np_img, sk_morphology.disk(disk_size)) if output_type == "bool": pass elif output_type == "float": local_otsu = local_otsu.astype(float) else: local_otsu = local_otsu.astype("uint8") * 255 util.np_info(local_otsu, "Otsu Local Threshold", t.elapsed()) return local_otsu
def filter_binary_fill_holes(np_img, output_type="bool"): """ Fill holes in a binary object (bool, float, or uint8). Args: np_img: Binary image as a NumPy array. output_type: Type of array to return (bool, float, or uint8). Returns: NumPy array (bool, float, or uint8) where holes have been filled. """ t = Time() if np_img.dtype == "uint8": np_img = np_img / 255 result = sc_morph.binary_fill_holes(np_img) if output_type == "bool": pass elif output_type == "float": result = result.astype(float) else: result = result.astype("uint8") * 255 util.np_info(result, "Binary Fill Holes", t.elapsed()) return result
def filter_remove_small_objects(np_img, min_size=3000, avoid_overmask=True, overmask_thresh=95, output_type="uint8"): """ Filter image to remove small objects (connected components) less than a particular minimum size. If avoid_overmask is True, this function can recursively call itself with progressively smaller minimum size objects to remove to reduce the amount of masking that this filter performs. Args: np_img: Image as a NumPy array of type bool. min_size: Minimum size of small object to remove. avoid_overmask: If True, avoid masking above the overmask_thresh percentage. overmask_thresh: If avoid_overmask is True, avoid masking above this threshold percentage value. output_type: Type of array to return (bool, float, or uint8). Returns: NumPy array (bool, float, or uint8). """ t = Time() rem_sm = np_img.astype(bool) # make sure mask is boolean rem_sm = sk_morphology.remove_small_objects(rem_sm, min_size=min_size) mask_percentage = mask_percent(rem_sm) if (mask_percentage >= overmask_thresh) and (min_size >= 1) and (avoid_overmask is True): new_min_size = min_size / 2 print("Mask percentage %3.2f%% >= overmask threshold %3.2f%% for Remove Small Objs size %d, so try %d" % ( mask_percentage, overmask_thresh, min_size, new_min_size)) rem_sm = filter_remove_small_objects(np_img, new_min_size, avoid_overmask, overmask_thresh, output_type) np_img = rem_sm if output_type == "bool": pass elif output_type == "float": np_img = np_img.astype(float) else: np_img = np_img.astype("uint8") * 255 util.np_info(np_img, "Remove Small Objs", t.elapsed()) return np_img
def filter_green_channel(np_img, green_thresh=200, avoid_overmask=True, overmask_thresh=90, output_type="bool"): """ Create a mask to filter out pixels with a green channel value greater than a particular threshold, since hematoxylin and eosin are purplish and pinkish, which do not have much green to them. Args: np_img: RGB image as a NumPy array. green_thresh: Green channel threshold value (0 to 255). If value is greater than green_thresh, mask out pixel. avoid_overmask: If True, avoid masking above the overmask_thresh percentage. overmask_thresh: If avoid_overmask is True, avoid masking above this threshold percentage value. output_type: Type of array to return (bool, float, or uint8). Returns: NumPy array representing a mask where pixels above a particular green channel threshold have been masked out. """ t = Time() g = np_img[:, :, 1] gr_ch_mask = (g < green_thresh) & (g > 0) mask_percentage = mask_percent(gr_ch_mask) if (mask_percentage >= overmask_thresh) and (green_thresh < 255) and (avoid_overmask is True): new_green_thresh = math.ceil((255 - green_thresh) / 2 + green_thresh) print( "Mask percentage %3.2f%% >= overmask threshold %3.2f%% for Remove Green Channel green_thresh=%d, so try %d" % ( mask_percentage, overmask_thresh, green_thresh, new_green_thresh)) gr_ch_mask = filter_green_channel(np_img, new_green_thresh, avoid_overmask, overmask_thresh, output_type) np_img = gr_ch_mask if output_type == "bool": pass elif output_type == "float": np_img = np_img.astype(float) else: np_img = np_img.astype("uint8") * 255 util.np_info(np_img, "Filter Green Channel", t.elapsed()) return np_img
def filter_canny(np_img, sigma=1, low_threshold=0, high_threshold=25, output_type="uint8"): """ Filter image based on Canny algorithm edges. Args: np_img: Image as a NumPy array. sigma: Width (std dev) of Gaussian. low_threshold: Low hysteresis threshold value. high_threshold: High hysteresis threshold value. output_type: Type of array to return (bool, float, or uint8). Returns: NumPy array (bool, float, or uint8) representing Canny edge map (binary image). """ t = Time() can = sk_feature.canny(np_img, sigma=sigma, low_threshold=low_threshold, high_threshold=high_threshold) if output_type == "bool": pass elif output_type == "float": can = can.astype(float) else: can = can.astype("uint8") * 255 util.np_info(can, "Canny Edges", t.elapsed()) return can
def filter_hsv_to_h(hsv, output_type="int", display_np_info=True): """ Obtain hue values from HSV NumPy array as a 1-dimensional array. If output as an int array, the original float values are multiplied by 360 for their degree equivalents for simplicity. For more information, see https://en.wikipedia.org/wiki/HSL_and_HSV Args: hsv: HSV image as a NumPy array. output_type: Type of array to return (float or int). display_np_info: If True, display NumPy array info and filter time. Returns: Hue values (float or int) as a 1-dimensional NumPy array. """ if display_np_info: t = Time() h = hsv[:, :, 0] h = h.flatten() if output_type == "int": h *= 360 h = h.astype("int") if display_np_info: util.np_info(hsv, "HSV to H", t.elapsed()) return h