// SPDX-FileCopyrightText: Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen // SPDX-License-Identifier: BSD-3-Clause // This tests B-spline image warping #include "vtkBSplineTransform.h" #include "vtkImageBSplineCoefficients.h" #include "vtkImageBSplineInterpolator.h" #include "vtkImageBlend.h" #include "vtkImageGridSource.h" #include "vtkImageMapToColors.h" #include "vtkImageReslice.h" #include "vtkImageViewer.h" #include "vtkLookupTable.h" #include "vtkPoints.h" #include "vtkRegressionTestImage.h" #include "vtkRenderWindow.h" #include "vtkRenderWindowInteractor.h" #include "vtkRenderer.h" #include "vtkSmartPointer.h" #include "vtkThinPlateSplineTransform.h" #include "vtkTransformToGrid.h" int TestBSplineWarp(int argc, char* argv[]) { // first, create an image that looks like // graph paper by combining two image grid // sources via vtkImageBlend vtkSmartPointer imageGrid1 = vtkSmartPointer::New(); imageGrid1->SetGridSpacing(4, 4, 0); imageGrid1->SetGridOrigin(0, 0, 0); imageGrid1->SetDataExtent(0, 255, 0, 255, 0, 0); imageGrid1->SetDataScalarTypeToUnsignedChar(); vtkSmartPointer imageGrid2 = vtkSmartPointer::New(); imageGrid2->SetGridSpacing(16, 16, 0); imageGrid2->SetGridOrigin(0, 0, 0); imageGrid2->SetDataExtent(0, 255, 0, 255, 0, 0); imageGrid2->SetDataScalarTypeToUnsignedChar(); vtkSmartPointer table1 = vtkSmartPointer::New(); table1->SetTableRange(0, 1); table1->SetValueRange(1.0, 0.7); table1->SetSaturationRange(0.0, 1.0); table1->SetHueRange(0.12, 0.12); table1->SetAlphaRange(1.0, 1.0); table1->Build(); vtkSmartPointer table2 = vtkSmartPointer::New(); table2->SetTableRange(0, 1); table2->SetValueRange(1.0, 0.0); table2->SetSaturationRange(0.0, 0.0); table2->SetHueRange(0.0, 0.0); table2->SetAlphaRange(0.0, 1.0); table2->Build(); vtkSmartPointer map1 = vtkSmartPointer::New(); map1->SetInputConnection(imageGrid1->GetOutputPort()); map1->SetLookupTable(table1); vtkSmartPointer map2 = vtkSmartPointer::New(); map2->SetInputConnection(imageGrid2->GetOutputPort()); map2->SetLookupTable(table2); vtkSmartPointer blend = vtkSmartPointer::New(); blend->AddInputConnection(map1->GetOutputPort()); blend->AddInputConnection(map2->GetOutputPort()); // next, create a ThinPlateSpline transform, which // will then be used to create the B-spline transform vtkSmartPointer p1 = vtkSmartPointer::New(); p1->SetNumberOfPoints(8); p1->SetPoint(0, 0, 0, 0); p1->SetPoint(1, 0, 255, 0); p1->SetPoint(2, 255, 0, 0); p1->SetPoint(3, 255, 255, 0); p1->SetPoint(4, 96, 96, 0); p1->SetPoint(5, 96, 159, 0); p1->SetPoint(6, 159, 159, 0); p1->SetPoint(7, 159, 96, 0); vtkSmartPointer p2 = vtkSmartPointer::New(); p2->SetNumberOfPoints(8); p2->SetPoint(0, 0, 0, 0); p2->SetPoint(1, 0, 255, 0); p2->SetPoint(2, 255, 0, 0); p2->SetPoint(3, 255, 255, 0); p2->SetPoint(4, 96, 159, 0); p2->SetPoint(5, 159, 159, 0); p2->SetPoint(6, 159, 96, 0); p2->SetPoint(7, 96, 96, 0); vtkSmartPointer thinPlate = vtkSmartPointer::New(); thinPlate->SetSourceLandmarks(p2); thinPlate->SetTargetLandmarks(p1); thinPlate->SetBasisToR2LogR(); // convert the thin plate spline into a B-spline, by // sampling it onto a grid and then computing the // B-spline coefficients vtkSmartPointer transformToGrid = vtkSmartPointer::New(); transformToGrid->SetInput(thinPlate); transformToGrid->SetGridSpacing(16.0, 16.0, 1.0); transformToGrid->SetGridOrigin(0.0, 0.0, 0.0); transformToGrid->SetGridExtent(0, 16, 0, 16, 0, 0); vtkSmartPointer grid = vtkSmartPointer::New(); grid->SetInputConnection(transformToGrid->GetOutputPort()); grid->UpdateWholeExtent(); // create the B-spline transform, scale the deformation by // half to demonstrate how deformation scaling works vtkSmartPointer transform = vtkSmartPointer::New(); transform->SetCoefficientData(grid->GetOutput()); transform->SetDisplacementScale(0.5); transform->SetBorderModeToZero(); // invert the transform before passing it to vtkImageReslice transform->Inverse(); // reslice the image through the B-spline transform, // using B-spline interpolation and the "Repeat" // boundary condition vtkSmartPointer prefilter = vtkSmartPointer::New(); prefilter->SetInputConnection(blend->GetOutputPort()); prefilter->SetBorderModeToRepeat(); prefilter->SetSplineDegree(3); vtkSmartPointer interpolator = vtkSmartPointer::New(); interpolator->SetSplineDegree(3); vtkSmartPointer reslice = vtkSmartPointer::New(); reslice->SetInputConnection(prefilter->GetOutputPort()); reslice->SetResliceTransform(transform); reslice->WrapOn(); reslice->SetInterpolator(interpolator); reslice->SetOutputSpacing(1.0, 1.0, 1.0); reslice->SetOutputOrigin(-32.0, -32.0, 0.0); reslice->SetOutputExtent(0, 319, 0, 319, 0, 0); // set the window/level to 255.0/127.5 to view full range vtkSmartPointer iren = vtkSmartPointer::New(); vtkSmartPointer viewer = vtkSmartPointer::New(); viewer->SetupInteractor(iren); viewer->SetInputConnection(reslice->GetOutputPort()); viewer->SetColorWindow(255.0); viewer->SetColorLevel(127.5); viewer->SetZSlice(0); viewer->Render(); vtkRenderWindow* renWin = viewer->GetRenderWindow(); int retVal = vtkRegressionTestImage(renWin); if (retVal == vtkRegressionTester::DO_INTERACTOR) { iren->Start(); } return !retVal; }