Import multiple images using vtkImageImport

Hello.
I have to implement MPR using VTK.
With the help of my last question, I succeeded in converting PixelData (c-style) read with a third-party DicomReader to vtkImageData using vtkImageImport.
However, I want to load multiple images (series).
How can I import multiple c-style images using vtkImageImport?
I lack knowledge of VTK and 3D MPR.
Thank you.

If you want to import a volume with vtkImageImport, your data for the volume must be stored in a single contiguous array.

In other words, you must sort your slices in the correct order, then you must concatenate the PixelData of all of the slices into a single large array (i.e. a volume), and only then you can import the volume with vtkImageImport.

Are you familiar with using the ImagePositionPatient and ImageOrientationPatient attributes of the DICOM files to sort the slices?

2 Likes

Thank you for the answer.
I now found Image Position Patient (0020, 0032) and Image Orientation Patient (0020,0037) in my DICOM sample.
But I don’t know how to sort about it.
Can you teach me how to sort?
Thank you for answering my poor English.

To create a 3D volume that can be used with MPR, it is necessary to compute the Slice Plane Normal, and to sort the slices so that the Slice Position progresses in the direction of the Slice Plane Normal. Here is the math:

Vector "p" is Slice Position:
px py pz = ImagePositionPatient

Vectors "u" and "v" are Slice Orientation:
ux uy uz vx vy vz = ImageOrientationPatient

Vector "w" is the Slice Plane Normal (is perpendicular to slice plane)
w = cross(u, v)

Formula for cross product:
wx = uy*vz - uz*vy
wy = uz*vx - ux*vz
wz = ux*vy - uy*vx

Signed Distance along Slice Plane Normal:
s = dot(w, p)

Formula for dot product:
s = wx*px + wy*py + wz*pz

Compute "s" for every slice, then sort the slices by "s".
Every slice must have a larger "s" than the previous slice.
1 Like

Thank you. David gobbi.

This has successfully loaded the DICOM series and displayed the MPR.
But the behavior is a bit strange.
Below is part of my code.

vtkSmartPointer<vtkImageImport> importer = vtkSmartPointer<vtkImageImport>::New();
importer->SetDataSpacing(PixelSpacing[0], PixelSpacing[1], SliceThickness);
importer->SetDataOrigin(0, 0, 0);
importer->SetWholeExtent(0, nWidth - 1, 0, nHeight - 1, 0, nCount - 1);
importer->SetDataExtentToWholeExtent();

I thought the third argument of the SetDataSpacing function was the slice thickness (0018, 0050). (Refer to the manual, but did not know the meaning of the third argument.)
In fact, the height has changed according to the third parameter value.
But Coronal and Sagittal are displayed incorrectly.

Second, using the formula for the dot product you gave me, I averaged the ‘s’ distance for each slice and used that value.
This will look like you used vtkDicomReader but will not change when you select Blend mode (min / max / mean).

What value should I use?

Do you mean the Δs value, the distance from each slice to its neighbor? If so, then that is the correct value to use for the spacing.

I don’t understand, can you explain what you mean?

1 Like

I referenced the term in the vtkImageSlabReslice::SetBlendMode(int) function.
I think this means MinIP / MIP / Average.

Sorry. It seems to be my mistake
The problem was that I used it as a negative number.
Using positive numbers works fine. (Is it correct to use absolute values even if the result is negative?)


My English is not good enough, so I’m not sure if you and I are saying the same thing.
Attach my sample code

double s, total, avg;
for (int i = 0; i < 20; i++) // There are 20 slices in total.
{
    
    if (i > 0)
    {
        total += abs(slice[i].dot() - s)  // Sum the distance between neighboring slices.
    }

    s = slice[i].dot(); // Signed Distance along Slice Plane Normal
}

avg = total / 19;  // use this value

vtkSmartPointer<vtkImageImport> importer = vtkSmartPointer<vtkImageImport>::New();
importer->SetDataSpacing(PixelSpacing[0], PixelSpacing[1], avg);
importer->SetDataOrigin(0, 0, 0);
importer->SetWholeExtent(0, nWidth - 1, 0, nHeight - 1, 0, nCount - 1);
importer->SetDataExtentToWholeExtent();

Yes, the slice spacing looks correct, but what is .dot()? Is it a method you wrote?

The other spacings are wrong, they should be switched:

importer->SetDataSpacing(PixelSpacing[1], PixelSpacing[0], avg);

According the the DICOM standards documents, the PixelSpacing is RowSpacing\ColumnSpacing.

1 Like

Yes, .dot () is a function I wrote.
The implementation of this .dot () function is the ‘Formula for dot product’ you told me.
Switched PixelSpacing position.