added cluster analyses on layers
This commit is contained in:
parent
559fef7bc4
commit
fe42e7b5c7
|
|
@ -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
|
||||||
|
|
||||||
|
|
@ -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:
|
||||||
|
|
||||||
|
|
@ -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)
|
||||||
|
|
@ -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")
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -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()
|
||||||
|
|
@ -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
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
53
Layer.py
53
Layer.py
|
|
@ -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
18
main.py
|
|
@ -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
|
||||||
|
|
|
||||||
BIN
output/short.txt
BIN
output/short.txt
Binary file not shown.
Loading…
Reference in New Issue