Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@
"source": [
"from obi_auth import get_token\n",
"from entitysdk.client import Client\n",
"from entitysdk.models import ReconstructionMorphology\n",
"from entitysdk.models import CellMorphology\n",
"import pandas as pd\n",
"\n",
"import neurom\n",
Expand Down Expand Up @@ -108,7 +108,8 @@
" morph_ids = get_entities.get_entities(\"reconstruction-morphology\", token, morph_ids,\n",
" project_context=project_context,\n",
" multi_select=True,\n",
" page_size=100)"
" page_size=100)\n",
" "
]
},
{
Expand All @@ -130,7 +131,7 @@
"metadata": {},
"outputs": [],
"source": [
"fetched = [client.get_entity(entity_id=morph_id_, entity_type=ReconstructionMorphology) for morph_id_ in morph_ids]\n",
"fetched = [client.get_entity(entity_id=morph_id_, entity_type=CellMorphology) for morph_id_ in morph_ids]\n",
"\n",
"asset_types = {}\n",
"for fetched_ in fetched:\n",
Expand Down Expand Up @@ -172,7 +173,7 @@
" if len(asset_) >= 1:\n",
" out_fn = client.download_file(\n",
" entity_id=morph_id_,\n",
" entity_type=ReconstructionMorphology,\n",
" entity_type=CellMorphology,\n",
" asset_id=asset_[0],\n",
" output_path=morphologies_root,\n",
" project_context=project_context\n",
Expand All @@ -182,7 +183,8 @@
" nm_morphs.setdefault(\"Morphology type\", []).append(fetched_.mtypes[0].pref_label)\n",
" nm_morphs.setdefault(\"Created by\", []).append(fetched_.created_by.pref_label)\n",
" nm_morphs.setdefault(\"Name\", []).append(fetched_.name)\n",
"nm_morphs = pd.DataFrame(nm_morphs)\n"
"nm_morphs = pd.DataFrame(nm_morphs)\n",
"print(nm_morphs)"
]
},
{
Expand Down Expand Up @@ -295,7 +297,7 @@
],
"metadata": {
"kernelspec": {
"display_name": ".venv (3.13.1)",
"display_name": "Python 3",
"language": "python",
"name": "python3"
},
Expand All @@ -309,7 +311,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.13.1"
"version": "3.12.3"
}
},
"nbformat": 4,
Expand Down
305 changes: 305 additions & 0 deletions Cellular/skeletonization/skeletonization.ipynb
Original file line number Diff line number Diff line change
@@ -0,0 +1,305 @@
{
"cells": [
{
"cell_type": "markdown",
"id": "51813ca7",
"metadata": {},
"source": [
"# Ultraliser Neuron Skeletonization Service Notebook\n",
"\n",
"Copyright (c) 2025 Open Brain Institute\n",
"\n",
"+ Author(s): \n",
" - Authors: Michael W. Reimann < michael.reimann@openbraininstitute.org >\n",
" - Marwan Abdellah < marwan.abdellah@openbraininstitute.org >\n",
"\n",
"Last modified: 10.2025\n"
]
},
{
"cell_type": "markdown",
"id": "e6687c74",
"metadata": {},
"source": [
"## Imports and setting up platform authentication\n",
"\n",
"Please follow the displayed instructions to authenticate."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "7cd5dc38",
"metadata": {},
"outputs": [],
"source": [
"# All essential imports needed for the notebook\n",
"import httpx\n",
"import os\n",
"import time \n",
"import json \n",
"import pathlib\n",
"from uuid import UUID\n",
"from urllib.parse import urlparse\n",
"from IPython.display import display, HTML\n",
"from obi_auth import get_token\n",
"from obi_notebook import get_projects, get_entities\n",
"from entitysdk.client import Client\n",
"from entitysdk.models import CellMorphology\n",
"from entitysdk.models.asset import AssetLabel\n",
"\n",
"# Ultraliser imports and version check\n",
"import ultraliser\n",
"ultraliser.copyrights()\n",
"ultraliser.version()\n",
"ultraliser.build()"
]
},
{
"cell_type": "markdown",
"id": "edf94fbf",
"metadata": {},
"source": [
"## Selection of meshes and download\n",
"\n",
"The meshes will be downloaded, then loaded and processed by Ultraliser.\n",
"\n",
"### Project selection\n",
"As a first step we select one of the projects we have access to that the meshes are associated with. "
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "67c0e91c",
"metadata": {},
"outputs": [],
"source": [
"environment = \"staging\"\n",
"token = get_token(environment=environment, auth_mode=\"daf\")\n",
"project_context = get_projects.get_projects(token, env=\"staging\")"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use env=environment instead of env="staging"

]
},
{
"cell_type": "markdown",
"id": "1a98051d",
"metadata": {},
"source": [
"## Set up clients\n",
"\n",
"Next, we select the meshes. A widget with all the meshes with their IDs will appear. Simply select the mesh you are interested to skeletonize and proceed. Note that a single mesh can only be selected."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "5702b0a9",
"metadata": {},
"outputs": [],
"source": [
"# Initialize the client and search for EMCellMesh entities\n",
"client = Client(environment=environment, token_manager=token, project_context=project_context)\n",
"entitycore_api_url = urlparse(client.api_url)\n",
"platform_base_url = f\"{entitycore_api_url.scheme}://{entitycore_api_url.netloc}\"\n",
"mesh_api_base_url = f\"{platform_base_url}/api/small-scale-simulator/mesh/skeletonization\"\n",
"\n",
"http_client = httpx.Client()\n",
"\n",
"mesh_api_headers = httpx.Headers({\n",
" \"Authorization\": f\"Bearer {token}\",\n",
" \"virtual-lab-id\": str(project_context.virtual_lab_id),\n",
" \"project-id\": str(project_context.project_id)\n",
"})"
]
},
{
"cell_type": "markdown",
"id": "ed65fc07",
"metadata": {},
"source": [
"## Display EMCellMesh table"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please add some instructions here. Explain what is being displayed and what a user is expected to do. And how they can understand what each mesh means.

]
},
{
"cell_type": "code",
"execution_count": null,
"id": "9b833a25",
"metadata": {},
"outputs": [],
"source": [
"mesh_ids = []\n",
"mesh_ids = get_entities.get_entities(\n",
" 'em-cell-mesh', token, [], env=environment, project_context=project_context, \n",
" multi_select=False, page_size=20)"
]
},
{
"cell_type": "markdown",
"id": "f1e61ca1",
"metadata": {},
"source": [
"## Check the selection "
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "b8872782",
"metadata": {},
"outputs": [],
"source": [
"if len(mesh_ids) == 0:\n",
" display(HTML(\"<h3 style='color:#EDB95E'>⚠️ No mesh selected</h3>\"))\n",
" raise SystemExit()\n",
"mesh_id = mesh_ids[0]"
]
},
{
"cell_type": "markdown",
"id": "a58a8716",
"metadata": {},
"source": [
"## Prepare task params"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "160775d6",
"metadata": {},
"outputs": [],
"source": [
"input_params = {\n",
" \"name\": mesh_id,\n",
" \"description\": \"Reconstructed morphology from an EM surface mesh\"\n",
"}\n",
"\n",
"skeletonization_params = {\n",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For the skeletonization params:
I suggest using ipywidgets (maybe Slider, Checkbox) for the selection.
Not just because it is fancier, but also because this allows us to set a "reasonable" interval for the resolution parameters. Right now, I would not know what range of resolutions makes sense at all.

Also, with some callback functions the behavior can be such that the spine resolution is always better than the other resolution.

" \"em_cell_mesh_id\": mesh_id,\n",
" \"neuron_voxel_size\": 0.1, # in microns\n",
" \"spines_voxel_size\": 0.05, # in microns\n",
" \"segment_spines\": True\n",
"}"
]
},
{
"cell_type": "markdown",
"id": "820fbf5a",
"metadata": {},
"source": [
"## Submit mesh skeletonization task"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "b3b4348f",
"metadata": {},
"outputs": [],
"source": [
"start_res = http_client.post(\n",
" f\"{mesh_api_base_url}/run\",\n",
" params=skeletonization_params,\n",
" headers=mesh_api_headers,\n",
" json=input_params\n",
")\n",
"\n",
"job_id = None\n",
"\n",
"if start_res.is_success:\n",
" job_id = start_res.json().get(\"id\")\n",
"else:\n",
" print(start_res.text)\n",
" raise RuntimeError(\"Failed to submit mesh skeletonization task\")"
]
},
{
"cell_type": "markdown",
"id": "9fbfb97d",
"metadata": {},
"source": [
"## Wait for the job to complete"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "574b35f2",
"metadata": {},
"outputs": [],
"source": [
"output_morphology_id = None\n",
"prev_status = None\n",
"\n",
"while True:\n",
" status_res = http_client.get(\n",
" f\"{mesh_api_base_url}/jobs/{job_id}\",\n",
" headers=mesh_api_headers\n",
" )\n",
"\n",
" if not status_res.is_success:\n",
" print(status_res.text)\n",
" raise RuntimeError(\"Failed to get job status\")\n",
"\n",
" job = status_res.json()\n",
" status = job.get('status')\n",
"\n",
" if status != prev_status:\n",
" print(f\"{time.strftime(\"%H:%M:%S\", time.localtime())} Status: {status}\")\n",
" prev_status = status\n",
"\n",
" if status == 'finished':\n",
" output_morphology_id = UUID(job.get('output').get('morphology').get('id'))\n",
" break\n",
" elif status == 'failed':\n",
" print(json.dumps(job, indent=2))\n",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This can be formatted better than json.dumps

" raise RuntimeError(\"Skeletonization failed\")\n",
"\n",
" time.sleep(15)"
]
},
{
"cell_type": "markdown",
"id": "eabd5eaf",
"metadata": {},
"source": [
"## Get the resulting registered morphology "
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "5034ac2f",
"metadata": {},
"outputs": [],
"source": [
"cell_morphology = client.get_entity(output_morphology_id, entity_type=CellMorphology)\n",
"asset = next((asset for asset in cell_morphology.assets if asset.label == AssetLabel.morphology_with_spines), None)\n",
"\n",
"# Download the file\n",
"client.download_assets(cell_morphology,selection={\"label\": AssetLabel.morphology_with_spines}, output_path=pathlib.Path(os.getcwd())).one()"
]
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.12.3"
}
},
"nbformat": 4,
"nbformat_minor": 5
}
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,10 @@ The folder has been divided into 3 main subfolders, each corresponding to the le
- [**display_morphology_population_features**](Cellular/morphologies/display_morphology_population_features/analysis_notebook.ipynb): Plot morphological features for a population of morphologies
- [**morphology_quality_check**](Cellular/morphologies/morphology_quality_check/analysis_notebook.ipynb): To test a list of morphologies against some common issues that may cause issues with tools and analyses using the morphologies.

- **skeletonization**: Notebooks for skeletonization
- [**skeletonization**](Cellular/skeletonization/skeletonization.ipynb): Provide a single function that performs skeletonization of neuronal mesh models reconstructed with electron microscopes.
- [**skeletonization_utilities**](Cellular/skeletonization/skeletonization_utilities.ipynb): Provide a step-by-step skeletonization functionality from neuronal mesh models reconstructed with electron microscopes with several utilities for visualization and validation.

- **Circuit**
- [**adjacency_matrix**](Circuit/adjacency_matrix/analysis_notebook.ipynb): extracts and visualize the connectivity between all pairs of pre- and post-synaptic neurons
- [**circuit_composition**](Circuit/circuit_composition/analysis_notebook.ipynb): displays the composition of a circuit model with respect to user-selected neuron properties, such as morphological types, electrical types, etc.
Expand Down