simba-uw-tf-dev 4.7.2__py3-none-any.whl → 4.7.5__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.
Potentially problematic release.
This version of simba-uw-tf-dev might be problematic. Click here for more details.
- simba/SimBA.py +13 -4
- 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/assets/lookups/yolo_schematics/yolo_mitra.csv +1 -1
- simba/mixins/geometry_mixin.py +357 -302
- simba/mixins/image_mixin.py +129 -4
- simba/model/yolo_fit.py +22 -15
- simba/model/yolo_pose_inference.py +7 -2
- simba/outlier_tools/skip_outlier_correction.py +2 -2
- simba/plotting/heat_mapper_clf_mp.py +45 -23
- simba/plotting/plot_clf_results.py +2 -1
- simba/plotting/plot_clf_results_mp.py +456 -455
- simba/roi_tools/roi_utils.py +2 -2
- simba/sandbox/convert_h264_to_mp4_lossless.py +129 -0
- simba/sandbox/extract_and_convert_videos.py +257 -0
- simba/sandbox/remove_end_of_video.py +80 -0
- simba/sandbox/video_timelaps.py +291 -0
- simba/ui/import_pose_frame.py +13 -13
- simba/ui/pop_ups/clf_plot_pop_up.py +1 -1
- simba/ui/pop_ups/run_machine_models_popup.py +2 -2
- simba/ui/pop_ups/video_processing_pop_up.py +3638 -3469
- simba/ui/tkinter_functions.py +3 -1
- simba/ui/video_timelaps.py +454 -0
- simba/utils/lookups.py +67 -1
- simba/utils/read_write.py +10 -3
- simba/video_processors/batch_process_create_ffmpeg_commands.py +0 -1
- simba/video_processors/video_processing.py +5385 -5264
- {simba_uw_tf_dev-4.7.2.dist-info → simba_uw_tf_dev-4.7.5.dist-info}/METADATA +1 -1
- {simba_uw_tf_dev-4.7.2.dist-info → simba_uw_tf_dev-4.7.5.dist-info}/RECORD +35 -26
- {simba_uw_tf_dev-4.7.2.dist-info → simba_uw_tf_dev-4.7.5.dist-info}/LICENSE +0 -0
- {simba_uw_tf_dev-4.7.2.dist-info → simba_uw_tf_dev-4.7.5.dist-info}/WHEEL +0 -0
- {simba_uw_tf_dev-4.7.2.dist-info → simba_uw_tf_dev-4.7.5.dist-info}/entry_points.txt +0 -0
- {simba_uw_tf_dev-4.7.2.dist-info → simba_uw_tf_dev-4.7.5.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,291 @@
|
|
|
1
|
+
from typing import Union, Optional, Tuple
|
|
2
|
+
import os
|
|
3
|
+
from simba.mixins.image_mixin import ImageMixin
|
|
4
|
+
import cv2
|
|
5
|
+
import numpy as np
|
|
6
|
+
from simba.utils.checks import check_file_exist_and_readable, check_int, check_valid_boolean
|
|
7
|
+
from simba.utils.read_write import get_video_meta_data, seconds_to_timestamp, read_frm_of_video
|
|
8
|
+
from simba.utils.lookups import get_fonts
|
|
9
|
+
from simba.utils.enums import Formats
|
|
10
|
+
from simba.ui.tkinter_functions import SimBAScaleBar, SimBALabel
|
|
11
|
+
from tkinter import *
|
|
12
|
+
from PIL import Image, ImageDraw, ImageFont, ImageTk
|
|
13
|
+
|
|
14
|
+
class TimelapseSlider():
|
|
15
|
+
"""
|
|
16
|
+
Interactive timelapse viewer with segment selection sliders.
|
|
17
|
+
|
|
18
|
+
Creates a Tkinter GUI window displaying a timelapse composite image generated from evenly-spaced frames
|
|
19
|
+
across a video. Includes interactive sliders to select start and end times for video segments, with
|
|
20
|
+
visual highlighting of the selected segment and frame previews.
|
|
21
|
+
|
|
22
|
+
:param Union[str, os.PathLike] video_path: Path to video file to create timelapse from.
|
|
23
|
+
:param int frame_cnt: Number of frames to include in timelapse composite. Default 25.
|
|
24
|
+
:param Optional[int] size: Width per frame in pixels. If None, calculated to match video width. Default None.
|
|
25
|
+
:param Optional[int] crop_ratio: Percentage of frame width to keep (0-100). Default 50.
|
|
26
|
+
:param int padding: Padding in pixels added to timelapse when ruler is shown. Default 60.
|
|
27
|
+
:param int ruler_divisions: Number of major divisions on time ruler. Default 6.
|
|
28
|
+
:param bool show_ruler: If True, display time ruler below timelapse. Default True.
|
|
29
|
+
:param int ruler_height: Height of ruler in pixels. Default 60.
|
|
30
|
+
|
|
31
|
+
:example:
|
|
32
|
+
>>> slider = TimelapseSlider(video_path='path/to/video.mp4', frame_cnt=25, crop_ratio=75)
|
|
33
|
+
>>> slider.run()
|
|
34
|
+
>>> # Use sliders to select segment, then access selected times:
|
|
35
|
+
>>> start_time = slider.img_window.get_selected_start_time()
|
|
36
|
+
>>> end_time = slider.img_window.get_selected_end_time()
|
|
37
|
+
>>> slider.close()
|
|
38
|
+
"""
|
|
39
|
+
|
|
40
|
+
def __init__(self,
|
|
41
|
+
video_path: Union[str, os.PathLike],
|
|
42
|
+
frame_cnt: int = 25,
|
|
43
|
+
size: Optional[int] = None,
|
|
44
|
+
crop_ratio: Optional[int] = 50,
|
|
45
|
+
padding: int = 60,
|
|
46
|
+
ruler_divisions: int = 6,
|
|
47
|
+
show_ruler: bool = True,
|
|
48
|
+
ruler_height: int = 60):
|
|
49
|
+
|
|
50
|
+
check_file_exist_and_readable(file_path=video_path)
|
|
51
|
+
check_int(name='frame_cnt', value=frame_cnt, min_value=1, raise_error=True)
|
|
52
|
+
if size is not None: check_int(name='size', value=size, min_value=1, raise_error=True)
|
|
53
|
+
check_int(name='padding', value=padding, min_value=1, raise_error=True)
|
|
54
|
+
check_int(name='ruler_height', value=ruler_height, min_value=1, raise_error=True)
|
|
55
|
+
check_valid_boolean(value=show_ruler, source=f'{self.__class__.__name__} show_ruler', raise_error=True)
|
|
56
|
+
self.video_meta = get_video_meta_data(video_path=video_path, raise_error=True)
|
|
57
|
+
if show_ruler: check_int(name='ruler_divisions', value=ruler_divisions, min_value=1, raise_error=True)
|
|
58
|
+
self.size, self.padding, self.crop_ratio, self.frame_cnt = size, padding, crop_ratio, frame_cnt
|
|
59
|
+
self.ruler_height, self.video_path, self.show_ruler, self.ruler_divisions = ruler_height, video_path, show_ruler, ruler_divisions
|
|
60
|
+
self.frm_name = f'{self.video_meta["video_name"]} - TIMELAPSE VIEWER'
|
|
61
|
+
self.video_capture = None # Will be initialized in run()
|
|
62
|
+
self._pending_frame_update = None # For debouncing frame preview updates
|
|
63
|
+
self._frame_debounce_ms = 300 # Wait 300ms after slider stops before updating frame preview
|
|
64
|
+
|
|
65
|
+
def _draw_img(self, img: np.ndarray, lbl: SimBALabel):
|
|
66
|
+
img_rgb = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
|
|
67
|
+
self.pil_image = Image.fromarray(img_rgb)
|
|
68
|
+
self.tk_image = ImageTk.PhotoImage(self.pil_image)
|
|
69
|
+
lbl.configure(image=self.tk_image)
|
|
70
|
+
lbl.image = self.tk_image
|
|
71
|
+
|
|
72
|
+
def _update_selection(self, slider_type: str):
|
|
73
|
+
start_sec = int(self.start_scale.get_value())
|
|
74
|
+
end_sec = int(self.end_scale.get_value())
|
|
75
|
+
max_sec = int(self.video_meta['video_length_s'])
|
|
76
|
+
if slider_type == 'start':
|
|
77
|
+
if start_sec >= end_sec:
|
|
78
|
+
end_sec = min(start_sec + 1, max_sec)
|
|
79
|
+
self.end_scale.set_value(end_sec)
|
|
80
|
+
else:
|
|
81
|
+
if end_sec <= start_sec:
|
|
82
|
+
start_sec = max(end_sec - 1, 0)
|
|
83
|
+
self.start_scale.set_value(start_sec)
|
|
84
|
+
|
|
85
|
+
self.selected_start[0] = start_sec
|
|
86
|
+
self.selected_end[0] = end_sec
|
|
87
|
+
|
|
88
|
+
# Update time labels immediately (fast, no I/O)
|
|
89
|
+
self.start_time_label.config(text=seconds_to_timestamp(start_sec), fg='blue')
|
|
90
|
+
self.end_time_label.config(text=seconds_to_timestamp(end_sec), fg='red')
|
|
91
|
+
|
|
92
|
+
if self.video_meta['video_length_s'] > 0:
|
|
93
|
+
# Update highlight immediately (moderate cost, but needed for visual feedback)
|
|
94
|
+
self._highlight_segment(start_sec, end_sec)
|
|
95
|
+
# Debounce frame preview update (expensive I/O operation)
|
|
96
|
+
self._schedule_frame_update(slider_type=slider_type)
|
|
97
|
+
|
|
98
|
+
def _schedule_frame_update(self, slider_type: str):
|
|
99
|
+
"""Schedule frame preview update with debouncing.
|
|
100
|
+
|
|
101
|
+
Cancels any pending frame update and schedules a new one. If the slider
|
|
102
|
+
moves again before the delay expires, the update is cancelled and rescheduled.
|
|
103
|
+
This prevents expensive frame reads during fast slider dragging.
|
|
104
|
+
"""
|
|
105
|
+
# Check if window exists (might be called during cleanup)
|
|
106
|
+
if not hasattr(self, 'img_window') or not self.img_window.winfo_exists():
|
|
107
|
+
return
|
|
108
|
+
|
|
109
|
+
# Cancel any pending frame update
|
|
110
|
+
if self._pending_frame_update is not None:
|
|
111
|
+
self.img_window.after_cancel(self._pending_frame_update)
|
|
112
|
+
|
|
113
|
+
# Schedule new update after debounce delay
|
|
114
|
+
self._pending_frame_update = self.img_window.after(
|
|
115
|
+
self._frame_debounce_ms,
|
|
116
|
+
lambda: self._update_frame_display(slider_type=slider_type)
|
|
117
|
+
)
|
|
118
|
+
|
|
119
|
+
def _update_frame_display(self, slider_type: str):
|
|
120
|
+
"""Update the frame preview display using the persistent VideoCapture object."""
|
|
121
|
+
if slider_type == 'start':
|
|
122
|
+
seconds = self.selected_start[0]
|
|
123
|
+
self.frame_label.config(text=f"Start Frame Preview ({seconds_to_timestamp(seconds)})", font=Formats.FONT_LARGE_BOLD.value, fg='blue')
|
|
124
|
+
else:
|
|
125
|
+
seconds = self.selected_end[0]
|
|
126
|
+
self.frame_label.config(text=f"End Frame Preview ({seconds_to_timestamp(seconds)})", font=Formats.FONT_LARGE_BOLD.value, fg='red')
|
|
127
|
+
|
|
128
|
+
frame_index = int(seconds * self.video_meta['fps'])
|
|
129
|
+
if frame_index >= self.video_meta['frame_count']: frame_index = self.video_meta['frame_count'] - 1
|
|
130
|
+
if frame_index < 0: frame_index = 0
|
|
131
|
+
|
|
132
|
+
# Use persistent VideoCapture object instead of read_frm_of_video
|
|
133
|
+
if self.video_capture is not None and self.video_capture.isOpened():
|
|
134
|
+
self.video_capture.set(cv2.CAP_PROP_POS_FRAMES, frame_index)
|
|
135
|
+
ret, frame = self.video_capture.read()
|
|
136
|
+
if ret and frame is not None:
|
|
137
|
+
self._draw_img(img=frame, lbl=self.frame_display_lbl)
|
|
138
|
+
|
|
139
|
+
def _create_time_ruler(self, width: int, height: int, num_divisions: int) -> np.ndarray:
|
|
140
|
+
total_width = width + (2 * self.padding)
|
|
141
|
+
bg_color, line_color, text_color = (255, 255, 255), (128, 128, 128), (0, 0, 0)
|
|
142
|
+
img = Image.new('RGB', (total_width, height), color=bg_color)
|
|
143
|
+
draw = ImageDraw.Draw(img)
|
|
144
|
+
font_dict = get_fonts()
|
|
145
|
+
try:
|
|
146
|
+
font_path = font_dict['Algerian']
|
|
147
|
+
pil_font = ImageFont.truetype(font_path, size=12)
|
|
148
|
+
except (KeyError, OSError):
|
|
149
|
+
pil_font = ImageFont.load_default()
|
|
150
|
+
major_tick_height, half_tick_height = height * 0.6, height * 0.4
|
|
151
|
+
quarter_tick_height, eighth_tick_height = height * 0.25, height * 0.15
|
|
152
|
+
|
|
153
|
+
for i in range(num_divisions + 1):
|
|
154
|
+
x = self.padding + int(i * width / num_divisions)
|
|
155
|
+
draw.line([(x, 0), (x, major_tick_height)], fill=line_color, width=2)
|
|
156
|
+
if self.video_meta['video_length_s'] is not None:
|
|
157
|
+
seconds_at_division = i * self.video_meta['video_length_s'] / num_divisions
|
|
158
|
+
label = seconds_to_timestamp(seconds=seconds_at_division)
|
|
159
|
+
elif self.video_meta['frame_count'] is not None:
|
|
160
|
+
label = str(int(i * self.video_meta['frame_count'] / num_divisions))
|
|
161
|
+
else:
|
|
162
|
+
label = str(i)
|
|
163
|
+
bbox = draw.textbbox((0, 0), label, font=pil_font)
|
|
164
|
+
text_width = bbox[2] - bbox[0]
|
|
165
|
+
if i == 0:
|
|
166
|
+
draw.text((x, major_tick_height + 5), label, fill=text_color, font=pil_font)
|
|
167
|
+
elif i == num_divisions:
|
|
168
|
+
draw.text((x - text_width, major_tick_height + 5), label, fill=text_color, font=pil_font)
|
|
169
|
+
else:
|
|
170
|
+
draw.text((x - text_width // 2, major_tick_height + 5), label, fill=text_color, font=pil_font)
|
|
171
|
+
if i < num_divisions:
|
|
172
|
+
x_half = self.padding + int((i + 0.5) * width / num_divisions)
|
|
173
|
+
draw.line([(x_half, 0), (x_half, half_tick_height)], fill=line_color, width=1)
|
|
174
|
+
for q in [0.25, 0.75]:
|
|
175
|
+
x_quarter = self.padding + int((i + q) * width / num_divisions)
|
|
176
|
+
draw.line([(x_quarter, 0), (x_quarter, quarter_tick_height)], fill=line_color, width=1)
|
|
177
|
+
for e in [0.125, 0.375, 0.625, 0.875]:
|
|
178
|
+
x_eighth = self.padding + int((i + e) * width / num_divisions)
|
|
179
|
+
draw.line([(x_eighth, 0), (x_eighth, eighth_tick_height)], fill=line_color, width=1)
|
|
180
|
+
|
|
181
|
+
draw.line([(0, height - 1), (total_width, height - 1)], fill=line_color, width=1)
|
|
182
|
+
img_array = np.array(img)
|
|
183
|
+
img_bgr = cv2.cvtColor(img_array, cv2.COLOR_RGB2BGR)
|
|
184
|
+
return img_bgr
|
|
185
|
+
|
|
186
|
+
def _highlight_segment(self, start_sec: int, end_sec: int):
|
|
187
|
+
timelapse_width = self.original_timelapse.shape[1]
|
|
188
|
+
start_x = int((start_sec / self.video_meta['video_length_s']) * timelapse_width)
|
|
189
|
+
end_x = int((end_sec / self.video_meta['video_length_s']) * timelapse_width)
|
|
190
|
+
highlighted = self.original_timelapse.copy()
|
|
191
|
+
mask = np.ones(highlighted.shape[:2], dtype=np.uint8) * 128
|
|
192
|
+
mask[:, start_x:end_x] = 255
|
|
193
|
+
mask = cv2.merge([mask, mask, mask])
|
|
194
|
+
highlighted = cv2.multiply(highlighted, mask.astype(np.uint8), scale=1/255.0)
|
|
195
|
+
cv2.line(highlighted, (start_x, 0), (start_x, highlighted.shape[0]), (0, 255, 0), 2)
|
|
196
|
+
cv2.line(highlighted, (end_x, 0), (end_x, highlighted.shape[0]), (0, 255, 0), 2)
|
|
197
|
+
self._draw_img(img=highlighted, lbl=self.img_lbl)
|
|
198
|
+
|
|
199
|
+
def run(self):
|
|
200
|
+
# Create persistent VideoCapture object for efficient frame reading
|
|
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 = self._create_time_ruler(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(False, False)
|
|
216
|
+
self.img_window.title(self.frm_name)
|
|
217
|
+
self.img_window.protocol("WM_DELETE_WINDOW", self.close)
|
|
218
|
+
|
|
219
|
+
|
|
220
|
+
self.img_lbl = SimBALabel(parent=self.img_window, txt='')
|
|
221
|
+
self.img_lbl.pack()
|
|
222
|
+
self._draw_img(img=self.timelapse_img, lbl=self.img_lbl)
|
|
223
|
+
self.frame_display_frame = Frame(self.img_window)
|
|
224
|
+
self.frame_display_frame.pack(pady=10, padx=10, fill=BOTH, expand=True)
|
|
225
|
+
self.frame_label = SimBALabel(parent=self.frame_display_frame, txt="Frame Preview", font=Formats.FONT_REGULAR_BOLD.value)
|
|
226
|
+
self.frame_label.pack()
|
|
227
|
+
self.frame_display_lbl = SimBALabel(parent=self.frame_display_frame, txt='', bg_clr='black')
|
|
228
|
+
self.frame_display_lbl.pack(pady=5)
|
|
229
|
+
self.slider_frame = Frame(self.img_window)
|
|
230
|
+
self.slider_frame.pack(pady=10, padx=10, fill=X)
|
|
231
|
+
self.slider_frame.columnconfigure(index=0, weight=1)
|
|
232
|
+
self.slider_frame.columnconfigure(index=1, weight=0)
|
|
233
|
+
self.slider_frame.columnconfigure(index=2, weight=0)
|
|
234
|
+
self.slider_frame.columnconfigure(index=3, weight=1)
|
|
235
|
+
|
|
236
|
+
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='blue', lbl_font=Formats.FONT_LARGE_BOLD.value)
|
|
237
|
+
self.start_scale.grid(row=0, column=1, padx=5)
|
|
238
|
+
self.start_scale.scale.config(command=lambda x: self._update_selection(slider_type='start'))
|
|
239
|
+
|
|
240
|
+
self.start_time_label = SimBALabel(parent=self.slider_frame, txt="00:00:00", font=Formats.FONT_LARGE_BOLD.value, width=10, txt_clr='blue')
|
|
241
|
+
self.start_time_label.grid(row=0, column=2, padx=5)
|
|
242
|
+
|
|
243
|
+
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)
|
|
244
|
+
self.end_scale.grid(row=1, column=1, padx=5)
|
|
245
|
+
self.end_scale.scale.config(command=lambda x: self._update_selection(slider_type='end'))
|
|
246
|
+
|
|
247
|
+
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')
|
|
248
|
+
self.end_time_label.grid(row=1, column=2, padx=5)
|
|
249
|
+
|
|
250
|
+
self.selected_start = [0]
|
|
251
|
+
self.selected_end = [int(self.video_meta['video_length_s'])]
|
|
252
|
+
|
|
253
|
+
self.img_window.get_selected_start = lambda: self.selected_start[0]
|
|
254
|
+
self.img_window.get_selected_end = lambda: self.selected_end[0]
|
|
255
|
+
self.img_window.get_selected_start_time = lambda: seconds_to_timestamp(self.selected_start[0])
|
|
256
|
+
self.img_window.get_selected_end_time = lambda: seconds_to_timestamp(self.selected_end[0])
|
|
257
|
+
|
|
258
|
+
self.img_window.update_idletasks()
|
|
259
|
+
self.img_window.update()
|
|
260
|
+
req_width, req_height = self.img_window.winfo_reqwidth(), self.img_window.winfo_reqheight()
|
|
261
|
+
min_width = max(self.timelapse_img.shape[1] + 60, req_width + 20)
|
|
262
|
+
min_height = max(self.timelapse_img.shape[0] + 800, req_height + 100)
|
|
263
|
+
self.img_window.minsize(min_width, min_height)
|
|
264
|
+
self.img_window.geometry(f"{min_width}x{min_height}")
|
|
265
|
+
self._update_frame_display(slider_type='start')
|
|
266
|
+
|
|
267
|
+
def close(self):
|
|
268
|
+
# Cancel any pending frame updates
|
|
269
|
+
if self._pending_frame_update is not None:
|
|
270
|
+
if hasattr(self, 'img_window') and self.img_window.winfo_exists():
|
|
271
|
+
self.img_window.after_cancel(self._pending_frame_update)
|
|
272
|
+
self._pending_frame_update = None
|
|
273
|
+
|
|
274
|
+
# Release VideoCapture object
|
|
275
|
+
if self.video_capture is not None:
|
|
276
|
+
self.video_capture.release()
|
|
277
|
+
self.video_capture = None
|
|
278
|
+
|
|
279
|
+
# Destroy window
|
|
280
|
+
if hasattr(self, 'img_window') and self.img_window.winfo_exists():
|
|
281
|
+
self.img_window.destroy()
|
|
282
|
+
|
|
283
|
+
|
|
284
|
+
|
|
285
|
+
|
|
286
|
+
x = TimelapseSlider(video_path=r"E:\troubleshooting\mitra_emergence\project_folder\clip_test\Box1_180mISOcontrol_Females_clipped_progress_bar.mp4",
|
|
287
|
+
frame_cnt=25,
|
|
288
|
+
crop_ratio=75,
|
|
289
|
+
size=100)
|
|
290
|
+
|
|
291
|
+
x.run()
|
simba/ui/import_pose_frame.py
CHANGED
|
@@ -82,7 +82,7 @@ class ImportPoseFrame(ConfigReader, PopUpMixin):
|
|
|
82
82
|
Label(self.import_tracking_frm, text="Please CREATE PROJECT CONFIG before importing tracking data \n", font=Formats.FONT_REGULAR.value).grid(row=0, column=0, sticky=NW)
|
|
83
83
|
else:
|
|
84
84
|
ConfigReader.__init__(self, config_path=config_path, read_video_info=False)
|
|
85
|
-
self.data_type_dropdown = SimBADropDown(parent=self.import_tracking_frm, dropdown_options=Options.IMPORT_TYPE_OPTIONS.value, label="DATA TYPE: ", label_width=25, command=self.create_import_menu, dropdown_width=25, value=Options.IMPORT_TYPE_OPTIONS.value[0])
|
|
85
|
+
self.data_type_dropdown = SimBADropDown(parent=self.import_tracking_frm, dropdown_options=Options.IMPORT_TYPE_OPTIONS.value, label="DATA TYPE: ", label_width=25, command=self.create_import_menu, dropdown_width=25, value=Options.IMPORT_TYPE_OPTIONS.value[0], img='file_type')
|
|
86
86
|
self.data_type_dropdown.grid(row=0, column=0, sticky=NW)
|
|
87
87
|
self.create_import_menu(data_type_choice=Options.IMPORT_TYPE_OPTIONS.value[0])
|
|
88
88
|
self.import_tracking_frm.grid(row=idx_row, column=idx_column, sticky=NW)
|
|
@@ -91,7 +91,7 @@ class ImportPoseFrame(ConfigReader, PopUpMixin):
|
|
|
91
91
|
|
|
92
92
|
def __show_smoothing_entry_box_from_dropdown(self, choice: str):
|
|
93
93
|
if (choice == GAUSSIAN) or (choice == SAVITZKY_GOLAY):
|
|
94
|
-
self.smoothing_time_eb.grid(row=
|
|
94
|
+
self.smoothing_time_eb.grid(row=1, column=0, sticky=E)
|
|
95
95
|
else:
|
|
96
96
|
self.smoothing_time_eb.grid_forget()
|
|
97
97
|
|
|
@@ -265,21 +265,21 @@ class ImportPoseFrame(ConfigReader, PopUpMixin):
|
|
|
265
265
|
self.animal_name_entry_boxes = None
|
|
266
266
|
|
|
267
267
|
self.interpolation_frm = CreateLabelFrameWithIcon(parent=self.choice_frm, header="INTERPOLATION METHOD", pady=5, padx=5,font=Formats.FONT_HEADER.value, icon_name='fill', relief='groove')
|
|
268
|
-
self.interpolation_dropdown = SimBADropDown(parent=self.interpolation_frm, dropdown_options=Options.INTERPOLATION_OPTIONS_W_NONE.value, label='INTERPOLATION METHOD: ', label_width=25, dropdown_width=35, value=Options.INTERPOLATION_OPTIONS_W_NONE.value[0])
|
|
268
|
+
self.interpolation_dropdown = SimBADropDown(parent=self.interpolation_frm, dropdown_options=Options.INTERPOLATION_OPTIONS_W_NONE.value, label='INTERPOLATION METHOD: ', label_width=25, dropdown_width=35, value=Options.INTERPOLATION_OPTIONS_W_NONE.value[0], img='fill')
|
|
269
269
|
self.interpolation_frm.grid(row=0, column=0, sticky=NW)
|
|
270
270
|
self.interpolation_dropdown.grid(row=0, column=0, sticky=NW)
|
|
271
271
|
|
|
272
272
|
self.smoothing_frm = CreateLabelFrameWithIcon(parent=self.choice_frm, header="SMOOTHING METHOD", pady=5, padx=5, font=Formats.FONT_HEADER.value, icon_name='smooth', relief='groove')
|
|
273
|
-
self.smoothing_dropdown = SimBADropDown(parent=self.smoothing_frm, dropdown_options=Options.SMOOTHING_OPTIONS_W_NONE.value, label='SMOOTHING: ', label_width=25, dropdown_width=35, value=Options.SMOOTHING_OPTIONS_W_NONE.value[0], command=self.__show_smoothing_entry_box_from_dropdown)
|
|
274
|
-
self.smoothing_time_eb = Entry_Box(self.smoothing_frm, "SMOOTHING PERIOD (MS):", labelwidth=
|
|
273
|
+
self.smoothing_dropdown = SimBADropDown(parent=self.smoothing_frm, dropdown_options=Options.SMOOTHING_OPTIONS_W_NONE.value, label='SMOOTHING: ', label_width=25, dropdown_width=35, value=Options.SMOOTHING_OPTIONS_W_NONE.value[0], command=self.__show_smoothing_entry_box_from_dropdown, img='smooth')
|
|
274
|
+
self.smoothing_time_eb = Entry_Box(self.smoothing_frm, "SMOOTHING PERIOD (MS):", labelwidth=25, validation="numeric", entry_box_width=35, img='timer_2')
|
|
275
275
|
self.smoothing_frm.grid(row=1, column=0, sticky=NW)
|
|
276
276
|
self.smoothing_dropdown.grid(row=0, column=0, sticky=NW)
|
|
277
277
|
|
|
278
278
|
if data_type_choice in ["CSV (DLC/DeepPoseKit)", "MAT (DANNCE 3D)", "JSON (BENTO)", "CSV (SimBA BLOB)", 'H5 (FaceMap)', 'CSV (SimBA YOLO)']: # DATA TYPES WHERE NO TRACKS HAVE TO BE SPECIFIED
|
|
279
279
|
self.import_directory_frm = LabelFrame(self.choice_frm, text=FRAME_DIR_IMPORT_TITLES[data_type_choice], pady=5, padx=5, font=Formats.FONT_HEADER.value,)
|
|
280
|
-
self.import_directory_select = FolderSelect(self.import_directory_frm, "Input data DIRECTORY:", lblwidth=25, initialdir=self.project_path)
|
|
280
|
+
self.import_directory_select = FolderSelect(self.import_directory_frm, "Input data DIRECTORY:", lblwidth=25, initialdir=self.project_path, lbl_icon='folder')
|
|
281
281
|
self.import_single_frm = LabelFrame(self.choice_frm, text=FRAME_FILE_IMPORT_TITLES[data_type_choice], pady=5, padx=5, font=Formats.FONT_HEADER.value,)
|
|
282
|
-
self.import_file_select = FileSelect(self.import_single_frm, "Input data FILE:", lblwidth=25, file_types=[("Pose data file", FILE_TYPES[data_type_choice])])
|
|
282
|
+
self.import_file_select = FileSelect(self.import_single_frm, "Input data FILE:", lblwidth=25, file_types=[("Pose data file", FILE_TYPES[data_type_choice])], lbl_icon='file_type')
|
|
283
283
|
|
|
284
284
|
if data_type_choice == "CSV (DLC/DeepPoseKit)":
|
|
285
285
|
self.import_dir_btn = Button(self.import_directory_frm, fg="blue", font=Formats.FONT_REGULAR.value, text="Import DLC CSV DIRECTORY to SimBA project", command=lambda: self.__import_dlc_csv_data(interpolation_settings=self.interpolation_dropdown.getChoices(),
|
|
@@ -342,7 +342,7 @@ class ImportPoseFrame(ConfigReader, PopUpMixin):
|
|
|
342
342
|
self.dlc_data_type_option_dropdown.setChoices(Options.MULTI_DLC_TYPE_IMPORT_OPTION.value[1])
|
|
343
343
|
self.tracking_type_frm.grid(row=5, column=0, sticky=NW)
|
|
344
344
|
self.dlc_data_type_option_dropdown.grid(row=0, column=0, sticky=NW)
|
|
345
|
-
self.data_dir_select = FolderSelect(self.data_dir_frm, "H5 DLC DIRECTORY: ", lblwidth=25)
|
|
345
|
+
self.data_dir_select = FolderSelect(self.data_dir_frm, "H5 DLC DIRECTORY: ", lblwidth=25, lbl_icon='folder')
|
|
346
346
|
self.instructions_lbl = Label(self.data_dir_frm, text="Please import videos BEFORE importing the \n multi animal DLC tracking data", font=Formats.FONT_REGULAR.value)
|
|
347
347
|
self.run_btn = Button(self.import_frm, text="IMPORT DLC .H5", fg="blue", command=lambda: self.__multi_animal_run_call(pose_estimation_tool=data_type_choice,
|
|
348
348
|
interpolation_settings=self.interpolation_dropdown.getChoices(),
|
|
@@ -352,7 +352,7 @@ class ImportPoseFrame(ConfigReader, PopUpMixin):
|
|
|
352
352
|
data_path=self.data_dir_select.folder_path,
|
|
353
353
|
tracking_data_type=self.dlc_data_type_option_dropdown.getChoices()))
|
|
354
354
|
elif data_type_choice == "SLP (SLEAP)":
|
|
355
|
-
self.data_dir_select = FolderSelect(self.data_dir_frm, "SLP SLEAP DIRECTORY: ", lblwidth=25)
|
|
355
|
+
self.data_dir_select = FolderSelect(self.data_dir_frm, "SLP SLEAP DIRECTORY: ", lblwidth=25, lbl_icon='folder')
|
|
356
356
|
self.instructions_lbl = Label(self.data_dir_frm, font=Formats.FONT_REGULAR.value, text="Please import videos before importing the \n multi animal SLEAP tracking data if you are tracking more than ONE animal")
|
|
357
357
|
self.run_btn = Button(self.import_frm, text="IMPORT SLEAP .SLP", fg="blue", font=Formats.FONT_REGULAR.value, command=lambda: self.__multi_animal_run_call(pose_estimation_tool=data_type_choice,
|
|
358
358
|
interpolation_settings=self.interpolation_dropdown.getChoices(),
|
|
@@ -362,7 +362,7 @@ class ImportPoseFrame(ConfigReader, PopUpMixin):
|
|
|
362
362
|
data_path=self.data_dir_select.folder_path))
|
|
363
363
|
|
|
364
364
|
elif data_type_choice == "TRK (multi-animal APT)":
|
|
365
|
-
self.data_dir_select = FolderSelect(self.data_dir_frm, "TRK APT DIRECTORY: ", lblwidth=25)
|
|
365
|
+
self.data_dir_select = FolderSelect(self.data_dir_frm, "TRK APT DIRECTORY: ", lblwidth=25, lbl_icon='folder')
|
|
366
366
|
self.instructions_lbl = Label(self.data_dir_frm, text="Please import videos before importing the \n multi animal TRK tracking data", font=Formats.FONT_REGULAR.value,)
|
|
367
367
|
self.run_btn = Button(self.import_frm, text="IMPORT APT .TRK", font=Formats.FONT_REGULAR.value, fg="blue", command=lambda: self.__multi_animal_run_call(pose_estimation_tool=data_type_choice,
|
|
368
368
|
interpolation_settings=self.interpolation_dropdown.getChoices(),
|
|
@@ -372,7 +372,7 @@ class ImportPoseFrame(ConfigReader, PopUpMixin):
|
|
|
372
372
|
data_path=self.data_dir_select.folder_path))
|
|
373
373
|
|
|
374
374
|
elif data_type_choice == "CSV (SLEAP)":
|
|
375
|
-
self.data_dir_select = FolderSelect(self.data_dir_frm, "CSV SLEAP DIRECTORY:", lblwidth=25)
|
|
375
|
+
self.data_dir_select = FolderSelect(self.data_dir_frm, "CSV SLEAP DIRECTORY:", lblwidth=25, lbl_icon='folder')
|
|
376
376
|
self.instructions_lbl = Label(self.data_dir_frm, font=Formats.FONT_REGULAR.value, text="Please import videos before importing the SLEAP tracking data \n IF you are tracking more than ONE animal")
|
|
377
377
|
self.run_btn = Button(self.import_frm, text="IMPORT SLEAP .CSV", fg="blue", font=Formats.FONT_REGULAR.value, command=lambda: self.__multi_animal_run_call(pose_estimation_tool=data_type_choice,
|
|
378
378
|
interpolation_settings=self.interpolation_dropdown.getChoices(),
|
|
@@ -382,7 +382,7 @@ class ImportPoseFrame(ConfigReader, PopUpMixin):
|
|
|
382
382
|
data_path=self.data_dir_select.folder_path))
|
|
383
383
|
|
|
384
384
|
elif data_type_choice == "H5 (SLEAP)":
|
|
385
|
-
self.data_dir_select = FolderSelect(self.data_dir_frm, "H5 SLEAP DIRECTORY", lblwidth=25)
|
|
385
|
+
self.data_dir_select = FolderSelect(self.data_dir_frm, "H5 SLEAP DIRECTORY", lblwidth=25, lbl_icon='folder')
|
|
386
386
|
self.instructions_lbl = Label(self.data_dir_frm, font=Formats.FONT_REGULAR.value, text="Please import videos before importing the SLEAP H5 tracking data \n IF you are tracking more than ONE animal")
|
|
387
387
|
self.run_btn = Button(self.import_frm, text="IMPORT SLEAP H5", font=Formats.FONT_REGULAR.value, fg="blue", command=lambda: self.__multi_animal_run_call(pose_estimation_tool=data_type_choice,
|
|
388
388
|
interpolation_settings=self.interpolation_dropdown.getChoices(),
|
|
@@ -391,7 +391,7 @@ class ImportPoseFrame(ConfigReader, PopUpMixin):
|
|
|
391
391
|
animal_names=self.animal_name_entry_boxes,
|
|
392
392
|
data_path=self.data_dir_select.folder_path))
|
|
393
393
|
elif data_type_choice == "H5 (SuperAnimal-TopView)":
|
|
394
|
-
self.data_dir_select = FolderSelect(self.data_dir_frm, "H5 SuperAnimal DIRECTORY:", lblwidth=25)
|
|
394
|
+
self.data_dir_select = FolderSelect(self.data_dir_frm, "H5 SuperAnimal DIRECTORY:", lblwidth=25, lbl_icon='folder')
|
|
395
395
|
self.instructions_lbl = Label(self.data_dir_frm, font=Formats.FONT_REGULAR.value, text="Please import videos before importing the H5 SuperAnimal tracking data \n IF you are tracking more than ONE animal")
|
|
396
396
|
self.run_btn = Button(self.import_frm, text="IMPORT SuperAnimal-Mouse-TopView H5", fg="blue", font=Formats.FONT_REGULAR.value, command=lambda: self.__multi_animal_run_call(pose_estimation_tool=data_type_choice,
|
|
397
397
|
interpolation_settings=self.interpolation_dropdown.getChoices(),
|
|
@@ -57,7 +57,7 @@ class SklearnVisualizationPopUp(PopUpMixin, ConfigReader):
|
|
|
57
57
|
self.bp_threshold_lbl.grid(row=0, column=0, sticky=NW)
|
|
58
58
|
self.bp_threshold_entry.grid(row=1, column=0, sticky=NW)
|
|
59
59
|
|
|
60
|
-
self.style_settings_frm = CreateLabelFrameWithIcon(parent=self.main_frm, header="SETTINGS", icon_name='style', icon_link=Links.SKLEARN_PLOTS.value, padx=5, pady=5, relief='solid')
|
|
60
|
+
self.style_settings_frm = CreateLabelFrameWithIcon(parent=self.main_frm, header="TEXT SETTINGS", icon_name='style', icon_link=Links.SKLEARN_PLOTS.value, padx=5, pady=5, relief='solid')
|
|
61
61
|
self.text_size_dropdown = SimBADropDown(parent=self.style_settings_frm, dropdown_options=TEXT_SIZE_OPTIONS, label='TEXT SIZE: ', label_width=40, dropdown_width=15, value='AUTO', img='text')
|
|
62
62
|
self.text_spacing_dropdown = SimBADropDown(parent=self.style_settings_frm, dropdown_options=TEXT_SIZE_OPTIONS, label='TEXT SPACING: ', label_width=40, dropdown_width=15, value='AUTO', img='text_spacing')
|
|
63
63
|
self.text_thickness_dropdown = SimBADropDown(parent=self.style_settings_frm, dropdown_options=TEXT_SIZE_OPTIONS, label='TEXT THICKNESS: ', label_width=40, dropdown_width=15, value='AUTO', img='bold')
|
|
@@ -8,8 +8,8 @@ from simba.mixins.config_reader import ConfigReader
|
|
|
8
8
|
from simba.mixins.pop_up_mixin import PopUpMixin
|
|
9
9
|
from simba.model.inference_batch import InferenceBatch
|
|
10
10
|
from simba.ui.tkinter_functions import (CreateLabelFrameWithIcon, Entry_Box,
|
|
11
|
-
FileSelect, SimbaButton,
|
|
12
|
-
|
|
11
|
+
FileSelect, SimbaButton, SimBALabel,
|
|
12
|
+
SimBASeperator)
|
|
13
13
|
from simba.utils.checks import (check_file_exist_and_readable, check_float,
|
|
14
14
|
check_int)
|
|
15
15
|
from simba.utils.enums import ConfigKey, Dtypes, Formats, Keys, Links
|