extract image slice from vtkImageData and convert it to qimage, which make unexpected result

Hello guys:
I extract a slice from vtkImageData with vtkImageReslice filter, then display the output of vtkImageReslice in renderwindow, meanwhile i convert the output of vtkImageReslice to qimage. BUT The result of qimage is different with picture displayed in renderwindow. PICTURE BELOW:

vtkimage displayed in renderwindow

qimage:
test.bmp (656.1 KB)

reslice matrix:
0.989176 0.0549455 0.136054 258.621
0.133589 0.0463236 0.989954 138.063
0.0606961 0.997414 0.0384822 521.809
0 0 0 1

function converting vtkimage to qimage:
QImage convert_vtkimagedata_to_qimage(vtkImageData *raw, float scalar_min, float scalar_max, double *spacing)
{
int x_dims = *raw->GetDimensions();
int y_dims = *(raw->GetDimensions()+1);
int z_dims = *(raw->GetDimensions()+2);

double spacing_x = spacing[0];
double spacing_y = spacing[1];
double spacing_z = spacing[2];

int scale_y = x_dims * spacing_x;
int scale_z = y_dims * spacing_z;



float scalar_range = scalar_max - scalar_min;

QImage img(x_dims, y_dims, QImage::Format_Grayscale8);


for(int i = 0; i < y_dims; ++i)
{
    for(int j = 0; j < x_dims; ++j)
    {
        float val = raw->GetScalarComponentAsFloat(j, i, 0, 0);
        int res = (int)(((val - scalar_min) / scalar_range) * 255.0);
        uchar *p = img.bits() + (i * x_dims + j);
        *p = res;
    }
}
QImage image_scaled = img.scaled(scale_y, scale_z);
img.save("/home/xianger/Pictures/test.bmp");
return image_scaled;

}

Hello,

It seems to me that the number of pixels widthwise should be greater and the scan order is upside down. I’d rewrite your scanning loop with something like this (redim arrays as needed):

for(int i = 0; i < y_dims; ++i)
{
    for(int j = 0; j < x_dims-1; ++j) //dimension widthwise should ensure correct vertical alignment
    {
        float val = raw->GetScalarComponentAsFloat(j, i, 0, 0);
        int res = (int)(((val - scalar_min) / scalar_range) * 255.0);
        uchar *p = img.bits() + ( (y_dims-1-i) * x_dims + j); //vertical scan order seems to differ between Qt and VTK
        *p = res;
    }
}

I hope this helps,

Paulo

Thanks a lot, I have try the code supplied by you, and THANKFULLY that was worked!
but i wonder why the x_dims should minus one? could you tell me the reason? THANKS!

It’s because the dimensions of vtkImageData referes to its vertices (mesh corners) and not its cells. So, to accomodate a 10x10x10 data array, you have to crate an 11x11x11 vtkImageData.

Here’s how I create my vtkImageData:

    //create a volumetric object (regular 3D grid)
    vtkSmartPointer<vtkImageData> imageData = vtkSmartPointer<vtkImageData>::New();
    imageData->SetSpacing( dX, dY, dZ );
    imageData->SetOrigin( X0frame, Y0frame, Z0frame );
    imageData->SetDimensions( nX+1, nY+1, nZ+1 ); 

Then, how I populate it with values for rendering (notice how I allocate the values array with one dimension less for each axis):

    //read sample values
    values->Allocate( nX*nY*nZ );
    for( int k = 0; k < nZ; ++k){
        for( int j = 0; j < nY; ++j){
            for( int i = 0; i < nX; ++i){
                // sample value
                double value = cartesianGrid->getDataValueIJK( variable_index, i, j, k );
                values->InsertNextValue( value );
            }
        }
    }

    //assign the grid values to the volume cells
    imageData->GetCellData()->SetScalars( values );

Edit: It’s most likely you need to use y_dims-1 too. I wonder why you don’t get a crash.

Conversely, when you extract an orthoslice, you get an 11x11 grid.

Hi Paulo,

You are assuming that the scalars are associated with the cells:

For the original question, however, I am 99% certain that the scalars are associated with the points. Hence, the -1 is not needed. It should loop like this:

    for(int j = 0; j < x_dims; ++j)

yes, the scalars are associated with the points. the vtkimagedata is structed point which is from ct data. for mine application, i change the function which convert vtkimagedata to qimage and i get the correct result finally. But i can’t explain the reason for that, could you tell me? thanks a lot!

the function below:
QImage convert_vtkimagedata_to_qimage(vtkImageData *raw, float scalar_min, float scalar_max, double *spacing)
{
int width = *raw->GetDimensions();
int height = *(raw->GetDimensions()+1);

double spacing_x = spacing[0];
double spacing_y = spacing[1];
double spacing_z = spacing[2];

int scale_width = width * spacing_y;
int scale_height = height * spacing_y;

float scalar_range = scalar_max - scalar_min;

vtkShortArray* scalars =
        vtkShortArray::SafeDownCast(raw->GetPointData()->GetScalars());

QImage qImage(width, height, QImage::Format_ARGB32);
vtkIdType tupleIndex=0;
int qImageBitIndex=0;

QRgb* qImageBits = (QRgb*)qImage.bits();
short* scalarTuples = scalars->GetPointer(0);

for(int j=0; j<height; j++)
{
    for(int i=0; i<width; i++)
    {
        short* tuple = scalarTuples+(tupleIndex++);
        int v = ((*tuple - scalar_min) / scalar_range ) *255.0;
        QRgb color = qRgba(v, v, v, 255);
        *(qImageBits+(qImageBitIndex++))=color;
    }
}

// qImage = qImage.mirrored(false, true);
qImage = qImage.scaled(scale_width, scale_height);
// qImage.save("/home/xianger/Pictures/asdq.bmp");

return qImage;

}

The main difference seems to be this:

    float val = raw->GetScalarComponentAsFloat(j, i, 0, 0);

The index for images does not always begin at zero. It begins at the start of the extent, and if the start of the extent is not zero, then an offset is needed:

int* extent = raw->GetExtent();
int x_min = extent[0];
int x_max = extent[1];
int y_min = extent[2];
int y_max = extent[3];
for(int i = y_min; i <= y_max; ++i)
{
    for(int j = x_min; j <= x_max; ++j)
    {
        float val = raw->GetScalarComponentAsFloat(j, i, 0, 0);
        int res = (int)(((val - scalar_min) / scalar_range) * 255.0);
        uchar *p = img.bits() + ((i - y_min) * x_dims + (j - x_min));
        *p = res;
    }
}

In this code vtkImageData index starts at x_start and the QImage index starts at 0.


Edit: Ugh! I had to fix my example code. I had an off-by-one error on the extent. Sigh…
The code is fixed now… maybe…


Edit: I actually think your new code is better than my example. The only change I recommend is this, so that you do not need to SafeDownCast() the scalars:

-        short* tuple = scalarTuples+(tupleIndex++);
-        int v = ((*tuple - scalar_min) / scalar_range ) *255.0;
+        double scalar_val = scalars->GetComponent(tupleIndex++, 0);
+        int v = ((scalar_val - scalar_min) / scalar_range ) * 255.0;

I don’t like to use GetScalarComponentAsFloat() because it is a very inefficient function.

We use the vtkImageDataToQImage implementation in CTK.

CTK has lots of other useful tools for implementing medical imaging applications (DICOM browser, slice and 3D viewer widgets, volume rendering transfer function editor, and many other widgets), and you can save even more time by building your application on higher-level VTK-based frameworks, such as 3D Slicer or MITK.