Source code for neuroconv.datainterfaces.ophys.inscopix.inscopixsegmentationdatainterface
from pydantic import FilePath
from ..basesegmentationextractorinterface import BaseSegmentationExtractorInterface
from ....utils import DeepDict
[docs]
class InscopixSegmentationInterface(BaseSegmentationExtractorInterface):
"""Conversion interface for Inscopix segmentation data."""
display_name = "Inscopix Segmentation"
associated_suffixes = (".isxd",)
info = "Interface for handling Inscopix segmentation."
[docs]
@classmethod
def get_extractor_class(cls):
from roiextractors import InscopixSegmentationExtractor
return InscopixSegmentationExtractor
def __init__(
self,
file_path: FilePath,
verbose: bool = False,
):
"""
Parameters
----------
file_path : FilePath
Path to the .isxd Inscopix file.
verbose : bool, optional
If True, outputs additional information during processing.
"""
super().__init__(file_path=file_path, verbose=verbose)
[docs]
def get_metadata(self) -> DeepDict:
"""
Retrieve the metadata for the Inscopix segmentation data.
Returns
-------
DeepDict
Dictionary containing metadata including device information, imaging plane details,
photon series configuration, and Inscopix-specific acquisition parameters.
TODO: Determine the excitation and emission wavelengths. For each Inscopix microscope they are fixed (e.g. NVista has an emission: 535 and excitation: 475).
We currently do not know how to map the names returned by get_acquisition_info['MicroscopeType']
to the actual microscope models, as we do not have example data for each type.
See related issue: https://github.com/inscopix/pyisx/issues/62
"""
metadata = super().get_metadata()
extractor = self.segmentation_extractor
# Get all metadata from extractor using the consolidated method
extractor_metadata = extractor._get_metadata()
# Extract individual components
session_info = extractor_metadata.get("session", {})
device_info = extractor_metadata.get("device", {})
subject_info = extractor_metadata.get("subject", {})
analysis_info = extractor_metadata.get("analysis", {})
probe_info = extractor_metadata.get("probe", {})
session_start_time = extractor_metadata.get("session_start_time")
# Session start time
if session_start_time:
metadata["NWBFile"]["session_start_time"] = session_start_time
# Session name and experimenter
if session_info.get("session_name"):
session_desc = f"Session: {session_info['session_name']}"
# Get subject info for potential description addition
if subject_info and subject_info.get("description"):
session_desc += f"; {subject_info['description']}"
metadata["NWBFile"]["session_description"] = session_desc
if session_info.get("experimenter_name"):
metadata["NWBFile"]["experimenter"] = [session_info["experimenter_name"]]
# Device information
if device_info:
device_metadata = metadata["Ophys"]["Device"][0]
# Update the actual device name
if device_info.get("device_name"):
device_metadata["name"] = device_info["device_name"]
# Build device description
device_desc_parts = []
if device_info.get("device_name"):
device_desc_parts.append(f"Inscopix {device_info['device_name']}")
if device_info.get("device_serial_number"):
device_desc_parts.append(f"SerialNumber: {device_info['device_serial_number']}")
if device_info.get("acquisition_software_version"):
device_desc_parts.append(f"Software version {device_info['acquisition_software_version']}")
# Add probe information specific to segmentation
if probe_info:
for field in [
"Probe Diameter (mm)",
"Probe Flip",
"Probe Length (mm)",
"Probe Pitch",
"Probe Rotation (degrees)",
"Probe Type",
]:
value = probe_info.get(field)
if value is not None:
device_desc_parts.append(f"{field}: {value}")
if device_desc_parts:
device_metadata["description"] = "; ".join(device_desc_parts)
# Update imaging plane metadata
imaging_plane_metadata = metadata["Ophys"]["ImagingPlane"][0]
# Update imaging plane device reference to match the actual device name
if device_info.get("device_name"):
imaging_plane_metadata["device"] = device_info["device_name"]
# Build imaging plane description
plane_desc = "Inscopix imaging plane"
if device_info.get("field_of_view_pixels"):
fov = device_info["field_of_view_pixels"]
plane_desc += f" with field of view {fov[1]}x{fov[0]} pixels"
desc_parts = [plane_desc]
if device_info.get("microscope_focus"):
desc_parts.append(f"Focus: {device_info['microscope_focus']} µm")
if device_info.get("exposure_time_ms"):
desc_parts.append(f"Exposure: {device_info['exposure_time_ms']} ms")
if device_info.get("microscope_gain"):
desc_parts.append(f"Gain: {device_info['microscope_gain']}")
imaging_plane_metadata["description"] = "; ".join(desc_parts)
# Sampling frequency
sampling_frequency = extractor.get_sampling_frequency()
imaging_plane_metadata["imaging_rate"] = sampling_frequency
# Optical channel
optical_channel = {
"name": "OpticalChannelDefault",
"description": "Inscopix optical channel",
"emission_lambda": float("nan"),
}
if device_info.get("channel"):
channel = device_info["channel"]
optical_channel["name"] = f"OpticalChannel{channel.capitalize()}"
optical_channel["description"] = f"Inscopix {channel} channel"
# LED power (segmentation uses different field name)
if device_info.get("led_power_1_mw_per_mm2"):
led_power = device_info["led_power_1_mw_per_mm2"]
optical_channel["description"] += f" (LED power: {led_power} mW/mm²)"
imaging_plane_metadata["optical_channel"] = [optical_channel]
# Image segmentation (specific to segmentation interface)
segmentation_desc = "Inscopix cell segmentation"
if analysis_info and analysis_info.get("cell_identification_method"):
segmentation_desc += f" using {analysis_info['cell_identification_method']}"
if analysis_info and analysis_info.get("trace_units"):
segmentation_desc += f" with traces in {analysis_info['trace_units']}"
metadata["Ophys"]["ImageSegmentation"]["description"] = segmentation_desc
# Subject metadata
subject_metadata = {}
has_any_subject_data = False
# Subject ID
if subject_info and subject_info.get("animal_id"):
subject_metadata["subject_id"] = subject_info["animal_id"]
has_any_subject_data = True
species_value = None
strain_value = None
if subject_info and subject_info.get("species"):
species_raw = subject_info["species"]
# If it contains genotype info or doesn't match format, put it in strain instead
# e.g., "CaMKIICre"
if " " in species_raw and species_raw[0].isupper() and species_raw.split()[1][0].islower():
species_value = species_raw
else:
strain_value = species_raw
if species_value:
subject_metadata["species"] = species_value
has_any_subject_data = True
if strain_value:
subject_metadata["strain"] = strain_value
has_any_subject_data = True
# Sex mapping
sex_mapping = {"m": "M", "male": "M", "f": "F", "female": "F", "u": "U", "unknown": "U"}
if subject_info and subject_info.get("sex"):
mapped_sex = sex_mapping.get(subject_info["sex"].lower(), "U")
subject_metadata["sex"] = mapped_sex
has_any_subject_data = True
# Optional subject fields
if subject_info:
if subject_info.get("description"):
subject_metadata["description"] = subject_info["description"]
has_any_subject_data = True
if subject_info.get("date_of_birth"):
subject_metadata["date_of_birth"] = subject_info["date_of_birth"]
has_any_subject_data = True
if subject_info.get("weight") and subject_info["weight"] > 0:
subject_metadata["weight"] = subject_info["weight"]
has_any_subject_data = True
# Add Subject if we have ANY subject information, filling required fields with defaults
if has_any_subject_data:
if "subject_id" not in subject_metadata:
subject_metadata["subject_id"] = "Unknown"
if "species" not in subject_metadata:
subject_metadata["species"] = "Unknown species"
if "sex" not in subject_metadata:
subject_metadata["sex"] = "U"
if "Subject" in metadata:
metadata["Subject"].update(subject_metadata)
else:
metadata["Subject"] = subject_metadata
return metadata