I have vtkImageData object with dimensions (nx,ny,nz) with 1 scalar component, but for various reasons, I’d like to be able to work on it as a vtkImageData with dimensions (ny,nz,1) and nx scalar components without having the reallocate and copy the entire structure. So essentially what I am looking for is a way to ShallowCopy the structure and then assign it different grid dimensions and a number of components.
Has anyone done this before? So far I can change the grid dimensions, but when I try to change the number of scalar components (image->GetPointData()->GetScalars()->SetNumberOfComponents()). It will still change the original object.
I don’t think that VTK provides any documented way of doing this. But you can try doing things in a specific order so that VTK doesn’t reallocate the array:
vtkDataArray* a image->GetPointData()->GetScalars();
// shrink the array and hope underlying memory won't change
a->SetNumberOfTuples(1);
// set the desired number of components
a->SetNumberOfComponents(nx);
// set the new size
a->SetNumberOfTuples(ny * nz);
This code assumes that VTK will not reallocate the memory if the new size fits within the old size.
I tried the following, but the issue is that the number of scalar components in the original object is changed, presumably because the ShallowCopy just copies the reference pointer to the data array, rather than separating data and metadata.
Let me know what you think
vtkSmartPointer<vtkImageData> image1 = vtkSmartPointer<vtkImageData>::New();
image1->SetDimensions(5, 5, 5);
image1->AllocateScalars(VTK_FLOAT, 1);
{
int* dims = image1->GetDimensions();
// Gives 125
cout << "\nImage 1 number of points = " << image1->GetNumberOfPoints() << endl;
// gives 5,5,5
cout << "Image 1 dimensions =" << dims[0] << ", " << dims[1] << ", " << dims[2] << endl;
// Gives 1
cout << "Image 1 number of scalar components = " << image1->GetNumberOfScalarComponents() << endl;
}
// Shallow copy and change the dimensions
vtkSmartPointer<vtkImageData> image2=vtkSmartPointer<vtkImageData>::New();
image2->ShallowCopy(image1);
image2->SetDimensions(5, 5, 1);
vtkDataArray* arr=image2->GetPointData()->GetScalars();
arr->SetNumberOfTuples(1);
arr->SetNumberOfComponents(5);
arr->SetNumberOfTuples(5*5);
{
int* dims = image2->GetDimensions();
// This correctly gives 25
cout << "\nImage 2 number of points = " << image2->GetNumberOfPoints() << endl;
// this correctly gives 5,5,1
cout << "Image 2 dimensions =" << dims[0] << ", " << dims[1] << ", " << dims[2] << endl;
// This correctly gives 5
cout << "Image 2 number of scalar components = " << image2->GetNumberOfScalarComponents() << endl;
}
// But the number of scalar components for image1 have also changed.
{
int* dims = image1->GetDimensions();
// This correctly gives 125
cout << "\nImage 1 number of points = " << image1->GetNumberOfPoints() << endl;
// This correctly gives 5,5,5
cout << "Image 1 dimensions =" << dims[0] << ", " << dims[1] << ", " << dims[2] << endl;
// This gives 5 but should be 1
cout << "Image 1 number of scalar components = " << image1->GetNumberOfScalarComponents() << endl;
}
You can use vtkImageImport on the memory pointer of the original image to reuse the memory area in another image.
Note that VTK supports different memory layouts, so this is not a robust or clean solution, but if you always obtain the original image the same way then most likely it will always behave the same way. It also breaks the update request propagation chain, so if a filter before the image import is changed then you need to update the filter output manually.
The clean solution would be just to not do this. You are probably forcing an import, export, or processing filter to do something that it is not supposed to do. If you have a valid reason to do this (e.g., an importer incorrectly reads a color 2D image as a 3D scalar image) then that should be fixed/improved in the affected filters.
For the new image, you can make a new array that points to the same memory as the old array. Then you can adjust the size of this new array, and the old array will stay as-is.
vtkNew<vtkFloatArray> b;
vtkFloatArray* a = vtkFloatArray::SafeDownCast(image1->GetPointData()->GetScalars());
if (a != nullptr)
{
b->SetName(a->GetName());
b->SetNumberOfComponents(nx);
b->SetArray(a->GetPointer(0), ny*nz, 1);
image2->GetPointData()->SetScalars(b);
}
(Note: I wrote this code off the top of my head so it’s likely to contain mistakes).
Note that if the first array is resized, destroyed, etc. then the memory in the second array becomes invalid, since they’re sharing the same block of memory. VTK does not provide a safe way for arrays to share memory.