scripts/ui/foreground_mask.py (64 lines of code) (raw):

#!/usr/bin/env python3 # Copyright 2004-present Facebook. All Rights Reserved. # This source code is licensed under the BSD-style license found in the # LICENSE file in the root directory of this source tree. """Back end for the foreground masks interaction model for the UI. Defines the interaction model for the foreground masks box. This is used in the depth estimation tab to segment the image into foreground and background by manipulating blur, closing, and threshold. Example: To see an instance of ForegroundMask, refer to the example in widget_image_thresholds.py: >>> foreground_mask = ForegroundMask() """ import cv2 import dep_util import numpy as np class ForegroundMask: """Back-end for the interactive element to visualize foreground mask thresholds. Attributes: blur (int): Gaussian blur radius. closing (int): Closure (for sealing holes). image_8bit_bg (np.array[uint8]): Thresholded background image (loaded as 8-bit). image_8bit_fg (np.array[uint8]): Thresholded foreground image (loaded as 8-bit). image_float_bg (np.array[float]): Thresholded background image (loaded as floats). image_float_fg (np.array[float]): Thresholded foreground image (loaded as floats). image_unchanged_bg (np.array[_]): Thresholded background image (loaded unchanged from disk). image_unchanged_fg (np.array[_]): Thresholded foreground image (loaded unchanged from disk). ready (bool): Whether or not the element is ready to display. thresh (int): Threshold applied to segment foreground and background """ def __init__(self, parent=None): """Initializes the back-end for foreground mask thresholds visualization with default parameters and no images. Args: parent (App(QDialog), optional): Object corresponding to the parent UI element. """ self.image_float_fg = None self.image_float_bg = None self.image_8bit_fg = None self.image_8bit_bg = None self.image_unchanged_fg = None self.image_unchanged_bg = None self.blur = -1 self.closing = -1 self.thresh = -1 self.ready = True def reset_params(self): """Sets blur, closing, and threshold to default values.""" self.blur = -1 self.closing = -1 self.thresh = -1 def set_images(self, filename_bg, filename_fg, res=2048): """Updates the foreground and background images per images on disk. Args: filename_bg (str): Path to the background image. filename_fg (str): Path to the foreground image. res (int, optional): Resolution of both images (assumed to be the same). """ self.reset_params() # Load and resize images self.image_unchanged_bg = dep_util.load_image_resized(filename_bg, res) self.image_unchanged_fg = dep_util.load_image_resized(filename_fg, res) self.image_float_bg = dep_util.convert_to_float(self.image_unchanged_bg) self.image_float_fg = dep_util.convert_to_float(self.image_unchanged_fg) self.image_8bit_bg = (255 * self.image_float_bg).astype("uint8") self.image_8bit_fg = (255 * self.image_float_fg).astype("uint8") def generate_fg_mask(self, image_bg, image_fg, blur, closing, thresh): """Creates a segmented (0, 255) image of the background vs. foreground. Args: image_bg (np.array[_]): Image of the background frame. image_fg (np.array[_]): Image of the foreground frame. blur (int): Gaussian blur radius. closing (int): Closure (for sealing holes). thresh (int): Threshold applied to segment foreground and background Returns: np.array[uint8]: Segmented image with 0 marking the background and 255 the foreground. """ blur_dims = (2 * blur + 1, 2 * blur + 1) bg_blur = cv2.GaussianBlur(image_bg, blur_dims, 0) fg_blur = cv2.GaussianBlur(image_fg, blur_dims, 0) # mask = ||template - frame||^2 > threshold diff = cv2.absdiff(bg_blur, fg_blur) mask = np.sum(diff ** 2, axis=2) ** (1.0 / 2) > thresh mask = np.array(mask, dtype=np.uint8) # Fill holes if closing > 0: element = cv2.getStructuringElement(cv2.MORPH_RECT, (closing, closing)) mask = cv2.morphologyEx(mask, cv2.MORPH_CLOSE, element) return mask def apply_thresholds(self, blur=-1, closing=-1, thresh=-1): """Displays colored visualization of the foreground masking. Args: blur (int, optional): Gaussian blur radius. closing (int, optional): Closure (for sealing holes). thresh (int, optional): Threshold applied to segment foreground and background Returns: np.array[uint8, uint8, uint8]: Colored image where green represents the foreground. """ if ( type(self.image_8bit_fg) is not np.ndarray or type(self.image_8bit_bg) is not np.ndarray ): return None if blur >= 0: self.blur = int(blur) if closing >= 0: self.closing = int(closing) if thresh >= 0: self.thresh = thresh # Ignore if we don't have values for all the parameters we need if self.blur < 0 or self.closing < 0 or self.thresh < 0: return None if not self.ready: return None mask = self.generate_fg_mask( self.image_float_bg, self.image_float_fg, self.blur, self.closing, self.thresh, ) mask_rgb = np.stack((mask,) * 3, axis=-1) mask_rgb[mask > 0] = [0, 255, 0] # green # Overlay mask on top of color for visualization purposes return cv2.addWeighted(self.image_8bit_fg, 1, mask_rgb, 0.5, 0)