Video-Summary/Application/ContourExctractor.py

139 lines
5.1 KiB
Python
Raw Normal View History

import os
import time
from multiprocessing import Pool, Process, Queue
2020-10-05 07:43:27 +00:00
from multiprocessing.pool import ThreadPool
2020-10-11 12:13:27 +00:00
from queue import Queue
from threading import Thread, activeCount
2020-12-26 13:58:58 +00:00
import cv2
import imutils
2020-12-26 13:58:58 +00:00
import numpy as np
from Application.Config import Config
from Application.VideoReader import VideoReader
2020-09-22 18:25:06 +00:00
2022-01-09 19:25:44 +00:00
2020-09-20 20:01:54 +00:00
class ContourExtractor:
2020-12-26 13:58:58 +00:00
# extracedContours = {frame_number: [(contour, (x,y,w,h)), ...], }
# dict with frame numbers as keys and the contour bounds of every contour for that frame
2020-09-22 18:25:06 +00:00
2022-09-11 09:25:36 +00:00
def get_extracted_contours(self):
return self.extracted_contours
2020-09-24 20:48:04 +00:00
2022-09-11 09:25:36 +00:00
def get_extracted_masks(self):
return self.extracted_masks
2020-11-27 00:06:25 +00:00
2020-10-11 15:09:49 +00:00
def __init__(self, config):
2022-09-11 09:25:36 +00:00
self.frame_buffer = Queue(16)
self.extracted_contours = dict()
self.extracted_masks = dict()
2020-10-11 15:09:49 +00:00
self.min_area = config["min_area"]
self.max_area = config["max_area"]
self.threashold = config["threashold"]
2022-09-11 09:25:36 +00:00
self.resize_width = config["resizeWidth"]
self.video_path = config["inputPath"]
self.x_dim = 0
self.y_dim = 0
2020-10-11 15:09:49 +00:00
self.config = config
2022-09-11 09:25:36 +00:00
self.last_frames = None
2020-10-21 19:56:00 +00:00
self.averages = dict()
2020-10-11 12:13:27 +00:00
print("ContourExtractor initiated")
2022-09-11 09:25:36 +00:00
def extract_contours(self):
2020-10-17 22:02:05 +00:00
self.start = time.time()
2022-08-17 09:54:52 +00:00
with VideoReader(self.config) as videoReader:
2022-09-11 09:25:36 +00:00
self.fps = videoReader.get_fps()
self.length = videoReader.get_length()
2022-08-17 09:54:52 +00:00
2022-09-11 09:25:36 +00:00
with ThreadPool(os.cpu_count()) as pool:
2022-08-17 09:54:52 +00:00
while True:
2022-09-11 09:25:36 +00:00
while not videoReader.video_ended() and videoReader.buffer.qsize() == 0:
2022-08-17 09:54:52 +00:00
time.sleep(0.5)
2020-10-05 07:43:27 +00:00
2022-09-11 09:25:36 +00:00
tmp_data = [videoReader.pop() for i in range(0, videoReader.buffer.qsize())]
if videoReader.video_ended():
2022-08-17 09:54:52 +00:00
break
2022-09-11 09:25:36 +00:00
pool.map(self.compute_moving_Average, (tmp_data,))
pool.map(self.get_contours, tmp_data)
2020-10-22 16:40:13 +00:00
2022-09-11 09:25:36 +00:00
return self.extracted_contours, self.extracted_masks
2020-12-22 12:58:47 +00:00
2022-09-11 09:25:36 +00:00
def get_contours(self, data):
frame_count, frame = data
2020-10-31 19:36:43 +00:00
# wait for the reference frame, which is calculated by averaging some revious frames
2022-09-11 09:25:36 +00:00
while frame_count not in self.averages:
2020-10-21 19:56:00 +00:00
time.sleep(0.1)
2022-09-11 09:25:36 +00:00
first_frame = self.averages.pop(frame_count, None)
2020-12-26 13:58:58 +00:00
2022-09-11 09:25:36 +00:00
if frame_count % (10 * self.fps) == 1:
2020-12-26 13:58:58 +00:00
print(
2022-09-11 09:25:36 +00:00
f" \r \033[K {round((frame_count/self.fps)*100/self.length, 2)} % processed in {round(time.time() - self.start, 2)}s",
2022-01-09 19:25:44 +00:00
end="\r",
)
2020-10-31 19:36:43 +00:00
2022-09-11 09:25:36 +00:00
gray = self.prepare_frame(frame)
frame_delta = cv2.absdiff(gray, first_frame)
thresh = cv2.threshold(frame_delta, self.threashold, 255, cv2.THRESH_BINARY)[1]
2020-10-05 07:43:27 +00:00
# dilate the thresholded image to fill in holes, then find contours
2020-10-17 22:02:05 +00:00
thresh = cv2.dilate(thresh, None, iterations=10)
2022-01-09 19:25:44 +00:00
# cv2.imshow("changes x", thresh)
# cv2.waitKey(10) & 0XFF
cnts = cv2.findContours(thresh.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
2020-10-05 07:43:27 +00:00
cnts = imutils.grab_contours(cnts)
contours = []
2020-11-27 00:06:25 +00:00
masks = []
2020-10-05 07:43:27 +00:00
for c in cnts:
ca = cv2.contourArea(c)
2020-11-27 00:06:25 +00:00
(x, y, w, h) = cv2.boundingRect(c)
2020-10-05 07:43:27 +00:00
if ca < self.min_area or ca > self.max_area:
continue
2020-10-11 12:13:27 +00:00
contours.append((x, y, w, h))
2020-12-26 13:58:58 +00:00
# the mask has to be packed like this, since np doesn't have a bit array,
# meaning every bit in the mask would take up 8bits, which migth be too much
2022-01-09 19:25:44 +00:00
masks.append(np.packbits(np.copy(thresh[y : y + h, x : x + w]), axis=0))
2020-12-26 13:58:58 +00:00
if len(contours) != 0 and contours is not None:
2020-10-11 12:13:27 +00:00
# this should be thread safe
2022-09-11 09:25:36 +00:00
self.extracted_contours[frame_count] = contours
self.extracted_masks[frame_count] = masks
2020-10-11 12:13:27 +00:00
2022-09-11 09:25:36 +00:00
def prepare_frame(self, frame):
frame = imutils.resize(frame, width=self.resize_width)
2020-10-21 19:56:00 +00:00
gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
gray = cv2.GaussianBlur(gray, (3, 3), 0)
2020-10-21 19:56:00 +00:00
return gray
2022-09-11 09:25:36 +00:00
def compute_moving_Average(self, frames):
average_frames = self.config["avgNum"]
2020-11-08 15:28:47 +00:00
2022-09-11 09:25:36 +00:00
if frames[0][0] < average_frames:
2020-10-22 16:40:13 +00:00
frame = frames[0][1]
2022-09-11 09:25:36 +00:00
frame = self.prepare_frame(frame)
2020-10-22 16:40:13 +00:00
for j in range(0, len(frames)):
2022-09-11 09:25:36 +00:00
frame_number, _ = frames[j]
self.averages[frame_number] = frame
2020-10-22 16:40:13 +00:00
# put last x frames into a buffer
2022-09-11 09:25:36 +00:00
self.last_frames = frames[-average_frames:]
2020-10-22 16:40:13 +00:00
return
2022-09-11 09:25:36 +00:00
if self.last_frames is not None:
frames = self.last_frames + frames
2020-10-22 16:40:13 +00:00
2022-09-11 09:25:36 +00:00
tmp = [[j, frames, average_frames] for j in range(average_frames, len(frames))]
2022-08-15 10:20:28 +00:00
with ThreadPool(int(os.cpu_count())) as pool:
2022-09-11 09:25:36 +00:00
pool.map(self.average_da_frames, tmp)
2020-10-22 16:40:13 +00:00
2022-09-11 09:25:36 +00:00
self.last_frames = frames[-average_frames:]
2020-09-22 18:25:06 +00:00
2022-09-11 09:25:36 +00:00
def average_da_frames(self, dat):
j, frames, average_frames = dat
frame_number, frame = frames[j]
frame = self.prepare_frame(frame)
2020-12-26 13:58:58 +00:00
2022-09-11 09:25:36 +00:00
avg = frame / average_frames
for jj in range(0, average_frames - 1):
avg += self.prepare_frame(frames[j - jj][1]) / average_frames
self.averages[frame_number] = np.array(np.round(avg), dtype=np.uint8)