Spaces:
Runtime error
Runtime error
Commit
•
523a361
1
Parent(s):
2027cf1
Full Commit
Browse files- lib/fish_eye/.gitignore +104 -0
- lib/fish_eye/LICENSE +21 -0
- lib/fish_eye/README.md +19 -0
- lib/fish_eye/beam_widths/ARIS1800_1200_48.csv +49 -0
- lib/fish_eye/beam_widths/ARIS1800_96.csv +97 -0
- lib/fish_eye/beam_widths/ARIS3000_128.csv +129 -0
- lib/fish_eye/beam_widths/ARIS3000_64.csv +65 -0
- lib/fish_eye/beam_widths/ARIS_Telephoto_48.csv +49 -0
- lib/fish_eye/beam_widths/ARIS_Telephoto_96.csv +97 -0
- lib/fish_eye/beams.py +154 -0
- lib/fish_eye/convert_annotations.py +143 -0
- lib/fish_eye/data_format.md +72 -0
- lib/fish_eye/fish_length.py +38 -0
- lib/fish_eye/pyARIS.py +1009 -0
- lib/fish_eye/sort.py +238 -0
- lib/fish_eye/tracker.py +162 -0
- models/download_model.sh +1 -0
- models/dump.rdb +0 -0
- models/v5m_896_300best.pt +3 -0
- scripts/infer.py +18 -0
- scripts/project_path.py +8 -0
- static/css/style.css +138 -0
- static/images/Github:fisheye_flask.jpeg +0 -0
- static/images/fish.png +0 -0
- static/images/fisheye.png +0 -0
- static/images/salmon_640.jpeg +0 -0
- static/images/some images from pixabay.com.txt +0 -0
- static/svg/book-open-svgrepo-com.svg +5 -0
- static/svg/chemistry-flask-svgrepo-com.svg +6 -0
- static/svg/done-1477-svgrepo-com.svg +19 -0
- static/svg/eye-svgrepo-com.svg +36 -0
- static/svg/house-svgrepo-com.svg +4 -0
- static/svg/svgs from svgrepo.com.txt +0 -0
- static/svg/upload-svgrepo-com.svg +5 -0
lib/fish_eye/.gitignore
ADDED
@@ -0,0 +1,104 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# Byte-compiled / optimized / DLL files
|
2 |
+
__pycache__/
|
3 |
+
*.py[cod]
|
4 |
+
*$py.class
|
5 |
+
|
6 |
+
# C extensions
|
7 |
+
*.so
|
8 |
+
|
9 |
+
# Distribution / packaging
|
10 |
+
.Python
|
11 |
+
build/
|
12 |
+
develop-eggs/
|
13 |
+
dist/
|
14 |
+
downloads/
|
15 |
+
eggs/
|
16 |
+
.eggs/
|
17 |
+
lib/
|
18 |
+
lib64/
|
19 |
+
parts/
|
20 |
+
sdist/
|
21 |
+
var/
|
22 |
+
wheels/
|
23 |
+
*.egg-info/
|
24 |
+
.installed.cfg
|
25 |
+
*.egg
|
26 |
+
MANIFEST
|
27 |
+
|
28 |
+
# PyInstaller
|
29 |
+
# Usually these files are written by a python script from a template
|
30 |
+
# before PyInstaller builds the exe, so as to inject date/other infos into it.
|
31 |
+
*.manifest
|
32 |
+
*.spec
|
33 |
+
|
34 |
+
# Installer logs
|
35 |
+
pip-log.txt
|
36 |
+
pip-delete-this-directory.txt
|
37 |
+
|
38 |
+
# Unit test / coverage reports
|
39 |
+
htmlcov/
|
40 |
+
.tox/
|
41 |
+
.coverage
|
42 |
+
.coverage.*
|
43 |
+
.cache
|
44 |
+
nosetests.xml
|
45 |
+
coverage.xml
|
46 |
+
*.cover
|
47 |
+
.hypothesis/
|
48 |
+
.pytest_cache/
|
49 |
+
|
50 |
+
# Translations
|
51 |
+
*.mo
|
52 |
+
*.pot
|
53 |
+
|
54 |
+
# Django stuff:
|
55 |
+
*.log
|
56 |
+
local_settings.py
|
57 |
+
db.sqlite3
|
58 |
+
|
59 |
+
# Flask stuff:
|
60 |
+
instance/
|
61 |
+
.webassets-cache
|
62 |
+
|
63 |
+
# Scrapy stuff:
|
64 |
+
.scrapy
|
65 |
+
|
66 |
+
# Sphinx documentation
|
67 |
+
docs/_build/
|
68 |
+
|
69 |
+
# PyBuilder
|
70 |
+
target/
|
71 |
+
|
72 |
+
# Jupyter Notebook
|
73 |
+
.ipynb_checkpoints
|
74 |
+
|
75 |
+
# pyenv
|
76 |
+
.python-version
|
77 |
+
|
78 |
+
# celery beat schedule file
|
79 |
+
celerybeat-schedule
|
80 |
+
|
81 |
+
# SageMath parsed files
|
82 |
+
*.sage.py
|
83 |
+
|
84 |
+
# Environments
|
85 |
+
.env
|
86 |
+
.venv
|
87 |
+
env/
|
88 |
+
venv/
|
89 |
+
ENV/
|
90 |
+
env.bak/
|
91 |
+
venv.bak/
|
92 |
+
|
93 |
+
# Spyder project settings
|
94 |
+
.spyderproject
|
95 |
+
.spyproject
|
96 |
+
|
97 |
+
# Rope project settings
|
98 |
+
.ropeproject
|
99 |
+
|
100 |
+
# mkdocs documentation
|
101 |
+
/site
|
102 |
+
|
103 |
+
# mypy
|
104 |
+
.mypy_cache/
|
lib/fish_eye/LICENSE
ADDED
@@ -0,0 +1,21 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
MIT License
|
2 |
+
|
3 |
+
Copyright (c) 2019 Grant Van Horn
|
4 |
+
|
5 |
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6 |
+
of this software and associated documentation files (the "Software"), to deal
|
7 |
+
in the Software without restriction, including without limitation the rights
|
8 |
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9 |
+
copies of the Software, and to permit persons to whom the Software is
|
10 |
+
furnished to do so, subject to the following conditions:
|
11 |
+
|
12 |
+
The above copyright notice and this permission notice shall be included in all
|
13 |
+
copies or substantial portions of the Software.
|
14 |
+
|
15 |
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16 |
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17 |
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18 |
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19 |
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20 |
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
21 |
+
SOFTWARE.
|
lib/fish_eye/README.md
ADDED
@@ -0,0 +1,19 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# Converting ARIS into image zipfiles that can be provided to annotators:
|
2 |
+
```
|
3 |
+
gen_clips.py --aris_dir [location of source files]
|
4 |
+
--clip_dir [location to output unzipped images]
|
5 |
+
--dump_dir [location to output json dumps of created clips]
|
6 |
+
--river_name [eg. kenai]
|
7 |
+
--river_location [eg. ak]
|
8 |
+
--zip_location [location to output zipped images for annotation]
|
9 |
+
```
|
10 |
+
|
11 |
+
# Annotate zip files:
|
12 |
+
https://kulits.github.io/vatic.js/index.html
|
13 |
+
|
14 |
+
# Convert outputs of annotation tool to common format:
|
15 |
+
```
|
16 |
+
convert_annotations.py --json_dump_path [path to json dump of annotated files created in the previous step]
|
17 |
+
--xml_dir [location of annotations created in the previous step]
|
18 |
+
--output_path [location of place to put converted annotations]
|
19 |
+
```
|
lib/fish_eye/beam_widths/ARIS1800_1200_48.csv
ADDED
@@ -0,0 +1,49 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
beam_num,beam_center,beam_left,beam_right
|
2 |
+
0,-13.5735,-13.7893,-13.3577
|
3 |
+
1,-13.1419,-13.3577,-12.9211
|
4 |
+
2,-12.7003,-12.9211,-12.4723
|
5 |
+
3,-12.2442,-12.4723,-12.0082
|
6 |
+
4,-11.7721,-12.0082,-11.5301
|
7 |
+
5,-11.2881,-11.5301,-11.0401
|
8 |
+
6,-10.7921,-11.0401,-10.5373
|
9 |
+
7,-10.2824,-10.5373,-10.0211
|
10 |
+
8,-9.7597,-10.0211,-9.4902
|
11 |
+
9,-9.2206,-9.4902,-8.9418
|
12 |
+
10,-8.663,-8.9418,-8.3765
|
13 |
+
11,-8.0899,-8.3765,-7.7963
|
14 |
+
12,-7.5026,-7.7963,-7.2023
|
15 |
+
13,-6.9019,-7.2023,-6.5958
|
16 |
+
14,-6.2897,-6.5958,-5.9779
|
17 |
+
15,-5.6661,-5.9779,-5.3476
|
18 |
+
16,-5.0291,-5.3476,-4.7057
|
19 |
+
17,-4.3823,-4.7057,-4.0544
|
20 |
+
18,-3.7264,-4.0544,-3.3948
|
21 |
+
19,-3.0632,-3.3948,-2.7282
|
22 |
+
20,-2.3931,-2.7282,-2.0551
|
23 |
+
21,-1.717,-2.0551,-1.3757
|
24 |
+
22,-1.0343,-1.3757,-0.6902
|
25 |
+
23,-0.3461,-0.6902,0.0
|
26 |
+
24,0.3461,0.0,0.6902
|
27 |
+
25,1.0343,0.6902,1.3757
|
28 |
+
26,1.717,1.3757,2.0551
|
29 |
+
27,2.3931,2.0551,2.7282
|
30 |
+
28,3.0632,2.7282,3.3948
|
31 |
+
29,3.7264,3.3948,4.0544
|
32 |
+
30,4.3823,4.0544,4.7057
|
33 |
+
31,5.0291,4.7057,5.3476
|
34 |
+
32,5.6661,5.3476,5.9779
|
35 |
+
33,6.2897,5.9779,6.5958
|
36 |
+
34,6.9019,6.5958,7.2023
|
37 |
+
35,7.5026,7.2023,7.7963
|
38 |
+
36,8.0899,7.7963,8.3765
|
39 |
+
37,8.663,8.3765,8.9418
|
40 |
+
38,9.2206,8.9418,9.4902
|
41 |
+
39,9.7597,9.4902,10.0211
|
42 |
+
40,10.2824,10.0211,10.5373
|
43 |
+
41,10.7921,10.5373,11.0401
|
44 |
+
42,11.2881,11.0401,11.5301
|
45 |
+
43,11.7721,11.5301,12.0082
|
46 |
+
44,12.2442,12.0082,12.4723
|
47 |
+
45,12.7003,12.4723,12.9211
|
48 |
+
46,13.1419,12.9211,13.3577
|
49 |
+
47,13.5735,13.3577,13.7893
|
lib/fish_eye/beam_widths/ARIS1800_96.csv
ADDED
@@ -0,0 +1,97 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
beam_num,beam_center,beam_left,beam_right
|
2 |
+
0,-13.749,-13.8562,-13.6419
|
3 |
+
1,-13.5347,-13.6419,-13.4261
|
4 |
+
2,-13.3174,-13.4261,-13.2075
|
5 |
+
3,-13.0975,-13.2075,-12.9867
|
6 |
+
4,-12.8758,-12.9867,-12.7628
|
7 |
+
5,-12.6498,-12.7628,-12.5348
|
8 |
+
6,-12.4197,-12.5348,-12.3026
|
9 |
+
7,-12.1855,-12.3026,-12.0666
|
10 |
+
8,-11.9476,-12.0666,-11.8273
|
11 |
+
9,-11.7069,-11.8273,-11.5853
|
12 |
+
10,-11.4636,-11.5853,-11.3405
|
13 |
+
11,-11.2173,-11.3405,-11.0925
|
14 |
+
12,-10.9676,-11.0925,-10.841
|
15 |
+
13,-10.7144,-10.841,-10.5862
|
16 |
+
14,-10.4579,-10.5862,-10.3282
|
17 |
+
15,-10.1985,-10.3282,-10.0669
|
18 |
+
16,-9.9352,-10.0669,-9.8016
|
19 |
+
17,-9.668,-9.8016,-9.5321
|
20 |
+
18,-9.3961,-9.5321,-9.2578
|
21 |
+
19,-9.1195,-9.2578,-8.979
|
22 |
+
20,-8.8385,-8.979,-8.696
|
23 |
+
21,-8.5535,-8.696,-8.4095
|
24 |
+
22,-8.2654,-8.4095,-8.1196
|
25 |
+
23,-7.9738,-8.1196,-7.826
|
26 |
+
24,-7.6781,-7.826,-7.5287
|
27 |
+
25,-7.3793,-7.5287,-7.2284
|
28 |
+
26,-7.0774,-7.2284,-6.9251
|
29 |
+
27,-6.7727,-6.9251,-6.619
|
30 |
+
28,-6.4652,-6.619,-6.3101
|
31 |
+
29,-6.1549,-6.3101,-5.9983
|
32 |
+
30,-5.8416,-5.9983,-5.6831
|
33 |
+
31,-5.5246,-5.6831,-5.3646
|
34 |
+
32,-5.2046,-5.3646,-5.0435
|
35 |
+
33,-4.8824,-5.0435,-4.7201
|
36 |
+
34,-4.5578,-4.7201,-4.3944
|
37 |
+
35,-4.231,-4.3944,-4.0665
|
38 |
+
36,-3.9019,-4.0665,-3.7365
|
39 |
+
37,-3.5711,-3.7365,-3.4049
|
40 |
+
38,-3.2387,-3.4049,-3.0716
|
41 |
+
39,-2.9044,-3.0716,-2.7365
|
42 |
+
40,-2.5686,-2.7365,-2.4
|
43 |
+
41,-2.2314,-2.4,-2.062
|
44 |
+
42,-1.8925,-2.062,-1.7222
|
45 |
+
43,-1.5519,-1.7222,-1.3809
|
46 |
+
44,-1.2098,-1.3809,-1.0381
|
47 |
+
45,-0.8664,-1.0381,-0.694
|
48 |
+
46,-0.5216,-0.694,-0.3487
|
49 |
+
47,-0.1757,-0.3487,-0.0026
|
50 |
+
48,0.1706,-0.0026,0.3438
|
51 |
+
49,0.5169,0.3438,0.6899
|
52 |
+
50,0.8628,0.6899,1.0352
|
53 |
+
51,1.2076,1.0352,1.3793
|
54 |
+
52,1.551,1.3793,1.7221
|
55 |
+
53,1.8931,1.7221,2.0634
|
56 |
+
54,2.2337,2.0634,2.4032
|
57 |
+
55,2.5726,2.4032,2.7412
|
58 |
+
56,2.9098,2.7412,3.0777
|
59 |
+
57,3.2456,3.0777,3.4128
|
60 |
+
58,3.5799,3.4128,3.7461
|
61 |
+
59,3.9123,3.7461,4.0777
|
62 |
+
60,4.2431,4.0777,4.4077
|
63 |
+
61,4.5722,4.4077,4.7356
|
64 |
+
62,4.899,4.7356,5.0613
|
65 |
+
63,5.2236,5.0613,5.3847
|
66 |
+
64,5.5458,5.3847,5.7058
|
67 |
+
65,5.8658,5.7058,6.0243
|
68 |
+
66,6.1828,6.0243,6.3395
|
69 |
+
67,6.4961,6.3395,6.6513
|
70 |
+
68,6.8064,6.6513,6.9602
|
71 |
+
69,7.1139,6.9602,7.2663
|
72 |
+
70,7.4186,7.2663,7.5696
|
73 |
+
71,7.7205,7.5696,7.8699
|
74 |
+
72,8.0193,7.8699,8.1672
|
75 |
+
73,8.315,8.1672,8.4608
|
76 |
+
74,8.6066,8.4608,8.7507
|
77 |
+
75,8.8947,8.7507,9.0372
|
78 |
+
76,9.1797,9.0372,9.3202
|
79 |
+
77,9.4607,9.3202,9.599
|
80 |
+
78,9.7373,9.599,9.8733
|
81 |
+
79,10.0092,9.8733,10.1428
|
82 |
+
80,10.2764,10.1428,10.4081
|
83 |
+
81,10.5397,10.4081,10.6694
|
84 |
+
82,10.7991,10.6694,10.9274
|
85 |
+
83,11.0556,10.9274,11.1822
|
86 |
+
84,11.3088,11.1822,11.4337
|
87 |
+
85,11.5585,11.4337,11.6817
|
88 |
+
86,11.8048,11.6817,11.9265
|
89 |
+
87,12.0481,11.9265,12.1685
|
90 |
+
88,12.2888,12.1685,12.4078
|
91 |
+
89,12.5267,12.4078,12.6438
|
92 |
+
90,12.7609,12.6438,12.876
|
93 |
+
91,12.991,12.876,13.104
|
94 |
+
92,13.217,13.104,13.3279
|
95 |
+
93,13.4387,13.3279,13.5487
|
96 |
+
94,13.6586,13.5487,13.7673
|
97 |
+
95,13.8759,13.7673,13.9831
|
lib/fish_eye/beam_widths/ARIS3000_128.csv
ADDED
@@ -0,0 +1,129 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
beam_num,beam_center,beam_left,beam_right
|
2 |
+
0,-15.0068,-15.1218,-14.8918
|
3 |
+
1,-14.7768,-14.8918,-14.6615
|
4 |
+
2,-14.5462,-14.6615,-14.4306
|
5 |
+
3,-14.315,-14.4306,-14.1992
|
6 |
+
4,-14.0833,-14.1992,-13.9672
|
7 |
+
5,-13.8511,-13.9672,-13.7349
|
8 |
+
6,-13.6186,-13.7349,-13.5022
|
9 |
+
7,-13.3858,-13.5022,-13.2693
|
10 |
+
8,-13.1528,-13.2693,-13.0362
|
11 |
+
9,-12.9196,-13.0362,-12.8029
|
12 |
+
10,-12.6861,-12.8029,-12.5692
|
13 |
+
11,-12.4523,-12.5692,-12.3353
|
14 |
+
12,-12.2182,-12.3353,-12.101
|
15 |
+
13,-11.9838,-12.101,-11.8665
|
16 |
+
14,-11.7491,-11.8665,-11.6316
|
17 |
+
15,-11.5141,-11.6316,-11.3965
|
18 |
+
16,-11.2789,-11.3965,-11.1612
|
19 |
+
17,-11.0435,-11.1612,-10.9257
|
20 |
+
18,-10.8079,-10.9257,-10.69
|
21 |
+
19,-10.5721,-10.69,-10.4541
|
22 |
+
20,-10.3361,-10.4541,-10.218
|
23 |
+
21,-10.0999,-10.218,-9.9817
|
24 |
+
22,-9.8635,-9.9817,-9.7452
|
25 |
+
23,-9.6269,-9.7452,-9.5086
|
26 |
+
24,-9.3902,-9.5086,-9.2718
|
27 |
+
25,-9.1534,-9.2718,-9.035
|
28 |
+
26,-8.9165,-9.035,-8.798
|
29 |
+
27,-8.6795,-8.798,-8.561
|
30 |
+
28,-8.4424,-8.561,-8.3239
|
31 |
+
29,-8.2053,-8.3239,-8.0868
|
32 |
+
30,-7.9682,-8.0868,-7.8496
|
33 |
+
31,-7.731,-7.8496,-7.6124
|
34 |
+
32,-7.4938,-7.6124,-7.3752
|
35 |
+
33,-7.2566,-7.3752,-7.138
|
36 |
+
34,-7.0193,-7.138,-6.9007
|
37 |
+
35,-6.782,-6.9007,-6.6633
|
38 |
+
36,-6.5446,-6.6633,-6.4259
|
39 |
+
37,-6.3072,-6.4259,-6.1885
|
40 |
+
38,-6.0698,-6.1885,-5.9511
|
41 |
+
39,-5.8324,-5.9511,-5.7137
|
42 |
+
40,-5.5949,-5.7137,-5.4762
|
43 |
+
41,-5.3574,-5.4762,-5.2387
|
44 |
+
42,-5.1199,-5.2387,-5.0011
|
45 |
+
43,-4.8823,-5.0011,-4.7635
|
46 |
+
44,-4.6447,-4.7635,-4.5259
|
47 |
+
45,-4.4071,-4.5259,-4.2883
|
48 |
+
46,-4.1695,-4.2883,-4.0507
|
49 |
+
47,-3.9318,-4.0507,-3.813
|
50 |
+
48,-3.6941,-3.813,-3.5753
|
51 |
+
49,-3.4564,-3.5753,-3.3376
|
52 |
+
50,-3.2187,-3.3376,-3.0998
|
53 |
+
51,-2.9809,-3.0998,-2.862
|
54 |
+
52,-2.743,-2.862,-2.624
|
55 |
+
53,-2.505,-2.624,-2.386
|
56 |
+
54,-2.2669,-2.386,-2.1478
|
57 |
+
55,-2.0287,-2.1478,-1.9096
|
58 |
+
56,-1.7904,-1.9096,-1.6712
|
59 |
+
57,-1.552,-1.6712,-1.4328
|
60 |
+
58,-1.3135,-1.4328,-1.1942
|
61 |
+
59,-1.0749,-1.1942,-0.9556
|
62 |
+
60,-0.8362,-0.9556,-0.7168
|
63 |
+
61,-0.5974,-0.7168,-0.478
|
64 |
+
62,-0.3585,-0.478,-0.2391
|
65 |
+
63,-0.1196,-0.2391,-0.0001
|
66 |
+
64,0.1194,-0.0001,0.2389
|
67 |
+
65,0.3584,0.2389,0.4779
|
68 |
+
66,0.5973,0.4779,0.7168
|
69 |
+
67,0.8362,0.7168,0.9556
|
70 |
+
68,1.075,0.9556,1.1944
|
71 |
+
69,1.3137,1.1944,1.433
|
72 |
+
70,1.5523,1.433,1.6716
|
73 |
+
71,1.7908,1.6716,1.91
|
74 |
+
72,2.0292,1.91,2.1484
|
75 |
+
73,2.2675,2.1484,2.3866
|
76 |
+
74,2.5057,2.3866,2.6248
|
77 |
+
75,2.7438,2.6248,2.8628
|
78 |
+
76,2.9818,2.8628,3.1008
|
79 |
+
77,3.2197,3.1008,3.3386
|
80 |
+
78,3.4575,3.3386,3.5764
|
81 |
+
79,3.6952,3.5764,3.8141
|
82 |
+
80,3.9329,3.8141,4.0518
|
83 |
+
81,4.1706,4.0518,4.2895
|
84 |
+
82,4.4083,4.2895,4.5271
|
85 |
+
83,4.6459,4.5271,4.7647
|
86 |
+
84,4.8835,4.7647,5.0023
|
87 |
+
85,5.1211,5.0023,5.2399
|
88 |
+
86,5.3587,5.2399,5.4775
|
89 |
+
87,5.5962,5.4775,5.715
|
90 |
+
88,5.8337,5.715,5.9525
|
91 |
+
89,6.0712,5.9525,6.1899
|
92 |
+
90,6.3086,6.1899,6.4273
|
93 |
+
91,6.546,6.4273,6.6647
|
94 |
+
92,6.7834,6.6647,6.9021
|
95 |
+
93,7.0208,6.9021,7.1395
|
96 |
+
94,7.2581,7.1395,7.3768
|
97 |
+
95,7.4954,7.3768,7.614
|
98 |
+
96,7.7326,7.614,7.8512
|
99 |
+
97,7.9698,7.8512,8.0884
|
100 |
+
98,8.207,8.0884,8.3256
|
101 |
+
99,8.4441,8.3256,8.5627
|
102 |
+
100,8.6812,8.5627,8.7998
|
103 |
+
101,8.9183,8.7998,9.0368
|
104 |
+
102,9.1553,9.0368,9.2738
|
105 |
+
103,9.3922,9.2738,9.5106
|
106 |
+
104,9.629,9.5106,9.7474
|
107 |
+
105,9.8657,9.7474,9.984
|
108 |
+
106,10.1023,9.984,10.2205
|
109 |
+
107,10.3387,10.2205,10.4568
|
110 |
+
108,10.5749,10.4568,10.6929
|
111 |
+
109,10.8109,10.6929,10.9288
|
112 |
+
110,11.0467,10.9288,11.1645
|
113 |
+
111,11.2823,11.1645,11.4
|
114 |
+
112,11.5177,11.4,11.6353
|
115 |
+
113,11.7529,11.6353,11.8704
|
116 |
+
114,11.9879,11.8704,12.1053
|
117 |
+
115,12.2226,12.1053,12.3398
|
118 |
+
116,12.457,12.3398,12.5741
|
119 |
+
117,12.6911,12.5741,12.808
|
120 |
+
118,12.9249,12.808,13.0417
|
121 |
+
119,13.1584,13.0417,13.275
|
122 |
+
120,13.3916,13.275,13.5081
|
123 |
+
121,13.6246,13.5081,13.741
|
124 |
+
122,13.8574,13.741,13.9737
|
125 |
+
123,14.0899,13.9737,14.206
|
126 |
+
124,14.3221,14.206,14.438
|
127 |
+
125,14.5538,14.438,14.6694
|
128 |
+
126,14.785,14.6694,14.9003
|
129 |
+
127,15.0156,14.9003,15.1306
|
lib/fish_eye/beam_widths/ARIS3000_64.csv
ADDED
@@ -0,0 +1,65 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
beam_num,beam_center,beam_left,beam_right
|
2 |
+
0,-14.8872,-15.1175,-14.6569
|
3 |
+
1,-14.4266,-14.6569,-14.1952
|
4 |
+
2,-13.9637,-14.1952,-13.7314
|
5 |
+
3,-13.499,-13.7314,-13.2661
|
6 |
+
4,-13.0332,-13.2661,-12.7999
|
7 |
+
5,-12.5665,-12.7999,-12.3326
|
8 |
+
6,-12.0986,-12.3326,-11.8641
|
9 |
+
7,-11.6295,-11.8641,-11.3944
|
10 |
+
8,-11.1593,-11.3944,-10.9238
|
11 |
+
9,-10.6883,-10.9238,-10.4524
|
12 |
+
10,-10.2165,-10.4524,-9.9802
|
13 |
+
11,-9.7439,-9.9802,-9.5073
|
14 |
+
12,-9.2706,-9.5073,-9.0338
|
15 |
+
13,-8.7969,-9.0338,-8.5599
|
16 |
+
14,-8.3228,-8.5599,-8.0857
|
17 |
+
15,-7.8486,-8.0857,-7.6114
|
18 |
+
16,-7.3742,-7.6114,-7.137
|
19 |
+
17,-6.8997,-7.137,-6.6624
|
20 |
+
18,-6.425,-6.6624,-6.1876
|
21 |
+
19,-5.9502,-6.1876,-5.7128
|
22 |
+
20,-5.4753,-5.7128,-5.2378
|
23 |
+
21,-5.0003,-5.2378,-4.7627
|
24 |
+
22,-4.5251,-4.7627,-4.2875
|
25 |
+
23,-4.0499,-4.2875,-3.8122
|
26 |
+
24,-3.5745,-3.8122,-3.3368
|
27 |
+
25,-3.0991,-3.3368,-2.8613
|
28 |
+
26,-2.6234,-2.8613,-2.3854
|
29 |
+
27,-2.1473,-2.3854,-1.9091
|
30 |
+
28,-1.6708,-1.9091,-1.4324
|
31 |
+
29,-1.1939,-1.4324,-0.9553
|
32 |
+
30,-0.7166,-0.9553,-0.4778
|
33 |
+
31,-0.2389,-0.4778,0.0
|
34 |
+
32,0.2389,0.0,0.4778
|
35 |
+
33,0.7166,0.4778,0.9553
|
36 |
+
34,1.1939,0.9553,1.4324
|
37 |
+
35,1.6708,1.4324,1.9091
|
38 |
+
36,2.1473,1.9091,2.3854
|
39 |
+
37,2.6234,2.3854,2.8613
|
40 |
+
38,3.0991,2.8613,3.3368
|
41 |
+
39,3.5745,3.3368,3.8122
|
42 |
+
40,4.0499,3.8122,4.2875
|
43 |
+
41,4.5251,4.2875,4.7627
|
44 |
+
42,5.0003,4.7627,5.2378
|
45 |
+
43,5.4753,5.2378,5.7128
|
46 |
+
44,5.9502,5.7128,6.1876
|
47 |
+
45,6.425,6.1876,6.6624
|
48 |
+
46,6.8997,6.6624,7.137
|
49 |
+
47,7.3742,7.137,7.6114
|
50 |
+
48,7.8486,7.6114,8.0857
|
51 |
+
49,8.3228,8.0857,8.5599
|
52 |
+
50,8.7969,8.5599,9.0338
|
53 |
+
51,9.2706,9.0338,9.5073
|
54 |
+
52,9.7439,9.5073,9.9802
|
55 |
+
53,10.2165,9.9802,10.4524
|
56 |
+
54,10.6883,10.4524,10.9238
|
57 |
+
55,11.1593,10.9238,11.3944
|
58 |
+
56,11.6295,11.3944,11.8641
|
59 |
+
57,12.0986,11.8641,12.3326
|
60 |
+
58,12.5665,12.3326,12.7999
|
61 |
+
59,13.0332,12.7999,13.2661
|
62 |
+
60,13.499,13.2661,13.7314
|
63 |
+
61,13.9637,13.7314,14.1952
|
64 |
+
62,14.4266,14.1952,14.6569
|
65 |
+
63,14.8872,14.6569,15.1175
|
lib/fish_eye/beam_widths/ARIS_Telephoto_48.csv
ADDED
@@ -0,0 +1,49 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
beam_num,beam_center,beam_left,beam_right
|
2 |
+
0,-7.339,-7.487,-7.345
|
3 |
+
1,-7.043,-7.193,-7.048
|
4 |
+
2,-6.744,-6.895,-6.749
|
5 |
+
3,-6.443,-6.595,-6.447
|
6 |
+
4,-6.14,-6.293,-6.144
|
7 |
+
5,-5.835,-5.988,-5.839
|
8 |
+
6,-5.529,-5.683,-5.533
|
9 |
+
7,-5.222,-5.377,-5.225
|
10 |
+
8,-4.913,-5.069,-4.915
|
11 |
+
9,-4.602,-4.758,-4.604
|
12 |
+
10,-4.29,-4.448,-4.291
|
13 |
+
11,-3.975,-4.133,-3.976
|
14 |
+
12,-3.659,-3.818,-3.66
|
15 |
+
13,-3.342,-3.501,-3.343
|
16 |
+
14,-3.025,-3.184,-3.026
|
17 |
+
15,-2.708,-2.867,-2.709
|
18 |
+
16,-2.391,-2.55,-2.392
|
19 |
+
17,-2.074,-2.233,-2.075
|
20 |
+
18,-1.756,-1.915,-1.757
|
21 |
+
19,-1.438,-1.598,-1.438
|
22 |
+
20,-1.119,-1.279,-1.119
|
23 |
+
21,-0.8,-0.96,-0.8
|
24 |
+
22,-0.48,-0.64,-0.48
|
25 |
+
23,-0.16,-0.32,-0.16
|
26 |
+
24,0.16,0.16,0.32
|
27 |
+
25,0.48,0.48,0.64
|
28 |
+
26,0.8,0.8,0.96
|
29 |
+
27,1.119,1.119,1.279
|
30 |
+
28,1.438,1.438,1.598
|
31 |
+
29,1.756,1.757,1.915
|
32 |
+
30,2.074,2.075,2.233
|
33 |
+
31,2.391,2.392,2.55
|
34 |
+
32,2.708,2.709,2.867
|
35 |
+
33,3.025,3.026,3.184
|
36 |
+
34,3.342,3.343,3.501
|
37 |
+
35,3.659,3.66,3.818
|
38 |
+
36,3.975,3.976,4.133
|
39 |
+
37,4.29,4.291,4.448
|
40 |
+
38,4.602,4.604,4.758
|
41 |
+
39,4.913,4.915,5.069
|
42 |
+
40,5.222,5.225,5.377
|
43 |
+
41,5.529,5.533,5.683
|
44 |
+
42,5.835,5.839,5.988
|
45 |
+
43,6.14,6.144,6.293
|
46 |
+
44,6.443,6.447,6.595
|
47 |
+
45,6.744,6.749,6.895
|
48 |
+
46,7.043,7.048,7.193
|
49 |
+
47,7.339,7.345,7.487
|
lib/fish_eye/beam_widths/ARIS_Telephoto_96.csv
ADDED
@@ -0,0 +1,97 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
beam_num,beam_center,beam_left,beam_right
|
2 |
+
0,-7.419,-7.493,-7.345
|
3 |
+
1,-7.271,-7.345,-7.197
|
4 |
+
2,-7.123,-7.197,-7.048
|
5 |
+
3,-6.974,-7.048,-6.899
|
6 |
+
4,-6.824,-6.899,-6.749
|
7 |
+
5,-6.674,-6.749,-6.598
|
8 |
+
6,-6.523,-6.598,-6.447
|
9 |
+
7,-6.372,-6.447,-6.296
|
10 |
+
8,-6.22,-6.296,-6.144
|
11 |
+
9,-6.068,-6.144,-5.992
|
12 |
+
10,-5.915,-5.992,-5.839
|
13 |
+
11,-5.763,-5.839,-5.686
|
14 |
+
12,-5.609,-5.686,-5.533
|
15 |
+
13,-5.456,-5.533,-5.379
|
16 |
+
14,-5.302,-5.379,-5.225
|
17 |
+
15,-5.148,-5.225,-5.07
|
18 |
+
16,-4.993,-5.07,-4.915
|
19 |
+
17,-4.838,-4.915,-4.76
|
20 |
+
18,-4.682,-4.76,-4.604
|
21 |
+
19,-4.526,-4.604,-4.448
|
22 |
+
20,-4.37,-4.448,-4.291
|
23 |
+
21,-4.213,-4.291,-4.134
|
24 |
+
22,-4.055,-4.134,-3.976
|
25 |
+
23,-3.897,-3.976,-3.818
|
26 |
+
24,-3.739,-3.818,-3.66
|
27 |
+
25,-3.581,-3.66,-3.501
|
28 |
+
26,-3.422,-3.501,-3.343
|
29 |
+
27,-3.264,-3.343,-3.185
|
30 |
+
28,-3.105,-3.185,-3.026
|
31 |
+
29,-2.947,-3.026,-2.868
|
32 |
+
30,-2.788,-2.868,-2.709
|
33 |
+
31,-2.63,-2.709,-2.55
|
34 |
+
32,-2.471,-2.55,-2.392
|
35 |
+
33,-2.313,-2.392,-2.233
|
36 |
+
34,-2.154,-2.233,-2.075
|
37 |
+
35,-1.995,-2.075,-1.916
|
38 |
+
36,-1.836,-1.916,-1.757
|
39 |
+
37,-1.677,-1.757,-1.598
|
40 |
+
38,-1.518,-1.598,-1.438
|
41 |
+
39,-1.359,-1.438,-1.279
|
42 |
+
40,-1.199,-1.279,-1.119
|
43 |
+
41,-1.039,-1.119,-0.96
|
44 |
+
42,-0.88,-0.96,-0.8
|
45 |
+
43,-0.72,-0.8,-0.64
|
46 |
+
44,-0.56,-0.64,-0.48
|
47 |
+
45,-0.4,-0.48,-0.32
|
48 |
+
46,-0.24,-0.32,-0.16
|
49 |
+
47,-0.08,-0.16,0.0
|
50 |
+
48,0.08,0.0,0.16
|
51 |
+
49,0.24,0.16,0.32
|
52 |
+
50,0.4,0.32,0.48
|
53 |
+
51,0.56,0.48,0.64
|
54 |
+
52,0.72,0.64,0.8
|
55 |
+
53,0.88,0.8,0.96
|
56 |
+
54,1.039,0.96,1.119
|
57 |
+
55,1.199,1.119,1.279
|
58 |
+
56,1.359,1.279,1.438
|
59 |
+
57,1.518,1.438,1.598
|
60 |
+
58,1.677,1.598,1.757
|
61 |
+
59,1.836,1.757,1.916
|
62 |
+
60,1.995,1.916,2.075
|
63 |
+
61,2.154,2.075,2.233
|
64 |
+
62,2.313,2.233,2.392
|
65 |
+
63,2.471,2.392,2.55
|
66 |
+
64,2.63,2.55,2.709
|
67 |
+
65,2.788,2.709,2.868
|
68 |
+
66,2.947,2.868,3.026
|
69 |
+
67,3.105,3.026,3.185
|
70 |
+
68,3.264,3.185,3.343
|
71 |
+
69,3.422,3.343,3.501
|
72 |
+
70,3.581,3.501,3.66
|
73 |
+
71,3.739,3.66,3.818
|
74 |
+
72,3.897,3.818,3.976
|
75 |
+
73,4.055,3.976,4.134
|
76 |
+
74,4.213,4.134,4.291
|
77 |
+
75,4.37,4.291,4.448
|
78 |
+
76,4.526,4.448,4.605
|
79 |
+
77,4.682,4.605,4.76
|
80 |
+
78,4.838,4.76,4.916
|
81 |
+
79,4.993,4.916,5.07
|
82 |
+
80,5.148,5.07,5.225
|
83 |
+
81,5.302,5.225,5.379
|
84 |
+
82,5.456,5.379,5.533
|
85 |
+
83,5.609,5.533,5.686
|
86 |
+
84,5.763,5.686,5.839
|
87 |
+
85,5.915,5.839,5.992
|
88 |
+
86,6.068,5.992,6.144
|
89 |
+
87,6.22,6.144,6.296
|
90 |
+
88,6.372,6.296,6.448
|
91 |
+
89,6.523,6.448,6.599
|
92 |
+
90,6.674,6.599,6.749
|
93 |
+
91,6.824,6.749,6.899
|
94 |
+
92,6.974,6.899,7.049
|
95 |
+
93,7.123,7.049,7.199
|
96 |
+
94,7.271,7.199,7.345
|
97 |
+
95,7.419,7.345,7.493
|
lib/fish_eye/beams.py
ADDED
@@ -0,0 +1,154 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
"""
|
2 |
+
Utility functions for converting the beam width header files into
|
3 |
+
python arrays.
|
4 |
+
|
5 |
+
See : https://github.com/SoundMetrics/aris-file-sdk/tree/master/beam-width-metrics
|
6 |
+
|
7 |
+
"""
|
8 |
+
|
9 |
+
import glob
|
10 |
+
import os
|
11 |
+
import re
|
12 |
+
|
13 |
+
import pandas as pd
|
14 |
+
|
15 |
+
def parse_beam_header_file(fp):
|
16 |
+
""" Utility to parse the beam width header files.
|
17 |
+
"""
|
18 |
+
|
19 |
+
# example of what we are trying to parse:
|
20 |
+
# DEFINE_BEAMWIDTH3(0, -13.5735, -13.7893, -13.3577)
|
21 |
+
|
22 |
+
pattern = re.compile(r"^DEFINE_BEAMWIDTH3\D*(\d+), ([-+]?\d*\.\d+), ([-+]?\d*\.\d+), ([-+]?\d*\.\d+)")
|
23 |
+
|
24 |
+
beam_angles = []
|
25 |
+
|
26 |
+
with open(fp) as f:
|
27 |
+
for line in f:
|
28 |
+
m = pattern.match(line)
|
29 |
+
if m:
|
30 |
+
|
31 |
+
beam_num = int(m.group(1))
|
32 |
+
beam_center = float(m.group(2))
|
33 |
+
beam_left = float(m.group(3))
|
34 |
+
beam_right = float(m.group(4))
|
35 |
+
|
36 |
+
beam_angles.append([beam_num, beam_center, beam_left, beam_right])
|
37 |
+
|
38 |
+
# Simple sanity check
|
39 |
+
for i in range(len(beam_angles)):
|
40 |
+
assert beam_angles[i][0] == i
|
41 |
+
|
42 |
+
beam_angles = pd.DataFrame(beam_angles, columns=['beam_num', 'beam_center', 'beam_left', 'beam_right'])
|
43 |
+
|
44 |
+
return beam_angles
|
45 |
+
|
46 |
+
|
47 |
+
def convert_beam_header_files(beam_dir):
|
48 |
+
""" Convert the aris-file-sdk/beam-width-metrics directory to pandas data frames.
|
49 |
+
"""
|
50 |
+
|
51 |
+
system_beam_angles = {}
|
52 |
+
|
53 |
+
for fp in glob.glob(os.path.join(beam_dir, "BeamWidths*")):
|
54 |
+
|
55 |
+
beam_angles = parse_beam_header_file(fp)
|
56 |
+
name = os.path.splitext(os.path.basename(fp))[0]
|
57 |
+
system_type = name.split("BeamWidths_")[1]
|
58 |
+
|
59 |
+
system_beam_angles[system_type] = beam_angles
|
60 |
+
|
61 |
+
return system_beam_angles
|
62 |
+
|
63 |
+
|
64 |
+
def make_csv_files_for_beam_widths(beam_dir, output_dir):
|
65 |
+
""" Convert the aris-file-sdk/beam-width-metrics directory to pandas data frames
|
66 |
+
and save them as csv files.
|
67 |
+
"""
|
68 |
+
|
69 |
+
system_beam_angles = convert_beam_header_files(beam_dir)
|
70 |
+
|
71 |
+
for system_type, beam_angles in system_beam_angles.items():
|
72 |
+
|
73 |
+
beam_angles.to_csv(os.path.join(output_dir, system_type + ".csv"), index=False)
|
74 |
+
|
75 |
+
|
76 |
+
|
77 |
+
def load_beam_width_data(frame, beam_width_dir):
|
78 |
+
""" Load in the beam spacing file that corresponds to the correct ARIS setup for this frame.
|
79 |
+
"""
|
80 |
+
|
81 |
+
system_type = frame.thesystemtype
|
82 |
+
beam_count = frame.BeamCount
|
83 |
+
used_telephoto = frame.largelens
|
84 |
+
|
85 |
+
beam_width_fn = None
|
86 |
+
|
87 |
+
# ARIS 1800
|
88 |
+
if system_type == 0:
|
89 |
+
|
90 |
+
if beam_count == 48:
|
91 |
+
|
92 |
+
if used_telephoto:
|
93 |
+
# ARIS_Telephoto_48
|
94 |
+
beam_width_fn = 'ARIS_Telephoto_48.csv'
|
95 |
+
else:
|
96 |
+
# ARIS1800_1200_48
|
97 |
+
beam_width_fn = 'ARIS1800_1200_48.csv'
|
98 |
+
|
99 |
+
elif beam_count == 96:
|
100 |
+
|
101 |
+
if used_telephoto:
|
102 |
+
# ARIS_Telephoto_96
|
103 |
+
beam_width_fn = 'ARIS_Telephoto_96.csv'
|
104 |
+
else:
|
105 |
+
# ARIS1800_96
|
106 |
+
beam_width_fn = 'ARIS1800_96.csv'
|
107 |
+
|
108 |
+
else:
|
109 |
+
raise ValueEror("Invalid Beam Count %d for ARIS 1800" % (beam_count,))
|
110 |
+
|
111 |
+
# ARIS 3000
|
112 |
+
elif system_type == 1:
|
113 |
+
|
114 |
+
if used_telephoto:
|
115 |
+
raise ValueEror("Don't know telephoto beam widths for ARIS 3000")
|
116 |
+
|
117 |
+
if beam_count == 64:
|
118 |
+
# ARIS3000_64
|
119 |
+
beam_width_fn = 'ARIS3000_64.csv'
|
120 |
+
|
121 |
+
elif beam_count == 128:
|
122 |
+
# ARIS3000_128
|
123 |
+
beam_width_fn = 'ARIS3000_128.csv'
|
124 |
+
|
125 |
+
else:
|
126 |
+
raise ValueEror("Invalid Beam Count %d for ARIS 3000" % (beam_count,))
|
127 |
+
|
128 |
+
# ARIS 1200
|
129 |
+
elif system_type == 2:
|
130 |
+
|
131 |
+
if beam_count != 48:
|
132 |
+
raise ValueEror("Invalid Beam Count %d for ARIS 1200" % (beam_count,))
|
133 |
+
|
134 |
+
if used_telephoto:
|
135 |
+
# ARIS_Telephoto_48
|
136 |
+
beam_width_fn = 'ARIS_Telephoto_48.csv'
|
137 |
+
else:
|
138 |
+
# ARIS1800_1200_48
|
139 |
+
beam_width_fn = 'ARIS1800_1200_48.csv'
|
140 |
+
|
141 |
+
else:
|
142 |
+
raise ValueError("Unknown System Type: %s" % (system_type,))
|
143 |
+
|
144 |
+
|
145 |
+
beam_width_fp = os.path.join(beam_width_dir, beam_width_fn)
|
146 |
+
|
147 |
+
# return pd.read_csv(beam_width_fp)
|
148 |
+
return (pd.read_csv(beam_width_fp), beam_width_fn.replace('.csv', ''))
|
149 |
+
|
150 |
+
|
151 |
+
|
152 |
+
|
153 |
+
|
154 |
+
# /Users/GVH/Code/soundmetrics/aris-file-sdk/beam-width-metrics
|
lib/fish_eye/convert_annotations.py
ADDED
@@ -0,0 +1,143 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
from absl import app
|
2 |
+
from absl import flags
|
3 |
+
from colorsys import hls_to_rgb
|
4 |
+
import json
|
5 |
+
import numpy as np
|
6 |
+
import os
|
7 |
+
import xml.etree.ElementTree as ET
|
8 |
+
|
9 |
+
from fish_length import Fish_Length
|
10 |
+
from tracker import Tracker
|
11 |
+
|
12 |
+
flags.DEFINE_string(
|
13 |
+
'json_dump_path', None, 'Path to json containing annotated clip info.'
|
14 |
+
)
|
15 |
+
flags.DEFINE_string(
|
16 |
+
'xml_dir', None, 'Directory containing xml annotation files.'
|
17 |
+
)
|
18 |
+
flags.DEFINE_string(
|
19 |
+
'output_path', None, 'Directory to output clip annotation jsons.'
|
20 |
+
)
|
21 |
+
flags.mark_flag_as_required('json_dump_path')
|
22 |
+
flags.mark_flag_as_required('xml_dir')
|
23 |
+
flags.mark_flag_as_required('output_path')
|
24 |
+
FLAGS = flags.FLAGS
|
25 |
+
|
26 |
+
def get_annotation_from_clip(clip):
|
27 |
+
root = ET.parse(os.path.join(FLAGS.xml_dir, clip['clip_name']+'.xml')).getroot()
|
28 |
+
|
29 |
+
data = {}
|
30 |
+
data['clip_id'] = clip['clip_id']
|
31 |
+
data['aris_filename'] = clip['aris_filename']
|
32 |
+
data['start_frame'] = clip['start_frame']
|
33 |
+
data['end_frame'] = clip['end_frame']
|
34 |
+
data['upstream_direction'] = clip['upstream_direction']
|
35 |
+
data['image_meter_width'] = clip['aris_info']['pixel_meter_size']*clip['aris_info']['xdim']
|
36 |
+
|
37 |
+
# Create image entries
|
38 |
+
frames = []
|
39 |
+
for i in range(int(root.findall('object')[0].findtext('startFrame')), 1 + int(root.findall('object')[0].findtext('endFrame'))):
|
40 |
+
frame = {
|
41 |
+
'frame_num': clip['start_frame'] + i,
|
42 |
+
'fish': []
|
43 |
+
}
|
44 |
+
frames.append(frame)
|
45 |
+
|
46 |
+
fishes = []
|
47 |
+
# Populate images with bboxes
|
48 |
+
for track_id, object in enumerate(root.findall('object')):
|
49 |
+
fish = {}
|
50 |
+
fish['id'] = track_id
|
51 |
+
fish['length'] = -1
|
52 |
+
fish['direction'] = 'N/A'
|
53 |
+
fish['start_frame_index'] = -1
|
54 |
+
fish['end_frame_index'] = -1
|
55 |
+
fish['color'] = Tracker.selectColor(track_id)
|
56 |
+
|
57 |
+
fishes.append(fish)
|
58 |
+
|
59 |
+
last_drawn = None
|
60 |
+
stat_interp = []
|
61 |
+
lengths = []
|
62 |
+
for polygon in object.findall('polygon'):
|
63 |
+
if int(polygon.findtext('pt/l')) == -1:
|
64 |
+
continue
|
65 |
+
|
66 |
+
index = int(polygon.find('t').text)
|
67 |
+
|
68 |
+
frame = frames[index]
|
69 |
+
|
70 |
+
frame_entry = {}
|
71 |
+
frame_entry['fish_id'] = track_id
|
72 |
+
frame_entry['bbox'] = None
|
73 |
+
frame_entry['visible'] = 1
|
74 |
+
frame_entry['human_labeled'] = int(polygon.findtext('pt/l'))
|
75 |
+
|
76 |
+
frame['fish'].append(frame_entry)
|
77 |
+
|
78 |
+
# Determine if polygon is stationary
|
79 |
+
if polygon.findtext('s') is not None and int(polygon.findtext('s')):
|
80 |
+
stat_interp.append(frame_entry)
|
81 |
+
else:
|
82 |
+
frame_entry['bbox'] = list(np.array([int(polygon.findtext('pt/x'))/clip['aris_info']['xdim'], int(polygon.findtext('pt/y'))/clip['aris_info']['ydim'],
|
83 |
+
int(polygon.findall('pt/x')[2].text)/clip['aris_info']['xdim'], int(polygon.findall('pt/y')[1].text)/clip['aris_info']['ydim']]))
|
84 |
+
|
85 |
+
# Coordinates of greater than 1.1 will cause training to fail
|
86 |
+
if (np.array(frame_entry['bbox']) > 1.1).any():
|
87 |
+
print('Error: Invalid bbox.')
|
88 |
+
frame['fish'].pop()
|
89 |
+
continue
|
90 |
+
|
91 |
+
lengths.append(int(polygon.findall('pt/x')[2].text)-int(polygon.findtext('pt/x')))
|
92 |
+
|
93 |
+
# Interpolate if there are stationary boxes
|
94 |
+
if stat_interp:
|
95 |
+
bbox_interp = last_drawn + np.dot(1 + np.array(range(len(stat_interp)))[:,np.newaxis], (np.array(frame_entry['bbox']) - np.array(last_drawn))[np.newaxis])/(len(stat_interp) + 1)
|
96 |
+
for frame_entry, bbox in zip(stat_interp, bbox_interp):
|
97 |
+
frame_entry['bbox'] = list(bbox)
|
98 |
+
stat_interp = []
|
99 |
+
last_drawn = frame_entry['bbox']
|
100 |
+
|
101 |
+
if fish['start_frame_index'] == -1:
|
102 |
+
fish['start_frame_index'] = index
|
103 |
+
fish['end_frame_index'] = index
|
104 |
+
|
105 |
+
if stat_interp:
|
106 |
+
for frame_entry in stat_interp:
|
107 |
+
frame_entry['bbox'] = last_drawn
|
108 |
+
|
109 |
+
data['frames'] = frames
|
110 |
+
data['fish'] = fishes
|
111 |
+
|
112 |
+
# Add track
|
113 |
+
for fish in fishes:
|
114 |
+
for frame_entry in frames[fish['start_frame_index']]['fish']:
|
115 |
+
if frame_entry['fish_id'] == fish['id']:
|
116 |
+
start_bbox = frame_entry['bbox']
|
117 |
+
break
|
118 |
+
else:
|
119 |
+
raise RuntimeWarning(f'Start box of fish {fish["id"]} in {clip["clip_name"]} is not defined.')
|
120 |
+
|
121 |
+
for frame_entry in frames[fish['end_frame_index']]['fish']:
|
122 |
+
if frame_entry['fish_id'] == fish['id']:
|
123 |
+
end_bbox = frame_entry['bbox']
|
124 |
+
break
|
125 |
+
else:
|
126 |
+
raise RuntimeWarning(f'End box of fish {fish["id"]} in {clip["clip_name"]} is not defined.')
|
127 |
+
|
128 |
+
fish['direction'] = Tracker.get_direction(start_bbox, end_bbox)
|
129 |
+
|
130 |
+
return Fish_Length.add_lengths(data)
|
131 |
+
|
132 |
+
def main(argv):
|
133 |
+
with open(FLAGS.json_dump_path) as json_file:
|
134 |
+
json_dump = json.load(json_file)
|
135 |
+
|
136 |
+
for clip in json_dump:
|
137 |
+
data = get_annotation_from_clip(clip)
|
138 |
+
|
139 |
+
with open(os.path.join(FLAGS.output_path, f'{clip["clip_name"]}.json'), 'w') as output_file:
|
140 |
+
json.dump(data, output_file, indent=2)
|
141 |
+
|
142 |
+
if __name__ == '__main__':
|
143 |
+
app.run(main)
|
lib/fish_eye/data_format.md
ADDED
@@ -0,0 +1,72 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
|
2 |
+
We are typically given an ARIS file and a "count file" that contains the human counts of fish at some temporal resolution. The "count file" might be a csv file, a text file, or something else. We'll create a json file that stores this information in a more convenient/universal format:
|
3 |
+
|
4 |
+
```
|
5 |
+
{ // Information for a single clip
|
6 |
+
"clip_id" : // Unique id for this clip (perhaps a UUID?)
|
7 |
+
"aris_filename" : // Path to the ARIS file
|
8 |
+
"clip_name" : // Basename of annotation files
|
9 |
+
"start_frame" : // Start frame for this "count event", used to index into the ARIS file
|
10 |
+
"end_frame" : // End frame for this "count event", used to index into the ARIS file
|
11 |
+
"start_time" : // Start time in for this "count event" (this should be the sonartimestamp of the start_frame )
|
12 |
+
"end_time " : // End time for this "count event" (this should be the sonartimestamp of the end_frame )
|
13 |
+
"upstream_direction" : // Either `left` or `right`
|
14 |
+
"fish": [ // Should have one entry for each fish
|
15 |
+
{
|
16 |
+
"frame" : , // Manual marking frame number
|
17 |
+
"direction" : , // Either `left`, `right`, or `undefined`
|
18 |
+
"length" : , // Length in meters
|
19 |
+
"x" : , // x position of marking
|
20 |
+
"y" : , // y position of marking
|
21 |
+
}
|
22 |
+
],
|
23 |
+
"aris_info" : { // Needed for generating a warped image from the raw ARIS samples
|
24 |
+
"camera_type" : , // ARIS camera type
|
25 |
+
"framerate" : , // Frames per second
|
26 |
+
"pixel_meter_size" : , // The size of a pixel in meters
|
27 |
+
"xdim" : , // The width of the warped image
|
28 |
+
"ydim" : , // The height of the warped image
|
29 |
+
"x_meter_start" : , // x start in meters
|
30 |
+
"y_meter_start" : , // y start in meters
|
31 |
+
"x_meter_stop" : , // x stop in meters
|
32 |
+
"y_meter_stop' : , // y stop in meters
|
33 |
+
}
|
34 |
+
}
|
35 |
+
```
|
36 |
+
|
37 |
+
|
38 |
+
When a sequence of frames are annotated from an ARIS file (i.e. a "clip"), we will produce the following json file:
|
39 |
+
|
40 |
+
```
|
41 |
+
{ // Annotation information for a single "clip"
|
42 |
+
"clip_id" : // a unique clip id, should match the `clip_id` in the clip info dictionary above.
|
43 |
+
"aris_filename" : // the name of the associated aris file
|
44 |
+
"start_frame" : // Start frame for this "clip", used to index into the ARIS file
|
45 |
+
"end_frame" : // End frame for this "clip", used to index into the ARIS file
|
46 |
+
"upstream_direction" : // Either `left` or `right`
|
47 |
+
"clip_meter_width" : // Width of a frame in meters
|
48 |
+
"clip_meter_height" : // Height of a frame in meters
|
49 |
+
"frames" : [ // Should have one entry for each frame
|
50 |
+
{
|
51 |
+
"frame_num" : , // the frame number from the ARIS file
|
52 |
+
"fish" : [ // should have one entry for each fish that is present in this frame
|
53 |
+
{
|
54 |
+
"fish_id" : // fish track id
|
55 |
+
"box" : [xmin, ymin, xmax, ymax] // in normalized coordinates (multiply by `xdim` and `ydim` to get unnormalized coordinates)
|
56 |
+
"visible" : // 0 means not visible (is this necessary?), 1 means visible
|
57 |
+
"human_labeled" : // 1 or 0 for whether a human did the annotation or if it was interpolated
|
58 |
+
}
|
59 |
+
]
|
60 |
+
}
|
61 |
+
],
|
62 |
+
"fish" : [ // Should have one entry for each fish
|
63 |
+
{
|
64 |
+
"id" : // fish track id (should be unique) (0 based, not unique across clips)
|
65 |
+
"length" : // computed fish length in meters
|
66 |
+
"direction" : // computed swimming direction {left, right, none}
|
67 |
+
"start_frame_index" : // first frame this fish appears in
|
68 |
+
"end_frame_index" : // last frame this fish appears in
|
69 |
+
"color" : // a unique hex color value for this fish
|
70 |
+
}
|
71 |
+
]
|
72 |
+
}
|
lib/fish_eye/fish_length.py
ADDED
@@ -0,0 +1,38 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
from collections import defaultdict
|
2 |
+
from copy import deepcopy
|
3 |
+
import json
|
4 |
+
import numpy as np
|
5 |
+
|
6 |
+
class Fish_Length:
|
7 |
+
@staticmethod
|
8 |
+
def mean_length(tracks, constant, aux=-1):
|
9 |
+
return [np.mean(track[2] - track[0])*constant for track in tracks]
|
10 |
+
|
11 |
+
@staticmethod
|
12 |
+
def quantile_length(tracks, constant, aux=-1):
|
13 |
+
return [np.quantile(track[2] - track[0], aux)*constant for track in tracks]
|
14 |
+
|
15 |
+
@staticmethod
|
16 |
+
def quantile_diagonal(tracks, constant, aux=-1):
|
17 |
+
return [np.quantile(np.sqrt((track[2] - track[0])**2 + (track[3] - track[1])**2), aux)*constant for track in tracks]
|
18 |
+
|
19 |
+
@staticmethod
|
20 |
+
def add_lengths(json_data, length_fn=quantile_length.__func__, constant=0.8348286633599985, aux=0.8773333335319834, output_path=None):
|
21 |
+
json_data = deepcopy(json_data)
|
22 |
+
|
23 |
+
tracks = defaultdict(list)
|
24 |
+
for frame in json_data['frames']:
|
25 |
+
for frame_entry in frame['fish']:
|
26 |
+
tracks[frame_entry['fish_id']].append(np.array(frame_entry['bbox']))
|
27 |
+
tracks = [np.array(track).T for _, track in sorted(tracks.items())]
|
28 |
+
|
29 |
+
lengths = np.array(length_fn(tracks, constant*json_data['image_meter_width'], aux=aux))
|
30 |
+
|
31 |
+
for fish, fish_length in zip(sorted(json_data['fish'], key=lambda k: k['id']), lengths):
|
32 |
+
fish['length'] = fish_length
|
33 |
+
|
34 |
+
if output_path is not None:
|
35 |
+
with open(output_path,'w') as output:
|
36 |
+
json.dump(json_data, output, indent=2)
|
37 |
+
|
38 |
+
return json_data
|
lib/fish_eye/pyARIS.py
ADDED
@@ -0,0 +1,1009 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# -*- coding: utf-8 -*-
|
2 |
+
"""
|
3 |
+
===================================================
|
4 |
+
A python interface for ARIS files
|
5 |
+
===================================================
|
6 |
+
|
7 |
+
Last modified on: January 31, 2017
|
8 |
+
The most recent version can be found at: https://github.com/EminentCodfish/pyARIS
|
9 |
+
|
10 |
+
@author: Chris Rillahan
|
11 |
+
"""
|
12 |
+
|
13 |
+
import struct, array, pytz, datetime, tqdm
|
14 |
+
import os
|
15 |
+
import subprocess as sp
|
16 |
+
from matplotlib import cm as colormap
|
17 |
+
from PIL import Image, ImageFont, ImageDraw
|
18 |
+
import numpy as np
|
19 |
+
from beams import load_beam_width_data
|
20 |
+
|
21 |
+
|
22 |
+
class ARIS_File:
|
23 |
+
'This is a class container for the ARIS file headers'
|
24 |
+
|
25 |
+
def __init__(self, filename, version_number, FrameCount, FrameRate, HighResolution, NumRawBeams, SampleRate, SamplesPerChannel, ReceiverGain,
|
26 |
+
WindowStart, WindowLength, Reverse, SN, strDate, strHeaderID, UserID1, UserID2, UserID3, UserID4, StartFrame,EndFrame,
|
27 |
+
TimeLapse, RecordInterval, RadioSeconds, FrameInterval, Flags, AuxFlags, Sspd, Flags3D, SoftwareVersion, WaterTemp,
|
28 |
+
Salinity, PulseLength, TxMode, VersionFGPA, VersionPSuC, ThumbnailFI, FileSize, OptionalHeaderSize, OptionalTailSize,
|
29 |
+
VersionMinor, LargeLens):
|
30 |
+
self.filename = filename #Name of the ARIS file
|
31 |
+
self.version_number = version_number #File format version DDF_05 = 0x05464444
|
32 |
+
#OBSOLETE: Calculate the number of frames from file size & beams*samples.
|
33 |
+
self.FrameCount = FrameCount #Total frames in file
|
34 |
+
#OBSOLETE: See frame header instead.
|
35 |
+
self.FrameRate = FrameRate #Initial recorded frame rate
|
36 |
+
#OBSOLETE: See frame header instead.
|
37 |
+
self.HighResolution = HighResolution #Non-zero if HF, zero if LF
|
38 |
+
#OBSOLETE: See frame header instead.
|
39 |
+
self.NumRawBeams = NumRawBeams #ARIS 3000 = 128/64, ARIS 1800 = 96/48, ARIS 1200 = 48
|
40 |
+
#OBSOLETE: See frame header instead.
|
41 |
+
self.SampleRate = SampleRate #1/Sample Period
|
42 |
+
#OBSOLETE: See frame header instead.
|
43 |
+
self.SamplesPerChannel = SamplesPerChannel #Number of range samples in each beam
|
44 |
+
#OBSOLETE: See frame header instead.
|
45 |
+
self.ReceiverGain = ReceiverGain #Relative gain in dB: 0 - 40
|
46 |
+
#OBSOLETE: See frame header instead.
|
47 |
+
self.WindowStart = WindowStart #Image window start range in meters (code [0..31] in DIDSON)
|
48 |
+
#OBSOLETE: See frame header instead.
|
49 |
+
self.WindowLength = WindowLength #Image window length in meters (code [0..3] in DIDSON)
|
50 |
+
#OBSOLETE: See frame header instead.
|
51 |
+
self.Reverse = Reverse #Non-zero = lens down (DIDSON) or lens up (ARIS), zero = opposite
|
52 |
+
self.SN = SN #Sonar serial number
|
53 |
+
self.strDate = strDate #Date that file was recorded
|
54 |
+
self.strHeaderID = strHeaderID #User input to identify file in 256 characters
|
55 |
+
self.UserID1 = UserID1 #User-defined integer quantity
|
56 |
+
self.UserID2 = UserID2 #User-defined integer quantity
|
57 |
+
self.UserID3 = UserID3 #User-defined integer quantity
|
58 |
+
self.UserID4 = UserID4 #User-defined integer quantity
|
59 |
+
self.StartFrame = StartFrame #First frame number from source file (for DIDSON snippet files)
|
60 |
+
self.EndFrame = EndFrame #Last frame number from source file (for DIDSON snippet files)
|
61 |
+
self.TimeLapse = TimeLapse #Non-zero indicates time lapse recording
|
62 |
+
self.RecordInterval = RecordInterval #Number of frames/seconds between recorded frames
|
63 |
+
self.RadioSeconds = RadioSeconds #Frames or seconds interval
|
64 |
+
self.FrameInterval = FrameInterval #Record every Nth frame
|
65 |
+
self.Flags = Flags #See DDF_04 file format document (OBSOLETE)
|
66 |
+
self.AuxFlags = AuxFlags #See DDF_04 file format document
|
67 |
+
#OBSOLETE: See frame header instead.
|
68 |
+
self.Sspd = Sspd #Sound velocity in water
|
69 |
+
self.Flags3D = Flags3D #See DDF_04 file format document
|
70 |
+
self.SoftwareVersion = SoftwareVersion #DIDSON software version that recorded the file
|
71 |
+
self.WaterTemp = WaterTemp #Water temperature code: 0 = 5-15C, 1 = 15-25C, 2 = 25-35C
|
72 |
+
self.Salinity = Salinity #Salinity code: 0 = fresh, 1 = brackish, 2 = salt
|
73 |
+
self.PulseLength = PulseLength #Added for ARIS but not used
|
74 |
+
self.TxMode = TxMode #Added for ARIS but not used
|
75 |
+
self.VersionFGPA = VersionFGPA #Reserved for future use
|
76 |
+
self.VersionPSuC = VersionPSuC #Reserved for future use
|
77 |
+
self.ThumbnailFI = ThumbnailFI #Frame index of frame used for thumbnail image of file
|
78 |
+
#OBSOLETE: Do not use; query your filesystem instead.
|
79 |
+
self.FileSize = FileSize #Total file size in bytes
|
80 |
+
self.OptionalHeaderSize = OptionalHeaderSize#Reserved for future use (Obsolete, not used)
|
81 |
+
self.OptionalTailSize = OptionalTailSize #Reserved for future use (Obsolete, not used)
|
82 |
+
self.VersionMinor = VersionMinor #DIDSON_ADJUSTED_VERSION_MINOR (Obsolete)
|
83 |
+
self.LargeLens = LargeLens #Non-zero if telephoto lens (large lens, hi-res lens, big lens) is present
|
84 |
+
|
85 |
+
def __len__(self):
|
86 |
+
return self.FrameCount
|
87 |
+
|
88 |
+
def __repr__(self):
|
89 |
+
return 'ARIS File: ' + self.filename
|
90 |
+
|
91 |
+
def info(self):
|
92 |
+
print('Filename: ' + str(self.filename))
|
93 |
+
print('Software Version: ' + str(self.SoftwareVersion))
|
94 |
+
print('ARIS S/N: ' + str(self.SN))
|
95 |
+
print('File size: ' + str(self.FileSize))
|
96 |
+
print('Number of Frames: ' + str(self.FrameCount))
|
97 |
+
print('Beam Count: ' + str(self.NumRawBeams))
|
98 |
+
print('Samples/Beam: ' + str(self.SamplesPerChannel))
|
99 |
+
|
100 |
+
class ARIS_Frame(ARIS_File):
|
101 |
+
"""This is a class container for the ARIS frame dataPI"""
|
102 |
+
|
103 |
+
def __init__(self, frameindex, frametime, version, status, sonartimestamp, tsday, tshour, tsminute, tssecond, tshsecond, transmitmode,
|
104 |
+
windowstart, windowlength, threshold, intensity, receivergain, degc1, degc2, humidity, focus, battery, uservalue1, uservalue2,
|
105 |
+
uservalue3, uservalue4, uservalue5, uservalue6, uservalue7, uservalue8, velocity, depth, altitude, pitch, pitchrate, roll,
|
106 |
+
rollrate, heading, headingrate, compassheading, compasspitch, compassroll, latitude, longitude, sonarposition, configflags,
|
107 |
+
beamtilt, targetrange, targetbearing, targetpresent, firmwarerevision, flags, sourceframe, watertemp, timerperiod, sonarx,
|
108 |
+
sonary, sonarz, sonarpan, sonartilt, sonarroll, panpnnl, tiltpnnl, rollpnnl, vehicletime, timeggk, dateggk, qualityggk, numsatsggk,
|
109 |
+
dopggk, ehtggk, heavetss, yeargps, monthgps, daygps, hourgps, minutegps, secondgps, hsecondgps, sonarpanoffset, sonartiltoffset,
|
110 |
+
sonarrolloffset, sonarxoffset, sonaryoffset, sonarzoffset, tmatrix, samplerate, accellx, accelly, accellz, pingmode, frequencyhilow,
|
111 |
+
pulsewidth, cycleperiod, sampleperiod, transmitenable, framerate, soundspeed, samplesperbeam, enable150v, samplestartdelay, largelens,
|
112 |
+
thesystemtype, sonarserialnumber, encryptedkey, ariserrorflagsuint, missedpackets, arisappversion, available2, reorderedsamples,
|
113 |
+
salinity, pressure, batteryvoltage, mainvoltage, switchvoltage, focusmotormoving, voltagechanging, focustimeoutfault, focusovercurrentfault,
|
114 |
+
focusnotfoundfault, focusstalledfault, fpgatimeoutfault, fpgabusyfault, fpgastuckfault, cputempfault, psutempfault, watertempfault,
|
115 |
+
humidityfault, pressurefault, voltagereadfault, voltagewritefault, focuscurrentposition, targetpan, targettilt, targetroll, panmotorerrorcode,
|
116 |
+
tiltmotorerrorcode, rollmotorerrorcode, panabsposition, tiltabsposition, rollabsposition, panaccelx, panaccely, panaccelz, tiltaccelx,
|
117 |
+
tiltaccely, tiltaccelz, rollaccelx, rollaccely, rollaccelz, appliedsettings, constrainedsettings, invalidsettings, enableinterpacketdelay,
|
118 |
+
interpacketdelayperiod, uptime, arisappversionmajor, arisappversionminor, gotime, panvelocity, tiltvelocity, rollvelocity, sentinel):
|
119 |
+
|
120 |
+
self.frameindex = frameindex #Frame number in file
|
121 |
+
self.frametime = frametime #PC time stamp when recorded; microseconds since epoch (Jan 1st 1970)
|
122 |
+
self.version = version #ARIS file format version = 0x05464444
|
123 |
+
self.status = status
|
124 |
+
self.sonartimestamp = sonartimestamp #On-sonar microseconds since epoch (Jan 1st 1970)
|
125 |
+
self.tsday = tsday
|
126 |
+
self.tshour = tshour
|
127 |
+
self.tsminute = tsminute
|
128 |
+
self.tssecond = tssecond
|
129 |
+
self.tshsecond = tshsecond
|
130 |
+
self.transmitmode = transmitmode
|
131 |
+
self.windowstart = windowstart #Window start in meters
|
132 |
+
self.windowlength = windowlength #Window length in meters
|
133 |
+
self.threshold = threshold
|
134 |
+
self.intensity = intensity
|
135 |
+
self.receivergain = receivergain #Note: 0-24 dB
|
136 |
+
self.degc1 = degc1 #CPU temperature (C)
|
137 |
+
self.degc2 = degc2 #Power supply temperature (C)
|
138 |
+
self.humidity = humidity #% relative humidity
|
139 |
+
self.focus = focus #Focus units 0-1000
|
140 |
+
self.battery = battery #OBSOLETE: Unused.
|
141 |
+
self.uservalue1 = uservalue1
|
142 |
+
self.uservalue2 = uservalue2
|
143 |
+
self.uservalue3 = uservalue3
|
144 |
+
self.uservalue4 = uservalue4
|
145 |
+
self.uservalue5 = uservalue5
|
146 |
+
self.uservalue6 = uservalue6
|
147 |
+
self.uservalue7 = uservalue7
|
148 |
+
self.uservalue8 = uservalue8
|
149 |
+
self.velocity = velocity # Platform velocity from AUV integration
|
150 |
+
self.depth = depth # Platform depth from AUV integration
|
151 |
+
self.altitude = altitude # Platform altitude from AUV integration
|
152 |
+
self.pitch = pitch # Platform pitch from AUV integration
|
153 |
+
self.pitchrate = pitchrate # Platform pitch rate from AUV integration
|
154 |
+
self.roll = roll # Platform roll from AUV integration
|
155 |
+
self.rollrate = rollrate # Platform roll rate from AUV integration
|
156 |
+
self.heading = heading # Platform heading from AUV integration
|
157 |
+
self.headingrate = headingrate # Platform heading rate from AUV integration
|
158 |
+
self.compassheading = compassheading # Sonar compass heading output
|
159 |
+
self.compasspitch = compasspitch # Sonar compass pitch output
|
160 |
+
self.compassroll = compassroll # Sonar compass roll output
|
161 |
+
self.latitude = latitude # from auxiliary GPS sensor
|
162 |
+
self.longitude = longitude # from auxiliary GPS sensor
|
163 |
+
self.sonarposition = sonarposition # special for PNNL
|
164 |
+
self.configflags = configflags
|
165 |
+
self.beamtilt = beamtilt
|
166 |
+
self.targetrange = targetrange
|
167 |
+
self.targetbearing = targetbearing
|
168 |
+
self.targetpresent = targetpresent
|
169 |
+
self.firmwarerevision = firmwarerevision #OBSOLETE: Unused.
|
170 |
+
self.flags = flags
|
171 |
+
self.sourceframe = sourceframe # Source file frame number for CSOT output files
|
172 |
+
self.watertemp = watertemp # Water temperature from housing temperature sensor
|
173 |
+
self.timerperiod = timerperiod
|
174 |
+
self.sonarx = sonarx # Sonar X location for 3D processing
|
175 |
+
self.sonary = sonary # Sonar Y location for 3D processing
|
176 |
+
self.sonayz = sonarz # Sonar Z location for 3D processing
|
177 |
+
self.sonarpan = sonarpan # X2 pan output
|
178 |
+
self.sonartilt = sonartilt # X2 tilt output
|
179 |
+
self.sonarroll = sonarroll # X2 roll output **** End of DDF_03 frame header data ****
|
180 |
+
self.panpnnl = panpnnl
|
181 |
+
self.tiltpnnl = tiltpnnl
|
182 |
+
self.rollpnnl = rollpnnl
|
183 |
+
self.vehicletime = vehicletime # special for Bluefin Robotics HAUV or other AUV integration
|
184 |
+
self.timeggk = timeggk # GPS output from NMEA GGK message
|
185 |
+
self.dateggk = dateggk # GPS output from NMEA GGK message
|
186 |
+
self.qualityggk = qualityggk # GPS output from NMEA GGK message
|
187 |
+
self.numsatsggk = numsatsggk # GPS output from NMEA GGK message
|
188 |
+
self.dopggk = dopggk # GPS output from NMEA GGK message
|
189 |
+
self.ehtggk = ehtggk # GPS output from NMEA GGK message
|
190 |
+
self.heavetss = heavetss # external sensor
|
191 |
+
self.yeargps = yeargps # GPS year output
|
192 |
+
self.monthgps = monthgps # GPS month output
|
193 |
+
self.daygps = daygps # GPS day output
|
194 |
+
self.hourgps = hourgps # GPS hour output
|
195 |
+
self.minutegps = minutegps # GPS minute output
|
196 |
+
self.secondgps = secondgps # GPS second output
|
197 |
+
self.hsecondgps = hsecondgps # GPS 1/100th second output
|
198 |
+
self.sonarpanoffset = sonarpanoffset # Sonar mount location pan offset for 3D processing
|
199 |
+
self.sonartiltoffset = sonartiltoffset # Sonar mount location tilt offset for 3D processing
|
200 |
+
self.sonarrolloffset = sonarrolloffset # Sonar mount location roll offset for 3D processing
|
201 |
+
self.sonarxoffset = sonarxoffset # Sonar mount location X offset for 3D processing
|
202 |
+
self.sonaryoffset = sonaryoffset # Sonar mount location Y offset for 3D processing
|
203 |
+
self.sonarzoffset = sonarzoffset # Sonar mount location Z offset for 3D processing
|
204 |
+
self.tmatirx = tmatrix # 3D processing transformation matrix
|
205 |
+
self.samplerate = samplerate # Calculated as 1e6/SamplePeriod
|
206 |
+
self.accellx = accellx # X-axis sonar acceleration
|
207 |
+
self.accelly = accelly # Y-axis sonar acceleration
|
208 |
+
self.accellz = accellz # Z-axis sonar acceleration
|
209 |
+
self.pingmode = pingmode # ARIS ping mode [1..12]
|
210 |
+
self.frequencyhilow = frequencyhilow # 1 = HF, 0 = LF
|
211 |
+
self.pulsewidth = pulsewidth # Width of transmit pulse in usec, [4..100]
|
212 |
+
self.cycleperiod = cycleperiod # Ping cycle time in usec, [1802..65535]
|
213 |
+
self.sampleperiod = sampleperiod # Downrange sample rate in usec, [4..100]
|
214 |
+
self.tranmitenable = transmitenable # 1 = Transmit ON, 0 = Transmit OFF
|
215 |
+
self.framerate = framerate # Instantaneous frame rate between frame N and frame N-1
|
216 |
+
self.soundspeed = soundspeed # Sound velocity in water calculated from water temperature and salinity setting
|
217 |
+
self.samplesperbeam = samplesperbeam # Number of downrange samples in each beam
|
218 |
+
self.enable150v = enable150v # 1 = 150V ON (Max Power), 0 = 150V OFF (Min Power, 12V)
|
219 |
+
self.samplestartdelay = samplestartdelay # Delay from transmit until start of sampling (window start) in usec, [930..65535]
|
220 |
+
self.largelens = largelens # 1 = telephoto lens (large lens, big lens, hi-res lens) present
|
221 |
+
self.thesystemtype = thesystemtype # 1 = ARIS 3000, 0 = ARIS 1800, 2 = ARIS 1200
|
222 |
+
self.sonarserialnumber = sonarserialnumber # Sonar serial number as labeled on housing
|
223 |
+
self.encryptedkey = encryptedkey # Reserved for future use
|
224 |
+
self.ariserrorflagsuint = ariserrorflagsuint # Error flag code bits
|
225 |
+
self.missedpackets = missedpackets # Missed packet count for Ethernet statistics reporting
|
226 |
+
self.arisappversion = arisappversion # Version number of ArisApp sending frame data
|
227 |
+
self.available2 = available2 # Reserved for future use
|
228 |
+
self.reorderedsamples = reorderedsamples # 1 = frame data already ordered into [beam,sample] array, 0 = needs reordering
|
229 |
+
self.salinity = salinity # Water salinity code: 0 = fresh, 15 = brackish, 35 = salt
|
230 |
+
self.pressure = pressure # Depth sensor output in meters (psi)
|
231 |
+
self.batteryvoltage = batteryvoltage # Battery input voltage before power steering
|
232 |
+
self.mainvoltage = mainvoltage # Main cable input voltage before power steering
|
233 |
+
self.switchvoltage = switchvoltage # Input voltage after power steering
|
234 |
+
self.focusmotormoving = focusmotormoving # Added 14-Aug-2012 for AutomaticRecording
|
235 |
+
self.voltagechanging = voltagechanging # Added 16-Aug (first two bits = 12V, second two bits = 150V, 00 = not changing, 01 = turning on, 10 = turning off)
|
236 |
+
self.focustimeoutfault = focustimeoutfault
|
237 |
+
self.focusovercurrentfault = focusovercurrentfault
|
238 |
+
self.focusnotfoundfault = focusnotfoundfault
|
239 |
+
self.focusstalledfault = focusstalledfault
|
240 |
+
self.fpgatimeoutfault = fpgatimeoutfault
|
241 |
+
self.fpgabusyfault = fpgabusyfault
|
242 |
+
self.fpgastuckfault = fpgastuckfault
|
243 |
+
self.cputempfault = cputempfault
|
244 |
+
self.psutempfault = psutempfault
|
245 |
+
self.watertempfault = watertempfault
|
246 |
+
self.humidityfault = humidityfault
|
247 |
+
self.pressurefault = pressurefault
|
248 |
+
self.voltagereadfault = voltagereadfault
|
249 |
+
self.voltagewritefault = voltagewritefault
|
250 |
+
self.focuscurrentposition = focuscurrentposition # Focus shaft current position in motor units [0.1000]
|
251 |
+
self.targetpan = targetpan # Commanded pan position
|
252 |
+
self.targettilt = targettilt # Commanded tilt position
|
253 |
+
self.targetroll = targetroll # Commanded roll position
|
254 |
+
self.panmotorerrorcode = panmotorerrorcode
|
255 |
+
self.tiltmotorerrorcode = tiltmotorerrorcode
|
256 |
+
self.rollmotorerrorcode = rollmotorerrorcode
|
257 |
+
self.panabsposition = panabsposition # Low-resolution magnetic encoder absolute pan position
|
258 |
+
self.tiltabsposition = tiltabsposition # Low-resolution magnetic encoder absolute tilt position
|
259 |
+
self.rollabsposition = rollabsposition # Low-resolution magnetic encoder absolute roll position
|
260 |
+
self.panaccelx = panaccelx # Accelerometer outputs from AR2 CPU board sensor
|
261 |
+
self.panaccely = panaccely
|
262 |
+
self.panaccelz = panaccelz
|
263 |
+
self.tiltaccelx = tiltaccelx
|
264 |
+
self.tiltaccely = tiltaccely
|
265 |
+
self.tiltaccelz = tiltaccelz
|
266 |
+
self.rollaccelx = rollaccelx
|
267 |
+
self.rollaccely = rollaccely
|
268 |
+
self.rollccelz = rollaccelz
|
269 |
+
self.appliedsettings = appliedsettings # Cookie indices for command acknowlege in frame header
|
270 |
+
self.constrainedsettings = constrainedsettings
|
271 |
+
self.invalidsettings = invalidsettings
|
272 |
+
self.enableinterpacketdelay = enableinterpacketdelay # If true delay is added between sending out image data packets
|
273 |
+
self.interpacketdelayperiod = interpacketdelayperiod # packet delay factor in us (does not include function overhead time)
|
274 |
+
self.uptime = uptime # Total number of seconds sonar has been running
|
275 |
+
self.arisappverionmajor = arisappversionmajor # Major version number
|
276 |
+
self.arisappversionminor = arisappversionminor # Minor version number
|
277 |
+
self.gotime = gotime # Sonar time when frame cycle is initiated in hardware
|
278 |
+
self.panvelocity = panvelocity # AR2 pan velocity in degrees/second
|
279 |
+
self.tiltvelocity = tiltvelocity # AR2 tilt velocity in degrees/second
|
280 |
+
self.rollvelocity = rollvelocity # AR2 roll velocity in degrees/second
|
281 |
+
self.sentinel = sentinel # Used to measure the frame header size
|
282 |
+
|
283 |
+
def __repr__(self):
|
284 |
+
return 'ARIS Frame Number: ' + str(self.frameindex)
|
285 |
+
|
286 |
+
def info(self):
|
287 |
+
print('Frame Number: ' + str(self.frameindex))
|
288 |
+
print('Frame Time: ' + str(datetime.datetime.fromtimestamp(self.sonartimestamp/1000000, pytz.timezone('UTC')).strftime('%Y-%m-%d %H:%M:%S.%f')))
|
289 |
+
print('Frame Rate: ' + str(self.framerate))
|
290 |
+
print('Window Start: ' + str(self.windowstart))
|
291 |
+
print('Window Length: ' + str(self.windowlength))
|
292 |
+
print('Ping Mode: ' + str(self.pingmode))
|
293 |
+
print('Frequency: ' + str(self.frequencyhilow))
|
294 |
+
|
295 |
+
|
296 |
+
def DataImport(filename, startFrame = 1, frameBuffer = 0):
|
297 |
+
"""DataImport reads in the file specified by the filename. The function populates
|
298 |
+
a ARIS_File data structure. This function then calls the FrameRead() method
|
299 |
+
to load a starting frame.
|
300 |
+
|
301 |
+
Parameters
|
302 |
+
-----------
|
303 |
+
filename : Input file (*.aris)
|
304 |
+
startFrame : The first frame to be populated into the data structure
|
305 |
+
frameBuffer : This parameter is passed into the FrameRead method. It adds a
|
306 |
+
specified number of pixels around the edges of the remapped frame.
|
307 |
+
|
308 |
+
Returns
|
309 |
+
-------
|
310 |
+
output_data : a ARIS_File data structure
|
311 |
+
frame : An ARIS_Frame data structure
|
312 |
+
|
313 |
+
Notes
|
314 |
+
-------
|
315 |
+
Basic frame attributes can be found by calling the file.info() method.
|
316 |
+
A list of all the frames attributes can be found by using dir(file), some
|
317 |
+
of these may or may not be used by the ARIS.
|
318 |
+
"""
|
319 |
+
|
320 |
+
try:
|
321 |
+
data = open(filename, 'rb')
|
322 |
+
except:
|
323 |
+
print('File Error: An error occurred trying to read the file.')
|
324 |
+
raise
|
325 |
+
|
326 |
+
#Start reading file header
|
327 |
+
version_number = struct.unpack('I', data.read(4))[0]
|
328 |
+
FrameCount = struct.unpack('I', data.read(4))[0]
|
329 |
+
FrameRate = struct.unpack('I', data.read(4))[0]
|
330 |
+
HighResolution = struct.unpack('I', data.read(4))[0]
|
331 |
+
NumRawBeams = struct.unpack('I', data.read(4))[0]
|
332 |
+
SampleRate = struct.unpack('f', data.read(4))[0]
|
333 |
+
SamplesPerChannel = struct.unpack('I', data.read(4))[0]
|
334 |
+
ReceiverGain = struct.unpack('I', data.read(4))[0]
|
335 |
+
WindowStart = struct.unpack('f', data.read(4))[0]
|
336 |
+
WindowLength = struct.unpack('f', data.read(4))[0]
|
337 |
+
Reverse = struct.unpack('I', data.read(4))[0]
|
338 |
+
SN = struct.unpack('I', data.read(4))[0]
|
339 |
+
strDate = struct.unpack('32s', data.read(32))[0]
|
340 |
+
strHeaderID = struct.unpack('256s', data.read(256))[0]
|
341 |
+
UserID1 = struct.unpack('i', data.read(4))[0]
|
342 |
+
UserID2 = struct.unpack('i', data.read(4))[0]
|
343 |
+
UserID3 = struct.unpack('i', data.read(4))[0]
|
344 |
+
UserID4 = struct.unpack('i', data.read(4))[0]
|
345 |
+
StartFrame = struct.unpack('I', data.read(4))[0]
|
346 |
+
EndFrame = struct.unpack('I', data.read(4))[0]
|
347 |
+
TimeLapse = struct.unpack('I', data.read(4))[0]
|
348 |
+
RecordInterval = struct.unpack('I', data.read(4))[0]
|
349 |
+
RadioSeconds = struct.unpack('I', data.read(4))[0]
|
350 |
+
FrameInterval = struct.unpack('I', data.read(4))[0]
|
351 |
+
Flags = struct.unpack('I', data.read(4))[0]
|
352 |
+
AuxFlags = struct.unpack('I', data.read(4))[0]
|
353 |
+
Sspd = struct.unpack('I', data.read(4))[0]
|
354 |
+
Flags3D = struct.unpack('I', data.read(4))[0]
|
355 |
+
SoftwareVersion = struct.unpack('I', data.read(4))[0]
|
356 |
+
WaterTemp = struct.unpack('I', data.read(4))[0]
|
357 |
+
Salinity = struct.unpack('I', data.read(4))[0]
|
358 |
+
PulseLength = struct.unpack('I', data.read(4))[0]
|
359 |
+
TxMode = struct.unpack('I', data.read(4))[0]
|
360 |
+
VersionFGPA = struct.unpack('I', data.read(4))[0]
|
361 |
+
VersionPSuC = struct.unpack('I', data.read(4))[0]
|
362 |
+
ThumbnailFI = struct.unpack('I', data.read(4))[0]
|
363 |
+
FileSize = struct.unpack('Q', data.read(8))[0]
|
364 |
+
OptionalHeaderSize = struct.unpack('Q', data.read(8))[0]
|
365 |
+
OptionalTailSize = struct.unpack('Q', data.read(8))[0]
|
366 |
+
VersionMinor = struct.unpack('I', data.read(4))[0]
|
367 |
+
LargeLens = struct.unpack('I', data.read(4))[0]
|
368 |
+
|
369 |
+
#Create data structure
|
370 |
+
output_data = ARIS_File(filename, version_number, FrameCount, FrameRate, HighResolution, NumRawBeams, SampleRate, SamplesPerChannel, ReceiverGain,
|
371 |
+
WindowStart, WindowLength, Reverse, SN, strDate, strHeaderID, UserID1, UserID2, UserID3, UserID4, StartFrame,EndFrame,
|
372 |
+
TimeLapse, RecordInterval, RadioSeconds, FrameInterval, Flags, AuxFlags, Sspd, Flags3D, SoftwareVersion, WaterTemp,
|
373 |
+
Salinity, PulseLength, TxMode, VersionFGPA, VersionPSuC, ThumbnailFI, FileSize, OptionalHeaderSize, OptionalTailSize,
|
374 |
+
VersionMinor, LargeLens)
|
375 |
+
|
376 |
+
#Close data file
|
377 |
+
data.close()
|
378 |
+
|
379 |
+
#Create an empty container for the lookup table
|
380 |
+
output_data.LUP = None
|
381 |
+
|
382 |
+
#Load the first frame
|
383 |
+
frame = FrameRead(output_data, startFrame)
|
384 |
+
|
385 |
+
#Return the data structure
|
386 |
+
return output_data, frame
|
387 |
+
|
388 |
+
def FrameRead(ARIS_data, frameIndex, frameBuffer = None):
|
389 |
+
"""The FrameRead function loads in the specified frame data from the raw ARIS data.
|
390 |
+
The function then calls the remapARIS() function which remaps the raw data into
|
391 |
+
a 2D real world projection.
|
392 |
+
|
393 |
+
Parameters
|
394 |
+
-----------
|
395 |
+
ARIS_data : ARIS data structure returned via pyARIS.DataImport()
|
396 |
+
frameIndex : frame number
|
397 |
+
frameBuffer : This parameter add a specified number of pixels around the edges
|
398 |
+
of the remapped frame.
|
399 |
+
|
400 |
+
Returns
|
401 |
+
-------
|
402 |
+
output : a frame data structure
|
403 |
+
|
404 |
+
Notes
|
405 |
+
-------
|
406 |
+
Basic frame attributes can be found by calling the frame.info() method.
|
407 |
+
A list of all the frames attributes can be found by using dir(frame), some
|
408 |
+
of these may or may not be used by the ARIS.
|
409 |
+
"""
|
410 |
+
|
411 |
+
FrameSize = ARIS_data.NumRawBeams*ARIS_data.SamplesPerChannel
|
412 |
+
|
413 |
+
frameoffset = (1024+(frameIndex*(1024+(FrameSize))))
|
414 |
+
|
415 |
+
data = open(ARIS_data.filename, 'rb')
|
416 |
+
data.seek(frameoffset, 0)
|
417 |
+
|
418 |
+
frameindex = struct.unpack('I', data.read(4))[0] #Frame number in file
|
419 |
+
frametime = struct.unpack('Q', data.read(8))[0] #PC time stamp when recorded; microseconds since epoch (Jan 1st 1970)
|
420 |
+
version = struct.unpack('I', data.read(4))[0] #ARIS file format version = 0x05464444
|
421 |
+
status = struct.unpack('I', data.read(4))[0]
|
422 |
+
sonartimestamp = struct.unpack('Q', data.read(8))[0] #On-sonar microseconds since epoch (Jan 1st 1970)
|
423 |
+
tsday = struct.unpack('I', data.read(4))[0]
|
424 |
+
tshour = struct.unpack('I', data.read(4))[0]
|
425 |
+
tsminute = struct.unpack('I', data.read(4))[0]
|
426 |
+
tssecond = struct.unpack('I', data.read(4))[0]
|
427 |
+
tshsecond = struct.unpack('I', data.read(4))[0]
|
428 |
+
transmitmode = struct.unpack('I', data.read(4))[0]
|
429 |
+
windowstart = struct.unpack('f', data.read(4))[0] #Window start in meters
|
430 |
+
windowlength = struct.unpack('f', data.read(4))[0] #Window length in meters
|
431 |
+
threshold = struct.unpack('I', data.read(4))[0]
|
432 |
+
intensity = struct.unpack('i', data.read(4))[0]
|
433 |
+
receivergain = struct.unpack('I', data.read(4))[0] #Note: 0-24 dB
|
434 |
+
degc1 = struct.unpack('I', data.read(4))[0] #CPU temperature (C)
|
435 |
+
degc2 = struct.unpack('I', data.read(4))[0] #Power supply temperature (C)
|
436 |
+
humidity = struct.unpack('I', data.read(4))[0] #% relative humidity
|
437 |
+
focus = struct.unpack('I', data.read(4))[0] #Focus units 0-1000
|
438 |
+
battery = struct.unpack('I', data.read(4))[0] #OBSOLETE: Unused.
|
439 |
+
uservalue1 = struct.unpack('f', data.read(4))[0]
|
440 |
+
uservalue2 = struct.unpack('f', data.read(4))[0]
|
441 |
+
uservalue3 = struct.unpack('f', data.read(4))[0]
|
442 |
+
uservalue4 = struct.unpack('f', data.read(4))[0]
|
443 |
+
uservalue5 = struct.unpack('f', data.read(4))[0]
|
444 |
+
uservalue6 = struct.unpack('f', data.read(4))[0]
|
445 |
+
uservalue7 = struct.unpack('f', data.read(4))[0]
|
446 |
+
uservalue8 = struct.unpack('f', data.read(4))[0]
|
447 |
+
velocity = struct.unpack('f', data.read(4))[0] # Platform velocity from AUV integration
|
448 |
+
depth = struct.unpack('f', data.read(4))[0] # Platform depth from AUV integration
|
449 |
+
altitude = struct.unpack('f', data.read(4))[0] # Platform altitude from AUV integration
|
450 |
+
pitch = struct.unpack('f', data.read(4))[0] # Platform pitch from AUV integration
|
451 |
+
pitchrate = struct.unpack('f', data.read(4))[0] # Platform pitch rate from AUV integration
|
452 |
+
roll = struct.unpack('f', data.read(4))[0] # Platform roll from AUV integration
|
453 |
+
rollrate = struct.unpack('f', data.read(4))[0] # Platform roll rate from AUV integration
|
454 |
+
heading = struct.unpack('f', data.read(4))[0] # Platform heading from AUV integration
|
455 |
+
headingrate = struct.unpack('f', data.read(4))[0] # Platform heading rate from AUV integration
|
456 |
+
compassheading = struct.unpack('f', data.read(4))[0] # Sonar compass heading output
|
457 |
+
compasspitch = struct.unpack('f', data.read(4))[0] # Sonar compass pitch output
|
458 |
+
compassroll = struct.unpack('f', data.read(4))[0] # Sonar compass roll output
|
459 |
+
latitude = struct.unpack('d', data.read(8))[0] # from auxiliary GPS sensor
|
460 |
+
longitude = struct.unpack('d', data.read(8))[0] # from auxiliary GPS sensor
|
461 |
+
sonarposition = struct.unpack('f', data.read(4))[0] # special for PNNL
|
462 |
+
configflags = struct.unpack('I', data.read(4))[0]
|
463 |
+
beamtilt = struct.unpack('f', data.read(4))[0]
|
464 |
+
targetrange = struct.unpack('f', data.read(4))[0]
|
465 |
+
targetbearing = struct.unpack('f', data.read(4))[0]
|
466 |
+
targetpresent = struct.unpack('I', data.read(4))[0]
|
467 |
+
firmwarerevision = struct.unpack('I', data.read(4))[0] #OBSOLETE: Unused.
|
468 |
+
flags = struct.unpack('I', data.read(4))[0]
|
469 |
+
sourceframe = struct.unpack('I', data.read(4))[0] # Source file frame number for CSOT output files
|
470 |
+
watertemp = struct.unpack('f', data.read(4))[0] # Water temperature from housing temperature sensor
|
471 |
+
timerperiod = struct.unpack('I', data.read(4))[0]
|
472 |
+
sonarx = struct.unpack('f', data.read(4))[0] # Sonar X location for 3D processing
|
473 |
+
sonary = struct.unpack('f', data.read(4))[0] # Sonar Y location for 3D processing
|
474 |
+
sonarz = struct.unpack('f', data.read(4))[0] # Sonar Z location for 3D processing
|
475 |
+
sonarpan = struct.unpack('f', data.read(4))[0] # X2 pan output
|
476 |
+
sonartilt = struct.unpack('f', data.read(4))[0] # X2 tilt output
|
477 |
+
sonarroll = struct.unpack('f', data.read(4))[0] # X2 roll output **** End of DDF_03 frame header data ****
|
478 |
+
panpnnl = struct.unpack('f', data.read(4))[0]
|
479 |
+
tiltpnnl = struct.unpack('f', data.read(4))[0]
|
480 |
+
rollpnnl = struct.unpack('f', data.read(4))[0]
|
481 |
+
vehicletime = struct.unpack('d', data.read(8))[0] # special for Bluefin Robotics HAUV or other AUV integration
|
482 |
+
timeggk = struct.unpack('f', data.read(4))[0] # GPS output from NMEA GGK message
|
483 |
+
dateggk = struct.unpack('I', data.read(4))[0] # GPS output from NMEA GGK message
|
484 |
+
qualityggk = struct.unpack('I', data.read(4))[0] # GPS output from NMEA GGK message
|
485 |
+
numsatsggk = struct.unpack('I', data.read(4))[0] # GPS output from NMEA GGK message
|
486 |
+
dopggk = struct.unpack('f', data.read(4))[0] # GPS output from NMEA GGK message
|
487 |
+
ehtggk = struct.unpack('f', data.read(4))[0] # GPS output from NMEA GGK message
|
488 |
+
heavetss = struct.unpack('f', data.read(4))[0] # external sensor
|
489 |
+
yeargps = struct.unpack('I', data.read(4))[0] # GPS year output
|
490 |
+
monthgps = struct.unpack('I', data.read(4))[0] # GPS month output
|
491 |
+
daygps = struct.unpack('I', data.read(4))[0] # GPS day output
|
492 |
+
hourgps = struct.unpack('I', data.read(4))[0] # GPS hour output
|
493 |
+
minutegps = struct.unpack('I', data.read(4))[0] # GPS minute output
|
494 |
+
secondgps = struct.unpack('I', data.read(4))[0] # GPS second output
|
495 |
+
hsecondgps = struct.unpack('I', data.read(4))[0] # GPS 1/100th second output
|
496 |
+
sonarpanoffset = struct.unpack('f', data.read(4))[0] # Sonar mount location pan offset for 3D processing
|
497 |
+
sonartiltoffset = struct.unpack('f', data.read(4))[0] # Sonar mount location tilt offset for 3D processing
|
498 |
+
sonarrolloffset = struct.unpack('f', data.read(4))[0] # Sonar mount location roll offset for 3D processing
|
499 |
+
sonarxoffset = struct.unpack('f', data.read(4))[0] # Sonar mount location X offset for 3D processing
|
500 |
+
sonaryoffset = struct.unpack('f', data.read(4))[0] # Sonar mount location Y offset for 3D processing
|
501 |
+
sonarzoffset = struct.unpack('f', data.read(4))[0] # Sonar mount location Z offset for 3D processing
|
502 |
+
tmatrix = array.array('f') # 3D processing transformation matrix
|
503 |
+
for i in range(16):
|
504 |
+
tmatrix.append(struct.unpack('f', data.read(4))[0])
|
505 |
+
samplerate = struct.unpack('f', data.read(4))[0] # Calculated as 1e6/SamplePeriod
|
506 |
+
accellx = struct.unpack('f', data.read(4))[0] # X-axis sonar acceleration
|
507 |
+
accelly = struct.unpack('f', data.read(4))[0] # Y-axis sonar acceleration
|
508 |
+
accellz = struct.unpack('f', data.read(4))[0] # Z-axis sonar acceleration
|
509 |
+
pingmode = struct.unpack('I', data.read(4))[0] # ARIS ping mode [1..12]
|
510 |
+
frequencyhilow = struct.unpack('I', data.read(4))[0] # 1 = HF, 0 = LF
|
511 |
+
pulsewidth = struct.unpack('I', data.read(4))[0] # Width of transmit pulse in usec, [4..100]
|
512 |
+
cycleperiod = struct.unpack('I', data.read(4))[0] # Ping cycle time in usec, [1802..65535]
|
513 |
+
sampleperiod = struct.unpack('I', data.read(4))[0] # Downrange sample rate in usec, [4..100]
|
514 |
+
transmitenable = struct.unpack('I', data.read(4))[0] # 1 = Transmit ON, 0 = Transmit OFF
|
515 |
+
framerate = struct.unpack('f', data.read(4))[0] # Instantaneous frame rate between frame N and frame N-1
|
516 |
+
soundspeed = struct.unpack('f', data.read(4))[0] # Sound velocity in water calculated from water temperature and salinity setting
|
517 |
+
samplesperbeam = struct.unpack('I', data.read(4))[0] # Number of downrange samples in each beam
|
518 |
+
enable150v = struct.unpack('I', data.read(4))[0] # 1 = 150V ON (Max Power), 0 = 150V OFF (Min Power, 12V)
|
519 |
+
samplestartdelay = struct.unpack('I', data.read(4))[0] # Delay from transmit until start of sampling (window start) in usec, [930..65535]
|
520 |
+
largelens = struct.unpack('I', data.read(4))[0] # 1 = telephoto lens (large lens, big lens, hi-res lens) present
|
521 |
+
thesystemtype = struct.unpack('I', data.read(4))[0] # 1 = ARIS 3000, 0 = ARIS 1800, 2 = ARIS 1200
|
522 |
+
sonarserialnumber = struct.unpack('I', data.read(4))[0] # Sonar serial number as labeled on housing
|
523 |
+
encryptedkey = struct.unpack('Q', data.read(8))[0] # Reserved for future use
|
524 |
+
ariserrorflagsuint = struct.unpack('I', data.read(4))[0] # Error flag code bits
|
525 |
+
missedpackets = struct.unpack('I', data.read(4))[0] # Missed packet count for Ethernet statistics reporting
|
526 |
+
arisappversion = struct.unpack('I', data.read(4))[0] # Version number of ArisApp sending frame data
|
527 |
+
available2 = struct.unpack('I', data.read(4))[0] # Reserved for future use
|
528 |
+
reorderedsamples = struct.unpack('I', data.read(4))[0] # 1 = frame data already ordered into [beam,sample] array, 0 = needs reordering
|
529 |
+
salinity = struct.unpack('I', data.read(4))[0] # Water salinity code: 0 = fresh, 15 = brackish, 35 = salt
|
530 |
+
pressure = struct.unpack('f', data.read(4))[0] # Depth sensor output in meters (psi)
|
531 |
+
batteryvoltage = struct.unpack('f', data.read(4))[0] # Battery input voltage before power steering
|
532 |
+
mainvoltage = struct.unpack('f', data.read(4))[0] # Main cable input voltage before power steering
|
533 |
+
switchvoltage = struct.unpack('f', data.read(4))[0] # Input voltage after power steering
|
534 |
+
focusmotormoving = struct.unpack('I', data.read(4))[0] # Added 14-Aug-2012 for AutomaticRecording
|
535 |
+
voltagechanging = struct.unpack('I', data.read(4))[0] # Added 16-Aug (first two bits = 12V, second two bits = 150V, 00 = not changing, 01 = turning on, 10 = turning off)
|
536 |
+
focustimeoutfault = struct.unpack('I', data.read(4))[0]
|
537 |
+
focusovercurrentfault = struct.unpack('I', data.read(4))[0]
|
538 |
+
focusnotfoundfault = struct.unpack('I', data.read(4))[0]
|
539 |
+
focusstalledfault = struct.unpack('I', data.read(4))[0]
|
540 |
+
fpgatimeoutfault = struct.unpack('I', data.read(4))[0]
|
541 |
+
fpgabusyfault = struct.unpack('I', data.read(4))[0]
|
542 |
+
fpgastuckfault = struct.unpack('I', data.read(4))[0]
|
543 |
+
cputempfault = struct.unpack('I', data.read(4))[0]
|
544 |
+
psutempfault = struct.unpack('I', data.read(4))[0]
|
545 |
+
watertempfault = struct.unpack('I', data.read(4))[0]
|
546 |
+
humidityfault = struct.unpack('I', data.read(4))[0]
|
547 |
+
pressurefault = struct.unpack('I', data.read(4))[0]
|
548 |
+
voltagereadfault = struct.unpack('I', data.read(4))[0]
|
549 |
+
voltagewritefault = struct.unpack('I', data.read(4))[0]
|
550 |
+
focuscurrentposition = struct.unpack('I', data.read(4))[0] # Focus shaft current position in motor units [0.1000]
|
551 |
+
targetpan = struct.unpack('f', data.read(4))[0] # Commanded pan position
|
552 |
+
targettilt = struct.unpack('f', data.read(4))[0] # Commanded tilt position
|
553 |
+
targetroll = struct.unpack('f', data.read(4))[0] # Commanded roll position
|
554 |
+
panmotorerrorcode = struct.unpack('I', data.read(4))[0]
|
555 |
+
tiltmotorerrorcode = struct.unpack('I', data.read(4))[0]
|
556 |
+
rollmotorerrorcode = struct.unpack('I', data.read(4))[0]
|
557 |
+
panabsposition = struct.unpack('f', data.read(4))[0] # Low-resolution magnetic encoder absolute pan position
|
558 |
+
tiltabsposition = struct.unpack('f', data.read(4))[0] # Low-resolution magnetic encoder absolute tilt position
|
559 |
+
rollabsposition = struct.unpack('f', data.read(4))[0] # Low-resolution magnetic encoder absolute roll position
|
560 |
+
panaccelx = struct.unpack('f', data.read(4))[0] # Accelerometer outputs from AR2 CPU board sensor
|
561 |
+
panaccely = struct.unpack('f', data.read(4))[0]
|
562 |
+
panaccelz = struct.unpack('f', data.read(4))[0]
|
563 |
+
tiltaccelx = struct.unpack('f', data.read(4))[0]
|
564 |
+
tiltaccely = struct.unpack('f', data.read(4))[0]
|
565 |
+
tiltaccelz = struct.unpack('f', data.read(4))[0]
|
566 |
+
rollaccelx = struct.unpack('f', data.read(4))[0]
|
567 |
+
rollaccely = struct.unpack('f', data.read(4))[0]
|
568 |
+
rollaccelz = struct.unpack('f', data.read(4))[0]
|
569 |
+
appliedsettings = struct.unpack('I', data.read(4))[0] # Cookie indices for command acknowlege in frame header
|
570 |
+
constrainedsettings = struct.unpack('I', data.read(4))[0]
|
571 |
+
invalidsettings = struct.unpack('I', data.read(4))[0]
|
572 |
+
enableinterpacketdelay = struct.unpack('I', data.read(4))[0] # If true delay is added between sending out image data packets
|
573 |
+
interpacketdelayperiod = struct.unpack('I', data.read(4))[0] # packet delay factor in us (does not include function overhead time)
|
574 |
+
uptime = struct.unpack('I', data.read(4))[0] # Total number of seconds sonar has been running
|
575 |
+
arisappversionmajor = struct.unpack('H', data.read(2))[0] # Major version number
|
576 |
+
arisappversionminor = struct.unpack('H', data.read(2))[0] # Minor version number
|
577 |
+
gotime = struct.unpack('Q', data.read(8))[0] # Sonar time when frame cycle is initiated in hardware
|
578 |
+
panvelocity = struct.unpack('f', data.read(4))[0] # AR2 pan velocity in degrees/second
|
579 |
+
tiltvelocity = struct.unpack('f', data.read(4))[0] # AR2 tilt velocity in degrees/second
|
580 |
+
rollvelocity = struct.unpack('f', data.read(4))[0] # AR2 roll velocity in degrees/second
|
581 |
+
sentinel = struct.unpack('I', data.read(4))[0] # Used to measure the frame header size
|
582 |
+
|
583 |
+
#Create the ARIS_frame data structure and add the meta-data
|
584 |
+
output = ARIS_Frame(frameindex, frametime, version, status, sonartimestamp, tsday, tshour, tsminute, tssecond, tshsecond, transmitmode,
|
585 |
+
windowstart, windowlength, threshold, intensity, receivergain, degc1, degc2, humidity, focus, battery, uservalue1, uservalue2,
|
586 |
+
uservalue3, uservalue4, uservalue5, uservalue6, uservalue7, uservalue8, velocity, depth, altitude, pitch, pitchrate, roll,
|
587 |
+
rollrate, heading, headingrate, compassheading, compasspitch, compassroll, latitude, longitude, sonarposition, configflags,
|
588 |
+
beamtilt, targetrange, targetbearing, targetpresent, firmwarerevision, flags, sourceframe, watertemp, timerperiod, sonarx,
|
589 |
+
sonary, sonarz, sonarpan, sonartilt, sonarroll, panpnnl, tiltpnnl, rollpnnl, vehicletime, timeggk, dateggk, qualityggk, numsatsggk,
|
590 |
+
dopggk, ehtggk, heavetss, yeargps, monthgps, daygps, hourgps, minutegps, secondgps, hsecondgps, sonarpanoffset, sonartiltoffset,
|
591 |
+
sonarrolloffset, sonarxoffset, sonaryoffset, sonarzoffset, tmatrix, samplerate, accellx, accelly, accellz, pingmode, frequencyhilow,
|
592 |
+
pulsewidth, cycleperiod, sampleperiod, transmitenable, framerate, soundspeed, samplesperbeam, enable150v, samplestartdelay, largelens,
|
593 |
+
thesystemtype, sonarserialnumber, encryptedkey, ariserrorflagsuint, missedpackets, arisappversion, available2, reorderedsamples,
|
594 |
+
salinity, pressure, batteryvoltage, mainvoltage, switchvoltage, focusmotormoving, voltagechanging, focustimeoutfault, focusovercurrentfault,
|
595 |
+
focusnotfoundfault, focusstalledfault, fpgatimeoutfault, fpgabusyfault, fpgastuckfault, cputempfault, psutempfault, watertempfault,
|
596 |
+
humidityfault, pressurefault, voltagereadfault, voltagewritefault, focuscurrentposition, targetpan, targettilt, targetroll, panmotorerrorcode,
|
597 |
+
tiltmotorerrorcode, rollmotorerrorcode, panabsposition, tiltabsposition, rollabsposition, panaccelx, panaccely, panaccelz, tiltaccelx,
|
598 |
+
tiltaccely, tiltaccelz, rollaccelx, rollaccely, rollaccelz, appliedsettings, constrainedsettings, invalidsettings, enableinterpacketdelay,
|
599 |
+
interpacketdelayperiod, uptime, arisappversionmajor, arisappversionminor, gotime, panvelocity, tiltvelocity, rollvelocity, sentinel)
|
600 |
+
|
601 |
+
|
602 |
+
#Add the frame data
|
603 |
+
if pingmode in [1,2]:
|
604 |
+
ARIS_Frame.BeamCount = 48
|
605 |
+
if pingmode in [3,4,5]:
|
606 |
+
ARIS_Frame.BeamCount = 96
|
607 |
+
if pingmode in [6,7,8]:
|
608 |
+
ARIS_Frame.BeamCount = 64
|
609 |
+
if pingmode in [9,10,11,12]:
|
610 |
+
ARIS_Frame.BeamCount = 128
|
611 |
+
|
612 |
+
data.seek(frameoffset+1024, 0)
|
613 |
+
frame = np.empty([samplesperbeam, ARIS_Frame.BeamCount], dtype=float)
|
614 |
+
for r in range(len(frame)):
|
615 |
+
for c in range(len(frame[r])):
|
616 |
+
frame[r][c] = struct.unpack('B', data.read(1))[0]
|
617 |
+
frame = np.fliplr(frame)
|
618 |
+
|
619 |
+
#Remap the data from 0-255 to 0-80 dB
|
620 |
+
#remap = lambda t: (t * 80)/255
|
621 |
+
#vfunc = np.vectorize(remap)
|
622 |
+
#frame = vfunc(frame)
|
623 |
+
|
624 |
+
output.frame_data = frame
|
625 |
+
output.WinStart = output.samplestartdelay * 0.000001 * output.soundspeed / 2
|
626 |
+
|
627 |
+
#Close the data file
|
628 |
+
data.close()
|
629 |
+
|
630 |
+
return output
|
631 |
+
|
632 |
+
|
633 |
+
def get_box_for_sample(beam_num, bin_num, frame, beam_data):
|
634 |
+
""" Get the box coordinates (in meters) for a sample.
|
635 |
+
This is a non-axis aligned box.
|
636 |
+
Returns:
|
637 |
+
back left, back right, front right, front left
|
638 |
+
"""
|
639 |
+
|
640 |
+
sample_start_delay = frame.samplestartdelay # usec
|
641 |
+
sound_speed = frame.soundspeed # meters / sec
|
642 |
+
sample_period = frame.sampleperiod # usec
|
643 |
+
|
644 |
+
WindowStart = sample_start_delay * 1e-6 * sound_speed / 2 # meters
|
645 |
+
sample_length = sample_period * 1e-6 * sound_speed / 2. # meters
|
646 |
+
|
647 |
+
bin_front_edge_distance = WindowStart + sample_length * bin_num
|
648 |
+
bin_back_edge_distance = WindowStart + sample_length* (bin_num + 1)
|
649 |
+
|
650 |
+
beam_angles = beam_data[beam_data['beam_num'] == beam_num]
|
651 |
+
a1 = beam_angles['beam_left'].iloc[0]
|
652 |
+
a2 = beam_angles['beam_right'].iloc[0]
|
653 |
+
c = beam_angles['beam_center'].iloc[0]
|
654 |
+
|
655 |
+
# I can't figure out whats going on with the beam spacing in the files.
|
656 |
+
# Once the center point crosses 0, the ordering of the left and right angles swap...
|
657 |
+
# For now I'll assume the y axis is the common line. Positive angles go to the left,
|
658 |
+
# negative angles go to the right
|
659 |
+
left = max(a1, a2)
|
660 |
+
right = min(a1, a2)
|
661 |
+
|
662 |
+
# Left Edge
|
663 |
+
beam_left_angle = np.deg2rad(left)
|
664 |
+
rot_matrix = np.array([[np.cos(beam_left_angle), -np.sin(beam_left_angle)], [np.sin(beam_left_angle), np.cos(beam_left_angle)]])
|
665 |
+
|
666 |
+
vec = np.array([0, bin_back_edge_distance])
|
667 |
+
bin_left_back_point = np.matmul(rot_matrix, vec)
|
668 |
+
|
669 |
+
vec = np.array([0, bin_front_edge_distance])
|
670 |
+
bin_left_front_point = np.matmul(rot_matrix, vec)
|
671 |
+
|
672 |
+
# Right Edge
|
673 |
+
beam_right_angle = np.deg2rad(right)
|
674 |
+
rot_matrix = np.array([[np.cos(beam_right_angle), -np.sin(beam_right_angle)], [np.sin(beam_right_angle), np.cos(beam_right_angle)]])
|
675 |
+
|
676 |
+
vec = np.array([0, bin_front_edge_distance])
|
677 |
+
bin_right_front_point = np.matmul(rot_matrix, vec)
|
678 |
+
|
679 |
+
vec = np.array([0, bin_back_edge_distance])
|
680 |
+
bin_right_back_point = np.matmul(rot_matrix, vec)
|
681 |
+
|
682 |
+
|
683 |
+
return bin_left_back_point, bin_right_back_point, bin_right_front_point, bin_left_front_point
|
684 |
+
|
685 |
+
|
686 |
+
def xy_to_sample(x, y, frame, beam_data):
|
687 |
+
""" Convert an x,y location (in meters) to a beam sample.
|
688 |
+
Returns:
|
689 |
+
beam num
|
690 |
+
bin num
|
691 |
+
"""
|
692 |
+
|
693 |
+
# Get the angle
|
694 |
+
angle = np.rad2deg(np.arctan(x / y))
|
695 |
+
|
696 |
+
beam_num = beam_data[(beam_data['beam_left'] <= angle) & (angle <= beam_data['beam_right'])]
|
697 |
+
|
698 |
+
if beam_num.shape[0] == 0:
|
699 |
+
return None, None
|
700 |
+
|
701 |
+
beam_num = beam_num['beam_num'].iloc[0]
|
702 |
+
|
703 |
+
# Get the distance
|
704 |
+
hyp = y / np.cos(np.arctan(x / y))
|
705 |
+
|
706 |
+
# Take into account the window start
|
707 |
+
hyp -= frame.WinStart
|
708 |
+
|
709 |
+
# Sample length
|
710 |
+
bin_length = frame.sampleperiod * 0.000001 * frame.soundspeed / 2.
|
711 |
+
|
712 |
+
# Convert to bins
|
713 |
+
bin_num = int(hyp / bin_length)
|
714 |
+
|
715 |
+
if bin_num < 0 or bin_num > (frame.BeamCount - 1):
|
716 |
+
return None, None
|
717 |
+
|
718 |
+
return beam_num, bin_num
|
719 |
+
|
720 |
+
|
721 |
+
def get_minimum_pixel_meter_size(frame, beam_width_data):
|
722 |
+
""" Compute the smallest pixel size that will bound a sample.
|
723 |
+
"""
|
724 |
+
|
725 |
+
all_widths = []
|
726 |
+
all_heights = []
|
727 |
+
|
728 |
+
for beam_num in range(frame.BeamCount):
|
729 |
+
bl, br, fr, fl = get_box_for_sample(beam_num, 0, frame, beam_width_data)
|
730 |
+
|
731 |
+
# determine the axis aligned box around this sample box.
|
732 |
+
xs = [bl[0], br[0], fr[0], fl[0]]
|
733 |
+
ys = [bl[1], br[1], fr[1], fl[1]]
|
734 |
+
min_x = min(xs)
|
735 |
+
max_x = max(xs)
|
736 |
+
min_y = min(ys)
|
737 |
+
max_y = max(ys)
|
738 |
+
|
739 |
+
width = max_x - min_x
|
740 |
+
height = max_y - min_y
|
741 |
+
|
742 |
+
all_widths.append(width)
|
743 |
+
all_heights.append(height)
|
744 |
+
|
745 |
+
min_width = min(all_widths)
|
746 |
+
min_height = min(all_heights)
|
747 |
+
|
748 |
+
return min(min_width, min_height)
|
749 |
+
|
750 |
+
def compute_image_bounds(pixel_meter_size, frame, beam_width_data, additional_pixel_padding_x=0, additional_pixel_padding_y=0):
|
751 |
+
""" Given the size of a pixel in meters, compute the bounds of an image that will contain the frame.
|
752 |
+
"""
|
753 |
+
|
754 |
+
# Compute the projected locations of all samples so that we can get the extent
|
755 |
+
all_bl = []
|
756 |
+
all_br = []
|
757 |
+
all_fr = []
|
758 |
+
all_fl = []
|
759 |
+
|
760 |
+
for beam_num in [0, frame.BeamCount / 2, frame.BeamCount - 1]:
|
761 |
+
for bin_num in [0, frame.samplesperbeam - 1]:
|
762 |
+
bl, br, fr, fl = get_box_for_sample(beam_num, bin_num, frame, beam_width_data)
|
763 |
+
|
764 |
+
all_bl.append(bl)
|
765 |
+
all_br.append(br)
|
766 |
+
all_fr.append(fr)
|
767 |
+
all_fl.append(fl)
|
768 |
+
|
769 |
+
all_bl = np.array(all_bl)
|
770 |
+
all_br = np.array(all_br)
|
771 |
+
all_fr = np.array(all_fr)
|
772 |
+
all_fl = np.array(all_fl)
|
773 |
+
|
774 |
+
# Get the xdim extent
|
775 |
+
min_back_left = np.min(all_bl[:,0])
|
776 |
+
min_back_right = np.min(all_br[:,0])
|
777 |
+
min_front_left = np.min(all_fl[:,0])
|
778 |
+
min_front_right = np.min(all_fr[:,0])
|
779 |
+
assert min_back_left < min_back_right
|
780 |
+
assert min_back_left < min_front_left
|
781 |
+
assert min_back_left < min_front_right
|
782 |
+
|
783 |
+
max_back_left = np.max(all_bl[:,0])
|
784 |
+
max_back_right = np.max(all_br[:,0])
|
785 |
+
max_front_left = np.max(all_fl[:,0])
|
786 |
+
max_front_right = np.max(all_fr[:,0])
|
787 |
+
assert max_back_right > max_back_left
|
788 |
+
assert max_back_right > max_front_left
|
789 |
+
assert max_back_right > max_front_right
|
790 |
+
|
791 |
+
xdim_extent = np.array([min_back_left, max_back_right])
|
792 |
+
|
793 |
+
|
794 |
+
# Get the ydim extent
|
795 |
+
min_back_left = np.min(all_bl[:,1])
|
796 |
+
min_back_right = np.min(all_br[:,1])
|
797 |
+
min_front_left = np.min(all_fl[:,1])
|
798 |
+
min_front_right = np.min(all_fr[:,1])
|
799 |
+
min_front = min(min_front_left, min_front_right)
|
800 |
+
assert min_front < min_back_right
|
801 |
+
assert min_front < min_back_left
|
802 |
+
|
803 |
+
|
804 |
+
max_back_left = np.max(all_bl[:,1])
|
805 |
+
max_back_right = np.max(all_br[:,1])
|
806 |
+
max_front_left = np.max(all_fl[:,1])
|
807 |
+
max_front_right = np.max(all_fr[:,1])
|
808 |
+
max_back = max(max_back_left, max_back_right)
|
809 |
+
assert max_back > max_front_right
|
810 |
+
assert max_back > max_front_left
|
811 |
+
|
812 |
+
ydim_extent = np.array([min_front, max_back])
|
813 |
+
|
814 |
+
# Determine which meter location corresponds to our "target center"
|
815 |
+
bl, br, fr, fl = get_box_for_sample(frame.BeamCount / 2, 0, frame, beam_width_data)
|
816 |
+
target_center_x = (fl[0] + fr[0]) / 2.
|
817 |
+
target_center_y = (bl[1] + fl[1]) / 2.
|
818 |
+
|
819 |
+
# Determine the x dimension size and what this corresponds to in meters
|
820 |
+
extra_padding_x = pixel_meter_size + pixel_meter_size * additional_pixel_padding_x
|
821 |
+
|
822 |
+
# X Min
|
823 |
+
xmin_len = target_center_x - xdim_extent[0]
|
824 |
+
xp = xmin_len % pixel_meter_size
|
825 |
+
xmin_padded = xdim_extent[0] - (extra_padding_x - xp)
|
826 |
+
xmin_len = target_center_x - xmin_padded
|
827 |
+
x_min_cells = np.abs(xmin_len / pixel_meter_size)
|
828 |
+
x_min_meters = target_center_x - xmin_len
|
829 |
+
assert x_min_meters <= xdim_extent[0]
|
830 |
+
|
831 |
+
|
832 |
+
# X Max
|
833 |
+
xmax_len = xdim_extent[1] - target_center_x
|
834 |
+
xp = xmax_len % pixel_meter_size
|
835 |
+
xmax_padded = xdim_extent[1] + (extra_padding_x - xp)
|
836 |
+
xmax_len = xmax_padded - target_center_x
|
837 |
+
x_max_cells = np.abs(xmax_len / pixel_meter_size)
|
838 |
+
x_max_meters = target_center_x + xmax_len
|
839 |
+
assert x_max_meters >= xdim_extent[1]
|
840 |
+
|
841 |
+
|
842 |
+
# if we want a specific beam to be the in the middle of the image then we should take the max?
|
843 |
+
xdim = int(x_min_cells + x_max_cells)
|
844 |
+
x_meter_start = x_min_meters
|
845 |
+
x_meter_stop = x_max_meters
|
846 |
+
|
847 |
+
# Determine the y dimension size and what this corresponds to in meters
|
848 |
+
extra_padding_y = pixel_meter_size + pixel_meter_size * additional_pixel_padding_y
|
849 |
+
|
850 |
+
# Y Min
|
851 |
+
ymin_len = target_center_y - ydim_extent[0]
|
852 |
+
yp = ymin_len % pixel_meter_size
|
853 |
+
ymin_padded = ydim_extent[0] - ( extra_padding_y - yp)
|
854 |
+
ymin_len = target_center_y - ymin_padded
|
855 |
+
y_min_cells = np.abs(ymin_len / pixel_meter_size)
|
856 |
+
y_min_meters = target_center_y - ymin_len
|
857 |
+
assert y_min_meters <= ydim_extent[0]
|
858 |
+
|
859 |
+
# Y Max
|
860 |
+
ymax_len = ydim_extent[1] - target_center_y
|
861 |
+
yp = ymax_len % pixel_meter_size
|
862 |
+
ymax_padded = ydim_extent[1] + (extra_padding_y - yp)
|
863 |
+
ymax_len = ymax_padded - target_center_y
|
864 |
+
y_max_cells = np.abs(ymax_len / pixel_meter_size)
|
865 |
+
y_max_meters = target_center_y + ymax_len
|
866 |
+
assert y_max_meters >= ydim_extent[1]
|
867 |
+
|
868 |
+
ydim = int(y_min_cells + y_max_cells)
|
869 |
+
y_meter_start = y_max_meters
|
870 |
+
y_meter_stop = y_min_meters
|
871 |
+
|
872 |
+
return xdim, ydim, x_meter_start, y_meter_start, x_meter_stop, y_meter_stop
|
873 |
+
|
874 |
+
|
875 |
+
def compute_mapping_from_sample_to_image(pixel_meter_size, xdim, ydim, x_meter_start, y_meter_start, frame, beam_width_data):
|
876 |
+
|
877 |
+
x_meter_values = np.array([x_meter_start + i * pixel_meter_size for i in range(xdim)])
|
878 |
+
y_meter_values = np.array([y_meter_start - i * pixel_meter_size for i in range(ydim)])
|
879 |
+
|
880 |
+
YY, XX = np.meshgrid(y_meter_values, x_meter_values, indexing='ij')
|
881 |
+
XYpairs = np.vstack([ XX.reshape(-1), YY.reshape(-1) ]).T
|
882 |
+
|
883 |
+
II, JJ = np.meshgrid(np.arange(ydim), np.arange(xdim), indexing='ij')
|
884 |
+
IJpairs = np.vstack([ II.reshape(-1), JJ.reshape(-1)]).T
|
885 |
+
|
886 |
+
# Get the angle of the xy pairs
|
887 |
+
angles = np.rad2deg(np.arctan(XYpairs[:,0] / XYpairs[:,1]))
|
888 |
+
|
889 |
+
# Discard pairs that have an angle that is out of range
|
890 |
+
min_angle = beam_width_data['beam_left'].min()
|
891 |
+
max_angle = beam_width_data['beam_right'].max()
|
892 |
+
valid_pairs = (angles > min_angle) & (angles < max_angle)
|
893 |
+
angles = angles[valid_pairs]
|
894 |
+
XYpairs = XYpairs[valid_pairs]
|
895 |
+
IJpairs = IJpairs[valid_pairs]
|
896 |
+
|
897 |
+
# Get the distance of the xy pairs
|
898 |
+
hyp = XYpairs[:,1] / np.cos(np.arctan(XYpairs[:,0] / XYpairs[:,1]))
|
899 |
+
|
900 |
+
# Take into account the window start
|
901 |
+
hyp -= frame.WinStart
|
902 |
+
|
903 |
+
# Sample length
|
904 |
+
bin_length = frame.sampleperiod * 0.000001 * frame.soundspeed / 2.
|
905 |
+
|
906 |
+
# Convert to bins
|
907 |
+
bin_nums = (hyp / bin_length).astype(int)
|
908 |
+
|
909 |
+
# Discard pairs that have a distance that is out of range
|
910 |
+
valid_pairs = (bin_nums >= 0) & (bin_nums < frame.samplesperbeam)
|
911 |
+
bin_nums = bin_nums[valid_pairs]
|
912 |
+
angles = angles[valid_pairs]
|
913 |
+
XYpairs = XYpairs[valid_pairs]
|
914 |
+
IJpairs = IJpairs[valid_pairs]
|
915 |
+
|
916 |
+
|
917 |
+
beam_edges = beam_width_data['beam_left'].to_numpy()
|
918 |
+
beam_nums = np.digitize(angles, beam_edges) - 1
|
919 |
+
|
920 |
+
# For each valid x,y pair compute which beam it falls into
|
921 |
+
write_to = [] # (i, j) of image
|
922 |
+
read_from = [] # (bin_num, beam_num) of frame data
|
923 |
+
for index in range(XYpairs.shape[0]):
|
924 |
+
|
925 |
+
bin_num = bin_nums[index]
|
926 |
+
beam_num = beam_nums[index]
|
927 |
+
|
928 |
+
write_to.append(IJpairs[index])
|
929 |
+
read_from.append((bin_num, beam_num))
|
930 |
+
|
931 |
+
read_from = np.array(read_from)
|
932 |
+
read_from_rows = np.array(read_from[:,0])
|
933 |
+
read_from_cols = np.array(read_from[:,1])
|
934 |
+
|
935 |
+
write_to = np.array(write_to)
|
936 |
+
write_to_rows = np.array(write_to[:,0])
|
937 |
+
write_to_cols = np.array(write_to[:,1])
|
938 |
+
|
939 |
+
return read_from_rows, read_from_cols, write_to_rows, write_to_cols
|
940 |
+
|
941 |
+
|
942 |
+
def make_video(data,
|
943 |
+
xdim, ydim, sample_read_rows, sample_read_cols, image_write_rows, image_write_cols,
|
944 |
+
directory, filename, fps = 24.0, start_frame = 1, end_frame = None, timestamp = False, fontsize = 30, ts_pos = (0,0), save_raw = False):
|
945 |
+
"""Output video using the ffmpeg pipeline. The current implementation
|
946 |
+
outputs compresses png files and outputs a mp4.
|
947 |
+
|
948 |
+
Parameters
|
949 |
+
-----------
|
950 |
+
data : (Str) ARIS data structure returned via pyARIS.DataImport()
|
951 |
+
filename : (Str) output filename. Must include file extension (i.e. 'video.mp4')
|
952 |
+
fps : (Float) Output video frame rate (frames per second). Default = 24 fps
|
953 |
+
start_frame, end_frame : (Int) Range of frames included in the output video
|
954 |
+
timestamp : (Bool) Add the timestamp from the sonar to the video frames
|
955 |
+
fontsize : (Int) Size of timestamp font
|
956 |
+
ts_pos : (Tuple) (x,y) location of the timestamp
|
957 |
+
|
958 |
+
Returns
|
959 |
+
-------
|
960 |
+
Returns a video into the current working directory
|
961 |
+
|
962 |
+
Notes
|
963 |
+
------
|
964 |
+
Currently this function looks for ffmpeg.exe in the current working directory.
|
965 |
+
Must have the '*.mp4' file extension.
|
966 |
+
Uses the tqdm package to display a status bar.
|
967 |
+
|
968 |
+
Example
|
969 |
+
-------
|
970 |
+
>>> pyARIS.VideoExport(data, 'test_video.mp4', fps = 24)
|
971 |
+
|
972 |
+
"""
|
973 |
+
|
974 |
+
#Command to send via the command prompt which specifies the pipe parameters
|
975 |
+
# command = ['ffmpeg',
|
976 |
+
# '-y', # (optional) overwrite output file if it exists
|
977 |
+
# '-f', 'image2pipe',
|
978 |
+
# '-vcodec', 'mjpeg', #'mjpeg',
|
979 |
+
# '-r', '1',
|
980 |
+
# '-r', str(fps), # frames per second
|
981 |
+
# '-i', '-', # The input comes from a pipe
|
982 |
+
# '-an', # Tells FFMPEG not to expect any audio
|
983 |
+
# '-vcodec', 'mpeg4',
|
984 |
+
# '-b:v', '5000k',
|
985 |
+
# directory + filename + "/"+filename+".mp4",
|
986 |
+
# '-hide_banner',
|
987 |
+
# '-loglevel', 'panic']
|
988 |
+
|
989 |
+
# Create directories if they don't exist
|
990 |
+
if not os.path.exists(os.path.join(directory, filename, 'frames/')):
|
991 |
+
os.makedirs(os.path.join(directory, filename, 'frames/'))
|
992 |
+
if save_raw and not os.path.exists(os.path.join(directory, filename, 'frames-raw/')):
|
993 |
+
os.makedirs(os.path.join(directory, filename, 'frames-raw/'))
|
994 |
+
|
995 |
+
if end_frame == None:
|
996 |
+
end_frame = data.FrameCount
|
997 |
+
|
998 |
+
cm = colormap.get_cmap('viridis')
|
999 |
+
|
1000 |
+
for i, frame_offset in enumerate(tqdm.tqdm(range(start_frame, end_frame))):
|
1001 |
+
frame = FrameRead(data, frame_offset)
|
1002 |
+
frame_image = np.zeros([ydim, xdim], dtype=np.uint8)
|
1003 |
+
frame_image[image_write_rows, image_write_cols] = frame.frame_data[sample_read_rows, sample_read_cols]
|
1004 |
+
|
1005 |
+
rgb_im = Image.fromarray(cm(frame_image, bytes=True)).convert('RGB')
|
1006 |
+
rgb_im.save(os.path.join(directory, filename, 'frames/', f'{i}.jpg'), 'JPEG')
|
1007 |
+
|
1008 |
+
if save_raw:
|
1009 |
+
Image.fromarray(np.uint8(frame.frame_data), mode='L').save(os.path.join(directory, filename, 'frames-raw/', f'{i}.jpg'), 'JPEG')
|
lib/fish_eye/sort.py
ADDED
@@ -0,0 +1,238 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
"""
|
2 |
+
SORT: A Simple, Online and Realtime Tracker
|
3 |
+
Copyright (C) 2016-2020 Alex Bewley alex@bewley.ai
|
4 |
+
|
5 |
+
This program is free software: you can redistribute it and/or modify
|
6 |
+
it under the terms of the GNU General Public License as published by
|
7 |
+
the Free Software Foundation, either version 3 of the License, or
|
8 |
+
(at your option) any later version.
|
9 |
+
|
10 |
+
This program is distributed in the hope that it will be useful,
|
11 |
+
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
12 |
+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
13 |
+
GNU General Public License for more details.
|
14 |
+
|
15 |
+
You should have received a copy of the GNU General Public License
|
16 |
+
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
17 |
+
"""
|
18 |
+
from filterpy.kalman import KalmanFilter
|
19 |
+
import numpy as np
|
20 |
+
|
21 |
+
def linear_assignment(cost_matrix):
|
22 |
+
try:
|
23 |
+
import lap
|
24 |
+
_, x, y = lap.lapjv(cost_matrix, extend_cost=True)
|
25 |
+
return np.array([[y[i],i] for i in x if i >= 0]) #
|
26 |
+
except ImportError:
|
27 |
+
from scipy.optimize import linear_sum_assignment
|
28 |
+
x, y = linear_sum_assignment(cost_matrix)
|
29 |
+
return np.array(list(zip(x, y)))
|
30 |
+
|
31 |
+
|
32 |
+
def iou_batch(bb_test, bb_gt):
|
33 |
+
"""
|
34 |
+
From SORT: Computes IOU between two bboxes in the form [l,t,w,h]
|
35 |
+
"""
|
36 |
+
bb_gt = np.expand_dims(bb_gt, 0)
|
37 |
+
bb_test = np.expand_dims(bb_test, 1)
|
38 |
+
|
39 |
+
xx1 = np.maximum(bb_test[..., 0], bb_gt[..., 0])
|
40 |
+
yy1 = np.maximum(bb_test[..., 1], bb_gt[..., 1])
|
41 |
+
xx2 = np.minimum(bb_test[..., 2], bb_gt[..., 2])
|
42 |
+
yy2 = np.minimum(bb_test[..., 3], bb_gt[..., 3])
|
43 |
+
w = np.maximum(0., xx2 - xx1)
|
44 |
+
h = np.maximum(0., yy2 - yy1)
|
45 |
+
wh = w * h
|
46 |
+
o = wh / ((bb_test[..., 2] - bb_test[..., 0]) * (bb_test[..., 3] - bb_test[..., 1])
|
47 |
+
+ (bb_gt[..., 2] - bb_gt[..., 0]) * (bb_gt[..., 3] - bb_gt[..., 1]) - wh)
|
48 |
+
return(o)
|
49 |
+
|
50 |
+
|
51 |
+
def convert_bbox_to_z(bbox):
|
52 |
+
"""
|
53 |
+
Takes a bounding box in the form [x1,y1,x2,y2] and returns z in the form
|
54 |
+
[x,y,s,r] where x,y is the centre of the box and s is the scale/area and r is
|
55 |
+
the aspect ratio
|
56 |
+
"""
|
57 |
+
w = bbox[2] - bbox[0]
|
58 |
+
h = bbox[3] - bbox[1]
|
59 |
+
x = bbox[0] + w/2.
|
60 |
+
y = bbox[1] + h/2.
|
61 |
+
s = w * h #scale is just area
|
62 |
+
r = w / float(h)
|
63 |
+
return np.array([x, y, s, r]).reshape((4, 1))
|
64 |
+
|
65 |
+
|
66 |
+
def convert_x_to_bbox(x,score=None):
|
67 |
+
"""
|
68 |
+
Takes a bounding box in the centre form [x,y,s,r] and returns it in the form
|
69 |
+
[x1,y1,x2,y2] where x1,y1 is the top left and x2,y2 is the bottom right
|
70 |
+
"""
|
71 |
+
w = np.sqrt(x[2] * x[3])
|
72 |
+
h = x[2] / w
|
73 |
+
if(score==None):
|
74 |
+
return np.array([x[0]-w/2.,x[1]-h/2.,x[0]+w/2.,x[1]+h/2.]).reshape((1,4))
|
75 |
+
else:
|
76 |
+
return np.array([x[0]-w/2.,x[1]-h/2.,x[0]+w/2.,x[1]+h/2.,score]).reshape((1,5))
|
77 |
+
|
78 |
+
|
79 |
+
class KalmanBoxTracker(object):
|
80 |
+
"""
|
81 |
+
This class represents the internal state of individual tracked objects observed as bbox.
|
82 |
+
"""
|
83 |
+
count = 0
|
84 |
+
def __init__(self,bbox):
|
85 |
+
"""
|
86 |
+
Initialises a tracker using initial bounding box.
|
87 |
+
"""
|
88 |
+
#define constant velocity model
|
89 |
+
self.kf = KalmanFilter(dim_x=7, dim_z=4)
|
90 |
+
self.kf.F = np.array([[1,0,0,0,1,0,0],[0,1,0,0,0,1,0],[0,0,1,0,0,0,1],[0,0,0,1,0,0,0], [0,0,0,0,1,0,0],[0,0,0,0,0,1,0],[0,0,0,0,0,0,1]])
|
91 |
+
self.kf.H = np.array([[1,0,0,0,0,0,0],[0,1,0,0,0,0,0],[0,0,1,0,0,0,0],[0,0,0,1,0,0,0]])
|
92 |
+
|
93 |
+
self.kf.R[2:,2:] *= 10.
|
94 |
+
self.kf.P[4:,4:] *= 1000. #give high uncertainty to the unobservable initial velocities
|
95 |
+
self.kf.P *= 10.
|
96 |
+
self.kf.Q[-1,-1] *= 0.01
|
97 |
+
self.kf.Q[4:,4:] *= 0.01
|
98 |
+
|
99 |
+
self.kf.x[:4] = convert_bbox_to_z(bbox)
|
100 |
+
self.time_since_update = 0
|
101 |
+
self.id = KalmanBoxTracker.count
|
102 |
+
KalmanBoxTracker.count += 1
|
103 |
+
self.history = []
|
104 |
+
self.hits = 0
|
105 |
+
self.hit_streak = 0
|
106 |
+
self.age = 0
|
107 |
+
|
108 |
+
def update(self,bbox):
|
109 |
+
"""
|
110 |
+
Updates the state vector with observed bbox.
|
111 |
+
"""
|
112 |
+
self.time_since_update = 0
|
113 |
+
self.history = []
|
114 |
+
self.hits += 1
|
115 |
+
self.hit_streak += 1
|
116 |
+
self.kf.update(convert_bbox_to_z(bbox))
|
117 |
+
|
118 |
+
def predict(self):
|
119 |
+
"""
|
120 |
+
Advances the state vector and returns the predicted bounding box estimate.
|
121 |
+
"""
|
122 |
+
if((self.kf.x[6]+self.kf.x[2])<=0):
|
123 |
+
self.kf.x[6] *= 0.0
|
124 |
+
self.kf.predict()
|
125 |
+
self.age += 1
|
126 |
+
if(self.time_since_update>0):
|
127 |
+
self.hit_streak = 0
|
128 |
+
self.time_since_update += 1
|
129 |
+
self.history.append(convert_x_to_bbox(self.kf.x))
|
130 |
+
return self.history[-1]
|
131 |
+
|
132 |
+
def get_state(self):
|
133 |
+
"""
|
134 |
+
Returns the current bounding box estimate.
|
135 |
+
"""
|
136 |
+
return convert_x_to_bbox(self.kf.x)
|
137 |
+
|
138 |
+
|
139 |
+
def associate_detections_to_trackers(detections,trackers,iou_threshold = 0.3):
|
140 |
+
"""
|
141 |
+
Assigns detections to tracked object (both represented as bounding boxes)
|
142 |
+
|
143 |
+
Returns 3 lists of matches, unmatched_detections and unmatched_trackers
|
144 |
+
"""
|
145 |
+
if(len(trackers)==0):
|
146 |
+
return np.empty((0,2),dtype=int), np.arange(len(detections)), np.empty((0,5),dtype=int)
|
147 |
+
|
148 |
+
iou_matrix = iou_batch(detections, trackers)
|
149 |
+
|
150 |
+
if min(iou_matrix.shape) > 0:
|
151 |
+
a = (iou_matrix > iou_threshold).astype(np.int32)
|
152 |
+
if a.sum(1).max() == 1 and a.sum(0).max() == 1:
|
153 |
+
matched_indices = np.stack(np.where(a), axis=1)
|
154 |
+
else:
|
155 |
+
matched_indices = linear_assignment(-iou_matrix)
|
156 |
+
else:
|
157 |
+
matched_indices = np.empty(shape=(0,2))
|
158 |
+
|
159 |
+
unmatched_detections = []
|
160 |
+
for d, _ in enumerate(detections):
|
161 |
+
if(d not in matched_indices[:,0]):
|
162 |
+
unmatched_detections.append(d)
|
163 |
+
unmatched_trackers = []
|
164 |
+
for t, _ in enumerate(trackers):
|
165 |
+
if(t not in matched_indices[:,1]):
|
166 |
+
unmatched_trackers.append(t)
|
167 |
+
|
168 |
+
#filter out matched with low IOU
|
169 |
+
matches = []
|
170 |
+
for m in matched_indices:
|
171 |
+
if(iou_matrix[m[0], m[1]]<iou_threshold):
|
172 |
+
unmatched_detections.append(m[0])
|
173 |
+
unmatched_trackers.append(m[1])
|
174 |
+
else:
|
175 |
+
matches.append(m.reshape(1,2))
|
176 |
+
if(len(matches)==0):
|
177 |
+
matches = np.empty((0,2),dtype=int)
|
178 |
+
else:
|
179 |
+
matches = np.concatenate(matches,axis=0)
|
180 |
+
|
181 |
+
return matches, np.array(unmatched_detections), np.array(unmatched_trackers)
|
182 |
+
|
183 |
+
|
184 |
+
class Sort(object):
|
185 |
+
def __init__(self, max_age=1, min_hits=3, iou_threshold=0.3):
|
186 |
+
"""
|
187 |
+
Sets key parameters for SORT
|
188 |
+
"""
|
189 |
+
self.max_age = max_age
|
190 |
+
self.min_hits = min_hits
|
191 |
+
self.iou_threshold = iou_threshold
|
192 |
+
self.trackers = []
|
193 |
+
self.frame_count = 0
|
194 |
+
|
195 |
+
def update(self, dets=np.empty((0, 5))):
|
196 |
+
"""
|
197 |
+
Params:
|
198 |
+
dets - a numpy array of detections in the format [[x1,y1,x2,y2,score],[x1,y1,x2,y2,score],...]
|
199 |
+
Requires: this method must be called once for each frame even with empty detections (use np.empty((0, 5)) for frames without detections).
|
200 |
+
Returns the a similar array, where the last column is the object ID.
|
201 |
+
|
202 |
+
NOTE: The number of objects returned may differ from the number of detections provided.
|
203 |
+
"""
|
204 |
+
self.frame_count += 1
|
205 |
+
# get predicted locations from existing trackers.
|
206 |
+
trks = np.zeros((len(self.trackers), 5))
|
207 |
+
to_del = []
|
208 |
+
ret = []
|
209 |
+
for t, trk in enumerate(trks):
|
210 |
+
pos = self.trackers[t].predict()[0]
|
211 |
+
trk[:] = [pos[0], pos[1], pos[2], pos[3], 0]
|
212 |
+
if np.any(np.isnan(pos)):
|
213 |
+
to_del.append(t)
|
214 |
+
trks = np.ma.compress_rows(np.ma.masked_invalid(trks))
|
215 |
+
for t in reversed(to_del):
|
216 |
+
self.trackers.pop(t)
|
217 |
+
matched, unmatched_dets, unmatched_trks = associate_detections_to_trackers(dets,trks, self.iou_threshold)
|
218 |
+
|
219 |
+
# update matched trackers with assigned detections
|
220 |
+
for m in matched:
|
221 |
+
self.trackers[m[1]].update(dets[m[0], :])
|
222 |
+
|
223 |
+
# create and initialise new trackers for unmatched detections
|
224 |
+
for i in unmatched_dets:
|
225 |
+
trk = KalmanBoxTracker(dets[i,:])
|
226 |
+
self.trackers.append(trk)
|
227 |
+
i = len(self.trackers)
|
228 |
+
for trk in reversed(self.trackers):
|
229 |
+
d = trk.get_state()[0]
|
230 |
+
if (trk.time_since_update < 1) and (trk.hit_streak >= self.min_hits or self.frame_count <= self.min_hits):
|
231 |
+
ret.append(np.concatenate((d,[trk.id+1])).reshape(1,-1)) # +1 as MOT benchmark requires positive
|
232 |
+
i -= 1
|
233 |
+
# remove dead tracklet
|
234 |
+
if(trk.time_since_update > self.max_age):
|
235 |
+
self.trackers.pop(i)
|
236 |
+
if(len(ret)>0):
|
237 |
+
return np.concatenate(ret)
|
238 |
+
return np.empty((0,5))
|
lib/fish_eye/tracker.py
ADDED
@@ -0,0 +1,162 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
from collections import Counter
|
2 |
+
from colorsys import hls_to_rgb
|
3 |
+
from copy import deepcopy
|
4 |
+
import json
|
5 |
+
import numpy as np
|
6 |
+
|
7 |
+
from fish_length import Fish_Length
|
8 |
+
from sort import Sort
|
9 |
+
|
10 |
+
class Tracker:
|
11 |
+
def __init__(self, clip_info, algorithm=Sort, args={'max_age':1, 'min_hits':0, 'iou_threshold':0.05}, min_hits=3):
|
12 |
+
self.algorithm = algorithm(**args)
|
13 |
+
self.fish_ids = Counter()
|
14 |
+
self.min_hits = min_hits
|
15 |
+
self.json_data = deepcopy(clip_info)
|
16 |
+
self.frame_id = self.json_data['start_frame']
|
17 |
+
self.json_data['frames'] = []
|
18 |
+
|
19 |
+
# Boxes should be given in normalized [x1,y1,x2,y2,c]
|
20 |
+
def update(self, dets=np.empty((0, 5))):
|
21 |
+
new_frame_entries = []
|
22 |
+
for track in self.algorithm.update(dets):
|
23 |
+
self.fish_ids[int(track[4])] += 1
|
24 |
+
new_frame_entries.append({
|
25 |
+
'fish_id': int(track[4]),
|
26 |
+
'bbox': list(track[:4]),
|
27 |
+
'visible': 1,
|
28 |
+
'human_labeled': 0
|
29 |
+
})
|
30 |
+
new_frame_entries = sorted(new_frame_entries, key=lambda k: k['fish_id'])
|
31 |
+
|
32 |
+
self.json_data['frames'].append(
|
33 |
+
{
|
34 |
+
'frame_num': self.frame_id,
|
35 |
+
'fish': new_frame_entries
|
36 |
+
})
|
37 |
+
self.frame_id += 1
|
38 |
+
|
39 |
+
def finalize(self, output_path=None, min_length=-1.0): # vert_margin=0.0
|
40 |
+
json_data = deepcopy(self.json_data)
|
41 |
+
|
42 |
+
# map (valid) fish IDs to 0, 1, 2, ...
|
43 |
+
fish_id_map = {}
|
44 |
+
for fish_id, count in self.fish_ids.items():
|
45 |
+
if count >= self.min_hits:
|
46 |
+
fish_id_map[fish_id] = len(fish_id_map)
|
47 |
+
|
48 |
+
# separate frame boxes into tracks, keyed by mapped IDs
|
49 |
+
# each track is a list of tuples ( bbox, frame_num )
|
50 |
+
tracks = { v : [] for _, v in fish_id_map.items() }
|
51 |
+
for frame in json_data['frames']:
|
52 |
+
for bbox in frame['fish']:
|
53 |
+
# check if valid
|
54 |
+
if bbox['fish_id'] in fish_id_map.keys():
|
55 |
+
track_id = fish_id_map[bbox['fish_id']]
|
56 |
+
tracks[track_id].append((bbox['bbox'], frame['frame_num']))
|
57 |
+
|
58 |
+
# map IDs and keep frame['fish'] sorted by ID
|
59 |
+
for i, frame in enumerate(json_data['frames']):
|
60 |
+
new_frame_entries = []
|
61 |
+
for frame_entry in frame['fish']:
|
62 |
+
if frame_entry['fish_id'] in fish_id_map:
|
63 |
+
frame_entry['fish_id'] = fish_id_map[frame_entry['fish_id']]
|
64 |
+
new_frame_entries.append(frame_entry)
|
65 |
+
frame['fish'] = sorted(new_frame_entries, key=lambda k: k['fish_id'])
|
66 |
+
|
67 |
+
# create summary 'fish' entry for json data
|
68 |
+
json_data['fish'] = []
|
69 |
+
for track_id, boxes in tracks.items():
|
70 |
+
fish_entry = {}
|
71 |
+
fish_entry['id'] = track_id
|
72 |
+
fish_entry['length'] = -1
|
73 |
+
|
74 |
+
# top = False
|
75 |
+
# bottom = False
|
76 |
+
# for frame in json_data['frames']:
|
77 |
+
# for frame_entry in frame['fish']:
|
78 |
+
# if frame_entry['fish_id'] == track_id:
|
79 |
+
# if frame_entry['bbox'][3] > vert_margin:
|
80 |
+
# top = True
|
81 |
+
# if frame_entry['bbox'][1] < 1 - vert_margin:
|
82 |
+
# bottom = True
|
83 |
+
# break
|
84 |
+
|
85 |
+
# if not top or not bottom:
|
86 |
+
# continue
|
87 |
+
|
88 |
+
start_bbox = boxes[0][0]
|
89 |
+
end_bbox = boxes[-1][0]
|
90 |
+
fish_entry['direction'] = Tracker.get_direction(start_bbox, end_bbox)
|
91 |
+
|
92 |
+
fish_entry['start_frame_index'] = boxes[0][1]
|
93 |
+
fish_entry['end_frame_index'] = boxes[-1][1]
|
94 |
+
fish_entry['color'] = Tracker.selectColor(track_id)
|
95 |
+
|
96 |
+
json_data['fish'].append(fish_entry)
|
97 |
+
|
98 |
+
# filter 'fish' field by fish length
|
99 |
+
json_data = Fish_Length.add_lengths(json_data)
|
100 |
+
invalid_ids = []
|
101 |
+
if min_length != -1.0:
|
102 |
+
new_fish = []
|
103 |
+
for fish in json_data['fish']:
|
104 |
+
if fish['length'] > min_length:
|
105 |
+
new_fish.append(fish)
|
106 |
+
else:
|
107 |
+
invalid_ids.append(fish['id'])
|
108 |
+
json_data['fish'] = new_fish
|
109 |
+
|
110 |
+
# filter 'frames' field by fish length
|
111 |
+
if len(invalid_ids):
|
112 |
+
for frame in json_data['frames']:
|
113 |
+
new_fish = []
|
114 |
+
for fish in frame['fish']:
|
115 |
+
if fish['fish_id'] not in invalid_ids:
|
116 |
+
new_fish.append(fish)
|
117 |
+
frame['fish'] = new_fish
|
118 |
+
|
119 |
+
if output_path is not None:
|
120 |
+
with open(output_path,'w') as output:
|
121 |
+
json.dump(json_data, output, indent=2)
|
122 |
+
|
123 |
+
return json_data
|
124 |
+
|
125 |
+
def state(self, output_path=None):
|
126 |
+
json_data = deepcopy(self.json_data)
|
127 |
+
|
128 |
+
if output_path is not None:
|
129 |
+
with open(output_path,'w') as output:
|
130 |
+
json.dump(json_data, output, indent=2)
|
131 |
+
|
132 |
+
return json_data
|
133 |
+
|
134 |
+
@staticmethod
|
135 |
+
def selectColor(number):
|
136 |
+
hue = ((number * 137.508 + 60) % 360) / 360
|
137 |
+
return '#{0:02x}{1:02x}{2:02x}'.format(*(int(n * 255) for n in hls_to_rgb(hue, 0.5, 0.75)))
|
138 |
+
|
139 |
+
@staticmethod
|
140 |
+
def get_direction(start_bbox, end_bbox):
|
141 |
+
start_center = (start_bbox[2] + start_bbox[0])/2
|
142 |
+
end_center = (end_bbox[2] + end_bbox[0])/2
|
143 |
+
if start_center < 0.5 and end_center >= 0.5:
|
144 |
+
return 'right'
|
145 |
+
elif start_center >= 0.5 and end_center < 0.5:
|
146 |
+
return 'left'
|
147 |
+
else:
|
148 |
+
return 'none'
|
149 |
+
|
150 |
+
@staticmethod
|
151 |
+
def count_dirs(json_data):
|
152 |
+
right = 0
|
153 |
+
left = 0
|
154 |
+
none = 0
|
155 |
+
for fish_entry in json_data['fish']:
|
156 |
+
if fish_entry['direction'] == 'right':
|
157 |
+
right += 1
|
158 |
+
elif fish_entry['direction'] == 'left':
|
159 |
+
left += 1
|
160 |
+
else:
|
161 |
+
none += 1
|
162 |
+
return (right, left, none)
|
models/download_model.sh
ADDED
@@ -0,0 +1 @@
|
|
|
|
|
1 |
+
aws s3 cp s3://fishcounting/Models/2021-10-yolov5/v5m_896_300best.pt .
|
models/dump.rdb
ADDED
Binary file (3.51 kB). View file
|
|
models/v5m_896_300best.pt
ADDED
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
1 |
+
version https://git-lfs.github.com/spec/v1
|
2 |
+
oid sha256:14e26883b08b5f07854c67d4ee17b5521eb98f7e0b88d25716542a5cf6dde10f
|
3 |
+
size 167806440
|
scripts/infer.py
ADDED
@@ -0,0 +1,18 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import project_path
|
2 |
+
import argparse
|
3 |
+
from predict import predict_task
|
4 |
+
from datetime import datetime
|
5 |
+
|
6 |
+
|
7 |
+
def main(args):
|
8 |
+
predict_task(args.aris, weights=args.weights)
|
9 |
+
|
10 |
+
def argument_parser():
|
11 |
+
parser = argparse.ArgumentParser()
|
12 |
+
parser.add_argument("--aris", required=True, help="Path to ARIS file. Required.")
|
13 |
+
parser.add_argument("--weights", default='../models/v5m_896_300best.pt', help="Path to saved YOLOv5 weights. Default: ../models/v5m_896_300best.pt")
|
14 |
+
return parser
|
15 |
+
|
16 |
+
if __name__ == "__main__":
|
17 |
+
args = argument_parser().parse_args()
|
18 |
+
main(args)
|
scripts/project_path.py
ADDED
@@ -0,0 +1,8 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import sys
|
2 |
+
import os
|
3 |
+
|
4 |
+
current_dir = os.path.dirname(os.path.realpath(__file__))
|
5 |
+
pardir = os.path.dirname(current_dir)
|
6 |
+
for d in [pardir, current_dir, os.path.join(pardir, "lib/fish_eye/"), os.path.join(pardir, "lib/"), os.path.join(pardir, "lib/yolov5/")]:
|
7 |
+
if d not in sys.path:
|
8 |
+
sys.path.append(d)
|
static/css/style.css
ADDED
@@ -0,0 +1,138 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
:root {
|
2 |
+
--fisheye_color: #58df94;
|
3 |
+
--caltech_orange: #FF6C0C;
|
4 |
+
}
|
5 |
+
|
6 |
+
html,
|
7 |
+
body {
|
8 |
+
height: 100%;
|
9 |
+
}
|
10 |
+
|
11 |
+
body {
|
12 |
+
/* padding-top: 40px; */
|
13 |
+
padding-bottom: 40px;
|
14 |
+
background-color: #f5f5f5;
|
15 |
+
}
|
16 |
+
|
17 |
+
#content {
|
18 |
+
padding-top: 40px;
|
19 |
+
}
|
20 |
+
|
21 |
+
#home_banner * {
|
22 |
+
transition: all .2s ease-in-out;
|
23 |
+
}
|
24 |
+
#home_banner:hover > svg {
|
25 |
+
transform: scale(1.3) rotate(180deg);
|
26 |
+
}
|
27 |
+
#home_banner:hover #banner_svg_eye {
|
28 |
+
fill: var(--fisheye_color)
|
29 |
+
}
|
30 |
+
#home_banner:hover #banner_svg_slit {
|
31 |
+
fill: #231F20;
|
32 |
+
}
|
33 |
+
#home_banner:hover #banner_svg_pupil {
|
34 |
+
fill: #FFFFFF;
|
35 |
+
}
|
36 |
+
#home_banner > span {
|
37 |
+
color: black;
|
38 |
+
font-style: italic;
|
39 |
+
}
|
40 |
+
#home_banner:hover > span {
|
41 |
+
transform: translateX(6px);
|
42 |
+
}
|
43 |
+
#home_banner:hover #banner_text_caltech {
|
44 |
+
/*color: var(--caltech_orange);*/
|
45 |
+
}
|
46 |
+
#home_banner:hover #banner_text_fisheye {
|
47 |
+
/*color: var(--fisheye_color);*/
|
48 |
+
}
|
49 |
+
|
50 |
+
.mode_screen {
|
51 |
+
width: calc(100% - 280px);
|
52 |
+
display: none;
|
53 |
+
max-height: 100vh;
|
54 |
+
overflow-y: scroll;
|
55 |
+
position: absolute;
|
56 |
+
top: 0px;
|
57 |
+
left: 280px;
|
58 |
+
}
|
59 |
+
.page_image {
|
60 |
+
max-width: min(95%, 600px);
|
61 |
+
}
|
62 |
+
.result-download {
|
63 |
+
color:gray;
|
64 |
+
font-size: 20px;
|
65 |
+
}
|
66 |
+
.result-download:hover {
|
67 |
+
color:black;
|
68 |
+
}
|
69 |
+
.result-analysis {
|
70 |
+
width: 25px;
|
71 |
+
margin-top: -5px;
|
72 |
+
}
|
73 |
+
.result-analysis path {
|
74 |
+
fill:gray;
|
75 |
+
}
|
76 |
+
.result-analysis:hover path {
|
77 |
+
fill:black;
|
78 |
+
}
|
79 |
+
|
80 |
+
|
81 |
+
#analysis_table {
|
82 |
+
width: 50%;
|
83 |
+
margin-left: 25%
|
84 |
+
}
|
85 |
+
#analysis_table th {
|
86 |
+
padding: 2px;
|
87 |
+
width: 200px;
|
88 |
+
text-align: right;
|
89 |
+
padding-right: 10px;
|
90 |
+
}
|
91 |
+
#analysis_table td {
|
92 |
+
padding: 2px;
|
93 |
+
width: 200px;
|
94 |
+
text-align: left;
|
95 |
+
padding-left: 10px;
|
96 |
+
}
|
97 |
+
|
98 |
+
#sidebar {
|
99 |
+
width: 280px;
|
100 |
+
height: 100vh;
|
101 |
+
border: lightgray;
|
102 |
+
border-right-style: solid;
|
103 |
+
}
|
104 |
+
|
105 |
+
|
106 |
+
|
107 |
+
|
108 |
+
|
109 |
+
|
110 |
+
|
111 |
+
|
112 |
+
|
113 |
+
|
114 |
+
|
115 |
+
analysis-fishstick {
|
116 |
+
width: 80%;
|
117 |
+
height: 40px;
|
118 |
+
display: block;
|
119 |
+
margin-left: 10%;
|
120 |
+
position: relative;
|
121 |
+
}
|
122 |
+
.fishstick_line {
|
123 |
+
position: absolute;
|
124 |
+
width: 100%;
|
125 |
+
height: 8px;
|
126 |
+
top: 50%;
|
127 |
+
translate: 0 -50%;
|
128 |
+
background-color: blue;
|
129 |
+
}
|
130 |
+
.fishstick_token {
|
131 |
+
position: absolute;
|
132 |
+
width: 20px;
|
133 |
+
height: 20px;
|
134 |
+
top: 50%;
|
135 |
+
border-radius: 50%;
|
136 |
+
background-color: orange;
|
137 |
+
translate: -50% -50%;
|
138 |
+
}
|
static/images/Github:fisheye_flask.jpeg
ADDED
![]() |
static/images/fish.png
ADDED
![]() |
static/images/fisheye.png
ADDED
![]() |
static/images/salmon_640.jpeg
ADDED
![]() |
static/images/some images from pixabay.com.txt
ADDED
File without changes
|
static/svg/book-open-svgrepo-com.svg
ADDED
|
static/svg/chemistry-flask-svgrepo-com.svg
ADDED
|
static/svg/done-1477-svgrepo-com.svg
ADDED
|
static/svg/eye-svgrepo-com.svg
ADDED
|
static/svg/house-svgrepo-com.svg
ADDED
|
static/svg/svgs from svgrepo.com.txt
ADDED
File without changes
|
static/svg/upload-svgrepo-com.svg
ADDED
|