Example #1
0
void parseDimensionToTemplate(MatlabImportFilter::Pointer matlabImport,
			      MatlabExportFilter::Pointer matlabExport) {

  // retrieve pointers to the inputs that we are going to need here
  typedef MatlabImportFilter::MatlabInputPointer MatlabInputPointer; 
  MatlabInputPointer inX         = matlabImport->GetRegisteredInput("X");

  // dimension of points
  mwSize Nx = mxGetN(inX->pm);

  // parse the dimension value
  switch (Nx) {
  case 2:
    parseTransformType<TScalarType, 2>(matlabImport, matlabExport);
    break;
  case 3:
    parseTransformType<TScalarType, 3>(matlabImport, matlabExport);
    break;
  default:
    mexErrMsgTxt("Input points can only have dimensions 2 or 3");
    break;
  }

  // exit function
  return;

}
Example #2
0
void parseTransformType(MatlabImportFilter::Pointer matlabImport,
			MatlabExportFilter::Pointer matlabExport) {


  // retrieve pointers to the inputs that we are going to need here
  typedef MatlabImportFilter::MatlabInputPointer MatlabInputPointer; 
  MatlabInputPointer inTRANSFORM = matlabImport->GetRegisteredInput("TRANSFORM");

  // get type of transform
  char *transform = mxArrayToString(inTRANSFORM->pm);
  if (transform == NULL) {
    mexErrMsgTxt("Cannot read TRANSFORM string");
  }
  
  // kernel transform types
  typedef itk::ElasticBodySplineKernelTransform<TScalarType, Dimension> 
    ElasticTransformType;
  typedef itk::ElasticBodyReciprocalSplineKernelTransform<TScalarType, Dimension> 
    ElasticReciprocalTransformType;
  typedef itk::ThinPlateSplineKernelTransform<TScalarType, Dimension> 
    TpsTransformType;
  typedef itk::ThinPlateR2LogRSplineKernelTransform<TScalarType, Dimension> 
    TpsR2LogRTransformType;
  typedef itk::VolumeSplineKernelTransform<TScalarType, Dimension> 
    VolumeTransformType;

  // select transform function
  if (!strcmp(transform, "elastic")) {
    runKernelTransform<TScalarType, Dimension, 
		       ElasticTransformType>(matlabImport, matlabExport);
  } else if (!strcmp(transform, "elasticr")) {
    runKernelTransform<TScalarType, Dimension, 
		       ElasticReciprocalTransformType>(matlabImport, matlabExport);
  } else if (!strcmp(transform, "tps")) {
    runKernelTransform<TScalarType, Dimension, 
		       TpsTransformType>(matlabImport, matlabExport);
  } else if (!strcmp(transform, "tpsr2")) {
    runKernelTransform<TScalarType, Dimension, 
		       TpsR2LogRTransformType>(matlabImport, matlabExport);
  } else if (!strcmp(transform, "volume")) {
    runKernelTransform<TScalarType, Dimension, 
		       VolumeTransformType>(matlabImport, matlabExport);
  } else if (!strcmp(transform, "bspline")) {
    runBSplineTransform<TScalarType, Dimension>(matlabImport, matlabExport);
  } else if (!strcmp(transform, "")) {
    std::cout << 
      "Implemented transform types: elastic, elasticr, tps, tpsr2, volume, bspline" 
	      << std::endl;
    return;
  } else {
    mexErrMsgTxt("Transform not implemented");
  }

  // exit function
  return;
  
}
Example #3
0
// parseInputTypeToTemplate()
void parseInputTypeToTemplate(MatlabImportFilter::Pointer matlabImport,
			      MatlabExportFilter::Pointer matlabExport) {

  // retrieve pointers to the inputs that we are going to need here
  typedef MatlabImportFilter::MatlabInputPointer MatlabInputPointer; 
  MatlabInputPointer inX         = matlabImport->GetRegisteredInput("X");
  MatlabInputPointer inY         = matlabImport->GetRegisteredInput("Y");
  MatlabInputPointer inXI        = matlabImport->GetRegisteredInput("XI");

  // point coordinate type
  mxClassID pointCoordClassId = mxGetClassID(inX->pm);

  // check that all point coordinates have the same type (it simplifies
  // things with templates)
  if ((pointCoordClassId != mxGetClassID(inY->pm))
      | (pointCoordClassId != mxGetClassID(inXI->pm))) {
    mexErrMsgTxt("Input arguments X, Y and XI must have the same type");
  }
  
  // swith input point type
  switch(pointCoordClassId) {
  case mxDOUBLE_CLASS:
    parseDimensionToTemplate<double>(matlabImport, matlabExport);
    break;
  case mxSINGLE_CLASS:
    parseDimensionToTemplate<float>(matlabImport, matlabExport);
    break;
  case mxUNKNOWN_CLASS:
    mexErrMsgTxt("Point coordinates have unknown type");
    break;
  default:
    mexErrMsgTxt("Point coordinates can only be of type single or double");
    break;
  }

  // exit function
  return;
  
}
Example #4
0
/*
 * mexFunction(): entry point for the mex function
 */
void mexFunction(int nlhs, mxArray *plhs[], 
		 int nrhs, const mxArray *prhs[]) {

  // interface to deal with input arguments from Matlab
  enum InputIndexType {IN_TRI, IN_X, InputIndexType_MAX};
  MatlabImportFilter::Pointer matlabImport = MatlabImportFilter::New();
  matlabImport->ConnectToMatlabFunctionInput(nrhs, prhs);

  // check that we have at least a filter name and input image
  matlabImport->CheckNumberOfArguments(2, InputIndexType_MAX);

  // register the inputs for this function at the import filter
  typedef MatlabImportFilter::MatlabInputPointer MatlabInputPointer;
  MatlabInputPointer inTRI = matlabImport->RegisterInput(IN_TRI, "TRI");
  MatlabInputPointer inX = matlabImport->RegisterInput(IN_X, "X");

  // interface to deal with outputs to Matlab
  enum OutputIndexType {OUT_A, OutputIndexType_MAX};
  MatlabExportFilter::Pointer matlabExport = MatlabExportFilter::New();
  matlabExport->ConnectToMatlabFunctionOutput(nlhs, plhs);

  // check number of outputs the user is asking for
  matlabExport->CheckNumberOfArguments(0, OutputIndexType_MAX);

  // register the outputs for this function at the export filter
  typedef MatlabExportFilter::MatlabOutputPointer MatlabOutputPointer;
  MatlabOutputPointer outA = matlabExport->RegisterOutput(OUT_A, "A");

  // default coordinates are NaN values, so that the user can spot
  // whether there was any problem reading them
  Point def(mxGetNaN(), mxGetNaN(), mxGetNaN());

  // if any of the inputs is empty, the output is empty too
  if (mxIsEmpty(prhs[IN_TRI]) || mxIsEmpty(prhs[IN_X])) {
    matlabExport->CopyEmptyArrayToMatlab(outA);
    return;
  }

  // get size of input matrix
  mwSize nrowsTri = mxGetM(prhs[IN_TRI]);
  mwSize ncolsTri = mxGetN(prhs[IN_TRI]);
  mwSize ncolsX = mxGetN(prhs[IN_X]);
  if ((ncolsTri != 3) || (ncolsX != 3)) {
    mexErrMsgTxt("Both input arguments must have 3 columns");
  }

  // initialise output
  double *area = matlabExport->AllocateColumnVectorInMatlab<double>(outA, nrowsTri);
  
  // read triangular mesh from function
  Triangle tri;
  mwIndex v0, v1, v2; // indices of the 3 vertices of each triangle
  Point x0, x1, x2; // coordinates of the 3 vertices of each triangle

  for (mwIndex i = 0; i < nrowsTri; ++i) {

    // exit if user pressed Ctrl+C
    ctrlcCheckPoint(__FILE__, __LINE__);

    // get indices of the 3 vertices of each triangle. These indices
    // follow Matlab's convention v0 = 1, 2, ..., n
    v0 = matlabImport->ReadScalarFromMatlab<mwIndex>(inTRI, i, 0, mxGetNaN());
    v1 = matlabImport->ReadScalarFromMatlab<mwIndex>(inTRI, i, 1, mxGetNaN());
    v2 = matlabImport->ReadScalarFromMatlab<mwIndex>(inTRI, i, 2, mxGetNaN());
    if (mxIsNaN(v0) || mxIsNaN(v1) || mxIsNaN(v2)) {
      mexErrMsgTxt("Input TRI: Vertex index is NaN");
    }
    
    // get coordinates of the 3 vertices (substracting 1 so that
    // indices follow the C++ convention 0, 1, ..., n-1)
    x0 = matlabImport->ReadRowVectorFromMatlab<void, Point>(inX, v0 - 1, def);
    x1 = matlabImport->ReadRowVectorFromMatlab<void, Point>(inX, v1 - 1, def);
    x2 = matlabImport->ReadRowVectorFromMatlab<void, Point>(inX, v2 - 1, def);

    // create triangle from the vertices read at the input
    tri = Triangle(x0, x1, x2);

    // compute triangle area
    area[i] = std::sqrt(tri.squared_area());
    
    // // DEBUG
    // std::cout << x0 << "\t" << x1 << "\t" << x2 << "\t" << std::sqrt(tri.squared_area()) << std::endl;
  }


  
}
Example #5
0
/*
 * mexFunction(): entry point for the mex function
 */
void mexFunction(int nlhs, mxArray *plhs[], 
		 int nrhs, const mxArray *prhs[]) {

  // interface to deal with input arguments from Matlab
  MatlabImportFilter::Pointer matlabImport = MatlabImportFilter::New();
  matlabImport->ConnectToMatlabFunctionInput(nrhs, prhs);

  // register all possible inputs for this function at the import filter
  typedef MatlabImportFilter::MatlabInputPointer MatlabInputPointer;
  MatlabInputPointer inTRANSFORM = matlabImport->RegisterInput(IN_TRANSFORM, "TRANSFORM");
  MatlabInputPointer inX         = matlabImport->RegisterInput(IN_X, "X");
  MatlabInputPointer inY         = matlabImport->RegisterInput(IN_Y, "Y");
  MatlabInputPointer inXI        = matlabImport->RegisterInput(IN_XI, "XI");
  MatlabInputPointer inORDER     = matlabImport->RegisterInput(IN_ORDER, "ORDER");
  MatlabInputPointer inLEVELS    = matlabImport->RegisterInput(IN_LEVELS, "LEVELS");

  // interface to deal with output arguments from Matlab
  MatlabExportFilter::Pointer matlabExport = MatlabExportFilter::New();
  matlabExport->ConnectToMatlabFunctionOutput(nlhs, plhs);
    
  // register the outputs for this function at the export filter
  typedef MatlabExportFilter::MatlabOutputPointer MatlabOutputPointer;
  MatlabOutputPointer outYI = matlabExport->RegisterOutput(OUT_YI, "YI");

  // check number of input and output arguments
  matlabImport->CheckNumberOfArguments(4, InputIndexType_MAX);
  matlabExport->CheckNumberOfArguments(0, OutputIndexType_MAX);
    
  // if there are no points to warp, return empty array
  if (mxIsEmpty(inXI->pm)) {
    matlabExport->CopyEmptyArrayToMatlab(outYI);
    return;
  }

  // check size of input arguments
  mwSize Mx = mxGetM(inX->pm); // number of source points
  mwSize My = mxGetM(inY->pm); // number of target points
  mwSize Dimension = mxGetN(inX->pm); // dimension of source points
  mwSize dimy = mxGetN(inY->pm); // dimension of target points
  mwSize dimxi = mxGetN(inXI->pm); // dimension of points to be warped
  
  // the landmark arrays must have the same number of points
  // (degenerate case, both are empty)
  if (Mx != My) {
    mexErrMsgTxt("X and Y must have the same number of points (i.e. rows).");
  }

  // if there are no landmarks, we apply no transformation to the
  // points to warp
  if (mxIsEmpty(inX->pm)) {
    *outYI->ppm = mxDuplicateArray(inXI->pm);
    return;
  }

  // if there are landmarks and points to warp, all must have the same dimension
  if (Dimension != dimy || Dimension != dimxi) {
    mexErrMsgTxt("X, Y and XI must all have the same dimension (i.e. number of columns).");
  }

  // run filter (this function starts a cascade of functions designed
  // to translate the run-time type variables like inputVoxelClassId
  // to templates, so that we don't need to nest lots of "switch" or
  // "if" statements)
  parseInputTypeToTemplate(matlabImport, matlabExport);

  // exit successfully
  return;

}
Example #6
0
void runKernelTransform(MatlabImportFilter::Pointer matlabImport,
			MatlabExportFilter::Pointer matlabExport) {

  // check number of input arguments (the kernel transform syntax
  // accepts up to 4 arguments only. Thus, we cannot use InputIndexType_MAX)
  matlabImport->CheckNumberOfArguments(4, 4);

  // retrieve pointers to the inputs that we are going to need here
  typedef MatlabImportFilter::MatlabInputPointer MatlabInputPointer; 
  MatlabInputPointer inX         = matlabImport->GetRegisteredInput("X");
  MatlabInputPointer inY         = matlabImport->GetRegisteredInput("Y");
  MatlabInputPointer inXI        = matlabImport->GetRegisteredInput("XI");

  // register the outputs for this function at the export filter
  typedef MatlabExportFilter::MatlabOutputPointer MatlabOutputPointer;
  MatlabOutputPointer outYI = matlabExport->RegisterOutput(OUT_YI, "YI");

  // get size of input arguments
  mwSize Mx = mxGetM(inX->pm); // number of source points
  mwSize Mxi = mxGetM(inXI->pm); // number of points to be warped
  mwSize ndimxi; // number of dimension of points to be warped
  const mwSize *dimsxi; // dimensions vector of array of points to be warped

  // create pointers to input matrices
  TScalarType *x 
    = (TScalarType *)mxGetData(inX->pm); // source points
  TScalarType *y 
    = (TScalarType *)mxGetData(inY->pm); // target points
  TScalarType *xi 
    = (TScalarType *)mxGetData(inXI->pm); // points to be warped
  if (x == NULL) {
    mexErrMsgTxt("Cannot get a pointer to input X");
  }
  if (y == NULL) {
    mexErrMsgTxt("Cannot get a pointer to input Y");
  }
  if (xi == NULL) {
    mexErrMsgTxt("Cannot get a pointer to input XI");
  }

  // type definitions and variables to store points for the kernel transform
  typedef typename TransformType::PointSetType PointSetType;
  typename PointSetType::Pointer fixedPointSet = PointSetType::New();
  typename PointSetType::Pointer movingPointSet = PointSetType::New();
  typename PointSetType::Pointer toWarpPointSet = PointSetType::New();
  typedef typename PointSetType::PointsContainer PointsContainer;
  typename PointsContainer::Pointer fixedPointContainer = PointsContainer::New();
  typename PointsContainer::Pointer movingPointContainer = PointsContainer::New();
  typedef typename PointSetType::PointType PointType;
  PointType fixedPoint;
  PointType movingPoint;
  PointType toWarpPoint;
  PointType warpedPoint;

  // duplicate the input x and y matrices to PointSet format so that
  // we can pass it to the ITK function
  mwSize pointId=0;
  for (mwSize row=0; row < Mx; ++row) {
    for (mwSize col=0; col < (mwSize)Dimension; ++col) {
      fixedPoint[CAST2MWSIZE(col)] = y[Mx * col + row];
      movingPoint[CAST2MWSIZE(col)] = x[Mx * col + row];
    }
    fixedPointContainer->InsertElement(pointId, fixedPoint);
    movingPointContainer->InsertElement(pointId, movingPoint);
    ++pointId;
  }
  fixedPointSet->SetPoints(fixedPointContainer);
  movingPointSet->SetPoints(movingPointContainer);

  // compute the transform
  typename TransformType::Pointer transform;
  transform = TransformType::New();
  
  transform->SetSourceLandmarks(movingPointSet);
  transform->SetTargetLandmarks(fixedPointSet);
  transform->ComputeWMatrix();
  
  // create output vector and pointer to populate it
  ndimxi = mxGetNumberOfDimensions(inXI->pm);
  dimsxi = mxGetDimensions(inXI->pm);
  std::vector<mwSize> size;
  for (mwIndex i = 0; i < ndimxi; ++i) {
    size.push_back(dimsxi[i]);
  }

  TScalarType *yi 
    = matlabExport->AllocateNDArrayInMatlab<TScalarType>(outYI, size);

  // transform points
  for (mwSize row=0; row < Mxi; ++row) {
    for (mwSize col=0; col < (mwSize)Dimension; ++col) {
      toWarpPoint[CAST2MWSIZE(col)] = xi[Mxi * col + row];
    }
    warpedPoint = transform->TransformPoint(toWarpPoint);
    for (mwSize col=0; col < (mwSize)Dimension; ++col) {
      yi[Mxi * col + row] = warpedPoint[CAST2MWSIZE(col)];
    }
  }
  
  // exit function
  return;
  
}
Example #7
0
void runBSplineTransform(MatlabImportFilter::Pointer matlabImport,
			 MatlabExportFilter::Pointer matlabExport) {

  // retrieve pointers to the inputs that we are going to need here
  typedef MatlabImportFilter::MatlabInputPointer MatlabInputPointer; 
  MatlabInputPointer inX         = matlabImport->GetRegisteredInput("X");
  MatlabInputPointer inY         = matlabImport->GetRegisteredInput("Y");
  MatlabInputPointer inXI        = matlabImport->GetRegisteredInput("XI");
  MatlabInputPointer inORDER     = matlabImport->GetRegisteredInput("ORDER");
  MatlabInputPointer inLEVELS    = matlabImport->GetRegisteredInput("LEVELS");

  // register the output for this function at the export filter
  typedef MatlabExportFilter::MatlabOutputPointer MatlabOutputPointer;
  MatlabOutputPointer outYI = matlabExport->RegisterOutput(OUT_YI, "YI");

  // spline order (input argument): default or user-provided
  unsigned int splineOrder = matlabImport->ReadScalarFromMatlab<unsigned int>(inORDER, 3);

  // number of levels (input argument): default or user-provided
  unsigned int numOfLevels = matlabImport->ReadScalarFromMatlab<unsigned int>(inLEVELS, 5);

  // get size of input arguments
  mwSize Mx = mxGetM(inX->pm); // number of source points
  mwSize Mxi = mxGetM(inXI->pm); // number of points to be warped

  // pointers to input matrices
  TScalarType *x 
    = (TScalarType *)mxGetData(inX->pm); // source points
  TScalarType *y 
    = (TScalarType *)mxGetData(inY->pm); // target points
  TScalarType *xi 
    = (TScalarType *)mxGetData(inXI->pm); // points to be warped
  if (x == NULL) {
    mexErrMsgTxt("Cannot get a pointer to input X");
  }
  if (y == NULL) {
    mexErrMsgTxt("Cannot get a pointer to input Y");
  }
  if (xi == NULL) {
    mexErrMsgTxt("Cannot get a pointer to input XI");
  }

  // type definitions for the BSPline transform
  typedef itk::Vector<TScalarType, Dimension> DataType;
  typedef itk::PointSet<DataType, Dimension> PointSetType;
  typedef itk::Image<DataType, Dimension> ImageType;
  typedef typename 
    itk::BSplineScatteredDataPointSetToImageFilter<PointSetType, 
						   ImageType> TransformType;

  // variables to store the input points
  typename PointSetType::Pointer pointSet = PointSetType::New();
  typename PointSetType::PointType xParam;
  DataType v; // v = y-x, i.e. displacement vector between source and
              // target landmark

  // init variables to contain the limits of a bounding box that
  // contains all the points
  typename ImageType::PointType orig, term;
  for (mwSize col=0; col < (mwSize)Dimension; ++col) {
    orig[CAST2MWSIZE(col)] = std::numeric_limits<TScalarType>::max();
    term[CAST2MWSIZE(col)] = std::numeric_limits<TScalarType>::min();
  }

  // find bounding box limits
  for (mwSize row=0; row < Mx; ++row) {
    for (mwSize col=0; col < (mwSize)Dimension; ++col) {
      orig[CAST2MWSIZE(col)] = std::min((TScalarType)orig[CAST2MWSIZE(col)], x[Mx * col + row]);
      term[CAST2MWSIZE(col)] = std::max((TScalarType)term[CAST2MWSIZE(col)], x[Mx * col + row]);
      orig[CAST2MWSIZE(col)] = std::min((TScalarType)orig[CAST2MWSIZE(col)], y[Mx * col + row]);
      term[CAST2MWSIZE(col)] = std::max((TScalarType)term[CAST2MWSIZE(col)], y[Mx * col + row]);
    }
  }
  for (mwSize row=0; row < Mxi; ++row) {
    for (mwSize col=0; col < (mwSize)Dimension; ++col) {
      orig[CAST2MWSIZE(col)] = std::min((TScalarType)orig[CAST2MWSIZE(col)], xi[Mxi * col + row]);
      term[CAST2MWSIZE(col)] = std::max((TScalarType)term[CAST2MWSIZE(col)], xi[Mxi * col + row]);
    }
  }

  // compute length of each size of the bounding box
  DataType len = term - orig;
  TScalarType lenmax = std::numeric_limits<TScalarType>::min();
  for (mwSize col=0; col < (mwSize)Dimension; ++col) {
    lenmax = std::max(lenmax, len[CAST2MWSIZE(col)]);
  }

  // duplicate the input x and y matrices to PointSet format so that
  // we can pass it to the ITK function
  //
  // we also translate and scale all points so that the bounding box
  // fits within the domain [0, 1] x [0, 1] x [0,1]. We need to do
  // this because the BSpline function requires the parametric domain
  // to be within [0, 1] x [0, 1] x [0,1]
  for (mwSize row=0; row < Mx; ++row) {
    for (mwSize col=0; col < (mwSize)Dimension; ++col) {
      v[CAST2MWSIZE(col)] = (y[Mx * col + row] - x[Mx * col + row]) / lenmax;
      xParam[CAST2MWSIZE(col)] = (x[Mx * col + row] - orig[CAST2MWSIZE(col)]) / lenmax;
    }
    pointSet->SetPoint(row, xParam);
    pointSet->SetPointData(row, v);
  }

  // instantiate and set-up transform
  typename TransformType::Pointer transform = TransformType::New();
  transform->SetGenerateOutputImage(false);
  transform->SetInput(pointSet);
  transform->SetSplineOrder(splineOrder);
  typename TransformType::ArrayType ncps ;
  ncps.Fill(splineOrder + 1);
  transform->SetNumberOfControlPoints(ncps);
  transform->SetNumberOfLevels(numOfLevels);

  // note that closedim, spacing, sz and orig are all refered to the
  // parametric domain, i.e. the domain of x and xi
  typename TransformType::ArrayType closedim;
  typename ImageType::SpacingType spacing;
  typename ImageType::SizeType sz;

  // the parametric domain is not periodic in any dimension
  closedim.Fill(0);

  // as we are not creating the image, we don't need to provide a
  // sensible number of voxels. But size has to be at least 2 voxels
  // to avoid a run-time error
  sz.Fill(2);

  // because the parameterization is in [0, 1] x [0, 1] x [0,1], and
  // we have only size = 2 voxels in every dimension, the spacing will
  // be 1.0 / (2 - 1) = 1.0
  spacing.Fill(1.0);

  // because of the reparameterization, the origin we have to pass to
  // the transform is not the origin of the real points, but the
  // origin of the [0, 1] x [0, 1] x [0,1] bounding box
  typename ImageType::PointType origZero;
  origZero.Fill(0.0);
  
  transform->SetCloseDimension(closedim);
  transform->SetSize(sz);
  transform->SetSpacing(spacing);
  transform->SetOrigin(origZero);

  // run transform
  transform->Update();

  // create output vector and pointer to populate it
  mwSize ndimxi = mxGetNumberOfDimensions(inXI->pm); 
  const mwSize *dimsxi = mxGetDimensions(inXI->pm);
  std::vector<mwSize> size;
  for (mwIndex i = 0; i < ndimxi; ++i) {
    size.push_back(dimsxi[i]);
  }
  TScalarType *yi 
    = matlabExport->AllocateNDArrayInMatlab<TScalarType>(outYI, size);

  // from ITK v4.x, we need to instantiate a function to evaluate
  // points of the B-spline, as the Evaluate() method has been removed
  // from the TransformType
#if ITK_VERSION_MAJOR>=4
  // Note: in the following, we have to use TCoordRep=double, because
  // ITK gives a compilation error of an abstract class not having
  // been implemented. Otherwise, we would use
  // TCoordRep=TScalar=float, as in the rest of this program
  typedef typename 
    itk::BSplineControlPointImageFunction<ImageType, double> EvalFunctionType;
  typename EvalFunctionType::Pointer function = EvalFunctionType::New();

  function->SetSplineOrder(splineOrder);
  function->SetOrigin(origZero);
  function->SetSpacing(spacing);
  function->SetSize(sz);
  function->SetInputImage(transform->GetPhiLattice());
#endif

  // sample the warp field
  DataType vi; // warp field sample
  typename PointSetType::PointType xiParam; // sampling coordinates
  for (mwSize row=0; row < Mxi; ++row) {
    for (mwSize col=0; col < (mwSize)Dimension; ++col) {
      xiParam[CAST2MWSIZE(col)] = (xi[Mxi * col + row] - orig[CAST2MWSIZE(col)]) / lenmax;
    }
#if ITK_VERSION_MAJOR<4
    transform->Evaluate(xiParam, vi);
#else
    vi = function->Evaluate(xiParam);
#endif
    for (mwSize col=0; col < (mwSize)Dimension; ++col) {
      yi[Mxi * col + row] = xi[Mxi * col + row] + vi[CAST2MWSIZE(col)] * lenmax;
    }
  }

  // exit function
  return;

}
/*
 * mexFunction(): entry point for the mex function
 */
void mexFunction(int nlhs, mxArray *plhs[], 
		 int nrhs, const mxArray *prhs[]) {

  // interface to deal with input arguments from Matlab
  enum InputIndexType {IN_TRI, IN_X, IN_ITRI, InputIndexType_MAX};
  MatlabImportFilter::Pointer matlabImport = MatlabImportFilter::New();
  matlabImport->ConnectToMatlabFunctionInput(nrhs, prhs);

  // check the number of input arguments
  matlabImport->CheckNumberOfArguments(2, InputIndexType_MAX);

  // register the inputs for this function at the import filter
  typedef MatlabImportFilter::MatlabInputPointer MatlabInputPointer;
  MatlabInputPointer inTRI = matlabImport->RegisterInput(IN_TRI, "TRI");
  MatlabInputPointer inX = matlabImport->RegisterInput(IN_X, "X");
  MatlabInputPointer inITRI = matlabImport->RegisterInput(IN_ITRI, "ITRI");

  // interface to deal with outputs to Matlab
  enum OutputIndexType {OUT_C, OutputIndexType_MAX};
  MatlabExportFilter::Pointer matlabExport = MatlabExportFilter::New();
  matlabExport->ConnectToMatlabFunctionOutput(nlhs, plhs);

  // check number of outputs the user is asking for
  matlabExport->CheckNumberOfArguments(0, OutputIndexType_MAX);

  // register the outputs for this function at the export filter
  typedef MatlabExportFilter::MatlabOutputPointer MatlabOutputPointer;
  MatlabOutputPointer outC = matlabExport->RegisterOutput(OUT_C, "C");

  // if any of the inputs is empty, the output is empty too
  if (mxIsEmpty(prhs[IN_TRI]) || mxIsEmpty(prhs[IN_X])) {
    matlabExport->CopyEmptyArrayToMatlab(outC);
    return;
  }

  // default coordinates are NaN values, so that the user can spot
  // whether there was any problem reading them
  Point def(mxGetNaN(), mxGetNaN(), mxGetNaN());

  // get size of input matrix
  mwSize nrowsTri = mxGetM(prhs[IN_TRI]);
  mwSize ncolsTri = mxGetN(prhs[IN_TRI]);
  mwSize ncolsX = mxGetN(prhs[IN_X]);
  if ((ncolsTri != 3) || (ncolsX != 3)) {
    mexErrMsgTxt("All input arguments must have 3 columns");
  }

  // get list of triangle indices the user wants to check
  // intersections for. By default, we check all triangles
  std::vector<mwIndex> itriDef(nrowsTri);
  for (mwSize i = 0; i < nrowsTri; ++i) {
    itriDef[i] = i + 1;
  }
  std::vector<mwIndex> itri = matlabImport->
    ReadRowVectorFromMatlab<mwIndex, std::vector<mwIndex> >(inITRI, itriDef);

  // read triangular mesh from function
  std::vector<Triangle> triangles(nrowsTri);
  mwIndex v0, v1, v2; // indices of the 3 vertices of each triangle
  Point x0, x1, x2; // coordinates of the 3 vertices of each triangle

  Iterator it = triangles.begin();
  for (mwIndex i = 0; i < nrowsTri; ++i, ++it) {

    // exit if user pressed Ctrl+C
    ctrlcCheckPoint(__FILE__, __LINE__);

    // get indices of the 3 vertices of each triangle. These indices
    // follow Matlab's convention v0 = 1, 2, ..., n
    v0 = matlabImport->ReadScalarFromMatlab<mwIndex>(inTRI, i, 0, mxGetNaN());
    v1 = matlabImport->ReadScalarFromMatlab<mwIndex>(inTRI, i, 1, mxGetNaN());
    v2 = matlabImport->ReadScalarFromMatlab<mwIndex>(inTRI, i, 2, mxGetNaN());
    if (mxIsNaN(v0) || mxIsNaN(v1) || mxIsNaN(v2)) {
      mexErrMsgTxt("Parameter TRI: Vertex index is NaN");
    }

    // get coordinates of the 3 vertices (substracting 1 so that
    // indices follow the C++ convention 0, 1, ..., n-1)
    x0 = matlabImport->ReadRowVectorFromMatlab<void, Point>(inX, v0 - 1, def);
    x1 = matlabImport->ReadRowVectorFromMatlab<void, Point>(inX, v1 - 1, def);
    x2 = matlabImport->ReadRowVectorFromMatlab<void, Point>(inX, v2 - 1, def);

    // add triangle to the vector of triangles in the surface
    triangles[i] = Triangle(x0, x1, x2);

    // // debug: show the memory address of each triangle in the std::vector
    // std::cout << "tri mem address: " << &(triangles[i]) << std::endl;

  }

  // // debug: show the memory address of each triangle in the std::vector
  // for (it = triangles.begin(); it != triangles.end(); ++it) {
  //   std::cout << "tri mem address: " << &(*(it)) << std::endl;
  // }

  // construct AABB tree
  Tree tree(triangles.begin(),triangles.end());

  // initialise outputs
  double *n = matlabExport->AllocateColumnVectorInMatlab<double>(outC, nrowsTri);

  // // DEBUG:
  // mwSize triNum = 0;

  // initialize variable to keep a list of intersections for the
  // current triangle
  std::list<Object_and_primitive_id> intersections;

  // loop every facet to see whether it intersects the mesh
  for (std::vector<mwIndex>::iterator itri_it = itri.begin(); itri_it != itri.end(); ++itri_it) {

    // triangle index (converting Matlab index convention 1, 2, 3,... to C++ 0, 1, 2,...
    mwIndex idx = *itri_it - 1;

    // iterator pointer to the triangle
    Iterator it = triangles.begin();
    it += idx;

    // // DEBUG:
    // std::cout << ++triNum << ": Triangle = " << *it << std::endl;

    // exit if user pressed Ctrl+C
    ctrlcCheckPoint(__FILE__, __LINE__);

    // if the triangle is degenerated, trying to find intersections
    // will produce a segfault. To avoid it, we just count one
    // intersection for this triangle and skip to the next one
    if (
	(it->vertex(0) == it->vertex(1))
	|| (it->vertex(0) == it->vertex(2))
	|| (it->vertex(1) == it->vertex(2))
	) {
      // // DEBUG:
      // mexWarnMsgTxt("Degenerate triangle found, skipping:");
      // std::cerr << it->vertex(0) << ", " << it->vertex(1) << ", " << it->vertex(2) << std::endl;

      // add one to the count of self intersections
      n[idx] += 1;

      continue;
    }

    // computes all intersections with segment query (as pairs object - primitive_id)
    intersections.clear();
    tree.all_intersections(*it, std::back_inserter(intersections));

    // // DEBUG:
    // std::cout << "\tTotal number of intersections found for current triangle = " 
    // 	      << intersections.size() << std::endl;

    // loop all intersections
    for (std::list<Object_and_primitive_id>::iterator itx = intersections.begin();
	 itx != intersections.end(); ++itx) {

      // two triangles sharing a vertex or edge are detected by CGAL
      // as intersecting. Of course, in those case we cannot talk
      // about triangles overlapping. In this block of code, be
      // identify what kind of intersection we have. The
      // self-intersections that will be considered as actual
      // self-intersections that make the mesh non-topological are:
      //
      // 1) all triangle-type intersections: This is the current
      //    triangle being parallel to another triangle, bigger or
      //    smaller. Note that an intersection will always be detected
      //    between the current triangle and itself. But it's easier
      //    to simply count and then discount that intersection than
      //    having to check the validity of each triangle-intersection
      //
      // 2) segment-type intersections where a triangle intersects
      //    another triangle. This excludes the intersections that are
      //    simply the triangle sharing an edge with its neighbours
      //
      // 3) point-type intersections where the triangle just touches
      //    another triangle. This excludes the intersections that are
      //    simply the triangle sharing a vertex with its neighbours
      Object_and_primitive_id op = *itx;
      CGAL::Object object = op.first;
      Triangle triangle;
      Segment segment;
      Point point;

      // 1) triangle intersection
      if(CGAL::assign(triangle, object)) {

	// add one to the count of self intersections
	n[idx] += 1;

	// // DEBUG:
	// std::cout << "\tTriangle: " << triangle << std::endl;
	// std::cout << "\t\tMesh self-intersection detected" << std::endl;

      } // end: triangle intersection

      // 2) segment intersection
      if(CGAL::assign(segment, object)) {

	// // DEBUG:
	// std::cout << "\tSegment: " << segment << std::endl;

	// for convenience, end points of the segment
	Point pa = segment[0];
	Point pb = segment[1];

	// for convenience, the three vertices of the current triangle
	Point vA = it->vertex(0);
	Point vB = it->vertex(1);
	Point vC = it->vertex(2);

	// for convenience, the three vertices of the intersected triangle
	Triangle intersectedTrianglePointer = *(op.second);
	Point vX = intersectedTrianglePointer.vertex(0);
	Point vY = intersectedTrianglePointer.vertex(1);
	Point vZ = intersectedTrianglePointer.vertex(2);

	// CGAL will detect as intersections the edges between the
	// current triangle and each neighbour. We need to detect and
	// disregard these cases
	if (
	    ((pa == vA) || (pa == vB) || (pa == vC))
	    && ((pb == vA) || (pb == vB) || (pb == vC))
	    && ((pa == vX) || (pa == vY) || (pa == vZ))
	    && ((pb == vX) || (pb == vY) || (pb == vZ))
	    ) {

	//   // DEBUG:
	//   std::cout << "\t\tDisregarding intersection: it's a valid edge between current triangle and neighbour" << std::endl;

	} else {

	//   // DEBUG:
	//   std::cout << "\t\tMesh self-intersection detected" << std::endl;

	  // add one to the count of self intersections
	  n[idx] += 1;
	}
	
      } // end: segment intersection

      // 3) point intersection
      if(CGAL::assign(point, object)) {

	// // DEBUG:
	// std::cout << "\tPoint: " << point << std::endl;

	// for convenience, the three vertices of the triangle
	Point vA = it->vertex(0);
	Point vB = it->vertex(1);
	Point vC = it->vertex(2);

	// for convenience, the three vertices of the intersected triangle
	Triangle intersectedTrianglePointer = *(op.second);
	Point vX = intersectedTrianglePointer.vertex(0);
	Point vY = intersectedTrianglePointer.vertex(1);
	Point vZ = intersectedTrianglePointer.vertex(2);

	// CGAL will detect as intersections the vertices shared by
	// the current triangle and each neighbour. We need to detect
	// and disregard those cases
	if (
	    ((point == vA) || (point == vB) || (point == vC))
	    && ((point == vX) || (point == vY) || (point == vZ))
	    ){

	//   // DEBUG:
	//   std::cout << "\t\tDisregarding intersection: it's a valid vertex shared by current triangle and neighbour" << std::endl;

	} else {

	//   // DEBUG:
	//   std::cout << "\t\tMesh self-intersection detected" << std::endl;

	  // add one to the count of self intersections
	  n[idx] += 1;
	}

      } // end: point intersection
      
    } // end: loop all intersections
    
    // substract one intersection, because each triangle will intersect itself
    n[idx] -= 1;

  }
  
}
Example #9
0
/*
 * mexFunction(): entry point for the mex function
 */
void mexFunction(int nlhs, mxArray *plhs[], 
		 int nrhs, const mxArray *prhs[]) {

  // interface to deal with input arguments from Matlab
  enum InputIndexType {IN_TRI, IN_X, IN_METHOD, IN_ITER, InputIndexType_MAX};
  MatlabImportFilter::Pointer matlabImport = MatlabImportFilter::New();
  matlabImport->ConnectToMatlabFunctionInput(nrhs, prhs);

  // check that we have all input arguments
  matlabImport->CheckNumberOfArguments(2, InputIndexType_MAX);

  // register the inputs for this function at the import filter
  MatlabInputPointer inTRI =        matlabImport->RegisterInput(IN_TRI, "TRI");
  MatlabInputPointer inX =          matlabImport->RegisterInput(IN_X, "X");
  MatlabInputPointer inMETHOD =     matlabImport->RegisterInput(IN_METHOD, "METHOD");
  MatlabInputPointer inITER =       matlabImport->RegisterInput(IN_ITER, "ITER");

  // interface to deal with outputs to Matlab
  enum OutputIndexType {OUT_TRI, OUT_N, OutputIndexType_MAX};
  MatlabExportFilter::Pointer matlabExport = MatlabExportFilter::New();
  matlabExport->ConnectToMatlabFunctionOutput(nlhs, plhs);

  // check number of outputs the user is asking for
  matlabExport->CheckNumberOfArguments(0, OutputIndexType_MAX);

  // register the outputs for this function at the export filter
  typedef MatlabExportFilter::MatlabOutputPointer MatlabOutputPointer;
  MatlabOutputPointer outTRI = matlabExport->RegisterOutput(OUT_TRI, "TRI");
  MatlabOutputPointer outN = matlabExport->RegisterOutput(OUT_N, "N");

  // if any of the inputs is empty, the output is empty too
  if (mxIsEmpty(prhs[IN_TRI]) || mxIsEmpty(prhs[IN_X])) {
    matlabExport->CopyEmptyArrayToMatlab(outTRI);
    matlabExport->CopyEmptyArrayToMatlab(outN);
    return;
  }

  // polyhedron to contain the input mesh
  Polyhedron mesh;
  PolyhedronBuilder<Polyhedron> builder(matlabImport, inTRI, inX);
  mesh.delegate(builder);

  // get size of input matrix with the points
  mwSize nrowsTri = mxGetM(inTRI->pm);
  mwSize nrowsX = mxGetM(inX->pm);

#ifdef DEBUG  
  std::cout << "Number of facets read = " << mesh.size_of_facets() << std::endl;
  std::cout << "Number of vertices read = " << mesh.size_of_vertices() << std::endl;
#endif

  if (nrowsTri != mesh.size_of_facets()) {
    mexErrMsgTxt(("Input " + inTRI->name + ": Number of triangles read into mesh different from triangles provided at the input").c_str());
  }
  if (nrowsX != mesh.size_of_vertices()) {
    mexErrMsgTxt(("Input " + inX->name + ": Number of vertices read into mesh different from vertices provided at the input").c_str());
  }

  // sort halfedges such that the non-border edges precede the
  // border edges. We need to do this before any halfedge iterator
  // operations are valid
  mesh.normalize_border();
  
#ifdef DEBUG
  std::cout << "Number of border halfedges = " << mesh.size_of_border_halfedges() << std::endl;
#endif
  
  // number of holes we have filled
  mwIndex n = 0;

  // a closed mesh with no holes will have no border edges. What we do
  // is grab a border halfedge and close the associated hole. This
  // makes the rest of the iterators invalid, so we have to normalize
  // the mesh again. Then we iterate, looking for a new border
  // halfedge, filling the hole, etc.
  //
  // Note that confusingly, mesh.border_halfedges_begin() gives a
  // pointer to the halfedge that is NOT a border in a border
  // edge. The border halfedge is instead
  // mesh.border_halfedges_begin()->opposite()
  while (!mesh.is_closed()) {
    
    // exit if user pressed Ctrl+C
    ctrlcCheckPoint(__FILE__, __LINE__);

    // get the first hole we can find, and close it
    mesh.fill_hole(mesh.border_halfedges_begin()->opposite());

    // increase the counter of number of holes we have filled
    n++;

    // renormalize mesh so that halfedge iterators are again valid
    mesh.normalize_border();

  }

  // split all facets to triangles
  CGAL::triangulate_polyhedron<Polyhedron>(mesh);

  // copy output with number of holes filled
  std::vector<double> nout(1, n);
  matlabExport->CopyVectorOfScalarsToMatlab<double, std::vector<double> >(outN, nout, 1);

  // allocate memory for Matlab outputs
  double *tri = matlabExport->AllocateMatrixInMatlab<double>(outTRI, mesh.size_of_facets(), 3);

   // extract the triangles of the solution
  // snippet adapted from CgalMeshSegmentation.cpp

  // vertices coordinates. Assign indices to the vertices by defining
  // a map between their handles and the index
  std::map<Vertex_handle, int> V;
  int inum = 0;
  for(Vertex_iterator vit = mesh.vertices_begin(); vit != mesh.vertices_end(); ++vit) {

    // save to internal list of vertices
    V[vit] = inum++;

  }  

  // triangles given as (i,j,k), where each index corresponds to a vertex in x
  mwIndex row = 0;
  for (Facet_iterator fit = mesh.facets_begin(); fit != mesh.facets_end(); ++fit, ++row) {

    if (fit->facet_degree() != 3) {
      std::cerr << "Facet has " << fit->facet_degree() << " edges" << std::endl;
      mexErrMsgTxt("Facet does not have 3 edges");
    }

    // go around the half-edges of the facet, to extract the vertices
    Halfedge_around_facet_circulator heit = fit->facet_begin();
    int idx = 0;
    do {
      
      // extract triangle indices and save to Matlab output
      // note that Matlab indices go like 1, 2, 3..., while C++ indices go like 0, 1, 2...
      tri[row + idx * mesh.size_of_facets()] = 1 + V[heit->vertex()];
      idx++;

    } while (++heit != fit->facet_begin());
    
  }
}
/*
 * mexFunction(): entry point for the mex function
 */
void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[]) {

  // interface to deal with input arguments from Matlab
  enum InputIndexType {IN_TRI, IN_X, IN_RES, IN_SIZE, IN_ORIGIN, InputIndexType_MAX};
  MatlabImportFilter::Pointer matlabImport = MatlabImportFilter::New();
  matlabImport->ConnectToMatlabFunctionInput(nrhs, prhs);

  // check the number of input arguments
  matlabImport->CheckNumberOfArguments(2, InputIndexType_MAX);

  // register the inputs for this function at the import filter
  typedef MatlabImportFilter::MatlabInputPointer MatlabInputPointer;
  MatlabInputPointer inTRI = matlabImport->RegisterInput(IN_TRI, "TRI");
  MatlabInputPointer inX = matlabImport->RegisterInput(IN_X, "X"); // (x, y, z)
  MatlabInputPointer inRES = matlabImport->RegisterInput(IN_RES, "RES"); // (r, c, s)
  MatlabInputPointer inSIZE = matlabImport->RegisterInput(IN_SIZE, "SIZE"); // (r, c, s)
  MatlabInputPointer inORIGIN = matlabImport->RegisterInput(IN_ORIGIN, "ORIGIN"); // (x, y, z)

  // interface to deal with outputs to Matlab
  enum OutputIndexType {OUT_IM, OutputIndexType_MAX};
  MatlabExportFilter::Pointer matlabExport = MatlabExportFilter::New();
  matlabExport->ConnectToMatlabFunctionOutput(nlhs, plhs);
  
  // check that the number of outputs the user is asking for is valid
  matlabExport->CheckNumberOfArguments(0, OutputIndexType_MAX);

  // register the outputs for this function at the export filter
  typedef MatlabExportFilter::MatlabOutputPointer MatlabOutputPointer;
  MatlabOutputPointer outIM = matlabExport->RegisterOutput(OUT_IM, "IM");

  // if any input point set is empty, the outputs are empty too
  if (mxIsEmpty(inTRI->pm) || mxIsEmpty(inX->pm)) {
    matlabExport->CopyEmptyArrayToMatlab(outIM);
    return;
  }

  // get number of rows in inputs X and TRI
  mwSize nrowsX = mxGetM(inX->pm);
  mwSize nrowsTRI = mxGetM(inTRI->pm);

  // instantiate mesh
  MeshType::Pointer mesh = MeshType::New();

  // read vertices
  PointSetType::Pointer xDef = PointSetType::New(); // default: empty point set
  PointSetType::Pointer x = PointSetType::New();
  x->GetPoints()->CastToSTLContainer()
    = matlabImport->ReadVectorOfVectorsFromMatlab<PointType::CoordRepType, PointType>
    (inX, xDef->GetPoints()->CastToSTLContainer());

#ifdef DEBUG
  std::cout << "Number of X points read = " << x->GetNumberOfPoints() << std::endl;
#endif

  // assertion check
  if (nrowsX != x->GetNumberOfPoints()) {
    mexErrMsgTxt(("Input " + inX->name 
		  + ": Number of points read different from number of points provided by user").c_str()); 
  }

  // swap XY coordinates to make them compliant with ITK convention
  // (see important programming note at the help header above)
  matlabImport->SwapXYInVectorOfVectors<PointType::CoordRepType, std::vector<PointType> >
    (x->GetPoints()->CastToSTLContainer(), x->GetNumberOfPoints());

  // populate mesh with vertices
  mesh->SetPoints(x->GetPoints());

  // read triangles
  PointType triDef;
  triDef.Fill(mxGetNaN());
  for (mwIndex i = 0; i < nrowsTRI; ++i) {

    PointType triangle = matlabImport->ReadRowVectorFromMatlab<CoordType, PointType>(inTRI, i, triDef);

    // create a triangle cell to read the vertex indices of the current input triangle
    CellAutoPointer cell;
    cell.TakeOwnership(new TriangleType);

    // assign to the 0, 1, 2 elements in the triangle cell the vertex
    // indices that we have just read. Note that we have to substract
    // 1 to convert Matlab's index convention 1, 2, 3, ... to C++
    // convention 0, 1, 2, ...
    cell->SetPointId(0, triangle[0] - 1);
    cell->SetPointId(1, triangle[1] - 1);
    cell->SetPointId(2, triangle[2] - 1);

    // insert cell into the mesh
    mesh->SetCell(i, cell);
  }

#ifdef DEBUG
  std::cout << "Number of triangles read = " << mesh->GetNumberOfCells() << std::endl;
#endif

  // assertion check
  if (nrowsTRI != mesh->GetNumberOfCells()) {
    mexErrMsgTxt(("Input " + inTRI->name 
		  + ": Number of triangles read different from number of triangles provided by user").c_str()); 
  }

  // get user input parameters for the output rasterization
  ImageType::SpacingType spacingDef;
  spacingDef.Fill(1.0);
  ImageType::SpacingType spacing = matlabImport->
    ReadRowVectorFromMatlab<ImageType::SpacingValueType, ImageType::SpacingType>(inRES, spacingDef);

  ImageType::SizeType sizeDef;
  sizeDef.Fill(10);
  ImageType::SizeType size = matlabImport->
    ReadRowVectorFromMatlab<ImageType::SizeValueType, ImageType::SizeType>(inSIZE, sizeDef);

  ImageType::PointType originDef;
  originDef.Fill(0.0);
  ImageType::PointType origin = matlabImport->
    ReadRowVectorFromMatlab<ImageType::PointType::ValueType, ImageType::PointType>(inORIGIN, originDef);
  // (see important programming note at the help header above)
  matlabImport->SwapXYInVector<ImageType::PointType::ValueType, ImageType::PointType>(origin);

  // instantiate rasterization filter
  MeshFilterType::Pointer meshFilter = MeshFilterType::New();

  // smallest voxel side length
  ImageType::SpacingValueType minSpacing = spacing[0];
  for (mwIndex i = 1; i < Dimension; ++i) {
    minSpacing = std::min(minSpacing, spacing[i]);
  }

  // pass input parameters to the filter
  meshFilter->SetInput(mesh);
  meshFilter->SetSpacing(spacing);
  meshFilter->SetSize(size);
  meshFilter->SetOrigin(origin);
  meshFilter->SetTolerance(minSpacing / 10.0);
  meshFilter->SetInsideValue(1);
  meshFilter->SetOutsideValue(0);

  ImageType::IndexType start;
  start.Fill(0);
  meshFilter->SetIndex(start);

  // convert image size from itk::Size format to std::vector<mwSize>
  // so that we can use it in GraftItkImageOntoMatlab
  std::vector<mwSize> sizeStdVector(Dimension);
  for (unsigned int i = 0; i < Dimension; ++i) {
    sizeStdVector[i] = size[i];
  }

  // graft ITK filter output onto Matlab output
  matlabExport->GraftItkImageOntoMatlab<PixelType, Dimension>
    (outIM, meshFilter->GetOutput(), sizeStdVector);

#ifdef DEBUG
  std::cout << "Resolution (spacing) = " << meshFilter->GetSpacing() << std::endl;
  std::cout << "Size = " << meshFilter->GetSize() << std::endl;
  std::cout << "Origin = " << meshFilter->GetOrigin() << std::endl;
#endif
  
  // run rasterization
  meshFilter->Update();

}