Skip to content

Commit 2bf3795

Browse files
author
arch
committed
add simple shift point function
1 parent 9a30a88 commit 2bf3795

File tree

7 files changed

+79
-23
lines changed

7 files changed

+79
-23
lines changed

funscript_editor/algorithms/funscriptgenerator.py

Lines changed: 44 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
from PyQt5 import QtGui, QtCore, QtWidgets
1616
from matplotlib.figure import Figure
1717
from funscript_editor.definitions import VIDEO_SCALING_CONFIG_FILE
18-
from funscript_editor.utils.config import HYPERPARAMETER
18+
from funscript_editor.utils.config import HYPERPARAMETER, SETTINGS
1919
from datetime import datetime
2020

2121
import funscript_editor.algorithms.signalprocessing as sp
@@ -29,9 +29,11 @@ class FunscriptGeneratorParameter:
2929
video_path: str # no default value
3030
start_frame: int = 0 # default is video start
3131
skip_frames: int = HYPERPARAMETER['skip_frames']
32-
max_playback_fps: int = HYPERPARAMETER['max_playback_fps']
33-
direction: str = HYPERPARAMETER['tracking_direction']
34-
use_zoom: bool = HYPERPARAMETER['use_zoom']
32+
max_playback_fps: int = SETTINGS['max_playback_fps']
33+
direction: str = SETTINGS['tracking_direction']
34+
use_zoom: bool = SETTINGS['use_zoom']
35+
shift_bottom_points :int = HYPERPARAMETER['shift_bottom_points']
36+
shift_top_points :int = HYPERPARAMETER['shift_top_points']
3537
track_men: bool = True
3638

3739

@@ -324,7 +326,7 @@ def plot_y_score(self, name: str, idx_list: list, dpi : int = 300) -> None:
324326
325327
Args:
326328
name (str): file name for the figure
327-
idx_list (list): list with funscript action points
329+
idx_list (list): list with all frame numbers with funscript action points
328330
dpi (int): picture output dpi
329331
"""
330332
if len(self.score_y) < 2: return
@@ -432,6 +434,9 @@ def tracking(self) -> str:
432434
if first_frame is None:
433435
return
434436

437+
if self.params.skip_frames < 0:
438+
self.params.skip_frames = 0
439+
435440
bboxWoman = self.get_bbox(first_frame, "Select Woman Feature")
436441
trackerWoman = StaticVideoTracker(first_frame, bboxWoman)
437442
self.bboxes['Woman'].append(bboxWoman)
@@ -441,7 +446,7 @@ def tracking(self) -> str:
441446
trackerMen = StaticVideoTracker(first_frame, bboxMen)
442447
self.bboxes['Men'].append(bboxMen)
443448

444-
if self.params.max_playback_fps > 2:
449+
if self.params.max_playback_fps > (self.params.skip_frames+1):
445450
cycle_time_in_ms = (float(1000) / float(self.params.max_playback_fps)) * (self.params.skip_frames+1)
446451
else:
447452
cycle_time_in_ms = 0
@@ -562,6 +567,23 @@ def finished(self, status: str, success :bool) -> None:
562567
self.stopped = True
563568

564569

570+
def apply_shift(self, frame_number, position: str) -> int:
571+
""" Apply shift to predicted frame positions
572+
573+
Args:
574+
position (str): is max or min
575+
"""
576+
if position in ['max', 'top'] and self.params.direction != 'x':
577+
if frame_number >= -1*self.params.shift_top_points and frame_number + self.params.shift_top_points < len(self.score_y):
578+
return self.params.start_frame + frame_number + self.params.shift_top_points
579+
580+
if position in ['min', 'bottom'] and self.params.direction != 'x':
581+
if frame_number >= -1*self.params.shift_bottom_points and frame_number + self.params.shift_bottom_points < len(self.score_y):
582+
return self.params.start_frame + frame_number + self.params.shift_bottom_points
583+
584+
return self.params.start_frame + frame_number
585+
586+
565587
def run(self) -> None:
566588
""" The Funscript Generator Thread Function """
567589
# NOTE: score_y and score_x should have the same number of elements so it should be enouth to check one score length
@@ -578,18 +600,28 @@ def run(self) -> None:
578600
return
579601

580602
if self.params.direction != 'x':
581-
idx_list = sp.get_local_max_and_min_idx(self.score_y, self.fps)
603+
idx_dict = sp.get_local_max_and_min_idx(self.score_y, self.fps)
582604
else:
583-
idx_list = sp.get_local_max_and_min_idx(self.score_x, self.fps)
605+
idx_dict = sp.get_local_max_and_min_idx(self.score_x, self.fps)
606+
607+
idx_list = [x for k in ['min', 'max'] for x in idx_dict[k]]
608+
idx_list.sort()
584609

585610
if False:
586-
if self.params.direction != 'x': self.plot_y_score('debug_001.png', idx_list)
587-
self.plot_scores('debug_002.png')
611+
self.plot_scores('debug_001.png')
612+
if self.params.direction != 'x':
613+
self.plot_y_score('debug_002.png', idx_list)
614+
615+
for idx in idx_dict['min']:
616+
self.funscript.add_action(
617+
self.score_y[idx],
618+
self.frame_to_millisec(self.apply_shift(idx, 'min'), self.fps)
619+
)
588620

589-
for idx in idx_list:
621+
for idx in idx_dict['max']:
590622
self.funscript.add_action(
591623
self.score_y[idx],
592-
self.frame_to_millisec(idx+self.params.start_frame, self.fps)
624+
self.frame_to_millisec(self.apply_shift(idx, 'max'), self.fps)
593625
)
594626

595627
self.finished(status, True)

funscript_editor/algorithms/signalprocessing.py

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -62,30 +62,39 @@ def moving_average(x :list, w: int) -> list:
6262
+[avg[-1] for _ in range(len(avg)+w,len(x))]
6363

6464

65-
def get_local_max_and_min_idx(score :list, fps: int) -> list:
65+
def get_local_max_and_min_idx(score :list, fps: int, shift_min :int = 0, shift_max :int = 0) -> dict:
6666
""" Get the local max and min positions in given signal
6767
6868
Args:
6969
score (list): list with float or int signal values
7070
fps (int): rounded fps of the video
71+
shift_min (int): shift the local min indexes by given value (default is 0)
72+
shift_max (int): shift the local max indexes by given value (default is 0)
7173
7274
Returns:
73-
list: list with all local max and min indexes
75+
dict: dict with 2 lists with all local max and min indexes ({'min':[], 'max':[]})
7476
"""
7577
avg = moving_average(score, w=round(fps * HYPERPARAMETER['avg_sec_for_local_min_max_extraction']))
76-
tmp_min_idx, tmp_max_idx, idx_list = -1, -1, []
78+
result = {'min': [], 'max': []}
79+
tmp_min_idx, tmp_max_idx = -1, -1
7780
for pos in range(len(score)):
7881
if score[pos] < avg[pos]:
7982
if tmp_min_idx < 0: tmp_min_idx = pos
8083
elif score[tmp_min_idx] > score[pos]: tmp_min_idx = pos
8184
elif tmp_min_idx >= 0:
82-
idx_list.append(tmp_min_idx)
85+
if tmp_min_idx >= -1*shift_min and tmp_min_idx + shift_min < len(score):
86+
result['min'].append(tmp_min_idx + shift_min)
87+
else:
88+
result['min'].append(tmp_min_idx)
8389
tmp_min_idx = -1
8490

8591
if score[pos] > avg[pos]:
8692
if tmp_max_idx < 0: tmp_max_idx = pos
8793
elif score[tmp_max_idx] < score[pos]: tmp_max_idx = pos
8894
elif tmp_max_idx >= 0:
89-
idx_list.append(tmp_max_idx)
95+
if tmp_max_idx >= -1*shift_max and tmp_max_idx + shift_max < len(score):
96+
result['max'].append(tmp_max_idx + shift_max)
97+
else:
98+
result['max'].append(tmp_max_idx)
9099
tmp_max_idx = -1
91-
return idx_list
100+
return result
Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,13 @@
1+
# interpolate given frames to increase the processing speed
12
skip_frames: 1
3+
4+
# specify the window size for the calculation of the reference value for the local min and max search.
25
avg_sec_for_local_min_max_extraction: 1.9
6+
7+
# specify the minimum required frames for the tracking.
38
min_frames: 120
4-
use_zoom: False
5-
tracking_direction: 'y'
6-
max_playback_fps: 0
9+
10+
# shift predicted points by given frame number.
11+
# positive values delay the position and negative values result in an earlier position.
12+
shift_top_points: 0
13+
shift_bottom_points: 0
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
use_zoom: False
2+
tracking_direction: 'y'
3+
max_playback_fps: 0

funscript_editor/definitions.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838
WINDOWS_LOG_CONFIG_FILE = os.path.join(CONFIG_DIR, 'logging_windows.yaml')
3939
LINUX_LOG_CONFIG_FILE = os.path.join(CONFIG_DIR, 'logging_linux.yaml')
4040
HYPERPARAMETER_CONFIG_FILE = os.path.join(CONFIG_DIR, 'hyperparameter.yaml')
41+
SETTINGS_CONFIG_FILE = os.path.join(CONFIG_DIR, 'settings.yaml')
4142

4243

4344
##########

funscript_editor/ui/video_player.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -291,7 +291,7 @@ def increase_speed(self) -> None:
291291

292292
def decrease_speed(self) -> None:
293293
""" Decrease the playback speed """
294-
self.player.speed -= 0.2
294+
if self.player.speed > 0.21: self.player.speed -= 0.2
295295
if 0.9 < self.player.speed < 1.1: self.player.speed = 1.0
296296
self.show_message('Speed: ' + str(self.player.speed))
297297

funscript_editor/utils/config.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,8 @@
33
import os
44
import yaml
55

6-
from funscript_editor.definitions import ROOT_DIR, UI_CONFIG_FILE, HYPERPARAMETER_CONFIG_FILE
6+
from funscript_editor.definitions import ROOT_DIR, UI_CONFIG_FILE, \
7+
HYPERPARAMETER_CONFIG_FILE, SETTINGS_CONFIG_FILE
78

89
def read_yaml_config(config_file: str) -> dict:
910
""" Parse a yaml config file
@@ -37,3 +38,6 @@ def read_version() -> str:
3738

3839
#: hyperparameter for the algorithms
3940
HYPERPARAMETER = read_yaml_config(HYPERPARAMETER_CONFIG_FILE)
41+
42+
#: settings
43+
SETTINGS = read_yaml_config(SETTINGS_CONFIG_FILE)

0 commit comments

Comments
 (0)