simba-uw-tf-dev 4.7.3__py3-none-any.whl → 4.7.4__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- simba/assets/icons/left_arrow_green.png +0 -0
- simba/assets/icons/left_arrow_red.png +0 -0
- simba/assets/icons/right_arrow_green.png +0 -0
- simba/assets/icons/right_arrow_red.png +0 -0
- simba/mixins/image_mixin.py +129 -4
- simba/roi_tools/roi_utils.py +2 -2
- simba/sandbox/remove_end_of_video.py +80 -0
- simba/sandbox/video_timelaps.py +291 -0
- simba/ui/pop_ups/video_processing_pop_up.py +3637 -3469
- simba/ui/tkinter_functions.py +3 -1
- simba/ui/video_timelaps.py +332 -0
- simba/utils/read_write.py +10 -3
- simba/video_processors/batch_process_create_ffmpeg_commands.py +0 -1
- simba/video_processors/video_processing.py +66 -3
- {simba_uw_tf_dev-4.7.3.dist-info → simba_uw_tf_dev-4.7.4.dist-info}/METADATA +1 -1
- {simba_uw_tf_dev-4.7.3.dist-info → simba_uw_tf_dev-4.7.4.dist-info}/RECORD +20 -13
- {simba_uw_tf_dev-4.7.3.dist-info → simba_uw_tf_dev-4.7.4.dist-info}/LICENSE +0 -0
- {simba_uw_tf_dev-4.7.3.dist-info → simba_uw_tf_dev-4.7.4.dist-info}/WHEEL +0 -0
- {simba_uw_tf_dev-4.7.3.dist-info → simba_uw_tf_dev-4.7.4.dist-info}/entry_points.txt +0 -0
- {simba_uw_tf_dev-4.7.3.dist-info → simba_uw_tf_dev-4.7.4.dist-info}/top_level.txt +0 -0
simba/ui/tkinter_functions.py
CHANGED
|
@@ -170,6 +170,7 @@ class SimBAScaleBar(Frame):
|
|
|
170
170
|
to: int = 100,
|
|
171
171
|
tickinterval: Optional[int] = None,
|
|
172
172
|
troughcolor: Optional[str] = None,
|
|
173
|
+
activebackground: Optional[str] = None,
|
|
173
174
|
sliderrelief: Literal["raised", "sunken", "flat", "ridge", "solid", "groove"] = 'flat'):
|
|
174
175
|
|
|
175
176
|
super().__init__(master=parent)
|
|
@@ -192,7 +193,8 @@ class SimBAScaleBar(Frame):
|
|
|
192
193
|
troughcolor=troughcolor,
|
|
193
194
|
tickinterval=tickinterval,
|
|
194
195
|
resolution=resolution,
|
|
195
|
-
showvalue=showvalue
|
|
196
|
+
showvalue=showvalue,
|
|
197
|
+
activebackground=activebackground)
|
|
196
198
|
|
|
197
199
|
if label is not None:
|
|
198
200
|
self.lbl = SimBALabel(parent=self, txt=label, font=lbl_font, txt_clr=label_clr, width=label_width)
|
|
@@ -0,0 +1,332 @@
|
|
|
1
|
+
import os
|
|
2
|
+
from tkinter import *
|
|
3
|
+
from typing import Optional, Union
|
|
4
|
+
|
|
5
|
+
import cv2
|
|
6
|
+
import numpy as np
|
|
7
|
+
from PIL import Image, ImageTk
|
|
8
|
+
|
|
9
|
+
from simba.mixins.image_mixin import ImageMixin
|
|
10
|
+
from simba.ui.tkinter_functions import SimbaButton, SimBALabel, SimBAScaleBar
|
|
11
|
+
from simba.utils.checks import (check_file_exist_and_readable, check_int,
|
|
12
|
+
check_valid_boolean)
|
|
13
|
+
from simba.utils.enums import Formats, TkBinds
|
|
14
|
+
from simba.utils.lookups import get_icons_paths, get_monitor_info
|
|
15
|
+
from simba.utils.read_write import (get_video_meta_data, read_frm_of_video,
|
|
16
|
+
seconds_to_timestamp)
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class TimelapseSlider():
|
|
20
|
+
|
|
21
|
+
"""
|
|
22
|
+
Interactive timelapse viewer with segment selection sliders.
|
|
23
|
+
|
|
24
|
+
Creates a Tkinter GUI window displaying a timelapse composite image generated from evenly-spaced frames
|
|
25
|
+
across a video. Includes interactive sliders to select start and end times for video segments, with
|
|
26
|
+
visual highlighting of the selected segment and frame previews.
|
|
27
|
+
|
|
28
|
+
.. image:: _static/img/TimelapseSlider.png
|
|
29
|
+
:width: 600
|
|
30
|
+
:align: center
|
|
31
|
+
|
|
32
|
+
:param Union[str, os.PathLike] video_path: Path to video file to create timelapse from.
|
|
33
|
+
:param int frame_cnt: Number of frames to include in timelapse composite. Default 25.
|
|
34
|
+
:param Optional[int] ruler_width: Width per frame in pixels. If None, calculated to match video width. Default None.
|
|
35
|
+
:param Optional[int] crop_ratio: Percentage of frame width to keep (0-100). Default 50.
|
|
36
|
+
:param int padding: Padding in pixels added to timelapse when ruler is shown. Default 60.
|
|
37
|
+
:param int ruler_divisions: Number of major divisions on time ruler. Default 6.
|
|
38
|
+
:param bool show_ruler: If True, display time ruler below timelapse. Default True.
|
|
39
|
+
:param int ruler_height: Height of ruler in pixels. Default 60.
|
|
40
|
+
|
|
41
|
+
:example:
|
|
42
|
+
>>> slider = TimelapseSlider(video_path='path/to/video.mp4', frame_cnt=25, crop_ratio=75)
|
|
43
|
+
>>> slider.run()
|
|
44
|
+
>>> # Use sliders to select segment, then access selected times and frames:
|
|
45
|
+
>>> start_time = slider.get_start_time() # seconds (float)
|
|
46
|
+
>>> end_time = slider.get_end_time() # seconds (float)
|
|
47
|
+
>>> start_time_str = slider.get_start_time_str() # "HH:MM:SS" string
|
|
48
|
+
>>> end_time_str = slider.get_end_time_str() # "HH:MM:SS" string
|
|
49
|
+
>>> start_frame = slider.get_start_frame() # frame number (int)
|
|
50
|
+
>>> end_frame = slider.get_end_frame() # frame number (int)
|
|
51
|
+
>>> slider.close()
|
|
52
|
+
"""
|
|
53
|
+
|
|
54
|
+
def __init__(self,
|
|
55
|
+
video_path: Union[str, os.PathLike],
|
|
56
|
+
frame_cnt: int = 25,
|
|
57
|
+
crop_ratio: Optional[int] = 50,
|
|
58
|
+
padding: int = 60,
|
|
59
|
+
ruler_divisions: int = 6,
|
|
60
|
+
show_ruler: bool = True,
|
|
61
|
+
ruler_height: Optional[int] = None,
|
|
62
|
+
ruler_width: Optional[int] = None,
|
|
63
|
+
img_width: Optional[int] = None,
|
|
64
|
+
img_height: Optional[int] = None):
|
|
65
|
+
|
|
66
|
+
check_file_exist_and_readable(file_path=video_path)
|
|
67
|
+
check_int(name='frame_cnt', value=frame_cnt, min_value=1, raise_error=True)
|
|
68
|
+
_, (self.monitor_width, self.monitor_height) = get_monitor_info()
|
|
69
|
+
if ruler_width is not None: check_int(name='size', value=ruler_width, min_value=1, raise_error=True)
|
|
70
|
+
else: ruler_width = int(self.monitor_width * 0.5)
|
|
71
|
+
if ruler_height is not None: check_int(name='ruler_height', value=ruler_height, min_value=1, raise_error=True)
|
|
72
|
+
else: ruler_height = int(self.monitor_height * 0.05)
|
|
73
|
+
if img_width is not None: check_int(name='img_width', value=img_width, min_value=1, raise_error=True)
|
|
74
|
+
else: img_width = int(self.monitor_width * 0.5)
|
|
75
|
+
if img_height is not None: check_int(name='img_height', value=img_height, min_value=1, raise_error=True)
|
|
76
|
+
else: img_height = int(self.monitor_height * 0.5)
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
check_int(name='padding', value=padding, min_value=1, raise_error=True)
|
|
80
|
+
check_valid_boolean(value=show_ruler, source=f'{self.__class__.__name__} show_ruler', raise_error=True)
|
|
81
|
+
self.video_meta = get_video_meta_data(video_path=video_path, raise_error=True)
|
|
82
|
+
if show_ruler: check_int(name='ruler_divisions', value=ruler_divisions, min_value=1, raise_error=True)
|
|
83
|
+
self.size, self.padding, self.crop_ratio, self.frame_cnt = ruler_width, padding, crop_ratio, frame_cnt
|
|
84
|
+
self.ruler_height, self.video_path, self.show_ruler, self.ruler_divisions = ruler_height, video_path, show_ruler, ruler_divisions
|
|
85
|
+
self.img_width, self.img_height = img_width, img_height
|
|
86
|
+
self.frm_name = f'{self.video_meta["video_name"]} - TIMELAPSE VIEWER - hit "X" or ESC to close'
|
|
87
|
+
self.video_capture, self._pending_frame_update, self._frame_debounce_ms = None, None, 50
|
|
88
|
+
|
|
89
|
+
def _draw_img(self, img: np.ndarray, lbl: SimBALabel):
|
|
90
|
+
img_rgb = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
|
|
91
|
+
self.pil_image = Image.fromarray(img_rgb)
|
|
92
|
+
self.tk_image = ImageTk.PhotoImage(self.pil_image)
|
|
93
|
+
lbl.configure(image=self.tk_image)
|
|
94
|
+
lbl.image = self.tk_image
|
|
95
|
+
|
|
96
|
+
def _update_selection(self, slider_type: str):
|
|
97
|
+
start_sec = int(self.start_scale.get_value())
|
|
98
|
+
end_sec = int(self.end_scale.get_value())
|
|
99
|
+
max_sec = int(self.video_meta['video_length_s'])
|
|
100
|
+
if slider_type == 'start':
|
|
101
|
+
if start_sec >= end_sec:
|
|
102
|
+
end_sec = min(start_sec + 1, max_sec)
|
|
103
|
+
self.end_scale.set_value(end_sec)
|
|
104
|
+
else:
|
|
105
|
+
if end_sec <= start_sec:
|
|
106
|
+
start_sec = max(end_sec - 1, 0)
|
|
107
|
+
self.start_scale.set_value(start_sec)
|
|
108
|
+
|
|
109
|
+
self.selected_start[0] = start_sec
|
|
110
|
+
self.selected_end[0] = end_sec
|
|
111
|
+
|
|
112
|
+
start_frame = int(start_sec * self.video_meta['fps'])
|
|
113
|
+
end_frame = int(end_sec * self.video_meta['fps'])
|
|
114
|
+
if start_frame >= self.video_meta['frame_count']: start_frame = self.video_meta['frame_count'] - 1
|
|
115
|
+
if end_frame >= self.video_meta['frame_count']: end_frame = self.video_meta['frame_count'] - 1
|
|
116
|
+
if start_frame < 0: start_frame = 0
|
|
117
|
+
if end_frame < 0: end_frame = 0
|
|
118
|
+
self.selected_start_frame[0] = start_frame
|
|
119
|
+
self.selected_end_frame[0] = end_frame
|
|
120
|
+
|
|
121
|
+
self.start_time_label.config(text=seconds_to_timestamp(start_sec), fg='green')
|
|
122
|
+
self.end_time_label.config(text=seconds_to_timestamp(end_sec), fg='red')
|
|
123
|
+
|
|
124
|
+
if self.video_meta['video_length_s'] > 0:
|
|
125
|
+
self._highlight_segment(start_sec, end_sec)
|
|
126
|
+
self._schedule_frame_update(slider_type=slider_type)
|
|
127
|
+
|
|
128
|
+
def _move_start_frame(self, direction: int):
|
|
129
|
+
current_seconds = self.selected_start[0]
|
|
130
|
+
new_seconds = current_seconds + direction
|
|
131
|
+
new_seconds = max(0, min(new_seconds, int(self.video_meta['video_length_s'])))
|
|
132
|
+
self.start_scale.set_value(int(new_seconds))
|
|
133
|
+
self._update_selection(slider_type='start')
|
|
134
|
+
if self._pending_frame_update is not None:
|
|
135
|
+
if hasattr(self, 'img_window') and self.img_window.winfo_exists():
|
|
136
|
+
self.img_window.after_cancel(self._pending_frame_update)
|
|
137
|
+
self._update_frame_display(slider_type='start')
|
|
138
|
+
|
|
139
|
+
def _move_end_frame(self, direction: int):
|
|
140
|
+
current_seconds = self.selected_end[0]
|
|
141
|
+
new_seconds = current_seconds + direction
|
|
142
|
+
new_seconds = max(0, min(new_seconds, int(self.video_meta['video_length_s'])))
|
|
143
|
+
self.end_scale.set_value(int(new_seconds))
|
|
144
|
+
self._update_selection(slider_type='end')
|
|
145
|
+
if self._pending_frame_update is not None:
|
|
146
|
+
if hasattr(self, 'img_window') and self.img_window.winfo_exists():
|
|
147
|
+
self.img_window.after_cancel(self._pending_frame_update)
|
|
148
|
+
self._update_frame_display(slider_type='end')
|
|
149
|
+
|
|
150
|
+
def _schedule_frame_update(self, slider_type: str):
|
|
151
|
+
"""Schedule frame preview update with debouncing.
|
|
152
|
+
|
|
153
|
+
Cancels any pending frame update and schedules a new one. If the slider
|
|
154
|
+
moves again before the delay expires, the update is cancelled and rescheduled.
|
|
155
|
+
This prevents expensive frame reads during fast slider dragging.
|
|
156
|
+
"""
|
|
157
|
+
if not hasattr(self, 'img_window') or not self.img_window.winfo_exists():
|
|
158
|
+
return
|
|
159
|
+
|
|
160
|
+
if self._pending_frame_update is not None: self.img_window.after_cancel(self._pending_frame_update)
|
|
161
|
+
|
|
162
|
+
self._pending_frame_update = self.img_window.after(self._frame_debounce_ms, lambda: self._update_frame_display(slider_type=slider_type))
|
|
163
|
+
|
|
164
|
+
def _update_frame_display(self, slider_type: str):
|
|
165
|
+
if slider_type == 'start':
|
|
166
|
+
seconds = self.selected_start[0]
|
|
167
|
+
self.frame_label.config(text=f"Start Frame Preview ({seconds_to_timestamp(seconds)})", font=Formats.FONT_LARGE_BOLD.value, fg='green')
|
|
168
|
+
else:
|
|
169
|
+
seconds = self.selected_end[0]
|
|
170
|
+
self.frame_label.config(text=f"End Frame Preview ({seconds_to_timestamp(seconds)})", font=Formats.FONT_LARGE_BOLD.value, fg='red')
|
|
171
|
+
|
|
172
|
+
frame_index = int(seconds * self.video_meta['fps'])
|
|
173
|
+
if frame_index >= self.video_meta['frame_count']: frame_index = self.video_meta['frame_count'] - 1
|
|
174
|
+
if frame_index < 0: frame_index = 0
|
|
175
|
+
|
|
176
|
+
if self.video_capture is not None and self.video_capture.isOpened():
|
|
177
|
+
self.video_capture.set(cv2.CAP_PROP_POS_FRAMES, frame_index)
|
|
178
|
+
ret, frame = self.video_capture.read()
|
|
179
|
+
if ret and frame is not None:
|
|
180
|
+
h, w = frame.shape[:2]
|
|
181
|
+
target_w, target_h = self.img_width, self.img_height
|
|
182
|
+
scale = min(target_w / w, target_h / h)
|
|
183
|
+
new_w, new_h = int(w * scale), int(h * scale)
|
|
184
|
+
frame = cv2.resize(frame, (new_w, new_h), interpolation=cv2.INTER_LINEAR)
|
|
185
|
+
self._draw_img(img=frame, lbl=self.frame_display_lbl)
|
|
186
|
+
|
|
187
|
+
def _highlight_segment(self, start_sec: int, end_sec: int):
|
|
188
|
+
timelapse_width = self.original_timelapse.shape[1]
|
|
189
|
+
start_x = int((start_sec / self.video_meta['video_length_s']) * timelapse_width)
|
|
190
|
+
end_x = int((end_sec / self.video_meta['video_length_s']) * timelapse_width)
|
|
191
|
+
highlighted = self.original_timelapse.copy()
|
|
192
|
+
mask = np.ones(highlighted.shape[:2], dtype=np.uint8) * 128
|
|
193
|
+
mask[:, start_x:end_x] = 255
|
|
194
|
+
mask = cv2.merge([mask, mask, mask])
|
|
195
|
+
highlighted = cv2.multiply(highlighted, mask.astype(np.uint8), scale=1/255.0)
|
|
196
|
+
cv2.line(highlighted, (start_x, 0), (start_x, highlighted.shape[0]), (0, 255, 0), 2)
|
|
197
|
+
cv2.line(highlighted, (end_x, 0), (end_x, highlighted.shape[0]), (0, 255, 0), 2)
|
|
198
|
+
self._draw_img(img=highlighted, lbl=self.img_lbl)
|
|
199
|
+
|
|
200
|
+
def run(self):
|
|
201
|
+
self.video_capture = cv2.VideoCapture(self.video_path)
|
|
202
|
+
if not self.video_capture.isOpened():
|
|
203
|
+
raise ValueError(f"Failed to open video file: {self.video_path}")
|
|
204
|
+
|
|
205
|
+
self.timelapse_img = ImageMixin.get_timelapse_img(video_path=self.video_path, frame_cnt=self.frame_cnt, size=self.size, crop_ratio=self.crop_ratio)
|
|
206
|
+
if self.show_ruler:
|
|
207
|
+
timelapse_height, timelapse_width = self.timelapse_img.shape[0], self.timelapse_img.shape[1]
|
|
208
|
+
padded_timelapse = np.zeros((timelapse_height, timelapse_width + (2 * self.padding), 3), dtype=np.uint8)
|
|
209
|
+
padded_timelapse[:, self.padding:self.padding + timelapse_width] = self.timelapse_img
|
|
210
|
+
ruler = ImageMixin.create_time_ruler(video_path=self.video_path, width=timelapse_width, height=self.ruler_height, num_divisions=self.ruler_divisions)
|
|
211
|
+
self.timelapse_img = cv2.vconcat([padded_timelapse, ruler])
|
|
212
|
+
|
|
213
|
+
self.original_timelapse = self.timelapse_img.copy()
|
|
214
|
+
self.img_window = Toplevel()
|
|
215
|
+
self.img_window.resizable(True, True)
|
|
216
|
+
self.img_window.title(self.frm_name)
|
|
217
|
+
self.img_window.protocol("WM_DELETE_WINDOW", self.close)
|
|
218
|
+
# Bind Escape key to close window
|
|
219
|
+
self.img_window.bind(TkBinds.ESCAPE.value, lambda event: self.close())
|
|
220
|
+
|
|
221
|
+
|
|
222
|
+
self.img_lbl = SimBALabel(parent=self.img_window, txt='')
|
|
223
|
+
self.img_lbl.pack()
|
|
224
|
+
self._draw_img(img=self.timelapse_img, lbl=self.img_lbl)
|
|
225
|
+
self.frame_display_frame = Frame(self.img_window)
|
|
226
|
+
self.frame_display_frame.pack(pady=10, padx=10, fill=BOTH, expand=True)
|
|
227
|
+
self.frame_label = SimBALabel(parent=self.frame_display_frame, txt="Frame Preview", font=Formats.FONT_REGULAR_BOLD.value)
|
|
228
|
+
self.frame_label.pack()
|
|
229
|
+
self.frame_display_lbl = SimBALabel(parent=self.frame_display_frame, txt='', bg_clr='black')
|
|
230
|
+
self.frame_display_lbl.pack(pady=5)
|
|
231
|
+
self.slider_frame = Frame(self.img_window)
|
|
232
|
+
self.slider_frame.pack(pady=10, padx=10, fill=X)
|
|
233
|
+
self.slider_frame.columnconfigure(index=0, weight=1)
|
|
234
|
+
self.slider_frame.columnconfigure(index=1, weight=0)
|
|
235
|
+
self.slider_frame.columnconfigure(index=2, weight=0)
|
|
236
|
+
self.slider_frame.columnconfigure(index=3, weight=0)
|
|
237
|
+
self.slider_frame.columnconfigure(index=4, weight=0)
|
|
238
|
+
self.slider_frame.columnconfigure(index=5, weight=1)
|
|
239
|
+
|
|
240
|
+
self.start_scale = SimBAScaleBar(parent=self.slider_frame, label="START TIME:", from_=0, to=self.video_meta['video_length_s'], orient=HORIZONTAL, length=400, resolution=1, value=0, showvalue=False, label_width=15, sliderrelief='raised', troughcolor='white', activebackground='green', lbl_font=Formats.FONT_LARGE_BOLD.value)
|
|
241
|
+
self.start_scale.grid(row=0, column=1, padx=5)
|
|
242
|
+
self.start_scale.scale.config(command=lambda x: self._update_selection(slider_type='start'))
|
|
243
|
+
|
|
244
|
+
self.start_time_label = SimBALabel(parent=self.slider_frame, txt="00:00:00", font=Formats.FONT_LARGE_BOLD.value, width=10, txt_clr='green')
|
|
245
|
+
self.start_time_label.grid(row=0, column=2, padx=5)
|
|
246
|
+
|
|
247
|
+
self.start_frame_left_btn = SimbaButton(parent=self.slider_frame, txt="-1s", tooltip_txt="Previous second", cmd=self._move_start_frame, cmd_kwargs={'direction': -1}, font=Formats.FONT_REGULAR_BOLD.value, img='left_arrow_green')
|
|
248
|
+
self.start_frame_left_btn.grid(row=0, column=3, padx=2)
|
|
249
|
+
self.start_frame_right_btn = SimbaButton(parent=self.slider_frame, txt="+1s", tooltip_txt="Next second", cmd=self._move_start_frame, cmd_kwargs={'direction': 1}, font=Formats.FONT_REGULAR_BOLD.value, img='right_arrow_green')
|
|
250
|
+
self.start_frame_right_btn.grid(row=0, column=4, padx=2)
|
|
251
|
+
|
|
252
|
+
self.end_scale = SimBAScaleBar(parent=self.slider_frame, label="END TIME:", from_=0, to=int(self.video_meta['video_length_s']), orient=HORIZONTAL, length=400, resolution=1, value=int(self.video_meta['video_length_s']), showvalue=False, label_width=15, sliderrelief='raised', troughcolor='white', activebackground='red', lbl_font=Formats.FONT_LARGE_BOLD.value)
|
|
253
|
+
self.end_scale.grid(row=1, column=1, padx=5)
|
|
254
|
+
self.end_scale.scale.config(command=lambda x: self._update_selection(slider_type='end'))
|
|
255
|
+
|
|
256
|
+
self.end_time_label = SimBALabel(parent=self.slider_frame, txt=seconds_to_timestamp(int(self.video_meta['video_length_s'])), font=Formats.FONT_LARGE_BOLD.value, width=10, txt_clr='red')
|
|
257
|
+
self.end_time_label.grid(row=1, column=2, padx=5)
|
|
258
|
+
|
|
259
|
+
self.end_frame_left_btn = SimbaButton(parent=self.slider_frame, txt="-1s", tooltip_txt="Previous second", cmd=self._move_end_frame, cmd_kwargs={'direction': -1}, font=Formats.FONT_REGULAR_BOLD.value, img='left_arrow_red')
|
|
260
|
+
self.end_frame_left_btn.grid(row=1, column=3, padx=2)
|
|
261
|
+
self.end_frame_right_btn = SimbaButton(parent=self.slider_frame, txt="+1s", tooltip_txt="Next second", cmd=self._move_end_frame, cmd_kwargs={'direction': 1}, font=Formats.FONT_REGULAR_BOLD.value, img='right_arrow_red')
|
|
262
|
+
self.end_frame_right_btn.grid(row=1, column=4, padx=2)
|
|
263
|
+
|
|
264
|
+
self.selected_start = [0]
|
|
265
|
+
self.selected_end = [int(self.video_meta['video_length_s'])]
|
|
266
|
+
self.selected_start_frame = [0]
|
|
267
|
+
end_frame = int(self.video_meta['frame_count']) - 1
|
|
268
|
+
if end_frame < 0: end_frame = 0
|
|
269
|
+
self.selected_end_frame = [end_frame]
|
|
270
|
+
|
|
271
|
+
self.img_window.update_idletasks()
|
|
272
|
+
self.img_window.update()
|
|
273
|
+
|
|
274
|
+
req_width, req_height = self.img_window.winfo_reqwidth(), self.img_window.winfo_reqheight()
|
|
275
|
+
min_width = max(self.timelapse_img.shape[1] + 60, req_width + 20)
|
|
276
|
+
timelapse_height = self.timelapse_img.shape[0]
|
|
277
|
+
frame_preview_height = self.img_height
|
|
278
|
+
slider_height, padding_total = 150, 50
|
|
279
|
+
calculated_min_height = timelapse_height + frame_preview_height + slider_height + padding_total
|
|
280
|
+
min_height = max(calculated_min_height, req_height + 50, timelapse_height + 400)
|
|
281
|
+
max_height = int(self.monitor_height * 0.95)
|
|
282
|
+
if min_height > max_height: min_height = max_height
|
|
283
|
+
|
|
284
|
+
self.img_window.minsize(min_width, min_height)
|
|
285
|
+
self.img_window.geometry(f"{min_width}x{min_height}")
|
|
286
|
+
self._update_frame_display(slider_type='start')
|
|
287
|
+
|
|
288
|
+
def get_start_time(self) -> float:
|
|
289
|
+
return self.selected_start[0]
|
|
290
|
+
|
|
291
|
+
def get_end_time(self) -> float:
|
|
292
|
+
return self.selected_end[0]
|
|
293
|
+
|
|
294
|
+
def get_start_time_str(self) -> str:
|
|
295
|
+
return seconds_to_timestamp(self.selected_start[0])
|
|
296
|
+
|
|
297
|
+
def get_end_time_str(self) -> str:
|
|
298
|
+
return seconds_to_timestamp(self.selected_end[0])
|
|
299
|
+
|
|
300
|
+
def get_start_frame(self) -> int:
|
|
301
|
+
return self.selected_start_frame[0]
|
|
302
|
+
|
|
303
|
+
def get_end_frame(self) -> int:
|
|
304
|
+
return self.selected_end_frame[0]
|
|
305
|
+
|
|
306
|
+
def close(self):
|
|
307
|
+
if self._pending_frame_update is not None:
|
|
308
|
+
if hasattr(self, 'img_window') and self.img_window.winfo_exists():
|
|
309
|
+
self.img_window.after_cancel(self._pending_frame_update)
|
|
310
|
+
self._pending_frame_update = None
|
|
311
|
+
|
|
312
|
+
# Unbind Escape key if window still exists
|
|
313
|
+
if hasattr(self, 'img_window') and self.img_window.winfo_exists():
|
|
314
|
+
try:
|
|
315
|
+
self.img_window.unbind(TkBinds.ESCAPE.value)
|
|
316
|
+
except:
|
|
317
|
+
pass
|
|
318
|
+
|
|
319
|
+
if self.video_capture is not None:
|
|
320
|
+
self.video_capture.release()
|
|
321
|
+
self.video_capture = None
|
|
322
|
+
|
|
323
|
+
if hasattr(self, 'img_window') and self.img_window.winfo_exists():
|
|
324
|
+
self.img_window.destroy()
|
|
325
|
+
|
|
326
|
+
|
|
327
|
+
|
|
328
|
+
#
|
|
329
|
+
# x = TimelapseSlider(video_path=r"E:\troubleshooting\mitra_emergence\project_folder\clip_test\Box1_180mISOcontrol_Females_clipped_progress_bar.mp4",
|
|
330
|
+
# frame_cnt=25,
|
|
331
|
+
# crop_ratio=75)
|
|
332
|
+
# x.run()
|
simba/utils/read_write.py
CHANGED
|
@@ -814,6 +814,7 @@ def read_frm_of_video(video_path: Union[str, os.PathLike, cv2.VideoCapture],
|
|
|
814
814
|
frame_index: Optional[int] = 0,
|
|
815
815
|
opacity: Optional[float] = None,
|
|
816
816
|
size: Optional[Tuple[int, int]] = None,
|
|
817
|
+
keep_aspect_ratio: bool = False,
|
|
817
818
|
greyscale: Optional[bool] = False,
|
|
818
819
|
black_and_white: Optional[bool] = False,
|
|
819
820
|
clahe: Optional[bool] = False,
|
|
@@ -831,7 +832,8 @@ def read_frm_of_video(video_path: Union[str, os.PathLike, cv2.VideoCapture],
|
|
|
831
832
|
:param Union[str, os.PathLike, cv2.VideoCapture] video_path: Path to video file, or cv2.VideoCapture object.
|
|
832
833
|
:param Optional[int] frame_index: The frame index to return (0-based). Default: 0. If -1 is passed, the last frame of the video is read.
|
|
833
834
|
:param Optional[float] opacity: Value between 0 and 100 or None. If float value, returns image with opacity. 100 fully opaque. 0.0 fully transparent.
|
|
834
|
-
:param Optional[Tuple[int, int]] size: If tuple, resizes the image
|
|
835
|
+
:param Optional[Tuple[int, int]] size: If tuple (width, height), resizes the image. If None, returns original image size. When used with keep_aspect_ratio=True, the image is resized to fit within the target size while maintaining aspect ratio.
|
|
836
|
+
:param bool keep_aspect_ratio: If True and size is provided, resizes the image to fit within the target size while maintaining aspect ratio. If False, resizes to exact size (may distort aspect ratio). Default False.
|
|
835
837
|
:param Optional[bool] greyscale: If True, returns the greyscale image. Default False.
|
|
836
838
|
:param Optional[bool] black_and_white: If True, returns black and white image at threshold 127. Default False.
|
|
837
839
|
:param Optional[bool] clahe: If True, returns CLAHE enhanced image. Default False.
|
|
@@ -906,8 +908,14 @@ def read_frm_of_video(video_path: Union[str, os.PathLike, cv2.VideoCapture],
|
|
|
906
908
|
h, w, clr = img.shape[:3]
|
|
907
909
|
opacity_image = np.ones((h, w, clr), dtype=np.uint8) * int(255 * opacity)
|
|
908
910
|
img = cv2.addWeighted( img.astype(np.uint8), 1 - opacity, opacity_image.astype(np.uint8), opacity, 0)
|
|
909
|
-
if size:
|
|
911
|
+
if size is not None and not keep_aspect_ratio:
|
|
910
912
|
img = cv2.resize(img, size, interpolation=cv2.INTER_LINEAR)
|
|
913
|
+
elif size is not None and keep_aspect_ratio:
|
|
914
|
+
target_w, target_h = size
|
|
915
|
+
h, w = img.shape[:2]
|
|
916
|
+
scale = min(target_w / w, target_h / h)
|
|
917
|
+
new_w, new_h = int(w * scale), int(h * scale)
|
|
918
|
+
img = cv2.resize(img, (new_w, new_h), interpolation=cv2.INTER_LINEAR)
|
|
911
919
|
if greyscale or black_and_white:
|
|
912
920
|
if len(img.shape) > 2:
|
|
913
921
|
img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
|
|
@@ -3761,7 +3769,6 @@ def find_closest_readable_frame(video_path: Union[str, os.PathLike],
|
|
|
3761
3769
|
return None, None
|
|
3762
3770
|
|
|
3763
3771
|
|
|
3764
|
-
|
|
3765
3772
|
#copy_multiple_videos_to_project(config_path=r"C:\troubleshooting\multi_animal_dlc_two_c57\project_folder\project_config.ini", source=r'E:\maplight_videos\video_test', file_type='mp4', recursive_search=False)
|
|
3766
3773
|
|
|
3767
3774
|
|
|
@@ -12,7 +12,6 @@ import shutil
|
|
|
12
12
|
|
|
13
13
|
from simba.utils.checks import (check_ffmpeg_available,
|
|
14
14
|
check_file_exist_and_readable, check_str)
|
|
15
|
-
from simba.utils.enums import Formats
|
|
16
15
|
from simba.utils.errors import CropError, FFMPEGNotFoundError, PermissionError
|
|
17
16
|
from simba.utils.lookups import (get_current_time, get_ffmpeg_encoders,
|
|
18
17
|
gpu_quality_to_cpu_quality_lk)
|
|
@@ -52,12 +52,12 @@ from simba.utils.errors import (CountError, DirectoryExistError,
|
|
|
52
52
|
NoDataError, NoFilesFoundError,
|
|
53
53
|
NotDirectoryError, ResolutionError,
|
|
54
54
|
SimBAGPUError)
|
|
55
|
-
from simba.utils.lookups import (get_current_time,
|
|
55
|
+
from simba.utils.lookups import (get_current_time, get_ffmpeg_codec,
|
|
56
56
|
get_ffmpeg_crossfade_methods, get_fonts,
|
|
57
57
|
get_named_colors, percent_to_crf_lookup,
|
|
58
58
|
percent_to_qv_lk, quality_pct_to_crf,
|
|
59
|
-
video_quality_to_preset_lookup
|
|
60
|
-
from simba.utils.printing import SimbaTimer,
|
|
59
|
+
video_quality_to_preset_lookup)
|
|
60
|
+
from simba.utils.printing import SimbaTimer, stdout_information, stdout_success
|
|
61
61
|
from simba.utils.read_write import (
|
|
62
62
|
check_if_hhmmss_timestamp_is_valid_part_of_video,
|
|
63
63
|
concatenate_videos_in_folder, create_directory,
|
|
@@ -1101,6 +1101,9 @@ def remove_beginning_of_video(file_path: Union[str, os.PathLike],
|
|
|
1101
1101
|
"""
|
|
1102
1102
|
Remove N seconds from the beginning of a video file.
|
|
1103
1103
|
|
|
1104
|
+
.. seealso::
|
|
1105
|
+
To remove N seconds from the end of the video, see :func:`simba.video_processors.video_processing.remove_end_of_video`.
|
|
1106
|
+
|
|
1104
1107
|
:param Union[str, os.PathLike] file_path: Path to video file
|
|
1105
1108
|
:param int time: Number of seconds to remove from the beginning of the video.
|
|
1106
1109
|
:param int quality: Video quality percentage (1-100). Higher values = higher quality. Default 60.
|
|
@@ -1144,6 +1147,66 @@ def remove_beginning_of_video(file_path: Union[str, os.PathLike],
|
|
|
1144
1147
|
stdout_success(msg=f"SIMBA COMPLETE: Video converted! {save_name} generated!", elapsed_time=timer.elapsed_time_str, source=remove_beginning_of_video.__name__)
|
|
1145
1148
|
|
|
1146
1149
|
|
|
1150
|
+
def remove_end_of_video(file_path: Union[str, os.PathLike],
|
|
1151
|
+
time: int,
|
|
1152
|
+
quality: int = 60,
|
|
1153
|
+
save_path: Optional[Union[str, os.PathLike]] = None,
|
|
1154
|
+
gpu: Optional[bool] = False) -> None:
|
|
1155
|
+
"""
|
|
1156
|
+
Remove N seconds from the end of a video file.
|
|
1157
|
+
|
|
1158
|
+
.. seealso::
|
|
1159
|
+
To remove N seconds from the beginning of the video, see :func:`simba.video_processors.video_processing.remove_beginning_of_video`
|
|
1160
|
+
|
|
1161
|
+
:param Union[str, os.PathLike] file_path: Path to video file
|
|
1162
|
+
:param int time: Number of seconds to remove from the end of the video.
|
|
1163
|
+
:param int quality: Video quality percentage (1-100). Higher values = higher quality. Default 60.
|
|
1164
|
+
:param Optional[Union[str, os.PathLike]] save_path: Optional save location for the shortened video. If None, then the new video is saved in the same directory as the input video with the ``_shortened`` suffix.
|
|
1165
|
+
:param Optional[bool] gpu: If True, use NVIDEA GPU codecs. Default False.
|
|
1166
|
+
:returns: None. If save_path is not passed, the result is stored in the same directory as the input file with the ``_shorten.mp4`` suffix.
|
|
1167
|
+
|
|
1168
|
+
.. note::
|
|
1169
|
+
Codec is automatically selected: libx264 for CPU encoding (ignored if gpu=True).
|
|
1170
|
+
|
|
1171
|
+
:example:
|
|
1172
|
+
>>> _ = remove_end_of_video(file_path='project_folder/videos/Video_1.avi', time=10)
|
|
1173
|
+
>>> remove_end_of_video(file_path=f'/Users/simon/Desktop/imgs_4/test/blahhhh.mp4', save_path='/Users/simon/Desktop/imgs_4/test/CUT.mp4', time=3)
|
|
1174
|
+
"""
|
|
1175
|
+
|
|
1176
|
+
check_ffmpeg_available(raise_error=True)
|
|
1177
|
+
if gpu and not check_nvidea_gpu_available():
|
|
1178
|
+
raise FFMPEGCodecGPUError(msg="No GPU found (as evaluated by nvidea-smi returning None)",
|
|
1179
|
+
source=remove_end_of_video.__name__)
|
|
1180
|
+
timer = SimbaTimer(start=True)
|
|
1181
|
+
check_file_exist_and_readable(file_path=file_path)
|
|
1182
|
+
video_meta_data = get_video_meta_data(video_path=file_path)
|
|
1183
|
+
check_int(name="Cut time", value=time, min_value=1)
|
|
1184
|
+
check_int(name=f'{remove_end_of_video.__name__} quality', value=quality, min_value=1, max_value=100,
|
|
1185
|
+
raise_error=True)
|
|
1186
|
+
quality_crf = quality_pct_to_crf(pct=int(quality))
|
|
1187
|
+
time = int(time)
|
|
1188
|
+
dir, file_name, ext = get_fn_ext(filepath=file_path)
|
|
1189
|
+
if video_meta_data['video_length_s'] <= time:
|
|
1190
|
+
raise InvalidInputError(
|
|
1191
|
+
msg=f"The cut time {time}s is invalid for video {file_name} with length {video_meta_data['video_length_s']}s",
|
|
1192
|
+
source=remove_end_of_video.__name__)
|
|
1193
|
+
if save_path is None:
|
|
1194
|
+
save_name = os.path.join(dir, f"{file_name}_shorten.mp4")
|
|
1195
|
+
else:
|
|
1196
|
+
check_if_dir_exists(in_dir=os.path.dirname(save_path), source=f'{remove_end_of_video.__name__} save_path',
|
|
1197
|
+
create_if_not_exist=True)
|
|
1198
|
+
save_name = save_path
|
|
1199
|
+
duration = video_meta_data['video_length_s'] - time
|
|
1200
|
+
if gpu:
|
|
1201
|
+
cmd = f'ffmpeg -hwaccel auto -c:v h264_cuvid -i "{file_path}" -t {duration} -rc vbr -cq {quality_crf} -c:v h264_nvenc -c:a aac "{save_name}" -loglevel error -stats -hide_banner -y'
|
|
1202
|
+
else:
|
|
1203
|
+
cmd = f'ffmpeg -i "{file_path}" -t {duration} -c:v libx264 -crf {quality_crf} -c:a aac "{save_name}" -loglevel error -stats -hide_banner -y'
|
|
1204
|
+
print(f"Removing final {time}s from {file_name}... ")
|
|
1205
|
+
subprocess.call(cmd, shell=True, stdout=subprocess.PIPE)
|
|
1206
|
+
timer.stop_timer()
|
|
1207
|
+
stdout_success(msg=f"SIMBA COMPLETE: Video converted! {save_name} generated!", elapsed_time=timer.elapsed_time_str, source=remove_end_of_video.__name__)
|
|
1208
|
+
|
|
1209
|
+
|
|
1147
1210
|
def clip_video_in_range(file_path: Union[str, os.PathLike],
|
|
1148
1211
|
start_time: str,
|
|
1149
1212
|
end_time: str,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: simba-uw-tf-dev
|
|
3
|
-
Version: 4.7.
|
|
3
|
+
Version: 4.7.4
|
|
4
4
|
Summary: Toolkit for computer classification and analysis of behaviors in experimental animals
|
|
5
5
|
Home-page: https://github.com/sgoldenlab/simba
|
|
6
6
|
Author: Simon Nilsson, Jia Jie Choong, Sophia Hwang
|
|
@@ -235,6 +235,8 @@ simba/assets/icons/last.png,sha256=FT0YsuoWI3OJviUlXvH_WCA6Xw91_hDsLS9datyMq74,3
|
|
|
235
235
|
simba/assets/icons/last_frame_blue.png,sha256=lZMsTmZAAu6SyQU5L472d3U_WRF1axX34c4hkRPQGmU,511
|
|
236
236
|
simba/assets/icons/last_red.png,sha256=qWwx3xKKPRAOeGUyMIrvv44xpo2vv_MEDsIa4vrG3OE,404
|
|
237
237
|
simba/assets/icons/leaf.png,sha256=coTkYfpw9PKZVyNTSC4wDwjbMncKEKc5k06V7fW3hHE,591
|
|
238
|
+
simba/assets/icons/left_arrow_green.png,sha256=MHqoHc1Kdj7QT-734E7ykS9JrUkkpk41IJoWK0n9TR8,583
|
|
239
|
+
simba/assets/icons/left_arrow_red.png,sha256=7rd7F1Ew0cwmNXTuMOse90E55bwYVRLzcytoQ9FbJjE,565
|
|
238
240
|
simba/assets/icons/left_ear.png,sha256=nFhf6MENIJdocklnzl4QXinxa3ZoX6nsgKFhVeuIi1s,530
|
|
239
241
|
simba/assets/icons/light_bulb.png,sha256=V8TIhHTL3aDhowEd8ytrxaQPOsBEMJwqlLJm2rGtuUY,507
|
|
240
242
|
simba/assets/icons/line.png,sha256=B_12LipvMHXooT4TgVjMAJB4xSZtnNEk2CjssThxk8k,230
|
|
@@ -337,6 +339,8 @@ simba/assets/icons/rewind.png,sha256=UMwE2s46EHZ0QOm5Cb_p4FdWhUE5mgs58bjxF6D2zVM
|
|
|
337
339
|
simba/assets/icons/rewind_blue_2.png,sha256=eFl-heXp3JskJn25TvcC1fr3TLxVgNxgxjCWoiU7fZo,552
|
|
338
340
|
simba/assets/icons/rewind_large.png,sha256=_fWLmuK4Bh8CwN3cPE0VPDUN2d3FR55nnGaEC62o4Q4,412
|
|
339
341
|
simba/assets/icons/rewind_red.png,sha256=91dtjMAcQjmVB0CK5cwsSydtiOB8cnW-gaMFbW10ync,445
|
|
342
|
+
simba/assets/icons/right_arrow_green.png,sha256=AyHVYdcI9iUqHERJcHRi9erMXZL6TIAZvVttfB7lq9g,581
|
|
343
|
+
simba/assets/icons/right_arrow_red.png,sha256=rUC66nkmsrtU3DQu7NarVadiHgrj6qXr9HJuINeCpsw,570
|
|
340
344
|
simba/assets/icons/rocket.png,sha256=iM7uzAol22gv27lpZU8uNHNFotOhIDhnEfLjey8aKrw,801
|
|
341
345
|
simba/assets/icons/roi.png,sha256=lG_ifGWlysaoBX4wc1UMznSvAYsFm9ZHxLRux7eBfaA,4823
|
|
342
346
|
simba/assets/icons/roi_green.png,sha256=ycX7hPtaYjCdtPDz_QL4MGRUQQ7XmVxnfkrdGHQ0JxY,1217
|
|
@@ -620,7 +624,7 @@ simba/mixins/config_reader.py,sha256=9su8ZGnGWghk5bAl12iaTQdqIOVfcW2ZQcxeEXmXtcI
|
|
|
620
624
|
simba/mixins/feature_extraction_mixin.py,sha256=rUwHEG3wKpyreme8nXAeBEhktHG_Q75v3OJI2aFOOOA,59587
|
|
621
625
|
simba/mixins/feature_extraction_supplement_mixin.py,sha256=6dPi1WFi26y-rmccsMgDe5sULm4fTQaBcSiH91DEkN0,44770
|
|
622
626
|
simba/mixins/geometry_mixin.py,sha256=pBOCFZvq_AF2Snmmy8izmtOG6xy6KOu1td0lm5c6uNg,234551
|
|
623
|
-
simba/mixins/image_mixin.py,sha256=
|
|
627
|
+
simba/mixins/image_mixin.py,sha256=C3FcZGp_kLMYMvbFxOedYPebIYm6KJIRPAB7rcu6J80,125807
|
|
624
628
|
simba/mixins/network_mixin.py,sha256=s_OsF6VSlsa2vb5sysDQKErLp4D5EfBF1MVQ_yKWC_Y,41125
|
|
625
629
|
simba/mixins/plotting_mixin.py,sha256=br3nWggOM776Ki30kq71P2geoGGgCLDn4MjDuiJ5IWs,95714
|
|
626
630
|
simba/mixins/pop_up_mixin.py,sha256=tAKC3Axp1jgQ8VH2GfvKO5HbLUpZzo_aWou2xS-SYxc,24701
|
|
@@ -773,7 +777,7 @@ simba/roi_tools/roi_selector_rectangle_tkinter.py,sha256=j_dPyXfSqWMwBuMndoHxD4u
|
|
|
773
777
|
simba/roi_tools/roi_time_bins_analyzer.py,sha256=fTt9iblXG0Y7ITdPGhRhSqwsjPiSMWIoKFr-U8VaVEo,25313
|
|
774
778
|
simba/roi_tools/roi_ui.py,sha256=e2znZUpY9n8i6Dia1hXLyh7BkF3XtuGw6W6En2XK7qc,9469
|
|
775
779
|
simba/roi_tools/roi_ui_mixin.py,sha256=ZVrzI_0L8QkAhwbCbi4GpOMYkBpUR7wPWIRBv5KalWM,93883
|
|
776
|
-
simba/roi_tools/roi_utils.py,sha256
|
|
780
|
+
simba/roi_tools/roi_utils.py,sha256=dEpZvX_tFb_giNg8lDfAPWr5Gm8BHuvE9O_6eBzcr1Y,37295
|
|
777
781
|
simba/sandbox/BackgroundRemoverDirectoryPopUp.py,sha256=wIoyJc9jhsjbnTAgoi_bHZBIaXFtCvQJ4IvqCXFuUCk,9249
|
|
778
782
|
simba/sandbox/BlobTrackingUI.py,sha256=RGIxPMdkW0xMvHLntrXLhnwVPZw3rveox4l5633HyrU,21407
|
|
779
783
|
simba/sandbox/BoundaryRearingFeaturizer.py,sha256=YPzISnoZD7u_EdbWqStjf0uoVE2-X-DiX7uv6IZTDzA,11096
|
|
@@ -1119,6 +1123,7 @@ simba/sandbox/reduce_imge_stack_size.py,sha256=JaOMzKVbRTV01SdcWkif0IgjrHd1Q0-rD
|
|
|
1119
1123
|
simba/sandbox/regression_errors.py,sha256=JqgC28VGnkNwVPIRXwHvUhJ-Ef5vmX7x6HgOSgus-JI,8327
|
|
1120
1124
|
simba/sandbox/relative_risk.py,sha256=_S_MlOUIks0vx3ow49--599UvoylMAICqdijWl3t8WA,2490
|
|
1121
1125
|
simba/sandbox/remove_background_single_video.py,sha256=pASwjxUxxz1x7fCSFlIyYrX0cXa5_jGZdEQwo5fv_-o,7227
|
|
1126
|
+
simba/sandbox/remove_end_of_video.py,sha256=ObVJyl6eoUoE15cbrhRRo6EBsr6CnEDRgx4CqfNC0To,4096
|
|
1122
1127
|
simba/sandbox/remove_substr_from_files.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
1123
1128
|
simba/sandbox/reverse_popup.py,sha256=qNCCwUa5u0KaPjOdv2Sux7M9eLBYNmtgx2uT-Fo2HfI,6723
|
|
1124
1129
|
simba/sandbox/reverse_videos.py,sha256=-CfXmqWOsyIvqKUSfbY0JRmuh2RLrblc1t6z_TmFKEk,5659
|
|
@@ -1230,6 +1235,7 @@ simba/sandbox/video_color.py,sha256=Tl1x6ZbkB5PGj1D7QGTqXuZxIkbogLggYbfvqGumxJg,
|
|
|
1230
1235
|
simba/sandbox/video_meta_data_get.py,sha256=foTzEuuqd0gYCVz54fWF4bITpANfBfCglNdLvRdpXj0,1979
|
|
1231
1236
|
simba/sandbox/video_rotator.py,sha256=gpO31jW8UPVU7BQiWaG0UVlLV3EmtYBfE07sp3VsFS0,5515
|
|
1232
1237
|
simba/sandbox/video_rotator_mp.py,sha256=gpO31jW8UPVU7BQiWaG0UVlLV3EmtYBfE07sp3VsFS0,5515
|
|
1238
|
+
simba/sandbox/video_timelaps.py,sha256=8FEGNmAgLgqMJIIuOcnMF3fBmk1PDrVEX5FmBOlVf8E,16327
|
|
1233
1239
|
simba/sandbox/video_to_frames.py,sha256=nil-6OpRQoGRN9YWf3Krm28tFT7B4_QDQNPaMJhiFXc,5107
|
|
1234
1240
|
simba/sandbox/violin.py,sha256=lUdumQm1CxgApmoXzvNIpy-d5ETJSbzjXi19H3CZRn8,1416
|
|
1235
1241
|
simba/sandbox/visualize_networks.py,sha256=zwzugqWk59WJHeKLJJSD0X2nr9Ns-_joy3YuebhCGUg,8843
|
|
@@ -1367,9 +1373,10 @@ simba/ui/import_videos_frame.py,sha256=i0LnQzPLFne9dA_nvpVWAHYGmi0aTRXpiHzEog_-R
|
|
|
1367
1373
|
simba/ui/machine_model_settings_ui.py,sha256=hTfpBxtfYGH9Rsf3JdQ5Sc8z874tYAoZefvjF1JD6gA,38292
|
|
1368
1374
|
simba/ui/ml_settings_frm.py,sha256=f1-E6pEGjWJVF3I6n0azO9zAnsskpZjInViguHIDntw,3101
|
|
1369
1375
|
simba/ui/px_to_mm_ui.py,sha256=ETedZPFkloU0l3JeGnhiSIAsGBQzzv0YrDWtiVGOmJ4,9387
|
|
1370
|
-
simba/ui/tkinter_functions.py,sha256=
|
|
1376
|
+
simba/ui/tkinter_functions.py,sha256=CUha5AY7iAuFL4Nt2oxHhV9eZIEuyDMq0J_VSoTjSio,41511
|
|
1371
1377
|
simba/ui/user_defined_pose_creator.py,sha256=QAfdp8r225DONLaCgQJe6kXiZZLj20VQCLJEnCvZsKs,9249
|
|
1372
1378
|
simba/ui/video_info_ui.py,sha256=ld_fN-3vCYJDFv1t9i5B-VVtoaBCspDmiDUIfOSJb_8,17262
|
|
1379
|
+
simba/ui/video_timelaps.py,sha256=ikqkCLpFCvifm1c6RKG6CT3xrfIlihgd1AUzMlz9ZkA,18736
|
|
1373
1380
|
simba/ui/pop_ups/.DS_Store,sha256=4PUcfpTZzBMd8yz2YahGab5mqPUVIpvdxzjvQ2MmsBY,6148
|
|
1374
1381
|
simba/ui/pop_ups/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
1375
1382
|
simba/ui/pop_ups/about_simba_pop_up.py,sha256=IcmA97HVV7qgSfaf_5GLNwQfse5lA1_xL8ymA4e7aTU,14522
|
|
@@ -1464,7 +1471,7 @@ simba/ui/pop_ups/subset_feature_extractor_pop_up.py,sha256=M24iJSqh-DpYdpw1pSaIm
|
|
|
1464
1471
|
simba/ui/pop_ups/targeted_annotation_clips_pop_up.py,sha256=PFh5ua2f_OMQ1Pth9Ha8Fo5lTPZNQV3bMnRGEoAPhTQ,6997
|
|
1465
1472
|
simba/ui/pop_ups/third_party_annotator_appender_pop_up.py,sha256=Xnha2UwM-08djObCkL_EXK2L4pernyipzbyNKQvX5aQ,7694
|
|
1466
1473
|
simba/ui/pop_ups/validation_plot_pop_up.py,sha256=yIo_el2dR_84ZAh_-2fYFg-BJDG0Eip_P_o9vzTQRkk,12174
|
|
1467
|
-
simba/ui/pop_ups/video_processing_pop_up.py,sha256=
|
|
1474
|
+
simba/ui/pop_ups/video_processing_pop_up.py,sha256=QnJtnAWKRoHHilfHdw993Z1U8EwK2-AG_XQQLhnG6mw,251956
|
|
1468
1475
|
simba/ui/pop_ups/visualize_pose_in_dir_pop_up.py,sha256=PpFs0zaqF4dnHJ_yH-PqYgsjAyxYPVP427Soj-kYtM0,8838
|
|
1469
1476
|
simba/ui/pop_ups/yolo_inference_popup.py,sha256=C4_WDvEHLp9JMUTjLZuRpKHxMCGpa_pxXELuj-zerCs,14679
|
|
1470
1477
|
simba/ui/pop_ups/yolo_plot_results.py,sha256=yi9D3WquDu4L8PWJLZsODulojgakfy7Dzh_CpYK6Vgk,10096
|
|
@@ -1513,7 +1520,7 @@ simba/utils/enums.py,sha256=ZR2175N06ZHZNiHk8n757T-WGyt1-55LLpcg3Sbb91k,38668
|
|
|
1513
1520
|
simba/utils/errors.py,sha256=aC-1qiGlh1vvHxUaPxBMQ9-LW-KKWXCGlH9acCPH0Cc,18788
|
|
1514
1521
|
simba/utils/lookups.py,sha256=hVUIis9FxgoKvTa2S2Rhrqg_LKrzW13tEBr3Tt8ZP44,50458
|
|
1515
1522
|
simba/utils/printing.py,sha256=2s-uESy1knuPiniqQ-q277uQ2teYM4OHo9Y4L20JQWM,5353
|
|
1516
|
-
simba/utils/read_write.py,sha256=
|
|
1523
|
+
simba/utils/read_write.py,sha256=Rn4BavywRGn2MxioYlMMABAdcVGHcNLDDkqulR91_QM,189463
|
|
1517
1524
|
simba/utils/warnings.py,sha256=K7w1RiDL4Un7rGaabOVCGc9fHcaKxk66iZyNLS_AtOE,8121
|
|
1518
1525
|
simba/utils/yolo.py,sha256=UZzpnDqZj81SOMnwsWPQIhFAsHHSSaDawi1UUh0-uAA,19264
|
|
1519
1526
|
simba/utils/cli/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
@@ -1521,7 +1528,7 @@ simba/utils/cli/cli_tools.py,sha256=vDPGuwqWTnHQ35pchC0uN3P8U_3GVnHalL3SFgGFc0g,
|
|
|
1521
1528
|
simba/video_processors/.DS_Store,sha256=6gsgZL1uIfKqBNSk6EAKBP9lJ1qMrQy6XrEvluyc2GE,6148
|
|
1522
1529
|
simba/video_processors/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
1523
1530
|
simba/video_processors/async_frame_reader.py,sha256=_17735pfAKUDHE18snAbWIbxUhIFkx3m-HipWqoE6r8,8059
|
|
1524
|
-
simba/video_processors/batch_process_create_ffmpeg_commands.py,sha256=
|
|
1531
|
+
simba/video_processors/batch_process_create_ffmpeg_commands.py,sha256=qfGb6KeJ7kOpRreRfpV1Lb9zCOL2g7cDqUFbEk9or2k,14194
|
|
1525
1532
|
simba/video_processors/batch_process_menus.py,sha256=cLgJCx90wvgzZJX2ygoPIQc1f4cWPaC6O02XH7Ta458,37627
|
|
1526
1533
|
simba/video_processors/blob_tracking_executor.py,sha256=hyB-FYwbCmk44ytOmYQsiWHh7ecE0h5A0-ySjpYWyvY,18395
|
|
1527
1534
|
simba/video_processors/brightness_contrast_ui.py,sha256=nWmzho1WeJuIp3CuDjJmqMIzge2sTZn6_H0lWyZYaz0,5202
|
|
@@ -1534,11 +1541,11 @@ simba/video_processors/multi_cropper.py,sha256=1BI0Ami4kB9rdMUHR0EistmIKqc-E5FK5
|
|
|
1534
1541
|
simba/video_processors/roi_selector.py,sha256=5N3s0Bi1Ub6c9gjE_-mV7AWr8Fqg7HQKdBKBF6whurg,8522
|
|
1535
1542
|
simba/video_processors/roi_selector_circle.py,sha256=SD_lv6V3MGiIQd0VtUFSKe83ySW_qvE1t8xsgAlr2hI,6436
|
|
1536
1543
|
simba/video_processors/roi_selector_polygon.py,sha256=DMtilt__gGwNu6VV73CWbnPqrPBXkan1_akUqGEzfGw,6742
|
|
1537
|
-
simba/video_processors/video_processing.py,sha256=
|
|
1544
|
+
simba/video_processors/video_processing.py,sha256=ynHC-9-Pt020GMZa5xPDhfpouHS5i6D26mT-5F9NAY0,327790
|
|
1538
1545
|
simba/video_processors/videos_to_frames.py,sha256=8hltNZpwUfb3GFi-63D0PsySmD5l59pbzQGJx8SscgU,7818
|
|
1539
|
-
simba_uw_tf_dev-4.7.
|
|
1540
|
-
simba_uw_tf_dev-4.7.
|
|
1541
|
-
simba_uw_tf_dev-4.7.
|
|
1542
|
-
simba_uw_tf_dev-4.7.
|
|
1543
|
-
simba_uw_tf_dev-4.7.
|
|
1544
|
-
simba_uw_tf_dev-4.7.
|
|
1546
|
+
simba_uw_tf_dev-4.7.4.dist-info/LICENSE,sha256=Sjn362upcvYFypam-b-ziOXU1Wl5GGuTt5ICrGimzyA,1720
|
|
1547
|
+
simba_uw_tf_dev-4.7.4.dist-info/METADATA,sha256=3efqgf6cgV9K_10w20ZZ66GV5QKccmTQUpcrNI4cJTA,11432
|
|
1548
|
+
simba_uw_tf_dev-4.7.4.dist-info/WHEEL,sha256=G16H4A3IeoQmnOrYV4ueZGKSjhipXx8zc8nu9FGlvMA,92
|
|
1549
|
+
simba_uw_tf_dev-4.7.4.dist-info/entry_points.txt,sha256=Nfh_EbfDGdKftLjCnGWtQrBHENiDYMdgupwLyLpU5dc,44
|
|
1550
|
+
simba_uw_tf_dev-4.7.4.dist-info/top_level.txt,sha256=ogtimvlqDxDTOBAPfT2WaQ2pGAAbKRXG8z8eUTzf6TU,14
|
|
1551
|
+
simba_uw_tf_dev-4.7.4.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|