From 77da58718ae6a829596bcf1e5cec33f208419514 Mon Sep 17 00:00:00 2001 From: Yiheng Wang <68361391+yiheng-wang-nv@users.noreply.github.com> Date: Tue, 18 Feb 2025 21:47:49 +0800 Subject: [PATCH] Add tutorials to showcase GDS for nifti and dicom images (#597) This PR addresses the tutorial contribution mentioned in #580 Authors: - Yiheng Wang (https://github.com/yiheng-wang-nv) - Mads R. B. Kristensen (https://github.com/madsbk) Approvers: - Mads R. B. Kristensen (https://github.com/madsbk) URL: https://github.com/rapidsai/kvikio/pull/597 --- .../medical_dicom_image_loading_example.ipynb | 424 ++++++++++++++++++ .../medical_nifti_image_loading_example.ipynb | 377 ++++++++++++++++ 2 files changed, 801 insertions(+) create mode 100644 notebooks/medical_dicom_image_loading_example.ipynb create mode 100644 notebooks/medical_nifti_image_loading_example.ipynb diff --git a/notebooks/medical_dicom_image_loading_example.ipynb b/notebooks/medical_dicom_image_loading_example.ipynb new file mode 100644 index 0000000000..3e47693ee5 --- /dev/null +++ b/notebooks/medical_dicom_image_loading_example.ipynb @@ -0,0 +1,424 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "99b73d7d", + "metadata": {}, + "source": [ + "## Introduction\n", + "\n", + "This tutorial showcases how to use Kvikio to accelerate the loading of DICOM images. We will also utilize the `pydicom` library to handle these medical image formats.\n", + "\n", + "### Common Medical Image Formats\n", + "\n", + "Medical images are complex due to the extensive metadata they contain, which includes patient information, imaging parameters, and more. DICOM (Digital Imaging and Communications in Medicine) is one of the most common formats:\n", + "\n", + "- **Description**: The most widely used standard for storing and transmitting medical images. It includes metadata about the patient, imaging parameters, and more.\n", + "- **Usage**: Commonly used in hospitals and clinics for storing images from modalities like MRI, CT, X-ray, and ultrasound.\n", + "\n", + "### Extra Library Used\n", + "\n", + "#### pydicom\n", + "- **Description**: A Python library for working with DICOM files. It allows for reading, modifying, and writing DICOM data.\n", + "- **Usage**: Widely used in clinical and research settings to handle DICOM files.\n", + "\n", + "### GPU Acceleration with Kvikio\n", + "\n", + "Kvikio is a powerful tool that leverages GPU acceleration to significantly speed up the loading and processing of medical images. In this tutorial, we will demonstrate how to use Kvikio to efficiently handle DICOM images, providing a performance comparison between CPU and GPU processing.\n", + "\n", + "By the end of this tutorial, you will understand:\n", + "- How to load DICOM images using `pydicom`.\n", + "- How to accelerate the loading and processing of these images using Kvikio.\n", + "- The performance benefits of using GPU acceleration for medical image processing." + ] + }, + { + "cell_type": "markdown", + "id": "c45b7e8b", + "metadata": {}, + "source": [ + "### Setup Environment" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "d2e043c2", + "metadata": {}, + "outputs": [], + "source": [ + "# Check if pydicom is installed, if not, install it\n", + "!python -c \"import pydicom\" || pip install -q pydicom" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "47d412b4", + "metadata": {}, + "outputs": [], + "source": [ + "import kvikio\n", + "import kvikio.defaults\n", + "import cupy as cp\n", + "import tempfile\n", + "import pydicom\n", + "from pydicom.dataset import Dataset, FileDataset\n", + "import numpy as np\n", + "import os\n", + "import datetime\n", + "import requests\n", + "import tarfile\n", + "import gzip\n", + "import shutil\n", + "import io\n", + "from timeit import default_timer as timer" + ] + }, + { + "cell_type": "markdown", + "id": "b35ea6d3", + "metadata": {}, + "source": [ + "### Warmup Kvikio" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "cc03538d", + "metadata": {}, + "outputs": [], + "source": [ + "def warmup_kvikio():\n", + " \"\"\"\n", + " Warm up the Kvikio library to initialize the internal buffers, cuFile, GDS, etc.\n", + " \"\"\"\n", + " # warmup cuFile\n", + " a = cp.arange(100)\n", + " with tempfile.NamedTemporaryFile() as tmp_file:\n", + " tmp_file_name = tmp_file.name\n", + " f = kvikio.CuFile(tmp_file_name, \"w\")\n", + " f.write(a)\n", + " f.close()\n", + "\n", + " b = cp.empty_like(a)\n", + " f = kvikio.CuFile(tmp_file_name, \"r\")\n", + " f.read(b)\n", + "\n", + " # warmup cupy\n", + " c = cp.random.rand(100, 100, 3)\n", + " d = cp.mean(c)\n", + "\n", + "warmup_kvikio()" + ] + }, + { + "cell_type": "markdown", + "id": "df2d2e9d", + "metadata": {}, + "source": [ + "### Set Kvikio Threads\n", + "\n", + "KvikIO can automatically use multiple threads for I/O operations. Setting the environment variable `KVIKIO_NTHREADS` to the desired number of threads may improve performance. In this tutorial, 4 threads are used. For more details, refer to the [official documentation](https://docs.rapids.ai/api/kvikio/nightly/runtime_settings/#thread-pool-kvikio-nthreads)." + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "1ad596a4", + "metadata": {}, + "outputs": [], + "source": [ + "kvikio.defaults.num_threads_reset(nthreads=4)" + ] + }, + { + "cell_type": "markdown", + "id": "52790010", + "metadata": { + "vscode": { + "languageId": "plaintext" + } + }, + "source": [ + "### DICOM Data Preparation\n", + "\n", + "A fake DICOM file will be prepared to test the performance." + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "7b8bfbe1", + "metadata": {}, + "outputs": [], + "source": [ + "temp_working_dir = tempfile.mkdtemp()" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "45f893a0", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Multi-frame DICOM file created at: /tmp/tmplw76c4_0/example.dcm\n" + ] + } + ], + "source": [ + "def create_multiframe_dicom(file_path, num_slices=128, pixel_array_shape=(1024, 1024), pixel_value_range=(0, 4095)):\n", + " # Create a new DICOM dataset\n", + " file_meta = pydicom.dataset.FileMetaDataset()\n", + " file_meta.MediaStorageSOPClassUID = pydicom.uid.generate_uid()\n", + " file_meta.MediaStorageSOPInstanceUID = pydicom.uid.generate_uid()\n", + " file_meta.ImplementationClassUID = pydicom.uid.PYDICOM_IMPLEMENTATION_UID\n", + " file_meta.TransferSyntaxUID = pydicom.uid.ExplicitVRLittleEndian\n", + "\n", + " # Create the FileDataset instance (inherits from Dataset)\n", + " ds = FileDataset(file_path, {}, file_meta=file_meta, preamble=b\"\\0\" * 128)\n", + "\n", + " # Set some basic DICOM attributes\n", + " ds.PatientName = \"Test^Patient\"\n", + " ds.PatientID = \"123456\"\n", + " ds.Modality = \"CT\"\n", + " ds.StudyInstanceUID = pydicom.uid.generate_uid()\n", + " ds.SeriesInstanceUID = pydicom.uid.generate_uid()\n", + " ds.SOPInstanceUID = file_meta.MediaStorageSOPInstanceUID\n", + " ds.StudyDate = datetime.date.today().strftime('%Y%m%d')\n", + " ds.ContentDate = datetime.date.today().strftime('%Y%m%d')\n", + " ds.StudyTime = datetime.datetime.now().strftime('%H%M%S')\n", + " ds.ContentTime = datetime.datetime.now().strftime('%H%M%S')\n", + "\n", + " # Set the pixel data with random integers\n", + " pixel_array = np.random.randint(\n", + " pixel_value_range[0],\n", + " pixel_value_range[1],\n", + " (num_slices, *pixel_array_shape),\n", + " dtype=np.uint16,\n", + " )\n", + " ds.Rows, ds.Columns = pixel_array_shape\n", + " ds.NumberOfFrames = num_slices\n", + " ds.PixelData = pixel_array.tobytes()\n", + " ds.SamplesPerPixel = 1\n", + " ds.PhotometricInterpretation = \"MONOCHROME2\"\n", + " ds.BitsAllocated = 16\n", + " ds.BitsStored = 16\n", + " ds.HighBit = 15\n", + " ds.PixelRepresentation = 0\n", + "\n", + " # Set multi-frame specific attributes\n", + " ds.PerFrameFunctionalGroupsSequence = []\n", + " for slice_index in range(num_slices):\n", + " frame = Dataset()\n", + " plane_position = Dataset()\n", + " plane_position.ImagePositionPatient = [0, 0, slice_index]\n", + " plane_orientation = Dataset()\n", + " plane_orientation.ImageOrientationPatient = [1, 0, 0, 0, 1, 0]\n", + " pixel_measures = Dataset()\n", + " pixel_measures.SliceThickness = 1\n", + "\n", + " frame.PlanePositionSequence = [plane_position]\n", + " frame.PlaneOrientationSequence = [plane_orientation]\n", + " frame.PixelMeasuresSequence = [pixel_measures]\n", + " ds.PerFrameFunctionalGroupsSequence.append(frame)\n", + "\n", + " ds.is_little_endian = True\n", + " ds.is_implicit_VR = True\n", + "\n", + " ds.save_as(file_path)\n", + " print(f\"Multi-frame DICOM file created at: {file_path}\")\n", + "\n", + "# Example usage\n", + "example_dcm_path = os.path.join(temp_working_dir, \"example.dcm\")\n", + "\n", + "create_multiframe_dicom(example_dcm_path)" + ] + }, + { + "cell_type": "markdown", + "id": "d0374c01", + "metadata": {}, + "source": [ + "### Test DICOM Data Loading" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "id": "67ea8d22", + "metadata": {}, + "outputs": [], + "source": [ + "def load_image_cpu(file_path):\n", + " ds = pydicom.dcmread(file_path)\n", + " pixel_array = ds.pixel_array\n", + " return pixel_array" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "id": "4eabb76a", + "metadata": {}, + "outputs": [], + "source": [ + "def load_image_gpu(file_path):\n", + " # set defer_size to prevent reading the entire file\n", + " dcm_read_data = pydicom.dcmread(file_path, defer_size=\"100 KB\")\n", + "\n", + " # Extract relevant attributes\n", + " rows = dcm_read_data.Rows\n", + " columns = dcm_read_data.Columns\n", + " bits_allocated = dcm_read_data.BitsAllocated\n", + " samples_per_pixel = dcm_read_data.SamplesPerPixel\n", + " number_of_frames = getattr(dcm_read_data, 'NumberOfFrames', 1)\n", + " pixel_representation = dcm_read_data.PixelRepresentation\n", + "\n", + " if bits_allocated == 8:\n", + " dtype = cp.int8 if pixel_representation == 1 else cp.uint8\n", + " elif bits_allocated == 16:\n", + " dtype = cp.int16 if pixel_representation == 1 else cp.uint16\n", + " elif bits_allocated == 32:\n", + " dtype = cp.int32 if pixel_representation == 1 else cp.uint32\n", + " else:\n", + " raise ValueError(\"Unsupported BitsAllocated value\")\n", + "\n", + " bytes_per_pixel = bits_allocated // 8\n", + " total_pixels = rows * columns * samples_per_pixel * number_of_frames\n", + " expected_pixel_data_length = total_pixels * bytes_per_pixel\n", + "\n", + " offset = dcm_read_data.get_item(0x7FE00010, keep_deferred=True).value_tell\n", + "\n", + " with kvikio.CuFile(file_path, \"r\") as f:\n", + " buffer = cp.empty(expected_pixel_data_length, dtype=cp.int8)\n", + " f.read(buffer, expected_pixel_data_length, offset)\n", + "\n", + " cupy_data_array = buffer.view(dtype).reshape((number_of_frames, rows, columns))\n", + "\n", + " return cupy_data_array" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "006bcd54", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "(128, 1024, 1024) 2046.878812596202\n", + "Kvikio GPU loading time: 0.0975 seconds\n" + ] + } + ], + "source": [ + "# Measure Kvikio GPU loading time\n", + "# the saved outputs are run with a Tesla V100-PCIE-16GB GPU\n", + "start_gpu = timer()\n", + "img_gpu = load_image_gpu(example_dcm_path)\n", + "print(img_gpu.shape, img_gpu.mean())\n", + "end_gpu = timer()\n", + "gpu_time = end_gpu - start_gpu\n", + "print(f\"Kvikio GPU loading time: {gpu_time:.4f} seconds\")" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "id": "d048fb3e", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "(128, 1024, 1024) 2046.878812596202\n", + "Normal CPU loading time: 0.3950 seconds\n" + ] + } + ], + "source": [ + "# Measure CPU loading time\n", + "start_cpu = timer()\n", + "img_cpu = load_image_cpu(example_dcm_path)\n", + "print(img_cpu.shape, img_cpu.mean())\n", + "end_cpu = timer()\n", + "cpu_time = end_cpu - start_cpu\n", + "print(f\"Normal CPU loading time: {cpu_time:.4f} seconds\")" + ] + }, + { + "cell_type": "markdown", + "id": "997a7116", + "metadata": {}, + "source": [ + "### validate cpu and gpu data are close" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "id": "15006dd8", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "True\n" + ] + } + ], + "source": [ + "print(np.allclose(img_gpu, img_cpu))" + ] + }, + { + "cell_type": "markdown", + "id": "b801dc5b", + "metadata": {}, + "source": [ + "### Cleanup tmp Directory" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "id": "0ba89f01", + "metadata": {}, + "outputs": [], + "source": [ + "shutil.rmtree(temp_working_dir)" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "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.10.12" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/notebooks/medical_nifti_image_loading_example.ipynb b/notebooks/medical_nifti_image_loading_example.ipynb new file mode 100644 index 0000000000..bc0ec2f837 --- /dev/null +++ b/notebooks/medical_nifti_image_loading_example.ipynb @@ -0,0 +1,377 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "99b73d7d", + "metadata": {}, + "source": [ + "## Introduction\n", + "\n", + "This tutorial showcases how to use Kvikio to accelerate the loading of NIFTI images. We will also utilize the `nibabel` library to handle this medical image format.\n", + "\n", + "### Common Medical Image Formats\n", + "\n", + "Medical images are complex due to the extensive metadata they contain, which includes patient information, imaging parameters, and more.\n", + "\n", + "NIfTI (Neuroimaging Informatics Technology Initiative) is one of the most common formats:\n", + "\n", + "- **Description**: A popular format for storing brain imaging data, particularly in research settings. It is designed to store volumetric data and is often used in neuroimaging.\n", + "- **Usage**: Widely used in neuroscience research and supported by many neuroimaging software packages.\n", + "\n", + "### Extra Library Used\n", + "\n", + "#### NiBabel\n", + "- **Description**: A Python library for reading and writing medical image formats, particularly NIfTI and Analyze.\n", + "- **Usage**: Commonly used in neuroimaging research for handling NIfTI files.\n", + "\n", + "### GPU Acceleration with Kvikio\n", + "\n", + "Kvikio is a powerful tool that leverages GPU acceleration to significantly speed up the loading and processing of medical images. In this tutorial, we will demonstrate how to use Kvikio to efficiently handle NIFTI images, providing a performance comparison between CPU and GPU processing.\n", + "\n", + "By the end of this tutorial, you will understand:\n", + "- How to load NIFTI images using `nibabel`.\n", + "- How to accelerate the loading and processing of these images using Kvikio.\n", + "- The performance benefits of using GPU acceleration for medical image processing." + ] + }, + { + "cell_type": "markdown", + "id": "c45b7e8b", + "metadata": {}, + "source": [ + "### Setup Environment" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "d2e043c2", + "metadata": {}, + "outputs": [], + "source": [ + "# Check if nibabel is installed, if not, install it\n", + "!python -c \"import nibabel\" || pip install -q nibabel" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "47d412b4", + "metadata": {}, + "outputs": [], + "source": [ + "import kvikio\n", + "import kvikio.defaults\n", + "import cupy as cp\n", + "import numpy as np\n", + "import tempfile\n", + "import nibabel as nib\n", + "import os\n", + "import requests\n", + "import tarfile\n", + "import gzip\n", + "import shutil\n", + "import io\n", + "from timeit import default_timer as timer" + ] + }, + { + "cell_type": "markdown", + "id": "b35ea6d3", + "metadata": {}, + "source": [ + "### Warmup Kvikio" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "cc03538d", + "metadata": {}, + "outputs": [], + "source": [ + "def warmup_kvikio():\n", + " \"\"\"\n", + " Warm up the Kvikio library to initialize the internal buffers, cuFile, GDS, etc.\n", + " \"\"\"\n", + " # warmup cuFile\n", + " a = cp.arange(100)\n", + " with tempfile.NamedTemporaryFile() as tmp_file:\n", + " tmp_file_name = tmp_file.name\n", + " f = kvikio.CuFile(tmp_file_name, \"w\")\n", + " f.write(a)\n", + " f.close()\n", + "\n", + " b = cp.empty_like(a)\n", + " f = kvikio.CuFile(tmp_file_name, \"r\")\n", + " f.read(b)\n", + "\n", + " # warmup cupy\n", + " c = cp.random.rand(100, 100, 3)\n", + " d = cp.mean(c)\n", + "\n", + "warmup_kvikio()" + ] + }, + { + "cell_type": "markdown", + "id": "b1efcccc", + "metadata": {}, + "source": [ + "### Set Kvikio Threads\n", + "\n", + "KvikIO can automatically use multiple threads for I/O operations. Setting the environment variable `KVIKIO_NTHREADS` to the desired number of threads may improve performance. In this tutorial, 4 threads are used. For more details, refer to the [official documentation](https://docs.rapids.ai/api/kvikio/nightly/runtime_settings/#thread-pool-kvikio-nthreads)." + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "547213d4", + "metadata": {}, + "outputs": [], + "source": [ + "kvikio.defaults.num_threads_reset(nthreads=4)" + ] + }, + { + "cell_type": "markdown", + "id": "52790010", + "metadata": { + "vscode": { + "languageId": "plaintext" + } + }, + "source": [ + "### NIFTI Data Preparation\n", + "\n", + "For NIFTI images, we will use the [MSD Spleen dataset](https://msd-for-monai.s3-us-west-2.amazonaws.com/Task09_Spleen.tar) from the [Medical Segmentation Decathlon](http://medicaldecathlon.com/dataaws/). This dataset is commonly used for training and evaluating medical image segmentation algorithms and provides a good example of volumetric medical imaging data.\n", + "\n", + "Larger datasets typically demonstrate more significant acceleration benefits when using GPU processing. If you are interested in comparing performance with a larger dataset, it is recommended to use images from the [MSD Liver dataset](https://msd-for-monai.s3-us-west-2.amazonaws.com/Task03_Liver.tar) for the following experiments. The MSD Liver dataset contains more extensive volumetric data, which can better showcase the advantages of GPU acceleration." + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "45f893a0", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Extraction completed! Files are saved in: /tmp/tmpcjecciqo\n" + ] + } + ], + "source": [ + "temp_working_dir = tempfile.mkdtemp()\n", + "\n", + "nifti_output_path = os.path.join(temp_working_dir, \"Task09_Spleen.tar\")\n", + "url = \"https://msd-for-monai.s3-us-west-2.amazonaws.com/Task09_Spleen.tar\"\n", + "response = requests.get(url, stream=True)\n", + "with open(nifti_output_path, \"wb\") as file:\n", + " for chunk in response.iter_content(chunk_size=8192):\n", + " file.write(chunk)\n", + "\n", + "# Extract the contents\n", + "with tarfile.open(nifti_output_path, \"r\") as tar:\n", + " tar.extractall(path=temp_working_dir)\n", + "\n", + "print(f\"Extraction completed! Files are saved in: {temp_working_dir}\")" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "028021e2", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "a decompressed nifti file is saved at: /tmp/tmpcjecciqo/Task09_Spleen/imagesTr/spleen_53.nii\n" + ] + } + ], + "source": [ + "# decompress the nii.gz file\n", + "example_nifti_path = os.path.join(temp_working_dir, \"Task09_Spleen\", \"imagesTr\", \"spleen_53.nii\")\n", + "with gzip.open(example_nifti_path+\".gz\", \"rb\") as f_in:\n", + " with open(example_nifti_path, \"wb\") as f_out:\n", + " shutil.copyfileobj(f_in, f_out)\n", + "print(\"a decompressed nifti file is saved at: \", example_nifti_path)" + ] + }, + { + "cell_type": "markdown", + "id": "d0374c01", + "metadata": {}, + "source": [ + "### Test NIFTI Data Loading" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "id": "fac454d7", + "metadata": {}, + "outputs": [], + "source": [ + "def nifti_gpu_load(filename):\n", + " file_size = os.path.getsize(filename)\n", + " image = cp.empty(file_size, dtype=cp.uint8)\n", + "\n", + " with kvikio.CuFile(filename, \"r\") as f:\n", + " f.read(image)\n", + "\n", + " header_bytes = cp.asnumpy(image[:348])\n", + " header = nib.Nifti1Header.from_fileobj(io.BytesIO(header_bytes))\n", + " data_offset = header.get_data_offset()\n", + " data_shape = header.get_data_shape()\n", + " data_dtype = header.get_data_dtype()\n", + " affine = header.get_best_affine()\n", + " meta = dict(header)\n", + " meta[\"affine\"] = affine\n", + " return image[data_offset:].view(data_dtype).reshape(data_shape, order=\"F\"), meta" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "id": "006bcd54", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "(512, 512, 156) -474.2267\n", + "Kvikio GPU loading time: 0.0505 seconds\n" + ] + } + ], + "source": [ + "# Measure Kvikio GPU loading time\n", + "# the saved outputs are run with a Tesla V100-PCIE-16GB GPU\n", + "start_gpu = timer()\n", + "img_gpu, meta_gpu = nifti_gpu_load(example_nifti_path)\n", + "print(img_gpu.shape, img_gpu.mean())\n", + "end_gpu = timer()\n", + "gpu_time = end_gpu - start_gpu\n", + "print(f\"Kvikio GPU loading time: {gpu_time:.4f} seconds\")" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "id": "d048fb3e", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "(512, 512, 156) -474.22673315879626\n", + "Normal CPU loading time: 0.1699 seconds\n" + ] + } + ], + "source": [ + "# Measure CPU loading time\n", + "start_cpu = timer()\n", + "img_cpu = nib.load(example_nifti_path)\n", + "img_cpu_array = img_cpu.get_fdata()\n", + "print(img_cpu_array.shape, img_cpu_array.mean())\n", + "end_cpu = timer()\n", + "cpu_time = end_cpu - start_cpu\n", + "print(f\"Normal CPU loading time: {cpu_time:.4f} seconds\")" + ] + }, + { + "cell_type": "markdown", + "id": "997a7116", + "metadata": {}, + "source": [ + "### validate cpu and gpu data are close" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "id": "72475fa5", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "True\n" + ] + } + ], + "source": [ + "# validate affine\n", + "print(np.all(img_cpu.affine == meta_gpu[\"affine\"]))" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "id": "784ee851", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "True\n" + ] + } + ], + "source": [ + "# validate array\n", + "print(np.allclose(img_cpu_array, img_gpu))" + ] + }, + { + "cell_type": "markdown", + "id": "7422f5a6", + "metadata": {}, + "source": [ + "### Cleanup tmp Directory" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "id": "bc85278e", + "metadata": {}, + "outputs": [], + "source": [ + "shutil.rmtree(temp_working_dir)" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "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.10.12" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +}