I am sharing my recent experience with VTKImageToImageFilter
.
I have a function poly_data_to_binary_image
that creates an image from an input vtkPolyData
, and using an input ITK image as a reference for dimensions and metadata (origin, spacing, direction).
At the beginning I was creating a vtkSmartPointer<vtkImageData>
as an input to VTKImageToImageFilter
, but because the vtkImageData was going out of scope, its pointer was deleted, and I ended with a dangling pointer in m_Buffer
of the output itk image.
The solution was to instead create a raw pointer vtkImageData *
and let m_Buffer
of the output ITK image (a itk::ImageContainer
) own the pointer, managing its deletion when needed.
Code below, hope somebody (or myself in the future) finds it useful.
BinaryImageType::Pointer poly_data_to_binary_image(
vtkPolyData *poly_data, const BinaryImageType::Pointer &reference_image) {
const auto &ref_size = reference_image->GetLargestPossibleRegion().GetSize();
const auto &ref_origin = reference_image->GetOrigin();
const auto &ref_spacing = reference_image->GetSpacing();
const auto &ref_direction = reference_image->GetDirection();
// Create a raw pointer, its memory will be owned (deleted when needed)
// by itkImageContainer of the output_itk_image.
vtkImageData *output_vtk_image = vtkImageData::New();
output_vtk_image->SetDimensions(ref_size[0], ref_size[1], ref_size[2]);
output_vtk_image->SetOrigin(ref_origin[0], ref_origin[1], ref_origin[2]);
output_vtk_image->SetSpacing(ref_spacing[0], ref_spacing[1], ref_spacing[2]);
output_vtk_image->AllocateScalars(VTK_UNSIGNED_CHAR, 1);
// vtk pipeline
vtkSmartPointer<vtkPolyDataToImageStencil> data_to_stencil =
vtkSmartPointer<vtkPolyDataToImageStencil>::New();
data_to_stencil->SetInputData(poly_data);
data_to_stencil->SetInformationInput(output_vtk_image);
data_to_stencil->Update();
vtkSmartPointer<vtkImageStencil> stencil_to_vtk_image =
vtkSmartPointer<vtkImageStencil>::New();
stencil_to_vtk_image->SetInputData(output_vtk_image);
stencil_to_vtk_image->SetStencilConnection(
data_to_stencil->GetOutputPort());
stencil_to_vtk_image->ReverseStencilOn(); // foreground will be 255
stencil_to_vtk_image->SetBackgroundValue(255);
stencil_to_vtk_image->Update();
// DeepCopy to avoid dangling pointers outside this scope.
output_vtk_image->DeepCopy(stencil_to_vtk_image->GetOutput());
itk::VTKImageToImageFilter<BinaryImageType>::Pointer vtk_to_itk_filter =
itk::VTKImageToImageFilter<BinaryImageType>::New();
vtk_to_itk_filter->SetInput(output_vtk_image);
vtk_to_itk_filter->Update();
BinaryImageType::Pointer output_itk_image = vtk_to_itk_filter->GetOutput();
output_itk_image->SetDirection(reference_image->GetDirection());
// We let the itk container to own (manage the deletion) of the vtkImageData
// managed by a raw pointer. If ImageData is managed by a vtkSmartPointer
// we will have a dangling pointer outside of this function.
output_itk_image->GetPixelContainer()->ContainerManageMemoryOn();
return output_itk_image;
}