7 #include "imstkSurfaceMeshDistanceTransform.h" 8 #include "imstkDataArray.h" 9 #include "imstkGeometryUtilities.h" 10 #include "imstkImageData.h" 11 #include "imstkLogger.h" 12 #include "imstkLooseOctree.h" 13 #include "imstkParallelUtils.h" 14 #include "imstkSurfaceMesh.h" 15 #include "imstkTimer.h" 16 #include "imstkSurfaceMeshImageMask.h" 18 #include <vtkDistancePolyDataFilter.h> 19 #include <vtkImageData.h> 20 #include <vtkImplicitPolyDataDistance.h> 21 #include <vtkOctreePointLocator.h> 22 #include <vtkPointData.h> 23 #include <vtkPolyData.h> 24 #include <vtkSelectEnclosedPoints.h> 57 isNeighborhoodEquivalent(
const Vec3i& pt,
const Vec3i& dim,
const float val,
const float* imgPtr,
const int dilateSize)
59 const Vec3i min = (pt - Vec3i(dilateSize, dilateSize, dilateSize)).cwiseMax(Vec3i(0, 0, 0)).cwiseMin(dim - Vec3i(1, 1, 1));
60 const Vec3i max = (pt + Vec3i(dilateSize, dilateSize, dilateSize)).cwiseMax(Vec3i(0, 0, 0)).cwiseMin(dim - Vec3i(1, 1, 1));
63 for (
int z = min[2]; z < max[2] + 1; z++)
65 for (
int y = min[1]; y < max[1] + 1; y++)
67 for (
int x = min[0]; x < max[0] + 1; x++)
70 if (val != imgPtr[index])
82 computeNarrowBandedDT(std::shared_ptr<ImageData> imageData, std::shared_ptr<SurfaceMesh> surfMesh,
const int dilateSize,
83 const double tolerance)
86 std::shared_ptr<SurfaceMeshImageMask> imageMask = std::make_shared<SurfaceMeshImageMask>();
87 imageMask->setInputMesh(surfMesh);
88 imageMask->setReferenceImage(imageData);
91 auto inputScalarsPtr = std::dynamic_pointer_cast<DataArray<float>>(imageMask->getOutputImage()->getScalars());
92 DataArray<float>& inputScalars = *inputScalarsPtr;
93 float* inputImgPtr = inputScalars.getPointer();
94 auto outputScalarsPtr = std::dynamic_pointer_cast<DataArray<double>>(imageData->getScalars());
95 DataArray<double>& outputScalars = *outputScalarsPtr;
96 double* outputImgPtr = outputScalars.getPointer();
100 vtkSmartPointer<vtkImplicitPolyDataDistance> distFunc = vtkSmartPointer<vtkImplicitPolyDataDistance>::New();
101 distFunc->SetInput(inputPolyData);
102 distFunc->SetTolerance(tolerance);
104 std::fill_n(outputImgPtr, outputScalars.size(), 10000.0);
107 const Vec3i& dim = imageData->getDimensions();
108 const Vec3d shift = imageData->getOrigin() + imageData->getSpacing() * 0.5;
109 const Vec3d& spacing = imageData->getSpacing();
111 for (
int z = 0; z < dim[2]; z++)
113 for (
int y = 0; y < dim[1]; y++)
115 for (
int x = 0; x < dim[0]; x++, i++)
117 const float val = inputImgPtr[i];
118 const Vec3i pt = Vec3i(x, y, z);
121 if (!isNeighborhoodEquivalent(pt, dim, val, inputImgPtr, dilateSize))
123 const Vec3d pos = pt.cast<
double>().cwiseProduct(spacing) + shift;
124 outputImgPtr[i] = distFunc->FunctionValue(pos.data());
129 outputImgPtr[i] = -10000.0;
132 if (i % 1000000 == 0)
134 double p =
static_cast<double>(i) / (dim[0] * dim[1] * dim[2]);
135 std::cout <<
"Progress " << p <<
"\n";
143 computeFullDT(std::shared_ptr<ImageData> imageData, std::shared_ptr<SurfaceMesh> surfMesh,
const double tolerance)
148 const Vec3i& dim = imageData->getDimensions();
149 const Vec3d spacing = imageData->getSpacing();
150 const Vec3d shift = imageData->getOrigin() + spacing * 0.5;
152 auto scalarsPtr = std::dynamic_pointer_cast<DataArray<double>>(imageData->getScalars());
153 DataArray<double>& scalars = *scalarsPtr.get();
156 ParallelUtils::parallelFor(numThreads, [&](
const int& i)
160 vtkSmartPointer<vtkImplicitPolyDataDistance> distFunc = vtkSmartPointer<vtkImplicitPolyDataDistance>::New();
161 distFunc->SetInput(inputPolyData);
162 distFunc->SetTolerance(tolerance);
164 for (
int z = i; z < dim[2]; z += numThreads)
166 int j = z * dim[0] * dim[1];
167 for (
int y = 0; y < dim[1]; y++)
169 for (
int x = 0; x < dim[0]; x++, j++)
171 double pos[3] = { x* spacing[0] + shift[0], y * spacing[1] + shift[1], z * spacing[2] + shift[2] };
172 scalars[j] = distFunc->FunctionValue(pos);
179 SurfaceMeshDistanceTransform::SurfaceMeshDistanceTransform()
182 setRequiredInputType<SurfaceMesh>(0);
185 setOutput(std::make_shared<ImageData>(), 0);
194 std::shared_ptr<ImageData>
195 SurfaceMeshDistanceTransform::getOutputImage()
201 SurfaceMeshDistanceTransform::setupDistFunc()
203 std::shared_ptr<SurfaceMesh> inputSurfaceMesh = std::dynamic_pointer_cast<
SurfaceMesh>(
getInput(0));
206 m_distFunc = vtkSmartPointer<vtkImplicitPolyDataDistance>::New();
207 m_distFunc->SetInput(inputPolyData);
213 m_distFunc->SetTolerance(m_Tolerance);
214 Vec3d closestPt = Vec3d::Zero();
216 m_distFunc->EvaluateFunctionAndGetClosestPoint(p.data(), closestPt.data());
224 if (m_Bounds.isZero())
226 LOG(WARNING) <<
"SurfaceMeshDistanceTransform Bounds are zero, the input SurfaceMesh bounds will be used instead.";
233 m_Bounds << min.x(), max.x(), min.y(), max.y(), min.z(), max.z();
234 if (m_Bounds.isZero())
236 LOG(WARNING) <<
"SurfaceMeshDistanceTransform Bounds are zero, the input SurfaceMesh bounds will be used instead.";
243 std::shared_ptr<SurfaceMesh> inputSurfaceMesh = std::dynamic_pointer_cast<
SurfaceMesh>(
getInput(0));
244 std::shared_ptr<ImageData> outputImageData = std::dynamic_pointer_cast<
ImageData>(
getOutput(0));
246 if (m_Dimensions[0] == 0 || m_Dimensions[1] == 0 || m_Dimensions[2] == 0)
248 LOG(WARNING) <<
"SurfaceMeshDistanceTransform Dimensions not set";
252 Vec6d bounds = m_Bounds;
256 inputSurfaceMesh->computeBoundingBox(min, max, 0.0);
257 bounds << min.x(), max.x(), min.y(), max.y(), min.z(), max.z();
258 LOG(WARNING) <<
"SurfaceMeshDistanceTransform Bounds are zero, the input SurfaceMesh bounds (" << bounds.transpose() <<
") will be used.";
261 const Vec3d size = Vec3d(bounds[1] - bounds[0], bounds[3] - bounds[2], bounds[5] - bounds[4]);
262 const Vec3d spacing = size.cwiseQuotient(m_Dimensions.cast<
double>());
263 const Vec3d origin = Vec3d(bounds[0], bounds[2], bounds[4]);
264 outputImageData->allocate(IMSTK_DOUBLE, 1, m_Dimensions, spacing, origin);
271 computeNarrowBandedDT(outputImageData, inputSurfaceMesh, m_DilateSize,
276 computeFullDT(outputImageData, inputSurfaceMesh, m_Tolerance);
size_t getScalarIndex(int x, int y, int z=0)
Returns index of data in scalar array given structured image coordinate, does no bounds checking...
vtkSmartPointer< vtkPolyData > copyToVtkPolyData(std::shared_ptr< LineMesh > imstkMesh)
Converts imstk line mesh into a vtk polydata.
std::shared_ptr< Geometry > getInput(size_t port=0) const
Returns input geometry given port, returns nullptr if doesn't exist.
void setNumOutputPorts(const size_t numPorts)
Get/Set the amount of output ports.
void setBounds(const Vec3d &min, const Vec3d &max)
Optionally one may specify bounds, if not specified bounds of the input SurfaceMesh is used...
std::shared_ptr< Geometry > getOutput(size_t port=0) const
Returns output geometry given port, returns nullptr if doesn't exist.
void setInput(std::shared_ptr< Geometry > inputGeometry, size_t port=0)
Set the input at the port.
void requestUpdate() override
Users can implement this for the logic to be run.
Represents a set of triangles & vertices via an array of Vec3d double vertices & Vec3i integer indice...
void setOutput(std::shared_ptr< Geometry > inputGeometry, const size_t port=0)
Set the output at the port.
void setNumInputPorts(const size_t numPorts)
Get/Set the amount of input ports.
static size_t getThreadPoolSize()
Returns the size of the thread pool.
Class to represent 1, 2, or 3D image data (i.e. structured points)
Vec3d getNearestPoint(const Vec3d &pos)
Get the nearest point.
void setInputMesh(std::shared_ptr< SurfaceMesh > surfMesh)
Required input, port 0.