pyushkevich / itksnap

ITK-SNAP medical image segmentation tool
http://www.itksnap.org
GNU General Public License v3.0
294 stars 86 forks source link

Orientation issues with dummy nifti files #7

Closed atylermorgan closed 4 years ago

atylermorgan commented 4 years ago

I am trying to load dummy nifti files (zeros everywhere other than the edges to create a volume border) into ITK-SNAP and overlay them on an anatomical scan. However, I am running into an issue where ITK-SNAP does not read the orientation information correctly if the dummy scan is overlaid on an anatomical. The scans instead look very large by comparison.

I am creating the dummy scans with nibabel and applying a rotation matrix to the qform, as I thought ITK-SNAP needs to read it. Here is the nibabel code to produce the scans:

# create outline data
data = np.ones((192, 192, 27))
data[1:-1, 1:-1, 1:-1] = 0   # creates an outline only

# create blank rotation with scaling
rot = np.diag([0.8, 0.8, 0.8, 1])

# create a nifti image and save
img = nb.Nifti1Image(data, rot)
img.set_qform(img.affine, 1)
img.set_sform(img.affine, 1)
img.to_filename('epi.nii.gz')

And the nifti headers that are produced look like this:

sizeof_hdr      : 348
data_type       : b''
db_name         : b''
extents         : 0
session_error   : 0
regular         : b''
dim_info        : 0
dim             : [  4 192 192  27   1   1   1   1]
intent_p1       : 0.0
intent_p2       : 0.0
intent_p3       : 0.0
intent_code     : none
datatype        : float64
bitpix          : 64
slice_start     : 0
pixdim          : [1.  0.8 0.8 0.8 1.  1.  1.  1. ]
vox_offset      : 0.0
scl_slope       : nan
scl_inter       : nan
slice_end       : 0
slice_code      : unknown
xyzt_units      : 0
cal_max         : 0.0
cal_min         : 0.0
slice_duration  : 0.0
toffset         : 0.0
glmax           : 0
glmin           : 0
descrip         : b''
aux_file        : b''
qform_code      : scanner
sform_code      : scanner
quatern_b       : 0.0
quatern_c       : 0.0
quatern_d       : 0.0
qoffset_x       : 0.0
qoffset_y       : 0.0
qoffset_z       : 0.0
srow_x          : [0.8 0.  0.  0. ]
srow_y          : [0.  0.8 0.  0. ]
srow_z          : [0.  0.  0.8 0. ]
intent_name     : b''
magic           : b'n+1'

Am I missing something that also needs added for ITK-SNAP to properly read these files?

pyushkevich commented 4 years ago

Could you run c3d epi.nii.gz -info-full to see if the header is being correctly read?

Thanks!

On Mon, Jan 13, 2020 at 1:10 PM Andrew Morgan notifications@github.com wrote:

I am trying to load dummy nifti files (zeros everywhere other than the edges to create a volume border) into ITK-SNAP and overlay them on an anatomical scan. However, I am running into an issue where ITK-SNAP does not read the orientation information correctly if the dummy scan is overlaid on an anatomical. The scans instead look very large by comparison.

I am creating the dummy scans with nibabel and applying a rotation matrix to the qform, as I thought ITK-SNAP needs to read it. Here is the nibabel code to produce the scans:

create outline data

data = np.ones((192, 192, 27)) data[1:-1, 1:-1, 1:-1] = 0 # creates an outline only

create blank rotation with scaling

rot = np.diag([0.8, 0.8, 0.8, 1])

create a nifti image and save

img = nb.Nifti1Image(data, rot) img.set_qform(img.affine, 1) img.set_sform(img.affine, 1) img.to_filename('epi.nii.gz')

And the nifti headers that are produced look like this:

sizeof_hdr : 348 data_type : b'' db_name : b'' extents : 0 session_error : 0 regular : b'' dim_info : 0 dim : [ 4 192 192 27 1 1 1 1] intent_p1 : 0.0 intent_p2 : 0.0 intent_p3 : 0.0 intent_code : none datatype : float64 bitpix : 64 slice_start : 0 pixdim : [1. 0.8 0.8 0.8 1. 1. 1. 1. ] vox_offset : 0.0 scl_slope : nan scl_inter : nan slice_end : 0 slice_code : unknown xyzt_units : 0 cal_max : 0.0 cal_min : 0.0 slice_duration : 0.0 toffset : 0.0 glmax : 0 glmin : 0 descrip : b'' aux_file : b'' qform_code : scanner sform_code : scanner quatern_b : 0.0 quatern_c : 0.0 quatern_d : 0.0 qoffset_x : 0.0 qoffset_y : 0.0 qoffset_z : 0.0 srow_x : [0.8 0. 0. 0. ] srow_y : [0. 0.8 0. 0. ] srow_z : [0. 0. 0.8 0. ] intent_name : b'' magic : b'n+1'

Am I missing something that also needs added for ITK-SNAP to properly read these files?

— You are receiving this because you are subscribed to this thread. Reply to this email directly, view it on GitHub https://github.com/pyushkevich/itksnap/issues/7?email_source=notifications&email_token=AAJPEW5MEBYHQDVYBNCR6R3Q5SVC5A5CNFSM4KGGRGDKYY3PNVWWK3TUL52HS4DFUVEXG43VMWVGG33NNVSW45C7NFSM4IF2RLPQ, or unsubscribe https://github.com/notifications/unsubscribe-auth/AAJPEW7MPM6KQTFM5YDS56LQ5SVC5ANCNFSM4KGGRGDA .

-- Paul A. Yushkevich, Ph.D. Associate Professor Penn Image Computing and Science Laboratory Department of Radiology University of Pennsylvania

atylermorgan commented 4 years ago

Hi Paul, thanks for your quick reply.

c3d seems to be able to read the header

>>c3d epi.nii.gz -info-full

Image #1:
  Image Dimensions   : [192, 192, 27]
  Bounding Box       : {[0 0 0], [153.6 153.6 21.6]}
  Voxel Spacing      : [0.8, 0.8, 0.8]
  Intensity Range    : [0, 1]
  Mean Intensity     : 0.0932637
  Canon. Orientation : LPI
  Direction Cos Mtx. : 
   -1.0000     0.0000     0.0000 
    0.0000    -1.0000     0.0000 
    0.0000     0.0000     1.0000 
  Voxel->RAS x-form  : 
         0.80000     -0.00000     -0.00000     -0.00000 
        -0.00000      0.80000     -0.00000     -0.00000 
         0.00000      0.00000      0.80000      0.00000 
         0.00000      0.00000      0.00000      1.00000 
  Image Metadata: 
    ITK_InputFilterName = NiftiImageIO
    bitpix = 64
    cal_max = 0
    cal_min = 0
    datatype = 64
    dim[0] = 4
    dim[1] = 192
    dim[2] = 192
    dim[3] = 27
    dim[4] = 1
    dim[5] = 1
    dim[6] = 1
    dim[7] = 1
    intent_code = 0
    intent_p1 = 0
    intent_p2 = 0
    intent_p3 = 0
    pixdim[0] = 1
    pixdim[1] = 0.8
    pixdim[2] = 0.8
    pixdim[3] = 0.8
    pixdim[4] = 1
    pixdim[5] = 1
    pixdim[6] = 1
    pixdim[7] = 1
    qform_code = 1
    qoffset_x = 0
    qoffset_y = 0
    qoffset_z = 0
    quatern_b = 0
    quatern_c = 0
    quatern_d = 0
    scl_inter = 0
    scl_slope = 1
    sform_code = 1
    slice_duration = 0
    slice_end = 0
    slice_start = 0
    srow_x = 0.8 0 0 0
    srow_y = 0 0.8 0 0
    srow_z = 0 0 0.8 0
    toffset = 0
    vox_offset = 352
pyushkevich commented 4 years ago

Yes, that looks correct. What does the header of the anatomical look like in C3D?

On Tue, Jan 14, 2020 at 5:54 AM Andrew Morgan notifications@github.com wrote:

Hi Paul, thanks for your quick reply.

c3d seems to be able to read the header

c3d epi.nii.gz -info-full

Image #1: Image Dimensions : [192, 192, 27] Bounding Box : {[0 0 0], [153.6 153.6 21.6]} Voxel Spacing : [0.8, 0.8, 0.8] Intensity Range : [0, 1] Mean Intensity : 0.0932637 Canon. Orientation : LPI Direction Cos Mtx. : -1.0000 0.0000 0.0000 0.0000 -1.0000 0.0000 0.0000 0.0000 1.0000 Voxel->RAS x-form : 0.80000 -0.00000 -0.00000 -0.00000 -0.00000 0.80000 -0.00000 -0.00000 0.00000 0.00000 0.80000 0.00000 0.00000 0.00000 0.00000 1.00000 Image Metadata: ITK_InputFilterName = NiftiImageIO bitpix = 64 cal_max = 0 cal_min = 0 datatype = 64 dim[0] = 4 dim[1] = 192 dim[2] = 192 dim[3] = 27 dim[4] = 1 dim[5] = 1 dim[6] = 1 dim[7] = 1 intent_code = 0 intent_p1 = 0 intent_p2 = 0 intent_p3 = 0 pixdim[0] = 1 pixdim[1] = 0.8 pixdim[2] = 0.8 pixdim[3] = 0.8 pixdim[4] = 1 pixdim[5] = 1 pixdim[6] = 1 pixdim[7] = 1 qform_code = 1 qoffset_x = 0 qoffset_y = 0 qoffset_z = 0 quatern_b = 0 quatern_c = 0 quatern_d = 0 scl_inter = 0 scl_slope = 1 sform_code = 1 slice_duration = 0 slice_end = 0 slice_start = 0 srow_x = 0.8 0 0 0 srow_y = 0 0.8 0 0 srow_z = 0 0 0.8 0 toffset = 0 vox_offset = 352

— You are receiving this because you commented. Reply to this email directly, view it on GitHub https://github.com/pyushkevich/itksnap/issues/7?email_source=notifications&email_token=AAJPEWYZD7MCK34CA6ALQJDQ5WKXRA5CNFSM4KGGRGDKYY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXHJKTDN5WW2ZLOORPWSZGOEI4FSLY#issuecomment-574118191, or unsubscribe https://github.com/notifications/unsubscribe-auth/AAJPEW4NLIX4HY3J3BTQF53Q5WKXRANCNFSM4KGGRGDA .

-- Paul A. Yushkevich, Ph.D. Associate Professor Penn Image Computing and Science Laboratory Department of Radiology University of Pennsylvania

atylermorgan commented 4 years ago

Sorry I am just getting back to this. I think the anatomical looks normal, too:

>>c3d anatomical.nii.gz -info-full

Image #1:
  Image Dimensions   : [192, 256, 256]
  Bounding Box       : {[83.1509 96.8613 -203.687], [275.151 352.861 52.3129]}
  Voxel Spacing      : [1, 1, 1]
  Intensity Range    : [0, 656]
  Mean Intensity     : 56.3744
  Canon. Orientation : Oblique, closest to LPI
  Direction Cos Mtx. : 
   -0.9980     0.0257     0.0575 
   -0.0209    -0.9963     0.0830 
    0.0594     0.0816     0.9949 
  Voxel->RAS x-form  : 
         0.99802     -0.02571     -0.05746    -83.15088 
         0.02089      0.99633     -0.08298    -96.86130 
         0.05938      0.08162      0.99489   -203.68710 
         0.00000      0.00000      0.00000      1.00000 
  Image Metadata: 
    ITK_FileNotes = TE=3;Time=104719.758;phase=1
    ITK_InputFilterName = NiftiImageIO
    bitpix = 16
    cal_max = 0
    cal_min = 0
    datatype = 4
    descrip = TE=3;Time=104719.758;phase=1
    dim[0] = 3
    dim[1] = 192
    dim[2] = 256
    dim[3] = 256
    dim[4] = 1
    dim[5] = 1
    dim[6] = 1
    dim[7] = 1
    dim_info = 6
    intent_code = 0
    intent_p1 = 0
    intent_p2 = 0
    intent_p3 = 0
    pixdim[0] = 1
    pixdim[1] = 1
    pixdim[2] = 1
    pixdim[3] = 1
    pixdim[4] = 2.3
    pixdim[5] = 0
    pixdim[6] = 0
    pixdim[7] = 0
    qform_code = 1
    qoffset_x = -83.1509
    qoffset_y = -96.8613
    qoffset_z = -203.687
    quatern_b = 0.0412066
    quatern_c = -0.0292501
    quatern_d = 0.0116661
    scl_inter = 0
    scl_slope = 1
    sform_code = 1
    slice_duration = 0
    slice_end = 0
    slice_start = 0
    srow_x = 0.998017 -0.0257113 -0.05746 -83.1509
    srow_y = 0.0208901 0.996332 -0.0829848 -96.8613
    srow_z = 0.0593829 0.0816199 0.994893 -203.687
    toffset = 0
    vox_offset = 352

I tried changing a few things in the epi.nii.gz header after looking at the EPI header of a file that I recorded at the scanner, but nothing seemed to help.

atylermorgan commented 4 years ago

I also tried starting from a c3d image rather than from nibabel:

c3d -background 1.0 -create 192x192x27 0.8x0.8x0.8mm -origin 0x0x0mm -o epi_c3d.nii.gz

I then changed the image values to make it a box outline (ones at the edges, zeros everywhere else):

import nibabel as nb

img = nb.load('epi_c3d.nii.gz')
data = img.get_fdata()

data[1:-1,1:-1,1:-1] = 0.0
img2 = nb.Nifti1Image(data, img.affine)
img2.to_filename('epi_c3d.nii.gz')

The resulting images still have the same issue:

itksnap -g anatomical.nii.gz -o epi_c3d.nii.gz

image

The whole image looks pink because the EPI is rendered in the spring colormap. Alternatively, if I load the images in the opposite order, things work fine:

itksnap -g epi_c3d.nii.gz -o anatomical.nii.gz

image

Any ideas on why this is happening would be greatly appreciated!

atylermorgan commented 4 years ago

This issue has been resolved. I now realize that when the "EPI" is overlaid on an anatomical, the entire field of view is rendered in the colormap, which is why it looked like the EPI covered the entire field of view. My solution was therefore to fill the interior of the EPI scan with a non-zero value and then adjust the contrast on the overlaid image to not include zero. This caused the areas outside the EPI to become transparent and allowed me to use the registration tool to move the scan around.

image

Thanks!