diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000000000000000000000000000000000000..c3258848cdb2652a0e3ee601766072a411abbd0f --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2023 Tamino Huxohl + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md index e92182c3f52daaf380035872fd4d415604560259..294c25ba83a34f31b013b459dd893185a62b787b 100644 --- a/README.md +++ b/README.md @@ -1,48 +1,57 @@ # Mu Map -Repository for the code to replicate the paper of Shi et al. with our SPECT/CT Scanner. - -## Differences -| | Authors | Ours | -| :-------------------- | :----------------------------: | :-------------------------------: | -| SPECT/CT Scanner | GE NM/CT 850 SPECT/CT | Siemens Symbia Intevo 16 SPECT/CT | -| Scan Arc | 180° | 208° | -| Angles | 60 | 34 | -| Energy Window Lower | 114-126 keV | 110-131 keV | -| Energy Window Upper | 126-155 keV | 131-152 keV | -| Dose | 15 mCi | ? | -| Reconstruction | OSEM (5 iterations, 4 subsets) | Siemens Conjugate Gradient | -| CT Energy | 120 kVp | ? | -| Attenuation Map Shape | (25-35)x64x64 | ? | -| Reconstruction Shape | 64x64x64 | 51x128x128 | -| Voxel Size | 6.8x6.8x6.8 mm³| 4.8x4.8x4.8 mm³ | - - -## Open Questions +Repository containing the code of the paper `Deep Learning Approximation of Attenuation Maps for Myocardial Perfusion SPECT with an IQ·SPECT Collimator`. + +Using this code, it is possible to train a deep neural network to predict attenuation maps from non-attenuation-corrected reconstructions. +The training is inspired by the description in [1] and is implemented with [PyTorch](https://pytorch.org/). +A random search procedure [2] is implemented for the optimization of hyperparameters. +To compute attenuation-corrected reconstructions, the [script](/mu_map/scripts/compute_recons.py) implements the post-reconstruction attenuation correction (PRAC) algorithm [3]. +It uses the open-source tomographic reconstruction software [STIR](https://stir.sourceforge.net/). +Reconstructions can be evaluated using the mean squared error or the normalized mean absolute error. +Polar maps cannot be generated with this code, but they can be evaluated. +For the purposes of this paper, grayscale polar maps were generated using the Cedars-Sinai Cardiac Suite [4]. + +## Results + +### Comparison of Polar Maps + + +## Release ToDo's +* Update dependency versions in pyproject.toml an requirements.txt +* Test installation instructions +* Add illustrations: + * Gif of the comparison between a predicted and an actual attenuation map + * polar maps + +## Installation +Installation was tested with Ubuntu 20.04 and 22.04. +1. Create and load a virtual environment `python -m venv .venv && source .venv/bin/activate` +2. Install python requirements `pip install -r requirements.txt` + +### STIR +STIR is required to project and reconstruct scans (using the PRAC algorithm). +For its installation, this repository contains STIR as a submodule as well as an installation script. +To install STIR run: +1. `git submodule init && git submodule update` +2. `cd libs && ./install.sh && cd ..` + +### Tesseract OCR +Because we could not export perfusion scores from polar maps generated with the Cedars-Sinai Cardiac Suite, but only RGB images with scores written in a green font, we added a [script](/mu_map/polar_map/get_perfusion.py) to help extract these scores. +This script uses [Tesseract](https://tesseract-ocr.github.io/) to automatically parse these numbers from the image. +To install tesseract, run: +`sudo apt install tesseract-ocr` + +## Usage +This code is not intended for direct usage as data structures likely differ. + +## Features +* cGAN training +* Random search [2] for hyperparameter optimization +* PRAC [3] algorithm +* Evaluation of polar maps ## References -* `Shi et al. "Deep learning-based attenuation map generation for myocardial perfusion SPECT". 2020 In: European Journal of Nuclear Medicine and Molecular Imaging 47.10` - * [DOI: 10.1007/s00259-020-04746-6](www.doi.org/10.1007/s00259-020-04746-6) - -## Install -Install libraries for tomographic reconstruction: - -### NiftyRec -* `mkdir libs` -* `cd libs` -* `git clone https://github.com/TomographyLab/NiftyRec` -* `cd NiftyRec` -* `git checkout 5329496` -* `cp README.md README.txt` - this file is needed by cmake -* `mkdir build` -* `cd build` -* `ccmake ..` - turn of the volume renderer -* `make -j 8` -* `make package` -* `sudo dpkg -i NiftyRec-3.1.0-Linux-x86_64.deb` - -### TomoLab -Make sure your python virtual environment is active for this. -* `mkdir libs` -* `cd libs` -* `git clone https://github.com/TomographyLab/TomoLab` -* `git checkout 86b9a58` +1. L. Shi et al. “Deep learning-based attenuation map generation for myocardial perfusion SPECTâ€. In: European Journal of Nuclear Medicine and Molecular Imaging 47 (2020). doi: [10.1007/s00259-020-04746-6](https://doi.org/10.1007/s00259-020-04746-6). +2. J. Bergstra and Y. Bengio. “Random Search for Hyper-Parameter Optimizationâ€. In: Journal of Machine Learning Research 13 (2012). +3. H. Liu et al. “Post-reconstruction attenuation correction for SPECT myocardium perfusion imaging facilitated by deep learning-based attenuation map generationâ€. In: Journal of Nuclear Cardiology 29 (2021). doi: [10.1007/s12350-021-02817-1](http://doi.org/10.1007/s12350-021-02817-1). +4. G. Germano et al. “Quantitation in gated perfusion SPECT imaging: The Cedars-Sinai approachâ€. In: Journal of Nuclear Cardiology 14 (2007). doi: [10.1016/j.nuclcard.2007.06.008](https://doi.org/10.1016/j.nuclcard.2007.06.008). + diff --git a/mu_map/scripts/create_video.py b/mu_map/scripts/create_video.py new file mode 100644 index 0000000000000000000000000000000000000000..665d72f0f0a8f239ddd50087beeb48a359df6074 --- /dev/null +++ b/mu_map/scripts/create_video.py @@ -0,0 +1,67 @@ +import os + +import cv2 as cv +import numpy as np +import torch + +from mu_map.dataset.default import MuMapDataset +from mu_map.dataset.transform import PadCropTranform, SequenceTransform +from mu_map.models.unet import UNet +from mu_map.util import to_grayscale, COLOR_WHITE +from mu_map.random_search.cgan import load_params +from mu_map.vis.slices import join_images + +torch.set_grad_enabled(False) + +random_search_iter_dir = "cgan_random_search/001" + +params = load_params(os.path.join(random_search_iter_dir, "params.json")) +print(params["normalization"]) +print(params["generator_features"]) +dataset = MuMapDataset( + "data/second/", + transform_normalization=SequenceTransform( + [params["normalization"], PadCropTranform(dim=3, size=32)] + ), + split_name="test", + scatter_correction=False, +) +recon, mu_map_ct = dataset[1] +mu_map_ct = mu_map_ct.squeeze().numpy() + +device = torch.device("cpu") +model = UNet(features=params["generator_features"]) +model.load_state_dict( + torch.load( + os.path.join(random_search_iter_dir, "snapshots/val_min_generator.pth"), map_location=device + ) +) + +mu_map_dl = model(recon.unsqueeze(dim=0)).squeeze().numpy() +mu_map_dl = np.clip(mu_map_dl, 0, mu_map_ct.max()) + +volumes = [mu_map_ct, mu_map_dl, np.abs(mu_map_dl - mu_map_ct)] +min_val = 0 +max_val = max(mu_map_ct.max(), mu_map_dl.max()) +print(mu_map_ct.max(), mu_map_dl.max()) + +fourcc = cv.VideoWriter_fourcc(*"mp4v") +frame_size = (512, 3 * 512 + 2 * 10) +print(f"Frame size {frame_size}") +# video_writer = cv.VideoWriter("mu_map_comparison.mp4", fourcc, 25, frame_size, isColor=False) +video_writer = cv.VideoWriter("mu_map_comparison.mp4", cv.VideoWriter_fourcc(*"mp4v"), 25, frame_size[::-1], isColor=False) +for i in range(mu_map_ct.shape[0]): + images = map(lambda volume: volume[i], volumes) + images = map( + lambda img: to_grayscale(img, min_val=min_val, max_val=max_val), images + ) + images = map(lambda img: cv.resize(img, (512, 512)), images) + images = list(images) + + txt = f"{str(i):{len(str(mu_map_ct.shape[0]))}}/{mu_map_ct.shape[0]}" + cv.putText(images[0], txt, (0, 30), cv.FONT_HERSHEY_SIMPLEX, 1, 255, 3) + + image = join_images(images) + for i in range(5): + video_writer.write(image) +video_writer.release() diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000000000000000000000000000000000000..0e27d116e1b1db5a9723911223ad952dc30ff18f --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,37 @@ +[build-system] +requires = ["setuptools>=61.0"] +build-backend = "setuptools.build_meta" + +[project] +name = "mu_map" +version = "0.0.1" +authors = [ + { name="Tamino Huxohl", email="thuxohl@hdz-nrw.de" }, + +] +description = "Deep learning approximation of Attenuation Maps from SPECT reconstructions" +readme = "README.md" +requires-python = ">=3.8" +classifiers = [ + "Environment :: GPU :: NVIDIA CUDA", + "License :: OSI Approved :: MIT License", + "Operating System :: Unix", + "Programming Language :: Python :: 3", + "Topic :: Scientific/Engineering :: Artificial Life", +] +dependencies = [ + "matplotlib>=3.5.3", + "numpy>=1.23.1", + "opencv-python>=4.6.0.66", + "pandas>=1.4.3", + "pydicom>=2.3.0", + "pytesseract", + "scipy", + "stir>=5.0.0", + "termcolor", + "torch>=1.12.1", + "torchvision>=0.13.1", +] + +[project.urls] +"Homepage" = "https://gitlab.ub.uni-bielefeld.de/thuxohl/mu-map" diff --git a/res/polar_maps.png b/res/polar_maps.png new file mode 100644 index 0000000000000000000000000000000000000000..0b60bb8d35522ac24131f63f82e1a7a7797c9582 Binary files /dev/null and b/res/polar_maps.png differ