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; }
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; }
// 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; }
/* * 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; } }
/* * 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; }
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; }
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; } }
/* * 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(); }