Source code for flyqma.bleedthrough.background

from copy import deepcopy
import numpy as np
import matplotlib.pyplot as plt
from scipy.ndimage import binary_dilation, generate_binary_structure
from matplotlib.gridspec import GridSpec

from ..visualization.masking import Mask


[docs]class BackgroundExtraction: """ Object for extracting image background pixels. Attributes: layer (Layer) - layer RGB image bg_mask (np.ndarray[bool]) - background mask, True where background """ def __init__(self, layer, niters=10): """ Instantiate background extraction. Args: layer (Layer) - layer RGB image niters (int) - number of binary foreground dilations """ # store layer self.layer = layer # build background mask self.bg_mask = self.build_background_mask(niters)
[docs] def build_background_mask(self, niters=0): """ Construct background mask by dilating foregound. Args: niters (int) - number of binary dilations Returns: bg_mask (np.ndarray[bool]) - background mask, True where background """ # re-run image preprocessing to obtain foreground threshold seg_params = self.layer.metadata['params']['segmentation_kw'] if 'preprocessing_kws' in seg_params.keys(): preprocessing_kws = seg_params['preprocessing_kws'] else: preprocessing_kws = None if preprocessing_kws is not None: bg = self.layer.get_channel(self.layer.metadata['bg']) _ = bg.preprocess(**preprocessing_kws) bg.set_otsu_mask() bg_mask = bg.mask else: bg_mask = (self.layer.labels!=0) # dilate foreground bg_mask = self.dilate_foreground(bg_mask, niters) return bg_mask
[docs] @staticmethod def dilate_foreground(foreground, niters=5): """ Apply binary dilation to foreground mask. """ struct = generate_binary_structure(2, 2) fg_mask = binary_dilation(foreground, struct, niters) bg_mask = ~fg_mask return bg_mask
[docs] def isolate_pixels(self, channel): """ Isolate pixels in image background. Args: channel (int) - color channel to be extracted Returns: px (np.ma.masked_array) - background masked pixel intensities """ px = self.layer.get_channel(channel).im return np.ma.masked_array(px, ~self.bg_mask)
[docs] def extract_pixels(self, channel): """ Extract pixels from image background. Args: channel (int) - color channel to be extracted Returns: px (np.ndarray[float]) - 1D array of background pixel intensities """ bg_px = self.isolate_pixels(channel) return bg_px[~bg_px.mask].data
[docs] def plot_foreground_mask(self, invert=False, ax=None, figsize=(3, 3)): """ Plot foreground mask. Args: invert (bool) - if True, mask background rather than foreground ax (matplotlib.axes.AxesSubplot) - if None, create figure figsize (tuple) - figure size Returns: figure """ if ax is None: fig, ax = plt.subplots(figsize=figsize) else: fig = plt.gcf() # TO DO: handle alternate channel specification... non RGB... etc # extract and visualize red/green channels rg = deepcopy(self.layer.im) rg[:,:,-1] = 0 ax.imshow(rg) # add foreground mask if invert: mask = Mask(~self.bg_mask) else: mask = Mask(self.bg_mask) mask.add_contourf(ax, alpha=0.5, hatches=['//']) mask.add_contour(ax, lw=2, color='w') ax.axis('off') return fig