From 45a2f1fad373399a275f44f9baa5011307b9664e Mon Sep 17 00:00:00 2001 From: Eric Denovellis Date: Wed, 23 Apr 2025 23:42:10 -0400 Subject: [PATCH 01/12] Initial design --- .../electrode_localization/__init__.py | 0 .../localization_merge.py | 22 ++++++ .../electrode_localization/v1/__init__.py | 0 .../v1/coordinate_system.py | 26 +++++++ .../electrode_localization/v1/histology.py | 44 ++++++++++++ .../electrode_localization/v1/micro_ct.py | 45 ++++++++++++ src/spyglass/histology/__init__.py | 0 src/spyglass/histology/v1/__init__.py | 0 src/spyglass/histology/v1/histology.py | 69 +++++++++++++++++++ src/spyglass/micro_ct/__init__.py | 0 src/spyglass/micro_ct/v1/__init__.py | 0 src/spyglass/micro_ct/v1/micro_ct.py | 58 ++++++++++++++++ 12 files changed, 264 insertions(+) create mode 100644 src/spyglass/electrode_localization/__init__.py create mode 100644 src/spyglass/electrode_localization/localization_merge.py create mode 100644 src/spyglass/electrode_localization/v1/__init__.py create mode 100644 src/spyglass/electrode_localization/v1/coordinate_system.py create mode 100644 src/spyglass/electrode_localization/v1/histology.py create mode 100644 src/spyglass/electrode_localization/v1/micro_ct.py create mode 100644 src/spyglass/histology/__init__.py create mode 100644 src/spyglass/histology/v1/__init__.py create mode 100644 src/spyglass/histology/v1/histology.py create mode 100644 src/spyglass/micro_ct/__init__.py create mode 100644 src/spyglass/micro_ct/v1/__init__.py create mode 100644 src/spyglass/micro_ct/v1/micro_ct.py diff --git a/src/spyglass/electrode_localization/__init__.py b/src/spyglass/electrode_localization/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/src/spyglass/electrode_localization/localization_merge.py b/src/spyglass/electrode_localization/localization_merge.py new file mode 100644 index 000000000..3ded75031 --- /dev/null +++ b/src/spyglass/electrode_localization/localization_merge.py @@ -0,0 +1,22 @@ +from spyglass.utils import SpyglassMixin +import datajoint as dj +from spyglass.utils.dj_merge_tables import _Merge + +from spyglass.electrode_localization.v1.micro_ct import ChannelBrainLocationMicroCTV1 # noqa: F401 +from spyglass.electrode_localization.v1.histology import ChannelBrainLocationHistologyV1 # noqa: F401 + +schema = dj.schema("electrode_localization_v1") + +@schema +class ChannelBrainLocation(_Merge, SpyglassMixin): + class HistologyV1(SpyglassMixin, dj.Part): + definition = """ + -> master + -> ChannelBrainLocationHistologyV1 + """" + + class MicroCTV1(SpyglassMixin, dj.Part): + definition = """ + -> master + -> ChannelBrainLocationMicroCTV1 + """ \ No newline at end of file diff --git a/src/spyglass/electrode_localization/v1/__init__.py b/src/spyglass/electrode_localization/v1/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/src/spyglass/electrode_localization/v1/coordinate_system.py b/src/spyglass/electrode_localization/v1/coordinate_system.py new file mode 100644 index 000000000..10c9d91a9 --- /dev/null +++ b/src/spyglass/electrode_localization/v1/coordinate_system.py @@ -0,0 +1,26 @@ +import datajoint as dj + +from spyglass.utils import SpyglassMixin, logger +from spyglass.utils.dj_merge_tables import _Merge + +schema = dj.schema("electrode_localization_v1") + + +@schema +class CoordinateSystem(dj.Lookup): + definition = """ + # Defines standard coordinate systems used for spatial data + coordinate_system_id: varchar(32) # e.g., 'Allen_CCF_v3_RAS_um' + --- + description: varchar(255) + """ + # Maybe use https://brainglobe.info/documentation/brainglobe-atlasapi/usage/atlas-details.html + contents = [ + [ + "Allen_CCF_v3_RAS_um", + "Allen CCF v3 Reference Atlas Space, RAS orientation, unit um", + ], + ["Histology_Image_Pixels", "2D Pixels from processed histology image"], + ["MicroCT_Voxel", "3D Voxel space from microCT scan"], + ["whs_sd_rat_39um", "3D Voxel space from Waxholm atlas"], + ] diff --git a/src/spyglass/electrode_localization/v1/histology.py b/src/spyglass/electrode_localization/v1/histology.py new file mode 100644 index 000000000..9108c1ff8 --- /dev/null +++ b/src/spyglass/electrode_localization/v1/histology.py @@ -0,0 +1,44 @@ +import datajoint as dj + +from spyglass.common import Electrode # noqa: F401 +from spyglass.electrode_localization.v1.coordinate_system import ( # noqa: F401 + CoordinateSystem, +) +from spyglass.histology.v1.histology import HistologyImage # noqa: F401 +from spyglass.utils import SpyglassMixin, logger + +schema = dj.schema("electrode_localization_v1") + + +class AlignmentHistology(SpyglassMixin, dj.Manual): + definition = """ + # Stores results and parameters of aligning histology image data to a target coordinate system + -> HistologyImage # Link to the source histology NWB file info + alignment_id: varchar(32) # Unique ID for this specific alignment instance/parameters (e.g., 'Elastix_Default_v1') + --- + -> CoordinateSystem # The TARGET coordinate system achieved by this alignment (e.g., 'allen_ccf_v3_ras_um') + # -> BrainAtlas # Optional: Link to the specific target atlas instance if needed + alignment_method: varchar(128) # Name of algorithm/tool used (e.g., 'Manual Landmark', 'Elastix', 'BRAINRegistration', 'ABBA', 'QuickNII') + alignment_params = NULL: blob # Store parameters as dict/json blob, or link -> AlignmentParams table if complex/reused + transformation_matrix = NULL: blob # Store affine matrix if computed/applicable (e.g., 4x4 np.array.tobytes()) + warp_field_path = NULL: varchar(512) # Store path to warp field file if non-linear (@external store might be better long term) + alignment_quality = NULL: float # Optional QC metric for the alignment (e.g., Dice score, landmark error) + alignment_time = CURRENT_TIMESTAMP: timestamp # Time this alignment entry was created/run + alignment_notes = "": varchar(2048) # Any specific notes about this alignment run + """ + + +@schema +class ChannelBrainLocationHistologyV1(SpyglassMixin, dj.Manual): + definition = """ + # Histology-derived coordinates and region assignment for an electrode + -> Electrode # Electrode being localized + -> HistologyImage # Source NWB file link for histology images + -> AlignmentHistology # Alignment parameters used + --- + -> CoordinateSystem # Defines the space for pos_x,y,z (e.g., Allen CCF RAS um) + pos_x: float # (um) coordinate in the specified space + pos_y: float # (um) coordinate in the specified space + pos_z: float # (um) coordinate in the specified space + -> BrainRegion # Assigned brain region + """ diff --git a/src/spyglass/electrode_localization/v1/micro_ct.py b/src/spyglass/electrode_localization/v1/micro_ct.py new file mode 100644 index 000000000..4dc62a30c --- /dev/null +++ b/src/spyglass/electrode_localization/v1/micro_ct.py @@ -0,0 +1,45 @@ +import datajoint as dj + +from spyglass.common import Electrode # noqa: F401 +from spyglass.electrode_localization.v1.coordinate_system import ( # noqa: F401 + CoordinateSystem, +) +from spyglass.micro_ct.v1.micro_ct import MicroCTImage # noqa: F401 +from spyglass.utils import SpyglassMixin, logger + +schema = dj.schema("electrode_localization_v1") + + +@schema +class AlignmentMicroCT(SpyglassMixin, dj.Manual): # Or dj.Computed + definition = """ + # Stores results and parameters of aligning microCT image data to a target coordinate system + -> MicroCTImage # Link to the source microCT NWB file info + alignment_id: varchar(32) # Unique ID for this specific alignment instance/parameters + --- + -> CoordinateSystem # The TARGET coordinate system achieved by this alignment + # -> BrainAtlas # Optional: Link to the specific target atlas instance + alignment_method: varchar(128) # Name of algorithm/tool used + alignment_params = NULL: blob # Store parameters dict/json + transformation_matrix = NULL: blob # Store affine matrix if applicable + warp_field_path = NULL: varchar(512) # Store path to warp field file if non-linear + alignment_quality = NULL: float # Optional QC metric for the alignment + alignment_time = CURRENT_TIMESTAMP: timestamp + alignment_notes = "": varchar(2048) + """ + + +@schema +class ChannelBrainLocationMicroCTV1(SpyglassMixin, dj.Manual): + definition = """ + # MicroCT-derived coordinates and region assignment for an electrode + -> Electrode # Electrode being localized + -> MicroCTImage # Source NWB file link for microCT data + -> AlignmentMicroCT # Alignment parameters used + --- + -> CoordinateSystem # Defines the space for pos_x,y,z + pos_x: float # (um) coordinate in the specified space + pos_y: float # (um) coordinate in the specified space + pos_z: float # (um) coordinate in the specified space + -> BrainRegion # Assigned brain region (controlled vocabulary) + """ diff --git a/src/spyglass/histology/__init__.py b/src/spyglass/histology/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/src/spyglass/histology/v1/__init__.py b/src/spyglass/histology/v1/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/src/spyglass/histology/v1/histology.py b/src/spyglass/histology/v1/histology.py new file mode 100644 index 000000000..b465d250a --- /dev/null +++ b/src/spyglass/histology/v1/histology.py @@ -0,0 +1,69 @@ +from pathlib import Path + +import datajoint as dj + +from spyglass.common.common_lab import LabMember # noqa: F401 +from spyglass.common.common_nwbfile import AnalysisNwbfile +from spyglass.common.common_subject import Subject # noqa: F401 +from spyglass.utils import SpyglassMixin, logger + +schema = dj.schema("histology_v1") + + +@schema +class Histology(SpyglassMixin, dj.Manual): + definition = """ + # Represents a single histology preparation for a subject + -> Subject + histology_id: varchar(32) # User-defined ID for this prep (e.g., 'probe_track_run1', 'anatomy_stain_seriesA') + --- + prep_date=NULL: date # Optional: Date of tissue preparation + slice_orientation: enum("coronal", "sagittal", "horizontal", "other") # Orientation of sections + slice_thickness: float # (um) Thickness of sections + mounting_medium = NULL : varchar(128) + experiment_purpose: varchar(1024) # e.g., 'Probe track recovery for Neuropixel P0', 'ChR2 expression check in mPFC', 'General anatomical reference' + notes = "": varchar(2048) # Optional general notes about the preparation + -> [nullable] LabMember.proj(histology_experimenter='user_name') # Optional: who did the prep? + """ + + class HistologyStain(SpyglassMixin, dj.Part): + definition = """ + # Details of specific stains used in a histology preparation + -> Histology + stain_index: tinyint unsigned # Use index for multiple stains per prep (0, 1, 2...) + --- + identified_feature: varchar(128) # Biological target, structure, or marker identified (e.g., 'GFAP+ Astrocytes', 'Nissl Bodies', 'ChR2-tdTomato+ Cells', 'ProbeTrack_DiI', 'Gad2 mRNA') + visualization_agent: varchar(128) # Method/molecule making feature visible (e.g., 'Alexa 488', 'Cresyl Violet', 'Native tdTomato Fluorescence', 'DiI', 'NBT/BCIP via ISH probe') + stain_type : enum("immunohistochemistry", "genetic_marker", "tracer", "anatomical", "histochemical", "in_situ_hybridization", "other") = "other" + stain_protocol_name = NULL : varchar(128) # Optional: name of the protocol used for this stain + antibody_details = NULL : varchar(255) # Optional: specific antibody info (e.g. company, cat#, lot#) + stain_notes = "": varchar(1024) # Optional notes about this specific stain (e.g., concentration, incubation) + """ + + +# Store the image file path and modality? +# Store the scale and color_to_stain mapping? +# Store in analysis nwb file? +class HistologyImage(dj.Manual): + definition = """ + -> Histology + image_id: int unsigned auto_increment + --- + image_file_path: varchar(255) # Path to image file + image_modality: enum("brightfield", "epifluorescence", "confocal", "slide_scanner", "other") + + """ + + +# # Example: Electrode Localization Element might have: +# class ElectrodePosition(dj.Computed): +# definition = """ +# -> Electrode +# -> Histology +# --- +# # Coordinates in some defined space (histology image, atlas, etc.) +# pos_x: float +# pos_y: float +# pos_z: float +# -> BrainRegion # Final assigned region +# """ diff --git a/src/spyglass/micro_ct/__init__.py b/src/spyglass/micro_ct/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/src/spyglass/micro_ct/v1/__init__.py b/src/spyglass/micro_ct/v1/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/src/spyglass/micro_ct/v1/micro_ct.py b/src/spyglass/micro_ct/v1/micro_ct.py new file mode 100644 index 000000000..8430df190 --- /dev/null +++ b/src/spyglass/micro_ct/v1/micro_ct.py @@ -0,0 +1,58 @@ +import datajoint as dj + +from spyglass.common.common_lab import LabMember # noqa: F401 +from spyglass.common.common_subject import Subject # noqa: F401 +from spyglass.utils import SpyglassMixin, logger + +schema = dj.schema("microct_v1") + + +@schema +class MicroCTScan(SpyglassMixin, dj.Manual): + definition = """ + # Metadata for a microCT scan of a subject's brain/tissue + -> Subject + scan_id: varchar(32) # User-defined ID for this scan (e.g., 'SubjX_OsO4_Scan1') + --- + # Preparation Details (can be brief if standardized, use notes for variations) + stain_reagent = 'Aqueous 2% OsO4': varchar(128) # Osmium Tetroxide details + stain_duration_days = 14.0 : float # Duration of staining + embedding_resin = 'Durcupan ACM': varchar(128) # Resin used for embedding + prep_protocol_notes = "": varchar(2048) # Notes on staining, dehydration, embedding variations + + # Scan Details + scan_date=NULL: date # Date of the scan itself + scanner_details: varchar(255) # e.g., 'Nikon Metrology X-Tek HMX ST 225 @ HCNS' + source_target_type = 'Molybdenum': varchar(64) # X-ray source target material + filter_material = 'None': varchar(64) # Filter material used (e.g., None, Copper) + filter_thickness_mm = 0.0: float # (mm) Filter thickness + voltage_kv: float # (kV) Scan voltage (e.g., 100 or 130) + current_ua: float # (uA) Scan current (e.g., 105 or 135) + exposure_time_s: float # (s) Exposure time per projection (e.g., 1.0) + num_projections: int # Number of projections acquired (e.g., 3184) + frames_per_projection = 1: int # Number of frames averaged per projection (e.g., 4) + + # Reconstruction Details (often from scanner software) + reconstruction_software = NULL: varchar(128) # e.g., 'CT Pro (Nikon)' + voxel_size_x: float # (um) Reconstructed voxel size X (CRITICAL) + voxel_size_y: float # (um) Reconstructed voxel size Y (CRITICAL) + voxel_size_z: float # (um) Reconstructed voxel size Z (CRITICAL) + output_format = 'TIFF stack': varchar(64) # Format of raw reconstructed data + + # Raw Data Location (Essential Input for downstream processing) + raw_scan_path: varchar(512) # Path to the raw output (e.g., folder containing TIFF stack) + + scan_notes = "": varchar(2048) # General notes about the scan itself + -> [nullable] LabMember.proj(scanner_operator='user_name') # Optional + """ + + +class MicroCTImage(SpyglassMixin, dj.Computed): + definition = """ + # Links MicroCTScan info to the Analysis NWB file containing the image volume data/links + -> MicroCTScan + --- + -> AnalysisNwbfile + processing_time=CURRENT_TIMESTAMP: timestamp + processing_notes = "": varchar(1024) + """ From e7093bd9c545fc999b584380ac4cabbae33e6dff Mon Sep 17 00:00:00 2001 From: Eric Denovellis Date: Thu, 24 Apr 2025 00:06:11 -0400 Subject: [PATCH 02/12] Add atlas_name to BrainRegion --- src/spyglass/common/common_region.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/spyglass/common/common_region.py b/src/spyglass/common/common_region.py index 759537c4f..52d312d5a 100644 --- a/src/spyglass/common/common_region.py +++ b/src/spyglass/common/common_region.py @@ -13,6 +13,7 @@ class BrainRegion(SpyglassMixin, dj.Lookup): region_name: varchar(200) # the name of the brain region subregion_name=NULL: varchar(200) # subregion name subsubregion_name=NULL: varchar(200) # subregion within subregion + atlas_name=NULL: varchar(200) # name of the atlas """ # TODO consider making (region_name, subregion_name, subsubregion_name) a From 575fadd8c3f98f265aeb01abe11499ca760c4cfa Mon Sep 17 00:00:00 2001 From: Eric Denovellis Date: Thu, 24 Apr 2025 07:51:04 -0400 Subject: [PATCH 03/12] Fix extra quote --- .../localization_merge.py | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/src/spyglass/electrode_localization/localization_merge.py b/src/spyglass/electrode_localization/localization_merge.py index 3ded75031..601375f15 100644 --- a/src/spyglass/electrode_localization/localization_merge.py +++ b/src/spyglass/electrode_localization/localization_merge.py @@ -1,22 +1,27 @@ -from spyglass.utils import SpyglassMixin import datajoint as dj -from spyglass.utils.dj_merge_tables import _Merge -from spyglass.electrode_localization.v1.micro_ct import ChannelBrainLocationMicroCTV1 # noqa: F401 -from spyglass.electrode_localization.v1.histology import ChannelBrainLocationHistologyV1 # noqa: F401 +from spyglass.electrode_localization.v1.histology import ( # noqa: F401 + ChannelBrainLocationHistologyV1, +) +from spyglass.electrode_localization.v1.micro_ct import ( # noqa: F401 + ChannelBrainLocationMicroCTV1, +) +from spyglass.utils import SpyglassMixin +from spyglass.utils.dj_merge_tables import _Merge schema = dj.schema("electrode_localization_v1") + @schema class ChannelBrainLocation(_Merge, SpyglassMixin): class HistologyV1(SpyglassMixin, dj.Part): definition = """ -> master -> ChannelBrainLocationHistologyV1 - """" + """ class MicroCTV1(SpyglassMixin, dj.Part): definition = """ -> master -> ChannelBrainLocationMicroCTV1 - """ \ No newline at end of file + """ From 9ea5599b03cd3a722c6ac5c61330a3a3bb9facfc Mon Sep 17 00:00:00 2001 From: Eric Denovellis Date: Thu, 24 Apr 2025 08:35:23 -0400 Subject: [PATCH 04/12] Reorganize --- src/spyglass/common/__init__.py | 2 +- src/spyglass/common/common_region.py | 20 ++++++ .../v1/coordinate_system.py | 26 -------- .../electrode_localization/v1/histology.py | 32 +++------- .../electrode_localization/v1/micro_ct.py | 35 +++-------- src/spyglass/histology/v1/histology.py | 61 +++++++++++-------- src/spyglass/micro_ct/v1/micro_ct.py | 35 ++++++++++- 7 files changed, 107 insertions(+), 104 deletions(-) delete mode 100644 src/spyglass/electrode_localization/v1/coordinate_system.py diff --git a/src/spyglass/common/__init__.py b/src/spyglass/common/__init__.py index ae144398b..9bc9170a4 100644 --- a/src/spyglass/common/__init__.py +++ b/src/spyglass/common/__init__.py @@ -52,7 +52,7 @@ PositionVideo, TrackGraph, ) -from spyglass.common.common_region import BrainRegion +from spyglass.common.common_region import BrainRegion, CoordinateSystem from spyglass.common.common_sensors import SensorData from spyglass.common.common_session import Session from spyglass.common.common_subject import Subject diff --git a/src/spyglass/common/common_region.py b/src/spyglass/common/common_region.py index 52d312d5a..1315ba64a 100644 --- a/src/spyglass/common/common_region.py +++ b/src/spyglass/common/common_region.py @@ -56,3 +56,23 @@ def fetch_add( cls.insert1(key) query = BrainRegion & key return query.fetch1("region_id") + + +@schema +class CoordinateSystem(dj.Lookup): + definition = """ + # Defines standard coordinate systems used for spatial data + coordinate_system_id: varchar(32) # e.g., 'Allen_CCF_v3_RAS_um' + --- + description: varchar(255) + """ + # Maybe use https://brainglobe.info/documentation/brainglobe-atlasapi/usage/atlas-details.html + contents = [ + [ + "Allen_CCF_v3_RAS_um", + "Allen CCF v3 Reference Atlas Space, RAS orientation, unit um", + ], + ["Histology_Image_Pixels", "2D Pixels from processed histology image"], + ["MicroCT_Voxel", "3D Voxel space from microCT scan"], + ["whs_sd_rat_39um", "3D Voxel space from Waxholm atlas"], + ] diff --git a/src/spyglass/electrode_localization/v1/coordinate_system.py b/src/spyglass/electrode_localization/v1/coordinate_system.py deleted file mode 100644 index 10c9d91a9..000000000 --- a/src/spyglass/electrode_localization/v1/coordinate_system.py +++ /dev/null @@ -1,26 +0,0 @@ -import datajoint as dj - -from spyglass.utils import SpyglassMixin, logger -from spyglass.utils.dj_merge_tables import _Merge - -schema = dj.schema("electrode_localization_v1") - - -@schema -class CoordinateSystem(dj.Lookup): - definition = """ - # Defines standard coordinate systems used for spatial data - coordinate_system_id: varchar(32) # e.g., 'Allen_CCF_v3_RAS_um' - --- - description: varchar(255) - """ - # Maybe use https://brainglobe.info/documentation/brainglobe-atlasapi/usage/atlas-details.html - contents = [ - [ - "Allen_CCF_v3_RAS_um", - "Allen CCF v3 Reference Atlas Space, RAS orientation, unit um", - ], - ["Histology_Image_Pixels", "2D Pixels from processed histology image"], - ["MicroCT_Voxel", "3D Voxel space from microCT scan"], - ["whs_sd_rat_39um", "3D Voxel space from Waxholm atlas"], - ] diff --git a/src/spyglass/electrode_localization/v1/histology.py b/src/spyglass/electrode_localization/v1/histology.py index 9108c1ff8..0a183dcf5 100644 --- a/src/spyglass/electrode_localization/v1/histology.py +++ b/src/spyglass/electrode_localization/v1/histology.py @@ -1,40 +1,26 @@ import datajoint as dj -from spyglass.common import Electrode # noqa: F401 -from spyglass.electrode_localization.v1.coordinate_system import ( # noqa: F401 +from spyglass.common import ( + BrainRegion, CoordinateSystem, + Electrode, +) # noqa: F401 +from spyglass.histology.v1.histology import ( # noqa: F401 + HistologyImage, + HistologyRegistration, ) -from spyglass.histology.v1.histology import HistologyImage # noqa: F401 -from spyglass.utils import SpyglassMixin, logger +from spyglass.utils import SpyglassMixin schema = dj.schema("electrode_localization_v1") -class AlignmentHistology(SpyglassMixin, dj.Manual): - definition = """ - # Stores results and parameters of aligning histology image data to a target coordinate system - -> HistologyImage # Link to the source histology NWB file info - alignment_id: varchar(32) # Unique ID for this specific alignment instance/parameters (e.g., 'Elastix_Default_v1') - --- - -> CoordinateSystem # The TARGET coordinate system achieved by this alignment (e.g., 'allen_ccf_v3_ras_um') - # -> BrainAtlas # Optional: Link to the specific target atlas instance if needed - alignment_method: varchar(128) # Name of algorithm/tool used (e.g., 'Manual Landmark', 'Elastix', 'BRAINRegistration', 'ABBA', 'QuickNII') - alignment_params = NULL: blob # Store parameters as dict/json blob, or link -> AlignmentParams table if complex/reused - transformation_matrix = NULL: blob # Store affine matrix if computed/applicable (e.g., 4x4 np.array.tobytes()) - warp_field_path = NULL: varchar(512) # Store path to warp field file if non-linear (@external store might be better long term) - alignment_quality = NULL: float # Optional QC metric for the alignment (e.g., Dice score, landmark error) - alignment_time = CURRENT_TIMESTAMP: timestamp # Time this alignment entry was created/run - alignment_notes = "": varchar(2048) # Any specific notes about this alignment run - """ - - @schema class ChannelBrainLocationHistologyV1(SpyglassMixin, dj.Manual): definition = """ # Histology-derived coordinates and region assignment for an electrode -> Electrode # Electrode being localized -> HistologyImage # Source NWB file link for histology images - -> AlignmentHistology # Alignment parameters used + -> HistologyRegistration # Alignment parameters used --- -> CoordinateSystem # Defines the space for pos_x,y,z (e.g., Allen CCF RAS um) pos_x: float # (um) coordinate in the specified space diff --git a/src/spyglass/electrode_localization/v1/micro_ct.py b/src/spyglass/electrode_localization/v1/micro_ct.py index 4dc62a30c..d6216f71e 100644 --- a/src/spyglass/electrode_localization/v1/micro_ct.py +++ b/src/spyglass/electrode_localization/v1/micro_ct.py @@ -1,45 +1,30 @@ import datajoint as dj -from spyglass.common import Electrode # noqa: F401 -from spyglass.electrode_localization.v1.coordinate_system import ( # noqa: F401 +from spyglass.common import ( + BrainRegion, CoordinateSystem, + Electrode, +) # noqa: F401 +from spyglass.micro_ct.v1.micro_ct import ( # noqa: F401 + MicroCTImage, + MicroCTRegistration, ) -from spyglass.micro_ct.v1.micro_ct import MicroCTImage # noqa: F401 -from spyglass.utils import SpyglassMixin, logger +from spyglass.utils import SpyglassMixin schema = dj.schema("electrode_localization_v1") -@schema -class AlignmentMicroCT(SpyglassMixin, dj.Manual): # Or dj.Computed - definition = """ - # Stores results and parameters of aligning microCT image data to a target coordinate system - -> MicroCTImage # Link to the source microCT NWB file info - alignment_id: varchar(32) # Unique ID for this specific alignment instance/parameters - --- - -> CoordinateSystem # The TARGET coordinate system achieved by this alignment - # -> BrainAtlas # Optional: Link to the specific target atlas instance - alignment_method: varchar(128) # Name of algorithm/tool used - alignment_params = NULL: blob # Store parameters dict/json - transformation_matrix = NULL: blob # Store affine matrix if applicable - warp_field_path = NULL: varchar(512) # Store path to warp field file if non-linear - alignment_quality = NULL: float # Optional QC metric for the alignment - alignment_time = CURRENT_TIMESTAMP: timestamp - alignment_notes = "": varchar(2048) - """ - - @schema class ChannelBrainLocationMicroCTV1(SpyglassMixin, dj.Manual): definition = """ # MicroCT-derived coordinates and region assignment for an electrode -> Electrode # Electrode being localized -> MicroCTImage # Source NWB file link for microCT data - -> AlignmentMicroCT # Alignment parameters used + -> MicroCTRegistration # Alignment parameters used --- -> CoordinateSystem # Defines the space for pos_x,y,z pos_x: float # (um) coordinate in the specified space pos_y: float # (um) coordinate in the specified space pos_z: float # (um) coordinate in the specified space - -> BrainRegion # Assigned brain region (controlled vocabulary) + -> BrainRegion # Assigned brain region """ diff --git a/src/spyglass/histology/v1/histology.py b/src/spyglass/histology/v1/histology.py index b465d250a..452824104 100644 --- a/src/spyglass/histology/v1/histology.py +++ b/src/spyglass/histology/v1/histology.py @@ -1,11 +1,12 @@ -from pathlib import Path - import datajoint as dj -from spyglass.common.common_lab import LabMember # noqa: F401 -from spyglass.common.common_nwbfile import AnalysisNwbfile -from spyglass.common.common_subject import Subject # noqa: F401 -from spyglass.utils import SpyglassMixin, logger +from spyglass.common import ( # noqa: F401 + AnalysisNwbfile, + CoordinateSystem, + LabMember, + Subject, +) +from spyglass.utils import SpyglassMixin schema = dj.schema("histology_v1") @@ -41,29 +42,37 @@ class HistologyStain(SpyglassMixin, dj.Part): """ -# Store the image file path and modality? -# Store the scale and color_to_stain mapping? -# Store in analysis nwb file? -class HistologyImage(dj.Manual): +class HistologyImage(SpyglassMixin, dj.Computed): definition = """ + # Links Histology info to the Analysis NWB file containing the image volume data/links -> Histology - image_id: int unsigned auto_increment --- - image_file_path: varchar(255) # Path to image file - image_modality: enum("brightfield", "epifluorescence", "confocal", "slide_scanner", "other") - + -> AnalysisNwbfile + color_to_stain = NULL: blob # Mapping of color channels to stains (e.g., {'DAPI': 'blue', 'GFAP': 'green'}) + scale = NULL: blob # Scale of the image (e.g., {'x': 0.5, 'y': 0.5, 'z': 1.0}) + image_modality: enum("fluorescence", "brightfield", "other") # Modality of the image (e.g., 'fluorescence', 'brightfield') """ + def make(self, key: dict) -> None: + """Populate HistologyImage table with NWB file links and metadata""" + # Placeholder for actual implementation + # This function should create an AnalysisNwbfile entry and link it to the Histology entry + # It should also populate the color_to_stain and scale fields based on the image data + pass -# # Example: Electrode Localization Element might have: -# class ElectrodePosition(dj.Computed): -# definition = """ -# -> Electrode -# -> Histology -# --- -# # Coordinates in some defined space (histology image, atlas, etc.) -# pos_x: float -# pos_y: float -# pos_z: float -# -> BrainRegion # Final assigned region -# """ + +class HistologyRegistration(SpyglassMixin, dj.Manual): + definition = """ + # Stores results and parameters of aligning histology image data to a target coordinate system + -> HistologyImage # Link to the source histology NWB file info + registration_id: varchar(32) # Unique ID for this specific registration instance/parameters (e.g., 'Elastix_Default_v1') + --- + -> CoordinateSystem # The TARGET coordinate system achieved by this registration (e.g., 'allen_ccf_v3_ras_um') + registration_method: varchar(128) # Name of algorithm/tool used (e.g., 'Manual Landmark', 'Elastix', 'BRAINRegistration', 'ABBA', 'QuickNII') + registration_params = NULL: blob # Store parameters as dict/json blob, or link -> registrationParams table if complex/reused + transformation_matrix = NULL: blob # Store affine matrix if computed/applicable (e.g., 4x4 np.array.tobytes()) + warp_field_path = NULL: varchar(512) # Store path to warp field file if non-linear (@external store might be better long term) + registration_quality = NULL: float # Optional QC metric for the registration (e.g., Dice score, landmark error) + registration_time = CURRENT_TIMESTAMP: timestamp # Time this registration entry was created/run + registration_notes = "": varchar(2048) # Any specific notes about this registration run + """ diff --git a/src/spyglass/micro_ct/v1/micro_ct.py b/src/spyglass/micro_ct/v1/micro_ct.py index 8430df190..469294899 100644 --- a/src/spyglass/micro_ct/v1/micro_ct.py +++ b/src/spyglass/micro_ct/v1/micro_ct.py @@ -1,8 +1,12 @@ import datajoint as dj -from spyglass.common.common_lab import LabMember # noqa: F401 -from spyglass.common.common_subject import Subject # noqa: F401 -from spyglass.utils import SpyglassMixin, logger +from spyglass.common import ( # noqa: F401 + AnalysisNwbfile, + CoordinateSystem, + LabMember, + Subject, +) +from spyglass.utils import SpyglassMixin schema = dj.schema("microct_v1") @@ -56,3 +60,28 @@ class MicroCTImage(SpyglassMixin, dj.Computed): processing_time=CURRENT_TIMESTAMP: timestamp processing_notes = "": varchar(1024) """ + + def make(self, key: dict) -> None: + """Populate MicroCTImage table with NWB file links and metadata""" + # Placeholder for actual implementation + # This function should create an AnalysisNwbfile entry and link it to the MicroCTScan entry + # It should also populate the processing_time and processing_notes fields based on the image data + pass + + +@schema +class MicroCTRegistration(SpyglassMixin, dj.Manual): + definition = """ + # Stores results and parameters of aligning microCT image data to a target coordinate system + -> MicroCTImage # Link to the source microCT NWB file info + registration_id: varchar(32) # Unique ID for this specific registration instance/parameters + --- + -> CoordinateSystem # The TARGET coordinate system achieved by this registration + registration_method: varchar(128) # Name of algorithm/tool used + registration_params = NULL: blob # Store parameters dict/json + transformation_matrix = NULL: blob # Store affine matrix if applicable + warp_field_path = NULL: varchar(512) # Store path to warp field file if non-linear + registration_quality = NULL: float # Optional QC metric for the registration + registration_time = CURRENT_TIMESTAMP: timestamp + registration_notes = "": varchar(2048) + """ From 13d5dea52db454f698fe33d6a21dbad014da60a1 Mon Sep 17 00:00:00 2001 From: Eric Denovellis Date: Thu, 24 Apr 2025 08:45:08 -0400 Subject: [PATCH 05/12] Add parameters --- src/spyglass/histology/v1/histology.py | 11 ++++++++--- src/spyglass/micro_ct/v1/micro_ct.py | 5 +++-- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/src/spyglass/histology/v1/histology.py b/src/spyglass/histology/v1/histology.py index 452824104..463894b00 100644 --- a/src/spyglass/histology/v1/histology.py +++ b/src/spyglass/histology/v1/histology.py @@ -42,14 +42,19 @@ class HistologyStain(SpyglassMixin, dj.Part): """ -class HistologyImage(SpyglassMixin, dj.Computed): +class HistologyImages(SpyglassMixin, dj.Computed): definition = """ # Links Histology info to the Analysis NWB file containing the image volume data/links -> Histology + images_id: varchar(32) # User-defined ID for these images (e.g., 'probe_track_run1', 'anatomy_stain_seriesA') --- -> AnalysisNwbfile color_to_stain = NULL: blob # Mapping of color channels to stains (e.g., {'DAPI': 'blue', 'GFAP': 'green'}) - scale = NULL: blob # Scale of the image (e.g., {'x': 0.5, 'y': 0.5, 'z': 1.0}) + pixel_size_x: float # (um) Pixel size in X direction + pixel_size_y: float # (um) Pixel size in Y direction + pixel_size_z: float # (um) Pixel size in Z direction + objective_magnification: float # Magnification of the objective lens used (e.g., 20x, 40x) + scale: float # Scale factor for the image (e.g., 1.0 for no scaling, 2.0 for double size) image_modality: enum("fluorescence", "brightfield", "other") # Modality of the image (e.g., 'fluorescence', 'brightfield') """ @@ -64,7 +69,7 @@ def make(self, key: dict) -> None: class HistologyRegistration(SpyglassMixin, dj.Manual): definition = """ # Stores results and parameters of aligning histology image data to a target coordinate system - -> HistologyImage # Link to the source histology NWB file info + -> HistologyImages # Link to the source histology NWB file info registration_id: varchar(32) # Unique ID for this specific registration instance/parameters (e.g., 'Elastix_Default_v1') --- -> CoordinateSystem # The TARGET coordinate system achieved by this registration (e.g., 'allen_ccf_v3_ras_um') diff --git a/src/spyglass/micro_ct/v1/micro_ct.py b/src/spyglass/micro_ct/v1/micro_ct.py index 469294899..4e0cf40f0 100644 --- a/src/spyglass/micro_ct/v1/micro_ct.py +++ b/src/spyglass/micro_ct/v1/micro_ct.py @@ -51,10 +51,11 @@ class MicroCTScan(SpyglassMixin, dj.Manual): """ -class MicroCTImage(SpyglassMixin, dj.Computed): +class MicroCTImages(SpyglassMixin, dj.Computed): definition = """ # Links MicroCTScan info to the Analysis NWB file containing the image volume data/links -> MicroCTScan + images_id: varchar(32) # User-defined ID for these images (e.g., 'SubjX_OsO4_Scan1') --- -> AnalysisNwbfile processing_time=CURRENT_TIMESTAMP: timestamp @@ -73,7 +74,7 @@ def make(self, key: dict) -> None: class MicroCTRegistration(SpyglassMixin, dj.Manual): definition = """ # Stores results and parameters of aligning microCT image data to a target coordinate system - -> MicroCTImage # Link to the source microCT NWB file info + -> MicroCTImages # Link to the source microCT NWB file info registration_id: varchar(32) # Unique ID for this specific registration instance/parameters --- -> CoordinateSystem # The TARGET coordinate system achieved by this registration From 95fbe79c9b2455b8807c30858bc80f1b0435901b Mon Sep 17 00:00:00 2001 From: Eric Denovellis Date: Thu, 24 Apr 2025 09:04:27 -0400 Subject: [PATCH 06/12] Add registration software and version --- src/spyglass/histology/v1/histology.py | 7 ++++--- src/spyglass/micro_ct/v1/micro_ct.py | 10 ++++++---- 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/src/spyglass/histology/v1/histology.py b/src/spyglass/histology/v1/histology.py index 463894b00..b0d461142 100644 --- a/src/spyglass/histology/v1/histology.py +++ b/src/spyglass/histology/v1/histology.py @@ -73,10 +73,11 @@ class HistologyRegistration(SpyglassMixin, dj.Manual): registration_id: varchar(32) # Unique ID for this specific registration instance/parameters (e.g., 'Elastix_Default_v1') --- -> CoordinateSystem # The TARGET coordinate system achieved by this registration (e.g., 'allen_ccf_v3_ras_um') - registration_method: varchar(128) # Name of algorithm/tool used (e.g., 'Manual Landmark', 'Elastix', 'BRAINRegistration', 'ABBA', 'QuickNII') - registration_params = NULL: blob # Store parameters as dict/json blob, or link -> registrationParams table if complex/reused + registration_method : varchar(128) # algorithmic approach, e.g. 'affine+bspline' + registration_software : varchar(128) # e.g. 'ANTs', 'elastix', 'SimpleITK' + registration_software_version : varchar(64) # e.g. '2.3.5', '1.3.0' transformation_matrix = NULL: blob # Store affine matrix if computed/applicable (e.g., 4x4 np.array.tobytes()) - warp_field_path = NULL: varchar(512) # Store path to warp field file if non-linear (@external store might be better long term) + warp_field_path = NULL: varchar(512) # Store path to warp field file if non-linear registration_quality = NULL: float # Optional QC metric for the registration (e.g., Dice score, landmark error) registration_time = CURRENT_TIMESTAMP: timestamp # Time this registration entry was created/run registration_notes = "": varchar(2048) # Any specific notes about this registration run diff --git a/src/spyglass/micro_ct/v1/micro_ct.py b/src/spyglass/micro_ct/v1/micro_ct.py index 4e0cf40f0..6d564a16c 100644 --- a/src/spyglass/micro_ct/v1/micro_ct.py +++ b/src/spyglass/micro_ct/v1/micro_ct.py @@ -38,9 +38,9 @@ class MicroCTScan(SpyglassMixin, dj.Manual): # Reconstruction Details (often from scanner software) reconstruction_software = NULL: varchar(128) # e.g., 'CT Pro (Nikon)' - voxel_size_x: float # (um) Reconstructed voxel size X (CRITICAL) - voxel_size_y: float # (um) Reconstructed voxel size Y (CRITICAL) - voxel_size_z: float # (um) Reconstructed voxel size Z (CRITICAL) + voxel_size_x: float # (um) Reconstructed voxel size X + voxel_size_y: float # (um) Reconstructed voxel size Y + voxel_size_z: float # (um) Reconstructed voxel size Z output_format = 'TIFF stack': varchar(64) # Format of raw reconstructed data # Raw Data Location (Essential Input for downstream processing) @@ -78,7 +78,9 @@ class MicroCTRegistration(SpyglassMixin, dj.Manual): registration_id: varchar(32) # Unique ID for this specific registration instance/parameters --- -> CoordinateSystem # The TARGET coordinate system achieved by this registration - registration_method: varchar(128) # Name of algorithm/tool used + registration_method : varchar(128) # algorithmic approach, e.g. 'affine+bspline' + registration_software : varchar(128) # e.g. 'ANTs', 'elastix', 'SimpleITK' + registration_software_version : varchar(64) # e.g. '2.3.5', '1.3.0' registration_params = NULL: blob # Store parameters dict/json transformation_matrix = NULL: blob # Store affine matrix if applicable warp_field_path = NULL: varchar(512) # Store path to warp field file if non-linear From dca9f89719c44dcfe641f3e6e93db1b311cdc55b Mon Sep 17 00:00:00 2001 From: Eric Denovellis Date: Thu, 24 Apr 2025 09:49:03 -0400 Subject: [PATCH 07/12] Make sure pipelines are aligned --- src/spyglass/electrode_localization/__init__.py | 5 +++++ .../electrode_localization/localization_merge.py | 9 +++++++++ .../electrode_localization/v1/histology.py | 6 ++++-- .../electrode_localization/v1/micro_ct.py | 6 ++++-- src/spyglass/histology/v1/histology.py | 10 ++++++++-- src/spyglass/micro_ct/v1/micro_ct.py | 15 +++++++-------- 6 files changed, 37 insertions(+), 14 deletions(-) diff --git a/src/spyglass/electrode_localization/__init__.py b/src/spyglass/electrode_localization/__init__.py index e69de29bb..42e2dedce 100644 --- a/src/spyglass/electrode_localization/__init__.py +++ b/src/spyglass/electrode_localization/__init__.py @@ -0,0 +1,5 @@ +from spyglass.electrode_localization.localization_merge import ( + ChannelBrainLocation, + ChannelBrainLocationHistologyV1, + ChannelBrainLocationMicroCTV1, +) diff --git a/src/spyglass/electrode_localization/localization_merge.py b/src/spyglass/electrode_localization/localization_merge.py index 601375f15..e6fe39fce 100644 --- a/src/spyglass/electrode_localization/localization_merge.py +++ b/src/spyglass/electrode_localization/localization_merge.py @@ -1,3 +1,5 @@ +"""Merge of electrode channel locations from histology and microCT pipelines.""" + import datajoint as dj from spyglass.electrode_localization.v1.histology import ( # noqa: F401 @@ -14,6 +16,13 @@ @schema class ChannelBrainLocation(_Merge, SpyglassMixin): + """Merge of electrode channel locations from histology and microCT pipelines. + + The master table lists each (subject, probe, channel), with parts: + - HistologyV1: ground‐truth coordinates from manual histology alignment. + - MicroCTV1: coregistered coordinates from the microCT volume. + """ + class HistologyV1(SpyglassMixin, dj.Part): definition = """ -> master diff --git a/src/spyglass/electrode_localization/v1/histology.py b/src/spyglass/electrode_localization/v1/histology.py index 0a183dcf5..b1e1d24de 100644 --- a/src/spyglass/electrode_localization/v1/histology.py +++ b/src/spyglass/electrode_localization/v1/histology.py @@ -1,3 +1,5 @@ +"""Histology-derived coordinates and region assignment for an electrode""" + import datajoint as dj from spyglass.common import ( @@ -6,7 +8,7 @@ Electrode, ) # noqa: F401 from spyglass.histology.v1.histology import ( # noqa: F401 - HistologyImage, + HistologyImages, HistologyRegistration, ) from spyglass.utils import SpyglassMixin @@ -19,7 +21,7 @@ class ChannelBrainLocationHistologyV1(SpyglassMixin, dj.Manual): definition = """ # Histology-derived coordinates and region assignment for an electrode -> Electrode # Electrode being localized - -> HistologyImage # Source NWB file link for histology images + -> HistologyImages # Source NWB file link for histology images -> HistologyRegistration # Alignment parameters used --- -> CoordinateSystem # Defines the space for pos_x,y,z (e.g., Allen CCF RAS um) diff --git a/src/spyglass/electrode_localization/v1/micro_ct.py b/src/spyglass/electrode_localization/v1/micro_ct.py index d6216f71e..18a994fb8 100644 --- a/src/spyglass/electrode_localization/v1/micro_ct.py +++ b/src/spyglass/electrode_localization/v1/micro_ct.py @@ -1,3 +1,5 @@ +"""MicroCT-derived coordinates and region assignment for an electrode""" + import datajoint as dj from spyglass.common import ( @@ -6,7 +8,7 @@ Electrode, ) # noqa: F401 from spyglass.micro_ct.v1.micro_ct import ( # noqa: F401 - MicroCTImage, + MicroCTImages, MicroCTRegistration, ) from spyglass.utils import SpyglassMixin @@ -19,7 +21,7 @@ class ChannelBrainLocationMicroCTV1(SpyglassMixin, dj.Manual): definition = """ # MicroCT-derived coordinates and region assignment for an electrode -> Electrode # Electrode being localized - -> MicroCTImage # Source NWB file link for microCT data + -> MicroCTImages # Source NWB file link for microCT data -> MicroCTRegistration # Alignment parameters used --- -> CoordinateSystem # Defines the space for pos_x,y,z diff --git a/src/spyglass/histology/v1/histology.py b/src/spyglass/histology/v1/histology.py index b0d461142..724ef4a56 100644 --- a/src/spyglass/histology/v1/histology.py +++ b/src/spyglass/histology/v1/histology.py @@ -25,6 +25,11 @@ class Histology(SpyglassMixin, dj.Manual): experiment_purpose: varchar(1024) # e.g., 'Probe track recovery for Neuropixel P0', 'ChR2 expression check in mPFC', 'General anatomical reference' notes = "": varchar(2048) # Optional general notes about the preparation -> [nullable] LabMember.proj(histology_experimenter='user_name') # Optional: who did the prep? + + output_format = 'TIFF stack': varchar(64) # Format of raw reconstructed data + + # Raw Data Location (Essential Input for downstream processing) + raw_scan_path: varchar(512) # Path to the raw output (e.g., folder containing TIFF stack) """ class HistologyStain(SpyglassMixin, dj.Part): @@ -49,13 +54,13 @@ class HistologyImages(SpyglassMixin, dj.Computed): images_id: varchar(32) # User-defined ID for these images (e.g., 'probe_track_run1', 'anatomy_stain_seriesA') --- -> AnalysisNwbfile + processing_time=CURRENT_TIMESTAMP: timestamp color_to_stain = NULL: blob # Mapping of color channels to stains (e.g., {'DAPI': 'blue', 'GFAP': 'green'}) pixel_size_x: float # (um) Pixel size in X direction pixel_size_y: float # (um) Pixel size in Y direction pixel_size_z: float # (um) Pixel size in Z direction objective_magnification: float # Magnification of the objective lens used (e.g., 20x, 40x) - scale: float # Scale factor for the image (e.g., 1.0 for no scaling, 2.0 for double size) - image_modality: enum("fluorescence", "brightfield", "other") # Modality of the image (e.g., 'fluorescence', 'brightfield') + image_modality: enum("fluorescence", "brightfield", "other") # Modality of the image """ def make(self, key: dict) -> None: @@ -76,6 +81,7 @@ class HistologyRegistration(SpyglassMixin, dj.Manual): registration_method : varchar(128) # algorithmic approach, e.g. 'affine+bspline' registration_software : varchar(128) # e.g. 'ANTs', 'elastix', 'SimpleITK' registration_software_version : varchar(64) # e.g. '2.3.5', '1.3.0' + registration_parameters = NULL: blob # Store parameters used for registration (e.g., JSON, YAML) transformation_matrix = NULL: blob # Store affine matrix if computed/applicable (e.g., 4x4 np.array.tobytes()) warp_field_path = NULL: varchar(512) # Store path to warp field file if non-linear registration_quality = NULL: float # Optional QC metric for the registration (e.g., Dice score, landmark error) diff --git a/src/spyglass/micro_ct/v1/micro_ct.py b/src/spyglass/micro_ct/v1/micro_ct.py index 6d564a16c..c002c551c 100644 --- a/src/spyglass/micro_ct/v1/micro_ct.py +++ b/src/spyglass/micro_ct/v1/micro_ct.py @@ -59,7 +59,6 @@ class MicroCTImages(SpyglassMixin, dj.Computed): --- -> AnalysisNwbfile processing_time=CURRENT_TIMESTAMP: timestamp - processing_notes = "": varchar(1024) """ def make(self, key: dict) -> None: @@ -75,16 +74,16 @@ class MicroCTRegistration(SpyglassMixin, dj.Manual): definition = """ # Stores results and parameters of aligning microCT image data to a target coordinate system -> MicroCTImages # Link to the source microCT NWB file info - registration_id: varchar(32) # Unique ID for this specific registration instance/parameters + registration_id: varchar(32) # Unique ID for this specific registration instance/parameters (e.g., 'Elastix_Default_v1') --- - -> CoordinateSystem # The TARGET coordinate system achieved by this registration + -> CoordinateSystem # The TARGET coordinate system achieved by this registration (e.g., 'allen_ccf_v3_ras_um') registration_method : varchar(128) # algorithmic approach, e.g. 'affine+bspline' registration_software : varchar(128) # e.g. 'ANTs', 'elastix', 'SimpleITK' registration_software_version : varchar(64) # e.g. '2.3.5', '1.3.0' - registration_params = NULL: blob # Store parameters dict/json - transformation_matrix = NULL: blob # Store affine matrix if applicable + registration_parameters = NULL: blob # Store parameters used for registration (e.g., JSON, YAML) + transformation_matrix = NULL: blob # Store affine matrix if computed/applicable (e.g., 4x4 np.array.tobytes()) warp_field_path = NULL: varchar(512) # Store path to warp field file if non-linear - registration_quality = NULL: float # Optional QC metric for the registration - registration_time = CURRENT_TIMESTAMP: timestamp - registration_notes = "": varchar(2048) + registration_quality = NULL: float # Optional QC metric for the registration (e.g., Dice score, landmark error) + registration_time = CURRENT_TIMESTAMP: timestamp # Time this registration entry was created/run + registration_notes = "": varchar(2048) # Any specific notes about this registration run """ From 35715dc8bd906dd8d4bfdec96ec2fc8a754be66e Mon Sep 17 00:00:00 2001 From: Eric Denovellis Date: Thu, 24 Apr 2025 09:55:18 -0400 Subject: [PATCH 08/12] Formatting --- src/spyglass/histology/v1/histology.py | 153 ++++++++++++++++--------- src/spyglass/micro_ct/v1/micro_ct.py | 135 +++++++++++++--------- 2 files changed, 185 insertions(+), 103 deletions(-) diff --git a/src/spyglass/histology/v1/histology.py b/src/spyglass/histology/v1/histology.py index 724ef4a56..9a51232bf 100644 --- a/src/spyglass/histology/v1/histology.py +++ b/src/spyglass/histology/v1/histology.py @@ -6,7 +6,7 @@ LabMember, Subject, ) -from spyglass.utils import SpyglassMixin +from spyglass.utils import SpyglassMixin, logger schema = dj.schema("histology_v1") @@ -16,75 +16,126 @@ class Histology(SpyglassMixin, dj.Manual): definition = """ # Represents a single histology preparation for a subject -> Subject - histology_id: varchar(32) # User-defined ID for this prep (e.g., 'probe_track_run1', 'anatomy_stain_seriesA') + histology_id: varchar(32) # User-defined ID (e.g., 'probe_track_run1') --- - prep_date=NULL: date # Optional: Date of tissue preparation - slice_orientation: enum("coronal", "sagittal", "horizontal", "other") # Orientation of sections - slice_thickness: float # (um) Thickness of sections - mounting_medium = NULL : varchar(128) - experiment_purpose: varchar(1024) # e.g., 'Probe track recovery for Neuropixel P0', 'ChR2 expression check in mPFC', 'General anatomical reference' - notes = "": varchar(2048) # Optional general notes about the preparation + prep_date=NULL: date # Optional: Date of tissue preparation + slice_orientation: enum( # Orientation of sections + "coronal", + "sagittal", + "horizontal", + "other" + ) + slice_thickness: float # (um) Thickness of sections + mounting_medium=NULL: varchar(128) # e.g., 'DPX', 'Fluoromount-G' + experiment_purpose: varchar(1024) # e.g., 'Probe track recovery', 'Anatomical ref' + notes="": varchar(2048) # Optional general notes about the preparation -> [nullable] LabMember.proj(histology_experimenter='user_name') # Optional: who did the prep? - output_format = 'TIFF stack': varchar(64) # Format of raw reconstructed data - - # Raw Data Location (Essential Input for downstream processing) - raw_scan_path: varchar(512) # Path to the raw output (e.g., folder containing TIFF stack) + # --- Data Source --- + output_format='TIFF stack': varchar(64) # Format of raw image data from scanner + raw_scan_path: varchar(512) # Path to the raw output (e.g., folder containing TIFF stack) """ class HistologyStain(SpyglassMixin, dj.Part): definition = """ # Details of specific stains used in a histology preparation -> Histology - stain_index: tinyint unsigned # Use index for multiple stains per prep (0, 1, 2...) + stain_index: tinyint unsigned # Use index for multiple stains (0, 1, 2...) --- - identified_feature: varchar(128) # Biological target, structure, or marker identified (e.g., 'GFAP+ Astrocytes', 'Nissl Bodies', 'ChR2-tdTomato+ Cells', 'ProbeTrack_DiI', 'Gad2 mRNA') - visualization_agent: varchar(128) # Method/molecule making feature visible (e.g., 'Alexa 488', 'Cresyl Violet', 'Native tdTomato Fluorescence', 'DiI', 'NBT/BCIP via ISH probe') - stain_type : enum("immunohistochemistry", "genetic_marker", "tracer", "anatomical", "histochemical", "in_situ_hybridization", "other") = "other" - stain_protocol_name = NULL : varchar(128) # Optional: name of the protocol used for this stain - antibody_details = NULL : varchar(255) # Optional: specific antibody info (e.g. company, cat#, lot#) - stain_notes = "": varchar(1024) # Optional notes about this specific stain (e.g., concentration, incubation) + identified_feature: varchar(128) # Biological target/marker (e.g., 'Nissl Bodies', 'ChR2-tdTomato+', 'ProbeTrack_DiI') + visualization_agent: varchar(128)# Method/molecule making feature visible (e.g., 'Cresyl Violet', 'Native tdTomato', 'DiI', 'Alexa 488') + stain_type: enum( # Type of staining method used + "immunohistochemistry", + "genetic_marker", + "tracer", + "anatomical", + "histochemical", + "in_situ_hybridization", + "other" + ) = "other" + stain_protocol_name=NULL: varchar(128) # Optional: name of the protocol used + antibody_details=NULL: varchar(255) # Optional: specific antibody info (e.g. company, cat#, lot#) + stain_notes="": varchar(1024) # Optional notes about this specific stain (e.g., concentration) """ +@schema class HistologyImages(SpyglassMixin, dj.Computed): definition = """ - # Links Histology info to the Analysis NWB file containing the image volume data/links - -> Histology - images_id: varchar(32) # User-defined ID for these images (e.g., 'probe_track_run1', 'anatomy_stain_seriesA') - --- - -> AnalysisNwbfile - processing_time=CURRENT_TIMESTAMP: timestamp - color_to_stain = NULL: blob # Mapping of color channels to stains (e.g., {'DAPI': 'blue', 'GFAP': 'green'}) - pixel_size_x: float # (um) Pixel size in X direction - pixel_size_y: float # (um) Pixel size in Y direction - pixel_size_z: float # (um) Pixel size in Z direction - objective_magnification: float # Magnification of the objective lens used (e.g., 20x, 40x) - image_modality: enum("fluorescence", "brightfield", "other") # Modality of the image - """ + # Links Histology info to the Analysis NWB file containing the image data + -> Histology + images_id: varchar(32) # User-defined ID for these images (e.g., histology_id) + --- + -> AnalysisNwbfile # Link to the NWB file storing image data + processing_time=CURRENT_TIMESTAMP: timestamp # Timestamp of NWB file creation + # --- Image Acquisition/Processing Details --- + color_to_stain=NULL: blob # Mapping channel colors to stain features (e.g., {'DAPI': 'blue', 'GFAP': 'green'}) + pixel_size_x: float # (um) Pixel size in X dimension after processing/scaling + pixel_size_y: float # (um) Pixel size in Y dimension after processing/scaling + pixel_size_z: float # (um) Pixel size in Z dimension (often slice_thickness or scan step) + objective_magnification: float # Magnification of the objective lens (e.g., 20 for 20x) + image_modality: enum( # Modality of the microscopy + "fluorescence", + "brightfield", + "other" + ) + processing_notes="": varchar(1024) # Notes on image processing applied before/during NWB creation + """ + + # Ensure this key is unique for HistologyImages entries + # key_source = Histology def make(self, key: dict) -> None: - """Populate HistologyImage table with NWB file links and metadata""" - # Placeholder for actual implementation - # This function should create an AnalysisNwbfile entry and link it to the Histology entry - # It should also populate the color_to_stain and scale fields based on the image data + """ + Populate HistologyImages table. + This method should: + 1. Find the raw image data using `raw_scan_path` from the `Histology` table. + 2. Process the images as needed (e.g., stitching, scaling). + 3. Create an NWB file containing the processed image stack (e.g., using `pynwb.image.ImageSeries`). + - Store relevant metadata (pixel sizes, objective, modality, etc.) within the NWB file. + 4. Create an `AnalysisNwbfile` entry for the new NWB file. + 5. Insert the key, including the `analysis_file_name` from AnalysisNwbfile, + along with image metadata like `pixel_size_*`, `color_to_stain`, etc., into this table. + """ + logger.info(f"Populating HistologyImages for key: {key}") + # Placeholder: Replace with actual NWB creation and insertion logic + # Example steps (conceptual): + # 1. histology_entry = (Histology & key).fetch1() + # 2. raw_path = histology_entry['raw_scan_path'] + # 3. image_data, metadata = process_histology_images(raw_path) # Your function + # 4. nwb_file_name = f"{key['subject_id']}_{key['histology_id']}_images.nwb" + # 5. nwb_file_path = AnalysisNwbfile().create(nwb_file_name) + # 6. create_histology_nwb(nwb_file_path, image_data, metadata) # Your function + # 7. AnalysisNwbfile().add(key['subject_id'], nwb_file_name) + # 8. self.insert1({ + # **key, + # 'images_id': key['histology_id'], # Or generate a new unique ID if needed + # 'analysis_file_name': nwb_file_name, + # 'pixel_size_x': metadata['pixel_size_x'], + # # ... other metadata fields ... + # }) pass +@schema class HistologyRegistration(SpyglassMixin, dj.Manual): definition = """ - # Stores results and parameters of aligning histology image data to a target coordinate system - -> HistologyImages # Link to the source histology NWB file info - registration_id: varchar(32) # Unique ID for this specific registration instance/parameters (e.g., 'Elastix_Default_v1') - --- - -> CoordinateSystem # The TARGET coordinate system achieved by this registration (e.g., 'allen_ccf_v3_ras_um') - registration_method : varchar(128) # algorithmic approach, e.g. 'affine+bspline' - registration_software : varchar(128) # e.g. 'ANTs', 'elastix', 'SimpleITK' - registration_software_version : varchar(64) # e.g. '2.3.5', '1.3.0' - registration_parameters = NULL: blob # Store parameters used for registration (e.g., JSON, YAML) - transformation_matrix = NULL: blob # Store affine matrix if computed/applicable (e.g., 4x4 np.array.tobytes()) - warp_field_path = NULL: varchar(512) # Store path to warp field file if non-linear - registration_quality = NULL: float # Optional QC metric for the registration (e.g., Dice score, landmark error) - registration_time = CURRENT_TIMESTAMP: timestamp # Time this registration entry was created/run - registration_notes = "": varchar(2048) # Any specific notes about this registration run - """ + # Stores results/params of aligning histology image data to a target coordinate system + -> HistologyImages # Link to the source histology NWB file info + registration_id: varchar(32) # Unique ID for this registration instance/parameters + --- + -> CoordinateSystem # The TARGET coordinate system (e.g., 'allen_ccf_v3_ras_um') + + # --- Registration Parameters --- + registration_method: varchar(128) # Algorithmic approach (e.g. 'affine+bspline', 'manual_landmark') + registration_software: varchar(128) # Software used (e.g. 'ANTs', 'elastix', 'SimpleITK', 'CloudCompare') + registration_software_version: varchar(64) # Software version (e.g. '2.3.5', '5.0.1') + registration_params=NULL: blob # Store detailed parameters (e.g., dict/JSON/YAML content) + + # --- Registration Results --- + transformation_matrix=NULL: blob # Store affine matrix (e.g., 4x4 np.array.tobytes()) + warp_field_path=NULL: varchar(512) # Path to non-linear warp field file (e.g., .nii.gz, .mha) + registration_quality=NULL: float # Optional QC metric (e.g., Dice score, landmark error in um) + registration_time=CURRENT_TIMESTAMP: timestamp # Time this registration entry was created/run + registration_notes="": varchar(2048) # Specific notes about this registration run + """ diff --git a/src/spyglass/micro_ct/v1/micro_ct.py b/src/spyglass/micro_ct/v1/micro_ct.py index c002c551c..70665bea7 100644 --- a/src/spyglass/micro_ct/v1/micro_ct.py +++ b/src/spyglass/micro_ct/v1/micro_ct.py @@ -6,7 +6,7 @@ LabMember, Subject, ) -from spyglass.utils import SpyglassMixin +from spyglass.utils import SpyglassMixin, logger schema = dj.schema("microct_v1") @@ -16,74 +16,105 @@ class MicroCTScan(SpyglassMixin, dj.Manual): definition = """ # Metadata for a microCT scan of a subject's brain/tissue -> Subject - scan_id: varchar(32) # User-defined ID for this scan (e.g., 'SubjX_OsO4_Scan1') + scan_id: varchar(32) # User-defined ID (e.g., 'SubjX_OsO4_Scan1') --- - # Preparation Details (can be brief if standardized, use notes for variations) - stain_reagent = 'Aqueous 2% OsO4': varchar(128) # Osmium Tetroxide details - stain_duration_days = 14.0 : float # Duration of staining - embedding_resin = 'Durcupan ACM': varchar(128) # Resin used for embedding - prep_protocol_notes = "": varchar(2048) # Notes on staining, dehydration, embedding variations + # --- Preparation Details --- + stain_reagent='Aqueous 2% OsO4': varchar(128) # Staining reagent details (e.g., 'OsO4', 'I2E') + stain_duration_days=14.0: float # Duration of staining in days + embedding_resin='Durcupan ACM': varchar(128) # Resin used for embedding + prep_protocol_notes="": varchar(2048) # Notes on staining, dehydration, embedding variations - # Scan Details - scan_date=NULL: date # Date of the scan itself - scanner_details: varchar(255) # e.g., 'Nikon Metrology X-Tek HMX ST 225 @ HCNS' - source_target_type = 'Molybdenum': varchar(64) # X-ray source target material - filter_material = 'None': varchar(64) # Filter material used (e.g., None, Copper) - filter_thickness_mm = 0.0: float # (mm) Filter thickness - voltage_kv: float # (kV) Scan voltage (e.g., 100 or 130) - current_ua: float # (uA) Scan current (e.g., 105 or 135) - exposure_time_s: float # (s) Exposure time per projection (e.g., 1.0) - num_projections: int # Number of projections acquired (e.g., 3184) - frames_per_projection = 1: int # Number of frames averaged per projection (e.g., 4) + # --- Scan Details --- + scan_date=NULL: date # Date of the scan itself + scanner_details: varchar(255) # e.g., 'Nikon Metrology X-Tek HMX ST 225 @ HCNS' + source_target_type='Molybdenum': varchar(64) # X-ray source target material + filter_material='None': varchar(64) # Filter material used (e.g., 'None', 'Copper') + filter_thickness_mm=0.0: float # (mm) Filter thickness + voltage_kv: float # (kV) Scan voltage (e.g., 100, 130) + current_ua: float # (uA) Scan current (e.g., 105, 135) + exposure_time_s: float # (s) Exposure time per projection (e.g., 1.0) + num_projections: int # Number of projections acquired (e.g., 3184) + frames_per_projection=1: int # Frames averaged per projection (e.g., 1, 4) - # Reconstruction Details (often from scanner software) - reconstruction_software = NULL: varchar(128) # e.g., 'CT Pro (Nikon)' - voxel_size_x: float # (um) Reconstructed voxel size X - voxel_size_y: float # (um) Reconstructed voxel size Y - voxel_size_z: float # (um) Reconstructed voxel size Z - output_format = 'TIFF stack': varchar(64) # Format of raw reconstructed data + # --- Reconstruction Details --- + reconstruction_software=NULL: varchar(128) # e.g., 'CT Pro (Nikon)', 'NRecon (Bruker)' + voxel_size_x: float # (um) Reconstructed voxel size X + voxel_size_y: float # (um) Reconstructed voxel size Y + voxel_size_z: float # (um) Reconstructed voxel size Z + output_format='TIFF stack': varchar(64) # Format of raw reconstructed data - # Raw Data Location (Essential Input for downstream processing) - raw_scan_path: varchar(512) # Path to the raw output (e.g., folder containing TIFF stack) - - scan_notes = "": varchar(2048) # General notes about the scan itself - -> [nullable] LabMember.proj(scanner_operator='user_name') # Optional + # --- Data Location & Notes --- + raw_scan_path: varchar(512) # Path to raw output (e.g., folder containing TIFF stack) + scan_notes="": varchar(2048) # General notes about the scan itself + -> [nullable] LabMember.proj(scanner_operator='user_name') # Optional: Who operated the scanner? """ +@schema class MicroCTImages(SpyglassMixin, dj.Computed): definition = """ - # Links MicroCTScan info to the Analysis NWB file containing the image volume data/links + # Links MicroCTScan info to the Analysis NWB file containing the image volume -> MicroCTScan - images_id: varchar(32) # User-defined ID for these images (e.g., 'SubjX_OsO4_Scan1') + images_id: varchar(32) # User-defined ID for these images (e.g., scan_id) --- - -> AnalysisNwbfile - processing_time=CURRENT_TIMESTAMP: timestamp + -> AnalysisNwbfile # Link to the NWB file storing image data + processing_time=CURRENT_TIMESTAMP: timestamp # Timestamp of NWB file creation + processing_notes="": varchar(1024) # Notes on image processing applied before/during NWB creation """ + # Ensure this key is unique for MicroCTImages entries + # key_source = MicroCTScan + def make(self, key: dict) -> None: - """Populate MicroCTImage table with NWB file links and metadata""" - # Placeholder for actual implementation - # This function should create an AnalysisNwbfile entry and link it to the MicroCTScan entry - # It should also populate the processing_time and processing_notes fields based on the image data + """ + Populate MicroCTImages table. + This method should: + 1. Find the raw reconstructed image data using `raw_scan_path` from `MicroCTScan`. + 2. Process the images if necessary (e.g., format conversion, cropping). + 3. Create an NWB file containing the image volume (e.g., using `pynwb.image.ImageSeries`). + - Store relevant metadata (voxel sizes from MicroCTScan, etc.) within the NWB file. + 4. Create an `AnalysisNwbfile` entry for the new NWB file. + 5. Insert the key, including the `analysis_file_name` from AnalysisNwbfile, + and any processing notes into this table. + """ + logger.info(f"Populating MicroCTImages for key: {key}") + # Placeholder: Replace with actual NWB creation and insertion logic + # Example steps (conceptual): + # 1. scan_entry = (MicroCTScan & key).fetch1() + # 2. raw_path = scan_entry['raw_scan_path'] + # 3. image_data, metadata = process_microct_images(raw_path, scan_entry) # Your function + # 4. nwb_file_name = f"{key['subject_id']}_{key['scan_id']}_images.nwb" + # 5. nwb_file_path = AnalysisNwbfile().create(nwb_file_name) + # 6. create_microct_nwb(nwb_file_path, image_data, metadata) # Your function + # 7. AnalysisNwbfile().add(key['subject_id'], nwb_file_name) + # 8. self.insert1({ + # **key, + # 'images_id': key['scan_id'], # Or generate a new unique ID if needed + # 'analysis_file_name': nwb_file_name, + # 'processing_notes': '...', + # }) pass @schema class MicroCTRegistration(SpyglassMixin, dj.Manual): definition = """ - # Stores results and parameters of aligning microCT image data to a target coordinate system - -> MicroCTImages # Link to the source microCT NWB file info - registration_id: varchar(32) # Unique ID for this specific registration instance/parameters (e.g., 'Elastix_Default_v1') - --- - -> CoordinateSystem # The TARGET coordinate system achieved by this registration (e.g., 'allen_ccf_v3_ras_um') - registration_method : varchar(128) # algorithmic approach, e.g. 'affine+bspline' - registration_software : varchar(128) # e.g. 'ANTs', 'elastix', 'SimpleITK' - registration_software_version : varchar(64) # e.g. '2.3.5', '1.3.0' - registration_parameters = NULL: blob # Store parameters used for registration (e.g., JSON, YAML) - transformation_matrix = NULL: blob # Store affine matrix if computed/applicable (e.g., 4x4 np.array.tobytes()) - warp_field_path = NULL: varchar(512) # Store path to warp field file if non-linear - registration_quality = NULL: float # Optional QC metric for the registration (e.g., Dice score, landmark error) - registration_time = CURRENT_TIMESTAMP: timestamp # Time this registration entry was created/run - registration_notes = "": varchar(2048) # Any specific notes about this registration run - """ + # Stores results/params of aligning microCT image data to a target coordinate system + -> MicroCTImages # Link to the source microCT NWB file info + registration_id: varchar(32) # Unique ID for this registration instance/parameters + --- + -> CoordinateSystem # The TARGET coordinate system (e.g., 'allen_ccf_v3_ras_um') + + # --- Registration Parameters --- + registration_method: varchar(128) # Algorithmic approach (e.g. 'affine+bspline', 'manual_landmark') + registration_software: varchar(128) # Software used (e.g. 'ANTs', 'elastix', 'SimpleITK', 'CloudCompare') + registration_software_version: varchar(64) # Software version (e.g. '2.3.5', '5.0.1') + registration_params=NULL: blob # Store detailed parameters (e.g., dict/JSON/YAML content) + + # --- Registration Results --- + transformation_matrix=NULL: blob # Store affine matrix (e.g., 4x4 np.array.tobytes()) + warp_field_path=NULL: varchar(512) # Path to non-linear warp field file (e.g., .nii.gz, .mha) + registration_quality=NULL: float # Optional QC metric (e.g., Dice score, landmark error in um) + registration_time=CURRENT_TIMESTAMP: timestamp # Time this registration entry was created/run + registration_notes="": varchar(2048) # Specific notes about this registration run + """ From 35f24bf11ccf4bd0cdf8ed90f479cb1e8e2c8b98 Mon Sep 17 00:00:00 2001 From: Eric Denovellis Date: Thu, 24 Apr 2025 10:25:58 -0400 Subject: [PATCH 09/12] Add abbreviations and warning. --- src/spyglass/common/common_region.py | 70 ++++++++++++++++++++++------ 1 file changed, 56 insertions(+), 14 deletions(-) diff --git a/src/spyglass/common/common_region.py b/src/spyglass/common/common_region.py index 1315ba64a..2ae14a185 100644 --- a/src/spyglass/common/common_region.py +++ b/src/spyglass/common/common_region.py @@ -1,6 +1,6 @@ import datajoint as dj -from spyglass.utils.dj_mixin import SpyglassMixin +from spyglass.utils.dj_mixin import SpyglassMixin, logger schema = dj.schema("common_region") @@ -10,10 +10,13 @@ class BrainRegion(SpyglassMixin, dj.Lookup): definition = """ region_id: smallint auto_increment --- - region_name: varchar(200) # the name of the brain region - subregion_name=NULL: varchar(200) # subregion name - subsubregion_name=NULL: varchar(200) # subregion within subregion - atlas_name=NULL: varchar(200) # name of the atlas + region_name: varchar(200) # Name of the region (e.g., 'Hippocampal formation') + region_abbr=NULL: varchar(64) # Standard abbreviation (e.g., 'HPF') + subregion_name=NULL: varchar(200) # Subregion name (e.g., 'Cornu Ammonis 1') + subregion_abbr=NULL: varchar(64) # Subregion abbreviation (e.g., 'CA1') + subsubregion_name=NULL: varchar(200) # Sub-subregion name (e.g., 'stratum pyramidale') + subsubregion_abbr=NULL: varchar(64) # Sub-subregion abbreviation (e.g., 'sp') + atlas_source: varchar(128) # Source atlas (e.g., 'Allen CCF v3', 'Paxinos Rat 6th Ed') """ # TODO consider making (region_name, subregion_name, subsubregion_name) a @@ -53,6 +56,11 @@ def fetch_add( ) query = BrainRegion & key if not query: + logger.info( + f"Brain region '{region_name}' not found. Adding to BrainRegion. " + "Please make sure to check the spelling and format." + "Remove any extra spaces or special characters." + ) cls.insert1(key) query = BrainRegion & key return query.fetch1("region_id") @@ -61,18 +69,52 @@ def fetch_add( @schema class CoordinateSystem(dj.Lookup): definition = """ - # Defines standard coordinate systems used for spatial data - coordinate_system_id: varchar(32) # e.g., 'Allen_CCF_v3_RAS_um' + # Defines standard coordinate systems used for spatial data. + coordinate_system_id: varchar(64) # Primary key (e.g., 'Allen_CCFv3_RAS_um') --- - description: varchar(255) + description: varchar(255) # Description of the coordinate system + orientation: enum( # Anatomical orientation convention + "RAS", "LPS", "PIR", "ASL", "XYZ", "Other" + ) + unit: enum( # Spatial unit + "um", "mm", "pixels", "voxels" + ) + atlas_source=NULL: varchar(128) # Source if based on an atlas (e.g., 'Allen CCF v3', 'WHS Rat v4') """ - # Maybe use https://brainglobe.info/documentation/brainglobe-atlasapi/usage/atlas-details.html contents = [ [ - "Allen_CCF_v3_RAS_um", - "Allen CCF v3 Reference Atlas Space, RAS orientation, unit um", + "Allen_CCFv3_RAS_um", + "Allen CCF v3 Mouse Atlas, RAS orientation, micrometers", + "RAS", + "um", + "Allen CCF v3", + ], + [ + "Paxinos_Rat_6th_PIR_um", + "Paxinos & Watson Rat Atlas 6th Ed, PIR orientation, micrometers", + "PIR", + "um", + "Paxinos Rat 6th Ed", + ], + [ + "WHS_Rat_v4_RAS_um", + "Waxholm Space Sprague Dawley Rat Atlas v4, RAS orientation, micrometers", + "RAS", + "um", + "WHS Rat v4", + ], + [ + "Histology_Image_Pixels", + "2D Pixels from processed histology image (Origin/Orientation Varies)", + "Other", + "pixels", + None, + ], + [ + "MicroCT_Voxel_Scan", + "3D Voxel space from raw microCT scan (Orientation relative to scanner)", + "XYZ", # Or 'Other' depending on context + "voxels", + None, ], - ["Histology_Image_Pixels", "2D Pixels from processed histology image"], - ["MicroCT_Voxel", "3D Voxel space from microCT scan"], - ["whs_sd_rat_39um", "3D Voxel space from Waxholm atlas"], ] From bf78853c91b5bfe6296a360872b549fbdfb7960d Mon Sep 17 00:00:00 2001 From: Eric Denovellis Date: Thu, 24 Apr 2025 10:53:17 -0400 Subject: [PATCH 10/12] Remove orientation and unit --- src/spyglass/common/common_region.py | 16 ---------------- 1 file changed, 16 deletions(-) diff --git a/src/spyglass/common/common_region.py b/src/spyglass/common/common_region.py index 2ae14a185..2b1d92f3d 100644 --- a/src/spyglass/common/common_region.py +++ b/src/spyglass/common/common_region.py @@ -73,48 +73,32 @@ class CoordinateSystem(dj.Lookup): coordinate_system_id: varchar(64) # Primary key (e.g., 'Allen_CCFv3_RAS_um') --- description: varchar(255) # Description of the coordinate system - orientation: enum( # Anatomical orientation convention - "RAS", "LPS", "PIR", "ASL", "XYZ", "Other" - ) - unit: enum( # Spatial unit - "um", "mm", "pixels", "voxels" - ) atlas_source=NULL: varchar(128) # Source if based on an atlas (e.g., 'Allen CCF v3', 'WHS Rat v4') """ contents = [ [ "Allen_CCFv3_RAS_um", "Allen CCF v3 Mouse Atlas, RAS orientation, micrometers", - "RAS", - "um", "Allen CCF v3", ], [ "Paxinos_Rat_6th_PIR_um", "Paxinos & Watson Rat Atlas 6th Ed, PIR orientation, micrometers", - "PIR", - "um", "Paxinos Rat 6th Ed", ], [ "WHS_Rat_v4_RAS_um", "Waxholm Space Sprague Dawley Rat Atlas v4, RAS orientation, micrometers", - "RAS", - "um", "WHS Rat v4", ], [ "Histology_Image_Pixels", "2D Pixels from processed histology image (Origin/Orientation Varies)", - "Other", - "pixels", None, ], [ "MicroCT_Voxel_Scan", "3D Voxel space from raw microCT scan (Orientation relative to scanner)", - "XYZ", # Or 'Other' depending on context - "voxels", None, ], ] From 58efe81fb12fb140b0b5e698b8021017fead09fe Mon Sep 17 00:00:00 2001 From: Eric Denovellis Date: Thu, 24 Apr 2025 11:00:17 -0400 Subject: [PATCH 11/12] Add null for atlas_source --- src/spyglass/common/common_region.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/spyglass/common/common_region.py b/src/spyglass/common/common_region.py index 2b1d92f3d..c20160870 100644 --- a/src/spyglass/common/common_region.py +++ b/src/spyglass/common/common_region.py @@ -16,7 +16,7 @@ class BrainRegion(SpyglassMixin, dj.Lookup): subregion_abbr=NULL: varchar(64) # Subregion abbreviation (e.g., 'CA1') subsubregion_name=NULL: varchar(200) # Sub-subregion name (e.g., 'stratum pyramidale') subsubregion_abbr=NULL: varchar(64) # Sub-subregion abbreviation (e.g., 'sp') - atlas_source: varchar(128) # Source atlas (e.g., 'Allen CCF v3', 'Paxinos Rat 6th Ed') + atlas_source=NULL: varchar(128) # Source atlas (e.g., 'Allen CCF v3', 'Paxinos Rat 6th Ed') """ # TODO consider making (region_name, subregion_name, subsubregion_name) a From f7ba99c73b087e746e4dc9f8ca341ca65df58471 Mon Sep 17 00:00:00 2001 From: Eric Denovellis Date: Thu, 24 Apr 2025 11:48:08 -0400 Subject: [PATCH 12/12] Rename table --- src/spyglass/common/__init__.py | 2 +- src/spyglass/common/common_region.py | 4 ++-- src/spyglass/electrode_localization/v1/histology.py | 4 ++-- src/spyglass/electrode_localization/v1/micro_ct.py | 4 ++-- src/spyglass/histology/v1/histology.py | 6 +++--- 5 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/spyglass/common/__init__.py b/src/spyglass/common/__init__.py index 9bc9170a4..a3b04d4a1 100644 --- a/src/spyglass/common/__init__.py +++ b/src/spyglass/common/__init__.py @@ -52,7 +52,7 @@ PositionVideo, TrackGraph, ) -from spyglass.common.common_region import BrainRegion, CoordinateSystem +from spyglass.common.common_region import BrainCoordinateSystem, BrainRegion from spyglass.common.common_sensors import SensorData from spyglass.common.common_session import Session from spyglass.common.common_subject import Subject diff --git a/src/spyglass/common/common_region.py b/src/spyglass/common/common_region.py index c20160870..27064ce6b 100644 --- a/src/spyglass/common/common_region.py +++ b/src/spyglass/common/common_region.py @@ -67,9 +67,9 @@ def fetch_add( @schema -class CoordinateSystem(dj.Lookup): +class BrainCoordinateSystem(dj.Lookup): definition = """ - # Defines standard coordinate systems used for spatial data. + # Defines standard coordinate systems used for brain spatial data. coordinate_system_id: varchar(64) # Primary key (e.g., 'Allen_CCFv3_RAS_um') --- description: varchar(255) # Description of the coordinate system diff --git a/src/spyglass/electrode_localization/v1/histology.py b/src/spyglass/electrode_localization/v1/histology.py index b1e1d24de..5b2d98de9 100644 --- a/src/spyglass/electrode_localization/v1/histology.py +++ b/src/spyglass/electrode_localization/v1/histology.py @@ -3,8 +3,8 @@ import datajoint as dj from spyglass.common import ( + BrainCoordinateSystem, BrainRegion, - CoordinateSystem, Electrode, ) # noqa: F401 from spyglass.histology.v1.histology import ( # noqa: F401 @@ -24,7 +24,7 @@ class ChannelBrainLocationHistologyV1(SpyglassMixin, dj.Manual): -> HistologyImages # Source NWB file link for histology images -> HistologyRegistration # Alignment parameters used --- - -> CoordinateSystem # Defines the space for pos_x,y,z (e.g., Allen CCF RAS um) + -> BrainCoordinateSystem # Defines the space for pos_x,y,z (e.g., Allen CCF RAS um) pos_x: float # (um) coordinate in the specified space pos_y: float # (um) coordinate in the specified space pos_z: float # (um) coordinate in the specified space diff --git a/src/spyglass/electrode_localization/v1/micro_ct.py b/src/spyglass/electrode_localization/v1/micro_ct.py index 18a994fb8..e2f5af1cb 100644 --- a/src/spyglass/electrode_localization/v1/micro_ct.py +++ b/src/spyglass/electrode_localization/v1/micro_ct.py @@ -3,8 +3,8 @@ import datajoint as dj from spyglass.common import ( + BrainCoordinateSystem, BrainRegion, - CoordinateSystem, Electrode, ) # noqa: F401 from spyglass.micro_ct.v1.micro_ct import ( # noqa: F401 @@ -24,7 +24,7 @@ class ChannelBrainLocationMicroCTV1(SpyglassMixin, dj.Manual): -> MicroCTImages # Source NWB file link for microCT data -> MicroCTRegistration # Alignment parameters used --- - -> CoordinateSystem # Defines the space for pos_x,y,z + -> BrainCoordinateSystem # Defines the space for pos_x,y,z pos_x: float # (um) coordinate in the specified space pos_y: float # (um) coordinate in the specified space pos_z: float # (um) coordinate in the specified space diff --git a/src/spyglass/histology/v1/histology.py b/src/spyglass/histology/v1/histology.py index 9a51232bf..393906560 100644 --- a/src/spyglass/histology/v1/histology.py +++ b/src/spyglass/histology/v1/histology.py @@ -2,7 +2,7 @@ from spyglass.common import ( # noqa: F401 AnalysisNwbfile, - CoordinateSystem, + BrainCoordinateSystem, LabMember, Subject, ) @@ -52,7 +52,7 @@ class HistologyStain(SpyglassMixin, dj.Part): "histochemical", "in_situ_hybridization", "other" - ) = "other" + ) = "anatomical" stain_protocol_name=NULL: varchar(128) # Optional: name of the protocol used antibody_details=NULL: varchar(255) # Optional: specific antibody info (e.g. company, cat#, lot#) stain_notes="": varchar(1024) # Optional notes about this specific stain (e.g., concentration) @@ -124,7 +124,7 @@ class HistologyRegistration(SpyglassMixin, dj.Manual): -> HistologyImages # Link to the source histology NWB file info registration_id: varchar(32) # Unique ID for this registration instance/parameters --- - -> CoordinateSystem # The TARGET coordinate system (e.g., 'allen_ccf_v3_ras_um') + -> BrainCoordinateSystem # The TARGET coordinate system (e.g., 'allen_ccf_v3_ras_um') # --- Registration Parameters --- registration_method: varchar(128) # Algorithmic approach (e.g. 'affine+bspline', 'manual_landmark')