Skip to content

Commit fac6004

Browse files
authored
Merge pull request #69 from kushalbakshi/main
PrairieViewReader updated for fixed Zaxis scans
2 parents d52b2d6 + 2ffb485 commit fac6004

4 files changed

Lines changed: 70 additions & 45 deletions

File tree

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,9 @@
22

33
Observes [Semantic Versioning](https://semver.org/spec/v2.0.0.html) standard and [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) convention.
44

5+
## [0.4.2] - 2022-12-16
6+
+ Update - PrairieView loader checks for multi-plane vs single-plane scans.
7+
58
## [0.4.1] - 2022-12-15
69

710
+ Update - PrairieView loader now reads recording start time from metadata file
@@ -41,6 +44,7 @@ Observes [Semantic Versioning](https://semver.org/spec/v2.0.0.html) standard and
4144

4245
+ Add - Readers for: `ScanImage`, `Suite2p`, `CaImAn`.
4346

47+
[0.4.2]: https://github.com/datajoint/element-interface/releases/tag/0.4.2
4448
[0.4.1]: https://github.com/datajoint/element-interface/releases/tag/0.4.1
4549
[0.4.0]: https://github.com/datajoint/element-interface/releases/tag/0.4.0
4650
[0.3.0]: https://github.com/datajoint/element-interface/releases/tag/0.3.0

docs/src/concepts.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,16 @@ run deconvolution using the `spikedetect` flag (
4040
`segmentation_suite2p` functions is only a subset of the keys generated with the
4141
`suite2p.default_ops()` function.
4242

43+
### PrairieView Reader
44+
45+
This Element provides a function to read the PrairieView Scanner's metadata
46+
file. The PrairieView software generates one `.ome.tif` imaging file per frame acquired. The
47+
metadata for all frames is contained in one `.xml` file. This function locates the `.xml`
48+
file and generates a dictionary necessary to populate the DataJoint ScanInfo and
49+
Field tables. PrairieView works with resonance scanners with a single field,
50+
does not support bidirectional x and y scanning, and the `.xml` file does not
51+
contain ROI information.
52+
4353
## Element Architecture
4454

4555
The functions for each acquisition and analysis package are stored within a separate

element_interface/prairieviewreader.py

Lines changed: 55 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -41,42 +41,21 @@ def get_pv_metadata(pvtiffile: str) -> dict:
4141
)
4242

4343
bidirectional_scan = False # Does not support bidirectional
44-
44+
roi = 1
4545
n_fields = 1 # Always contains 1 field
46-
47-
record_start_time = root.findall(".//Sequence/[@cycle='1']").attrib.get("time")
46+
record_start_time = root.find(
47+
".//Sequence/[@cycle='1']").attrib.get("time")
4848

4949
# Get all channels and find unique values
5050
channel_list = [
5151
int(channel.attrib.get("channel"))
5252
for channel in root.iterfind(".//Sequence/Frame/File/[@channel]")
5353
]
5454
n_channels = len(set(channel_list))
55-
56-
# One "Frame" per depth. Gets number of frames in first sequence
57-
planes = [
58-
int(plane.attrib.get("index"))
59-
for plane in root.findall(".//Sequence/[@cycle='1']/Frame")
60-
]
61-
n_depths = len(set(planes))
62-
6355
n_frames = len(root.findall(".//Sequence/Frame"))
64-
65-
roi = 1
66-
# x and y coordinate values for the center of the field
67-
x_field = float(
68-
root.find(
69-
".//PVStateValue/[@key='currentScanCenter']/IndexedValue/[@index='XAxis']"
70-
).attrib.get("value")
71-
)
72-
y_field = float(
73-
root.find(
74-
".//PVStateValue/[@key='currentScanCenter']/IndexedValue/[@index='YAxis']"
75-
).attrib.get("value")
76-
)
77-
7856
framerate = 1 / float(
79-
root.findall('.//PVStateValue/[@key="framePeriod"]')[0].attrib.get("value")
57+
root.findall(
58+
'.//PVStateValue/[@key="framePeriod"]')[0].attrib.get("value")
8059
) # rate = 1/framePeriod
8160

8261
usec_per_line = (
@@ -88,16 +67,16 @@ def get_pv_metadata(pvtiffile: str) -> dict:
8867
* 1e6
8968
) # Convert from seconds to microseconds
9069

91-
scan_datetime = datetime.strptime(root.attrib.get("date"), "%m/%d/%Y %I:%M:%S %p")
70+
scan_datetime = datetime.strptime(
71+
root.attrib.get("date"), "%m/%d/%Y %I:%M:%S %p")
9272

9373
total_duration = float(
9474
root.findall(".//Sequence/Frame")[-1].attrib.get("relativeTime")
9575
)
9676

97-
bidirection_z = bool(root.find(".//Sequence").attrib.get("bidirectionalZ"))
98-
9977
px_height = int(
100-
root.findall(".//PVStateValue/[@key='pixelsPerLine']")[0].attrib.get("value")
78+
root.findall(
79+
".//PVStateValue/[@key='pixelsPerLine']")[0].attrib.get("value")
10180
)
10281
# All PrairieView-acquired images have square dimensions (512 x 512; 1024 x 1024)
10382
px_width = px_height
@@ -110,23 +89,55 @@ def get_pv_metadata(pvtiffile: str) -> dict:
11089

11190
um_height = um_width = float(px_height) * um_per_pixel
11291

113-
z_min = float(
114-
root.findall(
115-
".//Sequence/[@cycle='1']/Frame/PVStateShard/PVStateValue/[@key='positionCurrent']/SubindexedValues/SubindexedValue/[@subindex='0']"
116-
)[0].attrib.get("value")
117-
)
118-
z_max = float(
119-
root.findall(
120-
".//Sequence/[@cycle='1']/Frame/PVStateShard/PVStateValue/[@key='positionCurrent']/SubindexedValues/SubindexedValue/[@subindex='0']"
121-
)[-1].attrib.get("value")
92+
# x and y coordinate values for the center of the field
93+
x_field = float(
94+
root.find(
95+
".//PVStateValue/[@key='currentScanCenter']/IndexedValue/[@index='XAxis']"
96+
).attrib.get("value")
12297
)
123-
z_step = float(
98+
y_field = float(
12499
root.find(
125-
".//PVStateShard/PVStateValue/[@key='micronsPerPixel']/IndexedValue/[@index='ZAxis']"
100+
".//PVStateValue/[@key='currentScanCenter']/IndexedValue/[@index='YAxis']"
126101
).attrib.get("value")
127102
)
128-
z_fields = np.arange(z_min, z_max + 1, z_step)
129-
assert z_fields.size == n_depths
103+
if root.find(".//Sequence/[@cycle='1']/Frame/PVStateShard/PVStateValue/[@key='positionCurrent']/SubindexedValues/[@index='ZAxis']") is None:
104+
105+
z_fields = np.float64(
106+
root.find(
107+
".//PVStateValue/[@key='positionCurrent']/SubindexedValues/[@index='ZAxis']/SubindexedValue").attrib.get("value")
108+
)
109+
n_depths = 1
110+
assert z_fields.size == n_depths
111+
bidirection_z = False
112+
113+
else:
114+
115+
bidirection_z = bool(
116+
root.find(".//Sequence").attrib.get("bidirectionalZ"))
117+
118+
# One "Frame" per depth. Gets number of frames in first sequence
119+
planes = [
120+
int(plane.attrib.get("index"))
121+
for plane in root.findall(".//Sequence/[@cycle='1']/Frame")
122+
]
123+
n_depths = len(set(planes))
124+
z_min = float(
125+
root.findall(
126+
".//Sequence/[@cycle='1']/Frame/PVStateShard/PVStateValue/[@key='positionCurrent']/SubindexedValues/SubindexedValue/[@subindex='0']"
127+
)[0].attrib.get("value")
128+
)
129+
z_max = float(
130+
root.findall(
131+
".//Sequence/[@cycle='1']/Frame/PVStateShard/PVStateValue/[@key='positionCurrent']/SubindexedValues/SubindexedValue/[@subindex='0']"
132+
)[-1].attrib.get("value")
133+
)
134+
z_step = float(
135+
root.find(
136+
".//PVStateShard/PVStateValue/[@key='micronsPerPixel']/IndexedValue/[@index='ZAxis']"
137+
).attrib.get("value")
138+
)
139+
z_fields = np.arange(z_min, z_max + 1, z_step)
140+
assert z_fields.size == n_depths
130141

131142
metainfo = dict(
132143
num_fields=n_fields,
@@ -150,7 +161,7 @@ def get_pv_metadata(pvtiffile: str) -> dict:
150161
fieldX=x_field,
151162
fieldY=y_field,
152163
fieldZ=z_fields,
153-
recording_time = record_start_time,
164+
recording_time=record_start_time,
154165
)
155166

156167
return metainfo

element_interface/version.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
"""Package metadata"""
22

3-
__version__ = "0.4.1"
3+
__version__ = "0.4.2"

0 commit comments

Comments
 (0)