[merger] move code to merger class

This commit is contained in:
Jan Grewe 2025-01-17 15:07:11 +01:00
parent 9946c4c72a
commit f2f1741a77

View File

@ -14,6 +14,7 @@ class ProducerSignals(QObject):
# start = pyqtSignal(float) # start = pyqtSignal(float)
# running = pyqtSignal() # running = pyqtSignal()
progress = pyqtSignal(float) progress = pyqtSignal(float)
progress2 = pyqtSignal((str, float, float))
class ImageReader(QRunnable): class ImageReader(QRunnable):
@ -97,6 +98,9 @@ class Merger(QRunnable):
self._right_cut = right_cut self._right_cut = right_cut
self._result = None self._result = None
self._stopRequest = False self._stopRequest = False
self._merged = None
self._current_task = ""
self._mergeprogress = 0.0
for df in [self._left_data, self._right_data]: for df in [self._left_data, self._right_data]:
if not self.check_dataframe(df): if not self.check_dataframe(df):
self.signals.error.emit("Merger.__init__: Error checking DataFrame structure!") self.signals.error.emit("Merger.__init__: Error checking DataFrame structure!")
@ -132,166 +136,6 @@ class Merger(QRunnable):
numpy.ndarray numpy.ndarray
2D array, Coordinates of the bounding box for each detection. Shape: (num_detections, 4) x1, y1, x2, y2 2D array, Coordinates of the bounding box for each detection. Shape: (num_detections, 4) x1, y1, x2, y2
""" """
key_columns = [c for c in df.columns if "key_" in c]
box_columns = [c for c in df.columns if "box_" in c]
num_detections = len(df)
num_keypoints = len(key_columns)
dimensions = 2
keypoints = np.empty((num_detections, num_keypoints, dimensions))
visibility = np.empty((num_detections, num_keypoints))
boxcoordinates = np.empty((num_detections, 4))
for i, row in df.iterrows():
for j, k in enumerate(key_columns):
key_data = row[k]
l = list(map(float, list(key_data[1:-1].split(","))))
keypoints[i, j, :] = l
for j, b in enumerate(box_columns):
boxcoordinates[i, j] = row[b]
if isinstance(row["visible"], str):
vis = list(map(float, row["visible"][1:-1].split()))
visibility[i, :] = vis
else:
visibility[i, :] = row["visible"]
return keypoints, visibility, boxcoordinates
def sort_detections(self, keypoints, threshold, left=True):
"""Categorize the detections into those that are easy (not in the visual overlap zone) and those that are tricky, i.e. right across the threshold.
Detections beyond threshold can be discarded, those across the threshold need to be treated separately.
Parameters
----------
keypoints : np.ndarray
3d array of keypoint coordinates (num detections, num keypoints, (x,y))
threshold : int
the threshold line at which the data should be merged
left : bool, optional
whether or not the data is from the left side, controls how the threshold is interpreted, by default True
Returns
-------
np.ndarray
The indices of the easy detections
np.ndarray
The tricky detections
"""
if left:
easyindeces = np.where(np.all(keypoints[:,:,0] < threshold, axis=1))[0]
trickyindices = np.where(np.any((keypoints[:,:,0] >= threshold) &
(keypoints[:,:,0] < threshold), axis=1))[0]
else:
easyindeces = np.where(np.all(keypoints[:,:,0] >= threshold, axis=1))[0]
trickyindices = np.where(np.any((keypoints[:,:,0] < threshold) &
(keypoints[:,:,0] >= threshold), axis=1))[0]
return easyindeces, trickyindices
@pyqtSlot()
def stop_request(self):
self._stopRequest = True
@pyqtSlot()
def run(self):
max_frames = len(self._left_data) + len(self._right_data)
logging.debug("Cutting left detections to limit %i", self.left_cut)
self.signals.progress.emit(0.1)
lkeypoints, lquality, lbox = self.to_numpy(self._left_data)
self.signals.progress.emit(0.2)
lframes = self._left_data.frame.values
led, ltd = self.sort_detections(lkeypoints, self.left_cut, left=True)
self.signals.progress.emit(0.3)
logging.debug("Cutting right detections to limit %i", self._right_cut_cut)
rkeypoints, rquality, rbox = self.to_numpy(self.right_data)
rframes = self.right_data.frame.values
red, rtd = self.sort_detections(rkeypoints, self.right_cut, left=False)
rkeypoints[:, :, 0] += (self.left_cut - self.right_cut)
# here we need to decide what to do with these data points, trust the left, or trust the right perspective?
# we could also discard them. unless it is a lot of data points, not much harm will be done...
# next step after handling the tricky ones is to export the data again to pandas? nixtrack?
# 1. the right coordinates have to adapted! x - right_threshold + left_threshold!
embed()
exit()
# logging.debug("Merger: running merge for %i frames", max_frames)
# self._stopRequest = False
# max_frames = max(self._left_data.frame.max(), self._right_data.frame.max())
# step = max_frames // 100
# self._result = pd.DataFrame(columns=self._left_data.columns)
# for frame in range(max_frames):
# if self._stopRequest:
# break
# lf = self._left_data[self._left_data.frame == frame]
# rf = self._right_data[self._right_data.frame == frame]
# merge_frame(lf, rf, self._left_cut, self._right_cut, self._result)
# if frame % step == 0:
# self.signals.progress.emit(frame/max_frames)
# time.sleep(0.01)
self._signals.finished.emit(True and (not self._stopRequest))
@property
def signals(self):
return self._signals
@property
def result(self):
return self._result
def merge_frames(left, right, leftcut, rightcut, destination):
# for
pass
def check_frame(frame, cut, left=True):
"""checks whether the detected object is (partially) in the overlapping zone of the two cameras.
A frame is 'ok' if the box is not in the danger zone.
Parameters
----------
frame : pd.Series,
a row of the DataFrame
cut : int
The cut x-position
left : bool, optional
whether we are looking at the right or left frame, by default True
Returns
-------
bool
whether or not the
"""
if left:
return not (frame.box_x1 > cut or frame.box_x2 > cut) # any of the box coordinates is beyond the cut
else:
return not (frame.box_x1 < cut or frame.box_x2 < cut)
def merge_detections(left_data:pd.DataFrame, right_data: pd.DataFrame, left_cut: int, right_cut: int) ->pd.DataFrame:
"""Merge the key-point detections based on the left and right video. Key-points with x-coordinates beyond each of the limits (left and right cut) are discarded.
How to merge the detections WITHIN one of the fish detections???
Parameters
----------
left_data : pd.DataFrame
Detections based on the left video
right_data : pd.DataFrame
Detections based on the right video.
left_cut : int
Where to cut off and discard the detected key points.
right_cut : int
Where to cut off and discard the detected key points.
Returns
-------
pd.DataFrame
merged detections of left and right data
"""
def check_dataframe(df):
return True
def to_numpy(df):
logging.info("Converting to numpy ...") logging.info("Converting to numpy ...")
key_columns = [c for c in df.columns if "key_" in c] key_columns = [c for c in df.columns if "key_" in c]
box_columns = [c for c in df.columns if "box_" in c] box_columns = [c for c in df.columns if "box_" in c]
@ -314,10 +158,10 @@ def merge_detections(left_data:pd.DataFrame, right_data: pd.DataFrame, left_cut:
visibility[i, :] = vis visibility[i, :] = vis
else: else:
visibility[i, :] = row["visible"] visibility[i, :] = row["visible"]
logging.debug("Converting to numpy done!") logging.info("Converting to numpy done!")
return keypoints, visibility, boxcoordinates return keypoints, visibility, boxcoordinates
def sort_detections(keypoints, threshold, left=True): def sort_detections(self, keypoints, threshold, left=True):
"""Categorize the detections into those that are easy (not in the visual overlap zone) and those that are tricky, i.e. right across the threshold. """Categorize the detections into those that are easy (not in the visual overlap zone) and those that are tricky, i.e. right across the threshold.
Detections beyond threshold are ignored, those across the threshold need to be treated separately. Detections beyond threshold are ignored, those across the threshold need to be treated separately.
@ -347,8 +191,9 @@ def merge_detections(left_data:pd.DataFrame, right_data: pd.DataFrame, left_cut:
trickyindices = np.where(np.any((keypoints[:,:,0] < threshold) & trickyindices = np.where(np.any((keypoints[:,:,0] < threshold) &
(keypoints[:,:,0] >= threshold), axis=1))[0] (keypoints[:,:,0] >= threshold), axis=1))[0]
return easyindeces, trickyindices return easyindeces, trickyindices
def select_and_transform(df, keypoints, boxes, quality, frames, valid_detections, left_threshold=None, right_threshold=None): def select_and_transform(self, df, keypoints, boxes, quality, frames, valid_detections,
left_threshold=None, right_threshold=None):
keypoints = keypoints[valid_detections, :, :] keypoints = keypoints[valid_detections, :, :]
boxes = boxes[valid_detections, :] boxes = boxes[valid_detections, :]
quality = quality[valid_detections, :] quality = quality[valid_detections, :]
@ -360,8 +205,8 @@ def merge_detections(left_data:pd.DataFrame, right_data: pd.DataFrame, left_cut:
return df, keypoints, quality, boxes, frames return df, keypoints, quality, boxes, frames
def to_dataframe(self, old_left, old_right, lkeypoints, rkeypoints, lboxes, rboxes,
def to_dataframe(old_left, old_right, lkeypoints, rkeypoints, lboxes, rboxes, lqualities, rqualities, lframes, rframes): lqualities, rqualities, lframes, rframes):
frames = np.concatenate([lframes, rframes]) frames = np.concatenate([lframes, rframes])
sorting = np.argsort(frames) sorting = np.argsort(frames)
frames = frames[sorting] frames = frames[sorting]
@ -391,31 +236,53 @@ def merge_detections(left_data:pd.DataFrame, right_data: pd.DataFrame, left_cut:
df = pd.DataFrame(d) df = pd.DataFrame(d)
return df return df
def save(self, filename):
if self._merged is None:
logging.error("Saving/pickling merged dataFrame is None!")
return
logging.info("Saving/pickling merged file to %s" % filename)
with open(filename, 'rb') as f:
pickle.dump(self._merged, f)
@pyqtSlot()
def stop_request(self):
self._stopRequest = True
logging.info("Cutting left detections to limit %i", left_cut) @pyqtSlot()
if not check_dataframe(left_data) or not check_dataframe(right_data): def run(self):
logging.error("Left or right dataframe structure does not match my expectations") logging.info("Cutting left detections to limit %i", self._left_cut)
return None self.signals.progress2.emit("Merging", self._mergeprogress, 0.)
logging.info("Converting to numpy %i", left_cut) if not self.check_dataframe(self._left_data) or not self.check_dataframe(self._right_data):
lkeypoints, lquality, lbox = to_numpy(left_data) logging.error("Left or right dataframe structure does not match my expectations")
rkeypoints, rquality, rbox = to_numpy(right_data) return None
lframes = left_data.frame.values logging.info("Converting to numpy... %s", "Left camera")
rframes = right_data.frame.values lkeypoints, lquality, lbox = self.to_numpy(self._left_data)
logging.info("Filtering detections") logging.info("Converting to numpy... %s", "Right camera")
left_easy, _ = sort_detections(lkeypoints, left_cut, left=True) rkeypoints, rquality, rbox = self.to_numpy(self._right_data)
right_easy, _ = sort_detections(rkeypoints, right_cut, left=False) lframes = self._left_data.frame.values
logging.info("Merging and transformation") rframes = self._right_data.frame.values
ldf, lkeypoints, lquality, lboxes, lframes = select_and_transform(left_data, lkeypoints, lbox, lquality, lframes, left_easy) logging.info("Filtering detections")
rdf, rkeypoints, rquality, rboxes, rframes = select_and_transform(right_data, rkeypoints, rbox, rquality, rframes, right_easy, left_cut, right_cut) left_easy, _ = self.sort_detections(lkeypoints, self._left_cut, left=True)
export_df = to_dataframe(ldf, rdf, lkeypoints, rkeypoints, lboxes, rboxes, lquality, rquality, lframes, rframes) right_easy, _ = self.sort_detections(rkeypoints, self._right_cut, left=False)
logging.info("Merging and transformation")
ldf, lkeypoints, lquality, lboxes, lframes = self.select_and_transform(self._left_data, lkeypoints, lbox,
lquality, lframes, left_easy)
rdf, rkeypoints, rquality, rboxes, rframes = self.select_and_transform(self._right_data, rkeypoints, rbox,
rquality, rframes, right_easy,
self._left_cut, self._right_cut)
self._merged = self.to_dataframe(ldf, rdf, lkeypoints, rkeypoints, lboxes, rboxes, lquality, rquality,
lframes, rframes)
logging.info("Merging done!")
self._signals.finished.emit(True and (not self._stopRequest))
filename = "test.pkl" @property
logging.info("Saving/pickling merged file to %s" % filename) def signals(self):
with open(filename, 'rb') as f: return self._signals
pickle.dump(export_df, f)
logging.info("Merging done!") @property
def result(self):
return self._result
def main(): def main():
@ -423,7 +290,9 @@ def main():
left = pd.read_csv("../data/left_tracks.csv", sep=";", index_col=0) left = pd.read_csv("../data/left_tracks.csv", sep=";", index_col=0)
logging.info("Loading data right") logging.info("Loading data right")
right = pd.read_csv("../data/right_tracks.csv", sep=";", index_col=0) right = pd.read_csv("../data/right_tracks.csv", sep=";", index_col=0)
merge_detections(left, right, 2000, 300) # merge_detections(left, right, 2000, 300)
merger = Merger(left, right, 2000, 300 )
merger.run()
if __name__ == "__main__": if __name__ == "__main__":