TEST(MovingLeastSquares, isInvertibleByExchangingLandmarks) { const double rbegin = 0; const double rend = 5; const double repsmarks = 2.0; // Spacing for landmarks. const double repstest = 0.1; // Spacing for test positions. const double shift = 1; // Fixed shift. const double sigma = 0.2; // For random shifts. const double tolerance = 0.03; const Landmarks marks = randomGrid(rbegin, rend, repsmarks, shift, sigma); MovingLeastSquares forward; forward.setLandmarks(marks.src, marks.dst); MovingLeastSquares backward; backward.setLandmarks(marks.dst, marks.src); // Test that forward transform does something and forward-backward // transform gets back to original position. for (double x = rbegin; x < rend; x += repstest) { for (double y = rbegin; y < rend; y += repstest) { const McVec2d pos(x, y); const McVec2d tpos = forward.interpolate(pos); const McVec2d ttpos = backward.interpolate(tpos); const McVec2d tdiff = tpos - pos; const McVec2d ttdiff = ttpos - pos; EXPECT_THAT(tdiff.length(), Gt(tolerance)); EXPECT_THAT(ttdiff.length(), Lt(tolerance)); #if 0 // Useful for debugging. printf("tdiff = %f, ttdiff = %f\n", tdiff.length(), ttdiff.length()); #endif } } }
TEST(MovingLeastSquares, shouldInterpolateSmoothly) { const double rbegin = 0; const double rend = 5; const double repsmarks = 2.0; // Spacing for landmarks. const double shift = 1; // Fixed shift. const double sigma = 0.2; // For random shifts. const double tolerance = 0.01; const Landmarks marks = randomGrid(rbegin, rend, repsmarks, shift, sigma); MovingLeastSquares mls; mls.setLandmarks(marks.src, marks.dst); // Exactly interpolate landmarks. for (int i = 0; i < marks.src.size(); i++) { const McVec2d pos = McVec2d(marks.src[i][0], marks.src[i][1]); const McVec2d tpos = mls.interpolate(pos); const McVec2d tdiff = tpos - McVec2d(marks.dst[i][0], marks.dst[i][1]); EXPECT_FLOAT_EQ(0, tdiff.length()); } const int seed = 38273; McRandom rand(seed); // Interpolate points close to landmarks. for (int i = 0; i < marks.src.size(); i++) { const double d = sigma * (rand.nextNumber() - 0.5); const McVec2d delta(d, d); const McVec2d pos = McVec2d(marks.src[i][0], marks.src[i][1]) + delta; const McVec2d tpos = mls.interpolate(pos); const McVec2d tdiff = tpos - delta - McVec2d(marks.dst[i][0], marks.dst[i][1]); EXPECT_THAT(tdiff.length(), Lt(tolerance)); #if 0 // Useful for debugging. printf("tdiff = %f.\n", tdiff.length()); #endif } }
void HxMovingLeastSquaresWarp::compute() { if (!portAction.wasHit()) return; HxUniformScalarField3* fromImage = (HxUniformScalarField3*)portFromImage.getSource(); HxLandmarkSet* pointSet = hxconnection_cast<HxLandmarkSet>(portData); if (!fromImage) return; /* It is ok to warp without landmarks, if method is rigid and input has a transformation */ if (!pointSet) { return; } else if (pointSet->getNumSets() < 2) { theMsg->printf( "Error: LandmarkWarp data has to contain at least 2 sets."); return; } HxUniformScalarField3* outputImage; HxUniformVectorField3* outputVectorField = 0; outputImage = createOutputDataSet(); outputVectorField = createOutputVectorDataSet(); float* outputImageData = (float*)outputImage->lattice().dataPtr(); McDArray<McVec2d> landmarks1, landmarks2; prepareLandmarks(landmarks1, landmarks2); MovingLeastSquares mls; mls.setAlpha(portAlpha.getValue()); mls.setLandmarks(landmarks2, landmarks1); const McDim3l& dims = outputImage->lattice().getDims(); McVec3f voxelSizeInOutputImage = outputImage->getVoxelSize(); const McBox3f& bboxOfOutputImage = outputImage->getBoundingBox(); const McBox3f& bboxOfFromImage = fromImage->getBoundingBox(); #ifdef _OPENMP #pragma omp parallel for #endif for (int i = 0; i < dims[0]; i++) { HxLocation3* locationInFromImage = fromImage->createLocation(); std::cout << "\n" << i << " of " << dims[0]; for (int j = 0; j < dims[1]; j++) { McVec2d currentPositionInOutput = McVec2d( bboxOfOutputImage[0] + (float)(i)*voxelSizeInOutputImage.x, bboxOfOutputImage[2] + (float)(j)*voxelSizeInOutputImage.y); McVec2d warpedCoordInFromImage = mls.interpolate(currentPositionInOutput); McVec3f displacement = McVec3f( warpedCoordInFromImage.x - currentPositionInOutput.x, warpedCoordInFromImage.y - currentPositionInOutput.y, 0); displacement = displacement * -1; locationInFromImage->move(McVec3f(warpedCoordInFromImage.x, warpedCoordInFromImage.y, bboxOfFromImage[4])); float resultValue[1]; fromImage->eval(*locationInFromImage, resultValue); unsigned long pos = latticePos(i, j, 0, dims); outputImageData[pos] = resultValue[0]; outputVectorField->lattice().set(i, j, 0, displacement.getValue()); } delete locationInFromImage; } outputImage->touch(); outputImage->fire(); }
void HxCPDSpatialGraphWarp::warpPoint(const McVec3f& source, McVec3f& target, MovingLeastSquares& mlsInterpolator) { McVec2d curPoint = McVec2d(source.x, source.y); McVec2d warpedPoint = mlsInterpolator.interpolate(curPoint); target = McVec3f(warpedPoint.x, warpedPoint.y, source.z); }