Video-Summary/Application/Layer.py

122 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
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