TwoPy 0.1.0__py3-none-any.whl

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 (84) hide show
  1. twopy/__init__.py +168 -0
  2. twopy/_segments.py +53 -0
  3. twopy/analysis/__init__.py +101 -0
  4. twopy/analysis/background_subtraction.py +672 -0
  5. twopy/analysis/dff.py +524 -0
  6. twopy/analysis/epoch_mapping.py +223 -0
  7. twopy/analysis/motion.py +66 -0
  8. twopy/analysis/persistence.py +975 -0
  9. twopy/analysis/response_processing/__init__.py +55 -0
  10. twopy/analysis/response_processing/apply.py +298 -0
  11. twopy/analysis/response_processing/options.py +192 -0
  12. twopy/analysis/response_processing/qc.py +270 -0
  13. twopy/analysis/response_processing/signals.py +200 -0
  14. twopy/analysis/responses.py +345 -0
  15. twopy/analysis/trials.py +341 -0
  16. twopy/analysis/workflow.py +457 -0
  17. twopy/api.py +78 -0
  18. twopy/config.py +200 -0
  19. twopy/conversion/__init__.py +40 -0
  20. twopy/conversion/alignment_crop.py +329 -0
  21. twopy/conversion/api.py +113 -0
  22. twopy/conversion/frame_ranges.py +35 -0
  23. twopy/conversion/hdf5_writing.py +362 -0
  24. twopy/conversion/matlab_values.py +123 -0
  25. twopy/conversion/source_loading.py +403 -0
  26. twopy/conversion/stimulus_code.py +248 -0
  27. twopy/conversion/types.py +295 -0
  28. twopy/converted.py +681 -0
  29. twopy/database/__init__.py +33 -0
  30. twopy/database/cache.py +166 -0
  31. twopy/database/catalog.py +90 -0
  32. twopy/database/connection.py +27 -0
  33. twopy/database/generic.py +254 -0
  34. twopy/database/modeled.py +419 -0
  35. twopy/database/sql.py +46 -0
  36. twopy/database/types.py +116 -0
  37. twopy/inspection.py +492 -0
  38. twopy/matlab.py +398 -0
  39. twopy/napari/__init__.py +27 -0
  40. twopy/napari/__main__.py +10 -0
  41. twopy/napari/constants.py +13 -0
  42. twopy/napari/controls.py +739 -0
  43. twopy/napari/display.py +181 -0
  44. twopy/napari/display_paths.py +197 -0
  45. twopy/napari/interactive.py +310 -0
  46. twopy/napari/launcher.py +175 -0
  47. twopy/napari/layout.py +147 -0
  48. twopy/napari/loading.py +207 -0
  49. twopy/napari/movie.py +71 -0
  50. twopy/napari/paths.py +181 -0
  51. twopy/napari/plotting/__init__.py +21 -0
  52. twopy/napari/plotting/data.py +266 -0
  53. twopy/napari/plotting/docks.py +1052 -0
  54. twopy/napari/plotting/export.py +803 -0
  55. twopy/napari/plotting/export_controls.py +209 -0
  56. twopy/napari/plotting/label_visibility.py +260 -0
  57. twopy/napari/plotting/options.py +269 -0
  58. twopy/napari/plotting/panels.py +119 -0
  59. twopy/napari/plotting/widgets.py +767 -0
  60. twopy/napari/protocols.py +90 -0
  61. twopy/napari/responses.py +91 -0
  62. twopy/napari/roi.py +238 -0
  63. twopy/napari/session.py +381 -0
  64. twopy/napari/state.py +106 -0
  65. twopy/napari/text.py +26 -0
  66. twopy/napari/types.py +39 -0
  67. twopy/napari/viewer.py +405 -0
  68. twopy/parity/__init__.py +30 -0
  69. twopy/parity/dff.py +399 -0
  70. twopy/parity/roi.py +145 -0
  71. twopy/parity/saved_analysis.py +174 -0
  72. twopy/photodiode.py +375 -0
  73. twopy/photodiode_classification.py +484 -0
  74. twopy/roi.py +462 -0
  75. twopy/session.py +167 -0
  76. twopy/spatial.py +154 -0
  77. twopy/stimulus.py +233 -0
  78. twopy/synchronization.py +235 -0
  79. twopy/typing_guards.py +255 -0
  80. twopy-0.1.0.dist-info/METADATA +311 -0
  81. twopy-0.1.0.dist-info/RECORD +84 -0
  82. twopy-0.1.0.dist-info/WHEEL +5 -0
  83. twopy-0.1.0.dist-info/entry_points.txt +2 -0
  84. twopy-0.1.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,295 @@
1
+ """Typed data objects used by twopy conversion.
2
+
3
+ Inputs: MATLAB/HDF5 source metadata and loaded NumPy arrays.
4
+ Outputs: immutable Python objects passed between conversion steps.
5
+ """
6
+
7
+ from dataclasses import dataclass
8
+ from pathlib import Path
9
+ from typing import cast
10
+
11
+ import h5py
12
+ import numpy as np
13
+ import numpy.typing as npt
14
+
15
+ from twopy.conversion.frame_ranges import normalize_frame_range
16
+ from twopy.session import TwoPhotonSessionFiles
17
+ from twopy.spatial import SpatialCrop
18
+
19
+
20
+ @dataclass(frozen=True)
21
+ class AlignedMovieSource:
22
+ """Lazy source for the aligned imaging movie before conversion.
23
+
24
+ Inputs: HDF5-backed MATLAB file path and dataset metadata.
25
+ Outputs: movie shape/dtype metadata plus methods for reading frames.
26
+
27
+ This object is only for conversion. Downstream analysis should read the
28
+ copied movie from the twopy aligned-movie HDF5 file.
29
+ """
30
+
31
+ path: Path
32
+ dataset_name: str
33
+ shape: tuple[int, ...]
34
+ dtype: str
35
+ chunks: tuple[int, ...] | None
36
+ compression: str | None
37
+
38
+ def read_frames(
39
+ self,
40
+ start: int | None = None,
41
+ stop: int | None = None,
42
+ ) -> npt.NDArray[np.float64]:
43
+ """Read a frame range from the aligned source movie.
44
+
45
+ Args:
46
+ start: First frame index to read. ``None`` starts at frame zero.
47
+ stop: Exclusive stop frame index. ``None`` reads through the end.
48
+
49
+ Returns:
50
+ NumPy array with shape ``(frames, x, y)`` for the requested range.
51
+
52
+ This method exists for conversion checks and small previews, not for
53
+ long-lived analysis workflows.
54
+ """
55
+ frame_slice = slice(start, stop)
56
+ with h5py.File(self.path, "r") as mat_file:
57
+ dataset = mat_file[self.dataset_name]
58
+ return cast(npt.NDArray[np.float64], dataset[frame_slice, :, :])
59
+
60
+ def mean_image(
61
+ self,
62
+ *,
63
+ start_frame: int | None = None,
64
+ stop_frame: int | None = None,
65
+ chunk_frames: int = 128,
66
+ ) -> npt.NDArray[np.float64]:
67
+ """Compute a mean image over a frame range without loading all frames.
68
+
69
+ Args:
70
+ start_frame: First frame included in the mean. ``None`` uses zero.
71
+ stop_frame: Exclusive stop frame. ``None`` uses the movie end.
72
+ chunk_frames: Number of frames read per chunk.
73
+
74
+ Returns:
75
+ Float64 mean image with shape ``(x, y)``.
76
+
77
+ The computation streams the movie in chunks, keeping memory use bounded
78
+ by ``chunk_frames`` rather than the full movie length.
79
+ """
80
+ start, stop = normalize_frame_range(
81
+ frame_count=self.shape[0],
82
+ start_frame=start_frame,
83
+ stop_frame=stop_frame,
84
+ )
85
+ if chunk_frames < 1:
86
+ msg = f"chunk_frames must be at least 1; got {chunk_frames}"
87
+ raise ValueError(msg)
88
+
89
+ spatial_shape = self.shape[1:]
90
+ accumulator = np.zeros(spatial_shape, dtype=np.float64)
91
+ with h5py.File(self.path, "r") as mat_file:
92
+ dataset = mat_file[self.dataset_name]
93
+ for chunk_start in range(start, stop, chunk_frames):
94
+ chunk_stop = min(chunk_start + chunk_frames, stop)
95
+ block = np.asarray(dataset[chunk_start:chunk_stop, :, :])
96
+ accumulator += block.sum(axis=0, dtype=np.float64)
97
+
98
+ return accumulator / float(stop - start)
99
+
100
+
101
+ @dataclass(frozen=True)
102
+ class AcquisitionMetadata:
103
+ """Recording acquisition metadata from ``imageDescription.mat``.
104
+
105
+ Inputs: MATLAB ScanImage ``state`` struct.
106
+ Outputs: selected scalar fields in a dictionary.
107
+
108
+ The raw MATLAB struct is not exposed because it is source-format data. The
109
+ converted HDF5 file stores this dictionary as HDF5 attributes.
110
+ """
111
+
112
+ path: Path
113
+ fields: dict[str, object]
114
+
115
+
116
+ @dataclass(frozen=True)
117
+ class StimulusParameters:
118
+ """Stimulus epoch definitions from ``stimParams.mat``.
119
+
120
+ Inputs: MATLAB ``stimParams`` structs.
121
+ Outputs: one plain dictionary per epoch.
122
+
123
+ Field names and values vary with the stimulus design, so dictionaries keep
124
+ the conversion auditable without inventing a premature fixed schema.
125
+ """
126
+
127
+ path: Path
128
+ epochs: tuple[dict[str, object], ...]
129
+
130
+
131
+ @dataclass(frozen=True)
132
+ class StimulusCodeMetadata:
133
+ """Stimulus-code metadata decoded from ``stimulusData/filebackup.zip``.
134
+
135
+ Inputs: Backed-up MATLAB stimulus code from the exact recording.
136
+ Outputs: ``stimtype`` to function-name lookup and per-function assignments
137
+ into the stimulus-specific ``stimData`` slots.
138
+
139
+ This keeps the stable numeric ``stimulus/data`` table unchanged while
140
+ preserving experiment-specific meaning for columns such as
141
+ ``stimulus_specific_04``.
142
+ """
143
+
144
+ path: Path
145
+ function_lookup: dict[str, str]
146
+ stimulus_specific_columns: dict[str, dict[str, object]]
147
+
148
+
149
+ @dataclass(frozen=True)
150
+ class RunMetadata:
151
+ """Stimulus-run metadata from ``stimulusData/runDetails.mat``.
152
+
153
+ Inputs: MATLAB run details produced by the stimulus computer.
154
+ Outputs: plain scalar fields such as rig name, genotype, and run number.
155
+
156
+ This is separate from acquisition metadata because it describes the
157
+ stimulus run and rig identity, not ScanImage acquisition settings.
158
+ """
159
+
160
+ path: Path
161
+ fields: dict[str, object]
162
+
163
+
164
+ @dataclass(frozen=True)
165
+ class StimulusData:
166
+ """Per-stimulus-frame data from ``stimdata.mat``.
167
+
168
+ Inputs: MATLAB ``stimData`` table.
169
+ Outputs: the full numeric table, column labels, and common columns as
170
+ direct arrays.
171
+
172
+ The stimulus data comes from the stimulus presentation computer. Its
173
+ clock is independent from the imaging computer clock, so response analysis
174
+ must use photodiode synchronization before assigning imaging frames to
175
+ stimulus trials.
176
+
177
+ The first observed columns are time, stimulus frame number, and epoch.
178
+ Later columns are protocol-specific closed-loop/stimulus/flash values, so
179
+ twopy stores auditable column labels with the full table.
180
+ """
181
+
182
+ path: Path
183
+ data: npt.NDArray[np.float64]
184
+ column_names: tuple[str, ...]
185
+ time_seconds: npt.NDArray[np.float64]
186
+ frame_numbers: npt.NDArray[np.int64]
187
+ epoch_numbers: npt.NDArray[np.int64]
188
+
189
+
190
+ @dataclass(frozen=True)
191
+ class PhotodiodeSignals:
192
+ """Photodiode signals used to align imaging frames and stimulus frames.
193
+
194
+ Inputs: frame-resolution and high-resolution MATLAB photodiode files.
195
+ Outputs: NumPy arrays ready to write into the twopy HDF5 file.
196
+
197
+ Imaging and stimulus presentation run on different computers with different
198
+ frame rates. The stimulus computer flashes the photodiode at key timepoints
199
+ such as start, trial transitions, and end. These signals are the bridge
200
+ between clocks and must be decoded before trial-level response analysis.
201
+ """
202
+
203
+ imaging_res_path: Path
204
+ high_res_path: Path
205
+ imaging_res_pd: npt.NDArray[np.float64]
206
+ high_res_pd: npt.NDArray[np.float64]
207
+
208
+
209
+ @dataclass(frozen=True)
210
+ class FrameCountAudit:
211
+ """Frame-count relationship checked during source conversion.
212
+
213
+ Inputs: aligned movie frames, imaging-resolution photodiode samples, and
214
+ ScanImage acquisition frame metadata.
215
+ Outputs: explicit counts and deltas for converted HDF5 audit metadata.
216
+
217
+ The aligned movie and imaging-resolution photodiode should describe the
218
+ same imaging frames. In sampled real recordings, ScanImage
219
+ ``acq.numberOfFrames`` was usually one less than the aligned movie frame
220
+ count. twopy treats that as a known metadata convention, not as a dropped
221
+ frame, and records the offset so response analysis can audit it.
222
+ """
223
+
224
+ aligned_movie_frames: int
225
+ imaging_res_pd_samples: int
226
+ acquisition_number_of_frames: int
227
+ imaging_res_pd_minus_movie: int
228
+ acquisition_minus_movie: int
229
+
230
+
231
+ @dataclass(frozen=True)
232
+ class AlignmentCropAudit:
233
+ """Alignment-derived spatial crop saved during conversion.
234
+
235
+ Inputs: microscope alignment offsets, acquisition metadata, and photodiode
236
+ stimulus bounds.
237
+ Outputs: the spatial crop plus audit values that explain how it was chosen.
238
+
239
+ The crop describes the frame region that stayed valid after motion alignment.
240
+ Keeping it separate from the full aligned movie lets twopy preserve source
241
+ pixels while reproducing cropped analysis paths when needed.
242
+ """
243
+
244
+ crop: SpatialCrop
245
+ alignment_frame_start: int
246
+ alignment_frame_stop: int
247
+ x_cutoff_pixels: int
248
+ y_cutoff_pixels: int
249
+ over_moved_frame_count: int
250
+ motion_threshold_pixels: float
251
+ alignment_shift_pixels: npt.NDArray[np.float64]
252
+ motion_artifact_mask: npt.NDArray[np.bool_]
253
+
254
+
255
+ @dataclass(frozen=True)
256
+ class SourceConversionInputs:
257
+ """All source inputs needed to create a twopy converted recording.
258
+
259
+ Inputs: one recording session directory.
260
+ Outputs: validated file paths and Python objects used only for conversion.
261
+
262
+ This object is the boundary between microscope source data and twopy-owned
263
+ analysis files.
264
+ """
265
+
266
+ session_files: TwoPhotonSessionFiles
267
+ aligned_movie: AlignedMovieSource
268
+ acquisition: AcquisitionMetadata
269
+ run: RunMetadata
270
+ stimulus_parameters: StimulusParameters
271
+ stimulus_code: StimulusCodeMetadata
272
+ stimulus_data: StimulusData
273
+ photodiode: PhotodiodeSignals
274
+ frame_counts: FrameCountAudit
275
+ alignment_crop: AlignmentCropAudit
276
+
277
+
278
+ @dataclass(frozen=True)
279
+ class ConvertedRecording:
280
+ """Summary of converted twopy recording files.
281
+
282
+ Inputs: conversion output path and source recording folder.
283
+ Outputs: HDF5 paths plus mean-image frame range and movie shape.
284
+
285
+ Later analysis modules should accept these converted HDF5 paths, not source
286
+ MATLAB file paths. ``path`` points at the small recording data file;
287
+ ``movie_path`` points at the large aligned movie file.
288
+ """
289
+
290
+ path: Path
291
+ movie_path: Path
292
+ source_session_dir: Path
293
+ movie_shape: tuple[int, ...]
294
+ mean_image_start_frame: int
295
+ mean_image_stop_frame: int