autogaita 1.2.0__tar.gz → 1.3.1__tar.gz
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.
- {autogaita-1.2.0 → autogaita-1.3.1}/PKG-INFO +1 -1
- {autogaita-1.2.0 → autogaita-1.3.1}/autogaita/common2D/common2D_1_preparation.py +17 -2
- {autogaita-1.2.0 → autogaita-1.3.1}/autogaita/common2D/common2D_2_sc_extraction.py +22 -10
- {autogaita-1.2.0 → autogaita-1.3.1}/autogaita/common2D/common2D_3_analysis.py +28 -29
- {autogaita-1.2.0 → autogaita-1.3.1}/autogaita/common2D/common2D_utils.py +95 -5
- {autogaita-1.2.0 → autogaita-1.3.1}/autogaita/group/group_1_preparation.py +144 -85
- {autogaita-1.2.0 → autogaita-1.3.1}/autogaita/group/group_main.py +3 -2
- {autogaita-1.2.0 → autogaita-1.3.1}/autogaita/gui/group_gui.py +1 -2
- {autogaita-1.2.0 → autogaita-1.3.1}/autogaita/resources/utils.py +68 -3
- {autogaita-1.2.0 → autogaita-1.3.1}/autogaita/universal3D/universal3D_1_preparation.py +4 -35
- {autogaita-1.2.0 → autogaita-1.3.1}/autogaita/universal3D/universal3D_2_sc_extraction.py +96 -0
- {autogaita-1.2.0 → autogaita-1.3.1}/autogaita/universal3D/universal3D_3_analysis.py +25 -31
- {autogaita-1.2.0 → autogaita-1.3.1}/autogaita.egg-info/PKG-INFO +1 -1
- {autogaita-1.2.0 → autogaita-1.3.1}/autogaita.egg-info/SOURCES.txt +2 -0
- {autogaita-1.2.0 → autogaita-1.3.1}/setup.py +1 -1
- autogaita-1.3.1/tests/test_common2D_unit_1_preparation.py +334 -0
- {autogaita-1.2.0 → autogaita-1.3.1}/tests/test_common2D_unit_2_sc_extraction.py +55 -3
- {autogaita-1.2.0 → autogaita-1.3.1}/tests/test_common2D_unit_3_analysis.py +2 -31
- autogaita-1.3.1/tests/test_common2D_unit_utils.py +134 -0
- {autogaita-1.2.0 → autogaita-1.3.1}/tests/test_dlc_unit_1_preparation.py +0 -14
- {autogaita-1.2.0 → autogaita-1.3.1}/tests/test_group_unit.py +67 -8
- autogaita-1.3.1/tests/test_universal3D_unit_2_sc_extraction.py +108 -0
- {autogaita-1.2.0 → autogaita-1.3.1}/tests/test_universal3D_unit_3_analysis.py +24 -11
- {autogaita-1.2.0 → autogaita-1.3.1}/tests/test_utils.py +97 -5
- autogaita-1.2.0/tests/test_common2D_unit_1_preparation.py +0 -235
- {autogaita-1.2.0 → autogaita-1.3.1}/LICENSE +0 -0
- {autogaita-1.2.0 → autogaita-1.3.1}/README.md +0 -0
- {autogaita-1.2.0 → autogaita-1.3.1}/autogaita/__init__.py +0 -0
- {autogaita-1.2.0 → autogaita-1.3.1}/autogaita/__main__.py +0 -0
- {autogaita-1.2.0 → autogaita-1.3.1}/autogaita/batchrun_scripts/__init__.py +0 -0
- {autogaita-1.2.0 → autogaita-1.3.1}/autogaita/batchrun_scripts/dlc_multirun.py +0 -0
- {autogaita-1.2.0 → autogaita-1.3.1}/autogaita/batchrun_scripts/dlc_singlerun.py +0 -0
- {autogaita-1.2.0 → autogaita-1.3.1}/autogaita/batchrun_scripts/group_dlcrun.py +0 -0
- {autogaita-1.2.0 → autogaita-1.3.1}/autogaita/batchrun_scripts/group_dlcrun_forpaper.py +0 -0
- {autogaita-1.2.0 → autogaita-1.3.1}/autogaita/batchrun_scripts/group_universal3Drun.py +0 -0
- {autogaita-1.2.0 → autogaita-1.3.1}/autogaita/batchrun_scripts/sleap_singlerun.py +0 -0
- {autogaita-1.2.0 → autogaita-1.3.1}/autogaita/batchrun_scripts/universal3D_multirun.py +0 -0
- {autogaita-1.2.0 → autogaita-1.3.1}/autogaita/batchrun_scripts/universal3D_singlerun.py +0 -0
- {autogaita-1.2.0 → autogaita-1.3.1}/autogaita/common2D/__init__.py +0 -0
- {autogaita-1.2.0 → autogaita-1.3.1}/autogaita/common2D/common2D_4_plots.py +0 -0
- {autogaita-1.2.0 → autogaita-1.3.1}/autogaita/common2D/common2D_constants.py +0 -0
- {autogaita-1.2.0 → autogaita-1.3.1}/autogaita/dlc/__init__.py +0 -0
- {autogaita-1.2.0 → autogaita-1.3.1}/autogaita/dlc/dlc_main.py +0 -0
- {autogaita-1.2.0 → autogaita-1.3.1}/autogaita/group/__init__.py +0 -0
- {autogaita-1.2.0 → autogaita-1.3.1}/autogaita/group/group_2_data_processing.py +0 -0
- {autogaita-1.2.0 → autogaita-1.3.1}/autogaita/group/group_3_PCA.py +0 -0
- {autogaita-1.2.0 → autogaita-1.3.1}/autogaita/group/group_4_stats.py +0 -0
- {autogaita-1.2.0 → autogaita-1.3.1}/autogaita/group/group_5_plots.py +0 -0
- {autogaita-1.2.0 → autogaita-1.3.1}/autogaita/group/group_constants.py +0 -0
- {autogaita-1.2.0 → autogaita-1.3.1}/autogaita/group/group_utils.py +0 -0
- {autogaita-1.2.0 → autogaita-1.3.1}/autogaita/gui/__init__.py +0 -0
- {autogaita-1.2.0 → autogaita-1.3.1}/autogaita/gui/common2D_advanced_config_gui.py +0 -0
- {autogaita-1.2.0 → autogaita-1.3.1}/autogaita/gui/common2D_columninfo_gui.py +0 -0
- {autogaita-1.2.0 → autogaita-1.3.1}/autogaita/gui/common2D_gui_constants.py +0 -0
- {autogaita-1.2.0 → autogaita-1.3.1}/autogaita/gui/common2D_gui_utils.py +0 -0
- {autogaita-1.2.0 → autogaita-1.3.1}/autogaita/gui/common2D_main_gui.py +0 -0
- {autogaita-1.2.0 → autogaita-1.3.1}/autogaita/gui/common2D_run_and_done_gui.py +0 -0
- {autogaita-1.2.0 → autogaita-1.3.1}/autogaita/gui/dlc_gui.py +0 -0
- {autogaita-1.2.0 → autogaita-1.3.1}/autogaita/gui/dlc_gui_config.json +0 -0
- {autogaita-1.2.0 → autogaita-1.3.1}/autogaita/gui/first_level_gui_utils.py +0 -0
- {autogaita-1.2.0 → autogaita-1.3.1}/autogaita/gui/gaita_widgets.py +0 -0
- {autogaita-1.2.0 → autogaita-1.3.1}/autogaita/gui/group_gui_config.json +0 -0
- {autogaita-1.2.0 → autogaita-1.3.1}/autogaita/gui/gui_constants.py +0 -0
- {autogaita-1.2.0 → autogaita-1.3.1}/autogaita/gui/gui_utils.py +0 -0
- {autogaita-1.2.0 → autogaita-1.3.1}/autogaita/gui/main_gui.py +0 -0
- {autogaita-1.2.0 → autogaita-1.3.1}/autogaita/gui/sleap_gui.py +0 -0
- {autogaita-1.2.0 → autogaita-1.3.1}/autogaita/gui/sleap_gui_config.json +0 -0
- {autogaita-1.2.0 → autogaita-1.3.1}/autogaita/gui/universal3D_gui.py +0 -0
- {autogaita-1.2.0 → autogaita-1.3.1}/autogaita/gui/universal3D_gui_config.json +0 -0
- {autogaita-1.2.0 → autogaita-1.3.1}/autogaita/resources/__init__.py +0 -0
- {autogaita-1.2.0 → autogaita-1.3.1}/autogaita/resources/constants.py +0 -0
- {autogaita-1.2.0 → autogaita-1.3.1}/autogaita/resources/icon.icns +0 -0
- {autogaita-1.2.0 → autogaita-1.3.1}/autogaita/resources/icon.ico +0 -0
- {autogaita-1.2.0 → autogaita-1.3.1}/autogaita/resources/logo.png +0 -0
- {autogaita-1.2.0 → autogaita-1.3.1}/autogaita/resources/pic_to_demo_for_repo.png +0 -0
- {autogaita-1.2.0 → autogaita-1.3.1}/autogaita/sleap/__init__.py +0 -0
- {autogaita-1.2.0 → autogaita-1.3.1}/autogaita/sleap/sleap_main.py +0 -0
- {autogaita-1.2.0 → autogaita-1.3.1}/autogaita/universal3D/__init__.py +0 -0
- {autogaita-1.2.0 → autogaita-1.3.1}/autogaita/universal3D/universal3D_4_plots.py +0 -0
- {autogaita-1.2.0 → autogaita-1.3.1}/autogaita/universal3D/universal3D_constants.py +0 -0
- {autogaita-1.2.0 → autogaita-1.3.1}/autogaita/universal3D/universal3D_datafile_preparation.py +0 -0
- {autogaita-1.2.0 → autogaita-1.3.1}/autogaita/universal3D/universal3D_main.py +0 -0
- {autogaita-1.2.0 → autogaita-1.3.1}/autogaita/universal3D/universal3D_utils.py +0 -0
- {autogaita-1.2.0 → autogaita-1.3.1}/autogaita.egg-info/dependency_links.txt +0 -0
- {autogaita-1.2.0 → autogaita-1.3.1}/autogaita.egg-info/requires.txt +0 -0
- {autogaita-1.2.0 → autogaita-1.3.1}/autogaita.egg-info/top_level.txt +0 -0
- {autogaita-1.2.0 → autogaita-1.3.1}/setup.cfg +0 -0
- {autogaita-1.2.0 → autogaita-1.3.1}/tests/__init__.py +0 -0
- {autogaita-1.2.0 → autogaita-1.3.1}/tests/test_dlc_approval.py +0 -0
- {autogaita-1.2.0 → autogaita-1.3.1}/tests/test_group_approval.py +0 -0
- {autogaita-1.2.0 → autogaita-1.3.1}/tests/test_universal3D_approval.py +0 -0
- {autogaita-1.2.0 → autogaita-1.3.1}/tests/test_universal3D_unit_1_preparation.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: autogaita
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.3.1
|
|
4
4
|
Summary: Automatic Gait Analysis in Python. A toolbox to streamline and standardise the analysis of kinematics across species after ML-based body posture tracking. Despite being optimised for gait analyses, AutoGaitA has the potential to be used for any kind of kinematic analysis.
|
|
5
5
|
Home-page: https://github.com/mahan-hosseini/AutoGaitA/
|
|
6
6
|
Author: Mahan Hosseini
|
|
@@ -45,6 +45,8 @@ def some_prep(tracking_software, info, folderinfo, cfg):
|
|
|
45
45
|
subtract_beam = cfg["subtract_beam"]
|
|
46
46
|
convert_to_mm = cfg["convert_to_mm"]
|
|
47
47
|
pixel_to_mm_ratio = cfg["pixel_to_mm_ratio"]
|
|
48
|
+
x_sc_broken_threshold = cfg["x_sc_broken_threshold"]
|
|
49
|
+
y_sc_broken_threshold = cfg["y_sc_broken_threshold"]
|
|
48
50
|
standardise_y_at_SC_level = cfg["standardise_y_at_SC_level"]
|
|
49
51
|
invert_y_axis = cfg["invert_y_axis"]
|
|
50
52
|
flip_gait_direction = cfg["flip_gait_direction"]
|
|
@@ -196,7 +198,11 @@ def some_prep(tracking_software, info, folderinfo, cfg):
|
|
|
196
198
|
config_vars_to_json = {
|
|
197
199
|
"sampling_rate": sampling_rate,
|
|
198
200
|
"convert_to_mm": convert_to_mm,
|
|
201
|
+
"pixel_to_mm_ratio": pixel_to_mm_ratio,
|
|
202
|
+
"x_sc_broken_threshold": x_sc_broken_threshold,
|
|
203
|
+
"y_sc_broken_threshold": y_sc_broken_threshold,
|
|
199
204
|
"standardise_y_at_SC_level": standardise_y_at_SC_level,
|
|
205
|
+
"flip_gait_direction": flip_gait_direction,
|
|
200
206
|
"analyse_average_x": analyse_average_x,
|
|
201
207
|
"standardise_x_coordinates": standardise_x_coordinates,
|
|
202
208
|
"x_standardisation_joint": x_standardisation_joint,
|
|
@@ -402,11 +408,20 @@ def check_this_filename_configuration(
|
|
|
402
408
|
prerun_string = folderinfo["prerun_string"]
|
|
403
409
|
whichvideo = "" # initialise
|
|
404
410
|
found_it = False
|
|
411
|
+
# handle leading zeros that we identified previously - if none convert nums to str
|
|
412
|
+
if "leading_mouse_num_zeros" in info.keys():
|
|
413
|
+
mouse_num = info["leading_mouse_num_zeros"] + str(mouse_num)
|
|
414
|
+
else:
|
|
415
|
+
mouse_num = str(mouse_num)
|
|
416
|
+
if "leading_run_num_zeros" in info.keys():
|
|
417
|
+
run_num = info["leading_run_num_zeros"] + str(run_num)
|
|
418
|
+
else:
|
|
419
|
+
run_num = str(run_num)
|
|
405
420
|
for filename in os.listdir(root_dir):
|
|
406
421
|
# the following condition is True for data & beam csv
|
|
407
422
|
if (
|
|
408
|
-
(premouse_string +
|
|
409
|
-
and (prerun_string +
|
|
423
|
+
(premouse_string + mouse_num + postmouse_string in filename)
|
|
424
|
+
and (prerun_string + run_num + postrun_string in filename)
|
|
410
425
|
and (filename.endswith(file_type_string))
|
|
411
426
|
):
|
|
412
427
|
found_it = True
|
|
@@ -4,6 +4,7 @@ from autogaita.common2D.common2D_utils import (
|
|
|
4
4
|
check_cycle_out_of_bounds,
|
|
5
5
|
check_cycle_duplicates,
|
|
6
6
|
check_cycle_order,
|
|
7
|
+
check_differing_angle_joint_coords,
|
|
7
8
|
check_tracking_xy_thresholds,
|
|
8
9
|
check_tracking_SLEAP_nans,
|
|
9
10
|
handle_issues,
|
|
@@ -208,14 +209,25 @@ def extract_stepcycles(tracking_software, data, info, folderinfo, cfg):
|
|
|
208
209
|
# ............................ clean all_cycles ..................................
|
|
209
210
|
# check if we skipped latencies because they were out of data-bounds
|
|
210
211
|
all_cycles = check_cycle_out_of_bounds(all_cycles)
|
|
211
|
-
if all_cycles: #
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
212
|
+
if not all_cycles: # returns None if no clean cycles found
|
|
213
|
+
return None
|
|
214
|
+
# check if there are any duplicates (e.g., SC2's start-lat == SC1's end-lat)
|
|
215
|
+
all_cycles = check_cycle_duplicates(all_cycles) # doesnt return None!
|
|
216
|
+
# check if user input progressively later latencies
|
|
217
|
+
all_cycles = check_cycle_order(all_cycles, info)
|
|
218
|
+
if not all_cycles: # returns empty list if no clean cycles found
|
|
219
|
+
return None
|
|
220
|
+
# check that joints used in angle computations have different coords at all tps
|
|
221
|
+
all_cycles = check_differing_angle_joint_coords(all_cycles, data, info, cfg)
|
|
222
|
+
if not all_cycles:
|
|
223
|
+
return None
|
|
224
|
+
# check if tracking broke for any SCs using user-provided x and y thresholds
|
|
225
|
+
all_cycles = check_tracking_xy_thresholds(all_cycles, data, info, cfg)
|
|
226
|
+
if not all_cycles:
|
|
227
|
+
return None
|
|
228
|
+
# for SLEAP - check if there were any NaNs in any joints/angle-joints in SCs
|
|
229
|
+
if tracking_software == "SLEAP":
|
|
230
|
+
all_cycles = check_tracking_SLEAP_nans(all_cycles, data, info, cfg)
|
|
231
|
+
if not all_cycles:
|
|
232
|
+
return None
|
|
221
233
|
return all_cycles
|
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
# %% imports
|
|
2
|
-
from autogaita.resources.utils import
|
|
2
|
+
from autogaita.resources.utils import (
|
|
3
|
+
bin_num_to_percentages,
|
|
4
|
+
compute_angle,
|
|
5
|
+
write_angle_warning,
|
|
6
|
+
)
|
|
3
7
|
import os
|
|
4
8
|
import warnings
|
|
5
9
|
import pandas as pd
|
|
6
10
|
import numpy as np
|
|
7
|
-
import math
|
|
8
11
|
|
|
9
12
|
# %% constants
|
|
10
13
|
from autogaita.resources.constants import TIME_COL, SC_PERCENTAGE_COL
|
|
@@ -50,14 +53,14 @@ def analyse_and_export_stepcycles(data, all_cycles, info, cfg):
|
|
|
50
53
|
this_step = data_copy.loc[all_cycles[0][0] : all_cycles[0][1]]
|
|
51
54
|
if standardise_x_coordinates:
|
|
52
55
|
all_steps_data, x_standardised_steps_data = (
|
|
53
|
-
standardise_x_y_and_add_features_to_one_step(this_step, cfg)
|
|
56
|
+
standardise_x_y_and_add_features_to_one_step(this_step, info, cfg)
|
|
54
57
|
)
|
|
55
58
|
normalised_steps_data = normalise_one_steps_data(
|
|
56
59
|
x_standardised_steps_data, bin_num
|
|
57
60
|
)
|
|
58
61
|
else:
|
|
59
62
|
all_steps_data = standardise_x_y_and_add_features_to_one_step(
|
|
60
|
-
this_step, cfg
|
|
63
|
+
this_step, info, cfg
|
|
61
64
|
)
|
|
62
65
|
normalised_steps_data = normalise_one_steps_data(all_steps_data, bin_num)
|
|
63
66
|
# 2 or more steps - build dataframe
|
|
@@ -69,14 +72,14 @@ def analyse_and_export_stepcycles(data, all_cycles, info, cfg):
|
|
|
69
72
|
first_step = data_copy.loc[all_cycles[0][0] : all_cycles[0][1]]
|
|
70
73
|
if standardise_x_coordinates:
|
|
71
74
|
all_steps_data, x_standardised_steps_data = (
|
|
72
|
-
standardise_x_y_and_add_features_to_one_step(first_step, cfg)
|
|
75
|
+
standardise_x_y_and_add_features_to_one_step(first_step, info, cfg)
|
|
73
76
|
)
|
|
74
77
|
normalised_steps_data = normalise_one_steps_data(
|
|
75
78
|
x_standardised_steps_data, bin_num
|
|
76
79
|
)
|
|
77
80
|
else:
|
|
78
81
|
all_steps_data = standardise_x_y_and_add_features_to_one_step(
|
|
79
|
-
first_step, cfg
|
|
82
|
+
first_step, info, cfg
|
|
80
83
|
)
|
|
81
84
|
normalised_steps_data = normalise_one_steps_data(all_steps_data, bin_num)
|
|
82
85
|
# some prep for addition of further steps
|
|
@@ -98,13 +101,15 @@ def analyse_and_export_stepcycles(data, all_cycles, info, cfg):
|
|
|
98
101
|
this_step = data_copy.loc[all_cycles[s][0] : all_cycles[s][1]]
|
|
99
102
|
if standardise_x_coordinates:
|
|
100
103
|
this_step, this_x_standardised_step = (
|
|
101
|
-
standardise_x_y_and_add_features_to_one_step(this_step, cfg)
|
|
104
|
+
standardise_x_y_and_add_features_to_one_step(this_step, info, cfg)
|
|
102
105
|
)
|
|
103
106
|
this_normalised_step = normalise_one_steps_data(
|
|
104
107
|
this_x_standardised_step, bin_num
|
|
105
108
|
)
|
|
106
109
|
else:
|
|
107
|
-
this_step = standardise_x_y_and_add_features_to_one_step(
|
|
110
|
+
this_step = standardise_x_y_and_add_features_to_one_step(
|
|
111
|
+
this_step, info, cfg
|
|
112
|
+
)
|
|
108
113
|
this_normalised_step = normalise_one_steps_data(this_step, bin_num)
|
|
109
114
|
# step separators & step-to-rest-concatenation
|
|
110
115
|
# => note that normalised_step is already based on x-stand if required
|
|
@@ -170,7 +175,7 @@ def analyse_and_export_stepcycles(data, all_cycles, info, cfg):
|
|
|
170
175
|
# ......................................................................................
|
|
171
176
|
|
|
172
177
|
|
|
173
|
-
def standardise_x_y_and_add_features_to_one_step(step, cfg):
|
|
178
|
+
def standardise_x_y_and_add_features_to_one_step(step, info, cfg):
|
|
174
179
|
"""For a single step cycle's data, standardise x & y if wanted and add features"""
|
|
175
180
|
# if user wanted this, standardise y (height) at step-cycle level
|
|
176
181
|
step_copy = step.copy()
|
|
@@ -184,11 +189,11 @@ def standardise_x_y_and_add_features_to_one_step(step, cfg):
|
|
|
184
189
|
step_copy[y_cols] -= this_y_min
|
|
185
190
|
# if no x-standardisation, just add features & return non-(x-)normalised step
|
|
186
191
|
if cfg["standardise_x_coordinates"] is False:
|
|
187
|
-
non_stand_step = add_features(step_copy, cfg)
|
|
192
|
+
non_stand_step = add_features(step_copy, info, cfg)
|
|
188
193
|
return non_stand_step
|
|
189
194
|
# else standardise x (horizontal dimension) at step-cycle level too
|
|
190
195
|
else:
|
|
191
|
-
non_stand_step = add_features(step_copy, cfg)
|
|
196
|
+
non_stand_step = add_features(step_copy, info, cfg)
|
|
192
197
|
x_stand_step = step_copy.copy()
|
|
193
198
|
x_cols = [col for col in x_stand_step.columns if col.endswith("x")]
|
|
194
199
|
# note the [0] here is important because it's still a list of len=1!!
|
|
@@ -196,11 +201,11 @@ def standardise_x_y_and_add_features_to_one_step(step, cfg):
|
|
|
196
201
|
cfg["x_standardisation_joint"][0] + "x"
|
|
197
202
|
].min()
|
|
198
203
|
x_stand_step[x_cols] -= min_x_standardisation_joint
|
|
199
|
-
x_stand_step = add_features(x_stand_step, cfg)
|
|
204
|
+
x_stand_step = add_features(x_stand_step, info, cfg)
|
|
200
205
|
return non_stand_step, x_stand_step
|
|
201
206
|
|
|
202
207
|
|
|
203
|
-
def add_features(step, cfg):
|
|
208
|
+
def add_features(step, info, cfg):
|
|
204
209
|
"""Add Features, i.e. Angles & Velocities"""
|
|
205
210
|
# unpack
|
|
206
211
|
hind_joints = cfg["hind_joints"]
|
|
@@ -208,12 +213,12 @@ def add_features(step, cfg):
|
|
|
208
213
|
if hind_joints:
|
|
209
214
|
step = add_x_velocities(step, cfg)
|
|
210
215
|
if angles["name"]: # if there is at least 1 string in the list
|
|
211
|
-
step = add_angles(step, cfg)
|
|
216
|
+
step = add_angles(step, info, cfg)
|
|
212
217
|
step = add_angular_velocities(step, cfg)
|
|
213
218
|
return step
|
|
214
219
|
|
|
215
220
|
|
|
216
|
-
def add_angles(step, cfg):
|
|
221
|
+
def add_angles(step, info, cfg):
|
|
217
222
|
"""Feature #1: Joint Angles"""
|
|
218
223
|
# unpack
|
|
219
224
|
angles = cfg["angles"]
|
|
@@ -234,26 +239,20 @@ def add_angles(step, cfg):
|
|
|
234
239
|
joint3[:, 1] = step[upper_joint + "y"]
|
|
235
240
|
# initialise the angle vector and assign looping over timepoints
|
|
236
241
|
this_angle = np.zeros(len(joint_angle))
|
|
242
|
+
broken_angle_idxs = [] # initialise broken idxs-list for each angle anew
|
|
237
243
|
for t in range(len(joint_angle)):
|
|
238
|
-
this_angle[t] = compute_angle(
|
|
244
|
+
this_angle[t], broken = compute_angle(
|
|
245
|
+
joint_angle[t, :], joint2[t, :], joint3[t, :]
|
|
246
|
+
)
|
|
247
|
+
if broken:
|
|
248
|
+
broken_angle_idxs.append(t)
|
|
249
|
+
if broken_angle_idxs:
|
|
250
|
+
write_angle_warning(step, a, angles, broken_angle_idxs, info)
|
|
239
251
|
this_colname = angle + "Angle"
|
|
240
252
|
step[this_colname] = this_angle
|
|
241
253
|
return step
|
|
242
254
|
|
|
243
255
|
|
|
244
|
-
def compute_angle(joint_angle, joint2, joint3):
|
|
245
|
-
"""Compute a given angle at a joint & a given timepoint"""
|
|
246
|
-
# Get vectors between the joints
|
|
247
|
-
v1 = (joint_angle[0] - joint2[0], joint_angle[1] - joint2[1])
|
|
248
|
-
v2 = (joint_angle[0] - joint3[0], joint_angle[1] - joint3[1])
|
|
249
|
-
# dot product, magnitude of vectors, angle in radians & convert 2 degrees
|
|
250
|
-
dot_product = v1[0] * v2[0] + v1[1] * v2[1]
|
|
251
|
-
mag_v1 = math.sqrt(v1[0] ** 2 + v1[1] ** 2)
|
|
252
|
-
mag_v2 = math.sqrt(v2[0] ** 2 + v2[1] ** 2)
|
|
253
|
-
angle = math.acos(dot_product / (mag_v1 * mag_v2))
|
|
254
|
-
return math.degrees(angle)
|
|
255
|
-
|
|
256
|
-
|
|
257
256
|
def add_x_velocities(step, cfg):
|
|
258
257
|
"""Feature #2: Joint x Velocities & Accelerations"""
|
|
259
258
|
# unpack
|
|
@@ -6,6 +6,9 @@ import copy
|
|
|
6
6
|
import numpy as np
|
|
7
7
|
import tkinter as tk
|
|
8
8
|
|
|
9
|
+
# %% constants
|
|
10
|
+
from autogaita.resources.constants import TIME_COL
|
|
11
|
+
|
|
9
12
|
|
|
10
13
|
def run_singlerun_in_multirun(tracking_software, idx, info, folderinfo, cfg):
|
|
11
14
|
"""When performing a multirun, either via Batch Analysis in GUI or batchrun scripts, run the analysis for a given dataset"""
|
|
@@ -61,7 +64,7 @@ def extract_info(tracking_software, folderinfo, in_GUI=False):
|
|
|
61
64
|
for string_addition in FILE_ID_STRING_ADDITIONS:
|
|
62
65
|
try:
|
|
63
66
|
candidate_postmouse_string = string_addition + postmouse_string
|
|
64
|
-
this_mouse_num = find_number(
|
|
67
|
+
this_mouse_num, leading_mouse_num_zeros = find_number(
|
|
65
68
|
filename,
|
|
66
69
|
premouse_string,
|
|
67
70
|
candidate_postmouse_string,
|
|
@@ -84,7 +87,7 @@ def extract_info(tracking_software, folderinfo, in_GUI=False):
|
|
|
84
87
|
for string_addition in FILE_ID_STRING_ADDITIONS:
|
|
85
88
|
try:
|
|
86
89
|
candidate_postrun_string = string_addition + postrun_string
|
|
87
|
-
this_run_num = find_number(
|
|
90
|
+
this_run_num, leading_run_num_zeros = find_number(
|
|
88
91
|
filename, prerun_string, candidate_postrun_string
|
|
89
92
|
)
|
|
90
93
|
except:
|
|
@@ -106,6 +109,14 @@ def extract_info(tracking_software, folderinfo, in_GUI=False):
|
|
|
106
109
|
info["name"].append(this_name)
|
|
107
110
|
info["mouse_num"].append(this_mouse_num)
|
|
108
111
|
info["run_num"].append(this_run_num)
|
|
112
|
+
# if we had to fix leading zeros, make sure to save them so we can use them in
|
|
113
|
+
# move_data_to_folders function later
|
|
114
|
+
# => make sure it's lists of strings because this is needed by the
|
|
115
|
+
# run_singlerun_in_multiruns function later
|
|
116
|
+
if leading_mouse_num_zeros:
|
|
117
|
+
info["leading_mouse_num_zeros"] = [leading_mouse_num_zeros]
|
|
118
|
+
if leading_run_num_zeros:
|
|
119
|
+
info["leading_run_num_zeros"] = [leading_run_num_zeros]
|
|
109
120
|
# this might happen if user entered wrong identifiers or folder
|
|
110
121
|
if len(info["name"]) < 1:
|
|
111
122
|
no_files_message = (
|
|
@@ -143,7 +154,12 @@ def find_number(fullstring, prestring, poststring):
|
|
|
143
154
|
"""Find (mouse/run) number based on user-defined strings in filenames"""
|
|
144
155
|
start_idx = fullstring.find(prestring) + len(prestring)
|
|
145
156
|
end_idx = fullstring.find(poststring)
|
|
146
|
-
|
|
157
|
+
# handle leading zeros
|
|
158
|
+
leading_zeros = ""
|
|
159
|
+
while fullstring[start_idx] == "0" and start_idx < end_idx - 1:
|
|
160
|
+
start_idx += 1
|
|
161
|
+
leading_zeros += "0"
|
|
162
|
+
return int(fullstring[start_idx:end_idx]), leading_zeros
|
|
147
163
|
|
|
148
164
|
|
|
149
165
|
# ........................... SC extraction helpers ..................................
|
|
@@ -210,7 +226,81 @@ def check_cycle_order(all_cycles, info):
|
|
|
210
226
|
return clean_cycles
|
|
211
227
|
|
|
212
228
|
|
|
213
|
-
def
|
|
229
|
+
def check_differing_angle_joint_coords(all_cycles, data, info, cfg):
|
|
230
|
+
"""Check if none of the joints used for angle computations later have equal values (since this would lead to math.domain errors due to floating point precision)"""
|
|
231
|
+
|
|
232
|
+
# Note
|
|
233
|
+
# ----
|
|
234
|
+
# In theory, I could fix this programatically in the add_angle function, but I feel
|
|
235
|
+
# like joint-coords should not often be exactly equal like this in a meaningful way
|
|
236
|
+
# We can still change it in the future.
|
|
237
|
+
|
|
238
|
+
# unpack
|
|
239
|
+
angles = cfg["angles"]
|
|
240
|
+
|
|
241
|
+
clean_cycles = None
|
|
242
|
+
for c, cycle in enumerate(all_cycles): # for each SC
|
|
243
|
+
cycle = check_a_single_cycle_for_joint_coords(cycle, angles, data, c, info)
|
|
244
|
+
if cycle: # if cycle was not valid (equal-joint-coords) this returns None
|
|
245
|
+
if clean_cycles == None:
|
|
246
|
+
clean_cycles = [cycle] # also makes a 2xscs list of lists
|
|
247
|
+
else:
|
|
248
|
+
clean_cycles.append(cycle)
|
|
249
|
+
return clean_cycles
|
|
250
|
+
|
|
251
|
+
|
|
252
|
+
def check_a_single_cycle_for_joint_coords(cycle, angles, data, c, info):
|
|
253
|
+
for a in range(len(angles["name"])): # for each angle configuration
|
|
254
|
+
# prepare a dict that has only the data of this angle config's joints
|
|
255
|
+
this_angle_data = {"name": [], "lower_joint": [], "upper_joint": []}
|
|
256
|
+
for key in this_angle_data.keys():
|
|
257
|
+
this_joint = angles[key][a]
|
|
258
|
+
this_angle_data[key] = np.array(
|
|
259
|
+
[data[this_joint + "x"], data[this_joint + "y"]]
|
|
260
|
+
)
|
|
261
|
+
# now check if any of the joints have the same coord at any idx
|
|
262
|
+
for idx in range(cycle[0], cycle[1]):
|
|
263
|
+
if (
|
|
264
|
+
np.array_equal(
|
|
265
|
+
this_angle_data["name"][:, idx],
|
|
266
|
+
this_angle_data["lower_joint"][:, idx],
|
|
267
|
+
)
|
|
268
|
+
or np.array_equal(
|
|
269
|
+
this_angle_data["name"][:, idx],
|
|
270
|
+
this_angle_data["upper_joint"][:, idx],
|
|
271
|
+
)
|
|
272
|
+
or np.array_equal(
|
|
273
|
+
this_angle_data["lower_joint"][:, idx],
|
|
274
|
+
this_angle_data["upper_joint"][:, idx],
|
|
275
|
+
)
|
|
276
|
+
):
|
|
277
|
+
this_message = (
|
|
278
|
+
"\n***********\n! WARNING !\n***********\n"
|
|
279
|
+
+ f"SC #{c + 1} has equal joint coordinates at "
|
|
280
|
+
+ f"{round(data[TIME_COL][idx],4)}s:"
|
|
281
|
+
+ "\n\nAngle - [x y]:\n"
|
|
282
|
+
+ angles["name"][a]
|
|
283
|
+
+ " - "
|
|
284
|
+
+ str(this_angle_data["name"][:, idx])
|
|
285
|
+
+ "\nLower joint: "
|
|
286
|
+
+ angles["lower_joint"][a]
|
|
287
|
+
+ " - "
|
|
288
|
+
+ str(this_angle_data["lower_joint"][:, idx])
|
|
289
|
+
+ "\nUpper joint: "
|
|
290
|
+
+ angles["upper_joint"][a]
|
|
291
|
+
+ " - "
|
|
292
|
+
+ str(this_angle_data["upper_joint"][:, idx])
|
|
293
|
+
+ "\nRemoving the SC from "
|
|
294
|
+
+ f"{round(data[TIME_COL][cycle[0]], 4)}-"
|
|
295
|
+
+ f"{round(data[TIME_COL][cycle[1]], 4)}s"
|
|
296
|
+
)
|
|
297
|
+
print(this_message)
|
|
298
|
+
write_issues_to_textfile(this_message, info)
|
|
299
|
+
return None # removes this SC
|
|
300
|
+
return cycle # if we never returned None, this SC is valid
|
|
301
|
+
|
|
302
|
+
|
|
303
|
+
def check_tracking_xy_thresholds(all_cycles, data, info, cfg):
|
|
214
304
|
"""Check if any x/y column of any joint has broken datapoints"""
|
|
215
305
|
# unpack
|
|
216
306
|
convert_to_mm = cfg["convert_to_mm"]
|
|
@@ -255,7 +345,7 @@ def check_tracking_xy_thresholds(data, info, all_cycles, cfg):
|
|
|
255
345
|
return clean_cycles
|
|
256
346
|
|
|
257
347
|
|
|
258
|
-
def check_tracking_SLEAP_nans(data, info,
|
|
348
|
+
def check_tracking_SLEAP_nans(all_cycles, data, info, cfg):
|
|
259
349
|
"""In SLEAP if tracking fails it generates NaNs - make sure we don't have those in any SC in any joint or angle-joint"""
|
|
260
350
|
# unpack
|
|
261
351
|
hind_joints = cfg["hind_joints"]
|