diff --git a/monai/monailabel_3dslicer_monaibundle_DICOM.ipynb b/monai/monailabel_3dslicer_monaibundle_DICOM.ipynb new file mode 100644 index 0000000..572aeff --- /dev/null +++ b/monai/monailabel_3dslicer_monaibundle_DICOM.ipynb @@ -0,0 +1,1124 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# MONAI Label and 3D Slicer (SlicerJupyter) DICOM Example - Whole Brain Segmentation\n", + "
\n", + "***As-Is Software Disclaimer***\n", + "\n", + "The content in this repository is delivered “As-Is”. Notwithstanding anything to the contrary, DNAnexus will have no warranty, support, liability or other obligations with respect to Materials provided hereunder.\n", + "\n", + "
\n", + "\n", + "This notebook provides a demonstration of auto-segmentation and model training on DICOM imaging data, utilizing MONAI Label and 3D Slicer (SlicerJupyter). \n", + "\n", + "Note: Data preprocessing may be required for optimal model performance, and the segmentation results shown here are for illustration and not guaranteed to be accurate.\n", + "\n", + "For general examples of using Slicer within Jupyter notebook, see: [Slicer/SlicerNotebooks](https://github.com/Slicer/SlicerNotebooks).\n", + "\n", + "For MONAI Label example notebooks and tutorials, see [Project-MONAI/tutorials/monailabel](https://github.com/Project-MONAI/tutorials/tree/main/monailabel).\n", + "\n", + "\n", + "MIT License applies to this notebook." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Preparing your environment\n", + "### Launch spec:\n", + "\n", + "* App name: JupyterLab with Python, R, Stata, ML, Image Processing\n", + "* App flavor: MONAI_ML\n", + "* Kernel: Slicer 5.8\n", + "* Instance type: mem2_ssd1_v2_x16\n", + "* Runtime: =~ 20 min + 10 min initialization (one sample)\n", + "* Data description: Input for this notebook is Brain MRI, T1 structural dataset in DICOM format. The dataset can be downloaded from https://biobank.ndph.ox.ac.uk/showcase/ukb/examples/eg_brain_t1.zip.\n", + " * Target: Brain\n", + " * Modality: MRI\n", + "\n", + "### Package and tools dependency:\n", + "\n", + "| Package | License | \n", + "| --- | --- |\n", + "| dcm2niix | BSD 3-Clause License |" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Imports" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "tags": [], + "trusted": true + }, + "outputs": [], + "source": [ + "import os\n", + "\n", + "import JupyterNotebooksLib as slicernb\n", + "import slicer\n", + "import vtk\n", + "from DICOMLib import DICOMUtils\n", + "from ipywidgets import Button, HBox, interact\n", + "from MONAILabel import MONAILabelLogic" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## 1. Prepare DICOM Data" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "tags": [], + "trusted": true + }, + "outputs": [], + "source": [ + "DICOM_DIR = os.path.join(os.getcwd(), \"eg_brain_t1\")\n", + "! export DICOM_DIR=\"${pwd}/eg_brain_t1\"" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Download data" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "tags": [], + "trusted": true + }, + "outputs": [], + "source": [ + "! wget -nd biobank.ndph.ox.ac.uk/ukb/ukb/examples/eg_brain_t1.zip" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "tags": [], + "trusted": true + }, + "outputs": [], + "source": [ + "! apt-get -qq update && apt-get install unzip\n", + "! unzip -qq eg_brain_t1.zip -d $DICOM_DIR" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "tags": [] + }, + "source": [ + "### Option 1: Convert to NIfTI\n", + "This is often the simplest approach for local DICOM directories. For example, use a tool like `dcm2niix` to convert your DICOM series into single NIfTI volumes, which MONAI Label can easily load. Start the server pointing to directory containing converted NIfTI files." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "trusted": true + }, + "outputs": [], + "source": [ + "! apt-get update && apt-get -y install dcm2niix" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "tags": [], + "trusted": true + }, + "outputs": [], + "source": [ + "! mkdir -p eg_brain_t1_nifti_1\n", + "! dcm2niix -o eg_brain_t1_nifti_1 $DICOM_DIR" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Option 2: Use the DICOM Module in 3D Slicer\n", + "Load your DICOM directory directly into the Slicer DICOM database and then either export entire DICOM database to research file format or use the MONAI Label module to upload the loaded volume to the server." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Load DICOM files into Slicer DICOM database" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "tags": [], + "trusted": true + }, + "outputs": [], + "source": [ + "# Switch to DICOM module\n", + "slicer.util.selectModule(\"DICOM\")\n", + "\n", + "# Instantiate a new DICOM browser\n", + "dicomBrowser = slicer.modules.DICOMWidget.browserWidget.dicomBrowser\n", + "\n", + "# Create database directory\n", + "dicomBrowser.createNewDatabaseDirectory()\n", + "\n", + "# Import directory containing DICOM files\n", + "dicomBrowser.importDirectory(DICOM_DIR)\n", + "\n", + "# Wait for import to finish before proceeding (optional, if removed then import runs in the background)\n", + "dicomBrowser.waitForImportFinished()\n", + "\n", + "print(f\"Database stored at: {slicer.dicomDatabase.databaseDirectory}\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Explore loaded data in the Slicer DICOM database" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "tags": [], + "trusted": true + }, + "outputs": [], + "source": [ + "db = slicer.dicomDatabase\n", + "\n", + "patientList = list(db.patients())\n", + "print(f\"Patients: {patientList}\")\n", + "\n", + "studyList = list(db.studiesForPatient(patientList[0]))\n", + "print(f\"Studies for patient {patientList[0]}: {studyList}\")\n", + "\n", + "seriesList = list(db.seriesForStudy(studyList[0]))\n", + "print(f\"Series for study {studyList[0]}: {seriesList}\")\n", + "\n", + "fileList = db.filesForSeries(seriesList[0])\n", + "print(f\"Number of files for series {seriesList[0]}: {len(fileList)}\")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "tags": [] + }, + "source": [ + "#### 2.a Export DICOM series from the database to research file format\n", + "Export the entire Slicer DICOM database content to nifti (or nrrd, etc.) file format with filtering of data type and naming of the output file based on DICOM tags. Start the server pointing to this directory." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "tags": [], + "trusted": true + }, + "outputs": [], + "source": [ + "outputFolder = os.path.join(os.getcwd(), \"eg_brain_t1_nifti_2/\")\n", + "\n", + "patientUIDs = slicer.dicomDatabase.patients()\n", + "for patientUID in patientUIDs:\n", + " loadedNodeIDs = DICOMUtils.loadPatientByUID(patientUID)\n", + " for loadedNodeID in loadedNodeIDs:\n", + " # Check if we want to save this node\n", + " node = slicer.mrmlScene.GetNodeByID(loadedNodeID)\n", + " # Only export images\n", + " if not node or not node.IsA(\"vtkMRMLScalarVolumeNode\"):\n", + " continue\n", + " # Construct filename\n", + " shNode = slicer.mrmlScene.GetSubjectHierarchyNode()\n", + " seriesItem = shNode.GetItemByDataNode(node)\n", + " studyItem = shNode.GetItemParent(seriesItem)\n", + " patientItem = shNode.GetItemParent(studyItem)\n", + " filename = shNode.GetItemAttribute(patientItem, \"DICOM.PatientID\")\n", + " filename += \"_\" + shNode.GetItemAttribute(studyItem, \"DICOM.StudyDate\")\n", + " filename += \"_\" + shNode.GetItemAttribute(seriesItem, \"DICOM.SeriesNumber\")\n", + " filename += \"_\" + shNode.GetItemAttribute(seriesItem, \"DICOM.Modality\")\n", + " filename = (\n", + " slicer.app.ioManager().forceFileNameValidCharacters(filename) + \".nii\"\n", + " )\n", + " # Save node\n", + " print(f\"Write {node.GetName()} to {filename}\")\n", + " success = slicer.util.saveNode(node, outputFolder + filename)\n", + " slicer.mrmlScene.Clear()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### 2.b Use the MONAI Label module to upload the loaded volume to the server\n", + "Start MONAI Label server with --studies pointing to an empty directory, where the volumes will be later uploaded." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "tags": [] + }, + "source": [ + "##### Load specific series to the Slicer scene" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "tags": [], + "trusted": true + }, + "outputs": [], + "source": [ + "seriesToLoad = seriesList[0]\n", + "print(f\"Loading series {seriesToLoad} into the Slicer scene\")\n", + "\n", + "loadedNodeIDs = DICOMUtils.loadSeriesByUID([seriesList[0]])\n", + "print(f\"Loaded node IDs: {loadedNodeIDs}\")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "tags": [] + }, + "source": [ + "##### Export loaded volume" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "tags": [], + "trusted": true + }, + "outputs": [], + "source": [ + "node = slicer.mrmlScene.GetNodeByID(loadedNodeIDs[0])\n", + "\n", + "image_id = \"image_1\"\n", + "image_path = os.path.join(os.getcwd(), \"image_1.nrrd\")\n", + "\n", + "slicer.util.saveNode(node, image_path)\n", + "print(f\"Volume saved locally to: {image_path}\")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "tags": [] + }, + "source": [ + "After you start MONAI Label server with empty directory and connect from Slicer notebook, you can upload exported volume to the server:\n", + "\n", + "```python\n", + "ml.upload_image(\n", + " image_in=image_path,\n", + " image_id=image_id\n", + ")\n", + "```\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## 2. Prepare MONAI Label server\n", + "Run following `monailabel` commands in Terminal or in a notebook with Python 3 kernel." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Download MONAI Bundle App\n", + "\n", + "```bash\n", + " monailabel apps --download --name monaibundle --output apps\n", + "```" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Start the MONAI Label server (with MONAI Bundle app)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "tags": [] + }, + "source": [ + "Start the server with MONAI Bundle App pointing to directory with sample data. The chosen `wholeBrainSeg_Large_UNEST_segmentation` model will be automatically downloaded upon starting the MONAI Label server. For the --studies flag, the command expects a path and will not execute correctly if we do not supply one. Even if you plan to upload volumes from Slicer, a directory is required. \n", + "\n", + "```bash\n", + " monailabel start_server --app apps/monaibundle --studies eg_brain_t1_nifti_2 --conf models wholeBrainSeg_Large_UNEST_segmentation \n", + "```" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## 3. Auto Segmentation with 3D Slicer (through SlicerJupyter)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Connect to MONAI Label server" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "tags": [], + "trusted": true + }, + "outputs": [], + "source": [ + "SERVER_URL = \"http://0.0.0.0:8000\"" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "tags": [], + "trusted": true + }, + "outputs": [], + "source": [ + "ml = MONAILabelLogic()\n", + "ml.setServer(SERVER_URL)\n", + "ml.server_url" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Get server/app information" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "tags": [], + "trusted": true + }, + "outputs": [], + "source": [ + "ml.info()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### ( For option 2.b: Upload volume to the server - server started with empty datastore )" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "tags": [], + "trusted": true + }, + "outputs": [], + "source": [ + "ml.upload_image(image_in=image_path, image_id=image_id)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Query datastore (cases/images on the server)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "tags": [], + "trusted": true + }, + "outputs": [], + "source": [ + "ml.datastore()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Load sample\n", + "\n", + "Load image data from study datastore." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "tags": [], + "trusted": true + }, + "outputs": [], + "source": [ + "sample = ml.next_sample(strategy=\"random\")\n", + "image_id = sample[\"id\"]\n", + "image_path = sample[\"path\"]\n", + "\n", + "print(sample)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Run Auto Segmentation\n", + "Do the inference on currently loaded data. The pre-trained segmentation model is downloaded upon starting MONAI Label server in the `model` folder, in this case the model is saved at `apps/monaibundle/model/wholeBrainSeg_Large_UNEST_segmentation/models/model.pt`." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "tags": [], + "trusted": true + }, + "outputs": [], + "source": [ + "label_file, params = ml.infer(\n", + " model=\"wholeBrainSeg_Large_UNEST_segmentation\", image_in=image_id\n", + ")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Save/submit label\n", + "\n", + "Save the ground truth label to the file system.\n", + "\n", + "The final annotations will be saved to `labels/final` folder in the study dataset, for example, in this use case, the ground truth label will be saved to `eg_brain_t1_nifti_2/labels/final`." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "tags": [], + "trusted": true + }, + "outputs": [], + "source": [ + "ml.save_label(image_id, label_file, params=params)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## 4. Visualize and edit annotations with 3D Slicer" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Set-up 3D Slicer scene" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Configure 3D Slicer views" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "tags": [], + "trusted": true + }, + "outputs": [], + "source": [ + "# Set image viewer size to 70%\n", + "slicernb.AppWindow.setWindowSize(scale=0.7)\n", + "# Hide patient information from slice view\n", + "slicernb.showSliceViewAnnotations(False)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Clear scene" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "tags": [], + "trusted": true + }, + "outputs": [], + "source": [ + "slicer.mrmlScene.Clear(False)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Load image and label into 3D Slicer" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "tags": [], + "trusted": true + }, + "outputs": [], + "source": [ + "# Load image and label\n", + "image_node = slicer.util.loadVolume(image_path)\n", + "label_node = slicer.util.loadLabelVolume(label_file)\n", + "\n", + "# Create segmentation node\n", + "segmentation_node = slicer.mrmlScene.AddNewNodeByClass(\"vtkMRMLSegmentationNode\")\n", + "slicer.modules.segmentations.logic().ImportLabelmapToSegmentationNode(\n", + " label_node, segmentation_node\n", + ")\n", + "# Set the segmentation geometry to match the image\n", + "segmentation_node.SetReferenceImageGeometryParameterFromVolumeNode(image_node)\n", + "# Generate a 3D surface model for visualization\n", + "segmentation_node.CreateClosedSurfaceRepresentation()\n", + "\n", + "# Set the label overlay on the slice viewers\n", + "slicer.util.setSliceViewerLayers(background=image_node, label=segmentation_node)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Visualize annotations" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Static views" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "tags": [], + "trusted": true + }, + "outputs": [], + "source": [ + "slicernb.ViewDisplay(\n", + " \"FourUp\"\n", + ") # other views: OneUpRedSlice, OneUpGreenSlice, OneUpYellowSlice, OneUp3D" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "tags": [], + "trusted": true + }, + "outputs": [], + "source": [ + "# Lightbox view\n", + "slicernb.ViewLightboxDisplay(\"Red\", rangeShrink=[45, 50])" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "tags": [] + }, + "source": [ + "#### (Optional) Update segment visibility (set to a specific segment)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "tags": [], + "trusted": true + }, + "outputs": [], + "source": [ + "segmentation = segmentation_node.GetSegmentation()\n", + "displayNode = segmentation_node.GetDisplayNode()\n", + "\n", + "# Define the target segment\n", + "targetSegmentName = \"14\" # Right-Cerebral-White-Matter, see apps/monaibundle/model/wholeBrainSeg_Large_UNEST_segmentation/docs/README.md\n", + "targetSegmentID = segmentation.GetSegmentIdBySegmentName(targetSegmentName)\n", + "\n", + "# Set segment visibility (making ONLY the target segment visible)\n", + "displayNode.SetAllSegmentsVisibility(False)\n", + "displayNode.SetSegmentVisibility(targetSegmentID, True)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "##### Reset visibility to all segments" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "tags": [], + "trusted": true + }, + "outputs": [], + "source": [ + "segmentation = segmentation_node.GetSegmentation()\n", + "displayNode = segmentation_node.GetDisplayNode()\n", + "\n", + "displayNode.SetAllSegmentsVisibility(True)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Dynamic views - Level 1. View objects + standard widgets\n", + "- Displayed content is saved in the notebook\n", + "- Views cannot be placed in a layout\n", + "- Low update rate (only for small adjustment of view parameters)\n", + "- Mouse and keyboard events are not captured" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "tags": [], + "trusted": true + }, + "outputs": [], + "source": [ + "# Slice view display\n", + "@interact(position=(0, 100))\n", + "def update(position=50):\n", + " return slicernb.ViewSliceDisplay(\"Red\", positionPercent=position)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Dynamic views - Level 2. View widgets\n", + "- Widgets can be placed in a layout\n", + "- Widget state (displayed content) is not saved in the notebook by default\n", + "- Low update rate (only for small adjustment of view parameters)\n", + "- Mouse and keyboard events are not captured" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "tags": [], + "trusted": true + }, + "outputs": [], + "source": [ + "# Slice widgets\n", + "slicernb.ViewSliceWidget(\"Red\")\n", + "display(\n", + " HBox(\n", + " [\n", + " slicernb.ViewSliceWidget(\"Red\"),\n", + " slicernb.ViewSliceWidget(\"Yellow\"),\n", + " slicernb.ViewSliceWidget(\"Green\"),\n", + " ]\n", + " )\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "tags": [], + "trusted": true + }, + "outputs": [], + "source": [ + "# 3D widget\n", + "display(\n", + " HBox(\n", + " [\n", + " slicernb.ViewSliceBaseWidget(\"Red\", width=\"40%\"),\n", + " slicernb.View3DWidget(0, width=\"40%\"),\n", + " ]\n", + " )\n", + ")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Dynamic views - Level 3. Interactive view widgets\n", + "- Some view controlling mouse and keyboard events are captured\n", + "- Only selected view can be displayed and controlled\n", + "- Medium update rate (somewhat usable on remote computers)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Note:** Run cells with interactive widgets individually. Batch execution (\"Run All Cells\") is discouraged, as the cells may run indefinitely and cause execution interference." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "tags": [], + "trusted": true + }, + "outputs": [], + "source": [ + "# Adjust maximum rate of Slicer's Jupyter kernel consuming Jupyter messages.\n", + "# Lower values make the notebook more responsive but too low values may make the Slicer application\n", + "# slow to respond.\n", + "slicer.modules.jupyterkernel.setPollIntervalSec(0.001)\n", + "\n", + "# Add image 3D display\n", + "# slicernb.showVolumeRendering(image_node)\n", + "\n", + "# 3D view\n", + "slicernb.AppWindow.setWindowSize(scale=0.8)\n", + "live3d = slicernb.ViewInteractiveWidget(\"1\")\n", + "live3d.trackMouseMove = False\n", + "display(live3d)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "tags": [], + "trusted": true + }, + "outputs": [], + "source": [ + "# Slice view (use arrow keys to move between slices, right-click-and-drag to zoom in/out)\n", + "liveRedSlice = slicernb.ViewInteractiveWidget(\"R\")\n", + "liveRedSlice.trackMouseMove = False\n", + "display(liveRedSlice)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Edit Annotations" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "tags": [], + "trusted": true + }, + "outputs": [], + "source": [ + "# Setup Segment Editor\n", + "slicer.util.selectModule(\"SegmentEditor\")\n", + "segmentEditorWidget = slicer.modules.segmenteditor.widgetRepresentation().self().editor\n", + "segmentEditorNode = segmentEditorWidget.mrmlSegmentEditorNode()\n", + "segmentEditorWidget.setSegmentationNode(segmentation_node)\n", + "segmentEditorWidget.setSourceVolumeNode(image_node)\n", + "\n", + "segmentation = segmentation_node.GetSegmentation()\n", + "displayNode = segmentation_node.GetDisplayNode()\n", + "\n", + "# Define the target segment\n", + "targetSegmentName = \"14\" # Right-Cerebral-White-Matter, see apps/monaibundle/model/wholeBrainSeg_Large_UNEST_segmentation/docs/README.md\n", + "targetSegmentID = segmentation.GetSegmentIdBySegmentName(targetSegmentName)\n", + "\n", + "# Set segment visibility (making ONLY the target segment visible)\n", + "displayNode.SetAllSegmentsVisibility(False)\n", + "displayNode.SetSegmentVisibility(targetSegmentID, True)\n", + "\n", + "# Set the Active Segment for Editing\n", + "segmentEditorNode.SetSelectedSegmentID(targetSegmentID)\n", + "\n", + "# Configure viewer\n", + "app = slicernb.AppWindow(contents=\"viewers\", windowScale=0.3)\n", + "slicernb.setViewLayout(\n", + " \"OneUpRedSlice\"\n", + ") # other views: OneUpYellowSlice, OneUpGreenSlice, OneUp3D\n", + "app.setWindowSize(scale=0.5)\n", + "app.setContents(\"viewers\")\n", + "\n", + "# Create an interactive widget for the Red slice view (\"R\")\n", + "liveRedSliceSeg = slicernb.ViewInteractiveWidget(\n", + " \"R\"\n", + ") # other interactive widgets: Y, G, 1 (3D view)\n", + "liveRedSliceSeg.trackMouseMove = False\n", + "display(liveRedSliceSeg)\n", + "\n", + "# Add buttons to switch between multiple effects\n", + "cutButton = Button(description=\"Cut\")\n", + "cutButton.on_click(\n", + " lambda button: segmentEditorWidget.setActiveEffectByName(\"Scissors\")\n", + ") # 2D, 3D view\n", + "paintButton = Button(description=\"Paint\")\n", + "paintButton.on_click(\n", + " lambda button: segmentEditorWidget.setActiveEffectByName(\"Paint\")\n", + ") # 2D view\n", + "eraseButton = Button(description=\"Erase\")\n", + "eraseButton.on_click(\n", + " lambda button: segmentEditorWidget.setActiveEffectByName(\"Erase\")\n", + ") # 2D view\n", + "HBox([cutButton, paintButton, eraseButton])" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Edit Annotations in 3D view" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "slicernb.setViewLayout(\"OneUp3D\")\n", + "\n", + "# Create an interactive widget for the 3D view (\"1\")\n", + "live3dSeg = slicernb.ViewInteractiveWidget(\"1\")\n", + "live3dSeg.trackMouseMove = False\n", + "display(live3dSeg)\n", + "\n", + "# Add buttons to switch between multiple effects\n", + "cutButton = Button(description=\"Cut\")\n", + "cutButton.on_click(lambda button: segmentEditorWidget.setActiveEffectByName(\"Scissors\"))\n", + "rotateButton = Button(description=\"Rotate\")\n", + "rotateButton.on_click(lambda button: segmentEditorWidget.setActiveEffectByName(\"\"))\n", + "HBox([cutButton, rotateButton])" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Save/submit edited label" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "tags": [], + "trusted": true + }, + "outputs": [], + "source": [ + "labelmapVolumeNode = slicer.mrmlScene.AddNewNodeByClass(\"vtkMRMLLabelMapVolumeNode\")\n", + "slicer.modules.segmentations.logic().ExportVisibleSegmentsToLabelmapNode(\n", + " segmentation_node, labelmapVolumeNode, image_node\n", + ")\n", + "slicer.util.saveNode(labelmapVolumeNode, label_file)\n", + "\n", + "ml.save_label(image_id, label_file, params=params)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## 5. Active Learning\n", + "Train models anytime when new annotated labels are saved. MONAI Label server will fetch saved final ground truth label and fine-tune the prior model.\n", + "\n", + "For each training loop, the new best metric model will be saved in `/models/model.pt`, in this use case, `apps/monaibundle/model/wholeBrainSeg_Large_UNEST_segmentation/models/model.pt` is saved." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Run Train Task" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "tags": [], + "trusted": true + }, + "outputs": [], + "source": [ + "ml.train_start(\n", + " model=\"wholeBrainSeg_Large_UNEST_segmentation\",\n", + " params={\"max_epochs\": 20, \"run_id\": \"training\"},\n", + ")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Check Train Task Status" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "trusted": true + }, + "outputs": [], + "source": [ + "ml.train_status(check_if_running=False)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Stop any running Train Task(s)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "tags": [], + "trusted": true + }, + "outputs": [], + "source": [ + "ml.train_stop()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## 6. Repeat the Interactive Labeling Process\n", + "Repeat the above process of fetching data and active learning until all unlabeled data are annotated and trained.\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## 7. Upload Data to the Project\n", + "For any data generated by your notebook that needs to be preserved, upload it to the project before the session ends and the JupyterLab worker terminates. " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Upload the labels\n", + "! dx upload -r eg_brain_t1_nifti_2/labels" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Upload updated model\n", + "! dx upload apps/monaibundle/model/wholeBrainSeg_Large_UNEST_segmentation/models/model.pt" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Slicer 5.8", + "language": "python", + "name": "slicer-5.8" + }, + "language_info": { + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "version": "3.9.10" + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/monai/monailabel_3dslicer_radiology.ipynb b/monai/monailabel_3dslicer_radiology.ipynb new file mode 100644 index 0000000..f40ddc1 --- /dev/null +++ b/monai/monailabel_3dslicer_radiology.ipynb @@ -0,0 +1,755 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# MONAI Label - Radiology App and 3D Slicer (SlicerJupyter) Example\n", + "
\n", + "***As-Is Software Disclaimer***\n", + "\n", + "The content in this repository is delivered “As-Is”. Notwithstanding anything to the contrary, DNAnexus will have no warranty, support, liability or other obligations with respect to Materials provided hereunder.\n", + "\n", + "
\n", + "\n", + "This notebook provides a demonstration of auto-segmentation and model training on NIfTI imaging data, utilizing MONAI Label and 3D Slicer (SlicerJupyter).\n", + "\n", + "For general examples of using Slicer within Jupyter notebook, see: [Slicer/SlicerNotebooks](https://github.com/Slicer/SlicerNotebooks).\n", + "\n", + "For MONAI Label example notebooks and tutorials, see [Project-MONAI/tutorials/monailabel](https://github.com/Project-MONAI/tutorials/tree/main/monailabel).\n", + "\n", + "MIT License applies to this notebook.\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Preparing your environment\n", + "### Launch spec:\n", + "\n", + "* App name: JupyterLab with Python, R, Stata, ML, Image Processing\n", + "* App flavor: MONAI_ML\n", + "* Kernel: Slicer 5.8\n", + "* Instance type: mem2_ssd1_gpu_x16\n", + "* Runtime: =~ 15 min + 10 min initialization (one sample)\n", + "* Data description: Input for this notebook is MSD Spleen dataset. The dataset can be downloaded from http://medicaldecathlon.com/.\n", + " * Target: Spleen\n", + " * Modality: CT\n", + " * Size: 61 3D volumes (41 Training + 20 Testing)\n", + " * Source: Memorial Sloan Kettering Cancer Center" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## 1. Prepare MONAI Label server\n", + "Run following `monailabel` commands in Terminal or in a notebook with Python 3 kernel." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Download Radiology App\n", + "\n", + "```bash\n", + " monailabel apps --download --name radiology --output apps\n", + "```" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Download data\n", + "\n", + "```bash\n", + " monailabel datasets --download --name Task09_Spleen --output datasets\n", + "```" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Start the MONAI Label server (with Radiology App)\n", + "\n", + "Start the server with Radiology App pointing to directory with sample data. The chosen `segmentation_spleen` model will be automatically downloaded upon starting the MONAI Label server.\n", + "\n", + "```bash\n", + " monailabel start_server --app apps/radiology --studies datasets/Task09_Spleen/imagesTr --conf models segmentation_spleen \n", + "```" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## 2. Auto Segmentation with 3D Slicer (through SlicerJupyter)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Imports" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "tags": [], + "trusted": true + }, + "outputs": [], + "source": [ + "import JupyterNotebooksLib as slicernb\n", + "import slicer\n", + "import vtk\n", + "from ipywidgets import Button, HBox, interact\n", + "from MONAILabel import MONAILabelLogic" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Connect to MONAI Label server" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "tags": [], + "trusted": true + }, + "outputs": [], + "source": [ + "SERVER_URL = \"http://0.0.0.0:8000\"" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "tags": [], + "trusted": true + }, + "outputs": [], + "source": [ + "ml = MONAILabelLogic()\n", + "ml.setServer(SERVER_URL)\n", + "ml.server_url" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Get server/app information" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "tags": [], + "trusted": true + }, + "outputs": [], + "source": [ + "ml.info()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Query datastore (cases/images on the server)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "tags": [], + "trusted": true + }, + "outputs": [], + "source": [ + "ml.datastore()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Load sample\n", + "\n", + "Load image data from study datastore." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "tags": [], + "trusted": true + }, + "outputs": [], + "source": [ + "sample = ml.next_sample(strategy=\"random\")\n", + "image_id = sample[\"id\"]\n", + "image_path = sample[\"path\"]\n", + "\n", + "print(sample)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Run Auto Segmentation\n", + "Do the inference on currently loaded data. The pre-trained segmentation model is downloaded upon starting MONAI Label server in the `model` folder, in this case the model is saved at `apps/radiology/model/pretrained_segmentation_spleen.pt`." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "tags": [], + "trusted": true + }, + "outputs": [], + "source": [ + "label_file, params = ml.infer(model=\"segmentation_spleen\", image_in=image_id)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Save/submit label\n", + "\n", + "Save the ground truth label to the file system.\n", + "\n", + "The final annotations will be saved to `labels/final` folder in the study dataset, in this use case, the ground truth label will be saved to `datasets/Task09_Spleen/imagesTr/labels/final`." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "tags": [], + "trusted": true + }, + "outputs": [], + "source": [ + "ml.save_label(image_id, label_file, params=params)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## 3. Visualize and edit annotations with 3D Slicer" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Set-up 3D Slicer scene" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Configure 3D Slicer views" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "tags": [], + "trusted": true + }, + "outputs": [], + "source": [ + "# Set image viewer size to 70%\n", + "slicernb.AppWindow.setWindowSize(scale=0.7)\n", + "# Hide patient information from slice view\n", + "slicernb.showSliceViewAnnotations(False)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Clear scene" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "tags": [], + "trusted": true + }, + "outputs": [], + "source": [ + "slicer.mrmlScene.Clear(False)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Load image and label into 3D Slicer" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "tags": [], + "trusted": true + }, + "outputs": [], + "source": [ + "# Load image and label\n", + "image_node = slicer.util.loadVolume(image_path)\n", + "label_node = slicer.util.loadLabelVolume(label_file)\n", + "\n", + "# Create segmentation node\n", + "segmentation_node = slicer.mrmlScene.AddNewNodeByClass(\"vtkMRMLSegmentationNode\")\n", + "slicer.modules.segmentations.logic().ImportLabelmapToSegmentationNode(\n", + " label_node, segmentation_node\n", + ")\n", + "# Set the segmentation geometry to match the image\n", + "segmentation_node.SetReferenceImageGeometryParameterFromVolumeNode(image_node)\n", + "# Generate a 3D surface model for visualization\n", + "segmentation_node.CreateClosedSurfaceRepresentation()\n", + "\n", + "# Set the label overlay on the slice viewers\n", + "slicer.util.setSliceViewerLayers(background=image_node, label=segmentation_node)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Visualize annotations" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Static views" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "tags": [], + "trusted": true + }, + "outputs": [], + "source": [ + "slicernb.ViewDisplay(\n", + " \"FourUp\"\n", + ") # other views: OneUpRedSlice, OneUpGreenSlice, OneUpYellowSlice, OneUp3D" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "tags": [], + "trusted": true + }, + "outputs": [], + "source": [ + "# Lightbox view\n", + "slicernb.ViewLightboxDisplay(\"Red\", rangeShrink=[45, 50])" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Dynamic views - Level 1. View objects + standard widgets\n", + "- Displayed content is saved in the notebook\n", + "- Views cannot be placed in a layout\n", + "- Low update rate (only for small adjustment of view parameters)\n", + "- Mouse and keyboard events are not captured" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "tags": [], + "trusted": true + }, + "outputs": [], + "source": [ + "# Slice view display\n", + "@interact(position=(0, 100))\n", + "def update(position=50):\n", + " return slicernb.ViewSliceDisplay(\"Red\", positionPercent=position)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Dynamic views - Level 2. View widgets\n", + "- Widgets can be placed in a layout\n", + "- Widget state (displayed content) is not saved in the notebook by default\n", + "- Low update rate (only for small adjustment of view parameters)\n", + "- Mouse and keyboard events are not captured" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "tags": [], + "trusted": true + }, + "outputs": [], + "source": [ + "# Slice widgets\n", + "slicernb.ViewSliceWidget(\"Red\")\n", + "display(\n", + " HBox(\n", + " [\n", + " slicernb.ViewSliceWidget(\"Red\"),\n", + " slicernb.ViewSliceWidget(\"Yellow\"),\n", + " slicernb.ViewSliceWidget(\"Green\"),\n", + " ]\n", + " )\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "tags": [], + "trusted": true + }, + "outputs": [], + "source": [ + "# 3D widget\n", + "display(\n", + " HBox(\n", + " [\n", + " slicernb.ViewSliceBaseWidget(\"Red\", width=\"40%\"),\n", + " slicernb.View3DWidget(0, width=\"40%\"),\n", + " ]\n", + " )\n", + ")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Dynamic views - Level 3. Interactive view widgets\n", + "- Some view controlling mouse and keyboard events are captured\n", + "- Only selected view can be displayed and controlled\n", + "- Medium update rate (somewhat usable on remote computers)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Note:** Run cells with interactive widgets individually. Batch execution (\"Run All Cells\") is discouraged, as the cells may run indefinitely and cause execution interference." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "tags": [], + "trusted": true + }, + "outputs": [], + "source": [ + "# Adjust maximum rate of Slicer's Jupyter kernel consuming Jupyter messages.\n", + "# Lower values make the notebook more responsive but too low values may make the Slicer application\n", + "# slow to respond.\n", + "slicer.modules.jupyterkernel.setPollIntervalSec(0.001)\n", + "\n", + "# Add image 3D display\n", + "# slicernb.showVolumeRendering(image_node)\n", + "\n", + "# 3D view\n", + "slicernb.AppWindow.setWindowSize(scale=0.8)\n", + "live3d = slicernb.ViewInteractiveWidget(\"1\")\n", + "live3d.trackMouseMove = False\n", + "display(live3d)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "tags": [], + "trusted": true + }, + "outputs": [], + "source": [ + "# Slice view (use arrow keys to move between slices, right-click-and-drag to zoom in/out)\n", + "liveRedSlice = slicernb.ViewInteractiveWidget(\"R\")\n", + "liveRedSlice.trackMouseMove = False\n", + "display(liveRedSlice)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Edit Annotations" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "tags": [], + "trusted": true + }, + "outputs": [], + "source": [ + "# Setup Segment Editor\n", + "slicer.util.selectModule(\"SegmentEditor\")\n", + "segmentEditorWidget = slicer.modules.segmenteditor.widgetRepresentation().self().editor\n", + "segmentEditorNode = segmentEditorWidget.mrmlSegmentEditorNode()\n", + "segmentEditorWidget.setSegmentationNode(segmentation_node)\n", + "segmentEditorWidget.setSourceVolumeNode(image_node)\n", + "\n", + "app = slicernb.AppWindow(contents=\"viewers\", windowScale=0.3)\n", + "slicernb.setViewLayout(\n", + " \"OneUpRedSlice\"\n", + ") # other views: OneUpYellowSlice, OneUpGreenSlice, OneUp3D\n", + "app.setWindowSize(scale=0.5)\n", + "app.setContents(\"viewers\")\n", + "\n", + "# Create an interactive widget for the Red slice view (\"R\")\n", + "liveRedSliceSeg = slicernb.ViewInteractiveWidget(\n", + " \"R\"\n", + ") # other interactive widgets: Y, G, 1 (3D view)\n", + "liveRedSliceSeg.trackMouseMove = False\n", + "display(liveRedSliceSeg)\n", + "\n", + "# Add buttons to switch between multiple effects\n", + "cutButton = Button(description=\"Cut\")\n", + "cutButton.on_click(\n", + " lambda button: segmentEditorWidget.setActiveEffectByName(\"Scissors\")\n", + ") # 2D, 3D view\n", + "paintButton = Button(description=\"Paint\")\n", + "paintButton.on_click(\n", + " lambda button: segmentEditorWidget.setActiveEffectByName(\"Paint\")\n", + ") # 2D view\n", + "eraseButton = Button(description=\"Erase\")\n", + "eraseButton.on_click(\n", + " lambda button: segmentEditorWidget.setActiveEffectByName(\"Erase\")\n", + ") # 2D view\n", + "HBox([cutButton, paintButton, eraseButton])" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Edit Annotations in 3D view" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "tags": [], + "trusted": true + }, + "outputs": [], + "source": [ + "slicernb.setViewLayout(\"OneUp3D\")\n", + "\n", + "# Create an interactive widget for the 3D view (\"1\")\n", + "live3dSeg = slicernb.ViewInteractiveWidget(\"1\")\n", + "live3dSeg.trackMouseMove = False\n", + "display(live3dSeg)\n", + "\n", + "# Add buttons to switch between multiple effects\n", + "cutButton = Button(description=\"Cut\")\n", + "cutButton.on_click(lambda button: segmentEditorWidget.setActiveEffectByName(\"Scissors\"))\n", + "rotateButton = Button(description=\"Rotate\")\n", + "rotateButton.on_click(lambda button: segmentEditorWidget.setActiveEffectByName(\"\"))\n", + "HBox([cutButton, rotateButton])" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Save/submit edited label" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "tags": [], + "trusted": true + }, + "outputs": [], + "source": [ + "labelmapVolumeNode = slicer.mrmlScene.AddNewNodeByClass(\"vtkMRMLLabelMapVolumeNode\")\n", + "slicer.modules.segmentations.logic().ExportVisibleSegmentsToLabelmapNode(\n", + " segmentation_node, labelmapVolumeNode, image_node\n", + ")\n", + "slicer.util.saveNode(labelmapVolumeNode, label_file)\n", + "\n", + "ml.save_label(image_id, label_file, params=params)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## 4. Active Learning\n", + "Train models anytime when new annotated labels are saved. MONAI Label server will fetch saved final ground truth label and fine-tune the prior model.\n", + "\n", + "For each training loop, the new best metric model will be saved in `model/.pt`, in this use case, `apps/radiology/model/segmentation_spleen.pt` is saved. For detailed training stats and step models, refer to `apps/radiology/model/segmentation_spleen/train_01`.\n", + "\n", + "Note: runs only on GPU " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Run Train Task" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "tags": [], + "trusted": true + }, + "outputs": [], + "source": [ + "ml.train_start(model=\"segmentation_spleen\", params={\"max_epochs\": 20})" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Check Train Task Status" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "trusted": true + }, + "outputs": [], + "source": [ + "ml.train_status(check_if_running=False)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Stop any running Train Task(s)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "tags": [], + "trusted": true + }, + "outputs": [], + "source": [ + "ml.train_stop()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## 5. Repeat the Interactive Labeling Process\n", + "Repeat the above process of fetching data and active learning until all unlabeled data are annotated and trained.\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## 6. Upload Data to the Project\n", + "For any data generated by your notebook that needs to be preserved, upload it to the project before the session ends and the JupyterLab worker terminates. " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "tags": [], + "trusted": true + }, + "outputs": [], + "source": [ + "# Upload the labels\n", + "! dx upload -r datasets/Task09_Spleen/imagesTr/labels" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "tags": [], + "trusted": true + }, + "outputs": [], + "source": [ + "# Upload updated model\n", + "! dx upload apps/radiology/model/segmentation_spleen.pt" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Slicer 5.8", + "language": "python", + "name": "slicer-5.8" + }, + "language_info": { + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "version": "3.9.10" + } + }, + "nbformat": 4, + "nbformat_minor": 4 +}