diff --git a/README.md b/README.md index 2cb8b12c31..759d04c979 100644 --- a/README.md +++ b/README.md @@ -31,6 +31,8 @@ This notebook compares the performance of `Dataset`, `CacheDataset` and `Persist #### [integrate_3rd_party_transforms](./integrate_3rd_party_transforms.ipynb) This tutorial shows how to integrate 3rd party transforms into MONAI program. Mainly shows transforms from BatchGenerator, TorchIO, Rising and ITK. +#### [load_medical_imagesl](./load_medical_images.ipynb) +This notebook introduces how to easily load different formats of medical images in MONAI and execute many additional operations. #### [mednist_GAN_tutorial](./mednist_GAN_tutorial.ipynb) This notebook illustrates the use of MONAI for training a network to generate images from a random input tensor. A simple GAN is employed to do with a separate Generator and Discriminator networks. diff --git a/automatic_mixed_precision.ipynb b/automatic_mixed_precision.ipynb index 21cfcd8d18..2d37ca94e5 100644 --- a/automatic_mixed_precision.ipynb +++ b/automatic_mixed_precision.ipynb @@ -673,6 +673,25 @@ "plt.legend(loc='best')\n", "plt.show()" ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Cleanup data directory\n", + "\n", + "Remove directory if a temporary was used." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "if directory is None:\n", + " shutil.rmtree(root_dir)" + ] } ], "metadata": { diff --git a/brats_segmentation_3d.ipynb b/brats_segmentation_3d.ipynb index 292534a1d0..60f6c686e3 100644 --- a/brats_segmentation_3d.ipynb +++ b/brats_segmentation_3d.ipynb @@ -727,7 +727,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.6.9" + "version": "3.6.10" } }, "nbformat": 4, diff --git a/load_medical_images.ipynb b/load_medical_images.ipynb new file mode 100644 index 0000000000..2db2667b7a --- /dev/null +++ b/load_medical_images.ipynb @@ -0,0 +1,415 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Load medical images\n", + "\n", + "This notebook introduces how to easily load different formats of medical images in MONAI and execute many additional operations.\n", + "\n", + "[![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/Project-MONAI/Tutorials/blob/master/load_medical_images.ipynb)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Setup environment" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": { + "tags": [] + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Note: you may need to restart the kernel to use updated packages.\n" + ] + } + ], + "source": [ + "%pip install -qU \"monai[itk, nibabel, pillow]\"" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": { + "tags": [] + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Note: you may need to restart the kernel to use updated packages.\n" + ] + } + ], + "source": [ + "# temporarily need this, FIXME: remove when MONAI v0.3 released\n", + "%pip install -qU git+https://github.com/Project-MONAI/MONAI#egg=MONAI" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Setup imports" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": { + "tags": [] + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "MONAI version: 0.2.0\n", + "Python version: 3.6.9 |Anaconda, Inc.| (default, Jul 30 2019, 19:07:31) [GCC 7.3.0]\n", + "Numpy version: 1.18.1\n", + "Pytorch version: 1.6.0\n", + "\n", + "Optional dependencies:\n", + "Pytorch Ignite version: NOT INSTALLED or UNKNOWN VERSION.\n", + "Nibabel version: 3.1.1\n", + "scikit-image version: 0.15.0\n", + "Pillow version: 7.2.0\n", + "Tensorboard version: 2.1.0\n", + "\n", + "For details about installing the optional dependencies, please visit:\n", + " https://docs.monai.io/en/latest/installation.html#installing-the-recommended-dependencies\n", + "\n" + ] + } + ], + "source": [ + "# Copyright 2020 MONAI Consortium\n", + "# Licensed under the Apache License, Version 2.0 (the \"License\");\n", + "# you may not use this file except in compliance with the License.\n", + "# You may obtain a copy of the License at\n", + "# http://www.apache.org/licenses/LICENSE-2.0\n", + "# Unless required by applicable law or agreed to in writing, software\n", + "# distributed under the License is distributed on an \"AS IS\" BASIS,\n", + "# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n", + "# See the License for the specific language governing permissions and\n", + "# limitations under the License.\n", + "\n", + "# Copyright 2020 MONAI Consortium\n", + "# Licensed under the Apache License, Version 2.0 (the \"License\");\n", + "# you may not use this file except in compliance with the License.\n", + "# You may obtain a copy of the License at\n", + "# http://www.apache.org/licenses/LICENSE-2.0\n", + "# Unless required by applicable law or agreed to in writing, software\n", + "# distributed under the License is distributed on an \"AS IS\" BASIS,\n", + "# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n", + "# See the License for the specific language governing permissions and\n", + "# limitations under the License.\n", + "\n", + "import os\n", + "import shutil\n", + "import numpy as np\n", + "import itk\n", + "from PIL import Image\n", + "import tempfile\n", + "from monai.data import ITKReader, NibabelReader, PILReader\n", + "from monai.transforms import LoadImage, LoadImaged, AddChanneld, Resized, ToTensord, Compose\n", + "from monai.config import print_config\n", + "\n", + "print_config()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Load image with default image reader\n", + "MONAI leverages `ITK` as the default image reader, it can support most of the common medical image formats.\n", + "More details, please check: https://github.com/InsightSoftwareConsortium/ITK/tree/master/Modules/IO" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": { + "tags": [] + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "image data shape:(128, 128, 128)\n", + "meta data:{'ITK_FileNotes': '', 'aux_file': '', 'bitpix': '64', 'cal_max': '0', 'cal_min': '0', 'datatype': '64', 'descrip': '', 'dim[0]': '3', 'dim[1]': '128', 'dim[2]': '128', 'dim[3]': '128', 'dim[4]': '1', 'dim[5]': '1', 'dim[6]': '1', 'dim[7]': '1', 'dim_info': '0', 'intent_code': '0', 'intent_name': '', 'intent_p1': '0', 'intent_p2': '0', 'intent_p3': '0', 'nifti_type': '1', 'pixdim[0]': '0', 'pixdim[1]': '1', 'pixdim[2]': '1', 'pixdim[3]': '1', 'pixdim[4]': '0', 'pixdim[5]': '0', 'pixdim[6]': '0', 'pixdim[7]': '0', 'qform_code': '1', 'qform_code_name': 'NIFTI_XFORM_SCANNER_ANAT', 'qoffset_x': '-0', 'qoffset_y': '-0', 'qoffset_z': '0', 'quatern_b': '0', 'quatern_c': '0', 'quatern_d': '1', 'scl_inter': '0', 'scl_slope': '1', 'sform_code': '0', 'sform_code_name': 'NIFTI_XFORM_UNKNOWN', 'slice_code': '0', 'slice_duration': '0', 'slice_end': '0', 'slice_start': '0', 'srow_x': '0 0 0 0', 'srow_y': '0 0 0 0', 'srow_z': '0 0 0 0', 'toffset': '0', 'vox_offset': '352', 'xyzt_units': '2', 'origin': array([0., 0., 0.]), 'spacing': array([1., 1., 1.]), 'direction': array([[1., 0., 0.],\n", + " [0., 1., 0.],\n", + " [0., 0., 1.]]), 'original_affine': array([[1., 0., 0., 0.],\n", + " [0., 1., 0., 0.],\n", + " [0., 0., 1., 0.],\n", + " [0., 0., 0., 1.]]), 'affine': array([[1., 0., 0., 0.],\n", + " [0., 1., 0., 0.],\n", + " [0., 0., 1., 0.],\n", + " [0., 0., 0., 1.]]), 'spatial_shape': [128, 128, 128], 'filename_or_obj': '/tmp/tmpg4lwxckh/test_image.nii.gz'}\n" + ] + } + ], + "source": [ + "# generate 3D test images\n", + "tempdir = tempfile.mkdtemp()\n", + "test_image = np.random.rand(128, 128, 128)\n", + "filename = os.path.join(tempdir, \"test_image.nii.gz\")\n", + "itk_np_view = itk.image_view_from_array(test_image)\n", + "itk.imwrite(itk_np_view, filename)\n", + "data, meta = LoadImage()(filename)\n", + "\n", + "print(f\"image data shape:{data.shape}\")\n", + "print(f\"meta data:{meta}\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Load a list of images and stack as 1 training item\n", + "Loading a list of files, stack them together and add a new dimension as first dimension.\n", + "\n", + "And use the meta data of the first image to represent the stacked result." + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "image data shape:(3, 128, 128, 128)\n", + "meta data:{'ITK_FileNotes': '', 'aux_file': '', 'bitpix': '64', 'cal_max': '0', 'cal_min': '0', 'datatype': '64', 'descrip': '', 'dim[0]': '3', 'dim[1]': '128', 'dim[2]': '128', 'dim[3]': '128', 'dim[4]': '1', 'dim[5]': '1', 'dim[6]': '1', 'dim[7]': '1', 'dim_info': '0', 'intent_code': '0', 'intent_name': '', 'intent_p1': '0', 'intent_p2': '0', 'intent_p3': '0', 'nifti_type': '1', 'pixdim[0]': '0', 'pixdim[1]': '1', 'pixdim[2]': '1', 'pixdim[3]': '1', 'pixdim[4]': '0', 'pixdim[5]': '0', 'pixdim[6]': '0', 'pixdim[7]': '0', 'qform_code': '1', 'qform_code_name': 'NIFTI_XFORM_SCANNER_ANAT', 'qoffset_x': '-0', 'qoffset_y': '-0', 'qoffset_z': '0', 'quatern_b': '0', 'quatern_c': '0', 'quatern_d': '1', 'scl_inter': '0', 'scl_slope': '1', 'sform_code': '0', 'sform_code_name': 'NIFTI_XFORM_UNKNOWN', 'slice_code': '0', 'slice_duration': '0', 'slice_end': '0', 'slice_start': '0', 'srow_x': '0 0 0 0', 'srow_y': '0 0 0 0', 'srow_z': '0 0 0 0', 'toffset': '0', 'vox_offset': '352', 'xyzt_units': '2', 'origin': array([0., 0., 0.]), 'spacing': array([1., 1., 1.]), 'direction': array([[1., 0., 0.],\n", + " [0., 1., 0.],\n", + " [0., 0., 1.]]), 'original_affine': array([[1., 0., 0., 0.],\n", + " [0., 1., 0., 0.],\n", + " [0., 0., 1., 0.],\n", + " [0., 0., 0., 1.]]), 'affine': array([[1., 0., 0., 0.],\n", + " [0., 1., 0., 0.],\n", + " [0., 0., 1., 0.],\n", + " [0., 0., 0., 1.]]), 'spatial_shape': [128, 128, 128], 'filename_or_obj': '/tmp/tmpg4lwxckh/test_image.nii.gz'}\n" + ] + } + ], + "source": [ + "filenames = [\"test_image.nii.gz\", \"test_image2.nii.gz\", \"test_image3.nii.gz\"]\n", + "for i, name in enumerate(filenames):\n", + " filenames[i] = os.path.join(tempdir, name)\n", + " itk_np_view = itk.image_view_from_array(test_image)\n", + " itk.imwrite(itk_np_view, filenames[i])\n", + "data, meta = LoadImage()(filenames)\n", + "\n", + "print(f\"image data shape:{data.shape}\")\n", + "print(f\"meta data:{meta}\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Load 2D image in PNG format" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "metadata": { + "tags": [] + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "image data shape:(256, 256)\n", + "meta data:{'origin': array([0., 0.]), 'spacing': array([1., 1.]), 'direction': array([[1., 0.],\n", + " [0., 1.]]), 'original_affine': array([[1., 0., 0.],\n", + " [0., 1., 0.],\n", + " [0., 0., 1.]]), 'affine': array([[1., 0., 0.],\n", + " [0., 1., 0.],\n", + " [0., 0., 1.]]), 'spatial_shape': [256, 256], 'filename_or_obj': '/tmp/tmpg4lwxckh/test_image.png'}\n" + ] + } + ], + "source": [ + "test_image = np.random.randint(0, 256, size=[256, 256])\n", + "filename = os.path.join(tempdir, \"test_image.png\")\n", + "Image.fromarray(test_image.astype(\"uint8\")).save(filename)\n", + "data, meta = LoadImage()(filename)\n", + "\n", + "print(f\"image data shape:{data.shape}\")\n", + "print(f\"meta data:{meta}\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Load image with specified image reader\n", + "The `LoadImage` transforms can automatically choose readers based on the supported subffixes and in below order:\n", + "- User specified reader at runtime when call this loader.\n", + "- Registered readers from the first to the last in list, user can register reader when initializing or at runtime.\n", + "- Default ITK reader.\n", + "\n", + "And we can set additional parameters for the image readers, for example, set `c_order_axis_indexing=True` for `ITKReader`, this parameter will pass to ITK `read()` function later." + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "image data shape:(256, 256)\n", + "meta data:{'origin': array([0., 0.]), 'spacing': array([1., 1.]), 'direction': array([[1., 0.],\n", + " [0., 1.]]), 'original_affine': array([[1., 0., 0.],\n", + " [0., 1., 0.],\n", + " [0., 0., 1.]]), 'affine': array([[1., 0., 0.],\n", + " [0., 1., 0.],\n", + " [0., 0., 1.]]), 'spatial_shape': [256, 256], 'filename_or_obj': '/tmp/tmpg4lwxckh/test_image.png'}\n" + ] + } + ], + "source": [ + "loader = LoadImage()\n", + "loader.register(ITKReader(c_order_axis_indexing=True))\n", + "data, meta = loader(filename)\n", + "\n", + "print(f\"image data shape:{data.shape}\")\n", + "print(f\"meta data:{meta}\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Load image and execute additional operations\n", + "Some image readers can support additional operations after reading the image from file.\n", + "\n", + "For example, we can set a converter for PILReader: `PILReader(converter=lambda image: image.convert(\"LA\"))`." + ] + }, + { + "cell_type": "code", + "execution_count": 18, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "image data shape:(256, 256, 2)\n", + "meta data:{'format': None, 'mode': 'LA', 'width': 256, 'height': 256, 'info': {}, 'spatial_shape': [256, 256], 'filename_or_obj': '/tmp/tmpg4lwxckh/test_image.png'}\n" + ] + } + ], + "source": [ + "loader = LoadImage(PILReader(converter=lambda image: image.convert(\"LA\")))\n", + "data, meta = loader(filename)\n", + "\n", + "print(f\"image data shape:{data.shape}\")\n", + "print(f\"meta data:{meta}\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Connect `LoadImage` with other transforms\n", + "It's very easy to connect `LoadImage` transform with other transforms to construct a transform chain." + ] + }, + { + "cell_type": "code", + "execution_count": 23, + "metadata": { + "tags": [] + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "image data shape:torch.Size([1, 64, 64])\n", + "meta data:{'origin': array([0., 0.]), 'spacing': array([1., 1.]), 'direction': array([[1., 0.],\n", + " [0., 1.]]), 'original_affine': array([[1., 0., 0.],\n", + " [0., 1., 0.],\n", + " [0., 0., 1.]]), 'affine': array([[1., 0., 0.],\n", + " [0., 1., 0.],\n", + " [0., 0., 1.]]), 'spatial_shape': [256, 256], 'filename_or_obj': '/tmp/tmpg4lwxckh/test_image.png'}\n" + ] + } + ], + "source": [ + "transform = Compose([\n", + " LoadImaged(keys=\"image\"),\n", + " AddChanneld(keys=\"image\"),\n", + " Resized(keys=\"image\", spatial_size=[64, 64]),\n", + " ToTensord(\"image\"),\n", + "])\n", + "test_data = {\"image\": filename}\n", + "result = transform(test_data)\n", + "print(f\"image data shape:{result['image'].shape}\")\n", + "print(f\"meta data:{result['image_meta_dict']}\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Cleanup data directory\n", + "\n", + "Remove directory if a temporary was used." + ] + }, + { + "cell_type": "code", + "execution_count": 27, + "metadata": {}, + "outputs": [], + "source": [ + "shutil.rmtree(tempdir)" + ] + } + ], + "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.6.10" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +}