simba-uw-tf-dev 4.6.4__py3-none-any.whl → 4.6.7__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of simba-uw-tf-dev might be problematic. Click here for more details.
- simba/data_processors/blob_location_computer.py +1 -1
- simba/data_processors/cuda/geometry.py +45 -27
- simba/data_processors/cuda/image.py +1624 -1600
- simba/data_processors/cuda/statistics.py +72 -25
- simba/data_processors/cuda/timeseries.py +1 -1
- simba/data_processors/egocentric_aligner.py +25 -7
- simba/data_processors/kleinberg_calculator.py +6 -2
- simba/feature_extractors/feature_subsets.py +14 -7
- simba/feature_extractors/straub_tail_analyzer.py +4 -6
- simba/labelling/standard_labeller.py +1 -1
- simba/mixins/geometry_mixin.py +8 -8
- simba/mixins/image_mixin.py +14 -14
- simba/mixins/statistics_mixin.py +48 -11
- 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/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/sandbox/analyze_runtimes.py +30 -0
- simba/sandbox/cuda/egocentric_rotator.py +374 -374
- 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/fsttc_pop_up.py +27 -25
- simba/ui/pop_ups/kleinberg_pop_up.py +3 -2
- simba/utils/custom_feature_extractor.py +1 -1
- simba/utils/data.py +2 -3
- simba/utils/errors.py +441 -440
- simba/utils/lookups.py +1203 -1203
- simba/utils/read_write.py +70 -31
- 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 -39
- simba/video_processors/multi_cropper.py +1 -1
- simba/video_processors/video_processing.py +5264 -5233
- simba/video_processors/videos_to_frames.py +43 -33
- {simba_uw_tf_dev-4.6.4.dist-info → simba_uw_tf_dev-4.6.7.dist-info}/METADATA +4 -3
- {simba_uw_tf_dev-4.6.4.dist-info → simba_uw_tf_dev-4.6.7.dist-info}/RECORD +53 -44
- {simba_uw_tf_dev-4.6.4.dist-info → simba_uw_tf_dev-4.6.7.dist-info}/LICENSE +0 -0
- {simba_uw_tf_dev-4.6.4.dist-info → simba_uw_tf_dev-4.6.7.dist-info}/WHEEL +0 -0
- {simba_uw_tf_dev-4.6.4.dist-info → simba_uw_tf_dev-4.6.7.dist-info}/entry_points.txt +0 -0
- {simba_uw_tf_dev-4.6.4.dist-info → simba_uw_tf_dev-4.6.7.dist-info}/top_level.txt +0 -0
|
@@ -3,7 +3,9 @@ __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
|
+
|
|
8
|
+
from simba.utils.printing import SimbaTimer
|
|
7
9
|
|
|
8
10
|
try:
|
|
9
11
|
from typing import Literal
|
|
@@ -17,16 +19,20 @@ from scipy.spatial import ConvexHull
|
|
|
17
19
|
from simba.utils.read_write import get_unique_values_in_iterable, read_df
|
|
18
20
|
from simba.utils.warnings import GPUToolsWarning
|
|
19
21
|
|
|
22
|
+
|
|
20
23
|
try:
|
|
21
24
|
import cupy as cp
|
|
22
|
-
from cuml.metrics import kl_divergence as kl_divergence_gpu
|
|
23
|
-
from cuml.metrics.cluster.adjusted_rand_index import adjusted_rand_score
|
|
24
|
-
from cuml.metrics.cluster.silhouette_score import cython_silhouette_score
|
|
25
25
|
from cupyx.scipy.spatial.distance import cdist
|
|
26
|
-
except:
|
|
27
|
-
GPUToolsWarning(msg='GPU tools not detected, reverting to CPU')
|
|
26
|
+
except Exception as e:
|
|
27
|
+
GPUToolsWarning(msg=f'GPU tools not detected, reverting to CPU: {e.args}')
|
|
28
28
|
import numpy as cp
|
|
29
29
|
from scipy.spatial.distance import cdist
|
|
30
|
+
try:
|
|
31
|
+
from cuml.metrics import kl_divergence as kl_divergence_gpu
|
|
32
|
+
from cuml.metrics.cluster.adjusted_rand_index import adjusted_rand_score
|
|
33
|
+
from cuml.metrics.cluster.silhouette_score import cython_silhouette_score
|
|
34
|
+
except Exception as e:
|
|
35
|
+
GPUToolsWarning(msg=f'GPU tools not detected, reverting to CPU: {e.args}')
|
|
30
36
|
from scipy.stats import entropy as kl_divergence_gpu
|
|
31
37
|
from sklearn.metrics import adjusted_rand_score
|
|
32
38
|
from sklearn.metrics import silhouette_score as cython_silhouette_score
|
|
@@ -39,7 +45,7 @@ except:
|
|
|
39
45
|
from simba.data_processors.cuda.utils import _cuda_are_rows_equal
|
|
40
46
|
from simba.mixins.statistics_mixin import Statistics
|
|
41
47
|
from simba.utils.checks import (check_int, check_str, check_valid_array,
|
|
42
|
-
check_valid_tuple)
|
|
48
|
+
check_valid_tuple, check_float)
|
|
43
49
|
from simba.utils.data import bucket_data
|
|
44
50
|
from simba.utils.enums import Formats
|
|
45
51
|
|
|
@@ -227,7 +233,6 @@ def get_euclidean_distance_cupy(x: np.ndarray,
|
|
|
227
233
|
using CuPy for GPU acceleration. The computation is performed in batches to handle large
|
|
228
234
|
datasets efficiently.
|
|
229
235
|
|
|
230
|
-
|
|
231
236
|
.. seealso::
|
|
232
237
|
For CPU function see :func:`~simba.mixins.feature_extraction_mixin.FeatureExtractionMixin.framewise_euclidean_distance`.
|
|
233
238
|
For CUDA JIT function see :func:`~simba.data_processors.cuda.statistics.get_euclidean_distance_cuda`.
|
|
@@ -380,9 +385,10 @@ def sliding_min(x: np.ndarray, time_window: float, sample_rate: int) -> np.ndarr
|
|
|
380
385
|
|
|
381
386
|
def sliding_spearmans_rank(x: np.ndarray,
|
|
382
387
|
y: np.ndarray,
|
|
383
|
-
time_window: float,
|
|
384
|
-
sample_rate: int,
|
|
385
|
-
batch_size: Optional[int] = int(1.6e+7)
|
|
388
|
+
time_window: Union[float, int],
|
|
389
|
+
sample_rate: Union[float, int],
|
|
390
|
+
batch_size: Optional[int] = int(1.6e+7),
|
|
391
|
+
verbose: bool = False) -> np.ndarray:
|
|
386
392
|
"""
|
|
387
393
|
Computes the Spearman's rank correlation coefficient between two 1D arrays `x` and `y`
|
|
388
394
|
over sliding windows of size `time_window * sample_rate`. The computation is performed
|
|
@@ -413,7 +419,13 @@ def sliding_spearmans_rank(x: np.ndarray,
|
|
|
413
419
|
>>> sliding_spearmans_rank(x, y, time_window=0.5, sample_rate=2)
|
|
414
420
|
"""
|
|
415
421
|
|
|
416
|
-
|
|
422
|
+
timer = SimbaTimer(start=True)
|
|
423
|
+
check_valid_array(data=x, source=f'{sliding_spearmans_rank.__name__} x', accepted_ndims=(1,), accepted_dtypes=Formats.NUMERIC_DTYPES.value)
|
|
424
|
+
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)
|
|
425
|
+
check_float(name=f'{sliding_spearmans_rank.__name__} time_window', value=time_window, allow_zero=False, allow_negative=False, raise_error=True)
|
|
426
|
+
check_float(name=f'{sliding_spearmans_rank.__name__} sample_rate', value=sample_rate, allow_zero=False, allow_negative=False, raise_error=True)
|
|
427
|
+
check_int(name=f'{sliding_spearmans_rank.__name__} batch_size', value=batch_size, allow_zero=False, allow_negative=False, raise_error=True)
|
|
428
|
+
window_size = np.int32(np.ceil(time_window * sample_rate))
|
|
417
429
|
n = x.shape[0]
|
|
418
430
|
results = cp.full(n, -1, dtype=cp.float32)
|
|
419
431
|
|
|
@@ -433,7 +445,11 @@ def sliding_spearmans_rank(x: np.ndarray,
|
|
|
433
445
|
|
|
434
446
|
results[left + window_size - 1:right] = s
|
|
435
447
|
|
|
436
|
-
|
|
448
|
+
r = cp.asnumpy(results)
|
|
449
|
+
timer.stop_timer()
|
|
450
|
+
if verbose: print(f'Sliding Spearmans rank for {x.shape[0]} observations computed (elapsed time: {timer.elapsed_time_str}s)')
|
|
451
|
+
return r
|
|
452
|
+
|
|
437
453
|
|
|
438
454
|
|
|
439
455
|
|
|
@@ -538,6 +554,12 @@ def euclidean_distance_to_static_point(data: np.ndarray,
|
|
|
538
554
|
"""
|
|
539
555
|
Computes the Euclidean distance between each point in a given 2D array `data` and a static point using GPU acceleration.
|
|
540
556
|
|
|
557
|
+
.. seealso::
|
|
558
|
+
For CPU-based distance to static point (ROI center), see :func:`simba.mixins.feature_extraction_mixin.FeatureExtractionMixin.framewise_euclidean_distance_roi`
|
|
559
|
+
For CPU-based framewise Euclidean distance, see :func:`simba.mixins.feature_extraction_mixin.FeatureExtractionMixin.framewise_euclidean_distance`
|
|
560
|
+
For GPU CuPy solution for distance between two sets of points, see :func:`simba.data_processors.cuda.statistics.get_euclidean_distance_cupy`
|
|
561
|
+
For GPU numba CUDA solution for distance between two sets of points, see :func:`simba.data_processors.cuda.statistics.get_euclidean_distance_cuda`
|
|
562
|
+
|
|
541
563
|
: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.
|
|
542
564
|
:param point: A tuple of two integers representing the static point (x, y) in the same space as `data`.
|
|
543
565
|
:param pixels_per_millimeter: A scaling factor that indicates how many pixels correspond to one millimeter. Defaults to 1 if no scaling is necessary.
|
|
@@ -789,13 +811,31 @@ def xie_beni(x: np.ndarray, y: np.ndarray) -> float:
|
|
|
789
811
|
return xb
|
|
790
812
|
|
|
791
813
|
|
|
792
|
-
def i_index(x: np.ndarray, y: np.ndarray):
|
|
814
|
+
def i_index(x: np.ndarray, y: np.ndarray, verbose: bool = False) -> float:
|
|
793
815
|
"""
|
|
794
816
|
Calculate the I-Index for evaluating clustering quality.
|
|
795
817
|
|
|
796
818
|
The I-Index is a metric that measures the compactness and separation of clusters.
|
|
797
819
|
A higher I-Index indicates better clustering with compact and well-separated clusters.
|
|
798
820
|
|
|
821
|
+
.. csv-table::
|
|
822
|
+
:header: EXPECTED RUNTIMES
|
|
823
|
+
:file: ../../../docs/tables/i_index_cuda.csv
|
|
824
|
+
:widths: 10, 45, 45
|
|
825
|
+
:align: center
|
|
826
|
+
:header-rows: 1
|
|
827
|
+
|
|
828
|
+
The I-Index is calculated as:
|
|
829
|
+
|
|
830
|
+
.. math::
|
|
831
|
+
I = \frac{SST}{k \times SWC}
|
|
832
|
+
|
|
833
|
+
where:
|
|
834
|
+
|
|
835
|
+
- :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)
|
|
836
|
+
- :math:`k` is the number of clusters
|
|
837
|
+
- :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)
|
|
838
|
+
|
|
799
839
|
.. seealso::
|
|
800
840
|
To compute Xie-Beni on the CPU, use :func:`~simba.mixins.statistics_mixin.Statistics.i_index`
|
|
801
841
|
|
|
@@ -806,17 +846,16 @@ def i_index(x: np.ndarray, y: np.ndarray):
|
|
|
806
846
|
|
|
807
847
|
:references:
|
|
808
848
|
.. [1] Zhao, Q., Xu, M., Fränti, P. (2009). Sum-of-Squares Based Cluster Validity Index and Significance Analysis.
|
|
809
|
-
In: Kolehmainen, M., Toivanen, P., Beliczynski, B. (eds) Adaptive and Natural Computing Algorithms. ICANNGA 2009.
|
|
810
|
-
Lecture Notes in Computer Science, vol 5495. Springer, Berlin, Heidelberg. https://doi.org/10.1007/978-3-642-04921-7_32
|
|
849
|
+
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
|
|
811
850
|
|
|
812
851
|
:example:
|
|
813
852
|
>>> X, y = make_blobs(n_samples=5000, centers=20, n_features=3, random_state=0, cluster_std=0.1)
|
|
814
853
|
>>> i_index(x=X, y=y)
|
|
815
854
|
"""
|
|
855
|
+
timer = SimbaTimer(start=True)
|
|
816
856
|
check_valid_array(data=x, accepted_ndims=(2,), accepted_dtypes=Formats.NUMERIC_DTYPES.value)
|
|
817
|
-
check_valid_array(data=y, accepted_ndims=(1,), accepted_dtypes=Formats.NUMERIC_DTYPES.value,
|
|
818
|
-
|
|
819
|
-
_ = get_unique_values_in_iterable(data=y, name=i_index.__name__, min=2)
|
|
857
|
+
check_valid_array(data=y, accepted_ndims=(1,), accepted_dtypes=Formats.NUMERIC_DTYPES.value, accepted_axis_0_shape=[x.shape[0], ])
|
|
858
|
+
cluster_cnt = get_unique_values_in_iterable(data=y, name=i_index.__name__, min=2)
|
|
820
859
|
x, y = cp.array(x), cp.array(y)
|
|
821
860
|
unique_y = cp.unique(y)
|
|
822
861
|
n_y = unique_y.shape[0]
|
|
@@ -830,12 +869,16 @@ def i_index(x: np.ndarray, y: np.ndarray):
|
|
|
830
869
|
swc += cp.sum(cp.linalg.norm(cluster_obs - cluster_centroid, axis=1) ** 2)
|
|
831
870
|
|
|
832
871
|
i_idx = sst / (n_y * swc)
|
|
833
|
-
|
|
872
|
+
i_idx = np.float32(i_idx.get()) if hasattr(i_idx, 'get') else np.float32(i_idx)
|
|
873
|
+
timer.stop_timer()
|
|
874
|
+
if verbose: print(f'I-index for {x.shape[0]} observations in {cluster_cnt} clusters computed (elapsed time: {timer.elapsed_time_str}s)')
|
|
834
875
|
return i_idx
|
|
835
876
|
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
877
|
+
def kullback_leibler_divergence_gpu(x: np.ndarray,
|
|
878
|
+
y: np.ndarray,
|
|
879
|
+
fill_value: int = 1,
|
|
880
|
+
bucket_method: Literal["fd", "doane", "auto", "scott", "stone", "rice", "sturges", "sqrt"] = "scott",
|
|
881
|
+
verbose: bool = False) -> float:
|
|
839
882
|
"""
|
|
840
883
|
Compute Kullback-Leibler divergence between two distributions.
|
|
841
884
|
|
|
@@ -847,7 +890,6 @@ def kullback_leibler_divergence_gpu(x: np.ndarray, y: np.ndarray, fill_value: in
|
|
|
847
890
|
.. seealso::
|
|
848
891
|
For CPU implementation, see :func:`simba.mixins.statistics_mixin.Statistics.kullback_leibler_divergence`.
|
|
849
892
|
|
|
850
|
-
|
|
851
893
|
:param ndarray x: First 1d array representing feature values.
|
|
852
894
|
:param ndarray y: Second 1d array representing feature values.
|
|
853
895
|
:param Optional[int] fill_value: Optional pseudo-value to use to fill empty buckets in ``y`` histogram
|
|
@@ -860,13 +902,18 @@ def kullback_leibler_divergence_gpu(x: np.ndarray, y: np.ndarray, fill_value: in
|
|
|
860
902
|
>>> kl = kullback_leibler_divergence_gpu(x=x, y=y)
|
|
861
903
|
"""
|
|
862
904
|
|
|
905
|
+
timer = SimbaTimer(start=True)
|
|
906
|
+
|
|
863
907
|
bin_width, bin_count = bucket_data(data=x, method=bucket_method)
|
|
864
908
|
r = np.array([np.min(x), np.max(x)])
|
|
865
909
|
x_hist = Statistics._hist_1d(data=x, bin_count=bin_count, range=r)
|
|
866
910
|
y_hist = Statistics._hist_1d(data=y, bin_count=bin_count, range=r)
|
|
867
911
|
y_hist[y_hist == 0] = fill_value
|
|
868
912
|
x_hist, y_hist = x_hist / np.sum(x_hist), y_hist / np.sum(y_hist)
|
|
869
|
-
|
|
913
|
+
r = kl_divergence_gpu(P=x_hist.astype(np.float32), Q=y_hist.astype(np.float32), convert_dtype=False)
|
|
914
|
+
timer.stop_timer()
|
|
915
|
+
if verbose: print(f'KL divergence performed on {x.shape[0]} observations (elapsed time: {timer.elapsed_time_str}s)')
|
|
916
|
+
return r
|
|
870
917
|
|
|
871
918
|
|
|
872
919
|
@cuda.jit()
|
|
@@ -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.
|
|
@@ -7,7 +7,8 @@ import pandas as pd
|
|
|
7
7
|
from simba.utils.checks import (check_if_dir_exists, check_if_valid_rgb_tuple,
|
|
8
8
|
check_int, check_str, check_valid_boolean,
|
|
9
9
|
check_valid_dataframe, check_valid_tuple)
|
|
10
|
-
from simba.utils.data import egocentrically_align_pose_numba
|
|
10
|
+
from simba.utils.data import (egocentrically_align_pose_numba, get_cpu_pool,
|
|
11
|
+
terminate_cpu_pool)
|
|
11
12
|
from simba.utils.enums import Formats, Options
|
|
12
13
|
from simba.utils.errors import InvalidInputError
|
|
13
14
|
from simba.utils.printing import SimbaTimer, stdout_success
|
|
@@ -51,7 +52,7 @@ class EgocentricalAligner():
|
|
|
51
52
|
:param Optional[int] core_cnt: Number of CPU cores to use for video rotation; `-1` uses all available cores.
|
|
52
53
|
|
|
53
54
|
:example:
|
|
54
|
-
>>> 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))
|
|
55
56
|
>>> aligner.run()
|
|
56
57
|
"""
|
|
57
58
|
|
|
@@ -73,7 +74,7 @@ class EgocentricalAligner():
|
|
|
73
74
|
check_str(name=f'{self.__class__.__name__} anchor_1', value=anchor_1, allow_blank=False)
|
|
74
75
|
check_str(name=f'{self.__class__.__name__} anchor_2', value=anchor_2, allow_blank=False)
|
|
75
76
|
check_int(name=f'{self.__class__.__name__} core_cnt', value=core_cnt, min_value=-1, max_value=find_core_cnt()[0], unaccepted_vals=[0])
|
|
76
|
-
if core_cnt == -1
|
|
77
|
+
self.core_cnt = find_core_cnt()[0] if core_cnt == -1 or core_cnt > find_core_cnt()[0] else core_cnt
|
|
77
78
|
check_int(name=f'{self.__class__.__name__} direction', value=direction, min_value=0, max_value=360)
|
|
78
79
|
if isinstance(anchor_location, tuple):
|
|
79
80
|
check_valid_tuple(x=anchor_location, source=f'{self.__class__.__name__} anchor_location', accepted_lengths=(2,), valid_dtypes=(int,))
|
|
@@ -98,6 +99,7 @@ class EgocentricalAligner():
|
|
|
98
99
|
|
|
99
100
|
def run(self):
|
|
100
101
|
timer = SimbaTimer(start=True)
|
|
102
|
+
self.pool = None if not self.rotate_video else get_cpu_pool(core_cnt=self.core_cnt, source=self.__class__.__name__)
|
|
101
103
|
for file_cnt, file_path in enumerate(self.data_paths):
|
|
102
104
|
video_timer = SimbaTimer(start=True)
|
|
103
105
|
_, self.video_name, _ = get_fn_ext(filepath=file_path)
|
|
@@ -127,8 +129,7 @@ class EgocentricalAligner():
|
|
|
127
129
|
if self.verbose:
|
|
128
130
|
print(f'{self.video_name} complete, saved at {save_path} (elapsed time: {video_timer.elapsed_time_str}s)')
|
|
129
131
|
if self.rotate_video:
|
|
130
|
-
if self.verbose:
|
|
131
|
-
print(f'Rotating video {self.video_name}...')
|
|
132
|
+
if self.verbose: print(f'Rotating video {self.video_name}...')
|
|
132
133
|
video_path = find_video_of_file(video_dir=self.videos_dir, filename=self.video_name, raise_error=False)
|
|
133
134
|
save_path = os.path.join(self.save_dir, f'{self.video_name}.mp4')
|
|
134
135
|
video_rotator = EgocentricVideoRotator(video_path=video_path,
|
|
@@ -139,11 +140,13 @@ class EgocentricalAligner():
|
|
|
139
140
|
gpu=self.gpu,
|
|
140
141
|
fill_clr=self.fill_clr,
|
|
141
142
|
core_cnt=self.core_cnt,
|
|
142
|
-
save_path=save_path
|
|
143
|
+
save_path=save_path,
|
|
144
|
+
pool=self.pool)
|
|
143
145
|
video_rotator.run()
|
|
144
146
|
if self.verbose:
|
|
145
147
|
print(f'Rotated data for video {self.video_name} ({file_cnt+1}/{len(self.data_paths)}) saved in {self.save_dir}.')
|
|
146
148
|
timer.stop_timer()
|
|
149
|
+
terminate_cpu_pool(pool=self.pool, source=self.__class__.__name__)
|
|
147
150
|
stdout_success(msg=f'Egocentrically aligned data for {len(self.data_paths)} files saved in {self.save_dir}', elapsed_time=timer.elapsed_time_str)
|
|
148
151
|
|
|
149
152
|
|
|
@@ -156,9 +159,24 @@ class EgocentricalAligner():
|
|
|
156
159
|
# direction=0,
|
|
157
160
|
# gpu=True,
|
|
158
161
|
# anchor_location=(600, 300),
|
|
159
|
-
# fill_clr=(128,128,128)
|
|
162
|
+
# fill_clr=(128,128,128),
|
|
163
|
+
# core_cnt=18)
|
|
160
164
|
# aligner.run()
|
|
161
165
|
|
|
166
|
+
|
|
167
|
+
if __name__ == "__main__":
|
|
168
|
+
aligner = EgocentricalAligner(anchor_1='butt/proximal tail',
|
|
169
|
+
anchor_2='snout',
|
|
170
|
+
data_dir=r'C:\troubleshooting\open_field_below\project_folder\csv\outlier_corrected_movement_location',
|
|
171
|
+
videos_dir=r'C:\troubleshooting\open_field_below\project_folder\videos',
|
|
172
|
+
save_dir=r"C:\troubleshooting\open_field_below\project_folder\videos\rotated",
|
|
173
|
+
direction=0,
|
|
174
|
+
gpu=True,
|
|
175
|
+
anchor_location=(600, 300),
|
|
176
|
+
fill_clr=(128,128,128),
|
|
177
|
+
core_cnt=18)
|
|
178
|
+
aligner.run()
|
|
179
|
+
|
|
162
180
|
# aligner = EgocentricalAligner(anchor_1='tail_base',
|
|
163
181
|
# anchor_2='nose',
|
|
164
182
|
# data_dir=r'C:\Users\sroni\OneDrive\Desktop\rotate_ex\data',
|
|
@@ -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
|
|
|
@@ -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
|
#
|
|
@@ -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/geometry_mixin.py
CHANGED
|
@@ -1556,7 +1556,7 @@ class GeometryMixin(object):
|
|
|
1556
1556
|
:rtype: List[float]
|
|
1557
1557
|
|
|
1558
1558
|
:example:
|
|
1559
|
-
>>> 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)
|
|
1560
1560
|
>>> animal_1_cols = [x for x in df.columns if '_1_' in x and not '_p' in x]
|
|
1561
1561
|
>>> animal_2_cols = [x for x in df.columns if '_2_' in x and not '_p' in x]
|
|
1562
1562
|
>>> animal_1_arr = df[animal_1_cols].values.reshape(len(df), int(len(animal_1_cols)/ 2), 2)
|
|
@@ -1622,7 +1622,7 @@ class GeometryMixin(object):
|
|
|
1622
1622
|
:return List[float]: List of overlap between corresponding Polygons. If overlap 1, else 0.
|
|
1623
1623
|
|
|
1624
1624
|
:example:
|
|
1625
|
-
>>> 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)
|
|
1626
1626
|
>>> animal_1_cols = [x for x in df.columns if '_1_' in x and not '_p' in x]
|
|
1627
1627
|
>>> animal_2_cols = [x for x in df.columns if '_2_' in x and not '_p' in x]
|
|
1628
1628
|
>>> animal_1_arr = df[animal_1_cols].values.reshape(len(df), int(len(animal_1_cols)/ 2), 2)
|
|
@@ -1693,7 +1693,7 @@ class GeometryMixin(object):
|
|
|
1693
1693
|
:rtype: List[float]
|
|
1694
1694
|
|
|
1695
1695
|
:example:
|
|
1696
|
-
>>> 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)
|
|
1697
1697
|
>>> animal_1_cols = [x for x in df.columns if '_1_' in x and not '_p' in x]
|
|
1698
1698
|
>>> animal_2_cols = [x for x in df.columns if '_2_' in x and not '_p' in x]
|
|
1699
1699
|
>>> animal_1_arr = df[animal_1_cols].values.reshape(len(df), int(len(animal_1_cols)/ 2), 2)
|
|
@@ -1763,7 +1763,7 @@ class GeometryMixin(object):
|
|
|
1763
1763
|
:rtype: List[Polygon]
|
|
1764
1764
|
|
|
1765
1765
|
:example:
|
|
1766
|
-
>>> 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)
|
|
1767
1767
|
>>> animal_1_cols = [x for x in df.columns if '_1_' in x and not '_p' in x]
|
|
1768
1768
|
>>> animal_1_arr = df[animal_1_cols].values.reshape(len(df), int(len(animal_1_cols)/ 2), 2)
|
|
1769
1769
|
>>> animal_1_geo = GeometryMixin.bodyparts_to_polygon(data=animal_1_arr)
|
|
@@ -3525,10 +3525,10 @@ class GeometryMixin(object):
|
|
|
3525
3525
|
:rtype: Tuple[Dict[Tuple[int, int], Dict[Tuple[int, int], float]], Dict[Tuple[int, int], Dict[Tuple[int, int], int]]]
|
|
3526
3526
|
|
|
3527
3527
|
:example:
|
|
3528
|
-
>>> 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")
|
|
3529
3529
|
>>> w, h = video_meta_data['width'], video_meta_data['height']
|
|
3530
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]
|
|
3531
|
-
>>> 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
|
|
3532
3532
|
>>> transition_probabilities, _ = geometry_transition_probabilities(data=data, grid=grid)
|
|
3533
3533
|
"""
|
|
3534
3534
|
|
|
@@ -3990,7 +3990,7 @@ class GeometryMixin(object):
|
|
|
3990
3990
|
:rtype: np.ndarray
|
|
3991
3991
|
|
|
3992
3992
|
:example:
|
|
3993
|
-
>>> data_path = r"C
|
|
3993
|
+
>>> data_path = r"C:/troubleshooting/mitra/project_folder/csv/outlier_corrected_movement_location/FRR_gq_Saline_0624.csv"
|
|
3994
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)
|
|
3995
3995
|
>>> animal_polygons = GeometryMixin().bodyparts_to_polygon(data=animal_data)
|
|
3996
3996
|
>>> GeometryMixin.geometries_to_exterior_keypoints(geometries=animal_polygons)
|
|
@@ -4160,7 +4160,7 @@ class GeometryMixin(object):
|
|
|
4160
4160
|
:rtype: Union[None, Dict[Any, dict]]
|
|
4161
4161
|
|
|
4162
4162
|
:example I:
|
|
4163
|
-
>>> 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")
|
|
4164
4164
|
>>> # Results structure: {track_id: {frame_idx: Polygon, ...}, ...}
|
|
4165
4165
|
|
|
4166
4166
|
:example II
|
simba/mixins/image_mixin.py
CHANGED
|
@@ -57,17 +57,16 @@ class ImageMixin(object):
|
|
|
57
57
|
pass
|
|
58
58
|
|
|
59
59
|
@staticmethod
|
|
60
|
-
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:
|
|
61
61
|
"""
|
|
62
62
|
Compute the average brightness intensity within each image within a list.
|
|
63
63
|
|
|
64
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.
|
|
65
65
|
|
|
66
66
|
.. seealso::
|
|
67
|
-
For GPU acceleration, see :func:`simba.data_processors.cuda.image.img_stack_brightness`.
|
|
68
|
-
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`
|
|
69
68
|
|
|
70
|
-
: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.
|
|
71
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.
|
|
72
71
|
:returns: List of floats of size len(imgs) with brightness intensities.
|
|
73
72
|
:rtype: List[float]
|
|
@@ -77,14 +76,12 @@ class ImageMixin(object):
|
|
|
77
76
|
>>> ImageMixin.brightness_intensity(imgs=[img], ignore_black=False)
|
|
78
77
|
>>> [159.0]
|
|
79
78
|
"""
|
|
80
|
-
results = []
|
|
81
|
-
check_instance(source=f"{ImageMixin().brightness_intensity.__name__} imgs", instance=imgs, accepted_types=list)
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
accepted_types=np.ndarray,
|
|
87
|
-
)
|
|
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)
|
|
88
85
|
if len(img) == 0:
|
|
89
86
|
results.append(0)
|
|
90
87
|
else:
|
|
@@ -92,7 +89,10 @@ class ImageMixin(object):
|
|
|
92
89
|
results.append(np.ceil(np.average(img[img != 0])))
|
|
93
90
|
else:
|
|
94
91
|
results.append(np.ceil(np.average(img)))
|
|
95
|
-
|
|
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
|
+
|
|
96
96
|
|
|
97
97
|
@staticmethod
|
|
98
98
|
def gaussian_blur(img: np.ndarray, kernel_size: Optional[Tuple] = (9, 9)) -> np.ndarray:
|
|
@@ -1898,7 +1898,7 @@ class ImageMixin(object):
|
|
|
1898
1898
|
:rtype: np.ndarray
|
|
1899
1899
|
|
|
1900
1900
|
:example:
|
|
1901
|
-
>>> VIDEO_PATH = r"D
|
|
1901
|
+
>>> VIDEO_PATH = r"D:/EPM_2/EPM_1.mp4"
|
|
1902
1902
|
>>> img = read_img_batch_from_video(video_path=VIDEO_PATH, greyscale=True, start_frm=0, end_frm=15, core_cnt=1)
|
|
1903
1903
|
>>> imgs = np.stack(list(img.values()))
|
|
1904
1904
|
>>> resized_img = resize_img_stack(imgs=imgs)
|