simba-uw-tf-dev 4.6.6__py3-none-any.whl → 4.6.8__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/data_processors/blob_location_computer.py +1 -1
- simba/data_processors/circling_detector.py +30 -13
- simba/data_processors/cuda/image.py +53 -25
- simba/data_processors/cuda/statistics.py +57 -19
- simba/data_processors/cuda/timeseries.py +1 -1
- simba/data_processors/egocentric_aligner.py +1 -1
- simba/data_processors/freezing_detector.py +54 -50
- simba/feature_extractors/feature_subsets.py +2 -2
- simba/feature_extractors/mitra_feature_extractor.py +2 -2
- simba/feature_extractors/straub_tail_analyzer.py +4 -4
- simba/labelling/standard_labeller.py +1 -1
- simba/mixins/config_reader.py +5 -2
- simba/mixins/geometry_mixin.py +8 -8
- simba/mixins/image_mixin.py +14 -14
- simba/mixins/plotting_mixin.py +28 -10
- simba/mixins/statistics_mixin.py +39 -9
- simba/mixins/timeseries_features_mixin.py +1 -1
- simba/mixins/train_model_mixin.py +65 -27
- 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/gantt_creator.py +29 -10
- simba/plotting/gantt_creator_mp.py +50 -17
- simba/plotting/heat_mapper_clf_mp.py +2 -2
- simba/pose_importers/simba_blob_importer.py +3 -3
- simba/roi_tools/roi_aggregate_stats_mp.py +1 -1
- simba/roi_tools/roi_clf_calculator_mp.py +1 -1
- 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/gantt_pop_up.py +31 -6
- simba/ui/pop_ups/video_processing_pop_up.py +1 -1
- simba/utils/custom_feature_extractor.py +1 -1
- simba/utils/data.py +2 -2
- simba/utils/read_write.py +32 -18
- 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 +3 -3
- simba/video_processors/multi_cropper.py +1 -1
- simba/video_processors/video_processing.py +27 -10
- simba/video_processors/videos_to_frames.py +2 -2
- {simba_uw_tf_dev-4.6.6.dist-info → simba_uw_tf_dev-4.6.8.dist-info}/METADATA +3 -2
- {simba_uw_tf_dev-4.6.6.dist-info → simba_uw_tf_dev-4.6.8.dist-info}/RECORD +49 -49
- {simba_uw_tf_dev-4.6.6.dist-info → simba_uw_tf_dev-4.6.8.dist-info}/LICENSE +0 -0
- {simba_uw_tf_dev-4.6.6.dist-info → simba_uw_tf_dev-4.6.8.dist-info}/WHEEL +0 -0
- {simba_uw_tf_dev-4.6.6.dist-info → simba_uw_tf_dev-4.6.8.dist-info}/entry_points.txt +0 -0
- {simba_uw_tf_dev-4.6.6.dist-info → simba_uw_tf_dev-4.6.8.dist-info}/top_level.txt +0 -0
|
@@ -51,7 +51,7 @@ class BlobLocationComputer(object):
|
|
|
51
51
|
:param Optional[bool] multiprocessing: If True, video background subtraction will be done using multiprocessing. Default is False.
|
|
52
52
|
|
|
53
53
|
:example:
|
|
54
|
-
>>> x = BlobLocationComputer(data_path=r"C
|
|
54
|
+
>>> x = BlobLocationComputer(data_path=r"C:/troubleshooting/RAT_NOR/project_folder/videos/2022-06-20_NOB_DOT_4_downsampled_bg_subtracted.mp4", multiprocessing=True, gpu=True, batch_size=2000, save_dir=r"C:/blob_positions")
|
|
55
55
|
>>> x.run()
|
|
56
56
|
"""
|
|
57
57
|
def __init__(self,
|
|
@@ -11,12 +11,13 @@ from simba.mixins.feature_extraction_mixin import FeatureExtractionMixin
|
|
|
11
11
|
from simba.mixins.timeseries_features_mixin import TimeseriesFeatureMixin
|
|
12
12
|
from simba.utils.checks import (
|
|
13
13
|
check_all_file_names_are_represented_in_video_log, check_if_dir_exists,
|
|
14
|
-
|
|
14
|
+
check_str, check_valid_dataframe)
|
|
15
15
|
from simba.utils.data import detect_bouts, plug_holes_shortest_bout
|
|
16
16
|
from simba.utils.enums import Formats
|
|
17
17
|
from simba.utils.printing import stdout_success
|
|
18
18
|
from simba.utils.read_write import (find_files_of_filetypes_in_directory,
|
|
19
|
-
get_fn_ext, read_df,
|
|
19
|
+
get_current_time, get_fn_ext, read_df,
|
|
20
|
+
read_video_info)
|
|
20
21
|
|
|
21
22
|
CIRCLING = 'CIRCLING'
|
|
22
23
|
|
|
@@ -58,30 +59,34 @@ class CirclingDetector(ConfigReader):
|
|
|
58
59
|
"""
|
|
59
60
|
|
|
60
61
|
def __init__(self,
|
|
61
|
-
data_dir: Union[str, os.PathLike],
|
|
62
62
|
config_path: Union[str, os.PathLike],
|
|
63
63
|
nose_name: Optional[str] = 'nose',
|
|
64
|
+
data_dir: Optional[Union[str, os.PathLike]] = None,
|
|
64
65
|
left_ear_name: Optional[str] = 'left_ear',
|
|
65
66
|
right_ear_name: Optional[str] = 'right_ear',
|
|
66
67
|
tail_base_name: Optional[str] = 'tail_base',
|
|
67
68
|
center_name: Optional[str] = 'center',
|
|
68
|
-
time_threshold: Optional[int] =
|
|
69
|
-
circular_range_threshold: Optional[int] =
|
|
69
|
+
time_threshold: Optional[int] = 7,
|
|
70
|
+
circular_range_threshold: Optional[int] = 350,
|
|
71
|
+
shortest_bout: int = 100,
|
|
70
72
|
movement_threshold: Optional[int] = 60,
|
|
71
73
|
save_dir: Optional[Union[str, os.PathLike]] = None):
|
|
72
74
|
|
|
73
|
-
check_if_dir_exists(in_dir=data_dir)
|
|
74
75
|
for bp_name in [nose_name, left_ear_name, right_ear_name, tail_base_name]: check_str(name='body part name', value=bp_name, allow_blank=False)
|
|
75
|
-
self.data_paths = find_files_of_filetypes_in_directory(directory=data_dir, extensions=['.csv'])
|
|
76
76
|
ConfigReader.__init__(self, config_path=config_path, read_video_info=True, create_logger=False)
|
|
77
|
+
if data_dir is not None:
|
|
78
|
+
check_if_dir_exists(in_dir=data_dir)
|
|
79
|
+
else:
|
|
80
|
+
data_dir = self.outlier_corrected_dir
|
|
81
|
+
self.data_paths = find_files_of_filetypes_in_directory(directory=data_dir, extensions=['.csv'])
|
|
77
82
|
self.nose_heads = [f'{nose_name}_x'.lower(), f'{nose_name}_y'.lower()]
|
|
78
83
|
self.left_ear_heads = [f'{left_ear_name}_x'.lower(), f'{left_ear_name}_y'.lower()]
|
|
79
84
|
self.right_ear_heads = [f'{right_ear_name}_x'.lower(), f'{right_ear_name}_y'.lower()]
|
|
80
85
|
self.center_heads = [f'{center_name}_x'.lower(), f'{center_name}_y'.lower()]
|
|
81
86
|
self.required_field = self.nose_heads + self.left_ear_heads + self.right_ear_heads
|
|
82
|
-
self.save_dir = save_dir
|
|
87
|
+
self.save_dir, self.shortest_bout = save_dir, shortest_bout
|
|
83
88
|
if self.save_dir is None:
|
|
84
|
-
self.save_dir = os.path.join(self.logs_path, f'circling_data_{self.datetime}')
|
|
89
|
+
self.save_dir = os.path.join(self.logs_path, f'circling_data_{time_threshold}s_{circular_range_threshold}d_{movement_threshold}mm_{self.datetime}')
|
|
85
90
|
os.makedirs(self.save_dir)
|
|
86
91
|
else:
|
|
87
92
|
check_if_dir_exists(in_dir=self.save_dir)
|
|
@@ -93,7 +98,7 @@ class CirclingDetector(ConfigReader):
|
|
|
93
98
|
check_all_file_names_are_represented_in_video_log(video_info_df=self.video_info_df, data_paths=self.data_paths)
|
|
94
99
|
for file_cnt, file_path in enumerate(self.data_paths):
|
|
95
100
|
video_name = get_fn_ext(filepath=file_path)[1]
|
|
96
|
-
print(f'Analyzing {video_name} ({file_cnt+1}/{len(self.data_paths)})
|
|
101
|
+
print(f'[{get_current_time()}] Analyzing circling {video_name}... (video {file_cnt+1}/{len(self.data_paths)})')
|
|
97
102
|
save_file_path = os.path.join(self.save_dir, f'{video_name}.csv')
|
|
98
103
|
df = read_df(file_path=file_path, file_type='csv').reset_index(drop=True)
|
|
99
104
|
_, px_per_mm, fps = read_video_info(video_info_df=self.video_info_df, video_name=video_name)
|
|
@@ -115,11 +120,24 @@ class CirclingDetector(ConfigReader):
|
|
|
115
120
|
circling_idx = np.argwhere(sliding_circular_range >= self.circular_range_threshold).astype(np.int32).flatten()
|
|
116
121
|
movement_idx = np.argwhere(movement_sum >= self.movement_threshold).astype(np.int32).flatten()
|
|
117
122
|
circling_idx = [x for x in movement_idx if x in circling_idx]
|
|
123
|
+
df[f'Probability_{CIRCLING}'] = 0
|
|
118
124
|
df[CIRCLING] = 0
|
|
119
125
|
df.loc[circling_idx, CIRCLING] = 1
|
|
126
|
+
df.loc[circling_idx, f'Probability_{CIRCLING}'] = 1
|
|
127
|
+
df = plug_holes_shortest_bout(data_df=df, clf_name=CIRCLING, fps=fps, shortest_bout=self.shortest_bout)
|
|
120
128
|
bouts = detect_bouts(data_df=df, target_lst=[CIRCLING], fps=fps)
|
|
121
|
-
|
|
129
|
+
if len(bouts) > 0:
|
|
130
|
+
df[CIRCLING] = 0
|
|
131
|
+
circling_idx = list(bouts.apply(lambda x: list(range(int(x["Start_frame"]), int(x["End_frame"]) + 1)), 1))
|
|
132
|
+
circling_idx = [x for xs in circling_idx for x in xs]
|
|
133
|
+
df.loc[circling_idx, CIRCLING] = 1
|
|
134
|
+
df.loc[circling_idx, f'Probability_{CIRCLING}'] = 1
|
|
135
|
+
else:
|
|
136
|
+
df[CIRCLING] = 0
|
|
137
|
+
circling_idx = []
|
|
138
|
+
|
|
122
139
|
df.to_csv(save_file_path)
|
|
140
|
+
#print(video_name, len(circling_idx), round(len(circling_idx) / fps, 4), df[CIRCLING].sum())
|
|
123
141
|
agg_results.loc[len(agg_results)] = [video_name, len(circling_idx), round(len(circling_idx) / fps, 4), len(bouts), round((len(circling_idx) / len(df)) * 100, 4), len(df), round(len(df)/fps, 2) ]
|
|
124
142
|
|
|
125
143
|
agg_results.to_csv(agg_results_path)
|
|
@@ -127,7 +145,6 @@ class CirclingDetector(ConfigReader):
|
|
|
127
145
|
|
|
128
146
|
#
|
|
129
147
|
#
|
|
130
|
-
# detector = CirclingDetector(
|
|
131
|
-
# config_path=r"C:\troubleshooting\mitra\project_folder\project_config.ini")
|
|
148
|
+
# detector = CirclingDetector(config_path=r"E:\troubleshooting\mitra_emergence\project_folder\project_config.ini")
|
|
132
149
|
# detector.run()
|
|
133
150
|
|
|
@@ -331,10 +331,22 @@ def _digital(data, results):
|
|
|
331
331
|
|
|
332
332
|
def img_stack_brightness(x: np.ndarray,
|
|
333
333
|
method: Optional[Literal['photometric', 'digital']] = 'digital',
|
|
334
|
-
ignore_black:
|
|
334
|
+
ignore_black: bool = True,
|
|
335
|
+
verbose: bool = False,
|
|
336
|
+
batch_size: int = 2500) -> np.ndarray:
|
|
335
337
|
"""
|
|
336
338
|
Calculate the average brightness of a stack of images using a specified method.
|
|
337
339
|
|
|
340
|
+
Useful for analyzing light cues or brightness changes over time. For example, compute brightness in images containing a light cue ROI, then perform clustering (e.g., k-means) on brightness values to identify frames when the light cue is on vs off.
|
|
341
|
+
|
|
342
|
+
.. csv-table::
|
|
343
|
+
:header: EXPECTED RUNTIMES
|
|
344
|
+
:file: ../../../docs/tables/img_stack_brightness_gpu.csv
|
|
345
|
+
:widths: 10, 45, 45
|
|
346
|
+
:align: center
|
|
347
|
+
:class: simba-table
|
|
348
|
+
:header-rows: 1
|
|
349
|
+
|
|
338
350
|
|
|
339
351
|
- **Photometric Method**: The brightness is calculated using the formula:
|
|
340
352
|
|
|
@@ -346,7 +358,7 @@ def img_stack_brightness(x: np.ndarray,
|
|
|
346
358
|
.. math::
|
|
347
359
|
\text{brightness} = 0.299 \cdot R + 0.587 \cdot G + 0.114 \cdot B
|
|
348
360
|
|
|
349
|
-
..
|
|
361
|
+
.. seealso::
|
|
350
362
|
For CPU function see :func:`~simba.mixins.image_mixin.ImageMixin.brightness_intensity`.
|
|
351
363
|
|
|
352
364
|
:param np.ndarray x: A 4D array of images with dimensions (N, H, W, C), where N is the number of images, H and W are the height and width, and C is the number of channels (RGB).
|
|
@@ -363,27 +375,42 @@ def img_stack_brightness(x: np.ndarray,
|
|
|
363
375
|
|
|
364
376
|
check_instance(source=img_stack_brightness.__name__, instance=x, accepted_types=(np.ndarray,))
|
|
365
377
|
check_if_valid_img(data=x[0], source=img_stack_brightness.__name__)
|
|
366
|
-
|
|
378
|
+
check_int(name=f'{img_stack_brightness.__name__} batch_size', value=batch_size, allow_zero=False, allow_negative=False, raise_error=True)
|
|
379
|
+
x, timer = np.ascontiguousarray(x).astype(np.uint8), SimbaTimer(start=True)
|
|
380
|
+
results = []
|
|
367
381
|
if x.ndim == 4:
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
382
|
+
batch_results_dev = cuda.device_array((batch_size, x.shape[1], x.shape[2]), dtype=np.uint8)
|
|
383
|
+
for batch_cnt, l in enumerate(range(0, x.shape[0], batch_size)):
|
|
384
|
+
r = l + batch_size
|
|
385
|
+
batch_x = x[l:r]
|
|
386
|
+
if batch_x.ndim == 4:
|
|
387
|
+
grid_x = (batch_x.shape[1] + 16 - 1) // 16
|
|
388
|
+
grid_y = (batch_x.shape[2] + 16 - 1) // 16
|
|
389
|
+
grid_z = batch_x.shape[0]
|
|
390
|
+
threads_per_block = (16, 16, 1)
|
|
391
|
+
blocks_per_grid = (grid_y, grid_x, grid_z)
|
|
392
|
+
x_dev = cuda.to_device(batch_x)
|
|
393
|
+
if method == PHOTOMETRIC:
|
|
394
|
+
_photometric[blocks_per_grid, threads_per_block](x_dev, batch_results_dev)
|
|
395
|
+
else:
|
|
396
|
+
_digital[blocks_per_grid, threads_per_block](x_dev, batch_results_dev)
|
|
397
|
+
batch_results_host = batch_results_dev.copy_to_host()[:batch_x.shape[0]]
|
|
398
|
+
batch_results_cp = cp.asarray(batch_results_host)
|
|
399
|
+
if ignore_black:
|
|
400
|
+
mask = batch_results_cp != 0
|
|
401
|
+
batch_results_cp = cp.where(mask, batch_results_cp, cp.nan)
|
|
402
|
+
batch_results = cp.nanmean(batch_results_cp, axis=(1, 2))
|
|
403
|
+
batch_results = cp.where(cp.isnan(batch_results), 0, batch_results)
|
|
404
|
+
batch_results = batch_results.get()
|
|
405
|
+
else:
|
|
406
|
+
batch_results = cp.mean(batch_results_cp, axis=(1, 2)).get()
|
|
377
407
|
else:
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
results = deepcopy(x)
|
|
385
|
-
results = np.mean(results, axis=(1, 2))
|
|
386
|
-
|
|
408
|
+
batch_results = deepcopy(x)
|
|
409
|
+
batch_results = np.mean(batch_results, axis=(1, 2))
|
|
410
|
+
results.append(batch_results)
|
|
411
|
+
timer.stop_timer()
|
|
412
|
+
results = np.concatenate(results) if len(results) > 0 else np.array([])
|
|
413
|
+
if verbose: print(f'Brightness computed in {results.shape[0]} images (elapsed time {timer.elapsed_time_str}s)')
|
|
387
414
|
return results
|
|
388
415
|
|
|
389
416
|
|
|
@@ -1602,10 +1629,11 @@ def pose_plotter(data: Union[str, os.PathLike, np.ndarray],
|
|
|
1602
1629
|
# SAVE_PATH = "/mnt/c/troubleshooting/mitra/project_folder/frames/output/pose_ex/test.mp4"
|
|
1603
1630
|
#
|
|
1604
1631
|
#
|
|
1605
|
-
|
|
1606
|
-
|
|
1607
|
-
|
|
1608
|
-
|
|
1632
|
+
if __name__ == "__main__":
|
|
1633
|
+
DATA_PATH = "/mnt/d/troubleshooting/mitra/project_folder/csv/outlier_corrected_movement_location/592_MA147_CNO1_0515.csv"
|
|
1634
|
+
VIDEO_PATH = "/mnt/d/troubleshooting/mitra/project_folder/videos/592_MA147_CNO1_0515.mp4"
|
|
1635
|
+
SAVE_PATH = "/mnt/d/troubleshooting/mitra/project_folder/videos/test_cuda.mp4"
|
|
1636
|
+
pose_plotter(data=DATA_PATH, video_path=VIDEO_PATH, save_path=SAVE_PATH, circle_size=10, batch_size=100)
|
|
1609
1637
|
|
|
1610
1638
|
|
|
1611
1639
|
|
|
@@ -3,7 +3,7 @@ __author__ = "Simon Nilsson; sronilsson@gmail.com"
|
|
|
3
3
|
|
|
4
4
|
import math
|
|
5
5
|
from itertools import combinations
|
|
6
|
-
from typing import Optional, Tuple
|
|
6
|
+
from typing import Optional, Tuple, Union
|
|
7
7
|
|
|
8
8
|
from simba.utils.printing import SimbaTimer
|
|
9
9
|
|
|
@@ -21,14 +21,17 @@ from simba.utils.warnings import GPUToolsWarning
|
|
|
21
21
|
|
|
22
22
|
try:
|
|
23
23
|
import cupy as cp
|
|
24
|
-
from cuml.metrics import kl_divergence as kl_divergence_gpu
|
|
25
|
-
#from cuml.metrics.cluster.adjusted_rand_index import adjusted_rand_score
|
|
26
|
-
#from cuml.metrics.cluster.silhouette_score import cython_silhouette_score
|
|
27
24
|
from cupyx.scipy.spatial.distance import cdist
|
|
28
25
|
except Exception as e:
|
|
29
26
|
GPUToolsWarning(msg=f'GPU tools not detected, reverting to CPU: {e.args}')
|
|
30
27
|
import numpy as cp
|
|
31
28
|
from scipy.spatial.distance import cdist
|
|
29
|
+
try:
|
|
30
|
+
from cuml.metrics import kl_divergence as kl_divergence_gpu
|
|
31
|
+
from cuml.metrics.cluster.adjusted_rand_index import adjusted_rand_score
|
|
32
|
+
from cuml.metrics.cluster.silhouette_score import cython_silhouette_score
|
|
33
|
+
except Exception as e:
|
|
34
|
+
GPUToolsWarning(msg=f'GPU tools not detected, reverting to CPU: {e.args}')
|
|
32
35
|
from scipy.stats import entropy as kl_divergence_gpu
|
|
33
36
|
from sklearn.metrics import adjusted_rand_score
|
|
34
37
|
from sklearn.metrics import silhouette_score as cython_silhouette_score
|
|
@@ -40,8 +43,8 @@ except:
|
|
|
40
43
|
|
|
41
44
|
from simba.data_processors.cuda.utils import _cuda_are_rows_equal
|
|
42
45
|
from simba.mixins.statistics_mixin import Statistics
|
|
43
|
-
from simba.utils.checks import (check_int, check_str,
|
|
44
|
-
check_valid_tuple)
|
|
46
|
+
from simba.utils.checks import (check_float, check_int, check_str,
|
|
47
|
+
check_valid_array, check_valid_tuple)
|
|
45
48
|
from simba.utils.data import bucket_data
|
|
46
49
|
from simba.utils.enums import Formats
|
|
47
50
|
|
|
@@ -381,9 +384,10 @@ def sliding_min(x: np.ndarray, time_window: float, sample_rate: int) -> np.ndarr
|
|
|
381
384
|
|
|
382
385
|
def sliding_spearmans_rank(x: np.ndarray,
|
|
383
386
|
y: np.ndarray,
|
|
384
|
-
time_window: float,
|
|
385
|
-
sample_rate: int,
|
|
386
|
-
batch_size: Optional[int] = int(1.6e+7)
|
|
387
|
+
time_window: Union[float, int],
|
|
388
|
+
sample_rate: Union[float, int],
|
|
389
|
+
batch_size: Optional[int] = int(1.6e+7),
|
|
390
|
+
verbose: bool = False) -> np.ndarray:
|
|
387
391
|
"""
|
|
388
392
|
Computes the Spearman's rank correlation coefficient between two 1D arrays `x` and `y`
|
|
389
393
|
over sliding windows of size `time_window * sample_rate`. The computation is performed
|
|
@@ -414,7 +418,13 @@ def sliding_spearmans_rank(x: np.ndarray,
|
|
|
414
418
|
>>> sliding_spearmans_rank(x, y, time_window=0.5, sample_rate=2)
|
|
415
419
|
"""
|
|
416
420
|
|
|
417
|
-
|
|
421
|
+
timer = SimbaTimer(start=True)
|
|
422
|
+
check_valid_array(data=x, source=f'{sliding_spearmans_rank.__name__} x', accepted_ndims=(1,), accepted_dtypes=Formats.NUMERIC_DTYPES.value)
|
|
423
|
+
check_valid_array(data=y, source=f'{sliding_spearmans_rank.__name__} y', accepted_ndims=(1,), accepted_axis_0_shape=(x.shape[0],), accepted_dtypes=Formats.NUMERIC_DTYPES.value)
|
|
424
|
+
check_float(name=f'{sliding_spearmans_rank.__name__} time_window', value=time_window, allow_zero=False, allow_negative=False, raise_error=True)
|
|
425
|
+
check_float(name=f'{sliding_spearmans_rank.__name__} sample_rate', value=sample_rate, allow_zero=False, allow_negative=False, raise_error=True)
|
|
426
|
+
check_int(name=f'{sliding_spearmans_rank.__name__} batch_size', value=batch_size, allow_zero=False, allow_negative=False, raise_error=True)
|
|
427
|
+
window_size = np.int32(np.ceil(time_window * sample_rate))
|
|
418
428
|
n = x.shape[0]
|
|
419
429
|
results = cp.full(n, -1, dtype=cp.float32)
|
|
420
430
|
|
|
@@ -434,7 +444,11 @@ def sliding_spearmans_rank(x: np.ndarray,
|
|
|
434
444
|
|
|
435
445
|
results[left + window_size - 1:right] = s
|
|
436
446
|
|
|
437
|
-
|
|
447
|
+
r = cp.asnumpy(results)
|
|
448
|
+
timer.stop_timer()
|
|
449
|
+
if verbose: print(f'Sliding Spearmans rank for {x.shape[0]} observations computed (elapsed time: {timer.elapsed_time_str}s)')
|
|
450
|
+
return r
|
|
451
|
+
|
|
438
452
|
|
|
439
453
|
|
|
440
454
|
|
|
@@ -539,6 +553,12 @@ def euclidean_distance_to_static_point(data: np.ndarray,
|
|
|
539
553
|
"""
|
|
540
554
|
Computes the Euclidean distance between each point in a given 2D array `data` and a static point using GPU acceleration.
|
|
541
555
|
|
|
556
|
+
.. seealso::
|
|
557
|
+
For CPU-based distance to static point (ROI center), see :func:`simba.mixins.feature_extraction_mixin.FeatureExtractionMixin.framewise_euclidean_distance_roi`
|
|
558
|
+
For CPU-based framewise Euclidean distance, see :func:`simba.mixins.feature_extraction_mixin.FeatureExtractionMixin.framewise_euclidean_distance`
|
|
559
|
+
For GPU CuPy solution for distance between two sets of points, see :func:`simba.data_processors.cuda.statistics.get_euclidean_distance_cupy`
|
|
560
|
+
For GPU numba CUDA solution for distance between two sets of points, see :func:`simba.data_processors.cuda.statistics.get_euclidean_distance_cuda`
|
|
561
|
+
|
|
542
562
|
:param data: A 2D array of shape (N, 2), where N is the number of points, and each point is represented by its (x, y) coordinates. The array can represent pixel coordinates.
|
|
543
563
|
:param point: A tuple of two integers representing the static point (x, y) in the same space as `data`.
|
|
544
564
|
:param pixels_per_millimeter: A scaling factor that indicates how many pixels correspond to one millimeter. Defaults to 1 if no scaling is necessary.
|
|
@@ -790,13 +810,31 @@ def xie_beni(x: np.ndarray, y: np.ndarray) -> float:
|
|
|
790
810
|
return xb
|
|
791
811
|
|
|
792
812
|
|
|
793
|
-
def i_index(x: np.ndarray, y: np.ndarray):
|
|
813
|
+
def i_index(x: np.ndarray, y: np.ndarray, verbose: bool = False) -> float:
|
|
794
814
|
"""
|
|
795
815
|
Calculate the I-Index for evaluating clustering quality.
|
|
796
816
|
|
|
797
817
|
The I-Index is a metric that measures the compactness and separation of clusters.
|
|
798
818
|
A higher I-Index indicates better clustering with compact and well-separated clusters.
|
|
799
819
|
|
|
820
|
+
.. csv-table::
|
|
821
|
+
:header: EXPECTED RUNTIMES
|
|
822
|
+
:file: ../../../docs/tables/i_index_cuda.csv
|
|
823
|
+
:widths: 10, 45, 45
|
|
824
|
+
:align: center
|
|
825
|
+
:header-rows: 1
|
|
826
|
+
|
|
827
|
+
The I-Index is calculated as:
|
|
828
|
+
|
|
829
|
+
.. math::
|
|
830
|
+
I = \frac{SST}{k \times SWC}
|
|
831
|
+
|
|
832
|
+
where:
|
|
833
|
+
|
|
834
|
+
- :math:`SST = \sum_{i=1}^{n} \|x_i - \mu\|^2` is the total sum of squares (sum of squared distances from all points to the global centroid)
|
|
835
|
+
- :math:`k` is the number of clusters
|
|
836
|
+
- :math:`SWC = \sum_{c=1}^{k} \sum_{i \in c} \|x_i - \mu_c\|^2` is the within-cluster sum of squares (sum of squared distances from points to their cluster centroids)
|
|
837
|
+
|
|
800
838
|
.. seealso::
|
|
801
839
|
To compute Xie-Beni on the CPU, use :func:`~simba.mixins.statistics_mixin.Statistics.i_index`
|
|
802
840
|
|
|
@@ -807,17 +845,16 @@ def i_index(x: np.ndarray, y: np.ndarray):
|
|
|
807
845
|
|
|
808
846
|
:references:
|
|
809
847
|
.. [1] Zhao, Q., Xu, M., Fränti, P. (2009). Sum-of-Squares Based Cluster Validity Index and Significance Analysis.
|
|
810
|
-
In: Kolehmainen, M., Toivanen, P., Beliczynski, B. (eds) Adaptive and Natural Computing Algorithms. ICANNGA 2009.
|
|
811
|
-
Lecture Notes in Computer Science, vol 5495. Springer, Berlin, Heidelberg. https://doi.org/10.1007/978-3-642-04921-7_32
|
|
848
|
+
In: Kolehmainen, M., Toivanen, P., Beliczynski, B. (eds) Adaptive and Natural Computing Algorithms. ICANNGA 2009. Lecture Notes in Computer Science, vol 5495. Springer, Berlin, Heidelberg. https://doi.org/10.1007/978-3-642-04921-7_32
|
|
812
849
|
|
|
813
850
|
:example:
|
|
814
851
|
>>> X, y = make_blobs(n_samples=5000, centers=20, n_features=3, random_state=0, cluster_std=0.1)
|
|
815
852
|
>>> i_index(x=X, y=y)
|
|
816
853
|
"""
|
|
854
|
+
timer = SimbaTimer(start=True)
|
|
817
855
|
check_valid_array(data=x, accepted_ndims=(2,), accepted_dtypes=Formats.NUMERIC_DTYPES.value)
|
|
818
|
-
check_valid_array(data=y, accepted_ndims=(1,), accepted_dtypes=Formats.NUMERIC_DTYPES.value,
|
|
819
|
-
|
|
820
|
-
_ = get_unique_values_in_iterable(data=y, name=i_index.__name__, min=2)
|
|
856
|
+
check_valid_array(data=y, accepted_ndims=(1,), accepted_dtypes=Formats.NUMERIC_DTYPES.value, accepted_axis_0_shape=[x.shape[0], ])
|
|
857
|
+
cluster_cnt = get_unique_values_in_iterable(data=y, name=i_index.__name__, min=2)
|
|
821
858
|
x, y = cp.array(x), cp.array(y)
|
|
822
859
|
unique_y = cp.unique(y)
|
|
823
860
|
n_y = unique_y.shape[0]
|
|
@@ -831,10 +868,11 @@ def i_index(x: np.ndarray, y: np.ndarray):
|
|
|
831
868
|
swc += cp.sum(cp.linalg.norm(cluster_obs - cluster_centroid, axis=1) ** 2)
|
|
832
869
|
|
|
833
870
|
i_idx = sst / (n_y * swc)
|
|
834
|
-
|
|
871
|
+
i_idx = np.float32(i_idx.get()) if hasattr(i_idx, 'get') else np.float32(i_idx)
|
|
872
|
+
timer.stop_timer()
|
|
873
|
+
if verbose: print(f'I-index for {x.shape[0]} observations in {cluster_cnt} clusters computed (elapsed time: {timer.elapsed_time_str}s)')
|
|
835
874
|
return i_idx
|
|
836
875
|
|
|
837
|
-
|
|
838
876
|
def kullback_leibler_divergence_gpu(x: np.ndarray,
|
|
839
877
|
y: np.ndarray,
|
|
840
878
|
fill_value: int = 1,
|
|
@@ -307,7 +307,7 @@ def sliding_hjort_parameters_gpu(data: np.ndarray, window_sizes: np.ndarray, sam
|
|
|
307
307
|
"""
|
|
308
308
|
Compute Hjorth parameters over sliding windows on the GPU.
|
|
309
309
|
|
|
310
|
-
..
|
|
310
|
+
.. seealso::
|
|
311
311
|
For CPU implementation, see :`simba.mixins.timeseries_features_mixin.TimeseriesFeatureMixin.hjort_parameters`
|
|
312
312
|
|
|
313
313
|
:param np.ndarray data: 1D numeric array of signal data.
|
|
@@ -52,7 +52,7 @@ class EgocentricalAligner():
|
|
|
52
52
|
:param Optional[int] core_cnt: Number of CPU cores to use for video rotation; `-1` uses all available cores.
|
|
53
53
|
|
|
54
54
|
:example:
|
|
55
|
-
>>> aligner = EgocentricalAligner(rotate_video=True, anchor_1='tail_base', anchor_2='nose', data_dir=r"/data_dir", videos_dir=r'/videos_dir', save_dir=r"/save_dir", video_info=r"C
|
|
55
|
+
>>> aligner = EgocentricalAligner(rotate_video=True, anchor_1='tail_base', anchor_2='nose', data_dir=r"/data_dir", videos_dir=r'/videos_dir', save_dir=r"/save_dir", video_info=r"C:/troubleshooting/mitra/project_folder/logs/video_info.csv", direction=0, anchor_location=(250, 250), fill_clr=(0, 0, 0))
|
|
56
56
|
>>> aligner.run()
|
|
57
57
|
"""
|
|
58
58
|
|
|
@@ -1,10 +1,8 @@
|
|
|
1
1
|
import os
|
|
2
2
|
from typing import Optional, Union
|
|
3
|
-
|
|
4
3
|
import numpy as np
|
|
5
4
|
import pandas as pd
|
|
6
5
|
from numba import typed
|
|
7
|
-
|
|
8
6
|
from simba.mixins.config_reader import ConfigReader
|
|
9
7
|
from simba.mixins.feature_extraction_mixin import FeatureExtractionMixin
|
|
10
8
|
from simba.mixins.timeseries_features_mixin import TimeseriesFeatureMixin
|
|
@@ -14,65 +12,73 @@ from simba.utils.checks import (
|
|
|
14
12
|
from simba.utils.data import detect_bouts, plug_holes_shortest_bout
|
|
15
13
|
from simba.utils.enums import Formats
|
|
16
14
|
from simba.utils.printing import stdout_success
|
|
17
|
-
from simba.utils.read_write import (find_files_of_filetypes_in_directory,
|
|
18
|
-
get_fn_ext, read_df, read_video_info)
|
|
15
|
+
from simba.utils.read_write import (find_files_of_filetypes_in_directory, get_fn_ext, read_df, read_video_info, get_current_time)
|
|
19
16
|
|
|
20
17
|
NAPE_X, NAPE_Y = 'nape_x', 'nape_y'
|
|
21
18
|
FREEZING = 'FREEZING'
|
|
22
19
|
|
|
23
20
|
class FreezingDetector(ConfigReader):
|
|
24
|
-
|
|
25
21
|
"""
|
|
26
|
-
Detect freezing behavior using heuristic rules.
|
|
27
|
-
|
|
22
|
+
Detect freezing behavior using heuristic rules based on movement velocity thresholds.
|
|
23
|
+
Analyzes pose-estimation data to detect freezing episodes by computing the mean velocity
|
|
24
|
+
of key body parts (nape, nose, and tail-base) and identifying periods where movement falls below
|
|
25
|
+
a specified threshold for a minimum duration.
|
|
28
26
|
.. important::
|
|
29
|
-
|
|
30
27
|
Freezing is detected as `present` when **the velocity (computed from the mean movement of the nape, nose, and tail-base body-parts) falls below
|
|
31
|
-
the movement threshold for the duration (and longer) of the
|
|
32
|
-
|
|
28
|
+
the movement threshold for the duration (and longer) of the specified time-window**.
|
|
33
29
|
Freezing is detected as `absent` when not present.
|
|
34
|
-
|
|
35
30
|
.. note::
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
:param Union[str, os.PathLike] data_dir: Path to directory containing pose-estimated body-part data in CSV format.
|
|
40
|
-
:param Union[str, os.PathLike] config_path: Path to SimBA project config file.
|
|
41
|
-
:param Optional[str] nose_name: The name of the pose-estimated nose body-part. Defaults to 'nose'.
|
|
42
|
-
:param Optional[str] left_ear_name: The name of the pose-estimated left ear body-part. Defaults to '
|
|
43
|
-
:param Optional[str] right_ear_name: The name of the pose-estimated right ear body-part. Defaults to 'right_ear'.
|
|
44
|
-
:param Optional[str] tail_base_name: The name of the pose-estimated tail base body-part. Defaults to 'tail_base'.
|
|
45
|
-
:param Optional[int] time_window: The time window in
|
|
46
|
-
:param Optional[int] movement_threshold:
|
|
47
|
-
:param Optional[
|
|
48
|
-
|
|
31
|
+
The method uses the left and right ear body-parts to compute the `nape` location of the animal
|
|
32
|
+
as the midpoint between the ears. The nape, nose, and tail-base movements are averaged to compute
|
|
33
|
+
overall animal movement velocity.
|
|
34
|
+
:param Union[str, os.PathLike] data_dir: Path to directory containing pose-estimated body-part data in CSV format. Each CSV file should contain pose estimation data for one video.
|
|
35
|
+
:param Union[str, os.PathLike] config_path: Path to SimBA project config file (`.ini` format) containing project settings and video information.
|
|
36
|
+
:param Optional[str] nose_name: The name of the pose-estimated nose body-part column (without _x/_y suffix). Defaults to 'nose'.
|
|
37
|
+
:param Optional[str] left_ear_name: The name of the pose-estimated left ear body-part column (without _x/_y suffix). Defaults to 'Left_ear'.
|
|
38
|
+
:param Optional[str] right_ear_name: The name of the pose-estimated right ear body-part column (without _x/_y suffix). Defaults to 'right_ear'.
|
|
39
|
+
:param Optional[str] tail_base_name: The name of the pose-estimated tail base body-part column (without _x/_y suffix). Defaults to 'tail_base'.
|
|
40
|
+
:param Optional[int] time_window: The minimum time window in seconds that movement must be below the threshold to be considered freezing. Only freezing bouts lasting at least this duration are retained. Defaults to 3.
|
|
41
|
+
:param Optional[int] movement_threshold: Movement threshold in millimeters per second. Frames with mean velocity below this threshold are considered potential freezing. Defaults to 5.
|
|
42
|
+
:param Optional[int] shortest_bout: Minimum duration in milliseconds for a freezing bout to be considered valid. Shorter bouts are filtered out. Defaults to 100.
|
|
43
|
+
:param Optional[Union[str, os.PathLike]] save_dir: Directory where to store the results. If None, then results are stored in a timestamped subdirectory within the ``logs`` directory of the SimBA project.
|
|
44
|
+
:returns: None. Results are saved to CSV files in the specified save directory:
|
|
45
|
+
- Individual video results: One CSV file per video with freezing annotations added as a 'FREEZING' column (1 = freezing, 0 = not freezing)
|
|
46
|
+
- Aggregate results: `aggregate_freezing_results.csv` containing summary statistics for all videos
|
|
49
47
|
:example:
|
|
50
|
-
>>> FreezingDetector(
|
|
51
|
-
|
|
48
|
+
>>> FreezingDetector(
|
|
49
|
+
... data_dir=r'D:\\troubleshooting\\mitra\\project_folder\\csv\\outlier_corrected_movement_location',
|
|
50
|
+
... config_path=r"D:\\troubleshooting\\mitra\\project_folder\\project_config.ini",
|
|
51
|
+
... time_window=3,
|
|
52
|
+
... movement_threshold=5,
|
|
53
|
+
... shortest_bout=100
|
|
54
|
+
... ).run()
|
|
52
55
|
References
|
|
53
56
|
----------
|
|
57
|
+
..
|
|
54
58
|
.. [1] Sabnis et al., Visual detection of seizures in mice using supervised machine learning, `biorxiv`, doi: https://doi.org/10.1101/2024.05.29.596520.
|
|
55
59
|
.. [2] Lopez et al., Region-specific Nucleus Accumbens Dopamine Signals Encode Distinct Aspects of Avoidance Learning, `biorxiv`, doi: https://doi.org/10.1101/2024.08.28.610149
|
|
56
|
-
.. [3] Lopez, Gabriela C., Louis D. Van Camp, Ryan F. Kovaleski, et al.
|
|
60
|
+
.. [3] Lopez, Gabriela C., Louis D. Van Camp, Ryan F. Kovaleski, et al. "Region-Specific Nucleus Accumbens Dopamine Signals Encode Distinct Aspects of Avoidance Learning." `Cell Biology`, Volume 35, Issue 10p2433-2443.e5May 19, 2025. DOI: 10.1016/j.cub.2025.04.006
|
|
57
61
|
.. [4] Lazaro et al., Brainwide Genetic Capture for Conscious State Transitions, `biorxiv`, doi: https://doi.org/10.1101/2025.03.28.646066
|
|
62
|
+
.. [5] Sabnis et al., Visual detection of seizures in mice using supervised machine learning, 2025, Cell Reports Methods 5, 101242 December 15, 2025.
|
|
58
63
|
"""
|
|
59
|
-
|
|
60
64
|
def __init__(self,
|
|
61
|
-
data_dir: Union[str, os.PathLike],
|
|
62
65
|
config_path: Union[str, os.PathLike],
|
|
63
|
-
nose_name:
|
|
64
|
-
left_ear_name:
|
|
65
|
-
right_ear_name:
|
|
66
|
-
tail_base_name:
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
66
|
+
nose_name: str = 'nose',
|
|
67
|
+
left_ear_name: str = 'Left_ear',
|
|
68
|
+
right_ear_name: str = 'right_ear',
|
|
69
|
+
tail_base_name: str = 'tail_base',
|
|
70
|
+
data_dir: Optional[Union[str, os.PathLike]] = None,
|
|
71
|
+
time_window: int = 4,
|
|
72
|
+
movement_threshold: int = 5,
|
|
73
|
+
shortest_bout: int = 100,
|
|
70
74
|
save_dir: Optional[Union[str, os.PathLike]] = None):
|
|
71
|
-
|
|
72
|
-
check_if_dir_exists(in_dir=data_dir)
|
|
73
75
|
for bp_name in [nose_name, left_ear_name, right_ear_name, tail_base_name]: check_str(name='body part name', value=bp_name, allow_blank=False)
|
|
74
|
-
self.data_paths = find_files_of_filetypes_in_directory(directory=data_dir, extensions=['.csv'])
|
|
75
76
|
ConfigReader.__init__(self, config_path=config_path, read_video_info=True, create_logger=False)
|
|
77
|
+
if data_dir is not None:
|
|
78
|
+
check_if_dir_exists(in_dir=data_dir)
|
|
79
|
+
else:
|
|
80
|
+
data_dir = self.outlier_corrected_dir
|
|
81
|
+
self.data_paths = find_files_of_filetypes_in_directory(directory=data_dir, extensions=['.csv'])
|
|
76
82
|
self.nose_heads = [f'{nose_name}_x'.lower(), f'{nose_name}_y'.lower()]
|
|
77
83
|
self.left_ear_heads = [f'{left_ear_name}_x'.lower(), f'{left_ear_name}_y'.lower()]
|
|
78
84
|
self.right_ear_heads = [f'{right_ear_name}_x'.lower(), f'{right_ear_name}_y'.lower()]
|
|
@@ -82,21 +88,19 @@ class FreezingDetector(ConfigReader):
|
|
|
82
88
|
check_int(name='movement_threshold', value=movement_threshold, min_value=1)
|
|
83
89
|
self.save_dir = save_dir
|
|
84
90
|
if self.save_dir is None:
|
|
85
|
-
self.save_dir = os.path.join(self.logs_path, f'freezing_data_time_{time_window}s_{self.datetime}')
|
|
91
|
+
self.save_dir = os.path.join(self.logs_path, f'freezing_data_time_{time_window}s_{movement_threshold}mm_{self.datetime}')
|
|
86
92
|
os.makedirs(self.save_dir)
|
|
87
93
|
else:
|
|
88
94
|
check_if_dir_exists(in_dir=self.save_dir)
|
|
89
95
|
self.time_window, self.movement_threshold = time_window, movement_threshold
|
|
90
96
|
self.movement_threshold, self.shortest_bout = movement_threshold, shortest_bout
|
|
91
|
-
self.run()
|
|
92
|
-
|
|
93
97
|
def run(self):
|
|
94
98
|
agg_results = pd.DataFrame(columns=['VIDEO', 'FREEZING FRAMES', 'FREEZING TIME (S)', 'FREEZING BOUT COUNTS', 'FREEZING PCT OF SESSION', 'VIDEO TOTAL FRAMES', 'VIDEO TOTAL TIME (S)'])
|
|
95
|
-
agg_results_path = os.path.join(self.save_dir, '
|
|
99
|
+
agg_results_path = os.path.join(self.save_dir, f'aggregate_freezing_results_{self.datetime}.csv')
|
|
96
100
|
check_all_file_names_are_represented_in_video_log(video_info_df=self.video_info_df, data_paths=self.data_paths)
|
|
97
101
|
for file_cnt, file_path in enumerate(self.data_paths):
|
|
98
102
|
video_name = get_fn_ext(filepath=file_path)[1]
|
|
99
|
-
print(f'Analyzing {video_name}...({file_cnt+1}/{len(self.data_paths)})')
|
|
103
|
+
print(f'[{get_current_time()}] Analyzing freezing {video_name}...(video {file_cnt+1}/{len(self.data_paths)})')
|
|
100
104
|
save_file_path = os.path.join(self.save_dir, f'{video_name}.csv')
|
|
101
105
|
df = read_df(file_path=file_path, file_type='csv').reset_index(drop=True)
|
|
102
106
|
_, px_per_mm, fps = read_video_info(vid_info_df=self.video_info_df, video_name=video_name)
|
|
@@ -118,23 +122,23 @@ class FreezingDetector(ConfigReader):
|
|
|
118
122
|
mean_movement = np.mean(movement, axis=1)
|
|
119
123
|
mm_s = TimeseriesFeatureMixin.sliding_descriptive_statistics(data=mean_movement.astype(np.float32), window_sizes=np.array([1], dtype=np.float64), sample_rate=int(fps), statistics=typed.List(["sum"]))[0].flatten()
|
|
120
124
|
freezing_idx = np.argwhere(mm_s <= self.movement_threshold).astype(np.int32).flatten()
|
|
125
|
+
df[f'Probability_{FREEZING}'] = 0
|
|
121
126
|
df[FREEZING] = 0
|
|
122
127
|
df.loc[freezing_idx, FREEZING] = 1
|
|
123
128
|
df = plug_holes_shortest_bout(data_df=df, clf_name=FREEZING, fps=fps, shortest_bout=self.shortest_bout)
|
|
124
129
|
bouts = detect_bouts(data_df=df, target_lst=[FREEZING], fps=fps)
|
|
125
130
|
bouts = bouts[bouts['Bout_time'] >= self.time_window]
|
|
126
131
|
if len(bouts) > 0:
|
|
132
|
+
df[FREEZING] = 0
|
|
127
133
|
freezing_idx = list(bouts.apply(lambda x: list(range(int(x["Start_frame"]), int(x["End_frame"]) + 1)), 1))
|
|
128
134
|
freezing_idx = [x for xs in freezing_idx for x in xs]
|
|
129
135
|
df.loc[freezing_idx, FREEZING] = 1
|
|
136
|
+
df.loc[freezing_idx, f'Probability_{FREEZING}'] = 1
|
|
130
137
|
else:
|
|
138
|
+
df[FREEZING] = 0
|
|
131
139
|
freezing_idx = []
|
|
132
140
|
df.to_csv(save_file_path)
|
|
141
|
+
print(video_name, len(freezing_idx), round(len(freezing_idx) / fps, 4), df[FREEZING].sum())
|
|
133
142
|
agg_results.loc[len(agg_results)] = [video_name, len(freezing_idx), round(len(freezing_idx) / fps, 4), len(bouts), round((len(freezing_idx) / len(df)) * 100, 4), len(df), round(len(df)/fps, 2) ]
|
|
134
|
-
|
|
135
143
|
agg_results.to_csv(agg_results_path)
|
|
136
|
-
stdout_success(msg=f'Results saved in {self.save_dir} directory.')
|
|
137
|
-
|
|
138
|
-
#
|
|
139
|
-
# FreezingDetector(data_dir=r'C:\troubleshooting\mitra\project_folder\csv\outlier_corrected_movement_location',
|
|
140
|
-
# config_path=r"C:\troubleshooting\mitra\project_folder\project_config.ini")
|
|
144
|
+
self.timer.stop_timer(); stdout_success(msg=f'Results saved in {self.save_dir} directory.', elapsed_time=self.timer.elapsed_time_str)
|
|
@@ -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
|
|
|
@@ -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
|
|