<div dir="ltr"><div>Hi Padraig,</div><div><br></div><div>What you are seeing is one of the big (very big) differences between ITK images and VTK images. It comes about because in ITK, the direction cosines are stored in the image class, but in VTK, they are not, and they must instead be stored in a separate matrix. You're probably wondering why this matters at all, since your image has direction cosines of ((1,0,0),(0,1,0),(0,0,1)). Hopefully I'll be able to provide an explanation that isn't too muddy.</div><div><br></div><div>In ITK, the transformation from the (I,J,K) indices of each voxel to the (x,y,z) coordinates is as follows:</div><div><br></div><div> let q = (I,J,K)</div><div> let p = (x,y,z)</div><div><br></div><div> p = R*S*q + o</div><div><br></div><div>where R is ITK's Direction matrix, S is the "scale" matrix (a diagonal matrix made from the Spacing), and "o" is ITK's Origin.</div><div><br></div><div>Since vtkImageData does not have a Direction matrix, but most medical image formats do have a Direction, it is necessary for VTK medical image readers to produce a vtkMatrix4x4 in addition to the vtkImageData. In VTK, the equivalent equation is:</div><div><br></div><div> p = R*(S*q + o)</div><div><br></div><div>Or we can expand this,</div><div><br></div><div> p = R*S*q + R*o</div><div><br></div><div>See how the ITK Origin and the VTK Origin mean different things? The ITK origin is rotated with respect to the VTK origin. In fact, the way that VTK defines the image "Origin" is incompatible with NIFTI (and with DICOM, too).</div><div><br></div><div>So, the vtkNIFTIImageReader always sets the VTK image Origin to (0,0,0). Then, it stores an ITK-style Origin as the 4th column of the SFormMatrix and the QFormMatrix. That's an advantage of using a 4x4 matrix: it can store both a rotation and an offset. When we introduce this matrix "M" and set the VTK image Origin to (0,0,0),</div><div><br></div><div> p = M*S*q</div><div><br></div><div>Of course, now we have to make p and q into (I, J, K, 1) and (x, y, z, 1) in order to use the 4x4 matrix. I'll let you work out the rest of the details.</div><div><br></div><div>In order to properly compute the bounds of an oriented image in VTK, you have to compute the coordinates of the four corners and then multiply them by the matrix M (i.e. by the SFormMatrix). In your case, since the "orientation" is identity, you could simply add the offset. </div><div><br></div><div>If you're wondering why the reader doesn't set the VTK image Origin to be equal to the offset for axial images, it's to avoid inconsistency between the way the reader treats axial images as compared to sagittal or coronal.</div><div><br></div><div>For Paraview, you'll have to find a way to get Paraview to use the SFormMatrix (or the QFormMatrix) that is provided by the reader. Unfortunately, I don't know enough about Paraview to help with that... hopefully one of the Paraview users around here can help.</div><div><br></div><div> - David</div><div><br></div></div><div class="gmail_extra"><br><div class="gmail_quote">On Sun, Nov 13, 2016 at 9:50 AM, padraig <span dir="ltr"><<a href="mailto:padraig.looney@gmail.com" target="_blank">padraig.looney@gmail.com</a>></span> wrote:<br><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">
<div bgcolor="#FFFFFF" text="#000000">
<p>Hi David,</p>
<p><br>
</p>
<p>I am still confused by this. Volumes that ITK tells me have the
same bounds, VTK displays in Paraview as not having the same
bounds. <br>
</p>
<p>Consider this header from <br>
</p>
<p>vtkNIFTIImageHeader (0x221e6b0)<span class=""><br>
Debug: Off<br>
Modified Time: 85<br>
Reference Count: 1<br>
Registered Events: (none)<br>
DimInfo: 0x0<br>
Dim: 3 224 153 52 1 1 1 1 <br></span>
PixDim: 1 0.9195 1.40018 2.89048 0 0 0 0 <br></p><div><div class="h5">
VoxOffset:352<br>
IntentP1: 0<br>
IntentP2: 0<br>
IntentP3: 0<br>
IntentCode: 0<br>
DataType: 2<br>
BitPix: 8<br>
SliceStart: 0<br>
SclSlope: 1<br>
SclInter: 0<br>
SliceEnd: 0<br>
SliceCode: 0<br>
XYZTUnits: 0x2<br>
CalMax: 0<br>
CalMin: 0<br>
SliceDuration: 0<br>
TOffset: 0<br>
Descrip: ""<br>
AuxFile: ""<br>
QFormCode: 2<br>
SFormCode: 1<br>
QuaternB: 0<br>
QuaternC: 0<br>
QuaternD: 1<br>
QOffsetX: 102.536<br>
QOffsetY: 106.413<br>
QOffsetZ: 30.1491<br></div></div>
SRowX: -0.9195 0 0 102.536<br>
SRowY: 0 -1.40018 0 106.413<br>
SRowZ: 0 0 2.89048 30.1491<br>
IntentName: ""<br>
Magic: "n+1"<br>
<p></p>
<p>That is the same as the affine from nibabel <br>
</p><span class="">
<p><br>
</p>
<p>array([[ -0.91949999, 0. , 0. ,
102.53600311],<br>
[ 0. , -1.40017998, 0. ,
106.41300201],<br>
[ 0. , 0. , 2.89048004,
30.14909935],<br>
[ 0. , 0. , 0. , 1.
]])<br>
</p>
<p><br>
</p>
</span><p>If you look at the SForm matrix below shouldn't the bounds be <br>
</p>
<p>X: (224-1)*-0.9195 + 102.536 = -102.51249999999999<br>
</p>
<p>Y: (153-1)*-1.40018 + 106.413 =--106.41436<br>
</p>
Z: (52-1)*2.89048 + 30.1491 = 177.56358<br>
<br>
<br>
In ITK I have checked the physical coordinates of the image and
found the bounds to be, as expected from the above,<br>
<br>
[-102.536, -106.413, 30.1491][102.512, 106.414, 177.564]<br>
<br>
In VTK I have checked the centre and origin with <br>
<br>
vtkNew<vtkNIFTIImageReader> niiReader;<br>
niiReader->SetFileName(str);<br>
niiReader->Update();<br>
int size[3];<br>
double center[3], spacing[3], bounds[6];<br>
niiReader->GetOutput()-><wbr>GetDimensions(size);<br>
niiReader->GetOutput()-><wbr>GetCenter(center);<br>
niiReader->GetOutput()-><wbr>GetBounds(bounds);<br>
std::vector<double>
bounds_vec(&bounds[0],&bounds[<wbr>6]);<br>
std::cout << bounds[0] << " " << bounds[1]
<< " " << bounds[2] << " " << bounds[3]
<< " " << bounds[4] << " " << bounds[5]
<< std::endl;<br>
niiReader->GetOutput()-><wbr>GetSpacing(spacing);<br>
double center1[3] = { center[0], center[1], center[2] };<br>
double center2[3] = { center[0], center[1], center[2] };<br>
std::cout << center[0] << " " << center[1]
<< " " << center[2] << std::endl;<br>
std::cout << size[0] << " " << size[1]
<< " " << size[2] << std::endl;<br>
<br>
and get <br>
<br>
0 205.048 0 212.827 0 147.414<br>
102.524 106.414 73.7072<br>
224 153 52<div><div class="h5"><br>
<br>
<div class="m_5161652102083939587moz-cite-prefix">On 12/11/16 15:39, David Gobbi wrote:<br>
</div>
<blockquote type="cite">
<div dir="ltr">Hi Padraig,
<div><br>
</div>
<div>The "direction" or "orientation" is the 3x3 portion of the
4x4 matrix, after dividing out the scale.</div>
<div><br>
</div>
<div>So if ITK is giving a direction like the following, it
means that ITK allows the direction to include a mirror-flip
(in this case, it's a flip along the z-axis):</div>
<div><br>
</div>
<div>1 0 0<br>
0 1 0<br>
0 0 -1</div>
<div><br>
</div>
<div>The vtkNIFTIImageReader doesn't allow a mirror flip in the
QFormMatrix, so instead it re-orders the nifti slices in
memory to undo the flip. When it does this, it also has to
adjust the 4th column of the QFormMatrix since the 4th column
now indicates the position of the last slice in the file,
instead of the first slice in the file. The difference in
position between the first slice and the last slice is (n -
1)*sz, where n is the number of slices and sz is the slice
spacing. For your image, we can add (n - 1)*sz to -117.265 in
order to get 30.149:</div>
<div><br>
</div>
<div> -117.265 + (52 - 1)*2.89048 = 30.149</div>
<div><br>
</div>
<div>
<div class="gmail_extra">
<div class="gmail_extra">This accounts for the discrepancy.</div>
<div class="gmail_extra"><br>
</div>
<div class="gmail_extra"> - David</div>
<div class="gmail_extra"><br>
</div>
<div class="gmail_quote">On Sat, Nov 12, 2016 at 7:42 AM,
padraig <span dir="ltr"><<a href="mailto:padraig.looney@gmail.com" target="_blank">padraig.looney@gmail.com</a>></span>
wrote:<br>
<blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">
<div bgcolor="#FFFFFF">
<div>
<div class="m_5161652102083939587gmail-h5">
<p>Hi David,</p>
<p><br>
</p>
<p>vtkNIFTIImageHeader (0x3346680)<br>
Debug: Off<br>
Modified Time: 85<br>
Reference Count: 1<br>
Registered Events: (none)<br>
DimInfo: 0x0<br>
Dim: 3 224 153 52 1 1 1 1 <br>
PixDim: -1 0.9195 1.40018 2.89048 0 0 0 0 <br>
VoxOffset:352<br>
IntentP1: 0<br>
IntentP2: 0<br>
IntentP3: 0<br>
IntentCode: 0<br>
DataType: 2<br>
BitPix: 8<br>
SliceStart: 0<br>
SclSlope: 1<br>
SclInter: 0<br>
SliceEnd: 0<br>
SliceCode: 0<br>
XYZTUnits: 0x2<br>
CalMax: 0<br>
CalMin: 0<br>
SliceDuration: 0<br>
TOffset: 0<br>
Descrip: ""<br>
AuxFile: ""<br>
QFormCode: 2<br>
SFormCode: 1<br>
QuaternB: 0<br>
QuaternC: 0<br>
QuaternD: 1<br>
QOffsetX: 102.536<br>
QOffsetY: 106.413<br>
QOffsetZ: 30.1491<br>
SRowX: -0.9195 0 -0 102.536<br>
SRowY: 0 -1.40018 -0 106.413<br>
SRowZ: 0 0 -2.89048 30.1491<br>
IntentName: ""<br>
Magic: "n+1"<br>
<br>
The direction in ITK is</p>
<p><br>
</p>
<p>1 0 0<br>
0 1 0<br>
0 0 -1<br>
</p>
I don't know how to see the direction in nibabel<br>
<br>
<br>
<div class="m_5161652102083939587gmail-m_-5251641299272646700moz-cite-prefix">On
12/11/16 14:10, David Gobbi wrote:<br>
</div>
</div>
</div>
<div>
<div class="m_5161652102083939587gmail-h5">
<blockquote type="cite">
<div dir="ltr">
<div>
<div>Hi Padraig,</div>
<div><br>
</div>
<div>Can you show us the raw qform and sform
fields from the NIFTI header itself? In
VTK, you can do this as follows:</div>
<div><br>
</div>
<div>>>>
print(reader.GetNIFTIHeader())<br>
</div>
<div><br>
</div>
<div>The difference you see between nibabel,
ITK, and VTK has to do with differences in
the way these packages deal with the
pixdim and the qfac of the nifti file. If
you print the Direction from the ITK image
you might find that it differs from
nibabel, too.</div>
<div><br>
</div>
<div>For VTK, you can read all the gory
details here:</div>
<div><br>
</div>
<div><a href="http://gitlab.kitware.com/vtk/vtk/blob/v7.0.0/IO/Image/vtkNIFTIImageReader.cxx#L738" target="_blank">http://gitlab.kitware.com/vtk/<wbr>vtk/blob/v7.0.0/IO/Image/vtkNI<wbr>FTIImageReader.cxx#L738</a></div>
</div>
<div><br>
</div>
<div> - David</div>
<div><br>
</div>
<div class="gmail_extra"><br>
<div class="gmail_quote">On Sat, Nov 12,
2016 at 6:10 AM, padraig <span dir="ltr"><<a href="mailto:padraig.looney@gmail.com" target="_blank">padraig.looney@gmail.com</a>></span>
wrote:<br>
<blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">
<div bgcolor="#FFFFFF">
<div class="m_5161652102083939587gmail-m_-5251641299272646700gmail-m_-103140333611607564moz-text-flowed" style="font-family:-moz-fixed;font-size:12px" lang="x-unicode">I have
noticed some inconsistencies in how
NIFTI images are handled in VTK,
ITK, and NiBabel. ITK and NiBabel
are in agreement with each other and
differ with VTK on the position of
the volume. The VTK output for a
file is below. <br>
<br>
vtkNIFTIImageReader (0x1de67d0) <br>
Debug: Off <br>
Modified Time: 91 <br>
Reference Count: 2 <br>
Registered Events: (none) <br>
Executive: 0x1de7720 <br>
ErrorCode: Success <br>
Information: 0x1de6ad0 <br>
AbortExecute: Off <br>
Progress: 1 <br>
Progress Text: (None) <br>
FileName:
/home/padraig/Desktop/59674/Se<wbr>eding/I0000003_59674_sn.nii
<br>
FileNames: 0 <br>
FilePrefix: (none) <br>
FilePattern: %s.%d <br>
FileNameSliceOffset: 0 <br>
FileNameSliceSpacing: 1 <br>
DataScalarType: unsigned char <br>
NumberOfScalarComponents: 1 <br>
File Dimensionality: 3 <br>
File Lower Left: On <br>
Swap Bytes: Off <br>
DataIncrements: (1, 1) <br>
DataExtent: (0, 223, 0, 152, 0,
51) <br>
DataSpacing: (0.9195, 1.40018,
2.89048) <br>
DataOrigin: (0, 0, 0) <br>
HeaderSize: 352 <br>
Internal File Name: (none) <br>
TimeAsVector: Off <br>
TimeDimension: 1 <br>
TimeSpacing: 1 <br>
RescaleSlope: 1 <br>
RescaleIntercept: 0 <br>
QFac: -1 <br>
QFormMatrix: -1 0 0 102.536 0 -1 0
106.413 0 0 1 -117.265 0 0 0 1 <br>
SFormMatrix: -1 0 -0 102.536 0 -1
-0 106.413 0 0 1 -117.265 0 0 0 1 <br>
NIFTIHeader: <br>
PlanarRGB: Off <br>
<br>
the qform given by nibabel is <br>
<br>
>>> ni_ob.get_qform() <br>
array([[ -0.91949999, 0.
, 0. , 102.53600311], <br>
[ 0. ,
-1.40017998, 0. ,
106.41300201], <br>
[ 0. , 0.
, 2.89048004, 30.14909935], <br>
[ 0. , 0.
, 0. , 1. ]]) <br>
<br>
<br>
ITK has the origin at <br>
<br>
[-102.536, -106.413, 30.1491] <br>
</div>
</div>
</blockquote>
</div>
</div>
</div>
</blockquote>
<br>
</div>
</div>
</div>
</blockquote>
</div>
<br>
</div>
</div>
</div>
</blockquote>
<br>
</div></div></div>
</blockquote></div><br></div>