Skip to content

Commit 1d3154f

Browse files
author
arch
committed
add supervised ignore tracking
1 parent 602dfd8 commit 1d3154f

File tree

4 files changed

+140
-46
lines changed

4 files changed

+140
-46
lines changed

funscript_editor/algorithms/funscriptgenerator.py

Lines changed: 77 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ class FunscriptGeneratorParameter:
3434
start_frame: int
3535
end_frame: int = -1 # default is video end (-1)
3636
number_of_trackers: int = 1
37+
supervised_tracking_is_exit_condition: bool = True
3738

3839
# Settings
3940
points: str = "local_min_max"
@@ -392,35 +393,91 @@ def init_trackers(self, ffmpeg_stream: FFmpegStream) -> tuple:
392393
first_frame = ffmpeg_stream.read()
393394
preview_frame = first_frame
394395
for tracker_number in range(self.params.number_of_trackers):
395-
bbox_woman = self.ui.bbox_selector(preview_frame, "Select {} Feature #{}".format(self.get_target_name(0), tracker_number+1))
396-
preview_frame = self.ui.draw_box_to_image(preview_frame, bbox_woman, color=(255,0,255))
396+
bbox_woman = self.ui.bbox_selector(
397+
preview_frame,
398+
"Select {} Feature #{}".format(self.get_target_name(0), tracker_number+1)
399+
)
400+
401+
preview_frame = self.ui.draw_box_to_image(
402+
preview_frame,
403+
bbox_woman,
404+
color=(255,0,255)
405+
)
406+
397407
if self.params.supervised_tracking:
398408
while True:
399-
tracking_areas_woman[tracker_number] = self.ui.bbox_selector(preview_frame, "Select the Supervised Tracking Area for the {} Feature #{}".format(self.get_target_name(0), tracker_number+1))
400-
if StaticVideoTracker.is_bbox_in_tracking_area(bbox_woman, tracking_areas_woman[tracker_number]): break
409+
tracking_areas_woman[tracker_number] = self.ui.bbox_selector(
410+
preview_frame,
411+
"Select the Supervised Tracking Area for the {} Feature #{}".format(self.get_target_name(0), tracker_number+1)
412+
)
413+
414+
if StaticVideoTracker.is_bbox_in_tracking_area(bbox_woman, tracking_areas_woman[tracker_number]):
415+
break
416+
401417
self.logger.error("Invalid supervised tracking area selected")
402-
preview_frame = self.ui.draw_box_to_image(preview_frame, tracking_areas_woman[tracker_number], color=(0,255,0))
403-
trackers_woman[tracker_number] = StaticVideoTracker(first_frame, bbox_woman, self.video_info.fps, supervised_tracking_area = tracking_areas_woman[tracker_number])
418+
419+
preview_frame = self.ui.draw_box_to_image(
420+
preview_frame,
421+
tracking_areas_woman[tracker_number],
422+
color=(0,255,0)
423+
)
424+
425+
trackers_woman[tracker_number] = StaticVideoTracker(
426+
first_frame,
427+
bbox_woman,
428+
self.video_info.fps,
429+
supervised_tracking_area = tracking_areas_woman[tracker_number],
430+
supervised_tracking_is_exit_condition=self.params.supervised_tracking_is_exit_condition
431+
)
404432
else:
405-
trackers_woman[tracker_number] = StaticVideoTracker(first_frame, bbox_woman, self.video_info.fps)
433+
trackers_woman[tracker_number] = StaticVideoTracker(
434+
first_frame,
435+
bbox_woman,
436+
self.video_info.fps
437+
)
406438

407439
if tracker_number == 0:
408440
bboxes['Woman'][1] = { tracker_number: bbox_woman }
409441
else:
410442
bboxes['Woman'][1][tracker_number] = bbox_woman
411443

412444
if self.params.track_men:
413-
bbox_men = self.ui.bbox_selector(preview_frame, "Select {} Feature #{}".format(self.get_target_name(1), tracker_number+1))
445+
bbox_men = self.ui.bbox_selector(
446+
preview_frame,
447+
"Select {} Feature #{}".format(self.get_target_name(1), tracker_number+1)
448+
)
414449
preview_frame = self.ui.draw_box_to_image(preview_frame, bbox_men, color=(255,0,255))
415450
if self.params.supervised_tracking:
416451
while True:
417-
tracking_areas_men[tracker_number] = self.ui.bbox_selector(preview_frame, "Select the Supervised Tracking Area for the {} Feature #{}".format(self.get_target_name(1), tracker_number+1))
418-
if StaticVideoTracker.is_bbox_in_tracking_area(bbox_men, tracking_areas_men[tracker_number]): break
452+
tracking_areas_men[tracker_number] = self.ui.bbox_selector(
453+
preview_frame,
454+
"Select the Supervised Tracking Area for the {} Feature #{}".format(self.get_target_name(1), tracker_number+1)
455+
)
456+
457+
if StaticVideoTracker.is_bbox_in_tracking_area(bbox_men, tracking_areas_men[tracker_number]):
458+
break
459+
419460
self.logger.error("Invalid supervised tracking area selected")
420-
preview_frame = self.ui.draw_box_to_image(preview_frame, tracking_areas_men[tracker_number], color=(255,0,255))
421-
trackers_men[tracker_number] = StaticVideoTracker(first_frame, bbox_men, self.video_info.fps, supervised_tracking_area = tracking_areas_men[tracker_number])
461+
462+
preview_frame = self.ui.draw_box_to_image(
463+
preview_frame,
464+
tracking_areas_men[tracker_number],
465+
color=(255,0,255)
466+
)
467+
468+
trackers_men[tracker_number] = StaticVideoTracker(
469+
first_frame,
470+
bbox_men,
471+
self.video_info.fps,
472+
supervised_tracking_area = tracking_areas_men[tracker_number],
473+
supervised_tracking_is_exit_condition = self.params.supervised_tracking_is_exit_condition
474+
)
422475
else:
423-
trackers_men[tracker_number] = StaticVideoTracker(first_frame, bbox_men, self.video_info.fps)
476+
trackers_men[tracker_number] = StaticVideoTracker(
477+
first_frame,
478+
bbox_men,
479+
self.video_info.fps
480+
)
424481

425482
if tracker_number == 0:
426483
bboxes['Men'][1] = { tracker_number: bbox_men }
@@ -434,7 +491,7 @@ def tracking(self) -> str:
434491
""" Tracking function to track the features in the video
435492
436493
TODO:
437-
Tracking lost for multiple tracker
494+
bring back Tracking lost function
438495
439496
Returns:
440497
str: a process status message e.g. 'end of video reached'
@@ -513,7 +570,6 @@ def tracking(self) -> str:
513570
boxes_to_draw.append(tracking_areas_men[tracker_number])
514571

515572

516-
517573
scene_change_quit_flag = False
518574
if scene_detector.is_scene_change(frame_num-1 + self.params.start_frame):
519575
self.logger.info("Scene change detected, Pause tracking")
@@ -615,16 +671,14 @@ def finished(self, status: str, success :bool) -> None:
615671
self.funscriptCompleted.emit(self.funscript, status, success)
616672

617673

618-
def apply_shift(self, frame_number: int, metric: str, position: str) -> int:
619-
""" Apply shift to predicted frame positions
674+
def get_absolute_framenumber(self, frame_number: int) -> int:
675+
""" Get the absoulte frame number
620676
621677
Args:
622678
frame_number (int): relative frame number
623-
metric (str): metric to apply the shift
624-
position (str): keyword ['max', 'min', 'None']
625679
626680
Returns:
627-
int: real frame position
681+
int: absolute frame position
628682
"""
629683
return self.params.start_frame + frame_number
630684

@@ -709,7 +763,7 @@ def create_funscript(self, idx_dict: dict) -> None:
709763
for idx in range(len(output_score)):
710764
self.funscript.add_action(
711765
output_score[idx],
712-
FFmpegStream.frame_to_millisec(self.apply_shift(idx, self.params.metric, 'None'), self.video_info.fps)
766+
FFmpegStream.frame_to_millisec(self.get_absolute_framenumber(idx), self.video_info.fps)
713767
)
714768

715769
else:
@@ -718,13 +772,13 @@ def create_funscript(self, idx_dict: dict) -> None:
718772
for idx in idx_dict['min']:
719773
self.funscript.add_action(
720774
round(output_score[idx]),
721-
FFmpegStream.frame_to_millisec(self.apply_shift(idx, self.params.metric, 'min'), self.video_info.fps)
775+
FFmpegStream.frame_to_millisec(self.get_absolute_framenumber(idx), self.video_info.fps)
722776
)
723777

724778
for idx in idx_dict['max']:
725779
self.funscript.add_action(
726780
round(output_score[idx]),
727-
FFmpegStream.frame_to_millisec(self.apply_shift(idx, self.params.metric, 'max'), self.video_info.fps)
781+
FFmpegStream.frame_to_millisec(self.get_absolute_framenumber(idx), self.video_info.fps)
728782
)
729783

730784

funscript_editor/algorithms/videotracker.py

Lines changed: 23 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,9 @@ def __init__(self,
4545
fps: float,
4646
limit_searchspace : dict = {'h': 0.45, 'w':0.4},
4747
supervised_tracking_area: tuple = None,
48+
supervised_tracking_is_exit_condition: bool = True,
4849
queue_size : int = 2):
50+
self.logger = logging.getLogger(__name__)
4951
self.params = StaticVideoTrackerParameter()
5052
self.first_frame = first_frame
5153
self.limit_searchspace = limit_searchspace
@@ -63,10 +65,8 @@ def __init__(self,
6365
self.tracking_counter = 0
6466
self.cluster_center = [0, 0]
6567
self.plausibility_thresholds = [0, 0]
66-
67-
68-
69-
__logger = logging.getLogger(__name__)
68+
self.last_valid_tracking_box = tracking_bbox
69+
self.supervised_tracking_is_exit_condition = supervised_tracking_is_exit_condition
7070

7171

7272
@dataclass
@@ -137,14 +137,14 @@ def result(self) -> tuple:
137137
def __setup_tracker(self) -> None:
138138
""" Setup the tracker specified in the config """
139139
if self.params.tracking_algorithm.upper() == 'MIL':
140-
self.__logger.info("Start MIL Tracker")
140+
self.logger.info("Start MIL Tracker")
141141
self.tracker = cv2.TrackerMIL_create()
142142
elif self.params.tracking_algorithm.upper() == 'KCF':
143-
self.__logger.info("Start KCF Tracker")
143+
self.logger.info("Start KCF Tracker")
144144
self.tracker = cv2.TrackerKCF_create()
145145
else:
146146
# falback is CSRT tracker
147-
self.__logger.info("Start CSRT Tracker")
147+
self.logger.info("Start CSRT Tracker")
148148
self.tracker = cv2.TrackerCSRT_create()
149149

150150

@@ -154,7 +154,7 @@ def __is_plausible(self, box) -> bool:
154154
self.tracking_points.append([box[0] + box[2]/2, box[1] + box[3]/2])
155155

156156
if self.tracking_counter == round(self.fps * self.params.tracking_init_phase_in_sec):
157-
self.__logger.info("Determine Plausibility Threshold for Tracker")
157+
self.logger.info("Determine Plausibility Threshold for Tracker")
158158
self.cluster_center = np.mean(np.array(self.tracking_points), axis = 0)
159159

160160
distances_x = [abs(self.cluster_center[0] - point[0]) for point in self.tracking_points]
@@ -167,7 +167,7 @@ def __is_plausible(self, box) -> bool:
167167
else:
168168
point = [box[0] + box[2]/2, box[1] + box[3]/2]
169169
if abs(point[0] - self.cluster_center[0]) > self.plausibility_thresholds[0]:
170-
self.__logger.warning(
170+
self.logger.warning(
171171
"Tracking point x is not plausible (%d > %d +- %d)",
172172
round(point[0]),
173173
round(self.cluster_center[0]),
@@ -176,7 +176,7 @@ def __is_plausible(self, box) -> bool:
176176
return False
177177

178178
if abs(point[1] - self.cluster_center[1]) > self.plausibility_thresholds[1]:
179-
self.__logger.warning(
179+
self.logger.warning(
180180
"Tracking point y is not plausible (%d > %d +- %d)",
181181
round(point[1]),
182182
round(self.cluster_center[1]),
@@ -219,9 +219,9 @@ def run(self) -> None:
219219
wait_counter += 1
220220
if wait_counter == 2000:
221221
if self.queue_in.qsize() == 0:
222-
self.__logger.error("Video Tracker still waiting for Input")
222+
self.logger.error("Video Tracker still waiting for Input")
223223
else:
224-
self.__logger.error("Video Tracker output queue overrun!!!")
224+
self.logger.error("Video Tracker output queue overrun!!!")
225225
else:
226226
frame = self.queue_in.get()
227227
frame_roi = frame[y0:y1, x0:x1]
@@ -233,15 +233,22 @@ def run(self) -> None:
233233
else:
234234
status = StaticVideoTracker.Status.OK
235235
bbox = (int(bbox[0] + x0), int(bbox[1] + y0), int(bbox[2]), int(bbox[3]))
236-
if not StaticVideoTracker.is_bbox_in_tracking_area(bbox, self.supervised_tracking_area):
237-
status = StaticVideoTracker.Status.FEATURE_OUTSIDE
238-
elif self.params.tracking_plausibility_check:
236+
if self.params.tracking_plausibility_check:
239237
if not self.__is_plausible(bbox):
240238
status = StaticVideoTracker.Status.IMPLAUSIBLE
239+
elif not StaticVideoTracker.is_bbox_in_tracking_area(bbox, self.supervised_tracking_area):
240+
if self.supervised_tracking_is_exit_condition:
241+
status = StaticVideoTracker.Status.FEATURE_OUTSIDE
242+
else:
243+
bbox = self.last_valid_tracking_box
244+
241245

242246
self.queue_out.put((status, bbox))
243247

248+
if status == StaticVideoTracker.Status.OK:
249+
self.last_valid_tracking_box = bbox
250+
244251
if platform.system() != 'Windows':
245252
# TODO logging here on windows cause open background process
246-
self.__logger.info("Video Tracker Stoped")
253+
self.logger.info("Video Tracker Stoped")
247254

funscript_editor/ui/funscript_generator_window.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,7 @@ def run(self) -> None:
126126
video_path = self.video_file,
127127
track_men = 'two' in self.settings['trackingMethod'],
128128
supervised_tracking = 'Supervised' in self.settings['trackingMethod'],
129+
supervised_tracking_is_exit_condition = "stopping" in self.settings['trackingMethod'],
129130
metric = self.settings['trackingMetric'].replace('inverted', '').strip(),
130131
projection = self.settings['videoType'],
131132
invert = "inverted" in self.settings['trackingMetric'],

funscript_editor/ui/settings_dialog.py

Lines changed: 39 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -114,11 +114,43 @@ def __setup_combo_boxes(self):
114114
self.ui.videoTypeComboBox.addItems([PROJECTION[key]['name'] \
115115
for key in PROJECTION.keys() \
116116
if 'vr' not in key.lower() or self.include_vr])
117-
self.ui.trackingMethodComboBox.addItems(['Unsupervised one moving person', 'Unsupervised two moving persons', 'Supervised one moving person', 'Supervised two moving persons']) # set before tracking metric
118-
self.ui.trackingMetricComboBox.addItems(['y (up-down)', 'y inverted (down-up)', 'x (left-right)', 'x inverted (right-left)', 'distance (p1-p2)', 'distance inverted (p2-p1)', 'roll (rotation)', 'roll inverted (rotation)'])
119-
self.ui.pointsComboBox.addItems(["Local Min Max", "Direction Changed"])
120-
self.ui.additionalPointsComboBox.addItems(["None", "High Second Derivative", "Distance Minimization"])
121-
self.ui.processingSpeedComboBox.addItems(["0 (accurate)", "1 (normal)", "2 (fast)"])
117+
118+
self.ui.trackingMethodComboBox.addItems([
119+
'Unsupervised one moving person',
120+
'Unsupervised two moving persons',
121+
'Supervised stopping one moving person',
122+
'Supervised stopping two moving persons',
123+
'Supervised ignoring one moving person',
124+
'Supervised ignoring two moving persons'
125+
]) # set before tracking metric!
126+
127+
self.ui.trackingMetricComboBox.addItems([
128+
'y (up-down)',
129+
'y inverted (down-up)',
130+
'x (left-right)',
131+
'x inverted (right-left)',
132+
'distance (p1-p2)',
133+
'distance inverted (p2-p1)',
134+
'roll (rotation)',
135+
'roll inverted (rotation)'
136+
])
137+
138+
self.ui.pointsComboBox.addItems([
139+
"Local Min Max",
140+
"Direction Changed"
141+
])
142+
143+
self.ui.additionalPointsComboBox.addItems([
144+
"None",
145+
"High Second Derivative",
146+
"Distance Minimization"
147+
])
148+
149+
self.ui.processingSpeedComboBox.addItems([
150+
"0 (accurate)",
151+
"1 (normal)",
152+
"2 (fast)"
153+
])
122154

123155
self.ui.numberOfTrackerComboBox.addItems([str(i) for i in range(1, 6)])
124156

@@ -129,11 +161,11 @@ def __set_tracking_metric(self, value):
129161

130162
if value in ['x', 'y', 'x inverted', 'y inverted']:
131163
if 'Unsupervised one moving person' not in current_tracking_method_items:
132-
self.ui.trackingMethodComboBox.addItems(['Unsupervised one moving person', 'Supervised one moving person'])
164+
self.ui.trackingMethodComboBox.addItems(['Unsupervised one moving person', 'Supervised stopping one moving person'])
133165
else:
134166
if 'Unsupervised one moving person' in current_tracking_method_items:
135167
self.ui.trackingMethodComboBox.clear()
136-
self.ui.trackingMethodComboBox.addItems(['Unsupervised two moving persons', 'Supervised two moving persons'])
168+
self.ui.trackingMethodComboBox.addItems(['Unsupervised two moving persons', 'Supervised stopping two moving persons'])
137169

138170
self.__set_str_setting('trackingMetric', value)
139171

0 commit comments

Comments
 (0)