TwoPy 0.1.7__tar.gz → 0.1.8__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.7 → twopy-0.1.8}/PKG-INFO +3 -3
- {twopy-0.1.7 → twopy-0.1.8}/README.md +2 -2
- {twopy-0.1.7 → twopy-0.1.8}/pyproject.toml +4 -1
- {twopy-0.1.7 → twopy-0.1.8}/src/twopy/__init__.py +70 -0
- {twopy-0.1.7 → twopy-0.1.8}/src/twopy/analysis/__init__.py +6 -1
- {twopy-0.1.7 → twopy-0.1.8}/src/twopy/analysis/background_subtraction.py +53 -2
- {twopy-0.1.7 → twopy-0.1.8}/src/twopy/analysis/dff_options.py +9 -4
- {twopy-0.1.7 → twopy-0.1.8}/src/twopy/analysis/persistence.py +19 -73
- twopy-0.1.8/src/twopy/analysis/response_context.py +280 -0
- {twopy-0.1.7 → twopy-0.1.8}/src/twopy/analysis/response_processing/persistence.py +19 -48
- {twopy-0.1.7 → twopy-0.1.8}/src/twopy/analysis/trials.py +39 -0
- {twopy-0.1.7 → twopy-0.1.8}/src/twopy/analysis/workflow.py +174 -142
- {twopy-0.1.7 → twopy-0.1.8}/src/twopy/analysis_cache.py +2 -1
- twopy-0.1.8/src/twopy/calibrations/pixel_calibration_profiles.csv +4 -0
- twopy-0.1.8/src/twopy/calibrations/pixel_size.csv +21 -0
- {twopy-0.1.7 → twopy-0.1.8}/src/twopy/config.py +36 -2
- {twopy-0.1.7 → twopy-0.1.8}/src/twopy/conversion/api.py +3 -4
- {twopy-0.1.7 → twopy-0.1.8}/src/twopy/conversion/hdf5_writing.py +6 -7
- {twopy-0.1.7 → twopy-0.1.8}/src/twopy/converted.py +15 -49
- twopy-0.1.8/src/twopy/filenames.py +42 -0
- twopy-0.1.8/src/twopy/hdf5_utils.py +147 -0
- {twopy-0.1.7 → twopy-0.1.8}/src/twopy/napari/controls.py +54 -23
- {twopy-0.1.7 → twopy-0.1.8}/src/twopy/napari/database_search.py +1 -1
- twopy-0.1.8/src/twopy/napari/dims.py +113 -0
- {twopy-0.1.7 → twopy-0.1.8}/src/twopy/napari/display_paths.py +42 -1
- {twopy-0.1.7 → twopy-0.1.8}/src/twopy/napari/interactive.py +41 -11
- {twopy-0.1.7 → twopy-0.1.8}/src/twopy/napari/launcher.py +5 -1
- twopy-0.1.8/src/twopy/napari/live_analysis.py +362 -0
- {twopy-0.1.7 → twopy-0.1.8}/src/twopy/napari/loading.py +1 -1
- {twopy-0.1.7 → twopy-0.1.8}/src/twopy/napari/paths.py +6 -5
- {twopy-0.1.7 → twopy-0.1.8}/src/twopy/napari/plotting/data.py +48 -3
- {twopy-0.1.7 → twopy-0.1.8}/src/twopy/napari/plotting/dff_options.py +43 -8
- {twopy-0.1.7 → twopy-0.1.8}/src/twopy/napari/plotting/docks/options_panel.py +53 -20
- {twopy-0.1.7 → twopy-0.1.8}/src/twopy/napari/plotting/docks/paths.py +14 -7
- {twopy-0.1.7 → twopy-0.1.8}/src/twopy/napari/plotting/docks/plot_area.py +20 -2
- {twopy-0.1.7 → twopy-0.1.8}/src/twopy/napari/plotting/docks/response_plot_widget.py +209 -11
- {twopy-0.1.7 → twopy-0.1.8}/src/twopy/napari/plotting/docks/save_actions.py +3 -2
- {twopy-0.1.7 → twopy-0.1.8}/src/twopy/napari/plotting/export.py +18 -28
- {twopy-0.1.7 → twopy-0.1.8}/src/twopy/napari/plotting/export_controls.py +4 -0
- {twopy-0.1.7 → twopy-0.1.8}/src/twopy/napari/plotting/label_visibility.py +58 -20
- {twopy-0.1.7 → twopy-0.1.8}/src/twopy/napari/plotting/panels.py +24 -21
- twopy-0.1.8/src/twopy/napari/plotting/roi_generation/__init__.py +26 -0
- twopy-0.1.8/src/twopy/napari/plotting/roi_generation/actions.py +130 -0
- twopy-0.1.8/src/twopy/napari/plotting/roi_generation/controls.py +706 -0
- twopy-0.1.8/src/twopy/napari/plotting/roi_generation/options.py +62 -0
- {twopy-0.1.7 → twopy-0.1.8}/src/twopy/napari/plotting/widgets.py +12 -0
- {twopy-0.1.7 → twopy-0.1.8}/src/twopy/napari/responses.py +2 -0
- {twopy-0.1.7 → twopy-0.1.8}/src/twopy/napari/roi.py +58 -1
- {twopy-0.1.7 → twopy-0.1.8}/src/twopy/napari/session.py +49 -2
- {twopy-0.1.7 → twopy-0.1.8}/src/twopy/napari/sidebar.py +7 -0
- twopy-0.1.8/src/twopy/napari/trial_timeline.py +719 -0
- {twopy-0.1.7 → twopy-0.1.8}/src/twopy/napari/types.py +1 -0
- {twopy-0.1.7 → twopy-0.1.8}/src/twopy/napari/viewer.py +14 -1
- {twopy-0.1.7 → twopy-0.1.8}/src/twopy/parity/__init__.py +12 -0
- {twopy-0.1.7 → twopy-0.1.8}/src/twopy/parity/psycho5.py +88 -2
- twopy-0.1.8/src/twopy/parity/roi_extraction.py +302 -0
- twopy-0.1.8/src/twopy/pixel_calibration.py +573 -0
- twopy-0.1.8/src/twopy/pixel_calibration_profiles.py +563 -0
- twopy-0.1.8/src/twopy/response_roi_extraction.py +750 -0
- {twopy-0.1.7 → twopy-0.1.8}/src/twopy/roi.py +3 -21
- twopy-0.1.8/src/twopy/roi_extraction.py +550 -0
- twopy-0.1.8/src/twopy/roi_mask_cleanup.py +74 -0
- {twopy-0.1.7 → twopy-0.1.8}/src/twopy.egg-info/PKG-INFO +3 -3
- {twopy-0.1.7 → twopy-0.1.8}/src/twopy.egg-info/SOURCES.txt +26 -1
- {twopy-0.1.7 → twopy-0.1.8}/tests/test_background_subtraction.py +29 -0
- {twopy-0.1.7 → twopy-0.1.8}/tests/test_config.py +28 -0
- twopy-0.1.8/tests/test_filenames.py +102 -0
- twopy-0.1.8/tests/test_hdf5_utils.py +83 -0
- {twopy-0.1.7 → twopy-0.1.8}/tests/test_napari.py +1279 -23
- {twopy-0.1.7 → twopy-0.1.8}/tests/test_parity_psycho5.py +43 -0
- twopy-0.1.8/tests/test_parity_roi_extraction.py +183 -0
- twopy-0.1.8/tests/test_pixel_calibration.py +224 -0
- twopy-0.1.8/tests/test_pixel_calibration_profiles.py +226 -0
- twopy-0.1.8/tests/test_response_roi_extraction.py +354 -0
- twopy-0.1.8/tests/test_roi_extraction.py +171 -0
- twopy-0.1.8/tests/test_roi_mask_cleanup.py +65 -0
- {twopy-0.1.7 → twopy-0.1.8}/tests/test_trials.py +20 -0
- {twopy-0.1.7 → twopy-0.1.8}/tests/test_workflow.py +138 -0
- twopy-0.1.7/src/twopy/napari/constants.py +0 -13
- {twopy-0.1.7 → twopy-0.1.8}/setup.cfg +0 -0
- {twopy-0.1.7 → twopy-0.1.8}/src/twopy/_segments.py +0 -0
- {twopy-0.1.7 → twopy-0.1.8}/src/twopy/_version.py +0 -0
- {twopy-0.1.7 → twopy-0.1.8}/src/twopy/analysis/dff.py +0 -0
- {twopy-0.1.7 → twopy-0.1.8}/src/twopy/analysis/epoch_mapping.py +0 -0
- {twopy-0.1.7 → twopy-0.1.8}/src/twopy/analysis/motion.py +0 -0
- {twopy-0.1.7 → twopy-0.1.8}/src/twopy/analysis/response_processing/__init__.py +0 -0
- {twopy-0.1.7 → twopy-0.1.8}/src/twopy/analysis/response_processing/apply.py +0 -0
- {twopy-0.1.7 → twopy-0.1.8}/src/twopy/analysis/response_processing/options.py +0 -0
- {twopy-0.1.7 → twopy-0.1.8}/src/twopy/analysis/response_processing/qc.py +0 -0
- {twopy-0.1.7 → twopy-0.1.8}/src/twopy/analysis/response_processing/signals.py +0 -0
- {twopy-0.1.7 → twopy-0.1.8}/src/twopy/analysis/response_window_options.py +0 -0
- {twopy-0.1.7 → twopy-0.1.8}/src/twopy/analysis/responses.py +0 -0
- {twopy-0.1.7 → twopy-0.1.8}/src/twopy/api.py +0 -0
- {twopy-0.1.7 → twopy-0.1.8}/src/twopy/conversion/__init__.py +0 -0
- {twopy-0.1.7 → twopy-0.1.8}/src/twopy/conversion/alignment_crop.py +0 -0
- {twopy-0.1.7 → twopy-0.1.8}/src/twopy/conversion/matlab_values.py +0 -0
- {twopy-0.1.7 → twopy-0.1.8}/src/twopy/conversion/source_loading.py +0 -0
- {twopy-0.1.7 → twopy-0.1.8}/src/twopy/conversion/stimulus_code.py +0 -0
- {twopy-0.1.7 → twopy-0.1.8}/src/twopy/conversion/types.py +0 -0
- {twopy-0.1.7 → twopy-0.1.8}/src/twopy/database/__init__.py +0 -0
- {twopy-0.1.7 → twopy-0.1.8}/src/twopy/database/cache.py +0 -0
- {twopy-0.1.7 → twopy-0.1.8}/src/twopy/database/catalog.py +0 -0
- {twopy-0.1.7 → twopy-0.1.8}/src/twopy/database/connection.py +0 -0
- {twopy-0.1.7 → twopy-0.1.8}/src/twopy/database/generic.py +0 -0
- {twopy-0.1.7 → twopy-0.1.8}/src/twopy/database/modeled.py +0 -0
- {twopy-0.1.7 → twopy-0.1.8}/src/twopy/database/search.py +0 -0
- {twopy-0.1.7 → twopy-0.1.8}/src/twopy/database/sql.py +0 -0
- {twopy-0.1.7 → twopy-0.1.8}/src/twopy/database/types.py +0 -0
- {twopy-0.1.7 → twopy-0.1.8}/src/twopy/frame_ranges.py +0 -0
- {twopy-0.1.7 → twopy-0.1.8}/src/twopy/inspection.py +0 -0
- {twopy-0.1.7 → twopy-0.1.8}/src/twopy/matlab.py +0 -0
- {twopy-0.1.7 → twopy-0.1.8}/src/twopy/napari/__init__.py +0 -0
- {twopy-0.1.7 → twopy-0.1.8}/src/twopy/napari/__main__.py +0 -0
- {twopy-0.1.7 → twopy-0.1.8}/src/twopy/napari/display.py +0 -0
- {twopy-0.1.7 → twopy-0.1.8}/src/twopy/napari/errors.py +0 -0
- {twopy-0.1.7 → twopy-0.1.8}/src/twopy/napari/movie.py +0 -0
- {twopy-0.1.7 → twopy-0.1.8}/src/twopy/napari/plotting/__init__.py +0 -0
- {twopy-0.1.7 → twopy-0.1.8}/src/twopy/napari/plotting/docks/__init__.py +0 -0
- {twopy-0.1.7 → twopy-0.1.8}/src/twopy/napari/plotting/docks/api.py +0 -0
- {twopy-0.1.7 → twopy-0.1.8}/src/twopy/napari/plotting/docks/dynamic_options.py +0 -0
- {twopy-0.1.7 → twopy-0.1.8}/src/twopy/napari/plotting/docks/visibility_state.py +0 -0
- {twopy-0.1.7 → twopy-0.1.8}/src/twopy/napari/plotting/form_controls.py +0 -0
- {twopy-0.1.7 → twopy-0.1.8}/src/twopy/napari/plotting/normalization_options.py +0 -0
- {twopy-0.1.7 → twopy-0.1.8}/src/twopy/napari/plotting/options.py +0 -0
- {twopy-0.1.7 → twopy-0.1.8}/src/twopy/napari/plotting/processing_options.py +0 -0
- {twopy-0.1.7 → twopy-0.1.8}/src/twopy/napari/plotting/response_window_options.py +0 -0
- {twopy-0.1.7 → twopy-0.1.8}/src/twopy/napari/protocols.py +0 -0
- {twopy-0.1.7 → twopy-0.1.8}/src/twopy/napari/state.py +0 -0
- {twopy-0.1.7 → twopy-0.1.8}/src/twopy/napari/text.py +0 -0
- {twopy-0.1.7 → twopy-0.1.8}/src/twopy/parity/dff.py +0 -0
- {twopy-0.1.7 → twopy-0.1.8}/src/twopy/parity/roi.py +0 -0
- {twopy-0.1.7 → twopy-0.1.8}/src/twopy/parity/saved_analysis.py +0 -0
- {twopy-0.1.7 → twopy-0.1.8}/src/twopy/photodiode.py +0 -0
- {twopy-0.1.7 → twopy-0.1.8}/src/twopy/photodiode_classification.py +0 -0
- {twopy-0.1.7 → twopy-0.1.8}/src/twopy/session.py +0 -0
- {twopy-0.1.7 → twopy-0.1.8}/src/twopy/spatial.py +0 -0
- {twopy-0.1.7 → twopy-0.1.8}/src/twopy/stimulus.py +0 -0
- {twopy-0.1.7 → twopy-0.1.8}/src/twopy/synchronization.py +0 -0
- {twopy-0.1.7 → twopy-0.1.8}/src/twopy/typing_guards.py +0 -0
- {twopy-0.1.7 → twopy-0.1.8}/src/twopy.egg-info/dependency_links.txt +0 -0
- {twopy-0.1.7 → twopy-0.1.8}/src/twopy.egg-info/entry_points.txt +0 -0
- {twopy-0.1.7 → twopy-0.1.8}/src/twopy.egg-info/requires.txt +0 -0
- {twopy-0.1.7 → twopy-0.1.8}/src/twopy.egg-info/top_level.txt +0 -0
- {twopy-0.1.7 → twopy-0.1.8}/tests/test_api.py +0 -0
- {twopy-0.1.7 → twopy-0.1.8}/tests/test_conversion.py +0 -0
- {twopy-0.1.7 → twopy-0.1.8}/tests/test_converted.py +0 -0
- {twopy-0.1.7 → twopy-0.1.8}/tests/test_database.py +0 -0
- {twopy-0.1.7 → twopy-0.1.8}/tests/test_dff.py +0 -0
- {twopy-0.1.7 → twopy-0.1.8}/tests/test_epoch_mapping.py +0 -0
- {twopy-0.1.7 → twopy-0.1.8}/tests/test_frame_ranges.py +0 -0
- {twopy-0.1.7 → twopy-0.1.8}/tests/test_import.py +0 -0
- {twopy-0.1.7 → twopy-0.1.8}/tests/test_inspection.py +0 -0
- {twopy-0.1.7 → twopy-0.1.8}/tests/test_matlab.py +0 -0
- {twopy-0.1.7 → twopy-0.1.8}/tests/test_motion.py +0 -0
- {twopy-0.1.7 → twopy-0.1.8}/tests/test_packaging.py +0 -0
- {twopy-0.1.7 → twopy-0.1.8}/tests/test_parity.py +0 -0
- {twopy-0.1.7 → twopy-0.1.8}/tests/test_persistence.py +0 -0
- {twopy-0.1.7 → twopy-0.1.8}/tests/test_photodiode_classification.py +0 -0
- {twopy-0.1.7 → twopy-0.1.8}/tests/test_real_fixture.py +0 -0
- {twopy-0.1.7 → twopy-0.1.8}/tests/test_response_processing.py +0 -0
- {twopy-0.1.7 → twopy-0.1.8}/tests/test_response_window_options.py +0 -0
- {twopy-0.1.7 → twopy-0.1.8}/tests/test_responses.py +0 -0
- {twopy-0.1.7 → twopy-0.1.8}/tests/test_roi.py +0 -0
- {twopy-0.1.7 → twopy-0.1.8}/tests/test_saved_analysis.py +0 -0
- {twopy-0.1.7 → twopy-0.1.8}/tests/test_session.py +0 -0
- {twopy-0.1.7 → twopy-0.1.8}/tests/test_stimulus.py +0 -0
- {twopy-0.1.7 → twopy-0.1.8}/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.8
|
|
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
|
|
@@ -64,10 +64,10 @@ twopy /path/to/recording_data.h5
|
|
|
64
64
|
|
|
65
65
|
Basic GUI flow:
|
|
66
66
|
|
|
67
|
-
1.
|
|
67
|
+
1. Search the database or load a recording manually.
|
|
68
68
|
2. Draw or edit ROIs in the `rois` Labels layer.
|
|
69
69
|
3. Update response plots from the current ROIs.
|
|
70
|
-
4. Click Save ROIs + analysis when the plots look right.
|
|
70
|
+
4. Click Save ROIs + analysis in Export when the plots look right.
|
|
71
71
|
|
|
72
72
|
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.
|
|
73
73
|
|
|
@@ -44,10 +44,10 @@ twopy /path/to/recording_data.h5
|
|
|
44
44
|
|
|
45
45
|
Basic GUI flow:
|
|
46
46
|
|
|
47
|
-
1.
|
|
47
|
+
1. Search the database or load a recording manually.
|
|
48
48
|
2. Draw or edit ROIs in the `rois` Labels layer.
|
|
49
49
|
3. Update response plots from the current ROIs.
|
|
50
|
-
4. Click Save ROIs + analysis when the plots look right.
|
|
50
|
+
4. Click Save ROIs + analysis in Export when the plots look right.
|
|
51
51
|
|
|
52
52
|
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.
|
|
53
53
|
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "twopy"
|
|
7
|
-
version = "0.1.
|
|
7
|
+
version = "0.1.8"
|
|
8
8
|
description = "Two-photon imaging analysis tool with a napari interface."
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.13,<3.14"
|
|
@@ -32,6 +32,9 @@ twopy = "twopy.napari:main"
|
|
|
32
32
|
[tool.setuptools.packages.find]
|
|
33
33
|
where = ["src"]
|
|
34
34
|
|
|
35
|
+
[tool.setuptools.package-data]
|
|
36
|
+
twopy = ["calibrations/*.csv"]
|
|
37
|
+
|
|
35
38
|
[tool.ruff]
|
|
36
39
|
line-length = 88
|
|
37
40
|
src = ["src", "tests"]
|
|
@@ -14,6 +14,7 @@ from twopy.analysis import (
|
|
|
14
14
|
BackgroundCorrectedRoiTraces,
|
|
15
15
|
BackgroundCorrectionMethod,
|
|
16
16
|
CorrelationFilterOptions,
|
|
17
|
+
DeltaFOverFBaselineMode,
|
|
17
18
|
DeltaFOverFFitMode,
|
|
18
19
|
DeltaFOverFOptions,
|
|
19
20
|
EpochFrameWindow,
|
|
@@ -35,6 +36,7 @@ from twopy.analysis import (
|
|
|
35
36
|
compute_recording_responses,
|
|
36
37
|
compute_roi_delta_f_over_f,
|
|
37
38
|
default_baseline_epoch_number,
|
|
39
|
+
default_recording_baseline_epoch_number,
|
|
38
40
|
extract_background_corrected_roi_traces,
|
|
39
41
|
frame_windows_from_photodiode_alignment,
|
|
40
42
|
group_delta_f_over_f_by_epoch,
|
|
@@ -43,6 +45,7 @@ from twopy.analysis import (
|
|
|
43
45
|
load_analysis_outputs,
|
|
44
46
|
make_frame_windows,
|
|
45
47
|
map_stimulus_epochs_to_frame_windows,
|
|
48
|
+
no_baseline_epoch_frame_windows,
|
|
46
49
|
resolve_baseline_frame_windows,
|
|
47
50
|
save_analysis_outputs,
|
|
48
51
|
select_baseline_frame_windows,
|
|
@@ -73,6 +76,33 @@ from twopy.photodiode_classification import (
|
|
|
73
76
|
PhotodiodeEventType,
|
|
74
77
|
classify_recording_photodiode_events,
|
|
75
78
|
)
|
|
79
|
+
from twopy.pixel_calibration import (
|
|
80
|
+
DEFAULT_PIXEL_CALIBRATION_PATH,
|
|
81
|
+
PixelCalibrationResolution,
|
|
82
|
+
PixelCalibrationResolutionMethod,
|
|
83
|
+
PixelCalibrationRow,
|
|
84
|
+
load_pixel_calibrations,
|
|
85
|
+
resolve_pixel_size_um,
|
|
86
|
+
)
|
|
87
|
+
from twopy.pixel_calibration_profiles import (
|
|
88
|
+
DEFAULT_PIXEL_CALIBRATION_PROFILE_PATH,
|
|
89
|
+
PixelCalibrationGroup,
|
|
90
|
+
PixelCalibrationProfile,
|
|
91
|
+
PixelCalibrationProfileMapping,
|
|
92
|
+
PixelCalibrationProfileSource,
|
|
93
|
+
load_pixel_calibration_profile_mappings,
|
|
94
|
+
resolve_pixel_calibration_profile,
|
|
95
|
+
select_pixel_calibration_group,
|
|
96
|
+
)
|
|
97
|
+
from twopy.response_roi_extraction import (
|
|
98
|
+
ResponseNormalization,
|
|
99
|
+
ResponsePolarity,
|
|
100
|
+
ResponseStatistic,
|
|
101
|
+
ResponseWatershedExtraction,
|
|
102
|
+
ResponseWatershedScoreImages,
|
|
103
|
+
extract_response_watershed_rois,
|
|
104
|
+
response_watershed_roi_set,
|
|
105
|
+
)
|
|
76
106
|
from twopy.roi import (
|
|
77
107
|
RoiSet,
|
|
78
108
|
RoiTraces,
|
|
@@ -83,6 +113,15 @@ from twopy.roi import (
|
|
|
83
113
|
roi_set_to_label_image,
|
|
84
114
|
save_roi_set,
|
|
85
115
|
)
|
|
116
|
+
from twopy.roi_extraction import (
|
|
117
|
+
RoiExtractionConfig,
|
|
118
|
+
RoiExtractionMethod,
|
|
119
|
+
extract_rois_from_image,
|
|
120
|
+
grid_roi_set,
|
|
121
|
+
grid_roi_set_microns,
|
|
122
|
+
grid_size_pixels_from_microns,
|
|
123
|
+
watershed_roi_set,
|
|
124
|
+
)
|
|
86
125
|
from twopy.spatial import SpatialCrop, SpatialDomain, full_frame_crop
|
|
87
126
|
from twopy.stimulus import (
|
|
88
127
|
StimulusSpecificColumnMapping,
|
|
@@ -119,13 +158,19 @@ __all__ = [
|
|
|
119
158
|
"convert_recording_to_twopy",
|
|
120
159
|
"DEFAULT_RESPONSE_POST_WINDOW_SECONDS",
|
|
121
160
|
"DEFAULT_RESPONSE_PRE_WINDOW_SECONDS",
|
|
161
|
+
"DEFAULT_PIXEL_CALIBRATION_PATH",
|
|
162
|
+
"DEFAULT_PIXEL_CALIBRATION_PROFILE_PATH",
|
|
122
163
|
"default_baseline_epoch_number",
|
|
164
|
+
"default_recording_baseline_epoch_number",
|
|
123
165
|
"detect_photodiode_events",
|
|
124
166
|
"detect_recording_photodiode_events",
|
|
167
|
+
"DeltaFOverFBaselineMode",
|
|
125
168
|
"DeltaFOverFFitMode",
|
|
126
169
|
"DeltaFOverFOptions",
|
|
127
170
|
"EpochFrameWindow",
|
|
128
171
|
"extract_background_corrected_roi_traces",
|
|
172
|
+
"extract_response_watershed_rois",
|
|
173
|
+
"extract_rois_from_image",
|
|
129
174
|
"extract_roi_traces",
|
|
130
175
|
"find_recordings",
|
|
131
176
|
"frame_windows_from_photodiode_alignment",
|
|
@@ -134,12 +179,17 @@ __all__ = [
|
|
|
134
179
|
"group_delta_f_over_f_by_epoch",
|
|
135
180
|
"GroupedRoiResponseSummary",
|
|
136
181
|
"GroupedRoiResponses",
|
|
182
|
+
"grid_roi_set",
|
|
183
|
+
"grid_roi_set_microns",
|
|
184
|
+
"grid_size_pixels_from_microns",
|
|
137
185
|
"InterpolatedEpochMapping",
|
|
138
186
|
"interpolate_stimulus_epochs_to_frame_windows",
|
|
139
187
|
"is_baseline_epoch_name",
|
|
140
188
|
"launch_napari",
|
|
141
189
|
"load_analysis_outputs",
|
|
142
190
|
"load_converted_recording",
|
|
191
|
+
"load_pixel_calibrations",
|
|
192
|
+
"load_pixel_calibration_profile_mappings",
|
|
143
193
|
"load_roi_set",
|
|
144
194
|
"load_source_conversion_inputs",
|
|
145
195
|
"LoadedAnalysisOutputs",
|
|
@@ -148,6 +198,7 @@ __all__ = [
|
|
|
148
198
|
"make_frame_windows",
|
|
149
199
|
"make_roi_set",
|
|
150
200
|
"map_stimulus_epochs_to_frame_windows",
|
|
201
|
+
"no_baseline_epoch_frame_windows",
|
|
151
202
|
"map_stimulus_specific_column",
|
|
152
203
|
"NapariRecordingView",
|
|
153
204
|
"NapariSidebarWidgets",
|
|
@@ -158,9 +209,26 @@ __all__ = [
|
|
|
158
209
|
"PhotodiodeEvent",
|
|
159
210
|
"PhotodiodeEventType",
|
|
160
211
|
"PhotodiodeEventSet",
|
|
212
|
+
"PixelCalibrationResolution",
|
|
213
|
+
"PixelCalibrationResolutionMethod",
|
|
214
|
+
"PixelCalibrationGroup",
|
|
215
|
+
"PixelCalibrationProfile",
|
|
216
|
+
"PixelCalibrationProfileMapping",
|
|
217
|
+
"PixelCalibrationProfileSource",
|
|
218
|
+
"PixelCalibrationRow",
|
|
219
|
+
"ResponseNormalization",
|
|
220
|
+
"ResponsePolarity",
|
|
161
221
|
"ResponseProcessingOptions",
|
|
222
|
+
"ResponseStatistic",
|
|
223
|
+
"ResponseWatershedExtraction",
|
|
224
|
+
"ResponseWatershedScoreImages",
|
|
162
225
|
"recording_frame_rate_hz",
|
|
226
|
+
"resolve_pixel_size_um",
|
|
227
|
+
"resolve_pixel_calibration_profile",
|
|
163
228
|
"resolve_baseline_frame_windows",
|
|
229
|
+
"response_watershed_roi_set",
|
|
230
|
+
"RoiExtractionConfig",
|
|
231
|
+
"RoiExtractionMethod",
|
|
164
232
|
"RoiDeltaFOverF",
|
|
165
233
|
"RoiCorrelationScores",
|
|
166
234
|
"RoiResponseSummary",
|
|
@@ -173,6 +241,7 @@ __all__ = [
|
|
|
173
241
|
"save_napari_label_rois",
|
|
174
242
|
"save_roi_set",
|
|
175
243
|
"select_baseline_frame_windows",
|
|
244
|
+
"select_pixel_calibration_group",
|
|
176
245
|
"split_traces_by_frame_windows",
|
|
177
246
|
"SmoothingOptions",
|
|
178
247
|
"SpatialCrop",
|
|
@@ -182,6 +251,7 @@ __all__ = [
|
|
|
182
251
|
"summarize_epoch_roi_responses",
|
|
183
252
|
"summarize_grouped_responses",
|
|
184
253
|
"validate_grouped_roi_responses",
|
|
254
|
+
"watershed_roi_set",
|
|
185
255
|
"WindowedRoiResponse",
|
|
186
256
|
"write_response_summary_grouped_csv",
|
|
187
257
|
"write_response_summary_trials_csv",
|
|
@@ -18,7 +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
|
+
from twopy.analysis.dff_options import DeltaFOverFBaselineMode, DeltaFOverFOptions
|
|
22
22
|
from twopy.analysis.epoch_mapping import (
|
|
23
23
|
InterpolatedEpochMapping,
|
|
24
24
|
interpolate_stimulus_epochs_to_frame_windows,
|
|
@@ -32,6 +32,7 @@ from twopy.analysis.persistence import (
|
|
|
32
32
|
write_response_summary_grouped_csv,
|
|
33
33
|
write_response_summary_trials_csv,
|
|
34
34
|
)
|
|
35
|
+
from twopy.analysis.response_context import default_recording_baseline_epoch_number
|
|
35
36
|
from twopy.analysis.response_processing import (
|
|
36
37
|
CorrelationFilterOptions,
|
|
37
38
|
LowPassFilterOptions,
|
|
@@ -58,6 +59,7 @@ from twopy.analysis.trials import (
|
|
|
58
59
|
is_baseline_epoch_name,
|
|
59
60
|
make_frame_windows,
|
|
60
61
|
map_stimulus_epochs_to_frame_windows,
|
|
62
|
+
no_baseline_epoch_frame_windows,
|
|
61
63
|
resolve_baseline_frame_windows,
|
|
62
64
|
select_baseline_frame_windows,
|
|
63
65
|
split_traces_by_frame_windows,
|
|
@@ -85,6 +87,8 @@ __all__ = [
|
|
|
85
87
|
"compute_recording_responses",
|
|
86
88
|
"CorrelationFilterOptions",
|
|
87
89
|
"default_baseline_epoch_number",
|
|
90
|
+
"default_recording_baseline_epoch_number",
|
|
91
|
+
"DeltaFOverFBaselineMode",
|
|
88
92
|
"DeltaFOverFOptions",
|
|
89
93
|
"DeltaFOverFFitMode",
|
|
90
94
|
"EpochFrameWindow",
|
|
@@ -102,6 +106,7 @@ __all__ = [
|
|
|
102
106
|
"LowPassFilterOptions",
|
|
103
107
|
"make_frame_windows",
|
|
104
108
|
"map_stimulus_epochs_to_frame_windows",
|
|
109
|
+
"no_baseline_epoch_frame_windows",
|
|
105
110
|
"RoiDeltaFOverF",
|
|
106
111
|
"ResponseProcessingOptions",
|
|
107
112
|
"RoiCorrelationScores",
|
|
@@ -9,6 +9,7 @@ trace extraction stay in ``twopy.roi``; dF/F and trial analysis can consume the
|
|
|
9
9
|
auditable outputs produced here.
|
|
10
10
|
"""
|
|
11
11
|
|
|
12
|
+
from collections.abc import Callable
|
|
12
13
|
from dataclasses import dataclass
|
|
13
14
|
from typing import Literal
|
|
14
15
|
|
|
@@ -75,6 +76,7 @@ def extract_background_corrected_roi_traces(
|
|
|
75
76
|
local_y_radius: int | None = None,
|
|
76
77
|
local_percentile: float = 20.0,
|
|
77
78
|
spatial_domain: SpatialDomain = "alignment_valid_crop",
|
|
79
|
+
check_cancelled: Callable[[], None] | None = None,
|
|
78
80
|
) -> BackgroundCorrectedRoiTraces:
|
|
79
81
|
"""Extract ROI traces plus an explicit background correction.
|
|
80
82
|
|
|
@@ -108,6 +110,8 @@ def extract_background_corrected_roi_traces(
|
|
|
108
110
|
uses the alignment-valid crop saved during conversion, matching the
|
|
109
111
|
crop-domain analysis contract while keeping ROI masks stored in
|
|
110
112
|
full-frame coordinates.
|
|
113
|
+
check_cancelled: Optional callback that raises ``CancelledError`` when
|
|
114
|
+
an interactive caller has superseded this computation.
|
|
111
115
|
|
|
112
116
|
Returns:
|
|
113
117
|
``BackgroundCorrectedRoiTraces`` with raw, background, and corrected
|
|
@@ -115,7 +119,10 @@ def extract_background_corrected_roi_traces(
|
|
|
115
119
|
|
|
116
120
|
Raises:
|
|
117
121
|
ValueError: If masks do not match the movie or a background mask is
|
|
118
|
-
empty.
|
|
122
|
+
empty. ROI y-stripe background correction also raises when the
|
|
123
|
+
selected spatial domain has no unlabeled pixels outside all ROIs,
|
|
124
|
+
which is expected for dense grid ROIs that cover the whole analysis
|
|
125
|
+
crop.
|
|
119
126
|
|
|
120
127
|
The function streams movie chunks from HDF5 and never loads the full movie
|
|
121
128
|
into memory. Each method records enough metadata to audit how the background
|
|
@@ -124,6 +131,7 @@ def extract_background_corrected_roi_traces(
|
|
|
124
131
|
_validate_background_method(method)
|
|
125
132
|
validate_trace_statistic(statistic)
|
|
126
133
|
_validate_roi_movie_shape(roi_set, recording)
|
|
134
|
+
_check_cancelled(check_cancelled)
|
|
127
135
|
|
|
128
136
|
frame_start, frame_stop = normalize_frame_range(
|
|
129
137
|
frame_count=recording.movie.shape[0],
|
|
@@ -144,6 +152,7 @@ def extract_background_corrected_roi_traces(
|
|
|
144
152
|
frame_stop=frame_stop,
|
|
145
153
|
chunk_frames=chunk_frames,
|
|
146
154
|
spatial_crop=crop,
|
|
155
|
+
check_cancelled=check_cancelled,
|
|
147
156
|
)
|
|
148
157
|
background_values = np.zeros_like(raw_values)
|
|
149
158
|
corrected_values = raw_values.copy()
|
|
@@ -162,6 +171,7 @@ def extract_background_corrected_roi_traces(
|
|
|
162
171
|
percentile=global_percentile,
|
|
163
172
|
spatial_crop=crop,
|
|
164
173
|
spatial_domain=spatial_domain,
|
|
174
|
+
check_cancelled=check_cancelled,
|
|
165
175
|
)
|
|
166
176
|
)
|
|
167
177
|
elif method == "movie_y_stripe_percentile":
|
|
@@ -176,6 +186,7 @@ def extract_background_corrected_roi_traces(
|
|
|
176
186
|
percentile=stripe_percentile,
|
|
177
187
|
spatial_crop=crop,
|
|
178
188
|
spatial_domain=spatial_domain,
|
|
189
|
+
check_cancelled=check_cancelled,
|
|
179
190
|
)
|
|
180
191
|
)
|
|
181
192
|
elif method == "roi_y_stripe_percentile":
|
|
@@ -191,6 +202,7 @@ def extract_background_corrected_roi_traces(
|
|
|
191
202
|
local_percentile=local_percentile,
|
|
192
203
|
spatial_crop=crop,
|
|
193
204
|
spatial_domain=spatial_domain,
|
|
205
|
+
check_cancelled=check_cancelled,
|
|
194
206
|
)
|
|
195
207
|
)
|
|
196
208
|
|
|
@@ -244,6 +256,7 @@ def _extract_mean_traces_from_indices(
|
|
|
244
256
|
frame_stop: int,
|
|
245
257
|
chunk_frames: int,
|
|
246
258
|
spatial_crop: SpatialCrop,
|
|
259
|
+
check_cancelled: Callable[[], None] | None = None,
|
|
247
260
|
) -> npt.NDArray[np.float64]:
|
|
248
261
|
"""Extract mean traces for arbitrary flattened pixel-index groups.
|
|
249
262
|
|
|
@@ -255,6 +268,7 @@ def _extract_mean_traces_from_indices(
|
|
|
255
268
|
frame_stop: Exclusive stop frame.
|
|
256
269
|
chunk_frames: Maximum number of frames per HDF5 read.
|
|
257
270
|
spatial_crop: Spatial domain read from each movie frame.
|
|
271
|
+
check_cancelled: Optional callback that raises when work is obsolete.
|
|
258
272
|
|
|
259
273
|
Returns:
|
|
260
274
|
Array shaped ``(frames, traces)``.
|
|
@@ -264,12 +278,14 @@ def _extract_mean_traces_from_indices(
|
|
|
264
278
|
rather than total recording length.
|
|
265
279
|
"""
|
|
266
280
|
traces = np.empty((frame_stop - frame_start, len(pixel_indices_by_trace)))
|
|
281
|
+
_check_cancelled(check_cancelled)
|
|
267
282
|
for chunk_start, chunk_stop, frames in recording.movie.iter_frame_batches(
|
|
268
283
|
chunk_frames=chunk_frames,
|
|
269
284
|
start=frame_start,
|
|
270
285
|
stop=frame_stop,
|
|
271
286
|
spatial_crop=spatial_crop,
|
|
272
287
|
):
|
|
288
|
+
_check_cancelled(check_cancelled)
|
|
273
289
|
chunk_offset = chunk_start - frame_start
|
|
274
290
|
chunk_slice = slice(chunk_offset, chunk_offset + (chunk_stop - chunk_start))
|
|
275
291
|
chunk_flat = frames.reshape(frames.shape[0], -1)
|
|
@@ -277,6 +293,7 @@ def _extract_mean_traces_from_indices(
|
|
|
277
293
|
traces[chunk_slice, trace_index] = chunk_flat[:, pixel_indices].mean(
|
|
278
294
|
axis=1,
|
|
279
295
|
)
|
|
296
|
+
_check_cancelled(check_cancelled)
|
|
280
297
|
|
|
281
298
|
return traces
|
|
282
299
|
|
|
@@ -291,6 +308,7 @@ def _extract_movie_global_percentile_corrected_traces(
|
|
|
291
308
|
percentile: float,
|
|
292
309
|
spatial_crop: SpatialCrop,
|
|
293
310
|
spatial_domain: SpatialDomain,
|
|
311
|
+
check_cancelled: Callable[[], None] | None = None,
|
|
294
312
|
) -> tuple[
|
|
295
313
|
npt.NDArray[np.float64],
|
|
296
314
|
npt.NDArray[np.float64],
|
|
@@ -308,6 +326,7 @@ def _extract_movie_global_percentile_corrected_traces(
|
|
|
308
326
|
percentile: Mean-image percentile used to define background pixels.
|
|
309
327
|
spatial_crop: Spatial domain used for background and ROI pixels.
|
|
310
328
|
spatial_domain: User-facing name of ``spatial_crop``.
|
|
329
|
+
check_cancelled: Optional callback that raises when work is obsolete.
|
|
311
330
|
|
|
312
331
|
Returns:
|
|
313
332
|
Raw ROI traces, repeated background traces, corrected ROI traces, and
|
|
@@ -317,6 +336,7 @@ def _extract_movie_global_percentile_corrected_traces(
|
|
|
317
336
|
negatives to zero, and then averages corrected pixels inside each ROI.
|
|
318
337
|
"""
|
|
319
338
|
_validate_percentile(percentile)
|
|
339
|
+
_check_cancelled(check_cancelled)
|
|
320
340
|
mean_image = spatial_crop.crop_image(recording.mean_image)
|
|
321
341
|
background_indices, threshold = _global_background_pixel_indices(
|
|
322
342
|
mean_image,
|
|
@@ -334,6 +354,7 @@ def _extract_movie_global_percentile_corrected_traces(
|
|
|
334
354
|
stop=frame_stop,
|
|
335
355
|
spatial_crop=spatial_crop,
|
|
336
356
|
):
|
|
357
|
+
_check_cancelled(check_cancelled)
|
|
337
358
|
chunk_offset = chunk_start - frame_start
|
|
338
359
|
chunk_slice = slice(chunk_offset, chunk_offset + (chunk_stop - chunk_start))
|
|
339
360
|
chunk_flat = frames.reshape(frames.shape[0], -1)
|
|
@@ -346,6 +367,7 @@ def _extract_movie_global_percentile_corrected_traces(
|
|
|
346
367
|
# corrected ROI trace below zero.
|
|
347
368
|
corrected_pixels = np.maximum(roi_pixels - background_trace[:, None], 0.0)
|
|
348
369
|
corrected_values[chunk_slice, roi_index] = corrected_pixels.mean(axis=1)
|
|
370
|
+
_check_cancelled(check_cancelled)
|
|
349
371
|
|
|
350
372
|
metadata: dict[str, BackgroundMetadataValue] = {
|
|
351
373
|
"method": "movie_global_percentile",
|
|
@@ -369,6 +391,7 @@ def _extract_movie_y_stripe_percentile_corrected_traces(
|
|
|
369
391
|
percentile: float,
|
|
370
392
|
spatial_crop: SpatialCrop,
|
|
371
393
|
spatial_domain: SpatialDomain,
|
|
394
|
+
check_cancelled: Callable[[], None] | None = None,
|
|
372
395
|
) -> tuple[
|
|
373
396
|
npt.NDArray[np.float64],
|
|
374
397
|
npt.NDArray[np.float64],
|
|
@@ -388,6 +411,7 @@ def _extract_movie_y_stripe_percentile_corrected_traces(
|
|
|
388
411
|
percentile: Percentile used inside each frame stripe.
|
|
389
412
|
spatial_crop: Spatial domain used for background and ROI pixels.
|
|
390
413
|
spatial_domain: User-facing name of ``spatial_crop``.
|
|
414
|
+
check_cancelled: Optional callback that raises when work is obsolete.
|
|
391
415
|
|
|
392
416
|
Returns:
|
|
393
417
|
Raw ROI traces, stripe-field background traces, corrected traces, and
|
|
@@ -404,6 +428,7 @@ def _extract_movie_y_stripe_percentile_corrected_traces(
|
|
|
404
428
|
near one ROI are safe neuropil reference pixels.
|
|
405
429
|
"""
|
|
406
430
|
_validate_percentile(percentile)
|
|
431
|
+
_check_cancelled(check_cancelled)
|
|
407
432
|
crop_shape = spatial_crop.crop_image(recording.mean_image).shape
|
|
408
433
|
stripe_height = _resolve_stripe_y_height(
|
|
409
434
|
row_count=crop_shape[0],
|
|
@@ -425,6 +450,7 @@ def _extract_movie_y_stripe_percentile_corrected_traces(
|
|
|
425
450
|
stop=frame_stop,
|
|
426
451
|
spatial_crop=spatial_crop,
|
|
427
452
|
):
|
|
453
|
+
_check_cancelled(check_cancelled)
|
|
428
454
|
chunk_offset = chunk_start - frame_start
|
|
429
455
|
chunk_slice = slice(chunk_offset, chunk_offset + (chunk_stop - chunk_start))
|
|
430
456
|
background_field = _framewise_y_stripe_background_field(
|
|
@@ -443,6 +469,7 @@ def _extract_movie_y_stripe_percentile_corrected_traces(
|
|
|
443
469
|
)
|
|
444
470
|
corrected_pixels = np.maximum(roi_pixels - roi_background_pixels, 0.0)
|
|
445
471
|
corrected_values[chunk_slice, roi_index] = corrected_pixels.mean(axis=1)
|
|
472
|
+
_check_cancelled(check_cancelled)
|
|
446
473
|
|
|
447
474
|
metadata: dict[str, BackgroundMetadataValue] = {
|
|
448
475
|
"method": "movie_y_stripe_percentile",
|
|
@@ -467,6 +494,7 @@ def _extract_roi_y_stripe_percentile_corrected_traces(
|
|
|
467
494
|
local_percentile: float,
|
|
468
495
|
spatial_crop: SpatialCrop,
|
|
469
496
|
spatial_domain: SpatialDomain,
|
|
497
|
+
check_cancelled: Callable[[], None] | None = None,
|
|
470
498
|
) -> tuple[
|
|
471
499
|
npt.NDArray[np.float64],
|
|
472
500
|
npt.NDArray[np.float64],
|
|
@@ -487,6 +515,7 @@ def _extract_roi_y_stripe_percentile_corrected_traces(
|
|
|
487
515
|
y-stripe.
|
|
488
516
|
spatial_crop: Spatial domain used for background and ROI pixels.
|
|
489
517
|
spatial_domain: User-facing name of ``spatial_crop``.
|
|
518
|
+
check_cancelled: Optional callback that raises when work is obsolete.
|
|
490
519
|
|
|
491
520
|
Returns:
|
|
492
521
|
Raw ROI traces, local background traces, corrected traces, and metadata.
|
|
@@ -508,6 +537,7 @@ def _extract_roi_y_stripe_percentile_corrected_traces(
|
|
|
508
537
|
local_y_radius=local_y_radius,
|
|
509
538
|
)
|
|
510
539
|
_validate_percentile(local_percentile)
|
|
540
|
+
_check_cancelled(check_cancelled)
|
|
511
541
|
background_indices_by_roi = _roi_y_stripe_background_pixel_indices(
|
|
512
542
|
roi_masks=roi_masks,
|
|
513
543
|
mean_image=spatial_crop.crop_image(recording.mean_image),
|
|
@@ -521,6 +551,7 @@ def _extract_roi_y_stripe_percentile_corrected_traces(
|
|
|
521
551
|
frame_stop=frame_stop,
|
|
522
552
|
chunk_frames=chunk_frames,
|
|
523
553
|
spatial_crop=spatial_crop,
|
|
554
|
+
check_cancelled=check_cancelled,
|
|
524
555
|
)
|
|
525
556
|
background_values = _extract_mean_traces_from_indices(
|
|
526
557
|
recording=recording,
|
|
@@ -529,6 +560,7 @@ def _extract_roi_y_stripe_percentile_corrected_traces(
|
|
|
529
560
|
frame_stop=frame_stop,
|
|
530
561
|
chunk_frames=chunk_frames,
|
|
531
562
|
spatial_crop=spatial_crop,
|
|
563
|
+
check_cancelled=check_cancelled,
|
|
532
564
|
)
|
|
533
565
|
corrected_values = raw_values - background_values
|
|
534
566
|
background_counts = tuple(
|
|
@@ -561,6 +593,13 @@ def _roi_pixel_indices_from_masks(
|
|
|
561
593
|
return tuple(np.flatnonzero(mask).astype(np.int64) for mask in flat_masks)
|
|
562
594
|
|
|
563
595
|
|
|
596
|
+
def _check_cancelled(check_cancelled: Callable[[], None] | None) -> None:
|
|
597
|
+
"""Raise ``CancelledError`` when an interactive caller superseded this work."""
|
|
598
|
+
if check_cancelled is None:
|
|
599
|
+
return
|
|
600
|
+
check_cancelled()
|
|
601
|
+
|
|
602
|
+
|
|
564
603
|
def _crop_roi_masks_for_domain(
|
|
565
604
|
roi_set: RoiSet,
|
|
566
605
|
spatial_crop: SpatialCrop,
|
|
@@ -770,6 +809,15 @@ def _roi_y_stripe_background_pixel_indices(
|
|
|
770
809
|
"""
|
|
771
810
|
occupied_mask = np.any(roi_masks, axis=0)
|
|
772
811
|
background_mask = ~occupied_mask
|
|
812
|
+
if not np.any(background_mask):
|
|
813
|
+
msg = (
|
|
814
|
+
"ROI y-stripe P% background subtraction needs unlabeled background "
|
|
815
|
+
"pixels outside all ROIs in the selected spatial domain. The current "
|
|
816
|
+
"ROI masks cover every pixel, which is common for dense grid ROIs; "
|
|
817
|
+
"use shared y-stripe P% or leave unlabeled background pixels."
|
|
818
|
+
)
|
|
819
|
+
raise ValueError(msg)
|
|
820
|
+
|
|
773
821
|
row_grid = np.arange(background_mask.shape[0])[:, None]
|
|
774
822
|
background_indices: list[npt.NDArray[np.int64]] = []
|
|
775
823
|
|
|
@@ -782,7 +830,10 @@ def _roi_y_stripe_background_pixel_indices(
|
|
|
782
830
|
if local_candidate_values.size == 0:
|
|
783
831
|
msg = (
|
|
784
832
|
f"ROI {roi_index} has no local background candidates within "
|
|
785
|
-
f"{local_y_radius} rows"
|
|
833
|
+
f"{local_y_radius} rows. ROI y-stripe P% only uses unlabeled "
|
|
834
|
+
"pixels outside all ROIs; increase the local y radius, use "
|
|
835
|
+
"shared y-stripe P%, or leave unlabeled background pixels near "
|
|
836
|
+
"this ROI."
|
|
786
837
|
)
|
|
787
838
|
raise ValueError(msg)
|
|
788
839
|
local_threshold = float(np.percentile(local_candidate_values, local_percentile))
|
|
@@ -7,19 +7,23 @@ without hiding the scientific workflow parameters.
|
|
|
7
7
|
"""
|
|
8
8
|
|
|
9
9
|
from dataclasses import dataclass
|
|
10
|
+
from typing import Literal
|
|
10
11
|
|
|
11
12
|
from twopy.analysis.background_subtraction import BackgroundCorrectionMethod
|
|
12
13
|
from twopy.analysis.dff import DeltaFOverFFitMode
|
|
13
14
|
|
|
14
|
-
__all__ = ["DeltaFOverFOptions"]
|
|
15
|
+
__all__ = ["DeltaFOverFBaselineMode", "DeltaFOverFOptions"]
|
|
16
|
+
|
|
17
|
+
DeltaFOverFBaselineMode = Literal["epoch", "no_baseline_epoch"]
|
|
15
18
|
|
|
16
19
|
|
|
17
20
|
@dataclass(frozen=True)
|
|
18
21
|
class DeltaFOverFOptions:
|
|
19
22
|
"""Options that control ROI dF/F computation before response grouping.
|
|
20
23
|
|
|
21
|
-
Inputs: baseline epoch selection, background correction
|
|
22
|
-
window sampling, dF/F fit mode, and motion-artifact
|
|
24
|
+
Inputs: baseline epoch selection, baseline mode, background correction
|
|
25
|
+
method, baseline window sampling, dF/F fit mode, and motion-artifact
|
|
26
|
+
masking choice.
|
|
23
27
|
Outputs: typed parameters that map directly to
|
|
24
28
|
``compute_recording_responses`` and ``analyze_recording_responses``.
|
|
25
29
|
|
|
@@ -27,8 +31,9 @@ class DeltaFOverFOptions:
|
|
|
27
31
|
response processing choices such as smoothing and correlation filtering.
|
|
28
32
|
"""
|
|
29
33
|
|
|
30
|
-
baseline_epoch_number: int =
|
|
34
|
+
baseline_epoch_number: int | None = None
|
|
31
35
|
baseline_epoch_name: str | None = None
|
|
36
|
+
baseline_mode: DeltaFOverFBaselineMode = "epoch"
|
|
32
37
|
background_method: BackgroundCorrectionMethod = "movie_global_percentile"
|
|
33
38
|
baseline_sample_seconds: float | None = 1.0
|
|
34
39
|
fit_mode: DeltaFOverFFitMode = "direct_bounded_tau"
|
|
@@ -38,6 +38,15 @@ from twopy.analysis.responses import (
|
|
|
38
38
|
validate_grouped_roi_responses,
|
|
39
39
|
)
|
|
40
40
|
from twopy.analysis.trials import EpochFrameWindow, FrameWindow
|
|
41
|
+
from twopy.hdf5_utils import (
|
|
42
|
+
plain_hdf5_attr_value,
|
|
43
|
+
)
|
|
44
|
+
from twopy.hdf5_utils import (
|
|
45
|
+
read_string_dataset as _read_string_dataset,
|
|
46
|
+
)
|
|
47
|
+
from twopy.hdf5_utils import (
|
|
48
|
+
write_string_dataset as _write_string_dataset,
|
|
49
|
+
)
|
|
41
50
|
from twopy.roi import RoiSet, TraceStatistic, make_roi_set
|
|
42
51
|
from twopy.typing_guards import (
|
|
43
52
|
require_bool_array,
|
|
@@ -418,12 +427,12 @@ def _read_background_traces(group: h5py.Group) -> BackgroundCorrectedRoiTraces:
|
|
|
418
427
|
start_frame=int(group.attrs["start_frame"]),
|
|
419
428
|
stop_frame=int(group.attrs["stop_frame"]),
|
|
420
429
|
statistic=require_string_choice(
|
|
421
|
-
|
|
430
|
+
plain_hdf5_attr_value(group.attrs["statistic"], scalar_only=True),
|
|
422
431
|
name="traces statistic",
|
|
423
432
|
allowed=_TRACE_STATISTICS,
|
|
424
433
|
),
|
|
425
434
|
method=require_string_choice(
|
|
426
|
-
|
|
435
|
+
plain_hdf5_attr_value(group.attrs["method"], scalar_only=True),
|
|
427
436
|
name="traces background correction method",
|
|
428
437
|
allowed=_BACKGROUND_CORRECTION_METHODS,
|
|
429
438
|
),
|
|
@@ -704,7 +713,9 @@ def _read_response_trial(group: h5py.Group) -> RoiResponseTrial:
|
|
|
704
713
|
"""
|
|
705
714
|
return RoiResponseTrial(
|
|
706
715
|
epoch_number=int(group.attrs["epoch_number"]),
|
|
707
|
-
epoch_name=str(
|
|
716
|
+
epoch_name=str(
|
|
717
|
+
plain_hdf5_attr_value(group.attrs["epoch_name"], scalar_only=True),
|
|
718
|
+
),
|
|
708
719
|
trial_index=int(group.attrs["trial_index"]),
|
|
709
720
|
window_index=int(group.attrs["window_index"]),
|
|
710
721
|
start_frame=int(group.attrs["start_frame"]),
|
|
@@ -789,41 +800,10 @@ def _read_metadata(group: h5py.Group) -> dict[str, str | int | float | bool]:
|
|
|
789
800
|
Returns:
|
|
790
801
|
Plain Python metadata dictionary.
|
|
791
802
|
"""
|
|
792
|
-
return {
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
group: h5py.Group,
|
|
797
|
-
name: str,
|
|
798
|
-
values: tuple[str, ...],
|
|
799
|
-
) -> None:
|
|
800
|
-
"""Write a tuple of strings as a UTF-8 HDF5 dataset.
|
|
801
|
-
|
|
802
|
-
Args:
|
|
803
|
-
group: Destination HDF5 group.
|
|
804
|
-
name: Dataset name.
|
|
805
|
-
values: String values to write.
|
|
806
|
-
|
|
807
|
-
Returns:
|
|
808
|
-
None.
|
|
809
|
-
"""
|
|
810
|
-
group.create_dataset(
|
|
811
|
-
name,
|
|
812
|
-
data=np.asarray(values, dtype=h5py.string_dtype("utf-8")),
|
|
813
|
-
)
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
def _read_string_dataset(group: h5py.Group, name: str) -> tuple[str, ...]:
|
|
817
|
-
"""Read a UTF-8 string dataset.
|
|
818
|
-
|
|
819
|
-
Args:
|
|
820
|
-
group: HDF5 group containing the dataset.
|
|
821
|
-
name: Dataset name.
|
|
822
|
-
|
|
823
|
-
Returns:
|
|
824
|
-
Tuple of decoded strings.
|
|
825
|
-
"""
|
|
826
|
-
return tuple(_decode_string(value) for value in group[name][()])
|
|
803
|
+
return {
|
|
804
|
+
key: plain_hdf5_attr_value(value, scalar_only=True)
|
|
805
|
+
for key, value in group.attrs.items()
|
|
806
|
+
}
|
|
827
807
|
|
|
828
808
|
|
|
829
809
|
def _time_columns(trials: Sequence[RoiResponseTrial]) -> tuple[str, ...]:
|
|
@@ -1045,41 +1025,7 @@ def _require_analysis_format(h5_file: h5py.File, path: Path) -> None:
|
|
|
1045
1025
|
Returns:
|
|
1046
1026
|
None.
|
|
1047
1027
|
"""
|
|
1048
|
-
actual = str(
|
|
1028
|
+
actual = str(plain_hdf5_attr_value(h5_file.attrs.get("twopy_format", "")))
|
|
1049
1029
|
if actual != ANALYSIS_OUTPUT_FILE_FORMAT:
|
|
1050
1030
|
msg = f"Expected {ANALYSIS_OUTPUT_FILE_FORMAT!r} file at {path}"
|
|
1051
1031
|
raise ValueError(msg)
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
def _plain_attr_value(value: object) -> str | int | float | bool:
|
|
1055
|
-
"""Convert one HDF5 attribute value to a plain Python scalar.
|
|
1056
|
-
|
|
1057
|
-
Args:
|
|
1058
|
-
value: Raw HDF5 attribute value.
|
|
1059
|
-
|
|
1060
|
-
Returns:
|
|
1061
|
-
Plain string, integer, float, or boolean.
|
|
1062
|
-
"""
|
|
1063
|
-
if isinstance(value, bytes):
|
|
1064
|
-
return value.decode("utf-8")
|
|
1065
|
-
if isinstance(value, np.bool_ | bool):
|
|
1066
|
-
return bool(value)
|
|
1067
|
-
if isinstance(value, np.integer | int):
|
|
1068
|
-
return int(value)
|
|
1069
|
-
if isinstance(value, np.floating | float):
|
|
1070
|
-
return float(value)
|
|
1071
|
-
return str(value)
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
def _decode_string(value: object) -> str:
|
|
1075
|
-
"""Decode one HDF5 string scalar.
|
|
1076
|
-
|
|
1077
|
-
Args:
|
|
1078
|
-
value: Raw scalar read from a string dataset.
|
|
1079
|
-
|
|
1080
|
-
Returns:
|
|
1081
|
-
Decoded text.
|
|
1082
|
-
"""
|
|
1083
|
-
if isinstance(value, bytes):
|
|
1084
|
-
return value.decode("utf-8")
|
|
1085
|
-
return str(value)
|