Using GE NM/CT 860 scanner data

Hello,

First, thanks for sharing this code/program; I think it is very useful. I managed to get the Dicom test data running. I am trying to run GE data, acquired on an NM/CT 860 with the LEHRS collimators, of a CTN anthropomorphic chest phantom filled with Tc99m and a ratio @4/1 lesion to the background.
I have CT files, ATT map and two files with the projection data for the EM and SC windows. We would like to explore different reconstructions offered by pytomography and use clinical data as well (not only with Tc99m but also with Lu177 and other isotopes).
My questions are:
Do you have any examples of how to reconstruct GE scanner data with the two separate files? What collimator shall I use from the library (G8-LHRS?)? Can you do reconstructions without PSF modelling? And last… in the dicom example, the Sy-ME collimator used returns:
kernel_dimensions = 2D
min_sigmas = 3
sigma_fit_params = [0.03161992134409504, 0.1248503046423388, 0.0]

What do the parameters mean? We would like to explore in the future measured collimator PSF at different distances and implement it with pytomography. Would that be possible?
Thanks a lot for your help

(If you want me to upload our data let me know)

Also, when trying to use the CT data returned by the scanner, the GE scanner, I get the error below: Is there a way to use the .dcm att map returned by the scanner?
(I should say I don’t know much Python)

att_transform = SPECTAttenuationTransform(filepath=files_CT)
Traceback (most recent call last):
File “”, line 1, in
File “C:\ProgramData\anaconda3\Lib\site-packages\pytomography\transforms\SPECT\attenuation.py”, line 49, in init
self.CT_unaligned_numpy = open_CT_file(filepath)
^^^^^^^^^^^^^^^^^^^^^^
File “C:\ProgramData\anaconda3\Lib\site-packages\pytomography\io\CT\dicom.py”, line 22, in open_CT_file
ds = pydicom.read_file(file)
^^^^^^^^^^^^^^^^^^^^^^^
File “C:\Users\Josuto\AppData\Roaming\Python\Python311\site-packages\pydicom\filereader.py”, line 1029, in dcmread
dataset = read_partial(
^^^^^^^^^^^^^
File “C:\Users\Josuto\AppData\Roaming\Python\Python311\site-packages\pydicom\filereader.py”, line 780, in read_partial
preamble = read_preamble(fileobj, force)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File “C:\Users\Josuto\AppData\Roaming\Python\Python311\site-packages\pydicom\filereader.py”, line 724, in read_preamble
raise InvalidDicomError(
pydicom.errors.InvalidDicomError: File is missing DICOM File Meta Information header or the ‘DICM’ prefix is missing from the header. Use force=True to force reading.

Hi Jose
I also begin with this API and it’s nice. I have a here.
I reconstructed some images from our GE NM/CT 670 but I used the CT w/ HU.
Why would you use the ATT map provided by the manufacturer?
Regarding reconstruction w/o PSF, you need to provide the ATT transform and it should do the trick.
If you want to use your own collimator parameters, I think you could edit the collimator file collim.col in data directory of pytomography
Best regards

1 Like

Thanks for your reply. Appreciated.

I tried the CT with HU and got the error above. So, I wondered if there is a way to use the ATT map the scanner generates and saves for each recon. I will try again with the CT images.

Do you have any examples of how you called the commands and the inputs you use? It will be highly appreciated to see them. Anything you have and if you have examples of how to set PSF will be excellent.

Excuse my ignorance and looking so needy; I have broad experience with PET data and offline tools such as Siemens e7tools or GE Duetto, but this is all new to me. I have not done any SPECT gamma cameras in the last ~15 years. I want to start introducing offline tools in our department for research purposes. Yesterday was day 1.

Best wishes
Jose

Hi @Janton ,

Glad your exploring using PyTomography! For your questions:

  1. Yes, we have just released a new version (2.0.0) of PyTomography where we reconstruct GE data with two bed positions and stitch them together. You can check out that tutorial here: DICOM Multiple Bed Positions — PyTomography 2.0.0 documentation

  2. The collimator depends on you particular system. Unfortunately, I don’t know how to access this from the DICOM file, so if you know which system you used, hopefully you can figure it out based on this table: External Data — PyTomography 2.0.0 documentation

  3. Yes, you can do reconstructions without PSF modeling, simply don’t include the PSFTransform in the obj2obj_transforms argument when constructing the system matrix.

  4. Those parameters give the slope and intercept corresponding to parameter sigma (in a Gaussian) as a function of distance from the collimator. Implementing your own model for PSF blurring is also totally possible. It’s very easy if you plan on using a Gaussian blurring function. See line 65+ here (PyTomography/src/pytomography/metadata/SPECT/spect_metadata.py at main · qurit/PyTomography · GitHub). You can come up with your own fit function for sigma as a function of distance that you can obtain by fitting to various PSFs.

  5. Can you check what your files_CT is? It should be a list of filepaths that end with .dcm. I suspect it may be trying to open something else here.

1 Like

Thanks a lot

I have managed—some issues here and there with data. I started again from scratch, and I managed. Visual results match scanner reconstruction. I ll investigate the PSF modelling.

Code I used:
import os
import numpy as np
from pytomography.io.SPECT import dicom
from pytomography.transforms.SPECT import SPECTAttenuationTransform, SPECTPSFTransform
from pytomography.algorithms import OSEM
from pytomography.projectors.SPECT import SPECTSystemMatrix
from pytomography.utils import print_collimator_parameters
import matplotlib.pyplot as plt
import pydicom

% data
path=‘D:\ctn3_body_phantom_SPECT’
path_CT= os.path.join(path, ‘ct_repeat’)
files_CT = [os.path.join(path_CT, file) for file in os.listdir(path_CT)]

file_NM = os.path.join(path, ‘TOMOMAA-repeat_EM001_DS.dcm’)
file_SC = os.path.join(path, ‘TOMOMAA-repeat_SC001_DS.dcm’)

object_meta, proj_meta = dicom.get_metadata(file_NM)

photopeak = dicom.get_projections(file_NM)
photopeak.shape

scatter = dicom.get_projections(file_SC)
scatter.shape

att_transform = SPECTAttenuationTransform(filepath=files_CT)

att_transform.configure(object_meta, proj_meta)
attenuation_map = att_transform.attenuation_map

collimator_name = ‘G8-LHRS’
energy_kev = 140
psf_meta = dicom.get_psfmeta_from_scanner_params(collimator_name, energy_kev)
print(psf_meta)

psf_transform = SPECTPSFTransform(psf_meta)

system_matrix = SPECTSystemMatrix(
obj2obj_transforms = [att_transform,psf_transform],
proj2proj_transforms = ,
object_meta = object_meta,
proj_meta = proj_meta)

reconstructed_object = reconstruction_algorithm(n_iters=4, n_subsets=10)

After following the OSEMOSL tutorial with my data the programs run but I would like to save the images into Dicom. I tried as the Dicom example but I haven’t managed. Apologies if it is too obvious.

I follow the example:

def reconstruct(
prior = None,
n_iters=40,
n_subsets=8,
):
reconstruction_algorithm = OSEMOSL(
projections = photopeak_poisson,
system_matrix = system_matrix,
scatter = scatter_poisson,
prior=prior)
return reconstruction_algorithm(n_iters, n_subsets)

% % %
recon_noprior = reconstruct(None)
(runs well)

How can I save this recon_noprior into Dicom (and other recons with priors)?

Thanks a lot for the help

Jose

@Janton wow this is very exciting! A few things

(i) Can you update to the newest PyTomography version (2.0.0)? There’s a few slight changes you’ll need to make to your code (such as defining a likelihood) but its not a big deal or big change, just see the latest DICOM tutorials.

(ii) The way to save data is at the end of this tutorial: DICOM Data Introduction — PyTomography 2.0.0 documentation

Thanks,

I updated to PyTomography version (2.0.0) :
Installing collected packages: pytomography
Successfully installed pytomography-2.0.0

But after that, I can run all commands of the Dicom tutorial (with your test data and with my data) but get stuck in creating the system matrix. The error message below:

system_matrix = SPECTSystemMatrix(
obj2obj_transforms = [att_transform,psf_transform],
proj2proj_transforms = ,
object_meta = object_meta,
proj_meta = proj_meta)

File “”, line 1, in
File “C:\Program Files\Python311\Lib\site-packages\pytomography\projectors\SPECT\spect_system_matrix.py”, line 37, in init
import parallelproj
ModuleNotFoundError: No module named ‘parallelproj’

I tried admin user, remove pytomography via command line first, re-start computer, … I am working on a windows computer. The previous version ran well, as shown in the example with the CTN body phantom.

Thanks a lot, and regards

Oh that’s totally my bad, I for some reason put an incorrect import statement in the wrong place. Version 2.0.1 has this fixed and should work now.

Thanks for sorting it out so quickly. All working good.
See phantom recons below.

Minor thing, I think you have a typo in the example saving dicom, objrct should be object (it does not work otherwise):

save_path = ‘/disk1/pytomography_tutorial_data/dicom_tutorial/output’
dicom.save_dcm(
save_path = save_path,
objrct = reconstructed_object,
file_NM = file_NM,
recon_name = ‘OSEM_4it_10ss’)

We are going to explore different reconstructions. What would you think is a good iterative reconstruction using prior/penalisation preserving resolution and reducing noise. Any suggestions? I have some students who are going to start looking at different options.
We are happy to share any results.

Best Wishes

HI @Janton , I’d recommend checking out all the algorithms here:

https://pytomography.readthedocs.io/en/latest/notebooks/t_siminddata.html

Mostly notably:
(i) You could explore varying beta and gamma in RDP with BSREM (I see you’ve used OSMAPOSL above). It would be interesting to compare the two: BSREM also has a relaxation parameter you can vary. You could also explore using different numbers of nearest neighbours based on anatomical information: this would be interesting.
(ii) You could look at using KEM and how different kernels and kernel sizes effect reconstruction (in general, one can define an arbitrary kernel, so the parameter space to explore here is very big).

1 Like

Thanks a lot. We will look into it. Thanks again for sharing your API and for your help.

Regards