Source code for pytb.detection.bboxes.bboxes_2d_detector.background_subtractor.background_subtractor

"""
Copyright (c) 2021-2022 UCLouvain, ICTEAM
Licensed under GPL-3.0 [see LICENSE for details]
Written by Jonathan Samelson (2021-2022)
"""

from pytb.detection.bboxes.bboxes_2d_detector.bboxes_2d_detector import BBoxes2DDetector
from pytb.output.bboxes_2d import BBoxes2D

from timeit import default_timer
import cv2
import numpy as np
import logging

log = logging.getLogger("aptitude-toolbox")


[docs]class BackgroundSubtractor(BBoxes2DDetector):
[docs] def __init__(self, proc_parameters: dict): """Initializes the detector with the given parameters. Args: proc_parameters (dict): A dictionary containing the BackgroundSubstractor detector parameters """ super().__init__(proc_parameters) # From cv2.approxPolyDP: Specifies the approximation accuracy. # This is the maximum distance between the original curve and its approximation. self.contour_thresh = proc_parameters["params"].get("contour_thresh", 3) # The minimum intensity of the pixels in the foreground image. self.intensity = proc_parameters["params"].get("intensity", 50) log.debug("BackgroundSubtractor {} implementation selected.".format(self.pref_implem)) if self.pref_implem == "mean" or self.pref_implem == "median": # If the pref_implem is "mean" or "median", the results will be based on the mean or median # values of the previous images. self.max_last_images = proc_parameters["params"].get("max_last_images", 50) self.last_images = [] elif self.pref_implem == "frame_diff": # If the pref_implem is "frame_diff", the results will be solely based on the previous image. self.prev_image = None else: assert False, "[ERROR] Unknown implementation of BackgroundSubtractor: {}".format(self.pref_implem)
[docs] def detect(self, frame: np.array) -> BBoxes2D: """Performs an inference using a background subtraction method on the given frame. Args: frame (np.array): The frame to infer detections from a background substractor. Returns: BBoxes2D: A set of 2D bounding boxes identifying the detected objects. """ img_sub = None # Convert the frame to the gray-scale representation frame_gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY) start = default_timer() if self.pref_implem == "mean" or self.pref_implem == "median": self.last_images.append(frame_gray) if len(self.last_images) == self.max_last_images + 1: self.last_images.pop(0) # Obtain mean or median values of the previous images if self.pref_implem == "mean": background_image = np.mean(np.array(self.last_images), axis=0).astype(np.uint8) else: background_image = np.median(np.array(self.last_images), axis=0).astype(np.uint8) # Get the difference between the current image and the previous images (that should describes the background) foreground_image = cv2.absdiff(frame_gray, background_image) # Keep the most important difference, where the pixels has a higher value than self.intensity, # to remove the noise. img_sub = np.where(foreground_image > self.intensity, frame_gray, np.array([0], np.uint8)) elif self.pref_implem == "frame_diff": # If no previous image, return an empty result. if self.prev_image is None: self.prev_image = frame_gray return BBoxes2D(0, np.array([]), np.array([]), np.array([]), frame.shape[1], frame.shape[0]) # Otherwise, take the difference with the previous image. else: foreground_image = cv2.absdiff(self.prev_image, frame_gray) img_sub = np.where(foreground_image > self.intensity, frame_gray, np.array([0], np.uint8)) self.prev_image = frame_gray else: assert False, "[ERROR] Unknown implementation of BackgroundSubtractor: {}".format(self.pref_implem) bboxes = [] # Find the contours of different objects that can be seen in the foreground image. contours = cv2.findContours(img_sub, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) for cont in contours[0]: poly = cv2.approxPolyDP(cont, self.contour_thresh, True) x, y, w, h = cv2.boundingRect(poly) bboxes.append([x, y, w, h]) end = default_timer() return BBoxes2D(end-start, np.array(bboxes), np.zeros(len(bboxes)).astype(int), np.ones(len(bboxes)), frame.shape[1], frame.shape[0])