TwoPy 0.1.7__tar.gz → 0.1.9__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.
Files changed (181) hide show
  1. {twopy-0.1.7 → twopy-0.1.9}/PKG-INFO +3 -3
  2. {twopy-0.1.7 → twopy-0.1.9}/README.md +2 -2
  3. {twopy-0.1.7 → twopy-0.1.9}/pyproject.toml +4 -1
  4. {twopy-0.1.7 → twopy-0.1.9}/src/twopy/__init__.py +106 -0
  5. {twopy-0.1.7 → twopy-0.1.9}/src/twopy/analysis/__init__.py +46 -1
  6. {twopy-0.1.7 → twopy-0.1.9}/src/twopy/analysis/background_subtraction.py +53 -2
  7. {twopy-0.1.7 → twopy-0.1.9}/src/twopy/analysis/dff_options.py +9 -4
  8. twopy-0.1.9/src/twopy/analysis/group_matching.py +463 -0
  9. {twopy-0.1.7 → twopy-0.1.9}/src/twopy/analysis/persistence.py +19 -73
  10. twopy-0.1.9/src/twopy/analysis/response_context.py +280 -0
  11. twopy-0.1.9/src/twopy/analysis/response_maps.py +726 -0
  12. {twopy-0.1.7 → twopy-0.1.9}/src/twopy/analysis/response_processing/persistence.py +19 -48
  13. {twopy-0.1.7 → twopy-0.1.9}/src/twopy/analysis/trials.py +39 -0
  14. {twopy-0.1.7 → twopy-0.1.9}/src/twopy/analysis/workflow.py +174 -142
  15. {twopy-0.1.7 → twopy-0.1.9}/src/twopy/analysis_cache.py +10 -1
  16. twopy-0.1.9/src/twopy/calibrations/pixel_calibration_profiles.csv +4 -0
  17. twopy-0.1.9/src/twopy/calibrations/pixel_size.csv +21 -0
  18. {twopy-0.1.7 → twopy-0.1.9}/src/twopy/config.py +36 -2
  19. {twopy-0.1.7 → twopy-0.1.9}/src/twopy/conversion/api.py +3 -4
  20. {twopy-0.1.7 → twopy-0.1.9}/src/twopy/conversion/hdf5_writing.py +6 -7
  21. {twopy-0.1.7 → twopy-0.1.9}/src/twopy/converted.py +15 -49
  22. {twopy-0.1.7 → twopy-0.1.9}/src/twopy/database/__init__.py +2 -0
  23. {twopy-0.1.7 → twopy-0.1.9}/src/twopy/database/search.py +34 -1
  24. twopy-0.1.9/src/twopy/filenames.py +46 -0
  25. twopy-0.1.9/src/twopy/hdf5_utils.py +147 -0
  26. {twopy-0.1.7 → twopy-0.1.9}/src/twopy/napari/controls.py +433 -73
  27. {twopy-0.1.7 → twopy-0.1.9}/src/twopy/napari/database_search.py +52 -7
  28. twopy-0.1.9/src/twopy/napari/dims.py +113 -0
  29. {twopy-0.1.7 → twopy-0.1.9}/src/twopy/napari/display_paths.py +42 -1
  30. twopy-0.1.9/src/twopy/napari/group_matching.py +585 -0
  31. twopy-0.1.9/src/twopy/napari/group_matching_images.py +283 -0
  32. twopy-0.1.9/src/twopy/napari/group_matching_roi.py +1147 -0
  33. {twopy-0.1.7 → twopy-0.1.9}/src/twopy/napari/interactive.py +41 -11
  34. twopy-0.1.9/src/twopy/napari/latest_worker.py +144 -0
  35. {twopy-0.1.7 → twopy-0.1.9}/src/twopy/napari/launcher.py +5 -1
  36. twopy-0.1.9/src/twopy/napari/live_analysis.py +362 -0
  37. twopy-0.1.9/src/twopy/napari/loaded_recordings_csv.py +110 -0
  38. {twopy-0.1.7 → twopy-0.1.9}/src/twopy/napari/loading.py +51 -3
  39. {twopy-0.1.7 → twopy-0.1.9}/src/twopy/napari/paths.py +6 -5
  40. {twopy-0.1.7 → twopy-0.1.9}/src/twopy/napari/plotting/data.py +142 -4
  41. {twopy-0.1.7 → twopy-0.1.9}/src/twopy/napari/plotting/dff_options.py +43 -8
  42. {twopy-0.1.7 → twopy-0.1.9}/src/twopy/napari/plotting/docks/options_panel.py +71 -20
  43. {twopy-0.1.7 → twopy-0.1.9}/src/twopy/napari/plotting/docks/paths.py +14 -7
  44. {twopy-0.1.7 → twopy-0.1.9}/src/twopy/napari/plotting/docks/plot_area.py +20 -2
  45. twopy-0.1.9/src/twopy/napari/plotting/docks/response_map_area.py +450 -0
  46. {twopy-0.1.7 → twopy-0.1.9}/src/twopy/napari/plotting/docks/response_plot_widget.py +390 -19
  47. {twopy-0.1.7 → twopy-0.1.9}/src/twopy/napari/plotting/docks/save_actions.py +25 -2
  48. {twopy-0.1.7 → twopy-0.1.9}/src/twopy/napari/plotting/export.py +151 -28
  49. {twopy-0.1.7 → twopy-0.1.9}/src/twopy/napari/plotting/export_controls.py +24 -3
  50. {twopy-0.1.7 → twopy-0.1.9}/src/twopy/napari/plotting/label_visibility.py +58 -20
  51. {twopy-0.1.7 → twopy-0.1.9}/src/twopy/napari/plotting/panels.py +24 -21
  52. {twopy-0.1.7 → twopy-0.1.9}/src/twopy/napari/plotting/processing_options.py +27 -0
  53. twopy-0.1.9/src/twopy/napari/plotting/response_map_colors.py +24 -0
  54. twopy-0.1.9/src/twopy/napari/plotting/response_map_display.py +131 -0
  55. twopy-0.1.9/src/twopy/napari/plotting/response_map_options.py +350 -0
  56. twopy-0.1.9/src/twopy/napari/plotting/roi_generation/__init__.py +26 -0
  57. twopy-0.1.9/src/twopy/napari/plotting/roi_generation/actions.py +130 -0
  58. twopy-0.1.9/src/twopy/napari/plotting/roi_generation/controls.py +706 -0
  59. twopy-0.1.9/src/twopy/napari/plotting/roi_generation/options.py +62 -0
  60. {twopy-0.1.7 → twopy-0.1.9}/src/twopy/napari/plotting/widgets.py +12 -0
  61. {twopy-0.1.7 → twopy-0.1.9}/src/twopy/napari/responses.py +7 -0
  62. {twopy-0.1.7 → twopy-0.1.9}/src/twopy/napari/roi.py +58 -1
  63. {twopy-0.1.7 → twopy-0.1.9}/src/twopy/napari/session.py +69 -13
  64. {twopy-0.1.7 → twopy-0.1.9}/src/twopy/napari/sidebar.py +88 -3
  65. twopy-0.1.9/src/twopy/napari/trial_timeline.py +719 -0
  66. {twopy-0.1.7 → twopy-0.1.9}/src/twopy/napari/types.py +1 -0
  67. {twopy-0.1.7 → twopy-0.1.9}/src/twopy/napari/viewer.py +14 -1
  68. {twopy-0.1.7 → twopy-0.1.9}/src/twopy/parity/__init__.py +12 -0
  69. {twopy-0.1.7 → twopy-0.1.9}/src/twopy/parity/psycho5.py +88 -2
  70. twopy-0.1.9/src/twopy/parity/roi_extraction.py +302 -0
  71. twopy-0.1.9/src/twopy/pixel_calibration.py +573 -0
  72. twopy-0.1.9/src/twopy/pixel_calibration_profiles.py +563 -0
  73. twopy-0.1.9/src/twopy/response_roi_extraction.py +750 -0
  74. {twopy-0.1.7 → twopy-0.1.9}/src/twopy/roi.py +3 -21
  75. twopy-0.1.9/src/twopy/roi_extraction.py +550 -0
  76. twopy-0.1.9/src/twopy/roi_mask_cleanup.py +74 -0
  77. {twopy-0.1.7 → twopy-0.1.9}/src/twopy.egg-info/PKG-INFO +3 -3
  78. {twopy-0.1.7 → twopy-0.1.9}/src/twopy.egg-info/SOURCES.txt +40 -1
  79. {twopy-0.1.7 → twopy-0.1.9}/tests/test_api.py +3 -2
  80. {twopy-0.1.7 → twopy-0.1.9}/tests/test_background_subtraction.py +41 -12
  81. {twopy-0.1.7 → twopy-0.1.9}/tests/test_config.py +41 -12
  82. {twopy-0.1.7 → twopy-0.1.9}/tests/test_conversion.py +13 -13
  83. {twopy-0.1.7 → twopy-0.1.9}/tests/test_converted.py +10 -10
  84. {twopy-0.1.7 → twopy-0.1.9}/tests/test_database.py +55 -12
  85. twopy-0.1.9/tests/test_filenames.py +104 -0
  86. twopy-0.1.9/tests/test_group_matching.py +180 -0
  87. twopy-0.1.9/tests/test_hdf5_utils.py +83 -0
  88. {twopy-0.1.7 → twopy-0.1.9}/tests/test_inspection.py +3 -3
  89. {twopy-0.1.7 → twopy-0.1.9}/tests/test_matlab.py +5 -5
  90. {twopy-0.1.7 → twopy-0.1.9}/tests/test_napari.py +2796 -111
  91. {twopy-0.1.7 → twopy-0.1.9}/tests/test_parity.py +3 -3
  92. {twopy-0.1.7 → twopy-0.1.9}/tests/test_parity_psycho5.py +47 -4
  93. twopy-0.1.9/tests/test_parity_roi_extraction.py +183 -0
  94. {twopy-0.1.7 → twopy-0.1.9}/tests/test_persistence.py +9 -9
  95. twopy-0.1.9/tests/test_pixel_calibration.py +225 -0
  96. twopy-0.1.9/tests/test_pixel_calibration_profiles.py +226 -0
  97. {twopy-0.1.7 → twopy-0.1.9}/tests/test_real_fixture.py +2 -2
  98. twopy-0.1.9/tests/test_response_map_area.py +98 -0
  99. twopy-0.1.9/tests/test_response_maps.py +311 -0
  100. twopy-0.1.9/tests/test_response_roi_extraction.py +354 -0
  101. {twopy-0.1.7 → twopy-0.1.9}/tests/test_roi.py +7 -7
  102. twopy-0.1.9/tests/test_roi_extraction.py +171 -0
  103. twopy-0.1.9/tests/test_roi_mask_cleanup.py +65 -0
  104. {twopy-0.1.7 → twopy-0.1.9}/tests/test_saved_analysis.py +2 -2
  105. {twopy-0.1.7 → twopy-0.1.9}/tests/test_session.py +7 -6
  106. {twopy-0.1.7 → twopy-0.1.9}/tests/test_stimulus.py +6 -6
  107. {twopy-0.1.7 → twopy-0.1.9}/tests/test_synchronization.py +2 -2
  108. {twopy-0.1.7 → twopy-0.1.9}/tests/test_trials.py +20 -0
  109. {twopy-0.1.7 → twopy-0.1.9}/tests/test_workflow.py +142 -4
  110. twopy-0.1.7/src/twopy/napari/constants.py +0 -13
  111. {twopy-0.1.7 → twopy-0.1.9}/setup.cfg +0 -0
  112. {twopy-0.1.7 → twopy-0.1.9}/src/twopy/_segments.py +0 -0
  113. {twopy-0.1.7 → twopy-0.1.9}/src/twopy/_version.py +0 -0
  114. {twopy-0.1.7 → twopy-0.1.9}/src/twopy/analysis/dff.py +0 -0
  115. {twopy-0.1.7 → twopy-0.1.9}/src/twopy/analysis/epoch_mapping.py +0 -0
  116. {twopy-0.1.7 → twopy-0.1.9}/src/twopy/analysis/motion.py +0 -0
  117. {twopy-0.1.7 → twopy-0.1.9}/src/twopy/analysis/response_processing/__init__.py +0 -0
  118. {twopy-0.1.7 → twopy-0.1.9}/src/twopy/analysis/response_processing/apply.py +0 -0
  119. {twopy-0.1.7 → twopy-0.1.9}/src/twopy/analysis/response_processing/options.py +0 -0
  120. {twopy-0.1.7 → twopy-0.1.9}/src/twopy/analysis/response_processing/qc.py +0 -0
  121. {twopy-0.1.7 → twopy-0.1.9}/src/twopy/analysis/response_processing/signals.py +0 -0
  122. {twopy-0.1.7 → twopy-0.1.9}/src/twopy/analysis/response_window_options.py +0 -0
  123. {twopy-0.1.7 → twopy-0.1.9}/src/twopy/analysis/responses.py +0 -0
  124. {twopy-0.1.7 → twopy-0.1.9}/src/twopy/api.py +0 -0
  125. {twopy-0.1.7 → twopy-0.1.9}/src/twopy/conversion/__init__.py +0 -0
  126. {twopy-0.1.7 → twopy-0.1.9}/src/twopy/conversion/alignment_crop.py +0 -0
  127. {twopy-0.1.7 → twopy-0.1.9}/src/twopy/conversion/matlab_values.py +0 -0
  128. {twopy-0.1.7 → twopy-0.1.9}/src/twopy/conversion/source_loading.py +0 -0
  129. {twopy-0.1.7 → twopy-0.1.9}/src/twopy/conversion/stimulus_code.py +0 -0
  130. {twopy-0.1.7 → twopy-0.1.9}/src/twopy/conversion/types.py +0 -0
  131. {twopy-0.1.7 → twopy-0.1.9}/src/twopy/database/cache.py +0 -0
  132. {twopy-0.1.7 → twopy-0.1.9}/src/twopy/database/catalog.py +0 -0
  133. {twopy-0.1.7 → twopy-0.1.9}/src/twopy/database/connection.py +0 -0
  134. {twopy-0.1.7 → twopy-0.1.9}/src/twopy/database/generic.py +0 -0
  135. {twopy-0.1.7 → twopy-0.1.9}/src/twopy/database/modeled.py +0 -0
  136. {twopy-0.1.7 → twopy-0.1.9}/src/twopy/database/sql.py +0 -0
  137. {twopy-0.1.7 → twopy-0.1.9}/src/twopy/database/types.py +0 -0
  138. {twopy-0.1.7 → twopy-0.1.9}/src/twopy/frame_ranges.py +0 -0
  139. {twopy-0.1.7 → twopy-0.1.9}/src/twopy/inspection.py +0 -0
  140. {twopy-0.1.7 → twopy-0.1.9}/src/twopy/matlab.py +0 -0
  141. {twopy-0.1.7 → twopy-0.1.9}/src/twopy/napari/__init__.py +0 -0
  142. {twopy-0.1.7 → twopy-0.1.9}/src/twopy/napari/__main__.py +0 -0
  143. {twopy-0.1.7 → twopy-0.1.9}/src/twopy/napari/display.py +0 -0
  144. {twopy-0.1.7 → twopy-0.1.9}/src/twopy/napari/errors.py +0 -0
  145. {twopy-0.1.7 → twopy-0.1.9}/src/twopy/napari/movie.py +0 -0
  146. {twopy-0.1.7 → twopy-0.1.9}/src/twopy/napari/plotting/__init__.py +0 -0
  147. {twopy-0.1.7 → twopy-0.1.9}/src/twopy/napari/plotting/docks/__init__.py +0 -0
  148. {twopy-0.1.7 → twopy-0.1.9}/src/twopy/napari/plotting/docks/api.py +0 -0
  149. {twopy-0.1.7 → twopy-0.1.9}/src/twopy/napari/plotting/docks/dynamic_options.py +0 -0
  150. {twopy-0.1.7 → twopy-0.1.9}/src/twopy/napari/plotting/docks/visibility_state.py +0 -0
  151. {twopy-0.1.7 → twopy-0.1.9}/src/twopy/napari/plotting/form_controls.py +0 -0
  152. {twopy-0.1.7 → twopy-0.1.9}/src/twopy/napari/plotting/normalization_options.py +0 -0
  153. {twopy-0.1.7 → twopy-0.1.9}/src/twopy/napari/plotting/options.py +0 -0
  154. {twopy-0.1.7 → twopy-0.1.9}/src/twopy/napari/plotting/response_window_options.py +0 -0
  155. {twopy-0.1.7 → twopy-0.1.9}/src/twopy/napari/protocols.py +0 -0
  156. {twopy-0.1.7 → twopy-0.1.9}/src/twopy/napari/state.py +0 -0
  157. {twopy-0.1.7 → twopy-0.1.9}/src/twopy/napari/text.py +0 -0
  158. {twopy-0.1.7 → twopy-0.1.9}/src/twopy/parity/dff.py +0 -0
  159. {twopy-0.1.7 → twopy-0.1.9}/src/twopy/parity/roi.py +0 -0
  160. {twopy-0.1.7 → twopy-0.1.9}/src/twopy/parity/saved_analysis.py +0 -0
  161. {twopy-0.1.7 → twopy-0.1.9}/src/twopy/photodiode.py +0 -0
  162. {twopy-0.1.7 → twopy-0.1.9}/src/twopy/photodiode_classification.py +0 -0
  163. {twopy-0.1.7 → twopy-0.1.9}/src/twopy/session.py +0 -0
  164. {twopy-0.1.7 → twopy-0.1.9}/src/twopy/spatial.py +0 -0
  165. {twopy-0.1.7 → twopy-0.1.9}/src/twopy/stimulus.py +0 -0
  166. {twopy-0.1.7 → twopy-0.1.9}/src/twopy/synchronization.py +0 -0
  167. {twopy-0.1.7 → twopy-0.1.9}/src/twopy/typing_guards.py +0 -0
  168. {twopy-0.1.7 → twopy-0.1.9}/src/twopy.egg-info/dependency_links.txt +0 -0
  169. {twopy-0.1.7 → twopy-0.1.9}/src/twopy.egg-info/entry_points.txt +0 -0
  170. {twopy-0.1.7 → twopy-0.1.9}/src/twopy.egg-info/requires.txt +0 -0
  171. {twopy-0.1.7 → twopy-0.1.9}/src/twopy.egg-info/top_level.txt +0 -0
  172. {twopy-0.1.7 → twopy-0.1.9}/tests/test_dff.py +0 -0
  173. {twopy-0.1.7 → twopy-0.1.9}/tests/test_epoch_mapping.py +0 -0
  174. {twopy-0.1.7 → twopy-0.1.9}/tests/test_frame_ranges.py +0 -0
  175. {twopy-0.1.7 → twopy-0.1.9}/tests/test_import.py +0 -0
  176. {twopy-0.1.7 → twopy-0.1.9}/tests/test_motion.py +0 -0
  177. {twopy-0.1.7 → twopy-0.1.9}/tests/test_packaging.py +0 -0
  178. {twopy-0.1.7 → twopy-0.1.9}/tests/test_photodiode_classification.py +0 -0
  179. {twopy-0.1.7 → twopy-0.1.9}/tests/test_response_processing.py +0 -0
  180. {twopy-0.1.7 → twopy-0.1.9}/tests/test_response_window_options.py +0 -0
  181. {twopy-0.1.7 → twopy-0.1.9}/tests/test_responses.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: twopy
3
- Version: 0.1.7
3
+ Version: 0.1.9
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. Choose a recording.
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. Choose a recording.
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"
7
+ version = "0.1.9"
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,15 +14,23 @@ from twopy.analysis import (
14
14
  BackgroundCorrectedRoiTraces,
15
15
  BackgroundCorrectionMethod,
16
16
  CorrelationFilterOptions,
17
+ DeltaFOverFBaselineMode,
17
18
  DeltaFOverFFitMode,
18
19
  DeltaFOverFOptions,
19
20
  EpochFrameWindow,
21
+ EpochResponseMap,
20
22
  FrameWindow,
21
23
  GroupedRoiResponses,
22
24
  GroupedRoiResponseSummary,
23
25
  InterpolatedEpochMapping,
24
26
  LoadedAnalysisOutputs,
25
27
  LowPassFilterOptions,
28
+ ManualFovGroupRow,
29
+ ManualRoiMatchRow,
30
+ ManualRoiMatchStatus,
31
+ ResponseMapData,
32
+ ResponseMapMode,
33
+ ResponseMapOptions,
26
34
  ResponseProcessingOptions,
27
35
  RoiCorrelationScores,
28
36
  RoiDeltaFOverF,
@@ -31,20 +39,33 @@ from twopy.analysis import (
31
39
  SmoothingOptions,
32
40
  WindowedRoiResponse,
33
41
  analyze_recording_responses,
42
+ append_manual_roi_match_rows,
34
43
  apply_motion_artifact_mask_to_delta_f_over_f,
44
+ compute_recording_response_maps,
35
45
  compute_recording_responses,
36
46
  compute_roi_delta_f_over_f,
37
47
  default_baseline_epoch_number,
48
+ default_recording_baseline_epoch_number,
38
49
  extract_background_corrected_roi_traces,
39
50
  frame_windows_from_photodiode_alignment,
40
51
  group_delta_f_over_f_by_epoch,
41
52
  interpolate_stimulus_epochs_to_frame_windows,
42
53
  is_baseline_epoch_name,
43
54
  load_analysis_outputs,
55
+ load_manual_fov_group_rows,
56
+ load_manual_roi_match_rows,
57
+ load_response_map_data,
44
58
  make_frame_windows,
59
+ make_manual_fov_group_rows,
60
+ make_manual_roi_match_rows,
45
61
  map_stimulus_epochs_to_frame_windows,
62
+ next_group_cell_id,
63
+ no_baseline_epoch_frame_windows,
46
64
  resolve_baseline_frame_windows,
47
65
  save_analysis_outputs,
66
+ save_manual_fov_group_rows,
67
+ save_manual_roi_match_rows,
68
+ save_response_map_data,
48
69
  select_baseline_frame_windows,
49
70
  split_traces_by_frame_windows,
50
71
  summarize_epoch_roi_responses,
@@ -73,6 +94,33 @@ from twopy.photodiode_classification import (
73
94
  PhotodiodeEventType,
74
95
  classify_recording_photodiode_events,
75
96
  )
97
+ from twopy.pixel_calibration import (
98
+ DEFAULT_PIXEL_CALIBRATION_PATH,
99
+ PixelCalibrationResolution,
100
+ PixelCalibrationResolutionMethod,
101
+ PixelCalibrationRow,
102
+ load_pixel_calibrations,
103
+ resolve_pixel_size_um,
104
+ )
105
+ from twopy.pixel_calibration_profiles import (
106
+ DEFAULT_PIXEL_CALIBRATION_PROFILE_PATH,
107
+ PixelCalibrationGroup,
108
+ PixelCalibrationProfile,
109
+ PixelCalibrationProfileMapping,
110
+ PixelCalibrationProfileSource,
111
+ load_pixel_calibration_profile_mappings,
112
+ resolve_pixel_calibration_profile,
113
+ select_pixel_calibration_group,
114
+ )
115
+ from twopy.response_roi_extraction import (
116
+ ResponseNormalization,
117
+ ResponsePolarity,
118
+ ResponseStatistic,
119
+ ResponseWatershedExtraction,
120
+ ResponseWatershedScoreImages,
121
+ extract_response_watershed_rois,
122
+ response_watershed_roi_set,
123
+ )
76
124
  from twopy.roi import (
77
125
  RoiSet,
78
126
  RoiTraces,
@@ -83,6 +131,15 @@ from twopy.roi import (
83
131
  roi_set_to_label_image,
84
132
  save_roi_set,
85
133
  )
134
+ from twopy.roi_extraction import (
135
+ RoiExtractionConfig,
136
+ RoiExtractionMethod,
137
+ extract_rois_from_image,
138
+ grid_roi_set,
139
+ grid_roi_set_microns,
140
+ grid_size_pixels_from_microns,
141
+ watershed_roi_set,
142
+ )
86
143
  from twopy.spatial import SpatialCrop, SpatialDomain, full_frame_crop
87
144
  from twopy.stimulus import (
88
145
  StimulusSpecificColumnMapping,
@@ -106,6 +163,7 @@ __all__ = [
106
163
  "AnalysisResponseComputation",
107
164
  "analyze_recording_responses",
108
165
  "add_twopy_magicgui_controls",
166
+ "append_manual_roi_match_rows",
109
167
  "apply_motion_artifact_mask_to_delta_f_over_f",
110
168
  "BackgroundCorrectedRoiTraces",
111
169
  "BackgroundCorrectionMethod",
@@ -114,18 +172,26 @@ __all__ = [
114
172
  "ClassifiedStimulusWindow",
115
173
  "classify_recording_photodiode_events",
116
174
  "compute_roi_delta_f_over_f",
175
+ "compute_recording_response_maps",
117
176
  "compute_recording_responses",
118
177
  "CorrelationFilterOptions",
119
178
  "convert_recording_to_twopy",
120
179
  "DEFAULT_RESPONSE_POST_WINDOW_SECONDS",
121
180
  "DEFAULT_RESPONSE_PRE_WINDOW_SECONDS",
181
+ "DEFAULT_PIXEL_CALIBRATION_PATH",
182
+ "DEFAULT_PIXEL_CALIBRATION_PROFILE_PATH",
122
183
  "default_baseline_epoch_number",
184
+ "default_recording_baseline_epoch_number",
123
185
  "detect_photodiode_events",
124
186
  "detect_recording_photodiode_events",
187
+ "DeltaFOverFBaselineMode",
125
188
  "DeltaFOverFFitMode",
126
189
  "DeltaFOverFOptions",
190
+ "EpochResponseMap",
127
191
  "EpochFrameWindow",
128
192
  "extract_background_corrected_roi_traces",
193
+ "extract_response_watershed_rois",
194
+ "extract_rois_from_image",
129
195
  "extract_roi_traces",
130
196
  "find_recordings",
131
197
  "frame_windows_from_photodiode_alignment",
@@ -134,20 +200,35 @@ __all__ = [
134
200
  "group_delta_f_over_f_by_epoch",
135
201
  "GroupedRoiResponseSummary",
136
202
  "GroupedRoiResponses",
203
+ "grid_roi_set",
204
+ "grid_roi_set_microns",
205
+ "grid_size_pixels_from_microns",
137
206
  "InterpolatedEpochMapping",
138
207
  "interpolate_stimulus_epochs_to_frame_windows",
139
208
  "is_baseline_epoch_name",
140
209
  "launch_napari",
141
210
  "load_analysis_outputs",
211
+ "load_response_map_data",
212
+ "load_manual_fov_group_rows",
213
+ "load_manual_roi_match_rows",
142
214
  "load_converted_recording",
215
+ "load_pixel_calibrations",
216
+ "load_pixel_calibration_profile_mappings",
143
217
  "load_roi_set",
144
218
  "load_source_conversion_inputs",
145
219
  "LoadedAnalysisOutputs",
146
220
  "LowPassFilterOptions",
221
+ "ManualFovGroupRow",
222
+ "ManualRoiMatchRow",
223
+ "ManualRoiMatchStatus",
147
224
  "make_roi_set_from_label_image",
148
225
  "make_frame_windows",
226
+ "make_manual_fov_group_rows",
227
+ "make_manual_roi_match_rows",
149
228
  "make_roi_set",
150
229
  "map_stimulus_epochs_to_frame_windows",
230
+ "next_group_cell_id",
231
+ "no_baseline_epoch_frame_windows",
151
232
  "map_stimulus_specific_column",
152
233
  "NapariRecordingView",
153
234
  "NapariSidebarWidgets",
@@ -158,9 +239,29 @@ __all__ = [
158
239
  "PhotodiodeEvent",
159
240
  "PhotodiodeEventType",
160
241
  "PhotodiodeEventSet",
242
+ "PixelCalibrationResolution",
243
+ "PixelCalibrationResolutionMethod",
244
+ "PixelCalibrationGroup",
245
+ "PixelCalibrationProfile",
246
+ "PixelCalibrationProfileMapping",
247
+ "PixelCalibrationProfileSource",
248
+ "PixelCalibrationRow",
249
+ "ResponseNormalization",
250
+ "ResponsePolarity",
251
+ "ResponseMapData",
252
+ "ResponseMapMode",
253
+ "ResponseMapOptions",
161
254
  "ResponseProcessingOptions",
255
+ "ResponseStatistic",
256
+ "ResponseWatershedExtraction",
257
+ "ResponseWatershedScoreImages",
162
258
  "recording_frame_rate_hz",
259
+ "resolve_pixel_size_um",
260
+ "resolve_pixel_calibration_profile",
163
261
  "resolve_baseline_frame_windows",
262
+ "response_watershed_roi_set",
263
+ "RoiExtractionConfig",
264
+ "RoiExtractionMethod",
164
265
  "RoiDeltaFOverF",
165
266
  "RoiCorrelationScores",
166
267
  "RoiResponseSummary",
@@ -170,9 +271,13 @@ __all__ = [
170
271
  "roi_label_image_from_layer",
171
272
  "roi_set_to_label_image",
172
273
  "save_analysis_outputs",
274
+ "save_response_map_data",
275
+ "save_manual_fov_group_rows",
276
+ "save_manual_roi_match_rows",
173
277
  "save_napari_label_rois",
174
278
  "save_roi_set",
175
279
  "select_baseline_frame_windows",
280
+ "select_pixel_calibration_group",
176
281
  "split_traces_by_frame_windows",
177
282
  "SmoothingOptions",
178
283
  "SpatialCrop",
@@ -182,6 +287,7 @@ __all__ = [
182
287
  "summarize_epoch_roi_responses",
183
288
  "summarize_grouped_responses",
184
289
  "validate_grouped_roi_responses",
290
+ "watershed_roi_set",
185
291
  "WindowedRoiResponse",
186
292
  "write_response_summary_grouped_csv",
187
293
  "write_response_summary_trials_csv",
@@ -18,11 +18,24 @@ 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,
25
25
  )
26
+ from twopy.analysis.group_matching import (
27
+ ManualFovGroupRow,
28
+ ManualRoiMatchRow,
29
+ ManualRoiMatchStatus,
30
+ append_manual_roi_match_rows,
31
+ load_manual_fov_group_rows,
32
+ load_manual_roi_match_rows,
33
+ make_manual_fov_group_rows,
34
+ make_manual_roi_match_rows,
35
+ next_group_cell_id,
36
+ save_manual_fov_group_rows,
37
+ save_manual_roi_match_rows,
38
+ )
26
39
  from twopy.analysis.motion import apply_motion_artifact_mask_to_delta_f_over_f
27
40
  from twopy.analysis.persistence import (
28
41
  ANALYSIS_OUTPUT_FILE_FORMAT,
@@ -32,6 +45,16 @@ from twopy.analysis.persistence import (
32
45
  write_response_summary_grouped_csv,
33
46
  write_response_summary_trials_csv,
34
47
  )
48
+ from twopy.analysis.response_context import default_recording_baseline_epoch_number
49
+ from twopy.analysis.response_maps import (
50
+ EpochResponseMap,
51
+ ResponseMapData,
52
+ ResponseMapMode,
53
+ ResponseMapOptions,
54
+ compute_recording_response_maps,
55
+ load_response_map_data,
56
+ save_response_map_data,
57
+ )
35
58
  from twopy.analysis.response_processing import (
36
59
  CorrelationFilterOptions,
37
60
  LowPassFilterOptions,
@@ -58,6 +81,7 @@ from twopy.analysis.trials import (
58
81
  is_baseline_epoch_name,
59
82
  make_frame_windows,
60
83
  map_stimulus_epochs_to_frame_windows,
84
+ no_baseline_epoch_frame_windows,
61
85
  resolve_baseline_frame_windows,
62
86
  select_baseline_frame_windows,
63
87
  split_traces_by_frame_windows,
@@ -83,10 +107,14 @@ __all__ = [
83
107
  "BackgroundCorrectionMethod",
84
108
  "compute_roi_delta_f_over_f",
85
109
  "compute_recording_responses",
110
+ "compute_recording_response_maps",
86
111
  "CorrelationFilterOptions",
87
112
  "default_baseline_epoch_number",
113
+ "default_recording_baseline_epoch_number",
114
+ "DeltaFOverFBaselineMode",
88
115
  "DeltaFOverFOptions",
89
116
  "DeltaFOverFFitMode",
117
+ "EpochResponseMap",
90
118
  "EpochFrameWindow",
91
119
  "extract_background_corrected_roi_traces",
92
120
  "frame_windows_from_photodiode_alignment",
@@ -98,17 +126,34 @@ __all__ = [
98
126
  "interpolate_stimulus_epochs_to_frame_windows",
99
127
  "is_baseline_epoch_name",
100
128
  "load_analysis_outputs",
129
+ "load_response_map_data",
101
130
  "LoadedAnalysisOutputs",
102
131
  "LowPassFilterOptions",
132
+ "ManualFovGroupRow",
133
+ "ManualRoiMatchRow",
134
+ "ManualRoiMatchStatus",
103
135
  "make_frame_windows",
136
+ "make_manual_fov_group_rows",
137
+ "make_manual_roi_match_rows",
104
138
  "map_stimulus_epochs_to_frame_windows",
139
+ "no_baseline_epoch_frame_windows",
140
+ "next_group_cell_id",
105
141
  "RoiDeltaFOverF",
142
+ "ResponseMapData",
143
+ "ResponseMapMode",
144
+ "ResponseMapOptions",
106
145
  "ResponseProcessingOptions",
107
146
  "RoiCorrelationScores",
108
147
  "RoiResponseSummary",
109
148
  "RoiResponseTrial",
110
149
  "resolve_baseline_frame_windows",
150
+ "append_manual_roi_match_rows",
151
+ "load_manual_fov_group_rows",
152
+ "load_manual_roi_match_rows",
111
153
  "save_analysis_outputs",
154
+ "save_response_map_data",
155
+ "save_manual_fov_group_rows",
156
+ "save_manual_roi_match_rows",
112
157
  "select_baseline_frame_windows",
113
158
  "split_traces_by_frame_windows",
114
159
  "SmoothingOptions",
@@ -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 method, baseline
22
- window sampling, dF/F fit mode, and motion-artifact masking choice.
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 = 1
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"