autogaita 0.0.5__tar.gz → 0.0.6__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-0.0.5 → autogaita-0.0.6}/PKG-INFO +3 -1
- {autogaita-0.0.5 → autogaita-0.0.6}/README.md +16 -9
- autogaita-0.0.6/autogaita/__init__.py +20 -0
- {autogaita-0.0.5 → autogaita-0.0.6}/autogaita/autogaita.py +1 -1
- {autogaita-0.0.5 → autogaita-0.0.6}/autogaita/autogaita_dlc.py +127 -34
- {autogaita-0.0.5 → autogaita-0.0.6}/autogaita/autogaita_dlc_gui.py +199 -89
- {autogaita-0.0.5 → autogaita-0.0.6}/autogaita/autogaita_group.py +95 -57
- {autogaita-0.0.5 → autogaita-0.0.6}/autogaita/autogaita_group_gui.py +31 -10
- {autogaita-0.0.5 → autogaita-0.0.6}/autogaita/autogaita_simi.py +74 -46
- {autogaita-0.0.5 → autogaita-0.0.6}/autogaita/autogaita_simi_gui.py +27 -23
- {autogaita-0.0.5 → autogaita-0.0.6}/autogaita/autogaita_utils.py +3 -5
- {autogaita-0.0.5 → autogaita-0.0.6}/autogaita/batchrun_scripts/autogaita_dlc_multirun.py +42 -33
- {autogaita-0.0.5 → autogaita-0.0.6}/autogaita/batchrun_scripts/autogaita_dlc_singlerun.py +31 -21
- {autogaita-0.0.5 → autogaita-0.0.6}/autogaita/batchrun_scripts/autogaita_group_dlcrun.py +3 -2
- {autogaita-0.0.5 → autogaita-0.0.6}/autogaita/batchrun_scripts/autogaita_group_simirun.py +8 -6
- {autogaita-0.0.5 → autogaita-0.0.6}/autogaita/batchrun_scripts/autogaita_simi_multirun.py +13 -10
- {autogaita-0.0.5 → autogaita-0.0.6}/autogaita/batchrun_scripts/autogaita_simi_singlerun.py +10 -7
- {autogaita-0.0.5 → autogaita-0.0.6}/autogaita.egg-info/PKG-INFO +3 -1
- autogaita-0.0.6/autogaita.egg-info/requires.txt +14 -0
- {autogaita-0.0.5 → autogaita-0.0.6}/setup.py +16 -14
- autogaita-0.0.5/autogaita/__init__.py +0 -1
- autogaita-0.0.5/autogaita.egg-info/requires.txt +0 -11
- {autogaita-0.0.5 → autogaita-0.0.6}/LICENSE +0 -0
- {autogaita-0.0.5 → autogaita-0.0.6}/autogaita/__main__.py +0 -0
- {autogaita-0.0.5 → autogaita-0.0.6}/autogaita/autogaita_icon.icns +0 -0
- {autogaita-0.0.5 → autogaita-0.0.6}/autogaita/autogaita_icon.ico +0 -0
- {autogaita-0.0.5 → autogaita-0.0.6}/autogaita/autogaita_logo.png +0 -0
- {autogaita-0.0.5 → autogaita-0.0.6}/autogaita/batchrun_scripts/__init__.py +0 -0
- {autogaita-0.0.5 → autogaita-0.0.6}/autogaita.egg-info/SOURCES.txt +0 -0
- {autogaita-0.0.5 → autogaita-0.0.6}/autogaita.egg-info/dependency_links.txt +0 -0
- {autogaita-0.0.5 → autogaita-0.0.6}/autogaita.egg-info/top_level.txt +0 -0
- {autogaita-0.0.5 → autogaita-0.0.6}/setup.cfg +0 -0
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: autogaita
|
|
3
|
-
Version: 0.0.
|
|
3
|
+
Version: 0.0.6
|
|
4
4
|
Summary: Automatic Gait Analysis in Python
|
|
5
5
|
Home-page: https://github.com/mahan-hosseini/AutoGaitA/
|
|
6
6
|
Author: Mahan Hosseini
|
|
7
7
|
License: GPLv3
|
|
8
|
+
Requires-Python: >=3.10
|
|
9
|
+
Provides-Extra: dev
|
|
8
10
|
License-File: LICENSE
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|

|
|
2
|
-
](https://badge.fury.io/py/autogaita)
|
|
3
|
+
[](https://github.com/mahan-hosseini/AutoGaitA/actions/workflows/auto_test_gaita.yml)
|
|
4
|
+

|
|
3
5
|

|
|
4
6
|

|
|
5
7
|
# Automated Gait Analysis in Python 🐸
|
|
@@ -13,17 +15,18 @@
|
|
|
13
15
|
|
|
14
16
|
***Note!** Our documentation provides step-by-step walkthroughs of how to install autogaita for **[Windows](https://docs.google.com/document/d/1Y4wrrsjs0ybLDKPzE2LAatqPDq9jtwjIuk4M0jRZ3wE/edit#heading=h.28j6wu2vamre)** and **[Mac](https://docs.google.com/document/d/1Y4wrrsjs0ybLDKPzE2LAatqPDq9jtwjIuk4M0jRZ3wE/edit)***
|
|
15
17
|
|
|
16
|
-
It is strongly recommended that a separate virtual environment for AutoGaitA is created
|
|
18
|
+
It is strongly recommended that a separate virtual environment for AutoGaitA is created (note that the approach below creates the virtual environment to your current directory):
|
|
17
19
|
|
|
18
|
-
|
|
20
|
+
- Create the virtual environment
|
|
21
|
+
- `python -m venv env_gaita`
|
|
19
22
|
|
|
20
|
-
After
|
|
23
|
+
- After creation, activate the virtual environment via:
|
|
24
|
+
- *Windows:* `env_gaita\Scripts\activate`
|
|
25
|
+
- *Mac:* `source env_gaita/bin/activate`
|
|
21
26
|
|
|
22
|
-
`pip install autogaita
|
|
27
|
+
- Once activated, install AutoGaitA in the virtual environment via pip: `pip install autogaita`.
|
|
23
28
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
`python -m autogaita`
|
|
29
|
+
- Access the main user interface via `python -m autogaita`.
|
|
27
30
|
|
|
28
31
|
## Tutorials and Examples
|
|
29
32
|
|
|
@@ -54,9 +57,13 @@ By default, *AutoGaitA DLC* and *AutoGaitA Simi* implement standard values for m
|
|
|
54
57
|
- [Documentation - AutoGaitA DLC](https://docs.google.com/document/d/1Y4wrrsjs0ybLDKPzE2LAatqPDq9jtwjIuk4M0jRZ3wE/edit#heading=h.20bg7b7ymt0b)
|
|
55
58
|
- [Documentation - AutoGaitA Simi](https://docs.google.com/document/d/1Y4wrrsjs0ybLDKPzE2LAatqPDq9jtwjIuk4M0jRZ3wE/edit#heading=h.uz61bpmua7qz)
|
|
56
59
|
|
|
60
|
+
## Reference
|
|
61
|
+
If you use this code or data please [cite our preprint](https://www.biorxiv.org/content/10.1101/2024.04.14.589409v1).
|
|
62
|
+
|
|
57
63
|
## License
|
|
64
|
+
AutoGaitA is licensed under [GPL v3.0](https://github.com/mahan-hosseini/AutoGaitA/blob/main/LICENSE) and Forschungszentrum Jülich GmbH holds all copyrights.
|
|
58
65
|
|
|
59
|
-
AutoGaitA is
|
|
66
|
+
The AutoGaitA software is provided without warranty of any kind, express or implied, including, but not limited to, the implied warranty of fitness for a particular purpose.
|
|
60
67
|
|
|
61
68
|
## Authors
|
|
62
69
|
Mahan Hosseini
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# main gui
|
|
2
|
+
from .autogaita import gui # autogaita.gui()
|
|
3
|
+
|
|
4
|
+
# 3 sub-guis
|
|
5
|
+
from .autogaita_dlc_gui import dlc_gui # autogaita.dlc_gui()
|
|
6
|
+
from .autogaita_simi_gui import simi_gui # autogaita.simi_gui()
|
|
7
|
+
from .autogaita_group_gui import group_gui # autogaita.group_gui()
|
|
8
|
+
|
|
9
|
+
# 3 main functions
|
|
10
|
+
from .autogaita_dlc import dlc # autogaita.dlc(info, folderinfo, cfg)
|
|
11
|
+
from .autogaita_simi import simi # autogaita.simi(info, folderinfo, cfg)
|
|
12
|
+
from .autogaita_group import group # autogaita.group(folderinfo, cfg)
|
|
13
|
+
|
|
14
|
+
# 6 batchrun functions - call via e.g. autogaita.dlc_singlerun()
|
|
15
|
+
from .batchrun_scripts.autogaita_dlc_singlerun import dlc_singlerun
|
|
16
|
+
from .batchrun_scripts.autogaita_dlc_multirun import dlc_multirun
|
|
17
|
+
from .batchrun_scripts.autogaita_simi_singlerun import simi_singlerun
|
|
18
|
+
from .batchrun_scripts.autogaita_simi_multirun import simi_multirun
|
|
19
|
+
from .batchrun_scripts.autogaita_group_dlcrun import group_dlcrun
|
|
20
|
+
from .batchrun_scripts.autogaita_group_simirun import group_simirun
|
|
@@ -124,7 +124,7 @@ def configure_the_icon(root):
|
|
|
124
124
|
try:
|
|
125
125
|
from Cocoa import NSApplication, NSImage
|
|
126
126
|
except ImportError:
|
|
127
|
-
print(
|
|
127
|
+
print("Unable to import pyobjc modules")
|
|
128
128
|
else:
|
|
129
129
|
with resources.path("autogaita", "autogaita_icon.icns") as icon_path:
|
|
130
130
|
ns_application = NSApplication.sharedApplication()
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
# %% imports
|
|
2
2
|
import os
|
|
3
3
|
import sys
|
|
4
|
-
import pdb
|
|
5
4
|
import shutil
|
|
6
5
|
import json
|
|
6
|
+
import warnings
|
|
7
7
|
import pandas as pd
|
|
8
8
|
import numpy as np
|
|
9
9
|
import math
|
|
@@ -17,8 +17,17 @@ TIME_COL = "Time"
|
|
|
17
17
|
ISSUES_TXT_FILENAME = "Issues.txt" # filename to which we write issues-info
|
|
18
18
|
CONFIG_JSON_FILENAME = "config.json" # filename to which we write cfg-infos
|
|
19
19
|
SCXLS_MOUSECOLS = [
|
|
20
|
-
"Mouse",
|
|
21
|
-
|
|
20
|
+
"Mouse",
|
|
21
|
+
"mouse",
|
|
22
|
+
"Fly",
|
|
23
|
+
"fly",
|
|
24
|
+
"Animal",
|
|
25
|
+
"animal",
|
|
26
|
+
"Subject",
|
|
27
|
+
"subject",
|
|
28
|
+
"ID",
|
|
29
|
+
"id",
|
|
30
|
+
] # SC XLS info
|
|
22
31
|
SCXLS_RUNCOLS = ["Run", "run", "Runs", "runs", "Trial", "trial", "Trials", "trials"]
|
|
23
32
|
SCXLS_SCCOLS = ["SC Number", "SC number", "sc number", "SC Num", "sc num", "SC num"]
|
|
24
33
|
SWINGSTART_COL = "Swing (ti)"
|
|
@@ -235,7 +244,7 @@ def some_prep(info, folderinfo, cfg):
|
|
|
235
244
|
# if wanted: fix that deeplabcut inverses y
|
|
236
245
|
if invert_y_axis:
|
|
237
246
|
for col in data.columns:
|
|
238
|
-
if col.endswith("y"):
|
|
247
|
+
if col.endswith(" y"):
|
|
239
248
|
data[col] = data[col] * -1
|
|
240
249
|
# if we don't have a beam to subtract, normalise y to global y minimum being 0
|
|
241
250
|
if not subtract_beam:
|
|
@@ -259,16 +268,18 @@ def some_prep(info, folderinfo, cfg):
|
|
|
259
268
|
# pushing it towards zero.
|
|
260
269
|
# => using list(set()) to ensure that we don't have duplicate values (if users
|
|
261
270
|
# should have provided them in both cfg vars by misstake)
|
|
271
|
+
# => beam_col_left and right is provided by users
|
|
262
272
|
if subtract_beam:
|
|
273
|
+
# note beam_col_left/right are always lists in cfg!
|
|
274
|
+
beam_col_left = cfg["beam_col_left"][0]
|
|
275
|
+
beam_col_right = cfg["beam_col_right"][0]
|
|
263
276
|
for joint in list(set(hind_joints + beam_hind_jointadd)):
|
|
264
|
-
data[joint + "y"] = data[joint + "y"] - data["
|
|
277
|
+
data[joint + "y"] = data[joint + "y"] - data[beam_col_left + "y"]
|
|
265
278
|
for joint in list(set(fore_joints + beam_fore_jointadd)):
|
|
266
|
-
data[joint + "y"] = data[joint + "y"] - data["
|
|
267
|
-
|
|
268
|
-
data.drop(columns=dropcols, inplace=True) # beamcols not needed anymore
|
|
279
|
+
data[joint + "y"] = data[joint + "y"] - data[beam_col_right + "y"]
|
|
280
|
+
data.drop(columns=list(beamdf.columns), inplace=True) # beam not needed anymore
|
|
269
281
|
# add Time and round based on sampling rate
|
|
270
282
|
data[TIME_COL] = data.index * (1 / sampling_rate)
|
|
271
|
-
pdb.set_trace()
|
|
272
283
|
if sampling_rate <= 100:
|
|
273
284
|
data[TIME_COL] = round(data[TIME_COL], 2)
|
|
274
285
|
elif 100 < sampling_rate <= 1000:
|
|
@@ -343,13 +354,16 @@ def test_and_expand_cfg(data, cfg, info):
|
|
|
343
354
|
Check that all features are present in the dataset
|
|
344
355
|
Add plot_joints & direction_joint
|
|
345
356
|
Make sure to set dont_show_plots to True if Python is not in interactive mode
|
|
357
|
+
If users subtract a beam, set normalise @ sc level to False
|
|
346
358
|
"""
|
|
347
359
|
|
|
348
|
-
# run the
|
|
360
|
+
# run the tests first
|
|
349
361
|
for cfg_key in [
|
|
350
362
|
"angles",
|
|
351
363
|
"hind_joints",
|
|
352
364
|
"fore_joints",
|
|
365
|
+
"beam_col_left", # note beamcols are lists even though len=1 bc. of
|
|
366
|
+
"beam_col_right", # check function's procedure
|
|
353
367
|
"beam_hind_jointadd",
|
|
354
368
|
"beam_fore_jointadd",
|
|
355
369
|
]:
|
|
@@ -373,19 +387,42 @@ def test_and_expand_cfg(data, cfg, info):
|
|
|
373
387
|
else:
|
|
374
388
|
cfg["plot_joints"] = hind_joints[: cfg["plot_joint_number"]]
|
|
375
389
|
|
|
376
|
-
# add direction_joint - used to determine gait direction
|
|
390
|
+
# check hindlimb joints & add direction_joint - used to determine gait direction
|
|
377
391
|
if not hind_joints: # no valid string after above cleaning
|
|
378
392
|
no_hind_joint_message = (
|
|
379
393
|
"\n******************\n! CRITICAL ERROR !\n******************\n"
|
|
380
394
|
+ "After testing your hind limb joint names, no valid joint was left to "
|
|
381
395
|
+ "perform gait direction checks on.\nPlease make sure that at least one "
|
|
382
396
|
+ "hind limb joint is provided & try again!"
|
|
383
|
-
|
|
397
|
+
)
|
|
384
398
|
write_issues_to_textfile(no_hind_joint_message, info)
|
|
385
399
|
print(no_hind_joint_message)
|
|
386
400
|
return
|
|
387
401
|
cfg["direction_joint"] = hind_joints[0]
|
|
388
402
|
|
|
403
|
+
# if subtracting beam, check that its colnames were valid.
|
|
404
|
+
if cfg["subtract_beam"]:
|
|
405
|
+
beam_col_error_message = (
|
|
406
|
+
"\n******************\n! CRITICAL ERROR !\n******************\n"
|
|
407
|
+
+ "It seems like you want to normalise heights to a baseline (beam)."
|
|
408
|
+
+ "\nUnfortunately we were unable to find the y-columns you listed in "
|
|
409
|
+
+ "your beam's csv-file.\nPlease try again.\nInvalid beam side(s) was/were:"
|
|
410
|
+
)
|
|
411
|
+
beam_error = False # check 3 possible cases
|
|
412
|
+
if (cfg["beam_col_left"]) and (not cfg["beam_col_right"]):
|
|
413
|
+
beam_error = True
|
|
414
|
+
beam_col_error_message += "\n right beam!"
|
|
415
|
+
elif (not cfg["beam_col_left"]) and (cfg["beam_col_right"]):
|
|
416
|
+
beam_error = True
|
|
417
|
+
beam_col_error_message += "\n left beam!"
|
|
418
|
+
elif (not cfg["beam_col_left"]) and (not cfg["beam_col_right"]):
|
|
419
|
+
beam_error = True
|
|
420
|
+
beam_col_error_message += "\n both beams!"
|
|
421
|
+
if beam_error: # if any case was True, stop everything
|
|
422
|
+
write_issues_to_textfile(beam_col_error_message, info)
|
|
423
|
+
print(beam_col_error_message)
|
|
424
|
+
return
|
|
425
|
+
|
|
389
426
|
# dont show plots
|
|
390
427
|
# !!! If users should complain that they dont get figures but they should, it might
|
|
391
428
|
# be because these lines wrongly determine user to be in non-interactive mode
|
|
@@ -394,6 +431,10 @@ def test_and_expand_cfg(data, cfg, info):
|
|
|
394
431
|
cfg["dont_show_plots"] = True
|
|
395
432
|
matplotlib.use("agg")
|
|
396
433
|
|
|
434
|
+
# never normalise @ SC level if user subtracted a beam
|
|
435
|
+
if cfg["subtract_beam"]:
|
|
436
|
+
cfg["normalise_height_at_SC_level"] = False
|
|
437
|
+
|
|
397
438
|
return cfg
|
|
398
439
|
|
|
399
440
|
|
|
@@ -438,7 +479,7 @@ def check_and_fix_cfg_strings(data, cfg, cfg_key, info):
|
|
|
438
479
|
clean_strings_message += "\n" + string
|
|
439
480
|
clean_strings_message += (
|
|
440
481
|
"\n\nNote that capitalisation matters."
|
|
441
|
-
+ "\nIf you are running a
|
|
482
|
+
+ "\nIf you are running a batch analysis, we'll use this updated cfg "
|
|
442
483
|
+ "throughout\nCheck out the config.json file for the full cfg used."
|
|
443
484
|
)
|
|
444
485
|
print(clean_strings_message)
|
|
@@ -446,6 +487,23 @@ def check_and_fix_cfg_strings(data, cfg, cfg_key, info):
|
|
|
446
487
|
|
|
447
488
|
# things are more involved for angle dicts
|
|
448
489
|
elif type(string_variable) is dict:
|
|
490
|
+
# 1) test if the lists of all keys are equally long, if not throw out last idxs
|
|
491
|
+
key_lengths = [len(string_variable[key]) for key in string_variable.keys()]
|
|
492
|
+
if not all(key_length == key_lengths[0] for key_length in key_lengths):
|
|
493
|
+
min_length = min(key_lengths)
|
|
494
|
+
for key in string_variable: # remove invalid idxs
|
|
495
|
+
string_variable[key] = string_variable[key][:min_length]
|
|
496
|
+
key_length_mismatch_message = ( # inform user
|
|
497
|
+
"\n***********\n! WARNING !\n***********\n"
|
|
498
|
+
+ "\nLength-mismatch in angle configuration!"
|
|
499
|
+
+ "\nCheck angles' name/upper/lower-joint entries."
|
|
500
|
+
+ "\nOnly processing first "
|
|
501
|
+
+ str(min_length)
|
|
502
|
+
+ " entries."
|
|
503
|
+
)
|
|
504
|
+
print(key_length_mismatch_message)
|
|
505
|
+
write_issues_to_textfile(key_length_mismatch_message, info)
|
|
506
|
+
# 2) check if all strings in the angle dict are valid columns
|
|
449
507
|
invalid_angletrio_message = ""
|
|
450
508
|
invalid_idxs = [] # these idxs hold across the 3 keys of our angles-dict
|
|
451
509
|
for key in string_variable:
|
|
@@ -460,7 +518,7 @@ def check_and_fix_cfg_strings(data, cfg, cfg_key, info):
|
|
|
460
518
|
invalid_idxs.append(idx)
|
|
461
519
|
if not this_keys_missing_strings: # first occurance
|
|
462
520
|
this_keys_missing_strings += "\nAngle's " + key + " key: "
|
|
463
|
-
this_keys_missing_strings +=
|
|
521
|
+
this_keys_missing_strings += string + "(#" + str(idx + 1) + ") / "
|
|
464
522
|
# string concat outside of idx-forloop above please
|
|
465
523
|
invalid_angletrio_message += this_keys_missing_strings
|
|
466
524
|
# if we have to remove idxs from all keys of our angles dict
|
|
@@ -497,7 +555,7 @@ def check_and_fix_cfg_strings(data, cfg, cfg_key, info):
|
|
|
497
555
|
)
|
|
498
556
|
clean_angles_message += (
|
|
499
557
|
"\n\nNote that capitalisation matters."
|
|
500
|
-
+ "\nIf you are running a
|
|
558
|
+
+ "\nIf you are running a batch analysis, we'll use this updated cfg "
|
|
501
559
|
+ "throughout\nCheck out the config.json file for the full cfg used."
|
|
502
560
|
)
|
|
503
561
|
print(clean_angles_message)
|
|
@@ -627,7 +685,7 @@ def extract_stepcycles(data, info, folderinfo, cfg):
|
|
|
627
685
|
SCdf = pd.read_excel(os.path.join(root_dir, sctable_filename))
|
|
628
686
|
else:
|
|
629
687
|
raise FileNotFoundError(
|
|
630
|
-
"No
|
|
688
|
+
"No Annotation Table found! sctable_filename has to be @ root_dir"
|
|
631
689
|
)
|
|
632
690
|
else:
|
|
633
691
|
# in cases below use string-concat (+) - otherwise xls added as path
|
|
@@ -637,7 +695,7 @@ def extract_stepcycles(data, info, folderinfo, cfg):
|
|
|
637
695
|
SCdf = pd.read_excel(os.path.join(root_dir, sctable_filename + ".xlsx"))
|
|
638
696
|
else:
|
|
639
697
|
raise FileNotFoundError(
|
|
640
|
-
"No
|
|
698
|
+
"No Annotation Table found! sctable_filename has to be @ root_dir"
|
|
641
699
|
)
|
|
642
700
|
# see if table columns are labelled correctly (try a couple to allow user typos)
|
|
643
701
|
valid_col_flags = [False, False, False]
|
|
@@ -770,6 +828,7 @@ def extract_stepcycles(data, info, folderinfo, cfg):
|
|
|
770
828
|
all_cycles = check_DLC_tracking(data, info, all_cycles, cfg)
|
|
771
829
|
return all_cycles
|
|
772
830
|
|
|
831
|
+
|
|
773
832
|
# .............................. helper functions ....................................
|
|
774
833
|
def check_cycle_out_of_bounds(all_cycles):
|
|
775
834
|
"""Check if user provided SC latencies that were not in video/data bounds"""
|
|
@@ -817,7 +876,7 @@ def check_cycle_order(all_cycles, info):
|
|
|
817
876
|
this_message = (
|
|
818
877
|
"\n***********\n! WARNING !\n***********\n"
|
|
819
878
|
+ "SC #"
|
|
820
|
-
+ str(c+1)
|
|
879
|
+
+ str(c + 1)
|
|
821
880
|
+ " has a later start than end latency - Skipping!"
|
|
822
881
|
)
|
|
823
882
|
print(this_message)
|
|
@@ -826,7 +885,7 @@ def check_cycle_order(all_cycles, info):
|
|
|
826
885
|
this_message = (
|
|
827
886
|
"\n***********\n! WARNING !\n***********\n"
|
|
828
887
|
+ "SC #"
|
|
829
|
-
+ str(c+1)
|
|
888
|
+
+ str(c + 1)
|
|
830
889
|
+ " has an earlier start than previous SC's end latency - Skipping!"
|
|
831
890
|
)
|
|
832
891
|
print(this_message)
|
|
@@ -930,7 +989,7 @@ def handle_issues(condition, info):
|
|
|
930
989
|
# ----
|
|
931
990
|
# There is quite a lot going on in this function. We:
|
|
932
991
|
# 1) loop through all step cycles for one leg at a time and extract data
|
|
933
|
-
# 2) for each step's data we normalise all y (height) values to the
|
|
992
|
+
# 2) for each step's data we normalise all y (height) values to the body's minimum
|
|
934
993
|
# if wanted
|
|
935
994
|
# 3) we compute and add features (angles, velocities, accelerations)
|
|
936
995
|
# ==> see norm_y_and_add_features_to_one_step & helper functions a
|
|
@@ -958,7 +1017,7 @@ def analyse_and_export_stepcycles(data, all_cycles, info, folderinfo, cfg):
|
|
|
958
1017
|
normalised_steps_data = normalise_one_steps_data(all_steps_data, bin_num)
|
|
959
1018
|
# 2 or more steps - build dataframe
|
|
960
1019
|
elif len(all_cycles) > 1:
|
|
961
|
-
# first step is added manually
|
|
1020
|
+
# first- step is added manually
|
|
962
1021
|
first_step = data_copy.loc[all_cycles[0][0] : all_cycles[0][1]]
|
|
963
1022
|
first_step = norm_y_and_add_features_to_one_step(first_step, cfg)
|
|
964
1023
|
all_steps_data = first_step
|
|
@@ -966,12 +1025,18 @@ def analyse_and_export_stepcycles(data, all_cycles, info, folderinfo, cfg):
|
|
|
966
1025
|
# some prep for addition of further steps
|
|
967
1026
|
sc_num = len(all_cycles)
|
|
968
1027
|
nanvector = data_copy.loc[[1]]
|
|
969
|
-
|
|
1028
|
+
with warnings.catch_warnings():
|
|
1029
|
+
warnings.simplefilter("ignore")
|
|
1030
|
+
nanvector[:] = np.nan
|
|
970
1031
|
# .............................. step-loop ...................................
|
|
971
1032
|
for s in range(1, sc_num, 1):
|
|
972
1033
|
# get step separators
|
|
973
1034
|
numvector = data_copy.loc[[1]]
|
|
974
|
-
|
|
1035
|
+
# we are ignoring this because we wont work with the incompatible dtypes ourselves much anymore (just export as xlsx and plot) - so its fine
|
|
1036
|
+
# https://docs.python.org/3/library/warnings.html#temporarily-suppressing-warnings
|
|
1037
|
+
with warnings.catch_warnings():
|
|
1038
|
+
warnings.simplefilter("ignore")
|
|
1039
|
+
numvector[:] = s + 1
|
|
975
1040
|
all_steps_data = add_step_separators(all_steps_data, nanvector, numvector)
|
|
976
1041
|
# this_step
|
|
977
1042
|
this_step = data_copy.loc[all_cycles[s][0] : all_cycles[s][1]]
|
|
@@ -1273,7 +1338,7 @@ def plot_results(info, results, folderinfo, cfg):
|
|
|
1273
1338
|
angular_acceleration = cfg["angular_acceleration"]
|
|
1274
1339
|
dont_show_plots = cfg["dont_show_plots"]
|
|
1275
1340
|
if dont_show_plots:
|
|
1276
|
-
plt.switch_backend(
|
|
1341
|
+
plt.switch_backend("Agg")
|
|
1277
1342
|
|
|
1278
1343
|
# ....................0 - extract SCs from all_steps_data...........................
|
|
1279
1344
|
sc_idxs = extract_sc_idxs(all_steps_data)
|
|
@@ -1397,9 +1462,19 @@ def plot_joint_y_by_x(all_steps_data, sc_idxs, info, cfg):
|
|
|
1397
1462
|
else:
|
|
1398
1463
|
float_precision = 4
|
|
1399
1464
|
this_label = (
|
|
1400
|
-
str(
|
|
1465
|
+
str(
|
|
1466
|
+
round(
|
|
1467
|
+
all_steps_data.iloc[sc_idxs[s][0], time_col_idx],
|
|
1468
|
+
float_precision,
|
|
1469
|
+
)
|
|
1470
|
+
)
|
|
1401
1471
|
+ "-"
|
|
1402
|
-
+ str(
|
|
1472
|
+
+ str(
|
|
1473
|
+
round(
|
|
1474
|
+
all_steps_data.iloc[sc_idxs[s][-1], time_col_idx],
|
|
1475
|
+
float_precision,
|
|
1476
|
+
)
|
|
1477
|
+
)
|
|
1403
1478
|
+ "s"
|
|
1404
1479
|
)
|
|
1405
1480
|
ax[j].plot(this_x, this_y, label=this_label)
|
|
@@ -1412,7 +1487,9 @@ def plot_joint_y_by_x(all_steps_data, sc_idxs, info, cfg):
|
|
|
1412
1487
|
figure_file_string = " - Foot y by x coordinates"
|
|
1413
1488
|
else:
|
|
1414
1489
|
figure_file_string = " - " + joint + "y by x coordinates"
|
|
1415
|
-
f[j].savefig(
|
|
1490
|
+
f[j].savefig(
|
|
1491
|
+
results_dir + name + figure_file_string + ".png", bbox_inches="tight"
|
|
1492
|
+
)
|
|
1416
1493
|
save_as_svg(f[j], results_dir, name, figure_file_string)
|
|
1417
1494
|
if dont_show_plots:
|
|
1418
1495
|
plt.close(f[j])
|
|
@@ -1448,7 +1525,9 @@ def plot_angles_by_time(all_steps_data, sc_idxs, info, cfg):
|
|
|
1448
1525
|
this_y = all_steps_data.iloc[sc_idxs[s], y_col_idx]
|
|
1449
1526
|
ax[a].plot(this_x, this_y)
|
|
1450
1527
|
figure_file_string = " - " + angle + " Angle by Time"
|
|
1451
|
-
f[a].savefig(
|
|
1528
|
+
f[a].savefig(
|
|
1529
|
+
results_dir + name + figure_file_string + ".png", bbox_inches="tight"
|
|
1530
|
+
)
|
|
1452
1531
|
save_as_svg(f[a], results_dir, name, figure_file_string)
|
|
1453
1532
|
if dont_show_plots:
|
|
1454
1533
|
plt.close(f[a])
|
|
@@ -1481,9 +1560,15 @@ def plot_hindlimb_stickdiagram(all_steps_data, sc_idxs, info, cfg):
|
|
|
1481
1560
|
else:
|
|
1482
1561
|
float_precision = 4
|
|
1483
1562
|
this_label = (
|
|
1484
|
-
str(
|
|
1563
|
+
str(
|
|
1564
|
+
round(all_steps_data.iloc[sc_idxs[s][0], time_col_idx], float_precision)
|
|
1565
|
+
)
|
|
1485
1566
|
+ "-"
|
|
1486
|
-
+ str(
|
|
1567
|
+
+ str(
|
|
1568
|
+
round(
|
|
1569
|
+
all_steps_data.iloc[sc_idxs[s][-1], time_col_idx], float_precision
|
|
1570
|
+
)
|
|
1571
|
+
)
|
|
1487
1572
|
+ "s"
|
|
1488
1573
|
)
|
|
1489
1574
|
for i in sc_idxs[s]: # loop over timepoints of current SC
|
|
@@ -1537,9 +1622,15 @@ def plot_forelimb_stickdiagram(all_steps_data, sc_idxs, info, cfg):
|
|
|
1537
1622
|
else:
|
|
1538
1623
|
float_precision = 4
|
|
1539
1624
|
this_label = (
|
|
1540
|
-
str(
|
|
1625
|
+
str(
|
|
1626
|
+
round(all_steps_data.iloc[sc_idxs[s][0], time_col_idx], float_precision)
|
|
1627
|
+
)
|
|
1541
1628
|
+ "-"
|
|
1542
|
-
+ str(
|
|
1629
|
+
+ str(
|
|
1630
|
+
round(
|
|
1631
|
+
all_steps_data.iloc[sc_idxs[s][-1], time_col_idx], float_precision
|
|
1632
|
+
)
|
|
1633
|
+
)
|
|
1543
1634
|
+ "s"
|
|
1544
1635
|
)
|
|
1545
1636
|
for i in sc_idxs[s]:
|
|
@@ -1827,7 +1918,9 @@ def save_as_svg(figure, results_dir, name, figure_file_string):
|
|
|
1827
1918
|
svg_dir = os.path.join(results_dir, "SVG Figures")
|
|
1828
1919
|
if not os.path.exists(svg_dir):
|
|
1829
1920
|
os.makedirs(svg_dir)
|
|
1830
|
-
figure.savefig(
|
|
1921
|
+
figure.savefig(
|
|
1922
|
+
svg_dir + "/" + name + figure_file_string + ".svg", bbox_inches="tight"
|
|
1923
|
+
)
|
|
1831
1924
|
|
|
1832
1925
|
|
|
1833
1926
|
def tickconvert_mm_to_cm(axis, whichlabel):
|
|
@@ -1872,5 +1965,5 @@ if __name__ == "__main__":
|
|
|
1872
1965
|
+ "possible.\nIf you prefer a non-GUI approach, please either: "
|
|
1873
1966
|
+ "\n1. Call this as a function, i.e. autogaita.dlc(info, folderinfo, cfg)"
|
|
1874
1967
|
+ "\n2. Use the single or multirun scripts in the batchrun_scripts folder"
|
|
1875
|
-
|
|
1968
|
+
)
|
|
1876
1969
|
print(dlc_info_message)
|