Updating a sub region in ImageToVTKImageFilter

Hi all,

I am performing some annotation functions which which modifies the ITK image -> ImageToVTKImageFilter -> vtkImageViewer2.

However, this is quite slow for large images because I need to call ImageToVTKImageFilter.Update() whenever then user mouse drags.

I was looking at the SetRequestedRegion() to see if I can just update the region that was drawn, however ImageToVTKImageFilter does not have SetRequestedRegion.

Is there any way I can update only the drawn region to speed things up?

Thanks

You might only need to call Modified() on the resulting VTK image.

If that does not solve your problem, you could try using vtkImageImport directly: SetImportVoidPointer to ITK image’s GetBufferPointer(), and call Modified() on the resulting VTK image when needed.

Another option, which works from Python, is to add an itk.RegionOfInterestImageFilter

ITK image → RegionOfInterestImageFilter → ImageToVTKImageFilter → vtkImageViewer2.

or, probably best, call SetDisplayExtent on the vtkImageViewer2’s vtkImageActor (untested).

Hi Dzenan. Can you detail a little bit your idea from here ? I am trying to import data from SimpleITK to VTK. And I have tried this:

    sitk::ImportImageFilter importer;
....
	sitk::Image img = importer.Execute();
	img = sitk::GrayscaleErode(img, 3);
	sitk::CurvatureFlowImageFilter blurFilter;
	blurFilter.SetNumberOfIterations(5);
	blurFilter.SetTimeStep(0.125);
	img = blurFilter.Execute(img);
...
	vtkImageImport* pImageImport = vtkImageImport::New();
	pImageImport->SetImportVoidPointer(&img);
	m_pDICOMReader->SetOutput(pImageImport->GetOutput());
	m_pDICOMReader->Update();
	pImageImport->Delete();

but I guess is not ok, obviously because the code is not correct. How can I migrate SimpleITK image data to vtkImageData.
Sorry for this (maybe) easy question, for me is not :slight_smile:

pImageImport->SetImportVoidPointer(&img); this needs to be replaced by pImageImport->SetImportVoidPointer(img.GetBufferAsVoidPointer()); or something similar. Then you need a series of pImageImport->SetSpacing(...); calls for spacing, size/extent, origin. Finally pImageImport->Update();. Why are you trying to mix in DICOM reader?

1 Like

Kindly thank you Dzenan ! I have just a little until I solve my task :slight_smile:

I have just one more question: how to convert from sitk::Image::GetSize into vtkImageImport::SetDataExtent, because there is no SetSize inside of vtkImageImport.

Here is the code source:

	sitk::ImportImageFilter importer;
	....
	importer.SetBufferAsUInt8(in);
	.....
	sitk::Image img = importer.Execute();
	sitk::ConnectedThresholdImageFilter segmentationFilter;
	segmentationFilter.SetLower(20.0);
	segmentationFilter.SetUpper(1000);
	segmentationFilter.SetReplaceValue(255);
	img = segmentationFilter.Execute(img);
	....
	pImageImport->SetImportVoidPointer((void*)img.GetBufferAsUInt8());
	std::vector<double> spacing = img.GetSpacing();
	std::vector<double> origin = img.GetOrigin();
	std::vector<unsigned int> size = img.GetSize();
	pImageImport->SetDataSpacing(spacing[0], spacing[1], spacing[2]);
	pImageImport->SetDataOrigin(origin[0], origin[1], origin[2]);
	pImageImport->SetDataExtent();		// <-- this is the question, what should I put here ? because pImageImport doesn't have SetDataSize(), just SetDataExtent()
	pImageImport->Update();

Here is the documentation for vtkImageImport::SetDataExtent:

  • Get/Set the extent of the data buffer. The dimensions of your data
  • must be equal to (extent[1]-extent[0]+1) * (extent[3]-extent[2]+1) *
  • (extent[5]-DataExtent[4]+1). For example, for a 2D image use
  • (0,width-1, 0,height-1, 0,0).
    */
    vtkSetVector6Macro(DataExtent,int);
    vtkGetVector6Macro(DataExtent,int);
    void SetDataExtentToWholeExtent()
    {this->SetDataExtent(this->GetWholeExtent());}
    //@}

What should I put into pImageImport->SetDataExtent ?

P.S. Here is the list of sitk::Image::GetBufferAs

int8_t   *GetBufferAsInt8( );
uint8_t  *GetBufferAsUInt8( );
int16_t  *GetBufferAsInt16( );
uint16_t *GetBufferAsUInt16( );
int32_t  *GetBufferAsInt32( );
uint32_t *GetBufferAsUInt32( );
int64_t  *GetBufferAsInt64( );
uint64_t *GetBufferAsUInt64( );
float    *GetBufferAsFloat( );
double   *GetBufferAsDouble( );

Computing the extent should be something like this:

auto index=img->GetLargestPossibleRegion().GetIndex(); // this might be different in SimpleITK
for (unsigned d=0; d<Dimension; d++)
{
  extent[d]=index[d];
  extent[d+1]=index[d]+size[d]-1;
}
1 Like

For SimpleITK the starting index is always 0, then you need to call GetSize for the image size.

1 Like

Is there any similar way to find this index with SimpleITK only ? I have struggled days to insert SimpleITK in my project, I don’t want the same experience with ITK :slight_smile:

Moreover, my image object with what I have worked is sitk::Image.

No, it’s always zero.

1 Like

I am about to finish my task. But I guess I didn’t done something right:

    sitk::Image img = importer.Execute();
	spacing = img.GetSpacing();
	origin = img.GetOrigin();
	size = img.GetSize();
	pImageImport->SetDataSpacing(spacing[0], spacing[1], spacing[2]);
	pImageImport->SetDataOrigin(origin[0], origin[1], origin[2]);
	int extent[6];
	int index[3];
	index[0] = 0; index[1] = 0; index[2] = 0;
	for (unsigned int d = 0; d < img.GetDimension(); ++d)
	{
		extent[d] = index[d];
		extent[d + 1] = index[d] + size[d] - 1;
	}
	pImageImport->SetDataExtent(extent);
	pImageImport->SetImportVoidPointer((void*)img.GetBufferAsUInt8());
	pImageImport->Update(); <-- Warning: In D:\VTK\VTK-8.2.0\IO\Image\vtkImageImport.cxx, line 507
								vtkImageImport (000001AA5E16E6D0): 
								There is a distinction between the whole extent and the buffered
								extent of an imported image.  Use SetWholeExtent to set the extent
								of the entire image.  Use SetDataExtent to set the extent of the
								portion of the image that is in the buffer set with
								SetImportVoidPointer.  Both should be called even if the extents are
								the same.

Obviously, I have done extent in the right way …

That is really a great warning message coming from VTK! It tells you exactly what needs to be done.

1 Like

You also need pImageImport->SetWholeExtent(extent);

1 Like

Here is transfer from vtkImageData to sitk:

int* nDim = m_pDICOMReader->GetOutput()->GetDimensions();
uint8_t* in = new uint8_t[nDim[0] * nDim[1] * nDim[2]];
double* dSpacing = m_pDICOMReader->GetOutput()->GetSpacing();
double* dSpacing = m_pDICOMReader->GetOutput()->GetSpacing();
std::vector<double> spacing;
spacing.push_back(dSpacing[0]);
spacing.push_back(dSpacing[1]);
spacing.push_back(dSpacing[2]);
importer.SetSpacing(spacing);
double* dOrigin = m_pDICOMReader->GetOutput()->GetOrigin();
std::vector<double> origin;
origin.push_back(dOrigin[0]);
origin.push_back(dOrigin[1]);
origin.push_back(dOrigin[2]);
importer.SetOrigin(origin);
std::vector<unsigned int> size;
size.push_back((unsigned int)nDim[0]);
size.push_back((unsigned int)nDim[1]);
size.push_back((unsigned int)nDim[2]);
importer.SetSize(size);
importer.SetBufferAsUInt8(in);
TRACE(">>>>%d|%d|%d\n", nDim[0], nDim[1], nDim[2]);

TRACE result: >>>>512|512|44 (my CT data)

Here is the transfer from sitk to vtkImageImport:

    sitk::Image img = importer.Execute();
	spacing = img.GetSpacing();
	origin = img.GetOrigin();
	size = img.GetSize();
	pImageImport->SetDataSpacing(spacing[0], spacing[1], spacing[2]);
	pImageImport->SetDataOrigin(origin[0], origin[1], origin[2]);
	int extent[6];
	int index[3];
	index[0] = 0; index[1] = 0; index[2] = 0;
	for (unsigned int d = 0; d < img.GetDimension(); ++d)
	{
		extent[d] = index[d];
		extent[d + 1] = index[d] + size[d] - 1;
	}
	pImageImport->SetWholeExtent(extent);
	pImageImport->SetDataExtentToWholeExtent();
	pImageImport->SetNumberOfScalarComponents(1);
	pImageImport->SetImportVoidPointer((void*)img.GetBufferAsUInt8());
	pImageImport->Update();
	nDim = pImageImport->GetOutput()->GetDimensions();
	TRACE(">>>>%d|%d|%d\n", nDim[0], nDim[1], nDim[2]);

TRACE result: >>>>1|44|1

Where I lost those two dimensions ? And is not the right order …

I think I succeeded … and I post here the code for the case who someone will have the same issue:

	spacing = img.GetSpacing();
	img = sitk::Cast(img, sitk::PixelIDValueEnum::sitkUInt8);
	pImageImport->SetImportVoidPointer((void*)img.GetBufferAsUInt8());
	pImageImport->SetDataScalarTypeToUnsignedChar();
	pImageImport->SetDataExtent(0, (int)size[0] - 1, 0, (int)size[1] - 1, 0, size[2] - 1);
	pImageImport->SetWholeExtent(0, (int)size[0] - 1, 0, (int)size[1] - 1, 0, size[2] - 1);
	pImageImport->SetDataSpacing(spacing[0], spacing[1], spacing[2]);
	pImageImport->Update();
	nDim = pImageImport->GetOutput()->GetDimensions();
	TRACE(">>>>%d|%d|%d\n", nDim[0], nDim[1], nDim[2]);

Result: >>>>512|512|44, just the input size. Please correct me if something is wrong in my code.

Thank you !

1 Like

@dzenanz It looks like you had a bug in the indexing of extent in your proposed code. Should have been something like this:

for (unsigned d=0; d<Dimension; d++)
{
  extent[2*d]=0;
  extent[2*d+1]=size[d]-1;
}

I also removed the always 0 index;

@flaviu2 Thank you for sharing the solution. I didn’t see the problem with the above code before you posted your solution. I like the way you directly set the extents:

pImageImport->SetDataExtent(0, (int)size[0] - 1, 0, (int)size[1] - 1, 0, size[2] - 1);

:+1:
Compared to your other code you forgot to set the Origin and the NumberOfComponents.

1 Like

I’am glad to be useful here !!

1 Like