Video-Summary/Application/Layer.py

124 lines
4.1 KiB
Python
Raw Normal View History

"""Layer data structure for grouping related contours across frames."""
from typing import Any, Dict, List, Optional, Tuple
2020-10-18 15:36:34 +00:00
import cv2
import imutils
import numpy as np
2020-10-18 15:36:34 +00:00
2022-01-09 19:25:44 +00:00
2020-10-18 15:36:34 +00:00
class Layer:
"""
Represents a layer of related contours across video frames.
Layers group contours that represent the same moving object or area
across multiple frames, allowing for coherent tracking and visualization.
Attributes:
start_frame: Frame number where this layer begins
last_frame: Frame number where this layer ends
length: Total length of the layer in frames
"""
2022-01-09 19:25:44 +00:00
# bounds = [[(x,y,w,h), ],]
2020-10-18 15:36:34 +00:00
start_frame: Optional[int] = None
last_frame: Optional[int] = None
length: Optional[int] = None
2020-10-18 15:36:34 +00:00
def __init__(self, start_frame: int, data: Tuple[int, int, int, int], mask: np.ndarray, config: Dict[str, Any]):
"""
Initialize a Layer.
Args:
start_frame: Frame number where this layer starts
data: Initial contour bounds as (x, y, w, h)
mask: Binary mask for the contour
config: Configuration dictionary
2022-01-09 19:25:44 +00:00
Note:
Layers are collections of contours with a start_frame,
which is the number of the frame the first contour of
this layer was extracted from.
2020-10-31 19:36:43 +00:00
A Contour is a CV2 Contour, which is a y*x*3 rgb numpy array,
but we only care about the corners of the contours.
So we save the bounds (x,y,w,h) in bounds[] and the actual content in data[]
2022-01-09 19:25:44 +00:00
"""
2022-09-11 09:25:36 +00:00
self.start_frame = start_frame
self.last_frame = start_frame
2020-10-18 15:36:34 +00:00
self.config = config
self.data: List = []
self.bounds: List = []
self.masks: List = []
self.stats: Dict[str, Any] = dict()
self.export_offset: int = 0
2020-10-18 15:36:34 +00:00
self.bounds.append([data])
2020-11-27 00:06:25 +00:00
self.masks.append([mask])
2020-10-18 15:36:34 +00:00
def add(self, frame_number: int, bound: Tuple[int, int, int, int], mask: np.ndarray):
"""
Add a bound to the Layer at the index corresponding to the frame number.
Args:
frame_number: Frame number for this bound
bound: Contour bounds as (x, y, w, h)
mask: Binary mask for the contour
"""
2022-09-11 09:25:36 +00:00
index = frame_number - self.start_frame
if index < 0:
return
2022-09-11 09:25:36 +00:00
if frame_number > self.last_frame:
for i in range(frame_number - self.last_frame):
2020-12-26 13:58:58 +00:00
self.bounds.append([])
self.masks.append([])
2022-09-11 09:25:36 +00:00
self.last_frame = frame_number
2020-10-18 15:36:34 +00:00
2020-12-18 20:27:19 +00:00
if bound not in self.bounds[index]:
self.bounds[index].append(bound)
self.masks[index].append(mask)
2022-09-11 09:25:36 +00:00
def get_length(self):
return len(self) + self.export_offset
2020-10-31 19:36:43 +00:00
def __len__(self):
2020-10-18 15:36:34 +00:00
self.length = len(self.bounds)
return self.length
2021-02-05 19:04:10 +00:00
2022-09-11 09:25:36 +00:00
def space_overlaps(self, layer2):
2022-01-09 19:25:44 +00:00
"""Checks if there is an overlap in the bounds of current layer with given layer"""
2021-02-05 19:04:10 +00:00
overlap = False
2022-09-11 09:25:36 +00:00
max_len = min(len(layer2.bounds), len(self.bounds))
bounds = self.bounds[:max_len]
for b1s, b2s in zip(bounds[::10], layer2.bounds[:max_len:10]):
2021-02-05 19:04:10 +00:00
for b1 in b1s:
for b2 in b2s:
if self.contours_overlay(
(b1[0], b1[1] + b1[3]), (b1[0] + b1[2], b1[1]), (b2[0], b2[1] + b2[3]), (b2[0] + b2[2], b2[1])
):
2021-02-05 19:04:10 +00:00
overlap = True
break
return overlap
2022-01-09 19:25:44 +00:00
2022-09-11 09:25:36 +00:00
def time_overlaps(self, layer2):
2022-01-09 19:25:44 +00:00
"""Checks for overlap in time between current and given layer"""
2022-09-11 09:25:36 +00:00
s1 = self.export_offset
e1 = self.last_frame - self.start_frame + self.export_offset
2022-10-07 21:10:26 +00:00
s2 = layer2.export_offset
e2 = layer2.last_frame - layer2.start_frame + layer2.export_offset
2021-02-05 19:04:10 +00:00
if s2 >= s1 and s2 <= e1:
return True
elif s1 >= s2 and s1 <= e2:
return True
else:
return False
2022-09-11 09:25:36 +00:00
def contours_overlay(self, l1, r1, l2, r2):
2022-01-09 19:25:44 +00:00
if l1[0] >= r2[0] or l2[0] >= r1[0]:
2021-02-05 19:04:10 +00:00
return False
2022-01-09 19:25:44 +00:00
if l1[1] <= r2[1] or l2[1] <= r1[1]:
2021-02-05 19:04:10 +00:00
return False
return True