[Insight-users] 4D Bspline approximation

DE CRAENE, MATHIEU mathieu.de_craene at philips.com
Wed Sep 19 13:39:06 EDT 2012


Hi Nick et al.




I am trying to use the itkBSplineScatteredDataPointSetToImageFilter to regularize the displacements of volumetric mesh nodes using a 4D Bspline field. The meshes represent the right and left ventricles of the heart over one cycle.

The idea is to enter velocity as "frame to frame displacements" for a subset of all mesh nodes. Then to find the best b spline fit using the filter.

To check that the result is a correct approximation, I integrated the velocity field using TimeVaryingBSplineVelocityFieldTransform and apply the resulting transform to the first mesh.

My problem is that I obtain a zero displacement i.e. an identity transform at every frame.
It could come from an incorrect way of using the "projection" filter or the final transform.

My input data is here: https://dl.dropbox.com/u/57469885/meshes-stacom.zip
And the source code is below.

It would be very helpful if you can spot any obvious misuse of the filters!

Some short questions :

- Is it correct to use the same size as the number of control points in itkBSplineScatteredDataPointSetToImageFilter ?
- Should I take care of having enough border control points around the mesh nodes (padding) ? How many nodes ?


Thanks for any hint !!



Math


==========================================================================

// Image types and other standard things
#include <iostream>
#include <cstdlib>
#include "vtkUnstructuredGrid.h"
#include <vtkUnstructuredGridReader.h>
#include <vtkUnstructuredGridWriter.h>
#include <vtkSmartPointer.h>
#include <vtkCellData.h>
#include "itkTimeVaryingBSplineVelocityFieldTransform.h"
#include "itkBSplineScatteredDataPointSetToImageFilter.h"
#include <itkPointSet.h>
#include <itkImageRegionIterator.h>

int main( int argc, char * argv[] )
{
        if (argc < 3)
        {
                std::cerr << "Usage : " << argv[0] << "InputMeshList.txt outputMeshPrefix" << std::endl;
                exit(EXIT_FAILURE);
        }
        const unsigned int MAX_PATH_LENGTH = 1024;
        const unsigned int ImageDimension = 3;

        int arg = 1;
        const char* InputMeshListFileName = argv[arg]; arg++;
        const char* outputMeshPrefixFileName = argv[arg]; arg++;

        // reading input list
        std::ifstream f (InputMeshListFileName, std::ifstream::in);
        if (f==0)
        {
                return EXIT_FAILURE;
        }

        std::string buffer;
        std::vector<std::string> inputUGFileNames;
        f>>buffer;

        while (!f.eof())
        {
                inputUGFileNames.push_back(buffer);
                f>>buffer;
        }

        // reading all meshes
        std::vector<vtkSmartPointer<vtkUnstructuredGrid>> ugs;
        for (unsigned int t=0; t<inputUGFileNames.size(); t++)
        {
                vtkSmartPointer<vtkUnstructuredGridReader> reader
                        = vtkSmartPointer<vtkUnstructuredGridReader>::New();
                reader->SetFileName(inputUGFileNames[t].c_str());
                reader->Update();
                ugs.push_back(reader->GetOutput());
        }

        // check that we have at least to meshes
        if (ugs.size() <=1)
        {
                std::cerr << "Insufficient (" << ugs.size() << ") number of meshes" << std::endl;
                exit(EXIT_FAILURE);
        }

        // Instantiate velocity and adjust bspline field to mesh bounding box
        typedef itk::TimeVaryingBSplineVelocityFieldTransform< double, ImageDimension > TransformType;
        typedef TransformType::DisplacementVectorType DisplacementVectorType;
        typedef TransformType::TimeVaryingVelocityFieldControlPointLatticeType VelocityFieldType;
        typedef itk::PointSet<DisplacementVectorType, ImageDimension + 1> PointSetType;
        typedef itk::BSplineScatteredDataPointSetToImageFilter<PointSetType, VelocityFieldType> BSplineFilterType;

        // Filling point set with velocity (incr. displ.) data
        PointSetType::Pointer velocityFieldPoints = PointSetType::New();
        velocityFieldPoints->Initialize();

        typedef BSplineFilterType::WeightsContainerType WeightsContainerType;
        WeightsContainerType::Pointer velocityFieldWeights = WeightsContainerType::New();

        PointSetType::PointType spatioTemporalPoint;
        DisplacementVectorType velocity;
        PointSetType::PointIdentifier id = 0;
        for (unsigned int t=0; t<ugs.size()-1; t++)
        {
                for (int p=0; p<ugs[t]->GetNumberOfPoints(); p+=50,id++)
                {
                        for (int d=0; d<ImageDimension; d++)
                        {
                                spatioTemporalPoint[d] = ugs[t]->GetPoints()->GetPoint(p)[d];
                                velocity[d] =
                                (   ugs[t+1]->GetPoints()->GetPoint(p)[d]
                                -       ugs[t  ]->GetPoints()->GetPoint(p)[d] )
                                * (double) ugs.size();
                        }
                        spatioTemporalPoint[ImageDimension] = (double)t / (double) ugs.size();

                        velocityFieldPoints->SetPoint(id,  spatioTemporalPoint);
                        velocityFieldPoints->SetPointData(id, velocity );
                        velocityFieldWeights->InsertElement( id, 1. );
                }
        }

        // Setting up Bspline regression of the velocity field
        BSplineFilterType::Pointer bspliner = BSplineFilterType::New();
        unsigned int nocp[4] = {20,20,20,10};
        BSplineFilterType::SizeType domainSize;
        for (int d=0; d<4; d++)
                domainSize[d] = nocp[d];
        BSplineFilterType::ArrayType numberOfControlPoints(nocp);
        BSplineFilterType::PointType origin;
        BSplineFilterType::SpacingType spacing;
        double bb[6];
        ugs[0]->GetBounds(bb);

        // safety margins
        double offset[4]={5., 5., 5., 0.5};

        spacing[0] = ( bb[1] - bb[0] + 2.*offset[0] ) / (double)(domainSize[0]-3);
        spacing[1] = ( bb[3] - bb[2] + 2.*offset[1] ) / (double)(domainSize[1]-3);
        spacing[2] = ( bb[5] - bb[4] + 2.*offset[2] ) / (double)(domainSize[2]-3);
        spacing[3] = ( 1.                        + 2.*offset[3] ) / (double)(domainSize[3]-3);

        origin[0] = bb[0] - offset[0] - spacing[0];
        origin[1] = bb[2] - offset[1] - spacing[1];
        origin[2] = bb[4] - offset[2] - spacing[2];
        origin[3] =       - offset[3] - spacing[3];

        bspliner->SetOrigin( origin );
        bspliner->SetSpacing( spacing );
        bspliner->SetNumberOfControlPoints( numberOfControlPoints );
        bspliner->SetNumberOfLevels( 1 );
        bspliner->SetSplineOrder( 3 );
        bspliner->SetSize( domainSize );
        bspliner->SetInput( velocityFieldPoints );
        bspliner->SetPointWeights( velocityFieldWeights );
        bspliner->GenerateOutputImageOff();

        try
        {
                bspliner->Update();
        }
        catch( itk::ExceptionObject & err )
        {
                std::cerr << "ExceptionObject caught !" << std::endl;
                std::cerr << err << std::endl;
                return EXIT_FAILURE;
        }

        TransformType::TimeVaryingVelocityFieldControlPointLatticePointer lattice = bspliner->GetPhiLattice();

        // Transformation that is supposed to approximate our velocity field
        TransformType::Pointer transform = TransformType::New();
        transform->SetTimeVaryingVelocityFieldControlPointLattice(lattice);
        transform->SetSplineOrder(bspliner->GetSplineOrder()[0]);

        // Allocating
        std::vector<vtkSmartPointer<vtkUnstructuredGrid>> out_ugs;
        for (unsigned int t=0; t<ugs.size(); t++)
        {
                vtkSmartPointer<vtkUnstructuredGrid> oug = vtkSmartPointer<vtkUnstructuredGrid>::New();
                oug->DeepCopy(ugs[0]);
                out_ugs.push_back(oug);
        }

        // Recomputing transform for all points
        TransformType::OutputPointType outp;
        for (unsigned int t=0; t<ugs.size(); t++)
        {
                transform->SetUpperTimeBound((double)t / (double)ugs.size());
                transform->SetLowerTimeBound((double)0);
                transform->IntegrateVelocityField();

                for (int p=0; p<ugs[0]->GetNumberOfPoints(); p++)
                {
                        for (int d=0; d<ImageDimension; d++)
                        {
                                spatioTemporalPoint[d] = ugs[0]->GetPoints()->GetPoint(p)[d];
                        }
                        spatioTemporalPoint[ImageDimension] = 0.;

                        outp = transform->TransformPoint(ugs[0]->GetPoints()->GetPoint(p));
                        out_ugs[t]->GetPoints()->SetPoint(p, outp.GetDataPointer());
                }
        }

        // Writing output
        char outputFileName[MAX_PATH_LENGTH];
        for (unsigned int t=0; t<ugs.size(); t++)
        {
                sprintf(outputFileName, "%s%03d.vtk", outputMeshPrefixFileName, t);
                vtkSmartPointer<vtkUnstructuredGridWriter> wr =                                                                 vtkSmartPointer<vtkUnstructuredGridWriter>::New();
                wr->SetFileName(outputFileName);
                wr->SetInput(out_ugs[t]);
                wr->Update();
        }


        exit(EXIT_SUCCESS);
}



________________________________
The information contained in this message may be confidential and legally protected under applicable law. The message is intended solely for the addressee(s). If you are not the intended recipient, you are hereby notified that any use, forwarding, dissemination, or reproduction of this message is strictly prohibited and may be unlawful. If you are not the intended recipient, please contact the sender by return e-mail and destroy all copies of the original message.



More information about the Insight-users mailing list