added cluster analyses on layers

This commit is contained in:
Askill 2020-10-18 17:36:34 +02:00
parent 559fef7bc4
commit fe42e7b5c7
11 changed files with 185 additions and 103 deletions

View File

@ -10,7 +10,7 @@ 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 Application.VideoReader import VideoReader
from multiprocessing.pool import ThreadPool from multiprocessing.pool import ThreadPool
import imutils import imutils

View File

@ -13,11 +13,11 @@ 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 Application.VideoReader import VideoReader
from queue import Queue from queue import Queue
import threading import threading
from Config import Config from Application.Config import Config
class ContourExtractor: class ContourExtractor:

View File

@ -1,9 +1,9 @@
import imageio import imageio
import imutils import imutils
import numpy as np import numpy as np
from Layer import Layer from Application.Layer import Layer
import cv2 import cv2
from VideoReader import VideoReader from Application.VideoReader import VideoReader
import pickle import pickle
class Exporter: class Exporter:
@ -49,6 +49,8 @@ class Exporter:
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]:
if x is None:
break
factor = videoReader.w / self.resizeWidth factor = videoReader.w / self.resizeWidth
x = int(x * factor) x = int(x * factor)
y = int(y * factor) y = int(y * factor)
@ -87,6 +89,8 @@ class Exporter:
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]:
if x is None:
break
factor = videoReader.w / self.resizeWidth factor = videoReader.w / self.resizeWidth
x = int(x * factor) x = int(x * factor)
y = int(y * factor) y = int(y * factor)

137
Application/Layer.py Normal file
View File

@ -0,0 +1,137 @@
import numpy as np
import cv2
import imutils
from kneed import KneeLocator
from sklearn.datasets import make_blobs
from sklearn.cluster import KMeans
from sklearn.metrics import silhouette_score
from sklearn.preprocessing import StandardScaler
import matplotlib.pyplot as plt
class Layer:
#bounds = [[(x,y,w,h), ],]
startFrame = None
lastFrame = None
length = None
def __init__(self, startFrame, data, config):
self.startFrame = startFrame
self.lastFrame = startFrame
self.config = config
self.data = []
self.bounds = []
self.bounds.append([data])
#print("Layer constructed")
def add(self, frameNumber, data):
if not self.startFrame + len(self.bounds) < frameNumber:
if len(self.bounds[self.startFrame - frameNumber]) >= 1:
self.bounds[self.startFrame - frameNumber].append(data)
else:
self.lastFrame = frameNumber
self.bounds.append([data])
self.getLength()
def getLength(self):
self.length = len(self.bounds)
return self.length
def fill(self, inputPath, resizeWidth):
'''reads in the contour data, needed for export'''
cap = cv2.VideoCapture(inputPath)
self.data = [None]*len(self.bounds)
i = 0
cap.set(1, self.startFrame)
while i < len(self.bounds):
ret, frame = cap.read()
if ret:
frame = imutils.resize(frame, width=resizeWidth)
(x, y, w, h) = self.bounds[i]
self.data[i] = frame[y:y+h, x:x+w]
i+=1
cap.release()
def clusterDelete(self):
org = self.bounds
mapped = []
mapping = []
clusterCount = 1
noiseSensitivity = 3/4
noiseThreashold = 0.05
for i, bounds in enumerate(org):
for j, bound in enumerate(bounds):
x = (bound[0] + bound[2]/2) / self.config["w"]
y = (bound[1] + bound[3]/2) / self.config["w"]
mapped.append(list((x,y)))
mapping.append([i,j])
mapped = np.array(mapped).astype(np.float16)
labels = []
centers = []
kmeans = None
while True:
kmeans = KMeans(init="random", n_clusters=clusterCount, n_init=5, max_iter=300, random_state=42)
kmeans.fit(mapped)
labels = list(kmeans.labels_)
if kmeans.n_features_in_ < clusterCount:
break
maxm = 0
for x in set(labels):
y = labels.count(x)
if y > maxm:
maxm = y
if maxm > len(mapped)*(noiseSensitivity):
clusterCount += 1
else:
centers = kmeans.cluster_centers_
break
classed = [[]]
for i, x in enumerate(list(labels)):
while len(classed) <= x:
classed.append([])
classed[x].append(i)
dists = []
for num, cen in enumerate(centers):
dist = 0
for i in classed[num]:
dist += (mapped[i][0]-cen[0])**2 + (mapped[i][1]-cen[1])**2
dist/=len(classed[num])
dists.append(dist*1000)
newContours = [[]]
for i, dis in enumerate(dists):
# copy contours which are spread out, delete rest by not yopying them
if dis > noiseThreashold:
for j in classed[i]:
x, y = mapping[j]
while x >= len(newContours):
newContours.append([])
while y > len(newContours[x]):
newContours[x].append((None, None, None, None))
newContours[x].append(org[x][y])
self.bounds = newContours
print(f"{clusterCount} clusters identified {dists}")
#fig, ax = plt.subplots()
#x=mapped[:,0]
#y=mapped[:,1]
#ax.scatter(x, y, labels, s=10, cmap="rainbow")
#ax.grid(True)
#plt.show()
#print("done")

View File

@ -1,5 +1,5 @@
from Layer import Layer from Application.Layer import Layer
from Config import Config from Application.Config import Config
from multiprocessing.pool import ThreadPool from multiprocessing.pool import ThreadPool
class LayerFactory: class LayerFactory:
@ -12,6 +12,7 @@ class LayerFactory:
self.maxLayerLength = config["maxLayerLength"] self.maxLayerLength = config["maxLayerLength"]
self.resizeWidth = config["resizeWidth"] self.resizeWidth = config["resizeWidth"]
self.footagePath = config["inputPath"] self.footagePath = config["inputPath"]
self.config = config
print("LayerFactory constructed") print("LayerFactory constructed")
self.data = data self.data = data
if data is not None: if data is not None:
@ -25,7 +26,11 @@ class LayerFactory:
for i, layer in enumerate(self.layers): for i, layer in enumerate(self.layers):
checks = 0 checks = 0
for bound in layer.bounds[0]: for bound in layer.bounds[0]:
if bound[0] is None:
continue
for bound2 in layer.bounds[-1]: for bound2 in layer.bounds[-1]:
if bound2[0] is None:
continue
if abs(bound[0] - bound2[0]) < 10: if abs(bound[0] - bound2[0]) < 10:
checks += 1 checks += 1
if abs(bound[1] - bound2[1]) < 10: if abs(bound[1] - bound2[1]) < 10:
@ -58,7 +63,7 @@ class LayerFactory:
frameNumber = min(data) frameNumber = min(data)
contours = data[frameNumber] contours = data[frameNumber]
for contour in contours: for contour in contours:
self.layers.append(Layer(frameNumber, contour)) self.layers.append(Layer(frameNumber, contour, self.config))
self.oldLayerIDs = [] self.oldLayerIDs = []
@ -72,9 +77,12 @@ class LayerFactory:
#pool.map_async(self.getLayers, tmp) #pool.map_async(self.getLayers, tmp)
for x in tmp: for x in tmp:
self.getLayers(x) self.getLayers(x)
self.freeData() self.freeData()
self.sortLayers() self.sortLayers()
self.cleanLayers()
self.freeData()
return self.layers return self.layers
def getLayers(self, data): def getLayers(self, data):
@ -98,7 +106,7 @@ class LayerFactory:
break break
if not foundLayer: if not foundLayer:
self.layers.append(Layer(frameNumber, (x,y,w,h))) self.layers.append(Layer(frameNumber, (x,y,w,h), self.config))
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
@ -117,3 +125,7 @@ class LayerFactory:
def sortLayers(self): def sortLayers(self):
self.layers.sort(key = lambda c:c.startFrame) self.layers.sort(key = lambda c:c.startFrame)
def cleanLayers(self):
for layer in self.layers:
layer.clusterDelete()

View File

@ -4,18 +4,18 @@ import cv2
from time import sleep from time import sleep
from queue import Queue from queue import Queue
import threading import threading
from Config import Config from Application.Config import Config
class VideoReader: class VideoReader:
listOfFrames = None listOfFrames = None
w = 0
h = 0
def __init__(self, config, setOfFrames = None): def __init__(self, config, setOfFrames = None):
videoPath = config["inputPath"] videoPath = config["inputPath"]
if videoPath is None: if videoPath is None:
print("Video reader needs a videoPath!") print("ERROR: Video reader needs a videoPath!")
return None return None
self.videoPath = videoPath self.videoPath = videoPath
@ -24,11 +24,15 @@ class VideoReader:
self.buffer = Queue(config["videoBufferLength"]) self.buffer = Queue(config["videoBufferLength"])
self.vc = cv2.VideoCapture(videoPath) self.vc = cv2.VideoCapture(videoPath)
self.stopped = False self.stopped = False
self.getWH()
if setOfFrames is not None:
self.listOfFrames = sorted(setOfFrames)
def getWH(self):
res, image = self.vc.read() res, image = self.vc.read()
self.w = image.shape[1] self.w = image.shape[1]
self.h = image.shape[0] self.h = image.shape[0]
if setOfFrames is not None: return (self.w, self.h)
self.listOfFrames = sorted(setOfFrames)
def pop(self): def pop(self):
return self.buffer.get(block=True) return self.buffer.get(block=True)
@ -91,29 +95,3 @@ class VideoReader:
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

@ -1,53 +0,0 @@
import numpy as np
import cv2
import imutils
class Layer:
#bounds = [[(x,y,w,h), ],]
startFrame = None
lastFrame = None
length = None
def __init__(self, startFrame, data):
self.startFrame = startFrame
self.lastFrame = startFrame
self.data = []
self.bounds = []
self.bounds.append([data])
#print("Layer constructed")
def add(self, frameNumber, data):
if not self.startFrame + len(self.bounds) < frameNumber:
if len(self.bounds[self.startFrame - frameNumber]) >= 1:
self.bounds[self.startFrame - frameNumber].append(data)
else:
self.lastFrame = frameNumber
self.bounds.append([data])
self.getLength()
def getLength(self):
self.length = len(self.bounds)
return self.length
def fill(self, inputPath, resizeWidth):
'''reads in the contour data, needed for export'''
cap = cv2.VideoCapture(inputPath)
self.data = [None]*len(self.bounds)
i = 0
cap.set(1, self.startFrame)
while i < len(self.bounds):
ret, frame = cap.read()
if ret:
frame = imutils.resize(frame, width=resizeWidth)
(x, y, w, h) = self.bounds[i]
self.data[i] = frame[y:y+h, x:x+w]
i+=1
cap.release()

18
main.py
View File

@ -1,12 +1,12 @@
import os import os
import time import time
from ContourExctractor import ContourExtractor from Application.ContourExctractor import ContourExtractor
from Exporter import Exporter from Application.Exporter import Exporter
from LayerFactory import LayerFactory from Application.LayerFactory import LayerFactory
from Analyzer import Analyzer from Application.Analyzer import Analyzer
from Config import Config from Application.Config import Config
from Importer import Importer from Application.Importer import Importer
import cv2 from Application.VideoReader import VideoReader
#TODO #TODO
# finden von relevanten Stellen anhand von zu findenen metriken für vergleichsbilder # finden von relevanten Stellen anhand von zu findenen metriken für vergleichsbilder
@ -15,10 +15,14 @@ def demo():
start = time.time() start = time.time()
config = Config() config = Config()
config["inputPath"] = os.path.join(os.path.dirname(__file__), "generate test footage/3.mp4") config["inputPath"] = os.path.join(os.path.dirname(__file__), "generate test footage/3.mp4")
#config["importPath"] = os.path.join(os.path.dirname(__file__), "output/short.txt") #config["importPath"] = os.path.join(os.path.dirname(__file__), "output/short.txt")
config["outputPath"] = os.path.join(os.path.dirname(__file__), "output/short.mp4") config["outputPath"] = os.path.join(os.path.dirname(__file__), "output/short.mp4")
vr = VideoReader(config)
config["w"], config["h"] = vr.getWH()
if config["importPath"] is None: if config["importPath"] is None:
#ana = Analyzer(config) #ana = Analyzer(config)
#ref = ana.avg #ref = ana.avg

Binary file not shown.