TwoPy 0.1.3__tar.gz → 0.1.4__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.
- {twopy-0.1.3 → twopy-0.1.4}/PKG-INFO +8 -18
- {twopy-0.1.3 → twopy-0.1.4}/README.md +7 -17
- {twopy-0.1.3 → twopy-0.1.4}/pyproject.toml +1 -1
- {twopy-0.1.3 → twopy-0.1.4}/src/twopy/__init__.py +16 -5
- {twopy-0.1.3 → twopy-0.1.4}/src/twopy/analysis/__init__.py +12 -4
- {twopy-0.1.3 → twopy-0.1.4}/src/twopy/analysis/background_subtraction.py +223 -49
- {twopy-0.1.3 → twopy-0.1.4}/src/twopy/analysis/dff.py +175 -118
- twopy-0.1.4/src/twopy/analysis/dff_options.py +35 -0
- {twopy-0.1.3 → twopy-0.1.4}/src/twopy/analysis/epoch_mapping.py +2 -25
- {twopy-0.1.3 → twopy-0.1.4}/src/twopy/analysis/motion.py +2 -2
- {twopy-0.1.3 → twopy-0.1.4}/src/twopy/analysis/persistence.py +147 -87
- {twopy-0.1.3 → twopy-0.1.4}/src/twopy/analysis/response_processing/apply.py +16 -24
- {twopy-0.1.3 → twopy-0.1.4}/src/twopy/analysis/response_processing/qc.py +6 -16
- twopy-0.1.4/src/twopy/analysis/response_window_options.py +99 -0
- {twopy-0.1.3 → twopy-0.1.4}/src/twopy/analysis/responses.py +105 -16
- {twopy-0.1.3 → twopy-0.1.4}/src/twopy/analysis/trials.py +95 -17
- {twopy-0.1.3 → twopy-0.1.4}/src/twopy/analysis/workflow.py +190 -63
- {twopy-0.1.3 → twopy-0.1.4}/src/twopy/conversion/api.py +2 -1
- {twopy-0.1.3 → twopy-0.1.4}/src/twopy/conversion/types.py +9 -2
- {twopy-0.1.3 → twopy-0.1.4}/src/twopy/converted.py +38 -31
- {twopy-0.1.3/src/twopy/conversion → twopy-0.1.4/src/twopy}/frame_ranges.py +10 -5
- {twopy-0.1.3 → twopy-0.1.4}/src/twopy/napari/interactive.py +40 -0
- twopy-0.1.4/src/twopy/napari/plotting/data.py +561 -0
- twopy-0.1.4/src/twopy/napari/plotting/dff_options.py +396 -0
- twopy-0.1.4/src/twopy/napari/plotting/docks/__init__.py +23 -0
- twopy-0.1.4/src/twopy/napari/plotting/docks/api.py +151 -0
- twopy-0.1.4/src/twopy/napari/plotting/docks/dynamic_options.py +208 -0
- twopy-0.1.4/src/twopy/napari/plotting/docks/options_panel.py +170 -0
- twopy-0.1.4/src/twopy/napari/plotting/docks/paths.py +100 -0
- twopy-0.1.4/src/twopy/napari/plotting/docks/plot_area.py +248 -0
- twopy-0.1.4/src/twopy/napari/plotting/docks/response_plot_widget.py +723 -0
- twopy-0.1.4/src/twopy/napari/plotting/docks/save_actions.py +127 -0
- twopy-0.1.4/src/twopy/napari/plotting/docks/visibility_state.py +153 -0
- twopy-0.1.4/src/twopy/napari/plotting/form_controls.py +88 -0
- {twopy-0.1.3 → twopy-0.1.4}/src/twopy/napari/plotting/options.py +17 -7
- {twopy-0.1.3 → twopy-0.1.4}/src/twopy/napari/plotting/panels.py +4 -1
- {twopy-0.1.3 → twopy-0.1.4}/src/twopy/napari/plotting/processing_options.py +58 -37
- twopy-0.1.4/src/twopy/napari/plotting/response_window_options.py +171 -0
- {twopy-0.1.3 → twopy-0.1.4}/src/twopy/napari/plotting/widgets.py +28 -14
- {twopy-0.1.3 → twopy-0.1.4}/src/twopy/napari/responses.py +30 -0
- {twopy-0.1.3 → twopy-0.1.4}/src/twopy/napari/viewer.py +2 -1
- {twopy-0.1.3 → twopy-0.1.4}/src/twopy/parity/__init__.py +9 -1
- {twopy-0.1.3 → twopy-0.1.4}/src/twopy/parity/dff.py +11 -33
- twopy-0.1.4/src/twopy/parity/psycho5.py +243 -0
- {twopy-0.1.3 → twopy-0.1.4}/src/twopy/roi.py +3 -25
- {twopy-0.1.3 → twopy-0.1.4}/src/twopy.egg-info/PKG-INFO +8 -18
- {twopy-0.1.3 → twopy-0.1.4}/src/twopy.egg-info/SOURCES.txt +19 -2
- {twopy-0.1.3 → twopy-0.1.4}/tests/test_background_subtraction.py +90 -3
- {twopy-0.1.3 → twopy-0.1.4}/tests/test_conversion.py +20 -0
- {twopy-0.1.3 → twopy-0.1.4}/tests/test_converted.py +85 -1
- {twopy-0.1.3 → twopy-0.1.4}/tests/test_dff.py +84 -26
- twopy-0.1.4/tests/test_frame_ranges.py +71 -0
- {twopy-0.1.3 → twopy-0.1.4}/tests/test_motion.py +2 -2
- {twopy-0.1.3 → twopy-0.1.4}/tests/test_napari.py +784 -39
- {twopy-0.1.3 → twopy-0.1.4}/tests/test_parity.py +1 -1
- twopy-0.1.4/tests/test_parity_psycho5.py +211 -0
- {twopy-0.1.3 → twopy-0.1.4}/tests/test_persistence.py +36 -5
- {twopy-0.1.3 → twopy-0.1.4}/tests/test_real_fixture.py +4 -4
- {twopy-0.1.3 → twopy-0.1.4}/tests/test_response_processing.py +56 -2
- twopy-0.1.4/tests/test_response_window_options.py +73 -0
- {twopy-0.1.3 → twopy-0.1.4}/tests/test_responses.py +158 -2
- {twopy-0.1.3 → twopy-0.1.4}/tests/test_roi.py +18 -0
- {twopy-0.1.3 → twopy-0.1.4}/tests/test_stimulus.py +8 -2
- {twopy-0.1.3 → twopy-0.1.4}/tests/test_trials.py +62 -3
- {twopy-0.1.3 → twopy-0.1.4}/tests/test_workflow.py +63 -7
- twopy-0.1.3/src/twopy/napari/plotting/data.py +0 -290
- twopy-0.1.3/src/twopy/napari/plotting/docks.py +0 -1165
- {twopy-0.1.3 → twopy-0.1.4}/setup.cfg +0 -0
- {twopy-0.1.3 → twopy-0.1.4}/src/twopy/_segments.py +0 -0
- {twopy-0.1.3 → twopy-0.1.4}/src/twopy/_version.py +0 -0
- {twopy-0.1.3 → twopy-0.1.4}/src/twopy/analysis/response_processing/__init__.py +0 -0
- {twopy-0.1.3 → twopy-0.1.4}/src/twopy/analysis/response_processing/options.py +0 -0
- {twopy-0.1.3 → twopy-0.1.4}/src/twopy/analysis/response_processing/persistence.py +0 -0
- {twopy-0.1.3 → twopy-0.1.4}/src/twopy/analysis/response_processing/signals.py +0 -0
- {twopy-0.1.3 → twopy-0.1.4}/src/twopy/api.py +0 -0
- {twopy-0.1.3 → twopy-0.1.4}/src/twopy/config.py +0 -0
- {twopy-0.1.3 → twopy-0.1.4}/src/twopy/conversion/__init__.py +0 -0
- {twopy-0.1.3 → twopy-0.1.4}/src/twopy/conversion/alignment_crop.py +0 -0
- {twopy-0.1.3 → twopy-0.1.4}/src/twopy/conversion/hdf5_writing.py +0 -0
- {twopy-0.1.3 → twopy-0.1.4}/src/twopy/conversion/matlab_values.py +0 -0
- {twopy-0.1.3 → twopy-0.1.4}/src/twopy/conversion/source_loading.py +0 -0
- {twopy-0.1.3 → twopy-0.1.4}/src/twopy/conversion/stimulus_code.py +0 -0
- {twopy-0.1.3 → twopy-0.1.4}/src/twopy/database/__init__.py +0 -0
- {twopy-0.1.3 → twopy-0.1.4}/src/twopy/database/cache.py +0 -0
- {twopy-0.1.3 → twopy-0.1.4}/src/twopy/database/catalog.py +0 -0
- {twopy-0.1.3 → twopy-0.1.4}/src/twopy/database/connection.py +0 -0
- {twopy-0.1.3 → twopy-0.1.4}/src/twopy/database/generic.py +0 -0
- {twopy-0.1.3 → twopy-0.1.4}/src/twopy/database/modeled.py +0 -0
- {twopy-0.1.3 → twopy-0.1.4}/src/twopy/database/sql.py +0 -0
- {twopy-0.1.3 → twopy-0.1.4}/src/twopy/database/types.py +0 -0
- {twopy-0.1.3 → twopy-0.1.4}/src/twopy/inspection.py +0 -0
- {twopy-0.1.3 → twopy-0.1.4}/src/twopy/matlab.py +0 -0
- {twopy-0.1.3 → twopy-0.1.4}/src/twopy/napari/__init__.py +0 -0
- {twopy-0.1.3 → twopy-0.1.4}/src/twopy/napari/__main__.py +0 -0
- {twopy-0.1.3 → twopy-0.1.4}/src/twopy/napari/constants.py +0 -0
- {twopy-0.1.3 → twopy-0.1.4}/src/twopy/napari/controls.py +0 -0
- {twopy-0.1.3 → twopy-0.1.4}/src/twopy/napari/display.py +0 -0
- {twopy-0.1.3 → twopy-0.1.4}/src/twopy/napari/display_paths.py +0 -0
- {twopy-0.1.3 → twopy-0.1.4}/src/twopy/napari/launcher.py +0 -0
- {twopy-0.1.3 → twopy-0.1.4}/src/twopy/napari/layout.py +0 -0
- {twopy-0.1.3 → twopy-0.1.4}/src/twopy/napari/loading.py +0 -0
- {twopy-0.1.3 → twopy-0.1.4}/src/twopy/napari/movie.py +0 -0
- {twopy-0.1.3 → twopy-0.1.4}/src/twopy/napari/paths.py +0 -0
- {twopy-0.1.3 → twopy-0.1.4}/src/twopy/napari/plotting/__init__.py +0 -0
- {twopy-0.1.3 → twopy-0.1.4}/src/twopy/napari/plotting/export.py +0 -0
- {twopy-0.1.3 → twopy-0.1.4}/src/twopy/napari/plotting/export_controls.py +0 -0
- {twopy-0.1.3 → twopy-0.1.4}/src/twopy/napari/plotting/label_visibility.py +0 -0
- {twopy-0.1.3 → twopy-0.1.4}/src/twopy/napari/protocols.py +0 -0
- {twopy-0.1.3 → twopy-0.1.4}/src/twopy/napari/roi.py +0 -0
- {twopy-0.1.3 → twopy-0.1.4}/src/twopy/napari/session.py +0 -0
- {twopy-0.1.3 → twopy-0.1.4}/src/twopy/napari/state.py +0 -0
- {twopy-0.1.3 → twopy-0.1.4}/src/twopy/napari/text.py +0 -0
- {twopy-0.1.3 → twopy-0.1.4}/src/twopy/napari/types.py +0 -0
- {twopy-0.1.3 → twopy-0.1.4}/src/twopy/parity/roi.py +0 -0
- {twopy-0.1.3 → twopy-0.1.4}/src/twopy/parity/saved_analysis.py +0 -0
- {twopy-0.1.3 → twopy-0.1.4}/src/twopy/photodiode.py +0 -0
- {twopy-0.1.3 → twopy-0.1.4}/src/twopy/photodiode_classification.py +0 -0
- {twopy-0.1.3 → twopy-0.1.4}/src/twopy/session.py +0 -0
- {twopy-0.1.3 → twopy-0.1.4}/src/twopy/spatial.py +0 -0
- {twopy-0.1.3 → twopy-0.1.4}/src/twopy/stimulus.py +0 -0
- {twopy-0.1.3 → twopy-0.1.4}/src/twopy/synchronization.py +0 -0
- {twopy-0.1.3 → twopy-0.1.4}/src/twopy/typing_guards.py +0 -0
- {twopy-0.1.3 → twopy-0.1.4}/src/twopy.egg-info/dependency_links.txt +0 -0
- {twopy-0.1.3 → twopy-0.1.4}/src/twopy.egg-info/entry_points.txt +0 -0
- {twopy-0.1.3 → twopy-0.1.4}/src/twopy.egg-info/requires.txt +0 -0
- {twopy-0.1.3 → twopy-0.1.4}/src/twopy.egg-info/top_level.txt +0 -0
- {twopy-0.1.3 → twopy-0.1.4}/tests/test_api.py +0 -0
- {twopy-0.1.3 → twopy-0.1.4}/tests/test_config.py +0 -0
- {twopy-0.1.3 → twopy-0.1.4}/tests/test_database.py +0 -0
- {twopy-0.1.3 → twopy-0.1.4}/tests/test_epoch_mapping.py +0 -0
- {twopy-0.1.3 → twopy-0.1.4}/tests/test_import.py +0 -0
- {twopy-0.1.3 → twopy-0.1.4}/tests/test_inspection.py +0 -0
- {twopy-0.1.3 → twopy-0.1.4}/tests/test_matlab.py +0 -0
- {twopy-0.1.3 → twopy-0.1.4}/tests/test_packaging.py +0 -0
- {twopy-0.1.3 → twopy-0.1.4}/tests/test_photodiode_classification.py +0 -0
- {twopy-0.1.3 → twopy-0.1.4}/tests/test_saved_analysis.py +0 -0
- {twopy-0.1.3 → twopy-0.1.4}/tests/test_session.py +0 -0
- {twopy-0.1.3 → twopy-0.1.4}/tests/test_synchronization.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: twopy
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.4
|
|
4
4
|
Summary: Two-photon imaging analysis tool with a napari interface.
|
|
5
5
|
Author-email: Gustavo Madeira Santana <fromweb1@gumadeiras.com>
|
|
6
6
|
License-Expression: MIT
|
|
@@ -26,13 +26,9 @@ Two-photon imaging analysis tool for the Clark Lab output format.
|
|
|
26
26
|
|
|
27
27
|
## Getting Started
|
|
28
28
|
|
|
29
|
-
twopy lets you open two-photon recordings, draw ROIs, plot responses in real time,
|
|
30
|
-
process and analyze them, and save them.
|
|
29
|
+
twopy lets you open two-photon recordings, draw ROIs, plot responses in real time, process and analyze them, and save them.
|
|
31
30
|
|
|
32
|
-
When you first load a recording, twopy converts it to a standardized HDF5 format.
|
|
33
|
-
The converted format includes the aligned movie, mean image, stimulus tables,
|
|
34
|
-
photodiode signals, and recording metadata. Analysis and the GUI both work from
|
|
35
|
-
the converted files, so the original source files remain separate from twopy's outputs.
|
|
31
|
+
When you first load a recording, twopy converts it to a standardized HDF5 format. The converted format includes the aligned movie, mean image, stimulus tables, photodiode signals, and recording metadata. Analysis and the GUI both work from the converted files, so the original source files remain separate from twopy's outputs.
|
|
36
32
|
|
|
37
33
|
## Install
|
|
38
34
|
|
|
@@ -50,8 +46,7 @@ Verify the install:
|
|
|
50
46
|
micromamba run -n twopy python -c 'import twopy; print(twopy.__name__)'
|
|
51
47
|
```
|
|
52
48
|
|
|
53
|
-
For a source checkout, copy `config.example.yml` to `config.yml` and edit the
|
|
54
|
-
paths for your computer. `config.yml` stays local and is not tracked by git.
|
|
49
|
+
For a source checkout, copy `config.example.yml` to `config.yml` and edit the paths for your computer. `config.yml` stays local and is not tracked by git.
|
|
55
50
|
|
|
56
51
|
## Start
|
|
57
52
|
|
|
@@ -75,20 +70,15 @@ Basic GUI flow:
|
|
|
75
70
|
4. Update response plots from the current ROIs.
|
|
76
71
|
5. Save analysis outputs.
|
|
77
72
|
|
|
78
|
-
When a source recording has not been converted yet, twopy converts it first.
|
|
79
|
-
Converted data includes the aligned movie, mean image, stimulus tables,
|
|
80
|
-
photodiode signals, and recording metadata.
|
|
73
|
+
When a source recording has not been converted yet, twopy converts it first. Converted data includes the aligned movie, mean image, stimulus tables, photodiode signals, and recording metadata.
|
|
81
74
|
|
|
82
75
|
## Docs
|
|
83
76
|
|
|
84
|
-
- [GUI guide](docs/gui.md): napari loading, ROI editing, plotting, and saved
|
|
85
|
-
|
|
86
|
-
- [Python API guide](docs/python_api.md): find recordings, convert data, run
|
|
87
|
-
analysis, and open napari from scripts.
|
|
77
|
+
- [GUI guide](docs/gui.md): napari loading, ROI editing, plotting, and saved outputs.
|
|
78
|
+
- [Python API guide](docs/python_api.md): find recordings, convert data, run analysis, and open napari from scripts.
|
|
88
79
|
- [Development guide](docs/development.md): source setup and local checks.
|
|
89
80
|
- [Input data spec](docs/input_data_spec.md): short recording folder contract.
|
|
90
|
-
- [Recording file reference](docs/recording_file_schema.md): detailed source
|
|
91
|
-
and converted file schema.
|
|
81
|
+
- [Recording file reference](docs/recording_file_schema.md): detailed source and converted file schema.
|
|
92
82
|
|
|
93
83
|
## Check
|
|
94
84
|
|
|
@@ -6,13 +6,9 @@ Two-photon imaging analysis tool for the Clark Lab output format.
|
|
|
6
6
|
|
|
7
7
|
## Getting Started
|
|
8
8
|
|
|
9
|
-
twopy lets you open two-photon recordings, draw ROIs, plot responses in real time,
|
|
10
|
-
process and analyze them, and save them.
|
|
9
|
+
twopy lets you open two-photon recordings, draw ROIs, plot responses in real time, process and analyze them, and save them.
|
|
11
10
|
|
|
12
|
-
When you first load a recording, twopy converts it to a standardized HDF5 format.
|
|
13
|
-
The converted format includes the aligned movie, mean image, stimulus tables,
|
|
14
|
-
photodiode signals, and recording metadata. Analysis and the GUI both work from
|
|
15
|
-
the converted files, so the original source files remain separate from twopy's outputs.
|
|
11
|
+
When you first load a recording, twopy converts it to a standardized HDF5 format. The converted format includes the aligned movie, mean image, stimulus tables, photodiode signals, and recording metadata. Analysis and the GUI both work from the converted files, so the original source files remain separate from twopy's outputs.
|
|
16
12
|
|
|
17
13
|
## Install
|
|
18
14
|
|
|
@@ -30,8 +26,7 @@ Verify the install:
|
|
|
30
26
|
micromamba run -n twopy python -c 'import twopy; print(twopy.__name__)'
|
|
31
27
|
```
|
|
32
28
|
|
|
33
|
-
For a source checkout, copy `config.example.yml` to `config.yml` and edit the
|
|
34
|
-
paths for your computer. `config.yml` stays local and is not tracked by git.
|
|
29
|
+
For a source checkout, copy `config.example.yml` to `config.yml` and edit the paths for your computer. `config.yml` stays local and is not tracked by git.
|
|
35
30
|
|
|
36
31
|
## Start
|
|
37
32
|
|
|
@@ -55,20 +50,15 @@ Basic GUI flow:
|
|
|
55
50
|
4. Update response plots from the current ROIs.
|
|
56
51
|
5. Save analysis outputs.
|
|
57
52
|
|
|
58
|
-
When a source recording has not been converted yet, twopy converts it first.
|
|
59
|
-
Converted data includes the aligned movie, mean image, stimulus tables,
|
|
60
|
-
photodiode signals, and recording metadata.
|
|
53
|
+
When a source recording has not been converted yet, twopy converts it first. Converted data includes the aligned movie, mean image, stimulus tables, photodiode signals, and recording metadata.
|
|
61
54
|
|
|
62
55
|
## Docs
|
|
63
56
|
|
|
64
|
-
- [GUI guide](docs/gui.md): napari loading, ROI editing, plotting, and saved
|
|
65
|
-
|
|
66
|
-
- [Python API guide](docs/python_api.md): find recordings, convert data, run
|
|
67
|
-
analysis, and open napari from scripts.
|
|
57
|
+
- [GUI guide](docs/gui.md): napari loading, ROI editing, plotting, and saved outputs.
|
|
58
|
+
- [Python API guide](docs/python_api.md): find recordings, convert data, run analysis, and open napari from scripts.
|
|
68
59
|
- [Development guide](docs/development.md): source setup and local checks.
|
|
69
60
|
- [Input data spec](docs/input_data_spec.md): short recording folder contract.
|
|
70
|
-
- [Recording file reference](docs/recording_file_schema.md): detailed source
|
|
71
|
-
and converted file schema.
|
|
61
|
+
- [Recording file reference](docs/recording_file_schema.md): detailed source and converted file schema.
|
|
72
62
|
|
|
73
63
|
## Check
|
|
74
64
|
|
|
@@ -15,6 +15,7 @@ from twopy.analysis import (
|
|
|
15
15
|
BackgroundCorrectionMethod,
|
|
16
16
|
CorrelationFilterOptions,
|
|
17
17
|
DeltaFOverFFitMode,
|
|
18
|
+
DeltaFOverFOptions,
|
|
18
19
|
EpochFrameWindow,
|
|
19
20
|
FrameWindow,
|
|
20
21
|
GroupedRoiResponses,
|
|
@@ -33,25 +34,28 @@ from twopy.analysis import (
|
|
|
33
34
|
apply_motion_artifact_mask_to_delta_f_over_f,
|
|
34
35
|
compute_recording_responses,
|
|
35
36
|
compute_roi_delta_f_over_f,
|
|
37
|
+
default_baseline_epoch_number,
|
|
36
38
|
extract_background_corrected_roi_traces,
|
|
37
39
|
frame_windows_from_photodiode_alignment,
|
|
38
40
|
group_delta_f_over_f_by_epoch,
|
|
39
41
|
interpolate_stimulus_epochs_to_frame_windows,
|
|
40
|
-
|
|
42
|
+
is_baseline_epoch_name,
|
|
41
43
|
load_analysis_outputs,
|
|
42
44
|
make_frame_windows,
|
|
43
45
|
map_stimulus_epochs_to_frame_windows,
|
|
46
|
+
resolve_baseline_frame_windows,
|
|
44
47
|
save_analysis_outputs,
|
|
45
|
-
|
|
48
|
+
select_baseline_frame_windows,
|
|
46
49
|
split_traces_by_frame_windows,
|
|
47
50
|
summarize_epoch_roi_responses,
|
|
48
51
|
summarize_grouped_responses,
|
|
52
|
+
validate_grouped_roi_responses,
|
|
49
53
|
write_response_summary_grouped_csv,
|
|
50
54
|
write_response_summary_trials_csv,
|
|
51
55
|
)
|
|
52
56
|
from twopy.api import find_recordings
|
|
53
57
|
from twopy.conversion import convert_recording_to_twopy, load_source_conversion_inputs
|
|
54
|
-
from twopy.converted import load_converted_recording
|
|
58
|
+
from twopy.converted import load_converted_recording, recording_frame_rate_hz
|
|
55
59
|
from twopy.napari import (
|
|
56
60
|
NapariControlDocks,
|
|
57
61
|
NapariRecordingView,
|
|
@@ -83,6 +87,7 @@ from twopy.spatial import SpatialCrop, SpatialDomain, full_frame_crop
|
|
|
83
87
|
from twopy.stimulus import (
|
|
84
88
|
StimulusSpecificColumnMapping,
|
|
85
89
|
map_stimulus_specific_column,
|
|
90
|
+
stimulus_epoch_names_by_number,
|
|
86
91
|
)
|
|
87
92
|
from twopy.synchronization import (
|
|
88
93
|
AlignedPhotodiodeEvent,
|
|
@@ -114,9 +119,11 @@ __all__ = [
|
|
|
114
119
|
"convert_recording_to_twopy",
|
|
115
120
|
"DEFAULT_RESPONSE_POST_WINDOW_SECONDS",
|
|
116
121
|
"DEFAULT_RESPONSE_PRE_WINDOW_SECONDS",
|
|
122
|
+
"default_baseline_epoch_number",
|
|
117
123
|
"detect_photodiode_events",
|
|
118
124
|
"detect_recording_photodiode_events",
|
|
119
125
|
"DeltaFOverFFitMode",
|
|
126
|
+
"DeltaFOverFOptions",
|
|
120
127
|
"EpochFrameWindow",
|
|
121
128
|
"extract_background_corrected_roi_traces",
|
|
122
129
|
"extract_roi_traces",
|
|
@@ -129,7 +136,7 @@ __all__ = [
|
|
|
129
136
|
"GroupedRoiResponses",
|
|
130
137
|
"InterpolatedEpochMapping",
|
|
131
138
|
"interpolate_stimulus_epochs_to_frame_windows",
|
|
132
|
-
"
|
|
139
|
+
"is_baseline_epoch_name",
|
|
133
140
|
"launch_napari",
|
|
134
141
|
"load_analysis_outputs",
|
|
135
142
|
"load_converted_recording",
|
|
@@ -152,6 +159,8 @@ __all__ = [
|
|
|
152
159
|
"PhotodiodeEventType",
|
|
153
160
|
"PhotodiodeEventSet",
|
|
154
161
|
"ResponseProcessingOptions",
|
|
162
|
+
"recording_frame_rate_hz",
|
|
163
|
+
"resolve_baseline_frame_windows",
|
|
155
164
|
"RoiDeltaFOverF",
|
|
156
165
|
"RoiCorrelationScores",
|
|
157
166
|
"RoiResponseSummary",
|
|
@@ -163,14 +172,16 @@ __all__ = [
|
|
|
163
172
|
"save_analysis_outputs",
|
|
164
173
|
"save_napari_label_rois",
|
|
165
174
|
"save_roi_set",
|
|
166
|
-
"
|
|
175
|
+
"select_baseline_frame_windows",
|
|
167
176
|
"split_traces_by_frame_windows",
|
|
168
177
|
"SmoothingOptions",
|
|
169
178
|
"SpatialCrop",
|
|
170
179
|
"SpatialDomain",
|
|
171
180
|
"StimulusSpecificColumnMapping",
|
|
181
|
+
"stimulus_epoch_names_by_number",
|
|
172
182
|
"summarize_epoch_roi_responses",
|
|
173
183
|
"summarize_grouped_responses",
|
|
184
|
+
"validate_grouped_roi_responses",
|
|
174
185
|
"WindowedRoiResponse",
|
|
175
186
|
"write_response_summary_grouped_csv",
|
|
176
187
|
"write_response_summary_trials_csv",
|
|
@@ -18,6 +18,7 @@ from twopy.analysis.dff import (
|
|
|
18
18
|
RoiDeltaFOverF,
|
|
19
19
|
compute_roi_delta_f_over_f,
|
|
20
20
|
)
|
|
21
|
+
from twopy.analysis.dff_options import DeltaFOverFOptions
|
|
21
22
|
from twopy.analysis.epoch_mapping import (
|
|
22
23
|
InterpolatedEpochMapping,
|
|
23
24
|
interpolate_stimulus_epochs_to_frame_windows,
|
|
@@ -44,18 +45,21 @@ from twopy.analysis.responses import (
|
|
|
44
45
|
RoiResponseSummary,
|
|
45
46
|
RoiResponseTrial,
|
|
46
47
|
group_delta_f_over_f_by_epoch,
|
|
47
|
-
is_gray_epoch_name,
|
|
48
48
|
summarize_epoch_roi_responses,
|
|
49
49
|
summarize_grouped_responses,
|
|
50
|
+
validate_grouped_roi_responses,
|
|
50
51
|
)
|
|
51
52
|
from twopy.analysis.trials import (
|
|
52
53
|
EpochFrameWindow,
|
|
53
54
|
FrameWindow,
|
|
54
55
|
WindowedRoiResponse,
|
|
56
|
+
default_baseline_epoch_number,
|
|
55
57
|
frame_windows_from_photodiode_alignment,
|
|
58
|
+
is_baseline_epoch_name,
|
|
56
59
|
make_frame_windows,
|
|
57
60
|
map_stimulus_epochs_to_frame_windows,
|
|
58
|
-
|
|
61
|
+
resolve_baseline_frame_windows,
|
|
62
|
+
select_baseline_frame_windows,
|
|
59
63
|
split_traces_by_frame_windows,
|
|
60
64
|
)
|
|
61
65
|
from twopy.analysis.workflow import (
|
|
@@ -80,6 +84,8 @@ __all__ = [
|
|
|
80
84
|
"compute_roi_delta_f_over_f",
|
|
81
85
|
"compute_recording_responses",
|
|
82
86
|
"CorrelationFilterOptions",
|
|
87
|
+
"default_baseline_epoch_number",
|
|
88
|
+
"DeltaFOverFOptions",
|
|
83
89
|
"DeltaFOverFFitMode",
|
|
84
90
|
"EpochFrameWindow",
|
|
85
91
|
"extract_background_corrected_roi_traces",
|
|
@@ -90,7 +96,7 @@ __all__ = [
|
|
|
90
96
|
"GroupedRoiResponses",
|
|
91
97
|
"InterpolatedEpochMapping",
|
|
92
98
|
"interpolate_stimulus_epochs_to_frame_windows",
|
|
93
|
-
"
|
|
99
|
+
"is_baseline_epoch_name",
|
|
94
100
|
"load_analysis_outputs",
|
|
95
101
|
"LoadedAnalysisOutputs",
|
|
96
102
|
"LowPassFilterOptions",
|
|
@@ -101,12 +107,14 @@ __all__ = [
|
|
|
101
107
|
"RoiCorrelationScores",
|
|
102
108
|
"RoiResponseSummary",
|
|
103
109
|
"RoiResponseTrial",
|
|
110
|
+
"resolve_baseline_frame_windows",
|
|
104
111
|
"save_analysis_outputs",
|
|
105
|
-
"
|
|
112
|
+
"select_baseline_frame_windows",
|
|
106
113
|
"split_traces_by_frame_windows",
|
|
107
114
|
"SmoothingOptions",
|
|
108
115
|
"summarize_epoch_roi_responses",
|
|
109
116
|
"summarize_grouped_responses",
|
|
117
|
+
"validate_grouped_roi_responses",
|
|
110
118
|
"WindowedRoiResponse",
|
|
111
119
|
"write_response_summary_grouped_csv",
|
|
112
120
|
"write_response_summary_trials_csv",
|
|
@@ -16,6 +16,7 @@ import numpy as np
|
|
|
16
16
|
import numpy.typing as npt
|
|
17
17
|
|
|
18
18
|
from twopy.converted import RecordingData
|
|
19
|
+
from twopy.frame_ranges import normalize_frame_range
|
|
19
20
|
from twopy.roi import RoiSet, TraceStatistic, validate_trace_statistic
|
|
20
21
|
from twopy.spatial import SpatialCrop, SpatialDomain, full_frame_crop
|
|
21
22
|
|
|
@@ -28,7 +29,8 @@ __all__ = [
|
|
|
28
29
|
BackgroundCorrectionMethod = Literal[
|
|
29
30
|
"none",
|
|
30
31
|
"movie_global_percentile",
|
|
31
|
-
"
|
|
32
|
+
"movie_y_stripe_percentile",
|
|
33
|
+
"roi_y_stripe_percentile",
|
|
32
34
|
]
|
|
33
35
|
BackgroundMetadataValue = str | int | float | bool
|
|
34
36
|
|
|
@@ -68,6 +70,8 @@ def extract_background_corrected_roi_traces(
|
|
|
68
70
|
chunk_frames: int = 128,
|
|
69
71
|
statistic: TraceStatistic = "mean",
|
|
70
72
|
global_percentile: float = 10.0,
|
|
73
|
+
stripe_y_height: int | None = None,
|
|
74
|
+
stripe_percentile: float = 20.0,
|
|
71
75
|
local_y_radius: int | None = None,
|
|
72
76
|
local_percentile: float = 20.0,
|
|
73
77
|
spatial_domain: SpatialDomain = "alignment_valid_crop",
|
|
@@ -80,9 +84,10 @@ def extract_background_corrected_roi_traces(
|
|
|
80
84
|
method: Background correction method. ``"none"`` keeps raw traces inside
|
|
81
85
|
the selected spatial domain, ``"movie_global_percentile"`` subtracts
|
|
82
86
|
a framewise background estimated from dim global pixels in the mean
|
|
83
|
-
image,
|
|
84
|
-
|
|
85
|
-
|
|
87
|
+
image, ``"movie_y_stripe_percentile"`` subtracts a framewise
|
|
88
|
+
low-percentile y-stripe background field, and
|
|
89
|
+
``"roi_y_stripe_percentile"`` subtracts a per-ROI y-stripe
|
|
90
|
+
background trace.
|
|
86
91
|
start_frame: Optional first frame to extract.
|
|
87
92
|
stop_frame: Optional exclusive stop frame.
|
|
88
93
|
chunk_frames: Number of movie frames to read per chunk.
|
|
@@ -90,10 +95,15 @@ def extract_background_corrected_roi_traces(
|
|
|
90
95
|
supported.
|
|
91
96
|
global_percentile: Percentile of the recording mean image used to pick
|
|
92
97
|
global background pixels.
|
|
93
|
-
|
|
98
|
+
stripe_y_height: Height in rows for framewise y-stripe background
|
|
99
|
+
correction. When omitted, twopy uses roughly five percent of the
|
|
100
|
+
image height.
|
|
101
|
+
stripe_percentile: Percentile inside each frame stripe used to estimate
|
|
102
|
+
the framewise y-stripe background field.
|
|
103
|
+
local_y_radius: Half-width in rows for the per-ROI y-stripe. When
|
|
94
104
|
omitted, twopy uses roughly five percent of the image height.
|
|
95
|
-
local_percentile: Percentile inside each
|
|
96
|
-
|
|
105
|
+
local_percentile: Percentile inside each ROI y-stripe used to keep dim
|
|
106
|
+
background pixels and reject bright non-ROI structures.
|
|
97
107
|
spatial_domain: Spatial region used for analysis extraction. The default
|
|
98
108
|
uses the alignment-valid crop saved during conversion, matching the
|
|
99
109
|
crop-domain analysis contract while keeping ROI masks stored in
|
|
@@ -115,10 +125,11 @@ def extract_background_corrected_roi_traces(
|
|
|
115
125
|
validate_trace_statistic(statistic)
|
|
116
126
|
_validate_roi_movie_shape(roi_set, recording)
|
|
117
127
|
|
|
118
|
-
frame_start, frame_stop =
|
|
128
|
+
frame_start, frame_stop = normalize_frame_range(
|
|
119
129
|
frame_count=recording.movie.shape[0],
|
|
120
130
|
start_frame=start_frame,
|
|
121
131
|
stop_frame=stop_frame,
|
|
132
|
+
context="trace frame range",
|
|
122
133
|
)
|
|
123
134
|
|
|
124
135
|
crop = _resolve_spatial_domain(recording, spatial_domain)
|
|
@@ -153,9 +164,23 @@ def extract_background_corrected_roi_traces(
|
|
|
153
164
|
spatial_domain=spatial_domain,
|
|
154
165
|
)
|
|
155
166
|
)
|
|
156
|
-
elif method == "
|
|
167
|
+
elif method == "movie_y_stripe_percentile":
|
|
157
168
|
raw_values, background_values, corrected_values, metadata = (
|
|
158
|
-
|
|
169
|
+
_extract_movie_y_stripe_percentile_corrected_traces(
|
|
170
|
+
recording=recording,
|
|
171
|
+
roi_pixel_indices=roi_pixel_indices,
|
|
172
|
+
frame_start=frame_start,
|
|
173
|
+
frame_stop=frame_stop,
|
|
174
|
+
chunk_frames=chunk_frames,
|
|
175
|
+
stripe_y_height=stripe_y_height,
|
|
176
|
+
percentile=stripe_percentile,
|
|
177
|
+
spatial_crop=crop,
|
|
178
|
+
spatial_domain=spatial_domain,
|
|
179
|
+
)
|
|
180
|
+
)
|
|
181
|
+
elif method == "roi_y_stripe_percentile":
|
|
182
|
+
raw_values, background_values, corrected_values, metadata = (
|
|
183
|
+
_extract_roi_y_stripe_percentile_corrected_traces(
|
|
159
184
|
recording=recording,
|
|
160
185
|
roi_masks=cropped_roi_masks,
|
|
161
186
|
roi_pixel_indices=roi_pixel_indices,
|
|
@@ -200,7 +225,8 @@ def _validate_background_method(method: BackgroundCorrectionMethod) -> None:
|
|
|
200
225
|
allowed_methods = {
|
|
201
226
|
"none",
|
|
202
227
|
"movie_global_percentile",
|
|
203
|
-
"
|
|
228
|
+
"movie_y_stripe_percentile",
|
|
229
|
+
"roi_y_stripe_percentile",
|
|
204
230
|
}
|
|
205
231
|
if method not in allowed_methods:
|
|
206
232
|
msg = (
|
|
@@ -332,7 +358,104 @@ def _extract_movie_global_percentile_corrected_traces(
|
|
|
332
358
|
return raw_values, background_values, corrected_values, metadata
|
|
333
359
|
|
|
334
360
|
|
|
335
|
-
def
|
|
361
|
+
def _extract_movie_y_stripe_percentile_corrected_traces(
|
|
362
|
+
*,
|
|
363
|
+
recording: RecordingData,
|
|
364
|
+
roi_pixel_indices: tuple[npt.NDArray[np.int64], ...],
|
|
365
|
+
frame_start: int,
|
|
366
|
+
frame_stop: int,
|
|
367
|
+
chunk_frames: int,
|
|
368
|
+
stripe_y_height: int | None,
|
|
369
|
+
percentile: float,
|
|
370
|
+
spatial_crop: SpatialCrop,
|
|
371
|
+
spatial_domain: SpatialDomain,
|
|
372
|
+
) -> tuple[
|
|
373
|
+
npt.NDArray[np.float64],
|
|
374
|
+
npt.NDArray[np.float64],
|
|
375
|
+
npt.NDArray[np.float64],
|
|
376
|
+
dict[str, BackgroundMetadataValue],
|
|
377
|
+
]:
|
|
378
|
+
"""Extract traces corrected by a framewise y-stripe background field.
|
|
379
|
+
|
|
380
|
+
Args:
|
|
381
|
+
recording: Converted recording with lazy movie batches.
|
|
382
|
+
roi_pixel_indices: Flattened pixel indices for each ROI.
|
|
383
|
+
frame_start: First frame to read.
|
|
384
|
+
frame_stop: Exclusive stop frame.
|
|
385
|
+
chunk_frames: Maximum number of frames per HDF5 read.
|
|
386
|
+
stripe_y_height: Optional stripe height in image rows. When omitted,
|
|
387
|
+
twopy uses roughly five percent of the image height.
|
|
388
|
+
percentile: Percentile used inside each frame stripe.
|
|
389
|
+
spatial_crop: Spatial domain used for background and ROI pixels.
|
|
390
|
+
spatial_domain: User-facing name of ``spatial_crop``.
|
|
391
|
+
|
|
392
|
+
Returns:
|
|
393
|
+
Raw ROI traces, stripe-field background traces, corrected traces, and
|
|
394
|
+
audit metadata.
|
|
395
|
+
|
|
396
|
+
Plain algorithm:
|
|
397
|
+
divide each frame into y-stripes
|
|
398
|
+
take the dim percentile inside each stripe for that frame
|
|
399
|
+
assign each ROI pixel the background from its stripe
|
|
400
|
+
average those background pixels over each ROI
|
|
401
|
+
subtract the stripe background from each ROI pixel
|
|
402
|
+
|
|
403
|
+
This models broad, row-dependent background without assuming that pixels
|
|
404
|
+
near one ROI are safe neuropil reference pixels.
|
|
405
|
+
"""
|
|
406
|
+
_validate_percentile(percentile)
|
|
407
|
+
crop_shape = spatial_crop.crop_image(recording.mean_image).shape
|
|
408
|
+
stripe_height = _resolve_stripe_y_height(
|
|
409
|
+
row_count=crop_shape[0],
|
|
410
|
+
stripe_y_height=stripe_y_height,
|
|
411
|
+
)
|
|
412
|
+
row_slices = _stripe_row_slices(
|
|
413
|
+
row_count=crop_shape[0],
|
|
414
|
+
stripe_y_height=stripe_height,
|
|
415
|
+
)
|
|
416
|
+
frame_count = frame_stop - frame_start
|
|
417
|
+
roi_count = len(roi_pixel_indices)
|
|
418
|
+
raw_values = np.empty((frame_count, roi_count), dtype=np.float64)
|
|
419
|
+
background_values = np.empty((frame_count, roi_count), dtype=np.float64)
|
|
420
|
+
corrected_values = np.empty((frame_count, roi_count), dtype=np.float64)
|
|
421
|
+
|
|
422
|
+
for chunk_start, chunk_stop, frames in recording.movie.iter_frame_batches(
|
|
423
|
+
chunk_frames=chunk_frames,
|
|
424
|
+
start=frame_start,
|
|
425
|
+
stop=frame_stop,
|
|
426
|
+
spatial_crop=spatial_crop,
|
|
427
|
+
):
|
|
428
|
+
chunk_offset = chunk_start - frame_start
|
|
429
|
+
chunk_slice = slice(chunk_offset, chunk_offset + (chunk_stop - chunk_start))
|
|
430
|
+
background_field = _framewise_y_stripe_background_field(
|
|
431
|
+
frames,
|
|
432
|
+
row_slices=row_slices,
|
|
433
|
+
percentile=percentile,
|
|
434
|
+
)
|
|
435
|
+
chunk_flat = frames.reshape(frames.shape[0], -1)
|
|
436
|
+
background_flat = background_field.reshape(background_field.shape[0], -1)
|
|
437
|
+
for roi_index, pixel_indices in enumerate(roi_pixel_indices):
|
|
438
|
+
roi_pixels = chunk_flat[:, pixel_indices]
|
|
439
|
+
roi_background_pixels = background_flat[:, pixel_indices]
|
|
440
|
+
raw_values[chunk_slice, roi_index] = roi_pixels.mean(axis=1)
|
|
441
|
+
background_values[chunk_slice, roi_index] = roi_background_pixels.mean(
|
|
442
|
+
axis=1,
|
|
443
|
+
)
|
|
444
|
+
corrected_pixels = np.maximum(roi_pixels - roi_background_pixels, 0.0)
|
|
445
|
+
corrected_values[chunk_slice, roi_index] = corrected_pixels.mean(axis=1)
|
|
446
|
+
|
|
447
|
+
metadata: dict[str, BackgroundMetadataValue] = {
|
|
448
|
+
"method": "movie_y_stripe_percentile",
|
|
449
|
+
"stripe_y_height": stripe_height,
|
|
450
|
+
"stripe_percentile": float(percentile),
|
|
451
|
+
"stripe_count": len(row_slices),
|
|
452
|
+
"negative_values_clamped": True,
|
|
453
|
+
**_spatial_crop_metadata(spatial_domain, spatial_crop),
|
|
454
|
+
}
|
|
455
|
+
return raw_values, background_values, corrected_values, metadata
|
|
456
|
+
|
|
457
|
+
|
|
458
|
+
def _extract_roi_y_stripe_percentile_corrected_traces(
|
|
336
459
|
*,
|
|
337
460
|
recording: RecordingData,
|
|
338
461
|
roi_masks: npt.NDArray[np.bool_],
|
|
@@ -350,7 +473,7 @@ def _extract_roi_local_y_corrected_traces(
|
|
|
350
473
|
npt.NDArray[np.float64],
|
|
351
474
|
dict[str, BackgroundMetadataValue],
|
|
352
475
|
]:
|
|
353
|
-
"""Extract ROI traces corrected by
|
|
476
|
+
"""Extract ROI traces corrected by per-ROI y-stripe background.
|
|
354
477
|
|
|
355
478
|
Args:
|
|
356
479
|
recording: Converted recording with lazy movie batches.
|
|
@@ -359,25 +482,33 @@ def _extract_roi_local_y_corrected_traces(
|
|
|
359
482
|
frame_start: First frame to read.
|
|
360
483
|
frame_stop: Exclusive stop frame.
|
|
361
484
|
chunk_frames: Maximum number of frames per HDF5 read.
|
|
362
|
-
local_y_radius: Optional row half-width for
|
|
363
|
-
local_percentile: Percentile used to keep dim
|
|
485
|
+
local_y_radius: Optional row half-width for the ROI y-stripe.
|
|
486
|
+
local_percentile: Percentile used to keep dim pixels in each ROI
|
|
487
|
+
y-stripe.
|
|
364
488
|
spatial_crop: Spatial domain used for background and ROI pixels.
|
|
365
489
|
spatial_domain: User-facing name of ``spatial_crop``.
|
|
366
490
|
|
|
367
491
|
Returns:
|
|
368
492
|
Raw ROI traces, local background traces, corrected traces, and metadata.
|
|
369
493
|
|
|
494
|
+
Plain algorithm:
|
|
495
|
+
take rows near that ROI center
|
|
496
|
+
exclude all ROI pixels
|
|
497
|
+
keep dim pixels by percentile
|
|
498
|
+
average those pixels over time
|
|
499
|
+
subtract that trace from that ROI only
|
|
500
|
+
|
|
370
501
|
The method estimates a background trace per ROI from dim pixels that are
|
|
371
|
-
outside every ROI and
|
|
372
|
-
|
|
373
|
-
|
|
502
|
+
outside every ROI and inside that ROI's y-stripe. It is designed for
|
|
503
|
+
stimulus bleedthrough that varies across rows without requiring relaxation
|
|
504
|
+
labeling or connected-component heuristics.
|
|
374
505
|
"""
|
|
375
|
-
radius =
|
|
506
|
+
radius = _resolve_roi_y_stripe_radius(
|
|
376
507
|
row_count=roi_masks.shape[1],
|
|
377
508
|
local_y_radius=local_y_radius,
|
|
378
509
|
)
|
|
379
510
|
_validate_percentile(local_percentile)
|
|
380
|
-
background_indices_by_roi =
|
|
511
|
+
background_indices_by_roi = _roi_y_stripe_background_pixel_indices(
|
|
381
512
|
roi_masks=roi_masks,
|
|
382
513
|
mean_image=spatial_crop.crop_image(recording.mean_image),
|
|
383
514
|
local_y_radius=radius,
|
|
@@ -404,7 +535,7 @@ def _extract_roi_local_y_corrected_traces(
|
|
|
404
535
|
int(pixel_indices.size) for pixel_indices in background_indices_by_roi
|
|
405
536
|
)
|
|
406
537
|
metadata: dict[str, BackgroundMetadataValue] = {
|
|
407
|
-
"method": "
|
|
538
|
+
"method": "roi_y_stripe_percentile",
|
|
408
539
|
"local_y_radius": radius,
|
|
409
540
|
"local_percentile": float(local_percentile),
|
|
410
541
|
"background_pixel_count_min": min(background_counts),
|
|
@@ -523,12 +654,12 @@ def _global_background_pixel_indices(
|
|
|
523
654
|
return background_indices, threshold
|
|
524
655
|
|
|
525
656
|
|
|
526
|
-
def
|
|
657
|
+
def _resolve_roi_y_stripe_radius(
|
|
527
658
|
*,
|
|
528
659
|
row_count: int,
|
|
529
660
|
local_y_radius: int | None,
|
|
530
661
|
) -> int:
|
|
531
|
-
"""Resolve the row radius for
|
|
662
|
+
"""Resolve the row radius for ROI y-stripe background correction.
|
|
532
663
|
|
|
533
664
|
Args:
|
|
534
665
|
row_count: Number of image rows.
|
|
@@ -544,14 +675,81 @@ def _resolve_local_y_radius(
|
|
|
544
675
|
return radius
|
|
545
676
|
|
|
546
677
|
|
|
547
|
-
def
|
|
678
|
+
def _resolve_stripe_y_height(
|
|
679
|
+
*,
|
|
680
|
+
row_count: int,
|
|
681
|
+
stripe_y_height: int | None,
|
|
682
|
+
) -> int:
|
|
683
|
+
"""Resolve y-stripe height for framewise background correction.
|
|
684
|
+
|
|
685
|
+
Args:
|
|
686
|
+
row_count: Number of image rows.
|
|
687
|
+
stripe_y_height: Optional caller-provided stripe height.
|
|
688
|
+
|
|
689
|
+
Returns:
|
|
690
|
+
Positive stripe height in rows.
|
|
691
|
+
"""
|
|
692
|
+
height = (
|
|
693
|
+
max(1, round(row_count / 20)) if stripe_y_height is None else stripe_y_height
|
|
694
|
+
)
|
|
695
|
+
if height <= 0:
|
|
696
|
+
msg = f"stripe_y_height must be positive; got {height}"
|
|
697
|
+
raise ValueError(msg)
|
|
698
|
+
return height
|
|
699
|
+
|
|
700
|
+
|
|
701
|
+
def _stripe_row_slices(
|
|
702
|
+
*,
|
|
703
|
+
row_count: int,
|
|
704
|
+
stripe_y_height: int,
|
|
705
|
+
) -> tuple[slice, ...]:
|
|
706
|
+
"""Split image rows into contiguous y-stripe slices.
|
|
707
|
+
|
|
708
|
+
Args:
|
|
709
|
+
row_count: Number of image rows.
|
|
710
|
+
stripe_y_height: Stripe height in rows.
|
|
711
|
+
|
|
712
|
+
Returns:
|
|
713
|
+
Tuple of row slices covering the image.
|
|
714
|
+
"""
|
|
715
|
+
return tuple(
|
|
716
|
+
slice(start, min(start + stripe_y_height, row_count))
|
|
717
|
+
for start in range(0, row_count, stripe_y_height)
|
|
718
|
+
)
|
|
719
|
+
|
|
720
|
+
|
|
721
|
+
def _framewise_y_stripe_background_field(
|
|
722
|
+
frames: npt.NDArray[np.float64],
|
|
723
|
+
*,
|
|
724
|
+
row_slices: tuple[slice, ...],
|
|
725
|
+
percentile: float,
|
|
726
|
+
) -> npt.NDArray[np.float64]:
|
|
727
|
+
"""Build one low-percentile y-stripe background image per frame.
|
|
728
|
+
|
|
729
|
+
Args:
|
|
730
|
+
frames: Movie chunk shaped ``(frames, rows, columns)``.
|
|
731
|
+
row_slices: Row stripes used for the background field.
|
|
732
|
+
percentile: Percentile used inside each frame stripe.
|
|
733
|
+
|
|
734
|
+
Returns:
|
|
735
|
+
Background field shaped like ``frames``.
|
|
736
|
+
"""
|
|
737
|
+
background = np.empty_like(frames, dtype=np.float64)
|
|
738
|
+
for row_slice in row_slices:
|
|
739
|
+
stripe_values = frames[:, row_slice, :].reshape(frames.shape[0], -1)
|
|
740
|
+
stripe_background = np.percentile(stripe_values, percentile, axis=1)
|
|
741
|
+
background[:, row_slice, :] = stripe_background[:, None, None]
|
|
742
|
+
return background
|
|
743
|
+
|
|
744
|
+
|
|
745
|
+
def _roi_y_stripe_background_pixel_indices(
|
|
548
746
|
*,
|
|
549
747
|
roi_masks: npt.NDArray[np.bool_],
|
|
550
748
|
mean_image: npt.NDArray[np.float64],
|
|
551
749
|
local_y_radius: int,
|
|
552
750
|
local_percentile: float,
|
|
553
751
|
) -> tuple[npt.NDArray[np.int64], ...]:
|
|
554
|
-
"""Find
|
|
752
|
+
"""Find ROI y-stripe background pixels for each ROI.
|
|
555
753
|
|
|
556
754
|
Args:
|
|
557
755
|
roi_masks: ROI masks used to exclude foreground pixels.
|
|
@@ -646,27 +844,3 @@ def _spatial_crop_metadata(
|
|
|
646
844
|
"spatial_original_axis0": spatial_crop.original_shape[0],
|
|
647
845
|
"spatial_original_axis1": spatial_crop.original_shape[1],
|
|
648
846
|
}
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
def _normalize_trace_frame_range(
|
|
652
|
-
*,
|
|
653
|
-
frame_count: int,
|
|
654
|
-
start_frame: int | None,
|
|
655
|
-
stop_frame: int | None,
|
|
656
|
-
) -> tuple[int, int]:
|
|
657
|
-
"""Normalize and validate the frame range for trace extraction.
|
|
658
|
-
|
|
659
|
-
Args:
|
|
660
|
-
frame_count: Number of frames in the movie.
|
|
661
|
-
start_frame: Optional first frame index.
|
|
662
|
-
stop_frame: Optional exclusive stop frame.
|
|
663
|
-
|
|
664
|
-
Returns:
|
|
665
|
-
``(start, stop)`` frame bounds.
|
|
666
|
-
"""
|
|
667
|
-
start = 0 if start_frame is None else start_frame
|
|
668
|
-
stop = frame_count if stop_frame is None else stop_frame
|
|
669
|
-
if start < 0 or stop > frame_count or start >= stop:
|
|
670
|
-
msg = f"Invalid trace frame range [{start}, {stop}) for {frame_count} frames"
|
|
671
|
-
raise ValueError(msg)
|
|
672
|
-
return start, stop
|