started contoured diff instaed of rect

This commit is contained in:
Askill 2020-11-27 01:06:25 +01:00
parent b6b77b08f5
commit a01b5d2a2f
8 changed files with 58 additions and 128 deletions

View File

@ -24,12 +24,17 @@ class ContourExtractor:
#extracedContours = {frame_number: [(contour, (x,y,w,h)), ...], } #extracedContours = {frame_number: [(contour, (x,y,w,h)), ...], }
# dict with frame numbers as keys and the contour bounds of every contour for that frame # dict with frame numbers as keys and the contour bounds of every contour for that frame
def getextractedContours(self): def getExtractedContours(self):
return self.extractedContours return self.extractedContours
def getExtractedMasks(self):
return self.extractedMasks
def __init__(self, config): def __init__(self, config):
self.frameBuffer = Queue(16) self.frameBuffer = Queue(16)
self.extractedContours = dict() self.extractedContours = dict()
self.extractedMasks = dict()
self.min_area = config["min_area"] self.min_area = config["min_area"]
self.max_area = config["max_area"] self.max_area = config["max_area"]
self.threashold = config["threashold"] self.threashold = config["threashold"]
@ -64,7 +69,7 @@ class ContourExtractor:
frameCount = tmpData[-1][0] frameCount = tmpData[-1][0]
videoReader.thread.join() videoReader.thread.join()
return self.extractedContours return self.extractedContours, self.extractedMasks
def getContours(self, data): def getContours(self, data):
frameCount, frame = data frameCount, frame = data
@ -89,17 +94,21 @@ class ContourExtractor:
cnts = imutils.grab_contours(cnts) cnts = imutils.grab_contours(cnts)
contours = [] contours = []
masks = []
for c in cnts: for c in cnts:
ca = cv2.contourArea(c) ca = cv2.contourArea(c)
(x, y, w, h) = cv2.boundingRect(c)
#ca = (x+w)*(y+h)
if ca < self.min_area or ca > self.max_area: if ca < self.min_area or ca > self.max_area:
continue continue
(x, y, w, h) = cv2.boundingRect(c)
contours.append((x, y, w, h)) contours.append((x, y, w, h))
masks.append(np.packbits(np.copy(thresh[y:y+h,x:x+w]), axis=0))
if len(contours) != 0 and contours is not None: if len(contours) != 0 and contours is not None:
# this should be thread safe # this should be thread safe
self.extractedContours[frameCount] = contours self.extractedContours[frameCount] = contours
self.extractedMasks[frameCount] = masks
def prepareFrame(self, frame): def prepareFrame(self, frame):
frame = imutils.resize(frame, width=self.resizeWidth) frame = imutils.resize(frame, width=self.resizeWidth)
@ -141,3 +150,4 @@ class ContourExtractor:
for jj in range(0,averageFrames-1): for jj in range(0,averageFrames-1):
avg += self.prepareFrame(frames[j-jj][1])/averageFrames avg += self.prepareFrame(frames[j-jj][1])/averageFrames
self.averages[frameNumber] = np.array(np.round(avg), dtype=np.uint8) self.averages[frameNumber] = np.array(np.round(avg), dtype=np.uint8)
#self.averages[frameNumber] = self.prepareFrame(frames[j-averageFrames - 1][1])

View File

@ -104,7 +104,10 @@ class Exporter:
frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB) frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
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 i in range(0, len(layer.bounds[frameCount - layer.startFrame])):
(x, y, w, h) = layer.bounds[frameCount - layer.startFrame][i]
mask = layer.masks[frameCount - layer.startFrame][i]
if x is None: if x is None:
break break
factor = videoReader.w / self.resizeWidth factor = videoReader.w / self.resizeWidth
@ -112,9 +115,11 @@ class Exporter:
y = int(y * factor) y = int(y * factor)
w = int(w * factor) w = int(w * factor)
h = int(h * factor) h = int(h * factor)
mask = imutils.resize(mask, width=w, height=h+1)*255
mask = np.resize(mask, (h,w))
# 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] = frame2[y:y+h, x:x+w]/2 + frame[y:y+h, x:x+w]/2 frame2[y:y+h, x:x+w] = cv2.bitwise_and(frame2[y:y+h, x:x+w],frame2[y:y+h, x:x+w], mask)# + frame2[y:y+h, x:x+w]/2
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) 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)
@ -132,7 +137,7 @@ class Exporter:
def exportRawData(self, layers, contours): def exportRawData(self, layers, contours):
with open(self.config["importPath"], "wb+") as file: with open(self.config["importPath"], "wb+") as file:
pickle.dump((layers, contours), file) pickle.dump((layers, contours, masks), file)

View File

@ -7,5 +7,5 @@ class Importer:
def importRawData(self): def importRawData(self):
print("Loading previous results") print("Loading previous results")
with open(self.path, "rb") as file: with open(self.path, "rb") as file:
layers, contours = pickle.load(file) layers, contours, masks = pickle.load(file)
return (layers, contours) return (layers, contours, masks)

View File

@ -16,7 +16,7 @@ class Layer:
lastFrame = None lastFrame = None
length = None length = None
def __init__(self, startFrame, data, config): def __init__(self, startFrame, data, mask, config):
'''returns a Layer object '''returns a Layer object
Layers are collections of contours with a StartFrame, Layers are collections of contours with a StartFrame,
@ -32,21 +32,26 @@ class Layer:
self.config = config self.config = config
self.data = [] self.data = []
self.bounds = [] self.bounds = []
self.masks = []
self.stats = dict() self.stats = dict()
self.bounds.append([data]) self.bounds.append([data])
self.masks.append([mask])
#print("Layer constructed") #print("Layer constructed")
def add(self, frameNumber, bound): def add(self, frameNumber, bound, mask):
'''Adds a bound to the Layer at the layer index which corresponds to the given framenumber''' '''Adds a bound to the Layer at the layer index which corresponds to the given framenumber'''
if self.startFrame + len(self.bounds) - 1 > frameNumber: if self.startFrame + len(self.bounds) - 1 > frameNumber:
if len(self.bounds[frameNumber - self.startFrame]) >= 1: if len(self.bounds[frameNumber - self.startFrame]) >= 1:
self.bounds[frameNumber - self.startFrame].append(bound) self.bounds[frameNumber - self.startFrame].append(bound)
self.masks[frameNumber - self.startFrame].append(mask)
else: else:
self.lastFrame = frameNumber self.lastFrame = frameNumber
self.bounds.append([bound]) self.bounds.append([bound])
self.masks.append([mask])
def calcStats(self): def calcStats(self):
'''calculates average distance, variation and deviation of layer movement'''
middles = [] middles = []
for i, bounds in enumerate(self.bounds): for i, bounds in enumerate(self.bounds):
for j, bound in enumerate(bounds): for j, bound in enumerate(bounds):
@ -79,92 +84,3 @@ class Layer:
self.length = len(self.bounds) self.length = len(self.bounds)
return self.length return self.length
def clusterDelete(self):
'''Uses a cluster analysis to remove contours which are not the result of movement'''
org = self.bounds
if len(org) == 1:
return
mapped = []
mapping = []
clusterCount = 1
noiseThreashold = self.config["noiseThreashold"]
# calculates the middle of each contour in the 2d bounds[] and saves it in 1d list
# and saves the 2d indexes in a mapping array
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
# the loop isn't nessecary (?) if the number of clusters is known, since it isn't the loop tries to optimize
kmeans = KMeans(init="random", n_clusters=clusterCount, n_init=10, max_iter=300, random_state=42)
kmeans.fit(mapped)
labels = list(kmeans.labels_)
maxm = 0
for x in set(labels):
y = labels.count(x)
if y > maxm:
maxm = y
# transformes the labels array
# new array:
# the index is the cluster id, the array is the id of the contour
# [
# [1,2,3]
# [3,4,5]
# [6,7,8,9]
# ]
classed = [[]]
for i, x in enumerate(list(labels)):
while len(classed) <= x:
classed.append([])
classed[x].append(i)
# calculates the euclidean distance (without the sqrt) of each point in a cluster to the cluster center
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)
# copy all contours of the clusters with more movement than the threshold
newContours = [[]]
for i, dis in enumerate(dists):
# copy contours which are spread out, delete rest by not copying them
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))
if dis > noiseThreashold:
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

@ -23,29 +23,28 @@ class LayerFactory:
if data is not None: if data is not None:
self.extractLayers(data) self.extractLayers(data)
def extractLayers(self, data = None): def extractLayers(self, data, maskArr):
'''Bundle given contours together into Layer Objects''' '''Bundle given contours together into Layer Objects'''
if self.data is None:
if data is None or len(data) == 0 :
print("LayerFactory data was none")
return None
else:
self.data = data
frameNumber = min(data) frameNumber = min(data)
contours = data[frameNumber] contours = data[frameNumber]
for contour in contours: masks = maskArr[frameNumber]
self.layers.append(Layer(frameNumber, contour, self.config))
for contour, mask in zip(contours, masks):
mask = np.unpackbits(mask, axis=0)
self.layers.append(Layer(frameNumber, contour, mask, self.config))
self.oldLayerIDs = [] self.oldLayerIDs = []
with ThreadPool(16) as pool: with ThreadPool(16) as pool:
for frameNumber in sorted(data.keys()): for frameNumber in sorted(data.keys()):
contours = data[frameNumber] contours = data[frameNumber]
masks = maskArr[frameNumber]
masks = [np.unpackbits(mask, axis=0) for mask, contours in zip(masks, contours)]
if frameNumber%5000 == 0: if frameNumber%5000 == 0:
print(f" {int(round(frameNumber/max(data.keys()), 2)*100)}% done with Layer extraction", end='\r') print(f" {int(round(frameNumber/max(data.keys()), 2)*100)}% done with Layer extraction", end='\r')
tmp = [[frameNumber, contour] for contour in contours] tmp = [[frameNumber, contour, mask] for contour, mask in zip(contours, masks)]
#pool.map(self.getLayers, tmp) #pool.map(self.getLayers, tmp)
for x in tmp: for x in tmp:
self.getLayers(x) self.getLayers(x)
@ -55,6 +54,7 @@ class LayerFactory:
def getLayers(self, data): def getLayers(self, data):
frameNumber = data[0] frameNumber = data[0]
bounds = data[1] bounds = data[1]
mask = data[2]
(x,y,w,h) = bounds (x,y,w,h) = bounds
tol = self.tolerance tol = self.tolerance
foundLayer = 0 foundLayer = 0
@ -81,13 +81,13 @@ class LayerFactory:
break break
(x2,y2,w2,h2) = bounds (x2,y2,w2,h2) = bounds
if self.contoursOverlay((x-tol,y+h+tol), (x+w+tol,y-tol), (x2,y2+h2), (x2+w2,y2)): 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)) self.layers[i].add(frameNumber, (x,y,w,h), mask)
foundLayer += 1 foundLayer += 1
foundLayerIDs.append(i) foundLayerIDs.append(i)
break break
if foundLayer == 0: if foundLayer == 0:
self.layers.append(Layer(frameNumber, (x,y,w,h), self.config)) self.layers.append(Layer(frameNumber, (x,y,w,h), mask, self.config))
if len(foundLayerIDs) > 1: if len(foundLayerIDs) > 1:
self.mergeLayers(foundLayerIDs) self.mergeLayers(foundLayerIDs)
@ -114,16 +114,23 @@ class LayerFactory:
dSF = layer2.startFrame - layer1.startFrame dSF = layer2.startFrame - layer1.startFrame
l1bounds = copy.deepcopy(layer1.bounds) l1bounds = copy.deepcopy(layer1.bounds)
l1masks = copy.deepcopy(layer1.masks)
for i in range(len(layer2.bounds)): for i in range(len(layer2.bounds)):
bounds = layer2.bounds[i] bounds = layer2.bounds[i]
masks = layer2.masks[i]
while dSF + i >= len(l1bounds): while dSF + i >= len(l1bounds):
l1bounds.append([]) l1bounds.append([])
for bound in bounds: while dSF + i >= len(l1masks):
l1masks.append([])
for bound, mask in zip(bounds, masks):
if bound not in l1bounds[dSF + i]: if bound not in l1bounds[dSF + i]:
l1bounds[dSF + i].append(bound) l1bounds[dSF + i].append(bound)
l1masks[dSF + i].append(mask)
layer1.bounds = l1bounds layer1.bounds = l1bounds
layer1.masks = l1masks
return layer1 return layer1

View File

@ -27,9 +27,8 @@ class LayerManager:
def transformLayers(self): def transformLayers(self):
print("'Cleaning' Layers") print("'Cleaning' Layers")
self.freeMin() self.freeMin()
self.sortLayers()
#self.cleanLayers2()
self.freeMax() self.freeMax()
self.sortLayers()
self.calcStats() self.calcStats()
def calcStats(self): def calcStats(self):
@ -110,10 +109,3 @@ class LayerManager:
def sortLayers(self): def sortLayers(self):
self.layers.sort(key = lambda c:c.startFrame) self.layers.sort(key = lambda c:c.startFrame)
def cleanLayers2(self):
#with ThreadPool(16) as pool:
# pool.map(self.getContours, tmpData)
for layer in self.layers:
layer.clusterDelete()

10
main.py
View File

@ -14,7 +14,7 @@ def main():
start = time.time() start = time.time()
config = Config() config = Config()
fileName = "x23.mp4" fileName = "3.mp4"
outputPath = os.path.join(os.path.dirname(__file__), "output") outputPath = os.path.join(os.path.dirname(__file__), "output")
dirName = os.path.join(os.path.dirname(__file__), "generate test footage") dirName = os.path.join(os.path.dirname(__file__), "generate test footage")
@ -26,14 +26,14 @@ def main():
config["w"], config["h"] = VideoReader(config).getWH() config["w"], config["h"] = VideoReader(config).getWH()
if not os.path.exists(config["importPath"]): if not os.path.exists(config["importPath"]):
contours = ContourExtractor(config).extractContours() contours, masks = ContourExtractor(config).extractContours()
print("Time consumed extracting: ", time.time() - start) print("Time consumed extracting: ", time.time() - start)
layerFactory = LayerFactory(config) layerFactory = LayerFactory(config)
layers = layerFactory.extractLayers(contours) layers = layerFactory.extractLayers(contours, masks)
else: else:
layers, contours = Importer(config).importRawData() layers, contours, masks = Importer(config).importRawData()
layerFactory = LayerFactory(config) layerFactory = LayerFactory(config)
layers = layerFactory.extractLayers(contours) layers = layerFactory.extractLayers(contours, masks)
layerManager = LayerManager(config, layers) layerManager = LayerManager(config, layers)
layerManager.transformLayers() layerManager.transformLayers()

Binary file not shown.

Before

Width:  |  Height:  |  Size: 314 KiB

After

Width:  |  Height:  |  Size: 364 KiB