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 numpy as np
import matplotlib.pyplot as plt
from VideoReader import VideoReader
from Application.VideoReader import VideoReader
from multiprocessing.pool import ThreadPool
import imutils

View File

@ -13,11 +13,11 @@ from threading import Thread
from multiprocessing import Queue, Process, Pool
from multiprocessing.pool import ThreadPool
import concurrent.futures
from VideoReader import VideoReader
from Application.VideoReader import VideoReader
from queue import Queue
import threading
from Config import Config
from Application.Config import Config
class ContourExtractor:

View File

@ -1,9 +1,9 @@
import imageio
import imutils
import numpy as np
from Layer import Layer
from Application.Layer import Layer
import cv2
from VideoReader import VideoReader
from Application.VideoReader import VideoReader
import pickle
class Exporter:
@ -49,6 +49,8 @@ class Exporter:
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]:
if x is None:
break
factor = videoReader.w / self.resizeWidth
x = int(x * factor)
y = int(y * factor)
@ -87,6 +89,8 @@ class Exporter:
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]:
if x is None:
break
factor = videoReader.w / self.resizeWidth
x = int(x * 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 Config import Config
from Application.Layer import Layer
from Application.Config import Config
from multiprocessing.pool import ThreadPool
class LayerFactory:
@ -12,6 +12,7 @@ class LayerFactory:
self.maxLayerLength = config["maxLayerLength"]
self.resizeWidth = config["resizeWidth"]
self.footagePath = config["inputPath"]
self.config = config
print("LayerFactory constructed")
self.data = data
if data is not None:
@ -25,7 +26,11 @@ class LayerFactory:
for i, layer in enumerate(self.layers):
checks = 0
for bound in layer.bounds[0]:
if bound[0] is None:
continue
for bound2 in layer.bounds[-1]:
if bound2[0] is None:
continue
if abs(bound[0] - bound2[0]) < 10:
checks += 1
if abs(bound[1] - bound2[1]) < 10:
@ -58,7 +63,7 @@ class LayerFactory:
frameNumber = min(data)
contours = data[frameNumber]
for contour in contours:
self.layers.append(Layer(frameNumber, contour))
self.layers.append(Layer(frameNumber, contour, self.config))
self.oldLayerIDs = []
@ -72,9 +77,12 @@ class LayerFactory:
#pool.map_async(self.getLayers, tmp)
for x in tmp:
self.getLayers(x)
self.freeData()
self.sortLayers()
self.sortLayers()
self.cleanLayers()
self.freeData()
return self.layers
def getLayers(self, data):
@ -98,7 +106,7 @@ class LayerFactory:
break
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):
# If one rectangle is on left side of other
@ -117,3 +125,7 @@ class LayerFactory:
def sortLayers(self):
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 queue import Queue
import threading
from Config import Config
from Application.Config import Config
class VideoReader:
listOfFrames = None
w = 0
h = 0
def __init__(self, config, setOfFrames = None):
videoPath = config["inputPath"]
if videoPath is None:
print("Video reader needs a videoPath!")
print("ERROR: Video reader needs a videoPath!")
return None
self.videoPath = videoPath
@ -24,11 +24,15 @@ class VideoReader:
self.buffer = Queue(config["videoBufferLength"])
self.vc = cv2.VideoCapture(videoPath)
self.stopped = False
self.getWH()
if setOfFrames is not None:
self.listOfFrames = sorted(setOfFrames)
def getWH(self):
res, image = self.vc.read()
self.w = image.shape[1]
self.h = image.shape[0]
if setOfFrames is not None:
self.listOfFrames = sorted(setOfFrames)
return (self.w, self.h)
def pop(self):
return self.buffer.get(block=True)
@ -91,29 +95,3 @@ class VideoReader:
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

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 time
from ContourExctractor import ContourExtractor
from Exporter import Exporter
from LayerFactory import LayerFactory
from Analyzer import Analyzer
from Config import Config
from Importer import Importer
import cv2
from Application.ContourExctractor import ContourExtractor
from Application.Exporter import Exporter
from Application.LayerFactory import LayerFactory
from Application.Analyzer import Analyzer
from Application.Config import Config
from Application.Importer import Importer
from Application.VideoReader import VideoReader
#TODO
# finden von relevanten Stellen anhand von zu findenen metriken für vergleichsbilder
@ -15,10 +15,14 @@ def demo():
start = time.time()
config = Config()
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["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:
#ana = Analyzer(config)
#ref = ana.avg

Binary file not shown.