VTK 16 bit data issue for 2D xray using QT

HI All

we are working on an 2D xray porject on VTK and QT and completely stuck at this point
The raw image file from xray detector plate is of 16 bit data. Our work flow is as below
we convert raw data file to Qimage an then Qimage to vtk data to load the image in Vtk renderer.
16 bit Raw data > QImage > VTKData

and after processing in vtk the vtk cast output data is convereted to Qimage first and then to raw data.

the code to create Qimage is


QByteArray array =RAWfile.readAll();
unsigned char* Data = (unsigned char*)&array.data()[0];
QImage myImage(Data,SelectedDetector.ImageWidth,SelectedDetector.ImageHeight,QImage::Format_Grayscale16); //change image size
myImage.invertPixels();

code to create VTK data


vtkNew qimageToImageSource;
qimageToImageSource->SetQImage(&myImage);
qimageToImageSource->Update();

vtkImageData * imgdataEnhance=vtkImageData::New();
imgdataEnhance = qimageToImageSource ->GetOutput();

code for vtk data to Qimage


imagedataSave is output of vtk imagecast in unsigned char scalar

QImage myImage = vtkImageDataToQImage(imgdataSave);

vtkImageDataToQImage(vtkSmartPointer imageData){

ImageWidth = imageData->GetDimensions()[0];
ImageHeight = imageData->GetDimensions()[1];

int dim[3];
imageData->GetDimensions(dim);

QImage m_image( ImageWidth, ImageHeight, QImage::Format_Grayscale16);
QRgb*  pixel = reinterpret_cast<QRgb*>(m_image.bits()) + dim[0] * (dim[1] - 1);

    for (int x = 0; x <dim[0]; x++)
    {
        for (int y = 0; y <dim[1]; y++)
        {
               unsigned char* h = static_cast<unsigned char*>(imageData->GetScalarPointer(x, y, 0));

                if(h!=NULL){
                            QRgb pixel = qRgb(*h, *h, *h);//Because it is processing grayscale images So the three RGB are the same
                            m_image.setPixel(x, y, pixel);//Each point assignmen
                }
        }
    }


QImage image = m_image.mirrored(false, true);//The obtained image is upside down, use mirror to change the direction.
return image;

}

to covert Qimage to raw data for dicom


QByteArray bytes;

QBuffer buffer(&bytes);
buffer.open(QIODevice::WriteOnly);
myImage.save(&buffer, "tiff");
buffer.close();

unsigned char *data = (unsigned char *) malloc(bytes.size());
memcpy(data, reinterpret_cast<unsigned char *>(bytes.data()), bytes.size());

RawDataSize =bytes.size();
RawData = data;

so the issue is when we load the image in VTK the image quality is not good as and when we change window leveling and zooming
seems lke 8 bit data is being used by VTK and this compromise the quality

alternatively when we create a tiff file from the raw data through Qimage and then call that tiff file through vtk image reader, it seems 16 bit image is loaded in vtk , and there is no issue in image quality.
But there is issue to save raw data for dicom the resulting out put image is distorted.

we tried to save the vtk cast output to tiff through tiffwriter, but then the output is 8bit one and image is distorted.

so is there ay way to manage with 16 bit data and retain the image quality in the final saved output raw data a well?
anyway to save vtkoutput to tiff or rawdata directly from vtk renderer? or through qimage in a better way to get the exact vtk output
to load 16 bit raw data directly to vtk and then save directly to 16 bit raw data from vtk?

your help is highly appreciated as we are completely stuck and in dark now
we are even ready to outsource this part on payment basis to someone who are ready to help on this.

Thanks

I don’t think that vtkQImageToImageSource will work on 16-bit images, it was only written to handle 8-bit data.

But if you have a raw pointer to the data, you can use that to create a vtkImageData, for example with the vtkImageImport class. Just be sure to use SetDataScalarTypeToShort() (or UnsignedShort() if the 16-bit data is unsigned).

Thanks a lot David

That really did work and there is no artifact in vtk rendering, its perfect

Now the next challenge is how to save the vtk cast output to a 16 bit raw file or tiff file preserving 16 bit image quality

we have tried the vtkexport class and getting unsigned char data pointer.
but how can we save this unisgned char data to bytes array raw file or as a tiff file data?
our ultimate aim is that the vtk cast output need to converted to dicom pixel data uncompresssed preserving 16 bit quality

can you pleasee help on this part?

Thanks once again

I have to ask you to write proper sentences (capitalization, punctuation, etc). It’s hard for me to understand exactly what your question is.

Sorry David for that.

What you have suggested yesterday, using vtkimport class, worked and we could load 16 bit raw data to vtk without any artifacts or issues.

Now we need to save the vtk cast output to raw data or as tiff data which should be again 16 bit one to preserve image quality.

How can we achieve this?

Our purpose is to use the vtk cast output as the source for dicom pixel data in 16 bit.

We have tried vtkImageExport class and we could get unsigned char data.

But how can we convert this unsigned char data to 16 bit raw data or as 16 bit tiff data in QT?

Can you please help on this part?

Thanks once again.

I’m assuming you’re basing your code on this example.
https://kitware.github.io/vtk-examples/site/Cxx/Images/ImageExport/

Have you tried just casting to the appropriate datatype for your case (short or unsigned short) instead of where the example uses char? e.g.,

unsigned short* pixel = static_cast<unsigned short*>(imageData->GetScalarPointer(row, col, 0));

Hello Mike

Thanks

Yes, we are using https://kitware.github.io/vtk-examples/site/Cxx/Images/ImageExport/

In the above example vtkimagedata is created from scratch to convert it to c style image.

But in our case we have vtkImageData already available from the vtkCast output (scalarDoubled).

So we are just using the c style image conversion part as below , where imageData is vtkCast output.

// Create the c-style image to convert the VTK image to
std::unique_ptr cImage(
new unsigned char[dims[0] * dims[1] * dims[2]]);

vtkNew exporter;
exporter->SetInputData(imageData);
exporter->ImageLowerLeftOn();
exporter->Export(cImage.get());
exporter->Update();

We are getting cImage.get(), but the question is how to convert cImage.get() to 16 bit raw data or 16 bit tiff data, so that we can use it to crate dicom pixel data.

We have converted the cImage.get() to Qbyte array and then tried to save as raw data file. But then then it seems like its 8 bit only (file size reduced to half from initial raw file) and image is distorted.

So any other way to propely convert vtkImagedata to 16 bit raw or tiff file?

I hope my point is clear, if not please let me know.

Thanks

I think part of the reason we have difficulty understanding your question is that your code blocks aren’t coming through properly. Can you format your code blocks with three backticks ``` above and below?

In general, if you want to work with 16-bit data, then make sure that everything is 16-bit from beginning to end. So as Mike said, use unsigned short (or short if your data is signed) all the way through. A 16-bit image isn’t going to fit in “unsigned char[dims[0] * dims[1] * dims[2]]”.

Hello David and Mike

Thanks a lot for the inputs.
Using unsigned char was the issue for 16 bit data.
We have used unsigned short and everything work good.
We could able to produce 16 bit raw data as well from the vtk cast output.

Is there any vtk algoirthams or filters to correct the histogram of the 2D image or to adjust the LUT?
I am looking for something to apply and save as vtkoutput , not just to visualize in render window.

Our images are more towards the brighter side in histogram (high scalar range) and would like to bring it evenly to midrange values so that it will look more greyish in appearance.
I know we can do this by adjusting window level and width, but i am looking for something to save the result as well to create a new image from that output.

Is there anyway to do it in VTK?

Thanks in advance.

Thanks & Regards

Santho

There is vtkImageHistogramStatistics, which can do some very basic histogram analysis.

    vtkNew<vtkImageHistogramStatistics> stats;
    stats->SetInputData(input_image);
    stats->Update();
    # use the histogram to get suitable window/level
    double* range = stats->GetAutoRange();
    double window_level = 0.5*(range[0] + range[1]);
    double window_width = range[1] - range[0];

So something like this will help with presentation of the data.

You say that you want to modify the data values and then re-save the data, but are you sure you really want to do that? DICOM very clearly separates the “data” from its “presentation”, so that the data can be left alone when the presentation parameters (window-level, LUT, annotations, etc) are changed.

Hello David

Yes, agree with you. It is not a good idea to change the raw data LUT and histogram.
So we are dropping that idea.

Instead we tried to apply some vtk filters to fine tune the image before creating raw data for dicom.

What we have used is the code from this example ( Butterworth HighPass filter)

https://kitware.github.io/vtk-examples/site/Cxx/ImageProcessing/IdealHighPass/

Code snippet is as below


	vtkNew<vtkImageFFT> fft;
	fft->SetInputData(imgdataEnhance);

               vtkNew<vtkImageButterworthHighPass> butterworthHighPass;
               butterworthHighPass->SetInputConnection(fft->GetOutputPort());
               butterworthHighPass->SetXCutOff(0.01);
               butterworthHighPass->SetYCutOff(0.01);

               vtkNew<vtkImageRFFT> butterworthRfft;
               butterworthRfft->SetInputConnection(butterworthHighPass->GetOutputPort());

               vtkNew<vtkImageExtractComponents> butterworthReal;
               butterworthReal->SetInputConnection(butterworthRfft->GetOutputPort());
               butterworthReal->SetComponents(0);

               double minButterReal = butterworthReal->GetOutput()->GetScalarRange()[0];
               double maxButterReal = butterworthReal->GetOutput()->GetScalarRange()[1];

               qDebug() << "min Butter Real: " << minButterReal ;
               qDebug() << "max Butter Real: " << maxButterReal ;

               cast->SetInputConnection(butterworthReal->GetOutputPort());

The cast output is good in vtk viewer, see the screenshot below

But when we save the cast data to 16bit data and create dicom, the image is is distorted, as in the picture below.

At first we thought it could be a window level issue, but we have tried adjsuting window levels , but no improvement.

What could be the reson for this? Do we need to convert the butterwurth filter output to some other scalar values?

The sclaroutput of input image was min: 18535, max : 65535.
But Butterworth out put scalar values are min: 0 and max:1

Any suggestions please?

Thanks & Regards

Santho

This looks like a problem with the photometric interpretation. Since the image is a radiograph, it should be displayed using a photometric interpretation of MONOCHROME1, where the “min” value is displayed as white and the “max” value is displayed as black. In other words, pixels with the smallest x-ray exposure (e.g. areas that are in the shadow of the bones) should have the smallest values, but should be displayed as brightest, because x-ray images are negatives.

So when you save the DICOM, make sure that Photometric Interpretation (0028,0004) is MONOCHROME1. Also make sure that in your raw data, and after any processing, that bones have the smallest pixel value. And make sure that the LUT that you use for display shows those small pixel values as white.

Thanks David

We have corrected the issue.
It was an image scalar value issue. After Butterworth filtering the scalar values where much below zero which were brought back to normal range using shiftscale filter.

Now everything ok.

Do you suggest any good scalar range for 16 bit images for better image quality?

I am asking this because we can bring the scalar range to any value range.

To 0- 65000 range

Or may be to a more narrow range of 25000-45000.

Or even keep the same scalar range of the original image as such.

So which one is ideal? A higher scalar range or a narrow one? which you recommend?

Thanks & Regards

Santosh

When you use vtkImageCast or vtkImageShiftScale, do you use ClampOverflowOn()? It is very important to do so, otherwise overflow or underflow can make a mess of the image.

I would need more information about your data in order to answer that question. In particular, is it signed or or unsigned? What is the range of the raw data?

Really, the question of “range” is fundamental to digital signal processing and isn’t specific to images. You want a large dynamic range (large enough for whatever diagnostic task you are preforming) and sufficient headroom to avoid overflow or underflow while processing the data.

If you just want a simple answer without the gory details, it’s usually a good choice to use the original range of the raw data. Just be sure to clamp when converting the data type.

Hello David

Thanks for the ClampOverflowOn() thing. We were not using it.
We will include it as well

The data is unsigned short and the intitial raw data scalar range is something in the range of 18535 - 65535.

For the time being we will keep the range in the original raw data range.

After the Butterwurth Filtering we are getting an image like below. Please note the more brighter and darker areas of the edges.

Is there any way to avoid this edge brightening and darkening and make it to more or less grey range one?

Thanks & Regard
Santho

My guess is that your raw image uses linear signal values. To make it suitable for viewing, you should apply a gamma to the values. This is the same thing that consumer digital cameras do before saving files, unless they’re saving in a RAW format.

There is no gamma filter in VTK, however, so you might have to write your own code to do it.

I think this link may be useful:

I think there is some gamma correction code there

Thank you David

We will try to implement gann correction.

Is there any c++ example available for vtkImageCroppingRegionsWidget ? Unfortunately couldnot fidn anything.

Thanks & Regards

Santosh

If you apply a gamma correction to raw radiographic data, the math will probably look something like the following, where you select an input range and an output range. It’s usually implemented with a 16-bit lookup table to avoid computing pow() for every pixel.

; Choose a range for the raw image:
NL = 4000    ; the noise level of the raw image
ML = 60000   ; the max level of the raw image

; Choose a range for the output image (e.g. for display):
BL = 0       ; the black level
WL = 1000    ; the white level

; The gamma
gamma = 2.2

; Equation to convert RV (raw values) to DV (display values)
DV = BL + (WL - BL)*pow((RV - NL)/(ML - NL), 1.0/gamma)

Before doing this, though, you should be certain that your “raw” data hasn’t already been gamma-corrected.


It’s best to open a new discussion topic for that, since it’s a new question unrelated to your original 16-bit data question.

Hello David

OK, I understand that we need to wrtie a code to correct the gamma values.
But bit confused how to apply this for saving the data as well.

We can apply gamma coorected LUT table to the mapper to display the new values.
But we need to save the data of the new lut corrected image as well.

We have tried to apply the LUT to vtkImageCast through vtkImageMapToColors output, but issue here is that the output of vtkImageMapToColors is unsigned char and so the sclar range is 0-256. Since we work with unisgned short (0-65536 scalar range) , there is issue in saving the data.

Our current workflow goes like this.

vtkImport output → vtkImageMapToColor ( Lut applied) output - > vtkImageCast output - > saving data/ display in renderer

Render display is ok here , but as i mentioned earlier data saving is having issue.

So is there any way to get ImageMapToColor output in scalar range 0-65536 scalar range? Tried applying SetOutputScalarTypeToUnsignedShort to ImageCast , but not working.

Or is there any alternate way to apply Lut table before imageCast so that we can save output?

Thanks & Regards

Santho