23 #include <itkLabelStatisticsImageFilter.h> 24 #include <itkNeighborhoodIterator.h> 25 #include <itkImageRegionConstIteratorWithIndex.h> 26 #include <itkLabelGeometryImageFilter.h> 29 #include <vtkSmartPointer.h> 30 #include <vtkImageMarchingCubes.h> 31 #include <vtkMassProperties.h> 32 #include <vtkDelaunay3D.h> 33 #include <vtkGeometryFilter.h> 34 #include <vtkDoubleArray.h> 35 #include <vtkPCAStatistics.h> 40 #include <vnl/vnl_math.h> 43 #include <Eigen/Dense> 45 struct GIFVolumetricDensityStatisticsParameters
51 template<
typename TPixel,
unsigned int VImageDimension>
55 typedef itk::Image<TPixel, VImageDimension>
ImageType;
56 typedef itk::Image<unsigned short, VImageDimension> MaskType;
58 double volume = params.volume;
59 std::string prefix = params.prefix;
61 typename MaskType::Pointer maskImage = MaskType::New();
64 itk::ImageRegionConstIteratorWithIndex<ImageType> imgA(itkImage, itkImage->GetLargestPossibleRegion());
65 itk::ImageRegionConstIteratorWithIndex<ImageType> imgB(itkImage, itkImage->GetLargestPossibleRegion());
66 itk::ImageRegionConstIteratorWithIndex<MaskType> maskA(maskImage, maskImage->GetLargestPossibleRegion());
67 itk::ImageRegionConstIteratorWithIndex<MaskType> maskB(maskImage, maskImage->GetLargestPossibleRegion());
76 typename ImageType::PointType pointA;
77 typename ImageType::PointType pointB;
79 while (!imgA.IsAtEnd())
93 while (!imgA.IsAtEnd())
99 while (!imgB.IsAtEnd())
101 if ((imgA.GetIndex() == imgB.GetIndex()) ||
108 itkImage->TransformIndexToPhysicalPoint(maskA.GetIndex(), pointA);
109 itkImage->TransformIndexToPhysicalPoint(maskB.GetIndex(), pointB);
111 double w = 1 / pointA.EuclideanDistanceTo(pointB);
112 moranA += w*(imgA.Get() - mean)* (imgB.Get() - mean);
113 geary += w * (imgA.Get() - imgB.Get()) * (imgA.Get() - imgB.Get());
120 moranB += (imgA.Get() - mean)* (imgA.Get() - mean);
128 featureList.push_back(std::make_pair(prefix +
"Volume integrated intensity", volume* mean));
129 featureList.push_back(std::make_pair(prefix +
"Volume Moran's I index", Nv / w_ij * moranA / moranB));
130 featureList.push_back(std::make_pair(prefix +
"Volume Geary's C measure", ( Nv -1 ) / 2 / w_ij * geary/ moranB));
137 for (
int cellID = 0; cellID < pointset->GetNumberOfCells(); ++cellID)
139 auto cell = pointset->GetCell(cellID);
141 for (
int edgeID = 0; edgeID < 3; ++edgeID)
143 auto edge = cell->GetEdge(edgeID);
146 double pAA[3], pBB[3];
148 vtkSmartPointer<vtkTransform> transform = vtkSmartPointer<vtkTransform>::New();
149 transform->PostMultiply();
150 pointset->GetPoint(edge->GetPointId(0), pA);
151 pointset->GetPoint(edge->GetPointId(1), pB);
153 double angleZ = std::atan2((- pA[2] + pB[2]) ,(pA[1] - pB[1]));
154 angleZ *= 180 / vnl_math::pi;
158 transform->RotateX(angleZ);
159 transform->TransformPoint(pA, pAA);
160 transform->TransformPoint(pB, pBB);
162 double angleY = std::atan2((pAA[1] -pBB[1]) ,-(pAA[0] - pBB[0]));
163 angleY *= 180 / vnl_math::pi;
164 if (pAA[1] == pBB[1])
166 transform->RotateZ(angleY);
169 pointset->GetPoint(edge->GetPointId(0), p0);
172 double curMaxX = std::numeric_limits<double>::lowest();
174 double curMaxY = std::numeric_limits<double>::lowest();
176 double curMaxZ = std::numeric_limits<double>::lowest();
177 for (
int pointID = 0; pointID < pointset->GetNumberOfPoints(); ++pointID)
181 pointset->GetPoint(pointID, p);
182 p[0] -= p0[0]; p[1] -= p0[1]; p[2] -= p0[2];
183 transform->TransformPoint(p, p2);
185 curMinX = std::min<double>(p2[0], curMinX);
186 curMaxX = std::max<double>(p2[0], curMaxX);
187 curMinY = std::min<double>(p2[1], curMinY);
188 curMaxY = std::max<double>(p2[1], curMaxY);
189 curMinZ = std::min<double>(p2[2], curMinZ);
190 curMaxZ = std::max<double>(p2[2], curMaxZ);
193 if ((curMaxX - curMinX)*(curMaxY - curMinY)*(curMaxZ - curMinZ) < volume)
195 volume = (curMaxX - curMinX)*(curMaxY - curMinY)*(curMaxZ - curMinZ);
196 surface = (curMaxX - curMinX)*(curMaxX - curMinX) + (curMaxY - curMinY)*(curMaxY - curMinY) + (curMaxZ - curMinZ)*(curMaxZ - curMinZ);
205 void calculateMEE(vtkPointSet *pointset,
double &vol,
double &surf,
double tolerance=0.0001)
209 int numberOfPoints = pointset->GetNumberOfPoints();
211 Eigen::MatrixXd points(3, numberOfPoints);
212 Eigen::MatrixXd Q(3+1, numberOfPoints);
215 std::cout <<
"Initialize Q " << std::endl;
216 for (
int i = 0; i < numberOfPoints; ++i)
218 pointset->GetPoint(i, p);
230 Eigen::VectorXd u_vector(numberOfPoints);
231 u_vector.fill(1.0 / numberOfPoints);
232 Eigen::DiagonalMatrix<double, Eigen::Dynamic> u = u_vector.asDiagonal();
233 Eigen::VectorXd ones(dimension + 1);
235 Eigen::MatrixXd Ones = ones.asDiagonal();
238 while (error > tolerance)
240 auto Qt = Q.transpose();
241 Eigen::MatrixXd X = Q*u*Qt;
242 Eigen::FullPivHouseholderQR<Eigen::MatrixXd> qr(X);
243 Eigen::MatrixXd Xi = qr.solve(Ones);
245 Eigen::MatrixXd M = Qt * Xi * Q;
247 double maximumValue = M(0, 0);
248 int maximumPosition = 0;
249 for (
int i = 0; i < numberOfPoints; ++i)
251 if (maximumValue < M(i, i))
253 maximumValue = M(i, i);
257 double stepsize = (maximumValue - dimension - 1) / ((dimension + 1) * (maximumValue - 1));
258 Eigen::DiagonalMatrix<double, Eigen::Dynamic> new_u = (1.0 - stepsize) * u;
259 new_u.diagonal()[maximumPosition] = (new_u.diagonal())(maximumPosition) + stepsize;
261 error = (new_u.diagonal() - u.diagonal()).norm();
262 u.diagonal() = new_u.diagonal();
267 Eigen::MatrixXd Ai = points * u * points.transpose() - points * u *(points * u).transpose();
268 Eigen::FullPivHouseholderQR<Eigen::MatrixXd> qr(Ai);
269 Eigen::VectorXd ones2(dimension);
271 Eigen::MatrixXd Ones2 = ones2.asDiagonal();
272 Eigen::MatrixXd A = qr.solve(Ones2)*1.0/dimension;
274 Eigen::JacobiSVD<Eigen::MatrixXd> svd(A);
275 double c = 1 / sqrt(svd.singularValues()[0]);
276 double b = 1 / sqrt(svd.singularValues()[1]);
277 double a = 1 / sqrt(svd.singularValues()[2]);
278 double V = 4 * vnl_math::pi*a*b*c / 3;
281 double alpha = std::sqrt(1 - b*b / a / a);
282 double beta = std::sqrt(1 - c*c / a / a);
283 for (
int i = 0; i < 20; ++i)
285 ad_mvee += 4 * vnl_math::pi*a*b*(alpha*alpha + beta*beta) / (2 * alpha*beta) * (std::pow(alpha*beta, i)) / (1 - 4 * i*i);
294 if (image->GetDimension() < 3)
299 std::string prefix = FeatureDescriptionPrefix();
301 vtkSmartPointer<vtkImageMarchingCubes> mesher = vtkSmartPointer<vtkImageMarchingCubes>::New();
302 vtkSmartPointer<vtkMassProperties> stats = vtkSmartPointer<vtkMassProperties>::New();
303 vtkSmartPointer<vtkMassProperties> stats2 = vtkSmartPointer<vtkMassProperties>::New();
304 mesher->SetInputData(mask->GetVtkImageData());
305 mesher->SetValue(0, 0.5);
306 stats->SetInputConnection(mesher->GetOutputPort());
309 vtkSmartPointer<vtkDelaunay3D> delaunay =
310 vtkSmartPointer< vtkDelaunay3D >::New();
311 delaunay->SetInputConnection(mesher->GetOutputPort());
312 delaunay->SetAlpha(0);
314 vtkSmartPointer<vtkGeometryFilter> geometryFilter =
315 vtkSmartPointer<vtkGeometryFilter>::New();
316 geometryFilter->SetInputConnection(delaunay->GetOutputPort());
317 geometryFilter->Update();
318 stats2->SetInputConnection(geometryFilter->GetOutputPort());
327 calculateMOBB(geometryFilter->GetOutput(), vol_mobb, surf_mobb);
329 double pi = vnl_math::pi;
331 double meshVolume = stats->GetVolume();
332 double meshSurf = stats->GetSurfaceArea();
334 GIFVolumetricDensityStatisticsParameters params;
335 params.volume = meshVolume;
336 params.prefix = prefix;
340 int xx = mask->GetDimensions()[0];
341 int yy = mask->GetDimensions()[1];
342 int zz = mask->GetDimensions()[2];
344 double xd = mask->GetGeometry()->GetSpacing()[0];
345 double yd = mask->GetGeometry()->GetSpacing()[1];
346 double zd = mask->GetGeometry()->GetSpacing()[2];
355 vtkSmartPointer<vtkDoubleArray> dataset1Arr = vtkSmartPointer<vtkDoubleArray>::New();
356 vtkSmartPointer<vtkDoubleArray> dataset2Arr = vtkSmartPointer<vtkDoubleArray>::New();
357 vtkSmartPointer<vtkDoubleArray> dataset3Arr = vtkSmartPointer<vtkDoubleArray>::New();
358 dataset1Arr->SetNumberOfComponents(1);
359 dataset2Arr->SetNumberOfComponents(1);
360 dataset3Arr->SetNumberOfComponents(1);
361 dataset1Arr->SetName(
"M1");
362 dataset2Arr->SetName(
"M2");
363 dataset3Arr->SetName(
"M3");
365 vtkSmartPointer<vtkDoubleArray> dataset1ArrU = vtkSmartPointer<vtkDoubleArray>::New();
366 vtkSmartPointer<vtkDoubleArray> dataset2ArrU = vtkSmartPointer<vtkDoubleArray>::New();
367 vtkSmartPointer<vtkDoubleArray> dataset3ArrU = vtkSmartPointer<vtkDoubleArray>::New();
368 dataset1ArrU->SetNumberOfComponents(1);
369 dataset2ArrU->SetNumberOfComponents(1);
370 dataset3ArrU->SetNumberOfComponents(1);
371 dataset1ArrU->SetName(
"M1");
372 dataset2ArrU->SetName(
"M2");
373 dataset3ArrU->SetName(
"M3");
375 vtkSmartPointer<vtkPoints> points =
376 vtkSmartPointer< vtkPoints >::New();
378 for (
int x = 0; x < xx; x++)
380 for (
int y = 0; y < yy; y++)
382 for (
int z = 0; z < zz; z++)
384 itk::Image<int,3>::IndexType index;
395 image->GetChannelDescriptor().GetPixelType(),
397 image->GetVolumeData(),
404 mask->GetChannelDescriptor().GetPixelType(),
406 mask->GetVolumeData(),
414 minimumX = std::min<int>(x, minimumX);
415 minimumY = std::min<int>(y, minimumY);
416 minimumZ = std::min<int>(z, minimumZ);
417 maximumX = std::max<int>(x, maximumX);
418 maximumY = std::max<int>(y, maximumY);
419 maximumZ = std::max<int>(z, maximumZ);
420 points->InsertNextPoint(x*xd, y*yd, z*zd);
422 if (pxImage == pxImage)
424 dataset1Arr->InsertNextValue(x*xd);
425 dataset2Arr->InsertNextValue(y*yd);
426 dataset3Arr->InsertNextValue(z*zd);
433 vtkSmartPointer<vtkTable> datasetTable = vtkSmartPointer<vtkTable>::New();
434 datasetTable->AddColumn(dataset1Arr);
435 datasetTable->AddColumn(dataset2Arr);
436 datasetTable->AddColumn(dataset3Arr);
438 vtkSmartPointer<vtkPCAStatistics> pcaStatistics = vtkSmartPointer<vtkPCAStatistics>::New();
439 pcaStatistics->SetInputData(vtkStatisticsAlgorithm::INPUT_DATA, datasetTable);
440 pcaStatistics->SetColumnStatus(
"M1", 1);
441 pcaStatistics->SetColumnStatus(
"M2", 1);
442 pcaStatistics->SetColumnStatus(
"M3", 1);
443 pcaStatistics->RequestSelectedColumns();
444 pcaStatistics->SetDeriveOption(
true);
445 pcaStatistics->Update();
447 vtkSmartPointer<vtkDoubleArray> eigenvalues = vtkSmartPointer<vtkDoubleArray>::New();
448 pcaStatistics->GetEigenvalues(eigenvalues);
450 std::vector<double> eigen_val(3);
451 eigen_val[2] = eigenvalues->GetValue(0);
452 eigen_val[1] = eigenvalues->GetValue(1);
453 eigen_val[0] = eigenvalues->GetValue(2);
455 double major = 2*sqrt(eigen_val[2]);
456 double minor = 2*sqrt(eigen_val[1]);
457 double least = 2*sqrt(eigen_val[0]);
459 double alpha = std::sqrt(1 - minor*minor / major / major);
460 double beta = std::sqrt(1 - least*least / major / major);
462 double a = (maximumX - minimumX+1) * xd;
463 double b = (maximumY - minimumY+1) * yd;
464 double c = (maximumZ - minimumZ+1) * zd;
466 double vd_aabb = meshVolume / (a*b*c);
467 double ad_aabb = meshSurf / (2 * a*b + 2 * a*c + 2 * b*c);
469 double vd_aee = 3 * meshVolume / (4.0*pi*major*minor*least);
471 for (
int i = 0; i < 20; ++i)
473 ad_aee += 4 * pi*major*minor*(alpha*alpha + beta*beta) / (2 * alpha*beta) * (std::pow(alpha*beta, i)) / (1 - 4 * i*i);
475 ad_aee = meshSurf / ad_aee;
477 double vd_ch = meshVolume / stats2->GetVolume();
478 double ad_ch = meshSurf / stats2->GetSurfaceArea();
480 featureList.push_back(std::make_pair(prefix +
"Volume density axis-aligned bounding box", vd_aabb));
481 featureList.push_back(std::make_pair(prefix +
"Surface density axis-aligned bounding box", ad_aabb));
482 featureList.push_back(std::make_pair(prefix +
"Volume density oriented minimum bounding box", meshVolume / vol_mobb));
483 featureList.push_back(std::make_pair(prefix +
"Surface density oriented minimum bounding box", meshSurf / surf_mobb));
484 featureList.push_back(std::make_pair(prefix +
"Volume density approx. enclosing ellipsoid", vd_aee));
485 featureList.push_back(std::make_pair(prefix +
"Surface density approx. enclosing ellipsoid", ad_aee));
486 featureList.push_back(std::make_pair(prefix +
"Volume density approx. minimum volume enclosing ellipsoid", meshVolume / vol_mvee));
487 featureList.push_back(std::make_pair(prefix +
"Surface density approx. minimum volume enclosing ellipsoid", meshSurf / surf_mvee));
488 featureList.push_back(std::make_pair(prefix +
"Volume density convex hull", vd_ch));
489 featureList.push_back(std::make_pair(prefix +
"Surface density convex hull", ad_ch));
496 SetLongName(
"volume-density");
497 SetShortName(
"volden");
498 SetFeatureClassName(
"Morphological Density");
510 std::string name = GetOptionPrefix();
518 auto parsedArgs = GetParameter();
519 if (parsedArgs.count(GetLongName()))
521 MITK_INFO <<
"Start calculating volumetric density features ....";
523 featureList.insert(featureList.end(), localResults.begin(), localResults.end());
524 MITK_INFO <<
"Finished calculating volumetric density features....";
FeatureNameListType GetFeatureNames() override
Returns a list of the names of all features that are calculated from this class.
mitk::ScalarType FastSinglePixelAccess(mitk::PixelType, mitk::Image::Pointer im, ImageDataItem *item, itk::Index< 3 > idx, mitk::ScalarType &val, int component=0)
#define AccessByItk_3(mitkImage, itkImageTypeFunction, arg1, arg2, arg3)
void calculateMEE(vtkPointSet *pointset, double &vol, double &surf, double tolerance=0.0001)
std::vector< std::string > FeatureNameListType
itk::Image< unsigned char, 3 > ImageType
GIFVolumetricDensityStatistics()
void CalculateVolumeDensityStatistic(itk::Image< TPixel, VImageDimension > *itkImage, mitk::Image::Pointer mask, GIFVolumetricDensityStatisticsParameters params, mitk::GIFVolumetricDensityStatistics::FeatureListType &featureList)
void CalculateFeaturesUsingParameters(const Image::Pointer &feature, const Image::Pointer &mask, const Image::Pointer &maskNoNAN, FeatureListType &featureList) override
Calculates the feature of this abstact interface. Does not necessarily considers the parameter settin...
void addArgument(const std::string &longarg, const std::string &shortarg, Type type, const std::string &argLabel, const std::string &argHelp=std::string(), const us::Any &defaultValue=us::Any(), bool optional=true, bool ignoreRest=false, bool deprecated=false, mitkCommandLineParser::Channel channel=mitkCommandLineParser::Channel::None)
void calculateMOBB(vtkPointSet *pointset, double &volume, double &surface)
FeatureListType CalculateFeatures(const Image::Pointer &image, const Image::Pointer &feature) override
Calculates the Cooccurence-Matrix based features for this class.
std::vector< std::pair< std::string, double > > FeatureListType
void AddArguments(mitkCommandLineParser &parser) override
mitk::Image::Pointer image
void MITKCORE_EXPORT CastToItkImage(const mitk::Image *mitkImage, itk::SmartPointer< ItkOutputImageType > &itkOutputImage)
Cast an mitk::Image to an itk::Image with a specific type.
mitk::Image::Pointer mask
#define mitkPixelTypeMultiplex5(function, ptype, param1, param2, param3, param4, param5)
void CalculateFeatures(mitk::CoocurenceMatrixHolder &holder, mitk::CoocurenceMatrixFeatures &results)