class_docstr: Region extractor for AI-VAD.
  
  Args:
      box_score_thresh (float): Confidence threshold for bounding box predictions.
      persons_only (bool): When enabled, only regions labeled as person are included.
      min_bbox_area (int): Minimum bounding box area. Regions with a surface area lower than this value are excluded.
      max_bbox_overlap (float): Maximum allowed overlap between bounding boxes.
      enable_foreground_detections (bool): Add additional foreground detections based on pixel difference between
          consecutive frames.
      foreground_kernel_size (int): Gaussian kernel size used in foreground detection.
      foreground_binary_threshold (int): Value between 0 and 255 which acts as binary threshold in foreground
          detection.
class_docstr_tok: ["Region","extractor","for","AI-VAD",".","Args",":","box_score_thresh","(","float",")",":","Confidence","threshold","for","bounding","box","predictions",".","persons_only","(","bool",")",":","When","enabled",",","only","regions","labeled","as","person","are","included",".","min_bbox_area","(","int",")",":","Minimum","bounding","box","area",".","Regions","with","a","surface","area","lower","than","this","value","are","excluded",".","max_bbox_overlap","(","float",")",":","Maximum","allowed","overlap","between","bounding","boxes",".","enable_foreground_detections","(","bool",")",":","Add","additional","foreground","detections","based","on","pixel","difference","between","consecutive","frames",".","foreground_kernel_size","(","int",")",":","Gaussian","kernel","size","used","in","foreground","detection",".","foreground_binary_threshold","(","int",")",":","Value","between","0","and","255","which","acts","as","binary","threshold","in","foreground","detection","."]
docstr: Add any foreground regions that were not detected by the region extractor.
  
  This method adds regions that likely belong to the foreground of the video scene, but were not detected by the
  region extractor module. The foreground pixels are determined by taking the pixel difference between two
  consecutive video frames and applying a binary threshold. The final detections consist of all connected
  components in the foreground that do not fall in one of the bounding boxes predicted by the region extractor.
  
  Args:
      regions (list[dict[str, Tensor]]): Region detections for a batch of images, generated by the region
          extraction module.
      first_frame (Tensor): video frame at time t-1
      last_frame (Tensor): Video frame time t
      kernel_size (int): Kernel size for Gaussian smoothing applied to input frames
      binary_threshold (int): Binary threshold used in foreground detection, should be in range [0, 255]
  
  Returns:
      list[dict[str, Tensor]]: region detections with foreground regions appended
docstr_tok: ["Add","any","foreground","regions","that","were","not","detected","by","the","region","extractor",".","This","method","adds","regions","that","likely","belong","to","the","foreground","of","the","video","scene",",","but","were","not","detected","by","the","region","extractor","module",".","The","foreground","pixels","are","determined","by","taking","the","pixel","difference","between","two","consecutive","video","frames","and","applying","a","binary","threshold",".","The","final","detections","consist","of","all","connected","components","in","the","foreground","that","do","not","fall","in","one","of","the","bounding","boxes","predicted","by","the","region","extractor",".","Args",":","regions","(","list","[","dict","[","str",",","Tensor","]","]",")",":","Region","detections","for","a","batch","of","images",",","generated","by","the","region","extraction","module",".","first_frame","(","Tensor",")",":","video","frame","at","time","t-1","last_frame","(","Tensor",")",":","Video","frame","time","t","kernel_size","(","int",")",":","Kernel","size","for","Gaussian","smoothing","applied","to","input","frames","binary_threshold","(","int",")",":","Binary","threshold","used","in","foreground","detection",",","should","be","in","range","[","0",",","255","]","Returns",":","list","[","dict","[","str",",","Tensor","]","]",":","region","detections","with","foreground","regions","appended"]
code: def add_foreground_boxes(
      self,
      regions: list[dict[str, Tensor]],
      first_frame: Tensor,
      last_frame: Tensor,
      kernel_size: int,
      binary_threshold: int,
  ) -> list[dict[str, Tensor]]:
      """Add any foreground regions that were not detected by the region extractor.
  
      This method adds regions that likely belong to the foreground of the video scene, but were not detected by the
      region extractor module. The foreground pixels are determined by taking the pixel difference between two
      consecutive video frames and applying a binary threshold. The final detections consist of all connected
      components in the foreground that do not fall in one of the bounding boxes predicted by the region extractor.
  
      Args:
          regions (list[dict[str, Tensor]]): Region detections for a batch of images, generated by the region
              extraction module.
          first_frame (Tensor): video frame at time t-1
          last_frame (Tensor): Video frame time t
          kernel_size (int): Kernel size for Gaussian smoothing applied to input frames
          binary_threshold (int): Binary threshold used in foreground detection, should be in range [0, 255]
  
      Returns:
          list[dict[str, Tensor]]: region detections with foreground regions appended
      """
      # apply gaussian blur to first and last frame
      first_frame = gaussian_blur(
          first_frame, [kernel_size, kernel_size]
      )
      last_frame = gaussian_blur(last_frame, [kernel_size, kernel_size])
  
      # take the abs diff between the blurred images and convert to grayscale
      pixel_diff = torch.abs(first_frame - last_frame)
      pixel_diff = rgb_to_grayscale(pixel_diff).squeeze(1)
  
      # apply binary threshold to the diff
      foreground_map = (pixel_diff > binary_threshold / 255).int()
  
      # remove regions already detected by region extractor
      boxes_list = [im_regions["boxes"] for im_regions in regions]
      boxes_list = [
          clip_boxes_to_image(
              boxes + Tensor([-2, -2, 2, 2]).to(boxes.device),
              foreground_map.shape[-2:],
          )
          for boxes in boxes_list
      ]  # extend boxes by 2 in all directions to ensure full object is included
      boxes_mask = boxes_to_masks(
          boxes_list, foreground_map.shape[-2:]
      ).int()
      foreground_map *= -boxes_mask + 1  # invert mask
  
      # find boxes from foreground map
      batch_boxes, _ = masks_to_boxes(foreground_map)
  
      # append foreground detections to region extractor detections
      for im_regions, boxes, pixel_mask in zip(
          regions, batch_boxes, foreground_map
      ):
          if boxes.shape[0] == 0:
              continue
  
          # append boxes, labels and scores
          im_regions["boxes"] = torch.cat([im_regions["boxes"], boxes])
          im_regions["labels"] = torch.cat(
              [
                  im_regions["labels"],
                  torch.zeros(boxes.shape[0], device=boxes.device),
              ]
          )  # set label as background, in accordance with region extractor predictions
          im_regions["scores"] = torch.cat(
              [
                  im_regions["scores"],
                  torch.ones(boxes.shape[0], device=boxes.device) * 0.5,
              ]
          )  # set confidence to 0.5
  
          # append masks
          im_boxes_as_list = [
              box.unsqueeze(0) for box in boxes
          ]  # list with one box per element
          boxes_mask = boxes_to_masks(
              im_boxes_as_list, pixel_mask.shape[-2:]
          ).int()
          new_masks = (
              pixel_mask.repeat((len(im_boxes_as_list), 1, 1))
              * boxes_mask
          )
          im_regions["masks"] = torch.cat(
              [im_regions["masks"], new_masks.unsqueeze(1)]
          )
  
      return regions
  
file_path: anomalib/src/anomalib/models/ai_vad/regions.py
imports: from __future__ import annotations
  import torch
  from torch import Tensor, nn
  from torchvision.models.detection import MaskRCNN_ResNet50_FPN_V2_Weights, maskrcnn_resnet50_fpn_v2
  from torchvision.ops import box_area, clip_boxes_to_image
  from torchvision.transforms.functional import gaussian_blur, rgb_to_grayscale
  from anomalib.data.utils.boxes import boxes_to_masks, masks_to_boxes
label_desc: Use image node_id 3 for calling the RegionExtractor obj's underlying member method code with example usage: obj.add_foreground_boxes(regions, first_frame, last_frame, kernel_size, binary_threshold) and returns: regions