import pymirc.fileio           as pymf
import pymirc.image_operations as pymi

#---------------------------------------------------------------------------------------------

data_dir = os.path.join('..','..','data','nema_petct')

if not os.path.exists(data_dir):
  url = 'https://kuleuven.box.com/s/wub9pk0yvt8kjqyj7p0bz11boca4334x'
  print('please first download example PET/CT data from:')
  print(url)
  print('and unzip into: ', data_dir)
  sys.exit()

# read PET/CT nema phantom dicom data sets from
pet_dcm = pymf.DicomVolume(os.path.join(data_dir,'PT','*.dcm'))
pet_vol = pet_dcm.get_data()

ct_dcm  = pymf.DicomVolume(os.path.join(data_dir,'CT','*.dcm'))
ct_vol  = ct_dcm.get_data()

# the PET and CT images are on different voxel grids
# to view them in parallel, we interpolate the PET volume to the CT grid
pet_vol_ct_grid = pymi.aff_transform(pet_vol, np.linalg.inv(pet_dcm.affine) @ ct_dcm.affine, 
                                     output_shape = ct_vol.shape)

imshow_kwargs = [{'cmap':py.cm.Greys},
                 {'cmap':py.cm.Greys_r,'vmin':-500,'vmax':500},
                 {'cmap':py.cm.Greys_r,'vmin':-500,'vmax':500}]

oimshow_kwargs = {'cmap':py.cm.hot, 'alpha':0.3}
Ejemplo n.º 2
0
#-------------------------------------------------------------------------------------
pymirc_path = os.path.join('..', '..')
if not pymirc_path in sys.path: sys.path.append(pymirc_path)
import pymirc.fileio as pymf
import pymirc.viewer as pymv

import numpy as np
import pylab as py

import nibabel as nib

data_dir = '/users/nexuz/gschra2/tmp/data_sets/ibsi_1_ct_radiomics_phantom'

# read CT vol that was used to generate ROIs in rtstruct file
ct_dcm = pymf.DicomVolume(os.path.join(data_dir, 'dicom', 'image', '*.dcm'))
ct_vol = ct_dcm.get_data()

aff = ct_dcm.affine
shape = ct_vol.shape

#-------------------------------------------------------------------------------------
# read the rt struct data
rtstruct_file = os.path.join(data_dir, 'dicom', 'mask', 'DCM_RS_00060.dcm')

# read the ROI contours (in world coordinates)
contour_data = pymf.read_rtstruct_contour_data(rtstruct_file)

# convert contour data to index arrays (voxel space)
# in this example we have to ignore the orientation of the saved 2D contours (polygons)
roi_inds = pymf.convert_contour_data_to_roi_indices(
Ejemplo n.º 3
0
import numpy as np
import pylab as py

# check of data is there
data_dir = os.path.join('..', '..', 'data', 'nema_petct')

if not os.path.exists(data_dir):
    url = 'https://kuleuven.box.com/s/wub9pk0yvt8kjqyj7p0bz11boca4334x'
    print('please first download example PET/CT data from:')
    print(url)
    print('and unzip into: ', data_dir)
    sys.exit()

# read CT vol that was used to generate ROIs in rtstruct file
ct_dcm = pymf.DicomVolume('../../data/nema_petct/CT/*.dcm')
ct_vol = ct_dcm.get_data()

aff = ct_dcm.affine
shape = ct_vol.shape

#-------------------------------------------------------------------------------------
# read the rt struct data
rtstruct_file = '../../data/nema_petct/rois/nonconvex_rtstruct.dcm'

# read the ROI contours (in world coordinates)
contour_data = pymf.read_rtstruct_contour_data(rtstruct_file)

# convert contour data to index arrays (voxel space)
roi_inds = pymf.convert_contour_data_to_roi_indices(contour_data, aff, shape)
Ejemplo n.º 4
0
if output_dir is None:
    output_dir = os.path.splitext(input_file)[0] + '_interpolated'

# check if output dir already exists
while os.path.exists(output_dir):
    output_dir += '_1'

# load the list of dicom tags to copy from the reference header from an input text file
with open(args.dcm_tag_file, 'r') as f:
    tags_to_copy = [x.strip() for x in f.read().splitlines()]

#--------------------------------------------------------------------------------
#--------------------------------------------------------------------------------

dcm = pf.DicomVolume(input_file)
vol = dcm.get_data()
aff = dcm.affine

# interpolate the volume to the target voxelsize using trilinear interpolation
vol_interp = pi.zoom3d(vol, dcm.voxsize / target_voxsize)

# generate the new affine of the interpolated array
aff_interp = aff.copy()
aff_interp[:, 0] *= (target_voxsize[0] / dcm.voxsize[0])
aff_interp[:, 1] *= (target_voxsize[1] / dcm.voxsize[1])
aff_interp[:, 2] *= (target_voxsize[2] / dcm.voxsize[2])

aff_interp[:-1, 3] = aff[:-1, -1] - 0.5 * dcm.voxsize + 0.5 * target_voxsize

# create the dictionary of tags and values that are copied from the reference dicom header
Ejemplo n.º 5
0
from scipy.ndimage import find_objects

import argparse

parser = argparse.ArgumentParser(
    description='NEMA small animal IQ scan analyzer')
parser.add_argument('dcm_dir', help='absolute path of input dicom directory')
parser.add_argument('--phantom',
                    help='phantom version',
                    choices=['standard', 'mini'],
                    default='standard')
args = parser.parse_args()

# read the PET volume from dicom
dcm = pf.DicomVolume(args.dcm_dir)
vol = dcm.get_data()

# align the PET volume to "standard" space (a digitial version of the phantom)
vol_aligned = nsa.align_nema_2008_small_animal_iq_phantom(vol,
                                                          dcm.voxsize,
                                                          version=args.phantom)

# generate the ROI label volume
roi_vol = nsa.nema_2008_small_animal_pet_rois(vol_aligned,
                                              dcm.voxsize,
                                              phantom=args.phantom)

# generate the report
nsa.nema_2008_small_animal_iq_phantom_report(vol_aligned, roi_vol)
Ejemplo n.º 6
0
import sys, os
pymirc_path = os.path.join('..','..')
if not pymirc_path in sys.path: sys.path.append(pymirc_path)

import pymirc.fileio as pymf
import pymirc.viewer as pymv

from argparse import ArgumentParser

parser = ArgumentParser()
parser.add_argument('dcm_path', help = 'dicom folder')
parser.add_argument('--dcm_pat', default = '*.dcm')
parser.add_argument('--overlay_tag', default = 0x6002)

args       = parser.parse_args()

#---------------------------------------------------------------------

dcm_data = pymf.DicomVolume(os.path.join(args.dcm_path,args.dcm_pat))
vol      = dcm_data.get_data()

voxsize = dcm_data.voxsize

oli = dcm_data.get_3d_overlay_img(tag=args.overlay_tag)

vi = pymv.ThreeAxisViewer([vol,oli], voxsize = voxsize)
Ejemplo n.º 7
0
def wb_nema_iq():
    parser = argparse.ArgumentParser(description='NEMA WB IQ scan analyzer')

    parser.add_argument('dcm_dir',
                        help='absolute path of input dicom directory')
    parser.add_argument('--dcm_pattern',
                        default='*',
                        help='file pattern for files in the dcm_dir')
    parser.add_argument(
        '--fwhm_mm',
        default=0,
        help=
        'FWHM (mm) of Gaussian filter applied to the input volumes before the analysis',
        type=float)
    parser.add_argument(
        '--radii_mm',
        default=[None],
        help=
        'The radii (mm) of the 6 spheres (seperated by blanks)). If not given this is set the values "18.5 14.0 11.0 8.5 6.5 5.0" are used',
        nargs='+')
    parser.add_argument(
        '--signal',
        default=None,
        help=
        'Fixed signal in [Bq/ml] (or the units of the volume) used when fitting all spheres. If not provided, the fitted value from the biggest sphere is used for all spheres',
        type=float)
    parser.add_argument(
        '--wall_mm',
        default=1.5,
        help='Fixed glass wall thickness (mm). If not provided 1.5mm is used.',
        type=float)
    parser.add_argument('--earl',
                        default=2,
                        help='EARL version to use for limits in plots',
                        type=int,
                        choices=[1, 2])
    parser.add_argument(
        '--true_act_conc',
        default=None,
        help=
        'True activity concentration in the spheres in [Bq/ml] (or the units of the volume). If not given, it is obtained from the fitted signal of the biggest sphere.',
        type=float)
    parser.add_argument('--output_dir',
                        help='name of the output directory',
                        default=None)
    parser.add_argument('--show', help='show the results', action='store_true')
    parser.add_argument('--verbose',
                        help='print (extra) verbose output',
                        action='store_true')

    args = parser.parse_args()

    #-------------------------------------------------------------------------------------------------
    # load modules
    import matplotlib.pyplot as plt
    from scipy.ndimage import gaussian_filter

    import pymirc.fileio as pmf
    import pymirc.viewer as pv

    import pynemaiqpet
    import pynemaiqpet.nema_wb as nema

    #-------------------------------------------------------------------------------------------------
    # parse input parameters

    dcm_dir = args.dcm_dir
    dcm_pattern = args.dcm_pattern
    sm_fwhm_mm = args.fwhm_mm
    Rfix = args.radii_mm
    Sfix = args.signal
    dfix = args.wall_mm
    earlversion = args.earl
    true_act_conc = args.true_act_conc
    output_dir = args.output_dir
    show = args.show
    verbose = args.verbose

    if Rfix[0] is None:
        Rfix = [18.5, 14.0, 11.0, 8.5, 6.5, 5.]
    elif Rfix[0] == 'fit':
        Rfix = None
    else:
        if len(Rfix) != 6:
            raise ValueError(
                'When manually specifying the sphere radii, 6 values must be given.'
            )
        Rfix = [float(x) for x in Rfix]

    #-------------------------------------------------------------------------------------------------
    # load the dicom volume

    dcm = pmf.DicomVolume(os.path.join(dcm_dir, dcm_pattern))
    vol = dcm.get_data()
    voxsize = dcm.voxsize

    # post smooth the image
    if sm_fwhm_mm > 0:
        vol = gaussian_filter(vol, sigma=sm_fwhm_mm / (2.35 * voxsize))

    #-------------------------------------------------------------------------------------------------
    # do a fit where we force all spheres to have the same signal (assuming that the activity
    # concentration in all sphere was the same)

    # try also doing the fit without fixing the radii
    fitres, sphere_results = nema.fit_WB_NEMA_sphere_profiles(vol,
                                                              voxsize,
                                                              sameSignal=True,
                                                              Rfix=Rfix,
                                                              Sfix=Sfix,
                                                              dfix=dfix,
                                                              showBGROI=True)

    if verbose:
        print('fit with same signal and fixed radii')
        print(sphere_results)

    if output_dir is not None:
        os.makedirs(output_dir, exist_ok=True)
        sphere_results.to_csv(os.path.join(output_dir, 'fit_results.csv'))

    #-------------------------------------------------------------------------------------------------
    # show the results

    fig = nema.show_WB_NEMA_profiles(fitres)

    # plot the max and a50 recoveries
    # the 2nd argument should be the true (expected) activity concentration in the spheres
    # and also show the limits given by EARL (vesion 2)

    if true_act_conc == None:
        true_act_conc = sphere_results['signal'].values[0]

    fig2 = nema.show_WB_NEMA_recoveries(sphere_results,
                                        true_act_conc,
                                        earlversion=earlversion)

    # show the volume
    vi = pv.ThreeAxisViewer(vol, voxsize=voxsize)

    # save plots
    if output_dir is not None:
        fig.savefig(os.path.join(output_dir, 'sphere_profiles.pdf'))
        fig2.savefig(os.path.join(output_dir, 'recoveries.pdf'))
        vi.fig.savefig(os.path.join(output_dir, 'volume.png'))

    if show:
        plt.show()
Ejemplo n.º 8
0
if not pymirc_path in sys.path: sys.path.append(pymirc_path)

import numpy as np
import nibabel as nib
import pymirc.fileio as pymf

from glob import glob

nii = nib.load('../../data/TestRTstruct/Multimask.nii.gz')
nii = nib.as_closest_canonical(nii)
roi_vol_ras = nii.get_data()[:, :, :, 0, 0, 0]
roi_vol = np.flip(roi_vol_ras, (0, 1))

aff_ras = nii.affine.copy()
aff = aff_ras.copy()

aff[0, -1] = (-1 * aff_ras @ np.array([roi_vol.shape[0] - 1, 0, 0, 1]))[0]
aff[1, -1] = (-1 * aff_ras @ np.array([0, roi_vol.shape[1] - 1, 0, 1]))[1]

ct_files = glob('../../data/TestRTstruct/CT/*.dcm')
ct_dcm = pymf.DicomVolume(ct_files)
ct = ct_dcm.get_data()
refdcm_file = ct_files

#------------------------------------------------------------
pymf.labelvol_to_rtstruct(roi_vol,
                          aff,
                          refdcm_file,
                          '../../data/TestRTstruct/t_rtstruct.dcm',
                          tags_to_add={'SpecificCharacterSet': 'ISO_IR 192'})
Ejemplo n.º 9
0
earlversion = args.earl_version

dcm_dir_pattern = args.dcm_dir_pattern
dcm_file_pattern = args.dcm_file_pattern

#------------------------------------------------------------------------------------------------

dcm_dirs = glob(dcm_dir_pattern)

for dcm_dir in dcm_dirs:

    print(os.path.basename(dcm_dir))

    # load example data set included in package
    dcm_pattern = os.path.join(dcm_dir, dcm_file_pattern)
    dcm = pmf.DicomVolume(dcm_pattern)
    vol = dcm.get_data()
    voxsize = dcm.voxsize

    # FWHM of Gaussian kernel to apply before analysis
    dcm_hdr = dcm.firstdcmheader

    # check if the data was already post-smoothed by analysing private GE tags
    # for trans-axial and axial filter
    if ((dcm_hdr[0x0009, 0x10ba].value == 0) and
        (dcm_hdr[0x0009, 0x10db].value == 0)) or force_smoothing:
        sm_str = f'_{sm_fwhm_mm}mm_ps'
    else:
        sm_fwhm_mm = 0
        sm_str = ''
parser.add_argument('--output_dir',         help = 'name of the output master directory, default = studyID', default = None)
parser.add_argument('--output_subdir',      help = 'name of the output sub directory, default = "kul_ct_recon"', default = 'kul_ct_recon')
parser.add_argument('--ref_dcm_pat',        help = 'file pattern for reference dicom files', default = '*')
parser.add_argument('--dcm_tag_file',       help = 'txt file with dcm tags to copy', 
                    default = 'ct_dcm_tags_to_copy.txt')
parser.add_argument('--kul_var_name',       help = 'name of recon variable in sav file', default = 'reconbone')
parser.add_argument('--series_desc_prefix', help = 'prefix for dcm series description', 
                    default = '(UZL motion corrected)')

args       = parser.parse_args()

#-----------------------------------------------------------------------------------------

# read the reference dicom volume
ref_dcm = pymf.DicomVolume(os.path.join(args.ref_dcm_dir, args.ref_dcm_pat))
ref_vol = ref_dcm.get_data()

# restore the KUL recon from save file
kul_recon = readsav(args.kul_sav_file)[args.kul_var_name]

# due to the memory conventions we have to reverse the axis order
kul_recon = np.swapaxes(kul_recon, 0, 2)

# to get the KUL recon in LPS we have to reverse the last axix
kul_recon = np.flip(kul_recon,2)

# load the list of dicom tags to copy from the reference header from an input text file
with open(args.dcm_tag_file,'r') as f:
  tags_to_copy = [x.strip() for x in f.read().splitlines()]
Ejemplo n.º 11
0
parser.add_argument('--output_fname',
                    help='output file name',
                    default='labelarray.nii')
parser.add_argument('--dcm_pattern',
                    help='dicom pattern for files in dicom directory',
                    default='*')

args = parser.parse_args()
dcm_vol_dir = args.dcm_vol_dir
rtstruct_file = args.rtstruct_file
output_fname = args.output_fname
dcm_pattern = args.dcm_pattern

#-------------------------------------------------------------------------------------
# read CT vol that was used to generate ROIs in rtstruct file
ct_dcm = pymf.DicomVolume(os.path.join(dcm_vol_dir, dcm_pattern))
ct_vol = ct_dcm.get_data()

aff = ct_dcm.affine
shape = ct_vol.shape

#-------------------------------------------------------------------------------------
# read the rt struct data

# read the ROI contours (in world coordinates)
contour_data = pymf.read_rtstruct_contour_data(rtstruct_file)

# convert contour data to index arrays (voxel space)
roi_inds = pymf.convert_contour_data_to_roi_indices(contour_data, aff, shape)

#---------------------------------------------------------------------------