tokeye 0.9.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- tokeye-0.9.0/PKG-INFO +138 -0
- tokeye-0.9.0/README.md +106 -0
- tokeye-0.9.0/pyproject.toml +91 -0
- tokeye-0.9.0/src/tokeye/__init__.py +0 -0
- tokeye-0.9.0/src/tokeye/analysis/__init__.py +4 -0
- tokeye-0.9.0/src/tokeye/analysis/batch_analysis.py +240 -0
- tokeye-0.9.0/src/tokeye/app/__init__.py +0 -0
- tokeye-0.9.0/src/tokeye/app/__main__.py +84 -0
- tokeye-0.9.0/src/tokeye/app/analyze/__init__.py +0 -0
- tokeye-0.9.0/src/tokeye/app/analyze/analyze.py +296 -0
- tokeye-0.9.0/src/tokeye/app/analyze/load.py +185 -0
- tokeye-0.9.0/src/tokeye/app/analyze/transforms.py +37 -0
- tokeye-0.9.0/src/tokeye/app/analyze/visualize.py +194 -0
- tokeye-0.9.0/src/tokeye/app/processing/QUICKSTART.md +310 -0
- tokeye-0.9.0/src/tokeye/app/processing/README.md +459 -0
- tokeye-0.9.0/src/tokeye/app/processing/__init__.py +0 -0
- tokeye-0.9.0/src/tokeye/app/processing/inference.py +66 -0
- tokeye-0.9.0/src/tokeye/app/processing/postprocess.py +386 -0
- tokeye-0.9.0/src/tokeye/app/processing/tiling.py +292 -0
- tokeye-0.9.0/src/tokeye/app/tabs/__init__.py +0 -0
- tokeye-0.9.0/src/tokeye/app/tabs/annotate.py +553 -0
- tokeye-0.9.0/src/tokeye/app/tabs/utilities.py +536 -0
- tokeye-0.9.0/src/tokeye/app/utils/__init__.py +0 -0
- tokeye-0.9.0/src/tokeye/app/utils/analyze.py +391 -0
- tokeye-0.9.0/src/tokeye/app/utils/theme.py +151 -0
- tokeye-0.9.0/src/tokeye/extra/D3D/__init__.py +0 -0
- tokeye-0.9.0/src/tokeye/extra/D3D/ece_rolloff.py +66 -0
- tokeye-0.9.0/src/tokeye/extra/eval/D3D/__init__.py +0 -0
- tokeye-0.9.0/src/tokeye/extra/eval/D3D/methods_pcg.py +70 -0
- tokeye-0.9.0/src/tokeye/extra/eval/silbidopy/__init__.py +0 -0
- tokeye-0.9.0/src/tokeye/extra/eval/silbidopy/data.py +674 -0
- tokeye-0.9.0/src/tokeye/extra/eval/silbidopy/eval.py +59 -0
- tokeye-0.9.0/src/tokeye/extra/eval/silbidopy/readBinaries.py +277 -0
- tokeye-0.9.0/src/tokeye/extra/eval/silbidopy/render.py +403 -0
- tokeye-0.9.0/src/tokeye/extra/eval/silbidopy/sigproc.py +53 -0
- tokeye-0.9.0/src/tokeye/extra/eval/silbidopy/writeBinaries.py +275 -0
- tokeye-0.9.0/src/tokeye/models/__init__.py +0 -0
- tokeye-0.9.0/src/tokeye/models/ae_tf_boxrcnn/config_ae_tf_boxrcnn.py +0 -0
- tokeye-0.9.0/src/tokeye/models/ae_tf_boxrcnn/model_ae_tf_boxrcnn.py +24 -0
- tokeye-0.9.0/src/tokeye/models/ae_tf_maskrcnn/config_ae_tf_maskrcnn.py +26 -0
- tokeye-0.9.0/src/tokeye/models/ae_tf_maskrcnn/model_ae_tf_maskrcnn.py +73 -0
- tokeye-0.9.0/src/tokeye/models/big_tf_unet/config_big_tf_unet.py +18 -0
- tokeye-0.9.0/src/tokeye/models/big_tf_unet/model_big_tf_unet.py +206 -0
- tokeye-0.9.0/src/tokeye/models/modules/__init__.py +9 -0
- tokeye-0.9.0/src/tokeye/models/modules/bsn.py +441 -0
- tokeye-0.9.0/src/tokeye/models/modules/nn.py +138 -0
- tokeye-0.9.0/src/tokeye/models/modules/unet.py +137 -0
- tokeye-0.9.0/src/tokeye/training/big_tf_unet/README.md +3 -0
- tokeye-0.9.0/src/tokeye/training/big_tf_unet/__init__.py +0 -0
- tokeye-0.9.0/src/tokeye/training/big_tf_unet/config/paths.yaml +0 -0
- tokeye-0.9.0/src/tokeye/training/big_tf_unet/step_0a_extract_faithdata.py +77 -0
- tokeye-0.9.0/src/tokeye/training/big_tf_unet/step_0b_filter_faithdata.py +71 -0
- tokeye-0.9.0/src/tokeye/training/big_tf_unet/step_0c_convert_faithdata.py +104 -0
- tokeye-0.9.0/src/tokeye/training/big_tf_unet/step_1a_make_timeseries.py +72 -0
- tokeye-0.9.0/src/tokeye/training/big_tf_unet/step_2a_make_spectrogram.py +89 -0
- tokeye-0.9.0/src/tokeye/training/big_tf_unet/step_2b_filter_spectrogram.py +147 -0
- tokeye-0.9.0/src/tokeye/training/big_tf_unet/step_3a_correlation_analysis.py +402 -0
- tokeye-0.9.0/src/tokeye/training/big_tf_unet/step_3b_extract_correlation.py +51 -0
- tokeye-0.9.0/src/tokeye/training/big_tf_unet/step_3c_coherence.py +69 -0
- tokeye-0.9.0/src/tokeye/training/big_tf_unet/step_4a_threshold.py +333 -0
- tokeye-0.9.0/src/tokeye/training/big_tf_unet/step_5a_combine_spectrogram.py +190 -0
- tokeye-0.9.0/src/tokeye/training/big_tf_unet/step_5b_manual_fix_spectrogram.py +1046 -0
- tokeye-0.9.0/src/tokeye/training/big_tf_unet/step_6a_convert_tif.py +473 -0
- tokeye-0.9.0/src/tokeye/training/big_tf_unet/step_6b_refiner.py +726 -0
- tokeye-0.9.0/src/tokeye/training/big_tf_unet/step_6c_convert_predictions.py +228 -0
- tokeye-0.9.0/src/tokeye/training/big_tf_unet/step_6d_final.py +434 -0
- tokeye-0.9.0/src/tokeye/training/big_tf_unet/step_7a_make_multiscale.py +200 -0
- tokeye-0.9.0/src/tokeye/training/big_tf_unet/step_7b_train_multiscale.py +557 -0
- tokeye-0.9.0/src/tokeye/training/big_tf_unet/utils/augmentations.py +528 -0
- tokeye-0.9.0/src/tokeye/training/big_tf_unet/utils/configuration.py +54 -0
- tokeye-0.9.0/src/tokeye/training/big_tf_unet/utils/convert_for_software.py +193 -0
- tokeye-0.9.0/src/tokeye/training/big_tf_unet/utils/generate_images.py +256 -0
- tokeye-0.9.0/src/tokeye/training/big_tf_unet/utils/hdf5_io.py +232 -0
- tokeye-0.9.0/src/tokeye/training/big_tf_unet/utils/inference_export.py +307 -0
- tokeye-0.9.0/src/tokeye/training/big_tf_unet/utils/losses.py +511 -0
- tokeye-0.9.0/src/tokeye/training/big_tf_unet/utils/measurements.py +69 -0
- tokeye-0.9.0/src/tokeye/training/big_tf_unet/utils/parmap.py +213 -0
- tokeye-0.9.0/src/tokeye/training/big_tf_unet/utils/visualize.py +56 -0
tokeye-0.9.0/PKG-INFO
ADDED
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
Metadata-Version: 2.3
|
|
2
|
+
Name: tokeye
|
|
3
|
+
Version: 0.9.0
|
|
4
|
+
Summary: Add your description here
|
|
5
|
+
Requires-Dist: gradio
|
|
6
|
+
Requires-Dist: numpy
|
|
7
|
+
Requires-Dist: scipy
|
|
8
|
+
Requires-Dist: torch
|
|
9
|
+
Requires-Dist: torchcodec
|
|
10
|
+
Requires-Dist: torchvision
|
|
11
|
+
Requires-Dist: torchinfo
|
|
12
|
+
Requires-Dist: omegaconf
|
|
13
|
+
Requires-Dist: tables
|
|
14
|
+
Requires-Dist: pydantic
|
|
15
|
+
Requires-Dist: mypy ; extra == 'dev'
|
|
16
|
+
Requires-Dist: pytest ; extra == 'dev'
|
|
17
|
+
Requires-Dist: pytest-cov ; extra == 'dev'
|
|
18
|
+
Requires-Dist: pytest-xdist ; extra == 'dev'
|
|
19
|
+
Requires-Dist: ruff ; extra == 'dev'
|
|
20
|
+
Requires-Dist: ipykernel ; extra == 'dev'
|
|
21
|
+
Requires-Dist: ipywidgets ; extra == 'dev'
|
|
22
|
+
Requires-Dist: matplotlib ; extra == 'dev'
|
|
23
|
+
Requires-Dist: wavio ; extra == 'dev'
|
|
24
|
+
Requires-Dist: h5py ; extra == 'dev'
|
|
25
|
+
Requires-Dist: fusionaihub ; extra == 'dev'
|
|
26
|
+
Requires-Dist: scikit-learn ; extra == 'dev'
|
|
27
|
+
Requires-Dist: lightning ; extra == 'train'
|
|
28
|
+
Requires-Python: >=3.13
|
|
29
|
+
Provides-Extra: dev
|
|
30
|
+
Provides-Extra: train
|
|
31
|
+
Description-Content-Type: text/markdown
|
|
32
|
+
|
|
33
|
+
<p align="center">
|
|
34
|
+
<img src="assets/logo.png" alt="TokEye Logo" width="400">
|
|
35
|
+
</p>
|
|
36
|
+
|
|
37
|
+
# TokEye
|
|
38
|
+
|
|
39
|
+
[](https://github.com/PlasmaControl/tokeye/actions/workflows/python-package.yml)
|
|
40
|
+
|
|
41
|
+
TokEye is a open-source Python-based application for automatic classification and localization of fluctuating signals.
|
|
42
|
+
It is designed to be used in the context of plasma physics, but can be used for any type of fluctuating signal.
|
|
43
|
+
|
|
44
|
+
Check out [this poster from APS DPP 2025](assets/aps_dpp_2025.pdf) for more information.
|
|
45
|
+
|
|
46
|
+
## Example Demonstration
|
|
47
|
+

|
|
48
|
+
|
|
49
|
+
Expected processing time:
|
|
50
|
+
- A100: < 0.5 seconds on any size spectrogram after warmup.
|
|
51
|
+
- CPU: not yet tested.
|
|
52
|
+
|
|
53
|
+
## Verified Datatypes
|
|
54
|
+
- DIII-D Fast Magnetics (cite)
|
|
55
|
+
- DIII-D CO2 Interferometer (cite)
|
|
56
|
+
- DIII-D Electron Cyclotron Emission (cite)
|
|
57
|
+
- DIII-D Beam Emission Spectroscopy (cite)
|
|
58
|
+
|
|
59
|
+
## Evaluation
|
|
60
|
+
Recall Scores:
|
|
61
|
+
- TJII2021: 0.8254
|
|
62
|
+
- DCLDE2011 (Delphinus capensis): 0.7708
|
|
63
|
+
- DCLDE2011 (Delphinus delphis): 0.7953
|
|
64
|
+
|
|
65
|
+
With more data, comes better models. Please contribute to the project!
|
|
66
|
+
|
|
67
|
+
## Installation
|
|
68
|
+
|
|
69
|
+
[uv](https://docs.astral.sh/uv/) (recommended)
|
|
70
|
+
```bash
|
|
71
|
+
git clone git@github.com:PlasmaControl/TokEye.git
|
|
72
|
+
cd TokEye
|
|
73
|
+
uv sync
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
pip (from source)
|
|
77
|
+
```bash
|
|
78
|
+
git clone git@github.com:PlasmaControl/TokEye.git
|
|
79
|
+
cd TokEye
|
|
80
|
+
python3 -m venv .venv
|
|
81
|
+
source venv/bin/activate
|
|
82
|
+
pip install uv
|
|
83
|
+
uv sync
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
pip (from PyPI)
|
|
87
|
+
```bash
|
|
88
|
+
pip install tokeye
|
|
89
|
+
```
|
|
90
|
+
Coming soon.
|
|
91
|
+
|
|
92
|
+
Containerized installation (Docker)
|
|
93
|
+
Coming soon.
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
## Usage
|
|
97
|
+
```bash
|
|
98
|
+
python -m TokEye.app
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
This will start a web app on `http://localhost:8888`.
|
|
102
|
+
|
|
103
|
+
If you are on a remote server, you can use SSH port forwarding to access the web app on your local machine:
|
|
104
|
+
```bash
|
|
105
|
+
ssh -L 8888:localhost:7860 user@remote_server
|
|
106
|
+
```
|
|
107
|
+
Then open your web browser and navigate to `http://localhost:8888`.
|
|
108
|
+
|
|
109
|
+
## Models
|
|
110
|
+
Pre-trained models are available at [this link](https://drive.google.com/drive/folders/1rXllPXB3eWhMvSIlp0CDSFx68lJOQG1u?usp=drive_link).
|
|
111
|
+
Copy them into the `models/` directory after downloading them.
|
|
112
|
+
- big_mode_v1.pt: Original training regime (window = 1024, hop = 128)
|
|
113
|
+
- big_mode_v2.pt: Trained on multiscale (multiwindow, multihop) spectrograms
|
|
114
|
+
|
|
115
|
+
Input should be a tensor that has shape (B, 1, H, W) where B, H, and W can vary
|
|
116
|
+
Output will be a tensor of shape (B, 2, H, W)
|
|
117
|
+
|
|
118
|
+
Best performance when spectrograms are oriented so that when they are plotted with matplotlib, the lowest frequency bin is oriented with the bottom when `origin='lower'`. Spectrograms should be standardized (mean = 0, std = 1). If baseline activity is very strong, clipping the input may help, but is generally not needed.
|
|
119
|
+
|
|
120
|
+
The first channel of the output will return preferential measurements of coherent activity (useful for most tasks)
|
|
121
|
+
THe second channel of the output will return preferential measurements of transient activity
|
|
122
|
+
|
|
123
|
+
## Data
|
|
124
|
+
Right now, keep all data as 1d numpy float arrays. No need to normalize or preprocess them.
|
|
125
|
+
Copy them into the `data/` directory.
|
|
126
|
+
|
|
127
|
+
## Citation
|
|
128
|
+
If you use this code in your research, please cite:
|
|
129
|
+
```bibtex
|
|
130
|
+
@article{NaN,
|
|
131
|
+
title={Paper not yet published},
|
|
132
|
+
author={Nathaniel Chen},
|
|
133
|
+
year={2025}
|
|
134
|
+
}
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
## Contact
|
|
138
|
+
Please check back for updates or reach out to Nathaniel Chen at nathaniel@princeton.edu.
|
tokeye-0.9.0/README.md
ADDED
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
<p align="center">
|
|
2
|
+
<img src="assets/logo.png" alt="TokEye Logo" width="400">
|
|
3
|
+
</p>
|
|
4
|
+
|
|
5
|
+
# TokEye
|
|
6
|
+
|
|
7
|
+
[](https://github.com/PlasmaControl/tokeye/actions/workflows/python-package.yml)
|
|
8
|
+
|
|
9
|
+
TokEye is a open-source Python-based application for automatic classification and localization of fluctuating signals.
|
|
10
|
+
It is designed to be used in the context of plasma physics, but can be used for any type of fluctuating signal.
|
|
11
|
+
|
|
12
|
+
Check out [this poster from APS DPP 2025](assets/aps_dpp_2025.pdf) for more information.
|
|
13
|
+
|
|
14
|
+
## Example Demonstration
|
|
15
|
+

|
|
16
|
+
|
|
17
|
+
Expected processing time:
|
|
18
|
+
- A100: < 0.5 seconds on any size spectrogram after warmup.
|
|
19
|
+
- CPU: not yet tested.
|
|
20
|
+
|
|
21
|
+
## Verified Datatypes
|
|
22
|
+
- DIII-D Fast Magnetics (cite)
|
|
23
|
+
- DIII-D CO2 Interferometer (cite)
|
|
24
|
+
- DIII-D Electron Cyclotron Emission (cite)
|
|
25
|
+
- DIII-D Beam Emission Spectroscopy (cite)
|
|
26
|
+
|
|
27
|
+
## Evaluation
|
|
28
|
+
Recall Scores:
|
|
29
|
+
- TJII2021: 0.8254
|
|
30
|
+
- DCLDE2011 (Delphinus capensis): 0.7708
|
|
31
|
+
- DCLDE2011 (Delphinus delphis): 0.7953
|
|
32
|
+
|
|
33
|
+
With more data, comes better models. Please contribute to the project!
|
|
34
|
+
|
|
35
|
+
## Installation
|
|
36
|
+
|
|
37
|
+
[uv](https://docs.astral.sh/uv/) (recommended)
|
|
38
|
+
```bash
|
|
39
|
+
git clone git@github.com:PlasmaControl/TokEye.git
|
|
40
|
+
cd TokEye
|
|
41
|
+
uv sync
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
pip (from source)
|
|
45
|
+
```bash
|
|
46
|
+
git clone git@github.com:PlasmaControl/TokEye.git
|
|
47
|
+
cd TokEye
|
|
48
|
+
python3 -m venv .venv
|
|
49
|
+
source venv/bin/activate
|
|
50
|
+
pip install uv
|
|
51
|
+
uv sync
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
pip (from PyPI)
|
|
55
|
+
```bash
|
|
56
|
+
pip install tokeye
|
|
57
|
+
```
|
|
58
|
+
Coming soon.
|
|
59
|
+
|
|
60
|
+
Containerized installation (Docker)
|
|
61
|
+
Coming soon.
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
## Usage
|
|
65
|
+
```bash
|
|
66
|
+
python -m TokEye.app
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
This will start a web app on `http://localhost:8888`.
|
|
70
|
+
|
|
71
|
+
If you are on a remote server, you can use SSH port forwarding to access the web app on your local machine:
|
|
72
|
+
```bash
|
|
73
|
+
ssh -L 8888:localhost:7860 user@remote_server
|
|
74
|
+
```
|
|
75
|
+
Then open your web browser and navigate to `http://localhost:8888`.
|
|
76
|
+
|
|
77
|
+
## Models
|
|
78
|
+
Pre-trained models are available at [this link](https://drive.google.com/drive/folders/1rXllPXB3eWhMvSIlp0CDSFx68lJOQG1u?usp=drive_link).
|
|
79
|
+
Copy them into the `models/` directory after downloading them.
|
|
80
|
+
- big_mode_v1.pt: Original training regime (window = 1024, hop = 128)
|
|
81
|
+
- big_mode_v2.pt: Trained on multiscale (multiwindow, multihop) spectrograms
|
|
82
|
+
|
|
83
|
+
Input should be a tensor that has shape (B, 1, H, W) where B, H, and W can vary
|
|
84
|
+
Output will be a tensor of shape (B, 2, H, W)
|
|
85
|
+
|
|
86
|
+
Best performance when spectrograms are oriented so that when they are plotted with matplotlib, the lowest frequency bin is oriented with the bottom when `origin='lower'`. Spectrograms should be standardized (mean = 0, std = 1). If baseline activity is very strong, clipping the input may help, but is generally not needed.
|
|
87
|
+
|
|
88
|
+
The first channel of the output will return preferential measurements of coherent activity (useful for most tasks)
|
|
89
|
+
THe second channel of the output will return preferential measurements of transient activity
|
|
90
|
+
|
|
91
|
+
## Data
|
|
92
|
+
Right now, keep all data as 1d numpy float arrays. No need to normalize or preprocess them.
|
|
93
|
+
Copy them into the `data/` directory.
|
|
94
|
+
|
|
95
|
+
## Citation
|
|
96
|
+
If you use this code in your research, please cite:
|
|
97
|
+
```bibtex
|
|
98
|
+
@article{NaN,
|
|
99
|
+
title={Paper not yet published},
|
|
100
|
+
author={Nathaniel Chen},
|
|
101
|
+
year={2025}
|
|
102
|
+
}
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
## Contact
|
|
106
|
+
Please check back for updates or reach out to Nathaniel Chen at nathaniel@princeton.edu.
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["uv_build"]
|
|
3
|
+
build-backend = "uv_build"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "tokeye"
|
|
7
|
+
version = "0.9.0"
|
|
8
|
+
description = "Add your description here"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.13"
|
|
11
|
+
dependencies = [
|
|
12
|
+
"gradio",
|
|
13
|
+
|
|
14
|
+
"numpy",
|
|
15
|
+
"scipy",
|
|
16
|
+
|
|
17
|
+
"torch",
|
|
18
|
+
"torchcodec",
|
|
19
|
+
"torchvision",
|
|
20
|
+
"torchinfo",
|
|
21
|
+
|
|
22
|
+
"omegaconf",
|
|
23
|
+
"tables",
|
|
24
|
+
"pydantic",
|
|
25
|
+
]
|
|
26
|
+
|
|
27
|
+
[project.optional-dependencies]
|
|
28
|
+
dev = [
|
|
29
|
+
"mypy",
|
|
30
|
+
"pytest",
|
|
31
|
+
"pytest-cov",
|
|
32
|
+
"pytest-xdist",
|
|
33
|
+
"ruff",
|
|
34
|
+
|
|
35
|
+
"ipykernel",
|
|
36
|
+
"ipywidgets",
|
|
37
|
+
"matplotlib",
|
|
38
|
+
"wavio",
|
|
39
|
+
"h5py",
|
|
40
|
+
"fusionaihub",
|
|
41
|
+
"scikit-learn",
|
|
42
|
+
]
|
|
43
|
+
train = [
|
|
44
|
+
"lightning",
|
|
45
|
+
]
|
|
46
|
+
|
|
47
|
+
[tool.ruff]
|
|
48
|
+
line-length = 88
|
|
49
|
+
|
|
50
|
+
[tool.ruff.lint]
|
|
51
|
+
extend-select = [
|
|
52
|
+
"F", # Pyflakes rules
|
|
53
|
+
"W", # PyCodeStyle warnings
|
|
54
|
+
"E", # PyCodeStyle errors
|
|
55
|
+
"I", # Sort imports properly
|
|
56
|
+
"UP", # Warn if certain things can changed due to newer Python versions
|
|
57
|
+
"C4", # Catch incorrect use of comprehensions, dict, list, etc
|
|
58
|
+
"FA", # Enforce from __future__ import annotations
|
|
59
|
+
"ISC", # Good use of string concatenation
|
|
60
|
+
"ICN", # Use common import conventions
|
|
61
|
+
"RET", # Good return practices
|
|
62
|
+
"SIM", # Common simplification rules
|
|
63
|
+
"TID", # Some good import practices
|
|
64
|
+
"TC", # Enforce importing certain types in a TYPE_CHECKING block
|
|
65
|
+
"PTH", # Use pathlib instead of os.path
|
|
66
|
+
"TD", # Be diligent with TODO comments
|
|
67
|
+
"NPY", # Some numpy-specific things
|
|
68
|
+
]
|
|
69
|
+
ignore = [
|
|
70
|
+
"E501",
|
|
71
|
+
"B008",
|
|
72
|
+
]
|
|
73
|
+
|
|
74
|
+
[tool.ruff.lint.per-file-ignores]
|
|
75
|
+
"__init__.py" = ["F401"]
|
|
76
|
+
"tests/*" = ["ARG"]
|
|
77
|
+
"*.ipynb" = ["E402"]
|
|
78
|
+
|
|
79
|
+
[tool.uv]
|
|
80
|
+
package = true
|
|
81
|
+
|
|
82
|
+
[tool.uv.build-backend]
|
|
83
|
+
module-name = "tokeye"
|
|
84
|
+
module-root = "src"
|
|
85
|
+
|
|
86
|
+
[pytest]
|
|
87
|
+
minversion = "9.0"
|
|
88
|
+
addopts = ["-ra", "-q"]
|
|
89
|
+
testpaths = [
|
|
90
|
+
"tests",
|
|
91
|
+
]
|
|
File without changes
|
|
@@ -0,0 +1,240 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
import time
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
|
|
5
|
+
import numpy as np
|
|
6
|
+
import torch
|
|
7
|
+
import torch.nn as nn
|
|
8
|
+
|
|
9
|
+
# Import model loading function from the analyze module
|
|
10
|
+
from TokEye.app.analyze.load import model_load
|
|
11
|
+
from tqdm.auto import tqdm
|
|
12
|
+
|
|
13
|
+
# Configure logging
|
|
14
|
+
logging.basicConfig(
|
|
15
|
+
level=logging.INFO,
|
|
16
|
+
format="%(asctime)s - %(name)s - %(levelname)s - %(message)s",
|
|
17
|
+
)
|
|
18
|
+
logger = logging.getLogger(__name__)
|
|
19
|
+
|
|
20
|
+
# Default settings
|
|
21
|
+
default_settings = {
|
|
22
|
+
"model_path": "data/models/big_mode_v1.pth",
|
|
23
|
+
"input_path": "data/batch_inputs",
|
|
24
|
+
"output_path": "data/batch_outputs",
|
|
25
|
+
"analysis_mode": "amplitude",
|
|
26
|
+
"threshold": 0.5,
|
|
27
|
+
"polling_interval": 5, # seconds between directory scans
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def load_spectrogram(filepath: Path) -> np.ndarray | None:
|
|
32
|
+
"""Load a 2D spectrogram from a numpy file.
|
|
33
|
+
|
|
34
|
+
Args:
|
|
35
|
+
filepath: Path to the .npy file containing a 2D spectrogram
|
|
36
|
+
|
|
37
|
+
Returns:
|
|
38
|
+
2D numpy array or None if loading fails
|
|
39
|
+
"""
|
|
40
|
+
try:
|
|
41
|
+
spec = np.load(filepath)
|
|
42
|
+
if spec.ndim != 2:
|
|
43
|
+
logger.error(f"Expected 2D array, got {spec.ndim}D: {filepath.name}")
|
|
44
|
+
return None
|
|
45
|
+
if spec.size == 0:
|
|
46
|
+
logger.error(f"Empty array: {filepath.name}")
|
|
47
|
+
return None
|
|
48
|
+
return spec
|
|
49
|
+
except Exception as e:
|
|
50
|
+
logger.error(f"Failed to load {filepath.name}: {e}")
|
|
51
|
+
return None
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def run_inference(
|
|
55
|
+
spec: np.ndarray,
|
|
56
|
+
model: nn.Module | torch.export.ExportedProgram,
|
|
57
|
+
) -> np.ndarray | None:
|
|
58
|
+
"""Run inference on a 2D spectrogram.
|
|
59
|
+
|
|
60
|
+
Args:
|
|
61
|
+
spec: 2D numpy array (H, W)
|
|
62
|
+
model: Loaded PyTorch model
|
|
63
|
+
|
|
64
|
+
Returns:
|
|
65
|
+
3D numpy array (2, H, W) with two output channels or None if inference fails
|
|
66
|
+
"""
|
|
67
|
+
try:
|
|
68
|
+
device = next(model.parameters()).device
|
|
69
|
+
|
|
70
|
+
# Normalize input
|
|
71
|
+
spec_norm = (spec - spec.mean()) / (spec.std() + 1e-6)
|
|
72
|
+
|
|
73
|
+
# Convert to tensor: (1, 1, H, W)
|
|
74
|
+
inp_tensor = torch.from_numpy(spec_norm)
|
|
75
|
+
inp_tensor = inp_tensor.unsqueeze(0).unsqueeze(0).float()
|
|
76
|
+
inp_tensor = inp_tensor.to(device)
|
|
77
|
+
|
|
78
|
+
# Run inference
|
|
79
|
+
with torch.no_grad():
|
|
80
|
+
out_tensor = model(inp_tensor)
|
|
81
|
+
|
|
82
|
+
# Remove batch dimension: (1, 2, H, W) -> (2, H, W)
|
|
83
|
+
out_tensor = out_tensor[0]
|
|
84
|
+
|
|
85
|
+
# Apply sigmoid activation
|
|
86
|
+
out_tensor = torch.sigmoid(out_tensor)
|
|
87
|
+
|
|
88
|
+
# Convert to numpy
|
|
89
|
+
return out_tensor.cpu().numpy()
|
|
90
|
+
|
|
91
|
+
except Exception as e:
|
|
92
|
+
logger.error(f"Inference failed: {e}")
|
|
93
|
+
return None
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
def process_file(
|
|
97
|
+
input_path: Path,
|
|
98
|
+
output_path: Path,
|
|
99
|
+
model: nn.Module | torch.export.ExportedProgram,
|
|
100
|
+
) -> bool:
|
|
101
|
+
"""Process a single file: load, infer, save.
|
|
102
|
+
|
|
103
|
+
Args:
|
|
104
|
+
input_path: Path to input .npy file
|
|
105
|
+
output_path: Path to output .npy file
|
|
106
|
+
model: Loaded PyTorch model
|
|
107
|
+
|
|
108
|
+
Returns:
|
|
109
|
+
True if successful, False otherwise
|
|
110
|
+
"""
|
|
111
|
+
# Load spectrogram
|
|
112
|
+
spec = load_spectrogram(input_path)
|
|
113
|
+
if spec is None:
|
|
114
|
+
return False
|
|
115
|
+
|
|
116
|
+
# Run inference
|
|
117
|
+
result = run_inference(spec, model)
|
|
118
|
+
if result is None:
|
|
119
|
+
return False
|
|
120
|
+
|
|
121
|
+
# Save result
|
|
122
|
+
try:
|
|
123
|
+
output_path.parent.mkdir(parents=True, exist_ok=True)
|
|
124
|
+
np.save(output_path, result)
|
|
125
|
+
return True
|
|
126
|
+
except Exception as e:
|
|
127
|
+
logger.error(f"Failed to save {output_path.name}: {e}")
|
|
128
|
+
return False
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
def scan_directory(input_dir: Path) -> set[str]:
|
|
132
|
+
"""Scan directory for .npy files.
|
|
133
|
+
|
|
134
|
+
Args:
|
|
135
|
+
input_dir: Directory to scan
|
|
136
|
+
|
|
137
|
+
Returns:
|
|
138
|
+
Set of filenames (not full paths)
|
|
139
|
+
"""
|
|
140
|
+
if not input_dir.exists():
|
|
141
|
+
return set()
|
|
142
|
+
|
|
143
|
+
npy_files = input_dir.glob("*.npy")
|
|
144
|
+
return {f.name for f in npy_files}
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
def run_batch_analysis(settings: dict):
|
|
148
|
+
"""Main batch analysis loop.
|
|
149
|
+
|
|
150
|
+
Continuously monitors input directory for new spectrogram files,
|
|
151
|
+
runs inference, and saves results. Runs until interrupted with Ctrl+C.
|
|
152
|
+
|
|
153
|
+
Args:
|
|
154
|
+
settings: Dictionary with configuration:
|
|
155
|
+
- model_path: Path to model file
|
|
156
|
+
- input_path: Input directory to monitor
|
|
157
|
+
- output_path: Output directory for results
|
|
158
|
+
- polling_interval: Seconds between directory scans
|
|
159
|
+
"""
|
|
160
|
+
# Extract settings
|
|
161
|
+
model_path = Path(settings["model_path"])
|
|
162
|
+
input_dir = Path(settings["input_path"])
|
|
163
|
+
output_dir = Path(settings["output_path"])
|
|
164
|
+
polling_interval = settings.get("polling_interval", 5)
|
|
165
|
+
|
|
166
|
+
# Ensure directories exist
|
|
167
|
+
input_dir.mkdir(parents=True, exist_ok=True)
|
|
168
|
+
output_dir.mkdir(parents=True, exist_ok=True)
|
|
169
|
+
|
|
170
|
+
# Load model
|
|
171
|
+
logger.info("=" * 60)
|
|
172
|
+
logger.info("Starting Batch Analysis System")
|
|
173
|
+
logger.info("=" * 60)
|
|
174
|
+
logger.info(f"Model: {model_path}")
|
|
175
|
+
logger.info(f"Input directory: {input_dir}")
|
|
176
|
+
logger.info(f"Output directory: {output_dir}")
|
|
177
|
+
logger.info(f"Polling interval: {polling_interval}s")
|
|
178
|
+
logger.info("=" * 60)
|
|
179
|
+
|
|
180
|
+
try:
|
|
181
|
+
model = model_load(model_path)
|
|
182
|
+
except Exception as e:
|
|
183
|
+
logger.error(f"Failed to load model: {e}")
|
|
184
|
+
return
|
|
185
|
+
|
|
186
|
+
# Initialize tracking
|
|
187
|
+
processed_files = set()
|
|
188
|
+
|
|
189
|
+
logger.info("Monitoring for new files... (Press Ctrl+C to stop)")
|
|
190
|
+
logger.info("")
|
|
191
|
+
|
|
192
|
+
# Main loop
|
|
193
|
+
try:
|
|
194
|
+
while True:
|
|
195
|
+
# Scan for all files in directory
|
|
196
|
+
current_files = scan_directory(input_dir)
|
|
197
|
+
|
|
198
|
+
# Update tracking: remove deleted files
|
|
199
|
+
deleted_files = processed_files - current_files
|
|
200
|
+
if deleted_files:
|
|
201
|
+
for fname in deleted_files:
|
|
202
|
+
processed_files.discard(fname)
|
|
203
|
+
logger.info(f"File deleted, will reprocess if added again: {fname}")
|
|
204
|
+
|
|
205
|
+
# Find new files to process
|
|
206
|
+
new_files = current_files - processed_files
|
|
207
|
+
|
|
208
|
+
if new_files:
|
|
209
|
+
logger.info(f"Found {len(new_files)} new file(s)")
|
|
210
|
+
|
|
211
|
+
# Process files with progress bar
|
|
212
|
+
new_files_list = sorted(new_files)
|
|
213
|
+
for fname in tqdm(new_files_list, desc="Processing", unit="file"):
|
|
214
|
+
input_path = input_dir / fname
|
|
215
|
+
output_path = output_dir / fname
|
|
216
|
+
|
|
217
|
+
# Process the file
|
|
218
|
+
success = process_file(input_path, output_path, model)
|
|
219
|
+
|
|
220
|
+
if success:
|
|
221
|
+
processed_files.add(fname)
|
|
222
|
+
logger.info(f"✓ Processed: {fname}")
|
|
223
|
+
else:
|
|
224
|
+
logger.warning(f"✗ Failed: {fname}")
|
|
225
|
+
|
|
226
|
+
logger.info("")
|
|
227
|
+
|
|
228
|
+
# Sleep before next scan
|
|
229
|
+
time.sleep(polling_interval)
|
|
230
|
+
|
|
231
|
+
except KeyboardInterrupt:
|
|
232
|
+
logger.info("")
|
|
233
|
+
logger.info("=" * 60)
|
|
234
|
+
logger.info("Shutting down gracefully...")
|
|
235
|
+
logger.info(f"Total files processed: {len(processed_files)}")
|
|
236
|
+
logger.info("=" * 60)
|
|
237
|
+
|
|
238
|
+
|
|
239
|
+
if __name__ == "__main__":
|
|
240
|
+
run_batch_analysis(default_settings)
|
|
File without changes
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
"""
|
|
2
|
+
TokEye Main Inference
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
import logging
|
|
6
|
+
import sys
|
|
7
|
+
from pathlib import Path
|
|
8
|
+
|
|
9
|
+
import gradio as gr
|
|
10
|
+
|
|
11
|
+
# Import tabs
|
|
12
|
+
from .analyze.analyze import analyze_tab
|
|
13
|
+
from .tabs.annotate import annotate_tab
|
|
14
|
+
from .tabs.utilities import utilities_tab
|
|
15
|
+
from .utils.theme import make_theme
|
|
16
|
+
|
|
17
|
+
# Constants
|
|
18
|
+
APP_TITLE = "TokEye"
|
|
19
|
+
DEFAULT_PORT = 7860
|
|
20
|
+
MAX_PORT_ATTEMPTS = 10
|
|
21
|
+
|
|
22
|
+
# Set up logging
|
|
23
|
+
logging.getLogger("uvicorn").setLevel(logging.WARNING)
|
|
24
|
+
logging.getLogger("httpx").setLevel(logging.WARNING)
|
|
25
|
+
|
|
26
|
+
# Current working directory
|
|
27
|
+
cwd = Path.cwd()
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def create_app() -> gr.Blocks:
|
|
31
|
+
with gr.Blocks(
|
|
32
|
+
title=APP_TITLE,
|
|
33
|
+
theme=make_theme(),
|
|
34
|
+
css="footer{display:none !important}",
|
|
35
|
+
) as app:
|
|
36
|
+
gr.Image(
|
|
37
|
+
str(Path.cwd() / "assets" / "logo.png"),
|
|
38
|
+
height=300,
|
|
39
|
+
interactive=False,
|
|
40
|
+
container=False,
|
|
41
|
+
show_download_button=False,
|
|
42
|
+
show_fullscreen_button=False,
|
|
43
|
+
)
|
|
44
|
+
with gr.Tab("Analyze"):
|
|
45
|
+
analyze_tab()
|
|
46
|
+
with gr.Tab("Annotate"):
|
|
47
|
+
annotate_tab()
|
|
48
|
+
with gr.Tab("Utilities"):
|
|
49
|
+
utilities_tab()
|
|
50
|
+
return app
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def get_port():
|
|
54
|
+
if "--port" in sys.argv:
|
|
55
|
+
port_index = sys.argv.index("--port") + 1
|
|
56
|
+
if port_index < len(sys.argv):
|
|
57
|
+
return int(sys.argv[port_index])
|
|
58
|
+
return DEFAULT_PORT
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def launch(app, port):
|
|
62
|
+
app.launch(
|
|
63
|
+
# favicon_path="assets/ICON.ico", # Set up favicon later
|
|
64
|
+
share="--share" in sys.argv,
|
|
65
|
+
inbrowser="--open" in sys.argv,
|
|
66
|
+
server_port=port,
|
|
67
|
+
)
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
if __name__ == "__main__":
|
|
71
|
+
logging.info(f"Initializing TokEye in: {cwd}")
|
|
72
|
+
# Set up
|
|
73
|
+
app = create_app()
|
|
74
|
+
# Launch application
|
|
75
|
+
port = get_port()
|
|
76
|
+
for _ in range(MAX_PORT_ATTEMPTS):
|
|
77
|
+
try:
|
|
78
|
+
launch(app, port)
|
|
79
|
+
except OSError:
|
|
80
|
+
print(f"Failed on port {port}")
|
|
81
|
+
port -= 1
|
|
82
|
+
except Exception as error:
|
|
83
|
+
print(f"{error}")
|
|
84
|
+
break
|
|
File without changes
|