Skip to content

Commit e8d8a6a

Browse files
author
arch
committed
refactoring
1 parent 0ecece3 commit e8d8a6a

File tree

5 files changed

+55
-75
lines changed

5 files changed

+55
-75
lines changed

funscript_editor/algorithms/funscriptgenerator.py

Lines changed: 49 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ def __init__(self,
6060
QtCore.QThread.__init__(self)
6161
self.params = params
6262
self.funscript = funscript
63+
self.video_info = FFmpegStream.get_video_info(self.params.video_path)
6364
self.timer = cv2.getTickCount()
6465

6566
# XXX destroyWindow(...) sems not to delete the trackbar. Workaround: we give the window each time a unique name
@@ -69,14 +70,14 @@ def __init__(self,
6970
self.x_text_start = 50
7071
self.font_size = 0.6
7172
self.tracking_fps = []
72-
self.scone_x = []
73-
self.scone_y = []
73+
self.score = {
74+
'x': [],
75+
'y': []
76+
}
7477
self.bboxes = {
7578
'Men': [],
7679
'Woman': []
77-
}
78-
79-
self.video_info = FFmpegStream.get_video_info(self.params.video_path)
80+
}
8081

8182

8283
#: completed event with reference to the funscript with the predicted actions, status message and success flag
@@ -88,7 +89,7 @@ def __init__(self,
8889
logger = logging.getLogger(__name__)
8990

9091

91-
def determine_monitor_scaling(self, frame_width, frame_height) -> float:
92+
def determine_preview_scaling(self, frame_width, frame_height) -> float:
9293
""" Determine the scaling for current monitor setup
9394
9495
Args:
@@ -106,7 +107,7 @@ def determine_monitor_scaling(self, frame_width, frame_height) -> float:
106107
self.logger.error("Monitor resolution info not found")
107108
else:
108109
# asume we use the largest monitor for scipting
109-
self.params.preview_scaling *= max(scale)
110+
self.params.preview_scaling = float(SETTINGS['preview_scaling']) * max(scale)
110111

111112

112113
def drawBox(self, img: np.ndarray, bbox: tuple) -> np.ndarray:
@@ -251,18 +252,14 @@ def calculate_score(self) -> None:
251252
We use x0,y0 from the predicted tracking boxes to create a diff score
252253
"""
253254
if self.params.track_men:
254-
self.score_x = [m[0] - w[0] for w, m in zip(self.bboxes['Woman'], self.bboxes['Men'])]
255-
self.score_y = [m[1] - w[1] for w, m in zip(self.bboxes['Woman'], self.bboxes['Men'])]
255+
self.score['x'] = [m[0] - w[0] for w, m in zip(self.bboxes['Woman'], self.bboxes['Men'])]
256+
self.score['y'] = [m[1] - w[1] for w, m in zip(self.bboxes['Woman'], self.bboxes['Men'])]
256257
else:
257-
self.score_x = [max([x[0] for x in self.bboxes['Woman']]) - w[0] for w in self.bboxes['Woman']]
258-
self.score_y = [max([x[1] for x in self.bboxes['Woman']]) - w[1] for w in self.bboxes['Woman']]
258+
self.score['x'] = [max([x[0] for x in self.bboxes['Woman']]) - w[0] for w in self.bboxes['Woman']]
259+
self.score['y'] = [max([x[1] for x in self.bboxes['Woman']]) - w[1] for w in self.bboxes['Woman']]
259260

260-
# NOTE: Scaling with the anomaly function sometimes makes the result even worse
261-
# self.score_x = sp.scale_signal_with_anomalies(self.score_x, 0, 100, lower_quantile=0.05, upper_quantile=0.99999)
262-
# self.score_y = sp.scale_signal_with_anomalies(self.score_y, 0, 100, lower_quantile=0.05, upper_quantile=0.99999)
263-
264-
self.score_x = sp.scale_signal(self.score_x, 0, 100)
265-
self.score_y = sp.scale_signal(self.score_y, 0, 100)
261+
self.score['x'] = sp.scale_signal(self.score['x'], 0, 100)
262+
self.score['y'] = sp.scale_signal(self.score['y'], 0, 100)
266263

267264

268265
@staticmethod
@@ -290,18 +287,18 @@ def scale_score(self, status: str, direction : str = 'y') -> None:
290287
status (str): a status/info message to display in the window
291288
direction (str): scale the 'y' or 'x' score
292289
"""
293-
if len(self.score_y) < 2: return
290+
if len(self.score['y']) < 2: return
294291

295292
cap = cv2.VideoCapture(self.params.video_path)
296293
width = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH))
297294
height = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))
298295

299296
if direction == 'x':
300-
min_frame = np.argmin(np.array(self.score_x)) + self.params.start_frame
301-
max_frame = np.argmax(np.array(self.score_x)) + self.params.start_frame
297+
min_frame = np.argmin(np.array(self.score['x'])) + self.params.start_frame
298+
max_frame = np.argmax(np.array(self.score['x'])) + self.params.start_frame
302299
else:
303-
min_frame = np.argmin(np.array(self.score_y)) + self.params.start_frame
304-
max_frame = np.argmax(np.array(self.score_y)) + self.params.start_frame
300+
min_frame = np.argmin(np.array(self.score['y'])) + self.params.start_frame
301+
max_frame = np.argmax(np.array(self.score['y'])) + self.params.start_frame
305302

306303
cap.set(cv2.CAP_PROP_POS_FRAMES, min_frame)
307304
successMin, imgMin = cap.read()
@@ -339,9 +336,9 @@ def scale_score(self, status: str, direction : str = 'y') -> None:
339336
desired_max = 99
340337

341338
if direction == 'x':
342-
self.score_x = sp.scale_signal(self.score_x, desired_min, desired_max)
339+
self.score['x'] = sp.scale_signal(self.score['x'], desired_min, desired_max)
343340
else:
344-
self.score_y = sp.scale_signal(self.score_y, desired_min, desired_max)
341+
self.score['y'] = sp.scale_signal(self.score['y'], desired_min, desired_max)
345342

346343

347344
def plot_y_score(self, name: str, idx_list: list, dpi : int = 300) -> None:
@@ -352,20 +349,20 @@ def plot_y_score(self, name: str, idx_list: list, dpi : int = 300) -> None:
352349
idx_list (list): list with all frame numbers with funscript action points
353350
dpi (int): picture output dpi
354351
"""
355-
if len(self.score_y) < 2: return
352+
if len(self.score['y']) < 2: return
356353
if len(idx_list) < 2: return
357354
rows = 2
358-
figure = Figure(figsize=(max([6,int(len(self.score_y)/50)]), rows*3+1), dpi=dpi)
355+
figure = Figure(figsize=(max([6,int(len(self.score['y'])/50)]), rows*3+1), dpi=dpi)
359356
ax = figure.add_subplot(2,1,1) # Rows, Columns, Position
360357
ax.title.set_text('Motion in y direction')
361358
# TODO why is there an offset of 1 in the data?
362-
ax.plot(self.score_y[max((0,idx_list[0]-1)):idx_list[-1]])
363-
ax.plot(idx_list, [self.score_y[idx] for idx in idx_list], 'o')
359+
ax.plot(self.score['y'][max((0,idx_list[0]-1)):idx_list[-1]])
360+
ax.plot(idx_list, [self.score['y'][idx] for idx in idx_list], 'o')
364361
ax.legend(['Tracker Prediction','Local Max and Min'], loc='upper right')
365362
ax = figure.add_subplot(2,1,2)
366363
ax.title.set_text('Funscript')
367-
ax.plot(idx_list, [self.score_y[idx] for idx in idx_list])
368-
ax.plot(idx_list, [self.score_y[idx] for idx in idx_list], 'o')
364+
ax.plot(idx_list, [self.score['y'][idx] for idx in idx_list])
365+
ax.plot(idx_list, [self.score['y'][idx] for idx in idx_list], 'o')
369366
figure.savefig(fname=name, dpi=dpi, bbox_inches='tight')
370367

371368

@@ -376,15 +373,15 @@ def plot_scores(self, name: str, dpi : int = 300) -> None:
376373
name (str): file name for the figure
377374
dpi (int): picture output dpi
378375
"""
379-
if len(self.score_y) < 2: return
376+
if len(self.score['y']) < 2: return
380377
rows = 2
381-
figure = Figure(figsize=(max([6,int(len(self.score_y)/50)]), rows*3+1), dpi=dpi)
378+
figure = Figure(figsize=(max([6,int(len(self.score['y'])/50)]), rows*3+1), dpi=dpi)
382379
ax = figure.add_subplot(2,1,1) # Rows, Columns, Position
383380
ax.title.set_text('Motion in x direction')
384-
ax.plot(self.score_x)
381+
ax.plot(self.score['x'])
385382
ax = figure.add_subplot(2,1,2)
386383
ax.title.set_text('Motion in y direction')
387-
ax.plot(self.score_y)
384+
ax.plot(self.score['y'])
388385
figure.savefig(fname=name, dpi=dpi, bbox_inches='tight')
389386

390387

@@ -428,7 +425,7 @@ def get_vr_projection_config(self, image :np.ndarray) -> None:
428425
"""
429426
config = PROJECTION[self.params.projection]
430427

431-
self.mointor_scale = self.determine_monitor_scaling(config['parameter']['width'], config['parameter']['height'])
428+
self.determine_preview_scaling(config['parameter']['width'], config['parameter']['height'])
432429

433430
# NOTE: improve processing speed to make this menu more responsive
434431
if image.shape[0] > 6000 or image.shape[1] > 6000:
@@ -514,6 +511,7 @@ def get_bbox(self, image: np.ndarray, txt: str) -> tuple:
514511
round(bbox[3]/self.params.preview_scaling)
515512
)
516513

514+
# revert the zoom
517515
if self.params.use_zoom:
518516
bbox = (round(bbox[0]/self.params.zoom_factor)+zoom_bbox[0],
519517
round(bbox[1]/self.params.zoom_factor)+zoom_bbox[1],
@@ -524,18 +522,6 @@ def get_bbox(self, image: np.ndarray, txt: str) -> tuple:
524522
return bbox
525523

526524

527-
def get_first_frame(self) -> np.ndarray:
528-
""" Get the first frame image
529-
530-
Returns:
531-
np.ndarray: opencv image
532-
"""
533-
cap = cv2.VideoCapture(str(self.params.video_path))
534-
if self.params.start_frame > 0: cap.set(cv2.CAP_PROP_POS_FRAMES, self.params.start_frame)
535-
success, first_frame = cap.read()
536-
cap.release()
537-
return first_frame
538-
539525

540526
def get_flat_projection_config(self,
541527
first_frame :np.ndarray) -> dict:
@@ -548,16 +534,16 @@ def get_flat_projection_config(self,
548534
dict: config
549535
"""
550536
h, w = first_frame.shape[:2]
551-
config = PROJECTION[self.params.projection]
537+
config = copy.deepcopy(PROJECTION[self.params.projection])
552538

553-
if config['parameter']['height'] == -1:
539+
if PROJECTION[self.params.projection]['parameter']['height'] == -1:
554540
scaling = config['parameter']['width'] / float(w)
555541
config['parameter']['height'] = round(h * scaling)
556-
elif config['parameter']['width'] == -1:
542+
elif PROJECTION[self.params.projection]['parameter']['width'] == -1:
557543
scaling = config['parameter']['height'] / float(h)
558544
config['parameter']['width'] = round(w * scaling)
559545

560-
self.mointor_scale = self.determine_monitor_scaling(config['parameter']['width'], config['parameter']['height'])
546+
self.determine_preview_scaling(config['parameter']['width'], config['parameter']['height'])
561547

562548
return config
563549

@@ -583,12 +569,12 @@ def tracking(self) -> str:
583569

584570
first_frame = video.read()
585571
bboxWoman = self.get_bbox(first_frame, "Select Woman Feature")
586-
trackerWoman = StaticVideoTracker(first_frame, bboxWoman, limit_searchspace = False) # TODO limit_searchspace for flat videos
572+
trackerWoman = StaticVideoTracker(first_frame, bboxWoman)
587573
self.bboxes['Woman'].append(bboxWoman)
588574

589575
if self.params.track_men:
590576
bboxMen = self.get_bbox(self.drawBox(first_frame, bboxWoman), "Select Men Feature")
591-
trackerMen = StaticVideoTracker(first_frame, bboxMen, limit_searchspace = False) # TODO limit_searchspace for flat videos
577+
trackerMen = StaticVideoTracker(first_frame, bboxMen)
592578
self.bboxes['Men'].append(bboxMen)
593579

594580
if self.params.max_playback_fps > (self.params.skip_frames+1):
@@ -598,7 +584,7 @@ def tracking(self) -> str:
598584

599585
status = "End of video reached"
600586
self.clear_keypress_queue()
601-
last_frame, frame_num = None, 1 # first frame is was init frame
587+
last_frame, frame_num = None, 1 # first frame is init frame
602588
while video.isOpen():
603589
cycle_start = time.time()
604590
frame = video.read()
@@ -726,12 +712,12 @@ def apply_shift(self, frame_number, position: str) -> int:
726712
"""
727713
if position in ['max', 'top'] and self.params.direction != 'x':
728714
if frame_number >= -1*self.params.shift_top_points \
729-
and frame_number + self.params.shift_top_points < len(self.score_y): \
715+
and frame_number + self.params.shift_top_points < len(self.score['y']): \
730716
return self.params.start_frame + frame_number + self.params.shift_top_points
731717

732718
if position in ['min', 'bottom'] and self.params.direction != 'x':
733719
if frame_number >= -1*self.params.shift_bottom_points \
734-
and frame_number + self.params.shift_bottom_points < len(self.score_y): \
720+
and frame_number + self.params.shift_bottom_points < len(self.score['y']): \
735721
return self.params.start_frame + frame_number + self.params.shift_bottom_points
736722

737723
return self.params.start_frame + frame_number
@@ -747,9 +733,9 @@ def get_score_with_offset(self, idx_dict) -> list:
747733
list: score with offset
748734
"""
749735
if self.params.direction == 'x':
750-
return self.score_x
736+
return self.score['x']
751737

752-
score = copy.deepcopy(self.score_y)
738+
score = copy.deepcopy(self.score['y'])
753739
score_min, score_max = min(score), max(score)
754740
for idx in idx_dict['min']:
755741
score[idx] = max(( score_min, min((score_max, score[idx] + self.params.bottom_points_offset)) ))
@@ -762,23 +748,23 @@ def get_score_with_offset(self, idx_dict) -> list:
762748

763749
def run(self) -> None:
764750
""" The Funscript Generator Thread Function """
765-
# NOTE: score_y and score_x should have the same number size so it should be enouth to check one score length
751+
# NOTE: score['y'] and score['x'] should have the same number size so it should be enouth to check one score length
766752
with Listener(on_press=self.on_key_press) as listener:
767753
status = self.tracking()
768-
if len(self.score_y) >= HYPERPARAMETER['min_frames']:
754+
if len(self.score['y']) >= HYPERPARAMETER['min_frames']:
769755
if self.params.direction != 'x':
770756
self.scale_score(status, direction='y')
771757
else:
772758
self.scale_score(status, direction='x')
773759

774-
if len(self.score_y) < HYPERPARAMETER['min_frames']:
760+
if len(self.score['y']) < HYPERPARAMETER['min_frames']:
775761
self.finished(status + ' -> Tracking time insufficient', False)
776762
return
777763

778764
if self.params.direction != 'x':
779-
idx_dict = sp.get_local_max_and_min_idx(self.score_y, self.video_info.fps)
765+
idx_dict = sp.get_local_max_and_min_idx(self.score['y'], self.video_info.fps)
780766
else:
781-
idx_dict = sp.get_local_max_and_min_idx(self.score_x, self.video_info.fps)
767+
idx_dict = sp.get_local_max_and_min_idx(self.score['x'], self.video_info.fps)
782768

783769
idx_list = [x for k in ['min', 'max'] for x in idx_dict[k]]
784770
idx_list.sort()

funscript_editor/algorithms/signalprocessing.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ def moving_average(x :list, w: int) -> list:
5656
"""
5757
w = round(w)
5858
avg = np.convolve(x, np.ones(int(w*2)), 'valid') / int(w*2)
59-
# XXX use extrapolation function
59+
# TODO use extrapolation function
6060
return [avg[0] for _ in range(int(w))]\
6161
+list(avg)\
6262
+[avg[-1] for _ in range(len(avg)+w,len(x))]

funscript_editor/algorithms/videotracker.py

Lines changed: 3 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -18,14 +18,14 @@ class StaticVideoTracker:
1818
Args:
1919
first_frame (np.ndarray): open cv image representing the start frame
2020
tracking_bbox (tuple): tuple with (x,y,w,h) of the init tracking box
21-
limit_searchspace (bool) : only insert the region of init tracking box into the tracker
21+
limit_searchspace (dict) : only insert the specified region around the init box
2222
queue_size (int): in (work) and out (result) queue size
2323
"""
2424

2525
def __init__(self,
2626
first_frame: np.ndarray,
2727
tracking_bbox: tuple,
28-
limit_searchspace : bool = True,
28+
limit_searchspace : dict = {'h': 0.45, 'w':0.2},
2929
queue_size : int = 2):
3030
self.first_frame = first_frame
3131
self.limit_searchspace = limit_searchspace
@@ -71,12 +71,7 @@ def run(self) -> None:
7171
self.tracker = cv2.TrackerCSRT_create() # NOTE: you can change this to your favorite tracker
7272
frame_heigt, frame_width = self.first_frame.shape[:2]
7373

74-
if self.limit_searchspace:
75-
# TODO Determine the tracking ROI, assume we have 3d Side by Side static VR video
76-
# The movement is mostly up-down, so we can restrict left and right more than up and down
77-
dh, dw = int(frame_heigt/12), int(frame_width/38)
78-
else:
79-
dh, dw = int(frame_heigt/2), int(frame_width/4)
74+
dh, dw = int(frame_heigt*self.limit_searchspace['h']), int(frame_width*self.limit_searchspace['w'])
8075
x0, y0 = max([0, self.first_tracking_bbox[0] - dw]), max([0, self.first_tracking_bbox[1] - dh])
8176
y1 = min([frame_heigt, self.first_tracking_bbox[1] + self.first_tracking_bbox[3] + dh])
8277
x1 = min([frame_width, self.first_tracking_bbox[0] + self.first_tracking_bbox[2] + dw])
@@ -99,7 +94,6 @@ def run(self) -> None:
9994
else:
10095
frame = self.queue_in.get()
10196
frame_roi = frame[y0:y1, x0:x1]
102-
# cv2.imwrite('debug.png', frame_roi)
10397
success, bbox = self.tracker.update(frame_roi)
10498
if success: bbox = (int(bbox[0] + x0), int(bbox[1] + y0), int(bbox[2]), int(bbox[3]))
10599
self.queue_out.put((success, bbox))

funscript_editor/config/settings.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ tracking_direction: 'y'
1313
# Limit the max player speed in the tracking preview window (0 = disable limit)
1414
max_playback_fps: 0
1515

16-
# Set the preview image scaling factor 1.0 should match your monitor size
16+
# Set the preview image scaling factor. 1.0 should match your monitor size
1717
preview_scaling: 0.66
1818

1919
# Set the video type

funscript_editor/data/ffmpegstream.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -210,7 +210,7 @@ def read(self) -> np.ndarray:
210210
"""
211211
while self.frame_buffer.qsize() == 0 and not self.stopped:
212212
time.sleep(self.sleep_time)
213-
return self.frame_buffer.get() if self.more() else None
213+
return self.frame_buffer.get() if self.frame_buffer.qsize() > 0 else None
214214

215215

216216
def isOpen(self) -> bool:

0 commit comments

Comments
 (0)