23 #include <itkLabelStatisticsImageFilter.h> 24 #include <itkNeighborhoodIterator.h> 27 #include <vtkSmartPointer.h> 28 #include <vtkImageMarchingCubes.h> 29 #include <vtkMassProperties.h> 30 #include <vtkDoubleArray.h> 31 #include <vtkPCAStatistics.h> 35 #include <vnl/vnl_math.h> 37 template<
typename TPixel,
unsigned int VImageDimension>
41 typedef itk::Image<TPixel, VImageDimension>
ImageType;
42 typedef itk::Image<int, VImageDimension> MaskType;
43 typedef itk::LabelStatisticsImageFilter<ImageType, MaskType> FilterType;
45 typename MaskType::Pointer maskImage = MaskType::New();
48 typename FilterType::Pointer labelStatisticsImageFilter = FilterType::New();
49 labelStatisticsImageFilter->SetInput( itkImage );
50 labelStatisticsImageFilter->SetLabelInput(maskImage);
51 labelStatisticsImageFilter->Update();
53 double volume = labelStatisticsImageFilter->GetCount(1);
54 double voxelVolume = 1;
55 for (
int i = 0; i < (int)(VImageDimension); ++i)
57 volume *= itkImage->GetSpacing()[i];
58 voxelVolume *= itkImage->GetSpacing()[i];
61 featureList.push_back(std::make_pair(prefix +
"Voxel Volume", voxelVolume));
62 featureList.push_back(std::make_pair(prefix +
"Volume (voxel based)", volume));
65 template<
typename TPixel,
unsigned int VImageDimension>
69 typedef itk::Image<double, VImageDimension> ValueImageType;
71 typename ValueImageType::Pointer itkValueImage = ValueImageType::New();
74 typedef itk::Image<TPixel, VImageDimension>
ImageType;
75 typedef typename ImageType::PointType PointType;
76 typename ImageType::SizeType radius;
77 for (
int i=0; i < (int)VImageDimension; ++i)
79 itk::NeighborhoodIterator<ImageType> iterator(radius, mask, mask->GetRequestedRegion());
80 itk::NeighborhoodIterator<ValueImageType> valueIter(radius, itkValueImage, itkValueImage->GetRequestedRegion());
81 std::vector<PointType> borderPoints;
83 unsigned int maskDimensionX = mask->GetLargestPossibleRegion().GetSize()[0];
84 unsigned int maskDimensionY = mask->GetLargestPossibleRegion().GetSize()[1];
85 unsigned int maskDimensionZ = mask->GetLargestPossibleRegion().GetSize()[2];
87 double maskVoxelSpacingX = mask->GetSpacing()[0];
88 double maskVoxelSpacingY = mask->GetSpacing()[1];
89 double maskVoxelSpacingZ = mask->GetSpacing()[2];
91 int maskMinimumX = maskDimensionX;
93 int maskMinimumY = maskDimensionY;
95 int maskMinimumZ = maskDimensionZ;
102 std::vector<double> directionSurface;
103 for (
int i = 0; i < (int)(iterator.Size()); ++i)
105 auto offset = iterator.GetOffset(i);
108 for (
unsigned int j = 0; j < VImageDimension; ++j)
110 if (
offset[j] != 0 && nonZeros == 0)
112 for (
unsigned int k = 0;
k < VImageDimension; ++
k)
115 deltaS *= mask->GetSpacing()[
k];
126 directionSurface.push_back(deltaS);
132 PointType normalCenter(0);
133 PointType normalCenterUncorrected(0);
134 PointType weightedCenter(0);
135 PointType currentPoint;
136 int numberOfPoints = 0;
137 int numberOfPointsUncorrected = 0;
138 double sumOfPoints = 0;
140 while(!iterator.IsAtEnd())
142 if (iterator.GetCenterPixel() == 0)
149 maskMinimumX = (maskMinimumX > iterator.GetIndex()[0]) ? iterator.GetIndex()[0] : maskMinimumX;
150 maskMaximumX = (maskMaximumX < iterator.GetIndex()[0]) ? iterator.GetIndex()[0] : maskMaximumX;
151 maskMinimumY = (maskMinimumY > iterator.GetIndex()[1]) ? iterator.GetIndex()[1] : maskMinimumY;
152 maskMaximumY = (maskMaximumY < iterator.GetIndex()[1]) ? iterator.GetIndex()[1] : maskMaximumY;
153 maskMinimumZ = (maskMinimumZ > iterator.GetIndex()[2]) ? iterator.GetIndex()[2] : maskMinimumZ;
154 maskMaximumZ = (maskMaximumZ < iterator.GetIndex()[2]) ? iterator.GetIndex()[2] : maskMaximumZ;
156 mask->TransformIndexToPhysicalPoint(iterator.GetIndex(), currentPoint);
158 normalCenterUncorrected += currentPoint.GetVectorFromOrigin();
159 ++numberOfPointsUncorrected;
161 double intensityValue = valueIter.GetCenterPixel();
162 if (intensityValue == intensityValue)
164 normalCenter += currentPoint.GetVectorFromOrigin();
165 weightedCenter += currentPoint.GetVectorFromOrigin() * intensityValue;
166 sumOfPoints += intensityValue;
171 for (
int i = 0; i < (int)(iterator.Size()); ++i)
173 if (iterator.GetPixel(i) == 0 || ( ! iterator.IndexInBounds(i)))
176 surface += directionSurface[i];
182 auto centerIndex = iterator.GetIndex();
183 PointType centerPoint;
184 mask->TransformIndexToPhysicalPoint(centerIndex, centerPoint );
185 borderPoints.push_back(centerPoint);
191 auto normalCenterVector = normalCenter.GetVectorFromOrigin() / numberOfPoints;
192 auto normalCenterVectorUncorrected = normalCenter.GetVectorFromOrigin() / numberOfPointsUncorrected;
193 auto weightedCenterVector = weightedCenter.GetVectorFromOrigin() / sumOfPoints;
194 auto differenceOfCentersUncorrected = (normalCenterVectorUncorrected - weightedCenterVector).GetNorm();
195 auto differenceOfCenters = (normalCenterVector - weightedCenterVector).GetNorm();
197 double longestDiameter = 0;
198 unsigned long numberOfBorderPoints = borderPoints.size();
199 for (
int i = 0; i < (int)numberOfBorderPoints; ++i)
201 auto point = borderPoints[i];
202 for (
int j = i; j < (int)numberOfBorderPoints; ++j)
204 double newDiameter=point.EuclideanDistanceTo(borderPoints[j]);
205 if (newDiameter > longestDiameter)
206 longestDiameter = newDiameter;
210 double boundingBoxVolume = maskVoxelSpacingX* (maskMaximumX - maskMinimumX) * maskVoxelSpacingY* (maskMaximumY - maskMinimumY) * maskVoxelSpacingZ* (maskMaximumZ - maskMinimumZ);
212 featureList.push_back(std::make_pair(prefix +
"Maximum 3D diameter", longestDiameter));
213 featureList.push_back(std::make_pair(prefix +
"Surface (voxel based)", surface));
214 featureList.push_back(std::make_pair(prefix +
"Centre of mass shift", differenceOfCenters));
215 featureList.push_back(std::make_pair(prefix +
"Centre of mass shift (uncorrected)", differenceOfCentersUncorrected));
216 featureList.push_back(std::make_pair(prefix +
"Bounding Box Volume", boundingBoxVolume));
229 if (image->GetDimension() < 3)
238 vtkSmartPointer<vtkImageMarchingCubes> mesher = vtkSmartPointer<vtkImageMarchingCubes>::New();
239 vtkSmartPointer<vtkMassProperties> stats = vtkSmartPointer<vtkMassProperties>::New();
240 mesher->SetInputData(mask->GetVtkImageData());
241 mesher->SetValue(0, 0.5);
242 stats->SetInputConnection(mesher->GetOutputPort());
245 double pi = vnl_math::pi;
247 double meshVolume = stats->GetVolume();
248 double meshSurf = stats->GetSurfaceArea();
249 double pixelVolume = featureList[1].second;
250 double pixelSurface = featureList[3].second;
252 MITK_INFO <<
"Surface: " << pixelSurface <<
" Volume: " << pixelVolume;
254 double compactness1 = pixelVolume / (std::sqrt(pi) * std::pow(meshSurf, 2.0 / 3.0));
255 double compactness1Pixel = pixelVolume / (std::sqrt(pi) * std::pow(pixelSurface, 2.0 / 3.0));
258 double compactness2 = 36 * pi*pixelVolume*pixelVolume / meshSurf / meshSurf / meshSurf;
259 double compactness2MeshMesh = 36 * pi*meshVolume*meshVolume / meshSurf / meshSurf / meshSurf;
260 double compactness2Pixel = 36 * pi*pixelVolume*pixelVolume / pixelSurface / pixelSurface / pixelSurface;
261 double compactness3 = pixelVolume / (std::sqrt(pi) * std::pow(meshSurf, 3.0 / 2.0));
262 double compactness3MeshMesh = meshVolume / (std::sqrt(pi) * std::pow(meshSurf, 3.0 / 2.0));
263 double compactness3Pixel = pixelVolume / (std::sqrt(pi) * std::pow(pixelSurface, 3.0 / 2.0));
265 double sphericity = std::pow(pi, 1 / 3.0) *std::pow(6 * pixelVolume, 2.0 / 3.0) / meshSurf;
266 double sphericityMesh = std::pow(pi, 1 / 3.0) *std::pow(6 * meshVolume, 2.0 / 3.0) / meshSurf;
267 double sphericityPixel = std::pow(pi, 1 / 3.0) *std::pow(6 * pixelVolume, 2.0 / 3.0) / pixelSurface;
268 double surfaceToVolume = meshSurf / meshVolume;
269 double surfaceToVolumePixel = pixelSurface / pixelVolume;
270 double sphericalDisproportion = meshSurf / 4 / pi / std::pow(3.0 / 4.0 / pi * pixelVolume, 2.0 / 3.0);
271 double sphericalDisproportionMesh = meshSurf / 4 / pi / std::pow(3.0 / 4.0 / pi * meshVolume, 2.0 / 3.0);
272 double sphericalDisproportionPixel = pixelSurface / 4 / pi / std::pow(3.0 / 4.0 / pi * pixelVolume, 2.0 / 3.0);
273 double asphericity = std::pow(1.0/compactness2, (1.0 / 3.0)) - 1;
274 double asphericityMesh = std::pow(1.0 / compactness2MeshMesh, (1.0 / 3.0)) - 1;
275 double asphericityPixel = std::pow(1.0/compactness2Pixel, (1.0 / 3.0)) - 1;
278 int xx = mask->GetDimensions()[0];
279 int yy = mask->GetDimensions()[1];
280 int zz = mask->GetDimensions()[2];
282 double xd = mask->GetGeometry()->GetSpacing()[0];
283 double yd = mask->GetGeometry()->GetSpacing()[1];
284 double zd = mask->GetGeometry()->GetSpacing()[2];
286 vtkSmartPointer<vtkDoubleArray> dataset1Arr = vtkSmartPointer<vtkDoubleArray>::New();
287 vtkSmartPointer<vtkDoubleArray> dataset2Arr = vtkSmartPointer<vtkDoubleArray>::New();
288 vtkSmartPointer<vtkDoubleArray> dataset3Arr = vtkSmartPointer<vtkDoubleArray>::New();
289 dataset1Arr->SetNumberOfComponents(1);
290 dataset2Arr->SetNumberOfComponents(1);
291 dataset3Arr->SetNumberOfComponents(1);
292 dataset1Arr->SetName(
"M1");
293 dataset2Arr->SetName(
"M2");
294 dataset3Arr->SetName(
"M3");
296 vtkSmartPointer<vtkDoubleArray> dataset1ArrU = vtkSmartPointer<vtkDoubleArray>::New();
297 vtkSmartPointer<vtkDoubleArray> dataset2ArrU = vtkSmartPointer<vtkDoubleArray>::New();
298 vtkSmartPointer<vtkDoubleArray> dataset3ArrU = vtkSmartPointer<vtkDoubleArray>::New();
299 dataset1ArrU->SetNumberOfComponents(1);
300 dataset2ArrU->SetNumberOfComponents(1);
301 dataset3ArrU->SetNumberOfComponents(1);
302 dataset1ArrU->SetName(
"M1");
303 dataset2ArrU->SetName(
"M2");
304 dataset3ArrU->SetName(
"M3");
306 for (
int x = 0; x < xx; x++)
308 for (
int y = 0; y < yy; y++)
310 for (
int z = 0; z < zz; z++)
312 itk::Image<int,3>::IndexType index;
323 image->GetChannelDescriptor().GetPixelType(),
325 image->GetVolumeData(),
332 mask->GetChannelDescriptor().GetPixelType(),
334 mask->GetVolumeData(),
342 dataset1ArrU->InsertNextValue(x*xd);
343 dataset2ArrU->InsertNextValue(y*yd);
344 dataset3ArrU->InsertNextValue(z*zd);
346 if (pxImage == pxImage)
348 dataset1Arr->InsertNextValue(x*xd);
349 dataset2Arr->InsertNextValue(y*yd);
350 dataset3Arr->InsertNextValue(z*zd);
357 vtkSmartPointer<vtkTable> datasetTable = vtkSmartPointer<vtkTable>::New();
358 datasetTable->AddColumn(dataset1Arr);
359 datasetTable->AddColumn(dataset2Arr);
360 datasetTable->AddColumn(dataset3Arr);
362 vtkSmartPointer<vtkTable> datasetTableU = vtkSmartPointer<vtkTable>::New();
363 datasetTableU->AddColumn(dataset1ArrU);
364 datasetTableU->AddColumn(dataset2ArrU);
365 datasetTableU->AddColumn(dataset3ArrU);
367 vtkSmartPointer<vtkPCAStatistics> pcaStatistics = vtkSmartPointer<vtkPCAStatistics>::New();
368 pcaStatistics->SetInputData(vtkStatisticsAlgorithm::INPUT_DATA, datasetTable);
369 pcaStatistics->SetColumnStatus(
"M1", 1);
370 pcaStatistics->SetColumnStatus(
"M2", 1);
371 pcaStatistics->SetColumnStatus(
"M3", 1);
372 pcaStatistics->RequestSelectedColumns();
373 pcaStatistics->SetDeriveOption(
true);
374 pcaStatistics->Update();
376 vtkSmartPointer<vtkDoubleArray> eigenvalues = vtkSmartPointer<vtkDoubleArray>::New();
377 pcaStatistics->GetEigenvalues(eigenvalues);
379 pcaStatistics->SetInputData(vtkStatisticsAlgorithm::INPUT_DATA, datasetTableU);
380 pcaStatistics->Update();
381 vtkSmartPointer<vtkDoubleArray> eigenvaluesU = vtkSmartPointer<vtkDoubleArray>::New();
382 pcaStatistics->GetEigenvalues(eigenvaluesU);
384 std::vector<double> eigen_val(3);
385 std::vector<double> eigen_valUC(3);
386 eigen_val[2] = eigenvalues->GetValue(0);
387 eigen_val[1] = eigenvalues->GetValue(1);
388 eigen_val[0] = eigenvalues->GetValue(2);
389 eigen_valUC[2] = eigenvaluesU->GetValue(0);
390 eigen_valUC[1] = eigenvaluesU->GetValue(1);
391 eigen_valUC[0] = eigenvaluesU->GetValue(2);
393 double major = 4*sqrt(eigen_val[2]);
394 double minor = 4*sqrt(eigen_val[1]);
395 double least = 4*sqrt(eigen_val[0]);
396 double elongation = (major == 0) ? 0 : sqrt(eigen_val[1] / eigen_val[2]);
397 double flatness = (major == 0) ? 0 : sqrt(eigen_val[0] / eigen_val[2]);
398 double majorUC = 4*sqrt(eigen_valUC[2]);
399 double minorUC = 4*sqrt(eigen_valUC[1]);
400 double leastUC = 4*sqrt(eigen_valUC[0]);
401 double elongationUC = majorUC == 0 ? 0 : sqrt(eigen_valUC[1] / eigen_valUC[2]);
402 double flatnessUC = majorUC == 0 ? 0 : sqrt(eigen_valUC[0] / eigen_valUC[2]);
405 featureList.push_back(std::make_pair(prefix +
"Volume (mesh based)",meshVolume));
406 featureList.push_back(std::make_pair(prefix +
"Surface (mesh based)",meshSurf));
407 featureList.push_back(std::make_pair(prefix +
"Surface to volume ratio (mesh based)",surfaceToVolume));
408 featureList.push_back(std::make_pair(prefix +
"Sphericity (mesh based)",sphericity));
409 featureList.push_back(std::make_pair(prefix +
"Sphericity (mesh, mesh based)", sphericityMesh));
410 featureList.push_back(std::make_pair(prefix +
"Asphericity (mesh based)", asphericity));
411 featureList.push_back(std::make_pair(prefix +
"Asphericity (mesh, mesh based)", asphericityMesh));
412 featureList.push_back(std::make_pair(prefix +
"Compactness 1 (mesh based)", compactness3));
413 featureList.push_back(std::make_pair(prefix +
"Compactness 1 old (mesh based)" ,compactness1));
414 featureList.push_back(std::make_pair(prefix +
"Compactness 2 (mesh based)",compactness2));
415 featureList.push_back(std::make_pair(prefix +
"Compactness 1 (mesh, mesh based)", compactness3MeshMesh));
416 featureList.push_back(std::make_pair(prefix +
"Compactness 2 (mesh, mesh based)", compactness2MeshMesh));
417 featureList.push_back(std::make_pair(prefix +
"Spherical disproportion (mesh based)", sphericalDisproportion));
418 featureList.push_back(std::make_pair(prefix +
"Spherical disproportion (mesh, mesh based)", sphericalDisproportionMesh));
419 featureList.push_back(std::make_pair(prefix +
"Surface to volume ratio (voxel based)", surfaceToVolumePixel));
420 featureList.push_back(std::make_pair(prefix +
"Sphericity (voxel based)", sphericityPixel));
421 featureList.push_back(std::make_pair(prefix +
"Asphericity (voxel based)", asphericityPixel));
422 featureList.push_back(std::make_pair(prefix +
"Compactness 1 (voxel based)", compactness3Pixel));
423 featureList.push_back(std::make_pair(prefix +
"Compactness 1 old (voxel based)", compactness1Pixel));
424 featureList.push_back(std::make_pair(prefix +
"Compactness 2 (voxel based)", compactness2Pixel));
425 featureList.push_back(std::make_pair(prefix +
"Spherical disproportion (voxel based)", sphericalDisproportionPixel));
426 featureList.push_back(std::make_pair(prefix +
"PCA Major axis length",major));
427 featureList.push_back(std::make_pair(prefix +
"PCA Minor axis length",minor));
428 featureList.push_back(std::make_pair(prefix +
"PCA Least axis length",least));
429 featureList.push_back(std::make_pair(prefix +
"PCA Elongation",elongation));
430 featureList.push_back(std::make_pair(prefix +
"PCA Flatness",flatness));
431 featureList.push_back(std::make_pair(prefix +
"PCA Major axis length (uncorrected)", majorUC));
432 featureList.push_back(std::make_pair(prefix +
"PCA Minor axis length (uncorrected)", minorUC));
433 featureList.push_back(std::make_pair(prefix +
"PCA Least axis length (uncorrected)", leastUC));
434 featureList.push_back(std::make_pair(prefix +
"PCA Elongation (uncorrected)", elongationUC));
435 featureList.push_back(std::make_pair(prefix +
"PCA Flatness (uncorrected)", flatnessUC));
460 MITK_INFO <<
"Start calculating Volumetric Features::....";
462 featureList.insert(featureList.end(), localResults.begin(), localResults.end());
463 MITK_INFO <<
"Finished calculating volumetric features....";
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)
GIFVolumetricStatistics()
void AddArguments(mitkCommandLineParser &parser) override
std::vector< std::string > FeatureNameListType
itk::Image< unsigned char, 3 > ImageType
virtual void SetLongName(std::string _arg)
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)
virtual void SetShortName(std::string _arg)
FeatureNameListType GetFeatureNames() override
Returns a list of the names of all features that are calculated from this class.
std::vector< std::pair< std::string, double > > FeatureListType
std::string FeatureDescriptionPrefix()
Returns a string that encodes the feature class name.
virtual std::string GetLongName() const
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...
std::string GetOptionPrefix() const
FeatureListType CalculateFeatures(const Image::Pointer &image, const Image::Pointer &feature) override
Calculates the Cooccurence-Matrix based features for this class.
mitk::Image::Pointer image
void CalculateLargestDiameter(itk::Image< TPixel, VImageDimension > *mask, mitk::Image::Pointer valueImage, mitk::GIFVolumetricStatistics::FeatureListType &featureList, std::string prefix)
virtual void SetFeatureClassName(std::string _arg)
void MITKCORE_EXPORT CastToItkImage(const mitk::Image *mitkImage, itk::SmartPointer< ItkOutputImageType > &itkOutputImage)
Cast an mitk::Image to an itk::Image with a specific type.
void CalculateVolumeStatistic(itk::Image< TPixel, VImageDimension > *itkImage, mitk::Image::Pointer mask, mitk::GIFVolumetricStatistics::FeatureListType &featureList, std::string prefix)
mitk::Image::Pointer mask
#define mitkPixelTypeMultiplex5(function, ptype, param1, param2, param3, param4, param5)
virtual ParameterTypes GetParameter() const