{ "cells": [ { "cell_type": "markdown", "id": "29abc7d4", "metadata": {}, "source": [ "# Differentiable beamforming for ultrasound autofocusing\n" ] }, { "cell_type": "markdown", "id": "f28d4bf8", "metadata": {}, "source": [ "In this tutorial we will implement a basic differential beamformer. We will use a gradient descent method to minimize a pixelwise common midpoint phase error to estimate a speed of sound map. The algorithm is slightly simplified, loss is computed without patching. \n", "\n", "For more information we would like to refer you to the original research [project page](https://waltersimson.com/dbua/) of the differential beamformer for ultrasound autofocusing (DBUA) [paper](https://doi.org/10.1007/978-3-031-43999-5_41):\n", "- Simson, W., Zhuang, L., Sanabria, S.J., Antil, N., Dahl, J.J., Hyun, D. (2023). Differentiable Beamforming for Ultrasound Autofocusing. Medical Image Computing and Computer Assisted Intervention (MICCAI)" ] }, { "cell_type": "markdown", "id": "b61732db", "metadata": {}, "source": [ "[![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/tue-bmd/zea/blob/main/docs/source/notebooks/pipeline/dbua_example.ipynb)\n", " \n", "[![View on GitHub](https://img.shields.io/badge/GitHub-View%20Source-blue?logo=github)](https://github.com/tue-bmd/zea/blob/main/docs/source/notebooks/pipeline/dbua_example.ipynb)\n", " \n", "[![Hugging Face dataset](https://img.shields.io/badge/Hugging%20Face-Dataset-yellow?logo=huggingface)](https://huggingface.co/datasets/zeahub/simulations/blob/main/circular_inclusion_simulation.hdf5)" ] }, { "cell_type": "markdown", "id": "231c7ee0", "metadata": {}, "source": [ "‼️ **Important:** This notebook is optimized for **GPU/TPU**. Code execution on a **CPU** may be very slow.\n", "\n", "If you are running in Colab, please enable a hardware accelerator via:\n", "\n", "**Runtime → Change runtime type → Hardware accelerator → GPU/TPU** 🚀." ] }, { "cell_type": "code", "execution_count": 1, "id": "701a9138", "metadata": {}, "outputs": [], "source": [ "%%capture\n", "%pip install zea" ] }, { "cell_type": "code", "execution_count": 2, "id": "87a10c28", "metadata": {}, "outputs": [], "source": [ "import os\n", "\n", "os.environ[\"KERAS_BACKEND\"] = \"jax\"\n", "os.environ[\"ZEA_DISABLE_CACHE\"] = \"1\"\n", "os.environ[\"ZEA_LOG_LEVEL\"] = \"INFO\"" ] }, { "cell_type": "code", "execution_count": 3, "id": "0f548c87", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "\u001b[1m\u001b[38;5;36mzea\u001b[0m\u001b[0m: Using backend 'jax'\n" ] } ], "source": [ "import time\n", "import numpy as np\n", "import matplotlib.pyplot as plt\n", "import keras\n", "from keras import ops\n", "from keras.utils import Progbar\n", "\n", "import zea\n", "from zea import File, init_device\n", "from zea.visualize import set_mpl_style\n", "from zea.io_lib import matplotlib_figure_to_numpy, save_to_gif\n", "from zea.backend.optimizer import adam\n", "from zea.backend.autograd import AutoGrad\n", "from zea.ops import (\n", " TOFCorrection,\n", " CommonMidpointPhaseError,\n", " PatchedGrid,\n", " EnvelopeDetect,\n", " Normalize,\n", " DelayAndSum,\n", " LogCompress,\n", " ReshapeGrid,\n", ")" ] }, { "cell_type": "markdown", "id": "ccdf3474", "metadata": {}, "source": [ "We will work with the GPU if available, and initialize using `init_device` to pick the best available device. Also, (optionally), we will set the matplotlib style for plotting." ] }, { "cell_type": "code", "execution_count": 4, "id": "62bf6ceb", "metadata": {}, "outputs": [], "source": [ "init_device(verbose=False)\n", "set_mpl_style()" ] }, { "cell_type": "markdown", "id": "76834576", "metadata": {}, "source": [ "## Load dataset \n", "First let's load a dataset. In this case a circular inclusion in an isoechoic medium, simulated in [k-Wave](http://www.k-wave.org/) and stored to [zea data format](../../data-acquisition.rst). It will automatically load the dataset from our [Hugging Face](https://huggingface.co/zeahub) repository." ] }, { "cell_type": "code", "execution_count": 5, "id": "9b8c0f96", "metadata": {}, "outputs": [ { "data": { "application/vnd.jupyter.widget-view+json": { "model_id": "cf957f6db96541c88a5f71457fedadfe", "version_major": 2, "version_minor": 0 }, "text/plain": [ "circular_inclusion_simulation.hdf5: 0%| | 0.00/308M [00:00 \u001b[33msos_optim.gif\u001b[0m\u001b[0m\n" ] } ], "source": [ "viz_frames = []\n", "progbar = Progbar(num_iterations)\n", "for i in range(num_iterations):\n", " flatgrid = resample_grid(parameters, xlims, zlims)\n", " grad, loss = compute_gradients(sos_map, data_frame, flatgrid)\n", " sos_map, opt_state = apply_gradients(opt_state, grad)\n", " progbar.update(i + 1, [(\"loss\", loss)])\n", " if (i + 1) % 5 == 0 or i == num_iterations - 1:\n", " bmode = image_plot_pipeline(\n", " data=data_frame,\n", " sos_map=sos_map,\n", " sos_grid_x=sos_grid_x,\n", " sos_grid_z=sos_grid_z,\n", " **parameters_plot,\n", " )[\"data\"][0].reshape(grid_size_x, grid_size_z)\n", "\n", " lossimage = loss_pipeline(\n", " data=data_frame,\n", " sos_map=sos_map,\n", " sos_grid_x=sos_grid_x,\n", " sos_grid_z=sos_grid_z,\n", " **parameters_plot,\n", " )[\"data\"][0].reshape(grid_size_x, grid_size_z)\n", "\n", " bmodeim.set_data(bmode)\n", " lossim.set_data(lossimage)\n", " im.set_data(ops.convert_to_numpy(sos_map))\n", " viz_frames.append(matplotlib_figure_to_numpy(fig))\n", "\n", "plt.close(fig)\n", "save_to_gif(viz_frames, \"sos_optim.gif\", shared_color_palette=True, fps=10)" ] }, { "cell_type": "markdown", "id": "0e0ded4c", "metadata": {}, "source": [ "![Speed of sound optimization progress](sos_optim.gif)" ] } ], "metadata": { "kernelspec": { "display_name": "Python 3", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.12.11" } }, "nbformat": 4, "nbformat_minor": 5 }