This commit is contained in:
Askill 2020-10-18 00:02:05 +02:00
parent f907094d1a
commit 559fef7bc4
9 changed files with 145 additions and 88 deletions

View File

@ -10,35 +10,48 @@ import _thread
import imageio import imageio
import numpy as np import numpy as np
import matplotlib.pyplot as plt import matplotlib.pyplot as plt
from VideoReader import VideoReader
from multiprocessing.pool import ThreadPool
import imutils
class Analyzer: class Analyzer:
def __init__(self, videoPath):
def __init__(self, config):
print("Analyzer constructed") print("Analyzer constructed")
data = self.readIntoMem(videoPath) videoReader = VideoReader(config)
videoReader.fillBuffer()
vs = cv2.VideoCapture(videoPath) self.config = config
threashold = 13 self.avg = imutils.resize(np.zeros((videoReader.h,videoReader.w,3),np.float), width=config["resizeWidth"])
res, image = vs.read() self.end = videoReader.endFrame
firstFrame = None self.c = 0
i = 0 start = time.time()
diff = [] fak = 10
while res: while not videoReader.videoEnded():
res, frame = vs.read() self.c, frame = videoReader.pop()
if not res: if not self.c%fak == 0:
break
frame = imutils.resize(frame, width=500)
gray = cv2.cvtColor(frame, cv2.COLOR_BGR2LAB)
if firstFrame is None:
firstFrame = gray
continue continue
frameDelta = cv2.absdiff(gray, firstFrame) if videoReader.endFrame - self.c <= fak:
thresh = cv2.threshold(frameDelta, threashold, 255, cv2.THRESH_BINARY)[1] break
diff.append(np.count_nonzero(thresh)) frame = imutils.resize(frame, width=self.config["resizeWidth"])
i+=1
if i % (60*30) == 0:
print("Minutes processed: ", i/(60*30)) self.avg += frame.astype(np.float)/(self.end/fak)
#print(diff) 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()

View File

@ -2,17 +2,17 @@
class Config: class Config:
c = { c = {
"min_area" : 500, "min_area" : 500,
"max_area" : 9000, "max_area" : 20000,
"threashold" : 10, "threashold" : 13,
"resizeWidth" : 512, "resizeWidth" : 512,
"inputPath" : None, "inputPath" : None,
"outputPath": None, "outputPath": None,
"maxLayerLength": 900, "maxLayerLength": 900,
"minLayerLength": 30, "minLayerLength": 20,
"tolerance": 10, "tolerance": 10,
"maxLength": None, "maxLength": None,
"ttolerance": 10, "ttolerance": 10,
"videoBufferLength": 16 "videoBufferLength": 1000
} }
def __init__(self): def __init__(self):

View File

@ -16,7 +16,7 @@ import concurrent.futures
from VideoReader import VideoReader from VideoReader import VideoReader
from queue import Queue from queue import Queue
import threading import threading
from multiprocessing.pool import ThreadPool
from Config import Config from Config import Config
class ContourExtractor: class ContourExtractor:
@ -38,6 +38,7 @@ class ContourExtractor:
self.xDim = 0 self.xDim = 0
self.yDim = 0 self.yDim = 0
self.config = config self.config = config
self.diff = []
print("ContourExtractor initiated") print("ContourExtractor initiated")
@ -50,6 +51,7 @@ class ContourExtractor:
videoReader.fillBuffer() videoReader.fillBuffer()
frameCount, frame = videoReader.pop() frameCount, frame = videoReader.pop()
#init compare image #init compare image
frame = imutils.resize(frame, width=self.resizeWidth) frame = imutils.resize(frame, width=self.resizeWidth)
gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY) gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
@ -57,36 +59,42 @@ class ContourExtractor:
gray = cv2.GaussianBlur(gray, (5, 5), 0) gray = cv2.GaussianBlur(gray, (5, 5), 0)
self.firstFrame = gray self.firstFrame = gray
threads = 16 threads = self.config["videoBufferLength"]
start = time.time() self.start = time.time()
with ThreadPool(threads) as pool: with ThreadPool(threads) as pool:
while not videoReader.videoEnded(): while not videoReader.videoEnded():
#FrameCount, frame = videoReader.pop() #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: if videoReader.buffer.qsize() == 0:
time.sleep(.5) time.sleep(.5)
tmpData = [videoReader.pop() for i in range(0, videoReader.buffer.qsize())] tmpData = [videoReader.pop() for i in range(0, videoReader.buffer.qsize())]
frameCount = tmpData[-1][0]
pool.map(self.getContours, tmpData) pool.map(self.getContours, tmpData)
#for data in tmpData:
#self.getContours(data)
frameCount = tmpData[-1][0]
videoReader.thread.join() videoReader.thread.join()
return self.extractedContours return self.extractedContours
def getContours(self, data): def getContours(self, data):
frameCount, frame = data frameCount, frame = data
firstFrame = self.firstFrame 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) frame = imutils.resize(frame, width=self.resizeWidth)
gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY) 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]
# dilate the thresholded image to fill in holes, then find contours # 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) cnts = cv2.findContours(thresh.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
self.diff.append(np.count_nonzero(thresh))
cnts = imutils.grab_contours(cnts) cnts = imutils.grab_contours(cnts)
contours = [] contours = []
@ -111,8 +119,7 @@ class ContourExtractor:
frame = np.zeros(shape=[self.yDim, self.xDim, 3], dtype=np.uint8) frame = np.zeros(shape=[self.yDim, self.xDim, 3], dtype=np.uint8)
frame = imutils.resize(frame, width=512) frame = imutils.resize(frame, width=512)
frame[y:y+v.shape[0], x:x+v.shape[1]] = v frame[y:y+v.shape[0], x:x+v.shape[1]] = v
cv2.imshow("changes overlayed", frame)
cv2.waitKey(10) & 0XFF
cv2.destroyAllWindows() cv2.destroyAllWindows()
def exportContours(self): def exportContours(self):

View File

@ -45,7 +45,7 @@ class Exporter:
continue continue
frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB) frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
frame2 = underlay frame2 = np.copy(underlay)
for layer in layers: for layer in layers:
if layer.startFrame <= frameCount and layer.startFrame + len(layer.bounds) > frameCount: if layer.startFrame <= frameCount and layer.startFrame + len(layer.bounds) > frameCount:
for (x, y, w, h) in layer.bounds[frameCount - layer.startFrame]: for (x, y, w, h) in layer.bounds[frameCount - layer.startFrame]:
@ -55,7 +55,8 @@ class Exporter:
w = int(w * factor) w = int(w * factor)
h = int(h * 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) 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 # if exportFrame as index instead of frameCount - layer.startFrame then we have layer after layer
frame2 = frames[frameCount - layer.startFrame] frame2 = frames[frameCount - layer.startFrame]
frame2[y:y+h, x:x+w] = frame[y:y+h, x:x+w] frame2[y:y+h, x:x+w] = frame[y:y+h, x:x+w]
frames[frameCount - layer.startFrame] = np.copy(frame2) 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() videoReader.thread.join()
self.fps = videoReader.getFPS() self.fps = videoReader.getFPS()

View File

@ -1,5 +1,6 @@
from Layer import Layer from Layer import Layer
from Config import Config from Config import Config
from multiprocessing.pool import ThreadPool
class LayerFactory: class LayerFactory:
def __init__(self, config, data=None): def __init__(self, config, data=None):
@ -23,10 +24,12 @@ class LayerFactory:
layers = [] layers = []
for i, layer in enumerate(self.layers): for i, layer in enumerate(self.layers):
checks = 0 checks = 0
if abs(self.layers[i].bounds[0][0][0] - self.layers[i].bounds[-1][0][0]) < 5: for bound in layer.bounds[0]:
checks += 1 for bound2 in layer.bounds[-1]:
if abs(self.layers[i].bounds[0][0][1] - self.layers[i].bounds[-1][0][1]) < 5: if abs(bound[0] - bound2[0]) < 10:
checks += 1 checks += 1
if abs(bound[1] - bound2[1]) < 10:
checks += 1
if checks <= 2: if checks <= 2:
layers.append(layer) layers.append(layer)
self.layers = layers self.layers = layers
@ -43,7 +46,7 @@ class LayerFactory:
def extractLayers(self, data = None): def extractLayers(self, data = None):
tol = self.tolerance
if self.data is None: if self.data is None:
if data is None: if data is None:
@ -57,35 +60,46 @@ class LayerFactory:
for contour in contours: for contour in contours:
self.layers.append(Layer(frameNumber, contour)) self.layers.append(Layer(frameNumber, contour))
oldLayerIDs = [] self.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")
for (x,y,w,h) in contours: with ThreadPool(16) as pool:
foundLayer = False for frameNumber in sorted(data.keys()):
for i in set(range(0, len(self.layers))).difference(set(oldLayerIDs)): contours = data[frameNumber]
if frameNumber - self.layers[i].lastFrame > self.ttolerance: if frameNumber%5000 == 0:
oldLayerIDs.append(i) print(f"{int(round(frameNumber/max(data.keys()), 2)*100)}% done with Layer extraction")
continue
for bounds in self.layers[i].bounds[-1]: tmp = [[frameNumber, contour] for contour in contours]
if bounds is None: #pool.map_async(self.getLayers, tmp)
break for x in tmp:
(x2,y2,w2,h2) = bounds self.getLayers(x)
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.freeData()
self.sortLayers() self.sortLayers()
return self.layers 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): def contoursOverlay(self, l1, r1, l2, r2):
# If one rectangle is on left side of other # If one rectangle is on left side of other
if(l1[0] >= r2[0] or l2[0] >= r1[0]): if(l1[0] >= r2[0] or l2[0] >= r1[0]):

View File

@ -68,29 +68,47 @@ class VideoReader:
self.endFrame = self.listOfFrames[-1] self.endFrame = self.listOfFrames[-1]
while self.lastFrame < self.endFrame: while self.lastFrame < self.endFrame:
if not self.buffer.full(): if self.lastFrame in self.listOfFrames:
if self.lastFrame in self.listOfFrames: 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)) # since the list is sorted the first element is always the lowest relevant framenumber
# 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]
# [0,1,2,3,32,33,34,35,67,68,69] self.listOfFrames.pop(0)
self.listOfFrames.pop(0) self.lastFrame += 1
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]
else: 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 self.stopped = True
def videoEnded(self): def videoEnded(self):
return self.stopped if self.stopped and self.buffer.empty():
return True
else:
return False
def getFPS(self): def getFPS(self):
return self.vc.get(cv2.CAP_PROP_FPS) 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

View File

@ -4,7 +4,6 @@ from ContourExctractor import ContourExtractor
from Exporter import Exporter from Exporter import Exporter
from LayerFactory import LayerFactory from LayerFactory import LayerFactory
from Analyzer import Analyzer from Analyzer import Analyzer
from VideoReader import VideoReader
from Config import Config from Config import Config
from Importer import Importer from Importer import Importer
import cv2 import cv2
@ -21,6 +20,8 @@ def demo():
config["outputPath"] = os.path.join(os.path.dirname(__file__), "output/short.mp4") config["outputPath"] = os.path.join(os.path.dirname(__file__), "output/short.mp4")
if config["importPath"] is None: if config["importPath"] is None:
#ana = Analyzer(config)
#ref = ana.avg
contours = ContourExtractor(config).extractContours() contours = ContourExtractor(config).extractContours()
print("Time consumed extracting: ", time.time() - start) print("Time consumed extracting: ", time.time() - start)
layerFactory = LayerFactory(config) layerFactory = LayerFactory(config)
@ -30,7 +31,7 @@ def demo():
exporter = Exporter(config) exporter = Exporter(config)
exporter.exportRawData(layers) exporter.exportRawData(layers)
exporter.exportOverlayed(layers) exporter.exportLayers(layers)
print("Total time: ", time.time() - start) print("Total time: ", time.time() - start)

BIN
output/3.txt Normal file

Binary file not shown.

Binary file not shown.