manager().queue()
This commit is contained in:
parent
1d7a56c417
commit
99a699b039
|
|
@ -17,68 +17,64 @@ 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 get_extracted_contours(self):
|
||||||
return self.extractedContours
|
return self.extracted_contours
|
||||||
|
|
||||||
def getExtractedMasks(self):
|
def get_extracted_masks(self):
|
||||||
return self.extractedMasks
|
return self.extracted_masks
|
||||||
|
|
||||||
def __init__(self, config):
|
def __init__(self, config):
|
||||||
self.frameBuffer = Queue(16)
|
self.frame_buffer = Queue(16)
|
||||||
self.extractedContours = dict()
|
self.extracted_contours = dict()
|
||||||
self.extractedMasks = dict()
|
self.extracted_masks = 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"]
|
||||||
self.resizeWidth = config["resizeWidth"]
|
self.resize_width = config["resizeWidth"]
|
||||||
self.videoPath = config["inputPath"]
|
self.video_path = config["inputPath"]
|
||||||
self.xDim = 0
|
self.x_dim = 0
|
||||||
self.yDim = 0
|
self.y_dim = 0
|
||||||
self.config = config
|
self.config = config
|
||||||
self.lastFrames = None
|
self.last_frames = None
|
||||||
self.averages = dict()
|
self.averages = dict()
|
||||||
|
|
||||||
print("ContourExtractor initiated")
|
print("ContourExtractor initiated")
|
||||||
|
|
||||||
def extractContours(self):
|
def extract_contours(self):
|
||||||
self.start = time.time()
|
self.start = time.time()
|
||||||
with VideoReader(self.config) as videoReader:
|
with VideoReader(self.config) as videoReader:
|
||||||
self.fps = videoReader.getFPS()
|
self.fps = videoReader.get_fps()
|
||||||
self.length = videoReader.getLength()
|
self.length = videoReader.get_length()
|
||||||
|
|
||||||
with ThreadPool(2) as pool:
|
with ThreadPool(os.cpu_count()) as pool:
|
||||||
while True:
|
while True:
|
||||||
while not videoReader.videoEnded() and videoReader.buffer.qsize() == 0:
|
while not videoReader.video_ended() and videoReader.buffer.qsize() == 0:
|
||||||
time.sleep(0.5)
|
time.sleep(0.5)
|
||||||
|
|
||||||
tmpData = [videoReader.pop() for i in range(0, videoReader.buffer.qsize())]
|
tmp_data = [videoReader.pop() for i in range(0, videoReader.buffer.qsize())]
|
||||||
if videoReader.videoEnded():
|
if videoReader.video_ended():
|
||||||
break
|
break
|
||||||
pool.map(self.computeMovingAverage, (tmpData,))
|
pool.map(self.compute_moving_Average, (tmp_data,))
|
||||||
pool.map(self.async2, (tmpData,))
|
pool.map(self.get_contours, tmp_data)
|
||||||
|
|
||||||
return self.extractedContours, self.extractedMasks
|
return self.extracted_contours, self.extracted_masks
|
||||||
|
|
||||||
def async2(self, tmpData):
|
def get_contours(self, data):
|
||||||
with ThreadPool(os.cpu_count()) as pool2:
|
frame_count, frame = data
|
||||||
pool2.map(self.getContours, tmpData)
|
|
||||||
|
|
||||||
def getContours(self, data):
|
|
||||||
frameCount, frame = data
|
|
||||||
# wait for the reference frame, which is calculated by averaging some revious frames
|
# wait for the reference frame, which is calculated by averaging some revious frames
|
||||||
while frameCount not in self.averages:
|
while frame_count not in self.averages:
|
||||||
time.sleep(0.1)
|
time.sleep(0.1)
|
||||||
firstFrame = self.averages.pop(frameCount, None)
|
first_frame = self.averages.pop(frame_count, None)
|
||||||
|
|
||||||
if frameCount % (10 * self.fps) == 1:
|
if frame_count % (10 * self.fps) == 1:
|
||||||
print(
|
print(
|
||||||
f" \r \033[K {round((frameCount/self.fps)*100/self.length, 2)} % processed in {round(time.time() - self.start, 2)}s",
|
f" \r \033[K {round((frame_count/self.fps)*100/self.length, 2)} % processed in {round(time.time() - self.start, 2)}s",
|
||||||
end="\r",
|
end="\r",
|
||||||
)
|
)
|
||||||
|
|
||||||
gray = self.prepareFrame(frame)
|
gray = self.prepare_frame(frame)
|
||||||
frameDelta = cv2.absdiff(gray, firstFrame)
|
frame_delta = cv2.absdiff(gray, first_frame)
|
||||||
thresh = cv2.threshold(frameDelta, self.threashold, 255, cv2.THRESH_BINARY)[1]
|
thresh = cv2.threshold(frame_delta, 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=10)
|
thresh = cv2.dilate(thresh, None, iterations=10)
|
||||||
# cv2.imshow("changes x", thresh)
|
# cv2.imshow("changes x", thresh)
|
||||||
|
|
@ -100,44 +96,43 @@ class ContourExtractor:
|
||||||
|
|
||||||
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.extracted_contours[frame_count] = contours
|
||||||
self.extractedMasks[frameCount] = masks
|
self.extracted_masks[frame_count] = masks
|
||||||
|
|
||||||
def prepareFrame(self, frame):
|
def prepare_frame(self, frame):
|
||||||
frame = imutils.resize(frame, width=self.resizeWidth)
|
frame = imutils.resize(frame, width=self.resize_width)
|
||||||
gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
|
gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
|
||||||
gray = cv2.GaussianBlur(gray, (3, 3), 0)
|
gray = cv2.GaussianBlur(gray, (3, 3), 0)
|
||||||
return gray
|
return gray
|
||||||
|
|
||||||
def computeMovingAverage(self, frames):
|
def compute_moving_Average(self, frames):
|
||||||
avg = []
|
average_frames = self.config["avgNum"]
|
||||||
averageFrames = self.config["avgNum"]
|
|
||||||
|
|
||||||
if frames[0][0] < averageFrames:
|
if frames[0][0] < average_frames:
|
||||||
frame = frames[0][1]
|
frame = frames[0][1]
|
||||||
frame = self.prepareFrame(frame)
|
frame = self.prepare_frame(frame)
|
||||||
for j in range(0, len(frames)):
|
for j in range(0, len(frames)):
|
||||||
frameNumber, _ = frames[j]
|
frame_number, _ = frames[j]
|
||||||
self.averages[frameNumber] = frame
|
self.averages[frame_number] = frame
|
||||||
# put last x frames into a buffer
|
# put last x frames into a buffer
|
||||||
self.lastFrames = frames[-averageFrames:]
|
self.last_frames = frames[-average_frames:]
|
||||||
return
|
return
|
||||||
|
|
||||||
if self.lastFrames is not None:
|
if self.last_frames is not None:
|
||||||
frames = self.lastFrames + frames
|
frames = self.last_frames + frames
|
||||||
|
|
||||||
tmp = [[j, frames, averageFrames] for j in range(averageFrames, len(frames))]
|
tmp = [[j, frames, average_frames] for j in range(average_frames, len(frames))]
|
||||||
with ThreadPool(int(os.cpu_count())) as pool:
|
with ThreadPool(int(os.cpu_count())) as pool:
|
||||||
pool.map(self.averageDaFrames, tmp)
|
pool.map(self.average_da_frames, tmp)
|
||||||
|
|
||||||
self.lastFrames = frames[-averageFrames:]
|
self.last_frames = frames[-average_frames:]
|
||||||
|
|
||||||
def averageDaFrames(self, dat):
|
def average_da_frames(self, dat):
|
||||||
j, frames, averageFrames = dat
|
j, frames, average_frames = dat
|
||||||
frameNumber, frame = frames[j]
|
frame_number, frame = frames[j]
|
||||||
frame = self.prepareFrame(frame)
|
frame = self.prepare_frame(frame)
|
||||||
|
|
||||||
avg = frame / averageFrames
|
avg = frame / average_frames
|
||||||
for jj in range(0, averageFrames - 1):
|
for jj in range(0, average_frames - 1):
|
||||||
avg += self.prepareFrame(frames[j - jj][1]) / averageFrames
|
avg += self.prepare_frame(frames[j - jj][1]) / average_frames
|
||||||
self.averages[frameNumber] = np.array(np.round(avg), dtype=np.uint8)
|
self.averages[frame_number] = np.array(np.round(avg), dtype=np.uint8)
|
||||||
|
|
|
||||||
|
|
@ -11,114 +11,112 @@ from Application.VideoReader import VideoReader
|
||||||
|
|
||||||
|
|
||||||
class Exporter:
|
class Exporter:
|
||||||
fps = 30
|
|
||||||
|
|
||||||
def __init__(self, config):
|
def __init__(self, config):
|
||||||
self.footagePath = config["inputPath"]
|
self.footage_path = config["inputPath"]
|
||||||
self.outputPath = config["outputPath"]
|
self.output_path = config["outputPath"]
|
||||||
self.resizeWidth = config["resizeWidth"]
|
self.resize_width = config["resizeWidth"]
|
||||||
self.config = config
|
self.config = config
|
||||||
print("Exporter initiated")
|
print("Exporter initiated")
|
||||||
|
|
||||||
def export(self, layers, contours, masks, raw=True, overlayed=True, blackBackground=False, showProgress=False):
|
def export(self, layers, contours, masks, raw=True, overlayed=True, black_background=False, show_progress=False):
|
||||||
if raw:
|
if raw:
|
||||||
self.exportRawData(layers, contours, masks)
|
self.export_raw_data(layers, contours, masks)
|
||||||
if overlayed:
|
if overlayed:
|
||||||
self.exportOverlayed(layers, blackBackground, showProgress)
|
self.export_overlayed(layers, black_background, show_progress)
|
||||||
else:
|
else:
|
||||||
self.exportLayers(layers)
|
self.export_layers(layers)
|
||||||
|
|
||||||
def exportLayers(self, layers):
|
def export_layers(self, layers):
|
||||||
listOfFrames = self.makeListOfFrames(layers)
|
list_of_frames = self.make_list_of_frames(layers)
|
||||||
with VideoReader(self.config, listOfFrames) as videoReader:
|
with VideoReader(self.config, list_of_frames) as video_reader:
|
||||||
|
|
||||||
underlay = cv2.VideoCapture(self.footagePath).read()[1]
|
underlay = cv2.VideoCapture(self.footage_path).read()[1]
|
||||||
underlay = cv2.cvtColor(underlay, cv2.COLOR_BGR2RGB)
|
underlay = cv2.cvtColor(underlay, cv2.COLOR_BGR2RGB)
|
||||||
|
|
||||||
fps = videoReader.getFPS()
|
fps = video_reader.get_fps()
|
||||||
writer = imageio.get_writer(self.outputPath, fps=fps)
|
writer = imageio.get_writer(self.output_path, fps=fps)
|
||||||
|
|
||||||
start = time.time()
|
start = time.time()
|
||||||
for i, layer in enumerate(layers):
|
for i, layer in enumerate(layers):
|
||||||
print(f"\r {i}/{len(layers)} {round(i/len(layers)*100,2)}% {round((time.time() - start), 2)}s", end="\r")
|
print(f"\r {i}/{len(layers)} {round(i/len(layers)*100,2)}% {round((time.time() - start), 2)}s", end="\r")
|
||||||
if len(layer.bounds[0]) == 0:
|
if len(layer.bounds[0]) == 0:
|
||||||
continue
|
continue
|
||||||
videoReader = VideoReader(self.config)
|
video_reader = VideoReader(self.config)
|
||||||
listOfFrames = self.makeListOfFrames([layer])
|
list_of_frames = self.make_list_of_frames([layer])
|
||||||
videoReader.fillBuffer(listOfFrames)
|
video_reader.fill_buffer(list_of_frames)
|
||||||
while not videoReader.videoEnded():
|
while not video_reader.video_ended():
|
||||||
frameCount, frame = videoReader.pop()
|
frame_count, frame = video_reader.pop()
|
||||||
frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
|
frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
|
||||||
frame2 = np.copy(underlay)
|
frame2 = np.copy(underlay)
|
||||||
for (x, y, w, h) in layer.bounds[frameCount - layer.startFrame]:
|
for (x, y, w, h) in layer.bounds[frame_count - layer.startFrame]:
|
||||||
if x is None:
|
if x is None:
|
||||||
continue
|
continue
|
||||||
factor = videoReader.w / self.resizeWidth
|
factor = video_reader.w / self.resize_width
|
||||||
x, y, w, h = (int(x * factor), int(y * factor), int(w * factor), int(h * factor))
|
x, y, w, h = (int(x * factor), int(y * factor), int(w * factor), int(h * factor))
|
||||||
|
|
||||||
frame2[y : y + h, x : x + w] = np.copy(frame[y : y + h, x : x + w])
|
frame2[y : y + h, x : x + w] = np.copy(frame[y : y + h, x : x + w])
|
||||||
|
|
||||||
self.addTimestamp(frame2, videoReader, frameCount, layer, x, y, w, h)
|
self.add_timestamp(frame2, video_reader, frame_count, x, y, w, h)
|
||||||
writer.append_data(frame2)
|
writer.append_data(frame2)
|
||||||
|
|
||||||
writer.close()
|
writer.close()
|
||||||
|
|
||||||
def exportOverlayed(self, layers, blackBackground=False, showProgress=False):
|
def export_overlayed(self, layers, black_background=False, show_progress=False):
|
||||||
|
|
||||||
listOfFrames = self.makeListOfFrames(layers)
|
list_of_frames = self.make_list_of_frames(layers)
|
||||||
maxLength = self.getMaxLengthOfLayers(layers)
|
max_length = self.get_max_length_of_layers(layers)
|
||||||
|
|
||||||
if blackBackground:
|
with VideoReader(self.config, list_of_frames) as videoReader:
|
||||||
underlay = np.zeros(shape=[videoReader.h, videoReader.w, 3], dtype=np.uint8)
|
if black_background:
|
||||||
else:
|
underlay = np.zeros(shape=[videoReader.h, videoReader.w, 3], dtype=np.uint8)
|
||||||
underlay = cv2.VideoCapture(self.footagePath).read()[1]
|
else:
|
||||||
underlay = cv2.cvtColor(underlay, cv2.COLOR_BGR2RGB)
|
underlay = cv2.VideoCapture(self.footage_path).read()[1]
|
||||||
|
underlay = cv2.cvtColor(underlay, cv2.COLOR_BGR2RGB)
|
||||||
|
|
||||||
frames = []
|
frames = []
|
||||||
for i in range(maxLength):
|
for i in range(max_length):
|
||||||
frames.append(np.copy(underlay))
|
frames.append(np.copy(underlay))
|
||||||
|
fps = videoReader.fps
|
||||||
with VideoReader(self.config, listOfFrames) as videoReader:
|
while not videoReader.video_ended():
|
||||||
while not videoReader.videoEnded():
|
frame_count, frame = videoReader.pop()
|
||||||
frameCount, frame = videoReader.pop()
|
if frame_count % (60 * fps) == 0:
|
||||||
if frameCount % (60 * self.fps) == 0:
|
print("Minutes processed: ", frame_count / (60 * fps), end="\r")
|
||||||
print("Minutes processed: ", frameCount / (60 * self.fps), end="\r")
|
|
||||||
if frame is None:
|
if frame is None:
|
||||||
print("ContourExtractor: frame was None")
|
print("ContourExtractor: frame was None")
|
||||||
continue
|
continue
|
||||||
|
|
||||||
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 <= frame_count and layer.startFrame + len(layer.bounds) > frame_count:
|
||||||
for i in range(0, len(layer.bounds[frameCount - layer.startFrame])):
|
for i in range(0, len(layer.bounds[frame_count - layer.startFrame])):
|
||||||
try:
|
try:
|
||||||
x, y, w, h = layer.bounds[frameCount - layer.startFrame][i]
|
x, y, w, h = layer.bounds[frame_count - layer.startFrame][i]
|
||||||
if None in (x, y, w, h):
|
if None in (x, y, w, h):
|
||||||
break
|
break
|
||||||
factor = videoReader.w / self.resizeWidth
|
factor = videoReader.w / self.resize_width
|
||||||
x, y, w, h = (int(x * factor), int(y * factor), int(w * factor), int(h * factor))
|
x, y, w, h = (int(x * factor), int(y * factor), int(w * factor), int(h * factor))
|
||||||
|
|
||||||
mask = self.getMask(i, frameCount, layer, w, h)
|
mask = self.get_mask(i, frame_count, layer, w, h)
|
||||||
background = frames[frameCount - layer.startFrame + layer.exportOffset]
|
background = frames[frame_count - layer.startFrame + layer.exportOffset]
|
||||||
self.addMaskedContent(frame, x, y, w, h, mask, background)
|
self.add_masked_content(frame, x, y, w, h, mask, background)
|
||||||
frames[frameCount - layer.startFrame + layer.exportOffset] = np.copy(background)
|
frames[frame_count - layer.startFrame + layer.exportOffset] = np.copy(background)
|
||||||
|
|
||||||
if showProgress:
|
if show_progress:
|
||||||
cv2.imshow("changes x", background)
|
cv2.imshow("changes x", background)
|
||||||
cv2.waitKey(10) & 0xFF
|
cv2.waitKey(10) & 0xFF
|
||||||
|
|
||||||
self.addTimestamp(frames[frameCount - layer.startFrame + layer.exportOffset], videoReader, frameCount, layer, x, y, w, h)
|
self.add_timestamp(frames[frame_count - layer.startFrame + layer.exportOffset], videoReader, frame_count, x, y, w, h)
|
||||||
except:
|
except:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
writer = imageio.get_writer(self.outputPath, fps=videoReader.getFPS())
|
writer = imageio.get_writer(self.output_path, fps=videoReader.get_fps())
|
||||||
for frame in frames:
|
for frame in frames:
|
||||||
writer.append_data(frame)
|
writer.append_data(frame)
|
||||||
|
|
||||||
writer.close()
|
writer.close()
|
||||||
|
|
||||||
def addMaskedContent(self, frame, x, y, w, h, mask, background):
|
def add_masked_content(self, frame, x, y, w, h, mask, background):
|
||||||
maskedFrame = np.copy(
|
masked_frame = np.copy(
|
||||||
cv2.bitwise_and(
|
cv2.bitwise_and(
|
||||||
background[y : y + h, x : x + w],
|
background[y : y + h, x : x + w],
|
||||||
background[y : y + h, x : x + w],
|
background[y : y + h, x : x + w],
|
||||||
|
|
@ -126,15 +124,15 @@ class Exporter:
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
background[y : y + h, x : x + w] = cv2.addWeighted(
|
background[y : y + h, x : x + w] = cv2.addWeighted(
|
||||||
maskedFrame,
|
masked_frame,
|
||||||
1,
|
1,
|
||||||
np.copy(cv2.bitwise_and(frame[y : y + h, x : x + w], frame[y : y + h, x : x + w], mask=mask)),
|
np.copy(cv2.bitwise_and(frame[y : y + h, x : x + w], frame[y : y + h, x : x + w], mask=mask)),
|
||||||
1,
|
1,
|
||||||
0,
|
0,
|
||||||
)
|
)
|
||||||
|
|
||||||
def addTimestamp(self, frame, videoReader, frameCount, layer, x, y, w, h):
|
def add_timestamp(self, frame, video_reader, frame_count, x, y, w, h):
|
||||||
time = datetime.fromtimestamp(int(frameCount / self.fps) + videoReader.getStartTime())
|
time = datetime.fromtimestamp(int(frame_count / video_reader.fps) + video_reader.get_start_time())
|
||||||
cv2.putText(
|
cv2.putText(
|
||||||
frame,
|
frame,
|
||||||
f"{time.hour}:{time.minute}:{time.second}",
|
f"{time.hour}:{time.minute}:{time.second}",
|
||||||
|
|
@ -145,29 +143,33 @@ class Exporter:
|
||||||
2,
|
2,
|
||||||
)
|
)
|
||||||
|
|
||||||
def getMask(self, i, frameCount, layer, w, h):
|
def get_mask(self, i, frame_count, layer, w, h):
|
||||||
mask = layer.masks[frameCount - layer.startFrame][i]
|
mask = layer.masks[frame_count - layer.startFrame][i]
|
||||||
mask = imutils.resize(mask, width=w, height=h + 1)
|
mask = imutils.resize(mask, width=w, height=h + 1)
|
||||||
mask = np.resize(mask, (h, w))
|
mask = np.resize(mask, (h, w))
|
||||||
mask = cv2.erode(mask, None, iterations=10)
|
mask = cv2.erode(mask, None, iterations=10)
|
||||||
mask *= 255
|
mask *= 255
|
||||||
return mask
|
return mask
|
||||||
|
|
||||||
def exportRawData(self, layers, contours, masks):
|
def export_raw_data(self, layers, contours, masks):
|
||||||
with open(self.config["importPath"], "wb+") as file:
|
with open(self.config["importPath"] + "_layers", "wb+") as file:
|
||||||
pickle.dump((layers, contours, masks), file)
|
pickle.dump(layers, file)
|
||||||
|
with open(self.config["importPath"] + "_contours", "wb+") as file:
|
||||||
|
pickle.dump(contours, file)
|
||||||
|
with open(self.config["importPath"] + "_masks", "wb+") as file:
|
||||||
|
pickle.dump(masks, file)
|
||||||
|
|
||||||
def getMaxLengthOfLayers(self, layers):
|
def get_max_length_of_layers(self, layers):
|
||||||
maxLength = 0
|
max_length = 0
|
||||||
for layer in layers:
|
for layer in layers:
|
||||||
if layer.getLength() > maxLength:
|
if layer.getLength() > max_length:
|
||||||
maxLength = layer.getLength()
|
max_length = layer.getLength()
|
||||||
return maxLength
|
return max_length
|
||||||
|
|
||||||
def makeListOfFrames(self, layers):
|
def make_list_of_frames(self, layers):
|
||||||
"""Returns set of all Frames which are relavant to the Layers"""
|
"""Returns set of all Frames which are relevant to the Layers"""
|
||||||
frameNumbers = set()
|
frame_numbers = set()
|
||||||
for layer in layers:
|
for layer in layers:
|
||||||
frameNumbers.update(list(range(layer.startFrame, layer.startFrame + len(layer))))
|
frame_numbers.update(list(range(layer.startFrame, layer.startFrame + len(layer))))
|
||||||
|
|
||||||
return sorted(list(frameNumbers))
|
return sorted(list(frame_numbers))
|
||||||
|
|
|
||||||
|
|
@ -1,26 +1,30 @@
|
||||||
import numpy as np
|
import numpy as np
|
||||||
from matplotlib import pyplot as plt
|
from matplotlib import pyplot as plt
|
||||||
|
from PIL import Image
|
||||||
|
|
||||||
class HeatMap:
|
class HeatMap:
|
||||||
def __init__(self, x, y, contours, resizeFactor=1):
|
def __init__(self, x, y, contours, resize_factor=1):
|
||||||
self.imageBW = np.zeros(shape=[y, x, 3], dtype=np.float64)
|
self.image_bw = np.zeros(shape=[y, x, 3], dtype=np.float64)
|
||||||
self._resizeFactor = resizeFactor
|
self._resize_factor = resize_factor
|
||||||
self._createImage(contours)
|
self._create_image(contours)
|
||||||
|
|
||||||
def _createImage(self, contours):
|
def _create_image(self, contours):
|
||||||
for contour in contours:
|
for contour in contours:
|
||||||
for x, y, w, h in contour:
|
for x, y, w, h in contour:
|
||||||
x, y, w, h = (
|
x, y, w, h = (
|
||||||
x * self._resizeFactor,
|
x * self._resize_factor,
|
||||||
y * self._resizeFactor,
|
y * self._resize_factor,
|
||||||
w * self._resizeFactor,
|
w * self._resize_factor,
|
||||||
h * self._resizeFactor,
|
h * self._resize_factor,
|
||||||
)
|
)
|
||||||
self.imageBW[int(y) : int(y + h), int(x) : int(x + w)] += 1
|
self.image_bw[int(y) : int(y + h), int(x) : int(x + w)] += 1
|
||||||
|
|
||||||
self.imageBW = np.nan_to_num(self.imageBW / self.imageBW.sum(axis=1)[:, np.newaxis], 0)
|
self.image_bw = np.nan_to_num(self.image_bw / self.image_bw.sum(axis=1)[:, np.newaxis], 0)
|
||||||
|
|
||||||
def showImage(self):
|
def show_image(self):
|
||||||
plt.imshow(self.imageBW * 255)
|
plt.imshow(self.image_bw * 255)
|
||||||
plt.show()
|
plt.show()
|
||||||
|
|
||||||
|
def save__image(self, path):
|
||||||
|
im = Image.fromarray(self.image_bw * 255)
|
||||||
|
im.save(path)
|
||||||
|
|
@ -1,12 +1,24 @@
|
||||||
import pickle
|
import pickle
|
||||||
|
import os.path
|
||||||
|
|
||||||
class Importer:
|
class Importer:
|
||||||
def __init__(self, config):
|
def __init__(self, config):
|
||||||
self.path = config["importPath"]
|
self.path = config["importPath"]
|
||||||
|
|
||||||
def importRawData(self):
|
def import_raw_data(self):
|
||||||
print("Loading previous results")
|
print("Loading previous results")
|
||||||
with open(self.path, "rb") as file:
|
|
||||||
layers, contours, masks = pickle.load(file)
|
layers = self.load_if_present(self.path + "_layers")
|
||||||
return (layers, contours, masks)
|
contours = self.load_if_present(self.path + "_contours")
|
||||||
|
masks = self.load_if_present(self.path + "_masks")
|
||||||
|
|
||||||
|
return layers, contours, masks
|
||||||
|
|
||||||
|
def load_if_present(self, path):
|
||||||
|
var = None
|
||||||
|
if os.path.isfile(path):
|
||||||
|
with open(path, "rb") as file:
|
||||||
|
var = pickle.load(file)
|
||||||
|
else:
|
||||||
|
print(path, "file not found")
|
||||||
|
return var
|
||||||
|
|
|
||||||
|
|
@ -6,11 +6,11 @@ import numpy as np
|
||||||
class Layer:
|
class Layer:
|
||||||
# bounds = [[(x,y,w,h), ],]
|
# bounds = [[(x,y,w,h), ],]
|
||||||
|
|
||||||
startFrame = None
|
start_frame = None
|
||||||
lastFrame = None
|
last_frame = None
|
||||||
length = None
|
length = None
|
||||||
|
|
||||||
def __init__(self, startFrame, data, mask, config):
|
def __init__(self, start_frame, 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,
|
||||||
|
|
@ -21,57 +21,57 @@ class Layer:
|
||||||
but we only care about the corners of the contours.
|
but we only care about the corners of the contours.
|
||||||
So we save the bounds (x,y,w,h) in bounds[] and the actual content in data[]
|
So we save the bounds (x,y,w,h) in bounds[] and the actual content in data[]
|
||||||
"""
|
"""
|
||||||
self.startFrame = startFrame
|
self.start_frame = start_frame
|
||||||
self.lastFrame = startFrame
|
self.last_frame = start_frame
|
||||||
self.config = config
|
self.config = config
|
||||||
self.data = []
|
self.data = []
|
||||||
self.bounds = []
|
self.bounds = []
|
||||||
self.masks = []
|
self.masks = []
|
||||||
self.stats = dict()
|
self.stats = dict()
|
||||||
self.exportOffset = 0
|
self.export_offset = 0
|
||||||
|
|
||||||
self.bounds.append([data])
|
self.bounds.append([data])
|
||||||
self.masks.append([mask])
|
self.masks.append([mask])
|
||||||
|
|
||||||
def add(self, frameNumber, bound, mask):
|
def add(self, frame_number, 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"""
|
||||||
index = frameNumber - self.startFrame
|
index = frame_number - self.start_frame
|
||||||
if index < 0:
|
if index < 0:
|
||||||
return
|
return
|
||||||
if frameNumber > self.lastFrame:
|
if frame_number > self.last_frame:
|
||||||
for i in range(frameNumber - self.lastFrame):
|
for i in range(frame_number - self.last_frame):
|
||||||
self.bounds.append([])
|
self.bounds.append([])
|
||||||
self.masks.append([])
|
self.masks.append([])
|
||||||
self.lastFrame = frameNumber
|
self.last_frame = frame_number
|
||||||
|
|
||||||
if bound not in self.bounds[index]:
|
if bound not in self.bounds[index]:
|
||||||
self.bounds[index].append(bound)
|
self.bounds[index].append(bound)
|
||||||
self.masks[index].append(mask)
|
self.masks[index].append(mask)
|
||||||
|
|
||||||
def getLength(self):
|
def get_length(self):
|
||||||
return len(self) + self.exportOffset
|
return len(self) + self.export_offset
|
||||||
|
|
||||||
def __len__(self):
|
def __len__(self):
|
||||||
self.length = len(self.bounds)
|
self.length = len(self.bounds)
|
||||||
return self.length
|
return self.length
|
||||||
|
|
||||||
def spaceOverlaps(self, layer2):
|
def space_overlaps(self, layer2):
|
||||||
"""Checks if there is an overlap in the bounds of current layer with given layer"""
|
"""Checks if there is an overlap in the bounds of current layer with given layer"""
|
||||||
overlap = False
|
overlap = False
|
||||||
maxLen = min(len(layer2.bounds), len(self.bounds))
|
max_len = min(len(layer2.bounds), len(self.bounds))
|
||||||
bounds = self.bounds[:maxLen]
|
bounds = self.bounds[:max_len]
|
||||||
for b1s, b2s in zip(bounds[::10], layer2.bounds[:maxLen:10]):
|
for b1s, b2s in zip(bounds[::10], layer2.bounds[:max_len:10]):
|
||||||
for b1 in b1s:
|
for b1 in b1s:
|
||||||
for b2 in b2s:
|
for b2 in b2s:
|
||||||
if self.contoursOverlay((b1[0], b1[1] + b1[3]), (b1[0] + b1[2], b1[1]), (b2[0], b2[1] + b2[3]), (b2[0] + b2[2], b2[1])):
|
if self.contours_overlay((b1[0], b1[1] + b1[3]), (b1[0] + b1[2], b1[1]), (b2[0], b2[1] + b2[3]), (b2[0] + b2[2], b2[1])):
|
||||||
overlap = True
|
overlap = True
|
||||||
break
|
break
|
||||||
return overlap
|
return overlap
|
||||||
|
|
||||||
def timeOverlaps(self, layer2):
|
def time_overlaps(self, layer2):
|
||||||
"""Checks for overlap in time between current and given layer"""
|
"""Checks for overlap in time between current and given layer"""
|
||||||
s1 = self.exportOffset
|
s1 = self.export_offset
|
||||||
e1 = self.lastFrame - self.startFrame + self.exportOffset
|
e1 = self.last_frame - self.start_frame + self.export_offset
|
||||||
s2 = layer2.exportOffset
|
s2 = layer2.exportOffset
|
||||||
e2 = layer2.lastFrame - layer2.startFrame + layer2.exportOffset
|
e2 = layer2.lastFrame - layer2.startFrame + layer2.exportOffset
|
||||||
|
|
||||||
|
|
@ -82,7 +82,7 @@ class Layer:
|
||||||
else:
|
else:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def contoursOverlay(self, l1, r1, l2, r2):
|
def contours_overlay(self, l1, r1, l2, r2):
|
||||||
if l1[0] >= r2[0] or l2[0] >= r1[0]:
|
if l1[0] >= r2[0] or l2[0] >= r1[0]:
|
||||||
return False
|
return False
|
||||||
if l1[1] <= r2[1] or l2[1] <= r1[1]:
|
if l1[1] <= r2[1] or l2[1] <= r1[1]:
|
||||||
|
|
|
||||||
|
|
@ -15,153 +15,153 @@ class LayerFactory:
|
||||||
self.layers = []
|
self.layers = []
|
||||||
self.tolerance = config["tolerance"]
|
self.tolerance = config["tolerance"]
|
||||||
self.ttolerance = config["ttolerance"]
|
self.ttolerance = config["ttolerance"]
|
||||||
self.minLayerLength = config["minLayerLength"]
|
self.min_layer_length = config["minLayerLength"]
|
||||||
self.maxLayerLength = config["maxLayerLength"]
|
self.max_layer_length = config["maxLayerLength"]
|
||||||
self.resizeWidth = config["resizeWidth"]
|
self.resize_width = config["resizeWidth"]
|
||||||
self.footagePath = config["inputPath"]
|
self.footage_path = config["inputPath"]
|
||||||
self.config = config
|
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:
|
||||||
self.extractLayers(data)
|
self.extract_layers(data)
|
||||||
|
|
||||||
def extractLayers(self, data, maskArr):
|
def extract_layers(self, data, mask_arr):
|
||||||
"""Bundle given contours together into Layer Objects"""
|
"""Bundle given contours together into Layer Objects"""
|
||||||
|
|
||||||
frameNumber = min(data)
|
frame_number = min(data)
|
||||||
contours = data[frameNumber]
|
contours = data[frame_number]
|
||||||
masks = maskArr[frameNumber]
|
masks = mask_arr[frame_number]
|
||||||
|
|
||||||
for contour, mask in zip(contours, masks):
|
for contour, mask in zip(contours, masks):
|
||||||
mask = np.unpackbits(mask, axis=0)
|
mask = np.unpackbits(mask, axis=0)
|
||||||
self.layers.append(Layer(frameNumber, contour, mask, self.config))
|
self.layers.append(Layer(frame_number, contour, mask, self.config))
|
||||||
|
|
||||||
self.oldLayerIDs = []
|
self.old_layer_i_ds = []
|
||||||
|
|
||||||
with ThreadPool(os.cpu_count()) as pool:
|
with ThreadPool(os.cpu_count()) as pool:
|
||||||
for frameNumber in sorted(data.keys()):
|
for frame_number in sorted(data.keys()):
|
||||||
contours = data[frameNumber]
|
contours = data[frame_number]
|
||||||
masks = maskArr[frameNumber]
|
masks = mask_arr[frame_number]
|
||||||
masks = [np.unpackbits(mask, axis=0) for mask, contours in zip(masks, contours)]
|
masks = [np.unpackbits(mask, axis=0) for mask, contours in zip(masks, contours)]
|
||||||
if frameNumber % 100 == 0:
|
if frame_number % 100 == 0:
|
||||||
print(
|
print(
|
||||||
f" {int(round(frameNumber/max(data.keys()), 2)*100)}% done with Layer extraction {len(self.layers)} Layers",
|
f" {int(round(frame_number/max(data.keys()), 2)*100)}% done with Layer extraction {len(self.layers)} Layers",
|
||||||
end="\r",
|
end="\r",
|
||||||
)
|
)
|
||||||
|
|
||||||
tmp = [[frameNumber, contour, mask] for contour, mask in zip(contours, masks)]
|
tmp = [[frame_number, 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.get_layers(x)
|
||||||
|
|
||||||
# self.joinLayers()
|
# self.joinLayers()
|
||||||
return self.layers
|
return self.layers
|
||||||
|
|
||||||
def getLayers(self, data):
|
def get_layers(self, data):
|
||||||
frameNumber = data[0]
|
frame_number = data[0]
|
||||||
bounds = data[1]
|
bounds = data[1]
|
||||||
mask = data[2]
|
mask = data[2]
|
||||||
(x, y, w, h) = bounds
|
(x, y, w, h) = bounds
|
||||||
tol = self.tolerance
|
tol = self.tolerance
|
||||||
|
|
||||||
foundLayerIDs = set()
|
found_layer_i_ds = set()
|
||||||
for i, layer in enumerate(self.layers):
|
for i, layer in enumerate(self.layers):
|
||||||
if frameNumber - layer.lastFrame > self.ttolerance:
|
if frame_number - layer.last_frame > self.ttolerance:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
lastXframes = min(40, len(layer))
|
last_xframes = min(40, len(layer))
|
||||||
lastBounds = [bound for bounds in layer.bounds[-lastXframes:] for bound in bounds]
|
last_bounds = [bound for bounds in layer.bounds[-last_xframes:] for bound in bounds]
|
||||||
|
|
||||||
for j, bounds in enumerate(sorted(lastBounds, reverse=True)):
|
for j, bounds in enumerate(sorted(last_bounds, reverse=True)):
|
||||||
if bounds is None:
|
if bounds is None:
|
||||||
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.contours_overlay((x - tol, y + h + tol), (x + w + tol, y - tol), (x2, y2 + h2), (x2 + w2, y2)):
|
||||||
layer.add(frameNumber, (x, y, w, h), mask)
|
layer.add(frame_number, (x, y, w, h), mask)
|
||||||
foundLayerIDs.add(i)
|
found_layer_i_ds.add(i)
|
||||||
break
|
break
|
||||||
|
|
||||||
foundLayerIDs = sorted(list(foundLayerIDs))
|
found_layer_i_ds = sorted(list(found_layer_i_ds))
|
||||||
if len(foundLayerIDs) == 0:
|
if len(found_layer_i_ds) == 0:
|
||||||
self.layers.append(Layer(frameNumber, (x, y, w, h), mask, self.config))
|
self.layers.append(Layer(frame_number, (x, y, w, h), mask, self.config))
|
||||||
if len(foundLayerIDs) > 1:
|
if len(found_layer_i_ds) > 1:
|
||||||
self.mergeLayers(foundLayerIDs)
|
self.merge_layers(found_layer_i_ds)
|
||||||
|
|
||||||
def mergeLayers(self, foundLayerIDs):
|
def merge_layers(self, found_layer_i_ds):
|
||||||
layers = self.getLayersByID(foundLayerIDs)
|
layers = self.get_layers_by_id(found_layer_i_ds)
|
||||||
mergedLayers = layers[0]
|
merged_layers = layers[0]
|
||||||
for layer in layers[1:]:
|
for layer in layers[1:]:
|
||||||
for i, (contours, masks) in enumerate(zip(layer.bounds, layer.masks)):
|
for i, (contours, masks) in enumerate(zip(layer.bounds, layer.masks)):
|
||||||
for contour, mask in zip(contours, masks):
|
for contour, mask in zip(contours, masks):
|
||||||
mergedLayers.add(layer.startFrame + i, contour, mask)
|
merged_layers.add(layer.startFrame + i, contour, mask)
|
||||||
|
|
||||||
for i, id in enumerate(foundLayerIDs):
|
for i, id in enumerate(found_layer_i_ds):
|
||||||
del self.layers[id - i]
|
del self.layers[id - i]
|
||||||
|
|
||||||
self.layers.append(mergedLayers)
|
self.layers.append(merged_layers)
|
||||||
|
|
||||||
def joinLayers(self):
|
def join_layers(self):
|
||||||
self.layers.sort(key=lambda c: c.startFrame)
|
self.layers.sort(key=lambda c: c.startFrame)
|
||||||
minFrame = self.getMinStart(self.layers)
|
min_frame = self.get_min_start(self.layers)
|
||||||
maxFrame = self.getMaxEnd(self.layers)
|
max_frame = self.get_max_end(self.layers)
|
||||||
|
|
||||||
for i in range(minFrame, maxFrame):
|
for i in range(min_frame, max_frame):
|
||||||
pL, indexes = self.getPossibleLayers(i)
|
p_l, indexes = self.get_possible_layers(i)
|
||||||
if len(pL) <= 1:
|
if len(p_l) <= 1:
|
||||||
continue
|
continue
|
||||||
merge = set()
|
merge = set()
|
||||||
innerMax = self.getMaxEnd(pL)
|
inner_max = self.get_max_end(p_l)
|
||||||
for x in range(self.getMinStart(pL), innerMax):
|
for x in range(self.get_min_start(p_l), inner_max):
|
||||||
for lc, l in enumerate(pL):
|
for lc, l in enumerate(p_l):
|
||||||
if l.startFrame < x or l.lastFrame > x:
|
if l.startFrame < x or l.lastFrame > x:
|
||||||
continue
|
continue
|
||||||
for lc2, l2 in enumerate(pL):
|
for lc2, l2 in enumerate(p_l):
|
||||||
if lc2 == lc:
|
if lc2 == lc:
|
||||||
continue
|
continue
|
||||||
for cnt in l.bounds[x - l.startFrame]:
|
for cnt in l.bounds[x - l.startFrame]:
|
||||||
for cnt2 in l2.bounds[x - l2.startFrame]:
|
for cnt2 in l2.bounds[x - l2.startFrame]:
|
||||||
if self.contoursOverlay(cnt, cnt2):
|
if self.contours_overlay(cnt, cnt2):
|
||||||
merge.add(indexes[lc])
|
merge.add(indexes[lc])
|
||||||
merge.add(indexes[lc2])
|
merge.add(indexes[lc2])
|
||||||
merge = list(merge)
|
merge = list(merge)
|
||||||
if len(merge) > 1:
|
if len(merge) > 1:
|
||||||
self.mergeLayers(merge)
|
self.merge_layers(merge)
|
||||||
i = innerMax
|
i = inner_max
|
||||||
|
|
||||||
def getPossibleLayers(self, t):
|
def get_possible_layers(self, t):
|
||||||
ret = []
|
ret = []
|
||||||
ii = []
|
ii = []
|
||||||
for i, layer in enumerate(self.layers):
|
for i, layer in enumerate(self.layers):
|
||||||
if layer.startFrame <= t and layer.lastFrame <= t:
|
if layer.start_frame <= t and layer.last_frame <= t:
|
||||||
ret.append(layer)
|
ret.append(layer)
|
||||||
ii.append(i)
|
ii.append(i)
|
||||||
return (ret, ii)
|
return (ret, ii)
|
||||||
|
|
||||||
def getMinStart(self, layers):
|
def get_min_start(self, layers):
|
||||||
minFrame = layers[0].startFrame
|
min_frame = layers[0].startFrame
|
||||||
for l in layers:
|
for l in layers:
|
||||||
if l.startFrame < minFrame:
|
if l.startFrame < min_frame:
|
||||||
minFrame = l.startFrame
|
min_frame = l.startFrame
|
||||||
return minFrame
|
return min_frame
|
||||||
|
|
||||||
def getMaxEnd(self, layers):
|
def get_max_end(self, layers):
|
||||||
maxFrame = layers[0].lastFrame
|
max_frame = layers[0].lastFrame
|
||||||
for l in layers:
|
for l in layers:
|
||||||
if l.lastFrame < maxFrame:
|
if l.lastFrame < max_frame:
|
||||||
maxFrame = l.lastFrame
|
max_frame = l.lastFrame
|
||||||
return maxFrame
|
return max_frame
|
||||||
|
|
||||||
def contoursOverlay(self, l1, r1, l2, r2):
|
def contours_overlay(self, l1, r1, l2, r2):
|
||||||
if l1[0] >= r2[0] or l2[0] >= r1[0]:
|
if l1[0] >= r2[0] or l2[0] >= r1[0]:
|
||||||
return False
|
return False
|
||||||
if l1[1] <= r2[1] or l2[1] <= r1[1]:
|
if l1[1] <= r2[1] or l2[1] <= r1[1]:
|
||||||
return False
|
return False
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def getLayersByID(self, foundLayerIDs):
|
def get_layers_by_id(self, found_layer_i_ds):
|
||||||
layers = []
|
layers = []
|
||||||
for layerID in foundLayerIDs:
|
for layer_id in found_layer_i_ds:
|
||||||
layers.append(self.layers[layerID])
|
layers.append(self.layers[layer_id])
|
||||||
|
|
||||||
layers.sort(key=lambda c: c.startFrame)
|
layers.sort(key=lambda c: c.startFrame)
|
||||||
return layers
|
return layers
|
||||||
|
|
|
||||||
|
|
@ -17,53 +17,53 @@ class LayerManager:
|
||||||
self.layers = layers
|
self.layers = layers
|
||||||
self.tolerance = config["tolerance"]
|
self.tolerance = config["tolerance"]
|
||||||
self.ttolerance = config["ttolerance"]
|
self.ttolerance = config["ttolerance"]
|
||||||
self.minLayerLength = config["minLayerLength"]
|
self.min_layer_length = config["minLayerLength"]
|
||||||
self.maxLayerLength = config["maxLayerLength"]
|
self.max_layer_length = config["maxLayerLength"]
|
||||||
self.resizeWidth = config["resizeWidth"]
|
self.resize_width = config["resizeWidth"]
|
||||||
self.footagePath = config["inputPath"]
|
self.footage_path = config["inputPath"]
|
||||||
self.config = config
|
self.config = config
|
||||||
# self.classifier = Classifier()
|
# self.classifier = Classifier()
|
||||||
self.tags = []
|
self.tags = []
|
||||||
print("LayerManager constructed")
|
print("LayerManager constructed")
|
||||||
|
|
||||||
def cleanLayers(self):
|
def clean_layers(self):
|
||||||
print("'Cleaning' Layers")
|
print("'Cleaning' Layers")
|
||||||
print("Before deleting short layers ", len(self.layers))
|
print("Before deleting short layers ", len(self.layers))
|
||||||
self.freeMin()
|
self.free_min()
|
||||||
print("Before deleting long layers ", len(self.layers))
|
print("Before deleting long layers ", len(self.layers))
|
||||||
self.freeMax()
|
self.free_max()
|
||||||
self.sortLayers()
|
self.sort_layers()
|
||||||
print("Before deleting sparse layers ", len(self.layers))
|
print("Before deleting sparse layers ", len(self.layers))
|
||||||
self.deleteSparse()
|
self.delete_sparse()
|
||||||
print("after deleting sparse layers ", len(self.layers))
|
print("after deleting sparse layers ", len(self.layers))
|
||||||
#self.calcTimeOffset()
|
#self.calcTimeOffset()
|
||||||
|
|
||||||
def deleteSparse(self):
|
def delete_sparse(self):
|
||||||
toDelete = []
|
to_delete = []
|
||||||
for i, l in enumerate(self.layers):
|
for i, l in enumerate(self.layers):
|
||||||
empty = l.bounds.count([])
|
empty = l.bounds.count([])
|
||||||
if empty / len(l) > 0.5:
|
if empty / len(l) > 0.5:
|
||||||
toDelete.append(i)
|
to_delete.append(i)
|
||||||
|
|
||||||
for i, id in enumerate(toDelete):
|
for i, id in enumerate(to_delete):
|
||||||
del self.layers[id - i]
|
del self.layers[id - i]
|
||||||
|
|
||||||
def freeMin(self):
|
def free_min(self):
|
||||||
self.data.clear()
|
self.data.clear()
|
||||||
layers = []
|
layers = []
|
||||||
for l in self.layers:
|
for l in self.layers:
|
||||||
if len(l) > self.minLayerLength:
|
if len(l) > self.min_layer_length:
|
||||||
layers.append(l)
|
layers.append(l)
|
||||||
self.layers = layers
|
self.layers = layers
|
||||||
|
|
||||||
def freeMax(self):
|
def free_max(self):
|
||||||
layers = []
|
layers = []
|
||||||
for l in self.layers:
|
for l in self.layers:
|
||||||
if len(l) < self.maxLayerLength:
|
if len(l) < self.max_layer_length:
|
||||||
layers.append(l)
|
layers.append(l)
|
||||||
self.layers = layers
|
self.layers = layers
|
||||||
|
|
||||||
def tagLayers(self):
|
def tag_layers(self):
|
||||||
"""Use classifieres the tag all Layers, by reading the contour content from the original video, then applying the classifier"""
|
"""Use classifieres the tag all Layers, by reading the contour content from the original video, then applying the classifier"""
|
||||||
print("Tagging Layers")
|
print("Tagging Layers")
|
||||||
exporter = Exporter(self.config)
|
exporter = Exporter(self.config)
|
||||||
|
|
@ -73,19 +73,19 @@ class LayerManager:
|
||||||
start = time.time()
|
start = time.time()
|
||||||
if len(layer.bounds[0]) == 0:
|
if len(layer.bounds[0]) == 0:
|
||||||
continue
|
continue
|
||||||
listOfFrames = exporter.makeListOfFrames([layer])
|
list_of_frames = exporter.make_list_of_frames([layer])
|
||||||
|
|
||||||
videoReader = VideoReader(self.config, listOfFrames)
|
video_reader = VideoReader(self.config, list_of_frames)
|
||||||
videoReader.fillBuffer()
|
video_reader.fill_buffer()
|
||||||
|
|
||||||
while not videoReader.videoEnded():
|
while not video_reader.video_ended():
|
||||||
frameCount, frame = videoReader.pop()
|
frame_count, frame = video_reader.pop()
|
||||||
frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
|
frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
|
||||||
data = []
|
data = []
|
||||||
for (x, y, w, h) in layer.bounds[frameCount - layer.startFrame]:
|
for (x, y, w, h) in layer.bounds[frame_count - layer.startFrame]:
|
||||||
if x is None:
|
if x is None:
|
||||||
break
|
break
|
||||||
factor = videoReader.w / self.resizeWidth
|
factor = video_reader.w / self.resize_width
|
||||||
x = int(x * factor)
|
x = int(x * factor)
|
||||||
y = int(y * factor)
|
y = int(y * factor)
|
||||||
w = int(w * factor)
|
w = int(w * factor)
|
||||||
|
|
@ -96,16 +96,16 @@ class LayerManager:
|
||||||
print(tags)
|
print(tags)
|
||||||
self.tags.append(tags)
|
self.tags.append(tags)
|
||||||
|
|
||||||
videoReader.thread.join()
|
video_reader.thread.join()
|
||||||
|
|
||||||
def sortLayers(self):
|
def sort_layers(self):
|
||||||
self.layers.sort(key=lambda c: c.startFrame)
|
self.layers.sort(key=lambda c: c.startFrame)
|
||||||
|
|
||||||
def calcTimeOffset(self):
|
def calc_time_offset(self):
|
||||||
lenL = len(self.layers)
|
len_l = len(self.layers)
|
||||||
for i in range(1, len(self.layers)):
|
for i in range(1, len(self.layers)):
|
||||||
layer = self.layers[i]
|
layer = self.layers[i]
|
||||||
print(f"\r {i}/{lenL}", end="\r")
|
print(f"\r {i}/{len_l}", end="\r")
|
||||||
overlap = True
|
overlap = True
|
||||||
tries = 1
|
tries = 1
|
||||||
while overlap:
|
while overlap:
|
||||||
|
|
|
||||||
|
|
@ -7,33 +7,33 @@ import cv2
|
||||||
|
|
||||||
|
|
||||||
class VideoReader:
|
class VideoReader:
|
||||||
listOfFrames = None
|
list_of_frames = None
|
||||||
w = None
|
w = None
|
||||||
h = None
|
h = None
|
||||||
|
|
||||||
def __init__(self, config, setOfFrames=None, multiprocess=False):
|
def __init__(self, config, set_of_frames=None, multiprocess=False):
|
||||||
videoPath = config["inputPath"]
|
video_path = config["inputPath"]
|
||||||
if videoPath is None:
|
if video_path is None:
|
||||||
raise Exception("ERROR: Video reader needs a videoPath!")
|
raise Exception("ERROR: Video reader needs a video_path!")
|
||||||
self.videoPath = videoPath
|
self.video_path = video_path
|
||||||
self.lastFrame = 0
|
self.last_frame = 0
|
||||||
# buffer data struct:
|
# buffer data struct:
|
||||||
# buffer = Queue([(frameNumber, frame), ])
|
# buffer = Queue([(frameNumber, frame), ])
|
||||||
self.multiprocess = multiprocess
|
self.multiprocess = multiprocess
|
||||||
if multiprocess:
|
if multiprocess:
|
||||||
self.buffer = multiprocessing.Queue(config["videoBufferLength"])
|
self.buffer = multiprocessing.Manager().Queue(config["videoBufferLength"])
|
||||||
else:
|
else:
|
||||||
self.buffer = queue.Queue(config["videoBufferLength"])
|
self.buffer = queue.Queue(config["videoBufferLength"])
|
||||||
self.stopped = False
|
self.stopped = False
|
||||||
self.getWH()
|
self.get_wh()
|
||||||
self.calcFPS()
|
self.calc_fps()
|
||||||
self.calcLength()
|
self.calc_length()
|
||||||
self.calcStartTime()
|
self.calc_start_time()
|
||||||
if setOfFrames is not None:
|
if set_of_frames is not None:
|
||||||
self.listOfFrames = sorted(setOfFrames)
|
self.list_of_frames = sorted(set_of_frames)
|
||||||
|
|
||||||
def __enter__(self):
|
def __enter__(self):
|
||||||
self.fillBuffer()
|
self.fill_buffer()
|
||||||
return self
|
return self
|
||||||
|
|
||||||
def __exit__(self, type, value, traceback):
|
def __exit__(self, type, value, traceback):
|
||||||
|
|
@ -43,97 +43,97 @@ class VideoReader:
|
||||||
self.thread.join()
|
self.thread.join()
|
||||||
|
|
||||||
def pop(self):
|
def pop(self):
|
||||||
frameNumber, frame = self.buffer.get(block=True)
|
frame_number, frame = self.buffer.get(block=True)
|
||||||
if frame is None:
|
if frame is None:
|
||||||
self.stopped = True
|
self.stopped = True
|
||||||
return frameNumber, frame
|
return frame_number, frame
|
||||||
|
|
||||||
def fillBuffer(self, listOfFrames=None):
|
def fill_buffer(self, list_of_frames=None):
|
||||||
self.endFrame = int(cv2.VideoCapture(self.videoPath).get(cv2.CAP_PROP_FRAME_COUNT))
|
self.end_frame = int(cv2.VideoCapture(self.video_path).get(cv2.CAP_PROP_FRAME_COUNT))
|
||||||
if listOfFrames is not None:
|
if list_of_frames is not None:
|
||||||
self.listOfFrames = listOfFrames
|
self.list_of_frames = list_of_frames
|
||||||
|
|
||||||
if self.multiprocess:
|
if self.multiprocess:
|
||||||
if self.listOfFrames is not None:
|
if self.list_of_frames is not None:
|
||||||
self.thread = multiprocessing.Process(target=self.readFramesByList, args=())
|
self.thread = multiprocessing.Process(target=self.read_frames_by_list, args=())
|
||||||
else:
|
else:
|
||||||
self.thread = multiprocessing.Process(target=self.readFrames, args=())
|
self.thread = multiprocessing.Process(target=self.read_frames, args=())
|
||||||
else:
|
else:
|
||||||
if self.listOfFrames is not None:
|
if self.list_of_frames is not None:
|
||||||
self.thread = threading.Thread(target=self.readFramesByList, args=())
|
self.thread = threading.Thread(target=self.read_frames_by_list, args=())
|
||||||
else:
|
else:
|
||||||
self.thread = threading.Thread(target=self.readFrames, args=())
|
self.thread = threading.Thread(target=self.read_frames, args=())
|
||||||
self.thread.start()
|
self.thread.start()
|
||||||
|
|
||||||
def readFrames(self):
|
def read_frames(self):
|
||||||
"""Reads video from start to finish"""
|
"""Reads video from start to finish"""
|
||||||
self.vc = cv2.VideoCapture(self.videoPath)
|
self.vc = cv2.VideoCapture(self.video_path)
|
||||||
while self.lastFrame < self.endFrame:
|
while self.last_frame < self.end_frame:
|
||||||
res, frame = self.vc.read()
|
res, frame = self.vc.read()
|
||||||
if res:
|
if res:
|
||||||
self.buffer.put((self.lastFrame, frame))
|
self.buffer.put((self.last_frame, frame))
|
||||||
self.lastFrame += 1
|
self.last_frame += 1
|
||||||
self.buffer.put((self.lastFrame, None))
|
self.buffer.put((self.last_frame, None))
|
||||||
|
|
||||||
def readFramesByList(self):
|
def read_frames_by_list(self):
|
||||||
"""Reads all frames from a list of frame numbers"""
|
"""Reads all frames from a list of frame numbers"""
|
||||||
self.vc = cv2.VideoCapture(self.videoPath)
|
self.vc = cv2.VideoCapture(self.video_path)
|
||||||
self.vc.set(1, self.listOfFrames[0])
|
self.vc.set(1, self.list_of_frames[0])
|
||||||
self.lastFrame = self.listOfFrames[0]
|
self.last_frame = self.list_of_frames[0]
|
||||||
self.endFrame = self.listOfFrames[-1]
|
self.end_frame = self.list_of_frames[-1]
|
||||||
|
|
||||||
while self.lastFrame < self.endFrame:
|
while self.last_frame < self.end_frame:
|
||||||
if self.lastFrame in self.listOfFrames:
|
if self.last_frame in self.list_of_frames:
|
||||||
res, frame = self.vc.read()
|
res, frame = self.vc.read()
|
||||||
if res:
|
if res:
|
||||||
self.buffer.put((self.lastFrame, frame))
|
self.buffer.put((self.last_frame, frame))
|
||||||
else:
|
else:
|
||||||
print("Couldn't read Frame")
|
print("Couldn't read 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.list_of_frames.pop(0)
|
||||||
self.lastFrame += 1
|
self.last_frame += 1
|
||||||
else:
|
else:
|
||||||
# if current Frame number is not in list of Frames, we can skip a few frames
|
# if current Frame number is not in list of Frames, we can skip a few frames
|
||||||
self.vc.set(1, self.listOfFrames[0])
|
self.vc.set(1, self.list_of_frames[0])
|
||||||
self.lastFrame = self.listOfFrames[0]
|
self.last_frame = self.list_of_frames[0]
|
||||||
self.buffer.put((self.lastFrame, None))
|
self.buffer.put((self.last_frame, None))
|
||||||
|
|
||||||
def videoEnded(self):
|
def video_ended(self):
|
||||||
if self.stopped and self.buffer.empty():
|
if self.stopped and self.buffer.empty():
|
||||||
return True
|
return True
|
||||||
else:
|
else:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def calcFPS(self):
|
def calc_fps(self):
|
||||||
self.fps = cv2.VideoCapture(self.videoPath).get(cv2.CAP_PROP_FPS)
|
self.fps = cv2.VideoCapture(self.video_path).get(cv2.CAP_PROP_FPS)
|
||||||
|
|
||||||
def getFPS(self):
|
def get_fps(self):
|
||||||
if self.fps is None:
|
if self.fps is None:
|
||||||
self.calcFPS()
|
self.calc_fps()
|
||||||
return self.fps
|
return self.fps
|
||||||
|
|
||||||
def calcLength(self):
|
def calc_length(self):
|
||||||
fc = int(cv2.VideoCapture(self.videoPath).get(cv2.CAP_PROP_FRAME_COUNT))
|
fc = int(cv2.VideoCapture(self.video_path).get(cv2.CAP_PROP_FRAME_COUNT))
|
||||||
self.length = fc / self.getFPS()
|
self.length = fc / self.get_fps()
|
||||||
|
|
||||||
def getLength(self):
|
def get_length(self):
|
||||||
if self.length is None:
|
if self.length is None:
|
||||||
self.calcLength()
|
self.calc_length()
|
||||||
return self.length
|
return self.length
|
||||||
|
|
||||||
def calcStartTime(self):
|
def calc_start_time(self):
|
||||||
starttime = os.stat(self.videoPath).st_mtime
|
starttime = os.stat(self.video_path).st_mtime
|
||||||
length = self.getLength()
|
length = self.get_length()
|
||||||
starttime = starttime - length
|
starttime = starttime - length
|
||||||
self.starttime = starttime
|
self.starttime = starttime
|
||||||
|
|
||||||
def getStartTime(self):
|
def get_start_time(self):
|
||||||
return self.starttime
|
return self.starttime
|
||||||
|
|
||||||
def getWH(self):
|
def get_wh(self):
|
||||||
"""get width and height"""
|
"""get width and height"""
|
||||||
vc = cv2.VideoCapture(self.videoPath)
|
vc = cv2.VideoCapture(self.video_path)
|
||||||
if self.w is None or self.h is None:
|
if self.w is None or self.h is None:
|
||||||
res, image = vc.read()
|
res, image = vc.read()
|
||||||
self.w = image.shape[1]
|
self.w = image.shape[1]
|
||||||
|
|
|
||||||
51
main.py
51
main.py
|
|
@ -1,5 +1,6 @@
|
||||||
import os
|
import os
|
||||||
import time
|
import time
|
||||||
|
import argparse
|
||||||
|
|
||||||
from Application.Config import Config
|
from Application.Config import Config
|
||||||
from Application.ContourExctractor import ContourExtractor
|
from Application.ContourExctractor import ContourExtractor
|
||||||
|
|
@ -12,45 +13,51 @@ from Application.VideoReader import VideoReader
|
||||||
|
|
||||||
|
|
||||||
def main(config):
|
def main(config):
|
||||||
startTotal = time.time()
|
start_total = time.time()
|
||||||
|
|
||||||
if not os.path.exists(config["importPath"]):
|
if not os.path.exists(config["importPath"]):
|
||||||
contours, masks = ContourExtractor(config).extractContours()
|
contours, masks = ContourExtractor(config).extract_contours()
|
||||||
layerFactory = LayerFactory(config)
|
layers = LayerFactory(config).extract_layers(contours, masks)
|
||||||
layers = layerFactory.extractLayers(contours, masks)
|
|
||||||
else:
|
else:
|
||||||
layers, contours, masks = Importer(config).importRawData()
|
layers, contours, masks = Importer(config).import_raw_data()
|
||||||
layerFactory = LayerFactory(config)
|
layers = LayerFactory(config).extract_layers(contours, masks)
|
||||||
layers = layerFactory.extractLayers(contours, masks)
|
|
||||||
|
|
||||||
layerManager = LayerManager(config, layers)
|
layer_manager = LayerManager(config, layers)
|
||||||
layerManager.cleanLayers()
|
layer_manager.clean_layers()
|
||||||
|
|
||||||
# layerManager.tagLayers()
|
# layerManager.tagLayers()
|
||||||
if len(layerManager.layers) == 0:
|
if len(layer_manager.layers) == 0:
|
||||||
exit(1)
|
exit(1)
|
||||||
|
|
||||||
heatmap = HeatMap(
|
heatmap = HeatMap(
|
||||||
config["w"], config["h"], [contour for layer in layerManager.layers for contour in layer.bounds], 1920 / config["resizeWidth"]
|
config["w"], config["h"], [contour for layer in layer_manager.layers for contour in layer.bounds], 1920 / config["resizeWidth"]
|
||||||
)
|
)
|
||||||
heatmap.showImage()
|
heatmap.save__image(config["outputPath"].split(".")[0] + "_heatmap.png")
|
||||||
|
|
||||||
print(f"Exporting {len(contours)} Contours and {len(layerManager.layers)} Layers")
|
print(f"Exporting {len(contours)} Contours and {len(layer_manager.layers)} Layers")
|
||||||
Exporter(config).export(layerManager.layers, contours, masks, raw=True, overlayed=True)
|
Exporter(config).export(layer_manager.layers, contours, masks, raw=True, overlayed=True)
|
||||||
print("Total time: ", time.time() - startTotal)
|
print("Total time: ", time.time() - start_total)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
|
||||||
|
parser = argparse.ArgumentParser(description='Extract movement from static camera recording')
|
||||||
|
parser.add_argument('input', metavar='input_file', type=str,
|
||||||
|
help='input video to extract movement from')
|
||||||
|
parser.add_argument('output', metavar='output_dir', type=str, nargs="?", default="output",
|
||||||
|
help='output directory to save results and cached files into')
|
||||||
|
args = parser.parse_args()
|
||||||
|
|
||||||
config = Config()
|
config = Config()
|
||||||
|
|
||||||
inputPath = os.path.join(os.path.dirname(__file__), "input/x23-1.mp4")
|
input_path = os.path.join(os.path.dirname(__file__), args.input)
|
||||||
outputPath = os.path.join(os.path.dirname(__file__), "output")
|
output_path = os.path.join(os.path.dirname(__file__), args.output)
|
||||||
|
|
||||||
fileName = inputPath.split("/")[-1]
|
file_name = input_path.split("/")[-1]
|
||||||
|
|
||||||
config["inputPath"] = inputPath
|
config["inputPath"] = input_path
|
||||||
config["outputPath"] = os.path.join(outputPath, fileName)
|
config["outputPath"] = os.path.join(output_path, file_name)
|
||||||
config["importPath"] = os.path.join(outputPath, fileName.split(".")[0] + ".txt")
|
config["importPath"] = os.path.join(output_path, file_name.split(".")[0] + ".txt")
|
||||||
config["w"], config["h"] = VideoReader(config).getWH()
|
config["w"], config["h"] = VideoReader(config).get_wh()
|
||||||
|
|
||||||
main(config)
|
main(config)
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue