TwoPy 0.1.9__tar.gz → 0.2.1__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.9 → twopy-0.2.1}/PKG-INFO +8 -3
- {twopy-0.1.9 → twopy-0.2.1}/README.md +5 -2
- {twopy-0.1.9 → twopy-0.2.1}/pyproject.toml +9 -1
- {twopy-0.1.9 → twopy-0.2.1}/src/twopy/__init__.py +40 -9
- {twopy-0.1.9 → twopy-0.2.1}/src/twopy/analysis/__init__.py +38 -10
- {twopy-0.1.9 → twopy-0.2.1}/src/twopy/analysis/background_subtraction.py +120 -21
- {twopy-0.1.9 → twopy-0.2.1}/src/twopy/analysis/dff.py +33 -0
- {twopy-0.1.9 → twopy-0.2.1}/src/twopy/analysis/group_matching.py +149 -0
- twopy-0.2.1/src/twopy/analysis/kernels/__init__.py +352 -0
- twopy-0.2.1/src/twopy/analysis/kernels/_fitting.py +360 -0
- twopy-0.2.1/src/twopy/analysis/kernels/_stimulus.py +432 -0
- {twopy-0.1.9 → twopy-0.2.1}/src/twopy/analysis/persistence.py +10 -19
- {twopy-0.1.9 → twopy-0.2.1}/src/twopy/analysis/response_context.py +8 -11
- {twopy-0.1.9 → twopy-0.2.1}/src/twopy/analysis/response_maps.py +9 -7
- {twopy-0.1.9 → twopy-0.2.1}/src/twopy/analysis/response_processing/__init__.py +1 -1
- {twopy-0.1.9 → twopy-0.2.1}/src/twopy/analysis/response_processing/options.py +7 -8
- {twopy-0.1.9 → twopy-0.2.1}/src/twopy/analysis/response_processing/signals.py +7 -4
- {twopy-0.1.9 → twopy-0.2.1}/src/twopy/analysis/response_window_options.py +1 -1
- {twopy-0.1.9 → twopy-0.2.1}/src/twopy/analysis/responses.py +63 -19
- twopy-0.2.1/src/twopy/analysis/timing.py +315 -0
- {twopy-0.1.9 → twopy-0.2.1}/src/twopy/analysis/trials.py +1 -84
- {twopy-0.1.9 → twopy-0.2.1}/src/twopy/analysis/workflow.py +9 -9
- {twopy-0.1.9 → twopy-0.2.1}/src/twopy/analysis_cache.py +25 -5
- {twopy-0.1.9 → twopy-0.2.1}/src/twopy/api.py +6 -4
- {twopy-0.1.9 → twopy-0.2.1}/src/twopy/config.py +211 -36
- {twopy-0.1.9 → twopy-0.2.1}/src/twopy/conversion/api.py +63 -6
- {twopy-0.1.9 → twopy-0.2.1}/src/twopy/conversion/hdf5_writing.py +1 -1
- {twopy-0.1.9 → twopy-0.2.1}/src/twopy/conversion/source_loading.py +26 -6
- {twopy-0.1.9 → twopy-0.2.1}/src/twopy/conversion/stimulus_code.py +1 -1
- {twopy-0.1.9 → twopy-0.2.1}/src/twopy/conversion/types.py +2 -2
- {twopy-0.1.9 → twopy-0.2.1}/src/twopy/converted.py +60 -1
- twopy-0.2.1/src/twopy/custom/__init__.py +71 -0
- twopy-0.2.1/src/twopy/custom/discovery.py +303 -0
- twopy-0.2.1/src/twopy/custom/native.py +27 -0
- twopy-0.2.1/src/twopy/custom/native_workflows/__init__.py +5 -0
- twopy-0.2.1/src/twopy/custom/native_workflows/direction_selectivity.py +202 -0
- twopy-0.2.1/src/twopy/custom/native_workflows/response_kernels.py +497 -0
- twopy-0.2.1/src/twopy/custom/parameters.py +380 -0
- twopy-0.2.1/src/twopy/custom/provenance.py +399 -0
- twopy-0.2.1/src/twopy/custom/registry.py +78 -0
- twopy-0.2.1/src/twopy/custom/types.py +1077 -0
- {twopy-0.1.9 → twopy-0.2.1}/src/twopy/database/__init__.py +4 -2
- {twopy-0.1.9 → twopy-0.2.1}/src/twopy/database/cache.py +1 -1
- {twopy-0.1.9 → twopy-0.2.1}/src/twopy/database/catalog.py +1 -1
- {twopy-0.1.9 → twopy-0.2.1}/src/twopy/database/generic.py +1 -1
- {twopy-0.1.9 → twopy-0.2.1}/src/twopy/database/modeled.py +7 -4
- {twopy-0.1.9 → twopy-0.2.1}/src/twopy/database/search.py +147 -6
- {twopy-0.1.9 → twopy-0.2.1}/src/twopy/database/types.py +0 -3
- {twopy-0.1.9 → twopy-0.2.1}/src/twopy/hdf5_utils.py +2 -2
- {twopy-0.1.9 → twopy-0.2.1}/src/twopy/napari/controls.py +152 -281
- twopy-0.2.1/src/twopy/napari/custom_tab.py +755 -0
- twopy-0.2.1/src/twopy/napari/database_favorites.py +416 -0
- {twopy-0.1.9 → twopy-0.2.1}/src/twopy/napari/database_search.py +387 -68
- {twopy-0.1.9 → twopy-0.2.1}/src/twopy/napari/display_paths.py +41 -3
- twopy-0.2.1/src/twopy/napari/group_matching/__init__.py +29 -0
- twopy-0.2.1/src/twopy/napari/group_matching/cards.py +127 -0
- twopy-0.2.1/src/twopy/napari/group_matching/fov_assignment.py +679 -0
- twopy-0.2.1/src/twopy/napari/group_matching/fov_cards.py +143 -0
- twopy-0.1.9/src/twopy/napari/group_matching_images.py → twopy-0.2.1/src/twopy/napari/group_matching/images.py +2 -2
- twopy-0.2.1/src/twopy/napari/group_matching/response_preview.py +334 -0
- twopy-0.2.1/src/twopy/napari/group_matching/responses.py +130 -0
- twopy-0.2.1/src/twopy/napari/group_matching/roi_assignment.py +1269 -0
- twopy-0.2.1/src/twopy/napari/group_matching/roi_cards.py +371 -0
- twopy-0.2.1/src/twopy/napari/group_matching/style.py +269 -0
- twopy-0.2.1/src/twopy/napari/group_matching/tables.py +27 -0
- twopy-0.2.1/src/twopy/napari/group_matching/window.py +267 -0
- {twopy-0.1.9 → twopy-0.2.1}/src/twopy/napari/interactive.py +17 -21
- {twopy-0.1.9 → twopy-0.2.1}/src/twopy/napari/live_analysis.py +125 -122
- twopy-0.2.1/src/twopy/napari/load_workflow.py +409 -0
- {twopy-0.1.9 → twopy-0.2.1}/src/twopy/napari/loading.py +69 -13
- {twopy-0.1.9 → twopy-0.2.1}/src/twopy/napari/plotting/data.py +89 -47
- {twopy-0.1.9 → twopy-0.2.1}/src/twopy/napari/plotting/dff_options.py +1 -0
- {twopy-0.1.9 → twopy-0.2.1}/src/twopy/napari/plotting/docks/api.py +2 -2
- {twopy-0.1.9 → twopy-0.2.1}/src/twopy/napari/plotting/docks/options_panel.py +18 -13
- {twopy-0.1.9 → twopy-0.2.1}/src/twopy/napari/plotting/docks/paths.py +5 -6
- {twopy-0.1.9 → twopy-0.2.1}/src/twopy/napari/plotting/docks/plot_area.py +88 -0
- {twopy-0.1.9 → twopy-0.2.1}/src/twopy/napari/plotting/docks/response_map_area.py +13 -0
- {twopy-0.1.9 → twopy-0.2.1}/src/twopy/napari/plotting/docks/response_plot_widget.py +737 -21
- {twopy-0.1.9 → twopy-0.2.1}/src/twopy/napari/plotting/docks/save_actions.py +30 -67
- {twopy-0.1.9 → twopy-0.2.1}/src/twopy/napari/plotting/docks/visibility_state.py +29 -2
- {twopy-0.1.9 → twopy-0.2.1}/src/twopy/napari/plotting/export.py +37 -0
- {twopy-0.1.9 → twopy-0.2.1}/src/twopy/napari/plotting/export_controls.py +13 -5
- {twopy-0.1.9 → twopy-0.2.1}/src/twopy/napari/plotting/form_controls.py +5 -1
- {twopy-0.1.9 → twopy-0.2.1}/src/twopy/napari/plotting/normalization_options.py +7 -1
- {twopy-0.1.9 → twopy-0.2.1}/src/twopy/napari/plotting/options.py +1 -1
- twopy-0.2.1/src/twopy/napari/plotting/panels.py +188 -0
- twopy-0.2.1/src/twopy/napari/plotting/preview_strip.py +218 -0
- {twopy-0.1.9 → twopy-0.2.1}/src/twopy/napari/plotting/processing_options.py +172 -94
- {twopy-0.1.9 → twopy-0.2.1}/src/twopy/napari/plotting/roi_generation/actions.py +2 -2
- {twopy-0.1.9 → twopy-0.2.1}/src/twopy/napari/plotting/widgets.py +470 -26
- twopy-0.2.1/src/twopy/napari/responses.py +302 -0
- twopy-0.2.1/src/twopy/napari/roi_signatures.py +20 -0
- {twopy-0.1.9 → twopy-0.2.1}/src/twopy/napari/session.py +42 -3
- {twopy-0.1.9 → twopy-0.2.1}/src/twopy/napari/sidebar.py +36 -14
- twopy-0.2.1/src/twopy/napari/state.py +192 -0
- twopy-0.2.1/src/twopy/napari/text.py +64 -0
- {twopy-0.1.9 → twopy-0.2.1}/src/twopy/napari/trial_timeline.py +3 -3
- {twopy-0.1.9 → twopy-0.2.1}/src/twopy.egg-info/PKG-INFO +8 -3
- {twopy-0.1.9 → twopy-0.2.1}/src/twopy.egg-info/SOURCES.txt +55 -3
- {twopy-0.1.9 → twopy-0.2.1}/tests/test_api.py +12 -2
- {twopy-0.1.9 → twopy-0.2.1}/tests/test_background_subtraction.py +2 -4
- {twopy-0.1.9 → twopy-0.2.1}/tests/test_config.py +176 -19
- {twopy-0.1.9 → twopy-0.2.1}/tests/test_conversion.py +181 -2
- {twopy-0.1.9 → twopy-0.2.1}/tests/test_converted.py +63 -101
- twopy-0.2.1/tests/test_custom_workflows.py +1053 -0
- {twopy-0.1.9 → twopy-0.2.1}/tests/test_database.py +40 -3
- {twopy-0.1.9 → twopy-0.2.1}/tests/test_dff.py +33 -3
- {twopy-0.1.9 → twopy-0.2.1}/tests/test_epoch_mapping.py +5 -30
- twopy-0.2.1/tests/test_function_inventory.py +155 -0
- twopy-0.2.1/tests/test_group_matching.py +384 -0
- {twopy-0.1.9 → twopy-0.2.1}/tests/test_hdf5_utils.py +1 -2
- twopy-0.2.1/tests/test_kernels.py +489 -0
- twopy-0.2.1/tests/test_load_workflow.py +617 -0
- {twopy-0.1.9 → twopy-0.2.1}/tests/test_motion.py +4 -33
- twopy-0.2.1/tests/test_napari.py +674 -0
- twopy-0.2.1/tests/test_napari_database_search.py +697 -0
- twopy-0.2.1/tests/test_napari_display.py +132 -0
- twopy-0.2.1/tests/test_napari_export.py +394 -0
- twopy-0.2.1/tests/test_napari_group_matching_responses.py +140 -0
- twopy-0.2.1/tests/test_napari_group_matching_roi_cards.py +156 -0
- twopy-0.2.1/tests/test_napari_live_controller.py +574 -0
- twopy-0.2.1/tests/test_napari_load_tab.py +634 -0
- twopy-0.2.1/tests/test_napari_loaded_recordings.py +1004 -0
- twopy-0.2.1/tests/test_napari_path_resolution.py +659 -0
- twopy-0.2.1/tests/test_napari_plot_controls.py +569 -0
- twopy-0.2.1/tests/test_napari_plot_data.py +366 -0
- twopy-0.2.1/tests/test_napari_plot_widgets.py +1808 -0
- twopy-0.2.1/tests/test_napari_processing_controls.py +747 -0
- twopy-0.2.1/tests/test_napari_response_map_display.py +85 -0
- twopy-0.2.1/tests/test_napari_response_workflow.py +756 -0
- twopy-0.2.1/tests/test_napari_roi_generation.py +620 -0
- twopy-0.2.1/tests/test_napari_roi_visibility.py +332 -0
- twopy-0.2.1/tests/test_napari_text.py +49 -0
- twopy-0.2.1/tests/test_napari_timeline.py +251 -0
- {twopy-0.1.9 → twopy-0.2.1}/tests/test_packaging.py +15 -23
- {twopy-0.1.9 → twopy-0.2.1}/tests/test_parity.py +9 -31
- {twopy-0.1.9 → twopy-0.2.1}/tests/test_parity_psycho5.py +7 -62
- {twopy-0.1.9 → twopy-0.2.1}/tests/test_photodiode_classification.py +4 -31
- twopy-0.2.1/tests/test_recording_timing.py +200 -0
- {twopy-0.1.9 → twopy-0.2.1}/tests/test_response_maps.py +6 -32
- {twopy-0.1.9 → twopy-0.2.1}/tests/test_response_processing.py +45 -28
- {twopy-0.1.9 → twopy-0.2.1}/tests/test_response_roi_extraction.py +7 -33
- {twopy-0.1.9 → twopy-0.2.1}/tests/test_responses.py +21 -0
- {twopy-0.1.9 → twopy-0.2.1}/tests/test_roi.py +2 -6
- {twopy-0.1.9 → twopy-0.2.1}/tests/test_stimulus.py +38 -73
- {twopy-0.1.9 → twopy-0.2.1}/tests/test_synchronization.py +25 -57
- twopy-0.2.1/tests/test_trials.py +182 -0
- {twopy-0.1.9 → twopy-0.2.1}/tests/test_workflow.py +9 -72
- twopy-0.1.9/src/twopy/napari/group_matching.py +0 -585
- twopy-0.1.9/src/twopy/napari/group_matching_roi.py +0 -1147
- twopy-0.1.9/src/twopy/napari/plotting/panels.py +0 -103
- twopy-0.1.9/src/twopy/napari/responses.py +0 -141
- twopy-0.1.9/src/twopy/napari/state.py +0 -106
- twopy-0.1.9/src/twopy/napari/text.py +0 -26
- twopy-0.1.9/tests/test_group_matching.py +0 -180
- twopy-0.1.9/tests/test_napari.py +0 -7001
- twopy-0.1.9/tests/test_trials.py +0 -372
- {twopy-0.1.9 → twopy-0.2.1}/setup.cfg +0 -0
- {twopy-0.1.9 → twopy-0.2.1}/src/twopy/_segments.py +0 -0
- {twopy-0.1.9 → twopy-0.2.1}/src/twopy/_version.py +0 -0
- {twopy-0.1.9 → twopy-0.2.1}/src/twopy/analysis/dff_options.py +0 -0
- {twopy-0.1.9 → twopy-0.2.1}/src/twopy/analysis/epoch_mapping.py +0 -0
- {twopy-0.1.9 → twopy-0.2.1}/src/twopy/analysis/motion.py +0 -0
- {twopy-0.1.9 → twopy-0.2.1}/src/twopy/analysis/response_processing/apply.py +0 -0
- {twopy-0.1.9 → twopy-0.2.1}/src/twopy/analysis/response_processing/persistence.py +0 -0
- {twopy-0.1.9 → twopy-0.2.1}/src/twopy/analysis/response_processing/qc.py +0 -0
- {twopy-0.1.9 → twopy-0.2.1}/src/twopy/calibrations/pixel_calibration_profiles.csv +0 -0
- {twopy-0.1.9 → twopy-0.2.1}/src/twopy/calibrations/pixel_size.csv +0 -0
- {twopy-0.1.9 → twopy-0.2.1}/src/twopy/conversion/__init__.py +0 -0
- {twopy-0.1.9 → twopy-0.2.1}/src/twopy/conversion/alignment_crop.py +0 -0
- {twopy-0.1.9 → twopy-0.2.1}/src/twopy/conversion/matlab_values.py +0 -0
- {twopy-0.1.9 → twopy-0.2.1}/src/twopy/database/connection.py +0 -0
- {twopy-0.1.9 → twopy-0.2.1}/src/twopy/database/sql.py +0 -0
- {twopy-0.1.9 → twopy-0.2.1}/src/twopy/filenames.py +0 -0
- {twopy-0.1.9 → twopy-0.2.1}/src/twopy/frame_ranges.py +0 -0
- {twopy-0.1.9 → twopy-0.2.1}/src/twopy/inspection.py +0 -0
- {twopy-0.1.9 → twopy-0.2.1}/src/twopy/matlab.py +0 -0
- {twopy-0.1.9 → twopy-0.2.1}/src/twopy/napari/__init__.py +0 -0
- {twopy-0.1.9 → twopy-0.2.1}/src/twopy/napari/__main__.py +0 -0
- {twopy-0.1.9 → twopy-0.2.1}/src/twopy/napari/dims.py +0 -0
- {twopy-0.1.9 → twopy-0.2.1}/src/twopy/napari/display.py +0 -0
- {twopy-0.1.9 → twopy-0.2.1}/src/twopy/napari/errors.py +0 -0
- {twopy-0.1.9 → twopy-0.2.1}/src/twopy/napari/latest_worker.py +0 -0
- {twopy-0.1.9 → twopy-0.2.1}/src/twopy/napari/launcher.py +0 -0
- {twopy-0.1.9 → twopy-0.2.1}/src/twopy/napari/loaded_recordings_csv.py +0 -0
- {twopy-0.1.9 → twopy-0.2.1}/src/twopy/napari/movie.py +0 -0
- {twopy-0.1.9 → twopy-0.2.1}/src/twopy/napari/paths.py +0 -0
- {twopy-0.1.9 → twopy-0.2.1}/src/twopy/napari/plotting/__init__.py +0 -0
- {twopy-0.1.9 → twopy-0.2.1}/src/twopy/napari/plotting/docks/__init__.py +0 -0
- {twopy-0.1.9 → twopy-0.2.1}/src/twopy/napari/plotting/docks/dynamic_options.py +0 -0
- {twopy-0.1.9 → twopy-0.2.1}/src/twopy/napari/plotting/label_visibility.py +0 -0
- {twopy-0.1.9 → twopy-0.2.1}/src/twopy/napari/plotting/response_map_colors.py +0 -0
- {twopy-0.1.9 → twopy-0.2.1}/src/twopy/napari/plotting/response_map_display.py +0 -0
- {twopy-0.1.9 → twopy-0.2.1}/src/twopy/napari/plotting/response_map_options.py +0 -0
- {twopy-0.1.9 → twopy-0.2.1}/src/twopy/napari/plotting/response_window_options.py +0 -0
- {twopy-0.1.9 → twopy-0.2.1}/src/twopy/napari/plotting/roi_generation/__init__.py +0 -0
- {twopy-0.1.9 → twopy-0.2.1}/src/twopy/napari/plotting/roi_generation/controls.py +0 -0
- {twopy-0.1.9 → twopy-0.2.1}/src/twopy/napari/plotting/roi_generation/options.py +0 -0
- {twopy-0.1.9 → twopy-0.2.1}/src/twopy/napari/protocols.py +0 -0
- {twopy-0.1.9 → twopy-0.2.1}/src/twopy/napari/roi.py +0 -0
- {twopy-0.1.9 → twopy-0.2.1}/src/twopy/napari/types.py +0 -0
- {twopy-0.1.9 → twopy-0.2.1}/src/twopy/napari/viewer.py +0 -0
- {twopy-0.1.9 → twopy-0.2.1}/src/twopy/parity/__init__.py +0 -0
- {twopy-0.1.9 → twopy-0.2.1}/src/twopy/parity/dff.py +0 -0
- {twopy-0.1.9 → twopy-0.2.1}/src/twopy/parity/psycho5.py +0 -0
- {twopy-0.1.9 → twopy-0.2.1}/src/twopy/parity/roi.py +0 -0
- {twopy-0.1.9 → twopy-0.2.1}/src/twopy/parity/roi_extraction.py +0 -0
- {twopy-0.1.9 → twopy-0.2.1}/src/twopy/parity/saved_analysis.py +0 -0
- {twopy-0.1.9 → twopy-0.2.1}/src/twopy/photodiode.py +0 -0
- {twopy-0.1.9 → twopy-0.2.1}/src/twopy/photodiode_classification.py +0 -0
- {twopy-0.1.9 → twopy-0.2.1}/src/twopy/pixel_calibration.py +0 -0
- {twopy-0.1.9 → twopy-0.2.1}/src/twopy/pixel_calibration_profiles.py +0 -0
- {twopy-0.1.9 → twopy-0.2.1}/src/twopy/response_roi_extraction.py +0 -0
- {twopy-0.1.9 → twopy-0.2.1}/src/twopy/roi.py +0 -0
- {twopy-0.1.9 → twopy-0.2.1}/src/twopy/roi_extraction.py +0 -0
- {twopy-0.1.9 → twopy-0.2.1}/src/twopy/roi_mask_cleanup.py +0 -0
- {twopy-0.1.9 → twopy-0.2.1}/src/twopy/session.py +0 -0
- {twopy-0.1.9 → twopy-0.2.1}/src/twopy/spatial.py +0 -0
- {twopy-0.1.9 → twopy-0.2.1}/src/twopy/stimulus.py +0 -0
- {twopy-0.1.9 → twopy-0.2.1}/src/twopy/synchronization.py +0 -0
- {twopy-0.1.9 → twopy-0.2.1}/src/twopy/typing_guards.py +0 -0
- {twopy-0.1.9 → twopy-0.2.1}/src/twopy.egg-info/dependency_links.txt +0 -0
- {twopy-0.1.9 → twopy-0.2.1}/src/twopy.egg-info/entry_points.txt +0 -0
- {twopy-0.1.9 → twopy-0.2.1}/src/twopy.egg-info/requires.txt +0 -0
- {twopy-0.1.9 → twopy-0.2.1}/src/twopy.egg-info/top_level.txt +0 -0
- {twopy-0.1.9 → twopy-0.2.1}/tests/test_filenames.py +0 -0
- {twopy-0.1.9 → twopy-0.2.1}/tests/test_frame_ranges.py +0 -0
- {twopy-0.1.9 → twopy-0.2.1}/tests/test_import.py +0 -0
- {twopy-0.1.9 → twopy-0.2.1}/tests/test_inspection.py +0 -0
- {twopy-0.1.9 → twopy-0.2.1}/tests/test_matlab.py +0 -0
- {twopy-0.1.9 → twopy-0.2.1}/tests/test_parity_roi_extraction.py +0 -0
- {twopy-0.1.9 → twopy-0.2.1}/tests/test_persistence.py +0 -0
- {twopy-0.1.9 → twopy-0.2.1}/tests/test_pixel_calibration.py +0 -0
- {twopy-0.1.9 → twopy-0.2.1}/tests/test_pixel_calibration_profiles.py +0 -0
- {twopy-0.1.9 → twopy-0.2.1}/tests/test_real_fixture.py +0 -0
- {twopy-0.1.9 → twopy-0.2.1}/tests/test_response_map_area.py +0 -0
- {twopy-0.1.9 → twopy-0.2.1}/tests/test_response_window_options.py +0 -0
- {twopy-0.1.9 → twopy-0.2.1}/tests/test_roi_extraction.py +0 -0
- {twopy-0.1.9 → twopy-0.2.1}/tests/test_roi_mask_cleanup.py +0 -0
- {twopy-0.1.9 → twopy-0.2.1}/tests/test_saved_analysis.py +0 -0
- {twopy-0.1.9 → twopy-0.2.1}/tests/test_session.py +0 -0
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: twopy
|
|
3
|
-
Version: 0.1
|
|
3
|
+
Version: 0.2.1
|
|
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
|
|
7
|
+
Project-URL: Homepage, https://twopy.gumadeiras.com
|
|
8
|
+
Project-URL: Repository, https://github.com/gumadeiras/twopy
|
|
7
9
|
Requires-Python: <3.14,>=3.13
|
|
8
10
|
Description-Content-Type: text/markdown
|
|
9
11
|
Requires-Dist: h5py<4,>=3.16
|
|
@@ -73,8 +75,11 @@ When a source recording has not been converted yet, twopy converts it first. Con
|
|
|
73
75
|
|
|
74
76
|
## Docs
|
|
75
77
|
|
|
76
|
-
- [
|
|
77
|
-
- [
|
|
78
|
+
- [Full documentation](https://twopy.gumadeiras.com)
|
|
79
|
+
- [Getting started](docs/getting_started.md): install, configure, launch, and save your first analysis.
|
|
80
|
+
- [GUI guide](docs/gui/index.md): one page per task — loading, ROIs, plots, custom workflows, group matching, saving.
|
|
81
|
+
- [Python API guide](docs/python_api.md): find recordings, convert data, extract traces, fit kernels, and open napari from scripts.
|
|
82
|
+
- [Writing custom workflows](docs/writing_custom_workflows.md): add your own analysis to the napari Custom tab.
|
|
78
83
|
- [Development guide](docs/development.md): source setup and local checks.
|
|
79
84
|
- [Input data spec](docs/input_data_spec.md): short recording folder contract.
|
|
80
85
|
- [Recording file reference](docs/recording_file_schema.md): detailed source and converted file schema.
|
|
@@ -53,8 +53,11 @@ When a source recording has not been converted yet, twopy converts it first. Con
|
|
|
53
53
|
|
|
54
54
|
## Docs
|
|
55
55
|
|
|
56
|
-
- [
|
|
57
|
-
- [
|
|
56
|
+
- [Full documentation](https://twopy.gumadeiras.com)
|
|
57
|
+
- [Getting started](docs/getting_started.md): install, configure, launch, and save your first analysis.
|
|
58
|
+
- [GUI guide](docs/gui/index.md): one page per task — loading, ROIs, plots, custom workflows, group matching, saving.
|
|
59
|
+
- [Python API guide](docs/python_api.md): find recordings, convert data, extract traces, fit kernels, and open napari from scripts.
|
|
60
|
+
- [Writing custom workflows](docs/writing_custom_workflows.md): add your own analysis to the napari Custom tab.
|
|
58
61
|
- [Development guide](docs/development.md): source setup and local checks.
|
|
59
62
|
- [Input data spec](docs/input_data_spec.md): short recording folder contract.
|
|
60
63
|
- [Recording file reference](docs/recording_file_schema.md): detailed source and converted file schema.
|
|
@@ -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.2.1"
|
|
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"
|
|
@@ -29,6 +29,10 @@ dependencies = [
|
|
|
29
29
|
[project.scripts]
|
|
30
30
|
twopy = "twopy.napari:main"
|
|
31
31
|
|
|
32
|
+
[project.urls]
|
|
33
|
+
Homepage = "https://twopy.gumadeiras.com"
|
|
34
|
+
Repository = "https://github.com/gumadeiras/twopy"
|
|
35
|
+
|
|
32
36
|
[tool.setuptools.packages.find]
|
|
33
37
|
where = ["src"]
|
|
34
38
|
|
|
@@ -43,11 +47,15 @@ target-version = "py313"
|
|
|
43
47
|
[tool.ruff.lint]
|
|
44
48
|
select = [
|
|
45
49
|
"ANN",
|
|
50
|
+
"ARG",
|
|
46
51
|
"B",
|
|
52
|
+
"C4",
|
|
47
53
|
"D",
|
|
48
54
|
"E",
|
|
49
55
|
"F",
|
|
50
56
|
"I",
|
|
57
|
+
"NPY",
|
|
58
|
+
"PTH",
|
|
51
59
|
"SIM",
|
|
52
60
|
"UP",
|
|
53
61
|
"W",
|
|
@@ -22,12 +22,17 @@ from twopy.analysis import (
|
|
|
22
22
|
FrameWindow,
|
|
23
23
|
GroupedRoiResponses,
|
|
24
24
|
GroupedRoiResponseSummary,
|
|
25
|
-
|
|
25
|
+
Hemisphere,
|
|
26
|
+
KernelFitMethod,
|
|
27
|
+
KernelStimulusModality,
|
|
26
28
|
LoadedAnalysisOutputs,
|
|
27
29
|
LowPassFilterOptions,
|
|
28
30
|
ManualFovGroupRow,
|
|
31
|
+
ManualRoiMatchGroup,
|
|
29
32
|
ManualRoiMatchRow,
|
|
30
33
|
ManualRoiMatchStatus,
|
|
34
|
+
RecordingKernelFit,
|
|
35
|
+
RecordingTiming,
|
|
31
36
|
ResponseMapData,
|
|
32
37
|
ResponseMapMode,
|
|
33
38
|
ResponseMapOptions,
|
|
@@ -37,7 +42,10 @@ from twopy.analysis import (
|
|
|
37
42
|
RoiResponseSummary,
|
|
38
43
|
RoiResponseTrial,
|
|
39
44
|
SmoothingOptions,
|
|
45
|
+
StimulusKernelOptions,
|
|
46
|
+
TimingSource,
|
|
40
47
|
WindowedRoiResponse,
|
|
48
|
+
add_manual_roi_match_group,
|
|
41
49
|
analyze_recording_responses,
|
|
42
50
|
append_manual_roi_match_rows,
|
|
43
51
|
apply_motion_artifact_mask_to_delta_f_over_f,
|
|
@@ -45,11 +53,13 @@ from twopy.analysis import (
|
|
|
45
53
|
compute_recording_responses,
|
|
46
54
|
compute_roi_delta_f_over_f,
|
|
47
55
|
default_baseline_epoch_number,
|
|
56
|
+
default_kernel_stimulus_column,
|
|
48
57
|
default_recording_baseline_epoch_number,
|
|
49
58
|
extract_background_corrected_roi_traces,
|
|
50
|
-
|
|
59
|
+
finite_mean_and_sem,
|
|
60
|
+
fit_recording_stimulus_kernels,
|
|
61
|
+
fit_stimulus_kernel,
|
|
51
62
|
group_delta_f_over_f_by_epoch,
|
|
52
|
-
interpolate_stimulus_epochs_to_frame_windows,
|
|
53
63
|
is_baseline_epoch_name,
|
|
54
64
|
load_analysis_outputs,
|
|
55
65
|
load_manual_fov_group_rows,
|
|
@@ -58,10 +68,13 @@ from twopy.analysis import (
|
|
|
58
68
|
make_frame_windows,
|
|
59
69
|
make_manual_fov_group_rows,
|
|
60
70
|
make_manual_roi_match_rows,
|
|
61
|
-
|
|
71
|
+
matched_manual_roi_groups,
|
|
62
72
|
next_group_cell_id,
|
|
63
73
|
no_baseline_epoch_frame_windows,
|
|
74
|
+
remove_manual_roi_match_group,
|
|
75
|
+
replace_manual_roi_match_group,
|
|
64
76
|
resolve_baseline_frame_windows,
|
|
77
|
+
resolve_recording_timing,
|
|
65
78
|
save_analysis_outputs,
|
|
66
79
|
save_manual_fov_group_rows,
|
|
67
80
|
save_manual_roi_match_rows,
|
|
@@ -76,7 +89,11 @@ from twopy.analysis import (
|
|
|
76
89
|
)
|
|
77
90
|
from twopy.api import find_recordings
|
|
78
91
|
from twopy.conversion import convert_recording_to_twopy, load_source_conversion_inputs
|
|
79
|
-
from twopy.converted import
|
|
92
|
+
from twopy.converted import (
|
|
93
|
+
load_converted_recording,
|
|
94
|
+
recording_frame_rate_hz,
|
|
95
|
+
recording_hemisphere,
|
|
96
|
+
)
|
|
80
97
|
from twopy.napari import (
|
|
81
98
|
NapariRecordingView,
|
|
82
99
|
NapariSidebarWidgets,
|
|
@@ -161,6 +178,7 @@ __all__ = [
|
|
|
161
178
|
"AlignedPhotodiodeEvent",
|
|
162
179
|
"AnalysisResponseRun",
|
|
163
180
|
"AnalysisResponseComputation",
|
|
181
|
+
"add_manual_roi_match_group",
|
|
164
182
|
"analyze_recording_responses",
|
|
165
183
|
"add_twopy_magicgui_controls",
|
|
166
184
|
"append_manual_roi_match_rows",
|
|
@@ -181,6 +199,7 @@ __all__ = [
|
|
|
181
199
|
"DEFAULT_PIXEL_CALIBRATION_PATH",
|
|
182
200
|
"DEFAULT_PIXEL_CALIBRATION_PROFILE_PATH",
|
|
183
201
|
"default_baseline_epoch_number",
|
|
202
|
+
"default_kernel_stimulus_column",
|
|
184
203
|
"default_recording_baseline_epoch_number",
|
|
185
204
|
"detect_photodiode_events",
|
|
186
205
|
"detect_recording_photodiode_events",
|
|
@@ -194,18 +213,20 @@ __all__ = [
|
|
|
194
213
|
"extract_rois_from_image",
|
|
195
214
|
"extract_roi_traces",
|
|
196
215
|
"find_recordings",
|
|
197
|
-
"
|
|
216
|
+
"fit_recording_stimulus_kernels",
|
|
217
|
+
"fit_stimulus_kernel",
|
|
198
218
|
"FrameWindow",
|
|
199
219
|
"full_frame_crop",
|
|
200
220
|
"group_delta_f_over_f_by_epoch",
|
|
201
221
|
"GroupedRoiResponseSummary",
|
|
202
222
|
"GroupedRoiResponses",
|
|
223
|
+
"Hemisphere",
|
|
203
224
|
"grid_roi_set",
|
|
204
225
|
"grid_roi_set_microns",
|
|
205
226
|
"grid_size_pixels_from_microns",
|
|
206
|
-
"InterpolatedEpochMapping",
|
|
207
|
-
"interpolate_stimulus_epochs_to_frame_windows",
|
|
208
227
|
"is_baseline_epoch_name",
|
|
228
|
+
"KernelFitMethod",
|
|
229
|
+
"KernelStimulusModality",
|
|
209
230
|
"launch_napari",
|
|
210
231
|
"load_analysis_outputs",
|
|
211
232
|
"load_response_map_data",
|
|
@@ -219,16 +240,19 @@ __all__ = [
|
|
|
219
240
|
"LoadedAnalysisOutputs",
|
|
220
241
|
"LowPassFilterOptions",
|
|
221
242
|
"ManualFovGroupRow",
|
|
243
|
+
"ManualRoiMatchGroup",
|
|
222
244
|
"ManualRoiMatchRow",
|
|
223
245
|
"ManualRoiMatchStatus",
|
|
246
|
+
"matched_manual_roi_groups",
|
|
224
247
|
"make_roi_set_from_label_image",
|
|
225
248
|
"make_frame_windows",
|
|
226
249
|
"make_manual_fov_group_rows",
|
|
227
250
|
"make_manual_roi_match_rows",
|
|
228
251
|
"make_roi_set",
|
|
229
|
-
"map_stimulus_epochs_to_frame_windows",
|
|
230
252
|
"next_group_cell_id",
|
|
231
253
|
"no_baseline_epoch_frame_windows",
|
|
254
|
+
"remove_manual_roi_match_group",
|
|
255
|
+
"replace_manual_roi_match_group",
|
|
232
256
|
"map_stimulus_specific_column",
|
|
233
257
|
"NapariRecordingView",
|
|
234
258
|
"NapariSidebarWidgets",
|
|
@@ -255,10 +279,14 @@ __all__ = [
|
|
|
255
279
|
"ResponseStatistic",
|
|
256
280
|
"ResponseWatershedExtraction",
|
|
257
281
|
"ResponseWatershedScoreImages",
|
|
282
|
+
"RecordingKernelFit",
|
|
283
|
+
"RecordingTiming",
|
|
258
284
|
"recording_frame_rate_hz",
|
|
285
|
+
"recording_hemisphere",
|
|
259
286
|
"resolve_pixel_size_um",
|
|
260
287
|
"resolve_pixel_calibration_profile",
|
|
261
288
|
"resolve_baseline_frame_windows",
|
|
289
|
+
"resolve_recording_timing",
|
|
262
290
|
"response_watershed_roi_set",
|
|
263
291
|
"RoiExtractionConfig",
|
|
264
292
|
"RoiExtractionMethod",
|
|
@@ -282,10 +310,13 @@ __all__ = [
|
|
|
282
310
|
"SmoothingOptions",
|
|
283
311
|
"SpatialCrop",
|
|
284
312
|
"SpatialDomain",
|
|
313
|
+
"StimulusKernelOptions",
|
|
285
314
|
"StimulusSpecificColumnMapping",
|
|
286
315
|
"stimulus_epoch_names_by_number",
|
|
316
|
+
"finite_mean_and_sem",
|
|
287
317
|
"summarize_epoch_roi_responses",
|
|
288
318
|
"summarize_grouped_responses",
|
|
319
|
+
"TimingSource",
|
|
289
320
|
"validate_grouped_roi_responses",
|
|
290
321
|
"watershed_roi_set",
|
|
291
322
|
"WindowedRoiResponse",
|
|
@@ -19,23 +19,34 @@ from twopy.analysis.dff import (
|
|
|
19
19
|
compute_roi_delta_f_over_f,
|
|
20
20
|
)
|
|
21
21
|
from twopy.analysis.dff_options import DeltaFOverFBaselineMode, DeltaFOverFOptions
|
|
22
|
-
from twopy.analysis.epoch_mapping import (
|
|
23
|
-
InterpolatedEpochMapping,
|
|
24
|
-
interpolate_stimulus_epochs_to_frame_windows,
|
|
25
|
-
)
|
|
26
22
|
from twopy.analysis.group_matching import (
|
|
27
23
|
ManualFovGroupRow,
|
|
24
|
+
ManualRoiMatchGroup,
|
|
28
25
|
ManualRoiMatchRow,
|
|
29
26
|
ManualRoiMatchStatus,
|
|
27
|
+
add_manual_roi_match_group,
|
|
30
28
|
append_manual_roi_match_rows,
|
|
31
29
|
load_manual_fov_group_rows,
|
|
32
30
|
load_manual_roi_match_rows,
|
|
33
31
|
make_manual_fov_group_rows,
|
|
34
32
|
make_manual_roi_match_rows,
|
|
33
|
+
matched_manual_roi_groups,
|
|
35
34
|
next_group_cell_id,
|
|
35
|
+
remove_manual_roi_match_group,
|
|
36
|
+
replace_manual_roi_match_group,
|
|
36
37
|
save_manual_fov_group_rows,
|
|
37
38
|
save_manual_roi_match_rows,
|
|
38
39
|
)
|
|
40
|
+
from twopy.analysis.kernels import (
|
|
41
|
+
Hemisphere,
|
|
42
|
+
KernelFitMethod,
|
|
43
|
+
KernelStimulusModality,
|
|
44
|
+
RecordingKernelFit,
|
|
45
|
+
StimulusKernelOptions,
|
|
46
|
+
default_kernel_stimulus_column,
|
|
47
|
+
fit_recording_stimulus_kernels,
|
|
48
|
+
fit_stimulus_kernel,
|
|
49
|
+
)
|
|
39
50
|
from twopy.analysis.motion import apply_motion_artifact_mask_to_delta_f_over_f
|
|
40
51
|
from twopy.analysis.persistence import (
|
|
41
52
|
ANALYSIS_OUTPUT_FILE_FORMAT,
|
|
@@ -67,20 +78,24 @@ from twopy.analysis.responses import (
|
|
|
67
78
|
GroupedRoiResponseSummary,
|
|
68
79
|
RoiResponseSummary,
|
|
69
80
|
RoiResponseTrial,
|
|
81
|
+
finite_mean_and_sem,
|
|
70
82
|
group_delta_f_over_f_by_epoch,
|
|
71
83
|
summarize_epoch_roi_responses,
|
|
72
84
|
summarize_grouped_responses,
|
|
73
85
|
validate_grouped_roi_responses,
|
|
74
86
|
)
|
|
87
|
+
from twopy.analysis.timing import (
|
|
88
|
+
RecordingTiming,
|
|
89
|
+
TimingSource,
|
|
90
|
+
resolve_recording_timing,
|
|
91
|
+
)
|
|
75
92
|
from twopy.analysis.trials import (
|
|
76
93
|
EpochFrameWindow,
|
|
77
94
|
FrameWindow,
|
|
78
95
|
WindowedRoiResponse,
|
|
79
96
|
default_baseline_epoch_number,
|
|
80
|
-
frame_windows_from_photodiode_alignment,
|
|
81
97
|
is_baseline_epoch_name,
|
|
82
98
|
make_frame_windows,
|
|
83
|
-
map_stimulus_epochs_to_frame_windows,
|
|
84
99
|
no_baseline_epoch_frame_windows,
|
|
85
100
|
resolve_baseline_frame_windows,
|
|
86
101
|
select_baseline_frame_windows,
|
|
@@ -117,27 +132,33 @@ __all__ = [
|
|
|
117
132
|
"EpochResponseMap",
|
|
118
133
|
"EpochFrameWindow",
|
|
119
134
|
"extract_background_corrected_roi_traces",
|
|
120
|
-
"
|
|
135
|
+
"fit_recording_stimulus_kernels",
|
|
136
|
+
"fit_stimulus_kernel",
|
|
137
|
+
"default_kernel_stimulus_column",
|
|
138
|
+
"Hemisphere",
|
|
121
139
|
"FrameWindow",
|
|
122
140
|
"group_delta_f_over_f_by_epoch",
|
|
123
141
|
"GroupedRoiResponseSummary",
|
|
124
142
|
"GroupedRoiResponses",
|
|
125
|
-
"InterpolatedEpochMapping",
|
|
126
|
-
"interpolate_stimulus_epochs_to_frame_windows",
|
|
127
143
|
"is_baseline_epoch_name",
|
|
144
|
+
"KernelFitMethod",
|
|
145
|
+
"KernelStimulusModality",
|
|
128
146
|
"load_analysis_outputs",
|
|
129
147
|
"load_response_map_data",
|
|
130
148
|
"LoadedAnalysisOutputs",
|
|
131
149
|
"LowPassFilterOptions",
|
|
132
150
|
"ManualFovGroupRow",
|
|
151
|
+
"ManualRoiMatchGroup",
|
|
133
152
|
"ManualRoiMatchRow",
|
|
134
153
|
"ManualRoiMatchStatus",
|
|
154
|
+
"matched_manual_roi_groups",
|
|
135
155
|
"make_frame_windows",
|
|
136
156
|
"make_manual_fov_group_rows",
|
|
137
157
|
"make_manual_roi_match_rows",
|
|
138
|
-
"map_stimulus_epochs_to_frame_windows",
|
|
139
158
|
"no_baseline_epoch_frame_windows",
|
|
140
159
|
"next_group_cell_id",
|
|
160
|
+
"RecordingKernelFit",
|
|
161
|
+
"RecordingTiming",
|
|
141
162
|
"RoiDeltaFOverF",
|
|
142
163
|
"ResponseMapData",
|
|
143
164
|
"ResponseMapMode",
|
|
@@ -147,18 +168,25 @@ __all__ = [
|
|
|
147
168
|
"RoiResponseSummary",
|
|
148
169
|
"RoiResponseTrial",
|
|
149
170
|
"resolve_baseline_frame_windows",
|
|
171
|
+
"resolve_recording_timing",
|
|
172
|
+
"add_manual_roi_match_group",
|
|
150
173
|
"append_manual_roi_match_rows",
|
|
151
174
|
"load_manual_fov_group_rows",
|
|
152
175
|
"load_manual_roi_match_rows",
|
|
176
|
+
"remove_manual_roi_match_group",
|
|
177
|
+
"replace_manual_roi_match_group",
|
|
153
178
|
"save_analysis_outputs",
|
|
154
179
|
"save_response_map_data",
|
|
155
180
|
"save_manual_fov_group_rows",
|
|
156
181
|
"save_manual_roi_match_rows",
|
|
157
182
|
"select_baseline_frame_windows",
|
|
183
|
+
"finite_mean_and_sem",
|
|
158
184
|
"split_traces_by_frame_windows",
|
|
185
|
+
"StimulusKernelOptions",
|
|
159
186
|
"SmoothingOptions",
|
|
160
187
|
"summarize_epoch_roi_responses",
|
|
161
188
|
"summarize_grouped_responses",
|
|
189
|
+
"TimingSource",
|
|
162
190
|
"validate_grouped_roi_responses",
|
|
163
191
|
"WindowedRoiResponse",
|
|
164
192
|
"write_response_summary_grouped_csv",
|
|
@@ -15,6 +15,7 @@ from typing import Literal
|
|
|
15
15
|
|
|
16
16
|
import numpy as np
|
|
17
17
|
import numpy.typing as npt
|
|
18
|
+
from scipy import sparse
|
|
18
19
|
|
|
19
20
|
from twopy.converted import RecordingData
|
|
20
21
|
from twopy.frame_ranges import normalize_frame_range
|
|
@@ -278,6 +279,7 @@ def _extract_mean_traces_from_indices(
|
|
|
278
279
|
rather than total recording length.
|
|
279
280
|
"""
|
|
280
281
|
traces = np.empty((frame_stop - frame_start, len(pixel_indices_by_trace)))
|
|
282
|
+
projection = _mean_projection(pixel_indices_by_trace)
|
|
281
283
|
_check_cancelled(check_cancelled)
|
|
282
284
|
for chunk_start, chunk_stop, frames in recording.movie.iter_frame_batches(
|
|
283
285
|
chunk_frames=chunk_frames,
|
|
@@ -289,10 +291,7 @@ def _extract_mean_traces_from_indices(
|
|
|
289
291
|
chunk_offset = chunk_start - frame_start
|
|
290
292
|
chunk_slice = slice(chunk_offset, chunk_offset + (chunk_stop - chunk_start))
|
|
291
293
|
chunk_flat = frames.reshape(frames.shape[0], -1)
|
|
292
|
-
|
|
293
|
-
traces[chunk_slice, trace_index] = chunk_flat[:, pixel_indices].mean(
|
|
294
|
-
axis=1,
|
|
295
|
-
)
|
|
294
|
+
traces[chunk_slice, :] = _project_trace_pixels(chunk_flat, projection)
|
|
296
295
|
_check_cancelled(check_cancelled)
|
|
297
296
|
|
|
298
297
|
return traces
|
|
@@ -344,6 +343,7 @@ def _extract_movie_global_percentile_corrected_traces(
|
|
|
344
343
|
)
|
|
345
344
|
frame_count = frame_stop - frame_start
|
|
346
345
|
roi_count = len(roi_pixel_indices)
|
|
346
|
+
projection = _mean_projection(roi_pixel_indices)
|
|
347
347
|
raw_values = np.empty((frame_count, roi_count), dtype=np.float64)
|
|
348
348
|
background_values = np.empty((frame_count, roi_count), dtype=np.float64)
|
|
349
349
|
corrected_values = np.empty((frame_count, roi_count), dtype=np.float64)
|
|
@@ -359,14 +359,19 @@ def _extract_movie_global_percentile_corrected_traces(
|
|
|
359
359
|
chunk_slice = slice(chunk_offset, chunk_offset + (chunk_stop - chunk_start))
|
|
360
360
|
chunk_flat = frames.reshape(frames.shape[0], -1)
|
|
361
361
|
background_trace = chunk_flat[:, background_indices].mean(axis=1)
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
362
|
+
roi_pixels = chunk_flat[:, projection.pixel_indices]
|
|
363
|
+
raw_values[chunk_slice, :] = _project_selected_pixels(roi_pixels, projection)
|
|
364
|
+
background_values[chunk_slice, :] = background_trace[:, np.newaxis]
|
|
365
|
+
# Clamp pixels before averaging so dim pixels cannot pull the
|
|
366
|
+
# corrected ROI trace below zero.
|
|
367
|
+
corrected_pixels = np.maximum(
|
|
368
|
+
roi_pixels - background_trace[:, np.newaxis],
|
|
369
|
+
0.0,
|
|
370
|
+
)
|
|
371
|
+
corrected_values[chunk_slice, :] = _project_selected_pixels(
|
|
372
|
+
corrected_pixels,
|
|
373
|
+
projection,
|
|
374
|
+
)
|
|
370
375
|
_check_cancelled(check_cancelled)
|
|
371
376
|
|
|
372
377
|
metadata: dict[str, BackgroundMetadataValue] = {
|
|
@@ -440,6 +445,7 @@ def _extract_movie_y_stripe_percentile_corrected_traces(
|
|
|
440
445
|
)
|
|
441
446
|
frame_count = frame_stop - frame_start
|
|
442
447
|
roi_count = len(roi_pixel_indices)
|
|
448
|
+
projection = _mean_projection(roi_pixel_indices)
|
|
443
449
|
raw_values = np.empty((frame_count, roi_count), dtype=np.float64)
|
|
444
450
|
background_values = np.empty((frame_count, roi_count), dtype=np.float64)
|
|
445
451
|
corrected_values = np.empty((frame_count, roi_count), dtype=np.float64)
|
|
@@ -460,15 +466,21 @@ def _extract_movie_y_stripe_percentile_corrected_traces(
|
|
|
460
466
|
)
|
|
461
467
|
chunk_flat = frames.reshape(frames.shape[0], -1)
|
|
462
468
|
background_flat = background_field.reshape(background_field.shape[0], -1)
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
469
|
+
roi_pixels = chunk_flat[:, projection.pixel_indices]
|
|
470
|
+
roi_background_pixels = background_flat[:, projection.pixel_indices]
|
|
471
|
+
raw_values[chunk_slice, :] = _project_selected_pixels(roi_pixels, projection)
|
|
472
|
+
background_values[chunk_slice, :] = _project_selected_pixels(
|
|
473
|
+
roi_background_pixels,
|
|
474
|
+
projection,
|
|
475
|
+
)
|
|
476
|
+
corrected_pixels = np.maximum(
|
|
477
|
+
roi_pixels - roi_background_pixels,
|
|
478
|
+
0.0,
|
|
479
|
+
)
|
|
480
|
+
corrected_values[chunk_slice, :] = _project_selected_pixels(
|
|
481
|
+
corrected_pixels,
|
|
482
|
+
projection,
|
|
483
|
+
)
|
|
472
484
|
_check_cancelled(check_cancelled)
|
|
473
485
|
|
|
474
486
|
metadata: dict[str, BackgroundMetadataValue] = {
|
|
@@ -593,6 +605,93 @@ def _roi_pixel_indices_from_masks(
|
|
|
593
605
|
return tuple(np.flatnonzero(mask).astype(np.int64) for mask in flat_masks)
|
|
594
606
|
|
|
595
607
|
|
|
608
|
+
@dataclass(frozen=True)
|
|
609
|
+
class _MeanProjection:
|
|
610
|
+
"""Sparse averaging matrix plus selected flattened pixel indices."""
|
|
611
|
+
|
|
612
|
+
pixel_indices: npt.NDArray[np.int64]
|
|
613
|
+
matrix: sparse.csc_matrix
|
|
614
|
+
|
|
615
|
+
|
|
616
|
+
def _mean_projection(
|
|
617
|
+
pixel_indices_by_trace: tuple[npt.NDArray[np.int64], ...],
|
|
618
|
+
) -> _MeanProjection:
|
|
619
|
+
"""Return selected pixels and a sparse pixel-to-trace averaging matrix.
|
|
620
|
+
|
|
621
|
+
Args:
|
|
622
|
+
pixel_indices_by_trace: One flattened pixel-index array per trace.
|
|
623
|
+
|
|
624
|
+
Returns:
|
|
625
|
+
Selected flattened pixel indices plus a sparse matrix shaped
|
|
626
|
+
``(selected_pixels, traces)`` whose columns average each trace.
|
|
627
|
+
|
|
628
|
+
Pixel indices are concatenated per trace instead of deduplicated. That
|
|
629
|
+
preserves the existing behavior for overlapping ROIs, where the same movie
|
|
630
|
+
pixel can contribute independently to more than one ROI mean.
|
|
631
|
+
"""
|
|
632
|
+
selected_pixel_indices: list[npt.NDArray[np.int64]] = []
|
|
633
|
+
rows: list[npt.NDArray[np.int64]] = []
|
|
634
|
+
columns: list[npt.NDArray[np.int64]] = []
|
|
635
|
+
values: list[npt.NDArray[np.float64]] = []
|
|
636
|
+
row_start = 0
|
|
637
|
+
for trace_index, pixel_indices in enumerate(pixel_indices_by_trace):
|
|
638
|
+
if pixel_indices.size == 0:
|
|
639
|
+
continue
|
|
640
|
+
selected_pixel_indices.append(pixel_indices)
|
|
641
|
+
rows.append(
|
|
642
|
+
np.arange(
|
|
643
|
+
row_start,
|
|
644
|
+
row_start + pixel_indices.size,
|
|
645
|
+
dtype=np.int64,
|
|
646
|
+
),
|
|
647
|
+
)
|
|
648
|
+
row_start += pixel_indices.size
|
|
649
|
+
columns.append(
|
|
650
|
+
np.full(pixel_indices.shape, trace_index, dtype=np.int64),
|
|
651
|
+
)
|
|
652
|
+
values.append(
|
|
653
|
+
np.full(
|
|
654
|
+
pixel_indices.shape,
|
|
655
|
+
1.0 / float(pixel_indices.size),
|
|
656
|
+
dtype=np.float64,
|
|
657
|
+
),
|
|
658
|
+
)
|
|
659
|
+
if len(selected_pixel_indices) == 0:
|
|
660
|
+
return _MeanProjection(
|
|
661
|
+
pixel_indices=np.array([], dtype=np.int64),
|
|
662
|
+
matrix=sparse.csc_matrix((0, len(pixel_indices_by_trace))),
|
|
663
|
+
)
|
|
664
|
+
return _MeanProjection(
|
|
665
|
+
pixel_indices=np.concatenate(selected_pixel_indices),
|
|
666
|
+
matrix=sparse.csc_matrix(
|
|
667
|
+
(
|
|
668
|
+
np.concatenate(values),
|
|
669
|
+
(np.concatenate(rows), np.concatenate(columns)),
|
|
670
|
+
),
|
|
671
|
+
shape=(row_start, len(pixel_indices_by_trace)),
|
|
672
|
+
),
|
|
673
|
+
)
|
|
674
|
+
|
|
675
|
+
|
|
676
|
+
def _project_trace_pixels(
|
|
677
|
+
chunk_flat: npt.NDArray[np.float64],
|
|
678
|
+
projection: _MeanProjection,
|
|
679
|
+
) -> npt.NDArray[np.float64]:
|
|
680
|
+
"""Return trace means from a flattened movie chunk and projection."""
|
|
681
|
+
return _project_selected_pixels(chunk_flat[:, projection.pixel_indices], projection)
|
|
682
|
+
|
|
683
|
+
|
|
684
|
+
def _project_selected_pixels(
|
|
685
|
+
selected_pixels: npt.NDArray[np.float64],
|
|
686
|
+
projection: _MeanProjection,
|
|
687
|
+
) -> npt.NDArray[np.float64]:
|
|
688
|
+
"""Return trace means from selected pixel columns."""
|
|
689
|
+
return np.asarray(
|
|
690
|
+
selected_pixels @ projection.matrix,
|
|
691
|
+
dtype=np.float64,
|
|
692
|
+
)
|
|
693
|
+
|
|
694
|
+
|
|
596
695
|
def _check_cancelled(check_cancelled: Callable[[], None] | None) -> None:
|
|
597
696
|
"""Raise ``CancelledError`` when an interactive caller superseded this work."""
|
|
598
697
|
if check_cancelled is None:
|
|
@@ -115,6 +115,10 @@ def compute_roi_delta_f_over_f(
|
|
|
115
115
|
data_rate_hz=data_rate_hz,
|
|
116
116
|
baseline_sample_seconds=baseline_sample_seconds,
|
|
117
117
|
)
|
|
118
|
+
_validate_baseline_samples_for_background_method(
|
|
119
|
+
traces=traces,
|
|
120
|
+
baseline_samples=baseline_samples,
|
|
121
|
+
)
|
|
118
122
|
tau = _fit_shared_tau(
|
|
119
123
|
frame_numbers=baseline_samples.frame_numbers,
|
|
120
124
|
mean_fluorescence=baseline_samples.values.mean(axis=1),
|
|
@@ -274,6 +278,35 @@ def _baseline_samples(
|
|
|
274
278
|
)
|
|
275
279
|
|
|
276
280
|
|
|
281
|
+
def _validate_baseline_samples_for_background_method(
|
|
282
|
+
*,
|
|
283
|
+
traces: BackgroundCorrectedRoiTraces,
|
|
284
|
+
baseline_samples: _BaselineSamples,
|
|
285
|
+
) -> None:
|
|
286
|
+
"""Reject background-corrected baselines that cannot support dF/F."""
|
|
287
|
+
if traces.method != "roi_y_stripe_percentile":
|
|
288
|
+
return
|
|
289
|
+
|
|
290
|
+
invalid = ~np.isfinite(baseline_samples.values) | (baseline_samples.values <= 0.0)
|
|
291
|
+
if not np.any(invalid):
|
|
292
|
+
return
|
|
293
|
+
|
|
294
|
+
roi_indices = np.flatnonzero(np.any(invalid, axis=0))
|
|
295
|
+
labels = ", ".join(traces.labels[index] for index in roi_indices[:5])
|
|
296
|
+
if roi_indices.size > 5:
|
|
297
|
+
labels = f"{labels}, ..."
|
|
298
|
+
minimum = float(np.nanmin(baseline_samples.values[:, roi_indices]))
|
|
299
|
+
msg = (
|
|
300
|
+
"ROI y-stripe P% background subtraction over-subtracted baseline "
|
|
301
|
+
f"fluorescence for {labels}; minimum corrected baseline sample is "
|
|
302
|
+
f"{minimum:.6g}. dF/F needs positive baseline fluorescence, so the "
|
|
303
|
+
"local y-stripe pixels are not a valid additive background for those "
|
|
304
|
+
"ROIs. Use shared y-stripe P%, global percentile, or revise the ROIs "
|
|
305
|
+
"to leave dim local background pixels."
|
|
306
|
+
)
|
|
307
|
+
raise ValueError(msg)
|
|
308
|
+
|
|
309
|
+
|
|
277
310
|
def _validate_baseline_local_range(
|
|
278
311
|
*,
|
|
279
312
|
window: FrameWindow,
|