Exemple #1
0
import pylab as py

pymirc_path = os.path.join('..', '..')
if not pymirc_path in sys.path: sys.path.append(pymirc_path)

import pymirc.viewer as pymv
import pymirc.image_operations as pymi

# seed random genrator for random deformation field
np.random.seed(2)

# setup demo volume
n0 = 120
n1 = 110
n2 = 100

x0, x1, x2 = np.meshgrid(np.arange(n0), np.arange(n1), np.arange(n2))

vol = np.pad(0.5 * ((((-1)**(x0 // 6)) * ((-1)**(x1 // 6)) *
                     ((-1)**(x2 // 6))) + 1),
             5,
             mode='constant')

# generate random deformation field
d0, d1, d2 = pymi.random_deformation_field(vol.shape, shift=2.5)

# apply warping
warped_vol = pymi.backward_3d_warp(vol, d0, d1, d2)

vi = pymv.ThreeAxisViewer([vol, warped_vol, np.sqrt(d0**2 + d1**2 + d2**2)])
Exemple #2
0
pred_bows = []

for model_name in model_names:
  # load the model
  model = load_model(os.path.join('../../data/trained_models', model_name))
  
  # make the prediction
  x = [np.expand_dims(np.expand_dims(pet,0),-1), np.expand_dims(np.expand_dims(mr,0),-1)]
  pred_bow = model.predict(x).squeeze()
  pred_bows.append(pred_bow)  

fig, ax = py.subplots(1,len(model_names), figsize = (len(model_names)*4,4), sharey = True)
for i, pred_bow in enumerate(pred_bows):

  ax[i].plot(img[:,n//2,n//2], label = 'gt')
  ax[i].plot(pet[:,n//2,n//2], label = 'osem')
  ax[i].plot(pred_bow[:,n//2,n//2], label = 'pred')
  ax[i].set_title(model_names[i], fontsize = 'small')
  ax[i].grid(ls = ':')

ax[0].legend()
fig.tight_layout()
fig.show()

import pymirc.viewer as pv
ims = {'vmin': 0, 'vmax':1.2}
pv.ThreeAxisViewer([img,pet,mr,], 
                   imshow_kwargs = ims, ls = '', sl_x = 68, sl_y = 63, sl_z = 64)
pv.ThreeAxisViewer(pred_bows, imshow_kwargs = ims, ls = '', sl_x = 68, sl_y = 63, sl_z = 64)
print('sum of abs. diff between ref. mask and read mask: ',
      np.abs(nii_mask - roi_vol).sum())
print('')

#---------------------------------------------------------------------------
# print some ROI statistics

print('ROI name.....:', [x['ROIName'] for x in contour_data])
print('ROI number...:', [x['ROINumber'] for x in contour_data])
print('ROI mean.....:', [ct_vol[x].mean() for x in roi_inds])
print('ROI max......:', [ct_vol[x].max() for x in roi_inds])
print('ROI min......:', [ct_vol[x].min() for x in roi_inds])
print('ROI # voxel..:', [len(ct_vol[x]) for x in roi_inds])

#---------------------------------------------------------------------------
# view the results
imshow_kwargs = {'cmap': py.cm.Greys_r, 'vmin': -500, 'vmax': 500}
oimshow_kwargs = {
    'cmap': py.cm.nipy_spectral,
    'alpha': 0.3,
    'vmax': 1.2 * roi_vol.max()
}
vi = pymv.ThreeAxisViewer([ct_vol, ct_vol],
                          ovols=[None, roi_vol],
                          voxsize=ct_dcm.voxsize,
                          imshow_kwargs=imshow_kwargs,
                          oimshow_kwargs=oimshow_kwargs)

print('\nPress "a" to hide/show overlay')
Exemple #4
0
mfac = np.percentile(mr, 99.99)

mlem /= pfac
bow /= pfac
mr /= mfac

# predictions with original data
p1 = model.predict([
    np.expand_dims(np.expand_dims(mlem, 0), -1),
    np.expand_dims(np.expand_dims(mr, 0), -1)
]).squeeze()

# prediction of 6mm res osem data
mlem_5mm = gaussian_filter(mlem,
                           np.sqrt(5**2 - 4.5**2) / (2.35 * training_voxsize))
mlem_6mm = gaussian_filter(mlem,
                           np.sqrt(6**2 - 4.5**2) / (2.35 * training_voxsize))

p5 = model.predict([
    np.expand_dims(np.expand_dims(mlem_5mm, 0), -1),
    np.expand_dims(np.expand_dims(mr, 0), -1)
]).squeeze()
p6 = model.predict([
    np.expand_dims(np.expand_dims(mlem_6mm, 0), -1),
    np.expand_dims(np.expand_dims(mr, 0), -1)
]).squeeze()

imshow_kwargs = 4 * [{'vmin': 0, 'vmax': 1.2}]
#pymv.ThreeAxisViewer([p1,p5,p6,bow], imshow_kwargs = imshow_kwargs)
pymv.ThreeAxisViewer([mlem, mlem_5mm, mlem_6mm], imshow_kwargs=imshow_kwargs)
  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}

print('\nPress "a" to hide/show overlay')

vi = pymv.ThreeAxisViewer([pet_vol_ct_grid,ct_vol,ct_vol], 
                          ovols = [None, None, pet_vol_ct_grid],
                          voxsize = ct_dcm.voxsize, imshow_kwargs = imshow_kwargs,
                          oimshow_kwargs = oimshow_kwargs)
# write the dicom volume
if not os.path.exists(output_dcm_dir):
    write_3d_static_dicom(pred,
                          output_dcm_dir,
                          affine=o_aff,
                          ReconstructionMethod='CNN MAP Bowsher',
                          SeriesDescription=f'CNN MAP Bowsher {model_name}',
                          **dcm_kwargs)
else:
    warn('Output dicom directory already exists. Not ovewrting it')

#------------------------------------------------------------------
# show the results
import pymirc.viewer as pv
pmax = np.percentile(pred, 99.9)
mmax = np.percentile(mr_preproc, 99.9)

ims = [{
    'vmin': 0,
    'vmax': mmax,
    'cmap': py.cm.Greys_r
}, {
    'vmin': 0,
    'vmax': pmax
}, {
    'vmin': 0,
    'vmax': pmax
}]
pv.ThreeAxisViewer([mr_preproc, pet_preproc, pred], imshow_kwargs=ims)
Exemple #7
0
#----------------------------------------------------------------------------

# parse the command line
parser = argparse.ArgumentParser()
parser.add_argument('ct_file',  help = 'CT nrrd file')
parser.add_argument('seg_file', help = 'segmentation nrrd file')
args = parser.parse_args()

ct, ct_hdr   = nrrd.read(args.ct_file)
seg, seg_hdr = nrrd.read(args.seg_file)

ct_aff  = get_lps_affine_from_hdr(ct_hdr)
seg_aff = get_lps_affine_from_hdr(seg_hdr) 

ct_voxsize = np.sqrt((ct_aff**2).sum(0))[:-1]

# the segmentation array usually a crop of the original array
seg_offset = np.round((seg_aff[:-1,-1] - ct_aff[:-1,-1]) / np.diag(ct_aff)[:3]).astype(int)
seg2 = np.zeros(ct.shape, dtype = np.int8)
seg2[seg_offset[0]:(seg_offset[0]+seg.shape[0]), seg_offset[1]:(seg_offset[1]+seg.shape[1]),
     seg_offset[2]:(seg_offset[2]+seg.shape[2])] = seg
seg2_aff = ct_aff.copy()

# reorient the images to standard LPS orientation
ct, ct_aff = pi.reorient_image_and_affine(ct, ct_aff)
seg2, seg2_aff = pi.reorient_image_and_affine(seg2, seg2_aff)

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

pv.ThreeAxisViewer([ct,seg2], voxsize = ct_voxsize, imshow_kwargs = imshow_kwargs)
Exemple #8
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)
Exemple #9
0
  pet_affine, mr_affine, training_voxsize, perc = 99.99, coreg = coreg_inputs, crop_mr = crop_mr)


#------------------------------------------------------------------
# the actual CNN prediction
x = [np.expand_dims(np.expand_dims(pet_preproc,0),-1), np.expand_dims(np.expand_dims(mr_preproc,0),-1)]
pred = model.predict(x).squeeze()

# undo the intensity normalization
pet_preproc *= pet_max
mr_preproc  *= mr_max
pred        *= pet_max


#------------------------------------------------------------------
# save the preprocessed input and output
nib.save(nib.Nifti1Image(pet_preproc, o_aff), 'pet_preproc.nii')
nib.save(nib.Nifti1Image(mr_preproc, o_aff), 'mr_preproc.nii')
nib.save(nib.Nifti1Image(pred, o_aff), output_fname)


# show the results
import pymirc.viewer as pv
pmax = np.percentile(pred,99.9)
mmax = np.percentile(mr_preproc,99.9)

ims = [{'vmin':0, 'vmax': mmax, 'cmap': py.cm.Greys_r}, 
       {'vmin':0, 'vmax': pmax}, {'vmin':0, 'vmax': pmax}]
pv.ThreeAxisViewer([np.flip(mr_preproc,(0,1)),np.flip(pet_preproc,(0,1)),np.flip(pred,(0,1))],
                   imshow_kwargs = ims)
Exemple #10
0
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)

# show the aligned volume and the ROI volume (cropped versions)
th = 0.3 * np.percentile(vol_aligned, 99.9)
bbox = find_objects(vol_aligned > th)[0]
vi = pv.ThreeAxisViewer([vol_aligned[bbox], vol_aligned[bbox]],
                        [None, roi_vol[bbox]**0.1],
                        voxsize=dcm.voxsize,
                        ls='')
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()
Exemple #12
0
ct_vol = ct_dcm.get_data()
ct_vol[ct_vol < -1024] = -1024

# we artificially rotate and shift the CT for the regisration of the PET
rp = np.array([10, -5, -20, 0.1, 0.2, -0.1])
R = pymi.kul_aff(rp, origin=np.array(ct_vol.shape) / 2)
ct_vol_rot = pymi.aff_transform(ct_vol, R, ct_vol.shape, cval=ct_vol.min())

pet_coreg, coreg_aff, coreg_params = pymi.rigid_registration(
    pet_vol, ct_vol_rot, pet_dcm.affine, ct_dcm.affine)

imshow_kwargs = [{
    'cmap': py.cm.Greys_r,
    'vmin': -200,
    'vmax': 200
}, {
    'cmap': py.cm.Greys_r,
    'vmin': -200,
    'vmax': 200
}, {
    'cmap': py.cm.Greys,
    'vmin': 0,
    'vmax': np.percentile(pet_coreg, 99.9)
}]

vi = pymv.ThreeAxisViewer([ct_vol_rot, ct_vol_rot, pet_coreg],
                          ovols=[None, pet_coreg, None],
                          imshow_kwargs=imshow_kwargs,
                          voxsize=ct_dcm.voxsize,
                          width=6)