diff --git a/ContourExctractor.py b/ContourExctractor.py index aa4ffab..f075177 100644 --- a/ContourExctractor.py +++ b/ContourExctractor.py @@ -14,6 +14,7 @@ from threading import Thread from multiprocessing import Queue, Process, Pool from multiprocessing.pool import ThreadPool import concurrent.futures +from VideoReader import VideoReader class ContourExtractor: @@ -32,61 +33,43 @@ class ContourExtractor: print("ContourExtractor initiated") def extractContours(self, videoPath, resizeWidth): - - - # initialize the first frame in the video stream - vs = cv2.VideoCapture(videoPath) - - res, image = vs.read() - self.xDim = image.shape[1] - self.yDim = image.shape[0] firstFrame = None - # loop over the frames of the video - frameCount = -1 - extractedContours = dict() - - results = [] - extractedContours = dict() + extractedContours = dict() + videoReader = VideoReader(videoPath) + self.xDim = videoReader.w + self.yDim = videoReader.h + videoReader.fillBuffer() - imageBuffer = [] - - with concurrent.futures.ProcessPoolExecutor() as executor: - while res: - frameCount += 1 - if frameCount % (60*30) == 0: - print("Minutes processed: ", frameCount/(60*30)) - + while not videoReader.videoEnded(): + frameCount, frame = videoReader.pop() + if frameCount % (60*30) == 0: + print("Minutes processed: ", frameCount/(60*30)) + + if frame is None: + print("ContourExtractor: frame was None") + continue - res, frame = vs.read() - # resize the frame, convert it to grayscale, and blur it - if frame is None: - print("ContourExtractor: frame was None") - break + # resize the frame, convert it to grayscale, and blur it + frame = imutils.resize(frame, width=resizeWidth) + gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY) - frame = imutils.resize(frame, width=resizeWidth) - gray = cv2.cvtColor(frame, cv2.COLOR_BGR2LAB) + # 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 = cv2.GaussianBlur(gray, (5, 5), 0) + firstFrame = gray + continue + x = self.getContours(gray, firstFrame) + if x is not None: + extractedContours[frameCount] = x - # 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 = cv2.GaussianBlur(gray, (5, 5), 0) - firstFrame = gray - continue - - results.append(executor.submit(self.getContours, frameCount, gray, firstFrame)) - - #contours = self.getContours(frameCount, gray, firstFrame) - - for f in concurrent.futures.as_completed(results): - x=f.result() - if x is not None: - extractedContours = {**extractedContours, **x} - + print("done") + videoReader.thread.join() self.extractedContours = extractedContours return extractedContours - def getContours(self, frameCount, gray, firstFrame): - gray = np.asarray(gray[:,:,1]/2 + gray[:,:,2]/2).astype(np.uint8) + def getContours(self, gray, firstFrame): + gray = cv2.GaussianBlur(gray, (5, 5), 0) frameDelta = cv2.absdiff(gray, firstFrame) thresh = cv2.threshold(frameDelta, self.threashold, 255, cv2.THRESH_BINARY)[1] @@ -104,10 +87,9 @@ class ContourExtractor: #print((x, y, w, h)) contours.append((x, y, w, h)) - if len(contours) != 0: - return {frameCount: contours} - else: - return None + if len(contours) != 0 and contours is not None: + return contours + def displayContours(self): values = self.extractedContours.values() diff --git a/LayerFactory.py b/LayerFactory.py index d95d5fe..d19ee38 100644 --- a/LayerFactory.py +++ b/LayerFactory.py @@ -53,10 +53,8 @@ class LayerFactory: # inserts all the fucking contours as layers? for frameNumber, contours in data.items(): if frameNumber%5000 == 0: - print(f"{round(frameNumber/max(data.keys()), 2)}% done with Layer extraction") + print(f"{int(round(frameNumber/max(data.keys()), 2)*100)}% done with Layer extraction") - for frameNumber in sorted(data): - contours = data[frameNumber] for (x,y,w,h) in contours: foundLayer = False for i in set(range(0, len(self.layers))).difference(set(oldLayerIDs)): @@ -85,15 +83,10 @@ class LayerFactory: def fillLayers(self, footagePath, resizeWidth): for i in range(len(self.layers)): + if i % 20 == 0: + print(f"filled {int(round(i/len(self.layers),2)*100)}% of all Layers") self.layers[i].fill(footagePath, resizeWidth) def sortLayers(self): # straight bubble self.layers.sort(key = lambda c:c.lastFrame) - - - - - - - diff --git a/VideoReader.py b/VideoReader.py new file mode 100644 index 0000000..8020441 --- /dev/null +++ b/VideoReader.py @@ -0,0 +1,70 @@ + +import multiprocessing +import cv2 +from time import sleep +from queue import Queue +import threading +class VideoReader: + + #buffer = [(frameNumber, frame)] + + def __init__(self, videoPath): + if videoPath is None: + print("Video reader needs a videoPath!") + return None + + self.videoPath = videoPath + self.lastFrame = 0 + self.buffer = Queue(16) + self.vc = cv2.VideoCapture(videoPath) + self.stopped = False + res, image = self.vc.read() + self.w = image.shape[1] + self.h = image.shape[0] + + print(f"Video reader startet with buffer length of 16") + + + def pop(self): + return self.buffer.get(block=True) + + def get(self): + return self.buffer[-1] + + def fillBuffer(self): + if self.buffer.full(): + print("VideoReader::fillBuffer was called when buffer was full.") + self.endFrame = int(self.vc.get(cv2.CAP_PROP_FRAME_COUNT)) + self.endFrame = 10*60*30 + self.thread = threading.Thread(target=self.readFrames, args=()) + self.thread.start() + + def stop(self): + self.thread.join() + self.vc.release() + + def readFrames(self): + while self.lastFrame < self.endFrame: + if not self.buffer.full(): + res, frame = self.vc.read() + if res: + self.buffer.put((self.lastFrame, frame)) + self.lastFrame += 1 + else: + sleep(0.5) + self.stopped = True + + def videoEnded(self): + if self.stopped: + return True + else: + return False + + + + + + + + + diff --git a/__pycache__/Analyzer.cpython-37.pyc b/__pycache__/Analyzer.cpython-37.pyc index 405b571..31b382e 100644 Binary files a/__pycache__/Analyzer.cpython-37.pyc and b/__pycache__/Analyzer.cpython-37.pyc differ diff --git a/__pycache__/ContourExctractor.cpython-37.pyc b/__pycache__/ContourExctractor.cpython-37.pyc index 49b7ae7..e56e16b 100644 Binary files a/__pycache__/ContourExctractor.cpython-37.pyc and b/__pycache__/ContourExctractor.cpython-37.pyc differ diff --git a/__pycache__/LayerFactory.cpython-37.pyc b/__pycache__/LayerFactory.cpython-37.pyc index 84f0163..def7d12 100644 Binary files a/__pycache__/LayerFactory.cpython-37.pyc and b/__pycache__/LayerFactory.cpython-37.pyc differ diff --git a/main.py b/main.py index fd16202..05d607b 100644 --- a/main.py +++ b/main.py @@ -4,20 +4,22 @@ from ContourExctractor import ContourExtractor from Exporter import Exporter from LayerFactory import LayerFactory from Analyzer import Analyzer +from VideoReader import VideoReader import cv2 #TODO # finden von relevanten Stellen anhand von zu findenen metriken für vergleichsbilder def demo(): print("startup") - resizeWidth = 1024 + resizeWidth = 512 maxLayerLength = 1*60*30 - minLayerLength = 3 + minLayerLength = 30 start = time.time() footagePath = os.path.join(os.path.dirname(__file__), "./generate test footage/3.mp4") #analyzer = Analyzer(footagePath) #print("Time consumed reading video: ", time.time() - start) + contours = ContourExtractor().extractContours(footagePath, resizeWidth) print("Time consumed in working: ", time.time() - start) layerFactory = LayerFactory(contours)