multithreaded contour extractor

This commit is contained in:
Askill 2020-10-11 14:13:27 +02:00
parent f52feb8f5f
commit c187b8ce9f
10 changed files with 95 additions and 70 deletions

2
.gitignore vendored
View File

@ -6,3 +6,5 @@ generate test footage/3.MP4
short.mp4 short.mp4
__pycache__/ __pycache__/
*.mp4

View File

@ -9,67 +9,75 @@ import traceback
import _thread import _thread
import imageio import imageio
import numpy as np import numpy as np
import time
from threading import Thread from threading import Thread
from multiprocessing import Queue, Process, Pool from multiprocessing import Queue, Process, Pool
from multiprocessing.pool import ThreadPool from multiprocessing.pool import ThreadPool
import concurrent.futures import concurrent.futures
from VideoReader import VideoReader from VideoReader import VideoReader
from queue import Queue
import threading
from multiprocessing.pool import ThreadPool
class ContourExtractor: class ContourExtractor:
#X = {frame_number: [(contour, (x,y,w,h)), ...], } #X = {frame_number: [(contour, (x,y,w,h)), ...], }
extractedContours = dict()
min_area = 100
max_area = 1000
threashold = 13
xDim = 0
yDim = 0
def getextractedContours(self): def getextractedContours(self):
return self.extractedContours return self.extractedContours
def __init__(self): def __init__(self):
self.frameBuffer = Queue(16)
self.extractedContours = dict()
self.min_area = 30
self.max_area = 1000
self.threashold = 13
self.xDim = 0
self.yDim = 0
print("ContourExtractor initiated") print("ContourExtractor initiated")
def extractContours(self, videoPath, resizeWidth): def extractContours(self, videoPath, resizeWidth):
firstFrame = None
extractedContours = dict() extractedContours = dict()
videoReader = VideoReader(videoPath) videoReader = VideoReader(videoPath)
self.xDim = videoReader.w self.xDim = videoReader.w
self.yDim = videoReader.h self.yDim = videoReader.h
self.resizeWidth = resizeWidth
videoReader.fillBuffer() videoReader.fillBuffer()
while not videoReader.videoEnded():
frameCount, frame = videoReader.pop() frameCount, frame = videoReader.pop()
if frameCount % (60*30) == 0:
print("Minutes processed: ", frameCount/(60*30))
if frame is None: #init compare image
print("ContourExtractor: frame was None")
continue
# resize the frame, convert it to grayscale, and blur it
frame = imutils.resize(frame, width=resizeWidth) frame = imutils.resize(frame, width=resizeWidth)
gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY) gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
# if the first frame is None, initialize it
if firstFrame is None:
#gray = np.asarray(gray[:,:,1]/2 + gray[:,:,2]/2).astype(np.uint8) #gray = np.asarray(gray[:,:,1]/2 + gray[:,:,2]/2).astype(np.uint8)
gray = cv2.GaussianBlur(gray, (5, 5), 0) gray = cv2.GaussianBlur(gray, (5, 5), 0)
firstFrame = gray self.firstFrame = gray
continue
x = self.getContours(gray, firstFrame) threads = 16
if x is not None: start = time.time()
extractedContours[frameCount] = x with ThreadPool(threads) as pool:
while not videoReader.videoEnded():
#FrameCount, frame = videoReader.pop()
if frameCount % (60*30) == 0:
print(f"Minutes processed: {frameCount/(60*30)} in {round((time.time() - start), 2)} each")
start = time.time()
if videoReader.buffer.qsize() == 0:
time.sleep(1)
tmpData = [videoReader.pop() for i in range(0, videoReader.buffer.qsize())]
frameCount = tmpData[-1][0]
pool.map(self.getContours, tmpData)
print("done")
videoReader.thread.join() videoReader.thread.join()
self.extractedContours = extractedContours
return extractedContours
def getContours(self, gray, firstFrame): return self.extractedContours
def getContours(self, data):
frameCount, frame = data
firstFrame = self.firstFrame
frame = imutils.resize(frame, width=self.resizeWidth)
gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
gray = cv2.GaussianBlur(gray, (5, 5), 0) gray = cv2.GaussianBlur(gray, (5, 5), 0)
frameDelta = cv2.absdiff(gray, firstFrame) frameDelta = cv2.absdiff(gray, firstFrame)
thresh = cv2.threshold(frameDelta, self.threashold, 255, cv2.THRESH_BINARY)[1] thresh = cv2.threshold(frameDelta, self.threashold, 255, cv2.THRESH_BINARY)[1]
@ -84,11 +92,17 @@ class ContourExtractor:
if ca < self.min_area or ca > self.max_area: if ca < self.min_area or ca > self.max_area:
continue continue
(x, y, w, h) = cv2.boundingRect(c) (x, y, w, h) = cv2.boundingRect(c)
#print((x, y, w, h))
contours.append((x, y, w, h)) contours.append((x, y, w, h))
if len(contours) != 0 and contours is not None: if len(contours) != 0 and contours is not None:
return contours # this should be thread safe
self.extractedContours[frameCount] = contours
def displayContours(self): def displayContours(self):

View File

@ -19,36 +19,47 @@ class Exporter:
writer.append_data(np.array(frame)) writer.append_data(np.array(frame))
writer.close() writer.close()
def exportLayers(self, layers, outputPath, resizeWidth): def exportLayers(self, layers, footagePath, outputPath, resizeWidth):
listOfFrames = self.makeListOfFrames(layers)
videoReader = VideoReader(footagePath, listOfFrames)
videoReader.fillBuffer()
maxLength = self.getMaxLengthOfLayers(layers)
underlay = cv2.VideoCapture(footagePath).read()[1] underlay = cv2.VideoCapture(footagePath).read()[1]
underlay = cv2.cvtColor(underlay, cv2.COLOR_BGR2RGB)
frames = [underlay]*maxLength
exportFrame = 0
fps = self.fps fps = self.fps
writer = imageio.get_writer(outputPath, fps=fps) writer = imageio.get_writer(outputPath, fps=fps)
i = 0 while not videoReader.videoEnded():
for layer in layers: frameCount, frame = videoReader.pop()
data = layer.data if frameCount % (60*self.fps) == 0:
contours = layer.bounds print("Minutes processed: ", frameCount/(60*self.fps))
if len(data) < 10: if frame is None:
print("ContourExtractor: frame was None")
continue continue
for frame, contour in zip(data, contours): frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
(x, y, w, h) = contour
frame = frame
frame1 = underlay for layer in layers:
frame1 = imutils.resize(frame1, width=resizeWidth) if layer.startFrame <= frameCount and layer.startFrame + len(layer.bounds) > frameCount:
frame1 = cv2.cvtColor(frame1, cv2.COLOR_BGR2RGB) (x, y, w, h) = layer.bounds[frameCount - layer.startFrame]
frame1[y:y+frame.shape[0], x:x+frame.shape[1]] = frame factor = videoReader.w / resizeWidth
cv2.putText(frame1, str(i), (30, 30), x = int(x * factor)
cv2.FONT_HERSHEY_SIMPLEX, 1, (255, 255, 255), 2) y = int(y * factor)
writer.append_data(np.array(frame1)) w = int(w * factor)
#cv2.imshow("changes overlayed", frame) h = int(h * factor)
#cv2.waitKey(10) & 0XFF # if exportFrame as index instead of frameCount - layer.startFrame then we have layer after layer
i += 1 frame2 = underlay
frame2[y:y+h, x:x+w] = frame[y:y+h, x:x+w]
writer.append_data(frame2)
videoReader.thread.join()
writer.close()
# cv2.destroyAllWindows()
def exportOverlayed(self, layers, footagePath, outputPath, resizeWidth): def exportOverlayed(self, layers, footagePath, outputPath, resizeWidth):
listOfFrames = self.makeListOfFrames(layers) listOfFrames = self.makeListOfFrames(layers)
videoReader = VideoReader(footagePath, listOfFrames) videoReader = VideoReader(footagePath, listOfFrames)
videoReader.fillBuffer() videoReader.fillBuffer()

View File

@ -3,3 +3,6 @@ time compression
Time consumed reading video: 369.0188868045807s 3.06GB 26min 1080p downscaled 500p 30fps Time consumed reading video: 369.0188868045807s 3.06GB 26min 1080p downscaled 500p 30fps
Time consumed reading video: 240.s 3.06GB 26min 1080p downscaled 500p 30fps when multithreaded Time consumed reading video: 240.s 3.06GB 26min 1080p downscaled 500p 30fps when multithreaded
contour extraction: 10.5 Sec. when only 2 Threads
8 secs when also mapping getContours()

View File

@ -51,13 +51,11 @@ class VideoReader:
def readFrames(self): def readFrames(self):
while self.lastFrame < self.endFrame: while self.lastFrame < self.endFrame:
if not self.buffer.full():
res, frame = self.vc.read() res, frame = self.vc.read()
if res: if res:
self.buffer.put((self.lastFrame, frame)) self.buffer.put((self.lastFrame, frame))
self.lastFrame += 1 self.lastFrame += 1
else:
sleep(0.5)
self.stopped = True self.stopped = True

Binary file not shown.

Binary file not shown.

View File

@ -21,14 +21,11 @@ def demo():
#print("Time consumed reading video: ", time.time() - start) #print("Time consumed reading video: ", time.time() - start)
contours = ContourExtractor().extractContours(footagePath, resizeWidth) contours = ContourExtractor().extractContours(footagePath, resizeWidth)
print("Time consumed in working: ", time.time() - start) print("Time consumed extracting: ", time.time() - start)
layerFactory = LayerFactory(contours) layerFactory = LayerFactory(contours)
print("freeing Data", time.time() - start)
layerFactory.freeData(maxLayerLength, minLayerLength) layerFactory.freeData(maxLayerLength, minLayerLength)
print("sort Layers") print("sort Layers")
layerFactory.sortLayers() layerFactory.sortLayers()
#print("fill Layers")
#layerFactory.fillLayers(footagePath, resizeWidth)
Exporter().exportOverlayed(layerFactory.layers,footagePath, os.path.join(os.path.dirname(__file__), "./short.mp4"), resizeWidth) Exporter().exportOverlayed(layerFactory.layers,footagePath, os.path.join(os.path.dirname(__file__), "./short.mp4"), resizeWidth)
print("Total time: ", time.time() - start) print("Total time: ", time.time() - start)

Binary file not shown.

Binary file not shown.