diff --git a/Analyzer.py b/Analyzer.py index d6f936b..8e3f7f4 100644 --- a/Analyzer.py +++ b/Analyzer.py @@ -10,35 +10,48 @@ import _thread import imageio import numpy as np import matplotlib.pyplot as plt +from VideoReader import VideoReader +from multiprocessing.pool import ThreadPool +import imutils + class Analyzer: - def __init__(self, videoPath): + + def __init__(self, config): print("Analyzer constructed") - data = self.readIntoMem(videoPath) - - vs = cv2.VideoCapture(videoPath) - threashold = 13 - res, image = vs.read() - firstFrame = None - i = 0 - diff = [] - while res: - res, frame = vs.read() - if not res: - break - frame = imutils.resize(frame, width=500) - gray = cv2.cvtColor(frame, cv2.COLOR_BGR2LAB) - if firstFrame is None: - firstFrame = gray + videoReader = VideoReader(config) + videoReader.fillBuffer() + self.config = config + self.avg = imutils.resize(np.zeros((videoReader.h,videoReader.w,3),np.float), width=config["resizeWidth"]) + self.end = videoReader.endFrame + self.c = 0 + start = time.time() + fak = 10 + while not videoReader.videoEnded(): + self.c, frame = videoReader.pop() + if not self.c%fak == 0: continue - frameDelta = cv2.absdiff(gray, firstFrame) - thresh = cv2.threshold(frameDelta, threashold, 255, cv2.THRESH_BINARY)[1] - diff.append(np.count_nonzero(thresh)) - i+=1 - if i % (60*30) == 0: - print("Minutes processed: ", i/(60*30)) - #print(diff) + if videoReader.endFrame - self.c <= fak: + break + frame = imutils.resize(frame, width=self.config["resizeWidth"]) + + + self.avg += frame.astype(np.float)/(self.end/fak) + if self.c%(1800*6) == 0: + print(f"{self.c/(60*30)} Minutes processed in {round((time.time() - start), 2)} each") + start = time.time() + + #print("done") + videoReader.thread.join() + self.avg = np.array(np.round(self.avg), dtype=np.uint8) + #return self.avg + cv2.imshow("changes overlayed", self.avg) + cv2.waitKey(10) & 0XFF + + + + def average(self, frame): + frame = imutils.resize(frame[1], width=self.config["resizeWidth"]) + self.avg += frame.astype(np.float)/(self.end/5) + - plt.plot(diff) - plt.ylabel('some numbers') - plt.show() \ No newline at end of file diff --git a/Config.py b/Config.py index 2f7d858..9190513 100644 --- a/Config.py +++ b/Config.py @@ -2,17 +2,17 @@ class Config: c = { "min_area" : 500, - "max_area" : 9000, - "threashold" : 10, + "max_area" : 20000, + "threashold" : 13, "resizeWidth" : 512, "inputPath" : None, "outputPath": None, "maxLayerLength": 900, - "minLayerLength": 30, + "minLayerLength": 20, "tolerance": 10, "maxLength": None, "ttolerance": 10, - "videoBufferLength": 16 + "videoBufferLength": 1000 } def __init__(self): diff --git a/ContourExctractor.py b/ContourExctractor.py index db1b568..1d13598 100644 --- a/ContourExctractor.py +++ b/ContourExctractor.py @@ -16,7 +16,7 @@ import concurrent.futures from VideoReader import VideoReader from queue import Queue import threading -from multiprocessing.pool import ThreadPool + from Config import Config class ContourExtractor: @@ -38,6 +38,7 @@ class ContourExtractor: self.xDim = 0 self.yDim = 0 self.config = config + self.diff = [] print("ContourExtractor initiated") @@ -50,6 +51,7 @@ class ContourExtractor: videoReader.fillBuffer() frameCount, frame = videoReader.pop() + #init compare image frame = imutils.resize(frame, width=self.resizeWidth) gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY) @@ -57,36 +59,42 @@ class ContourExtractor: gray = cv2.GaussianBlur(gray, (5, 5), 0) self.firstFrame = gray - threads = 16 - start = time.time() + threads = self.config["videoBufferLength"] + self.start = time.time() with ThreadPool(threads) as pool: while not videoReader.videoEnded(): #FrameCount, frame = videoReader.pop() - if frameCount % (60*30) == 0: - print(f"{frameCount/(60*30)} Minutes processed in {round((time.time() - start), 2)} each") - start = time.time() + if videoReader.buffer.qsize() == 0: time.sleep(.5) tmpData = [videoReader.pop() for i in range(0, videoReader.buffer.qsize())] - frameCount = tmpData[-1][0] pool.map(self.getContours, tmpData) + #for data in tmpData: + #self.getContours(data) + frameCount = tmpData[-1][0] videoReader.thread.join() return self.extractedContours def getContours(self, data): frameCount, frame = data firstFrame = self.firstFrame + if frameCount % (60*30) == 0: + print(f"{frameCount/(60*30)} Minutes processed in {round((time.time() - self.start), 2)} each") + self.start = time.time() frame = imutils.resize(frame, width=self.resizeWidth) gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY) gray = cv2.GaussianBlur(gray, (5, 5), 0) 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 - thresh = cv2.dilate(thresh, None, iterations=4) + thresh = cv2.dilate(thresh, None, iterations=10) + #cv2.imshow("changes x", thresh) + #cv2.waitKey(10) & 0XFF cnts = cv2.findContours(thresh.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) + self.diff.append(np.count_nonzero(thresh)) cnts = imutils.grab_contours(cnts) contours = [] @@ -111,8 +119,7 @@ class ContourExtractor: frame = np.zeros(shape=[self.yDim, self.xDim, 3], dtype=np.uint8) frame = imutils.resize(frame, width=512) frame[y:y+v.shape[0], x:x+v.shape[1]] = v - cv2.imshow("changes overlayed", frame) - cv2.waitKey(10) & 0XFF + cv2.destroyAllWindows() def exportContours(self): diff --git a/Exporter.py b/Exporter.py index 18751bc..3fb8ee8 100644 --- a/Exporter.py +++ b/Exporter.py @@ -45,7 +45,7 @@ class Exporter: continue frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB) - frame2 = underlay + frame2 = np.copy(underlay) for layer in layers: if layer.startFrame <= frameCount and layer.startFrame + len(layer.bounds) > frameCount: for (x, y, w, h) in layer.bounds[frameCount - layer.startFrame]: @@ -55,7 +55,8 @@ class Exporter: w = int(w * factor) h = int(h * factor) - frame2[y:y+h, x:x+w] = frame[y:y+h, x:x+w] + frame2[y:y+h, x:x+w] = np.copy(frame[y:y+h, x:x+w]) + cv2.putText(frame2, str(int(frameCount/self.fps)), (int(x+w/2), int(y+h/2)), cv2.FONT_HERSHEY_SIMPLEX, 1,(255,255,255), 2) writer.append_data(frame2) @@ -94,7 +95,10 @@ class Exporter: # if exportFrame as index instead of frameCount - layer.startFrame then we have layer after layer frame2 = frames[frameCount - layer.startFrame] frame2[y:y+h, x:x+w] = frame[y:y+h, x:x+w] + frames[frameCount - layer.startFrame] = np.copy(frame2) + cv2.putText(frames[frameCount - layer.startFrame], str(int(frameCount/self.fps)), (int(x+w/2), int(y+h/2)), cv2.FONT_HERSHEY_SIMPLEX, 1,(255,255,255), 2) + videoReader.thread.join() self.fps = videoReader.getFPS() diff --git a/LayerFactory.py b/LayerFactory.py index 1831b96..afc8b5a 100644 --- a/LayerFactory.py +++ b/LayerFactory.py @@ -1,5 +1,6 @@ from Layer import Layer from Config import Config +from multiprocessing.pool import ThreadPool class LayerFactory: def __init__(self, config, data=None): @@ -23,10 +24,12 @@ class LayerFactory: layers = [] for i, layer in enumerate(self.layers): checks = 0 - if abs(self.layers[i].bounds[0][0][0] - self.layers[i].bounds[-1][0][0]) < 5: - checks += 1 - if abs(self.layers[i].bounds[0][0][1] - self.layers[i].bounds[-1][0][1]) < 5: - checks += 1 + for bound in layer.bounds[0]: + for bound2 in layer.bounds[-1]: + if abs(bound[0] - bound2[0]) < 10: + checks += 1 + if abs(bound[1] - bound2[1]) < 10: + checks += 1 if checks <= 2: layers.append(layer) self.layers = layers @@ -43,7 +46,7 @@ class LayerFactory: def extractLayers(self, data = None): - tol = self.tolerance + if self.data is None: if data is None: @@ -57,35 +60,46 @@ class LayerFactory: for contour in contours: self.layers.append(Layer(frameNumber, contour)) - oldLayerIDs = [] - # inserts all the fucking contours as layers? - for frameNumber in sorted(data.keys()): - contours = data[frameNumber] - if frameNumber%5000 == 0: - print(f"{int(round(frameNumber/max(data.keys()), 2)*100)}% done with Layer extraction") + self.oldLayerIDs = [] + + with ThreadPool(16) as pool: + for frameNumber in sorted(data.keys()): + contours = data[frameNumber] + if frameNumber%5000 == 0: + print(f"{int(round(frameNumber/max(data.keys()), 2)*100)}% done with Layer extraction") - for (x,y,w,h) in contours: - foundLayer = False - for i in set(range(0, len(self.layers))).difference(set(oldLayerIDs)): - if frameNumber - self.layers[i].lastFrame > self.ttolerance: - oldLayerIDs.append(i) - continue + tmp = [[frameNumber, contour] for contour in contours] + #pool.map_async(self.getLayers, tmp) + for x in tmp: + self.getLayers(x) - for bounds in self.layers[i].bounds[-1]: - if bounds is None: - break - (x2,y2,w2,h2) = bounds - if self.contoursOverlay((x-tol,y+h+tol), (x+w+tol,y-tol), (x2,y2+h2), (x2+w2,y2)): - self.layers[i].add(frameNumber, (x,y,w,h)) - foundLayer = True - break - - if not foundLayer: - self.layers.append(Layer(frameNumber, (x,y,w,h))) self.freeData() self.sortLayers() return self.layers + def getLayers(self, data): + frameNumber = data[0] + bounds = data[1] + (x,y,w,h) = bounds + tol = self.tolerance + foundLayer = False + for i in set(range(0, len(self.layers))).difference(set(self.oldLayerIDs)): + if frameNumber - self.layers[i].lastFrame > self.ttolerance: + self.oldLayerIDs.append(i) + continue + + for bounds in self.layers[i].bounds[-1]: + if bounds is None: + break + (x2,y2,w2,h2) = bounds + if self.contoursOverlay((x-tol,y+h+tol), (x+w+tol,y-tol), (x2,y2+h2), (x2+w2,y2)): + self.layers[i].add(frameNumber, (x,y,w,h)) + foundLayer = True + break + + if not foundLayer: + self.layers.append(Layer(frameNumber, (x,y,w,h))) + def contoursOverlay(self, l1, r1, l2, r2): # If one rectangle is on left side of other if(l1[0] >= r2[0] or l2[0] >= r1[0]): diff --git a/VideoReader.py b/VideoReader.py index b3f9de1..528e6ba 100644 --- a/VideoReader.py +++ b/VideoReader.py @@ -68,28 +68,46 @@ class VideoReader: self.endFrame = self.listOfFrames[-1] while self.lastFrame < self.endFrame: - if not self.buffer.full(): - if self.lastFrame in self.listOfFrames: - res, frame = self.vc.read() - if res: - self.buffer.put((self.lastFrame, frame)) - # since the list is sorted the first element is always the lowest relevant framenumber - # [0,1,2,3,32,33,34,35,67,68,69] - self.listOfFrames.pop(0) - self.lastFrame += 1 - else: - # if current Frame number is not in list of Frames, we can skip a few frames - self.vc.set(1, self.listOfFrames[0]) - self.lastFrame = self.listOfFrames[0] + if self.lastFrame in self.listOfFrames: + res, frame = self.vc.read() + if res: + self.buffer.put((self.lastFrame, frame)) + # since the list is sorted the first element is always the lowest relevant framenumber + # [0,1,2,3,32,33,34,35,67,68,69] + self.listOfFrames.pop(0) + self.lastFrame += 1 else: - sleep(0.1) + # if current Frame number is not in list of Frames, we can skip a few frames + self.vc.set(1, self.listOfFrames[0]) + self.lastFrame = self.listOfFrames[0] + self.stopped = True def videoEnded(self): - return self.stopped + if self.stopped and self.buffer.empty(): + return True + else: + return False def getFPS(self): return self.vc.get(cv2.CAP_PROP_FPS) + + def get_file_metadata(self, path, filename, metadata): + # Path shouldn't end with backslash, i.e. "E:\Images\Paris" + # filename must include extension, i.e. "PID manual.pdf" + # Returns dictionary containing all file metadata. + sh = win32com.client.gencache.EnsureDispatch('Shell.Application', 0) + ns = sh.NameSpace(path) + + # Enumeration is necessary because ns.GetDetailsOf only accepts an integer as 2nd argument + file_metadata = dict() + item = ns.ParseName(str(filename)) + for ind, attribute in enumerate(metadata): + attr_value = ns.GetDetailsOf(item, ind) + if attr_value: + file_metadata[attribute] = attr_value + + return file_metadata diff --git a/main.py b/main.py index ac4e1d6..31a9c6e 100644 --- a/main.py +++ b/main.py @@ -4,7 +4,6 @@ from ContourExctractor import ContourExtractor from Exporter import Exporter from LayerFactory import LayerFactory from Analyzer import Analyzer -from VideoReader import VideoReader from Config import Config from Importer import Importer import cv2 @@ -21,6 +20,8 @@ def demo(): config["outputPath"] = os.path.join(os.path.dirname(__file__), "output/short.mp4") if config["importPath"] is None: + #ana = Analyzer(config) + #ref = ana.avg contours = ContourExtractor(config).extractContours() print("Time consumed extracting: ", time.time() - start) layerFactory = LayerFactory(config) @@ -30,7 +31,7 @@ def demo(): exporter = Exporter(config) exporter.exportRawData(layers) - exporter.exportOverlayed(layers) + exporter.exportLayers(layers) print("Total time: ", time.time() - start) diff --git a/output/3.txt b/output/3.txt new file mode 100644 index 0000000..0606308 Binary files /dev/null and b/output/3.txt differ diff --git a/output/short.txt b/output/short.txt index a552346..774f013 100644 Binary files a/output/short.txt and b/output/short.txt differ