Video-Summary/Application/ContourExctractor.py

156 lines
5.4 KiB
Python
Raw Normal View History

2020-09-22 18:25:06 +00:00
from imutils.video import VideoStream
import argparse
import datetime
import imutils
import time
import cv2
import os
import traceback
import _thread
import imageio
import numpy as np
2020-10-05 07:43:27 +00:00
from threading import Thread
from multiprocessing import Queue, Process, Pool
from multiprocessing.pool import ThreadPool
import concurrent.futures
2020-10-18 15:36:34 +00:00
from Application.VideoReader import VideoReader
2020-10-11 12:13:27 +00:00
from queue import Queue
import threading
2020-10-17 22:02:05 +00:00
2020-10-18 15:36:34 +00:00
from Application.Config import Config
2020-09-22 18:25:06 +00:00
2020-09-20 20:01:54 +00:00
class ContourExtractor:
2020-09-22 18:25:06 +00:00
2020-10-31 19:36:43 +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
2020-11-27 00:06:25 +00:00
def getExtractedContours(self):
2020-09-24 20:48:04 +00:00
return self.extractedContours
2020-11-27 00:06:25 +00:00
def getExtractedMasks(self):
return self.extractedMasks
2020-10-11 15:09:49 +00:00
def __init__(self, config):
2020-10-11 12:13:27 +00:00
self.frameBuffer = Queue(16)
self.extractedContours = dict()
2020-11-27 00:06:25 +00:00
self.extractedMasks = 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"]
self.resizeWidth = config["resizeWidth"]
self.videoPath = config["inputPath"]
2020-10-11 12:13:27 +00:00
self.xDim = 0
self.yDim = 0
2020-10-11 15:09:49 +00:00
self.config = config
2020-10-17 22:02:05 +00:00
self.diff = []
2020-10-21 19:56:00 +00:00
self.lastFrames = None
self.averages = dict()
2020-10-11 12:13:27 +00:00
print("ContourExtractor initiated")
2020-10-31 19:36:43 +00:00
def extractContours(self):
videoReader = VideoReader(self.config)
2020-10-22 16:40:13 +00:00
videoReader.fillBuffer()
2020-10-08 20:26:29 +00:00
2020-10-17 22:02:05 +00:00
threads = self.config["videoBufferLength"]
self.start = time.time()
2020-10-31 19:36:43 +00:00
# start a bunch of frames and let them read from the video reader buffer until the video reader reaches EOF
2020-10-11 12:13:27 +00:00
with ThreadPool(threads) as pool:
while not videoReader.videoEnded():
if videoReader.buffer.qsize() == 0:
2020-10-11 15:09:49 +00:00
time.sleep(.5)
2020-10-05 07:43:27 +00:00
2020-10-11 12:13:27 +00:00
tmpData = [videoReader.pop() for i in range(0, videoReader.buffer.qsize())]
2020-10-21 19:56:00 +00:00
self.computeMovingAverage(tmpData)
#pool.map(self.getContours, tmpData)
for data in tmpData:
self.getContours(data)
2020-10-17 22:02:05 +00:00
frameCount = tmpData[-1][0]
2020-10-22 16:40:13 +00:00
2020-10-08 20:26:29 +00:00
videoReader.thread.join()
2020-11-27 00:06:25 +00:00
return self.extractedContours, self.extractedMasks
2020-10-21 19:56:00 +00:00
2020-10-11 12:13:27 +00:00
def getContours(self, data):
frameCount, frame = data
2020-10-31 19:36:43 +00:00
# wait for the reference frame, which is calculated by averaging some revious frames
2020-10-21 19:56:00 +00:00
while frameCount not in self.averages:
time.sleep(0.1)
firstFrame = self.averages.pop(frameCount, None)
2020-10-22 16:40:13 +00:00
2020-10-17 22:02:05 +00:00
if frameCount % (60*30) == 0:
2020-11-21 18:13:17 +00:00
print(f" \r {frameCount/(60*30)} Minutes processed in {round((time.time() - self.start), 2)} each", end='\r')
2020-10-17 22:02:05 +00:00
self.start = time.time()
2020-10-31 19:36:43 +00:00
2020-10-21 19:56:00 +00:00
gray = self.prepareFrame(frame)
2020-10-05 07:43:27 +00:00
frameDelta = cv2.absdiff(gray, firstFrame)
thresh = cv2.threshold(frameDelta, self.threashold, 255, cv2.THRESH_BINARY)[1]
# 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)
#cv2.imshow("changes x", thresh)
#cv2.waitKey(10) & 0XFF
2020-10-05 07:43:27 +00:00
cnts = cv2.findContours(thresh.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
2020-10-17 22:02:05 +00:00
self.diff.append(np.count_nonzero(thresh))
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)
#ca = (x+w)*(y+h)
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-11-27 00:06:25 +00:00
masks.append(np.packbits(np.copy(thresh[y:y+h,x:x+w]), axis=0))
2020-10-08 20:26:29 +00:00
if len(contours) != 0 and contours is not None:
2020-10-11 12:13:27 +00:00
# this should be thread safe
self.extractedContours[frameCount] = contours
2020-11-27 00:06:25 +00:00
self.extractedMasks[frameCount] = masks
2020-10-11 12:13:27 +00:00
2020-10-21 19:56:00 +00:00
def prepareFrame(self, frame):
frame = imutils.resize(frame, width=self.resizeWidth)
gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
gray = cv2.GaussianBlur(gray, (3, 3), 0)
2020-10-21 19:56:00 +00:00
return gray
2020-10-22 16:40:13 +00:00
def computeMovingAverage(self, frames):
avg = []
2020-11-08 15:28:47 +00:00
averageFrames = self.config["avgNum"]
nth = int(averageFrames/3) # only take /x x frames to average
2020-10-22 16:40:13 +00:00
if frames[0][0] < averageFrames:
frame = frames[0][1]
frame = self.prepareFrame(frame)
for j in range(0, len(frames)):
frameNumber, _ = frames[j]
self.averages[frameNumber] = frame
# put last x frames into a buffer
self.lastFrames = frames[-averageFrames:]
return
if self.lastFrames is not None:
frames = self.lastFrames + frames
2020-11-08 15:28:47 +00:00
tmp = [[j, frames, averageFrames] for j in range(averageFrames, len(frames))]
2020-10-22 16:40:13 +00:00
with ThreadPool(16) as pool:
pool.map(self.averageDaFrames, tmp)
self.lastFrames = frames[-averageFrames:]
2020-09-22 18:25:06 +00:00
2020-10-22 16:40:13 +00:00
def averageDaFrames(self, dat):
j, frames, averageFrames = dat
frameNumber, frame = frames[j]
frame = self.prepareFrame(frame)
avg = frame/averageFrames
2020-11-08 15:28:47 +00:00
for jj in range(0,averageFrames-1):
2020-10-22 16:40:13 +00:00
avg += self.prepareFrame(frames[j-jj][1])/averageFrames
self.averages[frameNumber] = np.array(np.round(avg), dtype=np.uint8)
2020-11-27 00:06:25 +00:00
#self.averages[frameNumber] = self.prepareFrame(frames[j-averageFrames - 1][1])