simba-uw-tf-dev 4.6.2__py3-none-any.whl → 4.7.2__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/assets/lookups/yolo_schematics/yolo_mitra.csv +9 -0
- 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 +68 -33
- simba/model/inference_batch.py +2 -2
- 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/third_party_label_appenders/transform/simba_to_yolo.py +8 -5
- 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/run_machine_models_popup.py +21 -21
- simba/ui/pop_ups/simba_to_yolo_keypoints_popup.py +2 -2
- simba/ui/pop_ups/video_processing_pop_up.py +37 -29
- simba/ui/pop_ups/yolo_inference_popup.py +1 -1
- simba/ui/pop_ups/yolo_pose_train_popup.py +1 -1
- 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 +75 -33
- simba/video_processors/videos_to_frames.py +43 -33
- {simba_uw_tf_dev-4.6.2.dist-info → simba_uw_tf_dev-4.7.2.dist-info}/METADATA +4 -3
- {simba_uw_tf_dev-4.6.2.dist-info → simba_uw_tf_dev-4.7.2.dist-info}/RECORD +96 -85
- {simba_uw_tf_dev-4.6.2.dist-info → simba_uw_tf_dev-4.7.2.dist-info}/LICENSE +0 -0
- {simba_uw_tf_dev-4.6.2.dist-info → simba_uw_tf_dev-4.7.2.dist-info}/WHEEL +0 -0
- {simba_uw_tf_dev-4.6.2.dist-info → simba_uw_tf_dev-4.7.2.dist-info}/entry_points.txt +0 -0
- {simba_uw_tf_dev-4.6.2.dist-info → simba_uw_tf_dev-4.7.2.dist-info}/top_level.txt +0 -0
|
@@ -13,10 +13,14 @@ from simba.data_processors.pybursts_calculator import kleinberg_burst_detection
|
|
|
13
13
|
from simba.mixins.config_reader import ConfigReader
|
|
14
14
|
from simba.utils.checks import (check_float, check_if_dir_exists,
|
|
15
15
|
check_if_filepath_list_is_empty, check_int,
|
|
16
|
-
check_that_column_exist,
|
|
16
|
+
check_that_column_exist, check_valid_boolean,
|
|
17
|
+
check_valid_lst)
|
|
17
18
|
from simba.utils.enums import Paths, TagNames
|
|
18
19
|
from simba.utils.printing import SimbaTimer, log_event, stdout_success
|
|
19
|
-
from simba.utils.read_write import
|
|
20
|
+
from simba.utils.read_write import (copy_files_to_directory,
|
|
21
|
+
find_files_of_filetypes_in_directory,
|
|
22
|
+
get_current_time, get_fn_ext, read_df,
|
|
23
|
+
remove_a_folder, write_df)
|
|
20
24
|
from simba.utils.warnings import KleinbergWarning
|
|
21
25
|
|
|
22
26
|
|
|
@@ -38,12 +42,13 @@ class KleinbergCalculator(ConfigReader):
|
|
|
38
42
|
|
|
39
43
|
:param str config_path: path to SimBA project config file in Configparser format
|
|
40
44
|
:param List[str] classifier_names: Classifier names to apply Kleinberg smoothing to.
|
|
41
|
-
:param float sigma:
|
|
42
|
-
:param float gamma:
|
|
43
|
-
:param int hierarchy:
|
|
44
|
-
:param bool hierarchical_search:
|
|
45
|
+
:param float sigma: State transition cost for moving to higher burst levels. Higher values (e.g., 2-3) produce fewer but longer bursts; lower values (e.g., 1.1-1.5) detect more frequent, shorter bursts. Must be > 1.01. Default: 2.
|
|
46
|
+
:param float gamma: State transition cost for moving to lower burst levels. Higher values (e.g., 0.5-1.0) reduce total burst count by making downward transitions costly; lower values (e.g., 0.1-0.3) allow more flexible state changes. Must be >= 0. Default: 0.3.
|
|
47
|
+
:param int hierarchy: Hierarchy level to extract bursts from (0=lowest, higher=more selective). Level 0 captures all bursts; level 1-2 typically filters noise; level 3+ selects only the most prominent, sustained bursts. Higher levels yield fewer but more confident detections. Must be >= 0. Default: 1.
|
|
48
|
+
:param bool hierarchical_search: If True, searches for target hierarchy level within detected burst periods, falling back to lower levels if target not found. If False, extracts only bursts at the exact specified hierarchy level. Recommended when target hierarchy may be sparse. Default: False.
|
|
45
49
|
:param Optional[Union[str, os.PathLike]] input_dir: The directory with files to perform kleinberg smoothing on. If None, defaults to `project_folder/csv/machine_results`
|
|
46
50
|
:param Optional[Union[str, os.PathLike]] output_dir: Location to save smoothened data in. If None, defaults to `project_folder/csv/machine_results`
|
|
51
|
+
:param Optional[bool] save_originals: If True, saves the original data in sub-directory of the ouput directory.`
|
|
47
52
|
|
|
48
53
|
:example I:
|
|
49
54
|
>>> kleinberg_calculator = KleinbergCalculator(config_path='MySimBAConfigPath', classifier_names=['Attack'], sigma=2, gamma=0.3, hierarchy=2, hierarchical_search=False)
|
|
@@ -68,10 +73,12 @@ class KleinbergCalculator(ConfigReader):
|
|
|
68
73
|
|
|
69
74
|
def __init__(self,
|
|
70
75
|
config_path: Union[str, os.PathLike],
|
|
71
|
-
classifier_names: List[str],
|
|
72
|
-
sigma:
|
|
73
|
-
gamma:
|
|
76
|
+
classifier_names: Optional[List[str]] = None,
|
|
77
|
+
sigma: float = 2,
|
|
78
|
+
gamma: float = 0.3,
|
|
74
79
|
hierarchy: Optional[int] = 1,
|
|
80
|
+
verbose: bool = True,
|
|
81
|
+
save_originals: bool = True,
|
|
75
82
|
hierarchical_search: Optional[bool] = False,
|
|
76
83
|
input_dir: Optional[Union[str, os.PathLike]] = None,
|
|
77
84
|
output_dir: Optional[Union[str, os.PathLike]] = None):
|
|
@@ -81,25 +88,31 @@ class KleinbergCalculator(ConfigReader):
|
|
|
81
88
|
check_float(value=sigma, name=f'{self.__class__.__name__} sigma', min_value=1.01)
|
|
82
89
|
check_float(value=gamma, name=f'{self.__class__.__name__} gamma', min_value=0)
|
|
83
90
|
check_int(value=hierarchy, name=f'{self.__class__.__name__} hierarchy', min_value=0)
|
|
84
|
-
|
|
91
|
+
if isinstance(classifier_names, list):
|
|
92
|
+
check_valid_lst(data=classifier_names, source=f'{self.__class__.__name__} classifier_names', valid_dtypes=(str,), min_len=1)
|
|
93
|
+
else:
|
|
94
|
+
classifier_names = deepcopy(self.clf_names)
|
|
95
|
+
check_valid_boolean(value=verbose, source=f'{self.__class__.__name__} verbose', raise_error=True)
|
|
96
|
+
check_valid_boolean(value=save_originals, source=f'{self.__class__.__name__} save_originals', raise_error=True)
|
|
85
97
|
self.hierarchical_search, sigma, gamma, hierarchy, self.output_dir = (hierarchical_search, float(sigma), float(gamma), int(hierarchy), output_dir)
|
|
86
|
-
self.sigma, self.gamma, self.hierarchy, self.clfs = ( float(sigma), float(gamma),
|
|
98
|
+
self.sigma, self.gamma, self.hierarchy, self.clfs = ( float(sigma), float(gamma), int(hierarchy), classifier_names)
|
|
99
|
+
self.verbose, self.save_originals = verbose, save_originals
|
|
87
100
|
if input_dir is None:
|
|
88
|
-
self.
|
|
89
|
-
check_if_filepath_list_is_empty(filepaths=self.machine_results_paths, error_msg=f"SIMBA ERROR: No data files found in {self.machine_results_dir}. Cannot perform Kleinberg smoothing")
|
|
90
|
-
original_data_files_folder = os.path.join(self.project_path, Paths.MACHINE_RESULTS_DIR.value, f"Pre_Kleinberg_{self.datetime}")
|
|
91
|
-
if not os.path.exists(original_data_files_folder):
|
|
92
|
-
os.makedirs(original_data_files_folder)
|
|
93
|
-
for file_path in self.machine_results_paths:
|
|
94
|
-
_, file_name, ext = get_fn_ext(file_path)
|
|
95
|
-
shutil.copyfile(file_path, os.path.join(original_data_files_folder, file_name + ext))
|
|
101
|
+
self.input_dir = os.path.join(self.project_path, Paths.MACHINE_RESULTS_DIR.value)
|
|
96
102
|
else:
|
|
97
103
|
check_if_dir_exists(in_dir=input_dir)
|
|
98
|
-
self.
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
104
|
+
self.input_dir = deepcopy(input_dir)
|
|
105
|
+
self.data_paths = find_files_of_filetypes_in_directory(directory=self.input_dir, extensions=[f'.{self.file_type}'], sort_alphabetically=True, raise_error=True)
|
|
106
|
+
if output_dir is None:
|
|
107
|
+
self.output_dir = deepcopy(self.input_dir)
|
|
108
|
+
else:
|
|
109
|
+
check_if_dir_exists(in_dir=output_dir)
|
|
110
|
+
self.output_dir = deepcopy(output_dir)
|
|
111
|
+
self.original_data_files_folder = os.path.join(self.output_dir, f"Pre_Kleinberg_{self.datetime}")
|
|
112
|
+
remove_a_folder(folder_dir=self.original_data_files_folder, ignore_errors=True)
|
|
113
|
+
os.makedirs(self.original_data_files_folder)
|
|
114
|
+
copy_files_to_directory(file_paths=self.data_paths, dir=self.original_data_files_folder, verbose=False, integer_save_names=False)
|
|
115
|
+
if self.verbose: print(f"Processing Kleinberg burst detection for {len(self.data_paths)} file(s) and {len(classifier_names)} classifier(s)...")
|
|
103
116
|
|
|
104
117
|
def hierarchical_searcher(self):
|
|
105
118
|
if (len(self.kleinberg_bouts["Hierarchy"]) == 1) and (int(self.kleinberg_bouts.at[0, "Hierarchy"]) == 0):
|
|
@@ -135,7 +148,7 @@ class KleinbergCalculator(ConfigReader):
|
|
|
135
148
|
for file_cnt, file_path in enumerate(self.data_paths):
|
|
136
149
|
_, video_name, _ = get_fn_ext(file_path)
|
|
137
150
|
video_timer = SimbaTimer(start=True)
|
|
138
|
-
print(f"Performing Kleinberg burst detection for video {video_name}
|
|
151
|
+
if self.verbose: print(f"[{get_current_time()}] Performing Kleinberg burst detection for video {video_name} (Video {file_cnt+1}/{len(self.data_paths)})...")
|
|
139
152
|
data_df = read_df(file_path, self.file_type).reset_index(drop=True)
|
|
140
153
|
video_out_df = deepcopy(data_df)
|
|
141
154
|
check_that_column_exist(df=data_df, column_name=self.clfs, file_name=video_name)
|
|
@@ -150,7 +163,7 @@ class KleinbergCalculator(ConfigReader):
|
|
|
150
163
|
self.kleinberg_bouts.insert(loc=0, column="Video", value=video_name)
|
|
151
164
|
detailed_df_lst.append(self.kleinberg_bouts)
|
|
152
165
|
if self.hierarchical_search:
|
|
153
|
-
print(f"Applying hierarchical search for video {video_name}...")
|
|
166
|
+
if self.verbose: print(f"[{get_current_time()}] Applying hierarchical search for video {video_name}...")
|
|
154
167
|
self.hierarchical_searcher()
|
|
155
168
|
else:
|
|
156
169
|
self.clf_bouts_in_hierarchy = self.kleinberg_bouts[self.kleinberg_bouts["Hierarchy"] == self.hierarchy]
|
|
@@ -160,19 +173,38 @@ class KleinbergCalculator(ConfigReader):
|
|
|
160
173
|
video_out_df.loc[hierarchy_idx, clf] = 1
|
|
161
174
|
write_df(video_out_df, self.file_type, save_path)
|
|
162
175
|
video_timer.stop_timer()
|
|
163
|
-
print(f'Kleinberg analysis complete for video {video_name} (saved at {save_path}), elapsed time: {video_timer.elapsed_time_str}s.')
|
|
176
|
+
if self.verbose: print(f'[{get_current_time()}] Kleinberg analysis complete for video {video_name} (saved at {save_path}), elapsed time: {video_timer.elapsed_time_str}s.')
|
|
164
177
|
|
|
165
178
|
self.timer.stop_timer()
|
|
179
|
+
if not self.save_originals:
|
|
180
|
+
remove_a_folder(folder_dir=self.original_data_files_folder, ignore_errors=False)
|
|
181
|
+
else:
|
|
182
|
+
if self.verbose: stdout_success(msg=f"Original, un-smoothened data, saved in {self.original_data_files_folder} directory", elapsed_time=self.timer.elapsed_time_str, source=self.__class__.__name__)
|
|
166
183
|
if len(detailed_df_lst) > 0:
|
|
167
184
|
self.detailed_df = pd.concat(detailed_df_lst, axis=0)
|
|
168
185
|
detailed_save_path = os.path.join(self.logs_path, f"Kleinberg_detailed_log_{self.datetime}.csv")
|
|
169
186
|
self.detailed_df.to_csv(detailed_save_path)
|
|
170
|
-
stdout_success(msg=f"Kleinberg analysis complete. See {detailed_save_path} for details of detected bouts of all classifiers in all hierarchies", elapsed_time=self.timer.elapsed_time_str, source=self.__class__.__name__)
|
|
187
|
+
if self.verbose: stdout_success(msg=f"Kleinberg analysis complete for {len(self.data_paths)} files. Results stored in {self.output_dir} directory. See {detailed_save_path} for details of detected bouts of all classifiers in all hierarchies", elapsed_time=self.timer.elapsed_time_str, source=self.__class__.__name__)
|
|
171
188
|
else:
|
|
172
|
-
print("Kleinberg analysis complete.")
|
|
189
|
+
if self.verbose: print(f"[{get_current_time()}] Kleinberg analysis complete for {len(self.data_paths)} files. Results stored in {self.output_dir} directory.")
|
|
173
190
|
KleinbergWarning(msg="All behavior bouts removed following kleinberg smoothing", source=self.__class__.__name__)
|
|
174
191
|
|
|
175
192
|
|
|
193
|
+
|
|
194
|
+
|
|
195
|
+
# test = KleinbergCalculator(config_path=r"C:\troubleshooting\mitra\project_folder\project_config.ini",
|
|
196
|
+
# classifier_names=['straub_tail'],
|
|
197
|
+
# sigma=1.1,
|
|
198
|
+
# gamma=0.1,
|
|
199
|
+
# hierarchy=1,
|
|
200
|
+
# save_originals=False,
|
|
201
|
+
# hierarchical_search=False)
|
|
202
|
+
#
|
|
203
|
+
# test.run()
|
|
204
|
+
#
|
|
205
|
+
|
|
206
|
+
|
|
207
|
+
|
|
176
208
|
# test = KleinbergCalculator(config_path='/Users/simon/Desktop/envs/simba/troubleshooting/levi/project_folder/project_config.ini',
|
|
177
209
|
# classifier_names=['No_Seizure_(0)'],
|
|
178
210
|
# sigma=1.1,
|
|
@@ -83,12 +83,12 @@ class FeatureSubsetsCalculator(ConfigReader, TrainModelMixin):
|
|
|
83
83
|
:align: center
|
|
84
84
|
|
|
85
85
|
:example:
|
|
86
|
-
>>> test = FeatureSubsetsCalculator(config_path=r"C
|
|
86
|
+
>>> test = FeatureSubsetsCalculator(config_path=r"C:/troubleshooting/mitra/project_folder/project_config.ini",
|
|
87
87
|
>>> feature_families=[FRAME_BP_MOVEMENT, WITHIN_ANIMAL_THREE_POINT_ANGLES],
|
|
88
88
|
>>> append_to_features_extracted=False,
|
|
89
89
|
>>> file_checks=False,
|
|
90
90
|
>>> append_to_targets_inserted=False,
|
|
91
|
-
>>> save_dir=r"C
|
|
91
|
+
>>> save_dir=r"C:/troubleshooting/mitra/project_folder/csv/new_features")
|
|
92
92
|
>>> test.run()
|
|
93
93
|
"""
|
|
94
94
|
|
|
@@ -154,11 +154,11 @@ class FeatureSubsetsCalculator(ConfigReader, TrainModelMixin):
|
|
|
154
154
|
self.within_animal_four_point_combs[animal] = np.array(list(combinations(animal_bps, 4)))
|
|
155
155
|
|
|
156
156
|
def _get_two_point_bp_distances(self):
|
|
157
|
-
for c in self.two_point_combs:
|
|
157
|
+
for cnt, c in enumerate(self.two_point_combs):
|
|
158
158
|
x1, y1, x2, y2 = list(sum([(f"{x}_x", f"{y}_y") for (x, y) in zip(c, c)], ()))
|
|
159
159
|
bp1 = self.data_df[[x1, y1]].values
|
|
160
160
|
bp2 = self.data_df[[x2, y2]].values
|
|
161
|
-
self.results[f"Distance (mm) {c[0]}-{c[1]}"] = FeatureExtractionMixin.
|
|
161
|
+
self.results[f"Distance (mm) {c[0]}-{c[1]}"] = FeatureExtractionMixin.bodypart_distance(bp1_coords=bp1.astype(np.int32), bp2_coords=bp2.astype(np.int32), px_per_mm=np.float64(self.px_per_mm), in_centimeters=False)
|
|
162
162
|
|
|
163
163
|
def __get_three_point_angles(self):
|
|
164
164
|
for animal, points in self.within_animal_three_point_combs.items():
|
|
@@ -342,13 +342,20 @@ class FeatureSubsetsCalculator(ConfigReader, TrainModelMixin):
|
|
|
342
342
|
|
|
343
343
|
|
|
344
344
|
|
|
345
|
+
# test = FeatureSubsetsCalculator(config_path=r"C:\troubleshooting\srami0619\project_folder\project_config.ini",
|
|
346
|
+
# feature_families=[TWO_POINT_BP_DISTANCES],
|
|
347
|
+
# append_to_features_extracted=False,
|
|
348
|
+
# file_checks=True,
|
|
349
|
+
# append_to_targets_inserted=False)
|
|
350
|
+
# test.run()
|
|
351
|
+
|
|
352
|
+
|
|
345
353
|
|
|
346
354
|
# test = FeatureSubsetsCalculator(config_path=r"C:\troubleshooting\mitra\project_folder\project_config.ini",
|
|
347
|
-
# feature_families=[
|
|
355
|
+
# feature_families=[TWO_POINT_BP_DISTANCES],
|
|
348
356
|
# append_to_features_extracted=False,
|
|
349
357
|
# file_checks=True,
|
|
350
|
-
# append_to_targets_inserted=False
|
|
351
|
-
# save_dir=r"C:\troubleshooting\mitra\project_folder\csv\feature_subset")
|
|
358
|
+
# append_to_targets_inserted=False)
|
|
352
359
|
# test.run()
|
|
353
360
|
|
|
354
361
|
#
|
|
@@ -28,7 +28,7 @@ RIGHT_EAR = 'right_ear'
|
|
|
28
28
|
CENTER = 'center'
|
|
29
29
|
TAIL_BASE = 'tail_base'
|
|
30
30
|
TAIL_CENTER = 'tail_center'
|
|
31
|
-
TAIL_TIP = '
|
|
31
|
+
TAIL_TIP = 'tail_end'
|
|
32
32
|
|
|
33
33
|
TIME_WINDOWS = np.array([0.25, 0.5, 1.0, 2.0])
|
|
34
34
|
|
|
@@ -207,7 +207,7 @@ class MitraFeatureExtractor(ConfigReader,
|
|
|
207
207
|
|
|
208
208
|
|
|
209
209
|
|
|
210
|
-
# feature_extractor = MitraFeatureExtractor(config_path=r"
|
|
210
|
+
# feature_extractor = MitraFeatureExtractor(config_path=r"E:\troubleshooting\mitra_emergence\project_folder\project_config.ini")
|
|
211
211
|
# feature_extractor.run()
|
|
212
212
|
|
|
213
213
|
|
|
@@ -9,8 +9,6 @@ try:
|
|
|
9
9
|
except:
|
|
10
10
|
from typing_extensions import Literal
|
|
11
11
|
|
|
12
|
-
import functools
|
|
13
|
-
import multiprocessing
|
|
14
12
|
from copy import deepcopy
|
|
15
13
|
|
|
16
14
|
import numpy as np
|
|
@@ -46,10 +44,10 @@ class StraubTailAnalyzer(ConfigReader):
|
|
|
46
44
|
.. [1] Lazaro et al., Brainwide Genetic Capture for Conscious State Transitions, `biorxiv`, doi: https://doi.org/10.1101/2025.03.28.646066
|
|
47
45
|
|
|
48
46
|
:example:
|
|
49
|
-
>>> runner = StraubTailAnalyzer(config_path=r"C
|
|
50
|
-
>>> data_dir=r'C
|
|
51
|
-
>>> video_dir=r'C
|
|
52
|
-
>>> save_dir=r'C
|
|
47
|
+
>>> runner = StraubTailAnalyzer(config_path=r"C:/troubleshooting/mitra/project_folder/project_config.ini",
|
|
48
|
+
>>> data_dir=r'C:/troubleshooting/mitra/project_folder/videos/additional/bg_removed/rotated',
|
|
49
|
+
>>> video_dir=r'C:/troubleshooting/mitra/project_folder/videos/additional/bg_removed/rotated',
|
|
50
|
+
>>> save_dir=r'C:/troubleshooting/mitra/project_folder/videos/additional/bg_removed/rotated/tail_features_additional',
|
|
53
51
|
>>> anchor_points=('tail_base', 'tail_center', 'tail_tip'),
|
|
54
52
|
>>> body_parts=('nose', 'left_ear', 'right_ear', 'right_side', 'left_side', 'tail_base'))
|
|
55
53
|
>>> runner.run()
|
|
@@ -64,7 +64,7 @@ class LabellingInterface(ConfigReader):
|
|
|
64
64
|
:param bool continuing: Set True to resume annotations from an existing targets file. Defaults to False.
|
|
65
65
|
|
|
66
66
|
:example:
|
|
67
|
-
>>> _ = LabellingInterface(config_path=r"C
|
|
67
|
+
>>> _ = LabellingInterface(config_path=r"C:/troubleshooting/mitra/project_folder/project_config.ini", file_path=r"C:/troubleshooting/mitra/project_folder/videos/501_MA142_Gi_CNO_0521.mp4", thresholds=None, continuing=False)
|
|
68
68
|
"""
|
|
69
69
|
|
|
70
70
|
def __init__(self,
|
simba/mixins/config_reader.py
CHANGED
|
@@ -41,8 +41,8 @@ from simba.utils.read_write import (find_core_cnt, get_all_clf_names,
|
|
|
41
41
|
get_fn_ext, read_config_file, read_df,
|
|
42
42
|
read_project_path_and_file_type, write_df)
|
|
43
43
|
from simba.utils.warnings import (BodypartColumnNotFoundWarning,
|
|
44
|
-
|
|
45
|
-
NoFileFoundWarning)
|
|
44
|
+
DuplicateNamesWarning, InvalidValueWarning,
|
|
45
|
+
NoDataFoundWarning, NoFileFoundWarning)
|
|
46
46
|
|
|
47
47
|
|
|
48
48
|
class ConfigReader(object):
|
|
@@ -610,11 +610,14 @@ class ConfigReader(object):
|
|
|
610
610
|
>>> config_reader.get_bp_headers()
|
|
611
611
|
"""
|
|
612
612
|
|
|
613
|
+
duplicates = list({x for x in self.body_parts_lst if self.body_parts_lst.count(x) > 1})
|
|
614
|
+
if len(duplicates) > 0: DuplicateNamesWarning(msg=f'The pose configuration file at {self.body_parts_path} contains duplicate entries: {duplicates}', source=self.__class__.__name__)
|
|
613
615
|
self.bp_headers = []
|
|
614
616
|
for bp in self.body_parts_lst:
|
|
615
617
|
c1, c2, c3 = (f"{bp}_x", f"{bp}_y", f"{bp}_p")
|
|
616
618
|
self.bp_headers.extend((c1, c2, c3))
|
|
617
619
|
|
|
620
|
+
|
|
618
621
|
def read_config_entry(
|
|
619
622
|
self,
|
|
620
623
|
config: ConfigParser,
|
simba/mixins/geometry_mixin.py
CHANGED
|
@@ -1339,8 +1339,7 @@ class GeometryMixin(object):
|
|
|
1339
1339
|
)
|
|
1340
1340
|
for cnt, result in enumerate(pool.imap(constants, data, chunksize=1)):
|
|
1341
1341
|
results.append(result)
|
|
1342
|
-
pool
|
|
1343
|
-
pool.terminate()
|
|
1342
|
+
terminate_cpu_pool(pool=pool, force=False)
|
|
1344
1343
|
if data_ndim == 2:
|
|
1345
1344
|
return [i for s in results for i in s]
|
|
1346
1345
|
else:
|
|
@@ -1370,8 +1369,7 @@ class GeometryMixin(object):
|
|
|
1370
1369
|
cap_style=cap_style)
|
|
1371
1370
|
for cnt, mp_return in enumerate(pool.imap(constants, geomety_lst, chunksize=1)):
|
|
1372
1371
|
results.append(mp_return)
|
|
1373
|
-
pool
|
|
1374
|
-
pool.terminate()
|
|
1372
|
+
terminate_cpu_pool(pool=pool, force=False)
|
|
1375
1373
|
return [l for ll in results for l in ll]
|
|
1376
1374
|
|
|
1377
1375
|
def multiframe_bodyparts_to_circle(self,
|
|
@@ -1524,8 +1522,7 @@ class GeometryMixin(object):
|
|
|
1524
1522
|
)
|
|
1525
1523
|
for cnt, result in enumerate(pool.imap(constants, data, chunksize=1)):
|
|
1526
1524
|
results.append(result)
|
|
1527
|
-
pool
|
|
1528
|
-
pool.terminate()
|
|
1525
|
+
terminate_cpu_pool(pool=pool, force=False)
|
|
1529
1526
|
return results
|
|
1530
1527
|
|
|
1531
1528
|
def multiframe_compute_pct_shape_overlap(self,
|
|
@@ -1559,7 +1556,7 @@ class GeometryMixin(object):
|
|
|
1559
1556
|
:rtype: List[float]
|
|
1560
1557
|
|
|
1561
1558
|
:example:
|
|
1562
|
-
>>> df = read_df(file_path=r"C
|
|
1559
|
+
>>> df = read_df(file_path=r"C:/troubleshooting/two_black_animals_14bp/project_folder/csv/outlier_corrected_movement_location/Together_2.csv", file_type='csv').astype(int)
|
|
1563
1560
|
>>> animal_1_cols = [x for x in df.columns if '_1_' in x and not '_p' in x]
|
|
1564
1561
|
>>> animal_2_cols = [x for x in df.columns if '_2_' in x and not '_p' in x]
|
|
1565
1562
|
>>> animal_1_arr = df[animal_1_cols].values.reshape(len(df), int(len(animal_1_cols)/ 2), 2)
|
|
@@ -1625,7 +1622,7 @@ class GeometryMixin(object):
|
|
|
1625
1622
|
:return List[float]: List of overlap between corresponding Polygons. If overlap 1, else 0.
|
|
1626
1623
|
|
|
1627
1624
|
:example:
|
|
1628
|
-
>>> df = read_df(file_path=r"C
|
|
1625
|
+
>>> df = read_df(file_path=r"C:/troubleshooting/two_black_animals_14bp/project_folder/csv/outlier_corrected_movement_location/Together_2.csv", file_type='csv').astype(int)
|
|
1629
1626
|
>>> animal_1_cols = [x for x in df.columns if '_1_' in x and not '_p' in x]
|
|
1630
1627
|
>>> animal_2_cols = [x for x in df.columns if '_2_' in x and not '_p' in x]
|
|
1631
1628
|
>>> animal_1_arr = df[animal_1_cols].values.reshape(len(df), int(len(animal_1_cols)/ 2), 2)
|
|
@@ -1696,7 +1693,7 @@ class GeometryMixin(object):
|
|
|
1696
1693
|
:rtype: List[float]
|
|
1697
1694
|
|
|
1698
1695
|
:example:
|
|
1699
|
-
>>> df = read_df(file_path=r"C
|
|
1696
|
+
>>> df = read_df(file_path=r"C:/troubleshooting/two_black_animals_14bp/project_folder/csv/outlier_corrected_movement_location/Together_2.csv", file_type='csv').astype(int)
|
|
1700
1697
|
>>> animal_1_cols = [x for x in df.columns if '_1_' in x and not '_p' in x]
|
|
1701
1698
|
>>> animal_2_cols = [x for x in df.columns if '_2_' in x and not '_p' in x]
|
|
1702
1699
|
>>> animal_1_arr = df[animal_1_cols].values.reshape(len(df), int(len(animal_1_cols)/ 2), 2)
|
|
@@ -1766,7 +1763,7 @@ class GeometryMixin(object):
|
|
|
1766
1763
|
:rtype: List[Polygon]
|
|
1767
1764
|
|
|
1768
1765
|
:example:
|
|
1769
|
-
>>> df = read_df(file_path=r"C
|
|
1766
|
+
>>> df = read_df(file_path=r"C:/troubleshooting/two_black_animals_14bp/project_folder/csv/outlier_corrected_movement_location/Together_2.csv", file_type='csv').astype(int)
|
|
1770
1767
|
>>> animal_1_cols = [x for x in df.columns if '_1_' in x and not '_p' in x]
|
|
1771
1768
|
>>> animal_1_arr = df[animal_1_cols].values.reshape(len(df), int(len(animal_1_cols)/ 2), 2)
|
|
1772
1769
|
>>> animal_1_geo = GeometryMixin.bodyparts_to_polygon(data=animal_1_arr)
|
|
@@ -1798,8 +1795,7 @@ class GeometryMixin(object):
|
|
|
1798
1795
|
timer.stop_timer()
|
|
1799
1796
|
if verbose:
|
|
1800
1797
|
stdout_success(msg="Rotated rectangles complete.", elapsed_time=timer.elapsed_time_str)
|
|
1801
|
-
pool
|
|
1802
|
-
pool.terminate()
|
|
1798
|
+
terminate_cpu_pool(pool=pool, force=False)
|
|
1803
1799
|
return results
|
|
1804
1800
|
|
|
1805
1801
|
@staticmethod
|
|
@@ -2003,8 +1999,7 @@ class GeometryMixin(object):
|
|
|
2003
1999
|
)
|
|
2004
2000
|
for cnt, result in enumerate(pool.imap(constants, shapes, chunksize=1)):
|
|
2005
2001
|
results.append(result)
|
|
2006
|
-
pool
|
|
2007
|
-
pool.terminate()
|
|
2002
|
+
terminate_cpu_pool(pool=pool, force=False)
|
|
2008
2003
|
return results
|
|
2009
2004
|
|
|
2010
2005
|
def multiframe_union(self, shapes: Iterable[Union[LineString, MultiLineString, Polygon]], core_cnt: int = -1) -> \
|
|
@@ -2043,8 +2038,7 @@ class GeometryMixin(object):
|
|
|
2043
2038
|
with multiprocessing.Pool(core_cnt, maxtasksperchild=Defaults.LARGE_MAX_TASK_PER_CHILD.value) as pool:
|
|
2044
2039
|
for cnt, result in enumerate(pool.imap(GeometryMixin().union, shapes, chunksize=1)):
|
|
2045
2040
|
results.append(result)
|
|
2046
|
-
pool
|
|
2047
|
-
pool.terminate()
|
|
2041
|
+
terminate_cpu_pool(pool=pool, force=False)
|
|
2048
2042
|
return results
|
|
2049
2043
|
|
|
2050
2044
|
def multiframe_symmetric_difference(self, shapes: Iterable[Union[LineString, MultiLineString, Polygon]],
|
|
@@ -2084,8 +2078,7 @@ class GeometryMixin(object):
|
|
|
2084
2078
|
pool.imap(GeometryMixin().symmetric_difference, shapes, chunksize=1)
|
|
2085
2079
|
):
|
|
2086
2080
|
results.append(result)
|
|
2087
|
-
pool
|
|
2088
|
-
pool.terminate()
|
|
2081
|
+
terminate_cpu_pool(pool=pool, force=False)
|
|
2089
2082
|
return results
|
|
2090
2083
|
|
|
2091
2084
|
def multiframe_delaunay_triangulate_keypoints(self, data: np.ndarray, core_cnt: int = -1) -> List[List[Polygon]]:
|
|
@@ -2132,8 +2125,7 @@ class GeometryMixin(object):
|
|
|
2132
2125
|
):
|
|
2133
2126
|
results.append(result)
|
|
2134
2127
|
|
|
2135
|
-
pool
|
|
2136
|
-
pool.terminate()
|
|
2128
|
+
terminate_cpu_pool(pool=pool, force=False)
|
|
2137
2129
|
return results
|
|
2138
2130
|
|
|
2139
2131
|
def multiframe_difference(
|
|
@@ -2221,8 +2213,7 @@ class GeometryMixin(object):
|
|
|
2221
2213
|
msg="Multi-frame difference compute complete",
|
|
2222
2214
|
elapsed_time=timer.elapsed_time_str,
|
|
2223
2215
|
)
|
|
2224
|
-
pool
|
|
2225
|
-
pool.terminate()
|
|
2216
|
+
terminate_cpu_pool(pool=pool, force=False)
|
|
2226
2217
|
return results
|
|
2227
2218
|
|
|
2228
2219
|
def multiframe_area(self,
|
|
@@ -2276,8 +2267,7 @@ class GeometryMixin(object):
|
|
|
2276
2267
|
|
|
2277
2268
|
timer.stop_timer()
|
|
2278
2269
|
stdout_success(msg="Multi-frame area compute complete", elapsed_time=timer.elapsed_time_str)
|
|
2279
|
-
pool
|
|
2280
|
-
pool.terminate()
|
|
2270
|
+
terminate_cpu_pool(pool=pool, force=False)
|
|
2281
2271
|
return results
|
|
2282
2272
|
|
|
2283
2273
|
def multiframe_bodyparts_to_multistring_skeleton(
|
|
@@ -2619,8 +2609,7 @@ class GeometryMixin(object):
|
|
|
2619
2609
|
pool.imap(GeometryMixin.is_shape_covered, shapes, chunksize=1)
|
|
2620
2610
|
):
|
|
2621
2611
|
results.append(mp_return)
|
|
2622
|
-
pool
|
|
2623
|
-
pool.terminate()
|
|
2612
|
+
terminate_cpu_pool(pool=pool, force=False)
|
|
2624
2613
|
return results
|
|
2625
2614
|
|
|
2626
2615
|
@staticmethod
|
|
@@ -3321,8 +3310,7 @@ class GeometryMixin(object):
|
|
|
3321
3310
|
for cnt, result in enumerate(pool.imap(constants, data, chunksize=1)):
|
|
3322
3311
|
if result[1] != -1:
|
|
3323
3312
|
img_arr[result[0], result[2] - 1, result[1] - 1] = 1
|
|
3324
|
-
pool
|
|
3325
|
-
pool.terminate()
|
|
3313
|
+
terminate_cpu_pool(pool=pool, force=False)
|
|
3326
3314
|
timer.stop_timer()
|
|
3327
3315
|
stdout_success(
|
|
3328
3316
|
msg="Cumulative coordinates in geometries complete",
|
|
@@ -3415,8 +3403,7 @@ class GeometryMixin(object):
|
|
|
3415
3403
|
for cnt, result in enumerate(pool.imap(constants, data, chunksize=1)):
|
|
3416
3404
|
if result[1] != -1:
|
|
3417
3405
|
img_arr[result[0], result[2] - 1, result[1] - 1] = 1
|
|
3418
|
-
pool
|
|
3419
|
-
pool.terminate()
|
|
3406
|
+
terminate_cpu_pool(pool=pool, force=False)
|
|
3420
3407
|
if fps is None:
|
|
3421
3408
|
return np.cumsum(img_arr, axis=0)
|
|
3422
3409
|
else:
|
|
@@ -3538,10 +3525,10 @@ class GeometryMixin(object):
|
|
|
3538
3525
|
:rtype: Tuple[Dict[Tuple[int, int], Dict[Tuple[int, int], float]], Dict[Tuple[int, int], Dict[Tuple[int, int], int]]]
|
|
3539
3526
|
|
|
3540
3527
|
:example:
|
|
3541
|
-
>>> video_meta_data = get_video_meta_data(video_path=r"C
|
|
3528
|
+
>>> video_meta_data = get_video_meta_data(video_path=r"C:/troubleshooting/mitra/project_folder/videos/708_MA149_Gq_CNO_0515.mp4")
|
|
3542
3529
|
>>> w, h = video_meta_data['width'], video_meta_data['height']
|
|
3543
3530
|
>>> grid = GeometryMixin().bucket_img_into_grid_square(bucket_grid_size=(5, 5), bucket_grid_size_mm=None, img_size=(h, w), verbose=False)[0]
|
|
3544
|
-
>>> data = read_df(file_path=r'C
|
|
3531
|
+
>>> data = read_df(file_path=r'C:/troubleshooting/mitra/project_folder/csv/outlier_corrected_movement_location/708_MA149_Gq_CNO_0515.csv', file_type='csv')[['Nose_x', 'Nose_y']].values
|
|
3545
3532
|
>>> transition_probabilities, _ = geometry_transition_probabilities(data=data, grid=grid)
|
|
3546
3533
|
"""
|
|
3547
3534
|
|
|
@@ -3559,8 +3546,7 @@ class GeometryMixin(object):
|
|
|
3559
3546
|
constants = functools.partial(GeometryMixin._compute_framewise_geometry_idx, grid=grid, verbose=verbose)
|
|
3560
3547
|
for cnt, result in enumerate(pool.imap(constants, data, chunksize=1)):
|
|
3561
3548
|
results.append(result)
|
|
3562
|
-
pool
|
|
3563
|
-
pool.terminate();
|
|
3549
|
+
terminate_cpu_pool(pool=pool, force=False)
|
|
3564
3550
|
del data
|
|
3565
3551
|
|
|
3566
3552
|
results = np.vstack(results)[:, 1:].astype(np.int32)
|
|
@@ -4004,7 +3990,7 @@ class GeometryMixin(object):
|
|
|
4004
3990
|
:rtype: np.ndarray
|
|
4005
3991
|
|
|
4006
3992
|
:example:
|
|
4007
|
-
>>> data_path = r"C
|
|
3993
|
+
>>> data_path = r"C:/troubleshooting/mitra/project_folder/csv/outlier_corrected_movement_location/FRR_gq_Saline_0624.csv"
|
|
4008
3994
|
>>> animal_data = read_df(file_path=data_path, file_type='csv', usecols=['Nose_x', 'Nose_y', 'Tail_base_x', 'Tail_base_y', 'Left_side_x', 'Left_side_y', 'Right_side_x', 'Right_side_y']).values.reshape(-1, 4, 2)[0:20].astype(np.int32)
|
|
4009
3995
|
>>> animal_polygons = GeometryMixin().bodyparts_to_polygon(data=animal_data)
|
|
4010
3996
|
>>> GeometryMixin.geometries_to_exterior_keypoints(geometries=animal_polygons)
|
|
@@ -4174,7 +4160,7 @@ class GeometryMixin(object):
|
|
|
4174
4160
|
:rtype: Union[None, Dict[Any, dict]]
|
|
4175
4161
|
|
|
4176
4162
|
:example I:
|
|
4177
|
-
>>> results = GeometryMixin.sleap_csv_to_geometries(data=r"C
|
|
4163
|
+
>>> results = GeometryMixin.sleap_csv_to_geometries(data=r"C:/troubleshooting/ants/pose_data/ant.csv")
|
|
4178
4164
|
>>> # Results structure: {track_id: {frame_idx: Polygon, ...}, ...}
|
|
4179
4165
|
|
|
4180
4166
|
:example II
|
simba/mixins/image_mixin.py
CHANGED
|
@@ -18,7 +18,7 @@ from collections import ChainMap
|
|
|
18
18
|
import cv2
|
|
19
19
|
import pandas as pd
|
|
20
20
|
from numba import float64, int64, jit, njit, prange, uint8
|
|
21
|
-
from shapely.geometry import
|
|
21
|
+
from shapely.geometry import Polygon
|
|
22
22
|
from skimage.metrics import structural_similarity
|
|
23
23
|
|
|
24
24
|
from simba.utils.checks import (check_file_exist_and_readable, check_float,
|
|
@@ -27,16 +27,14 @@ from simba.utils.checks import (check_file_exist_and_readable, check_float,
|
|
|
27
27
|
check_int, check_str, check_valid_array,
|
|
28
28
|
check_valid_boolean, check_valid_lst,
|
|
29
29
|
check_valid_tuple, is_img_bw, is_img_greyscale)
|
|
30
|
+
from simba.utils.data import terminate_cpu_pool
|
|
30
31
|
from simba.utils.enums import Defaults, Formats, GeometryEnum, Options
|
|
31
|
-
from simba.utils.errors import
|
|
32
|
-
FrameRangeError, InvalidInputError,
|
|
33
|
-
NotDirectoryError)
|
|
32
|
+
from simba.utils.errors import ArrayError, FrameRangeError, InvalidInputError
|
|
34
33
|
from simba.utils.printing import SimbaTimer, stdout_success
|
|
35
34
|
from simba.utils.read_write import (find_core_cnt,
|
|
36
35
|
find_files_of_filetypes_in_directory,
|
|
37
36
|
get_fn_ext, get_video_meta_data,
|
|
38
|
-
read_frm_of_video
|
|
39
|
-
read_img_batch_from_video_gpu, write_df)
|
|
37
|
+
read_frm_of_video)
|
|
40
38
|
|
|
41
39
|
|
|
42
40
|
class ImageMixin(object):
|
|
@@ -59,17 +57,16 @@ class ImageMixin(object):
|
|
|
59
57
|
pass
|
|
60
58
|
|
|
61
59
|
@staticmethod
|
|
62
|
-
def brightness_intensity(imgs: List[np.ndarray], ignore_black:
|
|
60
|
+
def brightness_intensity(imgs: Union[List[np.ndarray], np.ndarray], ignore_black: bool = True, verbose: bool = False) -> np.ndarray:
|
|
63
61
|
"""
|
|
64
62
|
Compute the average brightness intensity within each image within a list.
|
|
65
63
|
|
|
66
64
|
For example, (i) create a list of images containing a light cue ROI, (ii) compute brightness in each image, (iii) perform kmeans on brightness, and get the frames when the light cue is on vs off.
|
|
67
65
|
|
|
68
66
|
.. seealso::
|
|
69
|
-
For GPU acceleration, see :func:`simba.data_processors.cuda.image.img_stack_brightness`.
|
|
70
|
-
For geometry based brightness, see :func:`simba.mixins.geometry_mixin.GeometryMixin.get_geometry_brightness_intensity`
|
|
67
|
+
For GPU acceleration, see :func:`simba.data_processors.cuda.image.img_stack_brightness`. For geometry based brightness, see :func:`simba.mixins.geometry_mixin.GeometryMixin.get_geometry_brightness_intensity`
|
|
71
68
|
|
|
72
|
-
:param List[np.ndarray] imgs: List of images as arrays to calculate average brightness intensity within.
|
|
69
|
+
:param Union[List[np.ndarray], np.ndarray] imgs: List of images as arrays or 3/4d array of images to calculate average brightness intensity within.
|
|
73
70
|
:param Optional[bool] ignore_black: If True, ignores black pixels. If the images are sliced non-rectangular geometric shapes created by ``slice_shapes_in_img``, then pixels that don't belong to the shape has been masked in black.
|
|
74
71
|
:returns: List of floats of size len(imgs) with brightness intensities.
|
|
75
72
|
:rtype: List[float]
|
|
@@ -79,14 +76,12 @@ class ImageMixin(object):
|
|
|
79
76
|
>>> ImageMixin.brightness_intensity(imgs=[img], ignore_black=False)
|
|
80
77
|
>>> [159.0]
|
|
81
78
|
"""
|
|
82
|
-
results = []
|
|
83
|
-
check_instance(source=f"{ImageMixin().brightness_intensity.__name__} imgs", instance=imgs, accepted_types=list)
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
accepted_types=np.ndarray,
|
|
89
|
-
)
|
|
79
|
+
results, timer = [], SimbaTimer(start=True)
|
|
80
|
+
check_instance(source=f"{ImageMixin().brightness_intensity.__name__} imgs", instance=imgs, accepted_types=(list, np.ndarray,))
|
|
81
|
+
if isinstance(imgs, np.ndarray): imgs = np.array(imgs)
|
|
82
|
+
for img_cnt in range(imgs.shape[0]):
|
|
83
|
+
img = imgs[img_cnt]
|
|
84
|
+
check_instance(source=f"{ImageMixin().brightness_intensity.__name__} img {img_cnt}", instance=img, accepted_types=np.ndarray)
|
|
90
85
|
if len(img) == 0:
|
|
91
86
|
results.append(0)
|
|
92
87
|
else:
|
|
@@ -94,7 +89,10 @@ class ImageMixin(object):
|
|
|
94
89
|
results.append(np.ceil(np.average(img[img != 0])))
|
|
95
90
|
else:
|
|
96
91
|
results.append(np.ceil(np.average(img)))
|
|
97
|
-
|
|
92
|
+
b = np.array(results).astype(np.float32)
|
|
93
|
+
timer.stop_timer()
|
|
94
|
+
if verbose: print(f'Brightness computed in {b.shape[0]} images (elapsed time {timer.elapsed_time_str}s)')
|
|
95
|
+
|
|
98
96
|
|
|
99
97
|
@staticmethod
|
|
100
98
|
def gaussian_blur(img: np.ndarray, kernel_size: Optional[Tuple] = (9, 9)) -> np.ndarray:
|
|
@@ -546,8 +544,8 @@ class ImageMixin(object):
|
|
|
546
544
|
pool.imap(constants, split_frm_idx, chunksize=1)
|
|
547
545
|
):
|
|
548
546
|
results.append(result)
|
|
549
|
-
|
|
550
|
-
pool
|
|
547
|
+
|
|
548
|
+
terminate_cpu_pool(pool=pool, force=False)
|
|
551
549
|
results = dict(ChainMap(*results))
|
|
552
550
|
|
|
553
551
|
max_value, max_frm = -np.inf, None
|
|
@@ -876,8 +874,7 @@ class ImageMixin(object):
|
|
|
876
874
|
pool.imap(ImageMixin()._image_reader_helper, file_paths, chunksize=1)
|
|
877
875
|
):
|
|
878
876
|
imgs.update(result)
|
|
879
|
-
pool
|
|
880
|
-
pool.terminate()
|
|
877
|
+
terminate_cpu_pool(pool=pool, force=False)
|
|
881
878
|
return imgs
|
|
882
879
|
|
|
883
880
|
@staticmethod
|
|
@@ -1027,8 +1024,7 @@ class ImageMixin(object):
|
|
|
1027
1024
|
for cnt, result in enumerate(pool.imap(constants, frm_lst, chunksize=1)):
|
|
1028
1025
|
results.update(result)
|
|
1029
1026
|
|
|
1030
|
-
pool
|
|
1031
|
-
pool.terminate()
|
|
1027
|
+
terminate_cpu_pool(pool=pool, force=False)
|
|
1032
1028
|
return results
|
|
1033
1029
|
|
|
1034
1030
|
@staticmethod
|
|
@@ -1509,8 +1505,8 @@ class ImageMixin(object):
|
|
|
1509
1505
|
for cnt, result in enumerate(pool.imap(constants, shapes, chunksize=1)):
|
|
1510
1506
|
results.append(result)
|
|
1511
1507
|
results = dict(ChainMap(*results))
|
|
1512
|
-
|
|
1513
|
-
pool
|
|
1508
|
+
|
|
1509
|
+
terminate_cpu_pool(pool=pool, force=False)
|
|
1514
1510
|
results = dict(sorted(results.items(), key=lambda item: int(item[0])))
|
|
1515
1511
|
timer.stop_timer()
|
|
1516
1512
|
stdout_success(msg="Geometry image slicing complete.", elapsed_time=timer.elapsed_time_str, source=self.__class__.__name__)
|
|
@@ -1902,7 +1898,7 @@ class ImageMixin(object):
|
|
|
1902
1898
|
:rtype: np.ndarray
|
|
1903
1899
|
|
|
1904
1900
|
:example:
|
|
1905
|
-
>>> VIDEO_PATH = r"D
|
|
1901
|
+
>>> VIDEO_PATH = r"D:/EPM_2/EPM_1.mp4"
|
|
1906
1902
|
>>> img = read_img_batch_from_video(video_path=VIDEO_PATH, greyscale=True, start_frm=0, end_frm=15, core_cnt=1)
|
|
1907
1903
|
>>> imgs = np.stack(list(img.values()))
|
|
1908
1904
|
>>> resized_img = resize_img_stack(imgs=imgs)
|