Skip to content

Instantly share code, notes, and snippets.

@pangyuteng
Last active June 20, 2023 05:53
Show Gist options
  • Save pangyuteng/d4b5e3d931a26075dd88965f183a8e3b to your computer and use it in GitHub Desktop.
Save pangyuteng/d4b5e3d931a26075dd88965f183a8e3b to your computer and use it in GitHub Desktop.
nifti-origin-orientation
images/*
images.zip
itk.nii.gz
ok/*

nifti-origin-orientation

summary

This gist was created to investigate and verify the patient orientation & position of nifti files created using libraries: dcm2niix and simpleitk (ITK) from the same DICOM files. Code is executed within a Docker container to ensure reproducibility. Additionally, 3dinfo from afni was executed to see how afni handles these nifti files, specifically the physical extents of the loaded 3d volume.

  • run below to build the container, download images from tcia, generate nifti files (with dcm2niix, simpleitk), print affine matrix from nifti files (nibabel) and print nifti info with afni 3dinfo.
bash run.sh
  • sample print out from run.sh using a series pulled from TCIA with oblique axial slices. Differences are observed between the affine transforms from the 2 nifti files, physical extent printout from afni's 3dinfo are also different.
first image (instance 1):
(0020,0013) IS [1]                                      #   2, 1 InstanceNumber
(0020,0037) DS [1\0\0\0\0.99452189536827\-0.1045284632677] #  42, 6 ImageOrientationPatient
(0020,0032) DS [-108.2880859375\-282.97187247236\115.51118721324] #  48, 3 ImagePositionPatient
last image (instance 72):
(0020,0013) IS [72]                                     #   2, 1 InstanceNumber
(0020,0037) DS [1\0\0\0\0.99452189536827\-0.1045284632677] #  42, 6 ImageOrientationPatient
(0020,0032) DS [-108.2880859375\-282.97187247236\281.57518721324] #  48, 3 ImagePositionPatient


affine matrix of nifti file generate by sitk:
[[-0.424 -0.000 -0.000  108.288]
 [ 0.000 -0.422 -0.244  282.972]
 [ 0.000 -0.044  2.326  115.511]
 [ 0.000  0.000  0.000  1.000]]
shape: (512, 512, 72)
physical coordinates | image coordinates:
[ 108.288  282.972  115.511  1.000] | [0, 0, 0, 1]
[ 108.193  67.582  92.873  1.000] | [0, 511, 0, 1]
[-108.288  283.067  115.516  1.000] | [511, 0, 0, 1]
[-108.384  67.678  92.878  1.000] | [511, 511, 0, 1]
[ 108.284  265.613  280.665  1.000] | [0, 0, 71, 1]
[ 108.189  50.224  258.027  1.000] | [0, 511, 71, 1]
[-108.292  265.709  280.670  1.000] | [511, 0, 71, 1]
[-108.387  50.319  258.032  1.000] | [511, 511, 71, 1]

Dataset File:    /workdir/itk.nii.gz
Identifier Code: NII_GT2VqUf8zzMwesHiW2tNSg  Creation Date: Mon Apr 26 23:02:40 2021
Template Space:  ORIG
Dataset Type:    Anat Bucket (-abuc)
Byte Order:      LSB_FIRST {assumed} [this CPU native = LSB_FIRST]
Storage Mode:    NIFTI
Storage Space:   37,748,736 (38 million) bytes
Geometry String: "MATRIX(0.423828,0,0,-108.2881,0,0.421506,0.244485,-282.9719,0,-0.044302,2.326117,115.5112):512,512,72"
Data Axes Tilt:  Oblique (6.000 deg. from plumb)
Data Axes Approximate Orientation:
  first  (x) = Right-to-Left
  second (y) = Anterior-to-Posterior
  third  (z) = Inferior-to-Superior   [-orient RAI]
R-to-L extent:  -108.288 [R] -to-   108.288 [L] -step-     0.424 mm [512 voxels]
A-to-P extent:  -282.972 [A] -to-   -66.396 [A] -step-     0.424 mm [512 voxels]
I-to-S extent:   115.511 [S] -to-   281.575 [S] -step-     2.339 mm [ 72 voxels]
Number of values stored at each pixel = 1
  -- At sub-brick #0 '?' datum type is short


--
affine matrix of nifti file generate by dcm2niix:
[[-0.424  0.000  0.000  108.288]
 [ 0.000  0.426 -0.000  67.582]
 [ 0.000  0.044  2.387  92.873]
 [ 0.000  0.000  0.000  1.000]]
shape: (512, 512, 72)
physical coordinates | image coordinates:
[ 108.288  67.582  92.873  1.000] | [0, 0, 0, 1]
[ 108.288  285.351  115.511  1.000] | [0, 511, 0, 1]
[-108.288  67.582  92.873  1.000] | [511, 0, 0, 1]
[-108.288  285.351  115.511  1.000] | [511, 511, 0, 1]
[ 108.288  67.582  262.324  1.000] | [0, 0, 71, 1]
[ 108.288  285.351  284.962  1.000] | [0, 511, 71, 1]
[-108.288  67.582  262.324  1.000] | [511, 0, 71, 1]
[-108.288  285.351  284.962  1.000] | [511, 511, 71, 1]

Dataset File:    /workdir/ok/ok.nii
Identifier Code: NII__o7Symja8fyulzCu9ZQtag  Creation Date: Mon Apr 26 23:02:40 2021
Template Space:  ORIG
Dataset Type:    Anat Bucket (-abuc)
Byte Order:      LSB_FIRST {assumed} [this CPU native = LSB_FIRST]
Storage Mode:    NIFTI
Storage Space:   75,497,472 (75 million) bytes
Geometry String: "MATRIX(0.423828,0,0,-108.2881,0,-0.426163,4.768372e-07,-67.58212,0,0.044302,2.386637,92.87281):512,512,72"
Data Axes Tilt:  Oblique (5.935 deg. from plumb)
Data Axes Approximate Orientation:
  first  (x) = Right-to-Left
  second (y) = Posterior-to-Anterior
  third  (z) = Inferior-to-Superior   [-orient RPI]
R-to-L extent:  -108.288 [R] -to-   108.288 [L] -step-     0.424 mm [512 voxels]
A-to-P extent:  -286.525 [A] -to-   -67.582 [A] -step-     0.428 mm [512 voxels]
I-to-S extent:    92.873 [S] -to-   262.324 [S] -step-     2.387 mm [ 72 voxels]
Number of values stored at each pixel = 1
  -- At sub-brick #0 '?' datum type is float


# dont judge
FROM tensorflow/tensorflow:2.2.0-gpu-jupyter
ENV DEBIAN_FRONTEND noninteractive
# dcmtk
RUN apt-get update && \
apt-get install -yq \
cmake pkg-config dcmtk
# dcm2niix
WORKDIR /opt
RUN git clone https://github.com/rordenlab/dcm2niix.git
WORKDIR /opt/dcm2niix
RUN git checkout v1.0.20210317
#RUN git checkout v1.0.20181125
RUN mkdir -p /opt/dcm2niix/build
WORKDIR /opt/dcm2niix/build
RUN cmake ..
RUN make
# # nibabel,simpleitk
RUN python -m pip install --upgrade pip
RUN pip install nibabel==3.2.1 SimpleITK==2.0.2 pydicom==2.1.2
# afni
RUN apt-get install tcsh -yq
RUN curl -O https://afni.nimh.nih.gov/pub/dist/bin/misc/@update.afni.binaries
RUN tcsh @update.afni.binaries -package linux_ubuntu_16_64 -do_extras
RUN cp $HOME/abin/AFNI.afnirc $HOME/.afnirc
import os,sys
import subprocess
import pydicom
import SimpleITK as sitk
import nibabel as nib
import numpy as np
np.set_printoptions(formatter={'float': '{: 0.3f}'.format})
image_folder = sys.argv[1]
sitk_path = 'itk.nii.gz'
dcm2niix_path = 'ok/ok.nii'
dcm2niix_eq1_path = 'ok/ok_Tilt_Eq_1.nii'
os.makedirs('ok',exist_ok=True)
if not os.path.exists(dcm2niix_path):
subprocess.call(['/opt/dcm2niix/build/bin/dcm2niix','-o','ok','-f','ok',image_folder])
if not os.path.exists(sitk_path):
dicom_names = os.listdir(image_folder)
dicom_names = [os.path.join(image_folder,x) for x in dicom_names]
mylist = []
for x in dicom_names:
ds = pydicom.dcmread(x)
mylist.append({'f':x,'i':ds.InstanceNumber})
mylist = sorted(mylist,key=lambda x:x['i'])
print(mylist)
dicom_names = [x['f'] for x in mylist]
reader = sitk.ImageSeriesReader()
reader.SetFileNames(dicom_names)
img = reader.Execute()
arr = sitk.GetArrayFromImage(img)
spacing = img.GetSpacing()
origin = img.GetOrigin()
direction = img.GetDirection()
use_compression = True
img = sitk.GetImageFromArray(arr)
img.SetSpacing(spacing)
img.SetOrigin(origin)
img.SetDirection(direction)
writer = sitk.ImageFileWriter()
writer.SetFileName(sitk_path)
writer.SetUseCompression(use_compression)
writer.Execute(img)
foobar = [
('sitk',sitk_path,nib.load(sitk_path)),
('dcm2niix',dcm2niix_path,nib.load(dcm2niix_path)),
#('dcm2niix-tilt?',dcm2niix_eq1_path,nib.load(dcm2niix_eq1_path)),
]
for libname,nii_path,img in foobar:
print(f'affine matrix of nifti file generate by {libname}:')
print(np.array(img.affine))
print(f'shape: {img.shape}')
x,y,z = np.array(img.shape)-np.array([1,1,1])
coord_list = [
[0,0,0,1],
[0,y,0,1],
[x,0,0,1],
[x,y,0,1],
[0,0,z,1],
[0,y,z,1],
[x,0,z,1],
[x,y,z,1],
]
print('physical coordinates | image coordinates:')
for coord in coord_list:
print(np.dot(np.array(img.affine),coord),'|',coord)
o=subprocess.check_output(['/root/abin/3dinfo',nii_path])
print(o.decode('utf-8'))
print('--')
if [ ! -f "/workdir/images.zip" ]; then
curl -X GET \
https://services.cancerimagingarchive.net/services/v4/TCIA/query/getImage?SeriesInstanceUID=1.3.6.1.4.1.14519.5.2.1.7009.2402.157952009005030405916049431043 \
-o images.zip
unzip images.zip -d images
fi
echo 'first image (instance 1):'
dcmdump images/30-182.dcm | grep InstanceNumber
dcmdump images/30-182.dcm | grep ImageOrientationPatient
dcmdump images/30-182.dcm | grep ImagePositionPatient
echo 'last image (instance 72):'
dcmdump images/15-167.dcm | grep InstanceNumber
dcmdump images/15-167.dcm | grep ImageOrientationPatient
dcmdump images/15-167.dcm | grep ImagePositionPatient
python foo.py images
docker build -t test .
docker run -v $PWD:/workdir -w /workdir test bash -c "bash foo.sh"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment