Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
14dc9ea
Added PrairieViewReader
kushalbakshi Oct 7, 2022
acf6046
Typo fixed in CHAGELOG
kushalbakshi Oct 7, 2022
1c4ff2d
Fixed unit conversion error
kushalbakshi Oct 10, 2022
f74009f
Added robust search for metadata file
kushalbakshi Oct 10, 2022
5f7bf0e
Extract field x, y, z metadata + black formatting
kushalbakshi Oct 10, 2022
60dccce
Added function docstring
kushalbakshi Oct 10, 2022
6406850
Update element_interface/prairieviewreader.py
kushalbakshi Oct 10, 2022
d43e323
Updated CHANGELOG
kushalbakshi Oct 10, 2022
00e6ce0
Merge branch 'main' of https://github.com/kushalbakshi/element-interface
kushalbakshi Oct 10, 2022
189bea7
Fixed typo in CHANGELOG
kushalbakshi Oct 10, 2022
b068c9c
Updated nframes in prairieviewreader.py
kushalbakshi Oct 10, 2022
a8b1641
Update CHANGELOG.md
kushalbakshi Oct 10, 2022
ae290e5
Update CHANGELOG.md
kushalbakshi Oct 11, 2022
ec01b7d
Update element_interface/prairieviewreader.py
kushalbakshi Oct 12, 2022
5059705
Update element_interface/prairieviewreader.py
kushalbakshi Oct 12, 2022
7e4b2d9
Update element_interface/prairieviewreader.py
kushalbakshi Oct 12, 2022
fffc8ef
Update element_interface/prairieviewreader.py
kushalbakshi Oct 12, 2022
21779bd
Update element_interface/prairieviewreader.py
kushalbakshi Oct 12, 2022
4f69233
Update element_interface/prairieviewreader.py
kushalbakshi Oct 12, 2022
512e819
Update element_interface/prairieviewreader.py
kushalbakshi Oct 12, 2022
8ce5de0
Update element_interface/prairieviewreader.py
kushalbakshi Oct 12, 2022
9faaa94
Added fixes after local test
kushalbakshi Oct 12, 2022
1c9f1e2
Fixes after local ingestion test
kushalbakshi Oct 12, 2022
c17d551
Syntax + styling changes
kushalbakshi Oct 13, 2022
3774999
Fix typo in prairieviewreader.py
kushalbakshi Oct 13, 2022
9f7d52d
Minor styling updates in prairieviewreader.py
kushalbakshi Oct 13, 2022
afac09d
x, y, z to None for ScanInfo
kushalbakshi Oct 13, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@

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

## [0.3.0] - 2022-10-7
+ Add - Function `prairieviewreader` to parse metadata from Bruker PrarieView acquisition system

## 0.2.1 - 2022-07-13
+ Add - Adopt `black` formatting
+ Add - Code of Conduct
Expand All @@ -19,4 +22,6 @@ Observes [Semantic Versioning](https://semver.org/spec/v2.0.0.html) standard and
+ Change - Rename the package `element-data-loader` to `element-interface`.

## 0.1.0a0 - 2021-06-21
+ Add - Readers for: `ScanImage`, `Suite2p`, `CaImAn`.
+ Add - Readers for: `ScanImage`, `Suite2p`, `CaImAn`.

[0.3.0]: https://github.com/datajoint/element-interface/releases/tag/0.3.0
144 changes: 144 additions & 0 deletions element_interface/prairieviewreader.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
import pathlib
import xml.etree.ElementTree as ET
from datetime import datetime
import numpy as np


def get_pv_metadata(pvtiffile):
"""Extract metadata for calcium imaging scans generated by Bruker Systems PrairieView acquisition software.

The PrairieView software generates one .ome.tif imaging file per frame acquired. The metadata for all frames is contained one .xml file. This function locates the .xml file and generates a dictionary necessary to populate the DataJoint ScanInfo and Field tables.

PrairieView works with resonance scanners with a single field.

PrairieView does not support bidirectional x and y scanning.

ROI information is not contained in the .xml file.

All images generated using PrairieView have square dimensions (e.g. 512x512).


Args:
pvtiffile: An absolute path to the .ome.tif image file.

Raises:
FileNotFoundError: No .xml file containing information about the acquired scan was found at path in parent directory at `pvtiffile`.

Returns:
metainfo: A dict mapping keys to corresponding metadata values fetched from the .xml file.
"""

# May return multiple xml files. Only need one that contains scan metadata.
xml_files = pathlib.Path(pvtiffile).parent.glob("*.xml")

for xml_file in xml_files:
tree = ET.parse(xml_file)
root = tree.getroot()
if root.find(".//Sequence"):
break
else:
raise FileNotFoundError(
f"No PrarieView metadata XML file found at {pvtiffile.parent}"
)

bidirectional_scan = False # Does not support bidirectional

n_fields = 1 # Always contains 1 field

# Get all channels and find unique values
channel_list = [
int(channel.attrib.get("channel"))
for channel in root.iterfind(".//Sequence/Frame/File/[@channel]")
]
n_channels = len(set(channel_list))

# One "Frame" per depth. Gets number of frames in first sequence
planes = [
int(plane.attrib.get("index"))
for plane in root.findall(".//Sequence/[@cycle='1']/Frame")
]
n_depths = len(set(planes))

n_frames = len(root.findall(".//Sequence/Frame"))

roi = 1
# x and y coordinate values for the center of the field
x_field = float(
root.find(
".//PVStateValue/[@key='currentScanCenter']/IndexedValue/[@index='XAxis']"
).attrib.get("value")
)
y_field = float(
root.find(
".//PVStateValue/[@key='currentScanCenter']/IndexedValue/[@index='YAxis']"
).attrib.get("value")
)

framerate = 1 / float(
root.findall('.//PVStateValue/[@key="framePeriod"]')[0].attrib.get("value")) # rate = 1/framePeriod

usec_per_line = float(
root.findall(".//PVStateValue/[@key='scanLinePeriod']")[0].attrib.get("value")) * 1e6 # Convert from seconds to microseconds

scan_datetime = datetime.strptime(
root.attrib.get("date"), "%m/%d/%Y %I:%M:%S %p")

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

bidirection_z = bool(
root.find(".//Sequence").attrib.get("bidirectionalZ"))

px_height = int(
root.findall(
".//PVStateValue/[@key='pixelsPerLine']")[0].attrib.get("value")
)
# All PrairieView-acquired images have square dimensions (512 x 512; 1024 x 1024)
px_width = px_height

um_per_pixel = float(
root.find(
".//PVStateValue/[@key='micronsPerPixel']/IndexedValue/[@index='XAxis']"
).attrib.get("value")
)

um_height = um_width = float(px_height) * um_per_pixel

z_min = float(root.findall(
".//Sequence/[@cycle='1']/Frame/PVStateShard/PVStateValue/[@key='positionCurrent']/SubindexedValues/SubindexedValue/[@subindex='0']"
)[0].attrib.get("value"))
z_max = float(root.findall(
".//Sequence/[@cycle='1']/Frame/PVStateShard/PVStateValue/[@key='positionCurrent']/SubindexedValues/SubindexedValue/[@subindex='0']"
)[-1].attrib.get("value"))
z_step = float(root.find(
".//PVStateShard/PVStateValue/[@key='micronsPerPixel']/IndexedValue/[@index='ZAxis']"
).attrib.get("value"))
z_fields = np.arange(z_min, z_max + 1, z_step)
assert z_fields.size == n_depths

metainfo = dict(
num_fields=n_fields,
num_channels=n_channels,
num_planes=n_depths,
num_frames=n_frames,
num_rois=roi,
x_pos=None,
y_pos=None,
z_pos=None,
frame_rate=framerate,
bidirectional=bidirectional_scan,
bidirectional_z=bidirection_z,
scan_datetime=scan_datetime,
usecs_per_line=usec_per_line,
scan_duration=total_duration,
height_in_pixels=px_height,
width_in_pixels=px_width,
height_in_um=um_height,
width_in_um=um_width,
fieldX=x_field,
fieldY=y_field,
fieldZ=z_fields,
)

return metainfo
2 changes: 1 addition & 1 deletion element_interface/version.py
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
"""Package metadata"""
__version__ = "0.2.1"
__version__ = "0.3.0"