Skip to content
Merged
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
40 changes: 2 additions & 38 deletions notebooks/01_getting_started/01_upload_data.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -326,43 +326,7 @@
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# Get some resources to add to a project\n",
"tutorial_resources = list(api.resources.get_list(\n",
" tags=['tutorial'],\n",
" status='inbox'\n",
"))\n",
"\n",
"if tutorial_resources:\n",
" resource_ids_for_project = [r.id for r in tutorial_resources[:3]] # Take first 3 resources\n",
"\n",
" # Create a new project\n",
" try:\n",
" project_id = api.projects.create(\n",
" name=\"Tutorial Project\",\n",
" description=\"A project created for demonstration purposes\",\n",
" resources_ids=resource_ids_for_project\n",
" )\n",
" project = api.projects.get_by_id(project_id)\n",
"\n",
" print(f\"Created project: {project.name} (ID: {project.id})\")\n",
"\n",
" # List all projects\n",
" all_projects = api.projects.get_list()\n",
" print(f\"\\nAll projects ({len(all_projects)}):\")\n",
" for proj in all_projects:\n",
" print(f\" - {proj.name} (ID: {proj.id})\")\n",
"\n",
" except Exception as e:\n",
" print(f\"Error creating project (may already exist): {e}\")\n",
"\n",
" # Try to find existing project\n",
" existing_project = api.projects.get_by_name(\"Tutorial Project\")\n",
" if existing_project:\n",
" print(f\"Found existing project: {existing_project.name}\")\n",
"else:\n",
" print(\"No tutorial resources found to add to project\")"
]
"source": "# Get some resources to add to a project\ntutorial_resources = list(api.resources.get_list(\n tags=['tutorial'],\n status='inbox'\n))\n\nif tutorial_resources:\n resource_ids_for_project = [r.id for r in tutorial_resources[:3]] # Take first 3 resources\n\n # Create a new project (exists_ok returns the existing project instead of raising)\n project = api.projects.create(\n name=\"Tutorial Project\",\n description=\"A project created for demonstration purposes\",\n resource_ids=resource_ids_for_project,\n exists_ok=True\n )\n\n print(f\"Created project: {project.name} (ID: {project.id})\")\n\n # List all projects\n all_projects = api.projects.get_list()\n print(f\"\\nAll projects ({len(all_projects)}):\")\n for proj in all_projects:\n print(f\" - {proj.name} (ID: {proj.id})\")\nelse:\n print(\"No tutorial resources found to add to project\")"
},
{
"cell_type": "markdown",
Expand Down Expand Up @@ -437,4 +401,4 @@
},
"nbformat": 4,
"nbformat_minor": 2
}
}
31 changes: 2 additions & 29 deletions notebooks/01_getting_started/02_explore_data.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -445,34 +445,7 @@
"id": "175ee2f9",
"metadata": {},
"outputs": [],
"source": [
"from datetime import date, timedelta\n",
"\n",
"# Filter by annotation type\n",
"category_annotations = api.annotations.get_list(\n",
" resource=selected_resource,\n",
" annotation_type='category'\n",
")\n",
"print(f\"Category annotations: {len(category_annotations)}\")\n",
"\n",
"# Filter by date range\n",
"date_to = date.today()\n",
"date_from = date_to - timedelta(days=30) # Last 30 days\n",
"\n",
"recent_annotations = api.annotations.get_list(\n",
" resource=selected_resource,\n",
" date_from=date_from,\n",
" date_to=date_to\n",
")\n",
"print(f\"Annotations from last 30 days: {len(recent_annotations)}\")\n",
"\n",
"# Filter by status\n",
"published_annotations = api.annotations.get_list(\n",
" resource=selected_resource,\n",
" status='published'\n",
")\n",
"print(f\"Published annotations: {len(published_annotations)}\")"
]
"source": "from datetime import date, timedelta\n\n# Filter by annotation type\ncategory_annotations = api.annotations.get_list(\n resource=selected_resource,\n annotation_type='category'\n)\nprint(f\"Category annotations: {len(category_annotations)}\")\n\n# Filter by date range\ndate_to = date.today()\ndate_from = date_to - timedelta(days=30) # Last 30 days\n\nrecent_annotations = api.annotations.get_list(\n resource=selected_resource,\n from_date=date_from,\n to_date=date_to\n)\nprint(f\"Annotations from last 30 days: {len(recent_annotations)}\")\n\n# Filter by status\npublished_annotations = api.annotations.get_list(\n resource=selected_resource,\n status='published'\n)\nprint(f\"Published annotations: {len(published_annotations)}\")"
},
{
"cell_type": "markdown",
Expand Down Expand Up @@ -516,4 +489,4 @@
},
"nbformat": 4,
"nbformat_minor": 5
}
}
37 changes: 6 additions & 31 deletions notebooks/02_annotations/01_upload_annotations.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -36,37 +36,21 @@
"id": "3f470fe9",
"metadata": {},
"outputs": [],
"source": [
"from datetime import date\n",
"from tqdm.auto import tqdm\n",
"from datamint import APIHandler\n",
"\n",
"api = APIHandler()\n",
"# resources = list(api.get_resources(project_name=\"Example Project\"))\n",
"# resource_id = resources[0][\"id\"]"
]
"source": "from datamint import Api\n\napi = Api()\n\nPROJECT_NAME = \"Example Project\"\nresources = list(api.resources.get_list(project_name=PROJECT_NAME))\nresource = resources[0]"
},
{
"cell_type": "code",
"execution_count": null,
"id": "cf9bf31f",
"metadata": {},
"outputs": [],
"source": [
"# Adds an annotation to a single resource, associated with a project.\n",
"api.add_annotations(resource_id=resource_id,\n",
" identifier='img-lb1',\n",
" project='Example Project' # You can pass None to use not associated with a project\n",
" )"
]
"source": "from datamint.api.dto import CreateAnnotationDto\nfrom datamint.entities.annotations import AnnotationType\n\n# Adds a label annotation to a single resource\nannotation = CreateAnnotationDto(type=AnnotationType.LABEL, identifier='img-lb1', scope='image')\napi.annotations.create(resource, annotation)\n\n# Associate the resource with a project. Resources aren't tied to a project\n# automatically anymore, so this is a separate, explicit step.\napi.projects.add_resources(resource, project=PROJECT_NAME)"
},
{
"cell_type": "markdown",
"id": "0db3307f",
"metadata": {},
"source": [
"See more details about the function parameter in the documentation [APIHandler.add_annotations](https://sonanceai.github.io/datamint-python-api/datamint.apihandler.html#datamint.apihandler.api_handler.APIHandler.add_annotations)."
]
"source": "See more details about the function parameters in the documentation [AnnotationsApi.create](https://sonanceai.github.io/datamint-python-api/datamint.api.endpoints.html#datamint.api.endpoints.annotations_api.AnnotationsApi.create)."
},
{
"cell_type": "markdown",
Expand All @@ -82,22 +66,13 @@
"id": "b2dc2320",
"metadata": {},
"outputs": [],
"source": [
"# Adds an annotation to a single resource, associated with a project.\n",
"api.add_image_category_annotation(resource_id,\n",
" identifier='img-cls',\n",
" value='cls1',\n",
" project='Example Project' # You can pass None to use not associated with a project\n",
" )"
]
"source": "# Adds an image classification annotation to a single resource\napi.annotations.create_image_classification(resource, identifier='img-cls', value='cls1')\n\n# Associate the resource with a project. Resources aren't tied to a project\n# automatically anymore, so this is a separate, explicit step.\napi.projects.add_resources(resource, project=PROJECT_NAME)"
},
{
"cell_type": "markdown",
"id": "b1746dae",
"metadata": {},
"source": [
"See more details about the function parameter in the documentation [APIHandler.add_image_category_annotation](https://sonanceai.github.io/datamint-python-api/datamint.apihandler.html#datamint.apihandler.api_handler.APIHandler.add_image_category_annotation)."
]
"source": "See more details about the function parameters in the documentation [AnnotationsApi.create_image_classification](https://sonanceai.github.io/datamint-python-api/datamint.api.endpoints.html#datamint.api.endpoints.annotations_api.AnnotationsApi.create_image_classification)."
}
],
"metadata": {
Expand All @@ -113,4 +88,4 @@
},
"nbformat": 4,
"nbformat_minor": 5
}
}
37 changes: 4 additions & 33 deletions notebooks/02_annotations/02_geometry_annotations.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -111,11 +111,7 @@
"cell_type": "markdown",
"id": "1ab6ebdc",
"metadata": {},
"source": [
"# Creating a Line annotation\n",
"\n",
"Reference: [APIHandler.add_line_annotation](https://sonanceai.github.io/datamint-python-api/datamint.apihandler.html#datamint.apihandler.api_handler.APIHandler.add_line_annotation)"
]
"source": "# Creating a Line annotation\n\nReference: [AnnotationsApi.add_line_annotation](https://sonanceai.github.io/datamint-python-api/datamint.api.endpoints.html#datamint.api.endpoints.annotations_api.AnnotationsApi.add_line_annotation)"
},
{
"cell_type": "code",
Expand Down Expand Up @@ -165,13 +161,7 @@
"cell_type": "markdown",
"id": "f8ba9aaf",
"metadata": {},
"source": [
"# Creating a Box annotation\n",
"\n",
"Reference: [APIHandler.add_box_annotation](https://sonanceai.github.io/datamint-python-api/datamint.apihandler.html#datamint.apihandler.api_handler.APIHandler.add_box_annotation)\n",
"\n",
"First, add a new box/rectangle shape named 'BoundingBox1' in your project settings (similar to the line shape setup above)."
]
"source": "# Creating a Box annotation\n\nReference: [AnnotationsApi.add_box_annotation](https://sonanceai.github.io/datamint-python-api/datamint.api.endpoints.html#datamint.api.endpoints.annotations_api.AnnotationsApi.add_box_annotation)\n\nFirst, add a new box/rectangle shape named 'BoundingBox1' in your project settings (similar to the line shape setup above)."
},
{
"cell_type": "code",
Expand All @@ -192,26 +182,7 @@
"cell_type": "markdown",
"id": "b0b73c10",
"metadata": {},
"source": [
"Both line and box annotations support:\n",
"- **DICOM coordinate conversion**: Automatically convert pixel coordinates to patient coordinates\n",
"- **Patient coordinates**: Direct specification of 3D patient coordinates\n",
"- **Model annotations**: Associate annotations with specific AI models\n",
"\n",
"### Example with DICOM metadata:\n",
"\n",
"```python\n",
"# Assuming you have DICOM metadata\n",
"dicom_path = \"/path/to/your/dicom/file.dcm\"\n",
"api.add_box_annotation((10, 10), (50, 50),\n",
" resource_id=res_id,\n",
" identifier='DicomBox',\n",
" frame_index=0,\n",
" dicom_metadata=dicom_path, # Automatically converts to patient coordinates\n",
" coords_system='pixel',\n",
" project=PROJECT_NAME)\n",
"```"
]
"source": "Both line and box annotations support:\n- **DICOM coordinate conversion**: Automatically convert pixel coordinates to patient coordinates\n- **Patient coordinates**: Direct specification of 3D patient coordinates\n- **Model annotations**: Associate annotations with specific AI models\n\n### Example with DICOM metadata:\n\n```python\n# Assuming you have DICOM metadata\nimport pydicom\n\ndicom_metadata = pydicom.dcmread(\"/path/to/your/dicom/file.dcm\")\napi.annotations.add_box_annotation((10, 10), (50, 50),\n resource=res,\n identifier='DicomBox',\n frame_index=0,\n metadata=dicom_metadata, # Required when coords_system='patient'\n coords_system='patient', # Automatically converts to patient coordinates\n worklist_id=proj.fetch_worklists()[0].id)\n```"
}
],
"metadata": {
Expand All @@ -235,4 +206,4 @@
},
"nbformat": 4,
"nbformat_minor": 5
}
}
11 changes: 2 additions & 9 deletions notebooks/04_experiment_tracking/02_model_registry.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -225,14 +225,7 @@
"cell_type": "markdown",
"id": "5edb693f",
"metadata": {},
"source": [
"## Next Steps",
"",
"Once you have a model version you're happy with, alias it and deploy it, see",
"`05_deployment/01_deploy_registered_model.ipynb`. Registered models are also created",
"automatically when you pass `--ai-model <name>` to `datamint-upload` with a name that doesn't",
"exist yet."
]
"source": "## Next Steps\n\nOnce you have a model version you're happy with, alias it and deploy it, see\n`05_deployment/01_deploy_registered_model.ipynb`. Registered models are also created\nautomatically when you pass `--ai-model <name>` to `datamint upload` with a name that doesn't\nexist yet."
}
],
"metadata": {
Expand All @@ -256,4 +249,4 @@
},
"nbformat": 4,
"nbformat_minor": 5
}
}
20 changes: 3 additions & 17 deletions notebooks/05_deployment/03_validate_model.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -65,25 +65,11 @@
},
{
"cell_type": "code",
"execution_count": 13,
"execution_count": null,
"id": "e5f6a7b8",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Resources : 364\n",
"Box labels: ['Platelets', 'RBC', 'WBC']\n"
]
}
],
"source": [
"dataset = build_dataset(project_name=PROJECT_NAME, allow_external_annotations=True)\n",
"\n",
"print(f\"Resources : {len(dataset.resources)}\")\n",
"print(f\"Box labels: {dataset.box_labels_set}\")"
]
"outputs": [],
"source": "dataset = build_dataset(project=PROJECT_NAME, allow_external_annotations=True)\n\nprint(f\"Resources : {len(dataset.resources)}\")\nprint(f\"Box labels: {dataset.box_labels_set}\")"
},
{
"cell_type": "markdown",
Expand Down
50 changes: 2 additions & 48 deletions notebooks/06_end_to_end/full_3d/02_synapse_nnunet.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -901,53 +901,7 @@
"id": "315e1ad6",
"metadata": {},
"outputs": [],
"source": [
"%matplotlib inline\n",
"import numpy as np\n",
"import matplotlib.pyplot as plt\n",
"\n",
"# Fetch CT volume\n",
"ct_nifti = r.fetch_file_data(use_cache=True, auto_convert=True)\n",
"ct_vol = ct_nifti.get_fdata()\n",
"\n",
"# Fetch the predicted segmentation (most recent annotation on this resource)\n",
"annotations = list(r.get_annotations())\n",
"pred_ann = annotations[-1]\n",
"pred_vol = pred_ann.fetch_file_data(auto_convert=True, use_cache=True)\n",
"\n",
"SYNAPSE_CLASSES = {\n",
" 1: 'aorta', 2: 'gallbladder', 3: 'spleen',\n",
" 4: 'left_kidney', 5: 'right_kidney', 6: 'liver',\n",
" 7: 'stomach', 8: 'pancreas',\n",
"}\n",
"num_classes = len(SYNAPSE_CLASSES)\n",
"CMAP = plt.get_cmap('tab10', num_classes + 1)\n",
"\n",
"# nnU-Net volumes are (H, W, D) — pick 3 axial slices\n",
"D = ct_vol.shape[2]\n",
"slice_indices = [D // 4, D // 2, 3 * D // 4]\n",
"\n",
"fig, axes = plt.subplots(len(slice_indices), 2, figsize=(10, 4 * len(slice_indices)))\n",
"fig.suptitle(f'nnU-Net inference result — {r.filename}', fontsize=13)\n",
"\n",
"for row, s in enumerate(slice_indices):\n",
" ct_slice = ct_vol[:, :, s]\n",
" pred_slice = pred_vol[:, :, s]\n",
"\n",
" axes[row, 0].imshow(ct_slice, cmap='gray')\n",
" axes[row, 0].set_title(f'CT — axial slice {s}')\n",
" axes[row, 0].axis('off')\n",
"\n",
" axes[row, 1].imshow(ct_slice, cmap='gray')\n",
" axes[row, 1].imshow(pred_slice, cmap=CMAP, alpha=0.5, vmin=0, vmax=num_classes)\n",
" axes[row, 1].set_title('nnU-Net Prediction')\n",
" axes[row, 1].axis('off')\n",
"\n",
"handles = [plt.Rectangle((0, 0), 1, 1, color=CMAP(i + 1)) for i in range(num_classes)]\n",
"fig.legend(handles, list(SYNAPSE_CLASSES.values()), loc='lower center', ncol=4, fontsize=9, title='Organ classes')\n",
"plt.tight_layout()\n",
"plt.show()"
]
"source": "%matplotlib inline\nimport numpy as np\nimport matplotlib.pyplot as plt\n\n# Fetch CT volume\nct_nifti = r.fetch_file_data(use_cache=True, auto_convert=True)\nct_vol = ct_nifti.get_fdata()\n\n# Fetch the predicted segmentation (most recent annotation on this resource)\nannotations = list(r.fetch_annotations())\npred_ann = annotations[-1]\npred_vol = pred_ann.fetch_file_data(auto_convert=True, use_cache=True)\n\nSYNAPSE_CLASSES = {\n 1: 'aorta', 2: 'gallbladder', 3: 'spleen',\n 4: 'left_kidney', 5: 'right_kidney', 6: 'liver',\n 7: 'stomach', 8: 'pancreas',\n}\nnum_classes = len(SYNAPSE_CLASSES)\nCMAP = plt.get_cmap('tab10', num_classes + 1)\n\n# nnU-Net volumes are (H, W, D) — pick 3 axial slices\nD = ct_vol.shape[2]\nslice_indices = [D // 4, D // 2, 3 * D // 4]\n\nfig, axes = plt.subplots(len(slice_indices), 2, figsize=(10, 4 * len(slice_indices)))\nfig.suptitle(f'nnU-Net inference result — {r.filename}', fontsize=13)\n\nfor row, s in enumerate(slice_indices):\n ct_slice = ct_vol[:, :, s]\n pred_slice = pred_vol[:, :, s]\n\n axes[row, 0].imshow(ct_slice, cmap='gray')\n axes[row, 0].set_title(f'CT — axial slice {s}')\n axes[row, 0].axis('off')\n\n axes[row, 1].imshow(ct_slice, cmap='gray')\n axes[row, 1].imshow(pred_slice, cmap=CMAP, alpha=0.5, vmin=0, vmax=num_classes)\n axes[row, 1].set_title('nnU-Net Prediction')\n axes[row, 1].axis('off')\n\nhandles = [plt.Rectangle((0, 0), 1, 1, color=CMAP(i + 1)) for i in range(num_classes)]\nfig.legend(handles, list(SYNAPSE_CLASSES.values()), loc='lower center', ncol=4, fontsize=9, title='Organ classes')\nplt.tight_layout()\nplt.show()"
}
],
"metadata": {
Expand All @@ -971,4 +925,4 @@
},
"nbformat": 4,
"nbformat_minor": 5
}
}
Loading