simba-uw-tf-dev 4.6.2__py3-none-any.whl → 4.7.1__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/.recent_projects.txt +1 -0
- simba/assets/lookups/tooptips.json +6 -1
- simba/data_processors/agg_clf_counter_mp.py +52 -53
- simba/data_processors/blob_location_computer.py +1 -1
- simba/data_processors/circling_detector.py +30 -13
- simba/data_processors/cuda/geometry.py +45 -27
- simba/data_processors/cuda/image.py +1648 -1598
- simba/data_processors/cuda/statistics.py +72 -26
- simba/data_processors/cuda/timeseries.py +1 -1
- simba/data_processors/cue_light_analyzer.py +5 -9
- simba/data_processors/egocentric_aligner.py +25 -7
- simba/data_processors/freezing_detector.py +55 -47
- simba/data_processors/kleinberg_calculator.py +61 -29
- simba/feature_extractors/feature_subsets.py +14 -7
- simba/feature_extractors/mitra_feature_extractor.py +2 -2
- simba/feature_extractors/straub_tail_analyzer.py +4 -6
- simba/labelling/standard_labeller.py +1 -1
- simba/mixins/config_reader.py +5 -2
- simba/mixins/geometry_mixin.py +22 -36
- simba/mixins/image_mixin.py +24 -28
- simba/mixins/plotting_mixin.py +28 -10
- simba/mixins/statistics_mixin.py +48 -11
- simba/mixins/timeseries_features_mixin.py +1 -1
- simba/mixins/train_model_mixin.py +67 -29
- simba/model/inference_batch.py +1 -1
- simba/model/yolo_seg_inference.py +3 -3
- simba/outlier_tools/skip_outlier_correction.py +1 -1
- simba/plotting/ROI_feature_visualizer_mp.py +3 -5
- simba/plotting/clf_validator_mp.py +4 -5
- simba/plotting/cue_light_visualizer.py +6 -7
- simba/plotting/directing_animals_visualizer_mp.py +2 -3
- simba/plotting/distance_plotter_mp.py +378 -378
- simba/plotting/gantt_creator.py +29 -10
- simba/plotting/gantt_creator_mp.py +96 -33
- simba/plotting/geometry_plotter.py +270 -272
- simba/plotting/heat_mapper_clf_mp.py +4 -6
- simba/plotting/heat_mapper_location_mp.py +2 -2
- simba/plotting/light_dark_box_plotter.py +2 -2
- simba/plotting/path_plotter_mp.py +26 -29
- simba/plotting/plot_clf_results_mp.py +455 -454
- simba/plotting/pose_plotter_mp.py +28 -29
- simba/plotting/probability_plot_creator_mp.py +288 -288
- simba/plotting/roi_plotter_mp.py +31 -31
- simba/plotting/single_run_model_validation_video_mp.py +427 -427
- simba/plotting/spontaneous_alternation_plotter.py +2 -3
- simba/plotting/yolo_pose_track_visualizer.py +32 -27
- simba/plotting/yolo_pose_visualizer.py +35 -36
- simba/plotting/yolo_seg_visualizer.py +2 -3
- simba/pose_importers/simba_blob_importer.py +3 -3
- simba/roi_tools/roi_aggregate_stats_mp.py +5 -4
- simba/roi_tools/roi_clf_calculator_mp.py +4 -4
- simba/sandbox/analyze_runtimes.py +30 -0
- simba/sandbox/cuda/egocentric_rotator.py +374 -374
- simba/sandbox/get_cpu_pool.py +5 -0
- simba/sandbox/proboscis_to_tip.py +28 -0
- simba/sandbox/test_directionality.py +47 -0
- simba/sandbox/test_nonstatic_directionality.py +27 -0
- simba/sandbox/test_pycharm_cuda.py +51 -0
- simba/sandbox/test_simba_install.py +41 -0
- simba/sandbox/test_static_directionality.py +26 -0
- simba/sandbox/test_static_directionality_2d.py +26 -0
- simba/sandbox/verify_env.py +42 -0
- simba/third_party_label_appenders/transform/coco_keypoints_to_yolo.py +3 -3
- simba/third_party_label_appenders/transform/coco_keypoints_to_yolo_bbox.py +2 -2
- simba/ui/pop_ups/clf_plot_pop_up.py +2 -2
- simba/ui/pop_ups/fsttc_pop_up.py +27 -25
- simba/ui/pop_ups/gantt_pop_up.py +31 -6
- simba/ui/pop_ups/kleinberg_pop_up.py +39 -40
- simba/ui/pop_ups/video_processing_pop_up.py +37 -29
- simba/ui/tkinter_functions.py +3 -0
- simba/utils/custom_feature_extractor.py +1 -1
- simba/utils/data.py +90 -14
- simba/utils/enums.py +1 -0
- simba/utils/errors.py +441 -440
- simba/utils/lookups.py +1203 -1203
- simba/utils/printing.py +124 -124
- simba/utils/read_write.py +3769 -3721
- simba/utils/yolo.py +10 -1
- simba/video_processors/blob_tracking_executor.py +2 -2
- simba/video_processors/clahe_ui.py +1 -1
- simba/video_processors/egocentric_video_rotator.py +44 -41
- simba/video_processors/multi_cropper.py +1 -1
- simba/video_processors/video_processing.py +5264 -5222
- simba/video_processors/videos_to_frames.py +43 -33
- {simba_uw_tf_dev-4.6.2.dist-info → simba_uw_tf_dev-4.7.1.dist-info}/METADATA +4 -3
- {simba_uw_tf_dev-4.6.2.dist-info → simba_uw_tf_dev-4.7.1.dist-info}/RECORD +90 -80
- {simba_uw_tf_dev-4.6.2.dist-info → simba_uw_tf_dev-4.7.1.dist-info}/LICENSE +0 -0
- {simba_uw_tf_dev-4.6.2.dist-info → simba_uw_tf_dev-4.7.1.dist-info}/WHEEL +0 -0
- {simba_uw_tf_dev-4.6.2.dist-info → simba_uw_tf_dev-4.7.1.dist-info}/entry_points.txt +0 -0
- {simba_uw_tf_dev-4.6.2.dist-info → simba_uw_tf_dev-4.7.1.dist-info}/top_level.txt +0 -0
|
@@ -8,12 +8,13 @@ from simba.data_processors.kleinberg_calculator import KleinbergCalculator
|
|
|
8
8
|
from simba.mixins.config_reader import ConfigReader
|
|
9
9
|
from simba.mixins.pop_up_mixin import PopUpMixin
|
|
10
10
|
from simba.ui.tkinter_functions import (CreateLabelFrameWithIcon, Entry_Box,
|
|
11
|
-
SimbaButton, SimBADropDown)
|
|
11
|
+
SimbaButton, SimBADropDown, SimBALabel)
|
|
12
12
|
from simba.utils.checks import check_float, check_int
|
|
13
13
|
from simba.utils.enums import Formats, Keys, Links
|
|
14
14
|
from simba.utils.errors import NoChoosenClassifierError, NoDataError
|
|
15
|
-
from simba.utils.read_write import str_2_bool
|
|
15
|
+
from simba.utils.read_write import get_current_time, str_2_bool
|
|
16
16
|
|
|
17
|
+
INSTRUCTIONS_TXT = 'Results in the project_folder/csv/machine_results folder are overwritten.\n If saving the originals, the original un-smoothened data is saved in a subdirectory of \nthe project_folder/csv/machine_results folder'
|
|
17
18
|
|
|
18
19
|
class KleinbergPopUp(PopUpMixin, ConfigReader):
|
|
19
20
|
def __init__(self,
|
|
@@ -24,61 +25,59 @@ class KleinbergPopUp(PopUpMixin, ConfigReader):
|
|
|
24
25
|
raise NoDataError(msg=f'Cannot perform Kleinberg smoothing: No data files found in {self.machine_results_dir} directory', source=self.__class__.__name__)
|
|
25
26
|
PopUpMixin.__init__(self, title="APPLY KLEINBERG BEHAVIOR CLASSIFICATION SMOOTHING", icon='smooth')
|
|
26
27
|
kleinberg_settings_frm = CreateLabelFrameWithIcon(parent=self.main_frm, header="KLEINBERG SETTINGS", icon_name=Keys.DOCUMENTATION.value, icon_link=Links.KLEINBERG.value)
|
|
27
|
-
self.k_sigma = Entry_Box(kleinberg_settings_frm, fileDescription="SIGMA", img='sigma', value='2', justify='center', labelwidth=35, entry_box_width=35)
|
|
28
|
-
self.k_gamma = Entry_Box(kleinberg_settings_frm, fileDescription="GAMMA", img='gamma', value='0.3', justify='center', labelwidth=35, entry_box_width=35)
|
|
29
|
-
self.k_hierarchy = Entry_Box(kleinberg_settings_frm, fileDescription="HIERARCHY", value=1, img='hierarchy_2', justify='center', labelwidth=35, entry_box_width=35)
|
|
30
|
-
self.h_search_dropdown = SimBADropDown(parent=kleinberg_settings_frm, dropdown_options=['TRUE', 'FALSE'], label="
|
|
28
|
+
self.k_sigma = Entry_Box(kleinberg_settings_frm, fileDescription="SIGMA", img='sigma', value='2', justify='center', labelwidth=35, entry_box_width=35, tooltip_key='KLEINBERG_SIGMA')
|
|
29
|
+
self.k_gamma = Entry_Box(kleinberg_settings_frm, fileDescription="GAMMA", img='gamma', value='0.3', justify='center', labelwidth=35, entry_box_width=35, tooltip_key='KLEINBERG_GAMMA')
|
|
30
|
+
self.k_hierarchy = Entry_Box(kleinberg_settings_frm, fileDescription="HIERARCHY", value=1, img='hierarchy_2', justify='center', labelwidth=35, entry_box_width=35, validation='numeric', tooltip_key='KLEINBERG_HIERARCHY')
|
|
31
|
+
self.h_search_dropdown = SimBADropDown(parent=kleinberg_settings_frm, dropdown_options=['TRUE', 'FALSE'], label="HIERARCHICAL SEARCH", value='FALSE', img='hierarchy', label_width=35, dropdown_width=35, tooltip_key='KLEINBERG_HIERARCHY_SEARCH')
|
|
32
|
+
self.save_originals_dropdown = SimBADropDown(parent=kleinberg_settings_frm, dropdown_options=['TRUE', 'FALSE'], label="SAVE ORIGINAL DATA:", value='TRUE', img='save', label_width=35, dropdown_width=35, tooltip_key='KLEINBERG_SAVE_ORIGINALS')
|
|
33
|
+
self.instructions_lbl = SimBALabel(parent=kleinberg_settings_frm, txt=INSTRUCTIONS_TXT, justify='center', txt_clr='blue', font=Formats.FONT_REGULAR_ITALICS.value)
|
|
31
34
|
|
|
32
35
|
|
|
33
|
-
kleinberg_table_frame =
|
|
36
|
+
kleinberg_table_frame = CreateLabelFrameWithIcon(parent=self.main_frm, header="CHOOSE CLASSIFIER(S) FOR KLEINBERG SMOOTHING", icon_name=Keys.DOCUMENTATION.value, icon_link=Links.KLEINBERG.value)
|
|
34
37
|
clf_var_dict, clf_cb_dict = {}, {}
|
|
35
38
|
for clf_cnt, clf in enumerate(self.clf_names):
|
|
36
39
|
clf_var_dict[clf] = BooleanVar()
|
|
37
40
|
clf_cb_dict[clf] = Checkbutton(kleinberg_table_frame, text=clf, font=Formats.FONT_REGULAR.value, variable=clf_var_dict[clf])
|
|
38
|
-
clf_cb_dict[clf].grid(row=clf_cnt, sticky=NW)
|
|
41
|
+
clf_cb_dict[clf].grid(row=clf_cnt, column=0, sticky=NW)
|
|
39
42
|
|
|
40
43
|
run_kleinberg_btn = SimbaButton(parent=self.main_frm, txt="APPLY KLEINBERG SMOOTHER", img='rocket', txt_clr="blue", font=Formats.FONT_REGULAR.value, cmd=self.run_kleinberg, cmd_kwargs={'behaviors_dict': lambda: clf_var_dict, 'hierarchical_search': lambda: str_2_bool(self.h_search_dropdown.get_value())})
|
|
41
|
-
kleinberg_settings_frm.grid(row=0, sticky=W,
|
|
42
|
-
self.
|
|
43
|
-
self.
|
|
44
|
-
self.
|
|
45
|
-
self.
|
|
46
|
-
|
|
47
|
-
|
|
44
|
+
kleinberg_settings_frm.grid(row=0, sticky=W, pady=(15, 0))
|
|
45
|
+
self.instructions_lbl.grid(row=0, sticky=W)
|
|
46
|
+
self.k_sigma.grid(row=1, sticky=W)
|
|
47
|
+
self.k_gamma.grid(row=2, sticky=W)
|
|
48
|
+
self.k_hierarchy.grid(row=3, sticky=W)
|
|
49
|
+
self.h_search_dropdown.grid(row=4, column=0, sticky=W)
|
|
50
|
+
self.save_originals_dropdown.grid(row=5, column=0, sticky=W)
|
|
51
|
+
kleinberg_table_frame.grid(row=1, column=0, sticky=NW, pady=(15, 0))
|
|
52
|
+
run_kleinberg_btn.grid(row=2, column=0, sticky=NW, pady=(15, 0))
|
|
48
53
|
self.main_frm.mainloop()
|
|
49
54
|
|
|
50
55
|
def run_kleinberg(self, behaviors_dict: dict, hierarchical_search: bool):
|
|
51
56
|
targets = []
|
|
52
57
|
for behaviour, behavior_val in behaviors_dict.items():
|
|
53
|
-
if behavior_val.get():
|
|
54
|
-
targets.append(behaviour)
|
|
58
|
+
if behavior_val.get(): targets.append(behaviour)
|
|
55
59
|
|
|
56
60
|
if len(targets) == 0:
|
|
57
61
|
raise NoChoosenClassifierError(source=self.__class__.__name__)
|
|
58
62
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
sigma=self.k_sigma.entry_get,
|
|
78
|
-
gamma=self.k_gamma.entry_get,
|
|
79
|
-
hierarchy=self.k_hierarchy.entry_get,
|
|
80
|
-
hierarchical_search=hierarchical_search,
|
|
81
|
-
)
|
|
63
|
+
k_hierarchy = self.k_hierarchy.entry_get
|
|
64
|
+
k_sigma = self.k_sigma.entry_get
|
|
65
|
+
k_gamma = self.k_gamma.entry_get
|
|
66
|
+
save_originals = str_2_bool(self.save_originals_dropdown.get_value())
|
|
67
|
+
|
|
68
|
+
check_int(name="Hierarchy", value=k_hierarchy, min_value=1, allow_negative=False, allow_zero=False, raise_error=True)
|
|
69
|
+
check_float(name="Sigma", value=k_sigma, allow_negative=False, allow_zero=False, raise_error=True)
|
|
70
|
+
check_float(name="Gamma", value=k_gamma, allow_negative=False, allow_zero=False, raise_error=True)
|
|
71
|
+
|
|
72
|
+
print(f"[{get_current_time()}] Applying kleinberg hyperparameter Setting: Sigma: {k_sigma}, Gamma: {k_gamma}, Hierarchy: {k_hierarchy}")
|
|
73
|
+
|
|
74
|
+
kleinberg_analyzer = KleinbergCalculator(config_path=self.config_path,
|
|
75
|
+
classifier_names=targets,
|
|
76
|
+
sigma=float(k_sigma),
|
|
77
|
+
gamma=float(k_gamma),
|
|
78
|
+
hierarchy=int(k_hierarchy),
|
|
79
|
+
hierarchical_search=hierarchical_search,
|
|
80
|
+
save_originals=save_originals)
|
|
82
81
|
kleinberg_analyzer.run()
|
|
83
82
|
|
|
84
83
|
|
|
@@ -24,7 +24,7 @@ from simba.ui.tkinter_functions import (CreateLabelFrameWithIcon,
|
|
|
24
24
|
FileSelect, FolderSelect, SimbaButton,
|
|
25
25
|
SimbaCheckbox, SimBADropDown,
|
|
26
26
|
SimBALabel, SimBARadioButton,
|
|
27
|
-
SimBAScaleBar)
|
|
27
|
+
SimBAScaleBar, SimBASeperator)
|
|
28
28
|
from simba.utils.checks import (check_ffmpeg_available,
|
|
29
29
|
check_file_exist_and_readable,
|
|
30
30
|
check_if_dir_exists,
|
|
@@ -1505,23 +1505,23 @@ class ClipMultipleVideosByFrameNumbersPopUp(PopUpMixin):
|
|
|
1505
1505
|
max_video_name_len = len(max(list(self.video_paths.keys())))
|
|
1506
1506
|
super().__init__(title="CLIP MULTIPLE VIDEOS BY FRAME NUMBERS", icon='clip')
|
|
1507
1507
|
self.save_dir = save_dir
|
|
1508
|
+
padx = (0, 25)
|
|
1508
1509
|
data_frm = CreateLabelFrameWithIcon(parent=self.main_frm, header="VIDEO SETTINGS", icon_name=Keys.DOCUMENTATION.value, icon_link=Links.VIDEO_TOOLS.value)
|
|
1509
|
-
data_frm.grid(row=
|
|
1510
|
-
|
|
1511
|
-
|
|
1510
|
+
SimBALabel(parent=data_frm, font=Formats.FONT_REGULAR.value, txt="VIDEO NAME", justify='center', img='video').grid(row=0, column=0, padx=padx, sticky=NW)
|
|
1511
|
+
SimBALabel(parent=data_frm, font=Formats.FONT_REGULAR.value, txt="START FRAME", justify='center', img='play').grid(row=0, column=2, padx=padx)
|
|
1512
|
+
SimBALabel(parent=data_frm, font=Formats.FONT_REGULAR.value, txt="END FRAME", justify='center', img='stop').grid(row=0, column=3, padx=padx)
|
|
1512
1513
|
|
|
1513
|
-
|
|
1514
|
-
|
|
1515
|
-
SimBALabel(parent=data_frm, width=max_video_name_len+20, font=Formats.FONT_REGULAR.value, txt="END FRAME", justify='center').grid(row=0, column=3)
|
|
1514
|
+
seperator = SimBASeperator(parent=data_frm, color=None, orient='horizontal', borderwidth=1)
|
|
1515
|
+
seperator.grid(row=1, column=0, columnspan=4, rowspan=1, sticky="ew")
|
|
1516
1516
|
|
|
1517
1517
|
self.entry_boxes = {}
|
|
1518
1518
|
for cnt, video_name in enumerate(self.video_paths.keys()):
|
|
1519
1519
|
self.entry_boxes[video_name] = {}
|
|
1520
|
-
SimBALabel(parent=data_frm,
|
|
1521
|
-
self.entry_boxes[video_name]["start"] = Entry_Box(data_frm, "", 0, validation="numeric")
|
|
1522
|
-
self.entry_boxes[video_name]["end"] = Entry_Box(data_frm, "", 0, validation="numeric")
|
|
1523
|
-
self.entry_boxes[video_name]["start"].grid(row=cnt +
|
|
1524
|
-
self.entry_boxes[video_name]["end"].grid(row=cnt +
|
|
1520
|
+
SimBALabel(parent=data_frm, font=Formats.FONT_REGULAR.value, txt=video_name + f' (frames: { self.video_meta_data[cnt]})', justify='left').grid(row=cnt + 2, column=0, padx=padx, sticky=NW)
|
|
1521
|
+
self.entry_boxes[video_name]["start"] = Entry_Box(data_frm, fileDescription="", labelwidth=0, validation="numeric", justify='center')
|
|
1522
|
+
self.entry_boxes[video_name]["end"] = Entry_Box(data_frm, fileDescription="", labelwidth=0, validation="numeric", justify='center')
|
|
1523
|
+
self.entry_boxes[video_name]["start"].grid(row=cnt + 2, column=2, sticky=NW, padx=padx)
|
|
1524
|
+
self.entry_boxes[video_name]["end"].grid(row=cnt + 2, column=3, sticky=NW, padx=padx)
|
|
1525
1525
|
|
|
1526
1526
|
gpu_state = NORMAL if check_nvidea_gpu_available(raise_error=False) else DISABLED
|
|
1527
1527
|
batch_settings_frm = CreateLabelFrameWithIcon(parent=self.main_frm, header="BATCH SETTINGS", icon_name=Keys.DOCUMENTATION.value, icon_link=Links.VIDEO_TOOLS.value)
|
|
@@ -1531,6 +1531,7 @@ class ClipMultipleVideosByFrameNumbersPopUp(PopUpMixin):
|
|
|
1531
1531
|
batch_end_btn = SimbaButton(parent=batch_settings_frm, txt='SET', img='tick', cmd=self._batch_set_val, cmd_kwargs={'text': lambda: batch_end_entry.entry_get.strip(), 'box_type': lambda: 'end'})
|
|
1532
1532
|
self.gpu_dropdown = SimBADropDown(parent=batch_settings_frm, dropdown_options=['TRUE', 'FALSE'], label='USE GPU:', label_width=25, dropdown_width=12, tooltip_key='USE_GPU', img='gpu_3', state=gpu_state, value='FALSE')
|
|
1533
1533
|
self.quality_dropdown = SimBADropDown(parent=batch_settings_frm, dropdown_options=list(range(10, 110, 10)), label='OUT VIDEO QUALITY:', label_width=25, dropdown_width=12, tooltip_key='OUTPUT_VIDEO_QUALITY', img='pct_2', value=60)
|
|
1534
|
+
data_frm.grid(row=2, column=0, sticky=NW)
|
|
1534
1535
|
batch_settings_frm.grid(row=1, column=0, sticky=NW)
|
|
1535
1536
|
batch_start_entry.grid(row=0, column=0, sticky=NW)
|
|
1536
1537
|
batch_start_btn.grid(row=0, column=1, sticky=NW)
|
|
@@ -1590,6 +1591,9 @@ class ClipMultipleVideosByFrameNumbersPopUp(PopUpMixin):
|
|
|
1590
1591
|
|
|
1591
1592
|
|
|
1592
1593
|
#ClipMultipleVideosByFrameNumbersPopUp(data_dir=r'E:\netholabs_videos\terry\mp4s\4_02_001_exp_2025_12_02_15_22_00\videos\Camera2', save_dir=r'E:\netholabs_videos\terry\mp4s\4_02_001_exp_2025_12_02_15_22_00\videos\Camera2\test')
|
|
1594
|
+
#ClipMultipleVideosByFrameNumbersPopUp(data_dir=r"E:\maplight_videos\test_0126", save_dir=r"E:\maplight_videos\clip_test")
|
|
1595
|
+
|
|
1596
|
+
|
|
1593
1597
|
|
|
1594
1598
|
class InitiateClipMultipleVideosByFrameNumbersPopUp(PopUpMixin):
|
|
1595
1599
|
def __init__(self):
|
|
@@ -1636,6 +1640,8 @@ class InitiateClipMultipleVideosByFrameNumbersPopUp(PopUpMixin):
|
|
|
1636
1640
|
)
|
|
1637
1641
|
|
|
1638
1642
|
|
|
1643
|
+
|
|
1644
|
+
|
|
1639
1645
|
class ClipMultipleVideosByTimestamps(PopUpMixin):
|
|
1640
1646
|
"""
|
|
1641
1647
|
:example:
|
|
@@ -1648,13 +1654,12 @@ class ClipMultipleVideosByTimestamps(PopUpMixin):
|
|
|
1648
1654
|
check_if_dir_exists(in_dir=save_dir, source=self.__class__.__name__, create_if_not_exist=True)
|
|
1649
1655
|
self.video_paths = find_all_videos_in_directory(directory=data_dir, as_dict=True, raise_error=True)
|
|
1650
1656
|
self.video_meta_data = [get_video_meta_data(video_path=x) for x in list(self.video_paths.values())]
|
|
1651
|
-
max_video_name_len = len(max(list(self.video_paths.keys()))) + 25
|
|
1652
1657
|
super().__init__(title="CLIP MULTIPLE VIDEOS BY TIME-STAMPS", icon='clip')
|
|
1653
1658
|
self.save_dir = save_dir
|
|
1654
1659
|
batch_settings_frm = CreateLabelFrameWithIcon(parent=self.main_frm, header="BATCH SETTINGS", icon_name=Keys.DOCUMENTATION.value, icon_link=Links.VIDEO_TOOLS.value)
|
|
1655
|
-
batch_start_entry = Entry_Box(parent=batch_settings_frm, fileDescription='START TIME (HH:MM:SS):', labelwidth=25, entry_box_width=12, img='play')
|
|
1660
|
+
batch_start_entry = Entry_Box(parent=batch_settings_frm, fileDescription='START TIME (HH:MM:SS):', labelwidth=25, entry_box_width=12, img='play', justify='center')
|
|
1656
1661
|
batch_start_btn = SimbaButton(parent=batch_settings_frm, txt='SET', img='tick', cmd=self._batch_set_val, cmd_kwargs={'text': lambda: batch_start_entry.entry_get.strip(), 'box_type': lambda: 'start'})
|
|
1657
|
-
batch_end_entry = Entry_Box(parent=batch_settings_frm, fileDescription='END TIME (HH:MM:SS):', labelwidth=25, entry_box_width=12, img='stop')
|
|
1662
|
+
batch_end_entry = Entry_Box(parent=batch_settings_frm, fileDescription='END TIME (HH:MM:SS):', labelwidth=25, entry_box_width=12, img='stop', justify='center')
|
|
1658
1663
|
batch_end_btn = SimbaButton(parent=batch_settings_frm, txt='SET', img='tick', cmd=self._batch_set_val, cmd_kwargs={'text': lambda: batch_end_entry.entry_get.strip(), 'box_type': lambda: 'end'})
|
|
1659
1664
|
batch_settings_frm.grid(row=0, column=0, sticky=NW)
|
|
1660
1665
|
batch_start_entry.grid(row=0, column=0, sticky=NW)
|
|
@@ -1667,25 +1672,28 @@ class ClipMultipleVideosByTimestamps(PopUpMixin):
|
|
|
1667
1672
|
self.gpu_dropdown.grid(row=2, column=0, sticky=NW)
|
|
1668
1673
|
self.quality_dropdown.grid(row=3, column=0, sticky=NW)
|
|
1669
1674
|
self.save_dir = save_dir
|
|
1675
|
+
padx = (0, 30)
|
|
1670
1676
|
data_frm = CreateLabelFrameWithIcon(parent=self.main_frm, header="VIDEO SETTINGS", icon_name='video', icon_link=Links.VIDEO_TOOLS.value)
|
|
1671
|
-
|
|
1672
|
-
|
|
1673
|
-
|
|
1674
|
-
|
|
1675
|
-
|
|
1677
|
+
|
|
1678
|
+
SimBALabel(data_frm, txt="VIDEO NAME", justify='center', font=Formats.FONT_REGULAR_BOLD.value, img='video').grid(row=0, column=0, sticky=NW, padx=padx)
|
|
1679
|
+
SimBALabel(data_frm, txt="VIDEO LENGTH", justify='center', font=Formats.FONT_REGULAR_BOLD.value, img='timer_2').grid(row=0, column=1, sticky=NW, padx=padx)
|
|
1680
|
+
SimBALabel(data_frm, txt="START TIME (HH:MM:SS)", justify='center', font=Formats.FONT_REGULAR_BOLD.value, img='play').grid(row=0, column=2, sticky=NW, padx=padx)
|
|
1681
|
+
SimBALabel(data_frm, txt="END TIME (HH:MM:SS)", justify='center', font=Formats.FONT_REGULAR_BOLD.value, img='stop').grid(row=0, column=3, sticky=NW, padx=padx)
|
|
1682
|
+
seperator = SimBASeperator(parent=data_frm, color=None, orient='horizontal', borderwidth=1)
|
|
1683
|
+
seperator.grid(row=1, column=0, columnspan=4, rowspan=1, sticky="ew")
|
|
1676
1684
|
|
|
1677
1685
|
self.entry_boxes = {}
|
|
1678
1686
|
for cnt, video_name in enumerate(self.video_paths.keys()):
|
|
1679
1687
|
self.entry_boxes[video_name] = {}
|
|
1680
|
-
SimBALabel(parent=data_frm, txt=video_name,
|
|
1688
|
+
SimBALabel(parent=data_frm, txt=video_name, justify='center').grid(row=cnt + 2, column=0, sticky=NW, padx=padx)
|
|
1681
1689
|
video_length = self.video_meta_data[cnt]["video_length_s"]
|
|
1682
1690
|
video_length_hhmmss = seconds_to_timestamp(seconds=video_length)
|
|
1683
|
-
|
|
1684
|
-
self.entry_boxes[video_name]["start"] = Entry_Box(data_frm,
|
|
1685
|
-
self.entry_boxes[video_name]["end"] = Entry_Box(data_frm, "",
|
|
1686
|
-
self.entry_boxes[video_name]["start"].grid(row=cnt +
|
|
1687
|
-
self.entry_boxes[video_name]["end"].grid(row=cnt +
|
|
1688
|
-
|
|
1691
|
+
SimBALabel(data_frm, txt=video_length_hhmmss, justify='center').grid(row=cnt + 2, column=1, sticky=NW, padx=padx)
|
|
1692
|
+
self.entry_boxes[video_name]["start"] = Entry_Box(data_frm, fileDescription="", labelwidth=0, justify='center')
|
|
1693
|
+
self.entry_boxes[video_name]["end"] = Entry_Box(data_frm, fileDescription="", labelwidth=0, justify='center')
|
|
1694
|
+
self.entry_boxes[video_name]["start"].grid(row=cnt + 2, column=2, sticky=NW, padx=padx)
|
|
1695
|
+
self.entry_boxes[video_name]["end"].grid(row=cnt + 2, column=3, sticky=NW, padx=padx)
|
|
1696
|
+
data_frm.grid(row=1, column=0, sticky=NW)
|
|
1689
1697
|
self.create_run_frm(run_function=self.run, btn_txt_clr="blue")
|
|
1690
1698
|
self.main_frm.mainloop()
|
|
1691
1699
|
|
|
@@ -1708,11 +1716,11 @@ class ClipMultipleVideosByTimestamps(PopUpMixin):
|
|
|
1708
1716
|
check_that_hhmmss_start_is_before_end(start_time=start, end_time=end, name=video_name)
|
|
1709
1717
|
check_if_hhmmss_timestamp_is_valid_part_of_video(timestamp=start, video_path=self.video_paths[video_name])
|
|
1710
1718
|
check_if_hhmmss_timestamp_is_valid_part_of_video(timestamp=end, video_path=self.video_paths[video_name])
|
|
1711
|
-
clip_video_in_range(file_path=self.video_paths[video_name], start_time=start, end_time=end, out_dir=self.save_dir, overwrite=True, include_clip_time_in_filename=False, gpu=gpu, quality=quality_pct)
|
|
1719
|
+
clip_video_in_range(file_path=self.video_paths[video_name], start_time=start, end_time=end, out_dir=self.save_dir, overwrite=True, include_clip_time_in_filename=False, gpu=gpu, quality=quality_pct, codec='libx264', verbose=True)
|
|
1712
1720
|
timer.stop_timer()
|
|
1713
1721
|
stdout_success(msg=f"{len(self.entry_boxes)} videos clipped by time-stamps and saved in {self.save_dir}", elapsed_time=timer.elapsed_time_str,)
|
|
1714
1722
|
|
|
1715
|
-
#ClipMultipleVideosByTimestamps(data_dir=r"
|
|
1723
|
+
#ClipMultipleVideosByTimestamps(data_dir=r"E:\maplight_videos\test_0126", save_dir=r"E:\maplight_videos\clip_test")
|
|
1716
1724
|
|
|
1717
1725
|
|
|
1718
1726
|
class InitiateClipMultipleVideosByTimestampsPopUp(PopUpMixin):
|
simba/ui/tkinter_functions.py
CHANGED
|
@@ -229,6 +229,7 @@ class Entry_Box(Frame):
|
|
|
229
229
|
value: Optional[Any] = None,
|
|
230
230
|
label_font: tuple = Formats.FONT_REGULAR.value,
|
|
231
231
|
entry_font: tuple = Formats.FONT_REGULAR.value,
|
|
232
|
+
tooltip_key: Optional[str] = None,
|
|
232
233
|
justify: Literal["left", "center", "right"] = 'left',
|
|
233
234
|
cmd: Optional[Callable] = None,
|
|
234
235
|
**kw):
|
|
@@ -249,6 +250,8 @@ class Entry_Box(Frame):
|
|
|
249
250
|
self.filePath = StringVar()
|
|
250
251
|
self.lblName = Label(self, text=fileDescription, width=labelwidth, anchor=W, font=label_font, bg=label_bg_clr)
|
|
251
252
|
self.lblName.grid(row=0, column=1)
|
|
253
|
+
if tooltip_key in TOOLTIPS.keys():
|
|
254
|
+
CreateToolTip(widget=self.lblName, text=TOOLTIPS[tooltip_key])
|
|
252
255
|
if not entry_box_width:
|
|
253
256
|
self.entPath = Entry(self, textvariable=self.filePath, state=self.status, validate="key", validatecommand=self.validation_methods.get(validation, None), font=entry_font, justify=justify, bg=entry_box_clr)
|
|
254
257
|
else:
|
|
@@ -30,7 +30,7 @@ class CustomFeatureExtractor(ConfigReader):
|
|
|
30
30
|
4. Handle cases of multiple classes and missing configuration arguments.
|
|
31
31
|
5. Invokes the feature extraction process if conditions are met.
|
|
32
32
|
|
|
33
|
-
..
|
|
33
|
+
.. note::
|
|
34
34
|
|
|
35
35
|
`Tutorial <https://github.com/sgoldenlab/simba/blob/master/docs/extractFeatures.md>`_.
|
|
36
36
|
|
simba/utils/data.py
CHANGED
|
@@ -5,6 +5,7 @@ import configparser
|
|
|
5
5
|
import gc
|
|
6
6
|
import io
|
|
7
7
|
import os
|
|
8
|
+
import platform
|
|
8
9
|
import subprocess
|
|
9
10
|
from copy import deepcopy
|
|
10
11
|
from datetime import datetime
|
|
@@ -38,15 +39,18 @@ from simba.utils.checks import (check_file_exist_and_readable, check_float,
|
|
|
38
39
|
check_if_valid_rgb_tuple, check_instance,
|
|
39
40
|
check_int, check_str, check_that_column_exist,
|
|
40
41
|
check_that_hhmmss_start_is_before_end,
|
|
41
|
-
check_valid_array,
|
|
42
|
-
check_valid_dataframe,
|
|
43
|
-
|
|
42
|
+
check_valid_array, check_valid_boolean,
|
|
43
|
+
check_valid_cpu_pool, check_valid_dataframe,
|
|
44
|
+
check_valid_lst)
|
|
45
|
+
from simba.utils.enums import (OS, ConfigKey, Defaults, Dtypes, Formats, Keys,
|
|
46
|
+
Options)
|
|
44
47
|
from simba.utils.errors import (BodypartColumnNotFoundError, CountError,
|
|
45
48
|
InvalidFileTypeError, InvalidInputError,
|
|
46
49
|
NoFilesFoundError, NoROIDataError,
|
|
47
50
|
SimBAModuleNotFoundError)
|
|
48
51
|
from simba.utils.printing import stdout_success, stdout_warning
|
|
49
|
-
from simba.utils.read_write import (
|
|
52
|
+
from simba.utils.read_write import (find_core_cnt, find_video_of_file,
|
|
53
|
+
get_current_time, get_fn_ext,
|
|
50
54
|
get_video_meta_data, read_config_entry,
|
|
51
55
|
read_config_file, read_df,
|
|
52
56
|
read_project_path_and_file_type,
|
|
@@ -1781,8 +1785,8 @@ def fft_lowpass_filter(data: np.ndarray, cut_off: float = 0.1) -> np.ndarray:
|
|
|
1781
1785
|
|
|
1782
1786
|
:example:
|
|
1783
1787
|
>>> from simba.utils.read_write import read_df
|
|
1784
|
-
>>> IN_PATH = r"C
|
|
1785
|
-
>>> OUT_PATH = r"C
|
|
1788
|
+
>>> IN_PATH = r"C:/troubleshooting/RAT_NOR/project_folder/csv/outlier_corrected_movement_location/2022-06-20_NOB_DOT_4.csv"
|
|
1789
|
+
>>> OUT_PATH = r"C:/troubleshooting/RAT_NOR/project_folder/csv/outlier_corrected_movement_location/2022-06-20_NOB_DOT_4_filtered.csv"
|
|
1786
1790
|
>>> df = read_df(file_path=IN_PATH)
|
|
1787
1791
|
>>> data = df.values
|
|
1788
1792
|
>>> x = fft_lowpass_filter(data=data, cut_off=0.1)
|
|
@@ -1813,33 +1817,105 @@ def fft_lowpass_filter(data: np.ndarray, cut_off: float = 0.1) -> np.ndarray:
|
|
|
1813
1817
|
return results.astype(data.dtype)
|
|
1814
1818
|
|
|
1815
1819
|
|
|
1816
|
-
def terminate_cpu_pool(pool:
|
|
1817
|
-
force: bool = False
|
|
1820
|
+
def terminate_cpu_pool(pool: multiprocessing.pool.Pool,
|
|
1821
|
+
force: bool = False,
|
|
1822
|
+
verbose: bool = True,
|
|
1823
|
+
source: Optional[str] = None) -> None:
|
|
1818
1824
|
"""
|
|
1819
|
-
Safely terminates a multiprocessing.Pool instance.
|
|
1825
|
+
Safely terminates a multiprocessing.Pool instance with optional graceful shutdown.
|
|
1820
1826
|
|
|
1821
|
-
|
|
1822
|
-
|
|
1823
|
-
|
|
1827
|
+
.. note::
|
|
1828
|
+
If pool is None or invalid, function returns without action. Exceptions during termination are silently caught.
|
|
1829
|
+
|
|
1830
|
+
:param multiprocessing.pool.Pool pool: The multiprocessing pool to terminate. If None, function returns without action.
|
|
1831
|
+
:param bool force: If True, skips graceful shutdown (close/join) and immediately terminates. Default: False.
|
|
1832
|
+
:param bool verbose: If True, prints termination message with timestamp. Default: True.
|
|
1833
|
+
:param Optional[str] source: Optional identifier string for logging purposes (e.g., 'VideoProcessor'). Default: None.
|
|
1824
1834
|
|
|
1825
1835
|
:example:
|
|
1826
1836
|
>>> import multiprocessing
|
|
1827
1837
|
>>> pool = multiprocessing.Pool(4)
|
|
1828
|
-
>>> terminate_cpu_pool(pool)
|
|
1838
|
+
>>> terminate_cpu_pool(pool=pool, force=False, verbose=True, source='FeatureExtractor')
|
|
1829
1839
|
"""
|
|
1830
1840
|
if pool is None:
|
|
1831
1841
|
return
|
|
1832
|
-
check_valid_cpu_pool(value=pool, source=terminate_cpu_pool.__name__, raise_error=
|
|
1842
|
+
if not check_valid_cpu_pool(value=pool, source=terminate_cpu_pool.__name__, raise_error=False):
|
|
1843
|
+
return
|
|
1833
1844
|
try:
|
|
1845
|
+
core_cnt = pool._processes if hasattr(pool, '_processes') else None
|
|
1834
1846
|
if not force:
|
|
1835
1847
|
pool.close()
|
|
1836
1848
|
pool.join()
|
|
1837
1849
|
pool.terminate()
|
|
1850
|
+
if verbose: print(f'[{get_current_time()}] {"" if source is None else f"{core_cnt} core"} SimBA CPU pool {"" if source is None else source} terminated.')
|
|
1838
1851
|
except (ValueError, AssertionError, AttributeError):
|
|
1839
1852
|
pass
|
|
1840
1853
|
gc.collect()
|
|
1841
1854
|
|
|
1842
1855
|
|
|
1856
|
+
|
|
1857
|
+
def get_cpu_pool(core_cnt: int = -1,
|
|
1858
|
+
maxtasksperchild: int = Defaults.MAXIMUM_MAX_TASK_PER_CHILD.value,
|
|
1859
|
+
context: Literal['fork', 'spawn', 'forkserver'] = None,
|
|
1860
|
+
verbose: bool = True,
|
|
1861
|
+
source: Optional[str] = None) -> multiprocessing.Pool:
|
|
1862
|
+
"""
|
|
1863
|
+
Creates and returns a multiprocessing.Pool instance with platform-appropriate defaults and validation.
|
|
1864
|
+
|
|
1865
|
+
:param int core_cnt: Number of worker processes. -1 uses all available cores. Default: -1.
|
|
1866
|
+
:param int maxtasksperchild: Maximum number of tasks a worker process can complete before being replaced. Default: From Defaults.MAXIMUM_MAX_TASK_PER_CHILD.
|
|
1867
|
+
:param Optional[Literal['fork', 'spawn', 'forkserver']] context: Multiprocessing start method. None uses platform default. Default: None.
|
|
1868
|
+
:param bool verbose: If True, prints pool creation message with timestamp. Default: True.
|
|
1869
|
+
:param Optional[str] source: Optional identifier string for logging purposes (e.g., 'VideoProcessor'). Default: None.
|
|
1870
|
+
:return: Configured multiprocessing.Pool instance.
|
|
1871
|
+
:rtype: multiprocessing.Pool
|
|
1872
|
+
|
|
1873
|
+
:example:
|
|
1874
|
+
>>> pool = get_cpu_pool(core_cnt=4, source='FeatureExtractor')
|
|
1875
|
+
>>> pool = get_cpu_pool(core_cnt=-1, context='spawn', verbose=True)
|
|
1876
|
+
>>> pool = get_cpu_pool(core_cnt=8, maxtasksperchild=100, source='VideoProcessor')
|
|
1877
|
+
"""
|
|
1878
|
+
|
|
1879
|
+
check_int(name=f'{get_cpu_pool.__name__} core_cnt', min_value=-1, unaccepted_vals=[0], value=core_cnt, raise_error=True)
|
|
1880
|
+
check_int(name=f'{get_cpu_pool.__name__} maxtasksperchild', min_value=1, value=maxtasksperchild, raise_error=True)
|
|
1881
|
+
check_valid_boolean(value=verbose, source=f'{get_cpu_pool.__name__} verbose', raise_error=True)
|
|
1882
|
+
if source is not None: check_str(name=f'{get_cpu_pool.__name__} source', value=source, raise_error=True, allow_blank=True)
|
|
1883
|
+
current_process = multiprocessing.current_process()
|
|
1884
|
+
if current_process.name != 'MainProcess': core_cnt = 1
|
|
1885
|
+
core_cnt = find_core_cnt()[0] if core_cnt == -1 or core_cnt > find_core_cnt()[0] else core_cnt
|
|
1886
|
+
if verbose: print(f'[{get_current_time()}] {core_cnt} core SimBA CPU pool {"" if source is None else source} started.')
|
|
1887
|
+
if context is not None:
|
|
1888
|
+
check_str(name=f'{get_cpu_pool.__name__} context', value=context, options=('fork', 'spawn', 'forkserver'), raise_error=True)
|
|
1889
|
+
else:
|
|
1890
|
+
existing_method = multiprocessing.get_start_method(allow_none=True)
|
|
1891
|
+
if existing_method is not None:
|
|
1892
|
+
context = existing_method
|
|
1893
|
+
else:
|
|
1894
|
+
system = platform.system()
|
|
1895
|
+
if system == OS.WINDOWS.value: context = OS.SPAWN.value
|
|
1896
|
+
elif system == OS.MAC.value: context = OS.SPAWN.value
|
|
1897
|
+
else: context = OS.FORK.value
|
|
1898
|
+
|
|
1899
|
+
if context is not None:
|
|
1900
|
+
try:
|
|
1901
|
+
ctx = multiprocessing.get_context(context)
|
|
1902
|
+
except ValueError:
|
|
1903
|
+
system = platform.system()
|
|
1904
|
+
if system == OS.WINDOWS.value: fallback_context = OS.SPAWN.value
|
|
1905
|
+
elif system == OS.MAC.value: fallback_context = OS.SPAWN.value
|
|
1906
|
+
else: fallback_context = OS.FORK.value
|
|
1907
|
+
try:
|
|
1908
|
+
ctx = multiprocessing.get_context(fallback_context)
|
|
1909
|
+
except ValueError:
|
|
1910
|
+
pool = multiprocessing.Pool(processes=core_cnt, maxtasksperchild=maxtasksperchild)
|
|
1911
|
+
return pool
|
|
1912
|
+
pool = ctx.Pool(processes=core_cnt, maxtasksperchild=maxtasksperchild)
|
|
1913
|
+
else:
|
|
1914
|
+
pool = multiprocessing.Pool(processes=core_cnt, maxtasksperchild=maxtasksperchild)
|
|
1915
|
+
return pool
|
|
1916
|
+
|
|
1917
|
+
|
|
1918
|
+
#get_cpu_pool()
|
|
1843
1919
|
# run_user_defined_feature_extraction_class(config_path='/Users/simon/Desktop/envs/troubleshooting/circular_features_zebrafish/project_folder/project_config.ini', file_path='/Users/simon/Desktop/fish_feature_extractor_2023_version_5.py')
|
|
1844
1920
|
|
|
1845
1921
|
|
simba/utils/enums.py
CHANGED
|
@@ -127,6 +127,7 @@ class OS(Enum):
|
|
|
127
127
|
LINUX = "Linux"
|
|
128
128
|
MAC = "Darwin"
|
|
129
129
|
SPAWN = 'spawn'
|
|
130
|
+
FORK = 'fork'
|
|
130
131
|
PYTHON_VER = str(f"{sys.version_info.major}.{sys.version_info.minor}")
|
|
131
132
|
try:
|
|
132
133
|
SIMBA_VERSION = pkg_resources.get_distribution("simba-uw-tf-dev").version
|