Soma Detection Analysis of Whole-Brain Fluorescence Images

[ ]:
from brainlit.BrainLine.analyze_results import SomaDistribution
from brainlit.BrainLine.util import (
    json_to_points,
    download_subvolumes,
)
from brainlit.BrainLine.apply_ilastik import (
    ApplyIlastik,
    ApplyIlastik_LargeImage,
    plot_results,
    examine_threshold,
)
from brainlit.BrainLine.parse_ara import *
import xml.etree.ElementTree as ET
from brainlit.BrainLine.imports import *

%gui qt5

1. Before Using this notebook

1a. Install brainlit, and dependencies

1b. Write images to s3 using CloudReg

- e.g. python -m cloudreg.scripts.create_precomputed_volumes --s3_input_paths <path-to-stitched-images> --s3_output_paths  <s3-address-for-output>  --voxel_size <x-resolution> <y-resolution> <z-resolution> --num_procs <num-cpus> --resample_iso <boolean-to-resample-isotropically>

1c. Make point annotations in neuroglancer to identify subvolumes for validation (and possible training)

Instructions. JSON snippet:

,
{
"type":"pointAnnotation",
"name": "soma_val",
"points": []
},
{
"type":"pointAnnotation",
"name": "nonsoma_val",
"points":[]
}

1d. Update soma_data.json file

This file stores information on how to access neuroglancer data.

Data should be stored in the brain2paths dictionary, with entries like:

"<sample ID>": {
    "base_s3": "<Path to directory with layers with CloudVolume prependings (ending with forward slash)>",
    "base_local": "<Path to directory with layers with CloudVolume prependings (ending with forward slash)>",
    "val_info": {
        "url": "<neuroglancer URL>",
        "somas_layer": "<name of layer with coordinates on somas>",
        "nonsomas_layer": "<name of layer with coordinates on non-somas>",
    },
    "somas_atlas_url": "<neuroglancer URL entered after processing with a single annotation layer which contains points of soma detections>",
    "subtype": "<subtype>"
    #Optional:
    "train_info": {
        "url": "<neuroglancer URL>",
        "somas_layer": "<name of layer with coordinates on somas>",
        "nonsomas_layer": "<name of layer with coordinates on non-somas>",
    },
}
[ ]:
brainlit_path = Path(os.path.abspath(""))
brainlit_path = brainlit_path.parents[3]
print(f"Path to brainlit: {brainlit_path}")
data_file = (
    brainlit_path / "docs" / "notebooks" / "pipelines" / "BrainLine" / "soma_data.json"
)

brain = "test"  # brain ID
soma_data_dir = (
    str(
        brainlit_path
        / "docs"
        / "notebooks"
        / "pipelines"
        / "BrainLine"
        / "validation-data"
        / "soma"
    )
    + "/"
)  # path to directory where training/validation data should be stored

# Modify base path of test sample according to your system
with open(data_file, "r") as f:
    data = json.load(f)

base = (
    "precomputed://file://"
    + str(
        brainlit_path
        / "docs"
        / "notebooks"
        / "pipelines"
        / "BrainLine"
        / "example-data"
    )
    + "/"
)
data["brain2paths"]["test"]["base_s3"] = base
data["brain2paths"]["test"]["base_local"] = base

brain2paths = data["brain2paths"]

with open(data_file, "w") as f:
    json.dump(data, f, indent=4)

Validation: Steps 2, 4-5, 7 below can be done via script with: brainlit/BrainLine/scripts/soma_validate.py

2. Download benchmark data

*Inputs*

For the test sample to work, you should be serving it via neuroglancer’s cors_webserver e.g. with the command python cors_webserver.py -d "<path to brainlit>/brainlit/BrainLine/data/example" -p 9010

[ ]:
dataset_to_save = "val"  # train or val

antibody_layer = "antibody"
background_layer = "background"
endogenous_layer = "endogenous"

layer_names = [antibody_layer, background_layer, endogenous_layer]

Download data

[ ]:
download_subvolumes(
    soma_data_dir,
    brain_id=brain,
    data_file=data_file,
    layer_names=layer_names,
    dataset_to_save=dataset_to_save,
)

3. View downloaded data (optional)

*Inputs*

[ ]:
fname = Path(soma_data_dir) / f"brain{brain}" / "val" / "2005_1978_1841_pos.h5"
scale = [1.8, 1.8, 2]  # voxel size in microns
[ ]:
with h5py.File(fname, "r") as f:
    im = f.get("image_3channel")
    image_fg = im[0, :, :, :]
    image_bg = im[1, :, :, :]
    image_endo = im[2, :, :, :]

viewer = napari.Viewer(ndisplay=3)
viewer.add_image(image_fg, scale=scale)
viewer.add_image(image_bg, scale=scale)
viewer.add_image(image_endo, scale=scale)
viewer.scale_bar.visible = True
viewer.scale_bar.unit = "um"

4. Apply ilastik to validation data

Ilastik can be downloaded here.

You can do this programmatically (below), or you can use the ilastik GUI (which is sometimes faster).

* Inputs *

[ ]:
ilastik_project = str(
    brainlit_path
    / Path("experiments/BrainLine/data/models/soma/matt_soma_rabies_pix_3ch.ilp")
)  # path to ilastik model to be used
ilastik_path = "/Applications/ilastik-1.4.0b21-OSX.app/Contents/ilastik-release/run_ilastik.sh"  # path to ilastik executable (see here: https://www.ilastik.org/documentation/basics/headless.html)
brains = [brain]
[ ]:
applyilastik = ApplyIlastik(
    ilastik_path=ilastik_path,
    project_path=ilastik_project,
    brains_path=soma_data_dir,
    brains=brains,
)
applyilastik.process_subvols(ncpu=6)
# applyilastik.move_results()

5. Check Results

Image Subvolumes with Two Cell Bodies

If image subvolumes contain two cell bodies, the performance statistics can be adjusted using the doubles variable, which is a list of file name strings. Enter the file name (without extension) of the subvolumes with two cell bodies.

[ ]:
doubles = ["2005_1978_1841_pos"]

Validation

[ ]:
fig, _, _ = plot_results(
    data_dir=soma_data_dir,
    brain_ids=[brain],
    object_type="soma",
    positive_channel=0,
    doubles=doubles,
)
plt.show()

If results above are not adequate, improve model and try again

In my case, I identify more subvolumes from the sample at hand using the same process as for validation data, and add it as training data to the model and retrain.

Examine best threshold

[ ]:
examine_threshold(
    data_dir=soma_data_dir,
    brain_id=brain,
    threshold=0.76,
    object_type="soma",
    positive_channel=0,
    doubles=doubles,
)

6. Apply ilastik to whole image

This can be done via a script with: brainlit/BrainLine/soma_detect_image

* Inputs *

[ ]:
threshold = 0.76  # threshold to use for ilastik
data_dir = (
    soma_data_dir + "brainr_temp/"
)  # "/data/tathey1/matt_wright/brainr_temp/"  # directory to store temporary subvolumes for segmentation
results_dir = (
    soma_data_dir + "brainr_results/"
)  # directory to store coordinates of soma detections

min_coords = [2000, 1900, 1800]
max_coords = [2100, 2000, 1900]  # -1 if you want to process the whole dimension

ncpu = 1  # 16  # number of cores to use for detection
chunk_size = [100, 100, 100]  # [256, 256, 300]

layer_names = [antibody_layer, background_layer, endogenous_layer]

ilastik_largeimage = ApplyIlastik_LargeImage(
    ilastik_path=ilastik_path,
    ilastik_project=ilastik_project,
    data_file=data_file,
    results_dir=results_dir,
    ncpu=1,
)
[ ]:
ilastik_largeimage.apply_ilastik_parallel(
    brain_id=brain,
    layer_names=layer_names,
    threshold=threshold,
    data_dir=data_dir,
    chunk_size=chunk_size,
    min_coords=min_coords,
    max_coords=max_coords,
)

Before this step you will need to make sure that the original data is being served to neuroglancer. For example, in this case, our data is local so we can serve it with the command:

python cors_webserver.py -d "<path-to-brainlit>/brainlit/brainlit/BrainLine/data/example" -p 9010

which needs to be run in the neuroglancer folder (git clone from here: https://github.com/google/neuroglancer)

[ ]:
ilastik_largeimage.collect_soma_results(
    brain_id="test"
)  # will not work if you are not serving the data (see neuroglancer cors_webserver info above)

7. Prepare Transformed Layers

[ ]:
atlas_vol = CloudVolume(
    "precomputed://https://open-neurodata.s3.amazonaws.com/ara_2016/sagittal_10um/annotation_10um_2017"
)
for layer in [
    antibody_layer,
    background_layer,
]:  # axon_mask is transformed into an image because nearest interpolation doesnt work well after downsampling
    layer_path = brain2paths[brain]["base_s3"] + layer + "_transformed"
    info = CloudVolume.create_new_info(
        num_channels=1,
        layer_type="image",
        data_type="uint16",  # Channel images might be 'uint8'
        encoding="raw",  # raw, jpeg, compressed_segmentation, fpzip, kempressed
        resolution=atlas_vol.resolution,  # Voxel scaling, units are in nanometers
        voxel_offset=atlas_vol.voxel_offset,
        chunk_size=[32, 32, 32],  # units are voxels
        volume_size=atlas_vol.volume_size,  # e.g. a cubic millimeter dataset
    )
    vol_mask = CloudVolume(layer_path, info=info)
    vol_mask.commit_info()

8. Register volume and transform data to atlas space using CloudReg

8a. You need to find an initial affine alignment using cloudreg.scripts.registration.get_affine_matrix. For example:

A link to the ARA parcellation is:

precomputed://https://open-neurodata.s3.amazonaws.com/ara_2016/sagittal_10um/annotation_10um_2017

And some python commands to help with affine alignment is:

from cloudreg.scripts.registration import get_affine_matrix

get_affine_matrix([1,1,1], [15,0,0], "PIR", "RAI", 1.15, "precomputed://https://open-neurodata.s3.amazonaws.com/ara_2016/sagittal_10um/annotation_10um_2017")

8b. Run registration using cloudreg.scripts.registration. For example:

python -m cloudreg.scripts.registration -input_s3_path precomputed://s3://smartspim-precomputed-volumes/2022_02_02/8604/Ch_561 --output_s3_path precomputed://s3://smartspim-precomputed-volumes/2022_02_02/8604/atlas_to_target --atlas_s3_path https://open-neurodata.s3.amazonaws.com/ara_2016/sagittal_50um/average_50um --parcellation_s3_path https://open-neurodata.s3.amazonaws.com/ara_2016/sagittal_10um/annotation_10um_2017 --atlas_orientation PIR -orientation ARS --rotation 0 0 0 --translation 0 0 0 --fixed_scale .7 -log_s3_path precomputed://s3://smartspim-precomputed-volumes/2022_02_02/8604/atlas_to_target --missing_data_correction True --grid_correction False --bias_correction True --regularization 5000.0 --iterations 3000 --registration_resolution 100

8c. Transform data to atlas space using CloudReg

example commands below

Soma coordinates

python -m cloudreg.scripts.transform_points --target_viz_link "https://viz.neurodata.io/?json_url=https://json.neurodata.io/v1?NGStateID=pLuy0SXtVkKT0A" --atlas_viz_link "https://ara.viz.neurodata.io/?json_url=https://json.neurodata.io/v1?NGStateID=HvyNDGaPsd1wyg" --affine_path "/Users/thomasathey/Documents/mimlab/mouselight/brainlit_parent/downloop_1_A.mat"  --velocity_path "/Users/thomasathey/Documents/mimlab/mouselight/brainlit_parent/downloop_1_v.mat"  --transformation_direction atlas

This will produce a neuroglancer link with the transformed soma coordinates, which should be added to soma_data.py under the somas_atlas_url key. Then the code below, or soma_brainrender.py, can be used to visualize the data.

Image

python -m cloudreg.scripts.transform_data --target_layer_source precomputed://s3://smartspim-precomputed-volumes/2022_09_20/887/Ch_647 --transformed_layer_source precomputed://s3://smartspim-precomputed-volumes/2022_09_20/887/Ch_647_transformed --affine_path /cis/home/tathey/887_Ch_561_registration/downloop_1_A.mat  --velocity_path /cis/home/tathey/887_Ch_561_registration/downloop_1_v.mat

Analysis: Steps 9-10 below can be done via a script with: brainlit/BrainLine/scripts/soma_analyze.py

9. View somas in brain space

*Inputs*

[ ]:
colors = {
    "test_type": "red",
}  # colors for different sample types
symbols = ["o", "+", "^", "vbar"]
brain_ids = ["test"]
fold_on = False

ontology_file = (
    brainlit_path / "brainlit" / "Brainline" / "data" / "ara_structure_ontology.json"
)
[ ]:
sd = SomaDistribution(
    brain_ids=brain_ids, data_file=data_file, ontology_file=ontology_file
)
[ ]:
f = sd.napari_coronal_section(
    z=1073, subtype_colors=colors, symbols=symbols, fold_on=fold_on, plot_type="plt"
)
plt.show()
[ ]:
sd.napari_coronal_section(
    z=1073, subtype_colors=colors, symbols=symbols, fold_on=fold_on
)
[ ]:
from vedo import embedWindow

embedWindow(None)  # needed for brainrender in jupyter notebook

sd.brainrender_somas(
    subtype_colors=colors, brain_region="VERM"
)  # might take a while to download the atlas for your first run

10. Display bar charts

[ ]:
regions = [
    512,  # cerebellum
    688,  # cerebral cortex
    698,  # olfactory areas
    1089,  # hippocampal formation
    # 583, # claustrum
    477,  # striatum
    # 803, # pallidum
    351,  # bed nuclei of stria terminalis
    # 703, #cortical subplate
    1097,  # hypothalamus
    549,  # thalamus
    186,  # lateral habenula
    519,  # cerebellar nuclei
    313,  # midbrain
    1065,  # hindbrain
]  # allen atlas region IDs to be shown
# see: https://connectivity.brain-map.org/projection/experiment/480074702?imageId=480075280&initImage=TWO_PHOTON&x=17028&y=11704&z=3

composite_regions = {
    "Amygdalar Nuclei": [131, 295, 319, 780]
}  # Custom composite allen regions where key is region name and value is list of allen regions

sd.region_barchart(regions, composite_regions=composite_regions, normalize_region=512)
[ ]: