diff --git a/Modules/AlgorithmsExt/src/mitkAutoCropImageFilter.cpp b/Modules/AlgorithmsExt/src/mitkAutoCropImageFilter.cpp index 99dc2d0424..c6f31baa56 100644 --- a/Modules/AlgorithmsExt/src/mitkAutoCropImageFilter.cpp +++ b/Modules/AlgorithmsExt/src/mitkAutoCropImageFilter.cpp @@ -1,366 +1,366 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkAutoCropImageFilter.h" #include "mitkGeometry3D.h" #include "mitkImageAccessByItk.h" #include "mitkImageCast.h" #include "mitkImageReadAccessor.h" #include "mitkPlaneGeometry.h" #include "mitkStatusBar.h" #include #include #include mitk::AutoCropImageFilter::AutoCropImageFilter() : m_BackgroundValue(0), m_MarginFactor(1.0), m_TimeSelector(nullptr), m_OverrideCroppingRegion(false) { } mitk::AutoCropImageFilter::~AutoCropImageFilter() { } template void mitk::AutoCropImageFilter::ITKCrop3DImage(itk::Image *inputItkImage, unsigned int timestep) { if (inputItkImage == nullptr) { mitk::StatusBar::GetInstance()->DisplayErrorText( "An internal error occurred. Can't convert Image. Please report to bugs@mitk.org"); MITK_ERROR << "image is nullptr...returning" << std::endl; return; } typedef itk::Image InternalImageType; typedef typename InternalImageType::Pointer InternalImagePointer; typedef itk::RegionOfInterestImageFilter ROIFilterType; typedef typename itk::RegionOfInterestImageFilter::Pointer ROIFilterPointer; InternalImagePointer outputItk = InternalImageType::New(); ROIFilterPointer roiFilter = ROIFilterType::New(); roiFilter->SetInput(0, inputItkImage); roiFilter->SetRegionOfInterest(this->GetCroppingRegion()); roiFilter->Update(); outputItk = roiFilter->GetOutput(); outputItk->DisconnectPipeline(); mitk::Image::Pointer newMitkImage = mitk::Image::New(); mitk::CastToMitkImage(outputItk, newMitkImage); MITK_INFO << "Crop-Output dimension: " << (newMitkImage->GetDimension() == 3) << " Filter-Output dimension: " << this->GetOutput()->GetDimension() << " Timestep: " << timestep; mitk::ImageReadAccessor newMitkImgAcc(newMitkImage); this->GetOutput()->SetVolume(newMitkImgAcc.GetData(), timestep); } void mitk::AutoCropImageFilter::GenerateOutputInformation() { mitk::Image::Pointer input = const_cast(this->GetInput()); mitk::Image::Pointer output = this->GetOutput(); if (input->GetDimension() <= 2) { MITK_ERROR << "Only 3D any 4D images are supported." << std::endl; return; } ComputeNewImageBounds(); if ((output->IsInitialized()) && (output->GetPipelineMTime() <= m_TimeOfHeaderInitialization.GetMTime())) return; itkDebugMacro(<< "GenerateOutputInformation()"); // PART I: initialize input requested region. We do this already here (and not // later when GenerateInputRequestedRegion() is called), because we // also need the information to setup the output. // pre-initialize input-requested-region to largest-possible-region // and correct time-region; spatial part will be cropped by // bounding-box of bounding-object below m_InputRequestedRegion = input->GetLargestPossibleRegion(); // build region out of index and size calculated in ComputeNewImageBounds() mitk::SlicedData::IndexType index; index[0] = m_RegionIndex[0]; index[1] = m_RegionIndex[1]; index[2] = m_RegionIndex[2]; index[3] = m_InputRequestedRegion.GetIndex()[3]; index[4] = m_InputRequestedRegion.GetIndex()[4]; mitk::SlicedData::SizeType size; size[0] = m_RegionSize[0]; size[1] = m_RegionSize[1]; size[2] = m_RegionSize[2]; size[3] = m_InputRequestedRegion.GetSize()[3]; size[4] = m_InputRequestedRegion.GetSize()[4]; mitk::SlicedData::RegionType cropRegion(index, size); // crop input-requested-region with cropping region computed from the image data if (m_InputRequestedRegion.Crop(cropRegion) == false) { // crop not possible => do nothing: set time size to 0. size.Fill(0); m_InputRequestedRegion.SetSize(size); return; } // set input-requested-region, because we access it later in // GenerateInputRequestedRegion (there we just set the time) input->SetRequestedRegion(&m_InputRequestedRegion); // PART II: initialize output image unsigned int dimension = input->GetDimension(); auto dimensions = new unsigned int[dimension]; itk2vtk(m_InputRequestedRegion.GetSize(), dimensions); if (dimension > 3) memcpy(dimensions + 3, input->GetDimensions() + 3, (dimension - 3) * sizeof(unsigned int)); // create basic slicedGeometry that will be initialized below output->Initialize(mitk::PixelType(GetOutputPixelType()), dimension, dimensions); delete[] dimensions; // clone the IndexToWorldTransform from the input, otherwise we will overwrite it, when adjusting the origin of the // output image!! itk::ScalableAffineTransform::Pointer cloneTransform = itk::ScalableAffineTransform::New(); cloneTransform->Compose(input->GetGeometry()->GetIndexToWorldTransform()); output->GetGeometry()->SetIndexToWorldTransform(cloneTransform.GetPointer()); // Position the output Image to match the corresponding region of the input image mitk::SlicedGeometry3D *slicedGeometry = output->GetSlicedGeometry(); mitk::SlicedGeometry3D::Pointer inputGeometry = input->GetSlicedGeometry(); const mitk::SlicedData::IndexType &start = m_InputRequestedRegion.GetIndex(); mitk::Point3D origin; vtk2itk(start, origin); input->GetSlicedGeometry()->IndexToWorld(origin, origin); slicedGeometry->SetOrigin(origin); // get the PlaneGeometry for the first slice of the original image mitk::PlaneGeometry::Pointer plane = dynamic_cast(inputGeometry->GetPlaneGeometry(0)->Clone().GetPointer()); assert(plane); // re-initialize the plane according to the new requirements: // dimensions of the cropped image // right- and down-vector as well as spacing do not change, so use the ones from // input image ScalarType dimX = output->GetDimensions()[0]; ScalarType dimY = output->GetDimensions()[1]; mitk::Vector3D right = plane->GetAxisVector(0); mitk::Vector3D down = plane->GetAxisVector(1); mitk::Vector3D spacing = plane->GetSpacing(); plane->InitializeStandardPlane(dimX, dimY, right, down, &spacing); // set the new origin on the PlaneGeometry as well plane->SetOrigin(origin); // re-initialize the slicedGeometry with the correct planeGeometry // in order to get a fully initialized SlicedGeometry3D slicedGeometry->InitializeEvenlySpaced( plane, inputGeometry->GetSpacing()[2], output->GetSlicedGeometry()->GetSlices()); mitk::TimeGeometry *timeSlicedGeometry = output->GetTimeGeometry(); - mitk::ProportionalTimeGeometry *propTimeGeometry = dynamic_cast(timeSlicedGeometry); + auto *propTimeGeometry = dynamic_cast(timeSlicedGeometry); propTimeGeometry->Initialize(slicedGeometry, output->GetDimension(3)); m_TimeOfHeaderInitialization.Modified(); output->SetPropertyList(input->GetPropertyList()->Clone()); } void mitk::AutoCropImageFilter::GenerateData() { mitk::Image::ConstPointer input = this->GetInput(); mitk::Image::Pointer output = this->GetOutput(); if (input.IsNull()) return; if (input->GetDimension() <= 2) { MITK_ERROR << "Only 3D and 4D images supported"; return; } if ((output->IsInitialized() == false)) return; if (m_TimeSelector.IsNull()) m_TimeSelector = mitk::ImageTimeSelector::New(); m_TimeSelector->SetInput(input); mitk::SlicedData::RegionType outputRegion = input->GetRequestedRegion(); int tstart = outputRegion.GetIndex(3); int tmax = tstart + outputRegion.GetSize(3); for (int timestep = tstart; timestep < tmax; ++timestep) { m_TimeSelector->SetTimeNr(timestep); m_TimeSelector->UpdateLargestPossibleRegion(); AccessFixedDimensionByItk_1(m_TimeSelector->GetOutput(), ITKCrop3DImage, 3, timestep); } // this->GetOutput()->Update(); // Not sure if this is necessary... m_TimeOfHeaderInitialization.Modified(); } void mitk::AutoCropImageFilter::ComputeNewImageBounds() { mitk::Image::ConstPointer inputMitk = this->GetInput(); if (m_OverrideCroppingRegion) { for (unsigned int i = 0; i < 3; ++i) { m_RegionIndex[i] = m_CroppingRegion.GetIndex()[i]; m_RegionSize[i] = m_CroppingRegion.GetSize()[i]; if (m_RegionIndex[i] >= static_cast(inputMitk->GetDimension(i))) { itkExceptionMacro("Cropping index is not inside the image. " << std::endl << "Index:" << std::endl << m_CroppingRegion.GetIndex() << std::endl << "Size:" << std::endl << m_CroppingRegion.GetSize()); } if (m_RegionIndex[i] + m_RegionSize[i] >= inputMitk->GetDimension(i)) { m_RegionSize[i] = inputMitk->GetDimension(i) - m_RegionIndex[i]; } } for (unsigned int i = 0; i < 3; ++i) { m_RegionIndex[i] = m_CroppingRegion.GetIndex()[i]; m_RegionSize[i] = m_CroppingRegion.GetSize()[i]; } } else { // Check if a 3D or 4D image is present unsigned int timeSteps = 1; if (inputMitk->GetDimension() == 4) timeSteps = inputMitk->GetDimension(3); ImageType::IndexType minima, maxima; if (inputMitk->GetDimension() == 4) { // initialize with time step 0 m_TimeSelector = mitk::ImageTimeSelector::New(); m_TimeSelector->SetInput(inputMitk); m_TimeSelector->SetTimeNr(0); m_TimeSelector->UpdateLargestPossibleRegion(); inputMitk = m_TimeSelector->GetOutput(); } ImagePointer inputItk = ImageType::New(); mitk::CastToItkImage(inputMitk, inputItk); // it is assumed that all volumes in a time series have the same 3D dimensions ImageType::RegionType origRegion = inputItk->GetLargestPossibleRegion(); // Initialize min and max on the first (or only) time step maxima = inputItk->GetLargestPossibleRegion().GetIndex(); minima[0] = inputItk->GetLargestPossibleRegion().GetSize()[0]; minima[1] = inputItk->GetLargestPossibleRegion().GetSize()[1]; minima[2] = inputItk->GetLargestPossibleRegion().GetSize()[2]; typedef itk::ImageRegionConstIterator ConstIteratorType; for (unsigned int idx = 0; idx < timeSteps; ++idx) { // if 4D image, update time step and itk image if (idx > 0) { m_TimeSelector->SetTimeNr(idx); m_TimeSelector->UpdateLargestPossibleRegion(); inputMitk = m_TimeSelector->GetOutput(); mitk::CastToItkImage(inputMitk, inputItk); } ConstIteratorType inIt(inputItk, origRegion); for (inIt.GoToBegin(); !inIt.IsAtEnd(); ++inIt) { float pix_val = inIt.Get(); if (fabs(pix_val - m_BackgroundValue) > mitk::eps) { for (int i = 0; i < 3; i++) { minima[i] = vnl_math_min((int)minima[i], (int)(inIt.GetIndex()[i])); maxima[i] = vnl_math_max((int)maxima[i], (int)(inIt.GetIndex()[i])); } } } } typedef ImageType::RegionType::SizeType::SizeValueType SizeValueType; m_RegionSize[0] = (SizeValueType)(m_MarginFactor * (maxima[0] - minima[0] + 1)); m_RegionSize[1] = (SizeValueType)(m_MarginFactor * (maxima[1] - minima[1] + 1)); m_RegionSize[2] = (SizeValueType)(m_MarginFactor * (maxima[2] - minima[2] + 1)); m_RegionIndex = minima; m_RegionIndex[0] -= (m_RegionSize[0] - maxima[0] + minima[0] - 1) / 2; m_RegionIndex[1] -= (m_RegionSize[1] - maxima[1] + minima[1] - 1) / 2; m_RegionIndex[2] -= (m_RegionSize[2] - maxima[2] + minima[2] - 1) / 2; ImageType::RegionType cropRegion(m_RegionIndex, m_RegionSize); origRegion.Crop(cropRegion); m_RegionSize[0] = origRegion.GetSize()[0]; m_RegionSize[1] = origRegion.GetSize()[1]; m_RegionSize[2] = origRegion.GetSize()[2]; m_RegionIndex[0] = origRegion.GetIndex()[0]; m_RegionIndex[1] = origRegion.GetIndex()[1]; m_RegionIndex[2] = origRegion.GetIndex()[2]; m_CroppingRegion = origRegion; } } void mitk::AutoCropImageFilter::GenerateInputRequestedRegion() { } const mitk::PixelType mitk::AutoCropImageFilter::GetOutputPixelType() { return this->GetInput()->GetPixelType(); } void mitk::AutoCropImageFilter::SetCroppingRegion(RegionType overrideRegion) { m_CroppingRegion = overrideRegion; m_OverrideCroppingRegion = true; } diff --git a/Modules/AlgorithmsExt/src/mitkBoundingObjectToSegmentationFilter.cpp b/Modules/AlgorithmsExt/src/mitkBoundingObjectToSegmentationFilter.cpp index 61a5199a6b..7bf490a766 100644 --- a/Modules/AlgorithmsExt/src/mitkBoundingObjectToSegmentationFilter.cpp +++ b/Modules/AlgorithmsExt/src/mitkBoundingObjectToSegmentationFilter.cpp @@ -1,120 +1,120 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkBoundingObjectToSegmentationFilter.h" #include "mitkImageCast.h" #include mitk::BoundingObjectToSegmentationFilter::BoundingObjectToSegmentationFilter() { this->SetNumberOfRequiredInputs(1); } mitk::BoundingObjectToSegmentationFilter::~BoundingObjectToSegmentationFilter() { } void mitk::BoundingObjectToSegmentationFilter::SetBoundingObject(mitk::BoundingObject::Pointer boundingObject) { - mitk::BoundingObjectGroup *testgroup = dynamic_cast(boundingObject.GetPointer()); + auto *testgroup = dynamic_cast(boundingObject.GetPointer()); if (testgroup) m_boundingObjectGroup = testgroup; else { m_boundingObjectGroup = mitk::BoundingObjectGroup::New(); m_boundingObjectGroup->AddBoundingObject(boundingObject); } } void mitk::BoundingObjectToSegmentationFilter::GenerateData() { typedef itk::Image itkImageType; mitk::Image::Pointer outputImage = this->GetOutput(); mitk::Image::ConstPointer inputImage = this->GetInput(); outputImage->Initialize(inputImage); itkImageType::Pointer itkImage; CastToItkImage(outputImage, itkImage); itkImage->FillBuffer(0); for (unsigned int i = 0; i < m_boundingObjectGroup->GetCount(); i++) { // create region for boundingobject mitk::BoundingObject::Pointer boundingObject = m_boundingObjectGroup->GetBoundingObjects().at(i); mitk::BaseGeometry::Pointer boGeometry = boundingObject->GetGeometry(); mitk::BaseGeometry::Pointer inputImageGeometry = inputImage->GetSlicedGeometry(); mitk::BoundingBox::Pointer boToIm = boGeometry->CalculateBoundingBoxRelativeToTransform(inputImageGeometry->GetIndexToWorldTransform()); mitk::BoundingBox::ConstPointer imgBB = inputImageGeometry->GetBoundingBox(); mitk::BoundingBox::PointType minImg = imgBB->GetMinimum(); mitk::BoundingBox::PointType maxImg = imgBB->GetMaximum(); itkImageType::IndexType boIndex; itkImageType::SizeType boSize; mitk::BoundingBox::PointType min = boToIm->GetMinimum(); mitk::BoundingBox::PointType max = boToIm->GetMaximum(); // check if boundingbox is inside imageregion for (int i = 0; i < 3; i++) { if (min[i] < minImg[i]) min[i] = minImg[i]; if (max[i] < minImg[i]) max[i] = minImg[i]; if (max[i] > maxImg[i]) max[i] = maxImg[i]; if (min[i] > maxImg[i]) min[i] = maxImg[i] - 1; } // add 0.5 (boGeometry is no image geometry) boIndex[0] = (mitk::SlicedData::IndexType::IndexValueType)(min[0] + 0.5); boIndex[1] = (mitk::SlicedData::IndexType::IndexValueType)(min[1] + 0.5); boIndex[2] = (mitk::SlicedData::IndexType::IndexValueType)(min[2] + 0.5); // add 1 because we need 0.5 for each index boSize[0] = (mitk::SlicedData::IndexType::IndexValueType)(max[0] - min[0]); boSize[1] = (mitk::SlicedData::IndexType::IndexValueType)(max[1] - min[1]); boSize[2] = (mitk::SlicedData::IndexType::IndexValueType)(max[2] - min[2]); itkImageType::RegionType region(boIndex, boSize); // create region iterator itk::ImageRegionIteratorWithIndex itBoundingObject = itk::ImageRegionIteratorWithIndex(itkImage, region); itBoundingObject.GoToBegin(); while (!itBoundingObject.IsAtEnd()) { itkImageType::IndexType index = itBoundingObject.GetIndex(); mitk::Point3D p; p[0] = index[0]; p[1] = index[1]; p[2] = index[2]; inputImageGeometry->IndexToWorld(p, p); if (boundingObject->IsInside(p) && boundingObject->GetPositive()) itBoundingObject.Set(1); else if (boundingObject->IsInside(p) && !boundingObject->GetPositive()) itBoundingObject.Set(0); ++itBoundingObject; } } CastToMitkImage(itkImage, outputImage); } diff --git a/Modules/AlgorithmsExt/src/mitkGeometryClipImageFilter.cpp b/Modules/AlgorithmsExt/src/mitkGeometryClipImageFilter.cpp index 2b27bf0b61..45aa112c0d 100644 --- a/Modules/AlgorithmsExt/src/mitkGeometryClipImageFilter.cpp +++ b/Modules/AlgorithmsExt/src/mitkGeometryClipImageFilter.cpp @@ -1,265 +1,265 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkGeometryClipImageFilter.h" #include "mitkImageTimeSelector.h" #include "mitkProperties.h" #include "mitkTimeHelper.h" #include "mitkImageToItk.h" #include "itkImageRegionConstIterator.h" #include "itkImageRegionIteratorWithIndex.h" #include mitk::GeometryClipImageFilter::GeometryClipImageFilter() : m_ClippingGeometry(nullptr), m_ClipPartAboveGeometry(true), m_OutsideValue(0), m_AutoOutsideValue(false), m_LabelBothSides(false), m_AutoOrientLabels(false), m_AboveGeometryLabel(1), m_BelowGeometryLabel(2) { this->SetNumberOfIndexedInputs(2); this->SetNumberOfRequiredInputs(2); m_InputTimeSelector = mitk::ImageTimeSelector::New(); m_OutputTimeSelector = mitk::ImageTimeSelector::New(); m_ClippingGeometryData = mitk::GeometryData::New(); } mitk::GeometryClipImageFilter::~GeometryClipImageFilter() { } void mitk::GeometryClipImageFilter::SetClippingGeometry(const mitk::TimeGeometry *timeClippingGeometry) { m_TimeClippingGeometry = timeClippingGeometry; SetClippingGeometry(timeClippingGeometry->GetGeometryForTimeStep(0)); } void mitk::GeometryClipImageFilter::SetClippingGeometry(const mitk::BaseGeometry *aClippingGeometry) { if (aClippingGeometry != m_ClippingGeometry.GetPointer()) { m_ClippingGeometry = aClippingGeometry; m_ClippingGeometryData->SetGeometry(const_cast(aClippingGeometry)); SetNthInput(1, m_ClippingGeometryData); Modified(); } } const mitk::BaseGeometry *mitk::GeometryClipImageFilter::GetClippingGeometry() const { return m_ClippingGeometry; } const mitk::TimeGeometry *mitk::GeometryClipImageFilter::GetClippingTimeGeometry() const { return m_TimeClippingGeometry; } void mitk::GeometryClipImageFilter::GenerateInputRequestedRegion() { Superclass::GenerateInputRequestedRegion(); mitk::Image *output = this->GetOutput(); - mitk::Image *input = const_cast(this->GetInput()); + auto *input = const_cast(this->GetInput()); if ((output->IsInitialized() == false) || (m_ClippingGeometry.IsNull())) return; input->SetRequestedRegionToLargestPossibleRegion(); GenerateTimeInInputRegion(output, input); } void mitk::GeometryClipImageFilter::GenerateOutputInformation() { mitk::Image::ConstPointer input = this->GetInput(); mitk::Image::Pointer output = this->GetOutput(); if ((output->IsInitialized()) && (this->GetMTime() <= m_TimeOfHeaderInitialization.GetMTime())) return; itkDebugMacro(<< "GenerateOutputInformation()"); unsigned int i; auto tmpDimensions = new unsigned int[input->GetDimension()]; for (i = 0; i < input->GetDimension(); ++i) tmpDimensions[i] = input->GetDimension(i); output->Initialize(input->GetPixelType(), input->GetDimension(), tmpDimensions, input->GetNumberOfChannels()); delete[] tmpDimensions; output->SetGeometry(static_cast(input->GetGeometry()->Clone().GetPointer())); output->SetPropertyList(input->GetPropertyList()->Clone()); m_TimeOfHeaderInitialization.Modified(); } template void mitk::_InternalComputeClippedImage(itk::Image *inputItkImage, mitk::GeometryClipImageFilter *geometryClipper, const mitk::PlaneGeometry *clippingPlaneGeometry) { typedef itk::Image ItkInputImageType; typedef itk::Image ItkOutputImageType; typedef itk::ImageRegionConstIteratorWithIndex ItkInputImageIteratorType; typedef itk::ImageRegionIteratorWithIndex ItkOutputImageIteratorType; typename mitk::ImageToItk::Pointer outputimagetoitk = mitk::ImageToItk::New(); outputimagetoitk->SetInput(geometryClipper->m_OutputTimeSelector->GetOutput()); outputimagetoitk->Update(); typename ItkOutputImageType::Pointer outputItkImage = outputimagetoitk->GetOutput(); // create the iterators typename ItkInputImageType::RegionType inputRegionOfInterest = inputItkImage->GetLargestPossibleRegion(); ItkInputImageIteratorType inputIt(inputItkImage, inputRegionOfInterest); ItkOutputImageIteratorType outputIt(outputItkImage, inputRegionOfInterest); typename ItkOutputImageType::PixelType outsideValue; if (geometryClipper->m_AutoOutsideValue) outsideValue = itk::NumericTraits::min(); else outsideValue = (typename ItkOutputImageType::PixelType)geometryClipper->m_OutsideValue; mitk::BaseGeometry *inputGeometry = geometryClipper->m_InputTimeSelector->GetOutput()->GetGeometry(); typedef itk::Index IndexType; Point3D indexPt; indexPt.Fill(0); int i, dim = IndexType::GetIndexDimension(); Point3D pointInMM; bool above = geometryClipper->m_ClipPartAboveGeometry; bool labelBothSides = geometryClipper->GetLabelBothSides(); if (geometryClipper->GetAutoOrientLabels()) { Point3D leftMostPoint; leftMostPoint.Fill(std::numeric_limits::min() / 2.0); if (clippingPlaneGeometry->IsAbove(pointInMM) != above) { // invert meaning of above --> left is always the "above" side above = !above; MITK_INFO << leftMostPoint << " is BELOW geometry. Inverting meaning of above" << std::endl; } else MITK_INFO << leftMostPoint << " is above geometry" << std::endl; } - typename ItkOutputImageType::PixelType aboveLabel = + auto aboveLabel = (typename ItkOutputImageType::PixelType)geometryClipper->GetAboveGeometryLabel(); - typename ItkOutputImageType::PixelType belowLabel = + auto belowLabel = (typename ItkOutputImageType::PixelType)geometryClipper->GetBelowGeometryLabel(); for (inputIt.GoToBegin(), outputIt.GoToBegin(); !inputIt.IsAtEnd(); ++inputIt, ++outputIt) { if ((typename ItkOutputImageType::PixelType)inputIt.Get() == outsideValue) { outputIt.Set(outsideValue); } else { for (i = 0; i < dim; ++i) indexPt[i] = (mitk::ScalarType)inputIt.GetIndex()[i]; inputGeometry->IndexToWorld(indexPt, pointInMM); if (clippingPlaneGeometry->IsAbove(pointInMM) == above) { if (labelBothSides) outputIt.Set(aboveLabel); else outputIt.Set(outsideValue); } else { if (labelBothSides) outputIt.Set(belowLabel); else outputIt.Set(inputIt.Get()); } } } } #include "mitkImageAccessByItk.h" void mitk::GeometryClipImageFilter::GenerateData() { Image::ConstPointer input = this->GetInput(); Image::Pointer output = this->GetOutput(); if ((output->IsInitialized() == false) || (m_ClippingGeometry.IsNull())) return; const PlaneGeometry *clippingGeometryOfCurrentTimeStep = nullptr; if (m_TimeClippingGeometry.IsNull()) { clippingGeometryOfCurrentTimeStep = dynamic_cast(m_ClippingGeometry.GetPointer()); } else { clippingGeometryOfCurrentTimeStep = dynamic_cast(m_TimeClippingGeometry->GetGeometryForTimeStep(0).GetPointer()); } if (clippingGeometryOfCurrentTimeStep == nullptr) return; m_InputTimeSelector->SetInput(input); m_OutputTimeSelector->SetInput(this->GetOutput()); mitk::Image::RegionType outputRegion = output->GetRequestedRegion(); const mitk::TimeGeometry *outputTimeGeometry = output->GetTimeGeometry(); const mitk::TimeGeometry *inputTimeGeometry = input->GetTimeGeometry(); ScalarType timeInMS; int timestep = 0; int tstart = outputRegion.GetIndex(3); int tmax = tstart + outputRegion.GetSize(3); int t; for (t = tstart; t < tmax; ++t) { timeInMS = outputTimeGeometry->TimeStepToTimePoint(t); timestep = inputTimeGeometry->TimePointToTimeStep(timeInMS); m_InputTimeSelector->SetTimeNr(timestep); m_InputTimeSelector->UpdateLargestPossibleRegion(); m_OutputTimeSelector->SetTimeNr(t); m_OutputTimeSelector->UpdateLargestPossibleRegion(); if (m_TimeClippingGeometry.IsNotNull()) { timestep = m_TimeClippingGeometry->TimePointToTimeStep(timeInMS); if (m_TimeClippingGeometry->IsValidTimeStep(timestep) == false) continue; clippingGeometryOfCurrentTimeStep = dynamic_cast(m_TimeClippingGeometry->GetGeometryForTimeStep(timestep).GetPointer()); } AccessByItk_2( m_InputTimeSelector->GetOutput(), _InternalComputeClippedImage, this, clippingGeometryOfCurrentTimeStep); } m_TimeOfHeaderInitialization.Modified(); } diff --git a/Modules/AlgorithmsExt/src/mitkHeightFieldSurfaceClipImageFilter.cpp b/Modules/AlgorithmsExt/src/mitkHeightFieldSurfaceClipImageFilter.cpp index 20e8a2b192..e2156b1bde 100644 --- a/Modules/AlgorithmsExt/src/mitkHeightFieldSurfaceClipImageFilter.cpp +++ b/Modules/AlgorithmsExt/src/mitkHeightFieldSurfaceClipImageFilter.cpp @@ -1,418 +1,418 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkHeightFieldSurfaceClipImageFilter.h" #include "mitkImageTimeSelector.h" #include "mitkProperties.h" #include "mitkTimeHelper.h" #include "mitkImageAccessByItk.h" #include "mitkImageToItk.h" #include #include #include #include #include #include namespace mitk { HeightFieldSurfaceClipImageFilter::HeightFieldSurfaceClipImageFilter() : m_ClippingMode(CLIPPING_MODE_CONSTANT), m_ClippingConstant(0.0), m_MultiplicationFactor(2.0), m_MultiPlaneValue(2), m_HeightFieldResolutionX(256), m_HeightFieldResolutionY(256), m_MaxHeight(1024.0) { this->SetNumberOfIndexedInputs(8); this->SetNumberOfRequiredInputs(2); m_InputTimeSelector = ImageTimeSelector::New(); m_OutputTimeSelector = ImageTimeSelector::New(); } HeightFieldSurfaceClipImageFilter::~HeightFieldSurfaceClipImageFilter() {} void HeightFieldSurfaceClipImageFilter::SetClippingSurface(Surface *clippingSurface) { this->SetNthInput(1, clippingSurface); } void HeightFieldSurfaceClipImageFilter::SetClippingSurfaces(ClippingPlaneList planeList) { if (planeList.size() > 7) { MITK_WARN << "Only 7 clipping planes are allowed!"; } for (unsigned int i = 0; i < planeList.size(); ++i) { this->SetNthInput(i + 1, planeList.at(i)); } } const Surface *HeightFieldSurfaceClipImageFilter::GetClippingSurface() const { return dynamic_cast(itk::ProcessObject::GetInput(1)); } void HeightFieldSurfaceClipImageFilter::SetClippingMode(int mode) { m_ClippingMode = mode; } int HeightFieldSurfaceClipImageFilter::GetClippingMode() { return m_ClippingMode; } void HeightFieldSurfaceClipImageFilter::SetClippingModeToConstant() { m_ClippingMode = CLIPPING_MODE_CONSTANT; } void HeightFieldSurfaceClipImageFilter::SetClippingModeToMultiplyByFactor() { m_ClippingMode = CLIPPING_MODE_MULTIPLYBYFACTOR; } void HeightFieldSurfaceClipImageFilter::SetClippingModeToMultiPlaneValue() { m_ClippingMode = CLIPPING_MODE_MULTIPLANE; } void HeightFieldSurfaceClipImageFilter::GenerateInputRequestedRegion() { Image *outputImage = this->GetOutput(); - Image *inputImage = const_cast(this->GetInput(0)); - const Surface *inputSurface = dynamic_cast(this->GetInput(1)); + auto *inputImage = const_cast(this->GetInput(0)); + const auto *inputSurface = dynamic_cast(this->GetInput(1)); if (!outputImage->IsInitialized() || inputSurface == nullptr) { return; } inputImage->SetRequestedRegionToLargestPossibleRegion(); GenerateTimeInInputRegion(outputImage, inputImage); } void HeightFieldSurfaceClipImageFilter::GenerateOutputInformation() { const Image *inputImage = this->GetInput(0); Image *outputImage = this->GetOutput(); if (outputImage->IsInitialized() && (this->GetMTime() <= m_TimeOfHeaderInitialization.GetMTime())) { return; } itkDebugMacro(<< "GenerateOutputInformation()"); unsigned int i; auto tmpDimensions = new unsigned int[inputImage->GetDimension()]; for (i = 0; i < inputImage->GetDimension(); ++i) { tmpDimensions[i] = inputImage->GetDimension(i); } outputImage->Initialize( inputImage->GetPixelType(), inputImage->GetDimension(), tmpDimensions, inputImage->GetNumberOfChannels()); delete[] tmpDimensions; outputImage->SetGeometry(static_cast(inputImage->GetGeometry()->Clone().GetPointer())); outputImage->SetPropertyList(inputImage->GetPropertyList()->Clone()); m_TimeOfHeaderInitialization.Modified(); } template void HeightFieldSurfaceClipImageFilter::_InternalComputeClippedImage( itk::Image *inputItkImage, HeightFieldSurfaceClipImageFilter *clipImageFilter, vtkPolyData *clippingPolyData, AffineTransform3D *imageToPlaneTransform) { typedef itk::Image ItkInputImageType; typedef itk::Image ItkOutputImageType; typedef itk::ImageSliceConstIteratorWithIndex ItkInputImageIteratorType; typedef itk::ImageRegionIteratorWithIndex ItkOutputImageIteratorType; typename ImageToItk::Pointer outputimagetoitk = ImageToItk::New(); outputimagetoitk->SetInput(clipImageFilter->m_OutputTimeSelector->GetOutput()); outputimagetoitk->Update(); typename ItkOutputImageType::Pointer outputItkImage = outputimagetoitk->GetOutput(); std::vector test; // create the iterators typename ItkInputImageType::RegionType inputRegionOfInterest = inputItkImage->GetLargestPossibleRegion(); ItkInputImageIteratorType inputIt(inputItkImage, inputRegionOfInterest); ItkOutputImageIteratorType outputIt(outputItkImage, inputRegionOfInterest); // Get bounds of clipping data clippingPolyData->ComputeBounds(); double *bounds = clippingPolyData->GetBounds(); double xWidth = bounds[1] - bounds[0]; double yWidth = bounds[3] - bounds[2]; // Create vtkCellLocator for clipping poly data vtkCellLocator *cellLocator = vtkCellLocator::New(); cellLocator->SetDataSet(clippingPolyData); cellLocator->CacheCellBoundsOn(); cellLocator->AutomaticOn(); cellLocator->BuildLocator(); // Allocate memory for 2D image to hold the height field generated by // projecting the clipping data onto the plane auto heightField = new double[m_HeightFieldResolutionX * m_HeightFieldResolutionY]; // Walk through height field and for each entry calculate height of the // clipping poly data at this point by means of vtkCellLocator. The // clipping data x/y bounds are used for converting from poly data space to // image (height-field) space. MITK_INFO << "Calculating Height Field..." << std::endl; for (unsigned int y = 0; y < m_HeightFieldResolutionY; ++y) { for (unsigned int x = 0; x < m_HeightFieldResolutionX; ++x) { double p0[3], p1[3], surfacePoint[3], pcoords[3]; p0[0] = bounds[0] + xWidth * x / (double)m_HeightFieldResolutionX; p0[1] = bounds[2] + yWidth * y / (double)m_HeightFieldResolutionY; p0[2] = -m_MaxHeight; p1[0] = p0[0]; p1[1] = p0[1]; p1[2] = m_MaxHeight; double t, distance; int subId; if (cellLocator->IntersectWithLine(p0, p1, 0.1, t, surfacePoint, pcoords, subId)) { distance = (2.0 * t - 1.0) * m_MaxHeight; } else { distance = -65536.0; } heightField[y * m_HeightFieldResolutionX + x] = distance; itk::Image::IndexType index; index[0] = x; index[1] = y; } } // Walk through entire input image and for each point determine its distance // from the x/y plane. MITK_INFO << "Performing clipping..." << std::endl; - TPixel factor = static_cast(clipImageFilter->m_MultiplicationFactor); + auto factor = static_cast(clipImageFilter->m_MultiplicationFactor); TPixel clippingConstant = clipImageFilter->m_ClippingConstant; inputIt.SetFirstDirection(0); inputIt.SetSecondDirection(1); // through all slices for (inputIt.GoToBegin(), outputIt.GoToBegin(); !inputIt.IsAtEnd(); inputIt.NextSlice()) { // through all lines of a slice for (; !inputIt.IsAtEndOfSlice(); inputIt.NextLine()) { // Transform the start(line) point from the image to the plane Point3D imageP0, planeP0; imageP0[0] = inputIt.GetIndex()[0]; imageP0[1] = inputIt.GetIndex()[1]; imageP0[2] = inputIt.GetIndex()[2]; planeP0 = imageToPlaneTransform->TransformPoint(imageP0); // Transform the end point (line) from the image to the plane Point3D imageP1, planeP1; imageP1[0] = imageP0[0] + inputRegionOfInterest.GetSize(0); imageP1[1] = imageP0[1]; imageP1[2] = imageP0[2]; planeP1 = imageToPlaneTransform->TransformPoint(imageP1); // calculate the step size (if the plane is rotate, you go "crossway" through the image) Vector3D step = (planeP1 - planeP0) / (double)inputRegionOfInterest.GetSize(0); // over all pixel for (; !inputIt.IsAtEndOfLine(); ++inputIt, ++outputIt, planeP0 += step) { // Only ConstantMode: if image pixel value == constant mode value-->set output pixel value directly if ((clipImageFilter->m_ClippingMode == CLIPPING_MODE_CONSTANT) && ((TPixel)inputIt.Get() == clippingConstant)) { outputIt.Set(clippingConstant); } else { - int x0 = (int)((double)(m_HeightFieldResolutionX) * (planeP0[0] - bounds[0]) / xWidth); - int y0 = (int)((double)(m_HeightFieldResolutionY) * (planeP0[1] - bounds[2]) / yWidth); + auto x0 = (int)((double)(m_HeightFieldResolutionX) * (planeP0[0] - bounds[0]) / xWidth); + auto y0 = (int)((double)(m_HeightFieldResolutionY) * (planeP0[1] - bounds[2]) / yWidth); bool clip; // if the current point is outside of the plane region (RegionOfInterest)-->clip the pixel allways if ((x0 < 0) || (x0 >= (int)m_HeightFieldResolutionX) || (y0 < 0) || (y0 >= (int)m_HeightFieldResolutionY)) { clip = true; } else { // Calculate bilinearly interpolated height field value at plane point int x1 = x0 + 1; int y1 = y0 + 1; if (x1 >= (int)m_HeightFieldResolutionX) { x1 = x0; } if (y1 >= (int)m_HeightFieldResolutionY) { y1 = y0; } // Get the neighbour points for the interpolation ScalarType q00, q01, q10, q11; q00 = heightField[y0 * m_HeightFieldResolutionX + x0]; q01 = heightField[y0 * m_HeightFieldResolutionX + x1]; q10 = heightField[y1 * m_HeightFieldResolutionX + x0]; q11 = heightField[y1 * m_HeightFieldResolutionX + x1]; double p00 = ((double)(m_HeightFieldResolutionX) * (planeP0[0] - bounds[0]) / xWidth); double p01 = ((double)(m_HeightFieldResolutionY) * (planeP0[1] - bounds[2]) / yWidth); ScalarType q = q00 * ((double)x1 - p00) * ((double)y1 - p01) + q01 * (p00 - (double)x0) * ((double)y1 - p01) + q10 * ((double)x1 - p00) * (p01 - (double)y0) + q11 * (p00 - (double)x0) * (p01 - (double)y0); if (q - planeP0[2] < 0) { clip = true; } else { clip = false; } } // different modes: differnt values for the clipped pixel if (clip) { if (clipImageFilter->m_ClippingMode == CLIPPING_MODE_CONSTANT) { outputIt.Set(clipImageFilter->m_ClippingConstant); } else if (clipImageFilter->m_ClippingMode == CLIPPING_MODE_MULTIPLYBYFACTOR) { outputIt.Set(inputIt.Get() * factor); } else if (clipImageFilter->m_ClippingMode == CLIPPING_MODE_MULTIPLANE) { if (inputIt.Get() != 0) outputIt.Set(inputIt.Get() + m_MultiPlaneValue); else outputIt.Set(inputIt.Get()); } } // the non-clipped pixel keeps his value else { outputIt.Set(inputIt.Get()); } } } } } MITK_INFO << "DONE!" << std::endl; // Clean-up cellLocator->Delete(); } void HeightFieldSurfaceClipImageFilter::GenerateData() { const Image *inputImage = this->GetInput(0); const Image *outputImage = this->GetOutput(); m_InputTimeSelector->SetInput(inputImage); m_OutputTimeSelector->SetInput(outputImage); Image::RegionType outputRegion = outputImage->GetRequestedRegion(); const TimeGeometry *outputTimeGeometry = outputImage->GetTimeGeometry(); const TimeGeometry *inputTimeGeometry = inputImage->GetTimeGeometry(); ScalarType timeInMS; int timestep = 0; int tstart = outputRegion.GetIndex(3); int tmax = tstart + outputRegion.GetSize(3); for (unsigned int i = 1; i < this->GetNumberOfInputs(); ++i) { - Surface *inputSurface = const_cast(dynamic_cast(itk::ProcessObject::GetInput(i))); + auto *inputSurface = const_cast(dynamic_cast(itk::ProcessObject::GetInput(i))); if (!outputImage->IsInitialized() || inputSurface == nullptr) return; MITK_INFO << "Plane: " << i; MITK_INFO << "Clipping: Start\n"; // const PlaneGeometry *clippingGeometryOfCurrentTimeStep = nullptr; int t; for (t = tstart; t < tmax; ++t) { timeInMS = outputTimeGeometry->TimeStepToTimePoint(t); timestep = inputTimeGeometry->TimePointToTimeStep(timeInMS); m_InputTimeSelector->SetTimeNr(timestep); m_InputTimeSelector->UpdateLargestPossibleRegion(); m_OutputTimeSelector->SetTimeNr(t); m_OutputTimeSelector->UpdateLargestPossibleRegion(); // Compose IndexToWorld transform of image with WorldToIndexTransform of // clipping data for conversion from image index space to plane index space AffineTransform3D::Pointer planeWorldToIndexTransform = AffineTransform3D::New(); inputSurface->GetGeometry(t)->GetIndexToWorldTransform()->GetInverse(planeWorldToIndexTransform); AffineTransform3D::Pointer imageToPlaneTransform = AffineTransform3D::New(); imageToPlaneTransform->SetIdentity(); imageToPlaneTransform->Compose(inputTimeGeometry->GetGeometryForTimeStep(t)->GetIndexToWorldTransform()); imageToPlaneTransform->Compose(planeWorldToIndexTransform); MITK_INFO << "Accessing ITK function...\n"; if (i == 1) { AccessByItk_3(m_InputTimeSelector->GetOutput(), _InternalComputeClippedImage, this, inputSurface->GetVtkPolyData(t), imageToPlaneTransform); } else { mitk::Image::Pointer extensionImage = m_OutputTimeSelector->GetOutput()->Clone(); AccessByItk_3( extensionImage, _InternalComputeClippedImage, this, inputSurface->GetVtkPolyData(t), imageToPlaneTransform); } if (m_ClippingMode == CLIPPING_MODE_MULTIPLANE) m_MultiPlaneValue = m_MultiPlaneValue * 2; } } m_TimeOfHeaderInitialization.Modified(); } } // namespace diff --git a/Modules/AlgorithmsExt/src/mitkMaskImageFilter.cpp b/Modules/AlgorithmsExt/src/mitkMaskImageFilter.cpp index 7dea8a330c..e03db7b54b 100644 --- a/Modules/AlgorithmsExt/src/mitkMaskImageFilter.cpp +++ b/Modules/AlgorithmsExt/src/mitkMaskImageFilter.cpp @@ -1,245 +1,245 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkMaskImageFilter.h" #include "mitkImageTimeSelector.h" #include "mitkProperties.h" #include "mitkTimeHelper.h" #include "mitkImageAccessByItk.h" #include "mitkImageToItk.h" #include "itkImageRegionConstIterator.h" #include "itkImageRegionIteratorWithIndex.h" #include mitk::MaskImageFilter::MaskImageFilter() : m_Mask(nullptr) { this->SetNumberOfIndexedInputs(2); this->SetNumberOfRequiredInputs(2); m_InputTimeSelector = mitk::ImageTimeSelector::New(); m_MaskTimeSelector = mitk::ImageTimeSelector::New(); m_OutputTimeSelector = mitk::ImageTimeSelector::New(); m_OverrideOutsideValue = false; m_OutsideValue = 0; } mitk::MaskImageFilter::~MaskImageFilter() { } void mitk::MaskImageFilter::SetMask(const mitk::Image *mask) { // Process object is not const-correct so the const_cast is required here m_Mask = const_cast(mask); this->ProcessObject::SetNthInput(1, m_Mask); } const mitk::Image *mitk::MaskImageFilter::GetMask() const { return m_Mask; } void mitk::MaskImageFilter::GenerateInputRequestedRegion() { Superclass::GenerateInputRequestedRegion(); mitk::Image *output = this->GetOutput(); - mitk::Image *input = const_cast(this->GetInput()); + auto *input = const_cast(this->GetInput()); mitk::Image *mask = m_Mask; if ((output->IsInitialized() == false) || (mask == nullptr) || (mask->GetTimeGeometry()->CountTimeSteps() == 0)) return; input->SetRequestedRegionToLargestPossibleRegion(); mask->SetRequestedRegionToLargestPossibleRegion(); GenerateTimeInInputRegion(output, input); GenerateTimeInInputRegion(output, mask); } void mitk::MaskImageFilter::GenerateOutputInformation() { mitk::Image::ConstPointer input = this->GetInput(); mitk::Image::Pointer output = this->GetOutput(); if ((output->IsInitialized()) && (this->GetMTime() <= m_TimeOfHeaderInitialization.GetMTime())) return; itkDebugMacro(<< "GenerateOutputInformation()"); output->Initialize(input->GetPixelType(), *input->GetTimeGeometry()); output->SetPropertyList(input->GetPropertyList()->Clone()); m_TimeOfHeaderInitialization.Modified(); } template void mitk::MaskImageFilter::InternalComputeMask(itk::Image *inputItkImage) { // dirty quick fix, duplicating code so both unsigned char and unsigned short are supported // this should be changed once unsigned char segmentations can be converted to unsigned short mitk::PixelType pixelType = m_MaskTimeSelector->GetOutput()->GetImageDescriptor()->GetChannelDescriptor().GetPixelType(); if (pixelType.GetComponentType() == itk::ImageIOBase::UCHAR) { typedef itk::Image ItkInputImageType; typedef itk::Image ItkMaskImageType; typedef itk::Image ItkOutputImageType; typedef itk::ImageRegionConstIterator ItkInputImageIteratorType; typedef itk::ImageRegionConstIterator ItkMaskImageIteratorType; typedef itk::ImageRegionIteratorWithIndex ItkOutputImageIteratorType; typename mitk::ImageToItk::Pointer maskimagetoitk = mitk::ImageToItk::New(); maskimagetoitk->SetInput(m_MaskTimeSelector->GetOutput()); maskimagetoitk->Update(); typename ItkMaskImageType::Pointer maskItkImage = maskimagetoitk->GetOutput(); typename mitk::ImageToItk::Pointer outputimagetoitk = mitk::ImageToItk::New(); outputimagetoitk->SetInput(m_OutputTimeSelector->GetOutput()); outputimagetoitk->Update(); typename ItkOutputImageType::Pointer outputItkImage = outputimagetoitk->GetOutput(); // create the iterators typename ItkInputImageType::RegionType inputRegionOfInterest = inputItkImage->GetLargestPossibleRegion(); ItkInputImageIteratorType inputIt(inputItkImage, inputRegionOfInterest); ItkMaskImageIteratorType maskIt(maskItkImage, inputRegionOfInterest); ItkOutputImageIteratorType outputIt(outputItkImage, inputRegionOfInterest); // typename ItkOutputImageType::PixelType outsideValue = itk::NumericTraits::min(); if (!m_OverrideOutsideValue) m_OutsideValue = itk::NumericTraits::min(); m_MinValue = std::numeric_limits::max(); m_MaxValue = std::numeric_limits::min(); for (inputIt.GoToBegin(), maskIt.GoToBegin(), outputIt.GoToBegin(); !inputIt.IsAtEnd() && !maskIt.IsAtEnd(); ++inputIt, ++maskIt, ++outputIt) { if (maskIt.Get() > itk::NumericTraits::Zero) { outputIt.Set(inputIt.Get()); m_MinValue = vnl_math_min((float)inputIt.Get(), (float)m_MinValue); m_MaxValue = vnl_math_max((float)inputIt.Get(), (float)m_MaxValue); } else { outputIt.Set(m_OutsideValue); } } } else { { typedef itk::Image ItkInputImageType; typedef itk::Image ItkMaskImageType; typedef itk::Image ItkOutputImageType; typedef itk::ImageRegionConstIterator ItkInputImageIteratorType; typedef itk::ImageRegionConstIterator ItkMaskImageIteratorType; typedef itk::ImageRegionIteratorWithIndex ItkOutputImageIteratorType; typename mitk::ImageToItk::Pointer maskimagetoitk = mitk::ImageToItk::New(); maskimagetoitk->SetInput(m_MaskTimeSelector->GetOutput()); maskimagetoitk->Update(); typename ItkMaskImageType::Pointer maskItkImage = maskimagetoitk->GetOutput(); typename mitk::ImageToItk::Pointer outputimagetoitk = mitk::ImageToItk::New(); outputimagetoitk->SetInput(m_OutputTimeSelector->GetOutput()); outputimagetoitk->Update(); typename ItkOutputImageType::Pointer outputItkImage = outputimagetoitk->GetOutput(); // create the iterators typename ItkInputImageType::RegionType inputRegionOfInterest = inputItkImage->GetLargestPossibleRegion(); ItkInputImageIteratorType inputIt(inputItkImage, inputRegionOfInterest); ItkMaskImageIteratorType maskIt(maskItkImage, inputRegionOfInterest); ItkOutputImageIteratorType outputIt(outputItkImage, inputRegionOfInterest); // typename ItkOutputImageType::PixelType outsideValue = itk::NumericTraits::min(); if (!m_OverrideOutsideValue) m_OutsideValue = itk::NumericTraits::min(); m_MinValue = std::numeric_limits::max(); m_MaxValue = std::numeric_limits::min(); for (inputIt.GoToBegin(), maskIt.GoToBegin(), outputIt.GoToBegin(); !inputIt.IsAtEnd() && !maskIt.IsAtEnd(); ++inputIt, ++maskIt, ++outputIt) { if (maskIt.Get() > itk::NumericTraits::Zero) { outputIt.Set(inputIt.Get()); m_MinValue = vnl_math_min((float)inputIt.Get(), (float)m_MinValue); m_MaxValue = vnl_math_max((float)inputIt.Get(), (float)m_MaxValue); } else { outputIt.Set(m_OutsideValue); } } } } } void mitk::MaskImageFilter::GenerateData() { mitk::Image::ConstPointer input = this->GetInput(); mitk::Image::Pointer mask = m_Mask; mitk::Image::Pointer output = this->GetOutput(); if ((output->IsInitialized() == false) || (mask.IsNull()) || (mask->GetTimeGeometry()->CountTimeSteps() == 0)) return; m_InputTimeSelector->SetInput(input); m_MaskTimeSelector->SetInput(mask); m_OutputTimeSelector->SetInput(this->GetOutput()); mitk::Image::RegionType outputRegion = output->GetRequestedRegion(); const mitk::TimeGeometry *outputTimeGeometry = output->GetTimeGeometry(); const mitk::TimeGeometry *inputTimeGeometry = input->GetTimeGeometry(); const mitk::TimeGeometry *maskTimeGeometry = mask->GetTimeGeometry(); ScalarType timeInMS; int timestep = 0; int tstart = outputRegion.GetIndex(3); int tmax = tstart + outputRegion.GetSize(3); int t; for (t = tstart; t < tmax; ++t) { timeInMS = outputTimeGeometry->TimeStepToTimePoint(t); timestep = inputTimeGeometry->TimePointToTimeStep(timeInMS); m_InputTimeSelector->SetTimeNr(timestep); m_InputTimeSelector->UpdateLargestPossibleRegion(); m_OutputTimeSelector->SetTimeNr(t); m_OutputTimeSelector->UpdateLargestPossibleRegion(); timestep = maskTimeGeometry->TimePointToTimeStep(timeInMS); m_MaskTimeSelector->SetTimeNr(timestep); m_MaskTimeSelector->UpdateLargestPossibleRegion(); AccessByItk(m_InputTimeSelector->GetOutput(), InternalComputeMask); } m_TimeOfHeaderInitialization.Modified(); } diff --git a/Modules/AlgorithmsExt/src/mitkMovieGenerator.cpp b/Modules/AlgorithmsExt/src/mitkMovieGenerator.cpp index f52cd1cbd6..f38818e738 100755 --- a/Modules/AlgorithmsExt/src/mitkMovieGenerator.cpp +++ b/Modules/AlgorithmsExt/src/mitkMovieGenerator.cpp @@ -1,143 +1,143 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkMovieGenerator.h" #include "mitkConfig.h" #include #include "vtk_glew.h" #if WIN32 #ifndef __GNUC__ //#if ! (_MSC_VER >= 1400) #include "mitkMovieGeneratorWin32.h" //#endif #else #include "GL/glext.h" #endif #endif #ifndef GL_BGR #define GL_BGR GL_BGR_EXT #endif mitk::MovieGenerator::MovieGenerator() : m_stepper(nullptr), m_renderer(nullptr), m_width(0), m_height(0), m_initialized(false), m_FrameRate(20) { m_fileName[0] = 0; } mitk::MovieGenerator::Pointer mitk::MovieGenerator::New() { Pointer smartPtr; MovieGenerator *rawPtr = ::itk::ObjectFactory::Create(); if (rawPtr == nullptr) { #ifdef WIN32 #ifndef __GNUC__ //#if ! (_MSC_VER >= 1400) mitk::MovieGenerator::Pointer wp = static_cast(mitk::MovieGeneratorWin32::New()); return wp; //#endif #endif #endif } smartPtr = rawPtr; if (rawPtr != nullptr) rawPtr->UnRegister(); return smartPtr; } bool mitk::MovieGenerator::WriteMovie() { bool ok = false; if (m_stepper) { if (m_renderer) m_renderer->GetRenderWindow()->MakeCurrent(); // m_stepper->First(); RenderingManager::GetInstance()->ForceImmediateUpdate(m_renderer->GetRenderWindow()); ok = InitGenerator(); if (!ok) { TerminateGenerator(); return false; } int imgSize = 3 * m_width * m_height; printf("Video size = %i x %i\n", m_width, m_height); - GLbyte *data = new GLbyte[imgSize]; + auto *data = new GLbyte[imgSize]; // duplicate steps if pingPong option is switched to on. unsigned int numOfSteps = m_stepper->GetSteps(); if (m_stepper->GetPingPong()) numOfSteps *= 2; for (unsigned int i = 0; i < numOfSteps; i++) { if (m_renderer) m_renderer->GetRenderWindow()->MakeCurrent(); RenderingManager::GetInstance()->ForceImmediateUpdate(m_renderer->GetRenderWindow()); glReadPixels(5, 5, m_width, m_height, GL_BGR, GL_UNSIGNED_BYTE, (void *)data); AddFrame(data); m_stepper->Next(); } ok = TerminateGenerator(); delete[] data; } return ok; } bool mitk::MovieGenerator::WriteCurrentFrameToMovie() { if (m_renderer) { m_renderer->GetRenderWindow()->MakeCurrent(); if (!m_initialized) { RenderingManager::GetInstance()->ForceImmediateUpdate(m_renderer->GetRenderWindow()); m_initialized = InitGenerator(); } if (!m_initialized) { TerminateGenerator(); return false; } int imgSize = 3 * m_width * m_height; - GLbyte *data = new GLbyte[imgSize]; + auto *data = new GLbyte[imgSize]; RenderingManager::GetInstance()->ForceImmediateUpdate(m_renderer->GetRenderWindow()); glReadPixels(5, 5, m_width, m_height, GL_BGR, GL_UNSIGNED_BYTE, (void *)data); AddFrame(data); delete[] data; } return true; } void mitk::MovieGenerator::ReleaseMovieWriter() { TerminateGenerator(); m_initialized = false; } void mitk::MovieGenerator::SetFrameRate(unsigned int rate) { m_FrameRate = rate; } unsigned int mitk::MovieGenerator::GetFrameRate() { return m_FrameRate; } diff --git a/Modules/AlgorithmsExt/src/mitkNonBlockingAlgorithm.cpp b/Modules/AlgorithmsExt/src/mitkNonBlockingAlgorithm.cpp index e5db7586c7..726910a162 100644 --- a/Modules/AlgorithmsExt/src/mitkNonBlockingAlgorithm.cpp +++ b/Modules/AlgorithmsExt/src/mitkNonBlockingAlgorithm.cpp @@ -1,207 +1,207 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkNonBlockingAlgorithm.h" #include "mitkCallbackFromGUIThread.h" #include "mitkDataStorage.h" #include namespace mitk { NonBlockingAlgorithm::NonBlockingAlgorithm() : m_ThreadID(-1), m_UpdateRequests(0), m_KillRequest(false) { m_ParameterListMutex = itk::FastMutexLock::New(); m_Parameters = PropertyList::New(); m_MultiThreader = itk::MultiThreader::New(); } NonBlockingAlgorithm::~NonBlockingAlgorithm() {} void mitk::NonBlockingAlgorithm::SetDataStorage(DataStorage &storage) { m_DataStorage = &storage; } DataStorage *mitk::NonBlockingAlgorithm::GetDataStorage() { return m_DataStorage; } void NonBlockingAlgorithm::Initialize(const NonBlockingAlgorithm *itkNotUsed(other)) { // define one input, one output basedata object // some basedata input - image, surface, whatever BaseData::Pointer input; SetPointerParameter("Input", input); // some basedata output BaseData::Pointer output; SetPointerParameter("Output", output); } void NonBlockingAlgorithm::SetPointerParameter(const char *parameter, BaseData *value) { m_ParameterListMutex->Lock(); m_Parameters->SetProperty(parameter, SmartPointerProperty::New(value)); m_ParameterListMutex->Unlock(); } void NonBlockingAlgorithm::DefineTriggerParameter(const char *parameter) { BaseProperty *value = m_Parameters->GetProperty(parameter); if (value && m_TriggerPropertyConnections.find(parameter) == m_TriggerPropertyConnections.end()) { itk::ReceptorMemberCommand::Pointer command = itk::ReceptorMemberCommand::New(); command->SetCallbackFunction(this, &NonBlockingAlgorithm::TriggerParameterModified); m_TriggerPropertyConnections[parameter] = value->AddObserver(itk::ModifiedEvent(), command); } } void NonBlockingAlgorithm::UnDefineTriggerParameter(const char *parameter) { auto iter = m_TriggerPropertyConnections.find(parameter); if (iter != m_TriggerPropertyConnections.end()) { BaseProperty *value = m_Parameters->GetProperty(parameter); MITK_ERROR(!value) << "NonBlockingAlgorithm::UnDefineTriggerProperty() in bad state." << std::endl; ; value->RemoveObserver(m_TriggerPropertyConnections[parameter]); m_TriggerPropertyConnections.erase(iter); } } void NonBlockingAlgorithm::Reset() { Initialize(); } void NonBlockingAlgorithm::StartBlockingAlgorithm() { StartAlgorithm(); StopAlgorithm(); } void NonBlockingAlgorithm::StartAlgorithm() { if (!ReadyToRun()) return; // let algorithm check if all input/parameters are ok if (m_KillRequest) return; // someone wants us to die m_ParameterListMutex->Lock(); m_ThreadParameters.m_Algorithm = this; ++m_UpdateRequests; m_ParameterListMutex->Unlock(); if (m_ThreadID != -1) // thread already running. But something obviously wants us to recalculate the output { return; // thread already running } // spawn a thread that calls ThreadedUpdateFunction(), and ThreadedUpdateFinished() on us itk::ThreadFunctionType fpointer = &StaticNonBlockingAlgorithmThread; m_ThreadID = m_MultiThreader->SpawnThread(fpointer, &m_ThreadParameters); } void NonBlockingAlgorithm::StopAlgorithm() { if (m_ThreadID == -1) return; // thread not running m_MultiThreader->TerminateThread(m_ThreadID); // waits for the thread to terminate on its own } // a static function to call a member of NonBlockingAlgorithm from inside an ITK thread ITK_THREAD_RETURN_TYPE NonBlockingAlgorithm::StaticNonBlockingAlgorithmThread(void *param) { // itk::MultiThreader provides an itk::MultiThreader::ThreadInfoStruct as parameter - itk::MultiThreader::ThreadInfoStruct *itkmttis = static_cast(param); + auto *itkmttis = static_cast(param); // we need the UserData part of that structure - ThreadParameters *flsp = static_cast(itkmttis->UserData); + auto *flsp = static_cast(itkmttis->UserData); NonBlockingAlgorithm::Pointer algorithm = flsp->m_Algorithm; // this UserData tells us, which BubbleTool's method to call if (!algorithm) { return ITK_THREAD_RETURN_VALUE; } algorithm->m_ParameterListMutex->Lock(); while (algorithm->m_UpdateRequests > 0) { algorithm->m_UpdateRequests = 0; algorithm->m_ParameterListMutex->Unlock(); // actually call the methods that do the work if (algorithm->ThreadedUpdateFunction()) // returns a bool for success/failure { itk::ReceptorMemberCommand::Pointer command = itk::ReceptorMemberCommand::New(); command->SetCallbackFunction(algorithm, &NonBlockingAlgorithm::ThreadedUpdateSuccessful); CallbackFromGUIThread::GetInstance()->CallThisFromGUIThread(command); // algorithm->ThreadedUpdateSuccessful(); } else { itk::ReceptorMemberCommand::Pointer command = itk::ReceptorMemberCommand::New(); command->SetCallbackFunction(algorithm, &NonBlockingAlgorithm::ThreadedUpdateFailed); CallbackFromGUIThread::GetInstance()->CallThisFromGUIThread(command); // algorithm->ThreadedUpdateFailed(); } algorithm->m_ParameterListMutex->Lock(); } algorithm->m_ParameterListMutex->Unlock(); return ITK_THREAD_RETURN_VALUE; } void NonBlockingAlgorithm::TriggerParameterModified(const itk::EventObject &) { StartAlgorithm(); } bool NonBlockingAlgorithm::ReadyToRun() { return true; // default is always ready } bool NonBlockingAlgorithm::ThreadedUpdateFunction() { return true; } // called from gui thread void NonBlockingAlgorithm::ThreadedUpdateSuccessful(const itk::EventObject &) { ThreadedUpdateSuccessful(); m_ParameterListMutex->Lock(); m_ThreadID = -1; // tested before starting m_ParameterListMutex->Unlock(); m_ThreadParameters.m_Algorithm = nullptr; } void NonBlockingAlgorithm::ThreadedUpdateSuccessful() { // notify observers that a result is ready InvokeEvent(ResultAvailable(this)); } // called from gui thread void NonBlockingAlgorithm::ThreadedUpdateFailed(const itk::EventObject &) { ThreadedUpdateFailed(); m_ParameterListMutex->Lock(); m_ThreadID = -1; // tested before starting m_ParameterListMutex->Unlock(); m_ThreadParameters.m_Algorithm = nullptr; // delete } void NonBlockingAlgorithm::ThreadedUpdateFailed() { // notify observers that something went wrong InvokeEvent(ProcessingError(this)); } } // namespace diff --git a/Modules/AlgorithmsExt/src/mitkSimpleHistogram.cpp b/Modules/AlgorithmsExt/src/mitkSimpleHistogram.cpp index 589271d07b..a569d0f71b 100644 --- a/Modules/AlgorithmsExt/src/mitkSimpleHistogram.cpp +++ b/Modules/AlgorithmsExt/src/mitkSimpleHistogram.cpp @@ -1,315 +1,315 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkSimpleHistogram.h" #include "mitkImageReadAccessor.h" #include "mitkSimpleUnstructuredGridHistogram.h" #include "mitkUnstructuredGrid.h" namespace mitk { void SimpleImageHistogram::ComputeFromBaseData(BaseData *src) { valid = false; // check if input is valid if (src == nullptr) return; - Image *source = dynamic_cast(src); + auto *source = dynamic_cast(src); if (source == nullptr) return; else if (source->IsEmpty()) return; // dummy histogram { min = 0; max = 1; first = 0; last = 1; } { int typInt = 0; { const int typ = source->GetPixelType().GetComponentType(); if (typ == itk::ImageIOBase::UCHAR) typInt = 0; else if (typ == itk::ImageIOBase::CHAR) typInt = 1; else if (typ == itk::ImageIOBase::USHORT) typInt = 2; else if (typ == itk::ImageIOBase::SHORT) typInt = 3; else if (typ == itk::ImageIOBase::INT) typInt = 4; else if (typ == itk::ImageIOBase::UINT) typInt = 5; else if (typ == itk::ImageIOBase::LONG) typInt = 6; else if (typ == itk::ImageIOBase::ULONG) typInt = 7; else if (typ == itk::ImageIOBase::FLOAT) typInt = 8; else if (typ == itk::ImageIOBase::DOUBLE) typInt = 9; else { MITK_WARN << "Pixel type not supported by SimpleImageHistogram"; return; } } first = -32768; last = 65535; // support at least full signed and unsigned short range if (histogram) delete histogram; histogram = new CountType[last - first + 1]; memset(histogram, 0, sizeof(CountType) * (last - first + 1)); highest = 0; max = first - 1; min = last + 1; unsigned int num = 1; for (unsigned int r = 0; r < source->GetDimension(); r++) num *= source->GetDimension(r); // MITK_INFO << "building histogramm of integer image: 0=" << source->GetDimension(0) << " 1=" << // source->GetDimension(1) << " 2=" << source->GetDimension(2) << " 3=" << source->GetDimension(3); ImageReadAccessor sourceAcc(source); const void *src = sourceAcc.GetData(); do { int value = 0; switch (typInt) { case 0: { - unsigned char *t = (unsigned char *)src; + auto *t = (unsigned char *)src; value = *t++; src = (void *)t; } break; case 1: { - signed char *t = (signed char *)src; + auto *t = (signed char *)src; value = *t++; src = (void *)t; } break; case 2: { - unsigned short *t = (unsigned short *)src; + auto *t = (unsigned short *)src; value = *t++; src = (void *)t; } break; case 3: { - signed short *t = (signed short *)src; + auto *t = (signed short *)src; value = *t++; src = (void *)t; } break; case 4: { - signed int *t = (signed int *)src; + auto *t = (signed int *)src; value = *t++; src = (void *)t; } break; case 5: { - unsigned int *t = (unsigned int *)src; + auto *t = (unsigned int *)src; value = *t++; src = (void *)t; } break; case 6: { - signed long *t = (signed long *)src; + auto *t = (signed long *)src; value = *t++; src = (void *)t; } break; case 7: { - unsigned long *t = (unsigned long *)src; + auto *t = (unsigned long *)src; value = *t++; src = (void *)t; } break; case 8: { - float *t = (float *)src; + auto *t = (float *)src; value = *t++; src = (void *)t; } break; case 9: { - double *t = (double *)src; + auto *t = (double *)src; value = *t++; src = (void *)t; } break; } if (value >= first && value <= last) { if (value < min) min = value; if (value > max) max = value; CountType tmp = ++histogram[value - first]; if (tmp > highest) highest = tmp; } } while (--num); MITK_INFO << "histogramm computed: min=" << min << " max=" << max << " highestBin=" << highest << " samples=" << num; } invLogHighest = 1.0 / log(double(highest)); valid = true; } bool SimpleImageHistogram::GetValid() { return valid; } float SimpleImageHistogram::GetRelativeBin(double left, double right) const { if (!valid) return 0.0f; int iLeft = floorf(left); int iRight = ceilf(right); /* double sum = 0; for( int r = 0 ; r < 256 ; r++) { int pos = left + (right-left) * r/255.0; int posInArray = floorf(pos+0.5f) - first; sum += float(log(double(histogram[posInArray]))); } sum /= 256.0; return float(sum*invLogHighest); */ CountType maximum = 0; for (int i = iLeft; i <= iRight; i++) { int posInArray = i - first; if (histogram[posInArray] > maximum) maximum = histogram[posInArray]; } return float(log(double(maximum)) * invLogHighest); } class ImageHistogramCacheElement : public SimpleHistogramCache::Element { public: void ComputeFromBaseData(BaseData *baseData) override { histogram.ComputeFromBaseData(baseData); } SimpleHistogram *GetHistogram() override { return &histogram; } SimpleImageHistogram histogram; }; class UnstructuredGridHistogramCacheElement : public SimpleHistogramCache::Element { public: void ComputeFromBaseData(BaseData *baseData) override { histogram.ComputeFromBaseData(baseData); } SimpleHistogram *GetHistogram() override { return &histogram; } SimpleUnstructuredGridHistogram histogram; }; SimpleHistogram *SimpleHistogramCache::operator[](BaseData::Pointer sp_BaseData) { BaseData *p_BaseData = sp_BaseData.GetPointer(); if (!p_BaseData) { MITK_WARN << "SimpleHistogramCache::operator[] with null base data called"; return nullptr; } Element *elementToUpdate = nullptr; bool first = true; for (auto iter = cache.begin(); iter != cache.end(); iter++) { Element *e = *iter; BaseData *p_tmp = e->baseData.GetPointer(); if (p_tmp == p_BaseData) { if (!first) { cache.erase(iter); cache.push_front(e); } if (p_BaseData->GetMTime() > e->m_LastUpdateTime.GetMTime()) { elementToUpdate = e; goto recomputeElement; } // MITK_INFO << "using a cached histogram"; return e->GetHistogram(); } first = false; } if (dynamic_cast(p_BaseData)) { elementToUpdate = new ImageHistogramCacheElement(); } else if (dynamic_cast(p_BaseData)) { elementToUpdate = new UnstructuredGridHistogramCacheElement(); } else { MITK_WARN << "not supported: " << p_BaseData->GetNameOfClass(); } elementToUpdate->baseData = p_BaseData; cache.push_front(elementToUpdate); TrimCache(); recomputeElement: // MITK_INFO << "computing a new histogram"; elementToUpdate->ComputeFromBaseData(p_BaseData); elementToUpdate->m_LastUpdateTime.Modified(); return elementToUpdate->GetHistogram(); } SimpleHistogramCache::Element::~Element() {} } diff --git a/Modules/AlgorithmsExt/src/mitkSimpleUnstructuredGridHistogram.cpp b/Modules/AlgorithmsExt/src/mitkSimpleUnstructuredGridHistogram.cpp index 831e6606ad..d35f999788 100644 --- a/Modules/AlgorithmsExt/src/mitkSimpleUnstructuredGridHistogram.cpp +++ b/Modules/AlgorithmsExt/src/mitkSimpleUnstructuredGridHistogram.cpp @@ -1,156 +1,156 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkSimpleUnstructuredGridHistogram.h" #include #include #include #include #include namespace mitk { SimpleUnstructuredGridHistogram::SimpleUnstructuredGridHistogram() : m_UGHistogram(HistogramType::New()), m_InvMaxFrequency(1) { // MITK_INFO << "####### Created a SimpleUnstructuredGridHistogram"; } double SimpleUnstructuredGridHistogram::GetMin() const { return m_UGHistogram->GetBinMin(0, 0); } double SimpleUnstructuredGridHistogram::GetMax() const { return m_UGHistogram->GetBinMax(0, m_UGHistogram->GetSize(0) - 1); } void SimpleUnstructuredGridHistogram::ComputeFromBaseData(BaseData *source) { - UnstructuredGrid *grid = dynamic_cast(source); + auto *grid = dynamic_cast(source); // m_UGHistogram->Initialize(grid); vtkUnstructuredGrid *vtkUGrid = grid->GetVtkUnstructuredGrid(); ListSampleType::Pointer listSample = ListSampleType::New(); listSample->SetMeasurementVectorSize(1); MeasurementVectorType v; MeasurementVectorType lowerBound; MeasurementVectorType upperBound; int numberOfBins = 1; HistogramType::SizeType size(1); vtkDataArray *data; /*if (m_UsePointData)*/ data = vtkUGrid->GetPointData()->GetScalars(); // else data = vtkUGrid->GetCellData()->GetScalars(); if (data == nullptr) { listSample->Resize(1); v[0] = 0; listSample->PushBack(v); lowerBound[0] = 0; upperBound[0] = 0; size.Fill(numberOfBins); } else { listSample->Resize(data->GetNumberOfTuples()); for (vtkIdType i = 0; i < data->GetNumberOfTuples(); ++i) { v[0] = data->GetComponent(i, 0); // if (v[0] != 0) MITK_INFO << "ug scalar: " << v[0]; listSample->PushBack(v); } vtkIdType numberOfTuples = data->GetNumberOfTuples(); if (numberOfTuples < 1000) numberOfBins = 250; else if (numberOfTuples < 30000) numberOfBins = 100; else if (numberOfTuples < 100000) numberOfBins = 50; else numberOfBins = 20; size.Fill(numberOfBins); double range[2]; data->GetRange(range); lowerBound[0] = range[0]; upperBound[0] = range[1]; } typedef itk::Statistics::SampleToHistogramFilter FilterType; FilterType::Pointer histoFilter = FilterType::New(); FilterType::HistogramMeasurementVectorType binMin(1); FilterType::HistogramMeasurementVectorType binMax(1); binMin[0] = lowerBound[0]; binMax[0] = upperBound[0]; histoFilter->SetInput(listSample); histoFilter->SetHistogramSize(size); histoFilter->SetHistogramBinMinimum(binMin); histoFilter->SetHistogramBinMaximum(binMax); histoFilter->Update(); m_UGHistogram = histoFilter->GetOutput(); m_BinSize = (GetMax() - GetMin()) / (double)numberOfBins; m_Mins = m_UGHistogram->GetMins(); m_Maxs = m_UGHistogram->GetMaxs(); HistogramType::AbsoluteFrequencyType maxFrequency = 0; HistogramType::SizeValueType histoSize = m_UGHistogram->GetSize(0); for (HistogramType::SizeValueType i = 0; i < histoSize; ++i) { HistogramType::AbsoluteFrequencyType f = m_UGHistogram->GetFrequency(i); if (f > maxFrequency) { maxFrequency = f; } // MITK_INFO << "Bin #" << i << ": " << m_UGHistogram->GetMeasurement(i,0); } if (maxFrequency) { m_InvMaxFrequency = 1.0 / log((double)maxFrequency); } // MITK_INFO << "UGHistogramm size: " << m_UGHistogram->GetSize(0) << ", maxF: " << maxFrequency // << " min count: " << m_Mins.size() << " max count: " << m_Maxs.size(); } float SimpleUnstructuredGridHistogram::GetRelativeBin(double start, double end) const { // MITK_INFO << "GetRelativeBin start: " << start << ", end: " << end; HistogramType::AbsoluteFrequencyType maxf = 0; for (double pos = start; pos < end; pos += m_BinSize) { HistogramType::AbsoluteFrequencyType f = m_UGHistogram->GetFrequency(m_UGHistogram->GetIndex(pos)); if (f > maxf) maxf = f; } return log(static_cast(maxf)) * m_InvMaxFrequency; } } diff --git a/Modules/AlgorithmsExt/test/mitkPlaneFitTest.cpp b/Modules/AlgorithmsExt/test/mitkPlaneFitTest.cpp index 6322a628df..134dbb80c3 100644 --- a/Modules/AlgorithmsExt/test/mitkPlaneFitTest.cpp +++ b/Modules/AlgorithmsExt/test/mitkPlaneFitTest.cpp @@ -1,92 +1,92 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include #include #include #include #include #include #include int mitkPlaneFitTest(int, char *[]) { // float bounds[]={0.0f,10.0f,0.0f,10.0f,0.0f,5.0f}; mitk::PlaneFit::Pointer PlaneFit = mitk::PlaneFit::New(); mitk::PointSet::Pointer PointSet = mitk::PointSet::New(); mitk::Point3D Point; // first without any point, then incrementally add points within thre points there will be a plane geometry std::cout << "Start PlaneFitTest " << std::endl; for (int position = 0; position < 6; position++) { // add a point directly mitk::FillVector3D(Point, (float)position, (float)position * 1.5, 2.5); PointSet->GetPointSet()->GetPoints()->InsertElement(position, Point); } // Set Input PlaneFit->SetInput(PointSet); const mitk::PointSet *testPointSet = PlaneFit->GetInput(); std::cout << " Size test of Input Method: "; if (testPointSet->GetSize() == PointSet->GetSize()) { std::cout << "[PASSED]" << std::endl; } else { std::cout << "[FAILED]" << std::endl; return EXIT_FAILURE; } // Test Centroid std::cout << " Testing centroid calculaation: "; PlaneFit->Update(); const mitk::Point3D ¢roid = PlaneFit->GetCentroid(); mitk::Point3D expectedCentroid; expectedCentroid[0] = 2.5; expectedCentroid[1] = 3.75; expectedCentroid[2] = 2.5; if (centroid == expectedCentroid) { std::cout << "[PASSED]" << std::endl; } else { std::cout << "[FAILED]" << std::endl; return EXIT_FAILURE; } // Test PlaneGeometry std::cout << " Test PlaneGeometry: "; - mitk::PlaneGeometry *PlaneGeometry = dynamic_cast(PlaneFit->GetOutput()->GetGeometry()); + auto *PlaneGeometry = dynamic_cast(PlaneFit->GetOutput()->GetGeometry()); if (PlaneGeometry) { std::cout << "[PASSED]" << std::endl; } else { std::cout << "[FAILED]" << std::endl; return EXIT_FAILURE; } std::cout << "[TEST DONE]" << std::endl; return EXIT_SUCCESS; } diff --git a/Modules/Annotation/src/mitkLabelAnnotation3D.cpp b/Modules/Annotation/src/mitkLabelAnnotation3D.cpp index 4546c7b933..c19d193171 100644 --- a/Modules/Annotation/src/mitkLabelAnnotation3D.cpp +++ b/Modules/Annotation/src/mitkLabelAnnotation3D.cpp @@ -1,170 +1,170 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkLabelAnnotation3D.h" #include #include #include #include #include #include #include #include #include #include #include mitk::LabelAnnotation3D::LabelAnnotation3D() : m_PointSetModifiedObserverTag(0) { } mitk::LabelAnnotation3D::~LabelAnnotation3D() { if (m_LabelCoordinates.IsNotNull()) m_LabelCoordinates->RemoveObserver(m_PointSetModifiedObserverTag); for (BaseRenderer *renderer : m_LSH.GetRegisteredBaseRenderer()) { if (renderer) { this->RemoveFromBaseRenderer(renderer); } } } mitk::LabelAnnotation3D::LocalStorage::~LocalStorage() { } mitk::LabelAnnotation3D::LocalStorage::LocalStorage() { m_Points = vtkSmartPointer::New(); // Add label array. m_Labels = vtkSmartPointer::New(); m_Labels->SetNumberOfValues(0); m_Labels->SetName("labels"); // Add priority array. m_Sizes = vtkSmartPointer::New(); m_Sizes->SetNumberOfValues(0); m_Sizes->SetName("sizes"); m_Points->GetPointData()->AddArray(m_Sizes); m_Points->GetPointData()->AddArray(m_Labels); m_PointSetToLabelHierarchyFilter = vtkSmartPointer::New(); m_PointSetToLabelHierarchyFilter->SetInputData(m_Points); m_PointSetToLabelHierarchyFilter->SetLabelArrayName("labels"); m_PointSetToLabelHierarchyFilter->SetPriorityArrayName("sizes"); m_PointSetToLabelHierarchyFilter->Update(); m_LabelMapper = vtkSmartPointer::New(); m_LabelMapper->SetInputConnection(m_PointSetToLabelHierarchyFilter->GetOutputPort()); m_LabelsActor = vtkSmartPointer::New(); m_LabelsActor->SetMapper(m_LabelMapper); } void mitk::LabelAnnotation3D::UpdateVtkAnnotation(mitk::BaseRenderer *renderer) { if (m_LabelCoordinates.IsNull()) { MITK_WARN << "No pointset defined to print labels!"; return; } LocalStorage *ls = this->m_LSH.GetLocalStorage(renderer); if (ls->IsGenerateDataRequired(renderer, this)) { vtkSmartPointer points = vtkSmartPointer::New(); - size_t pointsetsize = (size_t)m_LabelCoordinates->GetSize(); + auto pointsetsize = (size_t)m_LabelCoordinates->GetSize(); ls->m_Labels->SetNumberOfValues(pointsetsize); ls->m_Sizes->SetNumberOfValues(pointsetsize); for (size_t i = 0; i < pointsetsize; i++) { mitk::Point3D coordinate = m_LabelCoordinates->GetPoint(i); points->InsertNextPoint(coordinate[0] + GetOffsetVector()[0], coordinate[1] + GetOffsetVector()[1], coordinate[2] + GetOffsetVector()[2]); if (m_LabelVector.size() > i) ls->m_Labels->SetValue(i, m_LabelVector[i]); else ls->m_Labels->SetValue(i, ""); if (m_PriorityVector.size() > i) ls->m_Sizes->SetValue(i, m_PriorityVector[i]); else ls->m_Sizes->SetValue(i, 1); } ls->m_Points->SetPoints(points); ls->m_PointSetToLabelHierarchyFilter->Update(); ls->m_LabelMapper->Update(); float color[3] = {1, 1, 1}; float opacity = 1.0; GetColor(color); GetOpacity(opacity); ls->m_LabelsActor->GetProperty()->SetColor(color[0], color[1], color[2]); ls->m_LabelsActor->GetProperty()->SetOpacity(opacity); ls->UpdateGenerateDataTime(); } } vtkProp *mitk::LabelAnnotation3D::GetVtkProp(BaseRenderer *renderer) const { LocalStorage *ls = this->m_LSH.GetLocalStorage(renderer); return ls->m_LabelsActor; } void mitk::LabelAnnotation3D::SetLabelVector(const std::vector &LabelVector) { m_LabelVector = LabelVector; this->Modified(); } void mitk::LabelAnnotation3D::SetPriorityVector(const std::vector &PriorityVector) { m_PriorityVector = PriorityVector; this->Modified(); } void mitk::LabelAnnotation3D::SetLabelCoordinates(mitk::PointSet::Pointer LabelCoordinates) { if (m_LabelCoordinates.IsNotNull()) { m_LabelCoordinates->RemoveObserver(m_PointSetModifiedObserverTag); m_PointSetModifiedObserverTag = 0; m_LabelCoordinates = nullptr; } if (LabelCoordinates.IsNull()) { return; } m_LabelCoordinates = LabelCoordinates; itk::MemberCommand::Pointer _PropertyListModifiedCommand = itk::MemberCommand::New(); _PropertyListModifiedCommand->SetCallbackFunction(this, &mitk::LabelAnnotation3D::PointSetModified); m_PointSetModifiedObserverTag = m_LabelCoordinates->AddObserver(itk::ModifiedEvent(), _PropertyListModifiedCommand); this->Modified(); } void mitk::LabelAnnotation3D::PointSetModified(const itk::Object * /*caller*/, const itk::EventObject &) { this->Modified(); } diff --git a/Modules/Annotation/src/mitkLayoutAnnotationRenderer.cpp b/Modules/Annotation/src/mitkLayoutAnnotationRenderer.cpp index 8fa1049f9e..a842e5706f 100644 --- a/Modules/Annotation/src/mitkLayoutAnnotationRenderer.cpp +++ b/Modules/Annotation/src/mitkLayoutAnnotationRenderer.cpp @@ -1,346 +1,346 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkLayoutAnnotationRenderer.h" #include "mitkBaseRenderer.h" #include "mitkAnnotationUtils.h" #include "mitkEnumerationProperty.h" #include namespace mitk { const std::string LayoutAnnotationRenderer::ANNOTATIONRENDERER_ID = "LayoutAnnotationRenderer"; const std::string LayoutAnnotationRenderer::PROP_LAYOUT = "Layout"; const std::string LayoutAnnotationRenderer::PROP_LAYOUT_PRIORITY = PROP_LAYOUT + ".priority"; const std::string LayoutAnnotationRenderer::PROP_LAYOUT_ALIGNMENT = PROP_LAYOUT + ".alignment"; const std::string LayoutAnnotationRenderer::PROP_LAYOUT_MARGIN = PROP_LAYOUT + ".margin"; void LayoutAnnotationRenderer::SetMargin2D(Annotation *Annotation, const Point2D &OffsetVector) { mitk::Point2dProperty::Pointer OffsetVectorProperty = mitk::Point2dProperty::New(OffsetVector); Annotation->SetProperty(PROP_LAYOUT_MARGIN, OffsetVectorProperty.GetPointer()); } Point2D LayoutAnnotationRenderer::GetMargin2D(Annotation *Annotation) { mitk::Point2D OffsetVector; OffsetVector.Fill(0); Annotation->GetPropertyValue(PROP_LAYOUT_MARGIN, OffsetVector); return OffsetVector; } LayoutAnnotationRenderer::LayoutAnnotationRenderer(const std::string &rendererId) : AbstractAnnotationRenderer(rendererId, LayoutAnnotationRenderer::ANNOTATIONRENDERER_ID) { } void LayoutAnnotationRenderer::AddAlignmentProperty(Annotation *Annotation, Alignment activeAlignment, Point2D margin, int priority) { EnumerationProperty::Pointer alignmentProperty(mitk::EnumerationProperty::New()); alignmentProperty->AddEnum("TopLeft", TopLeft); alignmentProperty->AddEnum("Top", Top); alignmentProperty->AddEnum("TopRight", TopRight); alignmentProperty->AddEnum("BottomLeft ", BottomLeft); alignmentProperty->AddEnum("Bottom", Bottom); alignmentProperty->AddEnum("BottomRight", BottomRight); alignmentProperty->AddEnum("Left", Left); alignmentProperty->AddEnum("Right", Right); alignmentProperty->SetValue(activeAlignment); Annotation->AddProperty(PROP_LAYOUT_ALIGNMENT, alignmentProperty.GetPointer()); Annotation->SetIntProperty(PROP_LAYOUT_PRIORITY, priority); SetMargin2D(Annotation, margin); } void LayoutAnnotationRenderer::OnAnnotationRenderersChanged() { if (!this->GetCurrentBaseRenderer()) return; m_AnnotationContainerMap.clear(); for (Annotation *annotation : this->GetServices()) { if (!annotation) continue; BaseProperty *prop = annotation->GetProperty(PROP_LAYOUT_ALIGNMENT); - EnumerationProperty *enumProb = dynamic_cast(prop); + auto *enumProb = dynamic_cast(prop); Alignment currentAlignment = TopLeft; Point2D margin; margin.Fill(5); int priority = -1; annotation->GetIntProperty(PROP_LAYOUT_PRIORITY, priority); if (!enumProb) { AddAlignmentProperty(annotation, currentAlignment, margin, priority); } else { // TODO19786 insert currentAlignment = static_cast(enumProb->GetValueAsId()); } AnnotationRankedMap &AnnotationVec = m_AnnotationContainerMap[currentAlignment]; if (!AnnotationVec.empty() && priority < 0) { int max = AnnotationVec.rbegin()->first; if (max < 100) priority = 100; else priority = max + 1; } AnnotationVec.insert(std::pair(priority, annotation)); } this->PrepareLayout(); } LayoutAnnotationRenderer::~LayoutAnnotationRenderer() {} const std::string LayoutAnnotationRenderer::GetID() const { return ANNOTATIONRENDERER_ID; } LayoutAnnotationRenderer *LayoutAnnotationRenderer::GetAnnotationRenderer(const std::string &rendererID) { LayoutAnnotationRenderer *result = nullptr; AbstractAnnotationRenderer *registeredService = AnnotationUtils::GetAnnotationRenderer(ANNOTATIONRENDERER_ID, rendererID); if (registeredService) result = dynamic_cast(registeredService); if (!result) { result = new LayoutAnnotationRenderer(rendererID); AnnotationUtils::RegisterAnnotationRenderer(result); } return result; } void LayoutAnnotationRenderer::OnRenderWindowModified() { PrepareLayout(); } void LayoutAnnotationRenderer::AddAnnotation(Annotation *Annotation, const std::string &rendererID, Alignment alignment, double marginX, double marginY, int priority) { GetAnnotationRenderer(rendererID); us::ServiceProperties props; props[Annotation::US_PROPKEY_AR_ID] = ANNOTATIONRENDERER_ID; props[Annotation::US_PROPKEY_RENDERER_ID] = rendererID; Annotation->RegisterAsMicroservice(props); Point2D margin; margin[0] = marginX; margin[1] = marginY; AddAlignmentProperty(Annotation, alignment, margin, priority); } void LayoutAnnotationRenderer::AddAnnotation( Annotation *Annotation, BaseRenderer *renderer, Alignment alignment, double marginX, double marginY, int priority) { AddAnnotation(Annotation, renderer->GetName(), alignment, marginX, marginY, priority); } void LayoutAnnotationRenderer::PrepareLayout() { if (!this->GetCurrentBaseRenderer()) return; int *size = this->GetCurrentBaseRenderer()->GetVtkRenderer()->GetSize(); PrepareTopLeftLayout(size); PrepareTopLayout(size); PrepareTopRightLayout(size); PrepareBottomLeftLayout(size); PrepareBottomLayout(size); PrepareBottomRightLayout(size); PrepareLeftLayout(size); PrepareRightLayout(size); } void LayoutAnnotationRenderer::PrepareTopLeftLayout(int *displaySize) { double posX, posY; Point2D margin; posX = 0; posY = displaySize[1]; mitk::Annotation::Bounds bounds; AnnotationRankedMap &AnnotationMap = m_AnnotationContainerMap[TopLeft]; for (auto it = AnnotationMap.cbegin(); it != AnnotationMap.cend(); ++it) { Annotation *Annotation = it->second; margin = GetMargin2D(Annotation); bounds = Annotation->GetBoundsOnDisplay(this->GetCurrentBaseRenderer()); posY -= bounds.Size[1] + margin[1]; bounds.Position[0] = posX + margin[0]; bounds.Position[1] = posY; Annotation->SetBoundsOnDisplay(this->GetCurrentBaseRenderer(), bounds); } } void LayoutAnnotationRenderer::PrepareTopLayout(int *displaySize) { double posX, posY; Point2D margin; posX = 0; posY = displaySize[1]; mitk::Annotation::Bounds bounds; AnnotationRankedMap &AnnotationMap = m_AnnotationContainerMap[Top]; for (auto it = AnnotationMap.cbegin(); it != AnnotationMap.cend(); ++it) { Annotation *Annotation = it->second; margin = GetMargin2D(Annotation); bounds = Annotation->GetBoundsOnDisplay(this->GetCurrentBaseRenderer()); posX = displaySize[0] / 2 - bounds.Size[0] / 2; posY -= bounds.Size[1] + margin[1]; bounds.Position[0] = posX; bounds.Position[1] = posY; Annotation->SetBoundsOnDisplay(this->GetCurrentBaseRenderer(), bounds); } } void LayoutAnnotationRenderer::PrepareTopRightLayout(int *displaySize) { double posX, posY; Point2D margin; posX = 0; posY = displaySize[1]; mitk::Annotation::Bounds bounds; AnnotationRankedMap &AnnotationMap = m_AnnotationContainerMap[TopRight]; for (auto it = AnnotationMap.cbegin(); it != AnnotationMap.cend(); ++it) { Annotation *Annotation = it->second; margin = GetMargin2D(Annotation); bounds = Annotation->GetBoundsOnDisplay(this->GetCurrentBaseRenderer()); posX = displaySize[0] - (bounds.Size[0] + margin[0]); posY -= bounds.Size[1] + margin[1]; bounds.Position[0] = posX; bounds.Position[1] = posY; Annotation->SetBoundsOnDisplay(this->GetCurrentBaseRenderer(), bounds); } } void LayoutAnnotationRenderer::PrepareRightLayout(int *displaySize) { double posY; Point2D margin; double height = GetHeight(m_AnnotationContainerMap[Right], GetCurrentBaseRenderer()); posY = (height / 2.0 + displaySize[1]) / 2.0; mitk::Annotation::Bounds bounds; AnnotationRankedMap &AnnotationMap = m_AnnotationContainerMap[Right]; for (auto it = AnnotationMap.cbegin(); it != AnnotationMap.cend(); ++it) { Annotation *Annotation = it->second; margin = GetMargin2D(Annotation); bounds = Annotation->GetBoundsOnDisplay(this->GetCurrentBaseRenderer()); posY -= bounds.Size[1] + margin[1]; bounds.Position[0] = displaySize[0] - (bounds.Size[0] + margin[0]); bounds.Position[1] = posY + margin[1]; Annotation->SetBoundsOnDisplay(this->GetCurrentBaseRenderer(), bounds); } } void LayoutAnnotationRenderer::PrepareLeftLayout(int *displaySize) { double posY; Point2D margin; double height = GetHeight(m_AnnotationContainerMap[Left], GetCurrentBaseRenderer()); posY = (height / 2.0 + displaySize[1]) / 2.0; mitk::Annotation::Bounds bounds; AnnotationRankedMap &AnnotationMap = m_AnnotationContainerMap[Left]; for (auto it = AnnotationMap.cbegin(); it != AnnotationMap.cend(); ++it) { Annotation *Annotation = it->second; margin = GetMargin2D(Annotation); bounds = Annotation->GetBoundsOnDisplay(this->GetCurrentBaseRenderer()); posY -= bounds.Size[1] + margin[1]; bounds.Position[0] = margin[0]; bounds.Position[1] = posY; Annotation->SetBoundsOnDisplay(this->GetCurrentBaseRenderer(), bounds); } } void LayoutAnnotationRenderer::PrepareBottomLeftLayout(int * /*displaySize*/) { double posX, posY; Point2D margin; posX = 0; posY = 0; mitk::Annotation::Bounds bounds; AnnotationRankedMap &AnnotationMap = m_AnnotationContainerMap[BottomLeft]; for (auto it = AnnotationMap.cbegin(); it != AnnotationMap.cend(); ++it) { Annotation *Annotation = it->second; margin = GetMargin2D(Annotation); bounds = Annotation->GetBoundsOnDisplay(this->GetCurrentBaseRenderer()); bounds.Position[0] = posX + margin[0]; bounds.Position[1] = posY + margin[1]; Annotation->SetBoundsOnDisplay(this->GetCurrentBaseRenderer(), bounds); posY += bounds.Size[1] + margin[1]; } } void LayoutAnnotationRenderer::PrepareBottomLayout(int *displaySize) { double posX, posY; Point2D margin; posX = 0; posY = 0; mitk::Annotation::Bounds bounds; AnnotationRankedMap &AnnotationMap = m_AnnotationContainerMap[Bottom]; for (auto it = AnnotationMap.cbegin(); it != AnnotationMap.cend(); ++it) { Annotation *Annotation = it->second; margin = GetMargin2D(Annotation); bounds = Annotation->GetBoundsOnDisplay(this->GetCurrentBaseRenderer()); posX = displaySize[0] / 2 - bounds.Size[0] / 2; bounds.Position[0] = posX; bounds.Position[1] = posY + margin[1]; Annotation->SetBoundsOnDisplay(this->GetCurrentBaseRenderer(), bounds); posY += bounds.Size[1] + margin[1]; } } void LayoutAnnotationRenderer::PrepareBottomRightLayout(int *displaySize) { double posX, posY; Point2D margin; posX = 0; posY = 0; mitk::Annotation::Bounds bounds; AnnotationRankedMap &AnnotationMap = m_AnnotationContainerMap[BottomRight]; for (auto it = AnnotationMap.cbegin(); it != AnnotationMap.cend(); ++it) { Annotation *Annotation = it->second; margin = GetMargin2D(Annotation); bounds = Annotation->GetBoundsOnDisplay(this->GetCurrentBaseRenderer()); posX = displaySize[0] - (bounds.Size[0] + margin[0]); bounds.Position[0] = posX; bounds.Position[1] = posY + margin[1]; Annotation->SetBoundsOnDisplay(this->GetCurrentBaseRenderer(), bounds); posY += bounds.Size[1] + margin[1]; } } double LayoutAnnotationRenderer::GetHeight(AnnotationRankedMap &annotations, BaseRenderer *renderer) { double height = 0; for (auto it = annotations.cbegin(); it != annotations.cend(); ++it) { Annotation *annotation = it->second; Annotation::Bounds bounds = annotation->GetBoundsOnDisplay(renderer); height += bounds.Size[0]; height += GetMargin2D(annotation)[0]; } return height; } } diff --git a/Modules/BoundingShape/src/Interactions/mitkBoundingShapeInteractor.cpp b/Modules/BoundingShape/src/Interactions/mitkBoundingShapeInteractor.cpp index 73fa0190cd..6b7c381207 100644 --- a/Modules/BoundingShape/src/Interactions/mitkBoundingShapeInteractor.cpp +++ b/Modules/BoundingShape/src/Interactions/mitkBoundingShapeInteractor.cpp @@ -1,613 +1,613 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "../DataManagement/mitkBoundingShapeUtil.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include "usGetModuleContext.h" #include "usModuleRegistry.h" // Properties to allow the user to interact with the base data const char *selectedColorPropertyName = "Bounding Shape.Selected Color"; const char *deselectedColorPropertyName = "Bounding Shape.Deselected Color"; const char *activeHandleIdPropertyName = "Bounding Shape.Active Handle ID"; const char *boundingShapePropertyName = "Bounding Shape"; namespace mitk { class BoundingShapeInteractor::Impl { public: Impl() : ScrollEnabled(false), RotationEnabled(false) { Point3D initialPoint; initialPoint.Fill(0.0); for (int i = 0; i < 6; ++i) Handles.push_back(Handle(initialPoint, i, GetHandleIndices(i))); } ~Impl() {} bool ScrollEnabled; Point3D InitialPickedWorldPoint; Point3D LastPickedWorldPoint; Point2D InitialPickedDisplayPoint; std::vector Handles; Handle ActiveHandle; Geometry3D::Pointer OriginalGeometry; bool RotationEnabled; std::map DisplayInteractorConfigs; }; } mitk::BoundingShapeInteractor::BoundingShapeInteractor() : m_Impl(new Impl) { } mitk::BoundingShapeInteractor::~BoundingShapeInteractor() { this->RestoreNodeProperties(); delete m_Impl; } void mitk::BoundingShapeInteractor::ConnectActionsAndFunctions() { // **Conditions** that can be used in the state machine, to ensure that certain conditions are met, before actually // executing an action CONNECT_CONDITION("isHoveringOverObject", CheckOverObject); CONNECT_CONDITION("isHoveringOverHandles", CheckOverHandles); // **Function** in the statemachine patterns also referred to as **Actions** CONNECT_FUNCTION("selectObject", SelectObject); CONNECT_FUNCTION("deselectObject", DeselectObject); CONNECT_FUNCTION("deselectHandles", DeselectHandles); CONNECT_FUNCTION("initInteraction", InitInteraction); CONNECT_FUNCTION("translateObject", TranslateObject); CONNECT_FUNCTION("selectHandle", SelectHandle); CONNECT_FUNCTION("scaleObject", ScaleObject); // CONNECT_FUNCTION("rotateObject",RotateObject); } // RotateObject(StateMachineAction*, InteractionEvent* interactionEvent) // void mitk::BoundingShapeInteractor::RotateGeometry(mitk::ScalarType angle, int rotationaxis, mitk::BaseGeometry* // geometry) //{ // mitk::Vector3D rotationAxis = geometry->GetAxisVector(rotationaxis); // float pointX = 0.0f; // float pointY = 0.0f; // float pointZ = 0.0f; // mitk::Point3D pointOfRotation; // pointOfRotation.Fill(0.0); // this->GetDataNode()->GetFloatProperty(anchorPointX, pointX); // this->GetDataNode()->GetFloatProperty(anchorPointY, pointY); // this->GetDataNode()->GetFloatProperty(anchorPointZ, pointZ); // pointOfRotation[0] = pointX; // pointOfRotation[1] = pointY; // pointOfRotation[2] = pointZ; // // mitk::RotationOperation* doOp = new mitk::RotationOperation(OpROTATE, pointOfRotation, rotationAxis, angle); // // geometry->ExecuteOperation(doOp); // delete doOp; //} void mitk::BoundingShapeInteractor::SetRotationEnabled(bool rotationEnabled) { m_Impl->RotationEnabled = rotationEnabled; } void mitk::BoundingShapeInteractor::DataNodeChanged() { mitk::DataNode::Pointer newInputNode = this->GetDataNode(); if (newInputNode == nullptr) return; // add color properties mitk::ColorProperty::Pointer selectedColor = dynamic_cast(newInputNode->GetProperty(selectedColorPropertyName)); mitk::ColorProperty::Pointer deselectedColor = dynamic_cast(newInputNode->GetProperty(deselectedColorPropertyName)); if (selectedColor.IsNull()) newInputNode->AddProperty(selectedColorPropertyName, mitk::ColorProperty::New(0.0, 1.0, 0.0)); if (deselectedColor.IsNull()) newInputNode->AddProperty(deselectedColorPropertyName, mitk::ColorProperty::New(1.0, 1.0, 1.0)); newInputNode->SetProperty(boundingShapePropertyName, mitk::BoolProperty::New(true)); newInputNode->AddProperty(activeHandleIdPropertyName, mitk::IntProperty::New(-1)); newInputNode->SetProperty("layer", mitk::IntProperty::New(101)); newInputNode->SetBoolProperty("fixedLayer", mitk::BoolProperty::New(true)); newInputNode->SetBoolProperty("pickable", true); mitk::ColorProperty::Pointer initialColor = dynamic_cast(newInputNode->GetProperty(deselectedColorPropertyName)); if (initialColor.IsNotNull()) { newInputNode->SetColor(initialColor->GetColor()); } mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } void mitk::BoundingShapeInteractor::HandlePositionChanged(const InteractionEvent *interactionEvent, Point3D ¢er) { GeometryData::Pointer geometryData = dynamic_cast(this->GetDataNode()->GetData()); int timeStep = interactionEvent->GetSender()->GetTimeStep(this->GetDataNode()->GetData()); mitk::BaseGeometry::Pointer geometry = geometryData->GetGeometry(timeStep); std::vector cornerPoints = GetCornerPoints(geometry, true); if (m_Impl->Handles.size() == 6) { // set handle positions Point3D pointLeft = CalcAvgPoint(cornerPoints[5], cornerPoints[6]); Point3D pointRight = CalcAvgPoint(cornerPoints[1], cornerPoints[2]); Point3D pointTop = CalcAvgPoint(cornerPoints[0], cornerPoints[6]); Point3D pointBottom = CalcAvgPoint(cornerPoints[7], cornerPoints[1]); Point3D pointFront = CalcAvgPoint(cornerPoints[2], cornerPoints[7]); Point3D pointBack = CalcAvgPoint(cornerPoints[4], cornerPoints[1]); m_Impl->Handles[0].SetPosition(pointLeft); m_Impl->Handles[1].SetPosition(pointRight); m_Impl->Handles[2].SetPosition(pointTop); m_Impl->Handles[3].SetPosition(pointBottom); m_Impl->Handles[4].SetPosition(pointFront); m_Impl->Handles[5].SetPosition(pointBack); // calculate center based on half way of the distance between two opposing cornerpoints center = CalcAvgPoint(cornerPoints[7], cornerPoints[0]); } } void mitk::BoundingShapeInteractor::SetDataNode(DataNode *node) { this->RestoreNodeProperties(); // if there is another node set, restore it's color if (node == nullptr) return; DataInteractor::SetDataNode(node); // calls DataNodeChanged internally this->DataNodeChanged(); } bool mitk::BoundingShapeInteractor::CheckOverObject(const InteractionEvent *interactionEvent) { - const InteractionPositionEvent *positionEvent = dynamic_cast(interactionEvent); + const auto *positionEvent = dynamic_cast(interactionEvent); if (positionEvent == nullptr) return false; GeometryData::Pointer geometryData = dynamic_cast(this->GetDataNode()->GetData()); int timeStep = interactionEvent->GetSender()->GetTimeStep(this->GetDataNode()->GetData()); BaseGeometry::Pointer geometry = geometryData->GetGeometry(timeStep); // calculates translation based on offset+extent not on the transformation matrix (because the cube is located in the // center not in the origin) vtkSmartPointer imageTransform = geometry->GetVtkTransform()->GetMatrix(); Point3D center = geometry->GetCenter(); auto translation = vtkSmartPointer::New(); auto transform = vtkSmartPointer::New(); translation->Translate(center[0] - imageTransform->GetElement(0, 3), center[1] - imageTransform->GetElement(1, 3), center[2] - imageTransform->GetElement(2, 3)); transform->SetMatrix(imageTransform); transform->PostMultiply(); transform->Concatenate(translation); transform->Update(); mitk::Vector3D extent; for (unsigned int i = 0; i < 3; ++i) extent[i] = (geometry->GetExtent(i)); Point3D currentWorldPosition; Point2D currentDisplayPosition = positionEvent->GetPointerPositionOnScreen(); interactionEvent->GetSender()->DisplayToWorld(currentDisplayPosition, currentWorldPosition); ScalarType transformedPosition[4]; transformedPosition[0] = currentWorldPosition[0]; transformedPosition[1] = currentWorldPosition[1]; transformedPosition[2] = currentWorldPosition[2]; transformedPosition[3] = 1; // transform point from world to object coordinates transform->GetInverse()->TransformPoint(transformedPosition, transformedPosition); // check if the world point is within bounds bool isInside = (transformedPosition[0] >= (-extent[0] / 2.0)) && (transformedPosition[0] <= (extent[0] / 2.0)) && (transformedPosition[1] >= (-extent[1] / 2.0)) && (transformedPosition[1] <= (extent[1] / 2.0)) && (transformedPosition[2] >= (-extent[2] / 2.0)) && (transformedPosition[2] <= (extent[2] / 2.0)); return isInside; } bool mitk::BoundingShapeInteractor::CheckOverHandles(const InteractionEvent *interactionEvent) { Point3D boundingBoxCenter; HandlePositionChanged(interactionEvent, boundingBoxCenter); - const InteractionPositionEvent *positionEvent = dynamic_cast(interactionEvent); + const auto *positionEvent = dynamic_cast(interactionEvent); if (positionEvent == nullptr) return false; Point2D displayCenterPoint; // to do: change to actual time step (currently not necessary because geometry remains the same for each timestep int timeStep = 0; GeometryData::Pointer geometryData = dynamic_cast(this->GetDataNode()->GetData()); BaseGeometry::Pointer geometry = geometryData->GetUpdatedTimeGeometry()->GetGeometryForTimeStep(timeStep); std::vector cornerPoints = GetCornerPoints(geometry, true); interactionEvent->GetSender()->WorldToDisplay(boundingBoxCenter, displayCenterPoint); double scale = interactionEvent->GetSender()->GetScaleFactorMMPerDisplayUnit(); // GetDisplaySizeInMM mitk::DoubleProperty::Pointer handleSizeProperty = dynamic_cast(this->GetDataNode()->GetProperty("Bounding Shape.Handle Size Factor")); ScalarType initialHandleSize; if (handleSizeProperty != nullptr) initialHandleSize = handleSizeProperty->GetValue(); else initialHandleSize = 1.0 / 40.0; mitk::Point2D displaysize = interactionEvent->GetSender()->GetDisplaySizeInMM(); ScalarType handlesize = ((displaysize[0] + displaysize[1]) / 2.0) * initialHandleSize; unsigned int handleNum = 0; for (auto &handle : m_Impl->Handles) { Point2D centerpoint; interactionEvent->GetSender()->WorldToDisplay(handle.GetPosition(), centerpoint); Point2D currentDisplayPosition = positionEvent->GetPointerPositionOnScreen(); if ((currentDisplayPosition.EuclideanDistanceTo(centerpoint) < (handlesize / scale)) && (currentDisplayPosition.EuclideanDistanceTo(displayCenterPoint) > (handlesize / scale))) // check if mouse is hovering over center point { handle.SetActive(true); m_Impl->ActiveHandle = handle; this->GetDataNode()->GetPropertyList()->SetProperty(activeHandleIdPropertyName, mitk::IntProperty::New(handleNum++)); this->GetDataNode()->GetData()->Modified(); interactionEvent->GetSender()->GetRenderingManager()->RequestUpdateAll(); return true; } else { handleNum++; handle.SetActive(false); } this->GetDataNode()->GetPropertyList()->SetProperty(activeHandleIdPropertyName, mitk::IntProperty::New(-1)); } return false; } void mitk::BoundingShapeInteractor::SelectHandle(StateMachineAction *, InteractionEvent *interactionEvent) { this->DisableCrosshairNavigation(); DataNode::Pointer node = this->GetDataNode(); if (node.IsNull()) return; mitk::ColorProperty::Pointer selectedColor = dynamic_cast(node->GetProperty(deselectedColorPropertyName)); if (selectedColor.IsNotNull()) { this->GetDataNode()->GetPropertyList()->SetProperty("color", selectedColor); } this->GetDataNode()->GetData()->UpdateOutputInformation(); // Geometry is up-to-date this->GetDataNode()->GetData()->Modified(); interactionEvent->GetSender()->GetRenderingManager()->RequestUpdateAll(); return; } void mitk::BoundingShapeInteractor::DeselectHandles(StateMachineAction *, InteractionEvent *interactionEvent) { this->DisableCrosshairNavigation(); DataNode::Pointer node = this->GetDataNode(); if (node.IsNull()) return; this->GetDataNode()->GetPropertyList()->SetProperty(activeHandleIdPropertyName, mitk::IntProperty::New(-1)); this->GetDataNode()->GetData()->UpdateOutputInformation(); // Geometry is up-to-date this->GetDataNode()->GetData()->Modified(); interactionEvent->GetSender()->GetRenderingManager()->RequestUpdateAll(); return; } void mitk::BoundingShapeInteractor::SelectObject(StateMachineAction *, InteractionEvent *) { this->DisableCrosshairNavigation(); // disable crosshair interaction and scolling if user is hovering over the object DataNode::Pointer node = this->GetDataNode(); if (node.IsNull()) return; mitk::ColorProperty::Pointer selectedColor = dynamic_cast(node->GetProperty(selectedColorPropertyName)); if (selectedColor.IsNotNull()) { node->GetPropertyList()->SetProperty("color", selectedColor); } this->GetDataNode()->GetData()->UpdateOutputInformation(); // Geometry is up-to-date this->GetDataNode()->GetData()->Modified(); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); return; } void mitk::BoundingShapeInteractor::DeselectObject(StateMachineAction *, InteractionEvent *interactionEvent) { this->EnableCrosshairNavigation(); // enable crosshair interaction and scolling if user is hovering over the object DataNode::Pointer node = this->GetDataNode(); if (node.IsNull()) return; mitk::ColorProperty::Pointer deselectedColor = dynamic_cast(node->GetProperty(deselectedColorPropertyName)); if (deselectedColor.IsNotNull()) { node->GetPropertyList()->SetProperty("color", deselectedColor); } this->GetDataNode()->GetData()->UpdateOutputInformation(); // Geometry is up-to-date this->GetDataNode()->GetData()->Modified(); interactionEvent->GetSender()->GetRenderingManager()->RequestUpdateAll(); return; } void mitk::BoundingShapeInteractor::InitInteraction(StateMachineAction *, InteractionEvent *interactionEvent) { InitMembers(interactionEvent); } bool mitk::BoundingShapeInteractor::InitMembers(InteractionEvent *interactionEvent) { - InteractionPositionEvent *positionEvent = dynamic_cast(interactionEvent); + auto *positionEvent = dynamic_cast(interactionEvent); if (positionEvent == nullptr) return false; // get initial position coordinates m_Impl->InitialPickedDisplayPoint = positionEvent->GetPointerPositionOnScreen(); m_Impl->InitialPickedWorldPoint = positionEvent->GetPositionInWorld(); m_Impl->LastPickedWorldPoint = positionEvent->GetPositionInWorld(); return true; } void mitk::BoundingShapeInteractor::TranslateObject(StateMachineAction *, InteractionEvent *interactionEvent) { - InteractionPositionEvent *positionEvent = dynamic_cast(interactionEvent); + auto *positionEvent = dynamic_cast(interactionEvent); if (positionEvent == nullptr) return; int timeStep = interactionEvent->GetSender()->GetTimeStep(this->GetDataNode()->GetData()); mitk::BaseGeometry::Pointer geometry = this->GetDataNode()->GetData()->GetUpdatedTimeGeometry()->GetGeometryForTimeStep(timeStep); Vector3D spacing = geometry->GetSpacing(); Point3D currentPickedPoint; interactionEvent->GetSender()->DisplayToWorld(positionEvent->GetPointerPositionOnScreen(), currentPickedPoint); Vector3D interactionMove; // pixel aligned shifting of the bounding box interactionMove[0] = std::round((currentPickedPoint[0] - m_Impl->LastPickedWorldPoint[0]) / spacing[0]) * spacing[0]; interactionMove[1] = std::round((currentPickedPoint[1] - m_Impl->LastPickedWorldPoint[1]) / spacing[1]) * spacing[1]; interactionMove[2] = std::round((currentPickedPoint[2] - m_Impl->LastPickedWorldPoint[2]) / spacing[2]) * spacing[2]; if ((interactionMove[0] + interactionMove[1] + interactionMove[2]) != 0.0) // only update current position if a movement occured { m_Impl->LastPickedWorldPoint = currentPickedPoint; geometry->SetOrigin(geometry->GetOrigin() + interactionMove); this->GetDataNode()->GetData()->UpdateOutputInformation(); // Geometry is up-to-date this->GetDataNode()->GetData()->Modified(); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } return; } void mitk::BoundingShapeInteractor::ScaleObject(StateMachineAction *, InteractionEvent *interactionEvent) { - InteractionPositionEvent *positionEvent = dynamic_cast(interactionEvent); + auto *positionEvent = dynamic_cast(interactionEvent); if (positionEvent == nullptr) return; GeometryData::Pointer geometryData = dynamic_cast(this->GetDataNode()->GetData()); Point3D handlePickedPoint = m_Impl->ActiveHandle.GetPosition(); Point3D currentPickedPoint; interactionEvent->GetSender()->DisplayToWorld(positionEvent->GetPointerPositionOnScreen(), currentPickedPoint); int timeStep = interactionEvent->GetSender()->GetTimeStep(this->GetDataNode()->GetData()); mitk::BaseGeometry::Pointer geometry = geometryData->GetGeometry(timeStep); Vector3D spacing = geometry->GetSpacing(); // pixel aligned bounding box Vector3D interactionMove; interactionMove[0] = (currentPickedPoint[0] - m_Impl->LastPickedWorldPoint[0]); interactionMove[1] = (currentPickedPoint[1] - m_Impl->LastPickedWorldPoint[1]); interactionMove[2] = (currentPickedPoint[2] - m_Impl->LastPickedWorldPoint[2]); std::vector faces = m_Impl->ActiveHandle.GetFaceIndices(); auto pointscontainer = mitk::BoundingBox::PointsContainer::New(); // calculate cornerpoints from geometry plus visualization offset std::vector cornerPoints = GetCornerPoints(geometry, true); unsigned int num = 0; for (auto point : cornerPoints) { pointscontainer->InsertElement(num++, point); } // calculate center based on half way of the distance between two opposing cornerpoints mitk::Point3D center = CalcAvgPoint(cornerPoints[7], cornerPoints[0]); Vector3D faceNormal; faceNormal[0] = handlePickedPoint[0] - center[0]; faceNormal[1] = handlePickedPoint[1] - center[1]; faceNormal[2] = handlePickedPoint[2] - center[2]; Vector3D faceShift = ((faceNormal * interactionMove) / (faceNormal.GetNorm() * faceNormal.GetNorm())) * faceNormal; // calculate cornerpoints from geometry without visualization offset to update actual geometry cornerPoints = GetCornerPoints(geometry, false); num = 0; for (auto point : cornerPoints) { pointscontainer->InsertElement(num++, point); } bool positionChangeThreshold = true; for (int numFaces = 0; numFaces < 8; numFaces++) // estimate the corresponding face and shift its assigned points { if ((numFaces != faces[0]) && (numFaces != faces[1]) && (numFaces != faces[2]) && (numFaces != faces[3])) { Point3D point = pointscontainer->GetElement(numFaces); if (m_Impl->RotationEnabled) // apply if geometry is rotated at a pixel aligned shift is not possible { point[0] += faceShift[0]; point[1] += faceShift[1]; point[2] += faceShift[2]; } else // shift pixelwise { point[0] += std::round(faceShift[0] / spacing[0]) * spacing[0]; point[1] += std::round(faceShift[1] / spacing[1]) * spacing[1]; point[2] += std::round(faceShift[2] / spacing[2]) * spacing[2]; } if (point == pointscontainer->GetElement(numFaces)) positionChangeThreshold = false; else m_Impl->LastPickedWorldPoint = point; pointscontainer->InsertElement(numFaces, point); } } if (positionChangeThreshold) // update only if bounding box is shifted at least by one pixel { auto inverse = mitk::AffineTransform3D::New(); geometry->GetIndexToWorldTransform()->GetInverse(inverse); for (unsigned int pointid = 0; pointid < 8; pointid++) { pointscontainer->InsertElement(pointid, inverse->TransformPoint(pointscontainer->GetElement(pointid))); } auto bbox = mitk::BoundingBox::New(); bbox->SetPoints(pointscontainer); bbox->ComputeBoundingBox(); mitk::Point3D BBmin = bbox->GetMinimum(); mitk::Point3D BBmax = bbox->GetMaximum(); if (std::abs(BBmin[0] - BBmax[0]) > 0.01 && std::abs(BBmin[1] - BBmax[1]) > 0.01 && std::abs(BBmin[2] - BBmax[2]) > 0.01) // TODO: check if the extent is greater than zero { geometry->SetBounds(bbox->GetBounds()); geometry->Modified(); this->GetDataNode()->GetData()->UpdateOutputInformation(); // Geometry is up-to-date this->GetDataNode()->GetData()->Modified(); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } } return; } void mitk::BoundingShapeInteractor::RestoreNodeProperties() { mitk::DataNode::Pointer inputNode = this->GetDataNode(); if (inputNode.IsNull()) return; mitk::ColorProperty::Pointer color = (mitk::ColorProperty::New(1.0, 1.0, 1.0)); if (color.IsNotNull()) { inputNode->GetPropertyList()->SetProperty("color", color); } inputNode->SetProperty("layer", mitk::IntProperty::New(99)); inputNode->SetProperty(boundingShapePropertyName, mitk::BoolProperty::New(false)); inputNode->GetPropertyList()->DeleteProperty(activeHandleIdPropertyName); EnableCrosshairNavigation(); // update rendering mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } void mitk::BoundingShapeInteractor::EnableCrosshairNavigation() { // enable the crosshair navigation // Re-enabling InteractionEventObservers that have been previously disabled for legacy handling of Tools // in new interaction framework - for (std::map::iterator it = m_Impl->DisplayInteractorConfigs.begin(); + for (auto it = m_Impl->DisplayInteractorConfigs.begin(); it != m_Impl->DisplayInteractorConfigs.end(); ++it) { if (it->first) { mitk::DisplayInteractor *displayInteractor = static_cast( us::GetModuleContext()->GetService(it->first)); if (displayInteractor != nullptr) { // here the regular configuration is loaded again displayInteractor->SetEventConfig(it->second); // MITK_INFO << "restore config"; } } } m_Impl->DisplayInteractorConfigs.clear(); m_Impl->ScrollEnabled = true; } void mitk::BoundingShapeInteractor::DisableCrosshairNavigation() { // dont deactivate twice, else we will clutter the config list ... if (m_Impl->ScrollEnabled == false) return; // As a legacy solution the display interaction of the new interaction framework is disabled here to avoid conflicts // with tools // Note: this only affects InteractionEventObservers (formerly known as Listeners) all DataNode specific interaction // will still be enabled m_Impl->DisplayInteractorConfigs.clear(); std::vector> listEventObserver = us::GetModuleContext()->GetServiceReferences(); - for (std::vector>::iterator it = listEventObserver.begin(); + for (auto it = listEventObserver.begin(); it != listEventObserver.end(); ++it) { - mitk::DisplayInteractor *displayInteractor = + auto *displayInteractor = dynamic_cast(us::GetModuleContext()->GetService(*it)); if (displayInteractor != nullptr) { // remember the original configuration m_Impl->DisplayInteractorConfigs.insert(std::make_pair(*it, displayInteractor->GetEventConfig())); // here the alternative configuration is loaded displayInteractor->SetEventConfig("DisplayConfigMITKNoCrosshair.xml"); // MITK_INFO << "change config"; } } m_Impl->ScrollEnabled = false; } diff --git a/Modules/BoundingShape/src/Rendering/mitkBoundingShapeVtkMapper3D.cpp b/Modules/BoundingShape/src/Rendering/mitkBoundingShapeVtkMapper3D.cpp index 398eb33a23..e80ef1798b 100644 --- a/Modules/BoundingShape/src/Rendering/mitkBoundingShapeVtkMapper3D.cpp +++ b/Modules/BoundingShape/src/Rendering/mitkBoundingShapeVtkMapper3D.cpp @@ -1,339 +1,339 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkBoundingShapeVtkMapper3D.h" #include "../DataManagement/mitkBoundingShapeUtil.h" #include #include #include #include #include #include #include #include #include #include namespace mitk { class BoundingShapeVtkMapper3D::Impl { class LocalStorage : public Mapper::BaseLocalStorage { public: LocalStorage(); ~LocalStorage(); LocalStorage(const LocalStorage &) = delete; LocalStorage &operator=(const LocalStorage &) = delete; std::vector> Handles; vtkSmartPointer Actor; vtkSmartPointer HandleActor; vtkSmartPointer SelectedHandleActor; vtkSmartPointer PropAssembly; }; public: Impl() : DistanceFromCam(1.0) { Point3D initialPoint; initialPoint.Fill(0); for (int i = 0; i < 6; ++i) HandlePropertyList.push_back(Handle(initialPoint, i, GetHandleIndices(i))); } double DistanceFromCam; std::vector HandlePropertyList; mitk::LocalStorageHandler LocalStorageHandler; }; } mitk::BoundingShapeVtkMapper3D::Impl::LocalStorage::LocalStorage() : Actor(vtkSmartPointer::New()), HandleActor(vtkSmartPointer::New()), SelectedHandleActor(vtkSmartPointer::New()), PropAssembly(vtkSmartPointer::New()) { for (int i = 0; i < 6; i++) Handles.push_back(vtkSmartPointer::New()); } mitk::BoundingShapeVtkMapper3D::Impl::LocalStorage::~LocalStorage() { } void mitk::BoundingShapeVtkMapper3D::SetDefaultProperties(DataNode *node, BaseRenderer *renderer, bool overwrite) { Superclass::SetDefaultProperties(node, renderer, overwrite); } mitk::BoundingShapeVtkMapper3D::BoundingShapeVtkMapper3D() : m_Impl(new Impl) { } mitk::BoundingShapeVtkMapper3D::~BoundingShapeVtkMapper3D() { delete m_Impl; } void mitk::BoundingShapeVtkMapper3D::ApplyColorAndOpacityProperties(BaseRenderer*, vtkActor*) { //Superclass::ApplyColorAndOpacityProperties(renderer, actor); } void mitk::BoundingShapeVtkMapper3D::ApplyBoundingShapeProperties(BaseRenderer *renderer, vtkActor *actor) { if (actor == nullptr) return; auto dataNode = this->GetDataNode(); if (dataNode == nullptr) return; bool isVisible = false; dataNode->GetBoolProperty("Bounding Shape.3D Rendering", isVisible, renderer); actor->SetVisibility(isVisible); float lineWidth = 1.0f; dataNode->GetFloatProperty("Bounding Shape.Line.Width", lineWidth, renderer); auto property = actor->GetProperty(); property->SetLineWidth(lineWidth); } void mitk::BoundingShapeVtkMapper3D::GenerateDataForRenderer(BaseRenderer *renderer) { auto dataNode = this->GetDataNode(); if (dataNode == nullptr) return; vtkCamera *camera = renderer->GetVtkRenderer()->GetActiveCamera(); auto localStorage = m_Impl->LocalStorageHandler.GetLocalStorage(renderer); bool needGenerateData = localStorage->GetLastGenerateDataTime() < dataNode->GetMTime(); double distance = camera->GetDistance(); if (std::abs(distance - m_Impl->DistanceFromCam) > mitk::eps) { m_Impl->DistanceFromCam = distance; needGenerateData = true; } if (needGenerateData) { bool isVisible = true; dataNode->GetVisibility(isVisible, renderer); if (!isVisible) { localStorage->Actor->VisibilityOff(); return; } // set the input-object at time t for the mapper - GeometryData *geometryData = dynamic_cast(dataNode->GetData()); + auto *geometryData = dynamic_cast(dataNode->GetData()); if (geometryData == nullptr) return; mitk::BaseGeometry::Pointer geometry = geometryData->GetGeometry(); mitk::Vector3D spacing = geometry->GetSpacing(); // calculate cornerpoints from geometry std::vector cornerPoints = GetCornerPoints(geometry, true); Point3D p0 = cornerPoints[0]; Point3D p1 = cornerPoints[1]; Point3D p2 = cornerPoints[2]; Point3D p4 = cornerPoints[4]; Point3D extent; extent[0] = sqrt((p0[0] - p4[0]) * (p0[0] - p4[0]) + (p0[1] - p4[1]) * (p0[1] - p4[1]) + (p0[2] - p4[2]) * (p0[2] - p4[2])); extent[1] = sqrt((p0[0] - p2[0]) * (p0[0] - p2[0]) + (p0[1] - p2[1]) * (p0[1] - p2[1]) + (p0[2] - p2[2]) * (p0[2] - p2[2])); extent[2] = sqrt((p0[0] - p1[0]) * (p0[0] - p1[0]) + (p0[1] - p1[1]) * (p0[1] - p1[1]) + (p0[2] - p1[2]) * (p0[2] - p1[2])); // calculate center based on half way of the distance between two opposing cornerpoints mitk::Point3D center = CalcAvgPoint(cornerPoints[7], cornerPoints[0]); if (m_Impl->HandlePropertyList.size() == 6) { // set handle positions Point3D pointLeft = CalcAvgPoint(cornerPoints[5], cornerPoints[6]); Point3D pointRight = CalcAvgPoint(cornerPoints[1], cornerPoints[2]); Point3D pointTop = CalcAvgPoint(cornerPoints[0], cornerPoints[6]); Point3D pointBottom = CalcAvgPoint(cornerPoints[7], cornerPoints[1]); Point3D pointFront = CalcAvgPoint(cornerPoints[2], cornerPoints[7]); Point3D pointBack = CalcAvgPoint(cornerPoints[4], cornerPoints[1]); m_Impl->HandlePropertyList[0].SetPosition(pointLeft); m_Impl->HandlePropertyList[1].SetPosition(pointRight); m_Impl->HandlePropertyList[2].SetPosition(pointTop); m_Impl->HandlePropertyList[3].SetPosition(pointBottom); m_Impl->HandlePropertyList[4].SetPosition(pointFront); m_Impl->HandlePropertyList[5].SetPosition(pointBack); } auto cube = vtkCubeSource::New(); cube->SetXLength(extent[0] / spacing[0]); cube->SetYLength(extent[1] / spacing[1]); cube->SetZLength(extent[2] / spacing[2]); // calculates translation based on offset+extent not on the transformation matrix vtkSmartPointer imageTransform = geometry->GetVtkTransform()->GetMatrix(); auto translation = vtkSmartPointer::New(); translation->Translate(center[0] - imageTransform->GetElement(0, 3), center[1] - imageTransform->GetElement(1, 3), center[2] - imageTransform->GetElement(2, 3)); auto transform = vtkSmartPointer::New(); transform->SetMatrix(imageTransform); transform->PostMultiply(); transform->Concatenate(translation); transform->Update(); cube->Update(); auto transformFilter = vtkSmartPointer::New(); transformFilter->SetInputData(cube->GetOutput()); transformFilter->SetTransform(transform); transformFilter->Update(); cube->Delete(); vtkSmartPointer polydata = transformFilter->GetPolyDataOutput(); if (polydata == nullptr) { localStorage->Actor->VisibilityOff(); return; } mitk::DoubleProperty::Pointer handleSizeProperty = dynamic_cast(this->GetDataNode()->GetProperty("Bounding Shape.Handle Size Factor")); ScalarType initialHandleSize; if (handleSizeProperty != nullptr) initialHandleSize = handleSizeProperty->GetValue(); else initialHandleSize = 1.0 / 40.0; double handlesize = ((camera->GetDistance() * std::tan(vtkMath::RadiansFromDegrees(camera->GetViewAngle()))) / 2.0) * initialHandleSize; if (localStorage->PropAssembly->GetParts()->IsItemPresent(localStorage->HandleActor)) localStorage->PropAssembly->RemovePart(localStorage->HandleActor); if (localStorage->PropAssembly->GetParts()->IsItemPresent(localStorage->Actor)) localStorage->PropAssembly->RemovePart(localStorage->Actor); auto selectedhandlemapper = vtkSmartPointer::New(); auto appendPoly = vtkSmartPointer::New(); mitk::IntProperty::Pointer activeHandleId = dynamic_cast(dataNode->GetProperty("Bounding Shape.Active Handle ID")); int i = 0; for (auto &handle : localStorage->Handles) { Point3D handlecenter = m_Impl->HandlePropertyList[i].GetPosition(); handle->SetCenter(handlecenter[0], handlecenter[1], handlecenter[2]); handle->SetRadius(handlesize); handle->Update(); if (activeHandleId == nullptr) { appendPoly->AddInputConnection(handle->GetOutputPort()); } else { if (activeHandleId->GetValue() != m_Impl->HandlePropertyList[i].GetIndex()) { appendPoly->AddInputConnection(handle->GetOutputPort()); } else { selectedhandlemapper->SetInputData(handle->GetOutput()); localStorage->SelectedHandleActor->SetMapper(selectedhandlemapper); localStorage->SelectedHandleActor->GetProperty()->SetColor(0, 1, 0); localStorage->SelectedHandleActor->GetMapper()->SetInputDataObject(handle->GetOutput()); localStorage->PropAssembly->AddPart(localStorage->SelectedHandleActor); } } i++; } appendPoly->Update(); auto mapper = vtkSmartPointer::New(); mapper->SetInputData(polydata); auto handlemapper = vtkSmartPointer::New(); handlemapper->SetInputData(appendPoly->GetOutput()); localStorage->Actor->SetMapper(mapper); localStorage->Actor->GetMapper()->SetInputDataObject(polydata); localStorage->Actor->GetProperty()->SetOpacity(0.3); mitk::ColorProperty::Pointer selectedColor = dynamic_cast(dataNode->GetProperty("color")); if (selectedColor != nullptr) { mitk::Color color = selectedColor->GetColor(); localStorage->Actor->GetProperty()->SetColor(color[0], color[1], color[2]); } localStorage->HandleActor->SetMapper(handlemapper); if (activeHandleId == nullptr) { localStorage->HandleActor->GetProperty()->SetColor(1, 1, 1); } else { localStorage->HandleActor->GetProperty()->SetColor(1, 0, 0); } localStorage->HandleActor->GetMapper()->SetInputDataObject(appendPoly->GetOutput()); this->ApplyColorAndOpacityProperties(renderer, localStorage->Actor); this->ApplyBoundingShapeProperties(renderer, localStorage->Actor); this->ApplyColorAndOpacityProperties(renderer, localStorage->HandleActor); this->ApplyBoundingShapeProperties(renderer, localStorage->HandleActor); this->ApplyColorAndOpacityProperties(renderer, localStorage->SelectedHandleActor); this->ApplyBoundingShapeProperties(renderer, localStorage->SelectedHandleActor); // apply properties read from the PropertyList // this->ApplyProperties(localStorage->m_Actor, renderer); // this->ApplyProperties(localStorage->m_HandleActor, renderer); // this->ApplyProperties(localStorage->m_SelectedHandleActor, renderer); localStorage->Actor->VisibilityOn(); localStorage->HandleActor->VisibilityOn(); localStorage->SelectedHandleActor->VisibilityOn(); localStorage->PropAssembly->AddPart(localStorage->Actor); localStorage->PropAssembly->AddPart(localStorage->HandleActor); localStorage->PropAssembly->VisibilityOn(); localStorage->UpdateGenerateDataTime(); } } vtkProp *mitk::BoundingShapeVtkMapper3D::GetVtkProp(BaseRenderer *renderer) { return m_Impl->LocalStorageHandler.GetLocalStorage(renderer)->PropAssembly; } diff --git a/Modules/CEST/src/mitkCustomTagParser.cpp b/Modules/CEST/src/mitkCustomTagParser.cpp index 3df7ce9892..0420b8d750 100644 --- a/Modules/CEST/src/mitkCustomTagParser.cpp +++ b/Modules/CEST/src/mitkCustomTagParser.cpp @@ -1,684 +1,684 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkCustomTagParser.h" #include #include #include #include "mitkIPropertyPersistence.h" #include "usGetModuleContext.h" #include "usModule.h" #include "usModuleContext.h" #include "usModuleResource.h" #include "usModuleResourceStream.h" #include #include #include #include #include #include #include #include namespace { mitk::IPropertyPersistence *GetPersistenceService() { mitk::IPropertyPersistence *result = nullptr; std::vector> persRegisters = us::GetModuleContext()->GetServiceReferences(); if (!persRegisters.empty()) { if (persRegisters.size() > 1) { MITK_WARN << "Multiple property description services found. Using just one."; } result = us::GetModuleContext()->GetService(persRegisters.front()); } return result; }; } const std::string mitk::CustomTagParser::m_CESTPropertyPrefix = "CEST."; const std::string mitk::CustomTagParser::m_OffsetsPropertyName = m_CESTPropertyPrefix + "Offsets"; const std::string mitk::CustomTagParser::m_RevisionPropertyName = m_CESTPropertyPrefix + "Revision"; const std::string mitk::CustomTagParser::m_JSONRevisionPropertyName = m_CESTPropertyPrefix + "revision_json"; const std::string mitk::CustomTagParser::m_RevisionIndependentMapping = "\n" " \"sProtConsistencyInfo.tSystemType\" : \"SysType\",\n" " \"sProtConsistencyInfo.flNominalB0\" : \"NominalB0\",\n" " \"sTXSPEC.asNucleusInfo[0].lFrequency\" : \"FREQ\",\n" " \"sTXSPEC.asNucleusInfo[0].flReferenceAmplitude\" : \"RefAmp\",\n" " \"alTR[0]\" : \"TR\",\n" " \"alTE[0]\" : \"TE\",\n" " \"lAverages\" : \"averages\",\n" " \"lRepetitions\" : \"repetitions\",\n" " \"adFlipAngleDegree[0]\" : \"ImageFlipAngle\",\n" " \"lTotalScanTimeSec\" : \"TotalScanTime\","; const std::string mitk::CustomTagParser::m_DefaultJsonString = "{\n" " \"default mapping, corresponds to revision 1416\" : \"revision_json\",\n" " \"sWiPMemBlock.alFree[1]\" : \"AdvancedMode\",\n" " \"sWiPMemBlock.alFree[2]\" : \"RecoveryMode\",\n" " \"sWiPMemBlock.alFree[3]\" : \"DoubleIrrMode\",\n" " \"sWiPMemBlock.alFree[4]\" : \"BinomMode\",\n" " \"sWiPMemBlock.alFree[5]\" : \"MtMode\",\n" " \"sWiPMemBlock.alFree[6]\" : \"PreparationType\",\n" " \"sWiPMemBlock.alFree[7]\" : \"PulseType\",\n" " \"sWiPMemBlock.alFree[8]\" : \"SamplingType\",\n" " \"sWiPMemBlock.alFree[9]\" : \"SpoilingType\",\n" " \"sWiPMemBlock.alFree[10]\" : \"measurements\",\n" " \"sWiPMemBlock.alFree[11]\" : \"NumberOfPulses\",\n" " \"sWiPMemBlock.alFree[12]\" : \"NumberOfLockingPulses\",\n" " \"sWiPMemBlock.alFree[13]\" : \"PulseDuration\",\n" " \"sWiPMemBlock.alFree[14]\" : \"DutyCycle\",\n" " \"sWiPMemBlock.alFree[15]\" : \"RecoveryTime\",\n" " \"sWiPMemBlock.alFree[16]\" : \"RecoveryTimeM0\",\n" " \"sWiPMemBlock.alFree[17]\" : \"ReadoutDelay\",\n" " \"sWiPMemBlock.alFree[18]\" : \"BinomDuration\",\n" " \"sWiPMemBlock.alFree[19]\" : \"BinomDistance\",\n" " \"sWiPMemBlock.alFree[20]\" : \"BinomNumberofPulses\",\n" " \"sWiPMemBlock.alFree[21]\" : \"BinomPreRepetions\",\n" " \"sWiPMemBlock.alFree[22]\" : \"BinomType\",\n" " \"sWiPMemBlock.adFree[1]\" : \"Offset\",\n" " \"sWiPMemBlock.adFree[2]\" : \"B1Amplitude\",\n" " \"sWiPMemBlock.adFree[3]\" : \"AdiabaticPulseMu\",\n" " \"sWiPMemBlock.adFree[4]\" : \"AdiabaticPulseBW\",\n" " \"sWiPMemBlock.adFree[5]\" : \"AdiabaticPulseLength\",\n" " \"sWiPMemBlock.adFree[6]\" : \"AdiabaticPulseAmp\",\n" " \"sWiPMemBlock.adFree[7]\" : \"FermiSlope\",\n" " \"sWiPMemBlock.adFree[8]\" : \"FermiFWHM\",\n" " \"sWiPMemBlock.adFree[9]\" : \"DoubleIrrDuration\",\n" " \"sWiPMemBlock.adFree[10]\" : \"DoubleIrrAmplitude\",\n" " \"sWiPMemBlock.adFree[11]\" : \"DoubleIrrRepetitions\",\n" " \"sWiPMemBlock.adFree[12]\" : \"DoubleIrrPreRepetitions\"\n" "}"; mitk::CustomTagParser::CustomTagParser(std::string relevantFile) : m_ClosestInternalRevision(""), m_ClosestExternalRevision("") { std::string pathToDirectory; std::string fileName; itksys::SystemTools::SplitProgramPath(relevantFile, pathToDirectory, fileName); m_DicomDataPath = pathToDirectory; m_ParseStrategy = "Automatic"; m_RevisionMappingStrategy = "Fuzzy"; } mitk::PropertyList::Pointer mitk::CustomTagParser::ParseDicomPropertyString(std::string dicomPropertyString) { auto results = mitk::PropertyList::New(); if ("" == dicomPropertyString) { MITK_ERROR << "Could not parse empty custom dicom string"; return results; } std::map privateParameters; // convert hex to ascii // the Siemens private tag contains the information like this // "43\52\23\34" we jump over each \ and convert the number int len = dicomPropertyString.length(); std::string asciiString; for (int i = 0; i < len; i += 3) { std::string byte = dicomPropertyString.substr(i, 2); - char chr = (char)(int)strtol(byte.c_str(), nullptr, 16); + auto chr = (char)(int)strtol(byte.c_str(), nullptr, 16); asciiString.push_back(chr); } // extract parameter list std::size_t beginning = asciiString.find("### ASCCONV BEGIN ###") + 21; std::size_t ending = asciiString.find("### ASCCONV END ###"); std::string parameterListString = asciiString.substr(beginning, ending - beginning); boost::replace_all(parameterListString, "\r\n", "\n"); boost::char_separator newlineSeparator("\n"); boost::tokenizer> parameters(parameterListString, newlineSeparator); for (const auto ¶meter : parameters) { std::vector parts; boost::split(parts, parameter, boost::is_any_of("=")); if (parts.size() == 2) { parts[0].erase(std::remove(parts[0].begin(), parts[0].end(), ' '), parts[0].end()); parts[1].erase(parts[1].begin(), parts[1].begin() + 1); // first character is a space privateParameters[parts[0]] = parts[1]; } } // determine what revision we are using to handle parameters appropriately std::string revisionPrefix = "CEST_Rev"; std::string lowerRevisionPrefix = revisionPrefix; std::string lowerParameter = privateParameters["tSequenceFileName"]; std::transform(lowerRevisionPrefix.begin(), lowerRevisionPrefix.end(), lowerRevisionPrefix.begin(), ::tolower ); std::transform(lowerParameter.begin(), lowerParameter.end(), lowerParameter.begin(), ::tolower); std::size_t foundPosition = lowerParameter.find(lowerRevisionPrefix); if (foundPosition == std::string::npos) { MITK_ERROR << "Could not find revision information."; return results; } std::string revisionString = privateParameters["tSequenceFileName"].substr(foundPosition + revisionPrefix.length(), std::string::npos); std::size_t firstNonNumber = revisionString.find_first_not_of("0123456789"); revisionString.erase(firstNonNumber, std::string::npos); results->SetProperty(m_RevisionPropertyName, mitk::StringProperty::New(revisionString)); std::string jsonString = GetRevisionAppropriateJSONString(revisionString); boost::property_tree::ptree root; std::istringstream jsonStream(jsonString); try { boost::property_tree::read_json(jsonStream, root); } catch (boost::property_tree::json_parser_error e) { mitkThrow() << "Could not parse json file. Error was:\n" << e.what(); } for (auto it : root) { if (it.second.empty()) { std::string propertyName = m_CESTPropertyPrefix + it.second.data(); if (m_JSONRevisionPropertyName == propertyName) { results->SetProperty(propertyName, mitk::StringProperty::New(it.first)); } else { results->SetProperty(propertyName, mitk::StringProperty::New(privateParameters[it.first])); } } else { MITK_ERROR << "Currently no support for nested dicom tag descriptors in json file."; } } std::string sampling = ""; std::string offset = ""; std::string measurements = ""; bool hasSamplingInformation = results->GetStringProperty("CEST.SamplingType", sampling); results->GetStringProperty("CEST.Offset", offset); results->GetStringProperty("CEST.measurements", measurements); if ("" == measurements) { std::string stringRepetitions = ""; std::string stringAverages = ""; results->GetStringProperty("CEST.repetitions", stringRepetitions); results->GetStringProperty("CEST.averages", stringAverages); std::stringstream measurementStream; try { measurementStream << std::stoi(stringRepetitions) + std::stoi(stringAverages); measurements = measurementStream.str(); MITK_INFO << "Could not find measurements, assuming repetitions + averages. Which is: " << measurements; } catch (const std::invalid_argument &ia) { MITK_ERROR << "Could not find measurements, fallback assumption of repetitions + averages could not be determined either: " << ia.what(); } } std::string preparationType = ""; std::string recoveryMode = ""; results->GetStringProperty("CEST.PreparationType", preparationType); results->GetStringProperty("CEST.RecoveryMode", recoveryMode); if ((("T1Recovery" == preparationType) || ("T1Inversion" == preparationType) || ("1" == recoveryMode) || ("T1" == m_ParseStrategy)) && !("CEST/WASABI" == m_ParseStrategy)) { MITK_INFO << "Parsed as T1 image"; mitk::LocaleSwitch localeSwitch("C"); std::stringstream trecStream; std::string trecPath = m_DicomDataPath + "/TREC.txt"; std::ifstream list(trecPath.c_str()); if (list.good()) { std::string currentTime; while (std::getline(list, currentTime)) { trecStream << currentTime << " "; } } else { MITK_WARN << "Assumed T1, but could not load TREC at " << trecPath; } results->SetStringProperty("CEST.TREC", trecStream.str().c_str()); } else { MITK_INFO << "Parsed as CEST or WASABI image"; } if (hasSamplingInformation) { std::string offsets = GetOffsetString(sampling, offset, measurements); results->SetStringProperty(m_OffsetsPropertyName.c_str(), offsets.c_str()); } else { MITK_WARN << "Could not determine sampling type."; } //persist all properties mitk::IPropertyPersistence *persSrv = GetPersistenceService(); if (persSrv) { auto propertyMap = results->GetMap(); for (auto const &prop : *propertyMap) { PropertyPersistenceInfo::Pointer info = PropertyPersistenceInfo::New(); std::string key = prop.first; std::replace(key.begin(), key.end(), '.', '_'); info->SetNameAndKey(prop.first, key); persSrv->AddInfo(info); } } return results; } mitk::PropertyList::Pointer mitk::CustomTagParser::ParseDicomProperty(mitk::TemporoSpatialStringProperty *dicomProperty) { if (!dicomProperty) { MITK_ERROR << "DICOM property empty"; } auto results = mitk::PropertyList::New(); if (dicomProperty) { results = ParseDicomPropertyString(dicomProperty->GetValue()); } return results; } std::vector mitk::CustomTagParser::GetInternalRevisions() { const std::vector configs = us::GetModuleContext()->GetModule()->FindResources("/", "*.json", false); std::vector availableRevisionsVector; for (auto const resource : configs) { availableRevisionsVector.push_back(std::stoi(resource.GetBaseName())); } return availableRevisionsVector; } std::vector mitk::CustomTagParser::GetExternalRevisions() { std::string stringToJSONDirectory = GetExternalJSONDirectory(); std::string prospectiveJsonsPath = stringToJSONDirectory + "/*.json"; std::set JsonFiles; Poco::Glob::glob(prospectiveJsonsPath, JsonFiles, Poco::Glob::GLOB_CASELESS); std::vector availableRevisionsVector; for (auto const jsonpath : JsonFiles) { std::string jsonDir; std::string jsonName; itksys::SystemTools::SplitProgramPath(jsonpath, jsonDir, jsonName); std::string revision = itksys::SystemTools::GetFilenameWithoutExtension(jsonName); // disregard jsons which contain letters in their name bool onlyNumbers = (revision.find_first_not_of("0123456789") == std::string::npos); if(onlyNumbers) { availableRevisionsVector.push_back(std::stoi(revision)); } } return availableRevisionsVector; } std::string mitk::CustomTagParser::GetClosestLowerRevision(std::string revisionString, std::vector availableRevisionsVector) { // descending order std::sort(availableRevisionsVector.begin(), availableRevisionsVector.end(), std::greater<>()); int revision = std::stoi(revisionString); int index = 0; int numberOfRevisions = availableRevisionsVector.size(); while (index < numberOfRevisions) { // current mapping still has a higher revision number if ((availableRevisionsVector[index] - revision) > 0) { ++index; } else { break; } } if (index < numberOfRevisions) { std::stringstream foundRevisionStream; foundRevisionStream << availableRevisionsVector[index]; return foundRevisionStream.str(); } return ""; } void mitk::CustomTagParser::GetClosestLowerRevision(std::string revisionString) { m_ClosestInternalRevision = GetClosestLowerRevision(revisionString, GetInternalRevisions()); m_ClosestExternalRevision = GetClosestLowerRevision(revisionString, GetExternalRevisions()); if ("Strict" == m_RevisionMappingStrategy && !((0 == m_ClosestInternalRevision.compare(revisionString)) || (0 == m_ClosestExternalRevision.compare(revisionString)))) { // strict revision mapping and neither revision does match the dicom meta data std::stringstream errorMessageStream; errorMessageStream << "\nCould not parse dicom data in strict mode, data revision " << revisionString << " has no known matching parameter mapping. To use the closest known older parameter mapping select the " << "\"Fuzzy\" revision mapping option when loading the data.\n" << "\nCurrently known revision mappings are:\n Precompiled:"; for (const auto revision : GetInternalRevisions()) { errorMessageStream << " " << revision; } errorMessageStream << "\n External:"; for (const auto revision : GetExternalRevisions()) { errorMessageStream << " " << revision; } errorMessageStream << "\n\nExternal revision mapping descriptions should be located at\n\n"; std::string stringToJSONDirectory = GetExternalJSONDirectory(); errorMessageStream << stringToJSONDirectory; errorMessageStream << "\n\nTo provide an external mapping for this revision create a " << revisionString << ".json there. You might need to create the directory first."; mitkThrow() << errorMessageStream.str(); } } std::string mitk::CustomTagParser::GetRevisionAppropriateJSONString(std::string revisionString) { std::string returnValue = ""; if ("" == revisionString) { MITK_WARN << "Could not extract revision"; } else { GetClosestLowerRevision(revisionString); bool useExternal = false; bool useInternal = false; if ("" != m_ClosestExternalRevision) { useExternal = true; } if ("" != m_ClosestInternalRevision) { useInternal = true; } if (useExternal && useInternal) { if (std::stoi(m_ClosestInternalRevision) > std::stoi(m_ClosestExternalRevision)) { useExternal = false; } } if (useExternal) { std::string stringToJSONDirectory = GetExternalJSONDirectory(); std::string prospectiveJsonPath = stringToJSONDirectory + "/" + m_ClosestExternalRevision + ".json"; std::ifstream externalJSON(prospectiveJsonPath.c_str()); if (externalJSON.good()) { MITK_INFO << "Found external json for CEST parameters at " << prospectiveJsonPath; std::stringstream buffer; buffer << externalJSON.rdbuf(); returnValue = buffer.str(); useInternal = false; } } if (useInternal) { std::string filename = m_ClosestInternalRevision + ".json"; us::ModuleResource jsonResource = us::GetModuleContext()->GetModule()->GetResource(filename); if (jsonResource.IsValid() && jsonResource.IsFile()) { MITK_INFO << "Found no external json for CEST parameters. Closest internal mapping is for revision " << m_ClosestInternalRevision; us::ModuleResourceStream jsonStream(jsonResource); std::stringstream buffer; buffer << jsonStream.rdbuf(); returnValue = buffer.str(); } } } if ("" == returnValue) { MITK_WARN << "Could not identify parameter mapping for the given revision " << revisionString << ", using default mapping."; returnValue = m_DefaultJsonString; } // inject the revision independent mapping before the first newline { returnValue.insert(returnValue.find("\n"), m_RevisionIndependentMapping); } return returnValue; } std::string mitk::CustomTagParser::GetOffsetString(std::string samplingType, std::string offset, std::string measurements) { mitk::LocaleSwitch localeSwitch("C"); std::stringstream results; std::string normalizationIndicatingOffset = "-300"; double offsetDouble = 0.0; int measurementsInt = 0; bool validOffset = false; bool validMeasurements = false; if ("" != offset) { validOffset = true; offsetDouble = std::stod(offset); } if ("" != measurements) { validMeasurements = true; measurementsInt = std::stoi(measurements); } std::vector offsetVector; if (validOffset && validMeasurements) { for (int step = 0; step < measurementsInt -1; ++step) { double currentOffset = -offsetDouble + 2 * step * offsetDouble / (measurementsInt - 2.0); offsetVector.push_back(currentOffset); } } else { MITK_WARN << "Invalid offset or measurements, offset calculation will only work for list sampling type."; } if (samplingType == "1" || samplingType == "Regular") { if (validOffset && validMeasurements) { results << normalizationIndicatingOffset << " "; for (const auto& entry : offsetVector) { results << entry << " "; } } } else if (samplingType == "2" || samplingType == "Alternating") { if (validOffset && validMeasurements) { results << normalizationIndicatingOffset << " "; for (auto& entry : offsetVector) { entry = std::abs(entry); } std::sort(offsetVector.begin(), offsetVector.end(), std::greater<>()); for (unsigned int index = 0; index < offsetVector.size(); ++index) { offsetVector[index] = std::pow(-1, index) * offsetVector[index]; } for (auto& entry : offsetVector) { results << entry << " "; } } } else if (samplingType == "3" || samplingType == "List") { std::string listPath = m_DicomDataPath + "/LIST.txt"; std::ifstream list(listPath.c_str()); if (list.good()) { std::string currentOffset; while (std::getline(list, currentOffset)) { results << currentOffset << " "; } } else { MITK_ERROR << "Could not load list at " << listPath; } } else if (samplingType == "4" || samplingType == "SingleOffset") { if (validOffset && validMeasurements) { results << normalizationIndicatingOffset << " "; for (int step = 0; step < measurementsInt - 1; ++step) { results << offsetDouble << " "; } } } else { MITK_WARN << "Encountered unknown sampling type."; } std::string resultString = results.str(); // replace multiple spaces by a single space std::string::iterator newEnditerator = std::unique(resultString.begin(), resultString.end(), [=](char lhs, char rhs) { return (lhs == rhs) && (lhs == ' '); } ); resultString.erase(newEnditerator, resultString.end()); if ((resultString.length() > 0) && (resultString.at(resultString.length() - 1) == ' ')) { resultString.erase(resultString.end() - 1, resultString.end()); } if ((resultString.length() > 0) && (resultString.at(0) == ' ')) { resultString.erase(resultString.begin(), ++(resultString.begin())); } return resultString; } void mitk::CustomTagParser::SetParseStrategy(std::string parseStrategy) { m_ParseStrategy = parseStrategy; } void mitk::CustomTagParser::SetRevisionMappingStrategy(std::string revisionMappingStrategy) { m_RevisionMappingStrategy = revisionMappingStrategy; } std::string mitk::CustomTagParser::GetExternalJSONDirectory() { std::string moduleLocation = us::GetModuleContext()->GetModule()->GetLocation(); std::string stringToModule; std::string libraryName; itksys::SystemTools::SplitProgramPath(moduleLocation, stringToModule, libraryName); std::stringstream jsonDirectory; jsonDirectory << stringToModule << "/CESTRevisionMapping"; return jsonDirectory.str(); } diff --git a/Modules/Classification/CLLibSVM/src/mitkLibSVMClassifier.cpp b/Modules/Classification/CLLibSVM/src/mitkLibSVMClassifier.cpp index af9641754d..6b1ef04c93 100644 --- a/Modules/Classification/CLLibSVM/src/mitkLibSVMClassifier.cpp +++ b/Modules/Classification/CLLibSVM/src/mitkLibSVMClassifier.cpp @@ -1,343 +1,343 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include #include namespace LibSVM { #include "svm.h" } #include mitk::LibSVMClassifier::LibSVMClassifier(): m_Model(nullptr),m_Parameter(nullptr) { this->m_Parameter = new LibSVM::svm_parameter(); } mitk::LibSVMClassifier::~LibSVMClassifier() { if (m_Model) { LibSVM::svm_free_and_destroy_model(&m_Model); } if( m_Parameter) LibSVM::svm_destroy_param(m_Parameter); } void mitk::LibSVMClassifier::Train(const Eigen::MatrixXd &X, const Eigen::MatrixXi &Y) { this->SetPointWiseWeight(Eigen::MatrixXd(Y.rows(),1)); this->UsePointWiseWeight(false); LibSVM::svm_node *xSpace; LibSVM::svm_problem problem; ConvertParameter(); ReadYValues(&problem, Y); ReadXValues(&problem, &xSpace,X); ReadWValues(&problem); const char * error_msg = nullptr; error_msg = LibSVM::svm_check_parameter(&problem, m_Parameter); if (error_msg) { LibSVM::svm_destroy_param(m_Parameter); free(problem.y); free(problem.x); free(xSpace); mitkThrow() << "Error: " << error_msg; } m_Model = LibSVM::svm_train(&problem, m_Parameter); // free(problem.y); // free(problem.x); // free(xSpace); } Eigen::MatrixXi mitk::LibSVMClassifier::Predict(const Eigen::MatrixXd &X) { if ( ! m_Model) { mitkThrow() << "No Model is trained. Train or load a model before predicting new values."; } - int noOfPoints = static_cast(X.rows()); - int noOfFeatures = static_cast(X.cols()); + auto noOfPoints = static_cast(X.rows()); + auto noOfFeatures = static_cast(X.cols()); Eigen::MatrixXi result(noOfPoints,1); - LibSVM::svm_node * xVector = static_cast(malloc(sizeof(LibSVM::svm_node) * (noOfFeatures+1))); + auto * xVector = static_cast(malloc(sizeof(LibSVM::svm_node) * (noOfFeatures+1))); for (int point = 0; point < noOfPoints; ++point) { for (int feature = 0; feature < noOfFeatures; ++feature) { xVector[feature].index = feature+1; xVector[feature].value = X(point, feature); } xVector[noOfFeatures].index = -1; result(point,0) = LibSVM::svm_predict(m_Model,xVector); } free(xVector); return result; } void mitk::LibSVMClassifier::ConvertParameter() { // Get the proerty // Some defaults if(!this->GetPropertyList()->Get("classifier.svm.svm-type",this->m_Parameter->svm_type)) this->m_Parameter->svm_type = 0; if(!this->GetPropertyList()->Get("classifier.svm.kernel-type",this->m_Parameter->kernel_type)) this->m_Parameter->kernel_type = 2; if(!this->GetPropertyList()->Get("classifier.svm.degree",this->m_Parameter->degree)) this->m_Parameter->degree = 3; if(!this->GetPropertyList()->Get("classifier.svm.gamma",this->m_Parameter->gamma)) this->m_Parameter->gamma = 0; // 1/n_features; if(!this->GetPropertyList()->Get("classifier.svm.coef0",this->m_Parameter->coef0)) this->m_Parameter->coef0 = 0; if(!this->GetPropertyList()->Get("classifier.svm.nu",this->m_Parameter->nu)) this->m_Parameter->nu = 0.5; if(!this->GetPropertyList()->Get("classifier.svm.cache-size",this->m_Parameter->cache_size)) this->m_Parameter->cache_size = 100.0; if(!this->GetPropertyList()->Get("classifier.svm.c",this->m_Parameter->C)) this->m_Parameter->C = 1.0; if(!this->GetPropertyList()->Get("classifier.svm.eps",this->m_Parameter->eps)) this->m_Parameter->eps = 1e-3; if(!this->GetPropertyList()->Get("classifier.svm.p",this->m_Parameter->p)) this->m_Parameter->p = 0.1; if(!this->GetPropertyList()->Get("classifier.svm.shrinking",this->m_Parameter->shrinking)) this->m_Parameter->shrinking = 1; if(!this->GetPropertyList()->Get("classifier.svm.probability",this->m_Parameter->probability)) this->m_Parameter->probability = 0; if(!this->GetPropertyList()->Get("classifier.svm.nr-weight",this->m_Parameter->nr_weight)) this->m_Parameter->nr_weight = 0; //options: //-s svm_type : set type of SVM (default 0) // 0 -- C-SVC // 1 -- nu-SVC // 2 -- one-class SVM // 3 -- epsilon-SVR // 4 -- nu-SVR //-t kernel_type : set type of kernel function (default 2) // 0 -- linear: u'*v // 1 -- polynomial: (gamma*u'*v + coef0)^degree // 2 -- radial basis function: exp(-gamma*|u-v|^2) // 3 -- sigmoid: tanh(gamma*u'*v + coef0) //-d degree : set degree in kernel function (default 3) //-g gamma : set gamma in kernel function (default 1/num_features) //-r coef0 : set coef0 in kernel function (default 0) //-c cost : set the parameter C of C-SVC, epsilon-SVR, and nu-SVR (default 1) //-n nu : set the parameter nu of nu-SVC, one-class SVM, and nu-SVR (default 0.5) //-p epsilon : set the epsilon in loss function of epsilon-SVR (default 0.1) //-m cachesize : set cache memory size in MB (default 100) //-e epsilon : set tolerance of termination criterion (default 0.001) //-h shrinking: whether to use the shrinking heuristics, 0 or 1 (default 1) //-b probability_estimates: whether to train a SVC or SVR model for probability estimates, 0 or 1 (default 0) //-wiweight: set the parameter C of class i to weight*C, for C-SVC (default 1) // this->m_Parameter->weight_label = nullptr; // this->m_Parameter->weight = 1; } /* these are for training only */ //int *weight_label; /* for C_SVC */ //double* weight; /* for C_SVC */ void mitk::LibSVMClassifier::SetProbability(int val) { this->GetPropertyList()->SetIntProperty("classifier.svm.probability",val); } void mitk::LibSVMClassifier::SetShrinking(int val) { this->GetPropertyList()->SetIntProperty("classifier.svm.shrinking",val); } void mitk::LibSVMClassifier::SetNrWeight(int val) { this->GetPropertyList()->SetIntProperty("classifier.svm.nr_weight",val); } void mitk::LibSVMClassifier::SetNu(double val) { this->GetPropertyList()->SetDoubleProperty("classifier.svm.nu",val); } void mitk::LibSVMClassifier::SetP(double val) { this->GetPropertyList()->SetDoubleProperty("classifier.svm.p",val); } void mitk::LibSVMClassifier::SetEps(double val) { this->GetPropertyList()->SetDoubleProperty("classifier.svm.eps",val); } void mitk::LibSVMClassifier::SetC(double val) { this->GetPropertyList()->SetDoubleProperty("classifier.svm.c",val); } void mitk::LibSVMClassifier::SetCacheSize(double val) { this->GetPropertyList()->SetDoubleProperty("classifier.svm.cache-size",val); } void mitk::LibSVMClassifier::SetSvmType(int val) { this->GetPropertyList()->SetIntProperty("classifier.svm.svm-type",val); } void mitk::LibSVMClassifier::SetKernelType(int val) { this->GetPropertyList()->SetIntProperty("classifier.svm.kernel-type",val); } void mitk::LibSVMClassifier::SetDegree(int val) { this->GetPropertyList()->SetIntProperty("classifier.svm.degree",val); } void mitk::LibSVMClassifier::SetGamma(double val) { this->GetPropertyList()->SetDoubleProperty("classifier.svm.gamma",val); } void mitk::LibSVMClassifier::SetCoef0(double val) { this->GetPropertyList()->SetDoubleProperty("classifier.svm.coef0",val); } void mitk::LibSVMClassifier::PrintParameter(std::ostream & str) { if(this->m_Parameter == nullptr) { MITK_WARN("LibSVMClassifier") << "Parameters are not initialized. Please call ConvertParameter() first!"; return; } this->ConvertParameter(); // Get the proerty // Some defaults if(!this->GetPropertyList()->Get("classifier.svm.svm-type",this->m_Parameter->svm_type)) str << "classifier.svm.svm-type\tNOT SET (default " << this->m_Parameter->svm_type << ")" << "\n"; else str << "classifier.svm.svm-type\t" << this->m_Parameter->svm_type << "\n"; if(!this->GetPropertyList()->Get("classifier.svm.kernel-type",this->m_Parameter->kernel_type)) str << "classifier.svm.kernel-type\tNOT SET (default " << this->m_Parameter->kernel_type << ")" << "\n"; else str << "classifier.svm.kernel-type\t" << this->m_Parameter->kernel_type << "\n"; if(!this->GetPropertyList()->Get("classifier.svm.degree",this->m_Parameter->degree)) str << "classifier.svm.degree\t\tNOT SET (default " << this->m_Parameter->degree << ")" << "\n"; else str << "classifier.svm.degree\t\t" << this->m_Parameter->degree << "\n"; if(!this->GetPropertyList()->Get("classifier.svm.gamma",this->m_Parameter->gamma)) str << "classifier.svm.gamma\t\tNOT SET (default " << this->m_Parameter->gamma << ")" << "\n"; else str << "classifier.svm.gamma\t\t" << this->m_Parameter->gamma << "\n"; if(!this->GetPropertyList()->Get("classifier.svm.coef0",this->m_Parameter->coef0)) str << "classifier.svm.coef0\t\tNOT SET (default " << this->m_Parameter->coef0 << ")" << "\n"; else str << "classifier.svm.coef0\t\t" << this->m_Parameter->coef0 << "\n"; if(!this->GetPropertyList()->Get("classifier.svm.nu",this->m_Parameter->nu)) str << "classifier.svm.nu\t\tNOT SET (default " << this->m_Parameter->nu << ")" << "\n"; else str << "classifier.svm.nu\t\t" << this->m_Parameter->nu << "\n"; if(!this->GetPropertyList()->Get("classifier.svm.cache-size",this->m_Parameter->cache_size)) str << "classifier.svm.cache-size\tNOT SET (default " << this->m_Parameter->cache_size << ")" << "\n"; else str << "classifier.svm.cache-size\t" << this->m_Parameter->cache_size << "\n"; if(!this->GetPropertyList()->Get("classifier.svm.c",this->m_Parameter->C)) str << "classifier.svm.c\t\tNOT SET (default " << this->m_Parameter->C << ")" << "\n"; else str << "classifier.svm.c\t\t" << this->m_Parameter->C << "\n"; if(!this->GetPropertyList()->Get("classifier.svm.eps",this->m_Parameter->eps)) str << "classifier.svm.eps\t\tNOT SET (default " << this->m_Parameter->eps << ")" << "\n"; else str << "classifier.svm.eps\t\t" << this->m_Parameter->eps << "\n"; if(!this->GetPropertyList()->Get("classifier.svm.p",this->m_Parameter->p)) str << "classifier.svm.p\t\tNOT SET (default " << this->m_Parameter->p << ")" << "\n"; else str << "classifier.svm.p\t\t" << this->m_Parameter->p << "\n"; if(!this->GetPropertyList()->Get("classifier.svm.shrinking",this->m_Parameter->shrinking)) str << "classifier.svm.shrinking\tNOT SET (default " << this->m_Parameter->shrinking << ")" << "\n"; else str << "classifier.svm.shrinking\t" << this->m_Parameter->shrinking << "\n"; if(!this->GetPropertyList()->Get("classifier.svm.probability",this->m_Parameter->probability)) str << "classifier.svm.probability\tNOT SET (default " << this->m_Parameter->probability << ")" << "\n"; else str << "classifier.svm.probability\t" << this->m_Parameter->probability << "\n"; if(!this->GetPropertyList()->Get("classifier.svm.nr-weight",this->m_Parameter->nr_weight)) str << "classifier.svm.nr-weight\tNOT SET (default " << this->m_Parameter->nr_weight << ")" << "\n"; else str << "classifier.svm.nr-weight\t" << this->m_Parameter->nr_weight << "\n"; } // Trying to assign from matrix to noOfPoints void mitk::LibSVMClassifier::ReadXValues(LibSVM::svm_problem * problem, LibSVM::svm_node** xSpace, const Eigen::MatrixXd &X) { - int noOfPoints = static_cast(X.rows()); - int features = static_cast(X.cols()); + auto noOfPoints = static_cast(X.rows()); + auto features = static_cast(X.cols()); problem->x = static_cast(malloc(sizeof(LibSVM::svm_node *) * noOfPoints)); (*xSpace) = static_cast (malloc(sizeof(LibSVM::svm_node) * noOfPoints * (features+1))); for (int row = 0; row < noOfPoints; ++row) { for (int col = 0; col < features; ++col) { (*xSpace)[row*features + col].index = col; (*xSpace)[row*features + col].value = X(row,col); } (*xSpace)[row*features + features].index = -1; problem->x[row] = &((*xSpace)[row*features]); } } void mitk::LibSVMClassifier::ReadYValues(LibSVM::svm_problem * problem, const Eigen::MatrixXi &Y) { problem->l = static_cast(Y.rows()); problem->y = static_cast(malloc(sizeof(double) * problem->l)); for (int i = 0; i < problem->l; ++i) { problem->y[i] = Y(i,0); } } void mitk::LibSVMClassifier::ReadWValues(LibSVM::svm_problem * problem) { Eigen::MatrixXd & W = this->GetPointWiseWeight(); int noOfPoints = problem->l; problem->W = static_cast(malloc(sizeof(double) * noOfPoints)); if (IsUsingPointWiseWeight()) { for (int i = 0; i < noOfPoints; ++i) { problem->W[i] = W(i,0); } } else { for (int i = 0; i < noOfPoints; ++i) { problem->W[i] = 1; } } } \ No newline at end of file diff --git a/Modules/Classification/CLLibSVM/src/svm.cpp b/Modules/Classification/CLLibSVM/src/svm.cpp index f20b6e670d..faafdf680b 100644 --- a/Modules/Classification/CLLibSVM/src/svm.cpp +++ b/Modules/Classification/CLLibSVM/src/svm.cpp @@ -1,3307 +1,3307 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ /*=================================================================== Copyright (c) 2000-2014 Chih-Chung Chang and Chih-Jen Lin All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. Neither name of copyright holders nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ===================================================================*/ #include #include #include #include #include #include #include #include #include #include #include "svm.h" int libsvm_version = LIBSVM_VERSION; typedef float Qfloat; typedef signed char schar; #ifndef min template static inline T min(T x,T y) { return (x static inline T max(T x,T y) { return (x>y)?x:y; } #endif template static inline void swap(T& x, T& y) { T t=x; x=y; y=t; } template static inline void clone(T*& dst, S* src, int n) { dst = new T[n]; memcpy((void *)dst,(void *)src,sizeof(T)*n); } static inline double powi(double base, int times) { double tmp = base, ret = 1.0; for(int t=times; t>0; t/=2) { if(t%2==1) ret*=tmp; tmp = tmp * tmp; } return ret; } #define INF HUGE_VAL #define TAU 1e-12 #define Malloc(type,n) (type *)malloc((n)*sizeof(type)) static void print_string_stdout(const char *s) { fputs(s,stdout); fflush(stdout); } static void (*svm_print_string) (const char *) = &print_string_stdout; #if 1 static void info(const char *fmt,...) { char buf[BUFSIZ]; va_list ap; va_start(ap,fmt); vsprintf(buf,fmt,ap); va_end(ap); (*svm_print_string)(buf); } #else static void info(const char *fmt,...) {} #endif // // Kernel Cache // // l is the number of total data items // size is the cache size limit in bytes // class Cache { public: Cache(int l,long int size); ~Cache(); // request data [0,len) // return some position p where [p,len) need to be filled // (p >= len if nothing needs to be filled) int get_data(const int index, Qfloat **data, int len); void swap_index(int i, int j); private: int l; long int size; struct head_t { head_t *prev, *next; // a circular list Qfloat *data; int len; // data[0,len) is cached in this entry }; head_t *head; head_t lru_head; void lru_delete(head_t *h); void lru_insert(head_t *h); }; Cache::Cache(int l_,long int size_):l(l_),size(size_) { head = (head_t *)calloc(l,sizeof(head_t)); // initialized to 0 size /= sizeof(Qfloat); size -= l * sizeof(head_t) / sizeof(Qfloat); size = max(size, 2 * (long int) l); // cache must be large enough for two columns lru_head.next = lru_head.prev = &lru_head; } Cache::~Cache() { for(head_t *h = lru_head.next; h != &lru_head; h=h->next) free(h->data); free(head); } void Cache::lru_delete(head_t *h) { // delete from current location h->prev->next = h->next; h->next->prev = h->prev; } void Cache::lru_insert(head_t *h) { // insert to last position h->next = &lru_head; h->prev = lru_head.prev; h->prev->next = h; h->next->prev = h; } int Cache::get_data(const int index, Qfloat **data, int len) { head_t *h = &head[index]; if(h->len) lru_delete(h); int more = len - h->len; if(more > 0) { // free old space while(size < more) { head_t *old = lru_head.next; lru_delete(old); free(old->data); size += old->len; old->data = nullptr; old->len = 0; } // allocate new space h->data = (Qfloat *)realloc(h->data,sizeof(Qfloat)*len); size -= more; swap(h->len,len); } lru_insert(h); *data = h->data; return len; } void Cache::swap_index(int i, int j) { if(i==j) return; if(head[i].len) lru_delete(&head[i]); if(head[j].len) lru_delete(&head[j]); swap(head[i].data,head[j].data); swap(head[i].len,head[j].len); if(head[i].len) lru_insert(&head[i]); if(head[j].len) lru_insert(&head[j]); if(i>j) swap(i,j); for(head_t *h = lru_head.next; h!=&lru_head; h=h->next) { if(h->len > i) { if(h->len > j) swap(h->data[i],h->data[j]); else { // give up lru_delete(h); free(h->data); size += h->len; h->data = nullptr; h->len = 0; } } } } // // Kernel evaluation // // the static method k_function is for doing single kernel evaluation // the constructor of Kernel prepares to calculate the l*l kernel matrix // the member function get_Q is for getting one column from the Q Matrix // class QMatrix { public: virtual Qfloat *get_Q(int column, int len) const = 0; virtual double *get_QD() const = 0; virtual void swap_index(int i, int j) const = 0; virtual ~QMatrix() {} }; class Kernel: public QMatrix { public: Kernel(int l, svm_node * const * x, const svm_parameter& param); ~Kernel() override; static double k_function(const svm_node *x, const svm_node *y, const svm_parameter& param); Qfloat *get_Q(int column, int len) const override = 0; double *get_QD() const override = 0; void swap_index(int i, int j) const override // no so const... { swap(x[i],x[j]); if(x_square) swap(x_square[i],x_square[j]); } protected: double (Kernel::*kernel_function)(int i, int j) const; private: const svm_node **x; double *x_square; // svm_parameter const int kernel_type; const int degree; const double gamma; const double coef0; static double dot(const svm_node *px, const svm_node *py); double kernel_linear(int i, int j) const { return dot(x[i],x[j]); } double kernel_poly(int i, int j) const { return powi(gamma*dot(x[i],x[j])+coef0,degree); } double kernel_rbf(int i, int j) const { return exp(-gamma*(x_square[i]+x_square[j]-2*dot(x[i],x[j]))); } double kernel_sigmoid(int i, int j) const { return tanh(gamma*dot(x[i],x[j])+coef0); } double kernel_precomputed(int i, int j) const { return x[i][(int)(x[j][0].value)].value; } }; Kernel::Kernel(int l, svm_node * const * x_, const svm_parameter& param) :kernel_type(param.kernel_type), degree(param.degree), gamma(param.gamma), coef0(param.coef0) { switch(kernel_type) { case LINEAR: kernel_function = &Kernel::kernel_linear; break; case POLY: kernel_function = &Kernel::kernel_poly; break; case RBF: kernel_function = &Kernel::kernel_rbf; break; case SIGMOID: kernel_function = &Kernel::kernel_sigmoid; break; case PRECOMPUTED: kernel_function = &Kernel::kernel_precomputed; break; } clone(x,x_,l); if(kernel_type == RBF) { x_square = new double[l]; for(int i=0;iindex != -1 && py->index != -1) { if(px->index == py->index) { sum += px->value * py->value; ++px; ++py; } else { if(px->index > py->index) ++py; else ++px; } } return sum; } double Kernel::k_function(const svm_node *x, const svm_node *y, const svm_parameter& param) { switch(param.kernel_type) { case LINEAR: return dot(x,y); case POLY: return powi(param.gamma*dot(x,y)+param.coef0,param.degree); case RBF: { double sum = 0; while(x->index != -1 && y->index !=-1) { if(x->index == y->index) { double d = x->value - y->value; sum += d*d; ++x; ++y; } else { if(x->index > y->index) { sum += y->value * y->value; ++y; } else { sum += x->value * x->value; ++x; } } } while(x->index != -1) { sum += x->value * x->value; ++x; } while(y->index != -1) { sum += y->value * y->value; ++y; } return exp(-param.gamma*sum); } case SIGMOID: return tanh(param.gamma*dot(x,y)+param.coef0); case PRECOMPUTED: //x: test (validation), y: SV return x[(int)(y->value)].value; default: return 0; // Unreachable } } // An SMO algorithm in Fan et al., JMLR 6(2005), p. 1889--1918 // Solves: // // min 0.5(\alpha^T Q \alpha) + p^T \alpha // // y^T \alpha = \delta // y_i = +1 or -1 // 0 <= alpha_i <= Cp for y_i = 1 // 0 <= alpha_i <= Cn for y_i = -1 // // Given: // // Q, p, y, Cp, Cn, and an initial feasible point \alpha // l is the size of vectors and matrices // eps is the stopping tolerance // // solution will be put in \alpha, objective value will be put in obj // class Solver { public: Solver() {}; virtual ~Solver() {}; struct SolutionInfo { double obj; double rho; double *upper_bound; double r; // for Solver_NU }; void Solve(int l, const QMatrix& Q, const double *p_, const schar *y_, double *alpha_, const double* C_, double eps, SolutionInfo* si, int shrinking); protected: int active_size; schar *y; double *G; // gradient of objective function enum { LOWER_BOUND, UPPER_BOUND, FREE }; char *alpha_status; // LOWER_BOUND, UPPER_BOUND, FREE double *alpha; const QMatrix *Q; const double *QD; double eps; double Cp,Cn; double *C; double *p; int *active_set; double *G_bar; // gradient, if we treat free variables as 0 int l; bool unshrink; // XXX double get_C(int i) { return C[i]; } void update_alpha_status(int i) { if(alpha[i] >= get_C(i)) alpha_status[i] = UPPER_BOUND; else if(alpha[i] <= 0) alpha_status[i] = LOWER_BOUND; else alpha_status[i] = FREE; } bool is_upper_bound(int i) { return alpha_status[i] == UPPER_BOUND; } bool is_lower_bound(int i) { return alpha_status[i] == LOWER_BOUND; } bool is_free(int i) { return alpha_status[i] == FREE; } void swap_index(int i, int j); void reconstruct_gradient(); virtual int select_working_set(int &i, int &j); virtual double calculate_rho(); virtual void do_shrinking(); private: bool be_shrunk(int i, double Gmax1, double Gmax2); }; void Solver::swap_index(int i, int j) { Q->swap_index(i,j); swap(y[i],y[j]); swap(G[i],G[j]); swap(alpha_status[i],alpha_status[j]); swap(alpha[i],alpha[j]); swap(p[i],p[j]); swap(active_set[i],active_set[j]); swap(G_bar[i],G_bar[j]); swap(C[i],C[j]); } void Solver::reconstruct_gradient() { // reconstruct inactive elements of G from G_bar and free variables if(active_size == l) return; int i,j; int nr_free = 0; for(j=active_size;j 2*active_size*(l-active_size)) { for(i=active_size;iget_Q(i,active_size); for(j=0;jget_Q(i,l); double alpha_i = alpha[i]; for(j=active_size;jl = l; this->Q = &Q; QD=Q.get_QD(); clone(p, p_,l); clone(y, y_,l); clone(alpha,alpha_,l); clone(C,C_,l); this->eps = eps; unshrink = false; // initialize alpha_status { alpha_status = new char[l]; for(int i=0;iINT_MAX/100 ? INT_MAX : 100*l); int counter = min(l,1000)+1; while(iter < max_iter) { // show progress and do shrinking if(--counter == 0) { counter = min(l,1000); if(shrinking) do_shrinking(); info("."); } int i,j; if(select_working_set(i,j)!=0) { // reconstruct the whole gradient reconstruct_gradient(); // reset active set size and check active_size = l; info("*"); if(select_working_set(i,j)!=0) break; else counter = 1; // do shrinking next iteration } ++iter; // update alpha[i] and alpha[j], handle bounds carefully const Qfloat *Q_i = Q.get_Q(i,active_size); const Qfloat *Q_j = Q.get_Q(j,active_size); double C_i = get_C(i); double C_j = get_C(j); double old_alpha_i = alpha[i]; double old_alpha_j = alpha[j]; if(y[i]!=y[j]) { double quad_coef = QD[i]+QD[j]+2*Q_i[j]; if (quad_coef <= 0) quad_coef = TAU; double delta = (-G[i]-G[j])/quad_coef; double diff = alpha[i] - alpha[j]; alpha[i] += delta; alpha[j] += delta; if(diff > 0) { if(alpha[j] < 0) { alpha[j] = 0; alpha[i] = diff; } } else { if(alpha[i] < 0) { alpha[i] = 0; alpha[j] = -diff; } } if(diff > C_i - C_j) { if(alpha[i] > C_i) { alpha[i] = C_i; alpha[j] = C_i - diff; } } else { if(alpha[j] > C_j) { alpha[j] = C_j; alpha[i] = C_j + diff; } } } else { double quad_coef = QD[i]+QD[j]-2*Q_i[j]; if (quad_coef <= 0) quad_coef = TAU; double delta = (G[i]-G[j])/quad_coef; double sum = alpha[i] + alpha[j]; alpha[i] -= delta; alpha[j] += delta; if(sum > C_i) { if(alpha[i] > C_i) { alpha[i] = C_i; alpha[j] = sum - C_i; } } else { if(alpha[j] < 0) { alpha[j] = 0; alpha[i] = sum; } } if(sum > C_j) { if(alpha[j] > C_j) { alpha[j] = C_j; alpha[i] = sum - C_j; } } else { if(alpha[i] < 0) { alpha[i] = 0; alpha[j] = sum; } } } // update G double delta_alpha_i = alpha[i] - old_alpha_i; double delta_alpha_j = alpha[j] - old_alpha_j; for(int k=0;k= max_iter) { if(active_size < l) { // reconstruct the whole gradient to calculate objective value reconstruct_gradient(); active_size = l; info("*"); } fprintf(stderr,"\nWARNING: reaching max number of iterations\n"); } // calculate rho si->rho = calculate_rho(); // calculate objective value { double v = 0; int i; for(i=0;iobj = v/2; } // put back the solution { for(int i=0;iupper_bound[i] = C[i]; info("\noptimization finished, #iter = %d\n",iter); delete[] p; delete[] y; delete[] C; delete[] alpha; delete[] alpha_status; delete[] active_set; delete[] G; delete[] G_bar; } // return 1 if already optimal, return 0 otherwise int Solver::select_working_set(int &out_i, int &out_j) { // return i,j such that // i: maximizes -y_i * grad(f)_i, i in I_up(\alpha) // j: minimizes the decrease of obj value // (if quadratic coefficeint <= 0, replace it with tau) // -y_j*grad(f)_j < -y_i*grad(f)_i, j in I_low(\alpha) double Gmax = -INF; double Gmax2 = -INF; int Gmax_idx = -1; int Gmin_idx = -1; double obj_diff_min = INF; for(int t=0;t= Gmax) { Gmax = -G[t]; Gmax_idx = t; } } else { if(!is_lower_bound(t)) if(G[t] >= Gmax) { Gmax = G[t]; Gmax_idx = t; } } int i = Gmax_idx; const Qfloat *Q_i = nullptr; if(i != -1) // nullptr Q_i not accessed: Gmax=-INF if i=-1 Q_i = Q->get_Q(i,active_size); for(int j=0;j= Gmax2) Gmax2 = G[j]; if (grad_diff > 0) { double obj_diff; double quad_coef = QD[i]+QD[j]-2.0*y[i]*Q_i[j]; if (quad_coef > 0) obj_diff = -(grad_diff*grad_diff)/quad_coef; else obj_diff = -(grad_diff*grad_diff)/TAU; if (obj_diff <= obj_diff_min) { Gmin_idx=j; obj_diff_min = obj_diff; } } } } else { if (!is_upper_bound(j)) { double grad_diff= Gmax-G[j]; if (-G[j] >= Gmax2) Gmax2 = -G[j]; if (grad_diff > 0) { double obj_diff; double quad_coef = QD[i]+QD[j]+2.0*y[i]*Q_i[j]; if (quad_coef > 0) obj_diff = -(grad_diff*grad_diff)/quad_coef; else obj_diff = -(grad_diff*grad_diff)/TAU; if (obj_diff <= obj_diff_min) { Gmin_idx=j; obj_diff_min = obj_diff; } } } } } if(Gmax+Gmax2 < eps) return 1; out_i = Gmax_idx; out_j = Gmin_idx; return 0; } bool Solver::be_shrunk(int i, double Gmax1, double Gmax2) { if(is_upper_bound(i)) { if(y[i]==+1) return(-G[i] > Gmax1); else return(-G[i] > Gmax2); } else if(is_lower_bound(i)) { if(y[i]==+1) return(G[i] > Gmax2); else return(G[i] > Gmax1); } else return(false); } void Solver::do_shrinking() { int i; double Gmax1 = -INF; // max { -y_i * grad(f)_i | i in I_up(\alpha) } double Gmax2 = -INF; // max { y_i * grad(f)_i | i in I_low(\alpha) } // find maximal violating pair first for(i=0;i= Gmax1) Gmax1 = -G[i]; } if(!is_lower_bound(i)) { if(G[i] >= Gmax2) Gmax2 = G[i]; } } else { if(!is_upper_bound(i)) { if(-G[i] >= Gmax2) Gmax2 = -G[i]; } if(!is_lower_bound(i)) { if(G[i] >= Gmax1) Gmax1 = G[i]; } } } if(unshrink == false && Gmax1 + Gmax2 <= eps*10) { unshrink = true; reconstruct_gradient(); active_size = l; info("*"); } for(i=0;i i) { if (!be_shrunk(active_size, Gmax1, Gmax2)) { swap_index(i,active_size); break; } active_size--; } } } double Solver::calculate_rho() { double r; int nr_free = 0; double ub = INF, lb = -INF, sum_free = 0; for(int i=0;i0) r = sum_free/nr_free; else r = (ub+lb)/2; return r; } // // Solver for nu-svm classification and regression // // additional constraint: e^T \alpha = constant // class Solver_NU: public Solver { public: Solver_NU() {} void Solve(int l, const QMatrix& Q, const double *p, const schar *y, double *alpha, double* C_, double eps, SolutionInfo* si, int shrinking) { this->si = si; Solver::Solve(l,Q,p,y,alpha,C_,eps,si,shrinking); } private: SolutionInfo *si; int select_working_set(int &i, int &j) override; double calculate_rho() override; bool be_shrunk(int i, double Gmax1, double Gmax2, double Gmax3, double Gmax4); void do_shrinking() override; }; // return 1 if already optimal, return 0 otherwise int Solver_NU::select_working_set(int &out_i, int &out_j) { // return i,j such that y_i = y_j and // i: maximizes -y_i * grad(f)_i, i in I_up(\alpha) // j: minimizes the decrease of obj value // (if quadratic coefficeint <= 0, replace it with tau) // -y_j*grad(f)_j < -y_i*grad(f)_i, j in I_low(\alpha) double Gmaxp = -INF; double Gmaxp2 = -INF; int Gmaxp_idx = -1; double Gmaxn = -INF; double Gmaxn2 = -INF; int Gmaxn_idx = -1; int Gmin_idx = -1; double obj_diff_min = INF; for(int t=0;t= Gmaxp) { Gmaxp = -G[t]; Gmaxp_idx = t; } } else { if(!is_lower_bound(t)) if(G[t] >= Gmaxn) { Gmaxn = G[t]; Gmaxn_idx = t; } } int ip = Gmaxp_idx; int in = Gmaxn_idx; const Qfloat *Q_ip = nullptr; const Qfloat *Q_in = nullptr; if(ip != -1) // nullptr Q_ip not accessed: Gmaxp=-INF if ip=-1 Q_ip = Q->get_Q(ip,active_size); if(in != -1) Q_in = Q->get_Q(in,active_size); for(int j=0;j= Gmaxp2) Gmaxp2 = G[j]; if (grad_diff > 0) { double obj_diff; double quad_coef = QD[ip]+QD[j]-2*Q_ip[j]; if (quad_coef > 0) obj_diff = -(grad_diff*grad_diff)/quad_coef; else obj_diff = -(grad_diff*grad_diff)/TAU; if (obj_diff <= obj_diff_min) { Gmin_idx=j; obj_diff_min = obj_diff; } } } } else { if (!is_upper_bound(j)) { double grad_diff=Gmaxn-G[j]; if (-G[j] >= Gmaxn2) Gmaxn2 = -G[j]; if (grad_diff > 0) { double obj_diff; double quad_coef = QD[in]+QD[j]-2*Q_in[j]; if (quad_coef > 0) obj_diff = -(grad_diff*grad_diff)/quad_coef; else obj_diff = -(grad_diff*grad_diff)/TAU; if (obj_diff <= obj_diff_min) { Gmin_idx=j; obj_diff_min = obj_diff; } } } } } if(max(Gmaxp+Gmaxp2,Gmaxn+Gmaxn2) < eps) return 1; if (y[Gmin_idx] == +1) out_i = Gmaxp_idx; else out_i = Gmaxn_idx; out_j = Gmin_idx; return 0; } bool Solver_NU::be_shrunk(int i, double Gmax1, double Gmax2, double Gmax3, double Gmax4) { if(is_upper_bound(i)) { if(y[i]==+1) return(-G[i] > Gmax1); else return(-G[i] > Gmax4); } else if(is_lower_bound(i)) { if(y[i]==+1) return(G[i] > Gmax2); else return(G[i] > Gmax3); } else return(false); } void Solver_NU::do_shrinking() { double Gmax1 = -INF; // max { -y_i * grad(f)_i | y_i = +1, i in I_up(\alpha) } double Gmax2 = -INF; // max { y_i * grad(f)_i | y_i = +1, i in I_low(\alpha) } double Gmax3 = -INF; // max { -y_i * grad(f)_i | y_i = -1, i in I_up(\alpha) } double Gmax4 = -INF; // max { y_i * grad(f)_i | y_i = -1, i in I_low(\alpha) } // find maximal violating pair first int i; for(i=0;i Gmax1) Gmax1 = -G[i]; } else if(-G[i] > Gmax4) Gmax4 = -G[i]; } if(!is_lower_bound(i)) { if(y[i]==+1) { if(G[i] > Gmax2) Gmax2 = G[i]; } else if(G[i] > Gmax3) Gmax3 = G[i]; } } if(unshrink == false && max(Gmax1+Gmax2,Gmax3+Gmax4) <= eps*10) { unshrink = true; reconstruct_gradient(); active_size = l; } for(i=0;i i) { if (!be_shrunk(active_size, Gmax1, Gmax2, Gmax3, Gmax4)) { swap_index(i,active_size); break; } active_size--; } } } double Solver_NU::calculate_rho() { int nr_free1 = 0,nr_free2 = 0; double ub1 = INF, ub2 = INF; double lb1 = -INF, lb2 = -INF; double sum_free1 = 0, sum_free2 = 0; for(int i=0;i 0) r1 = sum_free1/nr_free1; else r1 = (ub1+lb1)/2; if(nr_free2 > 0) r2 = sum_free2/nr_free2; else r2 = (ub2+lb2)/2; si->r = (r1+r2)/2; return (r1-r2)/2; } // // Q matrices for various formulations // class SVC_Q: public Kernel { public: SVC_Q(const svm_problem& prob, const svm_parameter& param, const schar *y_) :Kernel(prob.l, prob.x, param) { clone(y,y_,prob.l); cache = new Cache(prob.l,(long int)(param.cache_size*(1<<20))); QD = new double[prob.l]; for(int i=0;i*kernel_function)(i,i); } Qfloat *get_Q(int i, int len) const override { Qfloat *data; int start, j; if((start = cache->get_data(i,&data,len)) < len) { for(j=start;j*kernel_function)(i,j)); } return data; } double *get_QD() const override { return QD; } void swap_index(int i, int j) const override { cache->swap_index(i,j); Kernel::swap_index(i,j); swap(y[i],y[j]); swap(QD[i],QD[j]); } ~SVC_Q() override { delete[] y; delete cache; delete[] QD; } private: schar *y; Cache *cache; double *QD; }; class ONE_CLASS_Q: public Kernel { public: ONE_CLASS_Q(const svm_problem& prob, const svm_parameter& param) :Kernel(prob.l, prob.x, param) { cache = new Cache(prob.l,(long int)(param.cache_size*(1<<20))); QD = new double[prob.l]; for(int i=0;i*kernel_function)(i,i); } Qfloat *get_Q(int i, int len) const override { Qfloat *data; int start, j; if((start = cache->get_data(i,&data,len)) < len) { for(j=start;j*kernel_function)(i,j); } return data; } double *get_QD() const override { return QD; } void swap_index(int i, int j) const override { cache->swap_index(i,j); Kernel::swap_index(i,j); swap(QD[i],QD[j]); } ~ONE_CLASS_Q() override { delete cache; delete[] QD; } private: Cache *cache; double *QD; }; class SVR_Q: public Kernel { public: SVR_Q(const svm_problem& prob, const svm_parameter& param) :Kernel(prob.l, prob.x, param) { l = prob.l; cache = new Cache(l,(long int)(param.cache_size*(1<<20))); QD = new double[2*l]; sign = new schar[2*l]; index = new int[2*l]; for(int k=0;k*kernel_function)(k,k); QD[k+l] = QD[k]; } buffer[0] = new Qfloat[2*l]; buffer[1] = new Qfloat[2*l]; next_buffer = 0; } void swap_index(int i, int j) const override { swap(sign[i],sign[j]); swap(index[i],index[j]); swap(QD[i],QD[j]); } Qfloat *get_Q(int i, int len) const override { Qfloat *data; int j, real_i = index[i]; if(cache->get_data(real_i,&data,l) < l) { for(j=0;j*kernel_function)(real_i,j); } // reorder and copy Qfloat *buf = buffer[next_buffer]; next_buffer = 1 - next_buffer; schar si = sign[i]; for(j=0;jl; - double *minus_ones = new double[l]; - schar *y = new schar[l]; - double *C = new double[l]; + auto *minus_ones = new double[l]; + auto *y = new schar[l]; + auto *C = new double[l]; int i; for(i=0;iy[i] > 0) { y[i] = +1; C[i] = prob->W[i]*Cp; } else { y[i] = -1; C[i] = prob->W[i]*Cn; } } Solver s; s.Solve(l, SVC_Q(*prob,*param,y), minus_ones, y, alpha, C, param->eps, si, param->shrinking); /* double sum_alpha=0; for(i=0;il)); */ for(i=0;il; double nu = param->nu; - schar *y = new schar[l]; - double *C = new double[l]; + auto *y = new schar[l]; + auto *C = new double[l]; for(i=0;iy[i]>0) y[i] = +1; else y[i] = -1; C[i] = prob->W[i]; } double nu_l = 0; for(i=0;ieps, si, param->shrinking); double r = si->r; info("C = %f\n",1/r); for(i=0;iupper_bound[i] /= r; } si->rho /= r; si->obj /= (r*r); delete[] C; delete[] y; delete[] zeros; } static void solve_one_class( const svm_problem *prob, const svm_parameter *param, double *alpha, Solver::SolutionInfo* si) { int l = prob->l; - double *zeros = new double[l]; - schar *ones = new schar[l]; - double *C = new double[l]; + auto *zeros = new double[l]; + auto *ones = new schar[l]; + auto *C = new double[l]; int i; double nu_l = 0; for(i=0;iW[i]; nu_l += C[i] * param->nu; } i = 0; while(nu_l > 0) { alpha[i] = min(C[i],nu_l); nu_l -= alpha[i]; ++i; } for(;ieps, si, param->shrinking); delete[] C; delete[] zeros; delete[] ones; } static void solve_epsilon_svr( const svm_problem *prob, const svm_parameter *param, double *alpha, Solver::SolutionInfo* si) { int l = prob->l; - double *alpha2 = new double[2*l]; - double *linear_term = new double[2*l]; - double *C = new double[2*l]; - schar *y = new schar[2*l]; + auto *alpha2 = new double[2*l]; + auto *linear_term = new double[2*l]; + auto *C = new double[2*l]; + auto *y = new schar[2*l]; int i; for(i=0;ip - prob->y[i]; y[i] = 1; C[i] = prob->W[i]*param->C; alpha2[i+l] = 0; linear_term[i+l] = param->p + prob->y[i]; y[i+l] = -1; C[i+l] = prob->W[i]*param->C; } Solver s; s.Solve(2*l, SVR_Q(*prob,*param), linear_term, y, alpha2, C, param->eps, si, param->shrinking); double sum_alpha = 0; for(i=0;iC*l)); delete[] alpha2; delete[] linear_term; delete[] C; delete[] y; } static void solve_nu_svr( const svm_problem *prob, const svm_parameter *param, double *alpha, Solver::SolutionInfo* si) { int l = prob->l; - double *C = new double[2*l]; - double *alpha2 = new double[2*l]; - double *linear_term = new double[2*l]; - schar *y = new schar[2*l]; + auto *C = new double[2*l]; + auto *alpha2 = new double[2*l]; + auto *linear_term = new double[2*l]; + auto *y = new schar[2*l]; int i; double sum = 0; for(i=0;iW[i]*param->C; sum += C[i] * param->nu; } sum /= 2; for(i=0;iy[i]; y[i] = 1; linear_term[i+l] = prob->y[i]; y[i+l] = -1; } Solver_NU s; s.Solve(2*l, SVR_Q(*prob,*param), linear_term, y, alpha2, C, param->eps, si, param->shrinking); info("epsilon = %f\n",-si->r); for(i=0;il); + auto *alpha = Malloc(double,prob->l); Solver::SolutionInfo si; switch(param->svm_type) { case C_SVC: si.upper_bound = Malloc(double,prob->l); solve_c_svc(prob,param,alpha,&si,Cp,Cn); break; case NU_SVC: si.upper_bound = Malloc(double,prob->l); solve_nu_svc(prob,param,alpha,&si); break; case ONE_CLASS: si.upper_bound = Malloc(double,prob->l); solve_one_class(prob,param,alpha,&si); break; case EPSILON_SVR: si.upper_bound = Malloc(double,2*prob->l); solve_epsilon_svr(prob,param,alpha,&si); break; case NU_SVR: si.upper_bound = Malloc(double,2*prob->l); solve_nu_svr(prob,param,alpha,&si); break; } info("obj = %f, rho = %f\n",si.obj,si.rho); // output SVs int nSV = 0; int nBSV = 0; for(int i=0;il;i++) { if(fabs(alpha[i]) > 0) { ++nSV; if(prob->y[i] > 0) { if(fabs(alpha[i]) >= si.upper_bound[i]) ++nBSV; } else { if(fabs(alpha[i]) >= si.upper_bound[i]) ++nBSV; } } } free(si.upper_bound); info("nSV = %d, nBSV = %d\n",nSV,nBSV); decision_function f; f.alpha = alpha; f.rho = si.rho; return f; } // Platt's binary SVM Probablistic Output: an improvement from Lin et al. static void sigmoid_train( int l, const double *dec_values, const double *labels, double& A, double& B) { double prior1=0, prior0 = 0; int i; for (i=0;i 0) prior1+=1; else prior0+=1; int max_iter=100; // Maximal number of iterations double min_step=1e-10; // Minimal step taken in line search double sigma=1e-12; // For numerically strict PD of Hessian double eps=1e-5; double hiTarget=(prior1+1.0)/(prior1+2.0); double loTarget=1/(prior0+2.0); - double *t=Malloc(double,l); + auto *t=Malloc(double,l); double fApB,p,q,h11,h22,h21,g1,g2,det,dA,dB,gd,stepsize; double newA,newB,newf,d1,d2; int iter; // Initial Point and Initial Fun Value A=0.0; B=log((prior0+1.0)/(prior1+1.0)); double fval = 0.0; for (i=0;i0) t[i]=hiTarget; else t[i]=loTarget; fApB = dec_values[i]*A+B; if (fApB>=0) fval += t[i]*fApB + log(1+exp(-fApB)); else fval += (t[i] - 1)*fApB +log(1+exp(fApB)); } for (iter=0;iter= 0) { p=exp(-fApB)/(1.0+exp(-fApB)); q=1.0/(1.0+exp(-fApB)); } else { p=1.0/(1.0+exp(fApB)); q=exp(fApB)/(1.0+exp(fApB)); } d2=p*q; h11+=dec_values[i]*dec_values[i]*d2; h22+=d2; h21+=dec_values[i]*d2; d1=t[i]-p; g1+=dec_values[i]*d1; g2+=d1; } // Stopping Criteria if (fabs(g1)= min_step) { newA = A + stepsize * dA; newB = B + stepsize * dB; // New function value newf = 0.0; for (i=0;i= 0) newf += t[i]*fApB + log(1+exp(-fApB)); else newf += (t[i] - 1)*fApB +log(1+exp(fApB)); } // Check sufficient decrease if (newf=max_iter) info("Reaching maximal iterations in two-class probability estimates\n"); free(t); } static double sigmoid_predict(double decision_value, double A, double B) { double fApB = decision_value*A+B; // 1-p used later; avoid catastrophic cancellation if (fApB >= 0) return exp(-fApB)/(1.0+exp(-fApB)); else return 1.0/(1+exp(fApB)) ; } // Method 2 from the multiclass_prob paper by Wu, Lin, and Weng static void multiclass_probability(int k, double **r, double *p) { int t,j; int iter = 0, max_iter=max(100,k); - double **Q=Malloc(double *,k); - double *Qp=Malloc(double,k); + auto **Q=Malloc(double *,k); + auto *Qp=Malloc(double,k); double pQp, eps=0.005/k; for (t=0;tmax_error) max_error=error; } if (max_error=max_iter) info("Exceeds max_iter in multiclass_prob\n"); for(t=0;tl); - double *dec_values = Malloc(double,prob->l); + auto *perm = Malloc(int,prob->l); + auto *dec_values = Malloc(double,prob->l); // random shuffle for(i=0;il;i++) perm[i]=i; for(i=0;il;i++) { int j = i+rand()%(prob->l-i); swap(perm[i],perm[j]); } for(i=0;il/nr_fold; int end = (i+1)*prob->l/nr_fold; int j,k; struct svm_problem subprob; subprob.l = prob->l-(end-begin); subprob.x = Malloc(struct svm_node*,subprob.l); subprob.y = Malloc(double,subprob.l); subprob.W = Malloc(double,subprob.l); k=0; for(j=0;jx[perm[j]]; subprob.y[k] = prob->y[perm[j]]; subprob.W[k] = prob->W[perm[j]]; ++k; } for(j=end;jl;j++) { subprob.x[k] = prob->x[perm[j]]; subprob.y[k] = prob->y[perm[j]]; subprob.W[k] = prob->W[perm[j]]; ++k; } int p_count=0,n_count=0; for(j=0;j0) p_count++; else n_count++; if(p_count==0 && n_count==0) for(j=begin;j 0 && n_count == 0) for(j=begin;j 0) for(j=begin;jx[perm[j]],&(dec_values[perm[j]])); // ensure +1 -1 order; reason not using CV subroutine dec_values[perm[j]] *= submodel->label[0]; } svm_free_and_destroy_model(&submodel); svm_destroy_param(&subparam); } free(subprob.x); free(subprob.y); free(subprob.W); } sigmoid_train(prob->l,dec_values,prob->y,probA,probB); free(dec_values); free(perm); } // Return parameter of a Laplace distribution static double svm_svr_probability( const svm_problem *prob, const svm_parameter *param) { int i; int nr_fold = 5; - double *ymv = Malloc(double,prob->l); + auto *ymv = Malloc(double,prob->l); double mae = 0; svm_parameter newparam = *param; newparam.probability = 0; svm_cross_validation(prob,&newparam,nr_fold,ymv); for(i=0;il;i++) { ymv[i]=prob->y[i]-ymv[i]; mae += fabs(ymv[i]); } mae /= prob->l; double std=sqrt(2*mae*mae); int count=0; mae=0; for(i=0;il;i++) if (fabs(ymv[i]) > 5*std) count=count+1; else mae+=fabs(ymv[i]); mae /= (prob->l-count); info("Prob. model for test data: target value = predicted value + z,\nz: Laplace distribution e^(-|z|/sigma)/(2sigma),sigma= %g\n",mae); free(ymv); return mae; } // label: label name, start: begin of each class, count: #data of classes, perm: indices to the original data // perm, length l, must be allocated before calling this subroutine static void svm_group_classes(const svm_problem *prob, int *nr_class_ret, int **label_ret, int **start_ret, int **count_ret, int *perm) { int l = prob->l; int max_nr_class = 16; int nr_class = 0; - int *label = Malloc(int,max_nr_class); - int *count = Malloc(int,max_nr_class); - int *data_label = Malloc(int,l); + auto *label = Malloc(int,max_nr_class); + auto *count = Malloc(int,max_nr_class); + auto *data_label = Malloc(int,l); int i; for(i=0;iy[i]; + auto this_label = (int)prob->y[i]; int j; for(j=0;j 0. // static void remove_zero_weight(svm_problem *newprob, const svm_problem *prob) { int i; int l = 0; for(i=0;il;i++) if(prob->W[i] > 0) l++; *newprob = *prob; newprob->l = l; newprob->x = Malloc(svm_node*,l); newprob->y = Malloc(double,l); newprob->W = Malloc(double,l); int j = 0; for(i=0;il;i++) if(prob->W[i] > 0) { newprob->x[j] = prob->x[i]; newprob->y[j] = prob->y[i]; newprob->W[j] = prob->W[i]; j++; } } // // Interface functions // svm_model *svm_train(const svm_problem *prob, const svm_parameter *param) { svm_problem newprob; remove_zero_weight(&newprob, prob); prob = &newprob; - svm_model *model = Malloc(svm_model,1); + auto *model = Malloc(svm_model,1); model->param = *param; model->free_sv = 0; // XXX if(param->svm_type == ONE_CLASS || param->svm_type == EPSILON_SVR || param->svm_type == NU_SVR) { // regression or one-class-svm model->nr_class = 2; model->label = nullptr; model->nSV = nullptr; model->probA = nullptr; model->probB = nullptr; model->sv_coef = Malloc(double *,1); if(param->probability && (param->svm_type == EPSILON_SVR || param->svm_type == NU_SVR)) { model->probA = Malloc(double,1); model->probA[0] = svm_svr_probability(prob,param); } decision_function f = svm_train_one(prob,param,0,0); model->rho = Malloc(double,1); model->rho[0] = f.rho; int nSV = 0; int i; for(i=0;il;i++) if(fabs(f.alpha[i]) > 0) ++nSV; model->l = nSV; model->SV = Malloc(svm_node *,nSV); model->sv_coef[0] = Malloc(double,nSV); model->sv_indices = Malloc(int,nSV); int j = 0; for(i=0;il;i++) if(fabs(f.alpha[i]) > 0) { model->SV[j] = prob->x[i]; model->sv_coef[0][j] = f.alpha[i]; model->sv_indices[j] = i+1; ++j; } free(f.alpha); } else { // classification int l = prob->l; int nr_class; int *label = nullptr; int *start = nullptr; int *count = nullptr; - int *perm = Malloc(int,l); + auto *perm = Malloc(int,l); // group training data of the same class svm_group_classes(prob,&nr_class,&label,&start,&count,perm); if(nr_class == 1) info("WARNING: training data in only one class. See README for details.\n"); - svm_node **x = Malloc(svm_node *,l); + auto **x = Malloc(svm_node *,l); double *W; W = Malloc(double,l); int i; for(i=0;ix[perm[i]]; W[i] = prob->W[perm[i]]; } // calculate weighted C - double *weighted_C = Malloc(double, nr_class); + auto *weighted_C = Malloc(double, nr_class); for(i=0;iC; for(i=0;inr_weight;i++) { int j; for(j=0;jweight_label[i] == label[j]) break; if(j == nr_class) fprintf(stderr,"WARNING: class label %d specified in weight is not found\n", param->weight_label[i]); else weighted_C[j] *= param->weight[i]; } // train k*(k-1)/2 models - bool *nonzero = Malloc(bool,l); + auto *nonzero = Malloc(bool,l); for(i=0;iprobability) { probA=Malloc(double,nr_class*(nr_class-1)/2); probB=Malloc(double,nr_class*(nr_class-1)/2); } int p = 0; for(i=0;iprobability) svm_binary_svc_probability(&sub_prob,param,weighted_C[i],weighted_C[j],probA[p],probB[p]); f[p] = svm_train_one(&sub_prob,param,weighted_C[i],weighted_C[j]); for(k=0;k 0) nonzero[si+k] = true; for(k=0;k 0) nonzero[sj+k] = true; free(sub_prob.x); free(sub_prob.y); free(sub_prob.W); ++p; } // build output model->nr_class = nr_class; model->label = Malloc(int,nr_class); for(i=0;ilabel[i] = label[i]; model->rho = Malloc(double,nr_class*(nr_class-1)/2); for(i=0;irho[i] = f[i].rho; if(param->probability) { model->probA = Malloc(double,nr_class*(nr_class-1)/2); model->probB = Malloc(double,nr_class*(nr_class-1)/2); for(i=0;iprobA[i] = probA[i]; model->probB[i] = probB[i]; } } else { model->probA=nullptr; model->probB=nullptr; } int total_sv = 0; - int *nz_count = Malloc(int,nr_class); + auto *nz_count = Malloc(int,nr_class); model->nSV = Malloc(int,nr_class); for(i=0;inSV[i] = nSV; nz_count[i] = nSV; } info("Total nSV = %d\n",total_sv); model->l = total_sv; model->SV = Malloc(svm_node *,total_sv); model->sv_indices = Malloc(int,total_sv); p = 0; for(i=0;iSV[p] = x[i]; model->sv_indices[p++] = perm[i] + 1; } - int *nz_start = Malloc(int,nr_class); + auto *nz_start = Malloc(int,nr_class); nz_start[0] = 0; for(i=1;isv_coef = Malloc(double *,nr_class-1); for(i=0;isv_coef[i] = Malloc(double,total_sv); p = 0; for(i=0;isv_coef[j-1][q++] = f[p].alpha[k]; q = nz_start[j]; for(k=0;ksv_coef[i][q++] = f[p].alpha[ci+k]; ++p; } free(label); free(probA); free(probB); free(count); free(perm); free(start); free(W); free(x); free(weighted_C); free(nonzero); for(i=0;il; - int *perm = Malloc(int,l); + auto *perm = Malloc(int,l); int nr_class; if (nr_fold > l) { nr_fold = l; fprintf(stderr,"WARNING: # folds > # data. Will use # folds = # data instead (i.e., leave-one-out cross validation)\n"); } fold_start = Malloc(int,nr_fold+1); // stratified cv may not give leave-one-out rate // Each class to l folds -> some folds may have zero elements if((param->svm_type == C_SVC || param->svm_type == NU_SVC) && nr_fold < l) { int *start = nullptr; int *label = nullptr; int *count = nullptr; svm_group_classes(prob,&nr_class,&label,&start,&count,perm); // random shuffle and then data grouped by fold using the array perm - int *fold_count = Malloc(int,nr_fold); + auto *fold_count = Malloc(int,nr_fold); int c; - int *index = Malloc(int,l); + auto *index = Malloc(int,l); for(i=0;ix[perm[j]]; subprob.y[k] = prob->y[perm[j]]; subprob.W[k] = prob->W[perm[j]]; ++k; } for(j=end;jx[perm[j]]; subprob.y[k] = prob->y[perm[j]]; subprob.W[k] = prob->W[perm[j]]; ++k; } struct svm_model *submodel = svm_train(&subprob,param); if(param->probability && (param->svm_type == C_SVC || param->svm_type == NU_SVC)) { - double *prob_estimates=Malloc(double,svm_get_nr_class(submodel)); + auto *prob_estimates=Malloc(double,svm_get_nr_class(submodel)); for(j=begin;jx[perm[j]],prob_estimates); free(prob_estimates); } else for(j=begin;jx[perm[j]]); svm_free_and_destroy_model(&submodel); free(subprob.x); free(subprob.y); free(subprob.W); } free(fold_start); free(perm); } int svm_get_svm_type(const svm_model *model) { return model->param.svm_type; } int svm_get_nr_class(const svm_model *model) { return model->nr_class; } void svm_get_labels(const svm_model *model, int* label) { if (model->label != nullptr) for(int i=0;inr_class;i++) label[i] = model->label[i]; } void svm_get_sv_indices(const svm_model *model, int* indices) { if (model->sv_indices != nullptr) for(int i=0;il;i++) indices[i] = model->sv_indices[i]; } int svm_get_nr_sv(const svm_model *model) { return model->l; } double svm_get_svr_probability(const svm_model *model) { if ((model->param.svm_type == EPSILON_SVR || model->param.svm_type == NU_SVR) && model->probA!=nullptr) return model->probA[0]; else { fprintf(stderr,"Model doesn't contain information for SVR probability inference\n"); return 0; } } double svm_predict_values(const svm_model *model, const svm_node *x, double* dec_values) { int i; if(model->param.svm_type == ONE_CLASS || model->param.svm_type == EPSILON_SVR || model->param.svm_type == NU_SVR) { double *sv_coef = model->sv_coef[0]; double sum = 0; for(i=0;il;i++) sum += sv_coef[i] * Kernel::k_function(x,model->SV[i],model->param); sum -= model->rho[0]; *dec_values = sum; if(model->param.svm_type == ONE_CLASS) return (sum>0)?1:-1; else return sum; } else { int nr_class = model->nr_class; int l = model->l; - double *kvalue = Malloc(double,l); + auto *kvalue = Malloc(double,l); for(i=0;iSV[i],model->param); - int *start = Malloc(int,nr_class); + auto *start = Malloc(int,nr_class); start[0] = 0; for(i=1;inSV[i-1]; - int *vote = Malloc(int,nr_class); + auto *vote = Malloc(int,nr_class); for(i=0;inSV[i]; int cj = model->nSV[j]; int k; double *coef1 = model->sv_coef[j-1]; double *coef2 = model->sv_coef[i]; for(k=0;krho[p]; dec_values[p] = sum; if(dec_values[p] > 0) ++vote[i]; else ++vote[j]; p++; } int vote_max_idx = 0; for(i=1;i vote[vote_max_idx]) vote_max_idx = i; free(kvalue); free(start); free(vote); return model->label[vote_max_idx]; } } double svm_predict(const svm_model *model, const svm_node *x) { int nr_class = model->nr_class; double *dec_values; if(model->param.svm_type == ONE_CLASS || model->param.svm_type == EPSILON_SVR || model->param.svm_type == NU_SVR) dec_values = Malloc(double, 1); else dec_values = Malloc(double, nr_class*(nr_class-1)/2); double pred_result = svm_predict_values(model, x, dec_values); free(dec_values); return pred_result; } double svm_predict_probability( const svm_model *model, const svm_node *x, double *prob_estimates) { if ((model->param.svm_type == C_SVC || model->param.svm_type == NU_SVC) && model->probA!=nullptr && model->probB!=nullptr) { int i; int nr_class = model->nr_class; - double *dec_values = Malloc(double, nr_class*(nr_class-1)/2); + auto *dec_values = Malloc(double, nr_class*(nr_class-1)/2); svm_predict_values(model, x, dec_values); double min_prob=1e-7; - double **pairwise_prob=Malloc(double *,nr_class); + auto **pairwise_prob=Malloc(double *,nr_class); for(i=0;iprobA[k],model->probB[k]),min_prob),1-min_prob); pairwise_prob[j][i]=1-pairwise_prob[i][j]; k++; } multiclass_probability(nr_class,pairwise_prob,prob_estimates); int prob_max_idx = 0; for(i=1;i prob_estimates[prob_max_idx]) prob_max_idx = i; for(i=0;ilabel[prob_max_idx]; } else return svm_predict(model, x); } static const char *svm_type_table[] = { "c_svc","nu_svc","one_class","epsilon_svr","nu_svr",nullptr }; static const char *kernel_type_table[]= { "linear","polynomial","rbf","sigmoid","precomputed",nullptr }; int svm_save_model(const char *model_file_name, const svm_model *model) { FILE *fp = fopen(model_file_name,"w"); if(fp==nullptr) return -1; mitk::LocaleSwitch localeSwitch("C"); const svm_parameter& param = model->param; fprintf(fp,"svm_type %s\n", svm_type_table[param.svm_type]); fprintf(fp,"kernel_type %s\n", kernel_type_table[param.kernel_type]); if(param.kernel_type == POLY) fprintf(fp,"degree %d\n", param.degree); if(param.kernel_type == POLY || param.kernel_type == RBF || param.kernel_type == SIGMOID) fprintf(fp,"gamma %g\n", param.gamma); if(param.kernel_type == POLY || param.kernel_type == SIGMOID) fprintf(fp,"coef0 %g\n", param.coef0); int nr_class = model->nr_class; int l = model->l; fprintf(fp, "nr_class %d\n", nr_class); fprintf(fp, "total_sv %d\n",l); { fprintf(fp, "rho"); for(int i=0;irho[i]); fprintf(fp, "\n"); } if(model->label) { fprintf(fp, "label"); for(int i=0;ilabel[i]); fprintf(fp, "\n"); } if(model->probA) // regression has probA only { fprintf(fp, "probA"); for(int i=0;iprobA[i]); fprintf(fp, "\n"); } if(model->probB) { fprintf(fp, "probB"); for(int i=0;iprobB[i]); fprintf(fp, "\n"); } if(model->nSV) { fprintf(fp, "nr_sv"); for(int i=0;inSV[i]); fprintf(fp, "\n"); } fprintf(fp, "SV\n"); const double * const *sv_coef = model->sv_coef; const svm_node * const *SV = model->SV; for(int i=0;ivalue)); else while(p->index != -1) { fprintf(fp,"%d:%.8g ",p->index,p->value); p++; } fprintf(fp, "\n"); } if (ferror(fp) != 0 || fclose(fp) != 0) return -1; else return 0; } static char *line = nullptr; static int max_line_len; static char* readline(FILE *input) { int len; if(fgets(line,max_line_len,input) == nullptr) return nullptr; while(strrchr(line,'\n') == nullptr) { max_line_len *= 2; line = (char *) realloc(line,max_line_len); len = (int) strlen(line); if(fgets(line+len,max_line_len-len,input) == nullptr) break; } return line; } // // FSCANF helps to handle fscanf failures. // Its do-while block avoids the ambiguity when // if (...) // FSCANF(); // is used // #define FSCANF(_stream, _format, _var) do{ if (fscanf(_stream, _format, _var) != 1) return false; }while(0) bool read_model_header(FILE *fp, svm_model* model) { svm_parameter& param = model->param; char cmd[81]; while(1) { FSCANF(fp,"%80s",cmd); if(strcmp(cmd,"svm_type")==0) { FSCANF(fp,"%80s",cmd); int i; for(i=0;svm_type_table[i];i++) { if(strcmp(svm_type_table[i],cmd)==0) { param.svm_type=i; break; } } if(svm_type_table[i] == nullptr) { fprintf(stderr,"unknown svm type.\n"); return false; } } else if(strcmp(cmd,"kernel_type")==0) { FSCANF(fp,"%80s",cmd); int i; for(i=0;kernel_type_table[i];i++) { if(strcmp(kernel_type_table[i],cmd)==0) { param.kernel_type=i; break; } } if(kernel_type_table[i] == nullptr) { fprintf(stderr,"unknown kernel function.\n"); return false; } } else if(strcmp(cmd,"degree")==0) FSCANF(fp,"%d",¶m.degree); else if(strcmp(cmd,"gamma")==0) FSCANF(fp,"%lf",¶m.gamma); else if(strcmp(cmd,"coef0")==0) FSCANF(fp,"%lf",¶m.coef0); else if(strcmp(cmd,"nr_class")==0) FSCANF(fp,"%d",&model->nr_class); else if(strcmp(cmd,"total_sv")==0) FSCANF(fp,"%d",&model->l); else if(strcmp(cmd,"rho")==0) { int n = model->nr_class * (model->nr_class-1)/2; model->rho = Malloc(double,n); for(int i=0;irho[i]); } else if(strcmp(cmd,"label")==0) { int n = model->nr_class; model->label = Malloc(int,n); for(int i=0;ilabel[i]); } else if(strcmp(cmd,"probA")==0) { int n = model->nr_class * (model->nr_class-1)/2; model->probA = Malloc(double,n); for(int i=0;iprobA[i]); } else if(strcmp(cmd,"probB")==0) { int n = model->nr_class * (model->nr_class-1)/2; model->probB = Malloc(double,n); for(int i=0;iprobB[i]); } else if(strcmp(cmd,"nr_sv")==0) { int n = model->nr_class; model->nSV = Malloc(int,n); for(int i=0;inSV[i]); } else if(strcmp(cmd,"SV")==0) { while(1) { int c = getc(fp); if(c==EOF || c=='\n') break; } break; } else { fprintf(stderr,"unknown text in model file: [%s]\n",cmd); return false; } } return true; } svm_model *svm_load_model(const char *model_file_name) { FILE *fp = fopen(model_file_name,"rb"); if(fp==nullptr) return nullptr; mitk::LocaleSwitch localeSwitch("C"); // read parameters - svm_model *model = Malloc(svm_model,1); + auto *model = Malloc(svm_model,1); model->rho = nullptr; model->probA = nullptr; model->probB = nullptr; model->sv_indices = nullptr; model->label = nullptr; model->nSV = nullptr; // read header if (!read_model_header(fp, model)) { fprintf(stderr, "ERROR: fscanf failed to read model\n"); free(model->rho); free(model->label); free(model->nSV); free(model); return nullptr; } // read sv_coef and SV int elements = 0; long pos = ftell(fp); max_line_len = 1024; line = Malloc(char,max_line_len); char *p,*endptr,*idx,*val; while(readline(fp)!=nullptr) { p = strtok(line,":"); while(1) { p = strtok(nullptr,":"); if(p == nullptr) break; ++elements; } } elements += model->l; fseek(fp,pos,SEEK_SET); int m = model->nr_class - 1; int l = model->l; model->sv_coef = Malloc(double *,m); int i; for(i=0;isv_coef[i] = Malloc(double,l); model->SV = Malloc(svm_node*,l); svm_node *x_space = nullptr; if(l>0) x_space = Malloc(svm_node,elements); int j=0; for(i=0;iSV[i] = &x_space[j]; p = strtok(line, " \t"); model->sv_coef[0][i] = strtod(p,&endptr); for(int k=1;ksv_coef[k][i] = strtod(p,&endptr); } while(1) { idx = strtok(nullptr, ":"); val = strtok(nullptr, " \t"); if(val == nullptr) break; x_space[j].index = (int) strtol(idx,&endptr,10); x_space[j].value = strtod(val,&endptr); ++j; } x_space[j++].index = -1; } free(line); if (ferror(fp) != 0 || fclose(fp) != 0) return nullptr; model->free_sv = 1; // XXX return model; } void svm_free_model_content(svm_model* model_ptr) { if(model_ptr->free_sv && model_ptr->l > 0 && model_ptr->SV != nullptr) free((void *)(model_ptr->SV[0])); if(model_ptr->sv_coef) { for(int i=0;inr_class-1;i++) free(model_ptr->sv_coef[i]); } free(model_ptr->SV); model_ptr->SV = nullptr; free(model_ptr->sv_coef); model_ptr->sv_coef = nullptr; free(model_ptr->rho); model_ptr->rho = nullptr; free(model_ptr->label); model_ptr->label= nullptr; free(model_ptr->probA); model_ptr->probA = nullptr; free(model_ptr->probB); model_ptr->probB= nullptr; free(model_ptr->sv_indices); model_ptr->sv_indices = nullptr; free(model_ptr->nSV); model_ptr->nSV = nullptr; } void svm_free_and_destroy_model(svm_model** model_ptr_ptr) { if(model_ptr_ptr != nullptr && *model_ptr_ptr != nullptr) { svm_free_model_content(*model_ptr_ptr); free(*model_ptr_ptr); *model_ptr_ptr = nullptr; } } void svm_destroy_param(svm_parameter* param) { free(param->weight_label); free(param->weight); } const char *svm_check_parameter(const svm_problem *prob, const svm_parameter *param) { // svm_type int svm_type = param->svm_type; if(svm_type != C_SVC && svm_type != NU_SVC && svm_type != ONE_CLASS && svm_type != EPSILON_SVR && svm_type != NU_SVR) return "unknown svm type"; // kernel_type, degree int kernel_type = param->kernel_type; if(kernel_type != LINEAR && kernel_type != POLY && kernel_type != RBF && kernel_type != SIGMOID && kernel_type != PRECOMPUTED) return "unknown kernel type"; if(param->gamma < 0) return "gamma < 0"; if(param->degree < 0) return "degree of polynomial kernel < 0"; // cache_size,eps,C,nu,p,shrinking if(param->cache_size <= 0) return "cache_size <= 0"; if(param->eps <= 0) return "eps <= 0"; if(svm_type == C_SVC || svm_type == EPSILON_SVR || svm_type == NU_SVR) if(param->C <= 0) return "C <= 0"; if(svm_type == NU_SVC || svm_type == ONE_CLASS || svm_type == NU_SVR) if(param->nu <= 0 || param->nu > 1) return "nu <= 0 or nu > 1"; if(svm_type == EPSILON_SVR) if(param->p < 0) return "p < 0"; if(param->shrinking != 0 && param->shrinking != 1) return "shrinking != 0 and shrinking != 1"; if(param->probability != 0 && param->probability != 1) return "probability != 0 and probability != 1"; if(param->probability == 1 && svm_type == ONE_CLASS) return "one-class SVM probability output not supported yet"; // check whether nu-svc is feasible if(svm_type == NU_SVC) { int l = prob->l; int max_nr_class = 16; int nr_class = 0; - int *label = Malloc(int,max_nr_class); - double *count = Malloc(double,max_nr_class); + auto *label = Malloc(int,max_nr_class); + auto *count = Malloc(double,max_nr_class); int i; for(i=0;iy[i]; + auto this_label = (int)prob->y[i]; int j; for(j=0;jW[i]; break; } if(j == nr_class) { if(nr_class == max_nr_class) { max_nr_class *= 2; label = (int *)realloc(label,max_nr_class*sizeof(int)); count = (double *)realloc(count,max_nr_class*sizeof(double)); } label[nr_class] = this_label; count[nr_class] = prob->W[i]; ++nr_class; } } for(i=0;inu*(n1+n2)/2 > min(n1,n2)) { free(label); free(count); return "specified nu is infeasible"; } } } free(label); free(count); } return nullptr; } int svm_check_probability_model(const svm_model *model) { return ((model->param.svm_type == C_SVC || model->param.svm_type == NU_SVC) && model->probA!=nullptr && model->probB!=nullptr) || ((model->param.svm_type == EPSILON_SVR || model->param.svm_type == NU_SVR) && model->probA!=nullptr); } void svm_set_print_string_function(void (*print_func)(const char *)) { if(print_func == nullptr) svm_print_string = &print_string_stdout; else svm_print_string = print_func; } diff --git a/Modules/Classification/CLLibSVM/test/mitkLibSVMClassifierTest.cpp b/Modules/Classification/CLLibSVM/test/mitkLibSVMClassifierTest.cpp index eec7e4b335..83dc871db9 100644 --- a/Modules/Classification/CLLibSVM/test/mitkLibSVMClassifierTest.cpp +++ b/Modules/Classification/CLLibSVM/test/mitkLibSVMClassifierTest.cpp @@ -1,314 +1,314 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include #include #include "mitkIOUtil.h" #include "itkArray2D.h" #include #include #include #include #include #include #include //#include class mitkLibSVMClassifierTestSuite : public mitk::TestFixture { CPPUNIT_TEST_SUITE(mitkLibSVMClassifierTestSuite); MITK_TEST(TrainSVMClassifier_MatlabDataSet_shouldReturnTrue); MITK_TEST(TrainSVMClassifier_BreastCancerDataSet_shouldReturnTrue); CPPUNIT_TEST_SUITE_END(); private: typedef Eigen::Matrix MatrixDoubleType; typedef Eigen::Matrix MatrixIntType; Eigen::MatrixXd m_TrainingMatrixX; Eigen::MatrixXi m_TrainingLabelMatrixY; Eigen::MatrixXd m_TestXPredict; Eigen::MatrixXi m_TestYPredict; mitk::LibSVMClassifier::Pointer classifier; public: /*Reading an file, which includes the trainingdataset and the testdataset, and convert the content of the file into an 2dim matrixpair. There are an delimiter, which separates the matrix into an trainingmatrix and testmatrix */ template std::pair,Eigen::Matrix >convertCSVToMatrix(const std::string &path, char delimiter,double range, bool isXMatrix) { typename itk::CSVArray2DFileReader::Pointer fr = itk::CSVArray2DFileReader::New(); fr->SetFileName(path); fr->SetFieldDelimiterCharacter(delimiter); fr->HasColumnHeadersOff(); fr->HasRowHeadersOff(); fr->Parse(); try{ fr->Update(); }catch(itk::ExceptionObject& ex){ cout << "Exception caught!" << std::endl; cout << ex << std::endl; } typename itk::CSVArray2DDataObject::Pointer p = fr->GetOutput(); unsigned int maxrowrange = p->GetMatrix().rows(); unsigned int c = p->GetMatrix().cols(); - unsigned int percentRange = (unsigned int)(maxrowrange*range); + auto percentRange = (unsigned int)(maxrowrange*range); if(isXMatrix == true) { Eigen::Matrix trainMatrixX(percentRange,c); Eigen::Matrix testMatrixXPredict(maxrowrange-percentRange,c); for(unsigned int row = 0; row < percentRange; row++){ for(unsigned int col = 0; col < c; col++){ trainMatrixX(row,col) = p->GetData(row,col); } } for(unsigned int row = percentRange; row < maxrowrange; row++){ for(unsigned int col = 0; col < c; col++){ testMatrixXPredict(row-percentRange,col) = p->GetData(row,col); } } return std::make_pair(trainMatrixX,testMatrixXPredict); } else { Eigen::Matrix trainLabelMatrixY(percentRange,c); Eigen::Matrix testMatrixYPredict(maxrowrange-percentRange,c); for(unsigned int row = 0; row < percentRange; row++){ for(unsigned int col = 0; col < c; col++){ trainLabelMatrixY(row,col) = p->GetData(row,col); } } for(unsigned int row = percentRange; row < maxrowrange; row++){ for(unsigned int col = 0; col < c; col++){ testMatrixYPredict(row-percentRange,col) = p->GetData(row,col); } } return std::make_pair(trainLabelMatrixY,testMatrixYPredict); } } /* Reading an csv-data and transfer the included datas into an matrix. */ template Eigen::Matrix readCsvData(const std::string &path, char delimiter) { typename itk::CSVArray2DFileReader::Pointer fr = itk::CSVArray2DFileReader::New(); fr->SetFileName(path); fr->SetFieldDelimiterCharacter(delimiter); fr->HasColumnHeadersOff(); fr->HasRowHeadersOff(); fr->Parse(); try{ fr->Update(); }catch(itk::ExceptionObject& ex){ cout << "Exception caught!" << std::endl; cout << ex << std::endl; } typename itk::CSVArray2DDataObject::Pointer p = fr->GetOutput(); unsigned int maxrowrange = p->GetMatrix().rows(); unsigned int maxcols = p->GetMatrix().cols(); Eigen::Matrix matrix(maxrowrange,maxcols); for(unsigned int rows = 0; rows < maxrowrange; rows++){ for(unsigned int cols = 0; cols < maxcols; cols++ ){ matrix(rows,cols) = p->GetData(rows,cols); } } return matrix; } /* Write the content of the array into an own csv-data in the following sequence: root.csv: 1 2 3 0 0 4 writen.csv: 1 1:2 2:3 3:0 4:0 5:4 */ template void writeMatrixToCsv(Eigen::Matrix paramMatrix,const std::string &path) { std::ofstream outputstream (path,std::ofstream::out); // 682 if(outputstream.is_open()){ for(int i = 0; i < paramMatrix.rows(); i++){ outputstream << paramMatrix(i,0); for(int j = 1; j < 11; j++){ outputstream << " " << j << ":" << paramMatrix(i,j); } outputstream << endl; } outputstream.close(); } else{ cout << "Unable to write into CSV" << endl; } } /* Train the classifier with an exampledataset of mattlab. Note: The included data are gaußan normaldistributed. */ void TrainSVMClassifier_MatlabDataSet_shouldReturnTrue() { /* Declarating an featurematrixdataset, the first matrix of the matrixpair is the trainingmatrix and the second one is the testmatrix.*/ std::pair matrixDouble; matrixDouble = convertCSVToMatrix(GetTestDataFilePath("Classification/FeaturematrixMatlab.csv"),';',0.5,true); m_TrainingMatrixX = matrixDouble.first; m_TestXPredict = matrixDouble.second; /* The declaration of the labelmatrixdataset is equivalent to the declaration of the featurematrixdataset.*/ std::pair matrixInt; matrixInt = convertCSVToMatrix(GetTestDataFilePath("Classification/LabelmatrixMatlab.csv"),';',0.5,false); m_TrainingLabelMatrixY = matrixInt.first; m_TestYPredict = matrixInt.second; classifier = mitk::LibSVMClassifier::New(); /* Setting of the SVM-Parameters*/ classifier->SetGamma(1/(double)(m_TrainingMatrixX.cols())); classifier->SetSvmType(0); classifier->SetKernelType(0); /* Train the classifier, by giving trainingdataset for the labels and features. The result in an colunmvector of the labels.*/ classifier->Train(m_TrainingMatrixX,m_TrainingLabelMatrixY); Eigen::MatrixXi classes = classifier->Predict(m_TestXPredict); /* Testing the matching between the calculated colunmvector and the result of the SVM */ unsigned int maxrows = classes.rows(); int count = 0; for (unsigned int i = 0; i < maxrows; i++) { if(classes(i, 0) == m_TestYPredict(i, 0)) ++count; } MITK_INFO << 100*count/(double)(maxrows) << "%"; MITK_TEST_CONDITION(isEqual(m_TestYPredict,classes),"Expected vector and occured vector match."); } // Method of testing for assertions. template bool isEqual(Eigen::Matrix expected, Eigen::Matrix actual) { bool isSimilar = true; unsigned int mrow = expected.rows(); unsigned int mcol = expected.cols(); for(unsigned int i = 0; i < mrow; i++){ for(unsigned int j = 0; j < mcol; j++){ if(expected(i,j) != actual(i,j)){ isSimilar = false; } } } return isSimilar; } // Method of intervalltesting template bool isIntervall(Eigen::Matrix expected, Eigen::Matrix actual, double lowrange, double toprange) { bool isInIntervall = false; int count = 0; unsigned int rowRange = expected.rows(); unsigned int colRange = expected.cols(); for(unsigned int i = 0; i < rowRange; i++){ for(unsigned int j = 0; j < colRange; j++){ if(expected(i,j) == actual(i,j)){ count++; } } double valueOfMatch = 100*count/(double)(rowRange); if((lowrange <= valueOfMatch) && (toprange >= valueOfMatch)){ isInIntervall = true; } } return isInIntervall; } /* Train the classifier with the dataset of breastcancer patients from the LibSVM Libary */ void TrainSVMClassifier_BreastCancerDataSet_shouldReturnTrue() { /* Declarating an featurematrixdataset, the first matrix of the matrixpair is the trainingmatrix and the second one is the testmatrix.*/ std::pair matrixDouble; matrixDouble = convertCSVToMatrix(GetTestDataFilePath("Classification/FeaturematrixBreastcancer.csv"),';',0.5,true); m_TrainingMatrixX = matrixDouble.first; m_TestXPredict = matrixDouble.second; /* The declaration of the labelmatrixdataset is equivalent to the declaration of the featurematrixdataset.*/ std::pair matrixInt; matrixInt = convertCSVToMatrix(GetTestDataFilePath("Classification/LabelmatrixBreastcancer.csv"),';',0.5,false); m_TrainingLabelMatrixY = matrixInt.first; m_TestYPredict = matrixInt.second; /* Setting of the SVM-Parameters*/ classifier = mitk::LibSVMClassifier::New(); classifier->SetGamma(1/(double)(m_TrainingMatrixX.cols())); classifier->SetSvmType(0); classifier->SetKernelType(2); /* Train the classifier, by giving trainingdataset for the labels and features. The result in an colunmvector of the labels.*/ classifier->Train(m_TrainingMatrixX,m_TrainingLabelMatrixY); Eigen::MatrixXi classes = classifier->Predict(m_TestXPredict); /* Testing the matching between the calculated colunmvector and the result of the SVM */ unsigned int maxrows = classes.rows(); int count = 0; for (unsigned int i = 0; i < maxrows; i++) { if (classes(i, 0) == m_TestYPredict(i, 0)) ++count; } MITK_INFO << 100*count/(double)(maxrows) << "%"; MITK_TEST_CONDITION(isIntervall(m_TestYPredict,classes,75,100),"Testvalue is in range."); } void TestThreadedDecisionForest() { } }; MITK_TEST_SUITE_REGISTRATION(mitkLibSVMClassifier) diff --git a/Modules/Classification/CLMRUtilities/src/MRNormalization/mitkMRNormLinearStatisticBasedFilter.cpp b/Modules/Classification/CLMRUtilities/src/MRNormalization/mitkMRNormLinearStatisticBasedFilter.cpp index f6894b5700..18c95cb868 100644 --- a/Modules/Classification/CLMRUtilities/src/MRNormalization/mitkMRNormLinearStatisticBasedFilter.cpp +++ b/Modules/Classification/CLMRUtilities/src/MRNormalization/mitkMRNormLinearStatisticBasedFilter.cpp @@ -1,147 +1,147 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkMRNormLinearStatisticBasedFilter.h" #include "mitkImageToItk.h" #include "mitkImageAccessByItk.h" #include "itkImageRegionIterator.h" // MITK #include #include #include // ITK #include #include mitk::MRNormLinearStatisticBasedFilter::MRNormLinearStatisticBasedFilter() : m_CenterMode(MRNormLinearStatisticBasedFilter::MEDIAN) { this->SetNumberOfIndexedInputs(3); this->SetNumberOfRequiredInputs(3); } mitk::MRNormLinearStatisticBasedFilter::~MRNormLinearStatisticBasedFilter() { } void mitk::MRNormLinearStatisticBasedFilter::SetMask( const mitk::Image* mask ) { // Process object is not const-correct so the const_cast is required here - Image* nonconstMask = const_cast< mitk::Image * >( mask ); + auto* nonconstMask = const_cast< mitk::Image * >( mask ); this->SetNthInput(1, nonconstMask ); } const mitk::Image* mitk::MRNormLinearStatisticBasedFilter::GetMask() const { return this->GetInput(1); } void mitk::MRNormLinearStatisticBasedFilter::GenerateInputRequestedRegion() { Superclass::GenerateInputRequestedRegion(); - mitk::Image* input = const_cast< mitk::Image * > ( this->GetInput() ); + auto* input = const_cast< mitk::Image * > ( this->GetInput() ); input->SetRequestedRegionToLargestPossibleRegion(); } void mitk::MRNormLinearStatisticBasedFilter::GenerateOutputInformation() { mitk::Image::ConstPointer input = this->GetInput(); mitk::Image::Pointer output = this->GetOutput(); itkDebugMacro(<<"GenerateOutputInformation()"); output->Initialize(input->GetPixelType(), *input->GetTimeGeometry()); output->SetPropertyList(input->GetPropertyList()->Clone()); } template < typename TPixel, unsigned int VImageDimension > void mitk::MRNormLinearStatisticBasedFilter::InternalComputeMask(itk::Image* itkImage) { // Define all necessary Types typedef itk::Image ImageType; typedef itk::Image MaskType; typedef itk::LabelStatisticsImageFilter FilterType; typedef itk::MinimumMaximumImageCalculator MinMaxComputerType; typename MaskType::Pointer itkMask0 = MaskType::New(); mitk::CastToItkImage(this->GetMask(), itkMask0); typename ImageType::Pointer outImage = ImageType::New(); mitk::CastToItkImage(this->GetOutput(0), outImage); typename MinMaxComputerType::Pointer minMaxComputer = MinMaxComputerType::New(); minMaxComputer->SetImage(itkImage); minMaxComputer->Compute(); typename FilterType::Pointer labelStatisticsImageFilter = FilterType::New(); labelStatisticsImageFilter->SetUseHistograms(true); labelStatisticsImageFilter->SetHistogramParameters(256, minMaxComputer->GetMinimum(),minMaxComputer->GetMaximum()); labelStatisticsImageFilter->SetInput( itkImage ); labelStatisticsImageFilter->SetLabelInput(itkMask0); labelStatisticsImageFilter->Update(); double median0 = labelStatisticsImageFilter->GetMedian(1); double mean0 = labelStatisticsImageFilter->GetMean(1); double stddev = labelStatisticsImageFilter->GetSigma(1); double modulo0=0; { auto histo = labelStatisticsImageFilter->GetHistogram(1); double maxFrequency=0; for (auto hIter=histo->Begin();hIter!=histo->End();++hIter) { if (maxFrequency < hIter.GetFrequency()) { maxFrequency = hIter.GetFrequency(); modulo0 = (histo->GetBinMin(0,hIter.GetInstanceIdentifier()) + histo->GetBinMax(0,hIter.GetInstanceIdentifier())) / 2.0; } } } double value0=0; switch (m_CenterMode) { case MRNormLinearStatisticBasedFilter::MEAN: value0=mean0; break; case MRNormLinearStatisticBasedFilter::MEDIAN: value0=median0; break; case MRNormLinearStatisticBasedFilter::MODE: value0=modulo0; break; } double offset = value0; double scaling = stddev; if (scaling < 0.0001) return; itk::ImageRegionIterator inIter(itkImage, itkImage->GetLargestPossibleRegion()); itk::ImageRegionIterator outIter(outImage, outImage->GetLargestPossibleRegion()); while (! inIter.IsAtEnd()) { TPixel value = inIter.Value(); outIter.Set((value - offset) / scaling); ++inIter; ++outIter; } } void mitk::MRNormLinearStatisticBasedFilter::GenerateData() { AccessByItk(GetInput(0),InternalComputeMask); } \ No newline at end of file diff --git a/Modules/Classification/CLMRUtilities/src/MRNormalization/mitkMRNormTwoRegionBasedFilter.cpp b/Modules/Classification/CLMRUtilities/src/MRNormalization/mitkMRNormTwoRegionBasedFilter.cpp index 7bef0a4a2c..028a404f33 100644 --- a/Modules/Classification/CLMRUtilities/src/MRNormalization/mitkMRNormTwoRegionBasedFilter.cpp +++ b/Modules/Classification/CLMRUtilities/src/MRNormalization/mitkMRNormTwoRegionBasedFilter.cpp @@ -1,188 +1,188 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkMRNormTwoRegionBasedFilter.h" #include "mitkImageToItk.h" #include "mitkImageAccessByItk.h" #include "itkImageRegionIterator.h" // MITK #include #include #include // ITK #include #include mitk::MRNormTwoRegionsBasedFilter::MRNormTwoRegionsBasedFilter() : m_Area1(MRNormTwoRegionsBasedFilter::MEDIAN), m_Area2(MRNormTwoRegionsBasedFilter::MEDIAN) { this->SetNumberOfIndexedInputs(3); this->SetNumberOfRequiredInputs(1); } mitk::MRNormTwoRegionsBasedFilter::~MRNormTwoRegionsBasedFilter() { } void mitk::MRNormTwoRegionsBasedFilter::SetMask1( const mitk::Image* mask ) { // Process object is not const-correct so the const_cast is required here - Image* nonconstMask = const_cast< mitk::Image * >( mask ); + auto* nonconstMask = const_cast< mitk::Image * >( mask ); this->SetNthInput(1, nonconstMask ); } void mitk::MRNormTwoRegionsBasedFilter::SetMask2( const mitk::Image* mask ) { // Process object is not const-correct so the const_cast is required here - Image* nonconstMask = const_cast< mitk::Image * >( mask ); + auto* nonconstMask = const_cast< mitk::Image * >( mask ); this->SetNthInput(2, nonconstMask ); } const mitk::Image* mitk::MRNormTwoRegionsBasedFilter::GetMask1() const { return this->GetInput(1); } const mitk::Image* mitk::MRNormTwoRegionsBasedFilter::GetMask2() const { return this->GetInput(2); } void mitk::MRNormTwoRegionsBasedFilter::GenerateInputRequestedRegion() { Superclass::GenerateInputRequestedRegion(); - mitk::Image* input = const_cast< mitk::Image * > ( this->GetInput() ); + auto* input = const_cast< mitk::Image * > ( this->GetInput() ); input->SetRequestedRegionToLargestPossibleRegion(); } void mitk::MRNormTwoRegionsBasedFilter::GenerateOutputInformation() { mitk::Image::ConstPointer input = this->GetInput(); mitk::Image::Pointer output = this->GetOutput(); itkDebugMacro(<<"GenerateOutputInformation()"); output->Initialize(input->GetPixelType(), *input->GetTimeGeometry()); output->SetPropertyList(input->GetPropertyList()->Clone()); } template < typename TPixel, unsigned int VImageDimension > void mitk::MRNormTwoRegionsBasedFilter::InternalComputeMask(itk::Image* itkImage) { // Define all necessary Types typedef itk::Image ImageType; typedef itk::Image MaskType; typedef itk::LabelStatisticsImageFilter FilterType; typedef itk::MinimumMaximumImageCalculator MinMaxComputerType; typename MaskType::Pointer itkMask0 = MaskType::New(); typename MaskType::Pointer itkMask1 = MaskType::New(); mitk::CastToItkImage(this->GetMask1(), itkMask0); mitk::CastToItkImage(this->GetMask2(), itkMask1); typename ImageType::Pointer outImage = ImageType::New(); mitk::CastToItkImage(this->GetOutput(0), outImage); typename MinMaxComputerType::Pointer minMaxComputer = MinMaxComputerType::New(); minMaxComputer->SetImage(itkImage); minMaxComputer->Compute(); typename FilterType::Pointer labelStatisticsImageFilter = FilterType::New(); labelStatisticsImageFilter->SetUseHistograms(true); labelStatisticsImageFilter->SetHistogramParameters(256, minMaxComputer->GetMinimum(),minMaxComputer->GetMaximum()); labelStatisticsImageFilter->SetInput( itkImage ); labelStatisticsImageFilter->SetLabelInput(itkMask0); labelStatisticsImageFilter->Update(); double median0 = labelStatisticsImageFilter->GetMedian(1); double mean0 = labelStatisticsImageFilter->GetMean(1); double modulo0=0; { auto histo = labelStatisticsImageFilter->GetHistogram(1); double maxFrequency=0; for (auto hIter=histo->Begin();hIter!=histo->End();++hIter) { if (maxFrequency < hIter.GetFrequency()) { maxFrequency = hIter.GetFrequency(); modulo0 = (histo->GetBinMin(0,hIter.GetInstanceIdentifier()) + histo->GetBinMax(0,hIter.GetInstanceIdentifier())) / 2.0; } } } labelStatisticsImageFilter->SetLabelInput(itkMask1); labelStatisticsImageFilter->Update(); double median1 = labelStatisticsImageFilter->GetMedian(1); double mean1 = labelStatisticsImageFilter->GetMean(1); double modulo1 = 0; { auto histo = labelStatisticsImageFilter->GetHistogram(1); double maxFrequency=0; for (auto hIter=histo->Begin();hIter!=histo->End();++hIter) { if (maxFrequency < hIter.GetFrequency()) { maxFrequency = hIter.GetFrequency(); modulo1 = (histo->GetBinMin(0,hIter.GetInstanceIdentifier()) + histo->GetBinMax(0,hIter.GetInstanceIdentifier())) / 2.0; } } } double value0=0; double value1=0; switch (m_Area1) { case MRNormTwoRegionsBasedFilter::MEAN: value0=mean0; break; case MRNormTwoRegionsBasedFilter::MEDIAN: value0=median0; break; case MRNormTwoRegionsBasedFilter::MODE: value0=modulo0; break; } switch (m_Area2) { case MRNormTwoRegionsBasedFilter::MEAN: value1=mean1; break; case MRNormTwoRegionsBasedFilter::MEDIAN: value1=median1; break; case MRNormTwoRegionsBasedFilter::MODE: value1=modulo1; break; } double offset = std::min(value0, value1); double scaling = std::max(value0, value1) - offset; if (scaling < 0.0001) return; itk::ImageRegionIterator inIter(itkImage, itkImage->GetLargestPossibleRegion()); itk::ImageRegionIterator outIter(outImage, outImage->GetLargestPossibleRegion()); while (! inIter.IsAtEnd()) { TPixel value = inIter.Value(); outIter.Set((value - offset) / scaling); ++inIter; ++outIter; } } void mitk::MRNormTwoRegionsBasedFilter::GenerateData() { AccessByItk(GetInput(0),InternalComputeMask); } \ No newline at end of file diff --git a/Modules/Classification/CLUtilities/include/itkMultiHistogramFilter.cpp b/Modules/Classification/CLUtilities/include/itkMultiHistogramFilter.cpp index 6dfd771416..159d92aef8 100644 --- a/Modules/Classification/CLUtilities/include/itkMultiHistogramFilter.cpp +++ b/Modules/Classification/CLUtilities/include/itkMultiHistogramFilter.cpp @@ -1,96 +1,96 @@ #ifndef itkMultiHistogramFilter_cpp #define itkMultiHistogramFilter_cpp #include #include #include template< class TInputImageType, class TOuputImageType> itk::MultiHistogramFilter::MultiHistogramFilter(): m_Delta(0.6), m_Offset(-3.0) { this->SetNumberOfRequiredOutputs(11); this->SetNumberOfRequiredInputs(0); for (unsigned int i = 0; i < 11; ++i) { this->SetNthOutput( i, this->MakeOutput(i) ); } } template< class TInputImageType, class TOuputImageType> void itk::MultiHistogramFilter::GenerateData() { double offset = m_Offset;// -3.0; double delta = m_Delta;// 0.6; typedef itk::NeighborhoodIterator IteratorType; typedef itk::ConstNeighborhoodIterator ConstIteratorType; InputImagePointer input = this->GetInput(0); CreateOutputImage(input, this->GetOutput(0)); CreateOutputImage(input, this->GetOutput(1)); CreateOutputImage(input, this->GetOutput(2)); CreateOutputImage(input, this->GetOutput(3)); CreateOutputImage(input, this->GetOutput(4)); CreateOutputImage(input, this->GetOutput(5)); CreateOutputImage(input, this->GetOutput(6)); CreateOutputImage(input, this->GetOutput(7)); CreateOutputImage(input, this->GetOutput(8)); CreateOutputImage(input, this->GetOutput(9)); CreateOutputImage(input, this->GetOutput(10)); typename TInputImageType::SizeType size; size.Fill(5); std::vector iterVector; for (int i = 0; i < 11; ++i) { IteratorType iter(size, this->GetOutput(i), this->GetOutput(i)->GetLargestPossibleRegion()); iterVector.push_back(iter); } ConstIteratorType inputIter( size, input, input->GetLargestPossibleRegion()); while (!inputIter.IsAtEnd()) { for (unsigned int i = 0; i < 11; ++i) { iterVector[i].SetCenterPixel(0); } for (unsigned int i = 0; i < inputIter.Size(); ++i) { double value = inputIter.GetPixel(i); value -= offset; value /= delta; - int pos = (int)(value); + auto pos = (int)(value); pos = std::max(0, std::min(10, pos)); iterVector[pos].SetCenterPixel(iterVector[pos].GetCenterPixel() + 1); } for (unsigned int i = 0; i < 11; ++i) { ++(iterVector[i]); } ++inputIter; } } template< class TInputImageType, class TOuputImageType> itk::ProcessObject::DataObjectPointer itk::MultiHistogramFilter::MakeOutput(itk::ProcessObject::DataObjectPointerArraySizeType /*idx*/) { itk::ProcessObject::DataObjectPointer output; output = ( TOuputImageType::New() ).GetPointer(); return output; } template< class TInputImageType, class TOuputImageType> void itk::MultiHistogramFilter::CreateOutputImage(InputImagePointer input, OutputImagePointer output) { output->SetRegions(input->GetLargestPossibleRegion()); output->Allocate(); } #endif //itkMultiHistogramFilter_cpp diff --git a/Modules/Classification/CLUtilities/src/Features/itkNeighborhoodFunctorImageFilter.cpp b/Modules/Classification/CLUtilities/src/Features/itkNeighborhoodFunctorImageFilter.cpp index 47287c8f9f..98b708314d 100644 --- a/Modules/Classification/CLUtilities/src/Features/itkNeighborhoodFunctorImageFilter.cpp +++ b/Modules/Classification/CLUtilities/src/Features/itkNeighborhoodFunctorImageFilter.cpp @@ -1,156 +1,156 @@ #ifndef itkNeighborhoodFunctorImageFilter_cpp #define itkNeighborhoodFunctorImageFilter_cpp #include "itkNeighborhoodFunctorImageFilter.h" #include "itkNeighborhoodAlgorithm.h" #include namespace itk { template< typename TInputImage, typename TFeatureImageType , class FunctorType> void NeighborhoodFunctorImageFilter< TInputImage, TFeatureImageType, FunctorType > ::BeforeThreadedGenerateData() { const TInputImage * input = this->GetInput(0); for(unsigned int i = 0 ; i < FunctorType::OutputCount; i ++) { typename FeatureImageType::Pointer output = TFeatureImageType::New(); output->SetRegions(input->GetLargestPossibleRegion()); output->SetSpacing(input->GetSpacing()); output->SetOrigin(input->GetOrigin()); output->SetDirection(input->GetDirection()); output->Allocate(); this->SetNthOutput( i, output.GetPointer() ); } if(m_MaskImage.IsNull()) { m_MaskImage = MaskImageType::New(); m_MaskImage->SetRegions(input->GetLargestPossibleRegion()); m_MaskImage->Allocate(); m_MaskImage->FillBuffer(1); } } template< typename TInputImage, typename TFeatureImageType , class FunctorType> void NeighborhoodFunctorImageFilter< TInputImage, TFeatureImageType, FunctorType > ::GenerateInputRequestedRegion() { // call the superclass' implementation of this method. this should // copy the output requested region to the input requested region Superclass::GenerateInputRequestedRegion(); // get pointers to the input and output - TInputImage * inputPtr = const_cast< TInputImage * >( this->GetInput() ); + auto * inputPtr = const_cast< TInputImage * >( this->GetInput() ); if ( !inputPtr ) { return; } // get a copy of the input requested region (should equal the output // requested region) typename TInputImage::RegionType inputRequestedRegion; inputRequestedRegion = inputPtr->GetRequestedRegion(); // pad the input requested region by the operator radius inputRequestedRegion.PadByRadius( m_Size ); // crop the input requested region at the input's largest possible region if ( inputRequestedRegion.Crop( inputPtr->GetLargestPossibleRegion() ) ) { inputPtr->SetRequestedRegion(inputRequestedRegion); return; } else { // Couldn't crop the region (requested region is outside the largest // possible region). Throw an exception. // store what we tried to request (prior to trying to crop) inputPtr->SetRequestedRegion(inputRequestedRegion); // build an exception InvalidRequestedRegionError e(__FILE__, __LINE__); e.SetLocation(ITK_LOCATION); e.SetDescription("Requested region is (at least partially) outside the largest possible region."); e.SetDataObject(inputPtr); throw e; } } template< typename TInputImage, typename TFeatureImageType, class FunctorType > void NeighborhoodFunctorImageFilter< TInputImage, TFeatureImageType, FunctorType > ::ThreadedGenerateData(const OutputImageRegionType & outputRegionForThread, ThreadIdType /*threadId*/) { typedef NeighborhoodAlgorithm::ImageBoundaryFacesCalculator< InputImageType > BFC; typedef typename BFC::FaceListType FaceListType; BFC faceCalculator; FaceListType faceList; // Allocate output const InputImageType *input = this->GetInput(); // Break the input into a series of regions. The first region is free // of boundary conditions, the rest with boundary conditions. Note, // we pass in the input image and the OUTPUT requested region. We are // only concerned with centering the neighborhood operator at the // pixels that correspond to output pixels. faceList = faceCalculator( input, outputRegionForThread, m_Size ); typename FaceListType::iterator fit; ImageRegionConstIterator< MaskImageType > mit; // Process non-boundary region and each of the boundary faces. // These are N-d regions which border the edge of the buffer. ConstNeighborhoodIterator< InputImageType > bit; // for ( fit = faceList.begin(); fit != faceList.end(); ++fit ) // { bit = ConstNeighborhoodIterator< InputImageType >(m_Size, input, outputRegionForThread); mit = ImageRegionConstIterator< MaskImageType >(m_MaskImage, outputRegionForThread); std::vector > featureImageIterators; for(unsigned int i = 0; i < FunctorType::OutputCount; i++) { featureImageIterators.push_back(ImageRegionIterator< FeatureImageType >(this->GetOutput(i), outputRegionForThread)); featureImageIterators[i].GoToBegin(); } mit.GoToBegin(); bit.GoToBegin(); while ( !bit.IsAtEnd() || !mit.IsAtEnd() ) { if(mit.Value() != 0) { //bit.GetNeighborhood().Print(std::cout); typename FunctorType::OutputVectorType features = ( m_Functor( bit ) ); for(unsigned int i = 0 ; i < FunctorType::OutputCount; i++) featureImageIterators[i].Set(features[i]); } for(unsigned int i = 0 ; i < FunctorType::OutputCount; i++) ++featureImageIterators[i]; ++bit; ++mit; } // } std::cout << "Thread done!" << std::endl; } } // end namespace itk #endif //itkNeighborhoodFunctorImageFilter_cpp diff --git a/Modules/ContourModel/Algorithms/mitkContourModelSubDivisionFilter.cpp b/Modules/ContourModel/Algorithms/mitkContourModelSubDivisionFilter.cpp index b0cf5b17ce..470c1dd2f7 100644 --- a/Modules/ContourModel/Algorithms/mitkContourModelSubDivisionFilter.cpp +++ b/Modules/ContourModel/Algorithms/mitkContourModelSubDivisionFilter.cpp @@ -1,206 +1,206 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkContourModelSubDivisionFilter.h" #include #include mitk::ContourModelSubDivisionFilter::ContourModelSubDivisionFilter() { OutputType::Pointer output = dynamic_cast(this->MakeOutput(0).GetPointer()); this->SetNumberOfRequiredInputs(1); this->SetNumberOfIndexedOutputs(1); this->SetNthOutput(0, output.GetPointer()); this->m_InterpolationIterations = 4; } mitk::ContourModelSubDivisionFilter::~ContourModelSubDivisionFilter() { } void mitk::ContourModelSubDivisionFilter::SetInput(const mitk::ContourModelSubDivisionFilter::InputType *input) { this->SetInput(0, input); } void mitk::ContourModelSubDivisionFilter::SetInput(unsigned int idx, const mitk::ContourModelSubDivisionFilter::InputType *input) { if (idx + 1 > this->GetNumberOfInputs()) { this->SetNumberOfRequiredInputs(idx + 1); } if (input != static_cast(this->ProcessObject::GetInput(idx))) { this->ProcessObject::SetNthInput(idx, const_cast(input)); this->Modified(); } } const mitk::ContourModelSubDivisionFilter::InputType *mitk::ContourModelSubDivisionFilter::GetInput(void) { if (this->GetNumberOfInputs() < 1) return nullptr; return static_cast(this->ProcessObject::GetInput(0)); } const mitk::ContourModelSubDivisionFilter::InputType *mitk::ContourModelSubDivisionFilter::GetInput(unsigned int idx) { if (this->GetNumberOfInputs() < 1) return nullptr; return static_cast(this->ProcessObject::GetInput(idx)); } void mitk::ContourModelSubDivisionFilter::GenerateData() { mitk::ContourModel::Pointer input = const_cast(this->GetInput(0)); // mitk::ContourModelSubDivisionFilter::OutputType::Pointer outputContour = this->GetOutput(); mitk::ContourModel::Pointer contour(input); - int timestep = static_cast(input->GetTimeSteps()); + auto timestep = static_cast(input->GetTimeSteps()); for (int currentTimestep = 0; currentTimestep < timestep; currentTimestep++) { if (input->GetNumberOfVertices(currentTimestep) >= 4) { for (int iterations = 0; iterations < this->m_InterpolationIterations; iterations++) { auto it = contour->IteratorBegin(); auto end = contour->IteratorEnd(); auto first = contour->IteratorBegin(); auto last = contour->IteratorEnd() - 1; // tempory contour to store result of a subdivision iteration mitk::ContourModel::Pointer tempContour = mitk::ContourModel::New(); // insert subpoints while (it != end) { // add the current point to the temp contour tempContour->AddVertex((*it)->Coordinates, (*it)->IsControlPoint, currentTimestep); // control points for interpolation auto Ci = it; InputType::VertexIterator CiPlus1; InputType::VertexIterator CiPlus2; InputType::VertexIterator CiMinus1; // consider all possible cases if (it == first) { if (input->IsClosed(currentTimestep)) { CiPlus1 = it + 1; CiPlus2 = it + 2; CiMinus1 = last; } else { CiPlus1 = it + 1; CiPlus2 = it + 2; CiMinus1 = it; } } else if (it == last) { if (input->IsClosed(currentTimestep)) { CiPlus1 = first; CiPlus2 = first + 1; CiMinus1 = it - 1; } else { // don't add point after last break; } } else if (it == (last - 1)) { if (input->IsClosed(currentTimestep)) { CiPlus1 = it + 1; CiPlus2 = first; CiMinus1 = it - 1; } else { CiPlus1 = it + 1; CiPlus2 = it + 1; CiMinus1 = it - 1; } } else { CiPlus1 = it + 1; CiPlus2 = it + 2; CiMinus1 = it - 1; } /* F2i = Ci * F2i+1 = -1/16Ci-1 + 9/16Ci + 9/16Ci+1 - 1/16Ci+2 */ mitk::Point3D subpoint; mitk::Point3D a; a[0] = (-1.0 / 16.0) * (*CiMinus1)->Coordinates[0]; a[1] = (-1.0 / 16.0) * (*CiMinus1)->Coordinates[1]; a[2] = (-1.0 / 16.0) * (*CiMinus1)->Coordinates[2]; mitk::Point3D b; b[0] = (9.0 / 16.0) * (*Ci)->Coordinates[0]; b[1] = (9.0 / 16.0) * (*Ci)->Coordinates[1]; b[2] = (9.0 / 16.0) * (*Ci)->Coordinates[2]; mitk::Point3D c; c[0] = (9.0 / 16.0) * (*CiPlus1)->Coordinates[0]; c[1] = (9.0 / 16.0) * (*CiPlus1)->Coordinates[1]; c[2] = (9.0 / 16.0) * (*CiPlus1)->Coordinates[2]; mitk::Point3D d; d[0] = (-1.0 / 16.0) * (*CiPlus2)->Coordinates[0]; d[1] = (-1.0 / 16.0) * (*CiPlus2)->Coordinates[1]; d[2] = (-1.0 / 16.0) * (*CiPlus2)->Coordinates[2]; subpoint[0] = a[0] + b[0] + c[0] + d[0]; subpoint[1] = a[1] + b[1] + c[1] + d[1]; subpoint[2] = a[2] + b[2] + c[2] + d[2]; InputType::VertexType subdivisionPoint(subpoint, false); // add the new subdivision point to our tempContour tempContour->AddVertex(subdivisionPoint.Coordinates, currentTimestep); it++; } // set the interpolated contour as the contour for the next iteration contour = tempContour; } } else { // filter not executeable - set input to output contour = input; } } // somehow the isClosed property is not set via copy constructor contour->SetClosed(input->IsClosed()); this->SetNthOutput(0, contour); } diff --git a/Modules/ContourModel/Algorithms/mitkContourModelToSurfaceFilter.cpp b/Modules/ContourModel/Algorithms/mitkContourModelToSurfaceFilter.cpp index fc71efc89a..6d80ebece3 100644 --- a/Modules/ContourModel/Algorithms/mitkContourModelToSurfaceFilter.cpp +++ b/Modules/ContourModel/Algorithms/mitkContourModelToSurfaceFilter.cpp @@ -1,149 +1,149 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include #include #include #include #include #include #include mitk::ContourModelToSurfaceFilter::ContourModelToSurfaceFilter() { this->SetNthOutput(0, mitk::Surface::New().GetPointer()); } mitk::ContourModelToSurfaceFilter::~ContourModelToSurfaceFilter() { } void mitk::ContourModelToSurfaceFilter::GenerateOutputInformation() { } void mitk::ContourModelToSurfaceFilter::SetInput(const mitk::ContourModelToSurfaceFilter::InputType *input) { this->SetInput(0, input); } void mitk::ContourModelToSurfaceFilter::SetInput(unsigned int idx, const mitk::ContourModelToSurfaceFilter::InputType *input) { if (idx + 1 > this->GetNumberOfInputs()) { this->SetNumberOfRequiredInputs(idx + 1); } if (input != static_cast(this->ProcessObject::GetInput(idx))) { this->ProcessObject::SetNthInput(idx, const_cast(input)); this->Modified(); } } const mitk::ContourModelToSurfaceFilter::InputType *mitk::ContourModelToSurfaceFilter::GetInput(void) { if (this->GetNumberOfInputs() < 1) return nullptr; return static_cast(this->ProcessObject::GetInput(0)); } const mitk::ContourModelToSurfaceFilter::InputType *mitk::ContourModelToSurfaceFilter::GetInput(unsigned int idx) { if (this->GetNumberOfInputs() < 1) return nullptr; return static_cast(this->ProcessObject::GetInput(idx)); } void mitk::ContourModelToSurfaceFilter::GenerateData() { mitk::Surface *surface = this->GetOutput(); - mitk::ContourModel *inputContour = (mitk::ContourModel *)GetInput(); + auto *inputContour = (mitk::ContourModel *)GetInput(); unsigned int numberOfTimeSteps = inputContour->GetTimeSteps(); surface->Expand(numberOfTimeSteps); for (unsigned int currentTimeStep = 0; currentTimeStep < numberOfTimeSteps; currentTimeStep++) { /* First of all convert the control points of the contourModel to vtk points * and add lines in between them */ vtkSmartPointer points = vtkSmartPointer::New(); // the points to draw vtkSmartPointer polygons = vtkSmartPointer::New(); vtkSmartPointer lines = vtkSmartPointer::New(); // the lines to connect the points // if the contour has less than 3 points, set empty PolyData for current timestep // polygon needs at least 3 points if (inputContour->GetNumberOfVertices(currentTimeStep) <= 2) { vtkSmartPointer emptyPolyData = vtkSmartPointer::New(); surface->SetVtkPolyData(emptyPolyData, currentTimeStep); continue; } // iterate over all control points auto current = inputContour->IteratorBegin(currentTimeStep); auto end = inputContour->IteratorEnd(currentTimeStep); vtkSmartPointer polygon = vtkSmartPointer::New(); polygon->GetPointIds()->SetNumberOfIds(inputContour->GetNumberOfVertices(currentTimeStep)); int j(0); while (current != end) { mitk::ContourModel::VertexType *currentPoint = *current; vtkIdType id = points->InsertNextPoint( currentPoint->Coordinates[0], currentPoint->Coordinates[1], currentPoint->Coordinates[2]); polygon->GetPointIds()->SetId(j, id); // create connections between the points // no previous point for first point available (ingnore id=0) if (id > 0) { lines->InsertNextCell(2); lines->InsertCellPoint(id - 1); lines->InsertCellPoint(id); } current++; j++; } /* * If the contour is closed an additional line has to be created between the first point * and the last point */ if (inputContour->IsClosed(currentTimeStep)) { lines->InsertNextCell(2); lines->InsertCellPoint(0); lines->InsertCellPoint((inputContour->GetNumberOfVertices(currentTimeStep) - 1)); } polygons->InsertNextCell(polygon); // Create a polydata to store everything in vtkSmartPointer polyData = vtkSmartPointer::New(); // Add the points to the dataset polyData->SetPoints(points); polyData->SetPolys(polygons); polyData->SetLines(lines); polyData->BuildLinks(); surface->SetVtkPolyData(polyData, currentTimeStep); } } diff --git a/Modules/ContourModel/IO/mitkContourModelSerializer.cpp b/Modules/ContourModel/IO/mitkContourModelSerializer.cpp index 78576bc6af..60ca692677 100644 --- a/Modules/ContourModel/IO/mitkContourModelSerializer.cpp +++ b/Modules/ContourModel/IO/mitkContourModelSerializer.cpp @@ -1,66 +1,66 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkContourModelSerializer.h" #include "mitkContourModelWriter.h" #include MITK_REGISTER_SERIALIZER(ContourModelSerializer) mitk::ContourModelSerializer::ContourModelSerializer() { } mitk::ContourModelSerializer::~ContourModelSerializer() { } std::string mitk::ContourModelSerializer::Serialize() { - const ContourModel *contour = dynamic_cast(m_Data.GetPointer()); + const auto *contour = dynamic_cast(m_Data.GetPointer()); if (!contour) { MITK_ERROR << " Object at " << (const void *)this->m_Data << " is not an mitk::ContourModel. Cannot serialize as contour model."; return ""; } std::string filename(this->GetUniqueFilenameInWorkingDirectory()); filename += "_"; filename += m_FilenameHint; filename += ".cnt"; std::string fullname(m_WorkingDirectory); fullname += "/"; fullname += itksys::SystemTools::ConvertToOutputPath(filename.c_str()); try { ContourModelWriter writer; writer.SetOutputLocation(fullname); writer.SetInput(const_cast(contour)); writer.Write(); } catch (std::exception &e) { MITK_ERROR << " Error serializing object at " << (const void *)this->m_Data << " to " << fullname << ": " << e.what(); return ""; } return filename; } diff --git a/Modules/ContourModel/IO/mitkContourModelSetSerializer.cpp b/Modules/ContourModel/IO/mitkContourModelSetSerializer.cpp index 47109e1bf5..5b09af63ac 100644 --- a/Modules/ContourModel/IO/mitkContourModelSetSerializer.cpp +++ b/Modules/ContourModel/IO/mitkContourModelSetSerializer.cpp @@ -1,66 +1,66 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkContourModelSetSerializer.h" #include "mitkContourModelSetWriter.h" #include MITK_REGISTER_SERIALIZER(ContourModelSetSerializer) mitk::ContourModelSetSerializer::ContourModelSetSerializer() { } mitk::ContourModelSetSerializer::~ContourModelSetSerializer() { } std::string mitk::ContourModelSetSerializer::Serialize() { - const ContourModelSet *contourSet = dynamic_cast(m_Data.GetPointer()); + const auto *contourSet = dynamic_cast(m_Data.GetPointer()); if (!contourSet) { MITK_ERROR << " Object at " << (const void *)this->m_Data << " is not an mitk::ContourModelSet. Cannot serialize as contour model set."; return ""; } std::string filename(this->GetUniqueFilenameInWorkingDirectory()); filename += "_"; filename += m_FilenameHint; filename += ".cnt_set"; std::string fullname(m_WorkingDirectory); fullname += "/"; fullname += itksys::SystemTools::ConvertToOutputPath(filename.c_str()); try { ContourModelSetWriter writer; writer.SetOutputLocation(fullname); writer.SetInput(const_cast(contourSet)); writer.Write(); } catch (std::exception &e) { MITK_ERROR << " Error serializing object at " << (const void *)this->m_Data << " to " << fullname << ": " << e.what(); return ""; } return filename; } diff --git a/Modules/ContourModel/IO/mitkContourModelWriter.cpp b/Modules/ContourModel/IO/mitkContourModelWriter.cpp index b65b4d853f..44198e80c0 100644 --- a/Modules/ContourModel/IO/mitkContourModelWriter.cpp +++ b/Modules/ContourModel/IO/mitkContourModelWriter.cpp @@ -1,339 +1,339 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkContourModelWriter.h" #include "mitkIOMimeTypes.h" #include "mitkTimeGeometry.h" #include #include #include /* * The xml file will look like: * * * * * * * * * * * * * * * * * * * */ // // Initialization of the xml tags. // const char *mitk::ContourModelWriter::XML_CONTOURMODEL = "contourModel"; const char *mitk::ContourModelWriter::XML_HEAD = "head"; const char *mitk::ContourModelWriter::XML_GEOMETRY_INFO = "geometryInfo"; const char *mitk::ContourModelWriter::XML_DATA = "data"; const char *mitk::ContourModelWriter::XML_TIME_STEP = "timestep"; const char *mitk::ContourModelWriter::XML_CONTROL_POINTS = "controlPoints"; const char *mitk::ContourModelWriter::XML_POINT = "point"; const char *mitk::ContourModelWriter::XML_X = "x"; const char *mitk::ContourModelWriter::XML_Y = "y"; const char *mitk::ContourModelWriter::XML_Z = "z"; mitk::ContourModelWriter::ContourModelWriter() : AbstractFileWriter(ContourModel::GetStaticNameOfClass()), m_IndentDepth(0), m_Indent(2) { std::string category = "Contour File"; mitk::CustomMimeType customMimeType; customMimeType.SetCategory(category); customMimeType.AddExtension("cnt"); this->SetDescription(category); this->SetMimeType(customMimeType); RegisterService(); } mitk::ContourModelWriter::ContourModelWriter(const mitk::ContourModelWriter &other) : AbstractFileWriter(other), m_IndentDepth(other.m_IndentDepth), m_Indent(other.m_Indent) { } mitk::ContourModelWriter::~ContourModelWriter() { } void mitk::ContourModelWriter::Write() { std::ostream *out; std::ofstream outStream; if (this->GetOutputStream()) { out = this->GetOutputStream(); } else { outStream.open(this->GetOutputLocation().c_str()); out = &outStream; } if (!out->good()) { mitkThrow() << "Stream not good."; } std::locale previousLocale(out->getloc()); std::locale I("C"); out->imbue(I); /*+++++++++++ Here the actual xml writing begins +++++++++*/ /*++++ ++++*/ WriteXMLHeader(*out); // // for each input object write its xml representation to // the stream // mitk::ContourModel::ConstPointer contourModel = dynamic_cast(this->GetInput()); assert(contourModel.IsNotNull()); WriteXML(contourModel.GetPointer(), *out); out->imbue(previousLocale); if (!out->good()) // some error during output { throw std::ios_base::failure("Some error during contour writing."); } } mitk::ContourModelWriter *mitk::ContourModelWriter::Clone() const { return new ContourModelWriter(*this); } void mitk::ContourModelWriter::WriteXML(const mitk::ContourModel *contourModel, std::ostream &out) { /*++++ ++++*/ WriteStartElement(XML_CONTOURMODEL, out); /*++++ ++++*/ WriteStartElement(XML_HEAD, out); /*++++ ++++*/ WriteStartElement(XML_GEOMETRY_INFO, out); WriteGeometryInformation(contourModel->GetTimeGeometry(), out); /*++++ ++++*/ WriteEndElement(XML_GEOMETRY_INFO, out); /*++++ ++++*/ WriteEndElement(XML_HEAD, out); /*++++ ++++*/ WriteStartElement(XML_DATA, out); unsigned int timecount = contourModel->GetTimeSteps(); for (unsigned int i = 0; i < timecount; i++) { /*++++ ++++*/ std::vector at; at.push_back("n"); std::vector val; val.push_back(ConvertToString(i)); at.push_back("isClosed"); val.push_back(ConvertToString(contourModel->IsClosed())); WriteStartElementWithAttribut(XML_TIME_STEP, at, val, out); /*++++ ++++*/ WriteStartElement(XML_CONTROL_POINTS, out); - mitk::ContourModel::VertexIterator it = contourModel->IteratorBegin(); - mitk::ContourModel::VertexIterator end = contourModel->IteratorEnd(); + auto it = contourModel->IteratorBegin(); + auto end = contourModel->IteratorEnd(); while (it != end) { mitk::ContourModel::VertexType *v = *it; /*++++ ++++*/ std::vector attr; attr.push_back("IsControlPoint"); std::vector value; value.push_back(ConvertToString(v->IsControlPoint)); WriteStartElementWithAttribut(XML_POINT, attr, value, out); /*++++ ++++*/ WriteStartElement(XML_X, out); WriteCharacterData(ConvertToString(v->Coordinates[0]).c_str(), out); /*++++ ++++*/ WriteEndElement(XML_X, out, false); /*++++ ++++*/ WriteStartElement(XML_Y, out); WriteCharacterData(ConvertToString(v->Coordinates[1]).c_str(), out); /*++++ ++++*/ WriteEndElement(XML_Y, out, false); /*++++ ++++*/ WriteStartElement(XML_Z, out); WriteCharacterData(ConvertToString(v->Coordinates[2]).c_str(), out); /*++++ ++++*/ WriteEndElement(XML_Z, out, false); /*++++ ++++*/ WriteEndElement(XML_POINT, out); it++; } /*++++ ++++*/ WriteEndElement(XML_CONTROL_POINTS, out); /*++++ ++++*/ WriteEndElement(XML_TIME_STEP, out); } /*++++ ++++*/ WriteEndElement(XML_DATA, out); /*++++ ++++*/ WriteEndElement(XML_CONTOURMODEL, out); } void mitk::ContourModelWriter::WriteGeometryInformation(const mitk::TimeGeometry * /*geometry*/, std::ostream &out) { WriteCharacterData("", out); } template std::string mitk::ContourModelWriter::ConvertToString(T value) { std::ostringstream o; std::locale I("C"); o.imbue(I); if (o << value) { return o.str(); } else return "conversion error"; } void mitk::ContourModelWriter::WriteXMLHeader(std::ostream &file) { file << ""; } void mitk::ContourModelWriter::WriteStartElement(const char *const tag, std::ostream &file) { file << std::endl; WriteIndent(file); file << '<' << tag << '>'; m_IndentDepth++; } void mitk::ContourModelWriter::WriteStartElementWithAttribut(const char *const tag, std::vector attributes, std::vector values, std::ostream &file) { file << std::endl; WriteIndent(file); file << '<' << tag; unsigned int attributesSize = attributes.size(); unsigned int valuesSize = values.size(); if (attributesSize == valuesSize) { - std::vector::iterator attributesIt = attributes.begin(); - std::vector::iterator end = attributes.end(); + auto attributesIt = attributes.begin(); + auto end = attributes.end(); - std::vector::iterator valuesIt = values.begin(); + auto valuesIt = values.begin(); while (attributesIt != end) { file << ' '; WriteCharacterData(*attributesIt, file); file << '=' << '"'; WriteCharacterData(*valuesIt, file); file << '"'; attributesIt++; valuesIt++; } } file << '>'; m_IndentDepth++; } void mitk::ContourModelWriter::WriteEndElement(const char *const tag, std::ostream &file, const bool &indent) { m_IndentDepth--; if (indent) { file << std::endl; WriteIndent(file); } file << '<' << '/' << tag << '>'; } void mitk::ContourModelWriter::WriteCharacterData(const char *const data, std::ostream &file) { file << data; } void mitk::ContourModelWriter::WriteStartElement(std::string &tag, std::ostream &file) { WriteStartElement(tag.c_str(), file); } void mitk::ContourModelWriter::WriteEndElement(std::string &tag, std::ostream &file, const bool &indent) { WriteEndElement(tag.c_str(), file, indent); } void mitk::ContourModelWriter::WriteCharacterData(std::string &data, std::ostream &file) { WriteCharacterData(data.c_str(), file); } void mitk::ContourModelWriter::WriteIndent(std::ostream &file) { std::string spaces(m_IndentDepth * m_Indent, ' '); file << spaces.c_str(); } diff --git a/Modules/ContourModel/Rendering/mitkContourModelGLMapper2DBase.cpp b/Modules/ContourModel/Rendering/mitkContourModelGLMapper2DBase.cpp index 180bb38318..3219e55b30 100644 --- a/Modules/ContourModel/Rendering/mitkContourModelGLMapper2DBase.cpp +++ b/Modules/ContourModel/Rendering/mitkContourModelGLMapper2DBase.cpp @@ -1,400 +1,400 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkContourModelSetGLMapper2D.h" #include "mitkColorProperty.h" #include "mitkContourModelSet.h" #include "mitkPlaneGeometry.h" #include "mitkProperties.h" #include #include "vtkPen.h" #include "vtkContext2D.h" #include "vtkContextDevice2D.h" #include "vtkOpenGLContextDevice2D.h" #include "mitkManualPlacementAnnotationRenderer.h" #include "mitkBaseRenderer.h" #include "mitkContourModel.h" #include "mitkTextAnnotation2D.h" mitk::ContourModelGLMapper2DBase::ContourModelGLMapper2DBase(): m_Initialized(false) { m_PointNumbersAnnotation = mitk::TextAnnotation2D::New(); m_ControlPointNumbersAnnotation = mitk::TextAnnotation2D::New(); } mitk::ContourModelGLMapper2DBase::~ContourModelGLMapper2DBase() { } void mitk::ContourModelGLMapper2DBase::Initialize(mitk::BaseRenderer *) { vtkOpenGLContextDevice2D *device = nullptr; device = vtkOpenGLContextDevice2D::New(); if (device) { this->m_Context->Begin(device); device->Delete(); this->m_Initialized = true; } else { } } void mitk::ContourModelGLMapper2DBase::ApplyColorAndOpacityProperties(mitk::BaseRenderer *renderer, vtkActor * /*actor*/) { float rgba[4] = { 1.0f, 1.0f, 1.0f, 1.0f }; // check for color prop and use it for rendering if it exists GetDataNode()->GetColor(rgba, renderer, "color"); // check for opacity prop and use it for rendering if it exists GetDataNode()->GetOpacity(rgba[3], renderer, "opacity"); if (this->m_Context->GetPen() == nullptr) { return; } this->m_Context->GetPen()->SetColorF((double)rgba[0], (double)rgba[1], (double)rgba[2], (double)rgba[3]); } void mitk::ContourModelGLMapper2DBase::DrawContour(mitk::ContourModel *renderingContour, mitk::BaseRenderer *renderer) { if (std::find(m_RendererList.begin(), m_RendererList.end(), renderer) == m_RendererList.end()) { m_RendererList.push_back(renderer); } mitk::ManualPlacementAnnotationRenderer::AddAnnotation(m_PointNumbersAnnotation.GetPointer(), renderer); m_PointNumbersAnnotation->SetVisibility(false); mitk::ManualPlacementAnnotationRenderer::AddAnnotation(m_ControlPointNumbersAnnotation.GetPointer(), renderer); m_ControlPointNumbersAnnotation->SetVisibility(false); InternalDrawContour(renderingContour, renderer); } void mitk::ContourModelGLMapper2DBase::InternalDrawContour(mitk::ContourModel *renderingContour, mitk::BaseRenderer *renderer) { if (!renderingContour) return; if (!this->m_Initialized) { this->Initialize(renderer); } vtkOpenGLContextDevice2D::SafeDownCast( this->m_Context->GetDevice())->Begin(renderer->GetVtkRenderer()); mitk::DataNode *dataNode = this->GetDataNode(); renderingContour->UpdateOutputInformation(); unsigned int timestep = renderer->GetTimeStep(); if (!renderingContour->IsEmptyTimeStep(timestep)) { // apply color and opacity read from the PropertyList ApplyColorAndOpacityProperties(renderer); mitk::ColorProperty::Pointer colorprop = dynamic_cast(dataNode->GetProperty("contour.color", renderer)); float opacity = 0.5; dataNode->GetFloatProperty("opacity", opacity, renderer); if (colorprop) { // set the color of the contour double red = colorprop->GetColor().GetRed(); double green = colorprop->GetColor().GetGreen(); double blue = colorprop->GetColor().GetBlue(); this->m_Context->GetPen()->SetColorF(red, green, blue, opacity); } mitk::ColorProperty::Pointer selectedcolor = dynamic_cast(dataNode->GetProperty("contour.points.color", renderer)); if (!selectedcolor) { selectedcolor = mitk::ColorProperty::New(1.0, 0.0, 0.1); } vtkLinearTransform *transform = dataNode->GetVtkTransform(); // ContourModel::OutputType point; mitk::Point3D point; mitk::Point3D p; float vtkp[3]; float lineWidth = 3.0; bool drawit = false; bool isHovering = false; dataNode->GetBoolProperty("contour.hovering", isHovering); if (isHovering) dataNode->GetFloatProperty("contour.hovering.width", lineWidth); else dataNode->GetFloatProperty("contour.width", lineWidth); bool showSegments = false; dataNode->GetBoolProperty("contour.segments.show", showSegments); bool showControlPoints = false; dataNode->GetBoolProperty("contour.controlpoints.show", showControlPoints); bool showPoints = false; dataNode->GetBoolProperty("contour.points.show", showPoints); bool showPointsNumbers = false; dataNode->GetBoolProperty("contour.points.text", showPointsNumbers); bool showControlPointsNumbers = false; dataNode->GetBoolProperty("contour.controlpoints.text", showControlPointsNumbers); bool projectmode = false; dataNode->GetVisibility(projectmode, renderer, "contour.project-onto-plane"); - mitk::ContourModel::VertexIterator pointsIt = renderingContour->IteratorBegin(timestep); + auto pointsIt = renderingContour->IteratorBegin(timestep); Point2D pt2d; // projected_p in display coordinates Point2D lastPt2d; int index = 0; mitk::ScalarType maxDiff = 0.25; while (pointsIt != renderingContour->IteratorEnd(timestep)) { lastPt2d = pt2d; point = (*pointsIt)->Coordinates; itk2vtk(point, vtkp); transform->TransformPoint(vtkp, vtkp); vtk2itk(vtkp, p); renderer->WorldToView(p, pt2d); ScalarType scalardiff = fabs(renderer->GetCurrentWorldPlaneGeometry()->SignedDistance(p)); // project to plane if (projectmode) { drawit = true; } else if (scalardiff < maxDiff) // point is close enough to be drawn { drawit = true; } else { drawit = false; } // draw line if (drawit) { if (showSegments) { // lastPt2d is not valid in first step if (!(pointsIt == renderingContour->IteratorBegin(timestep))) { this->m_Context->GetPen()->SetWidth(lineWidth); this->m_Context->DrawLine(pt2d[0], pt2d[1], lastPt2d[0], lastPt2d[1]); this->m_Context->GetPen()->SetWidth(1); } } if (showControlPoints) { // draw ontrol points if ((*pointsIt)->IsControlPoint) { float pointsize = 4; Point2D tmp; Vector2D horz, vert; horz[1] = 0; vert[0] = 0; horz[0] = pointsize; vert[1] = pointsize; this->m_Context->GetPen()->SetColorF(selectedcolor->GetColor().GetRed(), selectedcolor->GetColor().GetBlue(), selectedcolor->GetColor().GetGreen()); this->m_Context->GetPen()->SetWidth(1); // a rectangle around the point with the selected color - float* rectPts = new float[8]; + auto* rectPts = new float[8]; tmp = pt2d - horz; rectPts[0] = tmp[0]; rectPts[1] = tmp[1]; tmp = pt2d + vert; rectPts[2] = tmp[0]; rectPts[3] = tmp[1]; tmp = pt2d + horz; rectPts[4] = tmp[0]; rectPts[5] = tmp[1]; tmp = pt2d - vert; rectPts[6] = tmp[0]; rectPts[7] = tmp[1]; this->m_Context->DrawPolygon(rectPts,4); // the actual point in the specified color to see the usual color of the point this->m_Context->GetPen()->SetColorF( colorprop->GetColor().GetRed(), colorprop->GetColor().GetGreen(), colorprop->GetColor().GetBlue()); this->m_Context->DrawPoint(pt2d[0], pt2d[1]); } } if (showPoints) { float pointsize = 3; Point2D tmp; Vector2D horz, vert; horz[1] = 0; vert[0] = 0; horz[0] = pointsize; vert[1] = pointsize; this->m_Context->GetPen()->SetColorF(0.0, 0.0, 0.0); this->m_Context->GetPen()->SetWidth(1); // a rectangle around the point with the selected color - float* rectPts = new float[8]; + auto* rectPts = new float[8]; tmp = pt2d - horz; rectPts[0] = tmp[0]; rectPts[1] = tmp[1]; tmp = pt2d + vert; rectPts[2] = tmp[0]; rectPts[3] = tmp[1]; tmp = pt2d + horz; rectPts[4] = tmp[0]; rectPts[5] = tmp[1]; tmp = pt2d - vert; rectPts[6] = tmp[0]; rectPts[7] = tmp[1]; this->m_Context->DrawPolygon(rectPts, 4); // the actual point in the specified color to see the usual color of the point this->m_Context->GetPen()->SetColorF( colorprop->GetColor().GetRed(), colorprop->GetColor().GetGreen(), colorprop->GetColor().GetBlue()); this->m_Context->DrawPoint(pt2d[0], pt2d[1]); } if (showPointsNumbers) { std::string l; std::stringstream ss; ss << index; l.append(ss.str()); float rgb[3]; rgb[0] = 0.0; rgb[1] = 0.0; rgb[2] = 0.0; WriteTextWithAnnotation(m_PointNumbersAnnotation, l.c_str(), rgb, pt2d, renderer); } if (showControlPointsNumbers && (*pointsIt)->IsControlPoint) { std::string l; std::stringstream ss; ss << index; l.append(ss.str()); float rgb[3]; rgb[0] = 1.0; rgb[1] = 1.0; rgb[2] = 0.0; WriteTextWithAnnotation(m_ControlPointNumbersAnnotation, l.c_str(), rgb, pt2d, renderer); } index++; } pointsIt++; } // end while iterate over controlpoints // close contour if necessary if (renderingContour->IsClosed(timestep) && drawit && showSegments) { lastPt2d = pt2d; point = renderingContour->GetVertexAt(0, timestep)->Coordinates; itk2vtk(point, vtkp); transform->TransformPoint(vtkp, vtkp); vtk2itk(vtkp, p); renderer->WorldToDisplay(p, pt2d); this->m_Context->GetPen()->SetWidth(lineWidth); this->m_Context->DrawLine(lastPt2d[0], lastPt2d[1], pt2d[0], pt2d[1]); this->m_Context->GetPen()->SetWidth(1); } // draw selected vertex if exists if (renderingContour->GetSelectedVertex()) { // transform selected vertex point = renderingContour->GetSelectedVertex()->Coordinates; itk2vtk(point, vtkp); transform->TransformPoint(vtkp, vtkp); vtk2itk(vtkp, p); renderer->WorldToDisplay(p, pt2d); ScalarType scalardiff = fabs(renderer->GetCurrentWorldPlaneGeometry()->SignedDistance(p)); //---------------------------------- // draw point if close to plane if (scalardiff < maxDiff) { float pointsize = 5; Point2D tmp; this->m_Context->GetPen()->SetColorF(0.0, 1.0, 0.0); this->m_Context->GetPen()->SetWidth(1); // a rectangle around the point with the selected color - float* rectPts = new float[8]; + auto* rectPts = new float[8]; // a diamond around the point // begin from upper left corner and paint clockwise rectPts[0] = pt2d[0] - pointsize; rectPts[1] = pt2d[1] + pointsize; rectPts[2] = pt2d[0] + pointsize; rectPts[3] = pt2d[1] + pointsize; rectPts[4] = pt2d[0] + pointsize; rectPts[5] = pt2d[1] - pointsize; rectPts[6] = pt2d[0] - pointsize; rectPts[7] = pt2d[1] - pointsize; this->m_Context->DrawPolygon(rectPts, 4); } //------------------------------------ } } this->m_Context->GetDevice()->End(); } void mitk::ContourModelGLMapper2DBase::WriteTextWithAnnotation(TextAnnotationPointerType textAnnotation, const char *text, float rgb[3], Point2D /*pt2d*/, mitk::BaseRenderer * /*renderer*/) { textAnnotation->SetText(text); textAnnotation->SetColor(rgb); textAnnotation->SetOpacity(1); textAnnotation->SetFontSize(16); textAnnotation->SetBoolProperty("drawShadow", false); textAnnotation->SetVisibility(true); } diff --git a/Modules/ContourModel/Rendering/mitkContourModelMapper2D.cpp b/Modules/ContourModel/Rendering/mitkContourModelMapper2D.cpp index 38e41d77d5..165e6d469d 100644 --- a/Modules/ContourModel/Rendering/mitkContourModelMapper2D.cpp +++ b/Modules/ContourModel/Rendering/mitkContourModelMapper2D.cpp @@ -1,376 +1,376 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include #include #include #include #include #include #include #include #include #include #include #include mitk::ContourModelMapper2D::ContourModelMapper2D() { } mitk::ContourModelMapper2D::~ContourModelMapper2D() { } const mitk::ContourModel *mitk::ContourModelMapper2D::GetInput(void) { // convient way to get the data from the dataNode return static_cast(GetDataNode()->GetData()); } vtkProp *mitk::ContourModelMapper2D::GetVtkProp(mitk::BaseRenderer *renderer) { // return the actor corresponding to the renderer return m_LSH.GetLocalStorage(renderer)->m_Actor; } void mitk::ContourModelMapper2D::GenerateDataForRenderer(mitk::BaseRenderer *renderer) { /*++ convert the contour to vtkPolyData and set it as input for our mapper ++*/ LocalStorage *localStorage = m_LSH.GetLocalStorage(renderer); - mitk::ContourModel *inputContour = static_cast(GetDataNode()->GetData()); + auto *inputContour = static_cast(GetDataNode()->GetData()); unsigned int timestep = renderer->GetTimeStep(); // if there's something to be rendered if (inputContour->GetNumberOfVertices(timestep) > 0) { localStorage->m_OutlinePolyData = this->CreateVtkPolyDataFromContour(inputContour, renderer); } this->ApplyContourProperties(renderer); localStorage->m_Mapper->SetInputData(localStorage->m_OutlinePolyData); } void mitk::ContourModelMapper2D::Update(mitk::BaseRenderer *renderer) { bool visible = true; GetDataNode()->GetVisibility(visible, renderer, "visible"); if (!visible) return; // check if there is something to be rendered - mitk::ContourModel *data = static_cast(GetDataNode()->GetData()); + auto *data = static_cast(GetDataNode()->GetData()); if (data == nullptr) { return; } // Calculate time step of the input data for the specified renderer (integer value) this->CalculateTimeStep(renderer); LocalStorage *localStorage = m_LSH.GetLocalStorage(renderer); // Check if time step is valid const TimeGeometry *dataTimeGeometry = data->GetTimeGeometry(); if ((dataTimeGeometry == nullptr) || (dataTimeGeometry->CountTimeSteps() == 0) || (!dataTimeGeometry->IsValidTimeStep(renderer->GetTimeStep()))) { // clear the rendered polydata localStorage->m_Mapper->RemoveAllInputs(); // SetInput(vtkSmartPointer::New()); return; } const DataNode *node = this->GetDataNode(); data->UpdateOutputInformation(); // check if something important has changed and we need to rerender if ((localStorage->m_LastUpdateTime < node->GetMTime()) // was the node modified? || (localStorage->m_LastUpdateTime < data->GetPipelineMTime()) // Was the data modified? || (localStorage->m_LastUpdateTime < renderer->GetCurrentWorldPlaneGeometryUpdateTime()) // was the geometry modified? || (localStorage->m_LastUpdateTime < renderer->GetCurrentWorldPlaneGeometry()->GetMTime()) || (localStorage->m_LastUpdateTime < node->GetPropertyList()->GetMTime()) // was a property modified? || (localStorage->m_LastUpdateTime < node->GetPropertyList(renderer)->GetMTime())) { this->GenerateDataForRenderer(renderer); } // since we have checked that nothing important has changed, we can set // m_LastUpdateTime to the current time localStorage->m_LastUpdateTime.Modified(); } vtkSmartPointer mitk::ContourModelMapper2D::CreateVtkPolyDataFromContour(mitk::ContourModel *inputContour, mitk::BaseRenderer *renderer) { unsigned int timestep = this->GetTimestep(); // Create a polydata to store everything in vtkSmartPointer resultingPolyData = vtkSmartPointer::New(); // check for the worldgeometry from the current render window const mitk::PlaneGeometry *currentWorldGeometry = renderer->GetCurrentWorldPlaneGeometry(); if (currentWorldGeometry) { // origin and normal of vtkPlane mitk::Point3D origin = currentWorldGeometry->GetOrigin(); mitk::Vector3D normal = currentWorldGeometry->GetNormal(); // the implicit function to slice through the polyData vtkSmartPointer plane = vtkSmartPointer::New(); plane->SetOrigin(origin[0], origin[1], origin[2]); plane->SetNormal(normal[0], normal[1], normal[2]); /* First of all convert the control points of the contourModel to vtk points * and add lines in between them */ // the points to draw vtkSmartPointer points = vtkSmartPointer::New(); // the lines to connect the points vtkSmartPointer lines = vtkSmartPointer::New(); // Create a polydata to store everything in vtkSmartPointer polyDataIn3D = vtkSmartPointer::New(); vtkSmartPointer appendPoly = vtkSmartPointer::New(); mitk::ContourModel::Pointer renderingContour = mitk::ContourModel::New(); renderingContour = inputContour; bool subdivision = false; this->GetDataNode()->GetBoolProperty("subdivision curve", subdivision, renderer); if (subdivision) { mitk::ContourModel::Pointer subdivContour = mitk::ContourModel::New(); mitk::ContourModelSubDivisionFilter::Pointer subdivFilter = mitk::ContourModelSubDivisionFilter::New(); subdivFilter->SetInput(inputContour); subdivFilter->Update(); subdivContour = subdivFilter->GetOutput(); if (subdivContour->GetNumberOfVertices() == 0) { subdivContour = inputContour; } renderingContour = subdivContour; } // iterate over all control points - mitk::ContourModel::VertexIterator current = renderingContour->IteratorBegin(timestep); - mitk::ContourModel::VertexIterator next = renderingContour->IteratorBegin(timestep); + auto current = renderingContour->IteratorBegin(timestep); + auto next = renderingContour->IteratorBegin(timestep); if (next != renderingContour->IteratorEnd(timestep)) { next++; - mitk::ContourModel::VertexIterator end = renderingContour->IteratorEnd(timestep); + auto end = renderingContour->IteratorEnd(timestep); while (next != end) { mitk::ContourModel::VertexType *currentControlPoint = *current; mitk::ContourModel::VertexType *nextControlPoint = *next; vtkIdType p1 = points->InsertNextPoint(currentControlPoint->Coordinates[0], currentControlPoint->Coordinates[1], currentControlPoint->Coordinates[2]); vtkIdType p2 = points->InsertNextPoint( nextControlPoint->Coordinates[0], nextControlPoint->Coordinates[1], nextControlPoint->Coordinates[2]); // add the line between both contorlPoints lines->InsertNextCell(2); lines->InsertCellPoint(p1); lines->InsertCellPoint(p2); if (currentControlPoint->IsControlPoint) { double coordinates[3]; coordinates[0] = currentControlPoint->Coordinates[0]; coordinates[1] = currentControlPoint->Coordinates[1]; coordinates[2] = currentControlPoint->Coordinates[2]; double distance = plane->DistanceToPlane(coordinates); if (distance < 0.1) { vtkSmartPointer sphere = vtkSmartPointer::New(); sphere->SetRadius(1.2); sphere->SetCenter(coordinates[0], coordinates[1], coordinates[2]); sphere->Update(); appendPoly->AddInputConnection(sphere->GetOutputPort()); } } current++; next++; } // end while (it!=end) // check if last control point is enabled to draw it if ((*current)->IsControlPoint) { double coordinates[3]; coordinates[0] = (*current)->Coordinates[0]; coordinates[1] = (*current)->Coordinates[1]; coordinates[2] = (*current)->Coordinates[2]; double distance = plane->DistanceToPlane(coordinates); if (distance < 0.1) { vtkSmartPointer sphere = vtkSmartPointer::New(); sphere->SetRadius(1.2); sphere->SetCenter(coordinates[0], coordinates[1], coordinates[2]); sphere->Update(); appendPoly->AddInputConnection(sphere->GetOutputPort()); } } /* If the contour is closed an additional line has to be created between the very first point * and the last point */ if (renderingContour->IsClosed(timestep)) { // add a line from the last to the first control point mitk::ContourModel::VertexType *firstControlPoint = *(renderingContour->IteratorBegin(timestep)); mitk::ContourModel::VertexType *lastControlPoint = *(--(renderingContour->IteratorEnd(timestep))); vtkIdType p2 = points->InsertNextPoint( lastControlPoint->Coordinates[0], lastControlPoint->Coordinates[1], lastControlPoint->Coordinates[2]); vtkIdType p1 = points->InsertNextPoint( firstControlPoint->Coordinates[0], firstControlPoint->Coordinates[1], firstControlPoint->Coordinates[2]); // add the line between both contorlPoints lines->InsertNextCell(2); lines->InsertCellPoint(p1); lines->InsertCellPoint(p2); } // end if(isClosed) // Add the points to the dataset polyDataIn3D->SetPoints(points); // Add the lines to the dataset polyDataIn3D->SetLines(lines); // cut through polyData bool useCuttingPlane = false; this->GetDataNode()->GetBoolProperty("use cutting plane", useCuttingPlane, renderer); if (useCuttingPlane) { // slice through the data to get a 2D representation of the (possible) 3D contour // needed because currently there is no outher solution if the contour is within the plane vtkSmartPointer tubeFilter = vtkSmartPointer::New(); tubeFilter->SetInputData(polyDataIn3D); tubeFilter->SetRadius(0.05); // cuts through vtkPolyData with a given implicit function. In our case a plane vtkSmartPointer cutter = vtkSmartPointer::New(); cutter->SetCutFunction(plane); cutter->SetInputConnection(tubeFilter->GetOutputPort()); // we want the scalars of the input - so turn off generating the scalars within vtkCutter cutter->GenerateCutScalarsOff(); cutter->Update(); // set to 2D representation of the contour resultingPolyData = cutter->GetOutput(); } // end if(project contour) else { // set to 3D polyData resultingPolyData = polyDataIn3D; } } // end if (it != end) appendPoly->AddInputData(resultingPolyData); appendPoly->Update(); // return contour with control points return appendPoly->GetOutput(); } else { // return empty polyData return resultingPolyData; } } void mitk::ContourModelMapper2D::ApplyContourProperties(mitk::BaseRenderer *renderer) { LocalStorage *localStorage = m_LSH.GetLocalStorage(renderer); float lineWidth(1.0); if (this->GetDataNode()->GetFloatProperty("width", lineWidth, renderer)) { localStorage->m_Actor->GetProperty()->SetLineWidth(lineWidth); } mitk::ColorProperty::Pointer colorprop = dynamic_cast(GetDataNode()->GetProperty("color", renderer)); if (colorprop) { // set the color of the contour double red = colorprop->GetColor().GetRed(); double green = colorprop->GetColor().GetGreen(); double blue = colorprop->GetColor().GetBlue(); localStorage->m_Actor->GetProperty()->SetColor(red, green, blue); } // make sure that directional lighting isn't used for our contour localStorage->m_Actor->GetProperty()->SetAmbient(1.0); localStorage->m_Actor->GetProperty()->SetDiffuse(0.0); localStorage->m_Actor->GetProperty()->SetSpecular(0.0); } /*+++++++++++++++++++ LocalStorage part +++++++++++++++++++++++++*/ mitk::ContourModelMapper2D::LocalStorage *mitk::ContourModelMapper2D::GetLocalStorage(mitk::BaseRenderer *renderer) { return m_LSH.GetLocalStorage(renderer); } mitk::ContourModelMapper2D::LocalStorage::LocalStorage() { m_Mapper = vtkSmartPointer::New(); m_Actor = vtkSmartPointer::New(); m_OutlinePolyData = vtkSmartPointer::New(); // set the mapper for the actor m_Actor->SetMapper(m_Mapper); } void mitk::ContourModelMapper2D::SetDefaultProperties(mitk::DataNode *node, mitk::BaseRenderer *renderer, bool overwrite) { node->AddProperty("color", ColorProperty::New(0.9, 1.0, 0.1), renderer, overwrite); node->AddProperty("width", mitk::FloatProperty::New(1.0), renderer, overwrite); node->AddProperty("use cutting plane", mitk::BoolProperty::New(true), renderer, overwrite); node->AddProperty("subdivision curve", mitk::BoolProperty::New(false), renderer, overwrite); Superclass::SetDefaultProperties(node, renderer, overwrite); } diff --git a/Modules/ContourModel/Rendering/mitkContourModelMapper3D.cpp b/Modules/ContourModel/Rendering/mitkContourModelMapper3D.cpp index a01e64bd55..88103a6feb 100644 --- a/Modules/ContourModel/Rendering/mitkContourModelMapper3D.cpp +++ b/Modules/ContourModel/Rendering/mitkContourModelMapper3D.cpp @@ -1,237 +1,237 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include #include #include #include mitk::ContourModelMapper3D::ContourModelMapper3D() { } mitk::ContourModelMapper3D::~ContourModelMapper3D() { } const mitk::ContourModel *mitk::ContourModelMapper3D::GetInput(void) { // convient way to get the data from the dataNode return static_cast(GetDataNode()->GetData()); } vtkProp *mitk::ContourModelMapper3D::GetVtkProp(mitk::BaseRenderer *renderer) { // return the actor corresponding to the renderer return m_LSH.GetLocalStorage(renderer)->m_Actor; } void mitk::ContourModelMapper3D::GenerateDataForRenderer(mitk::BaseRenderer *renderer) { /* First convert the contourModel to vtkPolyData, then tube filter it and * set it input for our mapper */ LocalStorage *localStorage = m_LSH.GetLocalStorage(renderer); - mitk::ContourModel *inputContour = static_cast(GetDataNode()->GetData()); + auto *inputContour = static_cast(GetDataNode()->GetData()); localStorage->m_OutlinePolyData = this->CreateVtkPolyDataFromContour(inputContour); this->ApplyContourProperties(renderer); // tube filter the polyData localStorage->m_TubeFilter->SetInputData(localStorage->m_OutlinePolyData); float lineWidth(1.0); if (this->GetDataNode()->GetFloatProperty("contour.3D.width", lineWidth, renderer)) { localStorage->m_TubeFilter->SetRadius(lineWidth); } else { localStorage->m_TubeFilter->SetRadius(0.5); } localStorage->m_TubeFilter->CappingOn(); localStorage->m_TubeFilter->SetNumberOfSides(10); localStorage->m_TubeFilter->Update(); localStorage->m_Mapper->SetInputConnection(localStorage->m_TubeFilter->GetOutputPort()); } void mitk::ContourModelMapper3D::Update(mitk::BaseRenderer *renderer) { bool visible = true; GetDataNode()->GetVisibility(visible, renderer, "visible"); - mitk::ContourModel *data = static_cast(GetDataNode()->GetData()); + auto *data = static_cast(GetDataNode()->GetData()); if (data == nullptr) { return; } // Calculate time step of the input data for the specified renderer (integer value) this->CalculateTimeStep(renderer); LocalStorage *localStorage = m_LSH.GetLocalStorage(renderer); // Check if time step is valid const TimeGeometry *dataTimeGeometry = data->GetTimeGeometry(); if ((dataTimeGeometry == nullptr) || (dataTimeGeometry->CountTimeSteps() == 0) || (!dataTimeGeometry->IsValidTimeStep(renderer->GetTimeStep())) || (this->GetTimestep() == -1)) { // clear the rendered polydata localStorage->m_Mapper->SetInputData(vtkSmartPointer::New()); return; } const DataNode *node = this->GetDataNode(); data->UpdateOutputInformation(); // check if something important has changed and we need to rerender if ((localStorage->m_LastUpdateTime < node->GetMTime()) // was the node modified? || (localStorage->m_LastUpdateTime < data->GetPipelineMTime()) // Was the data modified? || (localStorage->m_LastUpdateTime < renderer->GetCurrentWorldPlaneGeometryUpdateTime()) // was the geometry modified? || (localStorage->m_LastUpdateTime < renderer->GetCurrentWorldPlaneGeometry()->GetMTime()) || (localStorage->m_LastUpdateTime < node->GetPropertyList()->GetMTime()) // was a property modified? || (localStorage->m_LastUpdateTime < node->GetPropertyList(renderer)->GetMTime())) { this->GenerateDataForRenderer(renderer); } // since we have checked that nothing important has changed, we can set // m_LastUpdateTime to the current time localStorage->m_LastUpdateTime.Modified(); } vtkSmartPointer mitk::ContourModelMapper3D::CreateVtkPolyDataFromContour(mitk::ContourModel *inputContour) { unsigned int timestep = this->GetTimestep(); // the points to draw vtkSmartPointer points = vtkSmartPointer::New(); // the lines to connect the points vtkSmartPointer lines = vtkSmartPointer::New(); // Create a polydata to store everything in vtkSmartPointer polyData = vtkSmartPointer::New(); // iterate over the control points - mitk::ContourModel::VertexIterator current = inputContour->IteratorBegin(timestep); - mitk::ContourModel::VertexIterator next = inputContour->IteratorBegin(timestep); + auto current = inputContour->IteratorBegin(timestep); + auto next = inputContour->IteratorBegin(timestep); if (next != inputContour->IteratorEnd(timestep)) { next++; - mitk::ContourModel::VertexIterator end = inputContour->IteratorEnd(timestep); + auto end = inputContour->IteratorEnd(timestep); while (next != end) { mitk::ContourModel::VertexType *currentControlPoint = *current; mitk::ContourModel::VertexType *nextControlPoint = *next; if (!(currentControlPoint->Coordinates[0] == nextControlPoint->Coordinates[0] && currentControlPoint->Coordinates[1] == nextControlPoint->Coordinates[1] && currentControlPoint->Coordinates[2] == nextControlPoint->Coordinates[2])) { vtkIdType p1 = points->InsertNextPoint(currentControlPoint->Coordinates[0], currentControlPoint->Coordinates[1], currentControlPoint->Coordinates[2]); vtkIdType p2 = points->InsertNextPoint( nextControlPoint->Coordinates[0], nextControlPoint->Coordinates[1], nextControlPoint->Coordinates[2]); // add the line between both contorlPoints lines->InsertNextCell(2); lines->InsertCellPoint(p1); lines->InsertCellPoint(p2); } current++; next++; } if (inputContour->IsClosed(timestep)) { // If the contour is closed add a line from the last to the first control point mitk::ContourModel::VertexType *firstControlPoint = *(inputContour->IteratorBegin(timestep)); mitk::ContourModel::VertexType *lastControlPoint = *(--(inputContour->IteratorEnd(timestep))); if (lastControlPoint->Coordinates[0] != firstControlPoint->Coordinates[0] || lastControlPoint->Coordinates[1] != firstControlPoint->Coordinates[1] || lastControlPoint->Coordinates[2] != firstControlPoint->Coordinates[2]) { vtkIdType p2 = points->InsertNextPoint( lastControlPoint->Coordinates[0], lastControlPoint->Coordinates[1], lastControlPoint->Coordinates[2]); vtkIdType p1 = points->InsertNextPoint( firstControlPoint->Coordinates[0], firstControlPoint->Coordinates[1], firstControlPoint->Coordinates[2]); // add the line to the cellArray lines->InsertNextCell(2); lines->InsertCellPoint(p1); lines->InsertCellPoint(p2); } } // Add the points to the dataset polyData->SetPoints(points); // Add the lines to the dataset polyData->SetLines(lines); } return polyData; } void mitk::ContourModelMapper3D::ApplyContourProperties(mitk::BaseRenderer *renderer) { LocalStorage *localStorage = m_LSH.GetLocalStorage(renderer); mitk::ColorProperty::Pointer colorprop = dynamic_cast(GetDataNode()->GetProperty("contour.color", renderer)); if (colorprop) { // set the color of the contour double red = colorprop->GetColor().GetRed(); double green = colorprop->GetColor().GetGreen(); double blue = colorprop->GetColor().GetBlue(); localStorage->m_Actor->GetProperty()->SetColor(red, green, blue); } } /*+++++++++++++++++++ LocalStorage part +++++++++++++++++++++++++*/ mitk::ContourModelMapper3D::LocalStorage *mitk::ContourModelMapper3D::GetLocalStorage(mitk::BaseRenderer *renderer) { return m_LSH.GetLocalStorage(renderer); } mitk::ContourModelMapper3D::LocalStorage::LocalStorage() { m_Mapper = vtkSmartPointer::New(); m_Actor = vtkSmartPointer::New(); m_OutlinePolyData = vtkSmartPointer::New(); m_TubeFilter = vtkSmartPointer::New(); // set the mapper for the actor m_Actor->SetMapper(m_Mapper); } void mitk::ContourModelMapper3D::SetDefaultProperties(mitk::DataNode *node, mitk::BaseRenderer *renderer, bool overwrite) { node->AddProperty("color", ColorProperty::New(1.0, 0.0, 0.0), renderer, overwrite); node->AddProperty("contour.3D.width", mitk::FloatProperty::New(0.5), renderer, overwrite); Superclass::SetDefaultProperties(node, renderer, overwrite); } diff --git a/Modules/ContourModel/Rendering/mitkContourModelSetGLMapper2D.cpp b/Modules/ContourModel/Rendering/mitkContourModelSetGLMapper2D.cpp index a5a5e8dd38..fd18c0a34a 100644 --- a/Modules/ContourModel/Rendering/mitkContourModelSetGLMapper2D.cpp +++ b/Modules/ContourModel/Rendering/mitkContourModelSetGLMapper2D.cpp @@ -1,96 +1,96 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkContourModelSetGLMapper2D.h" #include "mitkColorProperty.h" #include "mitkContourModelSet.h" #include "mitkPlaneGeometry.h" #include "mitkProperties.h" #include #include "mitkGL.h" mitk::ContourModelSetGLMapper2D::ContourModelSetGLMapper2D() { } mitk::ContourModelSetGLMapper2D::~ContourModelSetGLMapper2D() { } void mitk::ContourModelSetGLMapper2D::MitkRender(mitk::BaseRenderer *renderer, mitk::VtkPropRenderer::RenderType /*type*/) { BaseLocalStorage *ls = m_LSH.GetLocalStorage(renderer); mitk::DataNode::Pointer dataNode = this->GetDataNode(); bool visible = true; dataNode->GetVisibility(visible, nullptr); if (!visible) return; mitk::ContourModelSet::Pointer input = this->GetInput(); auto centerOfViewPointZ = renderer->GetCurrentWorldPlaneGeometry()->GetCenter()[2]; - mitk::ContourModelSet::ContourModelSetIterator it = input->Begin(); + auto it = input->Begin(); - mitk::ContourModelSet::ContourModelSetIterator end = input->End(); + auto end = input->End(); while (it != end) { //we have the assumption that each contour model vertex has the same z coordinate auto currentZValue = (*it)->GetVertexAt(0)->Coordinates[2]; double acceptedDeviationInMM = 5.0; //only draw contour if it is visible if (currentZValue - acceptedDeviationInMM < centerOfViewPointZ && currentZValue + acceptedDeviationInMM > centerOfViewPointZ){ this->DrawContour(it->GetPointer(), renderer); } ++it; } if (input->GetSize() < 1) return; ls->UpdateGenerateDataTime(); } mitk::ContourModelSet *mitk::ContourModelSetGLMapper2D::GetInput(void) { return const_cast(static_cast(GetDataNode()->GetData())); } void mitk::ContourModelSetGLMapper2D::SetDefaultProperties(mitk::DataNode *node, mitk::BaseRenderer *renderer, bool overwrite) { node->AddProperty("contour.color", ColorProperty::New(0.9, 1.0, 0.1), renderer, overwrite); node->AddProperty("contour.points.color", ColorProperty::New(1.0, 0.0, 0.1), renderer, overwrite); node->AddProperty("contour.points.show", mitk::BoolProperty::New(false), renderer, overwrite); node->AddProperty("contour.segments.show", mitk::BoolProperty::New(true), renderer, overwrite); node->AddProperty("contour.controlpoints.show", mitk::BoolProperty::New(false), renderer, overwrite); node->AddProperty("contour.width", mitk::FloatProperty::New(1.0), renderer, overwrite); node->AddProperty("contour.hovering.width", mitk::FloatProperty::New(3.0), renderer, overwrite); node->AddProperty("contour.hovering", mitk::BoolProperty::New(false), renderer, overwrite); node->AddProperty("contour.points.text", mitk::BoolProperty::New(false), renderer, overwrite); node->AddProperty("contour.controlpoints.text", mitk::BoolProperty::New(false), renderer, overwrite); node->AddProperty("contour.project-onto-plane", mitk::BoolProperty::New(false), renderer, overwrite); node->AddProperty("opacity", mitk::FloatProperty::New(1.0f), renderer, overwrite); Superclass::SetDefaultProperties(node, renderer, overwrite); } diff --git a/Modules/ContourModel/Rendering/mitkContourModelSetMapper3D.cpp b/Modules/ContourModel/Rendering/mitkContourModelSetMapper3D.cpp index 8c9974f602..e970fdf2f6 100644 --- a/Modules/ContourModel/Rendering/mitkContourModelSetMapper3D.cpp +++ b/Modules/ContourModel/Rendering/mitkContourModelSetMapper3D.cpp @@ -1,236 +1,236 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include #include "mitkSurface.h" #include #include #include #include mitk::ContourModelSetMapper3D::ContourModelSetMapper3D() { } mitk::ContourModelSetMapper3D::~ContourModelSetMapper3D() { } const mitk::ContourModelSet *mitk::ContourModelSetMapper3D::GetInput(void) { // convenient way to get the data from the dataNode return static_cast(GetDataNode()->GetData()); } vtkProp *mitk::ContourModelSetMapper3D::GetVtkProp(mitk::BaseRenderer *renderer) { // return the actor corresponding to the renderer return m_LSH.GetLocalStorage(renderer)->m_Assembly; } void mitk::ContourModelSetMapper3D::GenerateDataForRenderer(mitk::BaseRenderer *renderer) { /* First convert the contourModel to vtkPolyData, then tube filter it and * set it input for our mapper */ LocalStorage *localStorage = m_LSH.GetLocalStorage(renderer); - ContourModelSet *contourModelSet = dynamic_cast(this->GetDataNode()->GetData()); + auto *contourModelSet = dynamic_cast(this->GetDataNode()->GetData()); if (contourModelSet != nullptr) { vtkSmartPointer points = vtkSmartPointer::New(); vtkSmartPointer cells = vtkSmartPointer::New(); vtkIdType baseIndex = 0; - ContourModelSet::ContourModelSetIterator it = contourModelSet->Begin(); - ContourModelSet::ContourModelSetIterator end = contourModelSet->End(); + auto it = contourModelSet->Begin(); + auto end = contourModelSet->End(); while (it != end) { ContourModel *contourModel = it->GetPointer(); - ContourModel::VertexIterator vertIt = contourModel->Begin(); - ContourModel::VertexIterator vertEnd = contourModel->End(); + auto vertIt = contourModel->Begin(); + auto vertEnd = contourModel->End(); while (vertIt != vertEnd) { points->InsertNextPoint((*vertIt)->Coordinates[0], (*vertIt)->Coordinates[1], (*vertIt)->Coordinates[2]); ++vertIt; } vtkSmartPointer line = vtkSmartPointer::New(); vtkIdList *pointIds = line->GetPointIds(); vtkIdType numPoints = contourModel->GetNumberOfVertices(); pointIds->SetNumberOfIds(numPoints + 1); for (vtkIdType i = 0; i < numPoints; ++i) pointIds->SetId(i, baseIndex + i); pointIds->SetId(numPoints, baseIndex); cells->InsertNextCell(line); baseIndex += numPoints; ++it; } vtkSmartPointer polyData = vtkSmartPointer::New(); polyData->SetPoints(points); polyData->SetLines(cells); vtkSmartPointer mapper = vtkSmartPointer::New(); vtkSmartPointer actor = vtkSmartPointer::New(); actor->SetMapper(mapper); mapper->SetInputData(polyData); localStorage->m_Assembly->AddPart(actor); } this->ApplyContourProperties(renderer); this->ApplyContourModelSetProperties(renderer); } void mitk::ContourModelSetMapper3D::Update(mitk::BaseRenderer *renderer) { bool visible = true; GetDataNode()->GetVisibility(visible, renderer, "visible"); - mitk::ContourModel *data = static_cast(GetDataNode()->GetData()); + auto *data = static_cast(GetDataNode()->GetData()); if (data == nullptr) { return; } // Calculate time step of the input data for the specified renderer (integer value) this->CalculateTimeStep(renderer); LocalStorage *localStorage = m_LSH.GetLocalStorage(renderer); if (this->GetTimestep() == -1) { return; } const DataNode *node = this->GetDataNode(); data->UpdateOutputInformation(); // check if something important has changed and we need to rerender if ((localStorage->m_LastUpdateTime < node->GetMTime()) // was the node modified? || (localStorage->m_LastUpdateTime < data->GetPipelineMTime()) // Was the data modified? || (localStorage->m_LastUpdateTime < renderer->GetCurrentWorldPlaneGeometryUpdateTime()) // was the geometry modified? || (localStorage->m_LastUpdateTime < renderer->GetCurrentWorldPlaneGeometry()->GetMTime()) || (localStorage->m_LastUpdateTime < node->GetPropertyList()->GetMTime()) // was a property modified? || (localStorage->m_LastUpdateTime < node->GetPropertyList(renderer)->GetMTime())) { this->GenerateDataForRenderer(renderer); } // since we have checked that nothing important has changed, we can set // m_LastUpdateTime to the current time localStorage->m_LastUpdateTime.Modified(); } vtkSmartPointer mitk::ContourModelSetMapper3D::CreateVtkPolyDataFromContour( mitk::ContourModel *inputContour, mitk::BaseRenderer *renderer) { unsigned int timestep = this->GetTimestep(); LocalStorage *localStorage = m_LSH.GetLocalStorage(renderer); localStorage->m_contourToPolyData->SetInput(inputContour); localStorage->m_contourToPolyData->Update(); vtkSmartPointer polyData = vtkSmartPointer::New(); polyData = localStorage->m_contourToPolyData->GetOutput()->GetVtkPolyData(timestep); return polyData; } void mitk::ContourModelSetMapper3D::ApplyContourModelSetProperties(BaseRenderer *renderer) { LocalStorage *localStorage = m_LSH.GetLocalStorage(renderer); DataNode *dataNode = this->GetDataNode(); if (dataNode != nullptr) { float lineWidth = 1; dataNode->GetFloatProperty("contour.3D.width", lineWidth, renderer); vtkSmartPointer collection = vtkSmartPointer::New(); localStorage->m_Assembly->GetActors(collection); collection->InitTraversal(); for (vtkIdType i = 0; i < collection->GetNumberOfItems(); i++) { vtkActor::SafeDownCast(collection->GetNextProp())->GetProperty()->SetLineWidth(lineWidth); } } } void mitk::ContourModelSetMapper3D::ApplyContourProperties(mitk::BaseRenderer *renderer) { LocalStorage *localStorage = m_LSH.GetLocalStorage(renderer); mitk::ColorProperty::Pointer colorprop = dynamic_cast(GetDataNode()->GetProperty("contour.color", renderer)); if (colorprop) { // set the color of the contour double red = colorprop->GetColor().GetRed(); double green = colorprop->GetColor().GetGreen(); double blue = colorprop->GetColor().GetBlue(); vtkSmartPointer collection = vtkSmartPointer::New(); localStorage->m_Assembly->GetActors(collection); collection->InitTraversal(); for (vtkIdType i = 0; i < collection->GetNumberOfItems(); i++) { vtkActor::SafeDownCast(collection->GetNextProp())->GetProperty()->SetColor(red, green, blue); } } } /*+++++++++++++++++++ LocalStorage part +++++++++++++++++++++++++*/ mitk::ContourModelSetMapper3D::LocalStorage *mitk::ContourModelSetMapper3D::GetLocalStorage( mitk::BaseRenderer *renderer) { return m_LSH.GetLocalStorage(renderer); } mitk::ContourModelSetMapper3D::LocalStorage::LocalStorage() { m_Assembly = vtkSmartPointer::New(); m_contourToPolyData = mitk::ContourModelToSurfaceFilter::New(); } void mitk::ContourModelSetMapper3D::SetDefaultProperties(mitk::DataNode *node, mitk::BaseRenderer *renderer, bool overwrite) { node->AddProperty("color", ColorProperty::New(1.0, 0.0, 0.0), renderer, overwrite); node->AddProperty("contour.3D.width", mitk::FloatProperty::New(0.5), renderer, overwrite); Superclass::SetDefaultProperties(node, renderer, overwrite); } diff --git a/Modules/ContourModel/Testing/mitkContourModelIOTest.cpp b/Modules/ContourModel/Testing/mitkContourModelIOTest.cpp index 7ae22b3569..b863bc91a0 100644 --- a/Modules/ContourModel/Testing/mitkContourModelIOTest.cpp +++ b/Modules/ContourModel/Testing/mitkContourModelIOTest.cpp @@ -1,105 +1,105 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkTestingConfig.h" #include #include #include #include #include #include static void TestContourModel(mitk::ContourModel *contour, std::string fileName) { std::string filename = std::string(MITK_TEST_OUTPUT_DIR) + fileName; mitk::IOUtil::Save(contour, filename); std::vector> readerOutput = mitk::IOUtil::Load(filename); mitk::ContourModel::Pointer contour2 = dynamic_cast(readerOutput.at(0).GetPointer()); MITK_TEST_CONDITION_REQUIRED(contour2.IsNotNull(), "contour is not null"); MITK_TEST_CONDITION_REQUIRED(contour->GetNumberOfVertices() == contour2->GetNumberOfVertices(), "contours have the same number of vertices"); - mitk::ContourModel::VertexIterator it = contour2->IteratorBegin(); - mitk::ContourModel::VertexIterator end = contour2->IteratorEnd(); + auto it = contour2->IteratorBegin(); + auto end = contour2->IteratorEnd(); - mitk::ContourModel::VertexIterator it2 = contour2->IteratorBegin(); + auto it2 = contour2->IteratorBegin(); bool areEqual = true; while (it != end) { areEqual &= ((*it)->Coordinates == (*it2)->Coordinates); it++; it2++; } MITK_TEST_CONDITION(areEqual, "contours are equal"); } static void TestContourModelIO_OneTimeStep() { mitk::ContourModel::Pointer contour = mitk::ContourModel::New(); mitk::Point3D p; p[0] = p[1] = p[2] = 0; contour->AddVertex(p); mitk::Point3D p2; p2[0] = p2[1] = p2[2] = 1; contour->AddVertex(p2); mitk::Point3D p3; p3[0] = -2; p3[1] = 10; p3[2] = 0; contour->AddVertex(p3); mitk::Point3D p4; p4[0] = -3; p4[1] = 6; p4[2] = -5; contour->AddVertex(p4); TestContourModel(contour.GetPointer(), "/contour.cnt"); } static void TestContourModelIO_EmptyContourModel() { // Commented out: Saving of empty basedatas is invalid since Reader/Writer redesign // mitk::ContourModel::Pointer contour = mitk::ContourModel::New(); // TestContourModel(contour.GetPointer(), "/contourEmpty.cnt"); } int mitkContourModelIOTest(int /*argc*/, char * /*argv*/ []) { MITK_TEST_BEGIN("mitkContourModelIOTest") TestContourModelIO_OneTimeStep(); TestContourModelIO_EmptyContourModel(); MITK_TEST_END() } diff --git a/Modules/ContourModel/Testing/mitkContourModelTest.cpp b/Modules/ContourModel/Testing/mitkContourModelTest.cpp index 8d14320f66..a425cadf2b 100644 --- a/Modules/ContourModel/Testing/mitkContourModelTest.cpp +++ b/Modules/ContourModel/Testing/mitkContourModelTest.cpp @@ -1,423 +1,423 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include #include // Add a vertex to the contour and see if size changed static void TestAddVertex() { mitk::ContourModel::Pointer contour = mitk::ContourModel::New(); mitk::Point3D p; p[0] = p[1] = p[2] = 0; contour->AddVertex(p); MITK_TEST_CONDITION(contour->GetNumberOfVertices() > 0, "Add a Vertex, size increased"); } // Select a vertex by index. successful if the selected vertex member of the contour is no longer set to null static void TestSelectVertexAtIndex() { mitk::ContourModel::Pointer contour = mitk::ContourModel::New(); mitk::Point3D p; p[0] = p[1] = p[2] = 0; contour->AddVertex(p); contour->SelectVertexAt(0); MITK_TEST_CONDITION(contour->GetSelectedVertex() != nullptr, "Vertex was selected at index"); } // Select a vertex by worldposition. successful if the selected vertex member of the contour is no longer set to null static void TestSelectVertexAtWorldposition() { mitk::ContourModel::Pointer contour = mitk::ContourModel::New(); mitk::Point3D p; p[0] = p[1] = p[2] = 0; contour->AddVertex(p); // same point is used here so the epsilon can be chosen very small contour->SelectVertexAt(p, 0.01); MITK_TEST_CONDITION(contour->GetSelectedVertex() != nullptr, "Vertex was selected at position"); } // Move a vertex by a translation vector static void TestMoveSelectedVertex() { mitk::ContourModel::Pointer contour = mitk::ContourModel::New(); mitk::Point3D p; p[0] = p[1] = p[2] = 0; contour->AddVertex(p); // Same point is used here so the epsilon can be chosen very small contour->SelectVertexAt(p, 0.01); mitk::Vector3D v; v[0] = 1; v[1] = 3; v[2] = -1; contour->ShiftSelectedVertex(v); const mitk::ContourModel::VertexType *vertex = contour->GetSelectedVertex(); bool correctlyMoved = false; correctlyMoved = (vertex->Coordinates)[0] == (v[0]) && (vertex->Coordinates)[1] == (v[1]) && (vertex->Coordinates)[2] == (v[2]); MITK_TEST_CONDITION(correctlyMoved, "Vertex has been moved"); } // Test to move the whole contour /* static void TestMoveContour() { mitk::ContourModel::Pointer contour = mitk::ContourModel::New(); mitk::Point3D p; p[0] = p[1] = p[2] = 0; contour->AddVertex(p); mitk::Point3D p2; p2[0] = p2[1] = p2[2] = 0; contour->AddVertex(p2); mitk::Vector3D v; v[0] = 1; v[1] = 3; v[2] = -1; contour->ShiftContour(v); mitk::ContourModel::VertexIterator it = contour->IteratorBegin(); mitk::ContourModel::VertexIterator end = contour->IteratorEnd(); bool correctlyMoved = false; while(it != end) { correctlyMoved &= (*it)->Coordinates[0] == (v[0]) && (*it)->Coordinates[1] == (v[1]) && (*it)->Coordinates[2] == (v[2]); } MITK_TEST_CONDITION(correctlyMoved, "Contour has been moved"); } */ // Remove a vertex by index static void TestRemoveVertexAtIndex() { mitk::ContourModel::Pointer contour = mitk::ContourModel::New(); mitk::Point3D p; p[0] = p[1] = p[2] = 0; contour->AddVertex(p); contour->RemoveVertexAt(0); MITK_TEST_CONDITION(contour->GetNumberOfVertices() == 0, "removed vertex"); } // Remove a vertex by position static void TestRemoveVertexAtWorldPosition() { mitk::ContourModel::Pointer contour = mitk::ContourModel::New(); mitk::Point3D p; p[0] = p[1] = p[2] = 0; contour->AddVertex(p); contour->RemoveVertexAt(p, 0.01); MITK_TEST_CONDITION(contour->GetNumberOfVertices() == 0, "removed vertex"); } // Check closeable contour static void TestIsclosed() { mitk::ContourModel::Pointer contour = mitk::ContourModel::New(); mitk::Point3D p; p[0] = p[1] = p[2] = 0; contour->AddVertex(p); mitk::Point3D p2; p2[0] = p2[1] = p2[2] = 1; contour->AddVertex(p2); contour->Close(); MITK_TEST_CONDITION(contour->IsClosed(), "closed contour"); // no vertices should be added to a closed contour int oldNumberOfVertices = contour->GetNumberOfVertices(); mitk::Point3D p3; p3[0] = p3[1] = p3[2] = 4; contour->AddVertex(p3); int newNumberOfVertices = contour->GetNumberOfVertices(); MITK_TEST_CONDITION(oldNumberOfVertices != newNumberOfVertices, "vertices added to closed contour"); } // Test concatenating two contours static void TestConcatenate() { mitk::ContourModel::Pointer contour = mitk::ContourModel::New(); mitk::Point3D p; p[0] = p[1] = p[2] = 0; contour->AddVertex(p); mitk::Point3D p2; p2[0] = p2[1] = p2[2] = 1; contour->AddVertex(p2); mitk::ContourModel::Pointer contour2 = mitk::ContourModel::New(); mitk::Point3D p3; p3[0] = -2; p3[1] = 10; p3[2] = 0; contour2->AddVertex(p3); mitk::Point3D p4; p4[0] = -3; p4[1] = 6; p4[2] = -5; contour2->AddVertex(p4); contour->Concatenate(contour2); MITK_TEST_CONDITION(contour->GetNumberOfVertices() == 4, "two contours were concatenated"); } // Try to select a vertex at position (within a epsilon of course) where no vertex is. // So the selected verted member should be null. static void TestSelectVertexAtWrongPosition() { mitk::ContourModel::Pointer contour = mitk::ContourModel::New(); MITK_TEST_CONDITION_REQUIRED(contour->GetSelectedVertex() == nullptr, "selected vertex is nullptr"); mitk::Point3D p; p[0] = p[1] = p[2] = 0; contour->AddVertex(p); mitk::Point3D p2; p2[0] = p2[1] = p2[2] = 2; contour->SelectVertexAt(p2, 0.1); MITK_TEST_CONDITION(contour->GetSelectedVertex() == nullptr, "Vertex was not selected"); } static void TestInsertVertex() { mitk::ContourModel::Pointer contour = mitk::ContourModel::New(); mitk::Point3D p; p[0] = p[1] = p[2] = 0; contour->AddVertex(p); mitk::Point3D p2; p2[0] = p2[1] = p2[2] = 1; contour->AddVertex(p2); mitk::Point3D pointToInsert; pointToInsert[0] = pointToInsert[1] = pointToInsert[2] = 10; contour->InsertVertexAtIndex(pointToInsert, 1); MITK_TEST_CONDITION(contour->GetNumberOfVertices() == 3, "test insert vertex"); MITK_TEST_CONDITION(contour->GetVertexAt(1)->Coordinates == pointToInsert, "compare inserted vertex"); } // try to access an invalid timestep static void TestInvalidTimeStep() { mitk::ContourModel::Pointer contour = mitk::ContourModel::New(); mitk::Point3D p; p[0] = p[1] = p[2] = 0; contour->AddVertex(p); mitk::Point3D p2; p2[0] = p2[1] = p2[2] = 1; contour->AddVertex(p2); int invalidTimeStep = 42; MITK_TEST_CONDITION_REQUIRED(contour->IsEmptyTimeStep(invalidTimeStep), "invalid timestep required"); MITK_TEST_FOR_EXCEPTION(std::exception, contour->IteratorBegin(-1)); contour->Close(invalidTimeStep); MITK_TEST_CONDITION(contour->IsClosed() == false, "test close for timestep 0"); MITK_TEST_CONDITION(contour->IsClosed(invalidTimeStep) == false, "test close at invalid timestep"); contour->SetClosed(true, invalidTimeStep); MITK_TEST_CONDITION(contour->GetNumberOfVertices(invalidTimeStep) == -1, "test number of vertices at invalid timestep"); contour->AddVertex(p2, invalidTimeStep); MITK_TEST_CONDITION(contour->GetNumberOfVertices(invalidTimeStep) == -1, "test add vertex at invalid timestep"); contour->InsertVertexAtIndex(p2, 0, false, invalidTimeStep); MITK_TEST_CONDITION(contour->GetNumberOfVertices(invalidTimeStep) == -1, "test insert vertex at invalid timestep"); MITK_TEST_CONDITION(contour->SelectVertexAt(0, invalidTimeStep) == false, "test select vertex at invalid timestep"); MITK_TEST_CONDITION(contour->RemoveVertexAt(0, invalidTimeStep) == false, "test remove vertex at invalid timestep"); } static void TestEmptyContour() { mitk::ContourModel::Pointer contour = mitk::ContourModel::New(); MITK_TEST_CONDITION(contour->IteratorBegin() == contour->IteratorEnd(), "test iterator of emtpy contour"); MITK_TEST_CONDITION(contour->GetNumberOfVertices() == 0, "test numberof vertices of empty contour"); } static void TestSetVertices() { mitk::ContourModel::Pointer contour = mitk::ContourModel::New(); mitk::Point3D p; p[0] = p[1] = p[2] = 0; contour->AddVertex(p); mitk::Point3D newCoordinates; newCoordinates[0] = newCoordinates[1] = newCoordinates[2] = 1; contour->SetVertexAt(0, newCoordinates); MITK_TEST_CONDITION(mitk::Equal(contour->GetVertexAt(0)->Coordinates, newCoordinates), "set coordinates"); mitk::ContourModel::Pointer contour2 = mitk::ContourModel::New(); mitk::Point3D p3; p3[0] = -2; p3[1] = 10; p3[2] = 0; contour2->AddVertex(p3); mitk::Point3D p4; p4[0] = -3; p4[1] = 6; p4[2] = -5; contour2->AddVertex(p4); contour->AddVertex(p); contour->SetVertexAt(1, contour2->GetVertexAt(1)); MITK_TEST_CONDITION( mitk::Equal(contour->GetVertexAt(1)->Coordinates, contour2->GetVertexAt(1)->Coordinates), "Use setter and getter combination"); } static void TestContourModelAPI() { mitk::ContourModel::Pointer contour1 = mitk::ContourModel::New(); mitk::Point3D p1; p1[0] = -2; p1[1] = 10; p1[2] = 0; contour1->AddVertex(p1); // adding vertices should always copy the content and not store pointers or references. MITK_TEST_CONDITION(&p1 != &(contour1->GetVertexAt(0)->Coordinates), "copied point"); mitk::Point3D p2; p2[0] = -3; p2[1] = 6; p2[2] = -5; contour1->AddVertex(p2); // test use of setter and getter with const and non-const pointers const mitk::ContourModel::VertexType *vertex = contour1->GetVertexAt(1); MITK_TEST_CONDITION(contour1->GetIndex(vertex) == 1, "Get index"); - mitk::ContourModel::VertexType *nonConstVertex = const_cast(vertex); + auto *nonConstVertex = const_cast(vertex); MITK_TEST_CONDITION(contour1->GetIndex(nonConstVertex) == 1, "Get index non-const"); mitk::ContourModel::Pointer contour2 = mitk::ContourModel::New(); contour2->AddVertex(contour1->GetVertexAt(0)); MITK_TEST_CONDITION(contour2->GetNumberOfVertices() == 1, "Add call with another contour"); } int mitkContourModelTest(int /*argc*/, char * /*argv*/ []) { MITK_TEST_BEGIN("mitkContourModelTest") TestAddVertex(); TestSelectVertexAtIndex(); TestSelectVertexAtWorldposition(); TestMoveSelectedVertex(); TestRemoveVertexAtIndex(); TestRemoveVertexAtWorldPosition(); TestIsclosed(); TestConcatenate(); TestInvalidTimeStep(); TestInsertVertex(); TestEmptyContour(); TestSetVertices(); TestSelectVertexAtWrongPosition(); TestContourModelAPI(); MITK_TEST_END() } diff --git a/Modules/Core/TestingHelper/src/mitkInteractionTestHelper.cpp b/Modules/Core/TestingHelper/src/mitkInteractionTestHelper.cpp index bbe21f608b..811b768609 100644 --- a/Modules/Core/TestingHelper/src/mitkInteractionTestHelper.cpp +++ b/Modules/Core/TestingHelper/src/mitkInteractionTestHelper.cpp @@ -1,437 +1,437 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ // MITK #include #include #include #include #include // VTK #include #include // us #include #include mitk::InteractionTestHelper::InteractionTestHelper(const std::string &interactionXmlFilePath) : m_InteractionFilePath(interactionXmlFilePath) { this->Initialize(interactionXmlFilePath); } void mitk::InteractionTestHelper::Initialize(const std::string &interactionXmlFilePath) { // TiXmlDocument document(interactionXmlPath.c_str()); TiXmlDocument document(interactionXmlFilePath); bool loadOkay = document.LoadFile(); if (loadOkay) { // get RenderingManager instance mitk::RenderingManager *rm = mitk::RenderingManager::GetInstance(); // create data storage m_DataStorage = mitk::StandaloneDataStorage::New(); // for each renderer found create a render window and configure for (TiXmlElement *element = document.FirstChildElement(mitk::InteractionEventConst::xmlTagInteractions()) ->FirstChildElement(mitk::InteractionEventConst::xmlTagConfigRoot()) ->FirstChildElement(mitk::InteractionEventConst::xmlTagRenderer()); element != nullptr; element = element->NextSiblingElement(mitk::InteractionEventConst::xmlTagRenderer())) { // get name of renderer const char *rendererName = element->Attribute(mitk::InteractionEventConst::xmlEventPropertyRendererName().c_str()); // get view direction mitk::SliceNavigationController::ViewDirection viewDirection = mitk::SliceNavigationController::Axial; if (element->Attribute(mitk::InteractionEventConst::xmlEventPropertyViewDirection()) != nullptr) { int viewDirectionNum = std::atoi(element->Attribute(mitk::InteractionEventConst::xmlEventPropertyViewDirection())->c_str()); viewDirection = static_cast(viewDirectionNum); } // get mapper slot id mitk::BaseRenderer::MapperSlotId mapperID = mitk::BaseRenderer::Standard2D; if (element->Attribute(mitk::InteractionEventConst::xmlEventPropertyMapperID()) != nullptr) { int mapperIDNum = std::atoi(element->Attribute(mitk::InteractionEventConst::xmlEventPropertyMapperID())->c_str()); mapperID = static_cast(mapperIDNum); } // Get Size of Render Windows int size[3]; size[0] = size[1] = size[2] = 0; if (element->Attribute(mitk::InteractionEventConst::xmlRenderSizeX()) != nullptr) { size[0] = std::atoi(element->Attribute(mitk::InteractionEventConst::xmlRenderSizeX())->c_str()); } if (element->Attribute(mitk::InteractionEventConst::xmlRenderSizeY()) != nullptr) { size[1] = std::atoi(element->Attribute(mitk::InteractionEventConst::xmlRenderSizeY())->c_str()); } if (element->Attribute(mitk::InteractionEventConst::xmlRenderSizeZ()) != nullptr) { size[2] = std::atoi(element->Attribute(mitk::InteractionEventConst::xmlRenderSizeZ())->c_str()); } // create renderWindow, renderer and dispatcher mitk::RenderWindow::Pointer rw = mitk::RenderWindow::New(nullptr, rendererName, rm); // VtkRenderWindow is created within constructor if nullptr if (size[0] != 0 && size[1] != 0) { rw->SetSize(size[0], size[1]); rw->GetRenderer()->Resize(size[0], size[1]); } // set storage of renderer rw->GetRenderer()->SetDataStorage(m_DataStorage); // set view direction to axial rw->GetSliceNavigationController()->SetDefaultViewDirection(viewDirection); // set renderer to render 2D rw->GetRenderer()->SetMapperID(mapperID); rw->GetRenderer()->PrepareRender(); // Some more magic for the 3D render window case: // Camera view direction, position and focal point if (mapperID == mitk::BaseRenderer::Standard3D) { if (element->Attribute(mitk::InteractionEventConst::xmlCameraFocalPointX()) != nullptr) { double cameraFocalPoint[3]; cameraFocalPoint[0] = std::atoi(element->Attribute(mitk::InteractionEventConst::xmlCameraFocalPointX())->c_str()); cameraFocalPoint[1] = std::atoi(element->Attribute(mitk::InteractionEventConst::xmlCameraFocalPointY())->c_str()); cameraFocalPoint[2] = std::atoi(element->Attribute(mitk::InteractionEventConst::xmlCameraFocalPointZ())->c_str()); rw->GetRenderer()->GetVtkRenderer()->GetActiveCamera()->SetFocalPoint(cameraFocalPoint); } if (element->Attribute(mitk::InteractionEventConst::xmlCameraPositionX()) != nullptr) { double cameraPosition[3]; cameraPosition[0] = std::atoi(element->Attribute(mitk::InteractionEventConst::xmlCameraPositionX())->c_str()); cameraPosition[1] = std::atoi(element->Attribute(mitk::InteractionEventConst::xmlCameraPositionY())->c_str()); cameraPosition[2] = std::atoi(element->Attribute(mitk::InteractionEventConst::xmlCameraPositionZ())->c_str()); rw->GetRenderer()->GetVtkRenderer()->GetActiveCamera()->SetPosition(cameraPosition); } if (element->Attribute(mitk::InteractionEventConst::xmlViewUpX()) != nullptr) { double viewUp[3]; viewUp[0] = std::atoi(element->Attribute(mitk::InteractionEventConst::xmlViewUpX())->c_str()); viewUp[1] = std::atoi(element->Attribute(mitk::InteractionEventConst::xmlViewUpY())->c_str()); viewUp[2] = std::atoi(element->Attribute(mitk::InteractionEventConst::xmlViewUpZ())->c_str()); rw->GetRenderer()->GetVtkRenderer()->GetActiveCamera()->SetViewUp(viewUp); } } rw->GetVtkRenderWindow()->Render(); rw->GetVtkRenderWindow()->WaitForCompletion(); // connect SliceNavigationControllers to timestep changed event of TimeNavigationController rw->GetSliceNavigationController()->ConnectGeometryTimeEvent(rm->GetTimeNavigationController(), false); rm->GetTimeNavigationController()->ConnectGeometryTimeEvent(rw->GetSliceNavigationController(), false); // add to list of kown render windows m_RenderWindowList.push_back(rw); } // TODO: check the following lines taken from QmitkStdMultiWidget and adapt them to be executed in our code here. // mitkWidget1->GetSliceNavigationController() // ->ConnectGeometrySendEvent(mitk::BaseRenderer::GetInstance(mitkWidget4->GetRenderWindow())); //########### register display interactor to handle scroll events ################## // use MouseModeSwitcher to ensure that the statemachine of DisplayInteractor is loaded correctly m_MouseModeSwitcher = mitk::MouseModeSwitcher::New(); } else { mitkThrow() << "Can not load interaction xml file <" << m_InteractionFilePath << ">"; } // WARNING assumes a 3D window exists !!!! this->AddDisplayPlaneSubTree(); } mitk::InteractionTestHelper::~InteractionTestHelper() { mitk::RenderingManager *rm = mitk::RenderingManager::GetInstance(); // unregister renderers - InteractionTestHelper::RenderWindowListType::iterator it = m_RenderWindowList.begin(); - InteractionTestHelper::RenderWindowListType::iterator end = m_RenderWindowList.end(); + auto it = m_RenderWindowList.begin(); + auto end = m_RenderWindowList.end(); for (; it != end; ++it) { rm->GetTimeNavigationController()->Disconnect((*it)->GetSliceNavigationController()); (*it)->GetSliceNavigationController()->Disconnect(rm->GetTimeNavigationController()); mitk::BaseRenderer::RemoveInstance((*it)->GetVtkRenderWindow()); } rm->RemoveAllObservers(); } mitk::DataStorage::Pointer mitk::InteractionTestHelper::GetDataStorage() { return m_DataStorage; } void mitk::InteractionTestHelper::AddNodeToStorage(mitk::DataNode::Pointer node) { this->m_DataStorage->Add(node); this->Set3dCameraSettings(); } void mitk::InteractionTestHelper::PlaybackInteraction() { mitk::RenderingManager::GetInstance()->InitializeViewsByBoundingObjects(m_DataStorage); // load events if not loaded yet if (m_Events.empty()) this->LoadInteraction(); - InteractionTestHelper::RenderWindowListType::iterator it = m_RenderWindowList.begin(); - InteractionTestHelper::RenderWindowListType::iterator end = m_RenderWindowList.end(); + auto it = m_RenderWindowList.begin(); + auto end = m_RenderWindowList.end(); for (; it != end; ++it) { (*it)->GetRenderer()->PrepareRender(); (*it)->GetVtkRenderWindow()->Render(); (*it)->GetVtkRenderWindow()->WaitForCompletion(); } mitk::RenderingManager::GetInstance()->InitializeViewsByBoundingObjects(m_DataStorage); it = m_RenderWindowList.begin(); for (; it != end; ++it) { (*it)->GetVtkRenderWindow()->Render(); (*it)->GetVtkRenderWindow()->WaitForCompletion(); } // mitk::RenderingManager::GetInstance()->ForceImmediateUpdateAll(); // playback all events in queue for (unsigned long i = 0; i < m_Events.size(); ++i) { // let dispatcher of sending renderer process the event m_Events.at(i)->GetSender()->GetDispatcher()->ProcessEvent(m_Events.at(i)); } if (false) { it--; (*it)->GetVtkRenderWindow()->GetInteractor()->Start(); } } void mitk::InteractionTestHelper::LoadInteraction() { // load interaction pattern from xml file std::ifstream xmlStream(m_InteractionFilePath.c_str()); mitk::XML2EventParser parser(xmlStream); m_Events = parser.GetInteractions(); xmlStream.close(); // Avoid VTK warning: Trying to delete object with non-zero reference count. parser.SetReferenceCount(0); } void mitk::InteractionTestHelper::SetTimeStep(int newTimeStep) { mitk::RenderingManager::GetInstance()->InitializeViewsByBoundingObjects(m_DataStorage); bool timeStepIsvalid = mitk::RenderingManager::GetInstance()->GetTimeNavigationController()->GetCreatedWorldGeometry()->IsValidTimeStep( newTimeStep); if (timeStepIsvalid) { mitk::RenderingManager::GetInstance()->GetTimeNavigationController()->GetTime()->SetPos(newTimeStep); } } mitk::RenderWindow *mitk::InteractionTestHelper::GetRenderWindowByName(const std::string &name) { - InteractionTestHelper::RenderWindowListType::iterator it = m_RenderWindowList.begin(); - InteractionTestHelper::RenderWindowListType::iterator end = m_RenderWindowList.end(); + auto it = m_RenderWindowList.begin(); + auto end = m_RenderWindowList.end(); for (; it != end; ++it) { if (name.compare((*it)->GetRenderer()->GetName()) == 0) return (*it).GetPointer(); } return nullptr; } mitk::RenderWindow *mitk::InteractionTestHelper::GetRenderWindowByDefaultViewDirection( mitk::SliceNavigationController::ViewDirection viewDirection) { - InteractionTestHelper::RenderWindowListType::iterator it = m_RenderWindowList.begin(); - InteractionTestHelper::RenderWindowListType::iterator end = m_RenderWindowList.end(); + auto it = m_RenderWindowList.begin(); + auto end = m_RenderWindowList.end(); for (; it != end; ++it) { if (viewDirection == (*it)->GetSliceNavigationController()->GetDefaultViewDirection()) return (*it).GetPointer(); } return nullptr; } mitk::RenderWindow *mitk::InteractionTestHelper::GetRenderWindow(unsigned int index) { if (index < m_RenderWindowList.size()) { return m_RenderWindowList.at(index).GetPointer(); } else { return nullptr; } } void mitk::InteractionTestHelper::AddDisplayPlaneSubTree() { // add the displayed planes of the multiwidget to a node to which the subtree // @a planesSubTree points ... mitk::PlaneGeometryDataMapper2D::Pointer mapper; mitk::IntProperty::Pointer layer = mitk::IntProperty::New(1000); mitk::DataNode::Pointer node = mitk::DataNode::New(); node->SetProperty("name", mitk::StringProperty::New("Widgets")); node->SetProperty("helper object", mitk::BoolProperty::New(true)); m_DataStorage->Add(node); for (auto it : m_RenderWindowList) { if (it->GetRenderer()->GetMapperID() == BaseRenderer::Standard3D) continue; // ... of widget 1 mitk::DataNode::Pointer planeNode1 = (mitk::BaseRenderer::GetInstance(it->GetVtkRenderWindow()))->GetCurrentWorldPlaneGeometryNode(); planeNode1->SetProperty("visible", mitk::BoolProperty::New(true)); planeNode1->SetProperty("name", mitk::StringProperty::New("widget1Plane")); planeNode1->SetProperty("includeInBoundingBox", mitk::BoolProperty::New(false)); planeNode1->SetProperty("helper object", mitk::BoolProperty::New(true)); planeNode1->SetProperty("layer", layer); planeNode1->SetColor(1.0, 0.0, 0.0); mapper = mitk::PlaneGeometryDataMapper2D::New(); planeNode1->SetMapper(mitk::BaseRenderer::Standard2D, mapper); m_DataStorage->Add(planeNode1, node); } } void mitk::InteractionTestHelper::Set3dCameraSettings() { TiXmlDocument document(m_InteractionFilePath); bool loadOkay = document.LoadFile(); if (loadOkay) { // for each renderer found create a render window and configure for (TiXmlElement *element = document.FirstChildElement(mitk::InteractionEventConst::xmlTagInteractions()) ->FirstChildElement(mitk::InteractionEventConst::xmlTagConfigRoot()) ->FirstChildElement(mitk::InteractionEventConst::xmlTagRenderer()); element != nullptr; element = element->NextSiblingElement(mitk::InteractionEventConst::xmlTagRenderer())) { // get name of renderer const char *rendererName = element->Attribute(mitk::InteractionEventConst::xmlEventPropertyRendererName().c_str()); // get mapper slot id mitk::BaseRenderer::MapperSlotId mapperID = mitk::BaseRenderer::Standard2D; if (element->Attribute(mitk::InteractionEventConst::xmlEventPropertyMapperID()) != nullptr) { int mapperIDNum = std::atoi(element->Attribute(mitk::InteractionEventConst::xmlEventPropertyMapperID())->c_str()); mapperID = static_cast(mapperIDNum); } if (mapperID == mitk::BaseRenderer::Standard3D) { RenderWindow *namedRenderer = nullptr; for (auto it : m_RenderWindowList) { if (strcmp(it->GetRenderer()->GetName(), rendererName) == 0) { namedRenderer = it.GetPointer(); break; } } if (namedRenderer == nullptr) { MITK_ERROR << "No match for render window was found."; return; } namedRenderer->GetRenderer()->PrepareRender(); if (element->Attribute(mitk::InteractionEventConst::xmlCameraFocalPointX()) != nullptr) { double cameraFocalPoint[3]; cameraFocalPoint[0] = std::atoi(element->Attribute(mitk::InteractionEventConst::xmlCameraFocalPointX())->c_str()); cameraFocalPoint[1] = std::atoi(element->Attribute(mitk::InteractionEventConst::xmlCameraFocalPointY())->c_str()); cameraFocalPoint[2] = std::atoi(element->Attribute(mitk::InteractionEventConst::xmlCameraFocalPointZ())->c_str()); namedRenderer->GetRenderer()->GetVtkRenderer()->GetActiveCamera()->SetFocalPoint(cameraFocalPoint); } if (element->Attribute(mitk::InteractionEventConst::xmlCameraPositionX()) != nullptr) { double cameraPosition[3]; cameraPosition[0] = std::atoi(element->Attribute(mitk::InteractionEventConst::xmlCameraPositionX())->c_str()); cameraPosition[1] = std::atoi(element->Attribute(mitk::InteractionEventConst::xmlCameraPositionY())->c_str()); cameraPosition[2] = std::atoi(element->Attribute(mitk::InteractionEventConst::xmlCameraPositionZ())->c_str()); namedRenderer->GetRenderer()->GetVtkRenderer()->GetActiveCamera()->SetPosition(cameraPosition); } if (element->Attribute(mitk::InteractionEventConst::xmlViewUpX()) != nullptr) { double viewUp[3]; viewUp[0] = std::atoi(element->Attribute(mitk::InteractionEventConst::xmlViewUpX())->c_str()); viewUp[1] = std::atoi(element->Attribute(mitk::InteractionEventConst::xmlViewUpY())->c_str()); viewUp[2] = std::atoi(element->Attribute(mitk::InteractionEventConst::xmlViewUpZ())->c_str()); namedRenderer->GetRenderer()->GetVtkRenderer()->GetActiveCamera()->SetViewUp(viewUp); } namedRenderer->GetVtkRenderWindow()->Render(); } } } } diff --git a/Modules/Core/include/vtkMitkRectangleProp.h b/Modules/Core/include/vtkMitkRectangleProp.h index 65bcde8ea5..8e1f59fa22 100644 --- a/Modules/Core/include/vtkMitkRectangleProp.h +++ b/Modules/Core/include/vtkMitkRectangleProp.h @@ -1,75 +1,75 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #ifndef vtkMitkRectangleProp_h #define vtkMitkRectangleProp_h #include #include #include #include class vtkPolyData; /** * @brief The vtkMitkRectangleProp2 class Renders a rectangle into a renderwindow as a frame. * * This class is a replacement for the deprecated vtkMitkRectangleProp, which * used to render the same effect with pure OpenGL. */ class MITKCORE_EXPORT vtkMitkRectangleProp : public vtkActor2D { public: static vtkMitkRectangleProp *New(); vtkTypeMacro(vtkMitkRectangleProp, vtkProp); /** * @brief SetColor Set the color of the rectangle. * @param col1 red * @param col2 green * @param col3 blue */ void SetColor(float col1, float col2, float col3); void SetLineWidth(unsigned int lineWidth); - int RenderOverlay(vtkViewport *viewport); + int RenderOverlay(vtkViewport *viewport) override; protected: vtkMitkRectangleProp(); virtual ~vtkMitkRectangleProp(); int m_Height; int m_Width; int m_OriginX; int m_OriginY; vtkIdType m_BottomLeftR, m_BottomRightL, m_BottomLeftU, m_TopLeftD, m_TopLeftR, m_TopRightL, m_TopRightD, m_BottomRightU; /** * @brief CreateRectangle internal helper to fill a vtkPolydata with a rectangle. */ void CreateRectangle(); void UpdateRectangle(); /** * @brief m_PolyData holds the rectangle. */ vtkSmartPointer m_PolyData; }; #endif /* vtkMitkRectangleProp_h */ diff --git a/Modules/Core/src/Algorithms/mitkClippedSurfaceBoundsCalculator.cpp b/Modules/Core/src/Algorithms/mitkClippedSurfaceBoundsCalculator.cpp index 3a28270f7b..48656f453d 100644 --- a/Modules/Core/src/Algorithms/mitkClippedSurfaceBoundsCalculator.cpp +++ b/Modules/Core/src/Algorithms/mitkClippedSurfaceBoundsCalculator.cpp @@ -1,296 +1,296 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkClippedSurfaceBoundsCalculator.h" #include "mitkLine.h" #define ROUND_P(x) ((x) >= 0 ? (int)((x) + 0.5) : (int)((x)-0.5)) mitk::ClippedSurfaceBoundsCalculator::ClippedSurfaceBoundsCalculator(const mitk::PlaneGeometry *geometry, mitk::Image::Pointer image) : m_PlaneGeometry(nullptr), m_Geometry3D(nullptr), m_Image(nullptr) { this->InitializeOutput(); this->SetInput(geometry, image); } mitk::ClippedSurfaceBoundsCalculator::ClippedSurfaceBoundsCalculator(const mitk::BaseGeometry *geometry, mitk::Image::Pointer image) : m_PlaneGeometry(nullptr), m_Geometry3D(nullptr), m_Image(nullptr) { this->InitializeOutput(); this->SetInput(geometry, image); } mitk::ClippedSurfaceBoundsCalculator::ClippedSurfaceBoundsCalculator(const PointListType pointlist, mitk::Image::Pointer image) : m_PlaneGeometry(nullptr), m_Geometry3D(nullptr), m_Image(image) { this->InitializeOutput(); m_ObjectPointsInWorldCoordinates = pointlist; } void mitk::ClippedSurfaceBoundsCalculator::InitializeOutput() { // initialize with meaningless slice indices m_MinMaxOutput.clear(); m_MinMaxOutput.reserve(3); for (int i = 0; i < 3; i++) { m_MinMaxOutput.push_back(OutputType(std::numeric_limits::max(), std::numeric_limits::min())); } } mitk::ClippedSurfaceBoundsCalculator::~ClippedSurfaceBoundsCalculator() { } void mitk::ClippedSurfaceBoundsCalculator::SetInput(const mitk::PlaneGeometry *geometry, mitk::Image *image) { if (geometry && image) { this->m_PlaneGeometry = geometry; this->m_Image = image; this->m_Geometry3D = nullptr; // Not possible to set both m_ObjectPointsInWorldCoordinates.clear(); } } void mitk::ClippedSurfaceBoundsCalculator::SetInput(const mitk::BaseGeometry *geometry, mitk::Image *image) { if (geometry && image) { this->m_Geometry3D = geometry; this->m_Image = image; this->m_PlaneGeometry = nullptr; // Not possible to set both m_ObjectPointsInWorldCoordinates.clear(); } } void mitk::ClippedSurfaceBoundsCalculator::SetInput(const std::vector pointlist, mitk::Image *image) { if (!pointlist.empty() && image) { m_Geometry3D = nullptr; m_PlaneGeometry = nullptr; m_Image = image; m_ObjectPointsInWorldCoordinates = pointlist; } } mitk::ClippedSurfaceBoundsCalculator::OutputType mitk::ClippedSurfaceBoundsCalculator::GetMinMaxSpatialDirectionX() { return this->m_MinMaxOutput[0]; } mitk::ClippedSurfaceBoundsCalculator::OutputType mitk::ClippedSurfaceBoundsCalculator::GetMinMaxSpatialDirectionY() { return this->m_MinMaxOutput[1]; } mitk::ClippedSurfaceBoundsCalculator::OutputType mitk::ClippedSurfaceBoundsCalculator::GetMinMaxSpatialDirectionZ() { return this->m_MinMaxOutput[2]; } void mitk::ClippedSurfaceBoundsCalculator::Update() { this->m_MinMaxOutput.clear(); m_MinMaxOutput.reserve(3); for (int i = 0; i < 3; i++) { this->m_MinMaxOutput.push_back(OutputType(std::numeric_limits::max(), std::numeric_limits::min())); } if (m_PlaneGeometry.IsNotNull()) { this->CalculateIntersectionPoints(m_PlaneGeometry); } else if (m_Geometry3D.IsNotNull()) { // go through all slices of the image, ... - const mitk::SlicedGeometry3D *slicedGeometry3D = + const auto *slicedGeometry3D = dynamic_cast(m_Geometry3D.GetPointer()); int allSlices = slicedGeometry3D->GetSlices(); this->CalculateIntersectionPoints(dynamic_cast(slicedGeometry3D->GetPlaneGeometry(0))); this->CalculateIntersectionPoints( dynamic_cast(slicedGeometry3D->GetPlaneGeometry(allSlices - 1))); } else if (!m_ObjectPointsInWorldCoordinates.empty()) { this->CalculateIntersectionPoints(m_ObjectPointsInWorldCoordinates); this->EnforceImageBounds(); } } void mitk::ClippedSurfaceBoundsCalculator::CalculateIntersectionPoints(const mitk::PlaneGeometry *geometry) { // SEE HEADER DOCUMENTATION for explanation const mitk::BaseGeometry::Pointer imageGeometry = m_Image->GetGeometry()->Clone(); // the cornerpoint(0) is the corner based Origin, which is original center based Point3D origin = imageGeometry->GetCornerPoint(0); // Left, bottom, front // Get axis vector for the spatial directions const Vector3D xDirection = imageGeometry->GetAxisVector(0); const Vector3D yDirection = imageGeometry->GetAxisVector(1); const Vector3D zDirection = imageGeometry->GetAxisVector(2); const Point3D leftBottomFront = origin; const Point3D leftTopFront = origin + yDirection; const Point3D leftBottomBack = origin + zDirection; const Point3D leftTopBack = origin + yDirection + zDirection; const Point3D rightBottomFront = origin + xDirection; const Point3D rightTopFront = origin + xDirection + yDirection; const Point3D rightBottomBack = origin + xDirection + zDirection; const Point3D rightTopBack = origin + xDirection + yDirection + zDirection; typedef std::vector> EdgesVector; EdgesVector edgesOf3DBox; edgesOf3DBox.reserve(12); edgesOf3DBox.push_back(std::make_pair(leftBottomFront, // x = left=xfront, y=bottom=yfront, z=front=zfront leftTopFront)); // left, top, front edgesOf3DBox.push_back(std::make_pair(leftBottomFront, // left, bottom, front leftBottomBack)); // left, bottom, back edgesOf3DBox.push_back(std::make_pair(leftBottomFront, // left, bottom, front rightBottomFront)); // right, bottom, front edgesOf3DBox.push_back(std::make_pair(leftTopFront, // left, top, front rightTopFront)); // right, top, front edgesOf3DBox.push_back(std::make_pair(leftTopFront, // left, top, front leftTopBack)); // left, top, back edgesOf3DBox.push_back(std::make_pair(rightTopFront, // right, top, front rightTopBack)); // right, top, back edgesOf3DBox.push_back(std::make_pair(rightTopFront, // right, top, front rightBottomFront)); // right, bottom, front edgesOf3DBox.push_back(std::make_pair(rightBottomFront, // right, bottom, front rightBottomBack)); // right, bottom, back edgesOf3DBox.push_back(std::make_pair(rightBottomBack, // right, bottom, back leftBottomBack)); // left, bottom, back edgesOf3DBox.push_back(std::make_pair(rightBottomBack, // right, bottom, back rightTopBack)); // right, top, back edgesOf3DBox.push_back(std::make_pair(rightTopBack, // right, top, back leftTopBack)); // left, top, back edgesOf3DBox.push_back(std::make_pair(leftTopBack, // left, top, back leftBottomBack)); // left, bottom, back for (auto iterator = edgesOf3DBox.cbegin(); iterator != edgesOf3DBox.cend(); ++iterator) { const Point3D startPoint = (*iterator).first; // start point of the line const Point3D endPoint = (*iterator).second; // end point of the line const Vector3D lineDirection = endPoint - startPoint; const mitk::Line3D line(startPoint, lineDirection); // Get intersection point of line and plane geometry Point3D intersectionWorldPoint(std::numeric_limits::min()); double t = -1.0; bool doesLineIntersectWithPlane(false); const double norm = line.GetDirection().GetNorm(); const double dist = geometry->Distance(line.GetPoint1()); if (norm < mitk::eps && dist < mitk::sqrteps) { t = 1.0; doesLineIntersectWithPlane = true; intersectionWorldPoint = line.GetPoint1(); } else { geometry->IntersectionPoint(line, intersectionWorldPoint); doesLineIntersectWithPlane = geometry->IntersectionPointParam(line, t); } // Get index point mitk::Point3D intersectionIndexPoint; imageGeometry->WorldToIndex(intersectionWorldPoint, intersectionIndexPoint); const bool lowerBoundGood = (0 - mitk::sqrteps) <= t; const bool upperBoundGood = t <= 1.0 + mitk::sqrteps; if (doesLineIntersectWithPlane && lowerBoundGood && upperBoundGood) { for (int dim = 0; dim < 3; ++dim) { m_MinMaxOutput[dim].first = std::min(m_MinMaxOutput[dim].first, ROUND_P(intersectionIndexPoint[dim])); m_MinMaxOutput[dim].second = std::max(m_MinMaxOutput[dim].second, ROUND_P(intersectionIndexPoint[dim])); } this->EnforceImageBounds(); } } } void mitk::ClippedSurfaceBoundsCalculator::CalculateIntersectionPoints(PointListType pointList) { PointListType::const_iterator pointIterator; const mitk::SlicedGeometry3D::Pointer imageGeometry = m_Image->GetSlicedGeometry(); for (pointIterator = pointList.cbegin(); pointIterator != pointList.cend(); ++pointIterator) { mitk::Point3D pntInIndexCoordinates; imageGeometry->WorldToIndex((*pointIterator), pntInIndexCoordinates); m_MinMaxOutput[0].first = pntInIndexCoordinates[0] < m_MinMaxOutput[0].first ? ROUND_P(pntInIndexCoordinates[0]) : m_MinMaxOutput[0].first; m_MinMaxOutput[0].second = pntInIndexCoordinates[0] > m_MinMaxOutput[0].second ? ROUND_P(pntInIndexCoordinates[0]) : m_MinMaxOutput[0].second; m_MinMaxOutput[1].first = pntInIndexCoordinates[1] < m_MinMaxOutput[1].first ? ROUND_P(pntInIndexCoordinates[1]) : m_MinMaxOutput[1].first; m_MinMaxOutput[1].second = pntInIndexCoordinates[1] > m_MinMaxOutput[1].second ? ROUND_P(pntInIndexCoordinates[1]) : m_MinMaxOutput[1].second; m_MinMaxOutput[2].first = pntInIndexCoordinates[2] < m_MinMaxOutput[2].first ? ROUND_P(pntInIndexCoordinates[2]) : m_MinMaxOutput[2].first; m_MinMaxOutput[2].second = pntInIndexCoordinates[2] > m_MinMaxOutput[2].second ? ROUND_P(pntInIndexCoordinates[2]) : m_MinMaxOutput[2].second; } // this->EnforceImageBounds(); } void mitk::ClippedSurfaceBoundsCalculator::EnforceImageBounds() { m_MinMaxOutput[0].first = std::max(m_MinMaxOutput[0].first, 0); m_MinMaxOutput[1].first = std::max(m_MinMaxOutput[1].first, 0); m_MinMaxOutput[2].first = std::max(m_MinMaxOutput[2].first, 0); m_MinMaxOutput[0].first = std::min(m_MinMaxOutput[0].first, (int)m_Image->GetDimension(0) - 1); m_MinMaxOutput[1].first = std::min(m_MinMaxOutput[1].first, (int)m_Image->GetDimension(1) - 1); m_MinMaxOutput[2].first = std::min(m_MinMaxOutput[2].first, (int)m_Image->GetDimension(2) - 1); m_MinMaxOutput[0].second = std::min(m_MinMaxOutput[0].second, (int)m_Image->GetDimension(0) - 1); m_MinMaxOutput[1].second = std::min(m_MinMaxOutput[1].second, (int)m_Image->GetDimension(1) - 1); m_MinMaxOutput[2].second = std::min(m_MinMaxOutput[2].second, (int)m_Image->GetDimension(2) - 1); m_MinMaxOutput[0].second = std::max(m_MinMaxOutput[0].second, 0); m_MinMaxOutput[1].second = std::max(m_MinMaxOutput[1].second, 0); m_MinMaxOutput[2].second = std::max(m_MinMaxOutput[2].second, 0); } diff --git a/Modules/Core/src/Algorithms/mitkDataNodeSource.cpp b/Modules/Core/src/Algorithms/mitkDataNodeSource.cpp index 1ef2241d03..3d881333fe 100644 --- a/Modules/Core/src/Algorithms/mitkDataNodeSource.cpp +++ b/Modules/Core/src/Algorithms/mitkDataNodeSource.cpp @@ -1,69 +1,69 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkDataNodeSource.h" mitk::DataNodeSource::DataNodeSource() { // Create the output. OutputType::Pointer output = static_cast(this->MakeOutput(0).GetPointer()); this->SetNthOutput(0, output.GetPointer()); } mitk::DataNodeSource::~DataNodeSource() { } itk::ProcessObject::DataObjectPointer mitk::DataNodeSource::MakeOutput(DataObjectPointerArraySizeType /*idx*/) { return OutputType::New().GetPointer(); } itk::ProcessObject::DataObjectPointer mitk::DataNodeSource::MakeOutput(const DataObjectIdentifierType &name) { itkDebugMacro("MakeOutput(" << name << ")"); if (this->IsIndexedOutputName(name)) { return this->MakeOutput(this->MakeIndexFromOutputName(name)); } return static_cast(OutputType::New().GetPointer()); } mitk::DataNodeSource::OutputType *mitk::DataNodeSource::GetOutput() { return itkDynamicCastInDebugMode(this->GetPrimaryOutput()); } const mitk::DataNodeSource::OutputType *mitk::DataNodeSource::GetOutput() const { return itkDynamicCastInDebugMode(this->GetPrimaryOutput()); } mitk::DataNodeSource::OutputType *mitk::DataNodeSource::GetOutput(DataObjectPointerArraySizeType idx) { return static_cast(Superclass::GetOutput(idx)); } const mitk::DataNodeSource::OutputType *mitk::DataNodeSource::GetOutput(DataObjectPointerArraySizeType idx) const { - const OutputType *out = dynamic_cast(this->ProcessObject::GetOutput(idx)); + const auto *out = dynamic_cast(this->ProcessObject::GetOutput(idx)); if (out == nullptr && this->ProcessObject::GetOutput(idx) != nullptr) { itkWarningMacro(<< "Unable to convert output number " << idx << " to type " << typeid(OutputType).name()); } return out; } diff --git a/Modules/Core/src/Algorithms/mitkExtractSliceFilter.cpp b/Modules/Core/src/Algorithms/mitkExtractSliceFilter.cpp index 369d002eff..6701203e5c 100644 --- a/Modules/Core/src/Algorithms/mitkExtractSliceFilter.cpp +++ b/Modules/Core/src/Algorithms/mitkExtractSliceFilter.cpp @@ -1,480 +1,480 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkExtractSliceFilter.h" #include #include #include #include #include #include #include mitk::ExtractSliceFilter::ExtractSliceFilter(vtkImageReslice *reslicer) { if (reslicer == nullptr) { m_Reslicer = vtkSmartPointer::New(); } else { m_Reslicer = reslicer; } m_TimeStep = 0; m_Reslicer->ReleaseDataFlagOn(); m_InterpolationMode = ExtractSliceFilter::RESLICE_NEAREST; m_ResliceTransform = nullptr; m_InPlaneResampleExtentByGeometry = false; m_OutPutSpacing = new mitk::ScalarType[2]; m_OutputDimension = 2; m_ZSpacing = 1.0; m_ZMin = 0; m_ZMax = 0; m_VtkOutputRequested = false; m_BackgroundLevel = -32768.0; m_Component = 0; } mitk::ExtractSliceFilter::~ExtractSliceFilter() { m_ResliceTransform = nullptr; m_WorldGeometry = nullptr; delete[] m_OutPutSpacing; } void mitk::ExtractSliceFilter::GenerateOutputInformation() { Superclass::GenerateOutputInformation(); // TODO try figure out how to set the specs of the slice before it is actually extracted /*Image::Pointer output = this->GetOutput(); Image::ConstPointer input = this->GetInput(); if (input.IsNull()) return; unsigned int dimensions[2]; dimensions[0] = m_WorldGeometry->GetExtent(0); dimensions[1] = m_WorldGeometry->GetExtent(1); output->Initialize(input->GetPixelType(), 2, dimensions, 1);*/ } void mitk::ExtractSliceFilter::GenerateInputRequestedRegion() { // As we want all pixel information fo the image in our plane, the requested region // is set to the largest possible region in the image. // This is needed because an oblique plane has a larger extent then the image // and the in pipeline it is checked via PropagateResquestedRegion(). But the // extent of the slice is actually fitting because it is oblique within the image. ImageToImageFilter::InputImagePointer input = const_cast(this->GetInput()); input->SetRequestedRegionToLargestPossibleRegion(); } mitk::ScalarType *mitk::ExtractSliceFilter::GetOutputSpacing() { return m_OutPutSpacing; } void mitk::ExtractSliceFilter::GenerateData() { - mitk::Image *input = const_cast(this->GetInput()); + auto *input = const_cast(this->GetInput()); if (!input) { MITK_ERROR << "mitk::ExtractSliceFilter: No input image available. Please set the input!" << std::endl; itkExceptionMacro("mitk::ExtractSliceFilter: No input image available. Please set the input!"); return; } if (!m_WorldGeometry) { MITK_ERROR << "mitk::ExtractSliceFilter: No Geometry for reslicing available." << std::endl; itkExceptionMacro("mitk::ExtractSliceFilter: No Geometry for reslicing available."); return; } const TimeGeometry *inputTimeGeometry = this->GetInput()->GetTimeGeometry(); if ((inputTimeGeometry == nullptr) || (inputTimeGeometry->CountTimeSteps() <= 0)) { itkWarningMacro(<< "Error reading input image TimeGeometry."); return; } // is it a valid timeStep? if (inputTimeGeometry->IsValidTimeStep(m_TimeStep) == false) { itkWarningMacro(<< "This is not a valid timestep: " << m_TimeStep); return; } // check if there is something to display. if (!input->IsVolumeSet(m_TimeStep)) { itkWarningMacro(<< "No volume data existent at given timestep " << m_TimeStep); return; } /*================#BEGIN setup vtkImageReslice properties================*/ Point3D origin; Vector3D right, bottom, normal; double widthInMM, heightInMM; Vector2D extent; - const PlaneGeometry *planeGeometry = dynamic_cast(m_WorldGeometry); + const auto *planeGeometry = dynamic_cast(m_WorldGeometry); // Code for curved planes, mostly taken 1:1 from imageVtkMapper2D and not tested yet. // Do we have an AbstractTransformGeometry? // This is the case for AbstractTransformGeometry's (e.g. a ThinPlateSplineCurvedGeometry ) - const mitk::AbstractTransformGeometry *abstractGeometry = + const auto *abstractGeometry = dynamic_cast(m_WorldGeometry); if (abstractGeometry != nullptr) { m_ResliceTransform = abstractGeometry; extent[0] = abstractGeometry->GetParametricExtent(0); extent[1] = abstractGeometry->GetParametricExtent(1); widthInMM = abstractGeometry->GetParametricExtentInMM(0); heightInMM = abstractGeometry->GetParametricExtentInMM(1); m_OutPutSpacing[0] = widthInMM / extent[0]; m_OutPutSpacing[1] = heightInMM / extent[1]; origin = abstractGeometry->GetPlane()->GetOrigin(); right = abstractGeometry->GetPlane()->GetAxisVector(0); right.Normalize(); bottom = abstractGeometry->GetPlane()->GetAxisVector(1); bottom.Normalize(); normal = abstractGeometry->GetPlane()->GetNormal(); normal.Normalize(); // Use a combination of the InputGeometry *and* the possible non-rigid // AbstractTransformGeometry for reslicing the 3D Image vtkSmartPointer composedResliceTransform = vtkSmartPointer::New(); composedResliceTransform->Identity(); composedResliceTransform->Concatenate( inputTimeGeometry->GetGeometryForTimeStep(m_TimeStep)->GetVtkTransform()->GetLinearInverse()); composedResliceTransform->Concatenate(abstractGeometry->GetVtkAbstractTransform()); m_Reslicer->SetResliceTransform(composedResliceTransform); // Set background level to BLACK instead of translucent, to avoid // boundary artifacts (see PlaneGeometryDataVtkMapper3D) // Note: Backgroundlevel was hardcoded before to -1023 m_Reslicer->SetBackgroundLevel(m_BackgroundLevel); } else { if (planeGeometry != nullptr) { // if the worldGeomatry is a PlaneGeometry everything is straight forward origin = planeGeometry->GetOrigin(); right = planeGeometry->GetAxisVector(0); bottom = planeGeometry->GetAxisVector(1); normal = planeGeometry->GetNormal(); if (m_InPlaneResampleExtentByGeometry) { // Resampling grid corresponds to the current world geometry. This // means that the spacing of the output 2D image depends on the // currently selected world geometry, and *not* on the image itself. extent[0] = m_WorldGeometry->GetExtent(0); extent[1] = m_WorldGeometry->GetExtent(1); } else { // Resampling grid corresponds to the input geometry. This means that // the spacing of the output 2D image is directly derived from the // associated input image, regardless of the currently selected world // geometry. Vector3D rightInIndex, bottomInIndex; inputTimeGeometry->GetGeometryForTimeStep(m_TimeStep)->WorldToIndex(right, rightInIndex); inputTimeGeometry->GetGeometryForTimeStep(m_TimeStep)->WorldToIndex(bottom, bottomInIndex); extent[0] = rightInIndex.GetNorm(); extent[1] = bottomInIndex.GetNorm(); } // Get the extent of the current world geometry and calculate resampling // spacing therefrom. widthInMM = m_WorldGeometry->GetExtentInMM(0); heightInMM = m_WorldGeometry->GetExtentInMM(1); m_OutPutSpacing[0] = widthInMM / extent[0]; m_OutPutSpacing[1] = heightInMM / extent[1]; right.Normalize(); bottom.Normalize(); normal.Normalize(); /* * Transform the origin to center based coordinates. * Note: * This is needed besause vtk's origin is center based too (!!!) ( see 'The VTK book' page 88 ) * and the worldGeometry surrouding the image is no imageGeometry. So the worldGeometry * has its origin at the corner of the voxel and needs to be transformed. */ origin += right * (m_OutPutSpacing[0] * 0.5); origin += bottom * (m_OutPutSpacing[1] * 0.5); // set the tranform for reslicing. // Use inverse transform of the input geometry for reslicing the 3D image. // This is needed if the image volume already transformed if (m_ResliceTransform.IsNotNull()) m_Reslicer->SetResliceTransform(m_ResliceTransform->GetVtkTransform()->GetLinearInverse()); // Set background level to TRANSLUCENT (see PlaneGeometryDataVtkMapper3D), // else the background of the image turns out gray // Note: Backgroundlevel was hardcoded to -32768 m_Reslicer->SetBackgroundLevel(m_BackgroundLevel); } else { itkExceptionMacro("mitk::ExtractSliceFilter: No fitting geometry for reslice axis!"); return; } } if (m_ResliceTransform.IsNotNull()) { // if the resliceTransform is set the reslice axis are recalculated. // Thus the geometry information is not fitting. Therefor a unitSpacingFilter // is used to set up a global spacing of 1 and compensate the transform. vtkSmartPointer unitSpacingImageFilter = vtkSmartPointer::New(); unitSpacingImageFilter->ReleaseDataFlagOn(); unitSpacingImageFilter->SetOutputSpacing(1.0, 1.0, 1.0); unitSpacingImageFilter->SetInputData(input->GetVtkImageData(m_TimeStep)); m_Reslicer->SetInputConnection(unitSpacingImageFilter->GetOutputPort()); } else { // if no transform is set the image can be used directly m_Reslicer->SetInputData(input->GetVtkImageData(m_TimeStep)); } /*setup the plane where vktImageReslice extracts the slice*/ // ResliceAxesOrigin is the anchor point of the plane double originInVtk[3]; itk2vtk(origin, originInVtk); m_Reslicer->SetResliceAxesOrigin(originInVtk); // the cosines define the plane: x and y are the direction vectors, n is the planes normal // this specifies a matrix 3x3 // x1 y1 n1 // x2 y2 n2 // x3 y3 n3 double cosines[9]; vnl2vtk(right.GetVnlVector(), cosines); // x vnl2vtk(bottom.GetVnlVector(), cosines + 3); // y vnl2vtk(normal.GetVnlVector(), cosines + 6); // n m_Reslicer->SetResliceAxesDirectionCosines(cosines); // we only have one slice, not a volume m_Reslicer->SetOutputDimensionality(m_OutputDimension); // set the interpolation mode for slicing switch (this->m_InterpolationMode) { case RESLICE_NEAREST: m_Reslicer->SetInterpolationModeToNearestNeighbor(); break; case RESLICE_LINEAR: m_Reslicer->SetInterpolationModeToLinear(); break; case RESLICE_CUBIC: m_Reslicer->SetInterpolationModeToCubic(); break; default: // the default interpolation used by mitk m_Reslicer->SetInterpolationModeToNearestNeighbor(); } /*========== BEGIN setup extent of the slice ==========*/ int xMin, xMax, yMin, yMax; xMin = yMin = 0; xMax = static_cast(extent[0]); yMax = static_cast(extent[1]); if (m_WorldGeometry->GetReferenceGeometry()) { double sliceBounds[6]; for (auto &sliceBound : sliceBounds) { sliceBound = 0.0; } if (this->GetClippedPlaneBounds(m_WorldGeometry->GetReferenceGeometry(), planeGeometry, sliceBounds)) { // Calculate output extent (integer values) xMin = static_cast(sliceBounds[0] / m_OutPutSpacing[0] + 0.5); xMax = static_cast(sliceBounds[1] / m_OutPutSpacing[0] + 0.5); yMin = static_cast(sliceBounds[2] / m_OutPutSpacing[1] + 0.5); yMax = static_cast(sliceBounds[3] / m_OutPutSpacing[1] + 0.5); } // ELSE we use the default values } // Set the output extents! First included pixel index and last included pixel index // xMax and yMax are one after the last pixel. so they have to be decremented by 1. // In case we have a 2D image, xMax or yMax might be 0. in this case, do not decrement, but take 0. m_Reslicer->SetOutputExtent(xMin, std::max(0, xMax - 1), yMin, std::max(0, yMax - 1), m_ZMin, m_ZMax); /*========== END setup extent of the slice ==========*/ m_Reslicer->SetOutputOrigin(0.0, 0.0, 0.0); m_Reslicer->SetOutputSpacing(m_OutPutSpacing[0], m_OutPutSpacing[1], m_ZSpacing); // TODO check the following lines, they are responsible whether vtk error outputs appear or not m_Reslicer->UpdateWholeExtent(); // this produces a bad allocation error for 2D images // m_Reslicer->GetOutput()->UpdateInformation(); // m_Reslicer->GetOutput()->SetUpdateExtentToWholeExtent(); // start the pipeline m_Reslicer->Update(); /*================ #END setup vtkImageReslice properties================*/ if (m_VtkOutputRequested) { // no conversion to mitk // no mitk geometry will be set, as the output is vtkImageData only!!! // no image component will be extracted, as the caller might need the whole multi-component image as vtk output return; } else { auto reslicedImage = vtkSmartPointer::New(); reslicedImage = m_Reslicer->GetOutput(); if (nullptr == reslicedImage) { itkWarningMacro(<< "Reslicer returned empty image"); return; } /*================ #BEGIN Extract component from image slice ================*/ int numberOfScalarComponent = reslicedImage->GetNumberOfScalarComponents(); if (numberOfScalarComponent > 1 && static_cast(numberOfScalarComponent) >= m_Component) { // image has more than one component, extract the correct component information with the given 'component' parameter auto vectorComponentExtractor = vtkSmartPointer::New(); vectorComponentExtractor->SetInputData(reslicedImage); vectorComponentExtractor->SetComponents(m_Component); vectorComponentExtractor->Update(); reslicedImage = vectorComponentExtractor->GetOutput(); } /*================ #END Extract component from image slice ================*/ /*================ #BEGIN Convert the slice to an mitk::Image ================*/ mitk::Image::Pointer resultImage = GetOutput(); // initialize resultimage with the specs of the vtkImageData object returned from vtkImageReslice if (reslicedImage->GetDataDimension() == 1) { // If original image was 2D, the slice might have an y extent of 0. // Still i want to ensure here that Image is 2D resultImage->Initialize(reslicedImage, 1, -1, -1, 1); } else { resultImage->Initialize(reslicedImage); } // transfer the voxel data resultImage->SetVolume(reslicedImage->GetScalarPointer()); // set the geometry from current worldgeometry for the reusultimage // this is needed that the image has the correct mitk geometry // the originalGeometry is the Geometry of the result slice // mitk::AffineGeometryFrame3D::Pointer originalGeometryAGF = m_WorldGeometry->Clone(); // PlaneGeometry::Pointer originalGeometry = dynamic_cast( originalGeometryAGF.GetPointer() ); PlaneGeometry::Pointer originalGeometry = m_WorldGeometry->Clone(); originalGeometry->GetIndexToWorldTransform()->SetMatrix(m_WorldGeometry->GetIndexToWorldTransform()->GetMatrix()); // the origin of the worldGeometry is transformed to center based coordinates to be an imageGeometry Point3D sliceOrigin = originalGeometry->GetOrigin(); sliceOrigin += right * (m_OutPutSpacing[0] * 0.5); sliceOrigin += bottom * (m_OutPutSpacing[1] * 0.5); // a worldGeometry is no imageGeometry, thus it is manually set to true originalGeometry->ImageGeometryOn(); /*At this point we have to adjust the geometry because the origin isn't correct. The wrong origin is related to the rotation of the current world geometry plane. This causes errors on transferring world to index coordinates. We just shift the origin in each direction about the amount of the expanding (needed while rotating the plane). */ Vector3D axis0 = originalGeometry->GetAxisVector(0); Vector3D axis1 = originalGeometry->GetAxisVector(1); axis0.Normalize(); axis1.Normalize(); // adapt the origin. Note that for orthogonal planes the minima are '0' and thus the origin stays the same. sliceOrigin += (axis0 * (xMin * m_OutPutSpacing[0])) + (axis1 * (yMin * m_OutPutSpacing[1])); originalGeometry->SetOrigin(sliceOrigin); originalGeometry->Modified(); resultImage->SetGeometry(originalGeometry); /*the bounds as well as the extent of the worldGeometry are not adapted correctly during crosshair rotation. This is only a quick fix and has to be evaluated. The new bounds are set via the max values of the calculated slice extent. It will look like [ 0, x, 0, y, 0, 1]. */ mitk::BoundingBox::BoundsArrayType boundsCopy; boundsCopy[0] = boundsCopy[2] = boundsCopy[4] = 0; boundsCopy[5] = 1; boundsCopy[1] = xMax - xMin; boundsCopy[3] = yMax - yMin; resultImage->GetGeometry()->SetBounds(boundsCopy); /*================ #END Convert the slice to an mitk::Image ================*/ } } bool mitk::ExtractSliceFilter::GetClippedPlaneBounds(double bounds[6]) { if (!m_WorldGeometry || !this->GetInput()) return false; return this->GetClippedPlaneBounds( m_WorldGeometry->GetReferenceGeometry(), dynamic_cast(m_WorldGeometry), bounds); } bool mitk::ExtractSliceFilter::GetClippedPlaneBounds(const BaseGeometry *boundingGeometry, const PlaneGeometry *planeGeometry, double *bounds) { bool b = mitk::PlaneClipping::CalculateClippedPlaneBounds(boundingGeometry, planeGeometry, bounds); return b; } diff --git a/Modules/Core/src/Algorithms/mitkImageSource.cpp b/Modules/Core/src/Algorithms/mitkImageSource.cpp index a82c19c58e..e0e939df23 100644 --- a/Modules/Core/src/Algorithms/mitkImageSource.cpp +++ b/Modules/Core/src/Algorithms/mitkImageSource.cpp @@ -1,203 +1,203 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkImageSource.h" #include "mitkImageVtkReadAccessor.h" #include "mitkImageVtkWriteAccessor.h" mitk::ImageSource::ImageSource() { // Create the output. We use static_cast<> here because we know the default // output must be of type TOutputImage OutputImageType::Pointer output = static_cast(this->MakeOutput(0).GetPointer()); Superclass::SetNumberOfRequiredOutputs(1); Superclass::SetNthOutput(0, output.GetPointer()); } itk::DataObject::Pointer mitk::ImageSource::MakeOutput(DataObjectPointerArraySizeType /*idx*/) { return static_cast(mitk::Image::New().GetPointer()); } itk::DataObject::Pointer mitk::ImageSource::MakeOutput(const DataObjectIdentifierType &name) { itkDebugMacro("MakeOutput(" << name << ")"); if (this->IsIndexedOutputName(name)) { return this->MakeOutput(this->MakeIndexFromOutputName(name)); } return static_cast(mitk::Image::New().GetPointer()); } //---------------------------------------------------------------------------- unsigned int mitk::ImageSource::SplitRequestedRegion(unsigned int i, unsigned int num, OutputImageRegionType &splitRegion) { // Get the output pointer OutputImageType *outputPtr = this->GetOutput(); const SlicedData::SizeType &requestedRegionSize = outputPtr->GetRequestedRegion().GetSize(); int splitAxis; SlicedData::IndexType splitIndex; SlicedData::SizeType splitSize; // Initialize the splitRegion to the output requested region splitRegion = outputPtr->GetRequestedRegion(); splitIndex = splitRegion.GetIndex(); splitSize = splitRegion.GetSize(); // split on the outermost dimension available splitAxis = outputPtr->GetDimension() - 1; while (requestedRegionSize[splitAxis] == 1) { --splitAxis; if (splitAxis < 0) { // cannot split itkDebugMacro(" Cannot Split"); return 1; } } // determine the actual number of pieces that will be generated SlicedData::SizeType::SizeValueType range = requestedRegionSize[splitAxis]; - unsigned int valuesPerThread = itk::Math::Ceil(range / (double)num); + auto valuesPerThread = itk::Math::Ceil(range / (double)num); unsigned int maxThreadIdUsed = itk::Math::Ceil(range / (double)valuesPerThread) - 1; // Split the region if (i < maxThreadIdUsed) { splitIndex[splitAxis] += i * valuesPerThread; splitSize[splitAxis] = valuesPerThread; } if (i == maxThreadIdUsed) { splitIndex[splitAxis] += i * valuesPerThread; // last thread needs to process the "rest" dimension being split splitSize[splitAxis] = splitSize[splitAxis] - i * valuesPerThread; } // set the split region ivars splitRegion.SetIndex(splitIndex); splitRegion.SetSize(splitSize); itkDebugMacro(" Split Piece: " << splitRegion); return maxThreadIdUsed + 1; } //---------------------------------------------------------------------------- void mitk::ImageSource::AllocateOutputs() { OutputImagePointer outputPtr; // Allocate the output memory for (unsigned int i = 0; i < this->GetNumberOfOutputs(); i++) { outputPtr = this->GetOutput(i); // outputPtr->SetBufferedRegion(outputPtr->GetRequestedRegion()); @FIXME??? // outputPtr->Allocate(); @FIXME??? } } //---------------------------------------------------------------------------- void mitk::ImageSource::GenerateData() { // Call a method that can be overriden by a subclass to allocate // memory for the filter's outputs this->AllocateOutputs(); // Call a method that can be overridden by a subclass to perform // some calculations prior to splitting the main computations into // separate threads this->BeforeThreadedGenerateData(); // Set up the multithreaded processing ThreadStruct str; str.Filter = this; this->GetMultiThreader()->SetNumberOfThreads(this->GetNumberOfThreads()); this->GetMultiThreader()->SetSingleMethod(this->ThreaderCallback, &str); // multithread the execution this->GetMultiThreader()->SingleMethodExecute(); // Call a method that can be overridden by a subclass to perform // some calculations after all the threads have completed this->AfterThreadedGenerateData(); } //---------------------------------------------------------------------------- // The execute method created by the subclass. void mitk::ImageSource::ThreadedGenerateData(const OutputImageRegionType &, itk::ThreadIdType) { itkExceptionMacro("subclass should override this method!!!"); } // Callback routine used by the threading library. This routine just calls // the ThreadedGenerateData method after setting the correct region for this // thread. ITK_THREAD_RETURN_TYPE mitk::ImageSource::ThreaderCallback(void *arg) { ThreadStruct *str; itk::ThreadIdType total, threadId, threadCount; threadId = ((itk::MultiThreader::ThreadInfoStruct *)(arg))->ThreadID; threadCount = ((itk::MultiThreader::ThreadInfoStruct *)(arg))->NumberOfThreads; str = (ThreadStruct *)(((itk::MultiThreader::ThreadInfoStruct *)(arg))->UserData); // execute the actual method with appropriate output region // first find out how many pieces extent can be split into. SlicedData::RegionType splitRegion; total = str->Filter->SplitRequestedRegion(threadId, threadCount, splitRegion); if (threadId < total) { str->Filter->ThreadedGenerateData(splitRegion, threadId); } // else // { // otherwise don't use this thread. Sometimes the threads dont // break up very well and it is just as efficient to leave a // few threads idle. // } return ITK_THREAD_RETURN_VALUE; } void mitk::ImageSource::PrepareOutputs() { Superclass::PrepareOutputs(); } vtkImageData *mitk::ImageSource::GetVtkImageData() { Update(); return GetOutput()->GetVtkImageData(); } const vtkImageData *mitk::ImageSource::GetVtkImageData() const { return GetOutput()->GetVtkImageData(); } mitkBaseDataSourceGetOutputDefinitions(mitk::ImageSource) diff --git a/Modules/Core/src/Algorithms/mitkImageToSurfaceFilter.cpp b/Modules/Core/src/Algorithms/mitkImageToSurfaceFilter.cpp index 22b38c9731..27fccd5fd5 100644 --- a/Modules/Core/src/Algorithms/mitkImageToSurfaceFilter.cpp +++ b/Modules/Core/src/Algorithms/mitkImageToSurfaceFilter.cpp @@ -1,234 +1,234 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkException.h" #include #include #include #include #include #include #include #include #include #include #include #include "mitkProgressBar.h" mitk::ImageToSurfaceFilter::ImageToSurfaceFilter() : m_Smooth(false), m_Decimate(NoDecimation), m_Threshold(1.0), m_TargetReduction(0.95f), m_SmoothIteration(50), m_SmoothRelaxation(0.1) { } mitk::ImageToSurfaceFilter::~ImageToSurfaceFilter() { } void mitk::ImageToSurfaceFilter::CreateSurface(int time, vtkImageData *vtkimage, mitk::Surface *surface, const ScalarType threshold) { vtkImageChangeInformation *indexCoordinatesImageFilter = vtkImageChangeInformation::New(); indexCoordinatesImageFilter->SetInputData(vtkimage); indexCoordinatesImageFilter->SetOutputOrigin(0.0, 0.0, 0.0); // MarchingCube -->create Surface vtkSmartPointer skinExtractor = vtkSmartPointer::New(); skinExtractor->ComputeScalarsOff(); skinExtractor->SetInputConnection(indexCoordinatesImageFilter->GetOutputPort()); // RC++ indexCoordinatesImageFilter->Delete(); skinExtractor->SetValue(0, threshold); vtkPolyData *polydata; skinExtractor->Update(); polydata = skinExtractor->GetOutput(); polydata->Register(nullptr); // RC++ if (m_Smooth) { vtkSmoothPolyDataFilter *smoother = vtkSmoothPolyDataFilter::New(); // read poly1 (poly1 can be the original polygon, or the decimated polygon) smoother->SetInputConnection(skinExtractor->GetOutputPort()); // RC++ smoother->SetNumberOfIterations(m_SmoothIteration); smoother->SetRelaxationFactor(m_SmoothRelaxation); smoother->SetFeatureAngle(60); smoother->FeatureEdgeSmoothingOff(); smoother->BoundarySmoothingOff(); smoother->SetConvergence(0); smoother->Update(); polydata->Delete(); // RC-- polydata = smoother->GetOutput(); polydata->Register(nullptr); // RC++ smoother->Delete(); } ProgressBar::GetInstance()->Progress(); // decimate = to reduce number of polygons if (m_Decimate == DecimatePro) { vtkDecimatePro *decimate = vtkDecimatePro::New(); decimate->SplittingOff(); decimate->SetErrorIsAbsolute(5); decimate->SetFeatureAngle(30); decimate->PreserveTopologyOn(); decimate->BoundaryVertexDeletionOff(); decimate->SetDegree(10); // std-value is 25! decimate->SetInputData(polydata); // RC++ decimate->SetTargetReduction(m_TargetReduction); decimate->SetMaximumError(0.002); decimate->Update(); polydata->Delete(); // RC-- polydata = decimate->GetOutput(); polydata->Register(nullptr); // RC++ decimate->Delete(); } else if (m_Decimate == QuadricDecimation) { vtkQuadricDecimation *decimate = vtkQuadricDecimation::New(); decimate->SetTargetReduction(m_TargetReduction); decimate->SetInputData(polydata); decimate->Update(); polydata->Delete(); polydata = decimate->GetOutput(); polydata->Register(nullptr); decimate->Delete(); } ProgressBar::GetInstance()->Progress(); if (polydata->GetNumberOfPoints() > 0) { mitk::Vector3D spacing = GetInput()->GetGeometry(time)->GetSpacing(); vtkPoints *points = polydata->GetPoints(); vtkMatrix4x4 *vtkmatrix = vtkMatrix4x4::New(); GetInput()->GetGeometry(time)->GetVtkTransform()->GetMatrix(vtkmatrix); double(*matrix)[4] = vtkmatrix->Element; unsigned int i, j; for (i = 0; i < 3; ++i) for (j = 0; j < 3; ++j) matrix[i][j] /= spacing[j]; unsigned int n = points->GetNumberOfPoints(); double point[3]; for (i = 0; i < n; i++) { points->GetPoint(i, point); mitkVtkLinearTransformPoint(matrix, point, point); points->SetPoint(i, point); } vtkmatrix->Delete(); } ProgressBar::GetInstance()->Progress(); // determine point_data normals for the poly data points. vtkSmartPointer normalsGenerator = vtkSmartPointer::New(); normalsGenerator->SetInputData(polydata); normalsGenerator->FlipNormalsOn(); vtkSmartPointer cleanPolyDataFilter = vtkSmartPointer::New(); cleanPolyDataFilter->SetInputConnection(normalsGenerator->GetOutputPort()); cleanPolyDataFilter->PieceInvariantOff(); cleanPolyDataFilter->ConvertLinesToPointsOff(); cleanPolyDataFilter->ConvertPolysToLinesOff(); cleanPolyDataFilter->ConvertStripsToPolysOff(); cleanPolyDataFilter->PointMergingOn(); cleanPolyDataFilter->Update(); surface->SetVtkPolyData(cleanPolyDataFilter->GetOutput(), time); polydata->UnRegister(nullptr); } void mitk::ImageToSurfaceFilter::GenerateData() { mitk::Surface *surface = this->GetOutput(); - mitk::Image *image = (mitk::Image *)GetInput(); + auto *image = (mitk::Image *)GetInput(); if (image == nullptr || !image->IsInitialized()) mitkThrow() << "No input image set, please set an valid input image!"; mitk::Image::RegionType outputRegion = image->GetRequestedRegion(); int tstart = outputRegion.GetIndex(3); int tmax = tstart + outputRegion.GetSize(3); // GetSize()==1 - will aber 0 haben, wenn nicht zeitaufgeloest if ((tmax - tstart) > 0) { ProgressBar::GetInstance()->AddStepsToDo(4 * (tmax - tstart)); } int t; for (t = tstart; t < tmax; ++t) { vtkImageData *vtkimagedata = image->GetVtkImageData(t); CreateSurface(t, vtkimagedata, surface, m_Threshold); ProgressBar::GetInstance()->Progress(); } } void mitk::ImageToSurfaceFilter::SetSmoothIteration(int smoothIteration) { m_SmoothIteration = smoothIteration; } void mitk::ImageToSurfaceFilter::SetSmoothRelaxation(float smoothRelaxation) { m_SmoothRelaxation = smoothRelaxation; } void mitk::ImageToSurfaceFilter::SetInput(const mitk::Image *image) { // Process object is not const-correct so the const_cast is required here this->ProcessObject::SetNthInput(0, const_cast(image)); } const mitk::Image *mitk::ImageToSurfaceFilter::GetInput(void) { if (this->GetNumberOfInputs() < 1) { return nullptr; } return static_cast(this->ProcessObject::GetInput(0)); } void mitk::ImageToSurfaceFilter::GenerateOutputInformation() { mitk::Image::ConstPointer inputImage = (mitk::Image *)this->GetInput(); // mitk::Image *inputImage = (mitk::Image*)this->GetImage(); mitk::Surface::Pointer output = this->GetOutput(); itkDebugMacro(<< "GenerateOutputInformation()"); if (inputImage.IsNull()) return; // Set Data } diff --git a/Modules/Core/src/Algorithms/mitkPlaneGeometryDataToSurfaceFilter.cpp b/Modules/Core/src/Algorithms/mitkPlaneGeometryDataToSurfaceFilter.cpp index facbca731f..45475a3545 100644 --- a/Modules/Core/src/Algorithms/mitkPlaneGeometryDataToSurfaceFilter.cpp +++ b/Modules/Core/src/Algorithms/mitkPlaneGeometryDataToSurfaceFilter.cpp @@ -1,395 +1,395 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkPlaneGeometryDataToSurfaceFilter.h" #include "mitkAbstractTransformGeometry.h" #include "mitkGeometry3D.h" #include "mitkPlaneGeometry.h" #include "mitkPlaneGeometryData.h" #include "mitkSurface.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include mitk::PlaneGeometryDataToSurfaceFilter::PlaneGeometryDataToSurfaceFilter() : m_UseGeometryParametricBounds(true), m_XResolution(10), m_YResolution(10), m_PlaceByGeometry(false), m_UseBoundingBox(false) { m_PlaneSource = vtkPlaneSource::New(); m_Transform = vtkTransform::New(); m_CubeSource = vtkCubeSource::New(); m_PolyDataTransformer = vtkTransformPolyDataFilter::New(); m_Plane = vtkPlane::New(); m_PlaneCutter = vtkCutter::New(); m_PlaneStripper = vtkStripper::New(); m_PlanePolyData = vtkPolyData::New(); m_NormalsUpdater = vtkPPolyDataNormals::New(); m_PlaneTriangler = vtkTriangleFilter::New(); m_TextureMapToPlane = vtkTextureMapToPlane::New(); m_Box = vtkBox::New(); m_PlaneClipper = vtkClipPolyData::New(); m_VtkTransformPlaneFilter = vtkTransformPolyDataFilter::New(); m_VtkTransformPlaneFilter->SetInputConnection(m_PlaneSource->GetOutputPort()); } mitk::PlaneGeometryDataToSurfaceFilter::~PlaneGeometryDataToSurfaceFilter() { m_PlaneSource->Delete(); m_Transform->Delete(); m_CubeSource->Delete(); m_PolyDataTransformer->Delete(); m_Plane->Delete(); m_PlaneCutter->Delete(); m_PlaneStripper->Delete(); m_PlanePolyData->Delete(); m_NormalsUpdater->Delete(); m_PlaneTriangler->Delete(); m_TextureMapToPlane->Delete(); m_Box->Delete(); m_PlaneClipper->Delete(); m_VtkTransformPlaneFilter->Delete(); } void mitk::PlaneGeometryDataToSurfaceFilter::GenerateOutputInformation() { mitk::PlaneGeometryData::ConstPointer input = this->GetInput(); mitk::Surface::Pointer output = this->GetOutput(); if (input.IsNull() || (input->GetPlaneGeometry() == nullptr) || (input->GetPlaneGeometry()->IsValid() == false) || (m_UseBoundingBox && (m_BoundingBox.IsNull() || (m_BoundingBox->GetDiagonalLength2() < mitk::eps)))) { return; } Point3D origin; Point3D right, bottom; vtkPolyData *planeSurface = nullptr; // Does the PlaneGeometryData contain an AbstractTransformGeometry? - if (mitk::AbstractTransformGeometry *abstractGeometry = + if (auto *abstractGeometry = dynamic_cast(input->GetPlaneGeometry())) { // In the case of an AbstractTransformGeometry (which holds a possibly // non-rigid transform), we proceed slightly differently: since the // plane can be arbitrarily deformed, we need to transform it by the // abstract transform before clipping it. The setup for this is partially // done in the constructor. origin = abstractGeometry->GetPlane()->GetOrigin(); right = origin + abstractGeometry->GetPlane()->GetAxisVector(0); bottom = origin + abstractGeometry->GetPlane()->GetAxisVector(1); // Define the plane m_PlaneSource->SetOrigin(origin[0], origin[1], origin[2]); m_PlaneSource->SetPoint1(right[0], right[1], right[2]); m_PlaneSource->SetPoint2(bottom[0], bottom[1], bottom[2]); // Set the plane's resolution (unlike for non-deformable planes, the plane // grid needs to have a certain resolution so that the deformation has the // desired effect). if (m_UseGeometryParametricBounds) { m_PlaneSource->SetXResolution((int)abstractGeometry->GetParametricExtent(0)); m_PlaneSource->SetYResolution((int)abstractGeometry->GetParametricExtent(1)); } else { m_PlaneSource->SetXResolution(m_XResolution); m_PlaneSource->SetYResolution(m_YResolution); } if (m_PlaceByGeometry) { // Let the output use the input geometry to appropriately transform the // coordinate system. mitk::Geometry3D::TransformType *affineTransform = abstractGeometry->GetIndexToWorldTransform(); TimeGeometry *timeGeometry = output->GetTimeGeometry(); BaseGeometry *g3d = timeGeometry->GetGeometryForTimeStep(0); g3d->SetIndexToWorldTransform(affineTransform); vtkGeneralTransform *composedResliceTransform = vtkGeneralTransform::New(); composedResliceTransform->Identity(); composedResliceTransform->Concatenate(abstractGeometry->GetVtkTransform()->GetLinearInverse()); composedResliceTransform->Concatenate(abstractGeometry->GetVtkAbstractTransform()); // Use the non-rigid transform for transforming the plane. m_VtkTransformPlaneFilter->SetTransform(composedResliceTransform); } else { // Use the non-rigid transform for transforming the plane. m_VtkTransformPlaneFilter->SetTransform(abstractGeometry->GetVtkAbstractTransform()); } if (m_UseBoundingBox) { mitk::BoundingBox::PointType boundingBoxMin = m_BoundingBox->GetMinimum(); mitk::BoundingBox::PointType boundingBoxMax = m_BoundingBox->GetMaximum(); // mitk::BoundingBox::PointType boundingBoxCenter = m_BoundingBox->GetCenter(); m_Box->SetXMin(boundingBoxMin[0], boundingBoxMin[1], boundingBoxMin[2]); m_Box->SetXMax(boundingBoxMax[0], boundingBoxMax[1], boundingBoxMax[2]); } else { // Plane will not be clipped m_Box->SetXMin(-10000.0, -10000.0, -10000.0); m_Box->SetXMax(10000.0, 10000.0, 10000.0); } m_Transform->Identity(); m_Transform->Concatenate(input->GetPlaneGeometry()->GetVtkTransform()); m_Transform->PreMultiply(); m_Box->SetTransform(m_Transform); m_PlaneClipper->SetInputConnection(m_VtkTransformPlaneFilter->GetOutputPort()); m_PlaneClipper->SetClipFunction(m_Box); m_PlaneClipper->GenerateClippedOutputOff(); // important to NOT generate normals data for clipped part m_PlaneClipper->InsideOutOn(); m_PlaneClipper->SetValue(0.0); m_PlaneClipper->Update(); planeSurface = m_PlaneClipper->GetOutput(); } // Does the PlaneGeometryData contain a PlaneGeometry? else if (dynamic_cast(input->GetPlaneGeometry()) != nullptr) { - mitk::PlaneGeometry *planeGeometry = dynamic_cast(input->GetPlaneGeometry()); + auto *planeGeometry = dynamic_cast(input->GetPlaneGeometry()); if (m_PlaceByGeometry) { // Let the output use the input geometry to appropriately transform the // coordinate system. mitk::Geometry3D::TransformType *affineTransform = planeGeometry->GetIndexToWorldTransform(); TimeGeometry *timeGeometry = output->GetTimeGeometry(); BaseGeometry *geometrie3d = timeGeometry->GetGeometryForTimeStep(0); geometrie3d->SetIndexToWorldTransform(affineTransform); } if (!m_UseBoundingBox) { // We do not have a bounding box, so no clipping is required. if (m_PlaceByGeometry) { // Derive coordinate axes and origin from input geometry extent origin.Fill(0.0); FillVector3D(right, planeGeometry->GetExtent(0), 0.0, 0.0); FillVector3D(bottom, 0.0, planeGeometry->GetExtent(1), 0.0); } else { // Take the coordinate axes and origin directly from the input geometry. origin = planeGeometry->GetOrigin(); right = planeGeometry->GetCornerPoint(false, true); bottom = planeGeometry->GetCornerPoint(true, false); } // Since the plane is planar, there is no need to subdivide the grid // (cf. AbstractTransformGeometry case) m_PlaneSource->SetXResolution(1); m_PlaneSource->SetYResolution(1); m_PlaneSource->SetOrigin(origin[0], origin[1], origin[2]); m_PlaneSource->SetPoint1(right[0], right[1], right[2]); m_PlaneSource->SetPoint2(bottom[0], bottom[1], bottom[2]); m_PlaneSource->Update(); planeSurface = m_PlaneSource->GetOutput(); } else { // Set up a cube with the extent and origin of the bounding box. This // cube will be clipped by a plane later on. The intersection of the // cube and the plane will be the surface we are interested in. Note // that the bounding box needs to be explicitly specified by the user // of this class, since it is not necessarily clear from the data // available herein which bounding box to use. In most cases, this // would be the bounding box of the input geometry's reference // geometry, but this is not an inevitable requirement. mitk::BoundingBox::PointType boundingBoxMin = m_BoundingBox->GetMinimum(); mitk::BoundingBox::PointType boundingBoxMax = m_BoundingBox->GetMaximum(); mitk::BoundingBox::PointType boundingBoxCenter = m_BoundingBox->GetCenter(); m_CubeSource->SetXLength(boundingBoxMax[0] - boundingBoxMin[0]); m_CubeSource->SetYLength(boundingBoxMax[1] - boundingBoxMin[1]); m_CubeSource->SetZLength(boundingBoxMax[2] - boundingBoxMin[2]); m_CubeSource->SetCenter(boundingBoxCenter[0], boundingBoxCenter[1], boundingBoxCenter[2]); // Now we have to transform the cube, so that it will cut our plane // appropriately. (As can be seen below, the plane corresponds to the // z-plane in the coordinate system and is *not* transformed.) Therefore, // we get the inverse of the plane geometry's transform and concatenate // it with the transform of the reference geometry, if available. m_Transform->Identity(); m_Transform->Concatenate(planeGeometry->GetVtkTransform()->GetLinearInverse()); const BaseGeometry *referenceGeometry = planeGeometry->GetReferenceGeometry(); if (referenceGeometry) { m_Transform->Concatenate(referenceGeometry->GetVtkTransform()); } // Transform the cube accordingly (s.a.) m_PolyDataTransformer->SetInputConnection(m_CubeSource->GetOutputPort()); m_PolyDataTransformer->SetTransform(m_Transform); // Initialize the plane to clip the cube with, as lying on the z-plane m_Plane->SetOrigin(0.0, 0.0, 0.0); m_Plane->SetNormal(0.0, 0.0, 1.0); // Cut the plane with the cube. m_PlaneCutter->SetInputConnection(m_PolyDataTransformer->GetOutputPort()); m_PlaneCutter->SetCutFunction(m_Plane); // The output of the cutter must be converted into appropriate poly data. m_PlaneStripper->SetInputConnection(m_PlaneCutter->GetOutputPort()); m_PlaneStripper->Update(); if (m_PlaneStripper->GetOutput()->GetNumberOfPoints() < 3) { return; } m_PlanePolyData->SetPoints(m_PlaneStripper->GetOutput()->GetPoints()); m_PlanePolyData->SetPolys(m_PlaneStripper->GetOutput()->GetLines()); m_PlaneTriangler->SetInputData(m_PlanePolyData); // Get bounds of the resulting surface and use it to generate the texture // mapping information m_PlaneTriangler->Update(); m_PlaneTriangler->GetOutput()->ComputeBounds(); double *surfaceBounds = m_PlaneTriangler->GetOutput()->GetBounds(); origin[0] = surfaceBounds[0]; origin[1] = surfaceBounds[2]; origin[2] = surfaceBounds[4]; right[0] = surfaceBounds[1]; right[1] = surfaceBounds[2]; right[2] = surfaceBounds[4]; bottom[0] = surfaceBounds[0]; bottom[1] = surfaceBounds[3]; bottom[2] = surfaceBounds[4]; // Now we tell the data how it shall be textured afterwards; // description see above. m_TextureMapToPlane->SetInputConnection(m_PlaneTriangler->GetOutputPort()); m_TextureMapToPlane->AutomaticPlaneGenerationOn(); m_TextureMapToPlane->SetOrigin(origin[0], origin[1], origin[2]); m_TextureMapToPlane->SetPoint1(right[0], right[1], right[2]); m_TextureMapToPlane->SetPoint2(bottom[0], bottom[1], bottom[2]); // Need to call update so that output data and bounds are immediately // available m_TextureMapToPlane->Update(); // Return the output of this generation process planeSurface = dynamic_cast(m_TextureMapToPlane->GetOutput()); } } m_NormalsUpdater->SetInputData(planeSurface); m_NormalsUpdater->AutoOrientNormalsOn(); // that's the trick! Brings consistency between // normals direction and front/back faces direction (see bug 1440) m_NormalsUpdater->ComputePointNormalsOn(); m_NormalsUpdater->Update(); output->SetVtkPolyData(m_NormalsUpdater->GetOutput()); output->CalculateBoundingBox(); } void mitk::PlaneGeometryDataToSurfaceFilter::GenerateData() { mitk::Surface::Pointer output = this->GetOutput(); if (output.IsNull()) return; if (output->GetVtkPolyData() == nullptr) return; // output->GetVtkPolyData()->Update(); //VTK6_TODO vtk pipeline } const mitk::PlaneGeometryData *mitk::PlaneGeometryDataToSurfaceFilter::GetInput() { if (this->GetNumberOfInputs() < 1) { return nullptr; } return static_cast(this->ProcessObject::GetInput(0)); } const mitk::PlaneGeometryData *mitk::PlaneGeometryDataToSurfaceFilter::GetInput(unsigned int idx) { return static_cast(this->ProcessObject::GetInput(idx)); } void mitk::PlaneGeometryDataToSurfaceFilter::SetInput(const mitk::PlaneGeometryData *input) { // Process object is not const-correct so the const_cast is required here this->ProcessObject::SetNthInput(0, const_cast(input)); } void mitk::PlaneGeometryDataToSurfaceFilter::SetInput(unsigned int index, const mitk::PlaneGeometryData *input) { if (index + 1 > this->GetNumberOfInputs()) { this->SetNumberOfRequiredInputs(index + 1); } // Process object is not const-correct so the const_cast is required here this->ProcessObject::SetNthInput(index, const_cast(input)); } void mitk::PlaneGeometryDataToSurfaceFilter::SetBoundingBox(const mitk::BoundingBox *boundingBox) { m_BoundingBox = boundingBox; this->UseBoundingBoxOn(); } const mitk::BoundingBox *mitk::PlaneGeometryDataToSurfaceFilter::GetBoundingBox() const { return m_BoundingBox.GetPointer(); } diff --git a/Modules/Core/src/Algorithms/mitkRGBToRGBACastImageFilter.cpp b/Modules/Core/src/Algorithms/mitkRGBToRGBACastImageFilter.cpp index ca12c22323..54fbacaae0 100644 --- a/Modules/Core/src/Algorithms/mitkRGBToRGBACastImageFilter.cpp +++ b/Modules/Core/src/Algorithms/mitkRGBToRGBACastImageFilter.cpp @@ -1,215 +1,215 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkRGBToRGBACastImageFilter.h" #include "mitkImageAccessByItk.h" #include "mitkImageTimeSelector.h" #include "mitkImageToItk.h" #include "mitkProperties.h" #include #include #include #include mitk::RGBToRGBACastImageFilter::RGBToRGBACastImageFilter() { this->SetNumberOfIndexedInputs(1); this->SetNumberOfRequiredInputs(1); m_InputTimeSelector = mitk::ImageTimeSelector::New(); m_OutputTimeSelector = mitk::ImageTimeSelector::New(); } mitk::RGBToRGBACastImageFilter::~RGBToRGBACastImageFilter() { } bool mitk::RGBToRGBACastImageFilter::IsRGBImage(const mitk::Image *image) { const mitk::PixelType &inputPixelType = image->GetPixelType(); if ((inputPixelType.GetPixelType() == itk::ImageIOBase::RGB) && ((inputPixelType.GetComponentType() == itk::ImageIOBase::UCHAR) || (inputPixelType.GetComponentType() == itk::ImageIOBase::USHORT) || (inputPixelType.GetComponentType() == itk::ImageIOBase::FLOAT) || (inputPixelType.GetComponentType() == itk::ImageIOBase::DOUBLE))) { return true; } return false; } void mitk::RGBToRGBACastImageFilter::GenerateInputRequestedRegion() { Superclass::GenerateInputRequestedRegion(); mitk::Image *output = this->GetOutput(); - mitk::Image *input = const_cast(this->GetInput()); + auto *input = const_cast(this->GetInput()); if (!output->IsInitialized()) { return; } input->SetRequestedRegionToLargestPossibleRegion(); // GenerateTimeInInputRegion(output, input); } void mitk::RGBToRGBACastImageFilter::GenerateOutputInformation() { mitk::Image::ConstPointer input = this->GetInput(); mitk::Image::Pointer output = this->GetOutput(); if ((output->IsInitialized()) && (this->GetMTime() <= m_TimeOfHeaderInitialization.GetMTime())) return; itkDebugMacro(<< "GenerateOutputInformation()"); // Initialize RGBA output with same pixel type as input image const mitk::PixelType &inputPixelType = input->GetPixelType(); typedef itk::Image UCRGBItkImageType; typedef itk::Image USRGBItkImageType; typedef itk::Image FloatCRGBItkImageType; typedef itk::Image DoubleRGBItkImageType; if (inputPixelType == mitk::MakePixelType()) { const mitk::PixelType refPtype = MakePixelType(); output->Initialize(refPtype, *input->GetTimeGeometry()); } else if (inputPixelType == mitk::MakePixelType()) { const mitk::PixelType refPtype = MakePixelType(); output->Initialize(refPtype, *input->GetTimeGeometry()); } else if (inputPixelType == mitk::MakePixelType()) { const mitk::PixelType refPtype = MakePixelType(); output->Initialize(refPtype, *input->GetTimeGeometry()); } else if (inputPixelType == mitk::MakePixelType()) { const mitk::PixelType refPtype = MakePixelType(); output->Initialize(refPtype, *input->GetTimeGeometry()); } output->SetPropertyList(input->GetPropertyList()->Clone()); m_TimeOfHeaderInitialization.Modified(); } void mitk::RGBToRGBACastImageFilter::GenerateData() { mitk::Image::ConstPointer input = this->GetInput(); mitk::Image::Pointer output = this->GetOutput(); if (!output->IsInitialized()) { return; } m_InputTimeSelector->SetInput(input); m_OutputTimeSelector->SetInput(this->GetOutput()); mitk::Image::RegionType outputRegion = output->GetRequestedRegion(); const mitk::TimeGeometry *outputTimeGeometry = output->GetTimeGeometry(); const mitk::TimeGeometry *inputTimeGeometry = input->GetTimeGeometry(); TimePointType timeInMS; int timestep = 0; int tstart = outputRegion.GetIndex(3); int tmax = tstart + outputRegion.GetSize(3); int t; for (t = tstart; t < tmax; ++t) { timeInMS = outputTimeGeometry->TimeStepToTimePoint(t); timestep = inputTimeGeometry->TimePointToTimeStep(timeInMS); m_InputTimeSelector->SetTimeNr(timestep); m_InputTimeSelector->UpdateLargestPossibleRegion(); m_OutputTimeSelector->SetTimeNr(t); m_OutputTimeSelector->UpdateLargestPossibleRegion(); mitk::Image *image = m_InputTimeSelector->GetOutput(); const mitk::PixelType &pixelType = image->GetPixelType(); // Check if the pixel type is supported if (pixelType == MakePixelType>()) { AccessFixedPixelTypeByItk_2(image, InternalCast, (UCRGBPixelType), this, 255); } else if (pixelType == MakePixelType>()) { AccessFixedPixelTypeByItk_2(image, InternalCast, (USRGBPixelType), this, 65535); } else if (pixelType == MakePixelType>()) { AccessFixedPixelTypeByItk_2(image, InternalCast, (FloatRGBPixelType), this, 1.0); } else if (pixelType == MakePixelType>()) { AccessFixedPixelTypeByItk_2(image, InternalCast, (DoubleRGBPixelType), this, 1.0); } else { // Otherwise, write warning and graft input to output // ...TBD... } } m_TimeOfHeaderInitialization.Modified(); } template void mitk::RGBToRGBACastImageFilter::InternalCast(itk::Image *inputItkImage, mitk::RGBToRGBACastImageFilter *addComponentFilter, typename TPixel::ComponentType defaultAlpha) { typedef TPixel InputPixelType; typedef itk::RGBAPixel OutputPixelType; typedef itk::Image InputImageType; typedef itk::Image OutputImageType; typedef itk::ImageRegionConstIterator InputImageIteratorType; typedef itk::ImageRegionIteratorWithIndex OutputImageIteratorType; typename mitk::ImageToItk::Pointer outputimagetoitk = mitk::ImageToItk::New(); outputimagetoitk->SetInput(addComponentFilter->m_OutputTimeSelector->GetOutput()); outputimagetoitk->Update(); typename OutputImageType::Pointer outputItkImage = outputimagetoitk->GetOutput(); // create the iterators typename InputImageType::RegionType inputRegionOfInterest = inputItkImage->GetLargestPossibleRegion(); InputImageIteratorType inputIt(inputItkImage, inputRegionOfInterest); OutputImageIteratorType outputIt(outputItkImage, inputRegionOfInterest); for (inputIt.GoToBegin(), outputIt.GoToBegin(); !inputIt.IsAtEnd(); ++inputIt, ++outputIt) { typename InputPixelType::Iterator pixelInputIt = inputIt.Get().Begin(); typename OutputPixelType::Iterator pixelOutputIt = outputIt.Get().Begin(); *pixelOutputIt++ = *pixelInputIt++; *pixelOutputIt++ = *pixelInputIt++; *pixelOutputIt++ = *pixelInputIt++; *pixelOutputIt = defaultAlpha; } } diff --git a/Modules/Core/src/Controllers/mitkCameraController.cpp b/Modules/Core/src/Controllers/mitkCameraController.cpp index d4f92d7b1e..1556cb09f2 100644 --- a/Modules/Core/src/Controllers/mitkCameraController.cpp +++ b/Modules/Core/src/Controllers/mitkCameraController.cpp @@ -1,351 +1,351 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkCameraController.h" #include "mitkRenderingManager.h" #include "mitkVtkPropRenderer.h" #include "vtkCommand.h" #include #include "vtkCamera.h" #include "vtkRenderer.h" #include #include mitk::CameraController::CameraController() : BaseController(), m_Renderer(nullptr) { } mitk::CameraController::~CameraController() { } mitk::ScalarType mitk::CameraController::ComputeMaxParallelScale() { double widthInMM = this->GetRenderer()->GetCurrentWorldPlaneGeometry()->GetExtentInMM(0); double heightInMM = this->GetRenderer()->GetCurrentWorldPlaneGeometry()->GetExtentInMM(1); double dispHeight = this->GetRenderer()->GetViewportSize()[1]; // in pixel! double dispWidth = this->GetRenderer()->GetViewportSize()[0]; // To get the right zooming factor, we need to set the (half) height to the vtk camera using SetParallelScale. // However, it could be, that our picture is so wide or the display so small, that we cannot take the height of the // picture. // For a wide picture, we have to take the width and adapt the width so that our image fits to the screen. // But we can only set the height. Therefore, if the width is the limiting factor, we need to get the ratio of scaling // for the width and multiply it with the height, so that we have a modified height and set this one. Believe us, we // figured it out... if ((dispWidth / widthInMM) < (dispHeight / heightInMM)) { heightInMM = widthInMM / dispWidth * dispHeight; } return heightInMM * 0.5; } void mitk::CameraController::AdjustConstrainedCameraPosition(mitk::Point2D &planePoint) { // TODO: GetExtentInMM is calculated wrong for rotated planes, e.g. crosshair rotation (bug 19105) double widthInMM = this->GetRenderer()->GetCurrentWorldPlaneGeometry()->GetExtentInMM(0); double heightInMM = this->GetRenderer()->GetCurrentWorldPlaneGeometry()->GetExtentInMM(1); mitk::Point2D dispSizeInMM = this->GetRenderer()->GetViewportSizeInMM(); double xMin, xMax, yMin, yMax; // different calculation of min/max if display is lager/smaller than image. // note, that the plane Position defines the middle of the display but is in image coordinates //([0,0] is defined by the image, so planePosition can sometimes be negative). if (dispSizeInMM[0] > widthInMM) { xMin = widthInMM - 0.5 * dispSizeInMM[0]; xMax = 0.5 * dispSizeInMM[0]; } else { xMin = 0.5 * dispSizeInMM[0]; xMax = widthInMM - 0.5 * dispSizeInMM[0]; } if (dispSizeInMM[1] > heightInMM) { yMin = heightInMM - 0.5 * dispSizeInMM[1]; yMax = 0.5 * dispSizeInMM[1]; } else { yMin = 0.5 * dispSizeInMM[1]; yMax = heightInMM - 0.5 * dispSizeInMM[1]; } if (planePoint[0] < xMin) { planePoint[0] = xMin; } if (planePoint[1] < yMin) { planePoint[1] = yMin; } if (planePoint[0] > xMax) { planePoint[0] = xMax; } if (planePoint[1] > yMax) { planePoint[1] = yMax; } } void mitk::CameraController::SetViewToAnterior() { this->SetStandardView(ANTERIOR); } void mitk::CameraController::SetViewToPosterior() { this->SetStandardView(POSTERIOR); } void mitk::CameraController::SetViewToSinister() { this->SetStandardView(SINISTER); } void mitk::CameraController::SetViewToDexter() { this->SetStandardView(DEXTER); } void mitk::CameraController::SetViewToCranial() { this->SetStandardView(CRANIAL); } void mitk::CameraController::SetViewToCaudal() { this->SetStandardView(CAUDAL); } void mitk::CameraController::SetStandardView(mitk::CameraController::StandardView view) { - const mitk::VtkPropRenderer *glRenderer = dynamic_cast(m_Renderer); + const auto *glRenderer = dynamic_cast(m_Renderer); if (glRenderer == nullptr) return; vtkRenderer *vtkRenderer = glRenderer->GetVtkRenderer(); assert(vtkRenderer); mitk::BoundingBox::Pointer bb; mitk::DataStorage *ds = m_Renderer->GetDataStorage(); if (ds != nullptr) bb = ds->ComputeBoundingBox(); else return; if (m_Renderer->GetMapperID() == mitk::BaseRenderer::Standard3D) { // set up the view for the 3D render window. The views for 2D are set up in the mitkVtkPropRenderer mitk::Point3D middle = bb->GetCenter(); vtkRenderer->GetActiveCamera()->SetFocalPoint(middle[0], middle[1], middle[2]); switch (view) { case ANTERIOR: case POSTERIOR: case SINISTER: case DEXTER: vtkRenderer->GetActiveCamera()->SetViewUp(0, 0, 1); break; case CRANIAL: case CAUDAL: vtkRenderer->GetActiveCamera()->SetViewUp(0, -1, 0); break; } switch (view) { case ANTERIOR: vtkRenderer->GetActiveCamera()->SetPosition(middle[0], -100000, middle[2]); break; case POSTERIOR: vtkRenderer->GetActiveCamera()->SetPosition(middle[0], +100000, middle[2]); break; case SINISTER: vtkRenderer->GetActiveCamera()->SetPosition(+100000, middle[1], middle[2]); break; case DEXTER: vtkRenderer->GetActiveCamera()->SetPosition(-100000, middle[1], middle[2]); break; case CRANIAL: vtkRenderer->GetActiveCamera()->SetPosition(middle[0], middle[1], 100000); break; case CAUDAL: vtkRenderer->GetActiveCamera()->SetPosition(middle[0], middle[1], -100000); break; } vtkRenderer->ResetCamera(); vtkRenderer->ResetCameraClippingRange(); } mitk::RenderingManager *rm = m_Renderer->GetRenderingManager(); rm->RequestUpdateAll(); } void mitk::CameraController::MoveCameraToPoint(const mitk::Point2D &planePoint) { Point2D moveToPoint = planePoint; AdjustCameraToPlane(moveToPoint); this->Modified(); } void mitk::CameraController::MoveBy(const mitk::Vector2D &moveVectorInMM) { MoveCameraToPoint(GetCameraPositionOnPlane() + moveVectorInMM); } void mitk::CameraController::Zoom(ScalarType factor, const Point2D &zoomPointInMM) { if (factor <= 0.0) return; if (this->GetRenderer()->GetMapperID() == BaseRenderer::Standard2D) { double parallelScale = this->GetRenderer()->GetVtkRenderer()->GetActiveCamera()->GetParallelScale() / factor; if (this->GetRenderer()->GetConstrainZoomingAndPanning() && factor < 1.0) { double maxParallelScale = ComputeMaxParallelScale(); if (maxParallelScale - parallelScale * factor < mitk::eps) // this is not the famous 05-bug... Return if already near max zooming return; if (parallelScale > maxParallelScale) { parallelScale = maxParallelScale; } } this->GetRenderer()->GetVtkRenderer()->GetActiveCamera()->SetParallelScale(parallelScale); // Move camera in a way that the clicked point stays visible on the display where it was. Point2D planePoint = GetCameraPositionOnPlane(); MoveCameraToPoint(planePoint + ((zoomPointInMM - planePoint) * (factor - 1))); } } mitk::Point2D mitk::CameraController::GetCameraPositionOnPlane() { Point2D CamPosOnPlane; CamPosOnPlane[0] = this->GetRenderer()->GetVtkRenderer()->GetCenter()[0]; CamPosOnPlane[1] = this->GetRenderer()->GetVtkRenderer()->GetCenter()[1]; this->GetRenderer()->DisplayToPlane(CamPosOnPlane, CamPosOnPlane); return CamPosOnPlane; } void mitk::CameraController::AdjustCameraToPlane() { if (this->GetRenderer()->GetMapperID() == BaseRenderer::Standard2D) { AdjustCameraToPlane(GetCameraPositionOnPlane()); } } void mitk::CameraController::AdjustCameraToPlane(const Point2D &PlanePoint) { if (this->GetRenderer()->GetMapperID() == BaseRenderer::Standard2D) { Point2D _planePoint = PlanePoint; // PlanePoint is const... if (this->GetRenderer()->GetConstrainZoomingAndPanning()) { double parallelScale = this->GetRenderer()->GetVtkRenderer()->GetActiveCamera()->GetParallelScale(); double maxParallelScale = ComputeMaxParallelScale(); if (parallelScale > maxParallelScale) { this->GetRenderer()->GetVtkRenderer()->GetActiveCamera()->SetParallelScale(maxParallelScale); } AdjustConstrainedCameraPosition(_planePoint); } const PlaneGeometry *planeGeometry = this->GetRenderer()->GetCurrentWorldPlaneGeometry(); if (planeGeometry != nullptr) { this->GetRenderer()->GetVtkRenderer()->GetActiveCamera()->SetViewUp(0, 1, 0); // set the view-up for the camera this->GetRenderer()->GetVtkRenderer()->GetActiveCamera()->SetPosition(_planePoint[0], _planePoint[1], 900000); this->GetRenderer()->GetVtkRenderer()->GetActiveCamera()->SetFocalPoint(_planePoint[0], _planePoint[1], 0); // Transform the camera to the current position (transversal, coronal and sagittal plane). // This is necessary, because the SetUserTransform() method does not manipulate the vtkCamera. //(Without not all three planes would be visible). vtkSmartPointer trans = vtkSmartPointer::New(); vtkSmartPointer matrix = vtkSmartPointer::New(); Point3D origin; Vector3D right, bottom, normal; origin = planeGeometry->GetOrigin(); right = planeGeometry->GetAxisVector(0); // right = Extent of Image in mm (worldspace) bottom = planeGeometry->GetAxisVector(1); normal = planeGeometry->GetNormal(); right.Normalize(); bottom.Normalize(); normal.Normalize(); matrix->SetElement(0, 0, right[0]); matrix->SetElement(1, 0, right[1]); matrix->SetElement(2, 0, right[2]); matrix->SetElement(0, 1, bottom[0]); matrix->SetElement(1, 1, bottom[1]); matrix->SetElement(2, 1, bottom[2]); matrix->SetElement(0, 2, normal[0]); matrix->SetElement(1, 2, normal[1]); matrix->SetElement(2, 2, normal[2]); matrix->SetElement(0, 3, origin[0]); matrix->SetElement(1, 3, origin[1]); matrix->SetElement(2, 3, origin[2]); matrix->SetElement(3, 0, 0.0); matrix->SetElement(3, 1, 0.0); matrix->SetElement(3, 2, 0.0); matrix->SetElement(3, 3, 1.0); trans->SetMatrix(matrix); // Transform the camera to the current position (transversal, coronal and sagittal plane). this->GetRenderer()->GetVtkRenderer()->GetActiveCamera()->ApplyTransform(trans); } } } void mitk::CameraController::Fit() { if (this->GetRenderer()->GetMapperID() == BaseRenderer::Standard2D) { this->GetRenderer()->GetVtkRenderer()->GetActiveCamera()->SetParallelScale(ComputeMaxParallelScale()); this->GetRenderer()->GetVtkRenderer()->GetActiveCamera()->SetClippingRange(0.1, 1000000); // Reason for huge range: VTK seems to calculate the clipping planes wrong for small values. See VTK bug (id #7823) // in VTK bugtracker. Point2D planePoint; planePoint[0] = this->GetRenderer()->GetCurrentWorldPlaneGeometry()->GetExtentInMM(0) * 0.5; planePoint[1] = this->GetRenderer()->GetCurrentWorldPlaneGeometry()->GetExtentInMM(1) * 0.5; MoveCameraToPoint(planePoint); } } void mitk::CameraController::SetScaleFactorInMMPerDisplayUnit(ScalarType scale) { if (this->GetRenderer()->GetMapperID() != BaseRenderer::Standard2D) { return; } this->GetRenderer()->GetVtkRenderer()->GetActiveCamera()->SetParallelScale(this->GetRenderer()->GetViewportSize()[1] * scale * 0.5); this->Modified(); } diff --git a/Modules/Core/src/Controllers/mitkCameraRotationController.cpp b/Modules/Core/src/Controllers/mitkCameraRotationController.cpp index 12f34d7552..d55b454240 100644 --- a/Modules/Core/src/Controllers/mitkCameraRotationController.cpp +++ b/Modules/Core/src/Controllers/mitkCameraRotationController.cpp @@ -1,93 +1,93 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkCameraRotationController.h" #include #include #include #include #include "mitkRenderingManager.h" #include "mitkVtkPropRenderer.h" mitk::CameraRotationController::CameraRotationController() : BaseController(), m_LastStepperValue(180), m_Camera(nullptr), m_RenderWindow(nullptr) { m_Slice->SetAutoRepeat(true); m_Slice->SetSteps(360); m_Slice->SetPos(180); itk::SimpleMemberCommand::Pointer sliceStepperChangedCommand, timeStepperChangedCommand; sliceStepperChangedCommand = itk::SimpleMemberCommand::New(); sliceStepperChangedCommand->SetCallbackFunction(this, &CameraRotationController::RotateCamera); m_Slice->AddObserver(itk::ModifiedEvent(), sliceStepperChangedCommand); } mitk::CameraRotationController::~CameraRotationController() { } void mitk::CameraRotationController::RotateCamera() { if (!m_Camera) { this->AcquireCamera(); } if (m_Camera) { int newStepperValue = m_Slice->GetPos(); m_Camera->Azimuth(m_LastStepperValue - newStepperValue); m_LastStepperValue = newStepperValue; // const_cast< RenderWindow* >(m_RenderWindow)->RequestUpdate(); // TODO does not work with movie generator! mitk::RenderingManager::GetInstance()->RequestUpdate(m_RenderWindow); // m_MultiWidget->RequestUpdate(); } } void mitk::CameraRotationController::AcquireCamera() { BaseRenderer *renderer = mitk::BaseRenderer::GetInstance(m_RenderWindow); - const mitk::VtkPropRenderer *propRenderer = dynamic_cast(renderer); + const auto *propRenderer = dynamic_cast(renderer); if (propRenderer) { // get vtk renderer vtkRenderer *vtkrenderer = propRenderer->GetVtkRenderer(); if (vtkrenderer) { // get vtk camera vtkCamera *vtkcam = vtkrenderer->GetActiveCamera(); if (vtkcam) { // vtk smart pointer handling if (!m_Camera) { m_Camera = vtkcam; m_Camera->Register(nullptr); } else { m_Camera->UnRegister(nullptr); m_Camera = vtkcam; m_Camera->Register(nullptr); } } } } } diff --git a/Modules/Core/src/Controllers/mitkLimitedLinearUndo.cpp b/Modules/Core/src/Controllers/mitkLimitedLinearUndo.cpp index 85f8a7f247..e8909d7d67 100644 --- a/Modules/Core/src/Controllers/mitkLimitedLinearUndo.cpp +++ b/Modules/Core/src/Controllers/mitkLimitedLinearUndo.cpp @@ -1,216 +1,216 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkLimitedLinearUndo.h" #include mitk::LimitedLinearUndo::LimitedLinearUndo() { // nothing to do } mitk::LimitedLinearUndo::~LimitedLinearUndo() { // delete undo and redo list this->ClearList(&m_UndoList); this->ClearList(&m_RedoList); } void mitk::LimitedLinearUndo::ClearList(UndoContainer *list) { while (!list->empty()) { UndoStackItem *item = list->back(); list->pop_back(); delete item; } } bool mitk::LimitedLinearUndo::SetOperationEvent(UndoStackItem *stackItem) { - OperationEvent *operationEvent = dynamic_cast(stackItem); + auto *operationEvent = dynamic_cast(stackItem); if (!operationEvent) return false; // clear the redolist, if a new operation is saved if (!m_RedoList.empty()) { this->ClearList(&m_RedoList); InvokeEvent(RedoEmptyEvent()); } m_UndoList.push_back(operationEvent); InvokeEvent(UndoNotEmptyEvent()); return true; } bool mitk::LimitedLinearUndo::Undo(bool fine) { if (fine) { // undo one object event ID return Undo(); } else { // undo one group event ID int oeid = FirstObjectEventIdOfCurrentGroup( m_UndoList); // get the Object Event ID of the first item with a differnt Group ID (as seen from the end of stack) return Undo(oeid); } } bool mitk::LimitedLinearUndo::Undo() { if (m_UndoList.empty()) return false; int undoObjectEventId = m_UndoList.back()->GetObjectEventId(); return Undo(undoObjectEventId); } bool mitk::LimitedLinearUndo::Undo(int oeid) { if (m_UndoList.empty()) return false; bool rc = true; do { m_UndoList.back()->ReverseAndExecute(); m_RedoList.push_back(m_UndoList.back()); // move to redo stack m_UndoList.pop_back(); InvokeEvent(RedoNotEmptyEvent()); if (m_UndoList.empty()) { InvokeEvent(UndoEmptyEvent()); rc = false; break; } } while (m_UndoList.back()->GetObjectEventId() >= oeid); // Update. Check Rendering Mechanism where to request updates mitk::RenderingManager::GetInstance()->RequestUpdateAll(); return rc; } bool mitk::LimitedLinearUndo::Redo(bool) { return Redo(); } bool mitk::LimitedLinearUndo::Redo() { if (m_RedoList.empty()) return false; int redoObjectEventId = m_RedoList.back()->GetObjectEventId(); return Redo(redoObjectEventId); } bool mitk::LimitedLinearUndo::Redo(int oeid) { if (m_RedoList.empty()) return false; do { m_RedoList.back()->ReverseAndExecute(); m_UndoList.push_back(m_RedoList.back()); m_RedoList.pop_back(); InvokeEvent(UndoNotEmptyEvent()); if (m_RedoList.empty()) { InvokeEvent(RedoEmptyEvent()); break; } } while (m_RedoList.back()->GetObjectEventId() <= oeid); // Update. This should belong into the ExecuteOperation() of OperationActors, but it seems not to be used everywhere mitk::RenderingManager::GetInstance()->RequestUpdateAll(); return true; } void mitk::LimitedLinearUndo::Clear() { this->ClearList(&m_UndoList); InvokeEvent(UndoEmptyEvent()); this->ClearList(&m_RedoList); InvokeEvent(RedoEmptyEvent()); } void mitk::LimitedLinearUndo::ClearRedoList() { this->ClearList(&m_RedoList); InvokeEvent(RedoEmptyEvent()); } bool mitk::LimitedLinearUndo::RedoListEmpty() { return m_RedoList.empty(); } int mitk::LimitedLinearUndo::GetLastObjectEventIdInList() { return m_UndoList.back()->GetObjectEventId(); } int mitk::LimitedLinearUndo::GetLastGroupEventIdInList() { return m_UndoList.back()->GetGroupEventId(); } mitk::OperationEvent *mitk::LimitedLinearUndo::GetLastOfType(OperationActor *destination, OperationType opType) { // When/where is this function needed? In CoordinateSupplier... for (auto iter = m_UndoList.rbegin(); iter != m_UndoList.rend(); ++iter) { - OperationEvent *opEvent = dynamic_cast(*iter); + auto *opEvent = dynamic_cast(*iter); if (!opEvent) continue; if (opEvent->GetOperation() != nullptr && opEvent->GetOperation()->GetOperationType() == opType && opEvent->IsValid() && opEvent->GetDestination() == destination) return opEvent; } return nullptr; } int mitk::LimitedLinearUndo::FirstObjectEventIdOfCurrentGroup(mitk::LimitedLinearUndo::UndoContainer &stack) { int currentGroupEventId = stack.back()->GetGroupEventId(); int firstObjectEventId = -1; for (auto iter = stack.rbegin(); iter != stack.rend(); ++iter) { if ((*iter)->GetGroupEventId() == currentGroupEventId) { firstObjectEventId = (*iter)->GetObjectEventId(); } else break; } return firstObjectEventId; } diff --git a/Modules/Core/src/Controllers/mitkOperationEvent.cpp b/Modules/Core/src/Controllers/mitkOperationEvent.cpp index 42ba8e9b02..39e91493d8 100644 --- a/Modules/Core/src/Controllers/mitkOperationEvent.cpp +++ b/Modules/Core/src/Controllers/mitkOperationEvent.cpp @@ -1,154 +1,154 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkOperationEvent.h" #include int mitk::UndoStackItem::m_CurrObjectEventId = 0; int mitk::UndoStackItem::m_CurrGroupEventId = 0; mitk::UndoStackItem::UndoStackItem(std::string description) : m_Reversed(false), m_Description(description) { m_ObjectEventId = GetCurrObjectEventId(); m_GroupEventId = GetCurrGroupEventId(); } mitk::UndoStackItem::~UndoStackItem() { // nothing to do } int mitk::UndoStackItem::GetCurrObjectEventId() { return m_CurrObjectEventId; } int mitk::UndoStackItem::GetCurrGroupEventId() { return m_CurrGroupEventId; } void mitk::UndoStackItem::IncCurrObjectEventId() { ++m_CurrObjectEventId; } void mitk::UndoStackItem::IncCurrGroupEventId() { ++m_CurrGroupEventId; } int mitk::UndoStackItem::GetObjectEventId() { return m_ObjectEventId; } int mitk::UndoStackItem::GetGroupEventId() { return m_GroupEventId; } std::string mitk::UndoStackItem::GetDescription() { return m_Description; } void mitk::UndoStackItem::ReverseOperations() { m_Reversed = !m_Reversed; } void mitk::UndoStackItem::ReverseAndExecute() { ReverseOperations(); } // ******************** mitk::OperationEvent ******************** mitk::Operation *mitk::OperationEvent::GetOperation() { return m_Operation; } mitk::OperationEvent::OperationEvent(OperationActor *destination, Operation *operation, Operation *undoOperation, std::string description) : UndoStackItem(description), m_Destination(destination), m_Operation(operation), m_UndoOperation(undoOperation), m_Invalid(false) { // connect to delete event - if (itk::Object *object = dynamic_cast(m_Destination)) + if (auto *object = dynamic_cast(m_Destination)) { itk::SimpleMemberCommand::Pointer command = itk::SimpleMemberCommand::New(); command->SetCallbackFunction(this, &OperationEvent::OnObjectDeleted); m_DeleteTag = object->AddObserver(itk::DeleteEvent(), command); } } mitk::OperationEvent::~OperationEvent() { // remove the observer if the data m_Destination still is present if (!m_Invalid) { - if (itk::Object *object = dynamic_cast(m_Destination)) + if (auto *object = dynamic_cast(m_Destination)) { object->RemoveObserver(m_DeleteTag); } } delete m_Operation; delete m_UndoOperation; } //##Documentation //## swaps the Undo and Redo- operation and changes m_Reversed void mitk::OperationEvent::ReverseOperations() { if (m_Operation == nullptr) return; Operation *tempOperation = m_Operation; m_Operation = m_UndoOperation; m_UndoOperation = tempOperation; UndoStackItem::ReverseOperations(); } void mitk::OperationEvent::ReverseAndExecute() { ReverseOperations(); if (m_Destination && m_Operation && !m_Invalid) m_Destination->ExecuteOperation(m_Operation); } mitk::OperationActor *mitk::OperationEvent::GetDestination() { return m_Destination; } void mitk::OperationEvent::OnObjectDeleted() { m_Invalid = true; } bool mitk::OperationEvent::IsValid() { return !m_Invalid; } diff --git a/Modules/Core/src/Controllers/mitkRenderingManager.cpp b/Modules/Core/src/Controllers/mitkRenderingManager.cpp index e74f03dea2..1b2cd3ebf4 100644 --- a/Modules/Core/src/Controllers/mitkRenderingManager.cpp +++ b/Modules/Core/src/Controllers/mitkRenderingManager.cpp @@ -1,744 +1,744 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkRenderingManager.h" #include "mitkBaseRenderer.h" #include "mitkCameraController.h" #include "mitkNodePredicateNot.h" #include "mitkNodePredicateProperty.h" #include "mitkProportionalTimeGeometry.h" #include "mitkRenderingManagerFactory.h" #include #include "mitkNumericTypes.h" #include #include #include #include #include namespace mitk { itkEventMacroDefinition(FocusChangedEvent, itk::AnyEvent) RenderingManager::Pointer RenderingManager::s_Instance = nullptr; RenderingManagerFactory *RenderingManager::s_RenderingManagerFactory = nullptr; RenderingManager::RenderingManager() : m_UpdatePending(false), m_MaxLOD(1), m_LODIncreaseBlocked(false), m_LODAbortMechanismEnabled(false), m_ClippingPlaneEnabled(false), m_TimeNavigationController(SliceNavigationController::New()), m_DataStorage(nullptr), m_ConstrainedPanningZooming(true), m_FocusedRenderWindow(nullptr) { m_ShadingEnabled.assign(3, false); m_ShadingValues.assign(4, 0.0); InitializePropertyList(); } RenderingManager::~RenderingManager() { // Decrease reference counts of all registered vtkRenderWindows for // proper destruction RenderWindowVector::iterator it; for (it = m_AllRenderWindows.begin(); it != m_AllRenderWindows.end(); ++it) { (*it)->UnRegister(nullptr); - RenderWindowCallbacksList::iterator callbacks_it = this->m_RenderWindowCallbacksList.find(*it); + auto callbacks_it = this->m_RenderWindowCallbacksList.find(*it); if (callbacks_it != this->m_RenderWindowCallbacksList.end()) { (*it)->RemoveObserver(callbacks_it->second.commands[0u]); (*it)->RemoveObserver(callbacks_it->second.commands[1u]); (*it)->RemoveObserver(callbacks_it->second.commands[2u]); } } } void RenderingManager::SetFactory(RenderingManagerFactory *factory) { s_RenderingManagerFactory = factory; } const RenderingManagerFactory *RenderingManager::GetFactory() { return s_RenderingManagerFactory; } bool RenderingManager::HasFactory() { if (RenderingManager::s_RenderingManagerFactory) { return true; } else { return false; } } RenderingManager::Pointer RenderingManager::New() { const RenderingManagerFactory *factory = GetFactory(); if (factory == nullptr) return nullptr; return factory->CreateRenderingManager(); } RenderingManager *RenderingManager::GetInstance() { if (!RenderingManager::s_Instance) { if (s_RenderingManagerFactory) { s_Instance = s_RenderingManagerFactory->CreateRenderingManager(); } } return s_Instance; } bool RenderingManager::IsInstantiated() { if (RenderingManager::s_Instance) return true; else return false; } void RenderingManager::AddRenderWindow(vtkRenderWindow *renderWindow) { if (renderWindow && (m_RenderWindowList.find(renderWindow) == m_RenderWindowList.end())) { m_RenderWindowList[renderWindow] = RENDERING_INACTIVE; m_AllRenderWindows.push_back(renderWindow); if (m_DataStorage.IsNotNull()) mitk::BaseRenderer::GetInstance(renderWindow)->SetDataStorage(m_DataStorage.GetPointer()); // Register vtkRenderWindow instance renderWindow->Register(nullptr); // Add callbacks for rendering abort mechanism // BaseRenderer *renderer = BaseRenderer::GetInstance( renderWindow ); vtkCallbackCommand *startCallbackCommand = vtkCallbackCommand::New(); startCallbackCommand->SetCallback(RenderingManager::RenderingStartCallback); renderWindow->AddObserver(vtkCommand::StartEvent, startCallbackCommand); vtkCallbackCommand *progressCallbackCommand = vtkCallbackCommand::New(); progressCallbackCommand->SetCallback(RenderingManager::RenderingProgressCallback); renderWindow->AddObserver(vtkCommand::AbortCheckEvent, progressCallbackCommand); vtkCallbackCommand *endCallbackCommand = vtkCallbackCommand::New(); endCallbackCommand->SetCallback(RenderingManager::RenderingEndCallback); renderWindow->AddObserver(vtkCommand::EndEvent, endCallbackCommand); RenderWindowCallbacks callbacks; callbacks.commands[0u] = startCallbackCommand; callbacks.commands[1u] = progressCallbackCommand; callbacks.commands[2u] = endCallbackCommand; this->m_RenderWindowCallbacksList[renderWindow] = callbacks; // Delete vtk variables correctly startCallbackCommand->Delete(); progressCallbackCommand->Delete(); endCallbackCommand->Delete(); } } void RenderingManager::RemoveRenderWindow(vtkRenderWindow *renderWindow) { if (m_RenderWindowList.erase(renderWindow)) { - RenderWindowCallbacksList::iterator callbacks_it = this->m_RenderWindowCallbacksList.find(renderWindow); + auto callbacks_it = this->m_RenderWindowCallbacksList.find(renderWindow); if (callbacks_it != this->m_RenderWindowCallbacksList.end()) { renderWindow->RemoveObserver(callbacks_it->second.commands[0u]); renderWindow->RemoveObserver(callbacks_it->second.commands[1u]); renderWindow->RemoveObserver(callbacks_it->second.commands[2u]); this->m_RenderWindowCallbacksList.erase(callbacks_it); } - RenderWindowVector::iterator rw_it = + auto rw_it = std::find(m_AllRenderWindows.begin(), m_AllRenderWindows.end(), renderWindow); if (rw_it != m_AllRenderWindows.cend()) { // Decrease reference count for proper destruction (*rw_it)->UnRegister(nullptr); m_AllRenderWindows.erase(rw_it); } } } const RenderingManager::RenderWindowVector &RenderingManager::GetAllRegisteredRenderWindows() { return m_AllRenderWindows; } void RenderingManager::RequestUpdate(vtkRenderWindow *renderWindow) { // If the renderWindow is not valid, we do not want to inadvertantly create // an entry in the m_RenderWindowList map. It is possible if the user is // regularly calling AddRenderer and RemoveRenderer for a rendering update // to come into this method with a renderWindow pointer that is valid in the // sense that the window does exist within the application, but that // renderWindow has been temporarily removed from this RenderingManager for // performance reasons. if (m_RenderWindowList.find(renderWindow) == m_RenderWindowList.cend()) { return; } m_RenderWindowList[renderWindow] = RENDERING_REQUESTED; if (!m_UpdatePending) { m_UpdatePending = true; this->GenerateRenderingRequestEvent(); } } void RenderingManager::ForceImmediateUpdate(vtkRenderWindow *renderWindow) { // If the renderWindow is not valid, we do not want to inadvertantly create // an entry in the m_RenderWindowList map. It is possible if the user is // regularly calling AddRenderer and RemoveRenderer for a rendering update // to come into this method with a renderWindow pointer that is valid in the // sense that the window does exist within the application, but that // renderWindow has been temporarily removed from this RenderingManager for // performance reasons. if (m_RenderWindowList.find(renderWindow) == m_RenderWindowList.cend()) { return; } // Erase potentially pending requests for this window m_RenderWindowList[renderWindow] = RENDERING_INACTIVE; m_UpdatePending = false; // Immediately repaint this window (implementation platform specific) // If the size is 0 it crashes int *size = renderWindow->GetSize(); if (0 != size[0] && 0 != size[1]) { // prepare the camera etc. before rendering // Note: this is a very important step which should be called before the VTK render! // If you modify the camera anywhere else or after the render call, the scene cannot be seen. - mitk::VtkPropRenderer *vPR = dynamic_cast(mitk::BaseRenderer::GetInstance(renderWindow)); + auto *vPR = dynamic_cast(mitk::BaseRenderer::GetInstance(renderWindow)); if (vPR) vPR->PrepareRender(); // Execute rendering renderWindow->Render(); } } void RenderingManager::RequestUpdateAll(RequestType type) { RenderWindowList::const_iterator it; for (it = m_RenderWindowList.cbegin(); it != m_RenderWindowList.cend(); ++it) { int id = BaseRenderer::GetInstance(it->first)->GetMapperID(); if ((type == REQUEST_UPDATE_ALL) || ((type == REQUEST_UPDATE_2DWINDOWS) && (id == 1)) || ((type == REQUEST_UPDATE_3DWINDOWS) && (id == 2))) { this->RequestUpdate(it->first); } } } void RenderingManager::ForceImmediateUpdateAll(RequestType type) { RenderWindowList::const_iterator it; for (it = m_RenderWindowList.cbegin(); it != m_RenderWindowList.cend(); ++it) { int id = BaseRenderer::GetInstance(it->first)->GetMapperID(); if ((type == REQUEST_UPDATE_ALL) || ((type == REQUEST_UPDATE_2DWINDOWS) && (id == 1)) || ((type == REQUEST_UPDATE_3DWINDOWS) && (id == 2))) { // Immediately repaint this window (implementation platform specific) // If the size is 0, it crashes this->ForceImmediateUpdate(it->first); } } } void RenderingManager::InitializeViewsByBoundingObjects(const DataStorage *ds) { if (!ds) return; // get all nodes that have not set "includeInBoundingBox" to false mitk::NodePredicateNot::Pointer pred = mitk::NodePredicateNot::New( mitk::NodePredicateProperty::New("includeInBoundingBox", mitk::BoolProperty::New(false))); mitk::DataStorage::SetOfObjects::ConstPointer rs = ds->GetSubset(pred); // calculate bounding geometry of these nodes mitk::TimeGeometry::Pointer bounds = ds->ComputeBoundingGeometry3D(rs, "visible"); // initialize the views to the bounding geometry this->InitializeViews(bounds); } // TODO_GOETZ // Remove old function, so only this one is working. bool RenderingManager::InitializeViews(const BaseGeometry *dataGeometry, RequestType type, bool preserveRoughOrientationInWorldSpace) { ProportionalTimeGeometry::Pointer propTimeGeometry = ProportionalTimeGeometry::New(); propTimeGeometry->Initialize(dynamic_cast(dataGeometry->Clone().GetPointer()), 1); return InitializeViews(propTimeGeometry, type, preserveRoughOrientationInWorldSpace); } bool RenderingManager::InitializeViews(const TimeGeometry *dataGeometry, RequestType type, bool /*preserveRoughOrientationInWorldSpace*/) { MITK_DEBUG << "initializing views"; bool boundingBoxInitialized = false; TimeGeometry::ConstPointer timeGeometry = dataGeometry; TimeGeometry::Pointer modifiedGeometry = nullptr; if (dataGeometry != nullptr) { modifiedGeometry = dataGeometry->Clone(); } int warningLevel = vtkObject::GetGlobalWarningDisplay(); vtkObject::GlobalWarningDisplayOff(); if ((timeGeometry.IsNotNull()) && (const_cast(timeGeometry->GetBoundingBoxInWorld())->GetDiagonalLength2() > mitk::eps)) { boundingBoxInitialized = true; } if (timeGeometry.IsNotNull()) { // make sure bounding box has an extent bigger than zero in any direction // clone the input geometry // Old Geometry3D::Pointer modifiedGeometry = dynamic_cast( dataGeometry->Clone().GetPointer() ); assert(modifiedGeometry.IsNotNull()); for (TimeStepType step = 0; step < modifiedGeometry->CountTimeSteps(); ++step) { BaseGeometry::BoundsArrayType newBounds = modifiedGeometry->GetGeometryForTimeStep(step)->GetBounds(); for (unsigned int dimension = 0; (2 * dimension) < newBounds.Size(); dimension++) { // check for equality but for an epsilon if (Equal(newBounds[2 * dimension], newBounds[2 * dimension + 1])) { newBounds[2 * dimension + 1] += 1; if (Equal( newBounds[2 * dimension], newBounds[2 * dimension + 1])) // newBounds will still be equal if values are beyond double precision { mitkThrow() << "One dimension of object data has zero length, please make sure you're not using numbers " "beyond double precision as coordinates."; } } } modifiedGeometry->GetGeometryForTimeStep(step)->SetBounds(newBounds); } } timeGeometry = modifiedGeometry; RenderWindowList::const_iterator it; for (it = m_RenderWindowList.cbegin(); it != m_RenderWindowList.cend(); ++it) { mitk::BaseRenderer *baseRenderer = mitk::BaseRenderer::GetInstance(it->first); baseRenderer->SetConstrainZoomingAndPanning(m_ConstrainedPanningZooming); int id = baseRenderer->GetMapperID(); if (((type == REQUEST_UPDATE_ALL) || ((type == REQUEST_UPDATE_2DWINDOWS) && (id == 1)) || ((type == REQUEST_UPDATE_3DWINDOWS) && (id == 2)))) { this->InternalViewInitialization(baseRenderer, timeGeometry, boundingBoxInitialized, id); } } if (boundingBoxInitialized) { m_TimeNavigationController->SetInputWorldTimeGeometry(timeGeometry); } m_TimeNavigationController->Update(); this->RequestUpdateAll(type); vtkObject::SetGlobalWarningDisplay(warningLevel); // Inform listeners that views have been initialized this->InvokeEvent(mitk::RenderingManagerViewsInitializedEvent()); return boundingBoxInitialized; } bool RenderingManager::InitializeViews(RequestType type) { RenderWindowList::const_iterator it; for (it = m_RenderWindowList.cbegin(); it != m_RenderWindowList.cend(); ++it) { mitk::BaseRenderer *baseRenderer = mitk::BaseRenderer::GetInstance(it->first); int id = baseRenderer->GetMapperID(); if ((type == REQUEST_UPDATE_ALL) || ((type == REQUEST_UPDATE_2DWINDOWS) && (id == 1)) || ((type == REQUEST_UPDATE_3DWINDOWS) && (id == 2))) { mitk::SliceNavigationController *nc = baseRenderer->GetSliceNavigationController(); // Re-initialize view direction nc->SetViewDirectionToDefault(); // Update the SNC nc->Update(); } } this->RequestUpdateAll(type); return true; } bool RenderingManager::InitializeView(vtkRenderWindow *renderWindow, const BaseGeometry *geometry, bool initializeGlobalTimeSNC) { ProportionalTimeGeometry::Pointer propTimeGeometry = ProportionalTimeGeometry::New(); propTimeGeometry->Initialize(dynamic_cast(geometry->Clone().GetPointer()), 1); return InitializeView(renderWindow, propTimeGeometry, initializeGlobalTimeSNC); } bool RenderingManager::InitializeView(vtkRenderWindow *renderWindow, const TimeGeometry *geometry, bool initializeGlobalTimeSNC) { bool boundingBoxInitialized = false; int warningLevel = vtkObject::GetGlobalWarningDisplay(); vtkObject::GlobalWarningDisplayOff(); if ((geometry != nullptr) && (const_cast(geometry->GetBoundingBoxInWorld())->GetDiagonalLength2() > mitk::eps)) { boundingBoxInitialized = true; } mitk::BaseRenderer *baseRenderer = mitk::BaseRenderer::GetInstance(renderWindow); int id = baseRenderer->GetMapperID(); this->InternalViewInitialization(baseRenderer, geometry, boundingBoxInitialized, id); if (boundingBoxInitialized && initializeGlobalTimeSNC) { m_TimeNavigationController->SetInputWorldTimeGeometry(geometry); } m_TimeNavigationController->Update(); this->RequestUpdate(renderWindow); vtkObject::SetGlobalWarningDisplay(warningLevel); return boundingBoxInitialized; } bool RenderingManager::InitializeView(vtkRenderWindow *renderWindow) { mitk::BaseRenderer *baseRenderer = mitk::BaseRenderer::GetInstance(renderWindow); mitk::SliceNavigationController *nc = baseRenderer->GetSliceNavigationController(); // Re-initialize view direction nc->SetViewDirectionToDefault(); // Update the SNC nc->Update(); this->RequestUpdate(renderWindow); return true; } void RenderingManager::InternalViewInitialization(mitk::BaseRenderer *baseRenderer, const mitk::TimeGeometry *geometry, bool boundingBoxInitialized, int mapperID) { mitk::SliceNavigationController *nc = baseRenderer->GetSliceNavigationController(); // Re-initialize view direction nc->SetViewDirectionToDefault(); if (boundingBoxInitialized) { // Set geometry for NC nc->SetInputWorldTimeGeometry(geometry); nc->Update(); if (mapperID == BaseRenderer::Standard2D) { // For 2D SNCs, steppers are set so that the cross is centered // in the image nc->GetSlice()->SetPos(nc->GetSlice()->GetSteps() / 2); } baseRenderer->GetCameraController()->SetViewToAnterior(); baseRenderer->GetCameraController()->Fit(); } else { nc->Update(); } } const SliceNavigationController *RenderingManager::GetTimeNavigationController() const { return m_TimeNavigationController.GetPointer(); } SliceNavigationController *RenderingManager::GetTimeNavigationController() { return m_TimeNavigationController.GetPointer(); } void RenderingManager::ExecutePendingRequests() { m_UpdatePending = false; // Satisfy all pending update requests RenderWindowList::const_iterator it; int i = 0; for (it = m_RenderWindowList.cbegin(); it != m_RenderWindowList.cend(); ++it, ++i) { if (it->second == RENDERING_REQUESTED) { this->ForceImmediateUpdate(it->first); } } } void RenderingManager::RenderingStartCallback(vtkObject *caller, unsigned long, void *, void *) { - vtkRenderWindow *renderWindow = dynamic_cast(caller); + auto *renderWindow = dynamic_cast(caller); mitk::RenderingManager *renman = mitk::BaseRenderer::GetInstance(renderWindow)->GetRenderingManager(); RenderWindowList &renderWindowList = renman->m_RenderWindowList; if (renderWindow) { renderWindowList[renderWindow] = RENDERING_INPROGRESS; } renman->m_UpdatePending = false; } void RenderingManager::RenderingProgressCallback(vtkObject *caller, unsigned long, void *, void *) { - vtkRenderWindow *renderWindow = dynamic_cast(caller); + auto *renderWindow = dynamic_cast(caller); mitk::RenderingManager *renman = mitk::BaseRenderer::GetInstance(renderWindow)->GetRenderingManager(); if (renman->m_LODAbortMechanismEnabled) { - vtkRenderWindow *renderWindow = dynamic_cast(caller); + auto *renderWindow = dynamic_cast(caller); if (renderWindow) { BaseRenderer *renderer = BaseRenderer::GetInstance(renderWindow); if (renderer && (renderer->GetNumberOfVisibleLODEnabledMappers() > 0)) { renman->DoMonitorRendering(); } } } } void RenderingManager::RenderingEndCallback(vtkObject *caller, unsigned long, void *, void *) { - vtkRenderWindow *renderWindow = dynamic_cast(caller); + auto *renderWindow = dynamic_cast(caller); mitk::RenderingManager *renman = mitk::BaseRenderer::GetInstance(renderWindow)->GetRenderingManager(); RenderWindowList &renderWindowList = renman->m_RenderWindowList; RendererIntMap &nextLODMap = renman->m_NextLODMap; if (renderWindow) { BaseRenderer *renderer = BaseRenderer::GetInstance(renderWindow); if (renderer) { renderWindowList[renderer->GetRenderWindow()] = RENDERING_INACTIVE; // Level-of-Detail handling if (renderer->GetNumberOfVisibleLODEnabledMappers() > 0) { if (nextLODMap[renderer] == 0) renman->StartOrResetTimer(); else nextLODMap[renderer] = 0; } } } } bool RenderingManager::IsRendering() const { RenderWindowList::const_iterator it; for (it = m_RenderWindowList.cbegin(); it != m_RenderWindowList.cend(); ++it) { if (it->second == RENDERING_INPROGRESS) { return true; } } return false; } void RenderingManager::AbortRendering() { RenderWindowList::const_iterator it; for (it = m_RenderWindowList.cbegin(); it != m_RenderWindowList.cend(); ++it) { if (it->second == RENDERING_INPROGRESS) { it->first->SetAbortRender(true); m_RenderingAbortedMap[BaseRenderer::GetInstance(it->first)] = true; } } } int RenderingManager::GetNextLOD(BaseRenderer *renderer) { if (renderer != nullptr) { return m_NextLODMap[renderer]; } else { return 0; } } void RenderingManager::ExecutePendingHighResRenderingRequest() { RenderWindowList::const_iterator it; for (it = m_RenderWindowList.cbegin(); it != m_RenderWindowList.cend(); ++it) { BaseRenderer *renderer = BaseRenderer::GetInstance(it->first); if (renderer->GetNumberOfVisibleLODEnabledMappers() > 0) { if (m_NextLODMap[renderer] == 0) { m_NextLODMap[renderer] = 1; RequestUpdate(it->first); } } } } void RenderingManager::SetMaximumLOD(unsigned int max) { m_MaxLOD = max; } // enable/disable shading void RenderingManager::SetShading(bool state, unsigned int lod) { if (lod > m_MaxLOD) { itkWarningMacro(<< "LOD out of range requested: " << lod << " maxLOD: " << m_MaxLOD); return; } m_ShadingEnabled[lod] = state; } bool RenderingManager::GetShading(unsigned int lod) { if (lod > m_MaxLOD) { itkWarningMacro(<< "LOD out of range requested: " << lod << " maxLOD: " << m_MaxLOD); return false; } return m_ShadingEnabled[lod]; } // enable/disable the clipping plane void RenderingManager::SetClippingPlaneStatus(bool status) { m_ClippingPlaneEnabled = status; } bool RenderingManager::GetClippingPlaneStatus() { return m_ClippingPlaneEnabled; } void RenderingManager::SetShadingValues(float ambient, float diffuse, float specular, float specpower) { m_ShadingValues[0] = ambient; m_ShadingValues[1] = diffuse; m_ShadingValues[2] = specular; m_ShadingValues[3] = specpower; } RenderingManager::FloatVector &RenderingManager::GetShadingValues() { return m_ShadingValues; } void RenderingManager::InitializePropertyList() { if (m_PropertyList.IsNull()) { m_PropertyList = PropertyList::New(); } this->SetProperty("coupled-zoom", BoolProperty::New(false)); this->SetProperty("coupled-plane-rotation", BoolProperty::New(false)); this->SetProperty("MIP-slice-rendering", BoolProperty::New(false)); } PropertyList::Pointer RenderingManager::GetPropertyList() const { return m_PropertyList; } BaseProperty *RenderingManager::GetProperty(const char *propertyKey) const { return m_PropertyList->GetProperty(propertyKey); } void RenderingManager::SetProperty(const char *propertyKey, BaseProperty *propertyValue) { m_PropertyList->SetProperty(propertyKey, propertyValue); } void RenderingManager::SetDataStorage(DataStorage *storage) { if (storage != nullptr) { m_DataStorage = storage; RenderingManager::RenderWindowVector::const_iterator iter; for (iter = m_AllRenderWindows.cbegin(); iter < m_AllRenderWindows.cend(); ++iter) { mitk::BaseRenderer::GetInstance((*iter))->SetDataStorage(m_DataStorage.GetPointer()); } } } mitk::DataStorage *RenderingManager::GetDataStorage() { return m_DataStorage; } void RenderingManager::SetRenderWindowFocus(vtkRenderWindow *focusWindow) { if (focusWindow != m_FocusedRenderWindow) { if (!focusWindow || (m_RenderWindowList.find(focusWindow) != m_RenderWindowList.cend())) { m_FocusedRenderWindow = focusWindow; this->InvokeEvent(FocusChangedEvent()); return; } MITK_ERROR << "Tried to set a RenderWindow that does not exist."; } } // Create and register generic RenderingManagerFactory. TestingRenderingManagerFactory renderingManagerFactory; } // namespace diff --git a/Modules/Core/src/Controllers/mitkSliceNavigationController.cpp b/Modules/Core/src/Controllers/mitkSliceNavigationController.cpp index 6bae144303..d3d4d1adcd 100644 --- a/Modules/Core/src/Controllers/mitkSliceNavigationController.cpp +++ b/Modules/Core/src/Controllers/mitkSliceNavigationController.cpp @@ -1,659 +1,659 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkSliceNavigationController.h" #include "mitkAction.h" #include "mitkBaseRenderer.h" #include "mitkCrosshairPositionEvent.h" #include "mitkInteractionConst.h" #include "mitkOperation.h" #include "mitkOperationActor.h" #include "mitkPlaneGeometry.h" #include "mitkProportionalTimeGeometry.h" #include "mitkArbitraryTimeGeometry.h" #include "mitkRenderingManager.h" #include "mitkSlicedGeometry3D.h" #include "mitkVtkPropRenderer.h" #include "mitkImage.h" #include "mitkImagePixelReadAccessor.h" #include "mitkInteractionConst.h" #include "mitkNodePredicateDataType.h" #include "mitkOperationEvent.h" #include "mitkPixelTypeMultiplex.h" #include "mitkPlaneOperation.h" #include "mitkPointOperation.h" #include "mitkStatusBar.h" #include "mitkUndoController.h" #include "mitkApplyTransformMatrixOperation.h" #include "mitkMemoryUtilities.h" #include namespace mitk { SliceNavigationController::SliceNavigationController() : BaseController(), m_InputWorldGeometry3D( mitk::BaseGeometry::ConstPointer() ), m_InputWorldTimeGeometry( mitk::TimeGeometry::ConstPointer() ), m_CreatedWorldGeometry( mitk::TimeGeometry::Pointer() ), m_ViewDirection(Axial), m_DefaultViewDirection(Axial), m_RenderingManager( mitk::RenderingManager::Pointer() ), m_Renderer( nullptr ), m_Top(false), m_FrontSide(false), m_Rotated(false), m_BlockUpdate(false), m_SliceLocked(false), m_SliceRotationLocked(false), m_OldPos(0) { typedef itk::SimpleMemberCommand SNCCommandType; SNCCommandType::Pointer sliceStepperChangedCommand, timeStepperChangedCommand; sliceStepperChangedCommand = SNCCommandType::New(); timeStepperChangedCommand = SNCCommandType::New(); sliceStepperChangedCommand->SetCallbackFunction(this, &SliceNavigationController::SendSlice); timeStepperChangedCommand->SetCallbackFunction(this, &SliceNavigationController::SendTime); m_Slice->AddObserver(itk::ModifiedEvent(), sliceStepperChangedCommand); m_Time->AddObserver(itk::ModifiedEvent(), timeStepperChangedCommand); m_Slice->SetUnitName("mm"); m_Time->SetUnitName("ms"); m_Top = false; m_FrontSide = false; m_Rotated = false; } SliceNavigationController::~SliceNavigationController() {} void SliceNavigationController::SetInputWorldGeometry3D(const BaseGeometry *geometry) { if ( geometry != nullptr ) { if (const_cast(geometry->GetBoundingBox())->GetDiagonalLength2() < eps) { itkWarningMacro("setting an empty bounding-box"); geometry = nullptr; } } if (m_InputWorldGeometry3D != geometry) { m_InputWorldGeometry3D = geometry; m_InputWorldTimeGeometry = mitk::TimeGeometry::ConstPointer(); this->Modified(); } } void SliceNavigationController::SetInputWorldTimeGeometry(const TimeGeometry *geometry) { if ( geometry != nullptr ) { if (const_cast(geometry->GetBoundingBoxInWorld())->GetDiagonalLength2() < eps) { itkWarningMacro("setting an empty bounding-box"); geometry = nullptr; } } if (m_InputWorldTimeGeometry != geometry) { m_InputWorldTimeGeometry = geometry; m_InputWorldGeometry3D = mitk::BaseGeometry::ConstPointer(); this->Modified(); } } RenderingManager *SliceNavigationController::GetRenderingManager() const { mitk::RenderingManager *renderingManager = m_RenderingManager.GetPointer(); if (renderingManager != nullptr) return renderingManager; if ( m_Renderer != nullptr ) { renderingManager = m_Renderer->GetRenderingManager(); if (renderingManager != nullptr) return renderingManager; } return mitk::RenderingManager::GetInstance(); } void SliceNavigationController::SetViewDirectionToDefault() { m_ViewDirection = m_DefaultViewDirection; } const char *SliceNavigationController::GetViewDirectionAsString() const { const char *viewDirectionString; switch (m_ViewDirection) { case SliceNavigationController::Axial: viewDirectionString = "Axial"; break; case SliceNavigationController::Sagittal: viewDirectionString = "Sagittal"; break; case SliceNavigationController::Frontal: viewDirectionString = "Coronal"; break; case SliceNavigationController::Original: viewDirectionString = "Original"; break; default: viewDirectionString = "No View Direction Available"; break; } return viewDirectionString; } void SliceNavigationController::Update() { if (!m_BlockUpdate) { if (m_ViewDirection == Sagittal) { this->Update(Sagittal, true, true, false); } else if (m_ViewDirection == Frontal) { this->Update(Frontal, false, true, false); } else if (m_ViewDirection == Axial) { this->Update(Axial, false, false, true); } else { this->Update(m_ViewDirection); } } } void SliceNavigationController::Update(SliceNavigationController::ViewDirection viewDirection, bool top, bool frontside, bool rotated) { TimeGeometry::ConstPointer worldTimeGeometry = m_InputWorldTimeGeometry; if (m_BlockUpdate || (m_InputWorldTimeGeometry.IsNull() && m_InputWorldGeometry3D.IsNull()) || ((worldTimeGeometry.IsNotNull()) && (worldTimeGeometry->CountTimeSteps() == 0))) { return; } m_BlockUpdate = true; if (m_InputWorldTimeGeometry.IsNotNull() && m_LastUpdateTime < m_InputWorldTimeGeometry->GetMTime()) { Modified(); } if (m_InputWorldGeometry3D.IsNotNull() && m_LastUpdateTime < m_InputWorldGeometry3D->GetMTime()) { Modified(); } this->SetViewDirection(viewDirection); this->SetTop(top); this->SetFrontSide(frontside); this->SetRotated(rotated); if (m_LastUpdateTime < GetMTime()) { m_LastUpdateTime = GetMTime(); // initialize the viewplane SlicedGeometry3D::Pointer slicedWorldGeometry = SlicedGeometry3D::Pointer(); BaseGeometry::ConstPointer currentGeometry = BaseGeometry::ConstPointer(); if (m_InputWorldTimeGeometry.IsNotNull()) if (m_InputWorldTimeGeometry->IsValidTimeStep(GetTime()->GetPos())) currentGeometry = m_InputWorldTimeGeometry->GetGeometryForTimeStep(GetTime()->GetPos()); else currentGeometry = m_InputWorldTimeGeometry->GetGeometryForTimeStep(0); else currentGeometry = m_InputWorldGeometry3D; m_CreatedWorldGeometry = mitk::TimeGeometry::Pointer(); switch (viewDirection) { case Original: if (worldTimeGeometry.IsNotNull()) { m_CreatedWorldGeometry = worldTimeGeometry->Clone(); worldTimeGeometry = m_CreatedWorldGeometry.GetPointer(); slicedWorldGeometry = dynamic_cast( m_CreatedWorldGeometry->GetGeometryForTimeStep(this->GetTime()->GetPos()).GetPointer()); if (slicedWorldGeometry.IsNotNull()) { break; } } else { - const SlicedGeometry3D *worldSlicedGeometry = + const auto *worldSlicedGeometry = dynamic_cast(currentGeometry.GetPointer()); if ( worldSlicedGeometry != nullptr ) { slicedWorldGeometry = static_cast(currentGeometry->Clone().GetPointer()); break; } } slicedWorldGeometry = SlicedGeometry3D::New(); slicedWorldGeometry->InitializePlanes(currentGeometry, PlaneGeometry::None, top, frontside, rotated); slicedWorldGeometry->SetSliceNavigationController(this); break; case Axial: slicedWorldGeometry = SlicedGeometry3D::New(); slicedWorldGeometry->InitializePlanes(currentGeometry, PlaneGeometry::Axial, top, frontside, rotated); slicedWorldGeometry->SetSliceNavigationController(this); break; case Frontal: slicedWorldGeometry = SlicedGeometry3D::New(); slicedWorldGeometry->InitializePlanes(currentGeometry, PlaneGeometry::Frontal, top, frontside, rotated); slicedWorldGeometry->SetSliceNavigationController(this); break; case Sagittal: slicedWorldGeometry = SlicedGeometry3D::New(); slicedWorldGeometry->InitializePlanes(currentGeometry, PlaneGeometry::Sagittal, top, frontside, rotated); slicedWorldGeometry->SetSliceNavigationController(this); break; default: itkExceptionMacro("unknown ViewDirection"); } m_Slice->SetPos(0); m_Slice->SetSteps((int)slicedWorldGeometry->GetSlices()); if ( worldTimeGeometry.IsNull() ) { auto createdTimeGeometry = ProportionalTimeGeometry::New(); createdTimeGeometry->Initialize( slicedWorldGeometry, 1 ); m_CreatedWorldGeometry = createdTimeGeometry; m_Time->SetSteps(0); m_Time->SetPos(0); m_Time->InvalidateRange(); } else { m_BlockUpdate = true; m_Time->SetSteps(worldTimeGeometry->CountTimeSteps()); m_Time->SetPos(0); const TimeBounds &timeBounds = worldTimeGeometry->GetTimeBounds(); m_Time->SetRange(timeBounds[0], timeBounds[1]); m_BlockUpdate = false; const auto currentTemporalPosition = this->GetTime()->GetPos(); assert( worldTimeGeometry->GetGeometryForTimeStep( currentTemporalPosition ).IsNotNull() ); if ( dynamic_cast( worldTimeGeometry.GetPointer() ) != nullptr ) { const TimePointType minimumTimePoint = worldTimeGeometry->TimeStepToTimePoint( currentTemporalPosition ); const TimePointType stepDuration = worldTimeGeometry->TimeStepToTimePoint( currentTemporalPosition + 1 ) - minimumTimePoint; auto createdTimeGeometry = ProportionalTimeGeometry::New(); createdTimeGeometry->Initialize( slicedWorldGeometry, worldTimeGeometry->CountTimeSteps() ); createdTimeGeometry->SetFirstTimePoint( minimumTimePoint ); createdTimeGeometry->SetStepDuration( stepDuration ); m_CreatedWorldGeometry = createdTimeGeometry; } else { auto createdTimeGeometry = mitk::ArbitraryTimeGeometry::New(); const TimeStepType numberOfTimeSteps = worldTimeGeometry->CountTimeSteps(); createdTimeGeometry->ReserveSpaceForGeometries( numberOfTimeSteps ); for ( TimeStepType i = 0; i < numberOfTimeSteps; ++i ) { const BaseGeometry::Pointer clonedGeometry = slicedWorldGeometry->Clone().GetPointer(); const auto bounds = worldTimeGeometry->GetTimeBounds( i ); createdTimeGeometry->AppendNewTimeStep( clonedGeometry, bounds[0], bounds[1]); } createdTimeGeometry->Update(); m_CreatedWorldGeometry = createdTimeGeometry; } } } // unblock update; we may do this now, because if m_BlockUpdate was already // true before this method was entered, then we will never come here. m_BlockUpdate = false; // Send the geometry. Do this even if nothing was changed, because maybe // Update() was only called to re-send the old geometry and time/slice data. this->SendCreatedWorldGeometry(); this->SendSlice(); this->SendTime(); // Adjust the stepper range of slice stepper according to geometry this->AdjustSliceStepperRange(); } void SliceNavigationController::SendCreatedWorldGeometry() { // Send the geometry. Do this even if nothing was changed, because maybe // Update() was only called to re-send the old geometry. if (!m_BlockUpdate) { this->InvokeEvent(GeometrySendEvent(m_CreatedWorldGeometry, 0)); } } void SliceNavigationController::SendCreatedWorldGeometryUpdate() { if (!m_BlockUpdate) { this->InvokeEvent(GeometryUpdateEvent(m_CreatedWorldGeometry, m_Slice->GetPos())); } } void SliceNavigationController::SendSlice() { if (!m_BlockUpdate) { if (m_CreatedWorldGeometry.IsNotNull()) { this->InvokeEvent(GeometrySliceEvent(m_CreatedWorldGeometry, m_Slice->GetPos())); // send crosshair event crosshairPositionEvent.Send(); // Request rendering update for all views this->GetRenderingManager()->RequestUpdateAll(); } } } void SliceNavigationController::SendTime() { if (!m_BlockUpdate) { if (m_CreatedWorldGeometry.IsNotNull()) { this->InvokeEvent(GeometryTimeEvent(m_CreatedWorldGeometry, m_Time->GetPos())); // Request rendering update for all views this->GetRenderingManager()->RequestUpdateAll(); } } } void SliceNavigationController::SetGeometry(const itk::EventObject &) {} void SliceNavigationController::SetGeometryTime(const itk::EventObject &geometryTimeEvent) { if (m_CreatedWorldGeometry.IsNull()) { return; } - const SliceNavigationController::GeometryTimeEvent *timeEvent = + const auto *timeEvent = dynamic_cast< const SliceNavigationController::GeometryTimeEvent * >(&geometryTimeEvent); assert( timeEvent != nullptr ); TimeGeometry *timeGeometry = timeEvent->GetTimeGeometry(); assert( timeGeometry != nullptr ); - int timeStep = (int)timeEvent->GetPos(); + auto timeStep = (int)timeEvent->GetPos(); ScalarType timeInMS; timeInMS = timeGeometry->TimeStepToTimePoint(timeStep); timeStep = m_CreatedWorldGeometry->TimePointToTimeStep(timeInMS); this->GetTime()->SetPos(timeStep); } void SliceNavigationController::SetGeometrySlice(const itk::EventObject &geometrySliceEvent) { - const SliceNavigationController::GeometrySliceEvent *sliceEvent = + const auto *sliceEvent = dynamic_cast(&geometrySliceEvent); assert(sliceEvent!=nullptr); this->GetSlice()->SetPos(sliceEvent->GetPos()); } void SliceNavigationController::SelectSliceByPoint(const Point3D &point) { if (m_CreatedWorldGeometry.IsNull()) { return; } //@todo add time to PositionEvent and use here!! SlicedGeometry3D *slicedWorldGeometry = dynamic_cast( m_CreatedWorldGeometry->GetGeometryForTimeStep(this->GetTime()->GetPos()).GetPointer()); if (slicedWorldGeometry) { int bestSlice = -1; double bestDistance = itk::NumericTraits::max(); int s, slices; slices = slicedWorldGeometry->GetSlices(); if (slicedWorldGeometry->GetEvenlySpaced()) { mitk::PlaneGeometry *plane = slicedWorldGeometry->GetPlaneGeometry(0); const Vector3D &direction = slicedWorldGeometry->GetDirectionVector(); Point3D projectedPoint; plane->Project(point, projectedPoint); // Check whether the point is somewhere within the slice stack volume; // otherwise, the default slice (0) will be selected if (direction[0] * (point[0] - projectedPoint[0]) + direction[1] * (point[1] - projectedPoint[1]) + direction[2] * (point[2] - projectedPoint[2]) >= 0) { bestSlice = (int)(plane->Distance(point) / slicedWorldGeometry->GetSpacing()[2] + 0.5); } } else { Point3D projectedPoint; for (s = 0; s < slices; ++s) { slicedWorldGeometry->GetPlaneGeometry(s)->Project(point, projectedPoint); const Vector3D distance = projectedPoint - point; ScalarType currentDistance = distance.GetSquaredNorm(); if (currentDistance < bestDistance) { bestDistance = currentDistance; bestSlice = s; } } } if (bestSlice >= 0) { this->GetSlice()->SetPos(bestSlice); } else { this->GetSlice()->SetPos(0); } this->SendCreatedWorldGeometryUpdate(); } } void SliceNavigationController::ReorientSlices(const Point3D &point, const Vector3D &normal) { if (m_CreatedWorldGeometry.IsNull()) { return; } PlaneOperation op(OpORIENT, point, normal); m_CreatedWorldGeometry->ExecuteOperation(&op); this->SendCreatedWorldGeometryUpdate(); } void SliceNavigationController::ReorientSlices(const mitk::Point3D &point, const mitk::Vector3D &axisVec0, const mitk::Vector3D &axisVec1) { if (m_CreatedWorldGeometry) { PlaneOperation op(OpORIENT, point, axisVec0, axisVec1); m_CreatedWorldGeometry->ExecuteOperation(&op); this->SendCreatedWorldGeometryUpdate(); } } mitk::TimeGeometry *SliceNavigationController::GetCreatedWorldGeometry() { return m_CreatedWorldGeometry; } const mitk::BaseGeometry *SliceNavigationController::GetCurrentGeometry3D() { if (m_CreatedWorldGeometry.IsNotNull()) { return m_CreatedWorldGeometry->GetGeometryForTimeStep(this->GetTime()->GetPos()); } else { return nullptr; } } const mitk::PlaneGeometry *SliceNavigationController::GetCurrentPlaneGeometry() { - const mitk::SlicedGeometry3D *slicedGeometry = + const auto *slicedGeometry = dynamic_cast(this->GetCurrentGeometry3D()); if (slicedGeometry) { const mitk::PlaneGeometry *planeGeometry = (slicedGeometry->GetPlaneGeometry(this->GetSlice()->GetPos())); return planeGeometry; } else { return nullptr; } } void SliceNavigationController::SetRenderer(BaseRenderer *renderer) { m_Renderer = renderer; } BaseRenderer *SliceNavigationController::GetRenderer() const { return m_Renderer; } void SliceNavigationController::AdjustSliceStepperRange() { - const mitk::SlicedGeometry3D *slicedGeometry = + const auto *slicedGeometry = dynamic_cast(this->GetCurrentGeometry3D()); const Vector3D &direction = slicedGeometry->GetDirectionVector(); int c = 0; int i, k = 0; for (i = 0; i < 3; ++i) { if (fabs(direction[i]) < 0.000000001) { ++c; } else { k = i; } } if (c == 2) { ScalarType min = slicedGeometry->GetOrigin()[k]; ScalarType max = min + slicedGeometry->GetExtentInMM(k); m_Slice->SetRange(min, max); } else { m_Slice->InvalidateRange(); } } void SliceNavigationController::ExecuteOperation(Operation *operation) { // switch on type // - select best slice for a given point // - rotate created world geometry according to Operation->SomeInfo() if (!operation || m_CreatedWorldGeometry.IsNull()) { return; } switch (operation->GetOperationType()) { case OpMOVE: // should be a point operation { if (!m_SliceLocked) // do not move the cross position { // select a slice - PointOperation *po = dynamic_cast(operation); + auto *po = dynamic_cast(operation); if (po && po->GetIndex() == -1) { this->SelectSliceByPoint(po->GetPoint()); } else if (po && po->GetIndex() != -1) // undo case because index != -1, index holds the old position of this slice { this->GetSlice()->SetPos(po->GetIndex()); } } break; } case OpRESTOREPLANEPOSITION: { m_CreatedWorldGeometry->ExecuteOperation(operation); this->SendCreatedWorldGeometryUpdate(); break; } case OpAPPLYTRANSFORMMATRIX: { m_CreatedWorldGeometry->ExecuteOperation(operation); this->SendCreatedWorldGeometryUpdate(); break; } default: { // do nothing break; } } } } // namespace diff --git a/Modules/Core/src/Controllers/mitkVtkLayerController.cpp b/Modules/Core/src/Controllers/mitkVtkLayerController.cpp index 9d26c07a0b..1a2c9f7438 100644 --- a/Modules/Core/src/Controllers/mitkVtkLayerController.cpp +++ b/Modules/Core/src/Controllers/mitkVtkLayerController.cpp @@ -1,337 +1,337 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkVtkLayerController.h" #include #include #include #include #include mitk::VtkLayerController::vtkLayerControllerMapType mitk::VtkLayerController::s_LayerControllerMap; mitk::VtkLayerController *mitk::VtkLayerController::GetInstance(vtkSmartPointer renWin) { for (auto mapit = s_LayerControllerMap.begin(); mapit != s_LayerControllerMap.end(); ++mapit) { if ((*mapit).first == renWin) return (*mapit).second; } return nullptr; } void mitk::VtkLayerController::AddInstance(vtkSmartPointer renWin, vtkSmartPointer mitkSceneRenderer) { // ensure that no vtkRenderWindow is managed twice mitk::VtkLayerController::RemoveInstance(renWin); // instanciate controller, add it to the map mitk::VtkLayerController *ControllerInstance = new mitk::VtkLayerController(renWin); ControllerInstance->InsertSceneRenderer(mitkSceneRenderer); s_LayerControllerMap.insert(vtkLayerControllerMapType::value_type(renWin, ControllerInstance)); } void mitk::VtkLayerController::RemoveInstance(vtkSmartPointer renWin) { auto mapit = s_LayerControllerMap.find(renWin); if (mapit != s_LayerControllerMap.end()) { delete mapit->second; s_LayerControllerMap.erase(mapit); } } mitk::VtkLayerController::VtkLayerController(vtkSmartPointer renderWindow) { m_RenderWindow = renderWindow; m_RenderWindow->Register(nullptr); m_BackgroundRenderers.clear(); m_ForegroundRenderers.clear(); m_SceneRenderers.clear(); } mitk::VtkLayerController::~VtkLayerController() { if (m_RenderWindow != nullptr) { m_RenderWindow->UnRegister(nullptr); } } /** * Connects a VTK renderer with a vtk renderwindow. The renderer will be rendered in the background. * With forceAbsoluteBackground set true a renderer can be placed at the absolute background of the scene. * Multiple calls with forceAbsoluteBackground set true will set the latest registered renderer as background. */ void mitk::VtkLayerController::InsertBackgroundRenderer(vtkSmartPointer renderer, bool forceAbsoluteBackground) { if (renderer == nullptr) return; // Remove renderer if it already exists RemoveRenderer(renderer); if (forceAbsoluteBackground) { auto it = m_BackgroundRenderers.begin(); m_BackgroundRenderers.insert(it, renderer); } else m_BackgroundRenderers.push_back(renderer); UpdateLayers(); } /** * Connects a VTK renderer with a vtk renderwindow. The renderer will be rendered in the foreground. * With forceAbsoluteBackground set true a renderer can be placed at the absolute foreground of the scene. * Multiple calls with forceAbsoluteForeground set true will set the latest registered renderer as foreground. */ void mitk::VtkLayerController::InsertForegroundRenderer(vtkSmartPointer renderer, bool forceAbsoluteForeground) { if (renderer == nullptr) return; // Remove renderer if it already exists RemoveRenderer(renderer); if (forceAbsoluteForeground) { auto it = m_ForegroundRenderers.begin(); m_ForegroundRenderers.insert(it, renderer); } else m_ForegroundRenderers.push_back(renderer); renderer->PreserveDepthBufferOn(); UpdateLayers(); } /** * Returns the Scene Renderer */ vtkSmartPointer mitk::VtkLayerController::GetSceneRenderer() { if (m_SceneRenderers.size() > 0) { auto it = m_SceneRenderers.begin(); return (*it); } else return nullptr; } /** * Connects a VTK renderer with a vtk renderwindow. The renderer will be rendered between background renderers and * foreground renderers. */ void mitk::VtkLayerController::InsertSceneRenderer(vtkSmartPointer renderer) { if (renderer == nullptr) return; // Remove renderer if it already exists RemoveRenderer(renderer); m_SceneRenderers.push_back(renderer); UpdateLayers(); } /** * A renderer which has been inserted via a insert... function can be removed from the vtkRenderWindow with * RemoveRenderer. */ void mitk::VtkLayerController::RemoveRenderer(vtkSmartPointer renderer) { RendererVectorType::iterator it; // background layers if (m_BackgroundRenderers.size() > 0) { it = std::find(m_BackgroundRenderers.begin(), m_BackgroundRenderers.end(), renderer); if (it != m_BackgroundRenderers.end()) { m_BackgroundRenderers.erase(it); UpdateLayers(); return; } } // scene layers if (m_SceneRenderers.size() > 0) { it = std::find(m_SceneRenderers.begin(), m_SceneRenderers.end(), renderer); if (it != m_SceneRenderers.end()) { m_SceneRenderers.erase(it); UpdateLayers(); return; } } // foreground layers if (m_ForegroundRenderers.size() > 0) { it = std::find(m_ForegroundRenderers.begin(), m_ForegroundRenderers.end(), renderer); if (it != m_ForegroundRenderers.end()) { m_ForegroundRenderers.erase(it); UpdateLayers(); return; } } } /** * Connects a VtkRenderWindow with the layer controller. */ void mitk::VtkLayerController::SetRenderWindow(vtkSmartPointer renwin) { if (renwin != nullptr) { RendererVectorType::iterator it; // Tell all renderers that there is a new renderwindow for (it = m_BackgroundRenderers.begin(); it != m_BackgroundRenderers.end(); ++it) { (*it)->SetRenderWindow(renwin); } for (it = m_SceneRenderers.begin(); it != m_SceneRenderers.end(); ++it) { (*it)->SetRenderWindow(renwin); } for (it = m_ForegroundRenderers.begin(); it != m_ForegroundRenderers.end(); ++it) { (*it)->SetRenderWindow(renwin); } // Set the new RenderWindow m_RenderWindow = renwin; } // Now sort renderers and add them to the renderwindow UpdateLayers(); } /** * Returns true if a renderer has been inserted */ bool mitk::VtkLayerController::IsRendererInserted(vtkSmartPointer renderer) { RendererVectorType::iterator it; // background layers if (m_BackgroundRenderers.size() > 0) { it = std::find(m_BackgroundRenderers.begin(), m_BackgroundRenderers.end(), renderer); if (it != m_BackgroundRenderers.end()) { return true; } } // scene layers if (m_SceneRenderers.size() > 0) { it = std::find(m_SceneRenderers.begin(), m_SceneRenderers.end(), renderer); if (it != m_SceneRenderers.end()) { return true; } } // foreground layers if (m_ForegroundRenderers.size() > 0) { it = std::find(m_ForegroundRenderers.begin(), m_ForegroundRenderers.end(), renderer); if (it != m_ForegroundRenderers.end()) { return true; } } return false; } /** * Internally used to sort all registered renderers and to connect the with the vtkRenderWindow. * Mention that VTK Version 5 and above is rendering higher numbers in the background and VTK * Verison < 5 in the foreground. */ void mitk::VtkLayerController::UpdateLayers() { // Remove all Renderers from renderwindow vtkSmartPointer v = m_RenderWindow->GetRenderers(); v->RemoveAllItems(); - unsigned int numberOfLayers = + auto numberOfLayers = static_cast(m_BackgroundRenderers.size() + m_SceneRenderers.size() + m_ForegroundRenderers.size()); int currentLayerNumber; bool traverseUpwards; currentLayerNumber = 0; traverseUpwards = true; m_RenderWindow->SetNumberOfLayers(numberOfLayers); RendererVectorType::iterator it; // assign a layer number for the backround renderers for (it = m_BackgroundRenderers.begin(); it != m_BackgroundRenderers.end(); ++it) { (*it)->SetRenderWindow(m_RenderWindow); (*it)->SetLayer(currentLayerNumber); m_RenderWindow->AddRenderer((*it)); if (traverseUpwards) currentLayerNumber++; else currentLayerNumber--; } // assign a layer number for the scene renderers for (it = m_SceneRenderers.begin(); it != m_SceneRenderers.end(); ++it) { (*it)->SetRenderWindow(m_RenderWindow); (*it)->SetLayer(currentLayerNumber); m_RenderWindow->AddRenderer((*it)); if (traverseUpwards) currentLayerNumber++; else currentLayerNumber--; } // assign a layer number for the foreground renderers for (it = m_ForegroundRenderers.begin(); it != m_ForegroundRenderers.end(); ++it) { (*it)->SetRenderWindow(m_RenderWindow); (*it)->SetLayer(currentLayerNumber); m_RenderWindow->AddRenderer((*it)); if (traverseUpwards) currentLayerNumber++; else currentLayerNumber--; } } /** * Returns the number of renderers in the renderwindow. */ unsigned int mitk::VtkLayerController::GetNumberOfRenderers() { return static_cast(m_BackgroundRenderers.size() + m_SceneRenderers.size() + m_ForegroundRenderers.size()); } void mitk::VtkLayerController::SetEraseForAllRenderers(int i) { this->m_RenderWindow->SetErase(i); RendererVectorType::iterator it; for (it = m_BackgroundRenderers.begin(); it != m_BackgroundRenderers.end(); ++it) (*it)->SetErase(i); for (it = m_SceneRenderers.begin(); it != m_SceneRenderers.end(); ++it) (*it)->SetErase(i); for (it = m_ForegroundRenderers.begin(); it != m_ForegroundRenderers.end(); ++it) (*it)->SetErase(i); } diff --git a/Modules/Core/src/DataManagement/mitkBaseData.cpp b/Modules/Core/src/DataManagement/mitkBaseData.cpp index 14e8ed7cdf..d93356e5f1 100644 --- a/Modules/Core/src/DataManagement/mitkBaseData.cpp +++ b/Modules/Core/src/DataManagement/mitkBaseData.cpp @@ -1,289 +1,289 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkBaseData.h" #include #include #include #include mitk::BaseData::BaseData() : m_SourceOutputIndexDuplicate(0), m_Initialized(true) { m_TimeGeometry = mitk::ProportionalTimeGeometry::New(); m_PropertyList = PropertyList::New(); } mitk::BaseData::BaseData(const BaseData &other) : itk::DataObject(), mitk::OperationActor(), m_SourceOutputIndexDuplicate(other.m_SourceOutputIndexDuplicate), m_Initialized(other.m_Initialized) { m_TimeGeometry = dynamic_cast(other.m_TimeGeometry->Clone().GetPointer()); m_PropertyList = other.m_PropertyList->Clone(); } mitk::BaseData::~BaseData() { } void mitk::BaseData::InitializeTimeGeometry(unsigned int timeSteps) { mitk::Geometry3D::Pointer geo3D = mitk::Geometry3D::New(); mitk::BaseGeometry::Pointer baseGeo = dynamic_cast(geo3D.GetPointer()); baseGeo->Initialize(); // The geometry is propagated automatically to the other items, // if EvenlyTimed is true... // Old timeGeometry->InitializeEvenlyTimed( g3d.GetPointer(), timeSteps ); TimeGeometry::Pointer timeGeometry = this->GetTimeGeometry(); timeGeometry->Initialize(); timeGeometry->Expand(timeSteps); for (TimeStepType step = 0; step < timeSteps; ++step) { timeGeometry->SetTimeStepGeometry(baseGeo.GetPointer(), step); } } void mitk::BaseData::UpdateOutputInformation() { if (this->GetSource()) { this->GetSource()->UpdateOutputInformation(); } if (m_TimeGeometry.IsNotNull()) { m_TimeGeometry->UpdateBoundingBox(); } } const mitk::TimeGeometry *mitk::BaseData::GetUpdatedTimeGeometry() { SetRequestedRegionToLargestPossibleRegion(); UpdateOutputInformation(); return GetTimeGeometry(); } void mitk::BaseData::Expand(unsigned int timeSteps) { if (m_TimeGeometry.IsNotNull()) { m_TimeGeometry->Expand(timeSteps); } else { this->InitializeTimeGeometry(timeSteps); } } const mitk::BaseGeometry *mitk::BaseData::GetUpdatedGeometry(int t) { SetRequestedRegionToLargestPossibleRegion(); UpdateOutputInformation(); return GetGeometry(t); } void mitk::BaseData::SetGeometry(BaseGeometry *geometry) { ProportionalTimeGeometry::Pointer timeGeometry = ProportionalTimeGeometry::New(); if (geometry != nullptr) { timeGeometry->Initialize(geometry, 1); } SetTimeGeometry(timeGeometry); return; } void mitk::BaseData::SetTimeGeometry(TimeGeometry *geometry) { m_TimeGeometry = geometry; this->Modified(); } void mitk::BaseData::SetClonedGeometry(const BaseGeometry *aGeometry3D) { SetGeometry(static_cast(aGeometry3D->Clone().GetPointer())); } void mitk::BaseData::SetClonedTimeGeometry(const TimeGeometry *geometry) { TimeGeometry::Pointer clonedGeometry = geometry->Clone(); SetTimeGeometry(clonedGeometry.GetPointer()); } void mitk::BaseData::SetClonedGeometry(const BaseGeometry *aGeometry3D, unsigned int time) { if (m_TimeGeometry) { m_TimeGeometry->SetTimeStepGeometry(static_cast(aGeometry3D->Clone().GetPointer()), time); } } bool mitk::BaseData::IsEmptyTimeStep(unsigned int) const { return IsInitialized() == false; } bool mitk::BaseData::IsEmpty() const { if (IsInitialized() == false) return true; const TimeGeometry *timeGeometry = const_cast(this)->GetUpdatedTimeGeometry(); if (timeGeometry == nullptr) return true; unsigned int timeSteps = timeGeometry->CountTimeSteps(); for (unsigned int t = 0; t < timeSteps; ++t) { if (IsEmptyTimeStep(t) == false) return false; } return true; } itk::SmartPointer mitk::BaseData::GetSource() const { return static_cast(Superclass::GetSource().GetPointer()); } mitk::PropertyList::Pointer mitk::BaseData::GetPropertyList() const { return m_PropertyList; } mitk::BaseProperty::Pointer mitk::BaseData::GetProperty(const char *propertyKey) const { return m_PropertyList->GetProperty(propertyKey); } void mitk::BaseData::SetProperty(const char *propertyKey, BaseProperty *propertyValue) { m_PropertyList->SetProperty(propertyKey, propertyValue); } void mitk::BaseData::SetPropertyList(PropertyList *pList) { m_PropertyList = pList; } void mitk::BaseData::SetOrigin(const mitk::Point3D &origin) { TimeGeometry *timeGeom = GetTimeGeometry(); assert(timeGeom != nullptr); TimeStepType steps = timeGeom->CountTimeSteps(); for (TimeStepType timestep = 0; timestep < steps; ++timestep) { auto geometry = GetGeometry(timestep); if (geometry != nullptr) { geometry->SetOrigin(origin); } } } unsigned long mitk::BaseData::GetMTime() const { unsigned long time = Superclass::GetMTime(); if (m_TimeGeometry.IsNotNull()) { if ((time < m_TimeGeometry->GetMTime())) { Modified(); return Superclass::GetMTime(); } } return time; } void mitk::BaseData::Graft(const itk::DataObject *) { itkExceptionMacro(<< "Graft not implemented for mitk::BaseData subclass " << this->GetNameOfClass()) } void mitk::BaseData::CopyInformation(const itk::DataObject *data) { - const Self *bd = dynamic_cast(data); + const auto *bd = dynamic_cast(data); if (bd != nullptr) { m_PropertyList = bd->GetPropertyList()->Clone(); if (bd->GetTimeGeometry() != nullptr) { m_TimeGeometry = bd->GetTimeGeometry()->Clone(); } } else { // pointer could not be cast back down; this can be the case if your filters input // and output objects differ in type; then you have to write your own GenerateOutputInformation method itkExceptionMacro(<< "mitk::BaseData::CopyInformation() cannot cast " << typeid(data).name() << " to " << typeid(Self *).name()); } } bool mitk::BaseData::IsInitialized() const { return m_Initialized; } void mitk::BaseData::Clear() { this->ClearData(); this->InitializeEmpty(); } void mitk::BaseData::ClearData() { if (m_Initialized) { ReleaseData(); m_Initialized = false; } } void mitk::BaseData::ExecuteOperation(mitk::Operation * /*operation*/) { // empty by default. override if needed! } void mitk::BaseData::PrintSelf(std::ostream &os, itk::Indent indent) const { os << std::endl; os << indent << " TimeGeometry: "; if (GetTimeGeometry() == nullptr) os << "nullptr" << std::endl; else GetTimeGeometry()->Print(os, indent); // print out all properties PropertyList::Pointer propertyList = this->GetPropertyList(); if (propertyList.IsNotNull() && !propertyList->IsEmpty()) { // general headline os << "Properties of BaseData:" << std::endl; const PropertyList::PropertyMap *map = propertyList->GetMap(); - for (PropertyList::PropertyMap::const_iterator iter = map->begin(); iter != map->end(); ++iter) + for (auto iter = map->begin(); iter != map->end(); ++iter) { os << " " << (*iter).first << " " << (*iter).second->GetValueAsString() << std::endl; } } } diff --git a/Modules/Core/src/DataManagement/mitkBaseGeometry.cpp b/Modules/Core/src/DataManagement/mitkBaseGeometry.cpp index 91aa7fbbf9..329e1aa10a 100644 --- a/Modules/Core/src/DataManagement/mitkBaseGeometry.cpp +++ b/Modules/Core/src/DataManagement/mitkBaseGeometry.cpp @@ -1,1007 +1,1007 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include #include #include #include #include "mitkApplyTransformMatrixOperation.h" #include "mitkBaseGeometry.h" #include "mitkGeometryTransformHolder.h" #include "mitkInteractionConst.h" #include "mitkMatrixConvert.h" #include "mitkModifiedLock.h" #include "mitkPointOperation.h" #include "mitkRestorePlanePositionOperation.h" #include "mitkRotationOperation.h" #include "mitkScaleOperation.h" #include "mitkVector.h" mitk::BaseGeometry::BaseGeometry() : Superclass(), mitk::OperationActor(), m_FrameOfReferenceID(0), m_IndexToWorldTransformLastModified(0), m_ImageGeometry(false), m_ModifiedLockFlag(false), m_ModifiedCalledFlag(false) { m_GeometryTransform = new GeometryTransformHolder(); Initialize(); } mitk::BaseGeometry::BaseGeometry(const BaseGeometry &other) : Superclass(), mitk::OperationActor(), m_FrameOfReferenceID(other.m_FrameOfReferenceID), m_IndexToWorldTransformLastModified(other.m_IndexToWorldTransformLastModified), m_ImageGeometry(other.m_ImageGeometry), m_ModifiedLockFlag(false), m_ModifiedCalledFlag(false) { m_GeometryTransform = new GeometryTransformHolder(*other.GetGeometryTransformHolder()); other.InitializeGeometry(this); } mitk::BaseGeometry::~BaseGeometry() { delete m_GeometryTransform; } void mitk::BaseGeometry::SetVtkMatrixDeepCopy(vtkTransform *vtktransform) { m_GeometryTransform->SetVtkMatrixDeepCopy(vtktransform); } const mitk::Point3D mitk::BaseGeometry::GetOrigin() const { return m_GeometryTransform->GetOrigin(); } void mitk::BaseGeometry::SetOrigin(const Point3D &origin) { mitk::ModifiedLock lock(this); if (origin != GetOrigin()) { m_GeometryTransform->SetOrigin(origin); Modified(); } } const mitk::Vector3D mitk::BaseGeometry::GetSpacing() const { return m_GeometryTransform->GetSpacing(); } void mitk::BaseGeometry::Initialize() { float b[6] = {0, 1, 0, 1, 0, 1}; SetFloatBounds(b); m_GeometryTransform->Initialize(); m_FrameOfReferenceID = 0; m_ImageGeometry = false; } void mitk::BaseGeometry::SetFloatBounds(const float bounds[6]) { mitk::BoundingBox::BoundsArrayType b; const float *input = bounds; int i = 0; for (mitk::BoundingBox::BoundsArrayType::Iterator it = b.Begin(); i < 6; ++i) *it++ = (mitk::ScalarType)*input++; SetBounds(b); } void mitk::BaseGeometry::SetFloatBounds(const double bounds[6]) { mitk::BoundingBox::BoundsArrayType b; const double *input = bounds; int i = 0; for (mitk::BoundingBox::BoundsArrayType::Iterator it = b.Begin(); i < 6; ++i) *it++ = (mitk::ScalarType)*input++; SetBounds(b); } /** Initialize the geometry */ void mitk::BaseGeometry::InitializeGeometry(BaseGeometry *newGeometry) const { newGeometry->SetBounds(m_BoundingBox->GetBounds()); newGeometry->SetFrameOfReferenceID(GetFrameOfReferenceID()); newGeometry->InitializeGeometryTransformHolder(this); newGeometry->m_ImageGeometry = m_ImageGeometry; } void mitk::BaseGeometry::InitializeGeometryTransformHolder(const BaseGeometry *otherGeometry) { this->m_GeometryTransform->Initialize(otherGeometry->GetGeometryTransformHolder()); } /** Set the bounds */ void mitk::BaseGeometry::SetBounds(const BoundsArrayType &bounds) { mitk::ModifiedLock lock(this); this->CheckBounds(bounds); m_BoundingBox = BoundingBoxType::New(); BoundingBoxType::PointsContainer::Pointer pointscontainer = BoundingBoxType::PointsContainer::New(); BoundingBoxType::PointType p; BoundingBoxType::PointIdentifier pointid; for (pointid = 0; pointid < 2; ++pointid) { unsigned int i; for (i = 0; i < m_NDimensions; ++i) { p[i] = bounds[2 * i + pointid]; } pointscontainer->InsertElement(pointid, p); } m_BoundingBox->SetPoints(pointscontainer); m_BoundingBox->ComputeBoundingBox(); this->Modified(); } void mitk::BaseGeometry::SetIndexToWorldTransform(mitk::AffineTransform3D *transform) { mitk::ModifiedLock lock(this); CheckIndexToWorldTransform(transform); m_GeometryTransform->SetIndexToWorldTransform(transform); Modified(); } void mitk::BaseGeometry::SetIndexToWorldTransformWithoutChangingSpacing(mitk::AffineTransform3D *transform) { // security check mitk::Vector3D originalSpacing = this->GetSpacing(); mitk::ModifiedLock lock(this); CheckIndexToWorldTransform(transform); m_GeometryTransform->SetIndexToWorldTransformWithoutChangingSpacing(transform); Modified(); // Security check. Spacig must not have changed if (!mitk::Equal(originalSpacing, this->GetSpacing())) { MITK_WARN << "Spacing has changed in a method, where the spacing must not change."; assert(false); } } const mitk::BaseGeometry::BoundsArrayType mitk::BaseGeometry::GetBounds() const { assert(m_BoundingBox.IsNotNull()); return m_BoundingBox->GetBounds(); } bool mitk::BaseGeometry::IsValid() const { return true; } void mitk::BaseGeometry::SetSpacing(const mitk::Vector3D &aSpacing, bool enforceSetSpacing) { PreSetSpacing(aSpacing); _SetSpacing(aSpacing, enforceSetSpacing); } void mitk::BaseGeometry::_SetSpacing(const mitk::Vector3D &aSpacing, bool enforceSetSpacing) { m_GeometryTransform->SetSpacing(aSpacing, enforceSetSpacing); } mitk::Vector3D mitk::BaseGeometry::GetAxisVector(unsigned int direction) const { Vector3D frontToBack; frontToBack.SetVnlVector(this->GetIndexToWorldTransform()->GetMatrix().GetVnlMatrix().get_column(direction)); frontToBack *= GetExtent(direction); return frontToBack; } mitk::ScalarType mitk::BaseGeometry::GetExtent(unsigned int direction) const { assert(m_BoundingBox.IsNotNull()); if (direction >= m_NDimensions) mitkThrow() << "Direction is too big. This geometry is for 3D Data"; BoundsArrayType bounds = m_BoundingBox->GetBounds(); return bounds[direction * 2 + 1] - bounds[direction * 2]; } bool mitk::BaseGeometry::Is2DConvertable() { bool isConvertableWithoutLoss = true; do { if (this->GetSpacing()[2] != 1) { isConvertableWithoutLoss = false; break; } if (this->GetOrigin()[2] != 0) { isConvertableWithoutLoss = false; break; } mitk::Vector3D col0, col1, col2; col0.SetVnlVector(this->GetIndexToWorldTransform()->GetMatrix().GetVnlMatrix().get_column(0)); col1.SetVnlVector(this->GetIndexToWorldTransform()->GetMatrix().GetVnlMatrix().get_column(1)); col2.SetVnlVector(this->GetIndexToWorldTransform()->GetMatrix().GetVnlMatrix().get_column(2)); if ((col0[2] != 0) || (col1[2] != 0) || (col2[0] != 0) || (col2[1] != 0) || (col2[2] != 1)) { isConvertableWithoutLoss = false; break; } } while (0); return isConvertableWithoutLoss; } mitk::Point3D mitk::BaseGeometry::GetCenter() const { assert(m_BoundingBox.IsNotNull()); Point3D c = m_BoundingBox->GetCenter(); if (m_ImageGeometry) { // Get Center returns the middel of min and max pixel index. In corner based images, this is the right position. // In center based images (imageGeometry == true), the index needs to be shifted back. c[0] -= 0.5; c[1] -= 0.5; c[2] -= 0.5; } this->IndexToWorld(c, c); return c; } double mitk::BaseGeometry::GetDiagonalLength2() const { Vector3D diagonalvector = GetCornerPoint() - GetCornerPoint(false, false, false); return diagonalvector.GetSquaredNorm(); } double mitk::BaseGeometry::GetDiagonalLength() const { return sqrt(GetDiagonalLength2()); } mitk::Point3D mitk::BaseGeometry::GetCornerPoint(int id) const { assert(id >= 0); assert(this->IsBoundingBoxNull() == false); BoundingBox::BoundsArrayType bounds = this->GetBoundingBox()->GetBounds(); Point3D cornerpoint; switch (id) { case 0: FillVector3D(cornerpoint, bounds[0], bounds[2], bounds[4]); break; case 1: FillVector3D(cornerpoint, bounds[0], bounds[2], bounds[5]); break; case 2: FillVector3D(cornerpoint, bounds[0], bounds[3], bounds[4]); break; case 3: FillVector3D(cornerpoint, bounds[0], bounds[3], bounds[5]); break; case 4: FillVector3D(cornerpoint, bounds[1], bounds[2], bounds[4]); break; case 5: FillVector3D(cornerpoint, bounds[1], bounds[2], bounds[5]); break; case 6: FillVector3D(cornerpoint, bounds[1], bounds[3], bounds[4]); break; case 7: FillVector3D(cornerpoint, bounds[1], bounds[3], bounds[5]); break; default: { itkExceptionMacro(<< "A cube only has 8 corners. These are labeled 0-7."); } } if (m_ImageGeometry) { // Here i have to adjust the 0.5 offset manually, because the cornerpoint is the corner of the // bounding box. The bounding box itself is no image, so it is corner-based FillVector3D(cornerpoint, cornerpoint[0] - 0.5, cornerpoint[1] - 0.5, cornerpoint[2] - 0.5); } return this->GetIndexToWorldTransform()->TransformPoint(cornerpoint); } mitk::Point3D mitk::BaseGeometry::GetCornerPoint(bool xFront, bool yFront, bool zFront) const { assert(this->IsBoundingBoxNull() == false); BoundingBox::BoundsArrayType bounds = this->GetBoundingBox()->GetBounds(); Point3D cornerpoint; cornerpoint[0] = (xFront ? bounds[0] : bounds[1]); cornerpoint[1] = (yFront ? bounds[2] : bounds[3]); cornerpoint[2] = (zFront ? bounds[4] : bounds[5]); if (m_ImageGeometry) { // Here i have to adjust the 0.5 offset manually, because the cornerpoint is the corner of the // bounding box. The bounding box itself is no image, so it is corner-based FillVector3D(cornerpoint, cornerpoint[0] - 0.5, cornerpoint[1] - 0.5, cornerpoint[2] - 0.5); } return this->GetIndexToWorldTransform()->TransformPoint(cornerpoint); } mitk::ScalarType mitk::BaseGeometry::GetExtentInMM(int direction) const { return this->GetIndexToWorldTransform()->GetMatrix().GetVnlMatrix().get_column(direction).magnitude() * GetExtent(direction); } void mitk::BaseGeometry::SetExtentInMM(int direction, ScalarType extentInMM) { mitk::ModifiedLock lock(this); ScalarType len = GetExtentInMM(direction); if (fabs(len - extentInMM) >= mitk::eps) { AffineTransform3D::MatrixType::InternalMatrixType vnlmatrix; vnlmatrix = m_GeometryTransform->GetVnlMatrix(); if (len > extentInMM) vnlmatrix.set_column(direction, vnlmatrix.get_column(direction) / len * extentInMM); else vnlmatrix.set_column(direction, vnlmatrix.get_column(direction) * extentInMM / len); Matrix3D matrix; matrix = vnlmatrix; m_GeometryTransform->SetMatrix(matrix); Modified(); } } bool mitk::BaseGeometry::IsInside(const mitk::Point3D &p) const { mitk::Point3D index; WorldToIndex(p, index); return IsIndexInside(index); } bool mitk::BaseGeometry::IsIndexInside(const mitk::Point3D &index) const { bool inside = false; // if it is an image geometry, we need to convert the index to discrete values // this is done by applying the rounding function also used in WorldToIndex (see line 323) if (m_ImageGeometry) { mitk::Point3D discretIndex; discretIndex[0] = itk::Math::RoundHalfIntegerUp(index[0]); discretIndex[1] = itk::Math::RoundHalfIntegerUp(index[1]); discretIndex[2] = itk::Math::RoundHalfIntegerUp(index[2]); inside = this->GetBoundingBox()->IsInside(discretIndex); // we have to check if the index is at the upper border of each dimension, // because the boundingbox is not centerbased if (inside) { const BoundingBox::BoundsArrayType &bounds = this->GetBoundingBox()->GetBounds(); if ((discretIndex[0] == bounds[1]) || (discretIndex[1] == bounds[3]) || (discretIndex[2] == bounds[5])) inside = false; } } else inside = this->GetBoundingBox()->IsInside(index); return inside; } void mitk::BaseGeometry::WorldToIndex(const mitk::Point3D &pt_mm, mitk::Point3D &pt_units) const { mitk::Vector3D tempIn, tempOut; const TransformType::OffsetType &offset = this->GetIndexToWorldTransform()->GetOffset(); tempIn = pt_mm.GetVectorFromOrigin() - offset; WorldToIndex(tempIn, tempOut); pt_units = tempOut; } void mitk::BaseGeometry::WorldToIndex(const mitk::Vector3D &vec_mm, mitk::Vector3D &vec_units) const { // Get WorldToIndex transform if (m_IndexToWorldTransformLastModified != this->GetIndexToWorldTransform()->GetMTime()) { if (!m_InvertedTransform) { m_InvertedTransform = TransformType::New(); } if (!this->GetIndexToWorldTransform()->GetInverse(m_InvertedTransform.GetPointer())) { itkExceptionMacro("Internal ITK matrix inversion error, cannot proceed."); } m_IndexToWorldTransformLastModified = this->GetIndexToWorldTransform()->GetMTime(); } // Check for valid matrix inversion const TransformType::MatrixType &inverse = m_InvertedTransform->GetMatrix(); if (inverse.GetVnlMatrix().has_nans()) { itkExceptionMacro("Internal ITK matrix inversion error, cannot proceed. Matrix was: " << std::endl << this->GetIndexToWorldTransform()->GetMatrix() << "Suggested inverted matrix is:" << std::endl << inverse); } vec_units = inverse * vec_mm; } void mitk::BaseGeometry::WorldToIndex(const mitk::Point3D & /*atPt3d_mm*/, const mitk::Vector3D &vec_mm, mitk::Vector3D &vec_units) const { MITK_WARN << "Warning! Call of the deprecated function BaseGeometry::WorldToIndex(point, vec, vec). Use " "BaseGeometry::WorldToIndex(vec, vec) instead!"; this->WorldToIndex(vec_mm, vec_units); } mitk::VnlVector mitk::BaseGeometry::GetOriginVnl() const { return const_cast(this)->GetOrigin().GetVnlVector(); } vtkLinearTransform *mitk::BaseGeometry::GetVtkTransform() const { return m_GeometryTransform->GetVtkTransform(); } void mitk::BaseGeometry::SetIdentity() { mitk::ModifiedLock lock(this); m_GeometryTransform->SetIdentity(); Modified(); } void mitk::BaseGeometry::Compose(const mitk::BaseGeometry::TransformType *other, bool pre) { mitk::ModifiedLock lock(this); m_GeometryTransform->Compose(other, pre); Modified(); } void mitk::BaseGeometry::Compose(const vtkMatrix4x4 *vtkmatrix, bool pre) { mitk::BaseGeometry::TransformType::Pointer itkTransform = mitk::BaseGeometry::TransformType::New(); TransferVtkMatrixToItkTransform(vtkmatrix, itkTransform.GetPointer()); Compose(itkTransform, pre); } void mitk::BaseGeometry::Translate(const Vector3D &vector) { if ((vector[0] != 0) || (vector[1] != 0) || (vector[2] != 0)) { this->SetOrigin(this->GetOrigin() + vector); } } void mitk::BaseGeometry::IndexToWorld(const mitk::Point3D &pt_units, mitk::Point3D &pt_mm) const { pt_mm = this->GetIndexToWorldTransform()->TransformPoint(pt_units); } void mitk::BaseGeometry::IndexToWorld(const mitk::Vector3D &vec_units, mitk::Vector3D &vec_mm) const { vec_mm = this->GetIndexToWorldTransform()->TransformVector(vec_units); } void mitk::BaseGeometry::ExecuteOperation(Operation *operation) { mitk::ModifiedLock lock(this); vtkTransform *vtktransform = vtkTransform::New(); vtktransform->SetMatrix(this->GetVtkMatrix()); switch (operation->GetOperationType()) { case OpNOTHING: break; case OpMOVE: { - mitk::PointOperation *pointOp = dynamic_cast(operation); + auto *pointOp = dynamic_cast(operation); if (pointOp == nullptr) { MITK_ERROR << "Point move operation is null!"; return; } mitk::Point3D newPos = pointOp->GetPoint(); ScalarType data[3]; vtktransform->GetPosition(data); vtktransform->PostMultiply(); vtktransform->Translate(newPos[0], newPos[1], newPos[2]); vtktransform->PreMultiply(); break; } case OpSCALE: { - mitk::ScaleOperation *scaleOp = dynamic_cast(operation); + auto *scaleOp = dynamic_cast(operation); if (scaleOp == nullptr) { MITK_ERROR << "Scale operation is null!"; return; } mitk::Point3D newScale = scaleOp->GetScaleFactor(); ScalarType scalefactor[3]; scalefactor[0] = 1 + (newScale[0] / GetMatrixColumn(0).magnitude()); scalefactor[1] = 1 + (newScale[1] / GetMatrixColumn(1).magnitude()); scalefactor[2] = 1 + (newScale[2] / GetMatrixColumn(2).magnitude()); mitk::Point3D anchor = scaleOp->GetScaleAnchorPoint(); vtktransform->PostMultiply(); vtktransform->Translate(-anchor[0], -anchor[1], -anchor[2]); vtktransform->Scale(scalefactor[0], scalefactor[1], scalefactor[2]); vtktransform->Translate(anchor[0], anchor[1], anchor[2]); break; } case OpROTATE: { - mitk::RotationOperation *rotateOp = dynamic_cast(operation); + auto *rotateOp = dynamic_cast(operation); if (rotateOp == nullptr) { MITK_ERROR << "Rotation operation is null!"; return; } Vector3D rotationVector = rotateOp->GetVectorOfRotation(); Point3D center = rotateOp->GetCenterOfRotation(); ScalarType angle = rotateOp->GetAngleOfRotation(); vtktransform->PostMultiply(); vtktransform->Translate(-center[0], -center[1], -center[2]); vtktransform->RotateWXYZ(angle, rotationVector[0], rotationVector[1], rotationVector[2]); vtktransform->Translate(center[0], center[1], center[2]); vtktransform->PreMultiply(); break; } case OpRESTOREPLANEPOSITION: { // Copy necessary to avoid vtk warning vtkMatrix4x4 *matrix = vtkMatrix4x4::New(); TransferItkTransformToVtkMatrix( dynamic_cast(operation)->GetTransform().GetPointer(), matrix); vtktransform->SetMatrix(matrix); matrix->Delete(); break; } case OpAPPLYTRANSFORMMATRIX: { - ApplyTransformMatrixOperation *applyMatrixOp = dynamic_cast(operation); + auto *applyMatrixOp = dynamic_cast(operation); vtktransform->SetMatrix(applyMatrixOp->GetMatrix()); break; } default: vtktransform->Delete(); return; } this->SetVtkMatrixDeepCopy(vtktransform); Modified(); vtktransform->Delete(); } mitk::VnlVector mitk::BaseGeometry::GetMatrixColumn(unsigned int direction) const { return this->GetIndexToWorldTransform()->GetMatrix().GetVnlMatrix().get_column(direction); } mitk::BoundingBox::Pointer mitk::BaseGeometry::CalculateBoundingBoxRelativeToTransform( const mitk::AffineTransform3D *transform) const { mitk::BoundingBox::PointsContainer::Pointer pointscontainer = mitk::BoundingBox::PointsContainer::New(); mitk::BoundingBox::PointIdentifier pointid = 0; unsigned char i; if (transform != nullptr) { mitk::AffineTransform3D::Pointer inverse = mitk::AffineTransform3D::New(); transform->GetInverse(inverse); for (i = 0; i < 8; ++i) pointscontainer->InsertElement(pointid++, inverse->TransformPoint(GetCornerPoint(i))); } else { for (i = 0; i < 8; ++i) pointscontainer->InsertElement(pointid++, GetCornerPoint(i)); } mitk::BoundingBox::Pointer result = mitk::BoundingBox::New(); result->SetPoints(pointscontainer); result->ComputeBoundingBox(); return result; } const std::string mitk::BaseGeometry::GetTransformAsString(TransformType *transformType) { std::ostringstream out; out << '['; for (int i = 0; i < 3; ++i) { out << '['; for (int j = 0; j < 3; ++j) out << transformType->GetMatrix().GetVnlMatrix().get(i, j) << ' '; out << ']'; } out << "]["; for (int i = 0; i < 3; ++i) out << transformType->GetOffset()[i] << ' '; out << "]\0"; return out.str(); } void mitk::BaseGeometry::SetIndexToWorldTransformByVtkMatrix(vtkMatrix4x4 *vtkmatrix) { m_GeometryTransform->SetIndexToWorldTransformByVtkMatrix(vtkmatrix); } void mitk::BaseGeometry::SetIndexToWorldTransformByVtkMatrixWithoutChangingSpacing(vtkMatrix4x4 *vtkmatrix) { m_GeometryTransform->SetIndexToWorldTransformByVtkMatrixWithoutChangingSpacing(vtkmatrix); } void mitk::BaseGeometry::IndexToWorld(const mitk::Point3D & /*atPt3d_units*/, const mitk::Vector3D &vec_units, mitk::Vector3D &vec_mm) const { MITK_WARN << "Warning! Call of the deprecated function BaseGeometry::IndexToWorld(point, vec, vec). Use " "BaseGeometry::IndexToWorld(vec, vec) instead!"; // vec_mm = m_IndexToWorldTransform->TransformVector(vec_units); this->IndexToWorld(vec_units, vec_mm); } vtkMatrix4x4 *mitk::BaseGeometry::GetVtkMatrix() { return m_GeometryTransform->GetVtkMatrix(); } bool mitk::BaseGeometry::IsBoundingBoxNull() const { return m_BoundingBox.IsNull(); } bool mitk::BaseGeometry::IsIndexToWorldTransformNull() const { return m_GeometryTransform->IsIndexToWorldTransformNull(); } void mitk::BaseGeometry::ChangeImageGeometryConsideringOriginOffset(const bool isAnImageGeometry) { // If Geometry is switched to ImageGeometry, you have to put an offset to the origin, because // imageGeometries origins are pixel-center-based // ... and remove the offset, if you switch an imageGeometry back to a normal geometry // For more information please see the Geometry documentation page if (m_ImageGeometry == isAnImageGeometry) return; const BoundingBox::BoundsArrayType &boundsarray = this->GetBoundingBox()->GetBounds(); Point3D originIndex; FillVector3D(originIndex, boundsarray[0], boundsarray[2], boundsarray[4]); if (isAnImageGeometry == true) FillVector3D(originIndex, originIndex[0] + 0.5, originIndex[1] + 0.5, originIndex[2] + 0.5); else FillVector3D(originIndex, originIndex[0] - 0.5, originIndex[1] - 0.5, originIndex[2] - 0.5); Point3D originWorld; originWorld = GetIndexToWorldTransform()->TransformPoint(originIndex); // instead could as well call IndexToWorld(originIndex,originWorld); SetOrigin(originWorld); this->SetImageGeometry(isAnImageGeometry); } void mitk::BaseGeometry::PrintSelf(std::ostream &os, itk::Indent indent) const { os << indent << " IndexToWorldTransform: "; if (this->IsIndexToWorldTransformNull()) os << "nullptr" << std::endl; else { // from itk::MatrixOffsetTransformBase unsigned int i, j; os << std::endl; os << indent << "Matrix: " << std::endl; for (i = 0; i < 3; i++) { os << indent.GetNextIndent(); for (j = 0; j < 3; j++) { os << this->GetIndexToWorldTransform()->GetMatrix()[i][j] << " "; } os << std::endl; } os << indent << "Offset: " << this->GetIndexToWorldTransform()->GetOffset() << std::endl; os << indent << "Center: " << this->GetIndexToWorldTransform()->GetCenter() << std::endl; os << indent << "Translation: " << this->GetIndexToWorldTransform()->GetTranslation() << std::endl; os << indent << "Inverse: " << std::endl; for (i = 0; i < 3; i++) { os << indent.GetNextIndent(); for (j = 0; j < 3; j++) { os << this->GetIndexToWorldTransform()->GetInverseMatrix()[i][j] << " "; } os << std::endl; } // from itk::ScalableAffineTransform os << indent << "Scale : "; for (i = 0; i < 3; i++) { os << this->GetIndexToWorldTransform()->GetScale()[i] << " "; } os << std::endl; } os << indent << " BoundingBox: "; if (this->IsBoundingBoxNull()) os << "nullptr" << std::endl; else { os << indent << "( "; for (unsigned int i = 0; i < 3; i++) { os << this->GetBoundingBox()->GetBounds()[2 * i] << "," << this->GetBoundingBox()->GetBounds()[2 * i + 1] << " "; } os << " )" << std::endl; } os << indent << " Origin: " << this->GetOrigin() << std::endl; os << indent << " ImageGeometry: " << this->GetImageGeometry() << std::endl; os << indent << " Spacing: " << this->GetSpacing() << std::endl; } void mitk::BaseGeometry::Modified() const { if (!m_ModifiedLockFlag) Superclass::Modified(); else m_ModifiedCalledFlag = true; } mitk::AffineTransform3D *mitk::BaseGeometry::GetIndexToWorldTransform() { return m_GeometryTransform->GetIndexToWorldTransform(); } const mitk::AffineTransform3D *mitk::BaseGeometry::GetIndexToWorldTransform() const { return m_GeometryTransform->GetIndexToWorldTransform(); } const mitk::GeometryTransformHolder *mitk::BaseGeometry::GetGeometryTransformHolder() const { return m_GeometryTransform; } bool mitk::Equal(const mitk::BaseGeometry::BoundingBoxType *leftHandSide, const mitk::BaseGeometry::BoundingBoxType *rightHandSide, ScalarType eps, bool verbose) { if ((leftHandSide == nullptr) || (rightHandSide == nullptr)) { MITK_ERROR << "mitk::Equal( const mitk::Geometry3D::BoundingBoxType *leftHandSide, const " "mitk::Geometry3D::BoundingBoxType *rightHandSide, ScalarType eps, bool verbose ) does not with nullptr " "pointer input."; return false; } return Equal(*leftHandSide, *rightHandSide, eps, verbose); } bool mitk::Equal(const mitk::BaseGeometry::BoundingBoxType &leftHandSide, const mitk::BaseGeometry::BoundingBoxType &rightHandSide, ScalarType eps, bool verbose) { bool result = true; BaseGeometry::BoundsArrayType rightBounds = rightHandSide.GetBounds(); BaseGeometry::BoundsArrayType leftBounds = leftHandSide.GetBounds(); BaseGeometry::BoundsArrayType::Iterator itLeft = leftBounds.Begin(); for (BaseGeometry::BoundsArrayType::Iterator itRight = rightBounds.Begin(); itRight != rightBounds.End(); ++itRight) { if ((!mitk::Equal(*itLeft, *itRight, eps))) { if (verbose) { MITK_INFO << "[( Geometry3D::BoundingBoxType )] bounds are not equal."; MITK_INFO << "rightHandSide is " << setprecision(12) << *itRight << " : leftHandSide is " << *itLeft << " and tolerance is " << eps; } result = false; } itLeft++; } return result; } bool mitk::Equal(const mitk::BaseGeometry *leftHandSide, const mitk::BaseGeometry *rightHandSide, ScalarType eps, bool verbose) { if ((leftHandSide == nullptr) || (rightHandSide == nullptr)) { MITK_ERROR << "mitk::Equal(const mitk::Geometry3D *leftHandSide, const mitk::Geometry3D *rightHandSide, ScalarType " "eps, bool verbose) does not with nullptr pointer input."; return false; } return Equal(*leftHandSide, *rightHandSide, eps, verbose); } bool mitk::Equal(const mitk::BaseGeometry &leftHandSide, const mitk::BaseGeometry &rightHandSide, ScalarType eps, bool verbose) { bool result = true; // Compare spacings if (!mitk::Equal(leftHandSide.GetSpacing(), rightHandSide.GetSpacing(), eps)) { if (verbose) { MITK_INFO << "[( Geometry3D )] Spacing differs."; MITK_INFO << "rightHandSide is " << setprecision(12) << rightHandSide.GetSpacing() << " : leftHandSide is " << leftHandSide.GetSpacing() << " and tolerance is " << eps; } result = false; } // Compare Origins if (!mitk::Equal(leftHandSide.GetOrigin(), rightHandSide.GetOrigin(), eps)) { if (verbose) { MITK_INFO << "[( Geometry3D )] Origin differs."; MITK_INFO << "rightHandSide is " << setprecision(12) << rightHandSide.GetOrigin() << " : leftHandSide is " << leftHandSide.GetOrigin() << " and tolerance is " << eps; } result = false; } // Compare Axis and Extents for (unsigned int i = 0; i < 3; ++i) { if (!mitk::Equal(leftHandSide.GetAxisVector(i), rightHandSide.GetAxisVector(i), eps)) { if (verbose) { MITK_INFO << "[( Geometry3D )] AxisVector #" << i << " differ"; MITK_INFO << "rightHandSide is " << setprecision(12) << rightHandSide.GetAxisVector(i) << " : leftHandSide is " << leftHandSide.GetAxisVector(i) << " and tolerance is " << eps; } result = false; } if (!mitk::Equal(leftHandSide.GetExtent(i), rightHandSide.GetExtent(i), eps)) { if (verbose) { MITK_INFO << "[( Geometry3D )] Extent #" << i << " differ"; MITK_INFO << "rightHandSide is " << setprecision(12) << rightHandSide.GetExtent(i) << " : leftHandSide is " << leftHandSide.GetExtent(i) << " and tolerance is " << eps; } result = false; } } // Compare ImageGeometry Flag if (rightHandSide.GetImageGeometry() != leftHandSide.GetImageGeometry()) { if (verbose) { MITK_INFO << "[( Geometry3D )] GetImageGeometry is different."; MITK_INFO << "rightHandSide is " << rightHandSide.GetImageGeometry() << " : leftHandSide is " << leftHandSide.GetImageGeometry(); } result = false; } // Compare FrameOfReference ID if (rightHandSide.GetFrameOfReferenceID() != leftHandSide.GetFrameOfReferenceID()) { if (verbose) { MITK_INFO << "[( Geometry3D )] GetFrameOfReferenceID is different."; MITK_INFO << "rightHandSide is " << rightHandSide.GetFrameOfReferenceID() << " : leftHandSide is " << leftHandSide.GetFrameOfReferenceID(); } result = false; } // Compare BoundingBoxes if (!mitk::Equal(*leftHandSide.GetBoundingBox(), *rightHandSide.GetBoundingBox(), eps, verbose)) { result = false; } // Compare IndexToWorldTransform Matrix if (!mitk::Equal(*leftHandSide.GetIndexToWorldTransform(), *rightHandSide.GetIndexToWorldTransform(), eps, verbose)) { result = false; } return result; } bool mitk::Equal(const mitk::BaseGeometry::TransformType *leftHandSide, const mitk::BaseGeometry::TransformType *rightHandSide, ScalarType eps, bool verbose) { if ((leftHandSide == nullptr) || (rightHandSide == nullptr)) { MITK_ERROR << "mitk::Equal(const Geometry3D::TransformType *leftHandSide, const Geometry3D::TransformType " "*rightHandSide, ScalarType eps, bool verbose ) does not with nullptr pointer input."; return false; } return Equal(*leftHandSide, *rightHandSide, eps, verbose); } bool mitk::Equal(const mitk::BaseGeometry::TransformType &leftHandSide, const mitk::BaseGeometry::TransformType &rightHandSide, ScalarType eps, bool verbose) { // Compare IndexToWorldTransform Matrix if (!mitk::MatrixEqualElementWise(leftHandSide.GetMatrix(), rightHandSide.GetMatrix(), eps)) { if (verbose) { MITK_INFO << "[( Geometry3D::TransformType )] Index to World Transformation matrix differs."; MITK_INFO << "rightHandSide is " << setprecision(12) << rightHandSide.GetMatrix() << " : leftHandSide is " << leftHandSide.GetMatrix() << " and tolerance is " << eps; } return false; } return true; } diff --git a/Modules/Core/src/DataManagement/mitkDataNode.cpp b/Modules/Core/src/DataManagement/mitkDataNode.cpp index b856974e72..0105ad141a 100644 --- a/Modules/Core/src/DataManagement/mitkDataNode.cpp +++ b/Modules/Core/src/DataManagement/mitkDataNode.cpp @@ -1,599 +1,599 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkDataNode.h" #include "mitkCoreObjectFactory.h" #include #include "mitkGroupTagProperty.h" #include "mitkProperties.h" #include "mitkSmartPointerProperty.h" #include "mitkStringProperty.h" //#include "mitkMaterialProperty.h" #include "mitkColorProperty.h" #include "mitkCoreObjectFactory.h" #include "mitkGenericProperty.h" #include "mitkGeometry3D.h" #include "mitkImageSource.h" #include "mitkLevelWindowProperty.h" #include "mitkRenderingManager.h" mitk::Mapper *mitk::DataNode::GetMapper(MapperSlotId id) const { if ((id >= m_Mappers.size()) || (m_Mappers[id].IsNull())) { if (id >= m_Mappers.capacity()) { // int i, size=id-m_Mappers.capacity()+10; m_Mappers.resize(id + 10); } m_Mappers[id] = CoreObjectFactory::GetInstance()->CreateMapper(const_cast(this), id); } return m_Mappers[id]; } mitk::BaseData *mitk::DataNode::GetData() const { return m_Data; } void mitk::DataNode::SetData(mitk::BaseData *baseData) { if (m_Data != baseData) { m_Mappers.clear(); m_Mappers.resize(10); if (m_Data.IsNotNull() && baseData != nullptr) { // Do previous and new data have same type? Keep existing properties. if (0 == strcmp(m_Data->GetNameOfClass(), baseData->GetNameOfClass())) { m_Data = baseData; } else { m_Data = baseData; this->GetPropertyList()->Clear(); mitk::CoreObjectFactory::GetInstance()->SetDefaultProperties(this); } } else { m_Data = baseData; mitk::CoreObjectFactory::GetInstance()->SetDefaultProperties(this); } m_DataReferenceChangedTime.Modified(); Modified(); } } mitk::DataNode::DataNode() : m_PropertyListModifiedObserverTag(0) { m_Mappers.resize(10); m_PropertyList = PropertyList::New(); // subscribe for modified event itk::MemberCommand::Pointer _PropertyListModifiedCommand = itk::MemberCommand::New(); _PropertyListModifiedCommand->SetCallbackFunction(this, &mitk::DataNode::PropertyListModified); m_PropertyListModifiedObserverTag = m_PropertyList->AddObserver(itk::ModifiedEvent(), _PropertyListModifiedCommand); } mitk::DataNode::~DataNode() { if (m_PropertyList.IsNotNull()) m_PropertyList->RemoveObserver(m_PropertyListModifiedObserverTag); m_Mappers.clear(); m_Data = nullptr; } mitk::DataNode &mitk::DataNode::operator=(const DataNode &right) { mitk::DataNode *node = mitk::DataNode::New(); node->SetData(right.GetData()); return *node; } mitk::DataNode &mitk::DataNode::operator=(mitk::BaseData *right) { mitk::DataNode *node = mitk::DataNode::New(); node->SetData(right); return *node; } #if (_MSC_VER > 1200) || !defined(_MSC_VER) MBI_STD::istream &mitk::operator>>(MBI_STD::istream &i, mitk::DataNode::Pointer &dtn) #endif #if ((defined(_MSC_VER)) && (_MSC_VER <= 1200)) MBI_STD::istream & operator>>(MBI_STD::istream &i, mitk::DataNode::Pointer &dtn) #endif { dtn = mitk::DataNode::New(); // i >> av.get(); return i; } #if (_MSC_VER > 1200) || !defined(_MSC_VER) MBI_STD::ostream &mitk::operator<<(MBI_STD::ostream &o, mitk::DataNode::Pointer &dtn) #endif #if ((defined(_MSC_VER)) && (_MSC_VER <= 1200)) MBI_STD::ostream & operator<<(MBI_STD::ostream &o, mitk::DataNode::Pointer &dtn) #endif { if (dtn->GetData() != nullptr) o << dtn->GetData()->GetNameOfClass(); else o << "empty data"; return o; } void mitk::DataNode::SetMapper(MapperSlotId id, mitk::Mapper *mapper) { m_Mappers[id] = mapper; if (mapper != nullptr) mapper->SetDataNode(this); } void mitk::DataNode::UpdateOutputInformation() { if (this->GetSource()) { this->GetSource()->UpdateOutputInformation(); } } void mitk::DataNode::SetRequestedRegionToLargestPossibleRegion() { } bool mitk::DataNode::RequestedRegionIsOutsideOfTheBufferedRegion() { return false; } bool mitk::DataNode::VerifyRequestedRegion() { return true; } void mitk::DataNode::SetRequestedRegion(const itk::DataObject * /*data*/) { } mitk::DataNode::PropertyListKeyNames mitk::DataNode::GetPropertyListNames() const { PropertyListKeyNames result; for (auto entries : m_MapOfPropertyLists) result.push_back(entries.first); return result; } void mitk::DataNode::CopyInformation(const itk::DataObject * /*data*/) { } mitk::PropertyList *mitk::DataNode::GetPropertyList(const mitk::BaseRenderer *renderer) const { if (renderer == nullptr) return m_PropertyList; return this->GetPropertyList(renderer->GetName()); } mitk::PropertyList *mitk::DataNode::GetPropertyList(const std::string &rendererName) const { if (rendererName.empty()) return m_PropertyList; mitk::PropertyList::Pointer &propertyList = m_MapOfPropertyLists[rendererName]; if (propertyList.IsNull()) propertyList = mitk::PropertyList::New(); assert(m_MapOfPropertyLists[rendererName].IsNotNull()); return propertyList; } void mitk::DataNode::ConcatenatePropertyList(PropertyList *pList, bool replace) { m_PropertyList->ConcatenatePropertyList(pList, replace); } mitk::BaseProperty *mitk::DataNode::GetProperty(const char *propertyKey, const mitk::BaseRenderer *renderer) const { if (propertyKey == nullptr) return nullptr; // renderer specified? if (renderer) { std::string rendererName = renderer->GetName(); MapOfPropertyLists::const_iterator it; // check for the renderer specific property it = m_MapOfPropertyLists.find(rendererName); if (it != m_MapOfPropertyLists.end()) // found { mitk::BaseProperty::Pointer property; property = it->second->GetProperty(propertyKey); if (property.IsNotNull()) // found an enabled property in the render specific list return property; else // found a renderer specific list, but not the desired property return m_PropertyList->GetProperty(propertyKey); // return renderer unspecific property } else // didn't find the property list of the given renderer { // return the renderer unspecific property if there is one return m_PropertyList->GetProperty(propertyKey); } } else // no specific renderer given; use the renderer independent one { mitk::BaseProperty::Pointer property; property = m_PropertyList->GetProperty(propertyKey); if (property.IsNotNull()) return property; } // only to satisfy compiler! return nullptr; } mitk::DataNode::GroupTagList mitk::DataNode::GetGroupTags() const { GroupTagList groups; const PropertyList::PropertyMap *propertyMap = m_PropertyList->GetMap(); - for (PropertyList::PropertyMap::const_iterator groupIter = + for (auto groupIter = propertyMap->begin(); // m_PropertyList is created in the constructor, so we don't check it here groupIter != propertyMap->end(); ++groupIter) { const BaseProperty *bp = groupIter->second; if (dynamic_cast(bp)) { groups.insert(groupIter->first); } } return groups; } bool mitk::DataNode::GetBoolProperty(const char *propertyKey, bool &boolValue, const mitk::BaseRenderer *renderer) const { mitk::BoolProperty::Pointer boolprop = dynamic_cast(GetProperty(propertyKey, renderer)); if (boolprop.IsNull()) return false; boolValue = boolprop->GetValue(); return true; } bool mitk::DataNode::GetIntProperty(const char *propertyKey, int &intValue, const mitk::BaseRenderer *renderer) const { mitk::IntProperty::Pointer intprop = dynamic_cast(GetProperty(propertyKey, renderer)); if (intprop.IsNull()) return false; intValue = intprop->GetValue(); return true; } bool mitk::DataNode::GetFloatProperty(const char *propertyKey, float &floatValue, const mitk::BaseRenderer *renderer) const { mitk::FloatProperty::Pointer floatprop = dynamic_cast(GetProperty(propertyKey, renderer)); if (floatprop.IsNull()) return false; floatValue = floatprop->GetValue(); return true; } bool mitk::DataNode::GetDoubleProperty(const char *propertyKey, double &doubleValue, const mitk::BaseRenderer *renderer) const { mitk::DoubleProperty::Pointer doubleprop = dynamic_cast(GetProperty(propertyKey, renderer)); if (doubleprop.IsNull()) { // try float instead float floatValue = 0; if (this->GetFloatProperty(propertyKey, floatValue, renderer)) { doubleValue = floatValue; return true; } return false; } doubleValue = doubleprop->GetValue(); return true; } bool mitk::DataNode::GetStringProperty(const char *propertyKey, std::string &string, const mitk::BaseRenderer *renderer) const { mitk::StringProperty::Pointer stringProp = dynamic_cast(GetProperty(propertyKey, renderer)); if (stringProp.IsNull()) { return false; } else { // memcpy((void*)string, stringProp->GetValue(), strlen(stringProp->GetValue()) + 1 ); // looks dangerous string = stringProp->GetValue(); return true; } } bool mitk::DataNode::GetColor(float rgb[3], const mitk::BaseRenderer *renderer, const char *propertyKey) const { mitk::ColorProperty::Pointer colorprop = dynamic_cast(GetProperty(propertyKey, renderer)); if (colorprop.IsNull()) return false; memcpy(rgb, colorprop->GetColor().GetDataPointer(), 3 * sizeof(float)); return true; } bool mitk::DataNode::GetOpacity(float &opacity, const mitk::BaseRenderer *renderer, const char *propertyKey) const { mitk::FloatProperty::Pointer opacityprop = dynamic_cast(GetProperty(propertyKey, renderer)); if (opacityprop.IsNull()) return false; opacity = opacityprop->GetValue(); return true; } bool mitk::DataNode::GetLevelWindow(mitk::LevelWindow &levelWindow, const mitk::BaseRenderer *renderer, const char *propertyKey) const { mitk::LevelWindowProperty::Pointer levWinProp = dynamic_cast(GetProperty(propertyKey, renderer)); if (levWinProp.IsNull()) return false; levelWindow = levWinProp->GetLevelWindow(); return true; } void mitk::DataNode::SetColor(const mitk::Color &color, const mitk::BaseRenderer *renderer, const char *propertyKey) { mitk::ColorProperty::Pointer prop; prop = mitk::ColorProperty::New(color); GetPropertyList(renderer)->SetProperty(propertyKey, prop); } void mitk::DataNode::SetColor( float red, float green, float blue, const mitk::BaseRenderer *renderer, const char *propertyKey) { float color[3]; color[0] = red; color[1] = green; color[2] = blue; SetColor(color, renderer, propertyKey); } void mitk::DataNode::SetColor(const float rgb[3], const mitk::BaseRenderer *renderer, const char *propertyKey) { mitk::ColorProperty::Pointer prop; prop = mitk::ColorProperty::New(rgb); GetPropertyList(renderer)->SetProperty(propertyKey, prop); } void mitk::DataNode::SetVisibility(bool visible, const mitk::BaseRenderer *renderer, const char *propertyKey) { mitk::BoolProperty::Pointer prop; prop = mitk::BoolProperty::New(visible); GetPropertyList(renderer)->SetProperty(propertyKey, prop); } void mitk::DataNode::SetOpacity(float opacity, const mitk::BaseRenderer *renderer, const char *propertyKey) { mitk::FloatProperty::Pointer prop; prop = mitk::FloatProperty::New(opacity); GetPropertyList(renderer)->SetProperty(propertyKey, prop); } void mitk::DataNode::SetLevelWindow(mitk::LevelWindow levelWindow, const mitk::BaseRenderer *renderer, const char *propertyKey) { mitk::LevelWindowProperty::Pointer prop; prop = mitk::LevelWindowProperty::New(levelWindow); GetPropertyList(renderer)->SetProperty(propertyKey, prop); } void mitk::DataNode::SetIntProperty(const char *propertyKey, int intValue, const mitk::BaseRenderer *renderer) { GetPropertyList(renderer)->SetProperty(propertyKey, mitk::IntProperty::New(intValue)); } void mitk::DataNode::SetBoolProperty(const char *propertyKey, bool boolValue, const mitk::BaseRenderer *renderer /*=nullptr*/) { GetPropertyList(renderer)->SetProperty(propertyKey, mitk::BoolProperty::New(boolValue)); } void mitk::DataNode::SetFloatProperty(const char *propertyKey, float floatValue, const mitk::BaseRenderer *renderer /*=nullptr*/) { if (dynamic_cast(this->GetProperty(propertyKey, renderer)) != nullptr) { MITK_WARN << "Setting float property " << propertyKey << " although a double property with the same name already exists"; } GetPropertyList(renderer)->SetProperty(propertyKey, mitk::FloatProperty::New(floatValue)); } void mitk::DataNode::SetDoubleProperty(const char *propertyKey, double doubleValue, const mitk::BaseRenderer *renderer) { if (dynamic_cast(this->GetProperty(propertyKey, renderer)) != nullptr) { MITK_WARN << "Setting double property " << propertyKey << " although a float property with the same name already exists"; } GetPropertyList(renderer)->SetProperty(propertyKey, mitk::DoubleProperty::New(doubleValue)); } void mitk::DataNode::SetStringProperty(const char *propertyKey, const char *stringValue, const mitk::BaseRenderer *renderer /*=nullptr*/) { GetPropertyList(renderer)->SetProperty(propertyKey, mitk::StringProperty::New(stringValue)); } void mitk::DataNode::SetProperty(const char *propertyKey, BaseProperty *propertyValue, const mitk::BaseRenderer *renderer) { GetPropertyList(renderer)->SetProperty(propertyKey, propertyValue); } void mitk::DataNode::ReplaceProperty(const char *propertyKey, BaseProperty *propertyValue, const mitk::BaseRenderer *renderer) { GetPropertyList(renderer)->ReplaceProperty(propertyKey, propertyValue); } void mitk::DataNode::AddProperty(const char *propertyKey, BaseProperty *propertyValue, const mitk::BaseRenderer *renderer, bool overwrite) { if ((overwrite) || (GetProperty(propertyKey, renderer) == nullptr)) { SetProperty(propertyKey, propertyValue, renderer); } } vtkLinearTransform *mitk::DataNode::GetVtkTransform(int t) const { assert(m_Data.IsNotNull()); mitk::BaseGeometry *geometry = m_Data->GetGeometry(t); if (geometry == nullptr) return nullptr; return geometry->GetVtkTransform(); } unsigned long mitk::DataNode::GetMTime() const { unsigned long time = Superclass::GetMTime(); if (m_Data.IsNotNull()) { if ((time < m_Data->GetMTime()) || ((m_Data->GetSource().IsNotNull()) && (time < m_Data->GetSource()->GetMTime()))) { Modified(); return Superclass::GetMTime(); } } return time; } void mitk::DataNode::SetSelected(bool selected, const mitk::BaseRenderer *renderer) { mitk::BoolProperty::Pointer selectedProperty = dynamic_cast(GetProperty("selected")); if (selectedProperty.IsNull()) { selectedProperty = mitk::BoolProperty::New(); selectedProperty->SetValue(false); SetProperty("selected", selectedProperty, renderer); } if (selectedProperty->GetValue() != selected) { selectedProperty->SetValue(selected); itk::ModifiedEvent event; InvokeEvent(event); } } /* class SelectedEvent : public itk::ModifiedEvent { public: typedef SelectedEvent Self; typedef itk::ModifiedEvent Superclass; SelectedEvent(DataNode* dataNode) { m_DataNode = dataNode; }; DataNode* GetDataNode() { return m_DataNode; }; virtual const char * GetEventName() const { return "SelectedEvent"; } virtual bool CheckEvent(const ::itk::EventObject* e) const { return dynamic_cast(e); } virtual ::itk::EventObject* MakeObject() const { return new Self(m_DataNode); } private: DataNode* m_DataNode; SelectedEvent(const Self& event) { m_DataNode = event.m_DataNode; }; void operator=(const Self& event) { m_DataNode = event.m_DataNode; } }; */ bool mitk::DataNode::IsSelected(const mitk::BaseRenderer *renderer) { bool selected; if (!GetBoolProperty("selected", selected, renderer)) return false; return selected; } void mitk::DataNode::SetDataInteractor(const DataInteractor::Pointer interactor) { if (m_DataInteractor == interactor) return; m_DataInteractor = interactor; this->Modified(); mitk::DataNode::InteractorChangedEvent changedEvent; this->InvokeEvent(changedEvent); } mitk::DataInteractor::Pointer mitk::DataNode::GetDataInteractor() const { return m_DataInteractor; } void mitk::DataNode::PropertyListModified(const itk::Object * /*caller*/, const itk::EventObject &) { Modified(); } diff --git a/Modules/Core/src/DataManagement/mitkDataStorage.cpp b/Modules/Core/src/DataManagement/mitkDataStorage.cpp index daea549540..f548fb9ced 100644 --- a/Modules/Core/src/DataManagement/mitkDataStorage.cpp +++ b/Modules/Core/src/DataManagement/mitkDataStorage.cpp @@ -1,518 +1,518 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkDataStorage.h" #include "itkCommand.h" #include "itkMutexLockHolder.h" #include "mitkDataNode.h" #include "mitkGroupTagProperty.h" #include "mitkImage.h" #include "mitkNodePredicateBase.h" #include "mitkNodePredicateProperty.h" #include "mitkProperties.h" mitk::DataStorage::DataStorage() : itk::Object(), m_BlockNodeModifiedEvents(false) { } mitk::DataStorage::~DataStorage() { ///// we can not call GetAll() in destructor, because it is implemented in a subclass // SetOfObjects::ConstPointer all = this->GetAll(); // for (SetOfObjects::ConstIterator it = all->Begin(); it != all->End(); ++it) // this->RemoveListeners(it->Value()); // m_NodeModifiedObserverTags.clear(); // m_NodeDeleteObserverTags.clear(); } void mitk::DataStorage::Add(mitk::DataNode *node, mitk::DataNode *parent) { mitk::DataStorage::SetOfObjects::Pointer parents = mitk::DataStorage::SetOfObjects::New(); if (parent != nullptr) //< Return empty set if parent is null parents->InsertElement(0, parent); this->Add(node, parents); } void mitk::DataStorage::Remove(const mitk::DataStorage::SetOfObjects *nodes) { if (nodes == nullptr) return; for (mitk::DataStorage::SetOfObjects::ConstIterator it = nodes->Begin(); it != nodes->End(); it++) this->Remove(it.Value()); } mitk::DataStorage::SetOfObjects::ConstPointer mitk::DataStorage::GetSubset(const NodePredicateBase *condition) const { mitk::DataStorage::SetOfObjects::ConstPointer result = this->FilterSetOfObjects(this->GetAll(), condition); return result; } mitk::DataNode *mitk::DataStorage::GetNamedNode(const char *name) const { if (name == nullptr) return nullptr; mitk::StringProperty::Pointer s(mitk::StringProperty::New(name)); mitk::NodePredicateProperty::Pointer p = mitk::NodePredicateProperty::New("name", s); mitk::DataStorage::SetOfObjects::ConstPointer rs = this->GetSubset(p); if (rs->Size() >= 1) return rs->GetElement(0); else return nullptr; } mitk::DataNode *mitk::DataStorage::GetNode(const NodePredicateBase *condition) const { if (condition == nullptr) return nullptr; mitk::DataStorage::SetOfObjects::ConstPointer rs = this->GetSubset(condition); if (rs->Size() >= 1) return rs->GetElement(0); else return nullptr; } mitk::DataNode *mitk::DataStorage::GetNamedDerivedNode(const char *name, const mitk::DataNode *sourceNode, bool onlyDirectDerivations) const { if (name == nullptr) return nullptr; mitk::StringProperty::Pointer s(mitk::StringProperty::New(name)); mitk::NodePredicateProperty::Pointer p = mitk::NodePredicateProperty::New("name", s); mitk::DataStorage::SetOfObjects::ConstPointer rs = this->GetDerivations(sourceNode, p, onlyDirectDerivations); if (rs->Size() >= 1) return rs->GetElement(0); else return nullptr; } void mitk::DataStorage::PrintSelf(std::ostream &os, itk::Indent indent) const { // Superclass::PrintSelf(os, indent); mitk::DataStorage::SetOfObjects::ConstPointer all = this->GetAll(); os << indent << "DataStorage " << this << " is managing " << all->Size() << " objects. List of objects:" << std::endl; for (mitk::DataStorage::SetOfObjects::ConstIterator allIt = all->Begin(); allIt != all->End(); allIt++) { std::string name; allIt.Value()->GetName(name); std::string datatype; if (allIt.Value()->GetData() != nullptr) datatype = allIt.Value()->GetData()->GetNameOfClass(); os << indent << " " << allIt.Value().GetPointer() << "<" << datatype << ">: " << name << std::endl; mitk::DataStorage::SetOfObjects::ConstPointer parents = this->GetSources(allIt.Value()); if (parents->Size() > 0) { os << indent << " Direct sources: "; for (mitk::DataStorage::SetOfObjects::ConstIterator parentIt = parents->Begin(); parentIt != parents->End(); parentIt++) os << parentIt.Value().GetPointer() << ", "; os << std::endl; } mitk::DataStorage::SetOfObjects::ConstPointer derivations = this->GetDerivations(allIt.Value()); if (derivations->Size() > 0) { os << indent << " Direct derivations: "; for (mitk::DataStorage::SetOfObjects::ConstIterator derivationIt = derivations->Begin(); derivationIt != derivations->End(); derivationIt++) os << derivationIt.Value().GetPointer() << ", "; os << std::endl; } } os << std::endl; } mitk::DataStorage::SetOfObjects::ConstPointer mitk::DataStorage::FilterSetOfObjects( const SetOfObjects *set, const NodePredicateBase *condition) const { if (set == nullptr) return nullptr; mitk::DataStorage::SetOfObjects::Pointer result = mitk::DataStorage::SetOfObjects::New(); for (mitk::DataStorage::SetOfObjects::ConstIterator it = set->Begin(); it != set->End(); it++) if (condition == nullptr || condition->CheckNode(it.Value()) == true) // alway copy the set, otherwise the iterator in mitk::DataStorage::Remove() will crash result->InsertElement(result->Size(), it.Value()); return mitk::DataStorage::SetOfObjects::ConstPointer(result); } const mitk::DataNode::GroupTagList mitk::DataStorage::GetGroupTags() const { DataNode::GroupTagList result; SetOfObjects::ConstPointer all = this->GetAll(); if (all.IsNull()) return result; for (mitk::DataStorage::SetOfObjects::ConstIterator nodeIt = all->Begin(); nodeIt != all->End(); nodeIt++) // for each node { mitk::PropertyList *pl = nodeIt.Value()->GetPropertyList(); - for (mitk::PropertyList::PropertyMap::const_iterator propIt = pl->GetMap()->begin(); propIt != pl->GetMap()->end(); + for (auto propIt = pl->GetMap()->begin(); propIt != pl->GetMap()->end(); ++propIt) if (dynamic_cast(propIt->second.GetPointer()) != nullptr) result.insert(propIt->first); } return result; } void mitk::DataStorage::EmitAddNodeEvent(const mitk::DataNode *node) { AddNodeEvent.Send(node); } void mitk::DataStorage::EmitRemoveNodeEvent(const mitk::DataNode *node) { RemoveNodeEvent.Send(node); } void mitk::DataStorage::OnNodeInteractorChanged(itk::Object *caller, const itk::EventObject &) { - const mitk::DataNode *_Node = dynamic_cast(caller); + const auto *_Node = dynamic_cast(caller); if (_Node) { InteractorChangedNodeEvent.Send(_Node); } } void mitk::DataStorage::OnNodeModifiedOrDeleted(const itk::Object *caller, const itk::EventObject &event) { if (m_BlockNodeModifiedEvents) return; - const mitk::DataNode *_Node = dynamic_cast(caller); + const auto *_Node = dynamic_cast(caller); if (_Node) { - const itk::ModifiedEvent *modEvent = dynamic_cast(&event); + const auto *modEvent = dynamic_cast(&event); if (modEvent) ChangedNodeEvent.Send(_Node); else DeleteNodeEvent.Send(_Node); } } void mitk::DataStorage::AddListeners(const mitk::DataNode *_Node) { itk::MutexLockHolder locked(m_MutexOne); // node must not be 0 and must not be yet registered - mitk::DataNode *NonConstNode = const_cast(_Node); + auto *NonConstNode = const_cast(_Node); if (_Node && m_NodeModifiedObserverTags.find(NonConstNode) == m_NodeModifiedObserverTags.end()) { itk::MemberCommand::Pointer nodeModifiedCommand = itk::MemberCommand::New(); nodeModifiedCommand->SetCallbackFunction(this, &mitk::DataStorage::OnNodeModifiedOrDeleted); m_NodeModifiedObserverTags[NonConstNode] = NonConstNode->AddObserver(itk::ModifiedEvent(), nodeModifiedCommand); itk::MemberCommand::Pointer interactorChangedCommand = itk::MemberCommand::New(); interactorChangedCommand->SetCallbackFunction(this, &mitk::DataStorage::OnNodeInteractorChanged); m_NodeInteractorChangedObserverTags[NonConstNode] = NonConstNode->AddObserver(mitk::DataNode::InteractorChangedEvent(), interactorChangedCommand); // add itk delete listener on datastorage itk::MemberCommand::Pointer deleteCommand = itk::MemberCommand::New(); deleteCommand->SetCallbackFunction(this, &mitk::DataStorage::OnNodeModifiedOrDeleted); // add observer m_NodeDeleteObserverTags[NonConstNode] = NonConstNode->AddObserver(itk::DeleteEvent(), deleteCommand); } } void mitk::DataStorage::RemoveListeners(const mitk::DataNode *_Node) { itk::MutexLockHolder locked(m_MutexOne); // node must not be 0 and must be registered - mitk::DataNode *NonConstNode = const_cast(_Node); + auto *NonConstNode = const_cast(_Node); if (_Node && m_NodeModifiedObserverTags.find(NonConstNode) != m_NodeModifiedObserverTags.end()) { // const cast is bad! but sometimes it is necessary. removing an observer does not really // touch the internal state NonConstNode->RemoveObserver(m_NodeModifiedObserverTags.find(NonConstNode)->second); NonConstNode->RemoveObserver(m_NodeDeleteObserverTags.find(NonConstNode)->second); NonConstNode->RemoveObserver(m_NodeInteractorChangedObserverTags.find(NonConstNode)->second); m_NodeModifiedObserverTags.erase(NonConstNode); m_NodeDeleteObserverTags.erase(NonConstNode); m_NodeInteractorChangedObserverTags.erase(NonConstNode); } } mitk::TimeGeometry::Pointer mitk::DataStorage::ComputeBoundingGeometry3D(const SetOfObjects *input, const char *boolPropertyKey, const mitk::BaseRenderer *renderer, const char *boolPropertyKey2) const { if (input == nullptr) throw std::invalid_argument("DataStorage: input is invalid"); BoundingBox::PointsContainer::Pointer pointscontainer = BoundingBox::PointsContainer::New(); BoundingBox::PointIdentifier pointid = 0; Point3D point; Vector3D minSpacing; minSpacing.Fill(itk::NumericTraits::max()); ScalarType stmin, stmax; stmin = itk::NumericTraits::NonpositiveMin(); stmax = itk::NumericTraits::max(); ScalarType minimalIntervallSize = stmax; ScalarType minimalTime = stmax; ScalarType maximalTime = 0; // Needed for check of zero bounding boxes mitk::ScalarType nullpoint[] = {0, 0, 0, 0, 0, 0}; BoundingBox::BoundsArrayType itkBoundsZero(nullpoint); for (SetOfObjects::ConstIterator it = input->Begin(); it != input->End(); ++it) { DataNode::Pointer node = it->Value(); if ((node.IsNotNull()) && (node->GetData() != nullptr) && (node->GetData()->IsEmpty() == false) && node->IsOn(boolPropertyKey, renderer) && node->IsOn(boolPropertyKey2, renderer)) { const TimeGeometry *timeGeometry = node->GetData()->GetUpdatedTimeGeometry(); if (timeGeometry != nullptr) { // bounding box (only if non-zero) BoundingBox::BoundsArrayType itkBounds = timeGeometry->GetBoundingBoxInWorld()->GetBounds(); if (itkBounds == itkBoundsZero) { continue; } unsigned char i; for (i = 0; i < 8; ++i) { point = timeGeometry->GetCornerPointInWorld(i); if (point[0] * point[0] + point[1] * point[1] + point[2] * point[2] < large) pointscontainer->InsertElement(pointid++, point); else { itkGenericOutputMacro(<< "Unrealistically distant corner point encountered. Ignored. Node: " << node); } } try { // time bounds // iterate over all time steps // Attention: Objects with zero bounding box are not respected in time bound calculation for (TimeStepType i = 0; i < timeGeometry->CountTimeSteps(); i++) { // We must not use 'node->GetData()->GetGeometry(i)->GetSpacing()' here, as it returns the spacing // in its original space, which, in case of an image geometry, can have the values in different // order than in world space. For the further calculations, we need to have the spacing values // in world coordinate order (sag-cor-ax). Vector3D spacing; spacing.Fill(1.0); node->GetData()->GetGeometry(i)->IndexToWorld(spacing, spacing); for (int axis = 0; axis < 3; ++ axis) { ScalarType space = std::abs(spacing[axis]); if (space < minSpacing[axis]) { minSpacing[axis] = space; } } const TimeBounds &curTimeBounds = node->GetData()->GetTimeGeometry()->GetTimeBounds(i); // get the minimal time of all objects in the DataStorage if ((curTimeBounds[0] < minimalTime) && (curTimeBounds[0] > stmin)) { minimalTime = curTimeBounds[0]; } // get the maximal time of all objects in the DataStorage if ((curTimeBounds[1] > maximalTime) && (curTimeBounds[1] < stmax)) { maximalTime = curTimeBounds[1]; } // get the minimal TimeBound of all time steps of the current DataNode if (curTimeBounds[1] - curTimeBounds[0] < minimalIntervallSize) { minimalIntervallSize = curTimeBounds[1] - curTimeBounds[0]; } } } catch (itk::ExceptionObject &e) { MITK_ERROR << e << std::endl; } } } } BoundingBox::Pointer result = BoundingBox::New(); result->SetPoints(pointscontainer); result->ComputeBoundingBox(); // compute the number of time steps unsigned int numberOfTimeSteps = 1; if (maximalTime == 0) // make sure that there is at least one time sliced geometry in the data storage { minimalTime = 0; maximalTime = 1; minimalIntervallSize = 1; } numberOfTimeSteps = static_cast((maximalTime - minimalTime) / minimalIntervallSize); TimeGeometry::Pointer timeGeometry = nullptr; if (result->GetPoints()->Size() > 0) { // Initialize a geometry of a single time step Geometry3D::Pointer geometry = Geometry3D::New(); geometry->Initialize(); // correct bounding-box (is now in mm, should be in index-coordinates) // according to spacing BoundingBox::BoundsArrayType bounds = result->GetBounds(); AffineTransform3D::OutputVectorType offset; for (int i = 0; i < 3; ++i) { offset[i] = bounds[i * 2]; bounds[i * 2] = 0.0; bounds[i * 2 + 1] = (bounds[i * 2 + 1] - offset[i]) / minSpacing[i]; } geometry->GetIndexToWorldTransform()->SetOffset(offset); geometry->SetBounds(bounds); geometry->SetSpacing(minSpacing); // Initialize the time sliced geometry timeGeometry = ProportionalTimeGeometry::New(); dynamic_cast(timeGeometry.GetPointer())->Initialize(geometry, numberOfTimeSteps); dynamic_cast(timeGeometry.GetPointer())->SetFirstTimePoint(minimalTime); dynamic_cast(timeGeometry.GetPointer())->SetStepDuration(minimalIntervallSize); } return timeGeometry; } mitk::TimeGeometry::Pointer mitk::DataStorage::ComputeBoundingGeometry3D(const char *boolPropertyKey, const mitk::BaseRenderer *renderer, const char *boolPropertyKey2) const { return this->ComputeBoundingGeometry3D(this->GetAll(), boolPropertyKey, renderer, boolPropertyKey2); } mitk::TimeGeometry::Pointer mitk::DataStorage::ComputeVisibleBoundingGeometry3D(const mitk::BaseRenderer *renderer, const char *boolPropertyKey) { return ComputeBoundingGeometry3D("visible", renderer, boolPropertyKey); } mitk::BoundingBox::Pointer mitk::DataStorage::ComputeBoundingBox(const char *boolPropertyKey, const mitk::BaseRenderer *renderer, const char *boolPropertyKey2) { BoundingBox::PointsContainer::Pointer pointscontainer = BoundingBox::PointsContainer::New(); BoundingBox::PointIdentifier pointid = 0; Point3D point; // Needed for check of zero bounding boxes mitk::ScalarType nullpoint[] = {0, 0, 0, 0, 0, 0}; BoundingBox::BoundsArrayType itkBoundsZero(nullpoint); SetOfObjects::ConstPointer all = this->GetAll(); for (SetOfObjects::ConstIterator it = all->Begin(); it != all->End(); ++it) { DataNode::Pointer node = it->Value(); if ((node.IsNotNull()) && (node->GetData() != nullptr) && (node->GetData()->IsEmpty() == false) && node->IsOn(boolPropertyKey, renderer) && node->IsOn(boolPropertyKey2, renderer)) { const TimeGeometry *geometry = node->GetData()->GetUpdatedTimeGeometry(); if (geometry != nullptr) { // bounding box (only if non-zero) BoundingBox::BoundsArrayType itkBounds = geometry->GetBoundingBoxInWorld()->GetBounds(); if (itkBounds == itkBoundsZero) { continue; } unsigned char i; for (i = 0; i < 8; ++i) { point = geometry->GetCornerPointInWorld(i); if (point[0] * point[0] + point[1] * point[1] + point[2] * point[2] < large) pointscontainer->InsertElement(pointid++, point); else { itkGenericOutputMacro(<< "Unrealistically distant corner point encountered. Ignored. Node: " << node); } } } } } BoundingBox::Pointer result = BoundingBox::New(); result->SetPoints(pointscontainer); result->ComputeBoundingBox(); return result; } mitk::TimeBounds mitk::DataStorage::ComputeTimeBounds(const char *boolPropertyKey, const mitk::BaseRenderer *renderer, const char *boolPropertyKey2) { TimeBounds timeBounds; ScalarType stmin, stmax, cur; stmin = itk::NumericTraits::NonpositiveMin(); stmax = itk::NumericTraits::max(); timeBounds[0] = stmax; timeBounds[1] = stmin; SetOfObjects::ConstPointer all = this->GetAll(); for (SetOfObjects::ConstIterator it = all->Begin(); it != all->End(); ++it) { DataNode::Pointer node = it->Value(); if ((node.IsNotNull()) && (node->GetData() != nullptr) && (node->GetData()->IsEmpty() == false) && node->IsOn(boolPropertyKey, renderer) && node->IsOn(boolPropertyKey2, renderer)) { const TimeGeometry *geometry = node->GetData()->GetUpdatedTimeGeometry(); if (geometry != nullptr) { const TimeBounds &curTimeBounds = geometry->GetTimeBounds(); cur = curTimeBounds[0]; // is it after -infinity, but before everything else that we found until now? if ((cur > stmin) && (cur < timeBounds[0])) timeBounds[0] = cur; cur = curTimeBounds[1]; // is it before infinity, but after everything else that we found until now? if ((cur < stmax) && (cur > timeBounds[1])) timeBounds[1] = cur; } } } if (!(timeBounds[0] < stmax)) { timeBounds[0] = stmin; timeBounds[1] = stmax; } return timeBounds; } void mitk::DataStorage::BlockNodeModifiedEvents(bool block) { m_BlockNodeModifiedEvents = block; } diff --git a/Modules/Core/src/DataManagement/mitkEnumerationProperty.cpp b/Modules/Core/src/DataManagement/mitkEnumerationProperty.cpp index acfeb4fee3..db406da736 100644 --- a/Modules/Core/src/DataManagement/mitkEnumerationProperty.cpp +++ b/Modules/Core/src/DataManagement/mitkEnumerationProperty.cpp @@ -1,190 +1,190 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkEnumerationProperty.h" #include // static map members of EnumerationProperty. These Maps point to per-classname-maps of ID <-> String. Accessed by // GetEnumIds() and GetEnumString(). mitk::EnumerationProperty::IdMapForClassNameContainerType mitk::EnumerationProperty::s_IdMapForClassName; mitk::EnumerationProperty::StringMapForClassNameContainerType mitk::EnumerationProperty::s_StringMapForClassName; mitk::EnumerationProperty::EnumerationProperty() { m_CurrentValue = 0; } mitk::EnumerationProperty::EnumerationProperty(const EnumerationProperty &other) : BaseProperty(other), m_CurrentValue(other.m_CurrentValue) { } bool mitk::EnumerationProperty::AddEnum(const std::string &name, const IdType &id) { if ((!IsValidEnumerationValue(name)) && (!IsValidEnumerationValue(id))) { GetEnumIds().insert(std::make_pair(id, name)); GetEnumStrings().insert(std::make_pair(name, id)); return true; } else { return false; } } bool mitk::EnumerationProperty::SetValue(const std::string &name) { if (IsValidEnumerationValue(name)) { m_CurrentValue = GetEnumId(name); Modified(); return true; } else { return false; } } bool mitk::EnumerationProperty::SetValue(const IdType &id) { if (IsValidEnumerationValue(id)) { m_CurrentValue = id; Modified(); return true; } else { return false; } } mitk::EnumerationProperty::IdType mitk::EnumerationProperty::GetValueAsId() const { return m_CurrentValue; } std::string mitk::EnumerationProperty::GetValueAsString() const { return GetEnumString(m_CurrentValue); } void mitk::EnumerationProperty::Clear() { GetEnumIds().clear(); GetEnumStrings().clear(); m_CurrentValue = 0; } mitk::EnumerationProperty::EnumIdsContainerType::size_type mitk::EnumerationProperty::Size() const { return GetEnumIds().size(); } mitk::EnumerationProperty::EnumConstIterator mitk::EnumerationProperty::Begin() const { return GetEnumIds().begin(); } mitk::EnumerationProperty::EnumConstIterator mitk::EnumerationProperty::End() const { return GetEnumIds().end(); } std::string mitk::EnumerationProperty::GetEnumString(const IdType &id) const { if (IsValidEnumerationValue(id)) { return GetEnumIds().find(id)->second; } else { return "invalid enum id or enums empty"; } } mitk::EnumerationProperty::IdType mitk::EnumerationProperty::GetEnumId(const std::string &name) const { if (IsValidEnumerationValue(name)) { return GetEnumStrings().find(name)->second; } else { return 0; } } bool mitk::EnumerationProperty::IsEqual(const BaseProperty &property) const { - const Self &other = static_cast(property); + const auto &other = static_cast(property); return this->Size() == other.Size() && this->GetValueAsId() == other.GetValueAsId() && std::equal(this->Begin(), this->End(), other.Begin()); } bool mitk::EnumerationProperty::Assign(const BaseProperty &property) { - const Self &other = static_cast(property); + const auto &other = static_cast(property); this->GetEnumIds() = other.GetEnumIds(); this->GetEnumStrings() = other.GetEnumStrings(); this->m_CurrentValue = other.m_CurrentValue; this->Size() == other.Size() && this->GetValueAsId() == other.GetValueAsId() && std::equal(this->Begin(), this->End(), other.Begin()); return true; } bool mitk::EnumerationProperty::IsValidEnumerationValue(const IdType &val) const { return (GetEnumIds().find(val) != GetEnumIds().end()); } bool mitk::EnumerationProperty::IsValidEnumerationValue(const std::string &val) const { return (GetEnumStrings().find(val) != GetEnumStrings().end()); } mitk::EnumerationProperty::EnumIdsContainerType &mitk::EnumerationProperty::GetEnumIds() { std::string className = this->GetNameOfClass(); // virtual! return s_IdMapForClassName[className]; } const mitk::EnumerationProperty::EnumIdsContainerType &mitk::EnumerationProperty::GetEnumIds() const { std::string className = this->GetNameOfClass(); // virtual! return s_IdMapForClassName[className]; } mitk::EnumerationProperty::EnumStringsContainerType &mitk::EnumerationProperty::GetEnumStrings() { std::string className = this->GetNameOfClass(); // virtual! return s_StringMapForClassName[className]; } const mitk::EnumerationProperty::EnumStringsContainerType &mitk::EnumerationProperty::GetEnumStrings() const { std::string className = this->GetNameOfClass(); // virtual! return s_StringMapForClassName[className]; } itk::LightObject::Pointer mitk::EnumerationProperty::InternalClone() const { itk::LightObject::Pointer result(new Self(*this)); result->UnRegister(); return result; } diff --git a/Modules/Core/src/DataManagement/mitkImage.cpp b/Modules/Core/src/DataManagement/mitkImage.cpp index 2793a476ef..dcd0f8078f 100644 --- a/Modules/Core/src/DataManagement/mitkImage.cpp +++ b/Modules/Core/src/DataManagement/mitkImage.cpp @@ -1,1551 +1,1551 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ // MITK #include "mitkImage.h" #include "mitkCompareImageDataFilter.h" #include "mitkImageStatisticsHolder.h" #include "mitkImageVtkReadAccessor.h" #include "mitkImageVtkWriteAccessor.h" #include "mitkPixelTypeMultiplex.h" #include // VTK #include // ITK #include // Other #include #define FILL_C_ARRAY(_arr, _size, _value) \ for (unsigned int i = 0u; i < _size; i++) \ \ { \ _arr[i] = _value; \ } mitk::Image::Image() : m_Dimension(0), m_Dimensions(nullptr), m_ImageDescriptor(nullptr), m_OffsetTable(nullptr), m_CompleteData(nullptr), m_ImageStatistics(nullptr) { m_Dimensions = new unsigned int[MAX_IMAGE_DIMENSIONS]; FILL_C_ARRAY(m_Dimensions, MAX_IMAGE_DIMENSIONS, 0u); m_Initialized = false; } mitk::Image::Image(const Image &other) : SlicedData(other), m_Dimension(0), m_Dimensions(nullptr), m_ImageDescriptor(nullptr), m_OffsetTable(nullptr), m_CompleteData(nullptr), m_ImageStatistics(nullptr) { m_Dimensions = new unsigned int[MAX_IMAGE_DIMENSIONS]; FILL_C_ARRAY(m_Dimensions, MAX_IMAGE_DIMENSIONS, 0u); this->Initialize(other.GetPixelType(), other.GetDimension(), other.GetDimensions()); // Since the above called "Initialize" method doesn't take the geometry into account we need to set it // here manually TimeGeometry::Pointer cloned = other.GetTimeGeometry()->Clone(); this->SetTimeGeometry(cloned.GetPointer()); if (this->GetDimension() > 3) { const unsigned int time_steps = this->GetDimension(3); for (unsigned int i = 0u; i < time_steps; ++i) { ImageDataItemPointer volume = const_cast(other).GetVolumeData(i); this->SetVolume(volume->GetData(), i); } } else { ImageDataItemPointer volume = const_cast(other).GetVolumeData(0); this->SetVolume(volume->GetData(), 0); } } mitk::Image::~Image() { this->Clear(); m_ReferenceCount = 3; m_ReferenceCount = 0; delete[] m_OffsetTable; delete m_ImageStatistics; } const mitk::PixelType mitk::Image::GetPixelType(int n) const { return this->m_ImageDescriptor->GetChannelTypeById(n); } unsigned int mitk::Image::GetDimension() const { return m_Dimension; } unsigned int mitk::Image::GetDimension(int i) const { if ((i >= 0) && (i < (int)m_Dimension)) return m_Dimensions[i]; return 1; } void *mitk::Image::GetData() { if (m_Initialized == false) { if (GetSource().IsNull()) return nullptr; if (GetSource()->Updating() == false) GetSource()->UpdateOutputInformation(); } m_CompleteData = GetChannelData(); // update channel's data // if data was not available at creation point, the m_Data of channel descriptor is nullptr // if data present, it won't be overwritten m_ImageDescriptor->GetChannelDescriptor(0).SetData(m_CompleteData->GetData()); return m_CompleteData->GetData(); } template void AccessPixel(const mitk::PixelType ptype, void *data, const unsigned int offset, double &value) { value = 0.0; if (data == nullptr) return; if (ptype.GetBpe() != 24) { value = (double)(((T *)data)[offset]); } else { const unsigned int rgboffset = offset; double returnvalue = (((T *)data)[rgboffset]); returnvalue += (((T *)data)[rgboffset + 1]); returnvalue += (((T *)data)[rgboffset + 2]); value = returnvalue; } } double mitk::Image::GetPixelValueByIndex(const itk::Index<3> &position, unsigned int timestep, unsigned int component) { double value = 0; if (this->GetTimeSteps() < timestep) { timestep = this->GetTimeSteps(); } value = 0.0; const unsigned int *imageDims = this->m_ImageDescriptor->GetDimensions(); const mitk::PixelType ptype = this->m_ImageDescriptor->GetChannelTypeById(0); // Comparison ?>=0 not needed since all position[i] and timestep are unsigned int // (position[0]>=0 && position[1] >=0 && position[2]>=0 && timestep>=0) // bug-11978 : we still need to catch index with negative values if (position[0] < 0 || position[1] < 0 || position[2] < 0) { MITK_WARN << "Given position (" << position << ") is out of image range, returning 0."; } // check if the given position is inside the index range of the image, the 3rd dimension needs to be compared only if // the dimension is not 0 else if ((unsigned int)position[0] >= imageDims[0] || (unsigned int)position[1] >= imageDims[1] || (imageDims[2] && (unsigned int)position[2] >= imageDims[2])) { MITK_WARN << "Given position (" << position << ") is out of image range, returning 0."; } else { const unsigned int offset = component + ptype.GetNumberOfComponents() * (position[0] + position[1] * imageDims[0] + position[2] * imageDims[0] * imageDims[1] + timestep * imageDims[0] * imageDims[1] * imageDims[2]); mitkPixelTypeMultiplex3(AccessPixel, ptype, this->GetData(), offset, value); } return value; } double mitk::Image::GetPixelValueByWorldCoordinate(const mitk::Point3D &position, unsigned int timestep, unsigned int component) { double value = 0.0; if (this->GetTimeSteps() < timestep) { timestep = this->GetTimeSteps(); } itk::Index<3> itkIndex; this->GetGeometry()->WorldToIndex(position, itkIndex); value = this->GetPixelValueByIndex(itkIndex, timestep, component); return value; } vtkImageData *mitk::Image::GetVtkImageData(int t, int n) { if (m_Initialized == false) { if (GetSource().IsNull()) return nullptr; if (GetSource()->Updating() == false) GetSource()->UpdateOutputInformation(); } ImageDataItemPointer volume = GetVolumeData(t, n); return volume.GetPointer() == nullptr ? nullptr : volume->GetVtkImageAccessor(this)->GetVtkImageData(); } const vtkImageData *mitk::Image::GetVtkImageData(int t, int n) const { if (m_Initialized == false) { if (GetSource().IsNull()) return nullptr; if (GetSource()->Updating() == false) GetSource()->UpdateOutputInformation(); } ImageDataItemPointer volume = GetVolumeData(t, n); return volume.GetPointer() == nullptr ? nullptr : volume->GetVtkImageAccessor(this)->GetVtkImageData(); } mitk::Image::ImageDataItemPointer mitk::Image::GetSliceData( int s, int t, int n, void *data, ImportMemoryManagementType importMemoryManagement) const { MutexHolder lock(m_ImageDataArraysLock); return GetSliceData_unlocked(s, t, n, data, importMemoryManagement); } mitk::Image::ImageDataItemPointer mitk::Image::GetSliceData_unlocked( int s, int t, int n, void *data, ImportMemoryManagementType importMemoryManagement) const { if (IsValidSlice(s, t, n) == false) return nullptr; const size_t ptypeSize = this->m_ImageDescriptor->GetChannelTypeById(n).GetSize(); // slice directly available? int pos = GetSliceIndex(s, t, n); if (m_Slices[pos].GetPointer() != nullptr) { return m_Slices[pos]; } // is slice available as part of a volume that is available? ImageDataItemPointer sl, ch, vol; vol = m_Volumes[GetVolumeIndex(t, n)]; if ((vol.GetPointer() != nullptr) && (vol->IsComplete())) { sl = new ImageDataItem(*vol, m_ImageDescriptor, t, 2, data, importMemoryManagement == ManageMemory, ((size_t)s) * m_OffsetTable[2] * (ptypeSize)); sl->SetComplete(true); return m_Slices[pos] = sl; } // is slice available as part of a channel that is available? ch = m_Channels[n]; if ((ch.GetPointer() != nullptr) && (ch->IsComplete())) { sl = new ImageDataItem(*ch, m_ImageDescriptor, t, 2, data, importMemoryManagement == ManageMemory, (((size_t)s) * m_OffsetTable[2] + ((size_t)t) * m_OffsetTable[3]) * (ptypeSize)); sl->SetComplete(true); return m_Slices[pos] = sl; } // slice is unavailable. Can we calculate it? if ((GetSource().IsNotNull()) && (GetSource()->Updating() == false)) { // ... wir mussen rechnen!!! .... m_RequestedRegion.SetIndex(0, 0); m_RequestedRegion.SetIndex(1, 0); m_RequestedRegion.SetIndex(2, s); m_RequestedRegion.SetIndex(3, t); m_RequestedRegion.SetIndex(4, n); m_RequestedRegion.SetSize(0, m_Dimensions[0]); m_RequestedRegion.SetSize(1, m_Dimensions[1]); m_RequestedRegion.SetSize(2, 1); m_RequestedRegion.SetSize(3, 1); m_RequestedRegion.SetSize(4, 1); m_RequestedRegionInitialized = true; GetSource()->Update(); if (IsSliceSet_unlocked(s, t, n)) // yes: now we can call ourselves without the risk of a endless loop (see "if" above) return GetSliceData_unlocked(s, t, n, data, importMemoryManagement); else return nullptr; } else { ImageDataItemPointer item = AllocateSliceData_unlocked(s, t, n, data, importMemoryManagement); item->SetComplete(true); return item; } } mitk::Image::ImageDataItemPointer mitk::Image::GetVolumeData(int t, int n, void *data, ImportMemoryManagementType importMemoryManagement) const { MutexHolder lock(m_ImageDataArraysLock); return GetVolumeData_unlocked(t, n, data, importMemoryManagement); } mitk::Image::ImageDataItemPointer mitk::Image::GetVolumeData_unlocked( int t, int n, void *data, ImportMemoryManagementType importMemoryManagement) const { if (IsValidVolume(t, n) == false) return nullptr; ImageDataItemPointer ch, vol; // volume directly available? int pos = GetVolumeIndex(t, n); vol = m_Volumes[pos]; if ((vol.GetPointer() != nullptr) && (vol->IsComplete())) return vol; const size_t ptypeSize = this->m_ImageDescriptor->GetChannelTypeById(n).GetSize(); // is volume available as part of a channel that is available? ch = m_Channels[n]; if ((ch.GetPointer() != nullptr) && (ch->IsComplete())) { vol = new ImageDataItem(*ch, m_ImageDescriptor, t, 3, data, importMemoryManagement == ManageMemory, (((size_t)t) * m_OffsetTable[3]) * (ptypeSize)); vol->SetComplete(true); return m_Volumes[pos] = vol; } // let's see if all slices of the volume are set, so that we can (could) combine them to a volume bool complete = true; unsigned int s; for (s = 0; s < m_Dimensions[2]; ++s) { if (m_Slices[GetSliceIndex(s, t, n)].GetPointer() == nullptr) { complete = false; break; } } if (complete) { // if there is only single slice we do not need to combine anything if (m_Dimensions[2] <= 1) { ImageDataItemPointer sl; sl = GetSliceData_unlocked(0, t, n, data, importMemoryManagement); vol = new ImageDataItem(*sl, m_ImageDescriptor, t, 3, data, importMemoryManagement == ManageMemory); vol->SetComplete(true); } else { mitk::PixelType chPixelType = this->m_ImageDescriptor->GetChannelTypeById(n); vol = m_Volumes[pos]; // ok, let's combine the slices! if (vol.GetPointer() == nullptr) { vol = new ImageDataItem(chPixelType, t, 3, m_Dimensions, nullptr, true); } vol->SetComplete(true); size_t size = m_OffsetTable[2] * (ptypeSize); for (s = 0; s < m_Dimensions[2]; ++s) { int posSl; ImageDataItemPointer sl; posSl = GetSliceIndex(s, t, n); sl = m_Slices[posSl]; if (sl->GetParent() != vol) { // copy data of slices in volume size_t offset = ((size_t)s) * size; std::memcpy(static_cast(vol->GetData()) + offset, sl->GetData(), size); // FIXME mitkIpPicDescriptor * pic = sl->GetPicDescriptor(); // replace old slice with reference to volume sl = new ImageDataItem( *vol, m_ImageDescriptor, t, 2, data, importMemoryManagement == ManageMemory, ((size_t)s) * size); sl->SetComplete(true); // mitkIpFuncCopyTags(sl->GetPicDescriptor(), pic); m_Slices[posSl] = sl; } } // if(vol->GetPicDescriptor()->info->tags_head==nullptr) // mitkIpFuncCopyTags(vol->GetPicDescriptor(), m_Slices[GetSliceIndex(0,t,n)]->GetPicDescriptor()); } return m_Volumes[pos] = vol; } // volume is unavailable. Can we calculate it? if ((GetSource().IsNotNull()) && (GetSource()->Updating() == false)) { // ... wir muessen rechnen!!! .... m_RequestedRegion.SetIndex(0, 0); m_RequestedRegion.SetIndex(1, 0); m_RequestedRegion.SetIndex(2, 0); m_RequestedRegion.SetIndex(3, t); m_RequestedRegion.SetIndex(4, n); m_RequestedRegion.SetSize(0, m_Dimensions[0]); m_RequestedRegion.SetSize(1, m_Dimensions[1]); m_RequestedRegion.SetSize(2, m_Dimensions[2]); m_RequestedRegion.SetSize(3, 1); m_RequestedRegion.SetSize(4, 1); m_RequestedRegionInitialized = true; GetSource()->Update(); if (IsVolumeSet_unlocked(t, n)) // yes: now we can call ourselves without the risk of a endless loop (see "if" above) return GetVolumeData_unlocked(t, n, data, importMemoryManagement); else return nullptr; } else { ImageDataItemPointer item = AllocateVolumeData_unlocked(t, n, data, importMemoryManagement); item->SetComplete(true); return item; } } mitk::Image::ImageDataItemPointer mitk::Image::GetChannelData(int n, void *data, ImportMemoryManagementType importMemoryManagement) const { MutexHolder lock(m_ImageDataArraysLock); return GetChannelData_unlocked(n, data, importMemoryManagement); } mitk::Image::ImageDataItemPointer mitk::Image::GetChannelData_unlocked( int n, void *data, ImportMemoryManagementType importMemoryManagement) const { if (IsValidChannel(n) == false) return nullptr; ImageDataItemPointer ch, vol; ch = m_Channels[n]; if ((ch.GetPointer() != nullptr) && (ch->IsComplete())) return ch; // let's see if all volumes are set, so that we can (could) combine them to a channel if (IsChannelSet_unlocked(n)) { // if there is only one time frame we do not need to combine anything if (m_Dimensions[3] <= 1) { vol = GetVolumeData_unlocked(0, n, data, importMemoryManagement); ch = new ImageDataItem(*vol, m_ImageDescriptor, 0, m_ImageDescriptor->GetNumberOfDimensions(), data, importMemoryManagement == ManageMemory); ch->SetComplete(true); } else { const size_t ptypeSize = this->m_ImageDescriptor->GetChannelTypeById(n).GetSize(); ch = m_Channels[n]; // ok, let's combine the volumes! if (ch.GetPointer() == nullptr) ch = new ImageDataItem(this->m_ImageDescriptor, -1, nullptr, true); ch->SetComplete(true); size_t size = m_OffsetTable[m_Dimension - 1] * (ptypeSize); unsigned int t; auto slicesIt = m_Slices.begin() + n * m_Dimensions[2] * m_Dimensions[3]; for (t = 0; t < m_Dimensions[3]; ++t) { int posVol; ImageDataItemPointer vol; posVol = GetVolumeIndex(t, n); vol = GetVolumeData_unlocked(t, n, data, importMemoryManagement); if (vol->GetParent() != ch) { // copy data of volume in channel size_t offset = ((size_t)t) * m_OffsetTable[3] * (ptypeSize); std::memcpy(static_cast(ch->GetData()) + offset, vol->GetData(), size); // REVEIW FIX mitkIpPicDescriptor * pic = vol->GetPicDescriptor(); // replace old volume with reference to channel vol = new ImageDataItem(*ch, m_ImageDescriptor, t, 3, data, importMemoryManagement == ManageMemory, offset); vol->SetComplete(true); // mitkIpFuncCopyTags(vol->GetPicDescriptor(), pic); m_Volumes[posVol] = vol; // get rid of slices - they may point to old volume ImageDataItemPointer dnull = nullptr; for (unsigned int i = 0; i < m_Dimensions[2]; ++i, ++slicesIt) { assert(slicesIt != m_Slices.end()); *slicesIt = dnull; } } } // REVIEW FIX // if(ch->GetPicDescriptor()->info->tags_head==nullptr) // mitkIpFuncCopyTags(ch->GetPicDescriptor(), m_Volumes[GetVolumeIndex(0,n)]->GetPicDescriptor()); } return m_Channels[n] = ch; } // channel is unavailable. Can we calculate it? if ((GetSource().IsNotNull()) && (GetSource()->Updating() == false)) { // ... wir muessen rechnen!!! .... m_RequestedRegion.SetIndex(0, 0); m_RequestedRegion.SetIndex(1, 0); m_RequestedRegion.SetIndex(2, 0); m_RequestedRegion.SetIndex(3, 0); m_RequestedRegion.SetIndex(4, n); m_RequestedRegion.SetSize(0, m_Dimensions[0]); m_RequestedRegion.SetSize(1, m_Dimensions[1]); m_RequestedRegion.SetSize(2, m_Dimensions[2]); m_RequestedRegion.SetSize(3, m_Dimensions[3]); m_RequestedRegion.SetSize(4, 1); m_RequestedRegionInitialized = true; GetSource()->Update(); // did it work? if (IsChannelSet_unlocked(n)) // yes: now we can call ourselves without the risk of a endless loop (see "if" above) return GetChannelData_unlocked(n, data, importMemoryManagement); else return nullptr; } else { ImageDataItemPointer item = AllocateChannelData_unlocked(n, data, importMemoryManagement); item->SetComplete(true); return item; } } bool mitk::Image::IsSliceSet(int s, int t, int n) const { MutexHolder lock(m_ImageDataArraysLock); return IsSliceSet_unlocked(s, t, n); } bool mitk::Image::IsSliceSet_unlocked(int s, int t, int n) const { if (IsValidSlice(s, t, n) == false) return false; if (m_Slices[GetSliceIndex(s, t, n)].GetPointer() != nullptr) { return true; } ImageDataItemPointer ch, vol; vol = m_Volumes[GetVolumeIndex(t, n)]; if ((vol.GetPointer() != nullptr) && (vol->IsComplete())) { return true; } ch = m_Channels[n]; if ((ch.GetPointer() != nullptr) && (ch->IsComplete())) { return true; } return false; } bool mitk::Image::IsVolumeSet(int t, int n) const { MutexHolder lock(m_ImageDataArraysLock); return IsVolumeSet_unlocked(t, n); } bool mitk::Image::IsVolumeSet_unlocked(int t, int n) const { if (IsValidVolume(t, n) == false) return false; ImageDataItemPointer ch, vol; // volume directly available? vol = m_Volumes[GetVolumeIndex(t, n)]; if ((vol.GetPointer() != nullptr) && (vol->IsComplete())) return true; // is volume available as part of a channel that is available? ch = m_Channels[n]; if ((ch.GetPointer() != nullptr) && (ch->IsComplete())) return true; // let's see if all slices of the volume are set, so that we can (could) combine them to a volume unsigned int s; for (s = 0; s < m_Dimensions[2]; ++s) { if (m_Slices[GetSliceIndex(s, t, n)].GetPointer() == nullptr) { return false; } } return true; } bool mitk::Image::IsChannelSet(int n) const { MutexHolder lock(m_ImageDataArraysLock); return IsChannelSet_unlocked(n); } bool mitk::Image::IsChannelSet_unlocked(int n) const { if (IsValidChannel(n) == false) return false; ImageDataItemPointer ch, vol; ch = m_Channels[n]; if ((ch.GetPointer() != nullptr) && (ch->IsComplete())) return true; // let's see if all volumes are set, so that we can (could) combine them to a channel unsigned int t; for (t = 0; t < m_Dimensions[3]; ++t) { if (IsVolumeSet_unlocked(t, n) == false) { return false; } } return true; } bool mitk::Image::SetSlice(const void *data, int s, int t, int n) { // const_cast is no risk for ImportMemoryManagementType == CopyMemory return SetImportSlice(const_cast(data), s, t, n, CopyMemory); } bool mitk::Image::SetVolume(const void *data, int t, int n) { // const_cast is no risk for ImportMemoryManagementType == CopyMemory return SetImportVolume(const_cast(data), t, n, CopyMemory); } bool mitk::Image::SetChannel(const void *data, int n) { // const_cast is no risk for ImportMemoryManagementType == CopyMemory return SetImportChannel(const_cast(data), n, CopyMemory); } bool mitk::Image::SetImportSlice(void *data, int s, int t, int n, ImportMemoryManagementType importMemoryManagement) { if (IsValidSlice(s, t, n) == false) return false; ImageDataItemPointer sl; const size_t ptypeSize = this->m_ImageDescriptor->GetChannelTypeById(n).GetSize(); if (IsSliceSet(s, t, n)) { sl = GetSliceData(s, t, n, data, importMemoryManagement); if (sl->GetManageMemory() == false) { sl = AllocateSliceData(s, t, n, data, importMemoryManagement); if (sl.GetPointer() == nullptr) return false; } if (sl->GetData() != data) std::memcpy(sl->GetData(), data, m_OffsetTable[2] * (ptypeSize)); sl->Modified(); // we have changed the data: call Modified()! Modified(); } else { sl = AllocateSliceData(s, t, n, data, importMemoryManagement); if (sl.GetPointer() == nullptr) return false; if (sl->GetData() != data) std::memcpy(sl->GetData(), data, m_OffsetTable[2] * (ptypeSize)); // we just added a missing slice, which is not regarded as modification. // Therefore, we do not call Modified()! } return true; } bool mitk::Image::SetImportVolume(void *data, int t, int n, ImportMemoryManagementType importMemoryManagement) { if (IsValidVolume(t, n) == false) return false; const size_t ptypeSize = this->m_ImageDescriptor->GetChannelTypeById(n).GetSize(); ImageDataItemPointer vol; if (IsVolumeSet(t, n)) { vol = GetVolumeData(t, n, data, importMemoryManagement); if (vol->GetManageMemory() == false) { vol = AllocateVolumeData(t, n, data, importMemoryManagement); if (vol.GetPointer() == nullptr) return false; } if (vol->GetData() != data) std::memcpy(vol->GetData(), data, m_OffsetTable[3] * (ptypeSize)); vol->Modified(); vol->SetComplete(true); // we have changed the data: call Modified()! Modified(); } else { vol = AllocateVolumeData(t, n, data, importMemoryManagement); if (vol.GetPointer() == nullptr) return false; if (vol->GetData() != data) { std::memcpy(vol->GetData(), data, m_OffsetTable[3] * (ptypeSize)); } vol->SetComplete(true); this->m_ImageDescriptor->GetChannelDescriptor(n).SetData(vol->GetData()); // we just added a missing Volume, which is not regarded as modification. // Therefore, we do not call Modified()! } return true; } bool mitk::Image::SetImportVolume(const void *const_data, int t, int n) { return this->SetImportVolume(const_cast(const_data), t, n, CopyMemory); } bool mitk::Image::SetImportChannel(void *data, int n, ImportMemoryManagementType importMemoryManagement) { if (IsValidChannel(n) == false) return false; // channel descriptor const size_t ptypeSize = this->m_ImageDescriptor->GetChannelTypeById(n).GetSize(); ImageDataItemPointer ch; if (IsChannelSet(n)) { ch = GetChannelData(n, data, importMemoryManagement); if (ch->GetManageMemory() == false) { ch = AllocateChannelData(n, data, importMemoryManagement); if (ch.GetPointer() == nullptr) return false; } if (ch->GetData() != data) std::memcpy(ch->GetData(), data, m_OffsetTable[4] * (ptypeSize)); ch->Modified(); ch->SetComplete(true); // we have changed the data: call Modified()! Modified(); } else { ch = AllocateChannelData(n, data, importMemoryManagement); if (ch.GetPointer() == nullptr) return false; if (ch->GetData() != data) std::memcpy(ch->GetData(), data, m_OffsetTable[4] * (ptypeSize)); ch->SetComplete(true); this->m_ImageDescriptor->GetChannelDescriptor(n).SetData(ch->GetData()); // we just added a missing Channel, which is not regarded as modification. // Therefore, we do not call Modified()! } return true; } void mitk::Image::Initialize() { ImageDataItemPointerArray::iterator it, end; for (it = m_Slices.begin(), end = m_Slices.end(); it != end; ++it) { (*it) = nullptr; } for (it = m_Volumes.begin(), end = m_Volumes.end(); it != end; ++it) { (*it) = nullptr; } for (it = m_Channels.begin(), end = m_Channels.end(); it != end; ++it) { (*it) = nullptr; } m_CompleteData = nullptr; if (m_ImageStatistics == nullptr) { m_ImageStatistics = new mitk::ImageStatisticsHolder(this); } SetRequestedRegionToLargestPossibleRegion(); } void mitk::Image::Initialize(const mitk::ImageDescriptor::Pointer inDesc) { // store the descriptor this->m_ImageDescriptor = inDesc; // initialize image this->Initialize( inDesc->GetChannelDescriptor(0).GetPixelType(), inDesc->GetNumberOfDimensions(), inDesc->GetDimensions(), 1); } void mitk::Image::Initialize(const mitk::PixelType &type, unsigned int dimension, const unsigned int *dimensions, unsigned int channels) { Clear(); m_Dimension = dimension; if (!dimensions) itkExceptionMacro(<< "invalid zero dimension image"); unsigned int i; for (i = 0; i < dimension; ++i) { if (dimensions[i] < 1) itkExceptionMacro(<< "invalid dimension[" << i << "]: " << dimensions[i]); } // create new array since the old was deleted m_Dimensions = new unsigned int[MAX_IMAGE_DIMENSIONS]; // initialize the first four dimensions to 1, the remaining 4 to 0 FILL_C_ARRAY(m_Dimensions, 4, 1u); FILL_C_ARRAY((m_Dimensions + 4), 4, 0u); // copy in the passed dimension information std::memcpy(m_Dimensions, dimensions, sizeof(unsigned int) * m_Dimension); this->m_ImageDescriptor = mitk::ImageDescriptor::New(); this->m_ImageDescriptor->Initialize(this->m_Dimensions, this->m_Dimension); for (i = 0; i < 4; ++i) { m_LargestPossibleRegion.SetIndex(i, 0); m_LargestPossibleRegion.SetSize(i, m_Dimensions[i]); } m_LargestPossibleRegion.SetIndex(i, 0); m_LargestPossibleRegion.SetSize(i, channels); if (m_LargestPossibleRegion.GetNumberOfPixels() == 0) { delete[] m_Dimensions; m_Dimensions = nullptr; return; } for (unsigned int i = 0u; i < channels; i++) { this->m_ImageDescriptor->AddNewChannel(type); } PlaneGeometry::Pointer planegeometry = PlaneGeometry::New(); planegeometry->InitializeStandardPlane(m_Dimensions[0], m_Dimensions[1]); SlicedGeometry3D::Pointer slicedGeometry = SlicedGeometry3D::New(); slicedGeometry->InitializeEvenlySpaced(planegeometry, m_Dimensions[2]); ProportionalTimeGeometry::Pointer timeGeometry = ProportionalTimeGeometry::New(); timeGeometry->Initialize(slicedGeometry, m_Dimensions[3]); for (TimeStepType step = 0; step < timeGeometry->CountTimeSteps(); ++step) { timeGeometry->GetGeometryForTimeStep(step)->ImageGeometryOn(); } SetTimeGeometry(timeGeometry); ImageDataItemPointer dnull = nullptr; m_Channels.assign(GetNumberOfChannels(), dnull); m_Volumes.assign(GetNumberOfChannels() * m_Dimensions[3], dnull); m_Slices.assign(GetNumberOfChannels() * m_Dimensions[3] * m_Dimensions[2], dnull); ComputeOffsetTable(); Initialize(); m_Initialized = true; } void mitk::Image::Initialize(const mitk::PixelType &type, const mitk::BaseGeometry &geometry, unsigned int channels, int tDim) { mitk::ProportionalTimeGeometry::Pointer timeGeometry = ProportionalTimeGeometry::New(); timeGeometry->Initialize(geometry.Clone(), tDim); this->Initialize(type, *timeGeometry, channels, tDim); } void mitk::Image::Initialize(const mitk::PixelType &type, const mitk::TimeGeometry &geometry, unsigned int channels, int tDim) { unsigned int dimensions[5]; dimensions[0] = (unsigned int)(geometry.GetGeometryForTimeStep(0)->GetExtent(0) + 0.5); dimensions[1] = (unsigned int)(geometry.GetGeometryForTimeStep(0)->GetExtent(1) + 0.5); dimensions[2] = (unsigned int)(geometry.GetGeometryForTimeStep(0)->GetExtent(2) + 0.5); dimensions[3] = (tDim > 0) ? tDim : geometry.CountTimeSteps(); dimensions[4] = 0; unsigned int dimension = 2; if (dimensions[2] > 1) dimension = 3; if (dimensions[3] > 1) dimension = 4; Initialize(type, dimension, dimensions, channels); if (geometry.CountTimeSteps() > 1) { TimeGeometry::Pointer cloned = geometry.Clone(); SetTimeGeometry(cloned.GetPointer()); // make sure the image geometry flag is properly set for all time steps for (TimeStepType step = 0; step < cloned->CountTimeSteps(); ++step) { if (!cloned->GetGeometryCloneForTimeStep(step)->GetImageGeometry()) { MITK_WARN("Image.3DnT.Initialize") << " Attempt to initialize an image with a non-image geometry. " "Re-interpretting the initialization geometry for timestep " << step << " as image geometry, the original geometry remains unchanged."; cloned->GetGeometryForTimeStep(step)->ImageGeometryOn(); } } } else { // make sure the image geometry coming from outside has proper value of the image geometry flag BaseGeometry::Pointer cloned = geometry.GetGeometryCloneForTimeStep(0)->Clone(); if (!cloned->GetImageGeometry()) { MITK_WARN("Image.Initialize") << " Attempt to initialize an image with a non-image geometry. Re-interpretting " "the initialization geometry as image geometry, the original geometry remains " "unchanged."; cloned->ImageGeometryOn(); } Superclass::SetGeometry(cloned); } /* //Old //TODO_GOETZ Really necessary? mitk::BoundingBox::BoundsArrayType bounds = geometry.GetBoundingBoxInWorld()->GetBounds(); if( (bounds[0] != 0.0) || (bounds[2] != 0.0) || (bounds[4] != 0.0) ) { SlicedGeometry3D* slicedGeometry = GetSlicedGeometry(0); mitk::Point3D origin; origin.Fill(0.0); slicedGeometry->IndexToWorld(origin, origin); bounds[1]-=bounds[0]; bounds[3]-=bounds[2]; bounds[5]-=bounds[4]; bounds[0] = 0.0; bounds[2] = 0.0; bounds[4] = 0.0; this->m_ImageDescriptor->Initialize( this->m_Dimensions, this->m_Dimension ); slicedGeometry->SetBounds(bounds); slicedGeometry->GetIndexToWorldTransform()->SetOffset(origin.GetVnlVector().data_block()); ProportionalTimeGeometry::Pointer timeGeometry = ProportionalTimeGeometry::New(); timeGeometry->Initialize(slicedGeometry, m_Dimensions[3]); SetTimeGeometry(timeGeometry); }*/ } void mitk::Image::Initialize(const mitk::PixelType &, int, const mitk::PlaneGeometry &, bool, unsigned int, int) { mitkThrow() << "Use this method without the flipped parameter (direction is specified by the handedness of the PlaneGeometry instead)."; } void mitk::Image::Initialize(const mitk::PixelType &type, int sDim, const mitk::PlaneGeometry &geometry2d, unsigned int channels, int tDim) { SlicedGeometry3D::Pointer slicedGeometry = SlicedGeometry3D::New(); slicedGeometry->InitializeEvenlySpaced(geometry2d.Clone(), sDim); Initialize(type, *slicedGeometry, channels, tDim); } void mitk::Image::Initialize(const mitk::Image *image) { Initialize(image->GetPixelType(), *image->GetTimeGeometry()); } void mitk::Image::Initialize(vtkImageData *vtkimagedata, int channels, int tDim, int sDim, int pDim) { if (vtkimagedata == nullptr) return; m_Dimension = vtkimagedata->GetDataDimension(); unsigned int i, *tmpDimensions = new unsigned int[m_Dimension > 4 ? m_Dimension : 4]; for (i = 0; i < m_Dimension; ++i) tmpDimensions[i] = vtkimagedata->GetDimensions()[i]; if (m_Dimension < 4) { unsigned int *p; for (i = 0, p = tmpDimensions + m_Dimension; i < 4 - m_Dimension; ++i, ++p) *p = 1; } if (pDim >= 0) { tmpDimensions[1] = pDim; if (m_Dimension < 2) m_Dimension = 2; } if (sDim >= 0) { tmpDimensions[2] = sDim; if (m_Dimension < 3) m_Dimension = 3; } if (tDim >= 0) { tmpDimensions[3] = tDim; if (m_Dimension < 4) m_Dimension = 4; } mitk::PixelType pixelType(MakePixelType(vtkimagedata)); Initialize(pixelType, m_Dimension, tmpDimensions, channels); const double *spacinglist = vtkimagedata->GetSpacing(); Vector3D spacing; FillVector3D(spacing, spacinglist[0], 1.0, 1.0); if (m_Dimension >= 2) spacing[1] = spacinglist[1]; if (m_Dimension >= 3) spacing[2] = spacinglist[2]; // access origin of vtkImage Point3D origin; double vtkorigin[3]; vtkimagedata->GetOrigin(vtkorigin); FillVector3D(origin, vtkorigin[0], 0.0, 0.0); if (m_Dimension >= 2) origin[1] = vtkorigin[1]; if (m_Dimension >= 3) origin[2] = vtkorigin[2]; SlicedGeometry3D *slicedGeometry = GetSlicedGeometry(0); // re-initialize PlaneGeometry with origin and direction - PlaneGeometry *planeGeometry = static_cast(slicedGeometry->GetPlaneGeometry(0)); + auto *planeGeometry = static_cast(slicedGeometry->GetPlaneGeometry(0)); planeGeometry->SetOrigin(origin); // re-initialize SlicedGeometry3D slicedGeometry->SetOrigin(origin); slicedGeometry->SetSpacing(spacing); ProportionalTimeGeometry::Pointer timeGeometry = ProportionalTimeGeometry::New(); timeGeometry->Initialize(slicedGeometry, m_Dimensions[3]); SetTimeGeometry(timeGeometry); delete[] tmpDimensions; } bool mitk::Image::IsValidSlice(int s, int t, int n) const { if (m_Initialized) return ((s >= 0) && (s < (int)m_Dimensions[2]) && (t >= 0) && (t < (int)m_Dimensions[3]) && (n >= 0) && (n < (int)GetNumberOfChannels())); else return false; } bool mitk::Image::IsValidVolume(int t, int n) const { if (m_Initialized) return IsValidSlice(0, t, n); else return false; } bool mitk::Image::IsValidChannel(int n) const { if (m_Initialized) return IsValidSlice(0, 0, n); else return false; } void mitk::Image::ComputeOffsetTable() { if (m_OffsetTable != nullptr) delete[] m_OffsetTable; m_OffsetTable = new size_t[m_Dimension > 4 ? m_Dimension + 1 : 4 + 1]; unsigned int i; size_t num = 1; m_OffsetTable[0] = 1; for (i = 0; i < m_Dimension; ++i) { num *= m_Dimensions[i]; m_OffsetTable[i + 1] = num; } for (; i < 4; ++i) m_OffsetTable[i + 1] = num; } bool mitk::Image::IsValidTimeStep(int t) const { return ((m_Dimension >= 4 && t <= (int)m_Dimensions[3] && t > 0) || (t == 0)); } void mitk::Image::Expand(unsigned int timeSteps) { if (timeSteps < 1) itkExceptionMacro(<< "Invalid timestep in Image!"); Superclass::Expand(timeSteps); } int mitk::Image::GetSliceIndex(int s, int t, int n) const { if (IsValidSlice(s, t, n) == false) return false; return ((size_t)s) + ((size_t)t) * m_Dimensions[2] + ((size_t)n) * m_Dimensions[3] * m_Dimensions[2]; //?? } int mitk::Image::GetVolumeIndex(int t, int n) const { if (IsValidVolume(t, n) == false) return false; return ((size_t)t) + ((size_t)n) * m_Dimensions[3]; //?? } mitk::Image::ImageDataItemPointer mitk::Image::AllocateSliceData( int s, int t, int n, void *data, ImportMemoryManagementType importMemoryManagement) const { MutexHolder lock(m_ImageDataArraysLock); return AllocateSliceData_unlocked(s, t, n, data, importMemoryManagement); } mitk::Image::ImageDataItemPointer mitk::Image::AllocateSliceData_unlocked( int s, int t, int n, void *data, ImportMemoryManagementType importMemoryManagement) const { int pos; pos = GetSliceIndex(s, t, n); const size_t ptypeSize = this->m_ImageDescriptor->GetChannelTypeById(n).GetSize(); // is slice available as part of a volume that is available? ImageDataItemPointer sl, ch, vol; vol = m_Volumes[GetVolumeIndex(t, n)]; if (vol.GetPointer() != nullptr) { sl = new ImageDataItem(*vol, m_ImageDescriptor, t, 2, data, importMemoryManagement == ManageMemory, ((size_t)s) * m_OffsetTable[2] * (ptypeSize)); sl->SetComplete(true); return m_Slices[pos] = sl; } // is slice available as part of a channel that is available? ch = m_Channels[n]; if (ch.GetPointer() != nullptr) { sl = new ImageDataItem(*ch, m_ImageDescriptor, t, 2, data, importMemoryManagement == ManageMemory, (((size_t)s) * m_OffsetTable[2] + ((size_t)t) * m_OffsetTable[3]) * (ptypeSize)); sl->SetComplete(true); return m_Slices[pos] = sl; } // allocate new volume (instead of a single slice to keep data together!) m_Volumes[GetVolumeIndex(t, n)] = vol = AllocateVolumeData_unlocked(t, n, nullptr, importMemoryManagement); sl = new ImageDataItem(*vol, m_ImageDescriptor, t, 2, data, importMemoryManagement == ManageMemory, ((size_t)s) * m_OffsetTable[2] * (ptypeSize)); sl->SetComplete(true); return m_Slices[pos] = sl; ////ALTERNATIVE: //// allocate new slice // sl=new ImageDataItem(*m_PixelType, 2, m_Dimensions); // m_Slices[pos]=sl; // return vol; } mitk::Image::ImageDataItemPointer mitk::Image::AllocateVolumeData( int t, int n, void *data, ImportMemoryManagementType importMemoryManagement) const { MutexHolder lock(m_ImageDataArraysLock); return AllocateVolumeData_unlocked(t, n, data, importMemoryManagement); } mitk::Image::ImageDataItemPointer mitk::Image::AllocateVolumeData_unlocked( int t, int n, void *data, ImportMemoryManagementType importMemoryManagement) const { int pos; pos = GetVolumeIndex(t, n); const size_t ptypeSize = this->m_ImageDescriptor->GetChannelTypeById(n).GetSize(); // is volume available as part of a channel that is available? ImageDataItemPointer ch, vol; ch = m_Channels[n]; if (ch.GetPointer() != nullptr) { vol = new ImageDataItem(*ch, m_ImageDescriptor, t, 3, data, importMemoryManagement == ManageMemory, (((size_t)t) * m_OffsetTable[3]) * (ptypeSize)); return m_Volumes[pos] = vol; } mitk::PixelType chPixelType = this->m_ImageDescriptor->GetChannelTypeById(n); // allocate new volume if (importMemoryManagement == CopyMemory) { vol = new ImageDataItem(chPixelType, t, 3, m_Dimensions, nullptr, true); if (data != nullptr) std::memcpy(vol->GetData(), data, m_OffsetTable[3] * (ptypeSize)); } else { vol = new ImageDataItem(chPixelType, t, 3, m_Dimensions, data, importMemoryManagement == ManageMemory); } m_Volumes[pos] = vol; return vol; } mitk::Image::ImageDataItemPointer mitk::Image::AllocateChannelData( int n, void *data, ImportMemoryManagementType importMemoryManagement) const { MutexHolder lock(m_ImageDataArraysLock); return AllocateChannelData_unlocked(n, data, importMemoryManagement); } mitk::Image::ImageDataItemPointer mitk::Image::AllocateChannelData_unlocked( int n, void *data, ImportMemoryManagementType importMemoryManagement) const { ImageDataItemPointer ch; // allocate new channel if (importMemoryManagement == CopyMemory) { const size_t ptypeSize = this->m_ImageDescriptor->GetChannelTypeById(n).GetSize(); ch = new ImageDataItem(this->m_ImageDescriptor, -1, nullptr, true); if (data != nullptr) std::memcpy(ch->GetData(), data, m_OffsetTable[4] * (ptypeSize)); } else { ch = new ImageDataItem(this->m_ImageDescriptor, -1, data, importMemoryManagement == ManageMemory); } m_Channels[n] = ch; return ch; } unsigned int *mitk::Image::GetDimensions() const { return m_Dimensions; } void mitk::Image::Clear() { Superclass::Clear(); delete[] m_Dimensions; m_Dimensions = nullptr; } void mitk::Image::SetGeometry(BaseGeometry *aGeometry3D) { // Please be aware of the 0.5 offset/pixel-center issue! See Geometry documentation for further information if (aGeometry3D->GetImageGeometry() == false) { MITK_INFO << "WARNING: Applied a non-image geometry onto an image. Please be SURE that this geometry is " "pixel-center-based! If it is not, you need to call " "Geometry3D->ChangeImageGeometryConsideringOriginOffset(true) before calling image->setGeometry(..)\n"; } Superclass::SetGeometry(aGeometry3D); for (TimeStepType step = 0; step < GetTimeGeometry()->CountTimeSteps(); ++step) GetTimeGeometry()->GetGeometryForTimeStep(step)->ImageGeometryOn(); } void mitk::Image::PrintSelf(std::ostream &os, itk::Indent indent) const { if (m_Initialized) { unsigned char i; os << indent << " Dimension: " << m_Dimension << std::endl; os << indent << " Dimensions: "; for (i = 0; i < m_Dimension; ++i) os << GetDimension(i) << " "; os << std::endl; for (unsigned int ch = 0; ch < this->m_ImageDescriptor->GetNumberOfChannels(); ch++) { mitk::PixelType chPixelType = this->m_ImageDescriptor->GetChannelTypeById(ch); os << indent << " Channel: " << this->m_ImageDescriptor->GetChannelName(ch) << std::endl; os << indent << " PixelType: " << chPixelType.GetPixelTypeAsString() << std::endl; os << indent << " BytesPerElement: " << chPixelType.GetSize() << std::endl; os << indent << " ComponentType: " << chPixelType.GetComponentTypeAsString() << std::endl; os << indent << " NumberOfComponents: " << chPixelType.GetNumberOfComponents() << std::endl; os << indent << " BitsPerComponent: " << chPixelType.GetBitsPerComponent() << std::endl; } } else { os << indent << " Image not initialized: m_Initialized: false" << std::endl; } Superclass::PrintSelf(os, indent); } bool mitk::Image::IsRotated() const { const mitk::BaseGeometry *geo = this->GetGeometry(); bool ret = false; if (geo) { const vnl_matrix_fixed &mx = geo->GetIndexToWorldTransform()->GetMatrix().GetVnlMatrix(); mitk::ScalarType ref = 0; for (short k = 0; k < 3; ++k) ref += mx[k][k]; ref /= 1000; // Arbitrary value; if a non-diagonal (nd) element is bigger then this, matrix is considered nd. for (short i = 0; i < 3; ++i) { for (short j = 0; j < 3; ++j) { if (i != j) { if (std::abs(mx[i][j]) > ref) // matrix is nd ret = true; } } } } return ret; } mitk::ScalarType mitk::Image::GetScalarValueMin(int t) const { return m_ImageStatistics->GetScalarValueMin(t); } //## \brief Get the maximum for scalar images mitk::ScalarType mitk::Image::GetScalarValueMax(int t) const { return m_ImageStatistics->GetScalarValueMax(t); } //## \brief Get the second smallest value for scalar images mitk::ScalarType mitk::Image::GetScalarValue2ndMin(int t) const { return m_ImageStatistics->GetScalarValue2ndMin(t); } mitk::ScalarType mitk::Image::GetScalarValueMinNoRecompute(unsigned int t) const { return m_ImageStatistics->GetScalarValueMinNoRecompute(t); } mitk::ScalarType mitk::Image::GetScalarValue2ndMinNoRecompute(unsigned int t) const { return m_ImageStatistics->GetScalarValue2ndMinNoRecompute(t); } mitk::ScalarType mitk::Image::GetScalarValue2ndMax(int t) const { return m_ImageStatistics->GetScalarValue2ndMax(t); } mitk::ScalarType mitk::Image::GetScalarValueMaxNoRecompute(unsigned int t) const { return m_ImageStatistics->GetScalarValueMaxNoRecompute(t); } mitk::ScalarType mitk::Image::GetScalarValue2ndMaxNoRecompute(unsigned int t) const { return m_ImageStatistics->GetScalarValue2ndMaxNoRecompute(t); } mitk::ScalarType mitk::Image::GetCountOfMinValuedVoxels(int t) const { return m_ImageStatistics->GetCountOfMinValuedVoxels(t); } mitk::ScalarType mitk::Image::GetCountOfMaxValuedVoxels(int t) const { return m_ImageStatistics->GetCountOfMaxValuedVoxels(t); } unsigned int mitk::Image::GetCountOfMaxValuedVoxelsNoRecompute(unsigned int t) const { return m_ImageStatistics->GetCountOfMaxValuedVoxelsNoRecompute(t); } unsigned int mitk::Image::GetCountOfMinValuedVoxelsNoRecompute(unsigned int t) const { return m_ImageStatistics->GetCountOfMinValuedVoxelsNoRecompute(t); } bool mitk::Equal(const mitk::Image *leftHandSide, const mitk::Image *rightHandSide, ScalarType eps, bool verbose) { if ((leftHandSide == nullptr) || (rightHandSide == nullptr)) { MITK_ERROR << "mitk::Equal(const mitk::Image* leftHandSide, const mitk::Image* rightHandSide, ScalarType eps, bool " "verbose) does not work with nullptr pointer input."; return false; } return mitk::Equal(*leftHandSide, *rightHandSide, eps, verbose); } bool mitk::Equal(const mitk::Image &leftHandSide, const mitk::Image &rightHandSide, ScalarType eps, bool verbose) { bool returnValue = true; // Dimensionality if (rightHandSide.GetDimension() != leftHandSide.GetDimension()) { if (verbose) { MITK_INFO << "[( Image )] Dimensionality differs."; MITK_INFO << "leftHandSide is " << leftHandSide.GetDimension() << "rightHandSide is " << rightHandSide.GetDimension(); } returnValue = false; } // Pair-wise dimension (size) comparison unsigned int minDimensionality = std::min(rightHandSide.GetDimension(), leftHandSide.GetDimension()); for (unsigned int i = 0; i < minDimensionality; ++i) { if (rightHandSide.GetDimension(i) != leftHandSide.GetDimension(i)) { returnValue = false; if (verbose) { MITK_INFO << "[( Image )] dimension differs."; MITK_INFO << "leftHandSide->GetDimension(" << i << ") is " << leftHandSide.GetDimension(i) << "rightHandSide->GetDimension(" << i << ") is " << rightHandSide.GetDimension(i); } } } // Pixeltype mitk::PixelType pixelTypeRightHandSide = rightHandSide.GetPixelType(); mitk::PixelType pixelTypeLeftHandSide = leftHandSide.GetPixelType(); if (!(pixelTypeRightHandSide == pixelTypeLeftHandSide)) { if (verbose) { MITK_INFO << "[( Image )] PixelType differs."; MITK_INFO << "leftHandSide is " << pixelTypeLeftHandSide.GetTypeAsString() << "rightHandSide is " << pixelTypeRightHandSide.GetTypeAsString(); } returnValue = false; } // Geometries if (!mitk::Equal(*leftHandSide.GetGeometry(), *rightHandSide.GetGeometry(), eps, verbose)) { if (verbose) { MITK_INFO << "[( Image )] Geometries differ."; } returnValue = false; } // Pixel values - default mode [ 0 threshold in difference ] // compare only if all previous checks were successfull, otherwise the ITK filter will throw an exception if (returnValue) { mitk::CompareImageDataFilter::Pointer compareFilter = mitk::CompareImageDataFilter::New(); compareFilter->SetInput(0, &rightHandSide); compareFilter->SetInput(1, &leftHandSide); compareFilter->SetTolerance(eps); compareFilter->Update(); if ((!compareFilter->GetResult())) { returnValue = false; if (verbose) { MITK_INFO << "[(Image)] Pixel values differ: "; compareFilter->GetCompareResults().PrintSelf(); } } } return returnValue; } diff --git a/Modules/Core/src/DataManagement/mitkImageStatisticsHolder.cpp b/Modules/Core/src/DataManagement/mitkImageStatisticsHolder.cpp index f1abea383c..4e352b644e 100644 --- a/Modules/Core/src/DataManagement/mitkImageStatisticsHolder.cpp +++ b/Modules/Core/src/DataManagement/mitkImageStatisticsHolder.cpp @@ -1,389 +1,389 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkImageStatisticsHolder.h" #include "mitkHistogramGenerator.h" //#include "mitkImageTimeSelector.h" #include mitk::ImageStatisticsHolder::ImageStatisticsHolder(mitk::Image *image) : m_Image(image) /*, m_TimeSelectorForExtremaObject(nullptr)*/ { m_CountOfMinValuedVoxels.resize(1, 0); m_CountOfMaxValuedVoxels.resize(1, 0); m_ScalarMin.resize(1, itk::NumericTraits::max()); m_ScalarMax.resize(1, itk::NumericTraits::NonpositiveMin()); m_Scalar2ndMin.resize(1, itk::NumericTraits::max()); m_Scalar2ndMax.resize(1, itk::NumericTraits::NonpositiveMin()); mitk::HistogramGenerator::Pointer generator = mitk::HistogramGenerator::New(); m_HistogramGeneratorObject = generator; // m_Image = image; // create time selector // this->GetTimeSelector(); } mitk::ImageStatisticsHolder::~ImageStatisticsHolder() { m_HistogramGeneratorObject = nullptr; // m_TimeSelectorForExtremaObject = nullptr; // m_Image = nullptr; } const mitk::ImageStatisticsHolder::HistogramType *mitk::ImageStatisticsHolder::GetScalarHistogram( int t, unsigned int /*component*/) { mitk::ImageTimeSelector *timeSelector = this->GetTimeSelector(); if (timeSelector != nullptr) { timeSelector->SetTimeNr(t); timeSelector->UpdateLargestPossibleRegion(); - mitk::HistogramGenerator *generator = + auto *generator = static_cast(m_HistogramGeneratorObject.GetPointer()); generator->SetImage(timeSelector->GetOutput()); generator->ComputeHistogram(); return static_cast(generator->GetHistogram()); } return nullptr; } bool mitk::ImageStatisticsHolder::IsValidTimeStep(int t) const { return m_Image->IsValidTimeStep(t); } mitk::ImageTimeSelector::Pointer mitk::ImageStatisticsHolder::GetTimeSelector() { // if(m_TimeSelectorForExtremaObject.IsNull()) //{ // m_TimeSelectorForExtremaObject = ImageTimeSelector::New(); ImageTimeSelector::Pointer timeSelector = ImageTimeSelector::New(); // static_cast( m_TimeSelectorForExtremaObject.GetPointer() ); timeSelector->SetInput(m_Image); //} return timeSelector; // static_cast( m_TimeSelectorForExtremaObject.GetPointer() ); } void mitk::ImageStatisticsHolder::Expand(unsigned int timeSteps) { if (!m_Image->IsValidTimeStep(timeSteps - 1)) return; // The BaseData needs to be expanded, call the mitk::Image::Expand() method m_Image->Expand(timeSteps); if (timeSteps > m_ScalarMin.size()) { m_ScalarMin.resize(timeSteps, itk::NumericTraits::max()); m_ScalarMax.resize(timeSteps, itk::NumericTraits::NonpositiveMin()); m_Scalar2ndMin.resize(timeSteps, itk::NumericTraits::max()); m_Scalar2ndMax.resize(timeSteps, itk::NumericTraits::NonpositiveMin()); m_CountOfMinValuedVoxels.resize(timeSteps, 0); m_CountOfMaxValuedVoxels.resize(timeSteps, 0); } } void mitk::ImageStatisticsHolder::ResetImageStatistics() { m_ScalarMin.assign(1, itk::NumericTraits::max()); m_ScalarMax.assign(1, itk::NumericTraits::NonpositiveMin()); m_Scalar2ndMin.assign(1, itk::NumericTraits::max()); m_Scalar2ndMax.assign(1, itk::NumericTraits::NonpositiveMin()); m_CountOfMinValuedVoxels.assign(1, 0); m_CountOfMaxValuedVoxels.assign(1, 0); } #include "mitkImageAccessByItk.h" //#define BOUNDINGOBJECT_IGNORE template void mitk::_ComputeExtremaInItkImage(const ItkImageType *itkImage, mitk::ImageStatisticsHolder *statisticsHolder, int t) { typename ItkImageType::RegionType region; region = itkImage->GetBufferedRegion(); if (region.Crop(itkImage->GetRequestedRegion()) == false) return; if (region != itkImage->GetRequestedRegion()) return; itk::ImageRegionConstIterator it(itkImage, region); typedef typename ItkImageType::PixelType TPixel; TPixel value = 0; if (statisticsHolder == nullptr || !statisticsHolder->IsValidTimeStep(t)) return; statisticsHolder->Expand(t + 1); // make sure we have initialized all arrays statisticsHolder->m_CountOfMinValuedVoxels[t] = 0; statisticsHolder->m_CountOfMaxValuedVoxels[t] = 0; statisticsHolder->m_Scalar2ndMin[t] = statisticsHolder->m_ScalarMin[t] = itk::NumericTraits::max(); statisticsHolder->m_Scalar2ndMax[t] = statisticsHolder->m_ScalarMax[t] = itk::NumericTraits::NonpositiveMin(); while (!it.IsAtEnd()) { value = it.Get(); // if ( (value > mitkImage->m_ScalarMin) && (value < mitkImage->m_Scalar2ndMin) ) mitkImage->m_Scalar2ndMin = // value; // else if ( (value < mitkImage->m_ScalarMax) && (value > mitkImage->m_Scalar2ndMax) ) mitkImage->m_Scalar2ndMax = // value; // else if (value > mitkImage->m_ScalarMax) mitkImage->m_ScalarMax = // value; // else if (value < mitkImage->m_ScalarMin) mitkImage->m_ScalarMin = // value; // if numbers start with 2ndMin or 2ndMax and never have that value again, the previous above logic failed #ifdef BOUNDINGOBJECT_IGNORE if (value > -32765) { #endif // update min if (value < statisticsHolder->m_ScalarMin[t]) { statisticsHolder->m_Scalar2ndMin[t] = statisticsHolder->m_ScalarMin[t]; statisticsHolder->m_ScalarMin[t] = value; statisticsHolder->m_CountOfMinValuedVoxels[t] = 1; } else if (value == statisticsHolder->m_ScalarMin[t]) { ++statisticsHolder->m_CountOfMinValuedVoxels[t]; } else if (value < statisticsHolder->m_Scalar2ndMin[t]) { statisticsHolder->m_Scalar2ndMin[t] = value; } // update max if (value > statisticsHolder->m_ScalarMax[t]) { statisticsHolder->m_Scalar2ndMax[t] = statisticsHolder->m_ScalarMax[t]; statisticsHolder->m_ScalarMax[t] = value; statisticsHolder->m_CountOfMaxValuedVoxels[t] = 1; } else if (value == statisticsHolder->m_ScalarMax[t]) { ++statisticsHolder->m_CountOfMaxValuedVoxels[t]; } else if (value > statisticsHolder->m_Scalar2ndMax[t]) { statisticsHolder->m_Scalar2ndMax[t] = value; } #ifdef BOUNDINGOBJECT_IGNORE } #endif ++it; } //// guard for wrong 2dMin/Max on single constant value images if (statisticsHolder->m_ScalarMax[t] == statisticsHolder->m_ScalarMin[t]) { statisticsHolder->m_Scalar2ndMax[t] = statisticsHolder->m_Scalar2ndMin[t] = statisticsHolder->m_ScalarMax[t]; } statisticsHolder->m_LastRecomputeTimeStamp.Modified(); // MITK_DEBUG <<"extrema "<::NonpositiveMin()<<" "<m_ScalarMin<<" // "<m_Scalar2ndMin<<" "<m_Scalar2ndMax<<" "<m_ScalarMax<<" // "<::max(); } template void mitk::_ComputeExtremaInItkVectorImage(const ItkImageType *itkImage, mitk::ImageStatisticsHolder *statisticsHolder, int t, unsigned int component) { typename ItkImageType::RegionType region; region = itkImage->GetBufferedRegion(); if (region.Crop(itkImage->GetRequestedRegion()) == false) return; if (region != itkImage->GetRequestedRegion()) return; itk::ImageRegionConstIterator it(itkImage, region); if (statisticsHolder == nullptr || !statisticsHolder->IsValidTimeStep(t)) return; statisticsHolder->Expand(t + 1); // make sure we have initialized all arrays statisticsHolder->m_CountOfMinValuedVoxels[t] = 0; statisticsHolder->m_CountOfMaxValuedVoxels[t] = 0; statisticsHolder->m_Scalar2ndMin[t] = statisticsHolder->m_ScalarMin[t] = itk::NumericTraits::max(); statisticsHolder->m_Scalar2ndMax[t] = statisticsHolder->m_ScalarMax[t] = itk::NumericTraits::NonpositiveMin(); while (!it.IsAtEnd()) { double value = it.Get()[component]; // if ( (value > mitkImage->m_ScalarMin) && (value < mitkImage->m_Scalar2ndMin) ) mitkImage->m_Scalar2ndMin = // value; // else if ( (value < mitkImage->m_ScalarMax) && (value > mitkImage->m_Scalar2ndMax) ) mitkImage->m_Scalar2ndMax = // value; // else if (value > mitkImage->m_ScalarMax) mitkImage->m_ScalarMax = // value; // else if (value < mitkImage->m_ScalarMin) mitkImage->m_ScalarMin = // value; // if numbers start with 2ndMin or 2ndMax and never have that value again, the previous above logic failed #ifdef BOUNDINGOBJECT_IGNORE if (value > -32765) { #endif // update min if (value < statisticsHolder->m_ScalarMin[t]) { statisticsHolder->m_Scalar2ndMin[t] = statisticsHolder->m_ScalarMin[t]; statisticsHolder->m_ScalarMin[t] = value; statisticsHolder->m_CountOfMinValuedVoxels[t] = 1; } else if (value == statisticsHolder->m_ScalarMin[t]) { ++statisticsHolder->m_CountOfMinValuedVoxels[t]; } else if (value < statisticsHolder->m_Scalar2ndMin[t]) { statisticsHolder->m_Scalar2ndMin[t] = value; } // update max if (value > statisticsHolder->m_ScalarMax[t]) { statisticsHolder->m_Scalar2ndMax[t] = statisticsHolder->m_ScalarMax[t]; statisticsHolder->m_ScalarMax[t] = value; statisticsHolder->m_CountOfMaxValuedVoxels[t] = 1; } else if (value == statisticsHolder->m_ScalarMax[t]) { ++statisticsHolder->m_CountOfMaxValuedVoxels[t]; } else if (value > statisticsHolder->m_Scalar2ndMax[t]) { statisticsHolder->m_Scalar2ndMax[t] = value; } #ifdef BOUNDINGOBJECT_IGNORE } #endif ++it; } //// guard for wrong 2dMin/Max on single constant value images if (statisticsHolder->m_ScalarMax[t] == statisticsHolder->m_ScalarMin[t]) { statisticsHolder->m_Scalar2ndMax[t] = statisticsHolder->m_Scalar2ndMin[t] = statisticsHolder->m_ScalarMax[t]; } statisticsHolder->m_LastRecomputeTimeStamp.Modified(); // MITK_DEBUG <<"extrema "<::NonpositiveMin()<<" "<m_ScalarMin<<" // "<m_Scalar2ndMin<<" "<m_Scalar2ndMax<<" "<m_ScalarMax<<" // "<::max(); } void mitk::ImageStatisticsHolder::ComputeImageStatistics(int t, unsigned int component) { // timestep valid? if (!m_Image->IsValidTimeStep(t)) return; // image modified? if (this->m_Image->GetMTime() > m_LastRecomputeTimeStamp.GetMTime()) this->ResetImageStatistics(); Expand(t + 1); // do we have valid information already? if (m_ScalarMin[t] != itk::NumericTraits::max() || m_Scalar2ndMin[t] != itk::NumericTraits::max()) return; // Values already calculated before... // used to avoid statistics calculation on Odf images. property will be replaced as soons as bug 17928 is merged and // the diffusion image refactoring is complete. mitk::BoolProperty *isOdf = dynamic_cast(m_Image->GetProperty("IsOdfImage").GetPointer()); const mitk::PixelType pType = m_Image->GetPixelType(0); if (pType.GetNumberOfComponents() == 1 && (pType.GetPixelType() != itk::ImageIOBase::UNKNOWNPIXELTYPE) && (pType.GetPixelType() != itk::ImageIOBase::VECTOR)) { // recompute mitk::ImageTimeSelector::Pointer timeSelector = this->GetTimeSelector(); if (timeSelector.IsNotNull()) { timeSelector->SetTimeNr(t); timeSelector->UpdateLargestPossibleRegion(); const mitk::Image *image = timeSelector->GetOutput(); AccessByItk_2(image, _ComputeExtremaInItkImage, this, t); } } else if (pType.GetPixelType() == itk::ImageIOBase::VECTOR && (!isOdf || !isOdf->GetValue())) // we have a vector image { // recompute mitk::ImageTimeSelector::Pointer timeSelector = this->GetTimeSelector(); if (timeSelector.IsNotNull()) { timeSelector->SetTimeNr(t); timeSelector->UpdateLargestPossibleRegion(); const mitk::Image *image = timeSelector->GetOutput(); AccessVectorPixelTypeByItk_n(image, _ComputeExtremaInItkVectorImage, (this, t, component)); } } else { m_ScalarMin[t] = 0; m_ScalarMax[t] = 255; m_Scalar2ndMin[t] = 0; m_Scalar2ndMax[t] = 255; } } mitk::ScalarType mitk::ImageStatisticsHolder::GetScalarValueMin(int t, unsigned int component) { ComputeImageStatistics(t, component); return m_ScalarMin[t]; } mitk::ScalarType mitk::ImageStatisticsHolder::GetScalarValueMax(int t, unsigned int component) { ComputeImageStatistics(t, component); return m_ScalarMax[t]; } mitk::ScalarType mitk::ImageStatisticsHolder::GetScalarValue2ndMin(int t, unsigned int component) { ComputeImageStatistics(t, component); return m_Scalar2ndMin[t]; } mitk::ScalarType mitk::ImageStatisticsHolder::GetScalarValue2ndMax(int t, unsigned int component) { ComputeImageStatistics(t, component); return m_Scalar2ndMax[t]; } mitk::ScalarType mitk::ImageStatisticsHolder::GetCountOfMinValuedVoxels(int t, unsigned int component) { ComputeImageStatistics(t, component); return m_CountOfMinValuedVoxels[t]; } mitk::ScalarType mitk::ImageStatisticsHolder::GetCountOfMaxValuedVoxels(int t, unsigned int component) { ComputeImageStatistics(t, component); return m_CountOfMaxValuedVoxels[t]; } diff --git a/Modules/Core/src/DataManagement/mitkLevelWindowManager.cpp b/Modules/Core/src/DataManagement/mitkLevelWindowManager.cpp index 75fd6cc88e..6d8667287a 100644 --- a/Modules/Core/src/DataManagement/mitkLevelWindowManager.cpp +++ b/Modules/Core/src/DataManagement/mitkLevelWindowManager.cpp @@ -1,622 +1,622 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkLevelWindowManager.h" #include "mitkDataStorage.h" #include "mitkImage.h" #include "mitkMessage.h" #include "mitkNodePredicateAnd.h" #include "mitkNodePredicateBase.h" #include "mitkNodePredicateDataType.h" #include "mitkNodePredicateNot.h" #include "mitkNodePredicateOr.h" #include "mitkNodePredicateProperty.h" #include "mitkProperties.h" #include "mitkRenderingModeProperty.h" #include mitk::LevelWindowManager::LevelWindowManager() : m_DataStorage(nullptr), m_LevelWindowProperty(nullptr), m_AutoTopMost(true), m_IsObserverTagSet(false), m_CurrentImage(nullptr), m_IsPropertyModifiedTagSet(false), m_SettingImgForLvlWinProp(false) { } mitk::LevelWindowManager::~LevelWindowManager() { if (m_DataStorage.IsNotNull()) { m_DataStorage->AddNodeEvent.RemoveListener( MessageDelegate1(this, &LevelWindowManager::DataStorageAddedNode)); m_DataStorage->RemoveNodeEvent.RemoveListener( MessageDelegate1(this, &LevelWindowManager::DataStorageRemovedNode)); m_DataStorage = nullptr; } if (m_IsPropertyModifiedTagSet && m_LevelWindowProperty.IsNotNull()) { m_LevelWindowProperty->RemoveObserver(m_PropertyModifiedTag); m_IsPropertyModifiedTagSet = false; } // clear both observer maps this->ClearPropObserverLists(); } void mitk::LevelWindowManager::SetDataStorage(mitk::DataStorage *ds) { if (ds == nullptr) return; /* remove listeners of old DataStorage */ if (m_DataStorage.IsNotNull()) { m_DataStorage->AddNodeEvent.RemoveListener( MessageDelegate1(this, &LevelWindowManager::DataStorageAddedNode)); m_DataStorage->RemoveNodeEvent.RemoveListener( MessageDelegate1(this, &LevelWindowManager::DataStorageRemovedNode)); } /* register listener for new DataStorage */ m_DataStorage = ds; // register m_DataStorage->AddNodeEvent.AddListener( MessageDelegate1(this, &LevelWindowManager::DataStorageAddedNode)); m_DataStorage->RemoveNodeEvent.AddListener( MessageDelegate1(this, &LevelWindowManager::DataStorageRemovedNode)); this->DataStorageAddedNode(); // update us with new DataStorage } void mitk::LevelWindowManager::OnPropertyModified(const itk::EventObject &) { Modified(); } void mitk::LevelWindowManager::SetAutoTopMostImage(bool autoTopMost, const mitk::DataNode *removedNode) { m_AutoTopMost = autoTopMost; if (m_AutoTopMost == false) return; if (m_IsPropertyModifiedTagSet && m_LevelWindowProperty.IsNotNull()) { m_LevelWindowProperty->RemoveObserver(m_PropertyModifiedTag); m_IsPropertyModifiedTagSet = false; } /* search topmost image */ if (m_DataStorage.IsNull()) { itkExceptionMacro("DataStorage not set"); } int maxLayer = itk::NumericTraits::min(); m_LevelWindowProperty = nullptr; mitk::DataNode::Pointer topLevelNode; mitk::DataStorage::SetOfObjects::ConstPointer all = this->GetRelevantNodes(); for (mitk::DataStorage::SetOfObjects::ConstIterator it = all->Begin(); it != all->End(); ++it) { mitk::DataNode::Pointer node = it->Value(); if (node.IsNull() || (removedNode != nullptr && node == removedNode)) continue; m_SettingImgForLvlWinProp = true; node->SetBoolProperty("imageForLevelWindow", false); m_SettingImgForLvlWinProp = false; if (node->IsVisible(nullptr) == false) continue; int layer = 0; node->GetIntProperty("layer", layer); if (layer < maxLayer) continue; mitk::LevelWindowProperty::Pointer levelWindowProperty = dynamic_cast(node->GetProperty("levelwindow")); if (levelWindowProperty.IsNull()) continue; int nonLvlWinMode1 = mitk::RenderingModeProperty::LOOKUPTABLE_COLOR; int nonLvlWinMode2 = mitk::RenderingModeProperty::COLORTRANSFERFUNCTION_COLOR; mitk::RenderingModeProperty::Pointer mode = dynamic_cast(node->GetProperty("Image Rendering.Mode")); if (mode.IsNotNull()) { int currMode = mode->GetRenderingMode(); if (currMode == nonLvlWinMode1 || currMode == nonLvlWinMode2) { continue; } } else continue; m_LevelWindowProperty = levelWindowProperty; m_CurrentImage = dynamic_cast(node->GetData()); topLevelNode = node; maxLayer = layer; } if (topLevelNode.IsNotNull()) { m_SettingImgForLvlWinProp = true; topLevelNode->SetBoolProperty("imageForLevelWindow", true); m_SettingImgForLvlWinProp = false; } this->SetLevelWindowProperty(m_LevelWindowProperty); if (m_LevelWindowProperty.IsNull()) { Modified(); } // else SetLevelWindowProperty will call Modified(); } // sets an specific LevelWindowProperty, all changes will affect the image belonging to this property. void mitk::LevelWindowManager::SetLevelWindowProperty(LevelWindowProperty::Pointer levelWindowProperty) { if (levelWindowProperty.IsNull()) return; /* search image than belongs to the property */ typedef mitk::DataStorage::SetOfObjects NodeSetType; NodeSetType::ConstPointer nodes = m_DataStorage->GetAll(); NodeSetType::ConstIterator it = nodes->Begin(); mitk::DataNode::Pointer propNode = nullptr; while (it != nodes->End()) { mitk::DataNode::Pointer node = it.Value(); mitk::LevelWindowProperty::Pointer prop = dynamic_cast(node->GetProperty("levelwindow")); if (prop == levelWindowProperty) { propNode = node; } else { m_SettingImgForLvlWinProp = true; node->SetBoolProperty("imageForLevelWindow", false); m_SettingImgForLvlWinProp = false; } ++it; } if (propNode.IsNull()) { mitkThrow() << "No Image in DataStorage that belongs to LevelWindow property" << m_LevelWindowProperty; } if (m_IsPropertyModifiedTagSet) // remove listener for old property { m_LevelWindowProperty->RemoveObserver(m_PropertyModifiedTag); m_IsPropertyModifiedTagSet = false; } m_LevelWindowProperty = levelWindowProperty; itk::ReceptorMemberCommand::Pointer command = itk::ReceptorMemberCommand::New(); // register listener for new property command->SetCallbackFunction(this, &LevelWindowManager::OnPropertyModified); m_PropertyModifiedTag = m_LevelWindowProperty->AddObserver(itk::ModifiedEvent(), command); m_IsPropertyModifiedTagSet = true; m_CurrentImage = dynamic_cast(propNode->GetData()); m_SettingImgForLvlWinProp = true; propNode->SetBoolProperty("imageForLevelWindow", true); m_SettingImgForLvlWinProp = false; this->Modified(); } // returns the current mitkLevelWindowProperty object from the image that is affected by changes mitk::LevelWindowProperty::Pointer mitk::LevelWindowManager::GetLevelWindowProperty() { return m_LevelWindowProperty; } // returns Level/Window values for the current image const mitk::LevelWindow &mitk::LevelWindowManager::GetLevelWindow() { if (m_LevelWindowProperty.IsNotNull()) { return m_LevelWindowProperty->GetLevelWindow(); } else { itkExceptionMacro("No LevelWindow available!"); } } // sets new Level/Window values and informs all listeners about changes void mitk::LevelWindowManager::SetLevelWindow(const mitk::LevelWindow &levelWindow) { if (m_LevelWindowProperty.IsNotNull()) { m_LevelWindowProperty->SetLevelWindow(levelWindow); } this->Modified(); } void mitk::LevelWindowManager::DataStorageAddedNode(const mitk::DataNode *) { // update observers with new data storage UpdateObservers(); // Initialize LevelWindowsManager to new image SetAutoTopMostImage(true); // check if everything is still ok if ((m_PropObserverToNode.size() != m_PropObserverToNode2.size()) || (m_PropObserverToNode2.size() != this->GetRelevantNodes()->size())) { mitkThrow() << "Wrong number of observers in Level Window Manager!"; } } void mitk::LevelWindowManager::DataStorageRemovedNode(const mitk::DataNode *removedNode) { // first: check if deleted node is part of relevant nodes. If not, abort method because there is no need change // anything. if ((this->GetRelevantNodes()->size() == 0)) return; bool removedNodeIsRelevant = false; /* Iterator code: is crashing, don't know why... so using for loop for (mitk::DataStorage::SetOfObjects::ConstIterator it = this->GetRelevantNodes()->Begin(); it != this->GetRelevantNodes()->End(); ++it) {if (it->Value() == removedNode) {removedNodeIsRelevant=true;}}*/ for (unsigned int i = 0; i < this->GetRelevantNodes()->size(); i++) { if (this->GetRelevantNodes()->at(i) == removedNode) { removedNodeIsRelevant = true; } } if (!removedNodeIsRelevant) return; // remember node which will be removed m_NodeMarkedToDelete = removedNode; // update observers UpdateObservers(); /* search image than belongs to the property */ if (m_LevelWindowProperty.IsNull()) { SetAutoTopMostImage(true, removedNode); } else { mitk::NodePredicateProperty::Pointer p2 = mitk::NodePredicateProperty::New("levelwindow", m_LevelWindowProperty); mitk::DataNode *n = m_DataStorage->GetNode(p2); if (n == nullptr || m_AutoTopMost) // if node was deleted, change our behaviour to AutoTopMost, if AutoTopMost is true // change level window to topmost node { SetAutoTopMostImage(true, removedNode); } } // reset variable m_NodeMarkedToDelete = nullptr; // check if everything is still ok if ((m_PropObserverToNode.size() != m_PropObserverToNode2.size()) || (m_PropObserverToNode2.size() != (this->GetRelevantNodes()->size() - 1))) { mitkThrow() << "Wrong number of observers in Level Window Manager!"; } } void mitk::LevelWindowManager::UpdateObservers() { this->ClearPropObserverLists(); // remove old observers CreatePropObserverLists(); // create new observer lists } int mitk::LevelWindowManager::GetNumberOfObservers() { return m_PropObserverToNode.size(); } mitk::DataStorage *mitk::LevelWindowManager::GetDataStorage() { return m_DataStorage.GetPointer(); } // true if changes on slider or line-edits will affect always the topmost layer image bool mitk::LevelWindowManager::isAutoTopMost() { return m_AutoTopMost; } void mitk::LevelWindowManager::RecaluclateLevelWindowForSelectedComponent(const itk::EventObject &event) { mitk::DataStorage::SetOfObjects::ConstPointer all = this->GetRelevantNodes(); for (mitk::DataStorage::SetOfObjects::ConstIterator it = all->Begin(); it != all->End(); ++it) { mitk::DataNode::Pointer node = it->Value(); if (node.IsNull()) continue; bool isSelected = false; node->GetBoolProperty("selected", isSelected); if (isSelected) { mitk::LevelWindow selectedLevelWindow; node->GetLevelWindow(selectedLevelWindow); // node is an image node because of predicates - mitk::Image *image = dynamic_cast(node->GetData()); + auto *image = dynamic_cast(node->GetData()); int displayedComponent = 0; if (image && (node->GetIntProperty("Image.Displayed Component", displayedComponent))) { // we found a selected image with a displayed component // let's recalculate the levelwindow for this. selectedLevelWindow.SetAuto(image, true, true, static_cast(displayedComponent)); node->SetLevelWindow(selectedLevelWindow); } } mitk::LevelWindow levelWindow; node->GetLevelWindow(levelWindow); } this->Update(event); } void mitk::LevelWindowManager::Update(const itk::EventObject &) // visible property of a image has changed { if (m_SettingImgForLvlWinProp) // no mutex, should still help { return; } if (m_AutoTopMost) { SetAutoTopMostImage(true); return; } int maxVisibleLayer = itk::NumericTraits::min(); mitk::DataNode::Pointer highestVisible = nullptr; std::vector visProbNodes; mitk::DataStorage::SetOfObjects::ConstPointer all = this->GetRelevantNodes(); for (mitk::DataStorage::SetOfObjects::ConstIterator it = all->Begin(); it != all->End(); ++it) { mitk::DataNode::Pointer node = it->Value(); if (node.IsNull()) { continue; } bool visible = node->IsVisible(nullptr); if (node->IsVisible(nullptr)) { int layer = -1; node->GetIntProperty("layer", layer); if (layer > maxVisibleLayer) { maxVisibleLayer = layer; highestVisible = node; } } bool prop = false; node->GetBoolProperty("imageForLevelWindow", prop); if (prop && visible) { visProbNodes.push_back(node); } } int numVisProbNodes = visProbNodes.size(); if (numVisProbNodes > 2) { MITK_ERROR << "Error: not more than two visible nodes are expected to have the imageForLevelWindow property set at " "any point."; } else if (numVisProbNodes == 2) { for (std::vector::const_iterator it = visProbNodes.begin(); it != visProbNodes.end(); ++it) { mitk::LevelWindowProperty::Pointer newProp = dynamic_cast((*it)->GetProperty("levelwindow")); if (newProp != m_LevelWindowProperty) { this->SetLevelWindowProperty(newProp); return; } } } else if (numVisProbNodes == 1) { mitk::LevelWindowProperty::Pointer newProp = dynamic_cast(visProbNodes[0]->GetProperty("levelwindow")); if (newProp != m_LevelWindowProperty) { this->SetLevelWindowProperty(newProp); return; } } else if (highestVisible) { mitk::LevelWindowProperty::Pointer lvlProp = dynamic_cast(highestVisible->GetProperty("levelwindow")); this->SetLevelWindowProperty(lvlProp); } else { Modified(); } } mitk::DataStorage::SetOfObjects::ConstPointer mitk::LevelWindowManager::GetRelevantNodes() { if (m_DataStorage.IsNull()) return mitk::DataStorage::SetOfObjects::ConstPointer(mitk::DataStorage::SetOfObjects::New()); // return empty set mitk::NodePredicateProperty::Pointer notBinary = mitk::NodePredicateProperty::New("binary", mitk::BoolProperty::New(false)); mitk::NodePredicateProperty::Pointer hasLevelWindow = mitk::NodePredicateProperty::New("levelwindow", nullptr); mitk::NodePredicateDataType::Pointer isImage = mitk::NodePredicateDataType::New("Image"); mitk::NodePredicateDataType::Pointer isDImage = mitk::NodePredicateDataType::New("DiffusionImage"); mitk::NodePredicateDataType::Pointer isTImage = mitk::NodePredicateDataType::New("TensorImage"); mitk::NodePredicateDataType::Pointer isOdfImage = mitk::NodePredicateDataType::New("OdfImage"); mitk::NodePredicateOr::Pointer predicateTypes = mitk::NodePredicateOr::New(); predicateTypes->AddPredicate(isImage); predicateTypes->AddPredicate(isDImage); predicateTypes->AddPredicate(isTImage); predicateTypes->AddPredicate(isOdfImage); mitk::NodePredicateAnd::Pointer predicate = mitk::NodePredicateAnd::New(); predicate->AddPredicate(notBinary); predicate->AddPredicate(hasLevelWindow); predicate->AddPredicate(predicateTypes); mitk::DataStorage::SetOfObjects::ConstPointer relevantNodes = m_DataStorage->GetSubset(predicate); return relevantNodes; } mitk::Image *mitk::LevelWindowManager::GetCurrentImage() { return m_CurrentImage; } void mitk::LevelWindowManager::ClearPropObserverLists() { - for (ObserverToPropertyMap::iterator iter = m_PropObserverToNode.begin(); iter != m_PropObserverToNode.end(); ++iter) + for (auto iter = m_PropObserverToNode.begin(); iter != m_PropObserverToNode.end(); ++iter) { (*iter).second->RemoveObserver((*iter).first.first); (*iter).second = nullptr; } m_PropObserverToNode.clear(); - for (ObserverToPropertyMap::iterator iter = m_PropObserverToNode2.begin(); iter != m_PropObserverToNode2.end(); + for (auto iter = m_PropObserverToNode2.begin(); iter != m_PropObserverToNode2.end(); ++iter) { (*iter).second->RemoveObserver((*iter).first.first); (*iter).second = nullptr; } m_PropObserverToNode2.clear(); - for (ObserverToPropertyMap::iterator iter = m_PropObserverToNode3.begin(); iter != m_PropObserverToNode3.end(); + for (auto iter = m_PropObserverToNode3.begin(); iter != m_PropObserverToNode3.end(); ++iter) { (*iter).second->RemoveObserver((*iter).first.first); (*iter).second = nullptr; } m_PropObserverToNode3.clear(); - for (ObserverToPropertyMap::iterator iter = m_PropObserverToNode4.begin(); iter != m_PropObserverToNode4.end(); + for (auto iter = m_PropObserverToNode4.begin(); iter != m_PropObserverToNode4.end(); ++iter) { (*iter).second->RemoveObserver((*iter).first.first); (*iter).second = nullptr; } m_PropObserverToNode4.clear(); - for (ObserverToPropertyMap::iterator iter = m_PropObserverToNode5.begin(); iter != m_PropObserverToNode5.end(); + for (auto iter = m_PropObserverToNode5.begin(); iter != m_PropObserverToNode5.end(); ++iter) { (*iter).second->RemoveObserver((*iter).first.first); (*iter).second = nullptr; } m_PropObserverToNode5.clear(); } void mitk::LevelWindowManager::CreatePropObserverLists() { if (m_DataStorage.IsNull()) // check if data storage is set { itkExceptionMacro("DataStorage not set"); } /* add observers for all relevant nodes */ mitk::DataStorage::SetOfObjects::ConstPointer all = this->GetRelevantNodes(); for (mitk::DataStorage::SetOfObjects::ConstIterator it = all->Begin(); it != all->End(); ++it) { if ((it->Value().IsNull()) || (it->Value() == m_NodeMarkedToDelete)) { continue; } /* register listener for changes in visible property */ itk::ReceptorMemberCommand::Pointer command = itk::ReceptorMemberCommand::New(); command->SetCallbackFunction(this, &LevelWindowManager::Update); unsigned long visIdx = it->Value()->GetProperty("visible")->AddObserver(itk::ModifiedEvent(), command); m_PropObserverToNode[PropDataPair(visIdx, it->Value())] = it->Value()->GetProperty("visible"); /* register listener for changes in layer property */ itk::ReceptorMemberCommand::Pointer command2 = itk::ReceptorMemberCommand::New(); command2->SetCallbackFunction(this, &LevelWindowManager::Update); unsigned long layerIdx = it->Value()->GetProperty("layer")->AddObserver(itk::ModifiedEvent(), command2); m_PropObserverToNode2[PropDataPair(layerIdx, it->Value())] = it->Value()->GetProperty("layer"); /* register listener for changes in layer property */ itk::ReceptorMemberCommand::Pointer command3 = itk::ReceptorMemberCommand::New(); command3->SetCallbackFunction(this, &LevelWindowManager::Update); mitk::BaseProperty::Pointer imageRenderingMode = it->Value()->GetProperty("Image Rendering.Mode"); if (imageRenderingMode.IsNotNull()) { unsigned long rendIdx = imageRenderingMode->AddObserver(itk::ModifiedEvent(), command3); m_PropObserverToNode3[PropDataPair(rendIdx, it->Value())] = imageRenderingMode.GetPointer(); } itk::ReceptorMemberCommand::Pointer command4 = itk::ReceptorMemberCommand::New(); command4->SetCallbackFunction(this, &LevelWindowManager::RecaluclateLevelWindowForSelectedComponent); mitk::BaseProperty::Pointer displayedImageComponent = it->Value()->GetProperty("Image.Displayed Component"); if (displayedImageComponent.IsNotNull()) { unsigned long dispIdx = displayedImageComponent->AddObserver(itk::ModifiedEvent(), command4); m_PropObserverToNode4[PropDataPair(dispIdx, it->Value())] = displayedImageComponent.GetPointer(); } itk::ReceptorMemberCommand::Pointer command5 = itk::ReceptorMemberCommand::New(); command5->SetCallbackFunction(this, &LevelWindowManager::Update); mitk::BaseProperty::Pointer imgForLvlWin = it->Value()->GetProperty("imageForLevelWindow"); if (imgForLvlWin.IsNull()) { it->Value()->SetBoolProperty("imageForLevelWindow", false); imgForLvlWin = it->Value()->GetProperty("imageForLevelWindow"); } unsigned long lvlWinIdx = imgForLvlWin->AddObserver(itk::ModifiedEvent(), command5); m_PropObserverToNode5[PropDataPair(lvlWinIdx, it->Value())] = it->Value()->GetProperty("imageForLevelWindow"); } } diff --git a/Modules/Core/src/DataManagement/mitkMaterial.cpp b/Modules/Core/src/DataManagement/mitkMaterial.cpp index 78aa2143f8..506e29ce46 100644 --- a/Modules/Core/src/DataManagement/mitkMaterial.cpp +++ b/Modules/Core/src/DataManagement/mitkMaterial.cpp @@ -1,380 +1,380 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkMaterial.h" #include "mitkBaseRenderer.h" #include "mitkDataNode.h" #include "mitkProperties.h" #include "mitkVtkInterpolationProperty.h" #include "mitkVtkRepresentationProperty.h" #include mitk::Material::Material(Color color, double opacity) { InitializeStandardValues(); SetColor(color); SetColorCoefficient(GetColorCoefficient()); SetSpecularColor(GetSpecularColor()); SetSpecularCoefficient(GetSpecularCoefficient()); SetSpecularPower(GetSpecularPower()); SetOpacity(opacity); SetInterpolation(GetInterpolation()); SetRepresentation(GetRepresentation()); SetLineWidth(GetLineWidth()); m_Name = ""; } mitk::Material::Material(double red, double green, double blue, double opacity) { InitializeStandardValues(); SetColor(red, green, blue); SetColorCoefficient(GetColorCoefficient()); SetSpecularColor(GetSpecularColor()); SetSpecularCoefficient(GetSpecularCoefficient()); SetSpecularPower(GetSpecularPower()); SetOpacity(opacity); SetInterpolation(GetInterpolation()); SetRepresentation(GetRepresentation()); SetLineWidth(GetLineWidth()); m_Name = ""; } mitk::Material::Material(double red, double green, double blue, double colorCoefficient, double specularCoefficient, double specularPower, double opacity) { InitializeStandardValues(); SetColor(red, green, blue); SetColorCoefficient(colorCoefficient); SetSpecularColor(GetSpecularColor()); SetSpecularCoefficient(specularCoefficient); SetSpecularPower(specularPower); SetOpacity(opacity); SetInterpolation(GetInterpolation()); SetRepresentation(GetRepresentation()); SetLineWidth(GetLineWidth()); m_Name = ""; } mitk::Material::Material(mitk::Material::Color color, double colorCoefficient, double specularCoefficient, double specularPower, double opacity) { InitializeStandardValues(); SetColor(color); SetColorCoefficient(colorCoefficient); SetSpecularColor(GetSpecularColor()); SetSpecularCoefficient(specularCoefficient); SetSpecularPower(specularPower); SetOpacity(opacity); SetInterpolation(GetInterpolation()); SetRepresentation(GetRepresentation()); SetLineWidth(GetLineWidth()); } mitk::Material::Material() { InitializeStandardValues(); SetColor(GetColor()); SetColorCoefficient(GetColorCoefficient()); SetSpecularColor(GetSpecularColor()); SetSpecularCoefficient(GetSpecularCoefficient()); SetSpecularPower(GetSpecularPower()); SetOpacity(GetOpacity()); SetInterpolation(GetInterpolation()); SetRepresentation(GetRepresentation()); SetLineWidth(GetLineWidth()); } mitk::Material::Material(const Material &property) : itk::Object() { Initialize(property); } mitk::Material::Material( const Material &property, double red, double green, double blue, double opacity, std::string name) { Initialize(property); SetColor(red, green, blue); SetOpacity(opacity); SetName(name); } bool mitk::Material::Assignable(const Material &other) const { try { - const Material &otherinstance = + const auto &otherinstance = dynamic_cast(other); // dear compiler, please don't optimize this away! Thanks. otherinstance.GetOpacity(); return true; } catch (std::bad_cast) { } return false; } mitk::Material &mitk::Material::operator=(const mitk::Material &other) { try { - const Self &otherProp(dynamic_cast(other)); + const auto &otherProp(dynamic_cast(other)); Initialize(otherProp); } catch (std::bad_cast) { // nothing to do then } return *this; } void mitk::Material::SetColor(mitk::Material::Color color) { m_Color = color; Modified(); } void mitk::Material::SetColor(double red, double green, double blue) { m_Color.Set(red, green, blue); Modified(); } void mitk::Material::SetColorCoefficient(double coefficient) { m_ColorCoefficient = coefficient; Modified(); } void mitk::Material::SetSpecularColor(mitk::Material::Color specularColor) { m_SpecularColor = specularColor; Modified(); } void mitk::Material::SetSpecularColor(double red, double green, double blue) { m_SpecularColor.Set(red, green, blue); Modified(); } void mitk::Material::SetSpecularCoefficient(double specularCoefficient) { m_SpecularCoefficient = specularCoefficient; Modified(); } void mitk::Material::SetSpecularPower(double specularPower) { m_SpecularPower = specularPower; Modified(); } void mitk::Material::SetOpacity(double opacity) { m_Opacity = opacity; Modified(); } void mitk::Material::SetInterpolation(InterpolationType interpolation) { m_Interpolation = interpolation; Modified(); } void mitk::Material::SetRepresentation(RepresentationType representation) { m_Representation = representation; Modified(); } void mitk::Material::SetLineWidth(float lineWidth) { m_LineWidth = lineWidth; Modified(); } mitk::Material::Color mitk::Material::GetColor() const { return m_Color; } double mitk::Material::GetColorCoefficient() const { return m_ColorCoefficient; } mitk::Material::Color mitk::Material::GetSpecularColor() const { return m_SpecularColor; } double mitk::Material::GetSpecularCoefficient() const { return m_SpecularCoefficient; } double mitk::Material::GetSpecularPower() const { return m_SpecularPower; } double mitk::Material::GetOpacity() const { return m_Opacity; } mitk::Material::InterpolationType mitk::Material::GetInterpolation() const { return m_Interpolation; } mitk::Material::RepresentationType mitk::Material::GetRepresentation() const { return m_Representation; } int mitk::Material::GetVtkInterpolation() const { switch (m_Interpolation) { case (Flat): return VTK_FLAT; case (Gouraud): return VTK_GOURAUD; case (Phong): return VTK_PHONG; } return VTK_GOURAUD; } int mitk::Material::GetVtkRepresentation() const { switch (m_Representation) { case (Points): return VTK_POINTS; case (Wireframe): return VTK_WIREFRAME; case (Surface): return VTK_SURFACE; } return VTK_SURFACE; } float mitk::Material::GetLineWidth() const { return m_LineWidth; } void mitk::Material::Initialize(const Material &property) { this->SetColor(property.GetColor()); this->SetColorCoefficient(property.GetColorCoefficient()); this->SetSpecularColor(property.GetSpecularColor()); this->SetSpecularCoefficient(property.GetSpecularCoefficient()); this->SetSpecularPower(property.GetSpecularPower()); this->SetOpacity(property.GetOpacity()); this->SetInterpolation(property.GetInterpolation()); this->SetRepresentation(property.GetRepresentation()); this->SetLineWidth(property.GetLineWidth()); this->SetName(property.GetName()); } bool mitk::Material::operator==(const Material &property) const { - const Self *other = dynamic_cast(&property); + const auto *other = dynamic_cast(&property); if (other == nullptr) return false; else return (m_Color == other->GetColor() && m_ColorCoefficient == other->GetColorCoefficient() && m_SpecularColor == other->GetSpecularColor() && m_SpecularCoefficient == other->GetSpecularCoefficient() && m_SpecularPower == other->GetSpecularPower() && m_Opacity == other->GetOpacity() && m_Interpolation == other->GetInterpolation() && m_Name == other->GetName() && m_Representation == other->GetRepresentation() && m_LineWidth == other->GetLineWidth()); } void mitk::Material::InitializeStandardValues() { m_Color.Set(0.5, 0.5, 0.0); m_ColorCoefficient = 0.5; m_SpecularColor.Set(1.0, 1.0, 1.0); m_SpecularCoefficient = 0.5; m_SpecularPower = 10.0; m_Opacity = 1.0; m_Interpolation = Gouraud; m_Representation = Surface; m_LineWidth = 1.0; m_Name = ""; } void mitk::Material::Update() { this->SetColor(this->GetColor()); this->SetColorCoefficient(this->GetColorCoefficient()); this->SetSpecularColor(this->GetSpecularColor()); this->SetSpecularCoefficient(this->GetSpecularCoefficient()); this->SetSpecularPower(this->GetSpecularPower()); this->SetOpacity(this->GetOpacity()); this->SetInterpolation(this->GetInterpolation()); this->SetRepresentation(this->GetRepresentation()); } void mitk::Material::PrintSelf(std::ostream &os, itk::Indent /* unused */) const { os << "Name: " << GetName() << std::endl; os << "Color: " << GetColor() << std::endl; os << "ColorCoefficient" << GetColorCoefficient() << std::endl; os << "SpecularColor: " << GetSpecularColor() << std::endl; os << "SpecularCoefficient: " << GetSpecularCoefficient() << std::endl; os << "SpecularPower: " << GetSpecularPower() << std::endl; os << "Opacity: " << GetOpacity() << std::endl; os << "Line width: " << GetLineWidth() << std::endl; switch (GetInterpolation()) { case (Flat): os << "Interpolation: Flat" << std::endl; break; case (Gouraud): os << "Interpolation: Gouraud" << std::endl; break; case (Phong): os << "Interpolation: Phong" << std::endl; break; } switch (GetRepresentation()) { case (Points): os << "Representation: Points" << std::endl; break; case (Wireframe): os << "Representation: Wireframe" << std::endl; break; case (Surface): os << "Representation: Surface" << std::endl; break; } } diff --git a/Modules/Core/src/DataManagement/mitkModalityProperty.cpp b/Modules/Core/src/DataManagement/mitkModalityProperty.cpp index 999e3669bb..7d6624ae56 100644 --- a/Modules/Core/src/DataManagement/mitkModalityProperty.cpp +++ b/Modules/Core/src/DataManagement/mitkModalityProperty.cpp @@ -1,73 +1,73 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkModalityProperty.h" mitk::ModalityProperty::ModalityProperty() { AddEnumerationTypes(); } mitk::ModalityProperty::ModalityProperty(const IdType &value) { AddEnumerationTypes(); if (IsValidEnumerationValue(value)) { SetValue(value); } else { SetValue(0); } } mitk::ModalityProperty::ModalityProperty(const std::string &value) { AddEnumerationTypes(); if (IsValidEnumerationValue(value)) { SetValue(value); } else { SetValue("undefined"); } } mitk::ModalityProperty::~ModalityProperty() { } void mitk::ModalityProperty::AddEnumerationTypes() { - IdType newId = static_cast(EnumerationProperty::Size()); + auto newId = static_cast(EnumerationProperty::Size()); AddEnum("undefined", newId++); AddEnum("CR", newId++); // computer radiography AddEnum("CT", newId++); // computed tomography AddEnum("MR", newId++); // magnetic resonance AddEnum("NM", newId++); // nuclear medicine AddEnum("US", newId++); // ultrasound AddEnum("Color Doppler", newId++); // ultrasound AddEnum("Power Doppler", newId++); // ultrasound } itk::LightObject::Pointer mitk::ModalityProperty::InternalClone() const { itk::LightObject::Pointer result(new Self(*this)); result->UnRegister(); return result; } diff --git a/Modules/Core/src/DataManagement/mitkNodePredicateDimension.cpp b/Modules/Core/src/DataManagement/mitkNodePredicateDimension.cpp index 9108fe6a54..93258994e7 100644 --- a/Modules/Core/src/DataManagement/mitkNodePredicateDimension.cpp +++ b/Modules/Core/src/DataManagement/mitkNodePredicateDimension.cpp @@ -1,47 +1,47 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkNodePredicateDimension.h" #include "mitkDataNode.h" #include "mitkImage.h" mitk::NodePredicateDimension::NodePredicateDimension(unsigned int dimension, int pixelComponents) : m_Dimension(dimension), m_PixelComponents(pixelComponents) { } mitk::NodePredicateDimension::NodePredicateDimension(unsigned int dimension) : m_Dimension(dimension), m_PixelComponents(1) { } mitk::NodePredicateDimension::~NodePredicateDimension() { } bool mitk::NodePredicateDimension::CheckNode(const mitk::DataNode *node) const { if (node == nullptr) throw std::invalid_argument("NodePredicateDimension: invalid node"); - mitk::Image *image = dynamic_cast(node->GetData()); + auto *image = dynamic_cast(node->GetData()); if (image != nullptr) { return (image->GetDimension() == m_Dimension && image->GetPixelType().GetNumberOfComponents() == m_PixelComponents); } return false; } diff --git a/Modules/Core/src/DataManagement/mitkPlaneGeometry.cpp b/Modules/Core/src/DataManagement/mitkPlaneGeometry.cpp index c9d64bffec..4be4a9b253 100644 --- a/Modules/Core/src/DataManagement/mitkPlaneGeometry.cpp +++ b/Modules/Core/src/DataManagement/mitkPlaneGeometry.cpp @@ -1,984 +1,984 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkPlaneGeometry.h" #include "mitkInteractionConst.h" #include "mitkLine.h" #include "mitkPlaneOperation.h" #include #include #include namespace mitk { PlaneGeometry::PlaneGeometry() : Superclass(), m_ReferenceGeometry(nullptr) { Initialize(); } PlaneGeometry::~PlaneGeometry() {} PlaneGeometry::PlaneGeometry(const PlaneGeometry &other) : Superclass(other), m_ReferenceGeometry(other.m_ReferenceGeometry) { } void PlaneGeometry::EnsurePerpendicularNormal(mitk::AffineTransform3D *transform) { /** \brief ensure column(2) of indexToWorldTransform-matrix to be perpendicular to plane, keep length and * handedness. */ VnlVector normal = vnl_cross_3d(transform->GetMatrix().GetVnlMatrix().get_column(0), transform->GetMatrix().GetVnlMatrix().get_column(1)); normal.normalize(); // Now normal is a righthand normal unit vector, perpendicular to the plane. ScalarType len = transform->GetMatrix().GetVnlMatrix().get_column(2).two_norm(); if (len == 0) { len = 1; } normal *= len; // Get the existing normal vector zed: vnl_vector_fixed zed = transform->GetMatrix().GetVnlMatrix().get_column(2); /** If det(matrix)<0, multiply normal vector by (-1) to keep geometry lefthanded. */ if (vnl_determinant(transform->GetMatrix().GetVnlMatrix()) < 0) { MITK_DEBUG << "EnsurePerpendicularNormal(): Lefthanded geometry preserved, rh-normal: [ " << normal << " ],"; normal *= (-1.0); MITK_DEBUG << "lh-normal: [ " << normal << " ], original vector zed is: [ " << zed << " ]"; } // Now lets compare and only replace if necessary and only then warn the user: // float epsilon is precise enough here, but we need to respect numerical condition: // Higham, N., 2002, Accuracy and Stability of Numerical Algorithms, // SIAM, page 37, 2nd edition: double feps = std::numeric_limits::epsilon(); double zedsMagnitude = zed.two_norm(); feps = feps * zedsMagnitude * 2; /** Check if normal (3. column) was perpendicular: If not, replace with calculated normal vector: */ if (normal != zed) { vnl_vector_fixed parallel; for (unsigned int i = 0; i < 3; ++i) { parallel[i] = normal[i] / zed[i]; // Remember linear algebra: checking for parallelity. } // Checking if really not paralell i.e. non-colinear vectors by comparing these floating point numbers: if ((parallel[0] + feps < parallel[1] || feps + parallel[1] < parallel[0]) && (parallel[0] + feps < parallel[2] || feps + parallel[2] < parallel[0])) { MITK_WARN << "EnsurePerpendicularNormal(): Plane geometry was _/askew/_, so here it gets rectified by substituting" << " the 3rd column of the indexToWorldMatrix with an appropriate normal vector: [ " << normal << " ], original vector zed was: [ " << zed << " ]."; Matrix3D matrix = transform->GetMatrix(); matrix.GetVnlMatrix().set_column(2, normal); transform->SetMatrix(matrix); } } else { // Nothing to do, 3rd column of indexToWorldTransformMatrix already was perfectly perpendicular. } } void PlaneGeometry::CheckIndexToWorldTransform(mitk::AffineTransform3D *transform) { EnsurePerpendicularNormal(transform); } void PlaneGeometry::CheckBounds(const BoundingBox::BoundsArrayType &bounds) { // error: unused parameter 'bounds' // this happens in release mode, where the assert macro is defined empty // hence we "use" the parameter: (void)bounds; // currently the unit rectangle must be starting at the origin [0,0] assert(bounds[0] == 0); assert(bounds[2] == 0); // the unit rectangle must be two-dimensional assert(bounds[1] > 0); assert(bounds[3] > 0); } void PlaneGeometry::IndexToWorld(const Point2D &pt_units, Point2D &pt_mm) const { pt_mm[0] = GetExtentInMM(0) / GetExtent(0) * pt_units[0]; pt_mm[1] = GetExtentInMM(1) / GetExtent(1) * pt_units[1]; } void PlaneGeometry::WorldToIndex(const Point2D &pt_mm, Point2D &pt_units) const { pt_units[0] = pt_mm[0] * (1.0 / (GetExtentInMM(0) / GetExtent(0))); pt_units[1] = pt_mm[1] * (1.0 / (GetExtentInMM(1) / GetExtent(1))); } void PlaneGeometry::IndexToWorld(const Point2D & /*atPt2d_units*/, const Vector2D &vec_units, Vector2D &vec_mm) const { MITK_WARN << "Warning! Call of the deprecated function PlaneGeometry::IndexToWorld(point, vec, vec). Use " "PlaneGeometry::IndexToWorld(vec, vec) instead!"; this->IndexToWorld(vec_units, vec_mm); } void PlaneGeometry::IndexToWorld(const Vector2D &vec_units, Vector2D &vec_mm) const { vec_mm[0] = (GetExtentInMM(0) / GetExtent(0)) * vec_units[0]; vec_mm[1] = (GetExtentInMM(1) / GetExtent(1)) * vec_units[1]; } void PlaneGeometry::WorldToIndex(const Point2D & /*atPt2d_mm*/, const Vector2D &vec_mm, Vector2D &vec_units) const { MITK_WARN << "Warning! Call of the deprecated function PlaneGeometry::WorldToIndex(point, vec, vec). Use " "PlaneGeometry::WorldToIndex(vec, vec) instead!"; this->WorldToIndex(vec_mm, vec_units); } void PlaneGeometry::WorldToIndex(const Vector2D &vec_mm, Vector2D &vec_units) const { vec_units[0] = vec_mm[0] * (1.0 / (GetExtentInMM(0) / GetExtent(0))); vec_units[1] = vec_mm[1] * (1.0 / (GetExtentInMM(1) / GetExtent(1))); } void PlaneGeometry::InitializeStandardPlane(mitk::ScalarType width, ScalarType height, const Vector3D &spacing, PlaneGeometry::PlaneOrientation planeorientation, ScalarType zPosition, bool frontside, bool rotated, bool top) { AffineTransform3D::Pointer transform; transform = AffineTransform3D::New(); AffineTransform3D::MatrixType matrix; AffineTransform3D::MatrixType::InternalMatrixType &vnlmatrix = matrix.GetVnlMatrix(); vnlmatrix.set_identity(); vnlmatrix(0, 0) = spacing[0]; vnlmatrix(1, 1) = spacing[1]; vnlmatrix(2, 2) = spacing[2]; transform->SetIdentity(); transform->SetMatrix(matrix); InitializeStandardPlane(width, height, transform.GetPointer(), planeorientation, zPosition, frontside, rotated, top); } void PlaneGeometry::InitializeStandardPlane(mitk::ScalarType width, mitk::ScalarType height, const AffineTransform3D *transform /* = nullptr */, PlaneGeometry::PlaneOrientation planeorientation /* = Axial */, mitk::ScalarType zPosition /* = 0 */, bool frontside /* = true */, bool rotated /* = false */, bool top /* = true */) { Superclass::Initialize(); /// construct standard view. // We define at the moment "frontside" as: axial from above, // coronal from front (nose), saggital from right. // TODO: Double check with medicals doctors or radiologists [ ]. // We define the orientation in patient's view, e.g. LAI is in a axial cut // (parallel to the triangle ear-ear-nose): // first axis: To the left ear of the patient // seecond axis: To the nose of the patient // third axis: To the legs of the patient. // Options are: L/R left/right; A/P anterior/posterior; I/S inferior/superior // (AKA caudal/cranial). // We note on all cases in the following switch block r.h. for right handed // or l.h. for left handed to describe the different cases. // However, which system is chosen is defined at the end of the switch block. // CAVE / be careful: the vectors right and bottom are relative to the plane // and do NOT describe e.g. the right side of the patient. Point3D origin; /** Bottom means downwards, DV means Direction Vector. Both relative to the image! */ VnlVector rightDV(3), bottomDV(3); /** Origin of this plane is by default a zero vector and implicitly in the top-left corner: */ origin.Fill(0); /** This is different to all definitions in MITK, except the QT mouse clicks. * But it is like this here and we don't want to change a running system. * Just be aware, that IN THIS FUNCTION we define the origin at the top left (e.g. your screen). */ /** NormalDirection defines which axis (i.e. column index in the transform matrix) * is perpendicular to the plane: */ int normalDirection; switch (planeorientation) // Switch through our limited choice of standard planes: { case None: /** Orientation 'None' shall be done like the axial plane orientation, * for whatever reasons. */ case Axial: if (frontside) // Radiologist's view from below. A cut along the triangle ear-ear-nose. { if (rotated == false) /** Origin in the top-left corner, x=[1; 0; 0], y=[0; 1; 0], z=[0; 0; 1], * origin=[0,0,zpos]: LAI (r.h.) * * 0---rightDV----> | * | | * | Picture of a finite, rectangular plane | * | ( insert LOLCAT-scan here ^_^ ) | * | | * v _________________________________________| * */ { FillVector3D(origin, 0, 0, zPosition); FillVector3D(rightDV, 1, 0, 0); FillVector3D(bottomDV, 0, 1, 0); } else // Origin rotated to the bottom-right corner, x=[-1; 0; 0], y=[0; -1; 0], z=[0; 0; 1], // origin=[w,h,zpos]: RPI (r.h.) { // Caveat emptor: Still using top-left as origin of index coordinate system! FillVector3D(origin, width, height, zPosition); FillVector3D(rightDV, -1, 0, 0); FillVector3D(bottomDV, 0, -1, 0); } } else // 'Backside, not frontside.' Neuro-Surgeons's view from above patient. { if (rotated == false) // x=[-1; 0; 0], y=[0; 1; 0], z=[0; 0; 1], origin=[w,0,zpos]: RAS (r.h.) { FillVector3D(origin, width, 0, zPosition); FillVector3D(rightDV, -1, 0, 0); FillVector3D(bottomDV, 0, 1, 0); } else // Origin in the bottom-left corner, x=[1; 0; 0], y=[0; -1; 0], z=[0; 0; 1], // origin=[0,h,zpos]: LPS (r.h.) { FillVector3D(origin, 0, height, zPosition); FillVector3D(rightDV, 1, 0, 0); FillVector3D(bottomDV, 0, -1, 0); } } normalDirection = 2; // That is S=Superior=z=third_axis=middlefinger in righthanded LPS-system. break; // Frontal is known as Coronal in mitk. Plane cuts through patient's ear-ear-heel-heel: case Frontal: if (frontside) { if (rotated == false) // x=[1; 0; 0], y=[0; 0; 1], z=[0; 1; 0], origin=[0,zpos,0]: LAI (r.h.) { FillVector3D(origin, 0, zPosition, 0); FillVector3D(rightDV, 1, 0, 0); FillVector3D(bottomDV, 0, 0, 1); } else // x=[-1;0;0], y=[0;0;-1], z=[0;1;0], origin=[w,zpos,h]: RAS (r.h.) { FillVector3D(origin, width, zPosition, height); FillVector3D(rightDV, -1, 0, 0); FillVector3D(bottomDV, 0, 0, -1); } } else { if (rotated == false) // x=[-1;0;0], y=[0;0;1], z=[0;1;0], origin=[w,zpos,0]: RPI (r.h.) { FillVector3D(origin, width, zPosition, 0); FillVector3D(rightDV, -1, 0, 0); FillVector3D(bottomDV, 0, 0, 1); } else // x=[1;0;0], y=[0;1;0], z=[0;0;-1], origin=[0,zpos,h]: LPS (r.h.) { FillVector3D(origin, 0, zPosition, height); FillVector3D(rightDV, 1, 0, 0); FillVector3D(bottomDV, 0, 0, -1); } } normalDirection = 1; // Normal vector = posterior direction. break; case Sagittal: // Sagittal=Medial plane, the symmetry-plane mirroring your face. if (frontside) { if (rotated == false) // x=[0;1;0], y=[0;0;1], z=[1;0;0], origin=[zpos,0,0]: LAI (r.h.) { FillVector3D(origin, zPosition, 0, 0); FillVector3D(rightDV, 0, 1, 0); FillVector3D(bottomDV, 0, 0, 1); } else // x=[0;-1;0], y=[0;0;-1], z=[1;0;0], origin=[zpos,w,h]: LPS (r.h.) { FillVector3D(origin, zPosition, width, height); FillVector3D(rightDV, 0, -1, 0); FillVector3D(bottomDV, 0, 0, -1); } } else { if (rotated == false) // x=[0;-1;0], y=[0;0;1], z=[1;0;0], origin=[zpos,w,0]: RPI (r.h.) { FillVector3D(origin, zPosition, width, 0); FillVector3D(rightDV, 0, -1, 0); FillVector3D(bottomDV, 0, 0, 1); } else // x=[0;1;0], y=[0;0;-1], z=[1;0;0], origin=[zpos,0,h]: RAS (r.h.) { FillVector3D(origin, zPosition, 0, height); FillVector3D(rightDV, 0, 1, 0); FillVector3D(bottomDV, 0, 0, -1); } } normalDirection = 0; // Normal vector = Lateral direction: Left in a LPS-system. break; default: itkExceptionMacro("unknown PlaneOrientation"); } VnlVector normal(3); FillVector3D(normal, 0, 0, 0); normal[normalDirection] = top ? 1 : -1; if ( transform != nullptr ) { origin = transform->TransformPoint( origin ); rightDV = transform->TransformVector( rightDV ); bottomDV = transform->TransformVector( bottomDV ); normal = transform->TransformVector( normal ); } ScalarType bounds[6] = {0, width, 0, height, 0, 1}; this->SetBounds(bounds); AffineTransform3D::Pointer planeTransform = AffineTransform3D::New(); Matrix3D matrix; matrix.GetVnlMatrix().set_column(0, rightDV); matrix.GetVnlMatrix().set_column(1, bottomDV); matrix.GetVnlMatrix().set_column(2, normal); planeTransform->SetMatrix(matrix); planeTransform->SetOffset(this->GetIndexToWorldTransform()->GetOffset()); this->SetIndexToWorldTransform(planeTransform); this->SetOrigin(origin); } void PlaneGeometry::InitializeStandardPlane(const BaseGeometry *geometry3D, PlaneOrientation planeorientation, ScalarType zPosition, bool frontside, bool rotated, bool top) { this->SetReferenceGeometry(geometry3D); ScalarType width, height; // Inspired by: // http://www.na-mic.org/Wiki/index.php/Coordinate_System_Conversion_Between_ITK_and_Slicer3 mitk::AffineTransform3D::MatrixType matrix = geometry3D->GetIndexToWorldTransform()->GetMatrix(); matrix.GetVnlMatrix().normalize_columns(); mitk::AffineTransform3D::MatrixType::InternalMatrixType inverseMatrix = matrix.GetInverse(); /// The index of the sagittal, coronal and axial axes in the reference geometry. int axes[3]; /// The direction of the sagittal, coronal and axial axes in the reference geometry. /// +1 means that the direction is straight, i.e. greater index translates to greater /// world coordinate. -1 means that the direction is inverted. int directions[3]; ScalarType extents[3]; ScalarType spacings[3]; for (int i = 0; i < 3; ++i) { int dominantAxis = itk::Function::Max3( inverseMatrix[0][i], inverseMatrix[1][i], inverseMatrix[2][i] ); axes[i] = dominantAxis; directions[i] = itk::Function::Sign(inverseMatrix[dominantAxis][i]); extents[i] = geometry3D->GetExtent(dominantAxis); spacings[i] = geometry3D->GetSpacing()[dominantAxis]; } // matrix(column) = inverseTransformMatrix(row) * flippedAxes * spacing matrix[0][0] = inverseMatrix[axes[0]][0] * directions[0] * spacings[0]; matrix[1][0] = inverseMatrix[axes[0]][1] * directions[0] * spacings[0]; matrix[2][0] = inverseMatrix[axes[0]][2] * directions[0] * spacings[0]; matrix[0][1] = inverseMatrix[axes[1]][0] * directions[1] * spacings[1]; matrix[1][1] = inverseMatrix[axes[1]][1] * directions[1] * spacings[1]; matrix[2][1] = inverseMatrix[axes[1]][2] * directions[1] * spacings[1]; matrix[0][2] = inverseMatrix[axes[2]][0] * directions[2] * spacings[2]; matrix[1][2] = inverseMatrix[axes[2]][1] * directions[2] * spacings[2]; matrix[2][2] = inverseMatrix[axes[2]][2] * directions[2] * spacings[2]; /// The "world origin" is the corner with the lowest physical coordinates. /// We use it as a reference point so that we get the correct anatomical /// orientations. Point3D worldOrigin = geometry3D->GetOrigin(); for (int i = 0; i < 3; ++i) { /// The distance of the plane origin from the world origin in voxels. double offset = directions[i] > 0 ? 0.0 : extents[i]; if (geometry3D->GetImageGeometry()) { offset += directions[i] * 0.5; } for (int j = 0; j < 3; ++j) { worldOrigin[j] -= offset * matrix[j][i]; } } switch(planeorientation) { case None: /** Orientation 'None' shall be done like the axial plane orientation, * for whatever reasons. */ case Axial: width = extents[0]; height = extents[1]; break; case Frontal: width = extents[0]; height = extents[2]; break; case Sagittal: width = extents[1]; height = extents[2]; break; default: itkExceptionMacro("unknown PlaneOrientation"); } ScalarType bounds[6]= { 0, width, 0, height, 0, 1 }; this->SetBounds( bounds ); AffineTransform3D::Pointer transform = AffineTransform3D::New(); transform->SetMatrix(matrix); transform->SetOffset(worldOrigin.GetVectorFromOrigin()); InitializeStandardPlane( width, height, transform, planeorientation, zPosition, frontside, rotated, top); } void PlaneGeometry::InitializeStandardPlane( const BaseGeometry *geometry3D, bool top, PlaneOrientation planeorientation, bool frontside, bool rotated) { /// The index of the sagittal, coronal and axial axes in world coordinate system. int worldAxis; switch(planeorientation) { case None: /** Orientation 'None' shall be done like the axial plane orientation, * for whatever reasons. */ case Axial: worldAxis = 2; break; case Frontal: worldAxis = 1; break; case Sagittal: worldAxis = 0; break; default: itkExceptionMacro("unknown PlaneOrientation"); } // Inspired by: // http://www.na-mic.org/Wiki/index.php/Coordinate_System_Conversion_Between_ITK_and_Slicer3 mitk::AffineTransform3D::ConstPointer affineTransform = geometry3D->GetIndexToWorldTransform(); mitk::AffineTransform3D::MatrixType matrix = affineTransform->GetMatrix(); matrix.GetVnlMatrix().normalize_columns(); mitk::AffineTransform3D::MatrixType::InternalMatrixType inverseMatrix = matrix.GetInverse(); /// The index of the sagittal, coronal and axial axes in the reference geometry. int dominantAxis = itk::Function::Max3( inverseMatrix[0][worldAxis], inverseMatrix[1][worldAxis], inverseMatrix[2][worldAxis]); ScalarType zPosition = top ? 0.5 : geometry3D->GetExtent(dominantAxis) - 0.5; InitializeStandardPlane(geometry3D, planeorientation, zPosition, frontside, rotated, top); } void PlaneGeometry::InitializeStandardPlane(const Vector3D &rightVector, const Vector3D &downVector, const Vector3D *spacing) { InitializeStandardPlane(rightVector.GetVnlVector(), downVector.GetVnlVector(), spacing); } void PlaneGeometry::InitializeStandardPlane(const VnlVector &rightVector, const VnlVector &downVector, const Vector3D *spacing) { ScalarType width = rightVector.two_norm(); ScalarType height = downVector.two_norm(); InitializeStandardPlane(width, height, rightVector, downVector, spacing); } void PlaneGeometry::InitializeStandardPlane(mitk::ScalarType width, ScalarType height, const Vector3D &rightVector, const Vector3D &downVector, const Vector3D *spacing) { InitializeStandardPlane(width, height, rightVector.GetVnlVector(), downVector.GetVnlVector(), spacing); } void PlaneGeometry::InitializeStandardPlane(mitk::ScalarType width, ScalarType height, const VnlVector &rightVector, const VnlVector &downVector, const Vector3D *spacing) { assert(width > 0); assert(height > 0); VnlVector rightDV = rightVector; rightDV.normalize(); VnlVector downDV = downVector; downDV.normalize(); VnlVector normal = vnl_cross_3d(rightVector, downVector); normal.normalize(); // Crossproduct vnl_cross_3d is always righthanded, but that is okay here // because in this method we create a new IndexToWorldTransform and // spacing with 1 or 3 negative components could still make it lefthanded. if (spacing != nullptr) { rightDV *= (*spacing)[0]; downDV *= (*spacing)[1]; normal *= (*spacing)[2]; } AffineTransform3D::Pointer transform = AffineTransform3D::New(); Matrix3D matrix; matrix.GetVnlMatrix().set_column(0, rightDV); matrix.GetVnlMatrix().set_column(1, downDV); matrix.GetVnlMatrix().set_column(2, normal); transform->SetMatrix(matrix); transform->SetOffset(this->GetIndexToWorldTransform()->GetOffset()); ScalarType bounds[6] = {0, width, 0, height, 0, 1}; this->SetBounds(bounds); this->SetIndexToWorldTransform(transform); } void PlaneGeometry::InitializePlane(const Point3D &origin, const Vector3D &normal) { VnlVector rightVectorVnl(3), downVectorVnl; if (Equal(normal[1], 0.0f) == false) { FillVector3D(rightVectorVnl, 1.0f, -normal[0] / normal[1], 0.0f); rightVectorVnl.normalize(); } else { FillVector3D(rightVectorVnl, 0.0f, 1.0f, 0.0f); } downVectorVnl = vnl_cross_3d(normal.GetVnlVector(), rightVectorVnl); downVectorVnl.normalize(); // Crossproduct vnl_cross_3d is always righthanded. InitializeStandardPlane(rightVectorVnl, downVectorVnl); SetOrigin(origin); } void PlaneGeometry::SetMatrixByVectors(const VnlVector &rightVector, const VnlVector &downVector, ScalarType thickness /* = 1.0 */) { VnlVector normal = vnl_cross_3d(rightVector, downVector); normal.normalize(); normal *= thickness; // Crossproduct vnl_cross_3d is always righthanded, but that is okay here // because in this method we create a new IndexToWorldTransform and // a negative thickness could still make it lefthanded. AffineTransform3D::Pointer transform = AffineTransform3D::New(); Matrix3D matrix; matrix.GetVnlMatrix().set_column(0, rightVector); matrix.GetVnlMatrix().set_column(1, downVector); matrix.GetVnlMatrix().set_column(2, normal); transform->SetMatrix(matrix); transform->SetOffset(this->GetIndexToWorldTransform()->GetOffset()); SetIndexToWorldTransform(transform); } Vector3D PlaneGeometry::GetNormal() const { Vector3D frontToBack; frontToBack.SetVnlVector(this->GetIndexToWorldTransform()->GetMatrix().GetVnlMatrix().get_column(2)); return frontToBack; } VnlVector PlaneGeometry::GetNormalVnl() const { return this->GetIndexToWorldTransform()->GetMatrix().GetVnlMatrix().get_column(2); } ScalarType PlaneGeometry::DistanceFromPlane(const Point3D &pt3d_mm) const { return fabs(SignedDistance(pt3d_mm)); } ScalarType PlaneGeometry::SignedDistance(const Point3D &pt3d_mm) const { return SignedDistanceFromPlane(pt3d_mm); } bool PlaneGeometry::IsAbove(const Point3D &pt3d_mm, bool considerBoundingBox) const { if (considerBoundingBox) { Point3D pt3d_units; BaseGeometry::WorldToIndex(pt3d_mm, pt3d_units); return (pt3d_units[2] > this->GetBoundingBox()->GetBounds()[4]); } else return SignedDistanceFromPlane(pt3d_mm) > 0; } bool PlaneGeometry::IntersectionLine(const PlaneGeometry *plane, Line3D &crossline) const { Vector3D normal = this->GetNormal(); normal.Normalize(); Vector3D planeNormal = plane->GetNormal(); planeNormal.Normalize(); Vector3D direction = itk::CrossProduct(normal, planeNormal); if (direction.GetSquaredNorm() < eps) return false; crossline.SetDirection(direction); double N1dN2 = normal * planeNormal; double determinant = 1.0 - N1dN2 * N1dN2; Vector3D origin = this->GetOrigin().GetVectorFromOrigin(); Vector3D planeOrigin = plane->GetOrigin().GetVectorFromOrigin(); double d1 = normal * origin; double d2 = planeNormal * planeOrigin; double c1 = (d1 - d2 * N1dN2) / determinant; double c2 = (d2 - d1 * N1dN2) / determinant; Vector3D p = normal * c1 + planeNormal * c2; crossline.GetPoint().GetVnlVector() = p.GetVnlVector(); return true; } unsigned int PlaneGeometry::IntersectWithPlane2D(const PlaneGeometry *plane, Point2D &lineFrom, Point2D &lineTo) const { Line3D crossline; if (this->IntersectionLine(plane, crossline) == false) return 0; Point2D point2; Vector2D direction2; this->Map(crossline.GetPoint(), point2); this->Map(crossline.GetPoint(), crossline.GetDirection(), direction2); return Line3D::RectangleLineIntersection( 0, 0, GetExtentInMM(0), GetExtentInMM(1), point2, direction2, lineFrom, lineTo); } double PlaneGeometry::Angle(const PlaneGeometry *plane) const { return angle(plane->GetMatrixColumn(2), GetMatrixColumn(2)); } double PlaneGeometry::Angle(const Line3D &line) const { return vnl_math::pi_over_2 - angle(line.GetDirection().GetVnlVector(), GetMatrixColumn(2)); } bool PlaneGeometry::IntersectionPoint(const Line3D &line, Point3D &intersectionPoint) const { Vector3D planeNormal = this->GetNormal(); planeNormal.Normalize(); Vector3D lineDirection = line.GetDirection(); lineDirection.Normalize(); double t = planeNormal * lineDirection; if (fabs(t) < eps) { return false; } Vector3D diff; diff = this->GetOrigin() - line.GetPoint(); t = (planeNormal * diff) / t; intersectionPoint = line.GetPoint() + lineDirection * t; return true; } bool PlaneGeometry::IntersectionPointParam(const Line3D &line, double &t) const { Vector3D planeNormal = this->GetNormal(); Vector3D lineDirection = line.GetDirection(); t = planeNormal * lineDirection; if (fabs(t) < eps) { return false; } Vector3D diff; diff = this->GetOrigin() - line.GetPoint(); t = (planeNormal * diff) / t; return true; } bool PlaneGeometry::IsParallel(const PlaneGeometry *plane) const { return ((Angle(plane) < 10.0 * mitk::sqrteps) || (Angle(plane) > (vnl_math::pi - 10.0 * sqrteps))); } bool PlaneGeometry::IsOnPlane(const Point3D &point) const { return Distance(point) < eps; } bool PlaneGeometry::IsOnPlane(const Line3D &line) const { return ((Distance(line.GetPoint()) < eps) && (Distance(line.GetPoint2()) < eps)); } bool PlaneGeometry::IsOnPlane(const PlaneGeometry *plane) const { return (IsParallel(plane) && (Distance(plane->GetOrigin()) < eps)); } Point3D PlaneGeometry::ProjectPointOntoPlane(const Point3D &pt) const { ScalarType len = this->GetNormalVnl().two_norm(); return pt - this->GetNormal() * this->SignedDistanceFromPlane(pt) / len; } itk::LightObject::Pointer PlaneGeometry::InternalClone() const { Self::Pointer newGeometry = new PlaneGeometry(*this); newGeometry->UnRegister(); return newGeometry.GetPointer(); } void PlaneGeometry::ExecuteOperation(Operation *operation) { vtkTransform *transform = vtkTransform::New(); transform->SetMatrix(this->GetVtkMatrix()); switch (operation->GetOperationType()) { case OpORIENT: { - mitk::PlaneOperation *planeOp = dynamic_cast(operation); + auto *planeOp = dynamic_cast(operation); if (planeOp == nullptr) { return; } Point3D center = planeOp->GetPoint(); Vector3D orientationVector = planeOp->GetNormal(); Vector3D defaultVector; FillVector3D(defaultVector, 0.0, 0.0, 1.0); Vector3D rotationAxis = itk::CrossProduct(orientationVector, defaultVector); // double rotationAngle = acos( orientationVector[2] / orientationVector.GetNorm() ); double rotationAngle = atan2((double)rotationAxis.GetNorm(), (double)(orientationVector * defaultVector)); rotationAngle *= 180.0 / vnl_math::pi; transform->PostMultiply(); transform->Identity(); transform->Translate(center[0], center[1], center[2]); transform->RotateWXYZ(rotationAngle, rotationAxis[0], rotationAxis[1], rotationAxis[2]); transform->Translate(-center[0], -center[1], -center[2]); break; } case OpRESTOREPLANEPOSITION: { - RestorePlanePositionOperation *op = dynamic_cast(operation); + auto *op = dynamic_cast(operation); if (op == nullptr) { return; } AffineTransform3D::Pointer transform2 = AffineTransform3D::New(); Matrix3D matrix; matrix.GetVnlMatrix().set_column(0, op->GetTransform()->GetMatrix().GetVnlMatrix().get_column(0)); matrix.GetVnlMatrix().set_column(1, op->GetTransform()->GetMatrix().GetVnlMatrix().get_column(1)); matrix.GetVnlMatrix().set_column(2, op->GetTransform()->GetMatrix().GetVnlMatrix().get_column(2)); transform2->SetMatrix(matrix); Vector3D offset = op->GetTransform()->GetOffset(); transform2->SetOffset(offset); this->SetIndexToWorldTransform(transform2); ScalarType bounds[6] = {0, op->GetWidth(), 0, op->GetHeight(), 0, 1}; this->SetBounds(bounds); this->Modified(); transform->Delete(); return; } default: Superclass::ExecuteOperation(operation); transform->Delete(); return; } this->SetVtkMatrixDeepCopy(transform); this->Modified(); transform->Delete(); } void PlaneGeometry::PrintSelf(std::ostream &os, itk::Indent indent) const { Superclass::PrintSelf(os, indent); os << indent << " ScaleFactorMMPerUnitX: " << GetExtentInMM(0) / GetExtent(0) << std::endl; os << indent << " ScaleFactorMMPerUnitY: " << GetExtentInMM(1) / GetExtent(1) << std::endl; os << indent << " Normal: " << GetNormal() << std::endl; } bool PlaneGeometry::Map(const mitk::Point3D &pt3d_mm, mitk::Point2D &pt2d_mm) const { assert(this->IsBoundingBoxNull() == false); Point3D pt3d_units; Superclass::WorldToIndex(pt3d_mm, pt3d_units); pt2d_mm[0] = pt3d_units[0] * GetExtentInMM(0) / GetExtent(0); pt2d_mm[1] = pt3d_units[1] * GetExtentInMM(1) / GetExtent(1); pt3d_units[2] = 0; return const_cast(this->GetBoundingBox())->IsInside(pt3d_units); } void PlaneGeometry::Map(const mitk::Point2D &pt2d_mm, mitk::Point3D &pt3d_mm) const { // pt2d_mm is measured from the origin of the world geometry (at leats it called form BaseRendere::Mouse...Event) Point3D pt3d_units; pt3d_units[0] = pt2d_mm[0] / (GetExtentInMM(0) / GetExtent(0)); pt3d_units[1] = pt2d_mm[1] / (GetExtentInMM(1) / GetExtent(1)); pt3d_units[2] = 0; // pt3d_units is a continuos index. We divided it with the Scale Factor (= spacing in x and y) to convert it from mm // to index units. // pt3d_mm = GetIndexToWorldTransform()->TransformPoint(pt3d_units); // now we convert the 3d index to a 3D world point in mm. We could have used IndexToWorld as well as // GetITW->Transform... } void PlaneGeometry::SetSizeInUnits(mitk::ScalarType width, mitk::ScalarType height) { ScalarType bounds[6] = {0, width, 0, height, 0, 1}; ScalarType extent, newextentInMM; if (GetExtent(0) > 0) { extent = GetExtent(0); if (width > extent) newextentInMM = GetExtentInMM(0) / width * extent; else newextentInMM = GetExtentInMM(0) * extent / width; SetExtentInMM(0, newextentInMM); } if (GetExtent(1) > 0) { extent = GetExtent(1); if (width > extent) newextentInMM = GetExtentInMM(1) / height * extent; else newextentInMM = GetExtentInMM(1) * extent / height; SetExtentInMM(1, newextentInMM); } SetBounds(bounds); } bool PlaneGeometry::Project(const mitk::Point3D &pt3d_mm, mitk::Point3D &projectedPt3d_mm) const { assert(this->IsBoundingBoxNull() == false); Point3D pt3d_units; Superclass::WorldToIndex(pt3d_mm, pt3d_units); pt3d_units[2] = 0; projectedPt3d_mm = GetIndexToWorldTransform()->TransformPoint(pt3d_units); return const_cast(this->GetBoundingBox())->IsInside(pt3d_units); } bool PlaneGeometry::Project(const mitk::Vector3D &vec3d_mm, mitk::Vector3D &projectedVec3d_mm) const { assert(this->IsBoundingBoxNull() == false); Vector3D vec3d_units; Superclass::WorldToIndex(vec3d_mm, vec3d_units); vec3d_units[2] = 0; projectedVec3d_mm = GetIndexToWorldTransform()->TransformVector(vec3d_units); return true; } bool PlaneGeometry::Project(const mitk::Point3D &atPt3d_mm, const mitk::Vector3D &vec3d_mm, mitk::Vector3D &projectedVec3d_mm) const { MITK_WARN << "Deprecated function! Call Project(vec3D,vec3D) instead."; assert(this->IsBoundingBoxNull() == false); Vector3D vec3d_units; Superclass::WorldToIndex(atPt3d_mm, vec3d_mm, vec3d_units); vec3d_units[2] = 0; projectedVec3d_mm = GetIndexToWorldTransform()->TransformVector(vec3d_units); Point3D pt3d_units; Superclass::WorldToIndex(atPt3d_mm, pt3d_units); return const_cast(this->GetBoundingBox())->IsInside(pt3d_units); } bool PlaneGeometry::Map(const mitk::Point3D &atPt3d_mm, const mitk::Vector3D &vec3d_mm, mitk::Vector2D &vec2d_mm) const { Point2D pt2d_mm_start, pt2d_mm_end; Point3D pt3d_mm_end; bool inside = Map(atPt3d_mm, pt2d_mm_start); pt3d_mm_end = atPt3d_mm + vec3d_mm; inside &= Map(pt3d_mm_end, pt2d_mm_end); vec2d_mm = pt2d_mm_end - pt2d_mm_start; return inside; } void PlaneGeometry::Map(const mitk::Point2D & /*atPt2d_mm*/, const mitk::Vector2D & /*vec2d_mm*/, mitk::Vector3D & /*vec3d_mm*/) const { //@todo implement parallel to the other Map method! assert(false); } void PlaneGeometry::SetReferenceGeometry(const mitk::BaseGeometry *geometry) { m_ReferenceGeometry = geometry; } const mitk::BaseGeometry *PlaneGeometry::GetReferenceGeometry() const { return m_ReferenceGeometry; } bool PlaneGeometry::HasReferenceGeometry() const { return (m_ReferenceGeometry != nullptr); } } // namespace diff --git a/Modules/Core/src/DataManagement/mitkPropertyAliases.cpp b/Modules/Core/src/DataManagement/mitkPropertyAliases.cpp index f03772b73c..56a51447f4 100644 --- a/Modules/Core/src/DataManagement/mitkPropertyAliases.cpp +++ b/Modules/Core/src/DataManagement/mitkPropertyAliases.cpp @@ -1,143 +1,143 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkPropertyAliases.h" #include #include #ifdef _MSC_VER #pragma warning(disable : 4503) // "decorated name length exceeded, name was truncated" #endif class AliasEquals { public: AliasEquals(const std::string &alias) : m_Alias(alias) {} bool operator()(const std::pair> &element) { - std::vector::const_iterator iter = std::find(element.second.begin(), element.second.end(), m_Alias); + auto iter = std::find(element.second.begin(), element.second.end(), m_Alias); return iter != element.second.end(); } private: std::string m_Alias; }; mitk::PropertyAliases::PropertyAliases() { } mitk::PropertyAliases::~PropertyAliases() { } bool mitk::PropertyAliases::AddAlias(const std::string &propertyName, const std::string &alias, const std::string &className) { if (alias.empty()) return false; AliasesMap &aliases = m_Aliases[className]; - AliasesMapIterator iter = aliases.find(propertyName); + auto iter = aliases.find(propertyName); if (iter != aliases.end()) { if (std::find(iter->second.begin(), iter->second.end(), alias) == iter->second.end()) iter->second.push_back(alias); } else { aliases.insert(std::make_pair(propertyName, std::vector(1, alias))); } return true; } std::vector mitk::PropertyAliases::GetAliases(const std::string &propertyName, const std::string &className) { if (!propertyName.empty()) { AliasesMap &aliases = m_Aliases[className]; AliasesMapConstIterator iter = aliases.find(propertyName); if (iter != aliases.end()) return iter->second; } return std::vector(); } std::string mitk::PropertyAliases::GetPropertyName(const std::string &alias, const std::string &className) { if (!alias.empty()) { AliasesMap &aliases = m_Aliases[className]; AliasesMapConstIterator iter = std::find_if(aliases.begin(), aliases.end(), AliasEquals(alias)); if (iter != aliases.end()) return iter->first; } return ""; } bool mitk::PropertyAliases::HasAliases(const std::string &propertyName, const std::string &className) { const AliasesMap &aliases = m_Aliases[className]; return !propertyName.empty() ? aliases.find(propertyName) != aliases.end() : false; } void mitk::PropertyAliases::RemoveAlias(const std::string &propertyName, const std::string &alias, const std::string &className) { if (!propertyName.empty() && !alias.empty()) { AliasesMap &aliases = m_Aliases[className]; - AliasesMapIterator iter = aliases.find(propertyName); + auto iter = aliases.find(propertyName); if (iter != aliases.end()) { - std::vector::iterator aliasIter = std::find(iter->second.begin(), iter->second.end(), alias); + auto aliasIter = std::find(iter->second.begin(), iter->second.end(), alias); if (aliasIter != iter->second.end()) { iter->second.erase(aliasIter); if (iter->second.empty()) aliases.erase(propertyName); } } } } void mitk::PropertyAliases::RemoveAliases(const std::string &propertyName, const std::string &className) { if (!propertyName.empty()) { AliasesMap &aliases = m_Aliases[className]; aliases.erase(propertyName); } } void mitk::PropertyAliases::RemoveAllAliases(const std::string &className) { AliasesMap &aliases = m_Aliases[className]; aliases.clear(); } diff --git a/Modules/Core/src/DataManagement/mitkPropertyDescriptions.cpp b/Modules/Core/src/DataManagement/mitkPropertyDescriptions.cpp index 0efcf5a68b..a2f49982ab 100644 --- a/Modules/Core/src/DataManagement/mitkPropertyDescriptions.cpp +++ b/Modules/Core/src/DataManagement/mitkPropertyDescriptions.cpp @@ -1,168 +1,168 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkPropertyDescriptions.h" #include #include #include mitk::PropertyDescriptions::PropertyDescriptions() { } mitk::PropertyDescriptions::~PropertyDescriptions() { } bool mitk::PropertyDescriptions::AddDescription(const std::string &propertyName, const std::string &description, const std::string &className, bool overwrite) { if (propertyName.empty()) return false; DescriptionMap &descriptions = m_Descriptions[className]; std::pair ret = descriptions.insert(std::make_pair(propertyName, description)); if (!ret.second && overwrite) { ret.first->second = description; ret.second = true; } return ret.second; } bool mitk::PropertyDescriptions::AddDescriptionRegEx(const std::string &propertyRegEx, const std::string &description, const std::string &className, bool overwrite) { if (propertyRegEx.empty()) return false; try { std::regex checker(propertyRegEx); // no exception => valid we can change the info } catch (std::regex_error) { return false; } DescriptionMap &descriptions = m_DescriptionsRegEx[className]; std::pair ret = descriptions.insert(std::make_pair(propertyRegEx, description)); if (!ret.second && overwrite) { ret.first->second = description; ret.second = true; } return ret.second; } std::string mitk::PropertyDescriptions::GetDescription(const std::string &propertyName, const std::string &className, bool allowNameRegEx) const { if (!propertyName.empty()) { auto descriptionsIter = m_Descriptions.find(className); if (descriptionsIter != m_Descriptions.cend()) { - DescriptionMapConstIterator iter = descriptionsIter->second.find(propertyName); + auto iter = descriptionsIter->second.find(propertyName); if (iter != descriptionsIter->second.end()) return iter->second; } } if (allowNameRegEx && !propertyName.empty()) { auto selector = [propertyName](const DescriptionMap::value_type &x) { std::regex ex(x.first); return std::regex_match(propertyName, ex); }; auto descriptionsIter = m_DescriptionsRegEx.find(className); if (descriptionsIter != m_DescriptionsRegEx.cend()) { auto finding = std::find_if(descriptionsIter->second.cbegin(), descriptionsIter->second.cend(), selector); if (finding != descriptionsIter->second.cend()) return finding->second; } } return ""; } bool mitk::PropertyDescriptions::HasDescription(const std::string &propertyName, const std::string &className, bool allowNameRegEx) const { if (!propertyName.empty()) { auto descriptionsIter = m_Descriptions.find(className); if (descriptionsIter != m_Descriptions.cend()) { - DescriptionMapConstIterator iter = descriptionsIter->second.find(propertyName); + auto iter = descriptionsIter->second.find(propertyName); if (iter != descriptionsIter->second.end()) return true; } } if (allowNameRegEx && !propertyName.empty()) { auto selector = [propertyName](const DescriptionMap::value_type &x) { std::regex ex(x.first); return std::regex_match(propertyName, ex); }; auto descriptionsIter = m_DescriptionsRegEx.find(className); if (descriptionsIter != m_DescriptionsRegEx.cend()) { auto finding = std::find_if(descriptionsIter->second.cbegin(), descriptionsIter->second.cend(), selector); if (finding != descriptionsIter->second.cend()) return true; } } return false; } void mitk::PropertyDescriptions::RemoveAllDescriptions(const std::string &className) { m_Descriptions[className].clear(); m_DescriptionsRegEx[className].clear(); } void mitk::PropertyDescriptions::RemoveDescription(const std::string &propertyName, const std::string &className) { if (!propertyName.empty()) { m_Descriptions[className].erase(propertyName); m_DescriptionsRegEx[className].erase(propertyName); } } diff --git a/Modules/Core/src/DataManagement/mitkPropertyFilters.cpp b/Modules/Core/src/DataManagement/mitkPropertyFilters.cpp index 8015a1390c..257333ca13 100644 --- a/Modules/Core/src/DataManagement/mitkPropertyFilters.cpp +++ b/Modules/Core/src/DataManagement/mitkPropertyFilters.cpp @@ -1,91 +1,91 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkPropertyFilters.h" #include #include mitk::PropertyFilters::PropertyFilters() { } mitk::PropertyFilters::~PropertyFilters() { } bool mitk::PropertyFilters::AddFilter(const PropertyFilter &filter, const std::string &className, bool overwrite) { if (!filter.IsEmpty()) { std::pair::iterator, bool> ret = m_Filters.insert(std::make_pair(className, filter)); if (!ret.second && overwrite) { ret.first->second = filter; ret.second = true; } return ret.second; } return false; } std::map mitk::PropertyFilters::ApplyFilter( const std::map &propertyMap, const std::string &className) const { std::map ret = propertyMap; PropertyFilter filter = this->GetFilter(""); if (!filter.IsEmpty()) ret = filter.Apply(ret); if (!className.empty()) { filter = this->GetFilter(className); if (!filter.IsEmpty()) ret = filter.Apply(ret); } return ret; } mitk::PropertyFilter mitk::PropertyFilters::GetFilter(const std::string &className) const { - std::map::const_iterator iter = m_Filters.find(className); + auto iter = m_Filters.find(className); if (iter != m_Filters.end()) return iter->second; return PropertyFilter(); } bool mitk::PropertyFilters::HasFilter(const std::string &className) const { return m_Filters.find(className) != m_Filters.end(); } void mitk::PropertyFilters::RemoveAllFilters() { m_Filters.clear(); } void mitk::PropertyFilters::RemoveFilter(const std::string &className) { m_Filters.erase(className); } diff --git a/Modules/Core/src/DataManagement/mitkPropertyList.cpp b/Modules/Core/src/DataManagement/mitkPropertyList.cpp index d2be580bcb..5547be1687 100644 --- a/Modules/Core/src/DataManagement/mitkPropertyList.cpp +++ b/Modules/Core/src/DataManagement/mitkPropertyList.cpp @@ -1,343 +1,343 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkPropertyList.h" #include "mitkNumericTypes.h" #include "mitkProperties.h" #include "mitkStringProperty.h" mitk::BaseProperty *mitk::PropertyList::GetProperty(const std::string &propertyKey) const { PropertyMap::const_iterator it; it = m_Properties.find(propertyKey); if (it != m_Properties.cend()) return it->second; else return nullptr; } void mitk::PropertyList::SetProperty(const std::string &propertyKey, BaseProperty *property) { if (!property) return; // make sure that BaseProperty*, which may have just been created and never been // assigned to a SmartPointer, is registered/unregistered properly. If we do not // do that, it will a) not deleted in case it is identical to the old one or // b) possibly deleted when temporarily added to a smartpointer somewhere below. BaseProperty::Pointer tmpSmartPointerToProperty = property; auto it(m_Properties.find(propertyKey)); // Is a property with key @a propertyKey contained in the list? if (it != m_Properties.cend()) { // yes // is the property contained in the list identical to the new one? if (it->second->operator==(*property)) { // yes? do nothing and return. return; } if (it->second->AssignProperty(*property)) { // The assignment was successfull this->Modified(); } else { MITK_ERROR << "In " __FILE__ ", l." << __LINE__ << ": Trying to set existing property " << it->first << " of type " << it->second->GetNameOfClass() << " to a property with different type " << property->GetNameOfClass() << "." << " Use ReplaceProperty() instead." << std::endl; } return; } // no? add it. m_Properties.insert(PropertyMap::value_type(propertyKey, property)); this->Modified(); } void mitk::PropertyList::ReplaceProperty(const std::string &propertyKey, BaseProperty *property) { if (!property) return; auto it(m_Properties.find(propertyKey)); // Is a property with key @a propertyKey contained in the list? if (it != m_Properties.cend()) { it->second = nullptr; m_Properties.erase(it); } // no? add/replace it. m_Properties.insert(PropertyMap::value_type(propertyKey, property)); Modified(); } void mitk::PropertyList::RemoveProperty(const std::string &propertyKey) { auto it(m_Properties.find(propertyKey)); // Is a property with key @a propertyKey contained in the list? if (it != m_Properties.cend()) { it->second = nullptr; m_Properties.erase(it); Modified(); } } mitk::PropertyList::PropertyList() { } mitk::PropertyList::PropertyList(const mitk::PropertyList &other) : itk::Object() { for (auto i = other.m_Properties.cbegin(); i != other.m_Properties.cend(); ++i) { m_Properties.insert(std::make_pair(i->first, i->second->Clone())); } } mitk::PropertyList::~PropertyList() { Clear(); } /** * Consider the list as changed when any of the properties has changed recently. */ unsigned long mitk::PropertyList::GetMTime() const { for (auto it = m_Properties.cbegin(); it != m_Properties.cend(); ++it) { if (it->second.IsNull()) { itkWarningMacro(<< "Property '" << it->first << "' contains nothing (nullptr)."); continue; } if (Superclass::GetMTime() < it->second->GetMTime()) { Modified(); break; } } return Superclass::GetMTime(); } bool mitk::PropertyList::DeleteProperty(const std::string &propertyKey) { - PropertyMap::iterator it = m_Properties.find(propertyKey); + auto it = m_Properties.find(propertyKey); if (it != m_Properties.end()) { it->second = nullptr; m_Properties.erase(it); Modified(); return true; } return false; } void mitk::PropertyList::Clear() { auto it = m_Properties.begin(), end = m_Properties.end(); while (it != end) { it->second = nullptr; ++it; } m_Properties.clear(); } itk::LightObject::Pointer mitk::PropertyList::InternalClone() const { itk::LightObject::Pointer result(new Self(*this)); result->UnRegister(); return result; } void mitk::PropertyList::ConcatenatePropertyList(PropertyList *pList, bool replace) { if (pList) { const PropertyMap *propertyMap = pList->GetMap(); for (auto iter = propertyMap->cbegin(); // m_PropertyList is created in the constructor, so we don't check it here iter != propertyMap->cend(); ++iter) { const std::string key = iter->first; BaseProperty *value = iter->second; if (replace) { ReplaceProperty(key.c_str(), value); } else { SetProperty(key.c_str(), value); } } } } bool mitk::PropertyList::GetBoolProperty(const char *propertyKey, bool &boolValue) const { BoolProperty *gp = dynamic_cast(GetProperty(propertyKey)); if (gp != nullptr) { boolValue = gp->GetValue(); return true; } return false; // Templated Method does not work on Macs // return GetPropertyValue(propertyKey, boolValue); } bool mitk::PropertyList::GetIntProperty(const char *propertyKey, int &intValue) const { IntProperty *gp = dynamic_cast(GetProperty(propertyKey)); if (gp != nullptr) { intValue = gp->GetValue(); return true; } return false; // Templated Method does not work on Macs // return GetPropertyValue(propertyKey, intValue); } bool mitk::PropertyList::GetFloatProperty(const char *propertyKey, float &floatValue) const { FloatProperty *gp = dynamic_cast(GetProperty(propertyKey)); if (gp != nullptr) { floatValue = gp->GetValue(); return true; } return false; // Templated Method does not work on Macs // return GetPropertyValue(propertyKey, floatValue); } bool mitk::PropertyList::GetStringProperty(const char *propertyKey, std::string &stringValue) const { StringProperty *sp = dynamic_cast(GetProperty(propertyKey)); if (sp != nullptr) { stringValue = sp->GetValue(); return true; } return false; } void mitk::PropertyList::SetIntProperty(const char *propertyKey, int intValue) { SetProperty(propertyKey, mitk::IntProperty::New(intValue)); } void mitk::PropertyList::SetBoolProperty(const char *propertyKey, bool boolValue) { SetProperty(propertyKey, mitk::BoolProperty::New(boolValue)); } void mitk::PropertyList::SetFloatProperty(const char *propertyKey, float floatValue) { SetProperty(propertyKey, mitk::FloatProperty::New(floatValue)); } void mitk::PropertyList::SetStringProperty(const char *propertyKey, const char *stringValue) { SetProperty(propertyKey, mitk::StringProperty::New(stringValue)); } void mitk::PropertyList::Set(const char *propertyKey, bool boolValue) { this->SetBoolProperty(propertyKey, boolValue); } void mitk::PropertyList::Set(const char *propertyKey, int intValue) { this->SetIntProperty(propertyKey, intValue); } void mitk::PropertyList::Set(const char *propertyKey, float floatValue) { this->SetFloatProperty(propertyKey, floatValue); } void mitk::PropertyList::Set(const char *propertyKey, double doubleValue) { this->SetDoubleProperty(propertyKey, doubleValue); } void mitk::PropertyList::Set(const char *propertyKey, const char *stringValue) { this->SetStringProperty(propertyKey, stringValue); } void mitk::PropertyList::Set(const char *propertyKey, const std::string &stringValue) { this->SetStringProperty(propertyKey, stringValue.c_str()); } bool mitk::PropertyList::Get(const char *propertyKey, bool &boolValue) const { return this->GetBoolProperty(propertyKey, boolValue); } bool mitk::PropertyList::Get(const char *propertyKey, int &intValue) const { return this->GetIntProperty(propertyKey, intValue); } bool mitk::PropertyList::Get(const char *propertyKey, float &floatValue) const { return this->GetFloatProperty(propertyKey, floatValue); } bool mitk::PropertyList::Get(const char *propertyKey, double &doubleValue) const { return this->GetDoubleProperty(propertyKey, doubleValue); } bool mitk::PropertyList::Get(const char *propertyKey, std::string &stringValue) const { return this->GetStringProperty(propertyKey, stringValue); } bool mitk::PropertyList::GetDoubleProperty(const char *propertyKey, double &doubleValue) const { DoubleProperty *gp = dynamic_cast(GetProperty(propertyKey)); if (gp != nullptr) { doubleValue = gp->GetValue(); return true; } return false; } void mitk::PropertyList::SetDoubleProperty(const char *propertyKey, double doubleValue) { SetProperty(propertyKey, mitk::DoubleProperty::New(doubleValue)); } diff --git a/Modules/Core/src/DataManagement/mitkPropertyObserver.cpp b/Modules/Core/src/DataManagement/mitkPropertyObserver.cpp index af7e2663b2..332d3f3467 100644 --- a/Modules/Core/src/DataManagement/mitkPropertyObserver.cpp +++ b/Modules/Core/src/DataManagement/mitkPropertyObserver.cpp @@ -1,133 +1,133 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include #include #include #include namespace mitk { //---- PropertyObserver ----------------------------------------------------------- PropertyObserver::PropertyObserver() : m_SelfCall(false) {} PropertyObserver::~PropertyObserver() {} void PropertyObserver::BeginModifyProperty() { m_SelfCall = true; } void PropertyObserver::EndModifyProperty() { m_SelfCall = false; } //---- PropertyView --------------------------------------------------------------- PropertyView::PropertyView(const mitk::BaseProperty *property) : m_Property(property) { if (!property) throw std::invalid_argument("nullptr pointer makes no sense in PropertyView()"); // no nullptr property allowed { itk::ReceptorMemberCommand::Pointer command = itk::ReceptorMemberCommand::New(); command->SetCallbackFunction(this, &PropertyView::OnModified); m_ModifiedTag = property->AddObserver(itk::ModifiedEvent(), command); } { itk::ReceptorMemberCommand::Pointer command = itk::ReceptorMemberCommand::New(); command->SetCallbackFunction(this, &PropertyView::OnDelete); m_DeleteTag = property->AddObserver(itk::DeleteEvent(), command); } } PropertyView::~PropertyView() { if (m_Property) { - mitk::BaseProperty *prop = const_cast(m_Property); + auto *prop = const_cast(m_Property); prop->RemoveObserver(m_ModifiedTag); prop->RemoveObserver(m_DeleteTag); m_Property = nullptr; } } void PropertyView::OnModified(const itk::EventObject & /*e*/) { if (m_SelfCall) return; PropertyChanged(); } void PropertyView::OnDelete(const itk::EventObject & /*e*/) { if (m_SelfCall) return; PropertyRemoved(); if (m_Property) { - mitk::BaseProperty *prop = const_cast(m_Property); + auto *prop = const_cast(m_Property); prop->RemoveObserver(m_ModifiedTag); prop->RemoveObserver(m_DeleteTag); m_Property = nullptr; } } //---- PropertyEditor ------------------------------------------------------------- PropertyEditor::PropertyEditor(mitk::BaseProperty *property) : m_Property(property) { if (!property) throw std::invalid_argument("nullptr pointer makes no sense in PropertyEditor()"); // no nullptr property allowed { itk::ReceptorMemberCommand::Pointer command = itk::ReceptorMemberCommand::New(); command->SetCallbackFunction(this, &PropertyEditor::OnModified); m_ModifiedTag = property->AddObserver(itk::ModifiedEvent(), command); } { itk::ReceptorMemberCommand::Pointer command = itk::ReceptorMemberCommand::New(); command->SetCallbackFunction(this, &PropertyEditor::OnDelete); m_DeleteTag = property->AddObserver(itk::DeleteEvent(), command); } } PropertyEditor::~PropertyEditor() { if (m_Property) { m_Property->RemoveObserver(m_ModifiedTag); m_Property->RemoveObserver(m_DeleteTag); m_Property = nullptr; } } void PropertyEditor::OnModified(const itk::EventObject & /*e*/) { if (m_SelfCall) return; PropertyChanged(); } void PropertyEditor::OnDelete(const itk::EventObject & /*e*/) { if (m_SelfCall) return; // does this make any sense? PropertyRemoved(); if (m_Property) { m_Property->RemoveObserver(m_ModifiedTag); m_Property->RemoveObserver(m_DeleteTag); m_Property = nullptr; } } } // namespace diff --git a/Modules/Core/src/DataManagement/mitkSlicedData.cpp b/Modules/Core/src/DataManagement/mitkSlicedData.cpp index 7855f8d367..da802ae131 100644 --- a/Modules/Core/src/DataManagement/mitkSlicedData.cpp +++ b/Modules/Core/src/DataManagement/mitkSlicedData.cpp @@ -1,365 +1,365 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkSlicedData.h" #include "mitkAbstractTransformGeometry.h" #include "mitkBaseProcess.h" #include mitk::SlicedData::SlicedData() : m_RequestedRegionInitialized(false), m_UseLargestPossibleRegion(false) { unsigned int i; for (i = 0; i < 4; ++i) { m_LargestPossibleRegion.SetIndex(i, 0); m_LargestPossibleRegion.SetSize(i, 1); } } mitk::SlicedData::SlicedData(const SlicedData &other) : BaseData(other), m_LargestPossibleRegion(other.m_LargestPossibleRegion), m_RequestedRegion(other.m_RequestedRegion), m_RequestedRegionInitialized(other.m_RequestedRegionInitialized), m_BufferedRegion(other.m_BufferedRegion), m_UseLargestPossibleRegion(other.m_UseLargestPossibleRegion) { } mitk::SlicedData::~SlicedData() { } void mitk::SlicedData::UpdateOutputInformation() { Superclass::UpdateOutputInformation(); if (this->GetSource().IsNull()) // If we don't have a source, then let's make our Image // span our buffer { m_UseLargestPossibleRegion = true; } // Now we should know what our largest possible region is. If our // requested region was not set yet, (or has been set to something // invalid - with no data in it ) then set it to the largest possible // region. if (!m_RequestedRegionInitialized) { this->SetRequestedRegionToLargestPossibleRegion(); m_RequestedRegionInitialized = true; } m_LastRequestedRegionWasOutsideOfTheBufferedRegion = 0; } void mitk::SlicedData::PrepareForNewData() { if (GetUpdateMTime() < GetPipelineMTime() || GetDataReleased()) { ReleaseData(); } } void mitk::SlicedData::SetRequestedRegionToLargestPossibleRegion() { m_UseLargestPossibleRegion = true; if (GetGeometry() == nullptr) return; unsigned int i; const RegionType::IndexType &index = GetLargestPossibleRegion().GetIndex(); const RegionType::SizeType &size = GetLargestPossibleRegion().GetSize(); for (i = 0; i < RegionDimension; ++i) { m_RequestedRegion.SetIndex(i, index[i]); m_RequestedRegion.SetSize(i, size[i]); } } bool mitk::SlicedData::RequestedRegionIsOutsideOfTheBufferedRegion() { // Is the requested region within the currently buffered data? // SlicedData and subclasses store entire volumes or slices. The // methods IsVolumeSet() and IsSliceSet are provided to check, // a volume or slice, respectively, is available. Thus, these // methods used here. const IndexType &requestedRegionIndex = m_RequestedRegion.GetIndex(); const SizeType &requestedRegionSize = m_RequestedRegion.GetSize(); const SizeType &largestPossibleRegionSize = GetLargestPossibleRegion().GetSize(); // are whole channels requested? int c, cEnd; c = requestedRegionIndex[4]; cEnd = c + static_cast(requestedRegionSize[4]); if (requestedRegionSize[3] == largestPossibleRegionSize[3]) { for (; c < cEnd; ++c) if (IsChannelSet(c) == false) return true; return false; } // are whole volumes requested? int t, tEnd; t = requestedRegionIndex[3]; tEnd = t + static_cast(requestedRegionSize[3]); if (requestedRegionSize[2] == largestPossibleRegionSize[2]) { for (; c < cEnd; ++c) for (; t < tEnd; ++t) if (IsVolumeSet(t, c) == false) return true; return false; } // ok, only slices are requested. Check if they are available. int s, sEnd; s = requestedRegionIndex[2]; sEnd = s + static_cast(requestedRegionSize[2]); for (; c < cEnd; ++c) for (; t < tEnd; ++t) for (; s < sEnd; ++s) if (IsSliceSet(s, t, c) == false) return true; return false; } bool mitk::SlicedData::VerifyRequestedRegion() { if (GetTimeGeometry() == nullptr) return false; unsigned int i; // Is the requested region within the LargestPossibleRegion? // Note that the test is indeed against the largest possible region // rather than the buffered region; see DataObject::VerifyRequestedRegion. const IndexType &requestedRegionIndex = m_RequestedRegion.GetIndex(); const IndexType &largestPossibleRegionIndex = GetLargestPossibleRegion().GetIndex(); const SizeType &requestedRegionSize = m_RequestedRegion.GetSize(); const SizeType &largestPossibleRegionSize = GetLargestPossibleRegion().GetSize(); for (i = 0; i < RegionDimension; ++i) { if ((requestedRegionIndex[i] < largestPossibleRegionIndex[i]) || ((requestedRegionIndex[i] + static_cast(requestedRegionSize[i])) > (largestPossibleRegionIndex[i] + static_cast(largestPossibleRegionSize[i])))) { return false; } } return true; } void mitk::SlicedData::SetRequestedRegion(const itk::DataObject *data) { m_UseLargestPossibleRegion = false; - const mitk::SlicedData *slicedData = dynamic_cast(data); + const auto *slicedData = dynamic_cast(data); if (slicedData) { m_RequestedRegion = slicedData->GetRequestedRegion(); m_RequestedRegionInitialized = true; } else { // pointer could not be cast back down itkExceptionMacro(<< "mitk::SlicedData::SetRequestedRegion(DataObject*) cannot cast " << typeid(data).name() << " to " << typeid(SlicedData *).name()); } } void mitk::SlicedData::SetRequestedRegion(SlicedData::RegionType *region) { m_UseLargestPossibleRegion = false; if (region != nullptr) { m_RequestedRegion = *region; m_RequestedRegionInitialized = true; } else { // pointer could not be cast back down itkExceptionMacro(<< "mitk::SlicedData::SetRequestedRegion(SlicedData::RegionType*) cannot cast " << typeid(region).name() << " to " << typeid(SlicedData *).name()); } } void mitk::SlicedData::SetLargestPossibleRegion(SlicedData::RegionType *region) { if (region != nullptr) { m_LargestPossibleRegion = *region; m_UseLargestPossibleRegion = true; } else { // pointer could not be cast back down itkExceptionMacro(<< "mitk::SlicedData::SetLargestPossibleRegion(SlicedData::RegionType*) cannot cast " << typeid(region).name() << " to " << typeid(SlicedData *).name()); } } void mitk::SlicedData::CopyInformation(const itk::DataObject *data) { // Standard call to the superclass' method Superclass::CopyInformation(data); const mitk::SlicedData *slicedData; slicedData = dynamic_cast(data); if (slicedData) { m_LargestPossibleRegion = slicedData->GetLargestPossibleRegion(); } else { // pointer could not be cast back down itkExceptionMacro(<< "mitk::SlicedData::CopyInformation(const DataObject *data) cannot cast " << typeid(data).name() << " to " << typeid(SlicedData *).name()); } } // const mitk::PlaneGeometry* mitk::SlicedData::GetPlaneGeometry(int s, int t) const //{ // const_cast(this)->SetRequestedRegionToLargestPossibleRegion(); // // const_cast(this)->UpdateOutputInformation(); // // return GetSlicedGeometry(t)->GetPlaneGeometry(s); //} // mitk::SlicedGeometry3D *mitk::SlicedData::GetSlicedGeometry(unsigned int t) const { if (GetTimeGeometry() == nullptr) return nullptr; return dynamic_cast(GetTimeGeometry()->GetGeometryForTimeStep(t).GetPointer()); } const mitk::SlicedGeometry3D *mitk::SlicedData::GetUpdatedSlicedGeometry(unsigned int t) { SetRequestedRegionToLargestPossibleRegion(); UpdateOutputInformation(); return GetSlicedGeometry(t); } void mitk::SlicedData::SetGeometry(BaseGeometry *aGeometry3D) { if (aGeometry3D != nullptr) { ProportionalTimeGeometry::Pointer timeGeometry = ProportionalTimeGeometry::New(); SlicedGeometry3D::Pointer slicedGeometry = dynamic_cast(aGeometry3D); if (slicedGeometry.IsNull()) { - PlaneGeometry *geometry2d = dynamic_cast(aGeometry3D); + auto *geometry2d = dynamic_cast(aGeometry3D); if (geometry2d != nullptr && dynamic_cast(aGeometry3D) == nullptr) { if ((GetSlicedGeometry()->GetPlaneGeometry(0) == geometry2d) && (GetSlicedGeometry()->GetSlices() == 1)) return; slicedGeometry = SlicedGeometry3D::New(); slicedGeometry->InitializeEvenlySpaced(geometry2d, 1); } else { slicedGeometry = SlicedGeometry3D::New(); PlaneGeometry::Pointer planeGeometry = PlaneGeometry::New(); planeGeometry->InitializeStandardPlane(aGeometry3D); slicedGeometry->InitializeEvenlySpaced(planeGeometry, (unsigned int)(aGeometry3D->GetExtent(2))); } } assert(slicedGeometry.IsNotNull()); timeGeometry->Initialize(slicedGeometry, 1); Superclass::SetTimeGeometry(timeGeometry); } else { if (GetGeometry() == nullptr) return; Superclass::SetGeometry(nullptr); } } void mitk::SlicedData::SetSpacing(const ScalarType aSpacing[3]) { this->SetSpacing((mitk::Vector3D)aSpacing); } void mitk::SlicedData::SetOrigin(const mitk::Point3D &origin) { TimeGeometry *timeGeometry = GetTimeGeometry(); assert(timeGeometry != nullptr); mitk::SlicedGeometry3D *slicedGeometry; unsigned int steps = timeGeometry->CountTimeSteps(); for (unsigned int timestep = 0; timestep < steps; ++timestep) { slicedGeometry = GetSlicedGeometry(timestep); if (slicedGeometry != nullptr) { slicedGeometry->SetOrigin(origin); if (slicedGeometry->GetEvenlySpaced()) { mitk::PlaneGeometry *geometry2D = slicedGeometry->GetPlaneGeometry(0); geometry2D->SetOrigin(origin); slicedGeometry->InitializeEvenlySpaced(geometry2D, slicedGeometry->GetSlices()); } } // ProportionalTimeGeometry* timeGeometry = dynamic_cast(GetTimeGeometry()); // if(timeGeometry != nullptr) //{ // timeGeometry->Initialize(slicedGeometry, steps); // break; //} } } void mitk::SlicedData::SetSpacing(mitk::Vector3D aSpacing) { TimeGeometry *timeGeometry = GetTimeGeometry(); assert(timeGeometry != nullptr); unsigned int steps = timeGeometry->CountTimeSteps(); for (unsigned int timestep = 0; timestep < steps; ++timestep) { mitk::SlicedGeometry3D *slicedGeometry = GetSlicedGeometry(timestep); if (slicedGeometry != nullptr) { slicedGeometry->SetSpacing(aSpacing); } } timeGeometry->Update(); } diff --git a/Modules/Core/src/DataManagement/mitkSlicedGeometry3D.cpp b/Modules/Core/src/DataManagement/mitkSlicedGeometry3D.cpp index 1bca8b48ba..b3988a2007 100644 --- a/Modules/Core/src/DataManagement/mitkSlicedGeometry3D.cpp +++ b/Modules/Core/src/DataManagement/mitkSlicedGeometry3D.cpp @@ -1,966 +1,966 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include #include "mitkSlicedGeometry3D.h" #include "mitkAbstractTransformGeometry.h" #include "mitkApplyTransformMatrixOperation.h" #include "mitkInteractionConst.h" #include "mitkPlaneGeometry.h" #include "mitkPlaneOperation.h" #include "mitkRestorePlanePositionOperation.h" #include "mitkRotationOperation.h" #include "mitkSliceNavigationController.h" const mitk::ScalarType PI = 3.14159265359; mitk::SlicedGeometry3D::SlicedGeometry3D() : m_EvenlySpaced(true), m_Slices(0), m_ReferenceGeometry(nullptr), m_SliceNavigationController(nullptr) { m_DirectionVector.Fill(0); this->InitializeSlicedGeometry(m_Slices); } mitk::SlicedGeometry3D::SlicedGeometry3D(const SlicedGeometry3D &other) : Superclass(other), m_EvenlySpaced(other.m_EvenlySpaced), m_Slices(other.m_Slices), m_ReferenceGeometry(other.m_ReferenceGeometry), m_SliceNavigationController(other.m_SliceNavigationController) { m_DirectionVector.Fill(0); SetSpacing(other.GetSpacing()); SetDirectionVector(other.GetDirectionVector()); if (m_EvenlySpaced) { assert(!other.m_PlaneGeometries.empty() && "This may happen when you use one of the old Initialize methods, which had a bool parameter that is implicitly casted to the number of slices now."); PlaneGeometry::Pointer geometry = other.m_PlaneGeometries[0]->Clone(); assert(geometry.IsNotNull()); SetPlaneGeometry(geometry, 0); } else { unsigned int s; for (s = 0; s < other.m_Slices; ++s) { if (other.m_PlaneGeometries[s].IsNull()) { assert(other.m_EvenlySpaced); m_PlaneGeometries[s] = nullptr; } else { PlaneGeometry *geometry2D = other.m_PlaneGeometries[s]->Clone(); assert(geometry2D != nullptr); SetPlaneGeometry(geometry2D, s); } } } } mitk::SlicedGeometry3D::~SlicedGeometry3D() { } mitk::PlaneGeometry *mitk::SlicedGeometry3D::GetPlaneGeometry(int s) const { mitk::PlaneGeometry::Pointer geometry2D = nullptr; if (this->IsValidSlice(s)) { geometry2D = m_PlaneGeometries[s]; // If (a) m_EvenlySpaced==true, (b) we don't have a PlaneGeometry stored // for the requested slice, and (c) the first slice (s=0) // is a PlaneGeometry instance, then we calculate the geometry of the // requested as the plane of the first slice shifted by m_Spacing[2]*s // in the direction of m_DirectionVector. if ((m_EvenlySpaced) && (geometry2D.IsNull())) { PlaneGeometry *firstSlice = m_PlaneGeometries[0]; if (firstSlice != nullptr && dynamic_cast(m_PlaneGeometries[0].GetPointer()) == nullptr) { if ((m_DirectionVector[0] == 0.0) && (m_DirectionVector[1] == 0.0) && (m_DirectionVector[2] == 0.0)) { m_DirectionVector = firstSlice->GetNormal(); m_DirectionVector.Normalize(); } Vector3D direction; direction = m_DirectionVector * this->GetSpacing()[2]; mitk::PlaneGeometry::Pointer requestedslice; requestedslice = static_cast(firstSlice->Clone().GetPointer()); requestedslice->SetOrigin(requestedslice->GetOrigin() + direction * s); geometry2D = requestedslice; m_PlaneGeometries[s] = geometry2D; } } return geometry2D; } else { return nullptr; } } const mitk::BoundingBox *mitk::SlicedGeometry3D::GetBoundingBox() const { assert(this->IsBoundingBoxNull() == false); return Superclass::GetBoundingBox(); } bool mitk::SlicedGeometry3D::SetPlaneGeometry(mitk::PlaneGeometry *geometry2D, int s) { if (this->IsValidSlice(s)) { m_PlaneGeometries[s] = geometry2D; m_PlaneGeometries[s]->SetReferenceGeometry(m_ReferenceGeometry); return true; } return false; } void mitk::SlicedGeometry3D::InitializeSlicedGeometry(unsigned int slices) { Superclass::Initialize(); m_Slices = slices; PlaneGeometry::Pointer gnull = nullptr; m_PlaneGeometries.assign(m_Slices, gnull); Vector3D spacing; spacing.Fill(1.0); this->SetSpacing(spacing); m_DirectionVector.Fill(0); } void mitk::SlicedGeometry3D::InitializeEvenlySpaced(mitk::PlaneGeometry *geometry2D, unsigned int slices) { assert(geometry2D != nullptr); this->InitializeEvenlySpaced(geometry2D, geometry2D->GetExtentInMM(2) / geometry2D->GetExtent(2), slices); } void mitk::SlicedGeometry3D::InitializeEvenlySpaced(mitk::PlaneGeometry *geometry2D, mitk::ScalarType zSpacing, unsigned int slices) { assert(geometry2D != nullptr); assert(geometry2D->GetExtent(0) > 0); assert(geometry2D->GetExtent(1) > 0); geometry2D->Register(); Superclass::Initialize(); m_Slices = slices; BoundingBox::BoundsArrayType bounds = geometry2D->GetBounds(); bounds[4] = 0; bounds[5] = slices; // clear and reserve PlaneGeometry::Pointer gnull = nullptr; m_PlaneGeometries.assign(m_Slices, gnull); Vector3D directionVector = geometry2D->GetAxisVector(2); directionVector.Normalize(); directionVector *= zSpacing; // Normally we should use the following four lines to create a copy of // the transform contrained in geometry2D, because it may not be changed // by us. But we know that SetSpacing creates a new transform without // changing the old (coming from geometry2D), so we can use the fifth // line instead. We check this at (**). // // AffineTransform3D::Pointer transform = AffineTransform3D::New(); // transform->SetMatrix(geometry2D->GetIndexToWorldTransform()->GetMatrix()); // transform->SetOffset(geometry2D->GetIndexToWorldTransform()->GetOffset()); // SetIndexToWorldTransform(transform); this->SetIndexToWorldTransform(const_cast(geometry2D->GetIndexToWorldTransform())); mitk::Vector3D spacing; FillVector3D(spacing, geometry2D->GetExtentInMM(0) / bounds[1], geometry2D->GetExtentInMM(1) / bounds[3], zSpacing); this->SetDirectionVector(directionVector); this->SetBounds(bounds); this->SetPlaneGeometry(geometry2D, 0); this->SetSpacing(spacing, true); this->SetEvenlySpaced(); // this->SetTimeBounds( geometry2D->GetTimeBounds() ); assert(this->GetIndexToWorldTransform() != geometry2D->GetIndexToWorldTransform()); // (**) see above. this->SetFrameOfReferenceID(geometry2D->GetFrameOfReferenceID()); this->SetImageGeometry(geometry2D->GetImageGeometry()); geometry2D->UnRegister(); } void mitk::SlicedGeometry3D::InitializePlanes(const mitk::BaseGeometry *geometry3D, mitk::PlaneGeometry::PlaneOrientation planeorientation, bool top, bool frontside, bool rotated) { m_ReferenceGeometry = geometry3D; PlaneGeometry::Pointer planeGeometry = mitk::PlaneGeometry::New(); planeGeometry->InitializeStandardPlane(geometry3D, top, planeorientation, frontside, rotated); int worldAxis = planeorientation == PlaneGeometry::Sagittal ? 0 : planeorientation == PlaneGeometry::Frontal ? 1 : 2; // Inspired by: // http://www.na-mic.org/Wiki/index.php/Coordinate_System_Conversion_Between_ITK_and_Slicer3 mitk::AffineTransform3D::MatrixType matrix = geometry3D->GetIndexToWorldTransform()->GetMatrix(); matrix.GetVnlMatrix().normalize_columns(); mitk::AffineTransform3D::MatrixType::InternalMatrixType inverseMatrix = matrix.GetInverse(); int dominantAxis = itk::Function::Max3( inverseMatrix[0][worldAxis], inverseMatrix[1][worldAxis], inverseMatrix[2][worldAxis]); ScalarType viewSpacing = geometry3D->GetSpacing()[dominantAxis]; /// Although the double value returned by GetExtent() holds a round number, /// you need to add 0.5 to safely convert it to unsigned it. I have seen a /// case when the result was less by one without this. - unsigned int slices = static_cast(geometry3D->GetExtent(dominantAxis) + 0.5); + auto slices = static_cast(geometry3D->GetExtent(dominantAxis) + 0.5); #ifndef NDEBUG int upDirection = itk::Function::Sign(inverseMatrix[dominantAxis][worldAxis]); /// The normal vector of an imaginary plane that points from the world origin (bottom left back /// corner or the world, with the lowest physical coordinates) towards the inside of the volume, /// along the renderer axis. Length is the slice thickness. Vector3D worldPlaneNormal = inverseMatrix.get_row(dominantAxis) * (upDirection * viewSpacing); /// The normal of the standard plane geometry just created. Vector3D standardPlaneNormal = planeGeometry->GetNormal(); /// The standard plane must be parallel to the 'world plane'. The normal of the standard plane /// must point against the world plane if and only if 'top' is 'false'. The length of the /// standard plane normal must be equal to the slice thickness. assert((standardPlaneNormal - (top ? 1.0 : -1.0) * worldPlaneNormal).GetSquaredNorm() < 0.000001); #endif this->InitializeEvenlySpaced(planeGeometry, viewSpacing, slices); #ifndef NDEBUG /// The standard plane normal and the z axis vector of the sliced geometry must point in /// the same direction. Vector3D zAxisVector = this->GetAxisVector(2); Vector3D upscaledStandardPlaneNormal = standardPlaneNormal; upscaledStandardPlaneNormal *= slices; assert((zAxisVector - upscaledStandardPlaneNormal).GetSquaredNorm() < 0.000001); /// You can use this test is to check the handedness of the coordinate system of the current /// geometry. In principle, you can use either left- or right-handed coordinate systems, but /// you normally want it to be consistent, that is the handedness should be the same across /// the renderers of the same viewer. // ScalarType det = vnl_det(this->GetIndexToWorldTransform()->GetMatrix().GetVnlMatrix()); // MITK_DEBUG << "world axis: " << worldAxis << (det > 0 ? " ; right-handed" : " ; left-handed"); #endif } void mitk::SlicedGeometry3D::ReinitializePlanes(const Point3D ¢er, const Point3D &referencePoint) { // Need a reference frame to align the rotated planes if (!m_ReferenceGeometry) { return; } // Get first plane of plane stack PlaneGeometry *firstPlane = m_PlaneGeometries[0]; // If plane stack is empty, exit if (!firstPlane || dynamic_cast(firstPlane)) { return; } // Calculate the "directed" spacing when taking the plane (defined by its axes // vectors and normal) as the reference coordinate frame. // // This is done by calculating the radius of the ellipsoid defined by the // original volume spacing axes, in the direction of the respective axis of the // reference frame. mitk::Vector3D axis0 = firstPlane->GetAxisVector(0); mitk::Vector3D axis1 = firstPlane->GetAxisVector(1); mitk::Vector3D normal = firstPlane->GetNormal(); normal.Normalize(); Vector3D spacing; spacing[0] = this->CalculateSpacing(axis0); spacing[1] = this->CalculateSpacing(axis1); spacing[2] = this->CalculateSpacing(normal); Superclass::SetSpacing(spacing); // Now we need to calculate the number of slices in the plane's normal // direction, so that the entire volume is covered. This is done by first // calculating the dot product between the volume diagonal (the maximum // distance inside the volume) and the normal, and dividing this value by // the directed spacing calculated above. ScalarType directedExtent = std::abs(m_ReferenceGeometry->GetExtentInMM(0) * normal[0]) + std::abs(m_ReferenceGeometry->GetExtentInMM(1) * normal[1]) + std::abs(m_ReferenceGeometry->GetExtentInMM(2) * normal[2]); if (directedExtent >= spacing[2]) { m_Slices = static_cast(directedExtent / spacing[2] + 0.5); } else { m_Slices = 1; } // The origin of our "first plane" needs to be adapted to this new extent. // To achieve this, we first calculate the current distance to the volume's // center, and then shift the origin in the direction of the normal by the // difference between this distance and half of the new extent. double centerOfRotationDistance = firstPlane->SignedDistanceFromPlane(center); if (centerOfRotationDistance > 0) { firstPlane->SetOrigin(firstPlane->GetOrigin() + normal * (centerOfRotationDistance - directedExtent / 2.0)); m_DirectionVector = normal; } else { firstPlane->SetOrigin(firstPlane->GetOrigin() + normal * (directedExtent / 2.0 + centerOfRotationDistance)); m_DirectionVector = -normal; } // Now we adjust this distance according with respect to the given reference // point: we need to make sure that the point is touched by one slice of the // new slice stack. double referencePointDistance = firstPlane->SignedDistanceFromPlane(referencePoint); - int referencePointSlice = static_cast(referencePointDistance / spacing[2]); + auto referencePointSlice = static_cast(referencePointDistance / spacing[2]); double alignmentValue = referencePointDistance / spacing[2] - referencePointSlice; firstPlane->SetOrigin(firstPlane->GetOrigin() + normal * alignmentValue * spacing[2]); // Finally, we can clear the previous geometry stack and initialize it with // our re-initialized "first plane". m_PlaneGeometries.assign(m_Slices, PlaneGeometry::Pointer(nullptr)); if (m_Slices > 0) { m_PlaneGeometries[0] = firstPlane; } // Reinitialize SNC with new number of slices m_SliceNavigationController->GetSlice()->SetSteps(m_Slices); this->Modified(); } double mitk::SlicedGeometry3D::CalculateSpacing(const mitk::Vector3D &d) const { // Need the spacing of the underlying dataset / geometry if (!m_ReferenceGeometry) { return 1.0; } const mitk::Vector3D &spacing = m_ReferenceGeometry->GetSpacing(); return SlicedGeometry3D::CalculateSpacing(spacing, d); } double mitk::SlicedGeometry3D::CalculateSpacing(const mitk::Vector3D &spacing, const mitk::Vector3D &d) { // The following can be derived from the ellipsoid equation // // 1 = x^2/a^2 + y^2/b^2 + z^2/c^2 // // where (a,b,c) = spacing of original volume (ellipsoid radii) // and (x,y,z) = scaled coordinates of vector d (according to ellipsoid) // double scaling = d[0] * d[0] / (spacing[0] * spacing[0]) + d[1] * d[1] / (spacing[1] * spacing[1]) + d[2] * d[2] / (spacing[2] * spacing[2]); scaling = sqrt(scaling); return (sqrt(d[0] * d[0] + d[1] * d[1] + d[2] * d[2]) / scaling); } mitk::Vector3D mitk::SlicedGeometry3D::AdjustNormal(const mitk::Vector3D &normal) const { TransformType::Pointer inverse = TransformType::New(); m_ReferenceGeometry->GetIndexToWorldTransform()->GetInverse(inverse); Vector3D transformedNormal = inverse->TransformVector(normal); transformedNormal.Normalize(); return transformedNormal; } void mitk::SlicedGeometry3D::SetImageGeometry(const bool isAnImageGeometry) { Superclass::SetImageGeometry(isAnImageGeometry); unsigned int s; for (s = 0; s < m_Slices; ++s) { mitk::BaseGeometry *geometry = m_PlaneGeometries[s]; if (geometry != nullptr) { geometry->SetImageGeometry(isAnImageGeometry); } } } void mitk::SlicedGeometry3D::ChangeImageGeometryConsideringOriginOffset(const bool isAnImageGeometry) { unsigned int s; for (s = 0; s < m_Slices; ++s) { mitk::BaseGeometry *geometry = m_PlaneGeometries[s]; if (geometry != nullptr) { geometry->ChangeImageGeometryConsideringOriginOffset(isAnImageGeometry); } } Superclass::ChangeImageGeometryConsideringOriginOffset(isAnImageGeometry); } bool mitk::SlicedGeometry3D::IsValidSlice(int s) const { return ((s >= 0) && (s < (int)m_Slices)); } const mitk::BaseGeometry *mitk::SlicedGeometry3D::GetReferenceGeometry() const { return m_ReferenceGeometry; } void mitk::SlicedGeometry3D::SetReferenceGeometry(const BaseGeometry *referenceGeometry) { m_ReferenceGeometry = referenceGeometry; std::vector::iterator it; for (it = m_PlaneGeometries.begin(); it != m_PlaneGeometries.end(); ++it) { (*it)->SetReferenceGeometry(referenceGeometry); } } bool mitk::SlicedGeometry3D::HasReferenceGeometry() const { return ( m_ReferenceGeometry != nullptr ); } void mitk::SlicedGeometry3D::PreSetSpacing(const mitk::Vector3D &aSpacing) { bool hasEvenlySpacedPlaneGeometry = false; mitk::Point3D origin; mitk::Vector3D rightDV, bottomDV; BoundingBox::BoundsArrayType bounds; // Check for valid spacing if (!(aSpacing[0] > 0 && aSpacing[1] > 0 && aSpacing[2] > 0)) { mitkThrow() << "You try to set a spacing with at least one element equal or " "smaller to \"0\". This might lead to a crash during rendering. Please double" " check your data!"; } // In case of evenly-spaced data: re-initialize instances of PlaneGeometry, // since the spacing influences them if ((m_EvenlySpaced) && (m_PlaneGeometries.size() > 0)) { const PlaneGeometry *planeGeometry = m_PlaneGeometries[0]; if (planeGeometry && !dynamic_cast(planeGeometry)) { this->WorldToIndex(planeGeometry->GetOrigin(), origin); this->WorldToIndex(planeGeometry->GetAxisVector(0), rightDV); this->WorldToIndex(planeGeometry->GetAxisVector(1), bottomDV); bounds = planeGeometry->GetBounds(); hasEvenlySpacedPlaneGeometry = true; } } BaseGeometry::_SetSpacing(aSpacing); mitk::PlaneGeometry::Pointer firstGeometry; // In case of evenly-spaced data: re-initialize instances of PlaneGeometry, // since the spacing influences them if (hasEvenlySpacedPlaneGeometry) { // create planeGeometry according to new spacing this->IndexToWorld(origin, origin); this->IndexToWorld(rightDV, rightDV); this->IndexToWorld(bottomDV, bottomDV); mitk::PlaneGeometry::Pointer planeGeometry = mitk::PlaneGeometry::New(); planeGeometry->SetImageGeometry(this->GetImageGeometry()); planeGeometry->SetReferenceGeometry(m_ReferenceGeometry); // Store spacing, as Initialize... needs a pointer mitk::Vector3D lokalSpacing = this->GetSpacing(); planeGeometry->InitializeStandardPlane(rightDV.GetVnlVector(), bottomDV.GetVnlVector(), &lokalSpacing); planeGeometry->SetOrigin(origin); planeGeometry->SetBounds(bounds); firstGeometry = planeGeometry; } else if ((m_EvenlySpaced) && (m_PlaneGeometries.size() > 0)) { firstGeometry = m_PlaneGeometries[0].GetPointer(); } // clear and reserve PlaneGeometry::Pointer gnull = nullptr; m_PlaneGeometries.assign(m_Slices, gnull); if (m_Slices > 0) { m_PlaneGeometries[0] = firstGeometry; } this->Modified(); } void mitk::SlicedGeometry3D::SetSliceNavigationController(SliceNavigationController *snc) { m_SliceNavigationController = snc; } mitk::SliceNavigationController *mitk::SlicedGeometry3D::GetSliceNavigationController() { return m_SliceNavigationController; } void mitk::SlicedGeometry3D::SetEvenlySpaced(bool on) { if (m_EvenlySpaced != on) { m_EvenlySpaced = on; this->Modified(); } } void mitk::SlicedGeometry3D::SetDirectionVector(const mitk::Vector3D &directionVector) { Vector3D newDir = directionVector; newDir.Normalize(); if (newDir != m_DirectionVector) { m_DirectionVector = newDir; this->Modified(); } } // void // mitk::SlicedGeometry3D::SetTimeBounds( const mitk::TimeBounds& timebounds ) //{ // Superclass::SetTimeBounds( timebounds ); // // unsigned int s; // for ( s = 0; s < m_Slices; ++s ) // { // if(m_Geometry2Ds[s].IsNotNull()) // { // m_Geometry2Ds[s]->SetTimeBounds( timebounds ); // } // } // m_TimeBounds = timebounds; //} itk::LightObject::Pointer mitk::SlicedGeometry3D::InternalClone() const { Self::Pointer newGeometry = new SlicedGeometry3D(*this); newGeometry->UnRegister(); return newGeometry.GetPointer(); } void mitk::SlicedGeometry3D::PrintSelf(std::ostream &os, itk::Indent indent) const { Superclass::PrintSelf(os, indent); os << indent << " EvenlySpaced: " << m_EvenlySpaced << std::endl; if (m_EvenlySpaced) { os << indent << " DirectionVector: " << m_DirectionVector << std::endl; } os << indent << " Slices: " << m_Slices << std::endl; os << std::endl; os << indent << " GetPlaneGeometry(0): "; if (this->GetPlaneGeometry(0) == nullptr) { os << "nullptr" << std::endl; } else { this->GetPlaneGeometry(0)->Print(os, indent); } } void mitk::SlicedGeometry3D::ExecuteOperation(Operation *operation) { PlaneGeometry::Pointer geometry2D; ApplyTransformMatrixOperation *applyMatrixOp; Point3D center; switch (operation->GetOperationType()) { case OpNOTHING: break; case OpROTATE: if (m_EvenlySpaced) { // Need a reference frame to align the rotation if (m_ReferenceGeometry) { // Clear all generated geometries and then rotate only the first slice. // The other slices will be re-generated on demand // Save first slice PlaneGeometry::Pointer geometry2D = m_PlaneGeometries[0]; - RotationOperation *rotOp = dynamic_cast(operation); + auto *rotOp = dynamic_cast(operation); // Generate a RotationOperation using the dataset center instead of // the supplied rotation center. This is necessary so that the rotated // zero-plane does not shift away. The supplied center is instead used // to adjust the slice stack afterwards. Point3D center = m_ReferenceGeometry->GetCenter(); RotationOperation centeredRotation( rotOp->GetOperationType(), center, rotOp->GetVectorOfRotation(), rotOp->GetAngleOfRotation()); // Rotate first slice geometry2D->ExecuteOperation(¢eredRotation); // Clear the slice stack and adjust it according to the center of // the dataset and the supplied rotation center (see documentation of // ReinitializePlanes) this->ReinitializePlanes(center, rotOp->GetCenterOfRotation()); geometry2D->SetSpacing(this->GetSpacing()); if (m_SliceNavigationController) { m_SliceNavigationController->SelectSliceByPoint(rotOp->GetCenterOfRotation()); m_SliceNavigationController->AdjustSliceStepperRange(); } BaseGeometry::ExecuteOperation(¢eredRotation); } else { // we also have to consider the case, that there is no reference geometry available. if (m_PlaneGeometries.size() > 0) { // Reach through to all slices in my container for (auto iter = m_PlaneGeometries.begin(); iter != m_PlaneGeometries.end(); ++iter) { // Test for empty slices, which can happen if evenly spaced geometry if ((*iter).IsNotNull()) { (*iter)->ExecuteOperation(operation); } } // rotate overall geometry - RotationOperation *rotOp = dynamic_cast(operation); + auto *rotOp = dynamic_cast(operation); BaseGeometry::ExecuteOperation(rotOp); } } } else { // Reach through to all slices for (auto iter = m_PlaneGeometries.begin(); iter != m_PlaneGeometries.end(); ++iter) { (*iter)->ExecuteOperation(operation); } } break; case OpORIENT: if (m_EvenlySpaced) { // get operation data - PlaneOperation *planeOp = dynamic_cast(operation); + auto *planeOp = dynamic_cast(operation); // Get first slice PlaneGeometry::Pointer planeGeometry = m_PlaneGeometries[0]; // Need a PlaneGeometry, a PlaneOperation and a reference frame to // carry out the re-orientation. If not all avaialble, stop here if (!m_ReferenceGeometry || (!planeGeometry || dynamic_cast(planeGeometry.GetPointer())) || !planeOp) { break; } // General Behavior: // Clear all generated geometries and then rotate only the first slice. // The other slices will be re-generated on demand // // 1st Step: Reorient Normal Vector of first plane // Point3D center = planeOp->GetPoint(); // m_ReferenceGeometry->GetCenter(); mitk::Vector3D currentNormal = planeGeometry->GetNormal(); mitk::Vector3D newNormal; if (planeOp->AreAxisDefined()) { // If planeOp was defined by one centerpoint and two axis vectors newNormal = CrossProduct(planeOp->GetAxisVec0(), planeOp->GetAxisVec1()); } else { // If planeOp was defined by one centerpoint and one normal vector newNormal = planeOp->GetNormal(); } // Get Rotation axis und angle currentNormal.Normalize(); newNormal.Normalize(); ScalarType rotationAngle = angle(currentNormal.GetVnlVector(), newNormal.GetVnlVector()); rotationAngle *= 180.0 / vnl_math::pi; // from rad to deg Vector3D rotationAxis = itk::CrossProduct(currentNormal, newNormal); if (std::abs(rotationAngle - 180) < mitk::eps) { // current Normal and desired normal are not linear independent!!(e.g 1,0,0 and -1,0,0). // Rotation Axis should be ANY vector that is 90� to current Normal mitk::Vector3D helpNormal; helpNormal = currentNormal; helpNormal[0] += 1; helpNormal[1] -= 1; helpNormal[2] += 1; helpNormal.Normalize(); rotationAxis = itk::CrossProduct(helpNormal, currentNormal); } RotationOperation centeredRotation(mitk::OpROTATE, center, rotationAxis, rotationAngle); // Rotate first slice planeGeometry->ExecuteOperation(¢eredRotation); // Reinitialize planes and select slice, if my rotations are all done. if (!planeOp->AreAxisDefined()) { // Clear the slice stack and adjust it according to the center of // rotation and plane position (see documentation of ReinitializePlanes) this->ReinitializePlanes(center, planeOp->GetPoint()); planeGeometry->SetSpacing(this->GetSpacing()); if (m_SliceNavigationController) { m_SliceNavigationController->SelectSliceByPoint(planeOp->GetPoint()); m_SliceNavigationController->AdjustSliceStepperRange(); } } // Also apply rotation on the slicedGeometry - Geometry3D (Bounding geometry) BaseGeometry::ExecuteOperation(¢eredRotation); // // 2nd step. If axis vectors were defined, rotate the plane around its normal to fit these // if (planeOp->AreAxisDefined()) { mitk::Vector3D vecAxixNew = planeOp->GetAxisVec0(); vecAxixNew.Normalize(); mitk::Vector3D VecAxisCurr = planeGeometry->GetAxisVector(0); VecAxisCurr.Normalize(); ScalarType rotationAngle = angle(VecAxisCurr.GetVnlVector(), vecAxixNew.GetVnlVector()); rotationAngle = rotationAngle * 180 / PI; // Rad to Deg // we rotate around the normal of the plane, but we do not know, if we need to rotate clockwise // or anti-clockwise. So we rotate around the crossproduct of old and new Axisvector. // Since both axis vectors lie in the plane, the crossproduct is the planes normal or the negative planes // normal rotationAxis = itk::CrossProduct(VecAxisCurr, vecAxixNew); if (std::abs(rotationAngle - 180) < mitk::eps) { // current axisVec and desired axisVec are not linear independent!!(e.g 1,0,0 and -1,0,0). // Rotation Axis can be just plane Normal. (have to rotate by 180�) rotationAxis = newNormal; } // Perfom Rotation mitk::RotationOperation op(mitk::OpROTATE, center, rotationAxis, rotationAngle); planeGeometry->ExecuteOperation(&op); // Apply changes on first slice to whole slice stack this->ReinitializePlanes(center, planeOp->GetPoint()); planeGeometry->SetSpacing(this->GetSpacing()); if (m_SliceNavigationController) { m_SliceNavigationController->SelectSliceByPoint(planeOp->GetPoint()); m_SliceNavigationController->AdjustSliceStepperRange(); } // Also apply rotation on the slicedGeometry - Geometry3D (Bounding geometry) BaseGeometry::ExecuteOperation(&op); } } else { // Reach through to all slices for (auto iter = m_PlaneGeometries.begin(); iter != m_PlaneGeometries.end(); ++iter) { (*iter)->ExecuteOperation(operation); } } break; case OpRESTOREPLANEPOSITION: if (m_EvenlySpaced) { // Save first slice PlaneGeometry::Pointer planeGeometry = m_PlaneGeometries[0]; - RestorePlanePositionOperation *restorePlaneOp = dynamic_cast(operation); + auto *restorePlaneOp = dynamic_cast(operation); // Need a PlaneGeometry, a PlaneOperation and a reference frame to // carry out the re-orientation if (m_ReferenceGeometry && (planeGeometry && dynamic_cast(planeGeometry.GetPointer()) == nullptr) && restorePlaneOp) { // Clear all generated geometries and then rotate only the first slice. // The other slices will be re-generated on demand // Rotate first slice planeGeometry->ExecuteOperation(restorePlaneOp); m_DirectionVector = restorePlaneOp->GetDirectionVector(); double centerOfRotationDistance = planeGeometry->SignedDistanceFromPlane(m_ReferenceGeometry->GetCenter()); if (centerOfRotationDistance <= 0) { m_DirectionVector = -m_DirectionVector; } Vector3D spacing = restorePlaneOp->GetSpacing(); Superclass::SetSpacing(spacing); // /*Now we need to calculate the number of slices in the plane's normal // direction, so that the entire volume is covered. This is done by first // calculating the dot product between the volume diagonal (the maximum // distance inside the volume) and the normal, and dividing this value by // the directed spacing calculated above.*/ ScalarType directedExtent = std::abs(m_ReferenceGeometry->GetExtentInMM(0) * m_DirectionVector[0]) + std::abs(m_ReferenceGeometry->GetExtentInMM(1) * m_DirectionVector[1]) + std::abs(m_ReferenceGeometry->GetExtentInMM(2) * m_DirectionVector[2]); if (directedExtent >= spacing[2]) { m_Slices = static_cast(directedExtent / spacing[2] + 0.5); } else { m_Slices = 1; } m_PlaneGeometries.assign(m_Slices, PlaneGeometry::Pointer(nullptr)); if (m_Slices > 0) { m_PlaneGeometries[0] = planeGeometry; } m_SliceNavigationController->GetSlice()->SetSteps(m_Slices); this->Modified(); // End Reinitialization if (m_SliceNavigationController) { m_SliceNavigationController->GetSlice()->SetPos(restorePlaneOp->GetPos()); m_SliceNavigationController->AdjustSliceStepperRange(); } BaseGeometry::ExecuteOperation(restorePlaneOp); } } else { // Reach through to all slices for (auto iter = m_PlaneGeometries.begin(); iter != m_PlaneGeometries.end(); ++iter) { (*iter)->ExecuteOperation(operation); } } break; case OpAPPLYTRANSFORMMATRIX: // Clear all generated geometries and then transform only the first slice. // The other slices will be re-generated on demand // Save first slice geometry2D = m_PlaneGeometries[0]; applyMatrixOp = dynamic_cast(operation); // Apply transformation to first plane geometry2D->ExecuteOperation(applyMatrixOp); // Generate a ApplyTransformMatrixOperation using the dataset center instead of // the supplied rotation center. The supplied center is instead used to adjust the // slice stack afterwards (see OpROTATE). center = m_ReferenceGeometry->GetCenter(); // Clear the slice stack and adjust it according to the center of // the dataset and the supplied rotation center (see documentation of // ReinitializePlanes) this->ReinitializePlanes(center, applyMatrixOp->GetReferencePoint()); BaseGeometry::ExecuteOperation(applyMatrixOp); break; default: // let handle by base class if we don't do anything BaseGeometry::ExecuteOperation(operation); } this->Modified(); } diff --git a/Modules/Core/src/DataManagement/mitkStandaloneDataStorage.cpp b/Modules/Core/src/DataManagement/mitkStandaloneDataStorage.cpp index 73887dec14..f77abadacf 100644 --- a/Modules/Core/src/DataManagement/mitkStandaloneDataStorage.cpp +++ b/Modules/Core/src/DataManagement/mitkStandaloneDataStorage.cpp @@ -1,262 +1,262 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkStandaloneDataStorage.h" #include "itkMutexLockHolder.h" #include "itkSimpleFastMutexLock.h" #include "mitkDataNode.h" #include "mitkGroupTagProperty.h" #include "mitkNodePredicateBase.h" #include "mitkNodePredicateProperty.h" #include "mitkProperties.h" mitk::StandaloneDataStorage::StandaloneDataStorage() : mitk::DataStorage() { } mitk::StandaloneDataStorage::~StandaloneDataStorage() { - for (AdjacencyList::iterator it = m_SourceNodes.begin(); it != m_SourceNodes.end(); ++it) + for (auto it = m_SourceNodes.begin(); it != m_SourceNodes.end(); ++it) { this->RemoveListeners(it->first); } } bool mitk::StandaloneDataStorage::IsInitialized() const { return true; } void mitk::StandaloneDataStorage::Add(mitk::DataNode *node, const mitk::DataStorage::SetOfObjects *parents) { { itk::MutexLockHolder locked(m_Mutex); if (!IsInitialized()) throw std::logic_error("DataStorage not initialized"); /* check if node is in its own list of sources */ if ((parents != nullptr) && (std::find(parents->begin(), parents->end(), node) != parents->end())) throw std::invalid_argument("Node is it's own parent"); /* check if node already exists in StandaloneDataStorage */ if (m_SourceNodes.find(node) != m_SourceNodes.end()) throw std::invalid_argument("Node is already in DataStorage"); /* create parent list if it does not exist */ mitk::DataStorage::SetOfObjects::ConstPointer sp; if (parents != nullptr) sp = parents; else sp = mitk::DataStorage::SetOfObjects::New(); /* Store node and parent list in sources adjacency list */ m_SourceNodes.insert(std::make_pair(node, sp)); /* Store node and an empty children list in derivations adjacency list */ mitk::DataStorage::SetOfObjects::Pointer childrenPointer = mitk::DataStorage::SetOfObjects::New(); mitk::DataStorage::SetOfObjects::ConstPointer children = childrenPointer.GetPointer(); m_DerivedNodes.insert(std::make_pair(node, children)); /* create entry in derivations adjacency list for each parent of the new node */ for (SetOfObjects::ConstIterator it = sp->Begin(); it != sp->End(); it++) { mitk::DataNode::ConstPointer parent = it.Value().GetPointer(); mitk::DataStorage::SetOfObjects::ConstPointer derivedObjects = m_DerivedNodes[parent]; // get or create pointer to list of derived objects for that parent node if (derivedObjects.IsNull()) m_DerivedNodes[parent] = mitk::DataStorage::SetOfObjects::New(); // Create a set of Objects, if it does not already exist - mitk::DataStorage::SetOfObjects *deob = const_cast( + auto *deob = const_cast( m_DerivedNodes[parent].GetPointer()); // temporarily get rid of const pointer to insert new element deob->InsertElement(deob->Size(), node); // node is derived from parent. Insert it into the parents list of derived objects } // register for ITK changed events this->AddListeners(node); } /* Notify observers */ EmitAddNodeEvent(node); } void mitk::StandaloneDataStorage::Remove(const mitk::DataNode *node) { if (!IsInitialized()) throw std::logic_error("DataStorage not initialized"); if (node == nullptr) return; // remove ITK modified event listener this->RemoveListeners(node); // muellerm, 22.9.10: add additional reference count to ensure // that the node is not deleted when removed from the relation map // while m_Mutex is locked. This would cause the an itk::DeleteEvent // is thrown and a deadlock will occur when event receivers // access the DataStorage again in their event processing function // mitk::DataNode::ConstPointer nodeGuard(node); /* Notify observers of imminent node removal */ EmitRemoveNodeEvent(node); { itk::MutexLockHolder locked(m_Mutex); /* remove node from both relation adjacency lists */ this->RemoveFromRelation(node, m_SourceNodes); this->RemoveFromRelation(node, m_DerivedNodes); } } bool mitk::StandaloneDataStorage::Exists(const mitk::DataNode *node) const { itk::MutexLockHolder locked(m_Mutex); return (m_SourceNodes.find(node) != m_SourceNodes.end()); } void mitk::StandaloneDataStorage::RemoveFromRelation(const mitk::DataNode *node, AdjacencyList &relation) { - for (AdjacencyList::const_iterator mapIter = relation.cbegin(); mapIter != relation.cend(); + for (auto mapIter = relation.cbegin(); mapIter != relation.cend(); ++mapIter) // for each node in the relation if (mapIter->second.IsNotNull()) // if node has a relation list { SetOfObjects::Pointer s = const_cast(mapIter->second.GetPointer()); // search for node to be deleted in the relation list - SetOfObjects::STLContainerType::iterator relationListIter = std::find( + auto relationListIter = std::find( s->begin(), s->end(), node); // this assumes, that the relation list does not contain duplicates (which should be safe to assume) if (relationListIter != s->end()) // if node to be deleted is in relation list s->erase(relationListIter); // remove it from parentlist } /* now remove node from the relation */ AdjacencyList::iterator adIt; adIt = relation.find(node); if (adIt != relation.end()) relation.erase(adIt); } mitk::DataStorage::SetOfObjects::ConstPointer mitk::StandaloneDataStorage::GetAll() const { itk::MutexLockHolder locked(m_Mutex); if (!IsInitialized()) throw std::logic_error("DataStorage not initialized"); mitk::DataStorage::SetOfObjects::Pointer resultset = mitk::DataStorage::SetOfObjects::New(); /* Fill resultset with all objects that are managed by the StandaloneDataStorage object */ unsigned int index = 0; - for (AdjacencyList::const_iterator it = m_SourceNodes.cbegin(); it != m_SourceNodes.cend(); ++it) + for (auto it = m_SourceNodes.cbegin(); it != m_SourceNodes.cend(); ++it) if (it->first.IsNull()) continue; else resultset->InsertElement(index++, const_cast(it->first.GetPointer())); return SetOfObjects::ConstPointer(resultset); } mitk::DataStorage::SetOfObjects::ConstPointer mitk::StandaloneDataStorage::GetRelations( const mitk::DataNode *node, const AdjacencyList &relation, const NodePredicateBase *condition, bool onlyDirectlyRelated) const { if (node == nullptr) throw std::invalid_argument("invalid node"); /* Either read direct relations directly from adjacency list */ if (onlyDirectlyRelated) { - AdjacencyList::const_iterator it = relation.find(node); // get parents of current node + auto it = relation.find(node); // get parents of current node if ((it == relation.cend()) || (it->second.IsNull())) // node not found in list or no set of parents return SetOfObjects::ConstPointer(mitk::DataStorage::SetOfObjects::New()); // return an empty set else return this->FilterSetOfObjects(it->second, condition); } /* Or traverse adjacency list to collect all related nodes */ std::vector resultset; std::vector openlist; /* Initialize openlist with node. this will add node to resultset, but that is necessary to detect circular relations that would lead to endless recursion */ openlist.push_back(node); while (openlist.size() > 0) { mitk::DataNode::ConstPointer current = openlist.back(); // get element that needs to be processed openlist.pop_back(); // remove last element, because it gets processed now resultset.push_back(current); // add current element to resultset - AdjacencyList::const_iterator it = relation.find(current); // get parents of current node + auto it = relation.find(current); // get parents of current node if ((it == relation.cend()) // if node not found in list || (it->second.IsNull()) // or no set of parents available || (it->second->Size() == 0)) // or empty set of parents continue; // then continue with next node in open list else for (SetOfObjects::ConstIterator parentIt = it->second->Begin(); parentIt != it->second->End(); ++parentIt) // for each parent of current node { mitk::DataNode::ConstPointer p = parentIt.Value().GetPointer(); if (!(std::find(resultset.cbegin(), resultset.cend(), p) != resultset.end()) // if it is not already in resultset && !(std::find(openlist.cbegin(), openlist.cend(), p) != openlist.cend())) // and not already in openlist openlist.push_back(p); // then add it to openlist, so that it can be processed } } /* now finally copy the results to a proper SetOfObjects variable exluding the initial node and checking the condition * if any is given */ mitk::DataStorage::SetOfObjects::Pointer realResultset = mitk::DataStorage::SetOfObjects::New(); if (condition != nullptr) { - for (std::vector::const_iterator resultIt = resultset.cbegin(); + for (auto resultIt = resultset.cbegin(); resultIt != resultset.cend(); ++resultIt) if ((*resultIt != node) && (condition->CheckNode(*resultIt) == true)) realResultset->InsertElement(realResultset->Size(), mitk::DataNode::Pointer(const_cast((*resultIt).GetPointer()))); } else { - for (std::vector::const_iterator resultIt = resultset.cbegin(); + for (auto resultIt = resultset.cbegin(); resultIt != resultset.cend(); ++resultIt) if (*resultIt != node) realResultset->InsertElement(realResultset->Size(), mitk::DataNode::Pointer(const_cast((*resultIt).GetPointer()))); } return SetOfObjects::ConstPointer(realResultset); } mitk::DataStorage::SetOfObjects::ConstPointer mitk::StandaloneDataStorage::GetSources( const mitk::DataNode *node, const NodePredicateBase *condition, bool onlyDirectSources) const { itk::MutexLockHolder locked(m_Mutex); return this->GetRelations(node, m_SourceNodes, condition, onlyDirectSources); } mitk::DataStorage::SetOfObjects::ConstPointer mitk::StandaloneDataStorage::GetDerivations( const mitk::DataNode *node, const NodePredicateBase *condition, bool onlyDirectDerivations) const { itk::MutexLockHolder locked(m_Mutex); return this->GetRelations(node, m_DerivedNodes, condition, onlyDirectDerivations); } void mitk::StandaloneDataStorage::PrintSelf(std::ostream &os, itk::Indent indent) const { os << indent << "StandaloneDataStorage:\n"; Superclass::PrintSelf(os, indent); } diff --git a/Modules/Core/src/DataManagement/mitkSurface.cpp b/Modules/Core/src/DataManagement/mitkSurface.cpp index 8670b592f1..d42609d796 100644 --- a/Modules/Core/src/DataManagement/mitkSurface.cpp +++ b/Modules/Core/src/DataManagement/mitkSurface.cpp @@ -1,513 +1,513 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkSurface.h" #include "mitkInteractionConst.h" #include "mitkSurfaceOperation.h" #include #include static vtkSmartPointer DeepCopy(vtkPolyData *other) { if (other == nullptr) return nullptr; vtkSmartPointer copy = vtkSmartPointer::New(); copy->DeepCopy(other); return copy; } static void Update(vtkPolyData * /*polyData*/) { // if (polyData != nullptr) // polyData->Update(); //VTK6_TODO vtk pipeline } mitk::Surface::Surface() : m_CalculateBoundingBox(false) { this->InitializeEmpty(); } mitk::Surface::Surface(const mitk::Surface &other) : BaseData(other), m_LargestPossibleRegion(other.m_LargestPossibleRegion), m_RequestedRegion(other.m_RequestedRegion), m_CalculateBoundingBox(other.m_CalculateBoundingBox) { if (!other.m_PolyDatas.empty()) { m_PolyDatas.resize(other.m_PolyDatas.size()); std::transform(other.m_PolyDatas.cbegin(), other.m_PolyDatas.cend(), m_PolyDatas.begin(), DeepCopy); } else { this->InitializeEmpty(); } } void mitk::Surface::Swap(mitk::Surface &other) { std::swap(m_PolyDatas, other.m_PolyDatas); std::swap(m_LargestPossibleRegion, other.m_LargestPossibleRegion); std::swap(m_RequestedRegion, other.m_RequestedRegion); std::swap(m_CalculateBoundingBox, other.m_CalculateBoundingBox); } mitk::Surface &mitk::Surface::operator=(Surface other) { this->Swap(other); return *this; } mitk::Surface::~Surface() { this->ClearData(); } void mitk::Surface::ClearData() { m_PolyDatas.clear(); Superclass::ClearData(); } const mitk::Surface::RegionType &mitk::Surface::GetLargestPossibleRegion() const { m_LargestPossibleRegion.SetIndex(3, 0); m_LargestPossibleRegion.SetSize(3, GetTimeGeometry()->CountTimeSteps()); return m_LargestPossibleRegion; } const mitk::Surface::RegionType &mitk::Surface::GetRequestedRegion() const { return m_RequestedRegion; } void mitk::Surface::InitializeEmpty() { if (!m_PolyDatas.empty()) this->ClearData(); Superclass::InitializeTimeGeometry(); m_PolyDatas.push_back(nullptr); m_Initialized = true; } void mitk::Surface::SetVtkPolyData(vtkPolyData *polyData, unsigned int t) { this->Expand(t + 1); if (m_PolyDatas[t] != nullptr) { if (m_PolyDatas[t].GetPointer() == polyData) return; } m_PolyDatas[t].TakeReference(polyData); if (polyData != nullptr) polyData->Register(nullptr); m_CalculateBoundingBox = true; this->Modified(); this->UpdateOutputInformation(); } bool mitk::Surface::IsEmptyTimeStep(unsigned int t) const { if (!IsInitialized()) return false; vtkPolyData *polyData = const_cast(this)->GetVtkPolyData(t); return polyData == nullptr || (polyData->GetNumberOfLines() == 0 && polyData->GetNumberOfPolys() == 0 && polyData->GetNumberOfStrips() == 0 && polyData->GetNumberOfVerts() == 0); } vtkPolyData *mitk::Surface::GetVtkPolyData(unsigned int t) const { if (t < m_PolyDatas.size()) { if (m_PolyDatas[t] == nullptr && this->GetSource().IsNotNull()) { RegionType requestedRegion; requestedRegion.SetIndex(3, t); requestedRegion.SetSize(3, 1); this->m_RequestedRegion = requestedRegion; this->GetSource()->Update(); } return m_PolyDatas[t].GetPointer(); } return nullptr; } void mitk::Surface::UpdateOutputInformation() { if (this->GetSource().IsNotNull()) this->GetSource()->UpdateOutputInformation(); if (m_CalculateBoundingBox == true && !m_PolyDatas.empty()) this->CalculateBoundingBox(); else this->GetTimeGeometry()->Update(); } void mitk::Surface::CalculateBoundingBox() { TimeGeometry *timeGeometry = this->GetTimeGeometry(); if (timeGeometry->CountTimeSteps() != m_PolyDatas.size()) mitkThrow() << "Number of geometry time steps is inconsistent with number of poly data pointers."; for (unsigned int i = 0; i < m_PolyDatas.size(); ++i) { vtkPolyData *polyData = m_PolyDatas[i].GetPointer(); double bounds[6] = {0}; if (polyData != nullptr && polyData->GetNumberOfPoints() > 0) { // polyData->Update(); //VTK6_TODO vtk pipeline polyData->ComputeBounds(); polyData->GetBounds(bounds); } BaseGeometry::Pointer geometry = timeGeometry->GetGeometryForTimeStep(i); if (geometry.IsNull()) mitkThrow() << "Time-sliced geometry is invalid (equals nullptr)."; geometry->SetFloatBounds(bounds); } timeGeometry->Update(); m_CalculateBoundingBox = false; } void mitk::Surface::SetRequestedRegionToLargestPossibleRegion() { m_RequestedRegion = GetLargestPossibleRegion(); } bool mitk::Surface::RequestedRegionIsOutsideOfTheBufferedRegion() { RegionType::IndexValueType end = m_RequestedRegion.GetIndex(3) + m_RequestedRegion.GetSize(3); if (static_cast(m_PolyDatas.size()) < end) return true; for (RegionType::IndexValueType t = m_RequestedRegion.GetIndex(3); t < end; ++t) { if (m_PolyDatas[t] == nullptr) return true; } return false; } bool mitk::Surface::VerifyRequestedRegion() { if (m_RequestedRegion.GetIndex(3) >= 0 && m_RequestedRegion.GetIndex(3) + m_RequestedRegion.GetSize(3) <= m_PolyDatas.size()) return true; return false; } void mitk::Surface::SetRequestedRegion(const itk::DataObject *data) { - const mitk::Surface *surface = dynamic_cast(data); + const auto *surface = dynamic_cast(data); if (surface != nullptr) m_RequestedRegion = surface->GetRequestedRegion(); else mitkThrow() << "Data object used to get requested region is not a mitk::Surface."; } void mitk::Surface::SetRequestedRegion(Surface::RegionType *region) { if (region == nullptr) mitkThrow() << "Requested region is invalid (equals nullptr)"; m_RequestedRegion = *region; } void mitk::Surface::CopyInformation(const itk::DataObject *data) { Superclass::CopyInformation(data); - const mitk::Surface *surface = dynamic_cast(data); + const auto *surface = dynamic_cast(data); if (surface == nullptr) mitkThrow() << "Data object used to get largest possible region is not a mitk::Surface."; m_LargestPossibleRegion = surface->GetLargestPossibleRegion(); } void mitk::Surface::Update() { using ::Update; if (this->GetSource().IsNull()) std::for_each(m_PolyDatas.begin(), m_PolyDatas.end(), Update); Superclass::Update(); } void mitk::Surface::Expand(unsigned int timeSteps) { if (timeSteps > m_PolyDatas.size()) { Superclass::Expand(timeSteps); m_PolyDatas.resize(timeSteps); m_CalculateBoundingBox = true; } } void mitk::Surface::ExecuteOperation(Operation *operation) { switch (operation->GetOperationType()) { case OpSURFACECHANGED: { - mitk::SurfaceOperation *surfaceOperation = dynamic_cast(operation); + auto *surfaceOperation = dynamic_cast(operation); if (surfaceOperation == nullptr) break; unsigned int timeStep = surfaceOperation->GetTimeStep(); if (m_PolyDatas[timeStep] != nullptr) { vtkPolyData *updatedPolyData = surfaceOperation->GetVtkPolyData(); if (updatedPolyData != nullptr) { this->SetVtkPolyData(updatedPolyData, timeStep); this->CalculateBoundingBox(); this->Modified(); } } break; } default: return; } } unsigned int mitk::Surface::GetSizeOfPolyDataSeries() const { return m_PolyDatas.size(); } void mitk::Surface::Graft(const DataObject *data) { - const Surface *surface = dynamic_cast(data); + const auto *surface = dynamic_cast(data); if (surface == nullptr) mitkThrow() << "Data object used to graft surface is not a mitk::Surface."; this->CopyInformation(data); m_PolyDatas.clear(); for (unsigned int i = 0; i < surface->GetSizeOfPolyDataSeries(); ++i) { m_PolyDatas.push_back(vtkSmartPointer::New()); m_PolyDatas.back()->DeepCopy(const_cast(surface)->GetVtkPolyData(i)); } } void mitk::Surface::PrintSelf(std::ostream &os, itk::Indent indent) const { Superclass::PrintSelf(os, indent); os << indent << "\nNumber PolyDatas: " << m_PolyDatas.size() << "\n"; unsigned int count = 0; for (auto it = m_PolyDatas.begin(); it != m_PolyDatas.end(); ++it) { os << "\n"; if (*it != nullptr) { os << indent << "PolyData at time step " << count << ":\n"; os << indent << "Number of cells: " << (*it)->GetNumberOfCells() << "\n"; os << indent << "Number of points: " << (*it)->GetNumberOfPoints() << "\n\n"; os << indent << "VTKPolyData:\n"; (*it)->Print(os); } else { os << indent << "Empty PolyData at time step " << count << "\n"; } ++count; } } bool mitk::Equal(vtkPolyData *leftHandSide, vtkPolyData *rightHandSide, mitk::ScalarType eps, bool verbose) { if ((leftHandSide == nullptr) || (rightHandSide == nullptr)) { MITK_ERROR << "mitk::Equal( vtkPolyData* leftHandSide, vtkPolyData* rightHandSide, mitk::ScalarType eps, bool " "verbose ) does not work for nullptr pointer input."; return false; } return Equal(*leftHandSide, *rightHandSide, eps, verbose); } bool mitk::Equal(vtkPolyData &leftHandSide, vtkPolyData &rightHandSide, mitk::ScalarType eps, bool verbose) { bool noDifferenceFound = true; if (!mitk::Equal(leftHandSide.GetNumberOfCells(), rightHandSide.GetNumberOfCells(), eps, verbose)) { if (verbose) MITK_INFO << "[Equal( vtkPolyData*, vtkPolyData* )] Number of cells not equal"; noDifferenceFound = false; } if (!mitk::Equal(leftHandSide.GetNumberOfVerts(), rightHandSide.GetNumberOfVerts(), eps, verbose)) { if (verbose) MITK_INFO << "[Equal( vtkPolyData*, vtkPolyData* )] Number of vertices not equal"; noDifferenceFound = false; } if (!mitk::Equal(leftHandSide.GetNumberOfLines(), rightHandSide.GetNumberOfLines(), eps, verbose)) { if (verbose) MITK_INFO << "[Equal( vtkPolyData*, vtkPolyData* )] Number of lines not equal"; noDifferenceFound = false; } if (!mitk::Equal(leftHandSide.GetNumberOfPolys(), rightHandSide.GetNumberOfPolys(), eps, verbose)) { if (verbose) MITK_INFO << "[Equal( vtkPolyData*, vtkPolyData* )] Number of polys not equal"; noDifferenceFound = false; } if (!mitk::Equal(leftHandSide.GetNumberOfStrips(), rightHandSide.GetNumberOfStrips(), eps, verbose)) { if (verbose) MITK_INFO << "[Equal( vtkPolyData*, vtkPolyData* )] Number of strips not equal"; noDifferenceFound = false; } { unsigned int numberOfPointsRight = rightHandSide.GetPoints()->GetNumberOfPoints(); unsigned int numberOfPointsLeft = leftHandSide.GetPoints()->GetNumberOfPoints(); if (!mitk::Equal(numberOfPointsLeft, numberOfPointsRight, eps, verbose)) { if (verbose) MITK_INFO << "[Equal( vtkPolyData*, vtkPolyData* )] Number of points not equal"; noDifferenceFound = false; } else { for (unsigned int i(0); i < numberOfPointsRight; i++) { bool pointFound = false; double pointOne[3]; rightHandSide.GetPoints()->GetPoint(i, pointOne); for (unsigned int j(0); j < numberOfPointsLeft; j++) { double pointTwo[3]; leftHandSide.GetPoints()->GetPoint(j, pointTwo); double x = pointOne[0] - pointTwo[0]; double y = pointOne[1] - pointTwo[1]; double z = pointOne[2] - pointTwo[2]; double distance = x * x + y * y + z * z; if (distance < eps) { pointFound = true; break; } } if (!pointFound) { if (verbose) { MITK_INFO << "[Equal( vtkPolyData*, vtkPolyData* )] Right hand side point with id " << i << " and coordinates ( " << std::setprecision(12) << pointOne[0] << " ; " << pointOne[1] << " ; " << pointOne[2] << " ) could not be found in left hand side with epsilon " << eps << "."; } noDifferenceFound = false; break; } } } } return noDifferenceFound; } bool mitk::Equal(mitk::Surface *leftHandSide, mitk::Surface *rightHandSide, mitk::ScalarType eps, bool verbose) { if ((leftHandSide == nullptr) || (rightHandSide == nullptr)) { MITK_ERROR << "mitk::Equal( mitk::Surface* leftHandSide, mitk::Surface* rightHandSide, mitk::ScalarType eps, bool " "verbose ) does not work with nullptr pointer input."; return false; } return Equal(*leftHandSide, *rightHandSide, eps, verbose); } bool mitk::Equal(mitk::Surface &leftHandSide, mitk::Surface &rightHandSide, mitk::ScalarType eps, bool verbose) { bool noDifferenceFound = true; if (!mitk::Equal(leftHandSide.GetSizeOfPolyDataSeries(), rightHandSide.GetSizeOfPolyDataSeries(), eps, verbose)) { if (verbose) MITK_INFO << "[Equal( mitk::surface&, mitk::surface& )] Size of PolyData series not equal."; return false; } // No mitk::Equal for TimeGeometry implemented. // if( ! mitk::Equal( leftHandSide->GetTimeGeometry(), rightHandSide->GetTimeGeometry(), eps, verbose ) ) //{ // if(verbose) // MITK_INFO << "[Equal( mitk::surface&, mitk::surface& )] Time sliced geometries not equal"; // noDifferenceFound = false; //} for (unsigned int i(0); i < rightHandSide.GetSizeOfPolyDataSeries(); i++) { if (!mitk::Equal(*leftHandSide.GetVtkPolyData(i), *rightHandSide.GetVtkPolyData(i), eps, verbose)) { if (verbose) MITK_INFO << "[Equal( mitk::surface&, mitk::surface& )] Poly datas not equal."; noDifferenceFound = false; } } return noDifferenceFound; } diff --git a/Modules/Core/src/DataManagement/mitkTemporoSpatialStringProperty.cpp b/Modules/Core/src/DataManagement/mitkTemporoSpatialStringProperty.cpp index 3fd6ca9580..1a26fc5021 100644 --- a/Modules/Core/src/DataManagement/mitkTemporoSpatialStringProperty.cpp +++ b/Modules/Core/src/DataManagement/mitkTemporoSpatialStringProperty.cpp @@ -1,365 +1,365 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include #include "mitkTemporoSpatialStringProperty.h" #ifdef _MSC_VER // has to be deactivated because of a bug in boost v1.59. see Boost bug ticket #11599 // as soon as MITK uses a boost version with a bug fix we can remove the disableling. #pragma warning(push) #pragma warning(disable : 4715) #endif #include #include #include mitk::TemporoSpatialStringProperty::TemporoSpatialStringProperty(const char *s) { if (s) { SliceMapType slices{{0, s}}; m_Values.insert(std::make_pair(0, slices)); } } mitk::TemporoSpatialStringProperty::TemporoSpatialStringProperty(const std::string &s) { SliceMapType slices{{0, s}}; m_Values.insert(std::make_pair(0, slices)); } mitk::TemporoSpatialStringProperty::TemporoSpatialStringProperty(const TemporoSpatialStringProperty &other) : BaseProperty(other), m_Values(other.m_Values) { } bool mitk::TemporoSpatialStringProperty::IsEqual(const BaseProperty &property) const { return this->m_Values == static_cast(property).m_Values; } bool mitk::TemporoSpatialStringProperty::Assign(const BaseProperty &property) { this->m_Values = static_cast(property).m_Values; return true; } std::string mitk::TemporoSpatialStringProperty::GetValueAsString() const { return GetValue(); } itk::LightObject::Pointer mitk::TemporoSpatialStringProperty::InternalClone() const { itk::LightObject::Pointer result(new Self(*this)); result->UnRegister(); return result; } mitk::TemporoSpatialStringProperty::ValueType mitk::TemporoSpatialStringProperty::GetValue() const { std::string result = ""; if (!m_Values.empty()) { if (!m_Values.begin()->second.empty()) { result = m_Values.begin()->second.begin()->second; } } return result; }; std::pair mitk::TemporoSpatialStringProperty::CheckValue( const TimeStepType &timeStep, const IndexValueType &zSlice, bool allowCloseTime, bool allowCloseSlice) const { std::string value = ""; bool found = false; - TimeMapType::const_iterator timeIter = m_Values.find(timeStep); - TimeMapType::const_iterator timeEnd = m_Values.end(); + auto timeIter = m_Values.find(timeStep); + auto timeEnd = m_Values.end(); if (timeIter == timeEnd && allowCloseTime) { // search for closest time step (earlier preverd) timeIter = m_Values.upper_bound(timeStep); if (timeIter != m_Values.begin()) { // there is a key lower than time step timeIter = std::prev(timeIter); } } if (timeIter != timeEnd) { const SliceMapType &slices = timeIter->second; - SliceMapType::const_iterator sliceIter = slices.find(zSlice); - SliceMapType::const_iterator sliceEnd = slices.end(); + auto sliceIter = slices.find(zSlice); + auto sliceEnd = slices.end(); if (sliceIter == sliceEnd && allowCloseSlice) { // search for closest slice (earlier preverd) sliceIter = slices.upper_bound(zSlice); if (sliceIter != slices.begin()) { // there is a key lower than slice sliceIter = std::prev(sliceIter); } } if (sliceIter != sliceEnd) { value = sliceIter->second; found = true; } } return std::make_pair(found, value); }; mitk::TemporoSpatialStringProperty::ValueType mitk::TemporoSpatialStringProperty::GetValue(const TimeStepType &timeStep, const IndexValueType &zSlice, bool allowCloseTime, bool allowCloseSlice) const { return CheckValue(timeStep, zSlice, allowCloseTime, allowCloseSlice).second; }; mitk::TemporoSpatialStringProperty::ValueType mitk::TemporoSpatialStringProperty::GetValueBySlice( const IndexValueType &zSlice, bool allowClose) const { return GetValue(0, zSlice, true, allowClose); }; mitk::TemporoSpatialStringProperty::ValueType mitk::TemporoSpatialStringProperty::GetValueByTimeStep( const TimeStepType &timeStep, bool allowClose) const { return GetValue(timeStep, 0, allowClose, true); }; bool mitk::TemporoSpatialStringProperty::HasValue() const { return !m_Values.empty(); }; bool mitk::TemporoSpatialStringProperty::HasValue(const TimeStepType &timeStep, const IndexValueType &zSlice, bool allowCloseTime, bool allowCloseSlice) const { return CheckValue(timeStep, zSlice, allowCloseTime, allowCloseSlice).first; }; bool mitk::TemporoSpatialStringProperty::HasValueBySlice(const IndexValueType &zSlice, bool allowClose) const { return HasValue(0, zSlice, true, allowClose); }; bool mitk::TemporoSpatialStringProperty::HasValueByTimeStep(const TimeStepType &timeStep, bool allowClose) const { return HasValue(timeStep, 0, allowClose, true); }; std::vector mitk::TemporoSpatialStringProperty::GetAvailableSlices( const TimeStepType &timeStep) const { std::vector result; - TimeMapType::const_iterator timeIter = m_Values.find(timeStep); - TimeMapType::const_iterator timeEnd = m_Values.end(); + auto timeIter = m_Values.find(timeStep); + auto timeEnd = m_Values.end(); if (timeIter != timeEnd) { for (auto const &element : timeIter->second) { result.push_back(element.first); } } return result; }; std::vector mitk::TemporoSpatialStringProperty::GetAvailableTimeSteps() const { std::vector result; for (auto const &element : m_Values) { result.push_back(element.first); } return result; }; void mitk::TemporoSpatialStringProperty::SetValue(const TimeStepType &timeStep, const IndexValueType &zSlice, const ValueType &value) { - TimeMapType::iterator timeIter = m_Values.find(timeStep); - TimeMapType::iterator timeEnd = m_Values.end(); + auto timeIter = m_Values.find(timeStep); + auto timeEnd = m_Values.end(); if (timeIter == timeEnd) { SliceMapType slices{{zSlice, value}}; m_Values.insert(std::make_pair(timeStep, slices)); } else { timeIter->second[zSlice] = value; } this->Modified(); }; void mitk::TemporoSpatialStringProperty::SetValue(const ValueType &value) { this->Modified(); m_Values.clear(); this->SetValue(0, 0, value); }; // Create necessary escape sequences from illegal characters // REMARK: This code is based upon code from boost::ptree::json_writer. // The corresponding boost function was not used directly, because it is not part of // the public interface of ptree::json_writer. :( // A own serialization strategy was implemented instead of using boost::ptree::json_write because // currently (<= boost 1.60) everything (even numbers) are converted into string representations // by the writer, so e.g. it becomes "t":"2" instaed of "t":2 template std::basic_string CreateJSONEscapes(const std::basic_string &s) { std::basic_string result; typename std::basic_string::const_iterator b = s.begin(); typename std::basic_string::const_iterator e = s.end(); while (b != e) { typedef typename boost::make_unsigned::type UCh; UCh c(*b); // This assumes an ASCII superset. // We escape everything outside ASCII, because this code can't // handle high unicode characters. if (c == 0x20 || c == 0x21 || (c >= 0x23 && c <= 0x2E) || (c >= 0x30 && c <= 0x5B) || (c >= 0x5D && c <= 0x7F)) result += *b; else if (*b == Ch('\b')) result += Ch('\\'), result += Ch('b'); else if (*b == Ch('\f')) result += Ch('\\'), result += Ch('f'); else if (*b == Ch('\n')) result += Ch('\\'), result += Ch('n'); else if (*b == Ch('\r')) result += Ch('\\'), result += Ch('r'); else if (*b == Ch('\t')) result += Ch('\\'), result += Ch('t'); else if (*b == Ch('/')) result += Ch('\\'), result += Ch('/'); else if (*b == Ch('"')) result += Ch('\\'), result += Ch('"'); else if (*b == Ch('\\')) result += Ch('\\'), result += Ch('\\'); else { const char *hexdigits = "0123456789ABCDEF"; unsigned long u = (std::min)(static_cast(static_cast(*b)), 0xFFFFul); int d1 = u / 4096; u -= d1 * 4096; int d2 = u / 256; u -= d2 * 256; int d3 = u / 16; u -= d3 * 16; int d4 = u; result += Ch('\\'); result += Ch('u'); result += Ch(hexdigits[d1]); result += Ch(hexdigits[d2]); result += Ch(hexdigits[d3]); result += Ch(hexdigits[d4]); } ++b; } return result; } ::std::string mitk::PropertyPersistenceSerialization::serializeTemporoSpatialStringPropertyToJSON( const mitk::BaseProperty *prop) { // REMARK: Implemented own serialization instead of using boost::ptree::json_write because // currently (<= boost 1.60) everything (even numbers) are converted into string representations // by the writer, so e.g. it becomes "t":"2" instaed of "t":2 // If this problem is fixed with boost, we shoud switch back to json_writer (and remove the custom // implementation of CreateJSONEscapes (see above)). - const mitk::TemporoSpatialStringProperty *tsProp = dynamic_cast(prop); + const auto *tsProp = dynamic_cast(prop); if (!tsProp) { return ""; } std::ostringstream stream; stream << "{\"values\":["; std::vector ts = tsProp->GetAvailableTimeSteps(); bool first = true; for (auto t : ts) { std::vector zs = tsProp->GetAvailableSlices(t); for (auto z : zs) { std::string value = CreateJSONEscapes(tsProp->GetValue(t, z)); if (first) { first = false; } else { stream << ", "; } stream << "{\"t\":" << t << ", \"z\":" << z << ", \"value\":\"" << value << "\"}"; } } stream << "]}"; return stream.str(); } mitk::BaseProperty::Pointer mitk::PropertyPersistenceDeserialization::deserializeJSONToTemporoSpatialStringProperty( const std::string &value) { mitk::TemporoSpatialStringProperty::Pointer prop = mitk::TemporoSpatialStringProperty::New(); boost::property_tree::ptree root; std::istringstream stream(value); boost::property_tree::read_json(stream, root); for (boost::property_tree::ptree::value_type &element : root.get_child("values")) { std::string value = element.second.get("value", ""); mitk::TemporoSpatialStringProperty::IndexValueType z = element.second.get("z", 0); TimeStepType t = element.second.get("t", 0); prop->SetValue(t, z, value); } return prop.GetPointer(); } #ifdef _MSC_VER #pragma warning(pop) #endif \ No newline at end of file diff --git a/Modules/Core/src/IO/mitkAbstractFileReader.cpp b/Modules/Core/src/IO/mitkAbstractFileReader.cpp index 37e6a72bc2..3a2ddc72ea 100644 --- a/Modules/Core/src/IO/mitkAbstractFileReader.cpp +++ b/Modules/Core/src/IO/mitkAbstractFileReader.cpp @@ -1,324 +1,324 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include #include #include #include #include #include #include #include #include #include namespace mitk { AbstractFileReader::InputStream::InputStream(IFileReader *reader, std::ios_base::openmode mode) : std::istream(nullptr), m_Stream(nullptr) { std::istream *stream = reader->GetInputStream(); if (stream) { this->init(stream->rdbuf()); } else { m_Stream = new std::ifstream(reader->GetInputLocation().c_str(), mode); this->init(m_Stream->rdbuf()); } } AbstractFileReader::InputStream::~InputStream() { delete m_Stream; } class AbstractFileReader::Impl : public FileReaderWriterBase { public: Impl() : FileReaderWriterBase(), m_Stream(nullptr), m_PrototypeFactory(nullptr) {} Impl(const Impl &other) : FileReaderWriterBase(other), m_Stream(nullptr), m_PrototypeFactory(nullptr) {} std::string m_Location; std::string m_TmpFile; std::istream *m_Stream; us::PrototypeServiceFactory *m_PrototypeFactory; us::ServiceRegistration m_Reg; }; AbstractFileReader::AbstractFileReader() : d(new Impl) {} AbstractFileReader::~AbstractFileReader() { UnregisterService(); delete d->m_PrototypeFactory; if (!d->m_TmpFile.empty()) { std::remove(d->m_TmpFile.c_str()); } } AbstractFileReader::AbstractFileReader(const AbstractFileReader &other) : IFileReader(), d(new Impl(*other.d.get())) { } AbstractFileReader::AbstractFileReader(const CustomMimeType &mimeType, const std::string &description) : d(new Impl) { d->SetMimeType(mimeType); d->SetDescription(description); } ////////////////////// Reading ///////////////////////// std::vector AbstractFileReader::Read() { std::vector result; DataStorage::Pointer ds = StandaloneDataStorage::New().GetPointer(); this->Read(*ds); DataStorage::SetOfObjects::ConstPointer dataNodes = ds->GetAll(); for (DataStorage::SetOfObjects::ConstIterator iter = dataNodes->Begin(), iterEnd = dataNodes->End(); iter != iterEnd; ++iter) { result.push_back(iter.Value()->GetData()); } return result; } DataStorage::SetOfObjects::Pointer AbstractFileReader::Read(DataStorage &ds) { DataStorage::SetOfObjects::Pointer result = DataStorage::SetOfObjects::New(); std::vector data = this->Read(); - for (std::vector::iterator iter = data.begin(); iter != data.end(); ++iter) + for (auto iter = data.begin(); iter != data.end(); ++iter) { mitk::DataNode::Pointer node = mitk::DataNode::New(); node->SetData(*iter); this->SetDefaultDataNodeProperties(node, this->GetInputLocation()); ds.Add(node); result->InsertElement(result->Size(), node); } return result; } IFileReader::ConfidenceLevel AbstractFileReader::GetConfidenceLevel() const { if (d->m_Stream) { if (*d->m_Stream) return Supported; } else { if (itksys::SystemTools::FileExists(this->GetInputLocation().c_str(), true)) { return Supported; } } return Unsupported; } //////////// µS Registration & Properties ////////////// us::ServiceRegistration AbstractFileReader::RegisterService(us::ModuleContext *context) { if (d->m_PrototypeFactory) return us::ServiceRegistration(); if (context == nullptr) { context = us::GetModuleContext(); } d->RegisterMimeType(context); if (this->GetMimeType()->GetName().empty()) { MITK_WARN << "Not registering reader due to empty MIME type."; return us::ServiceRegistration(); } struct PrototypeFactory : public us::PrototypeServiceFactory { AbstractFileReader *const m_Prototype; PrototypeFactory(AbstractFileReader *prototype) : m_Prototype(prototype) {} us::InterfaceMap GetService(us::Module * /*module*/, const us::ServiceRegistrationBase & /*registration*/) override { return us::MakeInterfaceMap(m_Prototype->Clone()); } void UngetService(us::Module * /*module*/, const us::ServiceRegistrationBase & /*registration*/, const us::InterfaceMap &service) override { delete us::ExtractInterface(service); } }; d->m_PrototypeFactory = new PrototypeFactory(this); us::ServiceProperties props = this->GetServiceProperties(); d->m_Reg = context->RegisterService(d->m_PrototypeFactory, props); return d->m_Reg; } void AbstractFileReader::UnregisterService() { try { d->m_Reg.Unregister(); } catch (const std::exception &) { } } us::ServiceProperties AbstractFileReader::GetServiceProperties() const { us::ServiceProperties result; result[IFileReader::PROP_DESCRIPTION()] = this->GetDescription(); result[IFileReader::PROP_MIMETYPE()] = this->GetMimeType()->GetName(); result[us::ServiceConstants::SERVICE_RANKING()] = this->GetRanking(); return result; } us::ServiceRegistration AbstractFileReader::RegisterMimeType(us::ModuleContext *context) { return d->RegisterMimeType(context); } std::vector< std::string > AbstractFileReader::GetReadFiles(){ return m_ReadFiles; } void AbstractFileReader::SetMimeType(const CustomMimeType &mimeType) { d->SetMimeType(mimeType); } void AbstractFileReader::SetDescription(const std::string &description) { d->SetDescription(description); } void AbstractFileReader::SetRanking(int ranking) { d->SetRanking(ranking); } int AbstractFileReader::GetRanking() const { return d->GetRanking(); } std::string AbstractFileReader::GetLocalFileName() const { std::string localFileName; if (d->m_Stream) { if (d->m_TmpFile.empty()) { // write the stream contents to temporary file std::string ext = itksys::SystemTools::GetFilenameExtension(this->GetInputLocation()); std::ofstream tmpStream; localFileName = mitk::IOUtil::CreateTemporaryFile( tmpStream, std::ios_base::out | std::ios_base::trunc | std::ios_base::binary, "XXXXXX" + ext); tmpStream << d->m_Stream->rdbuf(); d->m_TmpFile = localFileName; } else { localFileName = d->m_TmpFile; } } else { localFileName = d->m_Location; } return localFileName; } //////////////////////// Options /////////////////////// void AbstractFileReader::SetDefaultOptions(const IFileReader::Options &defaultOptions) { d->SetDefaultOptions(defaultOptions); } IFileReader::Options AbstractFileReader::GetDefaultOptions() const { return d->GetDefaultOptions(); } void AbstractFileReader::SetInput(const std::string &location) { d->m_Location = location; d->m_Stream = nullptr; } void AbstractFileReader::SetInput(const std::string &location, std::istream *is) { if (d->m_Stream != is && !d->m_TmpFile.empty()) { std::remove(d->m_TmpFile.c_str()); d->m_TmpFile.clear(); } d->m_Location = location; d->m_Stream = is; } std::string AbstractFileReader::GetInputLocation() const { return d->m_Location; } std::istream *AbstractFileReader::GetInputStream() const { return d->m_Stream; } MimeType AbstractFileReader::GetRegisteredMimeType() const { return d->GetRegisteredMimeType(); } IFileReader::Options AbstractFileReader::GetOptions() const { return d->GetOptions(); } us::Any AbstractFileReader::GetOption(const std::string &name) const { return d->GetOption(name); } void AbstractFileReader::SetOptions(const Options &options) { d->SetOptions(options); } void AbstractFileReader::SetOption(const std::string &name, const us::Any &value) { d->SetOption(name, value); } ////////////////// MISC ////////////////// void AbstractFileReader::AddProgressCallback(const ProgressCallback &callback) { d->AddProgressCallback(callback); } void AbstractFileReader::RemoveProgressCallback(const ProgressCallback &callback) { d->RemoveProgressCallback(callback); } ////////////////// µS related Getters ////////////////// const CustomMimeType *AbstractFileReader::GetMimeType() const { return d->GetMimeType(); } void AbstractFileReader::SetMimeTypePrefix(const std::string &prefix) { d->SetMimeTypePrefix(prefix); } std::string AbstractFileReader::GetMimeTypePrefix() const { return d->GetMimeTypePrefix(); } std::string AbstractFileReader::GetDescription() const { return d->GetDescription(); } void AbstractFileReader::SetDefaultDataNodeProperties(DataNode *node, const std::string &filePath) { // path if (!filePath.empty()) { mitk::StringProperty::Pointer pathProp = mitk::StringProperty::New(itksys::SystemTools::GetFilenamePath(filePath)); node->SetProperty(StringProperty::PATH, pathProp); } // name already defined? mitk::StringProperty::Pointer nameProp = dynamic_cast(node->GetProperty("name")); if (nameProp.IsNull() || (strcmp(nameProp->GetValue(), "No Name!") == 0)) { // name already defined in BaseData mitk::StringProperty::Pointer baseDataNameProp = dynamic_cast(node->GetData()->GetProperty("name").GetPointer()); if (baseDataNameProp.IsNull() || (strcmp(baseDataNameProp->GetValue(), "No Name!") == 0)) { // name neither defined in node, nor in BaseData -> name = filebasename; nameProp = mitk::StringProperty::New(this->GetRegisteredMimeType().GetFilenameWithoutExtension(filePath)); node->SetProperty("name", nameProp); } else { // name defined in BaseData! nameProp = mitk::StringProperty::New(baseDataNameProp->GetValue()); node->SetProperty("name", nameProp); } } // visibility if (!node->GetProperty("visible")) { node->SetVisibility(true); } } } diff --git a/Modules/Core/src/IO/mitkDicomSeriesReader.cpp b/Modules/Core/src/IO/mitkDicomSeriesReader.cpp index 0d30593eec..52b90ef252 100644 --- a/Modules/Core/src/IO/mitkDicomSeriesReader.cpp +++ b/Modules/Core/src/IO/mitkDicomSeriesReader.cpp @@ -1,1861 +1,1861 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ // uncomment for learning more about the internal sorting mechanisms //#define MBILOG_ENABLE_DEBUG #include #include #include #include #include #include #include #include #include #include #include #include #include #include "mitkProperties.h" namespace mitk { std::string DicomSeriesReader::ReaderImplementationLevelToString(const ReaderImplementationLevel &enumValue) { switch (enumValue) { case ReaderImplementationLevel_Supported: return "Supported"; case ReaderImplementationLevel_PartlySupported: return "PartlySupported"; case ReaderImplementationLevel_Implemented: return "Implemented"; case ReaderImplementationLevel_Unsupported: return "Unsupported"; default: return ""; }; } std::string DicomSeriesReader::PixelSpacingInterpretationToString(const PixelSpacingInterpretation &enumValue) { switch (enumValue) { case PixelSpacingInterpretation_SpacingInPatient: return "In Patient"; case PixelSpacingInterpretation_SpacingAtDetector: return "At Detector"; case PixelSpacingInterpretation_SpacingUnknown: return "Unknown spacing"; default: return ""; }; } const DicomSeriesReader::TagToPropertyMapType &DicomSeriesReader::GetDICOMTagsToMITKPropertyMap() { static bool initialized = false; static TagToPropertyMapType dictionary; if (!initialized) { /* Selection criteria: - no sequences because we cannot represent that - nothing animal related (specied, breed registration number), MITK focusses on human medical image processing. - only general attributes so far When extending this, we should make use of a real dictionary (GDCM/DCMTK and lookup the tag names there) */ // Patient module dictionary["0010|0010"] = "dicom.patient.PatientsName"; dictionary["0010|0020"] = "dicom.patient.PatientID"; dictionary["0010|0030"] = "dicom.patient.PatientsBirthDate"; dictionary["0010|0040"] = "dicom.patient.PatientsSex"; dictionary["0010|0032"] = "dicom.patient.PatientsBirthTime"; dictionary["0010|1000"] = "dicom.patient.OtherPatientIDs"; dictionary["0010|1001"] = "dicom.patient.OtherPatientNames"; dictionary["0010|2160"] = "dicom.patient.EthnicGroup"; dictionary["0010|4000"] = "dicom.patient.PatientComments"; dictionary["0012|0062"] = "dicom.patient.PatientIdentityRemoved"; dictionary["0012|0063"] = "dicom.patient.DeIdentificationMethod"; // General Study module dictionary["0020|000d"] = "dicom.study.StudyInstanceUID"; dictionary["0008|0020"] = "dicom.study.StudyDate"; dictionary["0008|0030"] = "dicom.study.StudyTime"; dictionary["0008|0090"] = "dicom.study.ReferringPhysiciansName"; dictionary["0020|0010"] = "dicom.study.StudyID"; dictionary["0008|0050"] = "dicom.study.AccessionNumber"; dictionary["0008|1030"] = "dicom.study.StudyDescription"; dictionary["0008|1048"] = "dicom.study.PhysiciansOfRecord"; dictionary["0008|1060"] = "dicom.study.NameOfPhysicianReadingStudy"; // General Series module dictionary["0008|0060"] = "dicom.series.Modality"; dictionary["0020|000e"] = "dicom.series.SeriesInstanceUID"; dictionary["0020|0011"] = "dicom.series.SeriesNumber"; dictionary["0020|0060"] = "dicom.series.Laterality"; dictionary["0008|0021"] = "dicom.series.SeriesDate"; dictionary["0008|0031"] = "dicom.series.SeriesTime"; dictionary["0008|1050"] = "dicom.series.PerformingPhysiciansName"; dictionary["0018|1030"] = "dicom.series.ProtocolName"; dictionary["0008|103e"] = "dicom.series.SeriesDescription"; dictionary["0008|1070"] = "dicom.series.OperatorsName"; dictionary["0018|0015"] = "dicom.series.BodyPartExamined"; dictionary["0018|5100"] = "dicom.series.PatientPosition"; dictionary["0028|0108"] = "dicom.series.SmallestPixelValueInSeries"; dictionary["0028|0109"] = "dicom.series.LargestPixelValueInSeries"; // VOI LUT module dictionary["0028|1050"] = "dicom.voilut.WindowCenter"; dictionary["0028|1051"] = "dicom.voilut.WindowWidth"; dictionary["0028|1055"] = "dicom.voilut.WindowCenterAndWidthExplanation"; // Image Pixel module dictionary["0028|0004"] = "dicom.pixel.PhotometricInterpretation"; dictionary["0028|0010"] = "dicom.pixel.Rows"; dictionary["0028|0011"] = "dicom.pixel.Columns"; // Image Plane module dictionary["0028|0030"] = "dicom.PixelSpacing"; dictionary["0018|1164"] = "dicom.ImagerPixelSpacing"; initialized = true; } return dictionary; } DataNode::Pointer DicomSeriesReader::LoadDicomSeries(const StringContainer &filenames, bool sort, bool check_4d, bool correctTilt, UpdateCallBackMethod callback, Image::Pointer preLoadedImageBlock) { DataNode::Pointer node = DataNode::New(); if (DicomSeriesReader::LoadDicomSeries( filenames, *node, sort, check_4d, correctTilt, callback, preLoadedImageBlock)) { if (filenames.empty()) { return nullptr; } return node; } else { return nullptr; } } bool DicomSeriesReader::LoadDicomSeries(const StringContainer &filenames, DataNode &node, bool sort, bool check_4d, bool correctTilt, UpdateCallBackMethod callback, itk::SmartPointer preLoadedImageBlock) { if (filenames.empty()) { MITK_DEBUG << "Calling LoadDicomSeries with empty filename string container. Probably invalid application logic."; node.SetData(nullptr); return true; // this is not actually an error but the result is very simple } DcmIoType::Pointer io = DcmIoType::New(); try { if (io->CanReadFile(filenames.front().c_str())) { io->SetFileName(filenames.front().c_str()); io->ReadImageInformation(); if (io->GetPixelType() == itk::ImageIOBase::SCALAR || io->GetPixelType() == itk::ImageIOBase::RGB) { LoadDicom(filenames, node, sort, check_4d, correctTilt, callback, preLoadedImageBlock); } if (node.GetData()) { return true; } } } catch (itk::MemoryAllocationError &e) { MITK_ERROR << "Out of memory. Cannot load DICOM series: " << e.what(); } catch (std::exception &e) { MITK_ERROR << "Error encountered when loading DICOM series:" << e.what(); } catch (...) { MITK_ERROR << "Unspecified error encountered when loading DICOM series."; } return false; } bool DicomSeriesReader::IsDicom(const std::string &filename) { DcmIoType::Pointer io = DcmIoType::New(); return io->CanReadFile(filename.c_str()); } bool DicomSeriesReader::IsPhilips3DDicom(const std::string &filename) { DcmIoType::Pointer io = DcmIoType::New(); if (io->CanReadFile(filename.c_str())) { // Look at header Tag 3001,0010 if it is "Philips3D" gdcm::Reader reader; reader.SetFileName(filename.c_str()); reader.Read(); gdcm::DataSet &data_set = reader.GetFile().GetDataSet(); gdcm::StringFilter sf; sf.SetFile(reader.GetFile()); if (data_set.FindDataElement(gdcm::Tag(0x3001, 0x0010)) && (sf.ToString(gdcm::Tag(0x3001, 0x0010)) == "Philips3D ")) { return true; } } return false; } bool DicomSeriesReader::ReadPhilips3DDicom(const std::string &filename, itk::SmartPointer output_image) { // Now get PhilipsSpecific Tags gdcm::PixmapReader reader; reader.SetFileName(filename.c_str()); reader.Read(); gdcm::DataSet &data_set = reader.GetFile().GetDataSet(); gdcm::StringFilter sf; sf.SetFile(reader.GetFile()); gdcm::Attribute<0x0028, 0x0011> dimTagX; // coloumns || sagittal gdcm::Attribute<0x3001, 0x1001, gdcm::VR::UL, gdcm::VM::VM1> dimTagZ; // I have no idea what is VM1. // (Philips specific) // axial gdcm::Attribute<0x0028, 0x0010> dimTagY; // rows || coronal gdcm::Attribute<0x0028, 0x0008> dimTagT; // how many frames gdcm::Attribute<0x0018, 0x602c> spaceTagX; // Spacing in X , unit is "physicalTagx" (usually centimeter) gdcm::Attribute<0x0018, 0x602e> spaceTagY; gdcm::Attribute<0x3001, 0x1003, gdcm::VR::FD, gdcm::VM::VM1> spaceTagZ; // (Philips specific) gdcm::Attribute<0x0018, 0x6024> physicalTagX; // if 3, then spacing params are centimeter gdcm::Attribute<0x0018, 0x6026> physicalTagY; gdcm::Attribute<0x3001, 0x1002, gdcm::VR::US, gdcm::VM::VM1> physicalTagZ; // (Philips specific) dimTagX.Set(data_set); dimTagY.Set(data_set); dimTagZ.Set(data_set); dimTagT.Set(data_set); spaceTagX.Set(data_set); spaceTagY.Set(data_set); spaceTagZ.Set(data_set); physicalTagX.Set(data_set); physicalTagY.Set(data_set); physicalTagZ.Set(data_set); unsigned int dimX = dimTagX.GetValue(), dimY = dimTagY.GetValue(), dimZ = dimTagZ.GetValue(), dimT = dimTagT.GetValue(), physicalX = physicalTagX.GetValue(), physicalY = physicalTagY.GetValue(), physicalZ = physicalTagZ.GetValue(); float spaceX = spaceTagX.GetValue(), spaceY = spaceTagY.GetValue(), spaceZ = spaceTagZ.GetValue(); if (physicalX == 3) // spacing parameter in cm, have to convert it to mm. spaceX = spaceX * 10; if (physicalY == 3) // spacing parameter in cm, have to convert it to mm. spaceY = spaceY * 10; if (physicalZ == 3) // spacing parameter in cm, have to convert it to mm. spaceZ = spaceZ * 10; // Ok, got all necessary Tags! // Now read Pixeldata (7fe0,0010) X x Y x Z x T Elements const gdcm::Pixmap &pixels = reader.GetPixmap(); gdcm::RAWCodec codec; codec.SetPhotometricInterpretation(gdcm::PhotometricInterpretation::MONOCHROME2); codec.SetPixelFormat(pixels.GetPixelFormat()); codec.SetPlanarConfiguration(0); gdcm::DataElement out; codec.Decode(data_set.GetDataElement(gdcm::Tag(0x7fe0, 0x0010)), out); const gdcm::ByteValue *bv = out.GetByteValue(); const char *new_pixels = bv->GetPointer(); // Create MITK Image + Geometry typedef itk::Image ImageType; // Pixeltype might be different sometimes? Maybe read it out from header ImageType::RegionType myRegion; ImageType::SizeType mySize; ImageType::IndexType myIndex; ImageType::SpacingType mySpacing; ImageType::Pointer imageItk = ImageType::New(); mySpacing[0] = spaceX; mySpacing[1] = spaceY; mySpacing[2] = spaceZ; mySpacing[3] = 1; myIndex[0] = 0; myIndex[1] = 0; myIndex[2] = 0; myIndex[3] = 0; mySize[0] = dimX; mySize[1] = dimY; mySize[2] = dimZ; mySize[3] = dimT; myRegion.SetSize(mySize); myRegion.SetIndex(myIndex); imageItk->SetSpacing(mySpacing); imageItk->SetRegions(myRegion); imageItk->Allocate(); imageItk->FillBuffer(0); itk::ImageRegionIterator iterator(imageItk, imageItk->GetLargestPossibleRegion()); iterator.GoToBegin(); unsigned long pixCount = 0; unsigned long planeSize = dimX * dimY; unsigned long planeCount = 0; unsigned long timeCount = 0; unsigned long numberOfSlices = dimZ; while (!iterator.IsAtEnd()) { unsigned long adressedPixel = pixCount + (numberOfSlices - 1 - planeCount) * planeSize // add offset to adress the first pixel of current plane + timeCount * numberOfSlices * planeSize; // add time offset iterator.Set(new_pixels[adressedPixel]); pixCount++; ++iterator; if (pixCount == planeSize) { pixCount = 0; planeCount++; } if (planeCount == numberOfSlices) { planeCount = 0; timeCount++; } if (timeCount == dimT) { break; } } mitk::CastToMitkImage(imageItk, output_image); return true; // actually never returns false yet.. but exception possible } std::string DicomSeriesReader::ConstCharStarToString(const char *s) { return s ? std::string(s) : std::string(); } bool DicomSeriesReader::DICOMStringToSpacing(const std::string &s, ScalarType &spacingX, ScalarType &spacingY) { bool successful = false; std::istringstream spacingReader(s); std::string spacing; if (std::getline(spacingReader, spacing, '\\')) { spacingY = atof(spacing.c_str()); if (std::getline(spacingReader, spacing, '\\')) { spacingX = atof(spacing.c_str()); successful = true; } } return successful; } Point3D DicomSeriesReader::DICOMStringToPoint3D(const std::string &s, bool &successful) { Point3D p; successful = true; std::istringstream originReader(s); std::string coordinate; unsigned int dim(0); while (std::getline(originReader, coordinate, '\\') && dim < 3) { p[dim++] = atof(coordinate.c_str()); } if (dim && dim != 3) { successful = false; MITK_ERROR << "Reader implementation made wrong assumption on tag (0020,0032). Found " << dim << " instead of 3 values."; } else if (dim == 0) { successful = false; p.Fill(0.0); // assume default (0,0,0) } return p; } void DicomSeriesReader::DICOMStringToOrientationVectors(const std::string &s, Vector3D &right, Vector3D &up, bool &successful) { successful = true; std::istringstream orientationReader(s); std::string coordinate; unsigned int dim(0); while (std::getline(orientationReader, coordinate, '\\') && dim < 6) { if (dim < 3) { right[dim++] = atof(coordinate.c_str()); } else { up[dim++ - 3] = atof(coordinate.c_str()); } } if (dim && dim != 6) { successful = false; MITK_ERROR << "Reader implementation made wrong assumption on tag (0020,0037). Found " << dim << " instead of 6 values."; } else if (dim == 0) { // fill with defaults right.Fill(0.0); right[0] = 1.0; up.Fill(0.0); up[1] = 1.0; successful = false; } } DicomSeriesReader::SliceGroupingAnalysisResult DicomSeriesReader::AnalyzeFileForITKImageSeriesReaderSpacingAssumption( const StringContainer &files, bool groupImagesWithGantryTilt, const gdcm::Scanner::MappingType &tagValueMappings_) { // result.first = files that fit ITK's assumption // result.second = files that do not fit, should be run through // AnalyzeFileForITKImageSeriesReaderSpacingAssumption() // again SliceGroupingAnalysisResult result; // we const_cast here, because I could not use a map.at(), which would make the code much more readable - gdcm::Scanner::MappingType &tagValueMappings = const_cast(tagValueMappings_); + auto &tagValueMappings = const_cast(tagValueMappings_); const gdcm::Tag tagImagePositionPatient(0x0020, 0x0032); // Image Position (Patient) const gdcm::Tag tagImageOrientation(0x0020, 0x0037); // Image Orientation const gdcm::Tag tagGantryTilt(0x0018, 0x1120); // gantry tilt Vector3D fromFirstToSecondOrigin; fromFirstToSecondOrigin.Fill(0.0); bool fromFirstToSecondOriginInitialized(false); Point3D thisOrigin; thisOrigin.Fill(0.0f); Point3D lastOrigin; lastOrigin.Fill(0.0f); Point3D lastDifferentOrigin; lastDifferentOrigin.Fill(0.0f); bool lastOriginInitialized(false); MITK_DEBUG << "--------------------------------------------------------------------------------"; MITK_DEBUG << "Analyzing files for z-spacing assumption of ITK's ImageSeriesReader (group tilted: " << groupImagesWithGantryTilt << ")"; unsigned int fileIndex(0); for (auto fileIter = files.begin(); fileIter != files.end(); ++fileIter, ++fileIndex) { bool fileFitsIntoPattern(false); std::string thisOriginString; // Read tag value into point3D. PLEASE replace this by appropriate GDCM code if you figure out how to do that thisOriginString = ConstCharStarToString(tagValueMappings[fileIter->c_str()][tagImagePositionPatient]); if (thisOriginString.empty()) { // don't let such files be in a common group. Everything without position information will be loaded as a single // slice: // with standard DICOM files this can happen to: CR, DX, SC MITK_DEBUG << " ==> Sort away " << *fileIter << " for later analysis (no position information)"; // we already have one occupying this position if (result.GetBlockFilenames().empty()) // nothing WITH position information yet { // ==> this is a group of its own, stop processing, come back later result.AddFileToSortedBlock(*fileIter); StringContainer remainingFiles; remainingFiles.insert(remainingFiles.end(), fileIter + 1, files.end()); result.AddFilesToUnsortedBlock(remainingFiles); fileFitsIntoPattern = false; break; // no files anymore } else { // ==> this does not match, consider later result.AddFileToUnsortedBlock(*fileIter); fileFitsIntoPattern = false; continue; // next file } } bool ignoredConversionError(-42); // hard to get here, no graceful way to react thisOrigin = DICOMStringToPoint3D(thisOriginString, ignoredConversionError); MITK_DEBUG << " " << fileIndex << " " << *fileIter << " at " /* << thisOriginString */ << "(" << thisOrigin[0] << "," << thisOrigin[1] << "," << thisOrigin[2] << ")"; if (lastOriginInitialized && (thisOrigin == lastOrigin)) { MITK_DEBUG << " ==> Sort away " << *fileIter << " for separate time step"; // we already have one occupying this position result.AddFileToUnsortedBlock(*fileIter); fileFitsIntoPattern = false; } else { if (!fromFirstToSecondOriginInitialized && lastOriginInitialized) // calculate vector as soon as possible when we get a new position { fromFirstToSecondOrigin = thisOrigin - lastDifferentOrigin; fromFirstToSecondOriginInitialized = true; // Here we calculate if this slice and the previous one are well aligned, // i.e. we test if the previous origin is on a line through the current // origin, directed into the normal direction of the current slice. // If this is NOT the case, then we have a data set with a TILTED GANTRY geometry, // which cannot be simply loaded into a single mitk::Image at the moment. // For this case, we flag this finding in the result and DicomSeriesReader // can correct for that later. Vector3D right; right.Fill(0.0); Vector3D up; right.Fill(0.0); // might be down as well, but it is just a name at this point DICOMStringToOrientationVectors( tagValueMappings[fileIter->c_str()][tagImageOrientation], right, up, ignoredConversionError); GantryTiltInformation tiltInfo(lastDifferentOrigin, thisOrigin, right, up, 1); if (tiltInfo.IsSheared()) // mitk::eps is too small; 1/1000 of a mm should be enough to detect tilt { /* optimistic approach, accepting gantry tilt: save file for later, check all further files */ // at this point we have TWO slices analyzed! if they are the only two files, we still split, because there // is // no third to verify our tilting assumption. // later with a third being available, we must check if the initial tilting vector is still valid. if yes, // continue. // if NO, we need to split the already sorted part (result.first) and the currently analyzed file // (*fileIter) // tell apart gantry tilt from overall skewedness // sort out irregularly sheared slices, that IS NOT tilting if (groupImagesWithGantryTilt && tiltInfo.IsRegularGantryTilt()) { // check if this is at least roughly the same angle as recorded in DICOM tags if (tagValueMappings[fileIter->c_str()].find(tagGantryTilt) != tagValueMappings[fileIter->c_str()].end()) { // read value, compare to calculated angle std::string tiltStr = ConstCharStarToString(tagValueMappings[fileIter->c_str()][tagGantryTilt]); double angle = atof(tiltStr.c_str()); MITK_DEBUG << "Comparing recorded tilt angle " << angle << " against calculated value " << tiltInfo.GetTiltAngleInDegrees(); // TODO we probably want the signs correct, too (that depends: this is just a rough check, nothing // serious) // TODO TODO TODO when angle -27 and tiltangle 63, this will never trigger the if-clause... useless // check // in this case! old bug..?! if (fabs(angle) - tiltInfo.GetTiltAngleInDegrees() > 0.25) { result.AddFileToUnsortedBlock(*fileIter); // sort away for further analysis fileFitsIntoPattern = false; } else // tilt angle from header is less than 0.25 degrees different from what we calculated, assume this // is // fine { result.FlagGantryTilt(); result.AddFileToSortedBlock(*fileIter); // this file is good for current block fileFitsIntoPattern = true; } } else // we cannot check the calculated tilt angle against the one from the dicom header (so we assume we // are // right) { result.FlagGantryTilt(); result.AddFileToSortedBlock(*fileIter); // this file is good for current block fileFitsIntoPattern = true; } } else // caller does not want tilt compensation OR shearing is more complicated than tilt { result.AddFileToUnsortedBlock(*fileIter); // sort away for further analysis fileFitsIntoPattern = false; } } else // not sheared { result.AddFileToSortedBlock(*fileIter); // this file is good for current block fileFitsIntoPattern = true; } } else if (fromFirstToSecondOriginInitialized) // we already know the offset between slices { Point3D assumedOrigin = lastDifferentOrigin + fromFirstToSecondOrigin; Vector3D originError = assumedOrigin - thisOrigin; double norm = originError.GetNorm(); double toleratedError(0.005); // max. 1/10mm error when measurement crosses 20 slices in z direction if (norm > toleratedError) { MITK_DEBUG << " File does not fit into the inter-slice distance pattern (diff = " << norm << ", allowed " << toleratedError << ")."; MITK_DEBUG << " Expected position (" << assumedOrigin[0] << "," << assumedOrigin[1] << "," << assumedOrigin[2] << "), got position (" << thisOrigin[0] << "," << thisOrigin[1] << "," << thisOrigin[2] << ")"; MITK_DEBUG << " ==> Sort away " << *fileIter << " for later analysis"; // At this point we know we deviated from the expectation of ITK's ImageSeriesReader // We split the input file list at this point, i.e. all files up to this one (excluding it) // are returned as group 1, the remaining files (including the faulty one) are group 2 /* Optimistic approach: check if any of the remaining slices fits in */ result.AddFileToUnsortedBlock(*fileIter); // sort away for further analysis fileFitsIntoPattern = false; } else { result.AddFileToSortedBlock(*fileIter); // this file is good for current block fileFitsIntoPattern = true; } } else // this should be the very first slice { result.AddFileToSortedBlock(*fileIter); // this file is good for current block fileFitsIntoPattern = true; } } // record current origin for reference in later iterations if (!lastOriginInitialized || (fileFitsIntoPattern && (thisOrigin != lastOrigin))) { lastDifferentOrigin = thisOrigin; } lastOrigin = thisOrigin; lastOriginInitialized = true; } if (result.ContainsGantryTilt()) { // check here how many files were grouped. // IF it was only two files AND we assume tiltedness (e.g. save "distance") // THEN we would want to also split the two previous files (simple) because // we don't have any reason to assume they belong together if (result.GetBlockFilenames().size() == 2) { result.UndoPrematureGrouping(); } } return result; } DicomSeriesReader::FileNamesGrouping DicomSeriesReader::GetSeries(const StringContainer &files, bool groupImagesWithGantryTilt, const StringContainer &restrictions) { return GetSeries(files, true, groupImagesWithGantryTilt, restrictions); } DicomSeriesReader::FileNamesGrouping DicomSeriesReader::GetSeries(const StringContainer &files, bool sortTo3DPlust, bool groupImagesWithGantryTilt, const StringContainer & /*restrictions*/) { /** assumption about this method: returns a map of uid-like-key --> list(filename) each entry should contain filenames that have images of same - series instance uid (automatically done by GDCMSeriesFileNames - 0020,0037 image orientation (patient) - 0028,0030 pixel spacing (x,y) - 0018,0050 slice thickness */ // use GDCM directly, itk::GDCMSeriesFileNames does not work with GDCM 2 // PART I: scan files for sorting relevant DICOM tags, // separate images that differ in any of those // attributes (they cannot possibly form a 3D block) // scan for relevant tags in dicom files gdcm::Scanner scanner; const gdcm::Tag tagSOPClassUID(0x0008, 0x0016); // SOP class UID scanner.AddTag(tagSOPClassUID); const gdcm::Tag tagSeriesInstanceUID(0x0020, 0x000e); // Series Instance UID scanner.AddTag(tagSeriesInstanceUID); const gdcm::Tag tagImageOrientation(0x0020, 0x0037); // image orientation scanner.AddTag(tagImageOrientation); const gdcm::Tag tagPixelSpacing(0x0028, 0x0030); // pixel spacing scanner.AddTag(tagPixelSpacing); const gdcm::Tag tagImagerPixelSpacing(0x0018, 0x1164); // imager pixel spacing scanner.AddTag(tagImagerPixelSpacing); const gdcm::Tag tagSliceThickness(0x0018, 0x0050); // slice thickness scanner.AddTag(tagSliceThickness); const gdcm::Tag tagNumberOfRows(0x0028, 0x0010); // number rows scanner.AddTag(tagNumberOfRows); const gdcm::Tag tagNumberOfColumns(0x0028, 0x0011); // number cols scanner.AddTag(tagNumberOfColumns); const gdcm::Tag tagGantryTilt(0x0018, 0x1120); // gantry tilt scanner.AddTag(tagGantryTilt); const gdcm::Tag tagModality(0x0008, 0x0060); // modality scanner.AddTag(tagModality); const gdcm::Tag tagNumberOfFrames(0x0028, 0x0008); // number of frames scanner.AddTag(tagNumberOfFrames); // additional tags read in this scan to allow later analysis // THESE tag are not used for initial separating of files const gdcm::Tag tagImagePositionPatient(0x0020, 0x0032); // Image Position (Patient) scanner.AddTag(tagImagePositionPatient); // TODO add further restrictions from arguments (when anybody asks for it) FileNamesGrouping result; // let GDCM scan files if (!scanner.Scan(files)) { MITK_ERROR << "gdcm::Scanner failed when scanning " << files.size() << " input files."; return result; } // assign files IDs that will separate them for loading into image blocks for (auto fileIter = scanner.Begin(); fileIter != scanner.End(); ++fileIter) { if (std::string(fileIter->first).empty()) continue; // TODO understand why Scanner has empty string entries if (std::string(fileIter->first) == std::string("DICOMDIR")) continue; /* sort out multi-frame if ( scanner.GetValue( fileIter->first , tagNumberOfFrames ) ) { MITK_INFO << "Ignoring " << fileIter->first << " because we cannot handle multi-frame images."; continue; } */ // we const_cast here, because I could not use a map.at() function in CreateMoreUniqueSeriesIdentifier. // doing the same thing with find would make the code less readable. Since we forget the Scanner results // anyway after this function, we can simply tolerate empty map entries introduced by bad operator[] access std::string moreUniqueSeriesId = CreateMoreUniqueSeriesIdentifier(const_cast(fileIter->second)); result[moreUniqueSeriesId].AddFile(fileIter->first); } // PART II: sort slices spatially (or at least consistently if this is NOT possible, see method) for (FileNamesGrouping::const_iterator groupIter = result.begin(); groupIter != result.end(); ++groupIter) { try { result[groupIter->first] = ImageBlockDescriptor(SortSeriesSlices(groupIter->second.GetFilenames())); // sort each slice group spatially } catch (...) { MITK_ERROR << "Caught something."; } } // PART III: analyze pre-sorted images for valid blocks (i.e. blocks of equal z-spacing), // separate into multiple blocks if necessary. // // Analysis performs the following steps: // * imitate itk::ImageSeriesReader: use the distance between the first two images as z-spacing // * check what images actually fulfill ITK's z-spacing assumption // * separate all images that fail the test into new blocks, re-iterate analysis for these blocks // * this includes images which DO NOT PROVIDE spatial information, i.e. all images w/o // ImagePositionPatient will be loaded separately FileNamesGrouping groupsOf3DPlusTBlocks; // final result of this function for (FileNamesGrouping::const_iterator groupIter = result.begin(); groupIter != result.end(); ++groupIter) { FileNamesGrouping groupsOf3DBlocks; // intermediate result for only this group(!) StringContainer filesStillToAnalyze = groupIter->second.GetFilenames(); std::string groupUID = groupIter->first; unsigned int subgroup(0); MITK_DEBUG << "Analyze group " << groupUID << " of " << groupIter->second.GetFilenames().size() << " files"; while (!filesStillToAnalyze.empty()) // repeat until all files are grouped somehow { SliceGroupingAnalysisResult analysisResult = AnalyzeFileForITKImageSeriesReaderSpacingAssumption( filesStillToAnalyze, groupImagesWithGantryTilt, scanner.GetMappings()); // enhance the UID for additional groups std::stringstream newGroupUID; newGroupUID << groupUID << '.' << subgroup; ImageBlockDescriptor thisBlock(analysisResult.GetBlockFilenames()); std::string firstFileInBlock = thisBlock.GetFilenames().front(); thisBlock.SetImageBlockUID(newGroupUID.str()); thisBlock.SetSeriesInstanceUID( DicomSeriesReader::ConstCharStarToString(scanner.GetValue(firstFileInBlock.c_str(), tagSeriesInstanceUID))); thisBlock.SetHasGantryTiltCorrected(analysisResult.ContainsGantryTilt()); thisBlock.SetSOPClassUID( DicomSeriesReader::ConstCharStarToString(scanner.GetValue(firstFileInBlock.c_str(), tagSOPClassUID))); thisBlock.SetNumberOfFrames( ConstCharStarToString(scanner.GetValue(firstFileInBlock.c_str(), tagNumberOfFrames))); thisBlock.SetModality( DicomSeriesReader::ConstCharStarToString(scanner.GetValue(firstFileInBlock.c_str(), tagModality))); thisBlock.SetPixelSpacingInformation( DicomSeriesReader::ConstCharStarToString(scanner.GetValue(firstFileInBlock.c_str(), tagPixelSpacing)), DicomSeriesReader::ConstCharStarToString(scanner.GetValue(firstFileInBlock.c_str(), tagImagerPixelSpacing))); thisBlock.SetHasMultipleTimePoints(false); groupsOf3DBlocks[newGroupUID.str()] = thisBlock; // MITK_DEBUG << "Result: sorted 3D group " << newGroupUID.str() << " with " << groupsOf3DBlocks[ // newGroupUID.str() ].GetFilenames().size() << " files"; MITK_DEBUG << "Result: sorted 3D group with " << groupsOf3DBlocks[newGroupUID.str()].GetFilenames().size() << " files"; StringContainer debugOutputFiles = analysisResult.GetBlockFilenames(); for (StringContainer::const_iterator siter = debugOutputFiles.begin(); siter != debugOutputFiles.end(); ++siter) MITK_DEBUG << " IN " << *siter; ++subgroup; filesStillToAnalyze = analysisResult.GetUnsortedFilenames(); // remember what needs further analysis for (StringContainer::const_iterator siter = filesStillToAnalyze.begin(); siter != filesStillToAnalyze.end(); ++siter) MITK_DEBUG << " OUT " << *siter; } // end of grouping, now post-process groups // PART IV: attempt to group blocks to 3D+t blocks if requested // inspect entries of groupsOf3DBlocks // - if number of files is identical to previous entry, collect for 3D+t block // - as soon as number of files changes from previous entry, record collected blocks as 3D+t block, // start // a new one, continue // decide whether or not to group 3D blocks into 3D+t blocks where possible if (!sortTo3DPlust) { // copy 3D blocks to output groupsOf3DPlusTBlocks.insert(groupsOf3DBlocks.begin(), groupsOf3DBlocks.end()); } else { // sort 3D+t (as described in "PART IV") MITK_DEBUG << "================================================================================"; MITK_DEBUG << "3D+t analysis:"; unsigned int numberOfFilesInPreviousBlock(0); std::string previousBlockKey; for (FileNamesGrouping::const_iterator block3DIter = groupsOf3DBlocks.begin(); block3DIter != groupsOf3DBlocks.end(); ++block3DIter) { unsigned int numberOfFilesInThisBlock = block3DIter->second.GetFilenames().size(); std::string thisBlockKey = block3DIter->first; if (numberOfFilesInPreviousBlock == 0) { numberOfFilesInPreviousBlock = numberOfFilesInThisBlock; groupsOf3DPlusTBlocks[thisBlockKey] = block3DIter->second; MITK_DEBUG << " 3D+t group " << thisBlockKey; previousBlockKey = thisBlockKey; } else { bool identicalOrigins; try { // check whether this and the previous block share a comon origin // TODO should be safe, but a little try/catch or other error handling wouldn't hurt const char *origin_value = scanner.GetValue(groupsOf3DBlocks[thisBlockKey].GetFilenames().front().c_str(), tagImagePositionPatient), *previous_origin_value = scanner.GetValue( groupsOf3DBlocks[previousBlockKey].GetFilenames().front().c_str(), tagImagePositionPatient), *destination_value = scanner.GetValue( groupsOf3DBlocks[thisBlockKey].GetFilenames().back().c_str(), tagImagePositionPatient), *previous_destination_value = scanner.GetValue( groupsOf3DBlocks[previousBlockKey].GetFilenames().back().c_str(), tagImagePositionPatient); if (!origin_value || !previous_origin_value || !destination_value || !previous_destination_value) { identicalOrigins = false; } else { std::string thisOriginString = ConstCharStarToString(origin_value); std::string previousOriginString = ConstCharStarToString(previous_origin_value); // also compare last origin, because this might differ if z-spacing is different std::string thisDestinationString = ConstCharStarToString(destination_value); std::string previousDestinationString = ConstCharStarToString(previous_destination_value); identicalOrigins = ((thisOriginString == previousOriginString) && (thisDestinationString == previousDestinationString)); } } catch (...) { identicalOrigins = false; } if (identicalOrigins && (numberOfFilesInPreviousBlock == numberOfFilesInThisBlock)) { // group with previous block groupsOf3DPlusTBlocks[previousBlockKey].AddFiles(block3DIter->second.GetFilenames()); groupsOf3DPlusTBlocks[previousBlockKey].SetHasMultipleTimePoints(true); MITK_DEBUG << " --> group enhanced with another timestep"; } else { // start a new block groupsOf3DPlusTBlocks[thisBlockKey] = block3DIter->second; int numberOfTimeSteps = groupsOf3DPlusTBlocks[previousBlockKey].GetFilenames().size() / numberOfFilesInPreviousBlock; MITK_DEBUG << " ==> group closed with " << numberOfTimeSteps << " time steps"; previousBlockKey = thisBlockKey; MITK_DEBUG << " 3D+t group " << thisBlockKey << " started"; } } numberOfFilesInPreviousBlock = numberOfFilesInThisBlock; } } } MITK_DEBUG << "================================================================================"; MITK_DEBUG << "Summary: "; for (FileNamesGrouping::const_iterator groupIter = groupsOf3DPlusTBlocks.begin(); groupIter != groupsOf3DPlusTBlocks.end(); ++groupIter) { ImageBlockDescriptor block = groupIter->second; MITK_DEBUG << " " << block.GetFilenames().size() << " '" << block.GetModality() << "' images (" << block.GetSOPClassUIDAsString() << ") in volume " << block.GetImageBlockUID(); MITK_DEBUG << " (gantry tilt : " << (block.HasGantryTiltCorrected() ? "Yes" : "No") << "; " "pixel spacing : " << PixelSpacingInterpretationToString(block.GetPixelSpacingType()) << "; " "3D+t: " << (block.HasMultipleTimePoints() ? "Yes" : "No") << "; " "reader support: " << ReaderImplementationLevelToString(block.GetReaderImplementationLevel()) << ")"; StringContainer debugOutputFiles = block.GetFilenames(); for (StringContainer::const_iterator siter = debugOutputFiles.begin(); siter != debugOutputFiles.end(); ++siter) MITK_DEBUG << " F " << *siter; } MITK_DEBUG << "================================================================================"; return groupsOf3DPlusTBlocks; } DicomSeriesReader::FileNamesGrouping DicomSeriesReader::GetSeries(const std::string &dir, bool groupImagesWithGantryTilt, const StringContainer &restrictions) { gdcm::Directory directoryLister; directoryLister.Load(dir.c_str(), false); // non-recursive return GetSeries(directoryLister.GetFilenames(), groupImagesWithGantryTilt, restrictions); } std::string DicomSeriesReader::CreateSeriesIdentifierPart(gdcm::Scanner::TagToValue &tagValueMap, const gdcm::Tag &tag) { std::string result; try { result = IDifyTagValue(tagValueMap[tag] ? tagValueMap[tag] : std::string("")); } catch (std::exception &) { // we are happy with even nothing, this will just group images of a series // MITK_WARN << "Could not access tag " << tag << ": " << e.what(); } return result; } std::string DicomSeriesReader::CreateMoreUniqueSeriesIdentifier(gdcm::Scanner::TagToValue &tagValueMap) { const gdcm::Tag tagSeriesInstanceUID(0x0020, 0x000e); // Series Instance UID const gdcm::Tag tagImageOrientation(0x0020, 0x0037); // image orientation const gdcm::Tag tagPixelSpacing(0x0028, 0x0030); // pixel spacing const gdcm::Tag tagImagerPixelSpacing(0x0018, 0x1164); // imager pixel spacing const gdcm::Tag tagSliceThickness(0x0018, 0x0050); // slice thickness const gdcm::Tag tagNumberOfRows(0x0028, 0x0010); // number rows const gdcm::Tag tagNumberOfColumns(0x0028, 0x0011); // number cols const gdcm::Tag tagNumberOfFrames(0x0028, 0x0008); // number of frames const char *tagSeriesInstanceUid = tagValueMap[tagSeriesInstanceUID]; if (!tagSeriesInstanceUid) { mitkThrow() << "CreateMoreUniqueSeriesIdentifier() could not access series instance UID. Something is seriously " "wrong with this image, so stopping here."; } std::string constructedID = tagSeriesInstanceUid; constructedID += CreateSeriesIdentifierPart(tagValueMap, tagNumberOfRows); constructedID += CreateSeriesIdentifierPart(tagValueMap, tagNumberOfColumns); constructedID += CreateSeriesIdentifierPart(tagValueMap, tagPixelSpacing); constructedID += CreateSeriesIdentifierPart(tagValueMap, tagImagerPixelSpacing); constructedID += CreateSeriesIdentifierPart(tagValueMap, tagSliceThickness); constructedID += CreateSeriesIdentifierPart(tagValueMap, tagNumberOfFrames); // be a bit tolerant for orienatation, let only the first few digits matter // (http://bugs.mitk.org/show_bug.cgi?id=12263) // NOT constructedID += CreateSeriesIdentifierPart( tagValueMap, tagImageOrientation ); if (tagValueMap.find(tagImageOrientation) != tagValueMap.end()) { bool conversionError(false); Vector3D right; right.Fill(0.0); Vector3D up; right.Fill(0.0); DICOMStringToOrientationVectors(tagValueMap[tagImageOrientation], right, up, conversionError); // string newstring sprintf(simplifiedOrientationString, "%.3f\\%.3f\\%.3f\\%.3f\\%.3f\\%.3f", right[0], // right[1], // right[2], up[0], up[1], up[2]); std::ostringstream ss; ss.setf(std::ios::fixed, std::ios::floatfield); ss.precision(5); ss << right[0] << "\\" << right[1] << "\\" << right[2] << "\\" << up[0] << "\\" << up[1] << "\\" << up[2]; std::string simplifiedOrientationString(ss.str()); constructedID += IDifyTagValue(simplifiedOrientationString); } constructedID.resize(constructedID.length() - 1); // cut of trailing '.' return constructedID; } std::string DicomSeriesReader::IDifyTagValue(const std::string &value) { std::string IDifiedValue(value); if (value.empty()) throw std::logic_error("IDifyTagValue() illegaly called with empty tag value"); // Eliminate non-alnum characters, including whitespace... // that may have been introduced by concats. for (std::size_t i = 0; i < IDifiedValue.size(); i++) { while (i < IDifiedValue.size() && !(IDifiedValue[i] == '.' || (IDifiedValue[i] >= 'a' && IDifiedValue[i] <= 'z') || (IDifiedValue[i] >= '0' && IDifiedValue[i] <= '9') || (IDifiedValue[i] >= 'A' && IDifiedValue[i] <= 'Z'))) { IDifiedValue.erase(i, 1); } } IDifiedValue += "."; return IDifiedValue; } DicomSeriesReader::StringContainer DicomSeriesReader::GetSeries(const std::string &dir, const std::string &series_uid, bool groupImagesWithGantryTilt, const StringContainer &restrictions) { FileNamesGrouping allSeries = GetSeries(dir, groupImagesWithGantryTilt, restrictions); StringContainer resultingFileList; for (FileNamesGrouping::const_iterator idIter = allSeries.begin(); idIter != allSeries.end(); ++idIter) { if (idIter->first.find(series_uid) == 0) // this ID starts with given series_uid { return idIter->second.GetFilenames(); } } return resultingFileList; } DicomSeriesReader::StringContainer DicomSeriesReader::SortSeriesSlices(const StringContainer &unsortedFilenames) { /* we CAN expect a group of equal - series instance uid - image orientation - pixel spacing - imager pixel spacing - slice thickness - number of rows/columns (each piece of information except the rows/columns might be missing) sorting with GdcmSortFunction tries its best by sorting by spatial position and more hints (acquisition number, acquisition time, trigger time) but will always produce a sorting by falling back to SOP Instance UID. */ gdcm::Sorter sorter; sorter.SetSortFunction(DicomSeriesReader::GdcmSortFunction); try { if (sorter.Sort(unsortedFilenames)) { return sorter.GetFilenames(); } else { MITK_WARN << "Sorting error. Leaving series unsorted."; return unsortedFilenames; } } catch (std::logic_error &) { MITK_WARN << "Sorting error. Leaving series unsorted."; return unsortedFilenames; } } bool DicomSeriesReader::GdcmSortFunction(const gdcm::DataSet &ds1, const gdcm::DataSet &ds2) { // This method MUST accept missing location and position information (and all else, too) // because we cannot rely on anything // (restriction on the sentence before: we have to provide consistent sorting, so we // rely on the minimum information all DICOM files need to provide: SOP Instance UID) /* we CAN expect a group of equal - series instance uid - image orientation - pixel spacing - imager pixel spacing - slice thickness - number of rows/columns */ static const gdcm::Tag tagImagePositionPatient(0x0020, 0x0032); // Image Position (Patient) static const gdcm::Tag tagImageOrientation(0x0020, 0x0037); // Image Orientation // see if we have Image Position and Orientation if (ds1.FindDataElement(tagImagePositionPatient) && ds1.FindDataElement(tagImageOrientation) && ds2.FindDataElement(tagImagePositionPatient) && ds2.FindDataElement(tagImageOrientation)) { gdcm::Attribute<0x0020, 0x0032> image_pos1; // Image Position (Patient) gdcm::Attribute<0x0020, 0x0037> image_orientation1; // Image Orientation (Patient) image_pos1.Set(ds1); image_orientation1.Set(ds1); gdcm::Attribute<0x0020, 0x0032> image_pos2; gdcm::Attribute<0x0020, 0x0037> image_orientation2; image_pos2.Set(ds2); image_orientation2.Set(ds2); /* we tolerate very small differences in image orientation, since we got to know about acquisitions where these values change across a single series (7th decimal digit) (http://bugs.mitk.org/show_bug.cgi?id=12263) still, we want to check if our assumption of 'almost equal' orientations is valid */ for (unsigned int dim = 0; dim < 6; ++dim) { if (fabs(image_orientation2[dim] - image_orientation1[dim]) > 0.0001) { MITK_ERROR << "Dicom images have different orientations."; throw std::logic_error( "Dicom images have different orientations. Call GetSeries() first to separate images."); } } double normal[3]; normal[0] = image_orientation1[1] * image_orientation1[5] - image_orientation1[2] * image_orientation1[4]; normal[1] = image_orientation1[2] * image_orientation1[3] - image_orientation1[0] * image_orientation1[5]; normal[2] = image_orientation1[0] * image_orientation1[4] - image_orientation1[1] * image_orientation1[3]; double dist1 = 0.0, dist2 = 0.0; // this computes the distance from world origin (0,0,0) ALONG THE NORMAL of the image planes for (unsigned char i = 0u; i < 3u; ++i) { dist1 += normal[i] * image_pos1[i]; dist2 += normal[i] * image_pos2[i]; } // if we can sort by just comparing the distance, we do exactly that if (fabs(dist1 - dist2) >= mitk::eps) { // default: compare position return dist1 < dist2; } else // we need to check more properties to distinguish slices { // try to sort by Acquisition Number static const gdcm::Tag tagAcquisitionNumber(0x0020, 0x0012); if (ds1.FindDataElement(tagAcquisitionNumber) && ds2.FindDataElement(tagAcquisitionNumber)) { gdcm::Attribute<0x0020, 0x0012> acquisition_number1; // Acquisition number gdcm::Attribute<0x0020, 0x0012> acquisition_number2; acquisition_number1.Set(ds1); acquisition_number2.Set(ds2); if (acquisition_number1 != acquisition_number2) { return acquisition_number1 < acquisition_number2; } else // neither position nor acquisition number are good for sorting, so check more { // try to sort by Acquisition Time static const gdcm::Tag tagAcquisitionTime(0x0008, 0x0032); if (ds1.FindDataElement(tagAcquisitionTime) && ds2.FindDataElement(tagAcquisitionTime)) { gdcm::Attribute<0x0008, 0x0032> acquisition_time1; // Acquisition time gdcm::Attribute<0x0008, 0x0032> acquisition_time2; acquisition_time1.Set(ds1); acquisition_time2.Set(ds2); if (acquisition_time1 != acquisition_time2) { return acquisition_time1 < acquisition_time2; } else // we gave up on image position, acquisition number and acquisition time now { // let's try trigger time static const gdcm::Tag tagTriggerTime(0x0018, 0x1060); if (ds1.FindDataElement(tagTriggerTime) && ds2.FindDataElement(tagTriggerTime)) { gdcm::Attribute<0x0018, 0x1060> trigger_time1; // Trigger time gdcm::Attribute<0x0018, 0x1060> trigger_time2; trigger_time1.Set(ds1); trigger_time2.Set(ds2); if (trigger_time1 != trigger_time2) { return trigger_time1 < trigger_time2; } // ELSE! // for this and many previous ifs we fall through if nothing lets us sort } // . } // . } // . } } } } // . // LAST RESORT: all valuable information for sorting is missing. // Sort by some meaningless but unique identifiers to satisfy the sort function static const gdcm::Tag tagSOPInstanceUID(0x0008, 0x0018); if (ds1.FindDataElement(tagSOPInstanceUID) && ds2.FindDataElement(tagSOPInstanceUID)) { MITK_DEBUG << "Dicom images are missing attributes for a meaningful sorting, falling back to SOP instance UID comparison."; gdcm::Attribute<0x0008, 0x0018> SOPInstanceUID1; // SOP instance UID is mandatory and unique gdcm::Attribute<0x0008, 0x0018> SOPInstanceUID2; SOPInstanceUID1.Set(ds1); SOPInstanceUID2.Set(ds2); return SOPInstanceUID1 < SOPInstanceUID2; } else { // no DICOM file should really come down here, this should only be reached with unskillful and unlucky // manipulation // of files std::string error_message("Malformed DICOM images, which do not even contain a SOP Instance UID."); MITK_ERROR << error_message; throw std::logic_error(error_message); } } std::string DicomSeriesReader::GetConfigurationString() { std::stringstream configuration; configuration << "MITK_USE_GDCMIO: "; configuration << "true"; configuration << "\n"; configuration << "GDCM_VERSION: "; #ifdef GDCM_MAJOR_VERSION configuration << GDCM_VERSION; #endif // configuration << "\n"; return configuration.str(); } void DicomSeriesReader::CopyMetaDataToImageProperties(StringContainer filenames, const gdcm::Scanner::MappingType &tagValueMappings_, DcmIoType *io, const ImageBlockDescriptor &blockInfo, Image *image) { std::list imageBlock; imageBlock.push_back(filenames); CopyMetaDataToImageProperties(imageBlock, tagValueMappings_, io, blockInfo, image); } void DicomSeriesReader::CopyMetaDataToImageProperties(std::list imageBlock, const gdcm::Scanner::MappingType &tagValueMappings_, DcmIoType *io, const ImageBlockDescriptor &blockInfo, Image *image) { if (!io || !image) return; StringLookupTable filesForSlices; StringLookupTable sliceLocationForSlices; StringLookupTable instanceNumberForSlices; StringLookupTable SOPInstanceNumberForSlices; - gdcm::Scanner::MappingType &tagValueMappings = const_cast(tagValueMappings_); + auto &tagValueMappings = const_cast(tagValueMappings_); // DICOM tags which should be added to the image properties const gdcm::Tag tagSliceLocation(0x0020, 0x1041); // slice location const gdcm::Tag tagInstanceNumber(0x0020, 0x0013); // (image) instance number const gdcm::Tag tagSOPInstanceNumber(0x0008, 0x0018); // SOP instance number unsigned int timeStep(0); std::string propertyKeySliceLocation = "dicom.image.0020.1041"; std::string propertyKeyInstanceNumber = "dicom.image.0020.0013"; std::string propertyKeySOPInstanceNumber = "dicom.image.0008.0018"; // tags for each image for (auto i = imageBlock.begin(); i != imageBlock.end(); i++, timeStep++) { const StringContainer &files = (*i); unsigned int slice(0); for (auto fIter = files.begin(); fIter != files.end(); ++fIter, ++slice) { filesForSlices.SetTableValue(slice, *fIter); gdcm::Scanner::TagToValue tagValueMapForFile = tagValueMappings[fIter->c_str()]; if (tagValueMapForFile.find(tagSliceLocation) != tagValueMapForFile.end()) sliceLocationForSlices.SetTableValue(slice, tagValueMapForFile[tagSliceLocation]); if (tagValueMapForFile.find(tagInstanceNumber) != tagValueMapForFile.end()) instanceNumberForSlices.SetTableValue(slice, tagValueMapForFile[tagInstanceNumber]); if (tagValueMapForFile.find(tagSOPInstanceNumber) != tagValueMapForFile.end()) SOPInstanceNumberForSlices.SetTableValue(slice, tagValueMapForFile[tagSOPInstanceNumber]); } image->SetProperty("files", StringLookupTableProperty::New(filesForSlices)); // If more than one time step add postfix ".t" + timestep if (timeStep != 0) { std::ostringstream postfix; postfix << ".t" << timeStep; propertyKeySliceLocation.append(postfix.str()); propertyKeyInstanceNumber.append(postfix.str()); propertyKeySOPInstanceNumber.append(postfix.str()); } image->SetProperty(propertyKeySliceLocation.c_str(), StringLookupTableProperty::New(sliceLocationForSlices)); image->SetProperty(propertyKeyInstanceNumber.c_str(), StringLookupTableProperty::New(instanceNumberForSlices)); image->SetProperty(propertyKeySOPInstanceNumber.c_str(), StringLookupTableProperty::New(SOPInstanceNumberForSlices)); } // Copy tags for series, study, patient level (leave interpretation to application). // These properties will be copied to the DataNode by DicomSeriesReader. // tags for the series (we just use the one that ITK copied to its dictionary (proably that of the last slice) const itk::MetaDataDictionary &dict = io->GetMetaDataDictionary(); const TagToPropertyMapType &propertyLookup = DicomSeriesReader::GetDICOMTagsToMITKPropertyMap(); auto dictIter = dict.Begin(); while (dictIter != dict.End()) { // MITK_DEBUG << "Key " << dictIter->first; std::string value; if (itk::ExposeMetaData(dict, dictIter->first, value)) { // MITK_DEBUG << "Value " << value; auto valuePosition = propertyLookup.find(dictIter->first); if (valuePosition != propertyLookup.end()) { std::string propertyKey = valuePosition->second; // MITK_DEBUG << "--> " << propertyKey; image->SetProperty(propertyKey.c_str(), StringProperty::New(value)); } } else { MITK_WARN << "Tag " << dictIter->first << " not read as string as expected. Ignoring..."; } ++dictIter; } // copy imageblockdescriptor as properties image->SetProperty("dicomseriesreader.SOPClass", StringProperty::New(blockInfo.GetSOPClassUIDAsString())); image->SetProperty( "dicomseriesreader.ReaderImplementationLevelString", StringProperty::New(ReaderImplementationLevelToString(blockInfo.GetReaderImplementationLevel()))); image->SetProperty("dicomseriesreader.ReaderImplementationLevel", GenericProperty::New(blockInfo.GetReaderImplementationLevel())); image->SetProperty("dicomseriesreader.PixelSpacingInterpretationString", StringProperty::New(PixelSpacingInterpretationToString(blockInfo.GetPixelSpacingType()))); image->SetProperty("dicomseriesreader.PixelSpacingInterpretation", GenericProperty::New(blockInfo.GetPixelSpacingType())); image->SetProperty("dicomseriesreader.MultiFrameImage", BoolProperty::New(blockInfo.IsMultiFrameImage())); image->SetProperty("dicomseriesreader.GantyTiltCorrected", BoolProperty::New(blockInfo.HasGantryTiltCorrected())); image->SetProperty("dicomseriesreader.3D+t", BoolProperty::New(blockInfo.HasMultipleTimePoints())); } void DicomSeriesReader::FixSpacingInformation(mitk::Image *image, const ImageBlockDescriptor &imageBlockDescriptor) { // spacing provided by ITK/GDCM Vector3D imageSpacing = image->GetGeometry()->GetSpacing(); ScalarType imageSpacingX = imageSpacing[0]; ScalarType imageSpacingY = imageSpacing[1]; // spacing as desired by MITK (preference for "in patient", else "on detector", or "1.0/1.0") ScalarType desiredSpacingX = imageSpacingX; ScalarType desiredSpacingY = imageSpacingY; imageBlockDescriptor.GetDesiredMITKImagePixelSpacing(desiredSpacingX, desiredSpacingY); MITK_DEBUG << "Loaded spacing: " << imageSpacingX << "/" << imageSpacingY; MITK_DEBUG << "Corrected spacing: " << desiredSpacingX << "/" << desiredSpacingY; imageSpacing[0] = desiredSpacingX; imageSpacing[1] = desiredSpacingY; image->GetGeometry()->SetSpacing(imageSpacing); } void DicomSeriesReader::LoadDicom(const StringContainer &filenames, DataNode &node, bool sort, bool load4D, bool correctTilt, UpdateCallBackMethod callback, Image::Pointer preLoadedImageBlock) { mitk::LocaleSwitch localeSwitch("C"); std::locale previousCppLocale(std::cin.getloc()); std::locale l("C"); std::cin.imbue(l); ImageBlockDescriptor imageBlockDescriptor; const gdcm::Tag tagImagePositionPatient(0x0020, 0x0032); // Image Position (Patient) const gdcm::Tag tagImageOrientation(0x0020, 0x0037); // Image Orientation const gdcm::Tag tagSeriesInstanceUID(0x0020, 0x000e); // Series Instance UID const gdcm::Tag tagSOPClassUID(0x0008, 0x0016); // SOP class UID const gdcm::Tag tagModality(0x0008, 0x0060); // modality const gdcm::Tag tagPixelSpacing(0x0028, 0x0030); // pixel spacing const gdcm::Tag tagImagerPixelSpacing(0x0018, 0x1164); // imager pixel spacing const gdcm::Tag tagNumberOfFrames(0x0028, 0x0008); // number of frames try { Image::Pointer image = preLoadedImageBlock.IsNull() ? Image::New() : preLoadedImageBlock; CallbackCommand *command = callback ? new CallbackCommand(callback) : nullptr; bool initialize_node = false; /* special case for Philips 3D+t ultrasound images */ if (DicomSeriesReader::IsPhilips3DDicom(filenames.front().c_str())) { // TODO what about imageBlockDescriptor? // TODO what about preLoadedImageBlock? ReadPhilips3DDicom(filenames.front().c_str(), image); initialize_node = true; } else { /* default case: assume "normal" image blocks, possibly 3D+t */ bool canLoadAs4D(true); gdcm::Scanner scanner; ScanForSliceInformation(filenames, scanner); // need non-const access for map - gdcm::Scanner::MappingType &tagValueMappings = const_cast(scanner.GetMappings()); + auto &tagValueMappings = const_cast(scanner.GetMappings()); std::list imageBlocks = SortIntoBlocksFor3DplusT(filenames, tagValueMappings, sort, canLoadAs4D); unsigned int volume_count = imageBlocks.size(); imageBlockDescriptor.SetSeriesInstanceUID( DicomSeriesReader::ConstCharStarToString(scanner.GetValue(filenames.front().c_str(), tagSeriesInstanceUID))); imageBlockDescriptor.SetSOPClassUID( DicomSeriesReader::ConstCharStarToString(scanner.GetValue(filenames.front().c_str(), tagSOPClassUID))); imageBlockDescriptor.SetModality( DicomSeriesReader::ConstCharStarToString(scanner.GetValue(filenames.front().c_str(), tagModality))); imageBlockDescriptor.SetNumberOfFrames( ConstCharStarToString(scanner.GetValue(filenames.front().c_str(), tagNumberOfFrames))); imageBlockDescriptor.SetPixelSpacingInformation( ConstCharStarToString(scanner.GetValue(filenames.front().c_str(), tagPixelSpacing)), ConstCharStarToString(scanner.GetValue(filenames.front().c_str(), tagImagerPixelSpacing))); GantryTiltInformation tiltInfo; // check possibility of a single slice with many timesteps. In this case, don't check for tilt, no second slice // possible if (!imageBlocks.empty() && imageBlocks.front().size() > 1 && correctTilt) { // check tiltedness here, potentially fixup ITK's loading result by shifting slice contents // check first and last position slice from tags, make some calculations to detect tilt std::string firstFilename(imageBlocks.front().front()); // calculate from first and last slice to minimize rounding errors std::string secondFilename(imageBlocks.front().back()); std::string imagePosition1( ConstCharStarToString(tagValueMappings[firstFilename.c_str()][tagImagePositionPatient])); std::string imageOrientation( ConstCharStarToString(tagValueMappings[firstFilename.c_str()][tagImageOrientation])); std::string imagePosition2( ConstCharStarToString(tagValueMappings[secondFilename.c_str()][tagImagePositionPatient])); bool ignoredConversionError(-42); // hard to get here, no graceful way to react Point3D origin1(DICOMStringToPoint3D(imagePosition1, ignoredConversionError)); Point3D origin2(DICOMStringToPoint3D(imagePosition2, ignoredConversionError)); Vector3D right; right.Fill(0.0); Vector3D up; right.Fill(0.0); // might be down as well, but it is just a name at this point DICOMStringToOrientationVectors(imageOrientation, right, up, ignoredConversionError); tiltInfo = GantryTiltInformation(origin1, origin2, right, up, filenames.size() - 1); correctTilt = tiltInfo.IsSheared() && tiltInfo.IsRegularGantryTilt(); } else { correctTilt = false; // we CANNOT do that } imageBlockDescriptor.SetHasGantryTiltCorrected(correctTilt); if (volume_count == 1 || !canLoadAs4D || !load4D) { DcmIoType::Pointer io; image = MultiplexLoadDICOMByITK( imageBlocks.front(), correctTilt, tiltInfo, io, command, preLoadedImageBlock); // load first 3D block imageBlockDescriptor.AddFiles(imageBlocks.front()); // only the first part is loaded imageBlockDescriptor.SetHasMultipleTimePoints(false); FixSpacingInformation(image, imageBlockDescriptor); CopyMetaDataToImageProperties(imageBlocks.front(), scanner.GetMappings(), io, imageBlockDescriptor, image); initialize_node = true; } else if (volume_count > 1) { imageBlockDescriptor.AddFiles(filenames); // all is loaded imageBlockDescriptor.SetHasMultipleTimePoints(true); DcmIoType::Pointer io; image = MultiplexLoadDICOMByITK4D( imageBlocks, imageBlockDescriptor, correctTilt, tiltInfo, io, command, preLoadedImageBlock); initialize_node = true; } } if (initialize_node) { // forward some image properties to node node.GetPropertyList()->ConcatenatePropertyList(image->GetPropertyList(), true); std::string patientName = "NoName"; if (node.GetProperty("dicom.patient.PatientsName")) patientName = node.GetProperty("dicom.patient.PatientsName")->GetValueAsString(); node.SetData(image); node.SetName(patientName); std::cin.imbue(previousCppLocale); } MITK_DEBUG << "--------------------------------------------------------------------------------"; MITK_DEBUG << "DICOM files loaded (from series UID " << imageBlockDescriptor.GetSeriesInstanceUID() << "):"; MITK_DEBUG << " " << imageBlockDescriptor.GetFilenames().size() << " '" << imageBlockDescriptor.GetModality() << "' files (" << imageBlockDescriptor.GetSOPClassUIDAsString() << ") loaded into 1 mitk::Image"; MITK_DEBUG << " multi-frame: " << (imageBlockDescriptor.IsMultiFrameImage() ? "Yes" : "No"); MITK_DEBUG << " reader support: " << ReaderImplementationLevelToString(imageBlockDescriptor.GetReaderImplementationLevel()); MITK_DEBUG << " pixel spacing type: " << PixelSpacingInterpretationToString(imageBlockDescriptor.GetPixelSpacingType()) << " " << image->GetGeometry()->GetSpacing()[0] << "/" << image->GetGeometry()->GetSpacing()[0]; MITK_DEBUG << " gantry tilt corrected: " << (imageBlockDescriptor.HasGantryTiltCorrected() ? "Yes" : "No"); MITK_DEBUG << " 3D+t: " << (imageBlockDescriptor.HasMultipleTimePoints() ? "Yes" : "No"); MITK_DEBUG << "--------------------------------------------------------------------------------"; } catch (std::exception &e) { // reset locale then throw up std::cin.imbue(previousCppLocale); MITK_DEBUG << "Caught exception in DicomSeriesReader::LoadDicom"; throw e; } } void DicomSeriesReader::ScanForSliceInformation(const StringContainer &filenames, gdcm::Scanner &scanner) { const gdcm::Tag tagImagePositionPatient(0x0020, 0x0032); // Image position (Patient) scanner.AddTag(tagImagePositionPatient); const gdcm::Tag tagSeriesInstanceUID(0x0020, 0x000e); // Series Instance UID scanner.AddTag(tagSeriesInstanceUID); const gdcm::Tag tagImageOrientation(0x0020, 0x0037); // Image orientation scanner.AddTag(tagImageOrientation); const gdcm::Tag tagSliceLocation(0x0020, 0x1041); // slice location scanner.AddTag(tagSliceLocation); const gdcm::Tag tagInstanceNumber(0x0020, 0x0013); // (image) instance number scanner.AddTag(tagInstanceNumber); const gdcm::Tag tagSOPInstanceNumber(0x0008, 0x0018); // SOP instance number scanner.AddTag(tagSOPInstanceNumber); const gdcm::Tag tagPixelSpacing(0x0028, 0x0030); // Pixel Spacing scanner.AddTag(tagPixelSpacing); const gdcm::Tag tagImagerPixelSpacing(0x0018, 0x1164); // Imager Pixel Spacing scanner.AddTag(tagImagerPixelSpacing); const gdcm::Tag tagModality(0x0008, 0x0060); // Modality scanner.AddTag(tagModality); const gdcm::Tag tagSOPClassUID(0x0008, 0x0016); // SOP Class UID scanner.AddTag(tagSOPClassUID); const gdcm::Tag tagNumberOfFrames(0x0028, 0x0008); // number of frames scanner.AddTag(tagNumberOfFrames); scanner.Scan(filenames); // make available image information for each file } std::list DicomSeriesReader::SortIntoBlocksFor3DplusT( const StringContainer &presortedFilenames, const gdcm::Scanner::MappingType &tagValueMappings, bool /*sort*/, bool &canLoadAs4D) { std::list imageBlocks; // ignore sort request, because most likely re-sorting is now needed due to changes in GetSeries(bug #8022) StringContainer sorted_filenames = DicomSeriesReader::SortSeriesSlices(presortedFilenames); std::string firstPosition; unsigned int numberOfBlocks(0); // number of 3D image blocks static const gdcm::Tag tagImagePositionPatient(0x0020, 0x0032); // Image position (Patient) const gdcm::Tag tagModality(0x0008, 0x0060); // loop files to determine number of image blocks for (StringContainer::const_iterator fileIter = sorted_filenames.begin(); fileIter != sorted_filenames.end(); ++fileIter) { gdcm::Scanner::TagToValue tagToValueMap = tagValueMappings.find(fileIter->c_str())->second; if (tagToValueMap.find(tagImagePositionPatient) == tagToValueMap.end()) { const std::string &modality = tagToValueMap.find(tagModality)->second; if (modality.compare("RTIMAGE ") == 0 || modality.compare("RTIMAGE") == 0) { MITK_WARN << "Modality " << modality << " is not fully supported yet."; numberOfBlocks = 1; break; } else { // we expect to get images w/ missing position information ONLY as separated blocks. assert(presortedFilenames.size() == 1); numberOfBlocks = 1; break; } } std::string position = tagToValueMap.find(tagImagePositionPatient)->second; MITK_DEBUG << " " << *fileIter << " at " << position; if (firstPosition.empty()) { firstPosition = position; } if (position == firstPosition) { ++numberOfBlocks; } else { break; // enough information to know the number of image blocks } } MITK_DEBUG << " ==> Assuming " << numberOfBlocks << " time steps"; if (numberOfBlocks == 0) return imageBlocks; // only possible if called with no files // loop files to sort them into image blocks unsigned int numberOfExpectedSlices(0); for (unsigned int block = 0; block < numberOfBlocks; ++block) { StringContainer filesOfCurrentBlock; for (StringContainer::const_iterator fileIter = sorted_filenames.begin() + block; fileIter != sorted_filenames.end(); // fileIter += numberOfBlocks) // TODO shouldn't this work? give invalid iterators on first attempts ) { filesOfCurrentBlock.push_back(*fileIter); for (unsigned int b = 0; b < numberOfBlocks; ++b) { if (fileIter != sorted_filenames.end()) ++fileIter; } } imageBlocks.push_back(filesOfCurrentBlock); if (block == 0) { numberOfExpectedSlices = filesOfCurrentBlock.size(); } else { if (filesOfCurrentBlock.size() != numberOfExpectedSlices) { MITK_WARN << "DicomSeriesReader expected " << numberOfBlocks << " image blocks of " << numberOfExpectedSlices << " images each. Block " << block << " got " << filesOfCurrentBlock.size() << " instead. Cannot load this as 3D+t"; // TODO implement recovery (load as many slices 3D+t as much // as possible) canLoadAs4D = false; } } } return imageBlocks; } Image::Pointer DicomSeriesReader::MultiplexLoadDICOMByITK(const StringContainer &filenames, bool correctTilt, const GantryTiltInformation &tiltInfo, DcmIoType::Pointer &io, CallbackCommand *command, Image::Pointer preLoadedImageBlock) { io = DcmIoType::New(); io->SetFileName(filenames.front().c_str()); io->ReadImageInformation(); if (io->GetPixelType() == itk::ImageIOBase::SCALAR) { return MultiplexLoadDICOMByITKScalar(filenames, correctTilt, tiltInfo, io, command, preLoadedImageBlock); } else if (io->GetPixelType() == itk::ImageIOBase::RGB) { return MultiplexLoadDICOMByITKRGBPixel(filenames, correctTilt, tiltInfo, io, command, preLoadedImageBlock); } else { return nullptr; } } Image::Pointer DicomSeriesReader::MultiplexLoadDICOMByITK4D(std::list &imageBlocks, ImageBlockDescriptor imageBlockDescriptor, bool correctTilt, const GantryTiltInformation &tiltInfo, DcmIoType::Pointer &io, CallbackCommand *command, Image::Pointer preLoadedImageBlock) { io = DcmIoType::New(); io->SetFileName(imageBlocks.front().front().c_str()); io->ReadImageInformation(); if (io->GetPixelType() == itk::ImageIOBase::SCALAR) { return MultiplexLoadDICOMByITK4DScalar( imageBlocks, imageBlockDescriptor, correctTilt, tiltInfo, io, command, preLoadedImageBlock); } else if (io->GetPixelType() == itk::ImageIOBase::RGB) { return MultiplexLoadDICOMByITK4DRGBPixel( imageBlocks, imageBlockDescriptor, correctTilt, tiltInfo, io, command, preLoadedImageBlock); } else { return nullptr; } } } // end namespace mitk diff --git a/Modules/Core/src/IO/mitkFileReaderRegistry.cpp b/Modules/Core/src/IO/mitkFileReaderRegistry.cpp index b1c6b72a07..e6d133956f 100644 --- a/Modules/Core/src/IO/mitkFileReaderRegistry.cpp +++ b/Modules/Core/src/IO/mitkFileReaderRegistry.cpp @@ -1,132 +1,132 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkFileReaderRegistry.h" #include "mitkCoreServices.h" #include "mitkIMimeTypeProvider.h" // Microservices #include #include #include #include #include "itksys/SystemTools.hxx" mitk::FileReaderRegistry::FileReaderRegistry() { } mitk::FileReaderRegistry::~FileReaderRegistry() { for (auto &elem : m_ServiceObjects) { elem.second.UngetService(elem.first); } } mitk::MimeType mitk::FileReaderRegistry::GetMimeTypeForFile(const std::string &path, us::ModuleContext *context) { if (path.empty()) { mitkThrow() << "FileReaderRegistry::GetMimeTypeForFile was called with empty path. Returning empty MimeType, " "please report this error to the developers."; } mitk::CoreServicePointer mimeTypeProvider(mitk::CoreServices::GetMimeTypeProvider(context)); std::vector mimeTypes = mimeTypeProvider->GetMimeTypesForFile(path); if (mimeTypes.empty()) { return MimeType(); } else { return mimeTypes.front(); } } std::vector mitk::FileReaderRegistry::GetReferences( const MimeType &mimeType, us::ModuleContext *context) { if (context == nullptr) context = us::GetModuleContext(); std::string filter = us::LDAPProp(us::ServiceConstants::OBJECTCLASS()) == us_service_interface_iid() && us::LDAPProp(IFileReader::PROP_MIMETYPE()) == mimeType.GetName(); return context->GetServiceReferences(filter); } mitk::IFileReader *mitk::FileReaderRegistry::GetReader(const mitk::FileReaderRegistry::ReaderReference &ref, us::ModuleContext *context) { if (context == nullptr) context = us::GetModuleContext(); us::ServiceObjects serviceObjects = context->GetServiceObjects(ref); mitk::IFileReader *reader = serviceObjects.GetService(); m_ServiceObjects.insert(std::make_pair(reader, serviceObjects)); return reader; } std::vector mitk::FileReaderRegistry::GetReaders(const MimeType &mimeType, us::ModuleContext *context) { if (context == nullptr) context = us::GetModuleContext(); std::vector result; if (!mimeType.IsValid()) return result; std::vector> refs = GetReferences(mimeType, context); std::sort(refs.begin(), refs.end()); result.reserve(refs.size()); // Translate List of ServiceRefs to List of Pointers for (std::vector>::const_reverse_iterator iter = refs.rbegin(), end = refs.rend(); iter != end; ++iter) { us::ServiceObjects serviceObjects = context->GetServiceObjects(*iter); mitk::IFileReader *reader = serviceObjects.GetService(); m_ServiceObjects.insert(std::make_pair(reader, serviceObjects)); result.push_back(reader); } return result; } void mitk::FileReaderRegistry::UngetReader(mitk::IFileReader *reader) { - std::map>::iterator readerIter = + auto readerIter = m_ServiceObjects.find(reader); if (readerIter != m_ServiceObjects.end()) { readerIter->second.UngetService(reader); m_ServiceObjects.erase(readerIter); } } void mitk::FileReaderRegistry::UngetReaders(const std::vector &readers) { for (const auto &reader : readers) { this->UngetReader(reader); } } diff --git a/Modules/Core/src/IO/mitkFileReaderWriterBase.cpp b/Modules/Core/src/IO/mitkFileReaderWriterBase.cpp index c102d5a533..8c076b9293 100644 --- a/Modules/Core/src/IO/mitkFileReaderWriterBase.cpp +++ b/Modules/Core/src/IO/mitkFileReaderWriterBase.cpp @@ -1,212 +1,212 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkFileReaderWriterBase.h" #include "mitkCoreServices.h" #include "mitkIMimeTypeProvider.h" #include "mitkIOMimeTypes.h" #include "mitkLogMacros.h" #include #include namespace mitk { FileReaderWriterBase::FileReaderWriterBase() : m_Ranking(0), m_MimeTypePrefix(IOMimeTypes::DEFAULT_BASE_NAME() + ".") { } FileReaderWriterBase::~FileReaderWriterBase() { this->UnregisterMimeType(); } FileReaderWriterBase::FileReaderWriterBase(const FileReaderWriterBase &other) : m_Description(other.m_Description), m_Ranking(other.m_Ranking), m_MimeTypePrefix(other.m_MimeTypePrefix), m_Options(other.m_Options), m_DefaultOptions(other.m_DefaultOptions), m_CustomMimeType(other.m_CustomMimeType->Clone()) { } FileReaderWriterBase::Options FileReaderWriterBase::GetOptions() const { Options options = m_Options; options.insert(m_DefaultOptions.begin(), m_DefaultOptions.end()); return options; } us::Any FileReaderWriterBase::GetOption(const std::string &name) const { - Options::const_iterator iter = m_Options.find(name); + auto iter = m_Options.find(name); if (iter != m_Options.end()) { return iter->second; } iter = m_DefaultOptions.find(name); if (iter != m_DefaultOptions.end()) { return iter->second; } return us::Any(); } void FileReaderWriterBase::SetOptions(const FileReaderWriterBase::Options &options) { for (const auto &option : options) { this->SetOption(option.first, option.second); } } void FileReaderWriterBase::SetOption(const std::string &name, const us::Any &value) { if (m_DefaultOptions.find(name) == m_DefaultOptions.end()) { MITK_WARN << "Ignoring unknown IFileReader option '" << name << "'"; } else { if (value.Empty()) { // an empty Any signals 'reset to default value' m_Options.erase(name); } else { m_Options[name] = value; } } } void FileReaderWriterBase::SetDefaultOptions(const FileReaderWriterBase::Options &defaultOptions) { m_DefaultOptions = defaultOptions; } FileReaderWriterBase::Options FileReaderWriterBase::GetDefaultOptions() const { return m_DefaultOptions; } void FileReaderWriterBase::SetRanking(int ranking) { m_Ranking = ranking; } int FileReaderWriterBase::GetRanking() const { return m_Ranking; } void FileReaderWriterBase::SetMimeType(const CustomMimeType &mimeType) { m_CustomMimeType.reset(mimeType.Clone()); } const CustomMimeType *FileReaderWriterBase::GetMimeType() const { return m_CustomMimeType.get(); } CustomMimeType *FileReaderWriterBase::GetMimeType() { return m_CustomMimeType.get(); } MimeType FileReaderWriterBase::GetRegisteredMimeType() const { MimeType result; if (!m_MimeTypeReg) { if (!m_CustomMimeType->GetName().empty()) { CoreServicePointer mimeTypeProvider( CoreServices::GetMimeTypeProvider(us::GetModuleContext())); return mimeTypeProvider->GetMimeTypeForName(m_CustomMimeType->GetName()); } return result; } us::ServiceReferenceU reference = m_MimeTypeReg.GetReference(); try { int rank = 0; us::Any rankProp = reference.GetProperty(us::ServiceConstants::SERVICE_RANKING()); if (!rankProp.Empty()) { rank = us::any_cast(rankProp); } - long id = us::any_cast(reference.GetProperty(us::ServiceConstants::SERVICE_ID())); + auto id = us::any_cast(reference.GetProperty(us::ServiceConstants::SERVICE_ID())); result = MimeType(*m_CustomMimeType, rank, id); } catch (const us::BadAnyCastException &e) { MITK_WARN << "Unexpected exception: " << e.what(); } return result; } void FileReaderWriterBase::SetMimeTypePrefix(const std::string &prefix) { m_MimeTypePrefix = prefix; } std::string FileReaderWriterBase::GetMimeTypePrefix() const { return m_MimeTypePrefix; } void FileReaderWriterBase::SetDescription(const std::string &description) { m_Description = description; } std::string FileReaderWriterBase::GetDescription() const { return m_Description; } void FileReaderWriterBase::AddProgressCallback(const FileReaderWriterBase::ProgressCallback &callback) { m_ProgressMessage += callback; } void FileReaderWriterBase::RemoveProgressCallback(const FileReaderWriterBase::ProgressCallback &callback) { m_ProgressMessage -= callback; } us::ServiceRegistration FileReaderWriterBase::RegisterMimeType(us::ModuleContext *context) { if (context == nullptr) throw std::invalid_argument("The context argument must not be nullptr."); CoreServicePointer mimeTypeProvider(CoreServices::GetMimeTypeProvider(context)); const std::vector extensions = m_CustomMimeType->GetExtensions(); // If the mime type name is set and the list of extensions is empty, // look up the mime type in the registry and print a warning if // there is none if (!m_CustomMimeType->GetName().empty() && extensions.empty()) { if (!mimeTypeProvider->GetMimeTypeForName(m_CustomMimeType->GetName()).IsValid()) { MITK_WARN << "Registering a MITK reader or writer with an unknown MIME type " << m_CustomMimeType->GetName(); } return m_MimeTypeReg; } // If the mime type name and extensions list is empty, print a warning if (m_CustomMimeType->GetName().empty() && extensions.empty()) { MITK_WARN << "Trying to register a MITK reader or writer with an empty mime type name and empty extension list."; return m_MimeTypeReg; } // extensions is not empty if (m_CustomMimeType->GetName().empty()) { // Create a synthetic mime type name from the // first extension in the list m_CustomMimeType->SetName(m_MimeTypePrefix + extensions.front()); } // Register a new mime type // us::ServiceProperties props; // props["name"] = m_CustomMimeType.GetName(); // props["extensions"] = m_CustomMimeType.GetExtensions(); m_MimeTypeReg = context->RegisterService(m_CustomMimeType.get()); return m_MimeTypeReg; } void FileReaderWriterBase::UnregisterMimeType() { if (m_MimeTypeReg) { try { m_MimeTypeReg.Unregister(); } catch (const std::logic_error &) { // service already unregistered } } } } diff --git a/Modules/Core/src/IO/mitkFileWriterRegistry.cpp b/Modules/Core/src/IO/mitkFileWriterRegistry.cpp index f3d29ed074..b825a0eaf0 100644 --- a/Modules/Core/src/IO/mitkFileWriterRegistry.cpp +++ b/Modules/Core/src/IO/mitkFileWriterRegistry.cpp @@ -1,155 +1,155 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkFileWriterRegistry.h" // MITK #include "mitkBaseData.h" #include "mitkCoreServices.h" #include "mitkIMimeTypeProvider.h" // Microservices #include #include #include #include mitk::FileWriterRegistry::FileWriterRegistry() { } mitk::FileWriterRegistry::~FileWriterRegistry() { for (auto &elem : m_ServiceObjects) { elem.second.UngetService(elem.first); } } std::vector mitk::FileWriterRegistry::GetReferences( const mitk::BaseData *baseData, us::ModuleContext *context) { return GetReferences(baseData, std::string(), context); } std::vector mitk::FileWriterRegistry::GetReferences( const mitk::BaseData *baseData, const std::string &mimeType, us::ModuleContext *context) { if (baseData == nullptr) { mitkThrow() << "FileWriterRegistry::GetReferences was called with null basedata."; std::vector emptyResult; return emptyResult; } if (context == nullptr) context = us::GetModuleContext(); std::vector result; // loop over the class hierarchy of baseData and get all writers // claiming to support the actual baseData class or any of its super classes std::vector classHierarchy = baseData->GetClassHierarchy(); for (std::vector::const_iterator clIter = classHierarchy.begin(), clIterEnd = classHierarchy.end(); clIter != clIterEnd; ++clIter) { std::string filter = us::LDAPProp(us::ServiceConstants::OBJECTCLASS()) == us_service_interface_iid() && us::LDAPProp(IFileWriter::PROP_BASEDATA_TYPE()) == *clIter && (mimeType.empty() ? us::LDAPPropExpr(std::string()) : us::LDAPProp(IFileWriter::PROP_MIMETYPE()) == mimeType); std::vector refs = context->GetServiceReferences(filter); result.insert(result.end(), refs.begin(), refs.end()); } return result; } mitk::IFileWriter *mitk::FileWriterRegistry::GetWriter(const mitk::FileWriterRegistry::WriterReference &ref, us::ModuleContext *context) { if (!ref) return nullptr; if (context == nullptr) context = us::GetModuleContext(); us::ServiceObjects serviceObjects = context->GetServiceObjects(ref); mitk::IFileWriter *writer = serviceObjects.GetService(); m_ServiceObjects.insert(std::make_pair(writer, serviceObjects)); return writer; } std::vector mitk::FileWriterRegistry::GetWriters(const mitk::BaseData *baseData, const std::string &mimeType, us::ModuleContext *context) { if (baseData == nullptr) { mitkThrow() << "FileWriterRegistry::GetReferences was called with null basedata."; std::vector emptyResult; return emptyResult; } if (context == nullptr) context = us::GetModuleContext(); std::vector result; std::vector> refs; if (mimeType.empty()) { refs = GetReferences(baseData, context); } else { refs = GetReferences(baseData, mimeType, context); } std::sort(refs.begin(), refs.end()); result.reserve(refs.size()); // Translate List of ServiceRefs to List of Pointers for (std::vector>::const_reverse_iterator iter = refs.rbegin(), end = refs.rend(); iter != end; ++iter) { us::ServiceObjects serviceObjects = context->GetServiceObjects(*iter); mitk::IFileWriter *writer = serviceObjects.GetService(); m_ServiceObjects.insert(std::make_pair(writer, serviceObjects)); result.push_back(writer); } return result; } void mitk::FileWriterRegistry::UngetWriter(mitk::IFileWriter *writer) { - std::map>::iterator writerIter = + auto writerIter = m_ServiceObjects.find(writer); if (writerIter != m_ServiceObjects.end()) { writerIter->second.UngetService(writer); m_ServiceObjects.erase(writerIter); } } void mitk::FileWriterRegistry::UngetWriters(const std::vector &writers) { for (const auto &writer : writers) { this->UngetWriter(writer); } } diff --git a/Modules/Core/src/IO/mitkFileWriterSelector.cpp b/Modules/Core/src/IO/mitkFileWriterSelector.cpp index b8703f5a66..90709d82bf 100644 --- a/Modules/Core/src/IO/mitkFileWriterSelector.cpp +++ b/Modules/Core/src/IO/mitkFileWriterSelector.cpp @@ -1,294 +1,294 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkFileWriterSelector.h" #include #include #include #include #include #include #include #include #include #include #include namespace mitk { struct FileWriterSelector::Item::Impl : us::SharedData { Impl() : m_FileWriter(nullptr), m_ConfidenceLevel(IFileWriter::Unsupported), m_BaseDataIndex(0), m_Id(-1) {} us::ServiceReference m_FileWriterRef; IFileWriter *m_FileWriter; IFileWriter::ConfidenceLevel m_ConfidenceLevel; std::size_t m_BaseDataIndex; MimeType m_MimeType; long m_Id; }; struct FileWriterSelector::Impl : us::SharedData { Impl() : m_BestId(-1), m_SelectedId(m_BestId) {} Impl(const Impl &other) : us::SharedData(other), m_BestId(-1), m_SelectedId(m_BestId) {} FileWriterRegistry m_WriterRegistry; std::map m_Items; std::set m_MimeTypes; long m_BestId; long m_SelectedId; }; FileWriterSelector::FileWriterSelector(const FileWriterSelector &other) : m_Data(other.m_Data) {} FileWriterSelector::FileWriterSelector(const BaseData *baseData, const std::string &mimeType, const std::string &path) : m_Data(new Impl) { mitk::CoreServicePointer mimeTypeProvider(mitk::CoreServices::GetMimeTypeProvider()); std::vector refs; std::string destMimeType = mimeType; if (destMimeType.empty() && !path.empty()) { // try to derive a mime-type from the file std::vector mimeTypes = mimeTypeProvider->GetMimeTypesForFile(path); if (!mimeTypes.empty()) { for (unsigned int index = 0; index < mimeTypes.size(); index++) { std::vector tempRefs = m_Data->m_WriterRegistry.GetReferences(baseData, mimeTypes.at(index).GetName()); for (unsigned int innerIndex = 0; innerIndex < tempRefs.size(); innerIndex++) { refs.push_back(tempRefs.at(innerIndex)); } } } else if (!itksys::SystemTools::GetFilenameExtension(path).empty()) { // If there are no suitable mime-type for the file AND an extension // was supplied, we stop here. return; } else { refs = m_Data->m_WriterRegistry.GetReferences(baseData, destMimeType); } } else { refs = m_Data->m_WriterRegistry.GetReferences(baseData, destMimeType); } std::vector classHierarchy = baseData->GetClassHierarchy(); // Get all writers and their mime types for the given base data type Item bestItem; for (std::vector::const_iterator iter = refs.begin(), iterEnd = refs.end(); iter != iterEnd; ++iter) { std::string mimeTypeName = iter->GetProperty(IFileWriter::PROP_MIMETYPE()).ToString(); if (!mimeTypeName.empty()) { MimeType mimeType = mimeTypeProvider->GetMimeTypeForName(mimeTypeName); if (mimeType.IsValid()) { // There is a registered mime-type for this writer. Now get the confidence level // of this writer for writing the given base data object. IFileWriter *writer = m_Data->m_WriterRegistry.GetWriter(*iter); if (writer == nullptr) continue; try { writer->SetInput(baseData); IFileWriter::ConfidenceLevel confidenceLevel = writer->GetConfidenceLevel(); if (confidenceLevel == IFileWriter::Unsupported) { continue; } std::string baseDataType = iter->GetProperty(IFileWriter::PROP_BASEDATA_TYPE()).ToString(); - std::vector::iterator idxIter = + auto idxIter = std::find(classHierarchy.begin(), classHierarchy.end(), baseDataType); std::size_t baseDataIndex = std::numeric_limits::max(); if (idxIter != classHierarchy.end()) { baseDataIndex = std::distance(classHierarchy.begin(), idxIter); } Item item; item.d->m_FileWriterRef = *iter; item.d->m_FileWriter = writer; item.d->m_ConfidenceLevel = confidenceLevel; item.d->m_BaseDataIndex = baseDataIndex; item.d->m_MimeType = mimeType; item.d->m_Id = us::any_cast(iter->GetProperty(us::ServiceConstants::SERVICE_ID())); m_Data->m_Items.insert(std::make_pair(item.d->m_Id, item)); m_Data->m_MimeTypes.insert(mimeType); if (!bestItem.GetReference() || bestItem < item) { bestItem = item; } } catch (const us::BadAnyCastException &e) { MITK_WARN << "Unexpected: " << e.what(); } catch (const std::exception &e) { // Log the error but continue MITK_WARN << "IFileWriter::GetConfidenceLevel exception: " << e.what(); } } } } if (bestItem.GetReference()) { m_Data->m_BestId = bestItem.GetServiceId(); m_Data->m_SelectedId = m_Data->m_BestId; } } FileWriterSelector::~FileWriterSelector() {} FileWriterSelector &FileWriterSelector::operator=(const FileWriterSelector &other) { m_Data = other.m_Data; return *this; } bool FileWriterSelector::IsEmpty() const { return m_Data->m_Items.empty(); } std::vector FileWriterSelector::Get(const std::string &mimeType) const { std::vector result; for (std::map::const_iterator iter = m_Data->m_Items.begin(), iterEnd = m_Data->m_Items.end(); iter != iterEnd; ++iter) { if (mimeType.empty() || iter->second.GetMimeType().GetName() == mimeType) { result.push_back(iter->second); } } std::sort(result.begin(), result.end()); return result; } std::vector FileWriterSelector::Get() const { return Get(this->GetSelected().d->m_MimeType.GetName()); } FileWriterSelector::Item FileWriterSelector::Get(long id) const { std::map::const_iterator iter = m_Data->m_Items.find(id); if (iter != m_Data->m_Items.end()) { return iter->second; } return Item(); } FileWriterSelector::Item FileWriterSelector::GetDefault() const { return Get(m_Data->m_BestId); } long FileWriterSelector::GetDefaultId() const { return m_Data->m_BestId; } FileWriterSelector::Item FileWriterSelector::GetSelected() const { return Get(m_Data->m_SelectedId); } long FileWriterSelector::GetSelectedId() const { return m_Data->m_SelectedId; } bool FileWriterSelector::Select(const std::string &mimeType) { std::vector items = Get(mimeType); if (items.empty()) return false; return Select(items.back()); } bool FileWriterSelector::Select(const FileWriterSelector::Item &item) { return Select(item.d->m_Id); } bool FileWriterSelector::Select(long id) { if (id > -1) { if (m_Data->m_Items.find(id) == m_Data->m_Items.end()) { return false; } m_Data->m_SelectedId = id; return true; } return false; } std::vector FileWriterSelector::GetMimeTypes() const { std::vector result; result.reserve(m_Data->m_MimeTypes.size()); result.assign(m_Data->m_MimeTypes.begin(), m_Data->m_MimeTypes.end()); return result; } void FileWriterSelector::Swap(FileWriterSelector &fws) { m_Data.Swap(fws.m_Data); } FileWriterSelector::Item::Item(const FileWriterSelector::Item &other) : d(other.d) {} FileWriterSelector::Item::~Item() {} FileWriterSelector::Item &FileWriterSelector::Item::operator=(const FileWriterSelector::Item &other) { d = other.d; return *this; } IFileWriter *FileWriterSelector::Item::GetWriter() const { return d->m_FileWriter; } std::string FileWriterSelector::Item::GetDescription() const { us::Any descr = d->m_FileWriterRef.GetProperty(IFileWriter::PROP_DESCRIPTION()); if (descr.Empty()) return std::string(); return descr.ToString(); } IFileWriter::ConfidenceLevel FileWriterSelector::Item::GetConfidenceLevel() const { return d->m_ConfidenceLevel; } MimeType FileWriterSelector::Item::GetMimeType() const { return d->m_MimeType; } std::string FileWriterSelector::Item::GetBaseDataType() const { us::Any any = d->m_FileWriterRef.GetProperty(IFileWriter::PROP_BASEDATA_TYPE()); if (any.Empty()) return std::string(); return any.ToString(); } us::ServiceReference FileWriterSelector::Item::GetReference() const { return d->m_FileWriterRef; } long FileWriterSelector::Item::GetServiceId() const { return d->m_Id; } bool FileWriterSelector::Item::operator<(const FileWriterSelector::Item &other) const { // sort by confidence level first (ascending) if (d->m_ConfidenceLevel == other.d->m_ConfidenceLevel) { // sort by class hierarchy index (writers for more derived // based data types are considered a better match) if (d->m_BaseDataIndex == other.d->m_BaseDataIndex) { // sort by file writer service ranking return d->m_FileWriterRef < other.d->m_FileWriterRef; } return other.d->m_BaseDataIndex < d->m_BaseDataIndex; } return d->m_ConfidenceLevel < other.d->m_ConfidenceLevel; } FileWriterSelector::Item::Item() : d(new Impl()) {} void swap(FileWriterSelector &fws1, FileWriterSelector &fws2) { fws1.Swap(fws2); } } diff --git a/Modules/Core/src/IO/mitkGeometry3DToXML.cpp b/Modules/Core/src/IO/mitkGeometry3DToXML.cpp index e91708d872..379441bb22 100644 --- a/Modules/Core/src/IO/mitkGeometry3DToXML.cpp +++ b/Modules/Core/src/IO/mitkGeometry3DToXML.cpp @@ -1,240 +1,240 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkGeometry3DToXML.h" #include #include TiXmlElement *mitk::Geometry3DToXML::ToXML(const Geometry3D *geom3D) { assert(geom3D); // really serialize const AffineTransform3D *transform = geom3D->GetIndexToWorldTransform(); // get transform parameters that would need to be serialized AffineTransform3D::MatrixType matrix = transform->GetMatrix(); AffineTransform3D::OffsetType offset = transform->GetOffset(); bool isImageGeometry = geom3D->GetImageGeometry(); BaseGeometry::BoundsArrayType bounds = geom3D->GetBounds(); // create XML file // construct XML tree describing the geometry - TiXmlElement *geomElem = new TiXmlElement("Geometry3D"); + auto *geomElem = new TiXmlElement("Geometry3D"); geomElem->SetAttribute("ImageGeometry", isImageGeometry ? "true" : "false"); geomElem->SetAttribute("FrameOfReferenceID", geom3D->GetFrameOfReferenceID()); // coefficients are matrix[row][column]! - TiXmlElement *matrixElem = new TiXmlElement("IndexToWorld"); + auto *matrixElem = new TiXmlElement("IndexToWorld"); matrixElem->SetAttribute("type", "Matrix3x3"); matrixElem->SetAttribute("m_0_0", boost::lexical_cast(matrix[0][0])); matrixElem->SetAttribute("m_0_1", boost::lexical_cast(matrix[0][1])); matrixElem->SetAttribute("m_0_2", boost::lexical_cast(matrix[0][2])); matrixElem->SetAttribute("m_1_0", boost::lexical_cast(matrix[1][0])); matrixElem->SetAttribute("m_1_1", boost::lexical_cast(matrix[1][1])); matrixElem->SetAttribute("m_1_2", boost::lexical_cast(matrix[1][2])); matrixElem->SetAttribute("m_2_0", boost::lexical_cast(matrix[2][0])); matrixElem->SetAttribute("m_2_1", boost::lexical_cast(matrix[2][1])); matrixElem->SetAttribute("m_2_2", boost::lexical_cast(matrix[2][2])); geomElem->LinkEndChild(matrixElem); - TiXmlElement *offsetElem = new TiXmlElement("Offset"); + auto *offsetElem = new TiXmlElement("Offset"); offsetElem->SetAttribute("type", "Vector3D"); offsetElem->SetAttribute("x", boost::lexical_cast(offset[0])); offsetElem->SetAttribute("y", boost::lexical_cast(offset[1])); offsetElem->SetAttribute("z", boost::lexical_cast(offset[2])); geomElem->LinkEndChild(offsetElem); - TiXmlElement *boundsElem = new TiXmlElement("Bounds"); - TiXmlElement *boundsMinElem = new TiXmlElement("Min"); + auto *boundsElem = new TiXmlElement("Bounds"); + auto *boundsMinElem = new TiXmlElement("Min"); boundsMinElem->SetAttribute("type", "Vector3D"); boundsMinElem->SetAttribute("x", boost::lexical_cast(bounds[0])); boundsMinElem->SetAttribute("y", boost::lexical_cast(bounds[2])); boundsMinElem->SetAttribute("z", boost::lexical_cast(bounds[4])); boundsElem->LinkEndChild(boundsMinElem); - TiXmlElement *boundsMaxElem = new TiXmlElement("Max"); + auto *boundsMaxElem = new TiXmlElement("Max"); boundsMaxElem->SetAttribute("type", "Vector3D"); boundsMaxElem->SetAttribute("x", boost::lexical_cast(bounds[1])); boundsMaxElem->SetAttribute("y", boost::lexical_cast(bounds[3])); boundsMaxElem->SetAttribute("z", boost::lexical_cast(bounds[5])); boundsElem->LinkEndChild(boundsMaxElem); geomElem->LinkEndChild(boundsElem); return geomElem; } mitk::Geometry3D::Pointer mitk::Geometry3DToXML::FromXML(TiXmlElement *geometryElement) { if (!geometryElement) { MITK_ERROR << "Cannot deserialize Geometry3D from nullptr."; return nullptr; } AffineTransform3D::MatrixType matrix; AffineTransform3D::OffsetType offset; bool isImageGeometry(false); unsigned int frameOfReferenceID(0); BaseGeometry::BoundsArrayType bounds; if (TIXML_SUCCESS != geometryElement->QueryUnsignedAttribute("FrameOfReferenceID", &frameOfReferenceID)) { MITK_WARN << "Missing FrameOfReference for Geometry3D."; } if (TIXML_SUCCESS != geometryElement->QueryBoolAttribute("ImageGeometry", &isImageGeometry)) { MITK_WARN << "Missing bool ImageGeometry for Geometry3D."; } // matrix if (TiXmlElement *matrixElem = geometryElement->FirstChildElement("IndexToWorld")->ToElement()) { bool matrixComplete = true; for (unsigned int r = 0; r < 3; ++r) { for (unsigned int c = 0; c < 3; ++c) { std::stringstream element_namer; element_namer << "m_" << r << "_" << c; std::string string_value; if (TIXML_SUCCESS == matrixElem->QueryStringAttribute(element_namer.str().c_str(), &string_value)) { try { matrix[r][c] = boost::lexical_cast(string_value); } catch (boost::bad_lexical_cast &e) { MITK_ERROR << "Could not parse '" << string_value << "' as number: " << e.what(); return nullptr; } } else { matrixComplete = false; } } } if (!matrixComplete) { MITK_ERROR << "Could not parse all Geometry3D matrix coefficients!"; return nullptr; } } else { MITK_ERROR << "Parse error: expected Matrix3x3 child below Geometry3D node"; return nullptr; } // offset if (TiXmlElement *offsetElem = geometryElement->FirstChildElement("Offset")->ToElement()) { bool vectorComplete = true; std::string offset_string[3]; vectorComplete &= TIXML_SUCCESS == offsetElem->QueryStringAttribute("x", &offset_string[0]); vectorComplete &= TIXML_SUCCESS == offsetElem->QueryStringAttribute("y", &offset_string[1]); vectorComplete &= TIXML_SUCCESS == offsetElem->QueryStringAttribute("z", &offset_string[2]); if (!vectorComplete) { MITK_ERROR << "Could not parse complete Geometry3D offset!"; return nullptr; } for (unsigned int d = 0; d < 3; ++d) try { offset[d] = boost::lexical_cast(offset_string[d]); } catch (boost::bad_lexical_cast &e) { MITK_ERROR << "Could not parse '" << offset_string[d] << "' as number: " << e.what(); return nullptr; } } else { MITK_ERROR << "Parse error: expected Offset3D child below Geometry3D node"; return nullptr; } // bounds if (TiXmlElement *boundsElem = geometryElement->FirstChildElement("Bounds")->ToElement()) { bool vectorsComplete(true); std::string bounds_string[6]; if (TiXmlElement *minElem = boundsElem->FirstChildElement("Min")->ToElement()) { vectorsComplete &= TIXML_SUCCESS == minElem->QueryStringAttribute("x", &bounds_string[0]); vectorsComplete &= TIXML_SUCCESS == minElem->QueryStringAttribute("y", &bounds_string[2]); vectorsComplete &= TIXML_SUCCESS == minElem->QueryStringAttribute("z", &bounds_string[4]); } else { vectorsComplete = false; } if (TiXmlElement *maxElem = boundsElem->FirstChildElement("Max")->ToElement()) { vectorsComplete &= TIXML_SUCCESS == maxElem->QueryStringAttribute("x", &bounds_string[1]); vectorsComplete &= TIXML_SUCCESS == maxElem->QueryStringAttribute("y", &bounds_string[3]); vectorsComplete &= TIXML_SUCCESS == maxElem->QueryStringAttribute("z", &bounds_string[5]); } else { vectorsComplete = false; } if (!vectorsComplete) { MITK_ERROR << "Could not parse complete Geometry3D bounds!"; return nullptr; } for (unsigned int d = 0; d < 6; ++d) try { bounds[d] = boost::lexical_cast(bounds_string[d]); } catch (boost::bad_lexical_cast &e) { MITK_ERROR << "Could not parse '" << bounds_string[d] << "' as number: " << e.what(); return nullptr; } } // build GeometryData from matrix/offset AffineTransform3D::Pointer newTransform = AffineTransform3D::New(); newTransform->SetMatrix(matrix); newTransform->SetOffset(offset); Geometry3D::Pointer newGeometry = Geometry3D::New(); newGeometry->SetFrameOfReferenceID(frameOfReferenceID); newGeometry->SetImageGeometry(isImageGeometry); newGeometry->SetIndexToWorldTransform(newTransform); newGeometry->SetBounds(bounds); return newGeometry; } diff --git a/Modules/Core/src/IO/mitkGeometryDataWriterService.cpp b/Modules/Core/src/IO/mitkGeometryDataWriterService.cpp index 173d696578..9b5ce75fd3 100644 --- a/Modules/Core/src/IO/mitkGeometryDataWriterService.cpp +++ b/Modules/Core/src/IO/mitkGeometryDataWriterService.cpp @@ -1,95 +1,95 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkGeometryDataWriterService.h" #include "mitkIOMimeTypes.h" #include "mitkProportionalTimeGeometryToXML.h" #include #include mitk::GeometryDataWriterService::GeometryDataWriterService() : AbstractFileWriter( GeometryData::GetStaticNameOfClass(), IOMimeTypes::GEOMETRY_DATA_MIMETYPE(), "MITK Geometry Data Writer") { RegisterService(); } mitk::GeometryDataWriterService::GeometryDataWriterService(const mitk::GeometryDataWriterService &other) : AbstractFileWriter(other) { } mitk::GeometryDataWriterService::~GeometryDataWriterService() { } void mitk::GeometryDataWriterService::Write() { /* using the stream interface produces files without line breaks or indentation.. But before changing to file interface, need to understand the new I/O classes */ OutputStream out(this); if (!out.good()) { mitkThrow() << "Stream not good."; } // Switch the current locale to "C" LocaleSwitch localeSwitch("C"); // Open XML file using TinyXML, // loop over all inputs, // call appropriate serializing functions TiXmlDocument doc; - TiXmlDeclaration *decl = new TiXmlDeclaration( + auto *decl = new TiXmlDeclaration( "1.0", "UTF-8", ""); // TODO what to write here? encoding? standalone would mean that we provide a DTD somewhere... doc.LinkEndChild(decl); - TiXmlElement *rootNode = new TiXmlElement("GeometryData"); + auto *rootNode = new TiXmlElement("GeometryData"); doc.LinkEndChild(rootNode); // note version info - TiXmlElement *version = new TiXmlElement("Version"); + auto *version = new TiXmlElement("Version"); version->SetAttribute("Writer", __FILE__); version->SetAttribute("FileVersion", 1); rootNode->LinkEndChild(version); - const GeometryData *data = static_cast(this->GetInput()); + const auto *data = static_cast(this->GetInput()); const ProportionalTimeGeometry *timeGeometry(nullptr); if ((timeGeometry = dynamic_cast(data->GetTimeGeometry()))) { TiXmlElement *timeGeometryElement = ProportionalTimeGeometryToXML::ToXML(timeGeometry); rootNode->LinkEndChild(timeGeometryElement); } else { MITK_WARN << "Serializing GeometryData that does not have a valid ProportionalTimeGeometry! Not implemented!"; } // Write out document out << doc; } mitk::GeometryDataWriterService *mitk::GeometryDataWriterService::Clone() const { return new GeometryDataWriterService(*this); } diff --git a/Modules/Core/src/IO/mitkIOUtil.cpp b/Modules/Core/src/IO/mitkIOUtil.cpp index bf9dac59ad..635905f554 100644 --- a/Modules/Core/src/IO/mitkIOUtil.cpp +++ b/Modules/Core/src/IO/mitkIOUtil.cpp @@ -1,1062 +1,1062 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkIOUtil.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include // ITK #include // VTK #include #include #include #include #include static std::string GetLastErrorStr() { #ifdef US_PLATFORM_POSIX return std::string(strerror(errno)); #else // Retrieve the system error message for the last-error code LPVOID lpMsgBuf; DWORD dw = GetLastError(); FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, nullptr, dw, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR)&lpMsgBuf, 0, nullptr); std::string errMsg((LPCTSTR)lpMsgBuf); LocalFree(lpMsgBuf); return errMsg; #endif } #ifdef US_PLATFORM_WINDOWS #include #include // make the posix flags point to the obsolte bsd types on windows #define S_IRUSR S_IREAD #define S_IWUSR S_IWRITE #else #include #include #include #endif #include #include static const char validLetters[] = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; // A cross-platform version of the mkstemps function static int mkstemps_compat(char *tmpl, int suffixlen) { static unsigned long long value = 0; int savedErrno = errno; // Lower bound on the number of temporary files to attempt to generate. #define ATTEMPTS_MIN (62 * 62 * 62) /* The number of times to attempt to generate a temporary file. To conform to POSIX, this must be no smaller than TMP_MAX. */ #if ATTEMPTS_MIN < TMP_MAX const unsigned int attempts = TMP_MAX; #else const unsigned int attempts = ATTEMPTS_MIN; #endif const int len = strlen(tmpl); if ((len - suffixlen) < 6 || strncmp(&tmpl[len - 6 - suffixlen], "XXXXXX", 6)) { errno = EINVAL; return -1; } /* This is where the Xs start. */ char *XXXXXX = &tmpl[len - 6 - suffixlen]; /* Get some more or less random data. */ #ifdef US_PLATFORM_WINDOWS { SYSTEMTIME stNow; FILETIME ftNow; // get system time GetSystemTime(&stNow); stNow.wMilliseconds = 500; if (!SystemTimeToFileTime(&stNow, &ftNow)) { errno = -1; return -1; } unsigned long long randomTimeBits = ((static_cast(ftNow.dwHighDateTime) << 32) | static_cast(ftNow.dwLowDateTime)); value = randomTimeBits ^ static_cast(GetCurrentThreadId()); } #else { struct timeval tv; gettimeofday(&tv, nullptr); unsigned long long randomTimeBits = ((static_cast(tv.tv_usec) << 32) | static_cast(tv.tv_sec)); value = randomTimeBits ^ static_cast(getpid()); } #endif for (unsigned int count = 0; count < attempts; value += 7777, ++count) { unsigned long long v = value; /* Fill in the random bits. */ XXXXXX[0] = validLetters[v % 62]; v /= 62; XXXXXX[1] = validLetters[v % 62]; v /= 62; XXXXXX[2] = validLetters[v % 62]; v /= 62; XXXXXX[3] = validLetters[v % 62]; v /= 62; XXXXXX[4] = validLetters[v % 62]; v /= 62; XXXXXX[5] = validLetters[v % 62]; int fd = open(tmpl, O_RDWR | O_CREAT | O_EXCL, S_IRUSR | S_IWUSR); if (fd >= 0) { errno = savedErrno; return fd; } else if (errno != EEXIST) { return -1; } } /* We got out of the loop because we ran out of combinations to try. */ errno = EEXIST; return -1; } // A cross-platform version of the POSIX mkdtemp function static char *mkdtemps_compat(char *tmpl, int suffixlen) { static unsigned long long value = 0; int savedErrno = errno; // Lower bound on the number of temporary dirs to attempt to generate. #define ATTEMPTS_MIN (62 * 62 * 62) /* The number of times to attempt to generate a temporary dir. To conform to POSIX, this must be no smaller than TMP_MAX. */ #if ATTEMPTS_MIN < TMP_MAX const unsigned int attempts = TMP_MAX; #else const unsigned int attempts = ATTEMPTS_MIN; #endif const int len = strlen(tmpl); if ((len - suffixlen) < 6 || strncmp(&tmpl[len - 6 - suffixlen], "XXXXXX", 6)) { errno = EINVAL; return nullptr; } /* This is where the Xs start. */ char *XXXXXX = &tmpl[len - 6 - suffixlen]; /* Get some more or less random data. */ #ifdef US_PLATFORM_WINDOWS { SYSTEMTIME stNow; FILETIME ftNow; // get system time GetSystemTime(&stNow); stNow.wMilliseconds = 500; if (!SystemTimeToFileTime(&stNow, &ftNow)) { errno = -1; return nullptr; } unsigned long long randomTimeBits = ((static_cast(ftNow.dwHighDateTime) << 32) | static_cast(ftNow.dwLowDateTime)); value = randomTimeBits ^ static_cast(GetCurrentThreadId()); } #else { struct timeval tv; gettimeofday(&tv, nullptr); unsigned long long randomTimeBits = ((static_cast(tv.tv_usec) << 32) | static_cast(tv.tv_sec)); value = randomTimeBits ^ static_cast(getpid()); } #endif unsigned int count = 0; for (; count < attempts; value += 7777, ++count) { unsigned long long v = value; /* Fill in the random bits. */ XXXXXX[0] = validLetters[v % 62]; v /= 62; XXXXXX[1] = validLetters[v % 62]; v /= 62; XXXXXX[2] = validLetters[v % 62]; v /= 62; XXXXXX[3] = validLetters[v % 62]; v /= 62; XXXXXX[4] = validLetters[v % 62]; v /= 62; XXXXXX[5] = validLetters[v % 62]; #ifdef US_PLATFORM_WINDOWS int fd = _mkdir(tmpl); //, _S_IREAD | _S_IWRITE | _S_IEXEC); #else int fd = mkdir(tmpl, S_IRUSR | S_IWUSR | S_IXUSR); #endif if (fd >= 0) { errno = savedErrno; return tmpl; } else if (errno != EEXIST) { return nullptr; } } /* We got out of the loop because we ran out of combinations to try. */ errno = EEXIST; return nullptr; } //#endif //************************************************************** // mitk::IOUtil method definitions namespace mitk { struct IOUtil::Impl { struct FixedReaderOptionsFunctor : public ReaderOptionsFunctorBase { FixedReaderOptionsFunctor(const IFileReader::Options &options) : m_Options(options) {} bool operator()(LoadInfo &loadInfo) const override { IFileReader *reader = loadInfo.m_ReaderSelector.GetSelected().GetReader(); if (reader) { reader->SetOptions(m_Options); } return false; } private: const IFileReader::Options &m_Options; }; struct FixedWriterOptionsFunctor : public WriterOptionsFunctorBase { FixedWriterOptionsFunctor(const IFileReader::Options &options) : m_Options(options) {} bool operator()(SaveInfo &saveInfo) const override { IFileWriter *writer = saveInfo.m_WriterSelector.GetSelected().GetWriter(); if (writer) { writer->SetOptions(m_Options); } return false; } private: const IFileWriter::Options &m_Options; }; static BaseData::Pointer LoadBaseDataFromFile(const std::string &path, const ReaderOptionsFunctorBase* optionsCallback = nullptr); static void SetDefaultDataNodeProperties(mitk::DataNode *node, const std::string &filePath = std::string()); }; BaseData::Pointer IOUtil::Impl::LoadBaseDataFromFile(const std::string &path, const ReaderOptionsFunctorBase *optionsCallback) { std::vector baseDataList = Load(path, optionsCallback); // The Load(path) call above should throw an exception if nothing could be loaded assert(!baseDataList.empty()); return baseDataList.front(); } #ifdef US_PLATFORM_WINDOWS std::string IOUtil::GetProgramPath() { char path[512]; std::size_t index = std::string(path, GetModuleFileName(nullptr, path, 512)).find_last_of('\\'); return std::string(path, index); } #elif defined(US_PLATFORM_APPLE) #include std::string IOUtil::GetProgramPath() { char path[512]; uint32_t size = sizeof(path); if (_NSGetExecutablePath(path, &size) == 0) { std::size_t index = std::string(path).find_last_of('/'); std::string strPath = std::string(path, index); // const char* execPath = strPath.c_str(); // mitk::StandardFileLocations::GetInstance()->AddDirectoryForSearch(execPath,false); return strPath; } return std::string(); } #else #include #include #include std::string IOUtil::GetProgramPath() { std::stringstream ss; ss << "/proc/" << getpid() << "/exe"; char proc[512] = {0}; ssize_t ch = readlink(ss.str().c_str(), proc, 512); if (ch == -1) return std::string(); std::size_t index = std::string(proc).find_last_of('/'); return std::string(proc, index); } #endif char IOUtil::GetDirectorySeparator() { #ifdef US_PLATFORM_WINDOWS return '\\'; #else return '/'; #endif } std::string IOUtil::GetTempPath() { static std::string result; if (result.empty()) { #ifdef US_PLATFORM_WINDOWS char tempPathTestBuffer[1]; DWORD bufferLength = ::GetTempPath(1, tempPathTestBuffer); if (bufferLength == 0) { mitkThrow() << GetLastErrorStr(); } std::vector tempPath(bufferLength); bufferLength = ::GetTempPath(bufferLength, &tempPath[0]); if (bufferLength == 0) { mitkThrow() << GetLastErrorStr(); } result.assign(tempPath.begin(), tempPath.begin() + static_cast(bufferLength)); #else result = "/tmp/"; #endif } return result; } std::string IOUtil::CreateTemporaryFile(const std::string &templateName, std::string path) { ofstream tmpOutputStream; std::string returnValue = CreateTemporaryFile(tmpOutputStream, templateName, path); tmpOutputStream.close(); return returnValue; } std::string IOUtil::CreateTemporaryFile(std::ofstream &f, const std::string &templateName, std::string path) { return CreateTemporaryFile(f, std::ios_base::out | std::ios_base::trunc, templateName, path); } std::string IOUtil::CreateTemporaryFile(std::ofstream &f, std::ios_base::openmode mode, const std::string &templateName, std::string path) { if (path.empty()) { path = GetTempPath(); } path += templateName; std::vector dst_path(path.begin(), path.end()); dst_path.push_back('\0'); std::size_t lastX = path.find_last_of('X'); std::size_t firstX = path.find_last_not_of('X', lastX); int firstNonX = firstX == std::string::npos ? -1 : firstX - 1; while (lastX != std::string::npos && (lastX - firstNonX) < 6) { lastX = path.find_last_of('X', firstX); firstX = path.find_last_not_of('X', lastX); firstNonX = firstX == std::string::npos ? -1 : firstX - 1; } std::size_t suffixlen = lastX == std::string::npos ? path.size() : path.size() - lastX - 1; int fd = mkstemps_compat(&dst_path[0], suffixlen); if (fd != -1) { path.assign(dst_path.begin(), dst_path.end() - 1); f.open(path.c_str(), mode | std::ios_base::out | std::ios_base::trunc); close(fd); } else { mitkThrow() << "Creating temporary file " << &dst_path[0] << " failed: " << GetLastErrorStr(); } return path; } std::string IOUtil::CreateTemporaryDirectory(const std::string &templateName, std::string path) { if (path.empty()) { path = GetTempPath(); } path += GetDirectorySeparator() + templateName; std::vector dst_path(path.begin(), path.end()); dst_path.push_back('\0'); std::size_t lastX = path.find_last_of('X'); std::size_t firstX = path.find_last_not_of('X', lastX); int firstNonX = firstX == std::string::npos ? -1 : firstX - 1; while (lastX != std::string::npos && (lastX - firstNonX) < 6) { lastX = path.find_last_of('X', firstX); firstX = path.find_last_not_of('X', lastX); firstNonX = firstX == std::string::npos ? -1 : firstX - 1; } std::size_t suffixlen = lastX == std::string::npos ? path.size() : path.size() - lastX - 1; if (mkdtemps_compat(&dst_path[0], suffixlen) == nullptr) { mitkThrow() << "Creating temporary directory " << &dst_path[0] << " failed: " << GetLastErrorStr(); } path.assign(dst_path.begin(), dst_path.end() - 1); return path; } DataStorage::SetOfObjects::Pointer IOUtil::Load(const std::string &path, DataStorage &storage, const ReaderOptionsFunctorBase *optionsCallback) { std::vector paths; paths.push_back(path); return Load(paths, storage, optionsCallback); } DataStorage::SetOfObjects::Pointer IOUtil::Load(const std::string &path, const IFileReader::Options &options, DataStorage &storage) { std::vector loadInfos; loadInfos.push_back(LoadInfo(path)); DataStorage::SetOfObjects::Pointer nodeResult = DataStorage::SetOfObjects::New(); Impl::FixedReaderOptionsFunctor optionsCallback(options); std::string errMsg = Load(loadInfos, nodeResult, &storage, &optionsCallback); if (!errMsg.empty()) { mitkThrow() << errMsg; } return nodeResult; } std::vector IOUtil::Load(const std::string &path, const ReaderOptionsFunctorBase *optionsCallback) { std::vector paths; paths.push_back(path); return Load(paths, optionsCallback); } std::vector IOUtil::Load(const std::string &path, const IFileReader::Options &options) { std::vector loadInfos; loadInfos.push_back(LoadInfo(path)); Impl::FixedReaderOptionsFunctor optionsCallback(options); std::string errMsg = Load(loadInfos, nullptr, nullptr, &optionsCallback); if (!errMsg.empty()) { mitkThrow() << errMsg; } return loadInfos.front().m_Output; } DataStorage::SetOfObjects::Pointer IOUtil::Load(const std::vector &paths, DataStorage &storage, const ReaderOptionsFunctorBase *optionsCallback) { DataStorage::SetOfObjects::Pointer nodeResult = DataStorage::SetOfObjects::New(); std::vector loadInfos; for (auto loadInfo : paths) { loadInfos.push_back(loadInfo); } std::string errMsg = Load(loadInfos, nodeResult, &storage, optionsCallback); if (!errMsg.empty()) { mitkThrow() << errMsg; } return nodeResult; } std::vector IOUtil::Load(const std::vector &paths, const ReaderOptionsFunctorBase *optionsCallback) { std::vector result; std::vector loadInfos; for (auto loadInfo : paths) { loadInfos.push_back(loadInfo); } std::string errMsg = Load(loadInfos, nullptr, nullptr, optionsCallback); if (!errMsg.empty()) { mitkThrow() << errMsg; } for (std::vector::const_iterator iter = loadInfos.begin(), iterEnd = loadInfos.end(); iter != iterEnd; ++iter) { result.insert(result.end(), iter->m_Output.begin(), iter->m_Output.end()); } return result; } Image::Pointer IOUtil::LoadImage(const std::string &path, const ReaderOptionsFunctorBase *optionsCallback) { BaseData::Pointer baseData = Impl::LoadBaseDataFromFile(path, optionsCallback); mitk::Image::Pointer image = dynamic_cast(baseData.GetPointer()); if (image.IsNull()) { mitkThrow() << path << " is not a mitk::Image but a " << baseData->GetNameOfClass(); } return image; } Surface::Pointer IOUtil::LoadSurface(const std::string &path, const ReaderOptionsFunctorBase *optionsCallback) { BaseData::Pointer baseData = Impl::LoadBaseDataFromFile(path, optionsCallback); mitk::Surface::Pointer surface = dynamic_cast(baseData.GetPointer()); if (surface.IsNull()) { mitkThrow() << path << " is not a mitk::Surface but a " << baseData->GetNameOfClass(); } return surface; } PointSet::Pointer IOUtil::LoadPointSet(const std::string &path, const ReaderOptionsFunctorBase *optionsCallback) { BaseData::Pointer baseData = Impl::LoadBaseDataFromFile(path, optionsCallback); mitk::PointSet::Pointer pointset = dynamic_cast(baseData.GetPointer()); if (pointset.IsNull()) { mitkThrow() << path << " is not a mitk::PointSet but a " << baseData->GetNameOfClass(); } return pointset; } std::string IOUtil::Load(std::vector &loadInfos, DataStorage::SetOfObjects *nodeResult, DataStorage *ds, const ReaderOptionsFunctorBase *optionsCallback) { if (loadInfos.empty()) { return "No input files given"; } int filesToRead = loadInfos.size(); mitk::ProgressBar::GetInstance()->AddStepsToDo(2 * filesToRead); std::string errMsg; std::map usedReaderItems; std::vector< std::string > read_files; for (auto &loadInfo : loadInfos) { if(std::find(read_files.begin(), read_files.end(), loadInfo.m_Path) != read_files.end()) continue; std::vector readers = loadInfo.m_ReaderSelector.Get(); if (readers.empty()) { if (!itksys::SystemTools::FileExists(loadInfo.m_Path.c_str())) { errMsg += "File '" + loadInfo.m_Path + "' does not exist\n"; } else { errMsg += "No reader available for '" + loadInfo.m_Path + "'\n"; } continue; } bool callOptionsCallback = readers.size() > 1 || !readers.front().GetReader()->GetOptions().empty(); // check if we already used a reader which should be re-used std::vector currMimeTypes = loadInfo.m_ReaderSelector.GetMimeTypes(); std::string selectedMimeType; for (std::vector::const_iterator mimeTypeIter = currMimeTypes.begin(), mimeTypeIterEnd = currMimeTypes.end(); mimeTypeIter != mimeTypeIterEnd; ++mimeTypeIter) { std::map::const_iterator oldSelectedItemIter = usedReaderItems.find(mimeTypeIter->GetName()); if (oldSelectedItemIter != usedReaderItems.end()) { // we found an already used item for a mime-type which is contained // in the current reader set, check all current readers if there service // id equals the old reader for (std::vector::const_iterator currReaderItem = readers.begin(), currReaderItemEnd = readers.end(); currReaderItem != currReaderItemEnd; ++currReaderItem) { if (currReaderItem->GetMimeType().GetName() == mimeTypeIter->GetName() && currReaderItem->GetServiceId() == oldSelectedItemIter->second.GetServiceId() && currReaderItem->GetConfidenceLevel() >= oldSelectedItemIter->second.GetConfidenceLevel()) { // okay, we used the same reader already, re-use its options selectedMimeType = mimeTypeIter->GetName(); callOptionsCallback = false; loadInfo.m_ReaderSelector.Select(oldSelectedItemIter->second.GetServiceId()); loadInfo.m_ReaderSelector.GetSelected().GetReader()->SetOptions( oldSelectedItemIter->second.GetReader()->GetOptions()); break; } } if (!selectedMimeType.empty()) break; } } if (callOptionsCallback && optionsCallback) { callOptionsCallback = (*optionsCallback)(loadInfo); if (!callOptionsCallback && !loadInfo.m_Cancel) { usedReaderItems.erase(selectedMimeType); FileReaderSelector::Item selectedItem = loadInfo.m_ReaderSelector.GetSelected(); usedReaderItems.insert(std::make_pair(selectedItem.GetMimeType().GetName(), selectedItem)); } } if (loadInfo.m_Cancel) { errMsg += "Reading operation(s) cancelled."; break; } IFileReader *reader = loadInfo.m_ReaderSelector.GetSelected().GetReader(); if (reader == nullptr) { errMsg += "Unexpected nullptr reader."; break; } // Do the actual reading try { DataStorage::SetOfObjects::Pointer nodes; if (ds != nullptr) { nodes = reader->Read(*ds); std::vector< std::string > new_files = reader->GetReadFiles(); read_files.insert( read_files.end(), new_files.begin(), new_files.end() ); } else { nodes = DataStorage::SetOfObjects::New(); std::vector baseData = reader->Read(); - for (std::vector::iterator iter = baseData.begin(); iter != baseData.end(); ++iter) + for (auto iter = baseData.begin(); iter != baseData.end(); ++iter) { if (iter->IsNotNull()) { mitk::DataNode::Pointer node = mitk::DataNode::New(); node->SetData(*iter); nodes->InsertElement(nodes->Size(), node); } } std::vector< std::string > new_files = reader->GetReadFiles(); read_files.insert( read_files.end(), new_files.begin(), new_files.end() ); } for (DataStorage::SetOfObjects::ConstIterator nodeIter = nodes->Begin(), nodeIterEnd = nodes->End(); nodeIter != nodeIterEnd; ++nodeIter) { const mitk::DataNode::Pointer &node = nodeIter->Value(); mitk::BaseData::Pointer data = node->GetData(); if (data.IsNull()) { continue; } mitk::StringProperty::Pointer pathProp = mitk::StringProperty::New(loadInfo.m_Path); data->SetProperty("path", pathProp); loadInfo.m_Output.push_back(data); if (nodeResult) { nodeResult->push_back(nodeIter->Value()); } } if (loadInfo.m_Output.empty() || (nodeResult && nodeResult->Size() == 0)) { errMsg += "Unknown read error occurred reading " + loadInfo.m_Path; } } catch (const std::exception &e) { errMsg += "Exception occured when reading file " + loadInfo.m_Path + ":\n" + e.what() + "\n\n"; } mitk::ProgressBar::GetInstance()->Progress(2); --filesToRead; } if (!errMsg.empty()) { MITK_ERROR << errMsg; } mitk::ProgressBar::GetInstance()->Progress(2 * filesToRead); return errMsg; } std::vector IOUtil::Load(const us::ModuleResource &usResource, std::ios_base::openmode mode) { us::ModuleResourceStream resStream(usResource, mode); mitk::CoreServicePointer mimeTypeProvider(mitk::CoreServices::GetMimeTypeProvider()); std::vector mimetypes = mimeTypeProvider->GetMimeTypesForFile(usResource.GetResourcePath()); std::vector data; if (mimetypes.empty()) { mitkThrow() << "No mimetype for resource stream: " << usResource.GetResourcePath(); return data; } mitk::FileReaderRegistry fileReaderRegistry; std::vector> refs = fileReaderRegistry.GetReferences(mimetypes[0]); if (refs.empty()) { mitkThrow() << "No reader available for resource stream: " << usResource.GetResourcePath(); return data; } mitk::IFileReader *reader = fileReaderRegistry.GetReader(refs[0]); reader->SetInput(usResource.GetResourcePath(), &resStream); data = reader->Read(); return data; } void IOUtil::Save(const BaseData *data, const std::string &path) { Save(data, path, IFileWriter::Options()); } void IOUtil::Save(const BaseData *data, const std::string &path, const IFileWriter::Options &options) { Save(data, std::string(), path, options); } void IOUtil::Save(const BaseData *data, const std::string &mimeType, const std::string &path, bool addExtension) { Save(data, mimeType, path, IFileWriter::Options(), addExtension); } void IOUtil::Save(const BaseData *data, const std::string &mimeType, const std::string &path, const IFileWriter::Options &options, bool addExtension) { if ((data == nullptr) || (data->IsEmpty())) mitkThrow() << "BaseData cannotbe null or empty for save methods in IOUtil.h."; std::string errMsg; if (options.empty()) { errMsg = Save(data, mimeType, path, nullptr, addExtension); } else { Impl::FixedWriterOptionsFunctor optionsCallback(options); errMsg = Save(data, mimeType, path, &optionsCallback, addExtension); } if (!errMsg.empty()) { mitkThrow() << errMsg; } } void IOUtil::Save(std::vector &saveInfos) { std::string errMsg = Save(saveInfos, nullptr); if (!errMsg.empty()) { mitkThrow() << errMsg; } } std::string IOUtil::Save(const BaseData *data, const std::string &mimeTypeName, const std::string &path, WriterOptionsFunctorBase *optionsCallback, bool addExtension) { if (path.empty()) { return "No output filename given"; } mitk::CoreServicePointer mimeTypeProvider(mitk::CoreServices::GetMimeTypeProvider()); MimeType mimeType = mimeTypeProvider->GetMimeTypeForName(mimeTypeName); SaveInfo saveInfo(data, mimeType, path); std::string ext = itksys::SystemTools::GetFilenameExtension(path); if (saveInfo.m_WriterSelector.IsEmpty()) { return std::string("No suitable writer found for the current data of type ") + data->GetNameOfClass() + (mimeType.IsValid() ? (std::string(" and mime-type ") + mimeType.GetName()) : std::string()) + (ext.empty() ? std::string() : (std::string(" with extension ") + ext)); } // Add an extension if not already specified if (ext.empty() && addExtension) { saveInfo.m_MimeType.GetExtensions().empty() ? std::string() : "." + saveInfo.m_MimeType.GetExtensions().front(); } std::vector infos; infos.push_back(saveInfo); return Save(infos, optionsCallback); } std::string IOUtil::Save(std::vector &saveInfos, WriterOptionsFunctorBase *optionsCallback) { if (saveInfos.empty()) { return "No data for saving available"; } int filesToWrite = saveInfos.size(); mitk::ProgressBar::GetInstance()->AddStepsToDo(2 * filesToWrite); std::string errMsg; std::set usedSaveInfos; for (auto &saveInfo : saveInfos) { const std::string baseDataType = saveInfo.m_BaseData->GetNameOfClass(); std::vector writers = saveInfo.m_WriterSelector.Get(); // Error out if no compatible Writer was found if (writers.empty()) { errMsg += std::string("No writer available for ") + baseDataType + " data.\n"; continue; } bool callOptionsCallback = writers.size() > 1 || !writers[0].GetWriter()->GetOptions().empty(); // check if we already used a writer for this base data type // which should be re-used - std::set::const_iterator oldSaveInfoIter = usedSaveInfos.find(saveInfo); + auto oldSaveInfoIter = usedSaveInfos.find(saveInfo); if (oldSaveInfoIter != usedSaveInfos.end()) { // we previously saved a base data object of the same data with the same mime-type, // check if the same writer is contained in the current writer set and if the // confidence level matches FileWriterSelector::Item oldSelectedItem = oldSaveInfoIter->m_WriterSelector.Get(oldSaveInfoIter->m_WriterSelector.GetSelectedId()); for (std::vector::const_iterator currWriterItem = writers.begin(), currWriterItemEnd = writers.end(); currWriterItem != currWriterItemEnd; ++currWriterItem) { if (currWriterItem->GetServiceId() == oldSelectedItem.GetServiceId() && currWriterItem->GetConfidenceLevel() >= oldSelectedItem.GetConfidenceLevel()) { // okay, we used the same writer already, re-use its options callOptionsCallback = false; saveInfo.m_WriterSelector.Select(oldSaveInfoIter->m_WriterSelector.GetSelectedId()); saveInfo.m_WriterSelector.GetSelected().GetWriter()->SetOptions(oldSelectedItem.GetWriter()->GetOptions()); break; } } } if (callOptionsCallback && optionsCallback) { callOptionsCallback = (*optionsCallback)(saveInfo); if (!callOptionsCallback && !saveInfo.m_Cancel) { usedSaveInfos.erase(saveInfo); usedSaveInfos.insert(saveInfo); } } if (saveInfo.m_Cancel) { errMsg += "Writing operation(s) cancelled."; break; } IFileWriter *writer = saveInfo.m_WriterSelector.GetSelected().GetWriter(); if (writer == nullptr) { errMsg += "Unexpected nullptr writer."; break; } // Do the actual writing try { writer->SetOutputLocation(saveInfo.m_Path); writer->Write(); } catch (const std::exception &e) { errMsg += std::string("Exception occurred when writing to ") + saveInfo.m_Path + ":\n" + e.what() + "\n"; } mitk::ProgressBar::GetInstance()->Progress(2); --filesToWrite; } if (!errMsg.empty()) { MITK_ERROR << errMsg; } mitk::ProgressBar::GetInstance()->Progress(2 * filesToWrite); return errMsg; } // This method can be removed after the deprecated LoadDataNode() method was removed void IOUtil::Impl::SetDefaultDataNodeProperties(DataNode *node, const std::string &filePath) { // path mitk::StringProperty::Pointer pathProp = mitk::StringProperty::New(itksys::SystemTools::GetFilenamePath(filePath)); node->SetProperty(StringProperty::PATH, pathProp); // name already defined? mitk::StringProperty::Pointer nameProp = dynamic_cast(node->GetProperty("name")); if (nameProp.IsNull() || (strcmp(nameProp->GetValue(), "No Name!") == 0)) { // name already defined in BaseData mitk::StringProperty::Pointer baseDataNameProp = dynamic_cast(node->GetData()->GetProperty("name").GetPointer()); if (baseDataNameProp.IsNull() || (strcmp(baseDataNameProp->GetValue(), "No Name!") == 0)) { // name neither defined in node, nor in BaseData -> name = filename nameProp = mitk::StringProperty::New(itksys::SystemTools::GetFilenameWithoutExtension(filePath)); node->SetProperty("name", nameProp); } else { // name defined in BaseData! nameProp = mitk::StringProperty::New(baseDataNameProp->GetValue()); node->SetProperty("name", nameProp); } } // visibility if (!node->GetProperty("visible")) { node->SetVisibility(true); } } IOUtil::SaveInfo::SaveInfo(const BaseData *baseData, const MimeType &mimeType, const std::string &path) : m_BaseData(baseData), m_WriterSelector(baseData, mimeType.GetName(), path), m_MimeType(mimeType.IsValid() ? mimeType // use the original mime-type : (m_WriterSelector.IsEmpty() ? mimeType // no writer found, use the original invalid mime-type : m_WriterSelector.GetDefault().GetMimeType() // use the found default mime-type )), m_Path(path), m_Cancel(false) { } bool IOUtil::SaveInfo::operator<(const IOUtil::SaveInfo &other) const { int r = strcmp(m_BaseData->GetNameOfClass(), other.m_BaseData->GetNameOfClass()); if (r == 0) { return m_WriterSelector.GetSelected().GetMimeType() < other.m_WriterSelector.GetSelected().GetMimeType(); } return r < 0; } IOUtil::LoadInfo::LoadInfo(const std::string &path) : m_Path(path), m_ReaderSelector(path), m_Cancel(false) {} } diff --git a/Modules/Core/src/IO/mitkImageVtkLegacyIO.cpp b/Modules/Core/src/IO/mitkImageVtkLegacyIO.cpp index 294440b0f9..bf3a7a0bfd 100644 --- a/Modules/Core/src/IO/mitkImageVtkLegacyIO.cpp +++ b/Modules/Core/src/IO/mitkImageVtkLegacyIO.cpp @@ -1,117 +1,117 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkImageVtkLegacyIO.h" #include "mitkIOMimeTypes.h" #include "mitkImage.h" #include "mitkImageVtkReadAccessor.h" #include #include #include #include #include namespace mitk { ImageVtkLegacyIO::ImageVtkLegacyIO() : AbstractFileIO(Image::GetStaticNameOfClass(), IOMimeTypes::VTK_IMAGE_LEGACY_MIMETYPE(), "VTK Legacy Image") { Options defaultOptions; defaultOptions["Save as binary file"] = false; this->SetDefaultWriterOptions(defaultOptions); this->RegisterService(); } std::vector ImageVtkLegacyIO::Read() { // The legay vtk reader cannot work with input streams const std::string fileName = this->GetLocalFileName(); vtkSmartPointer reader = vtkSmartPointer::New(); reader->SetFileName(fileName.c_str()); reader->Update(); if (reader->GetOutput() != nullptr) { mitk::Image::Pointer output = mitk::Image::New(); output->Initialize(reader->GetOutput()); output->SetVolume(reader->GetOutput()->GetScalarPointer()); std::vector result; result.push_back(output.GetPointer()); return result; } else { mitkThrow() << "vtkStructuredPointsReader error: " << vtkErrorCode::GetStringFromErrorCode(reader->GetErrorCode()); } } IFileIO::ConfidenceLevel ImageVtkLegacyIO::GetReaderConfidenceLevel() const { if (AbstractFileIO::GetReaderConfidenceLevel() == Unsupported) return Unsupported; vtkSmartPointer reader = vtkSmartPointer::New(); reader->SetFileName(this->GetLocalFileName().c_str()); if (reader->IsFileStructuredPoints()) { return Supported; } return Unsupported; } void ImageVtkLegacyIO::Write() { ValidateOutputLocation(); - const Image *input = dynamic_cast(this->GetInput()); + const auto *input = dynamic_cast(this->GetInput()); vtkSmartPointer writer = vtkSmartPointer::New(); // The legacy vtk image writer cannot write to streams LocalFile localFile(this); writer->SetFileName(localFile.GetFileName().c_str()); if (us::any_cast(GetWriterOption("Save as binary file"))) { writer->SetFileTypeToBinary(); } ImageVtkReadAccessor vtkReadAccessor(Image::ConstPointer(input), nullptr, input->GetVtkImageData()); writer->SetInputData(const_cast(vtkReadAccessor.GetVtkImageData())); if (writer->Write() == 0 || writer->GetErrorCode() != 0) { mitkThrow() << "vtkStructuredPointesWriter error: " << vtkErrorCode::GetStringFromErrorCode(writer->GetErrorCode()); } } IFileIO::ConfidenceLevel ImageVtkLegacyIO::GetWriterConfidenceLevel() const { if (AbstractFileIO::GetWriterConfidenceLevel() == Unsupported) return Unsupported; - const Image *input = static_cast(this->GetInput()); + const auto *input = static_cast(this->GetInput()); if (input->GetDimension() == 3) return Supported; else if (input->GetDimension() < 3) return PartiallySupported; return Unsupported; } ImageVtkLegacyIO *ImageVtkLegacyIO::IOClone() const { return new ImageVtkLegacyIO(*this); } } diff --git a/Modules/Core/src/IO/mitkImageVtkXmlIO.cpp b/Modules/Core/src/IO/mitkImageVtkXmlIO.cpp index 9d9e6232fd..91cf5a161c 100644 --- a/Modules/Core/src/IO/mitkImageVtkXmlIO.cpp +++ b/Modules/Core/src/IO/mitkImageVtkXmlIO.cpp @@ -1,148 +1,148 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkImageVtkXmlIO.h" #include "mitkIOMimeTypes.h" #include "mitkImage.h" #include "mitkImageVtkReadAccessor.h" #include #include #include #include #include namespace mitk { class VtkXMLImageDataReader : public ::vtkXMLImageDataReader { public: static VtkXMLImageDataReader *New() { return new VtkXMLImageDataReader(); } vtkTypeMacro(VtkXMLImageDataReader, vtkXMLImageDataReader) void SetStream(std::istream *is) { this->Stream = is; } std::istream *GetStream() const { return this->Stream; } }; class VtkXMLImageDataWriter : public ::vtkXMLImageDataWriter { public: static VtkXMLImageDataWriter *New() { return new VtkXMLImageDataWriter(); } vtkTypeMacro(VtkXMLImageDataWriter, vtkXMLImageDataWriter) void SetStream(std::ostream *os) { this->Stream = os; } std::ostream *GetStream() const { return this->Stream; } }; ImageVtkXmlIO::ImageVtkXmlIO() : AbstractFileIO(Image::GetStaticNameOfClass(), IOMimeTypes::VTK_IMAGE_MIMETYPE(), "VTK XML Image") { this->RegisterService(); } std::vector ImageVtkXmlIO::Read() { vtkSmartPointer reader = vtkSmartPointer::New(); if (this->GetInputStream()) { reader->SetStream(this->GetInputStream()); } else { reader->SetFileName(this->GetInputLocation().c_str()); } reader->Update(); if (reader->GetOutput() != nullptr) { mitk::Image::Pointer output = mitk::Image::New(); output->Initialize(reader->GetOutput()); output->SetVolume(reader->GetOutput()->GetScalarPointer()); std::vector result; result.push_back(output.GetPointer()); return result; } else { mitkThrow() << "vtkXMLImageDataReader error: " << vtkErrorCode::GetStringFromErrorCode(reader->GetErrorCode()); } } IFileIO::ConfidenceLevel ImageVtkXmlIO::GetReaderConfidenceLevel() const { if (AbstractFileIO::GetReaderConfidenceLevel() == Unsupported) return Unsupported; if (this->GetInputStream() == nullptr) { // check if the xml vtk reader can handle the file vtkSmartPointer xmlReader = vtkSmartPointer::New(); if (xmlReader->CanReadFile(this->GetInputLocation().c_str()) != 0) { return Supported; } return Unsupported; } // in case of an input stream, VTK does not seem to have methods for // validating it return Supported; } void ImageVtkXmlIO::Write() { ValidateOutputLocation(); - const Image *input = dynamic_cast(this->GetInput()); + const auto *input = dynamic_cast(this->GetInput()); vtkSmartPointer writer = vtkSmartPointer::New(); if (this->GetOutputStream()) { writer->SetStream(this->GetOutputStream()); } else { writer->SetFileName(this->GetOutputLocation().c_str()); } ImageVtkReadAccessor vtkReadAccessor(Image::ConstPointer(input), nullptr, input->GetVtkImageData()); writer->SetInputData(const_cast(vtkReadAccessor.GetVtkImageData())); if (writer->Write() == 0 || writer->GetErrorCode() != 0) { mitkThrow() << "vtkXMLImageDataWriter error: " << vtkErrorCode::GetStringFromErrorCode(writer->GetErrorCode()); } } IFileIO::ConfidenceLevel ImageVtkXmlIO::GetWriterConfidenceLevel() const { if (AbstractFileIO::GetWriterConfidenceLevel() == Unsupported) return Unsupported; - const Image *input = static_cast(this->GetInput()); + const auto *input = static_cast(this->GetInput()); if (input->GetDimension() == 3) return Supported; else if (input->GetDimension() < 3) return PartiallySupported; return Unsupported; } ImageVtkXmlIO *ImageVtkXmlIO::IOClone() const { return new ImageVtkXmlIO(*this); } } diff --git a/Modules/Core/src/IO/mitkItkImageIO.cpp b/Modules/Core/src/IO/mitkItkImageIO.cpp index f20c45ea28..a8f927532c 100644 --- a/Modules/Core/src/IO/mitkItkImageIO.cpp +++ b/Modules/Core/src/IO/mitkItkImageIO.cpp @@ -1,671 +1,671 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkItkImageIO.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace mitk { const char *const PROPERTY_NAME_TIMEGEOMETRY_TYPE = "org.mitk.timegeometry.type"; const char *const PROPERTY_NAME_TIMEGEOMETRY_TIMEPOINTS = "org.mitk.timegeometry.timepoints"; const char *const PROPERTY_KEY_TIMEGEOMETRY_TYPE = "org_mitk_timegeometry_type"; const char *const PROPERTY_KEY_TIMEGEOMETRY_TIMEPOINTS = "org_mitk_timegeometry_timepoints"; ItkImageIO::ItkImageIO(const ItkImageIO &other) : AbstractFileIO(other), m_ImageIO(dynamic_cast(other.m_ImageIO->Clone().GetPointer())) { this->InitializeDefaultMetaDataKeys(); } std::vector ItkImageIO::FixUpImageIOExtensions(const std::string &imageIOName) { std::vector extensions; // Try to fix-up some known ITK image IO classes if (imageIOName == "GiplImageIO") { extensions.push_back("gipl"); extensions.push_back("gipl.gz"); } else if (imageIOName == "GDCMImageIO") { extensions.push_back("gdcm"); extensions.push_back("dcm"); extensions.push_back("DCM"); extensions.push_back("dc3"); extensions.push_back("DC3"); extensions.push_back("ima"); extensions.push_back("img"); } else if (imageIOName == "PNGImageIO") { extensions.push_back("png"); extensions.push_back("PNG"); } else if (imageIOName == "StimulateImageIO") { extensions.push_back("spr"); } else if (imageIOName == "HDF5ImageIO") { extensions.push_back("hdf"); extensions.push_back("h4"); extensions.push_back("hdf4"); extensions.push_back("h5"); extensions.push_back("hdf5"); extensions.push_back("he4"); extensions.push_back("he5"); extensions.push_back("hd5"); } else if (imageIOName == "GE4ImageIO" || imageIOName == "GE5ImageIO") { extensions.push_back(""); } if (!extensions.empty()) { MITK_DEBUG << "Fixing up known extensions for " << imageIOName; } return extensions; } ItkImageIO::ItkImageIO(itk::ImageIOBase::Pointer imageIO) : AbstractFileIO(Image::GetStaticNameOfClass()), m_ImageIO(imageIO) { if (m_ImageIO.IsNull()) { mitkThrow() << "ITK ImageIOBase argument must not be nullptr"; } this->AbstractFileReader::SetMimeTypePrefix(IOMimeTypes::DEFAULT_BASE_NAME() + ".image."); this->InitializeDefaultMetaDataKeys(); std::vector readExtensions = m_ImageIO->GetSupportedReadExtensions(); if (readExtensions.empty()) { std::string imageIOName = m_ImageIO->GetNameOfClass(); MITK_DEBUG << "ITK ImageIOBase " << imageIOName << " does not provide read extensions"; readExtensions = FixUpImageIOExtensions(imageIOName); } CustomMimeType customReaderMimeType; customReaderMimeType.SetCategory("Images"); for (std::vector::const_iterator iter = readExtensions.begin(), endIter = readExtensions.end(); iter != endIter; ++iter) { std::string extension = *iter; if (!extension.empty() && extension[0] == '.') { extension.assign(iter->begin() + 1, iter->end()); } customReaderMimeType.AddExtension(extension); } this->AbstractFileReader::SetMimeType(customReaderMimeType); std::vector writeExtensions = imageIO->GetSupportedWriteExtensions(); if (writeExtensions.empty()) { std::string imageIOName = imageIO->GetNameOfClass(); MITK_DEBUG << "ITK ImageIOBase " << imageIOName << " does not provide write extensions"; writeExtensions = FixUpImageIOExtensions(imageIOName); } if (writeExtensions != readExtensions) { CustomMimeType customWriterMimeType; customWriterMimeType.SetCategory("Images"); for (std::vector::const_iterator iter = writeExtensions.begin(), endIter = writeExtensions.end(); iter != endIter; ++iter) { std::string extension = *iter; if (!extension.empty() && extension[0] == '.') { extension.assign(iter->begin() + 1, iter->end()); } customWriterMimeType.AddExtension(extension); } this->AbstractFileWriter::SetMimeType(customWriterMimeType); } std::string description = std::string("ITK ") + imageIO->GetNameOfClass(); this->SetReaderDescription(description); this->SetWriterDescription(description); this->RegisterService(); } ItkImageIO::ItkImageIO(const CustomMimeType &mimeType, itk::ImageIOBase::Pointer imageIO, int rank) : AbstractFileIO(Image::GetStaticNameOfClass(), mimeType, std::string("ITK ") + imageIO->GetNameOfClass()), m_ImageIO(imageIO) { if (m_ImageIO.IsNull()) { mitkThrow() << "ITK ImageIOBase argument must not be nullptr"; } this->AbstractFileReader::SetMimeTypePrefix(IOMimeTypes::DEFAULT_BASE_NAME() + ".image."); this->InitializeDefaultMetaDataKeys(); if (rank) { this->AbstractFileReader::SetRanking(rank); this->AbstractFileWriter::SetRanking(rank); } this->RegisterService(); } /**Helper function that converts the content of a meta data into a time point vector. * If MetaData is not valid or cannot be converted an empty vector is returned.*/ std::vector ConvertMetaDataObjectToTimePointList(const itk::MetaDataObjectBase *data) { - const itk::MetaDataObject *timeGeometryTimeData = + const auto *timeGeometryTimeData = dynamic_cast *>(data); std::vector result; if (timeGeometryTimeData) { std::string dataStr = timeGeometryTimeData->GetMetaDataObjectValue(); std::stringstream stream(dataStr); TimePointType tp; while (stream >> tp) { result.push_back(tp); } } return result; }; std::vector ItkImageIO::Read() { std::vector result; mitk::LocaleSwitch localeSwitch("C"); Image::Pointer image = Image::New(); const unsigned int MINDIM = 2; const unsigned int MAXDIM = 4; const std::string path = this->GetLocalFileName(); MITK_INFO << "loading " << path << " via itk::ImageIOFactory... " << std::endl; // Check to see if we can read the file given the name or prefix if (path.empty()) { mitkThrow() << "Empty filename in mitk::ItkImageIO "; } // Got to allocate space for the image. Determine the characteristics of // the image. m_ImageIO->SetFileName(path); m_ImageIO->ReadImageInformation(); unsigned int ndim = m_ImageIO->GetNumberOfDimensions(); if (ndim < MINDIM || ndim > MAXDIM) { MITK_WARN << "Sorry, only dimensions 2, 3 and 4 are supported. The given file has " << ndim << " dimensions! Reading as 4D."; ndim = MAXDIM; } itk::ImageIORegion ioRegion(ndim); itk::ImageIORegion::SizeType ioSize = ioRegion.GetSize(); itk::ImageIORegion::IndexType ioStart = ioRegion.GetIndex(); unsigned int dimensions[MAXDIM]; dimensions[0] = 0; dimensions[1] = 0; dimensions[2] = 0; dimensions[3] = 0; ScalarType spacing[MAXDIM]; spacing[0] = 1.0f; spacing[1] = 1.0f; spacing[2] = 1.0f; spacing[3] = 1.0f; Point3D origin; origin.Fill(0); unsigned int i; for (i = 0; i < ndim; ++i) { ioStart[i] = 0; ioSize[i] = m_ImageIO->GetDimensions(i); if (i < MAXDIM) { dimensions[i] = m_ImageIO->GetDimensions(i); spacing[i] = m_ImageIO->GetSpacing(i); if (spacing[i] <= 0) spacing[i] = 1.0f; } if (i < 3) { origin[i] = m_ImageIO->GetOrigin(i); } } ioRegion.SetSize(ioSize); ioRegion.SetIndex(ioStart); MITK_INFO << "ioRegion: " << ioRegion << std::endl; m_ImageIO->SetIORegion(ioRegion); void *buffer = new unsigned char[m_ImageIO->GetImageSizeInBytes()]; m_ImageIO->Read(buffer); image->Initialize(MakePixelType(m_ImageIO), ndim, dimensions); image->SetImportChannel(buffer, 0, Image::ManageMemory); const itk::MetaDataDictionary &dictionary = m_ImageIO->GetMetaDataDictionary(); // access direction of itk::Image and include spacing mitk::Matrix3D matrix; matrix.SetIdentity(); unsigned int j, itkDimMax3 = (ndim >= 3 ? 3 : ndim); for (i = 0; i < itkDimMax3; ++i) for (j = 0; j < itkDimMax3; ++j) matrix[i][j] = m_ImageIO->GetDirection(j)[i]; // re-initialize PlaneGeometry with origin and direction PlaneGeometry *planeGeometry = image->GetSlicedGeometry(0)->GetPlaneGeometry(0); planeGeometry->SetOrigin(origin); planeGeometry->GetIndexToWorldTransform()->SetMatrix(matrix); // re-initialize SlicedGeometry3D SlicedGeometry3D *slicedGeometry = image->GetSlicedGeometry(0); slicedGeometry->InitializeEvenlySpaced(planeGeometry, image->GetDimension(2)); slicedGeometry->SetSpacing(spacing); MITK_INFO << slicedGeometry->GetCornerPoint(false, false, false); MITK_INFO << slicedGeometry->GetCornerPoint(true, true, true); // re-initialize TimeGeometry TimeGeometry::Pointer timeGeometry; if (dictionary.HasKey(PROPERTY_NAME_TIMEGEOMETRY_TYPE) || dictionary.HasKey(PROPERTY_KEY_TIMEGEOMETRY_TYPE)) { // also check for the name because of backwards compatibility. Past code version stored with the name and not with // the key itk::MetaDataObject::ConstPointer timeGeometryTypeData = nullptr; if (dictionary.HasKey(PROPERTY_NAME_TIMEGEOMETRY_TYPE)) { timeGeometryTypeData = dynamic_cast *>(dictionary.Get(PROPERTY_NAME_TIMEGEOMETRY_TYPE)); } else { timeGeometryTypeData = dynamic_cast *>(dictionary.Get(PROPERTY_KEY_TIMEGEOMETRY_TYPE)); } if (timeGeometryTypeData->GetMetaDataObjectValue() == ArbitraryTimeGeometry::GetStaticNameOfClass()) { MITK_INFO << "used time geometry: " << ArbitraryTimeGeometry::GetStaticNameOfClass() << std::endl; typedef std::vector TimePointVector; TimePointVector timePoints; if (dictionary.HasKey(PROPERTY_NAME_TIMEGEOMETRY_TIMEPOINTS)) { timePoints = ConvertMetaDataObjectToTimePointList(dictionary.Get(PROPERTY_NAME_TIMEGEOMETRY_TIMEPOINTS)); } else if (dictionary.HasKey(PROPERTY_KEY_TIMEGEOMETRY_TIMEPOINTS)) { timePoints = ConvertMetaDataObjectToTimePointList(dictionary.Get(PROPERTY_KEY_TIMEGEOMETRY_TIMEPOINTS)); } if (timePoints.size() - 1 != image->GetDimension(3)) { MITK_ERROR << "Stored timepoints (" << timePoints.size() - 1 << ") and size of image time dimension (" << image->GetDimension(3) << ") do not match. Switch to ProportionalTimeGeometry fallback" << std::endl; } else { ArbitraryTimeGeometry::Pointer arbitraryTimeGeometry = ArbitraryTimeGeometry::New(); TimePointVector::const_iterator pos = timePoints.begin(); - TimePointVector::const_iterator prePos = pos++; + auto prePos = pos++; for (; pos != timePoints.end(); ++prePos, ++pos) { arbitraryTimeGeometry->AppendNewTimeStepClone(slicedGeometry, *prePos, *pos); } timeGeometry = arbitraryTimeGeometry; } } } if (timeGeometry.IsNull()) { // Fallback. If no other valid time geometry has been created, create a ProportionalTimeGeometry MITK_INFO << "used time geometry: " << ProportionalTimeGeometry::GetStaticNameOfClass() << std::endl; ProportionalTimeGeometry::Pointer propTimeGeometry = ProportionalTimeGeometry::New(); propTimeGeometry->Initialize(slicedGeometry, image->GetDimension(3)); timeGeometry = propTimeGeometry; } image->SetTimeGeometry(timeGeometry); buffer = nullptr; MITK_INFO << "number of image components: " << image->GetPixelType().GetNumberOfComponents() << std::endl; - for (itk::MetaDataDictionary::ConstIterator iter = dictionary.Begin(), iterEnd = dictionary.End(); iter != iterEnd; + for (auto iter = dictionary.Begin(), iterEnd = dictionary.End(); iter != iterEnd; ++iter) { if (iter->second->GetMetaDataObjectTypeInfo() == typeid(std::string)) { const std::string &key = iter->first; std::string assumedPropertyName = key; std::replace(assumedPropertyName.begin(), assumedPropertyName.end(), '_', '.'); std::string mimeTypeName = GetMimeType()->GetName(); // Check if there is already a info for the key and our mime type. IPropertyPersistence::InfoResultType infoList = mitk::CoreServices::GetPropertyPersistence()->GetInfoByKey(key); auto predicate = [mimeTypeName](const PropertyPersistenceInfo::ConstPointer &x) { return x.IsNotNull() && x->GetMimeTypeName() == mimeTypeName; }; auto finding = std::find_if(infoList.begin(), infoList.end(), predicate); if (finding == infoList.end()) { auto predicateWild = [](const PropertyPersistenceInfo::ConstPointer &x) { return x.IsNotNull() && x->GetMimeTypeName() == PropertyPersistenceInfo::ANY_MIMETYPE_NAME(); }; finding = std::find_if(infoList.begin(), infoList.end(), predicateWild); } PropertyPersistenceInfo::ConstPointer info; if (finding != infoList.end()) { assumedPropertyName = (*finding)->GetName(); info = *finding; } else { // we have not found anything suitable so we generate our own info PropertyPersistenceInfo::Pointer newInfo = PropertyPersistenceInfo::New(); newInfo->SetNameAndKey(assumedPropertyName, key); newInfo->SetMimeTypeName(PropertyPersistenceInfo::ANY_MIMETYPE_NAME()); info = newInfo; } std::string value = dynamic_cast *>(iter->second.GetPointer())->GetMetaDataObjectValue(); mitk::BaseProperty::Pointer loadedProp = info->GetDeserializationFunction()(value); image->SetProperty(assumedPropertyName.c_str(), loadedProp); // Read properties should be persisted unless they are default properties // which are written anyway bool isDefaultKey(false); for (const auto &defaultKey : m_DefaultMetaDataKeys) { if (defaultKey.length() <= assumedPropertyName.length()) { // does the start match the default key if (assumedPropertyName.substr(0, defaultKey.length()).find(defaultKey) != std::string::npos) { isDefaultKey = true; break; } } } if (!isDefaultKey) { mitk::CoreServices::GetPropertyPersistence()->AddInfo(info); } } } MITK_INFO << "...finished!" << std::endl; result.push_back(image.GetPointer()); return result; } AbstractFileIO::ConfidenceLevel ItkImageIO::GetReaderConfidenceLevel() const { return m_ImageIO->CanReadFile(GetLocalFileName().c_str()) ? IFileReader::Supported : IFileReader::Unsupported; } void ItkImageIO::Write() { - const mitk::Image *image = dynamic_cast(this->GetInput()); + const auto *image = dynamic_cast(this->GetInput()); if (image == nullptr) { mitkThrow() << "Cannot write non-image data"; } // Switch the current locale to "C" LocaleSwitch localeSwitch("C"); // Clone the image geometry, because we might have to change it // for writing purposes BaseGeometry::Pointer geometry = image->GetGeometry()->Clone(); // Check if geometry information will be lost if (image->GetDimension() == 2 && !geometry->Is2DConvertable()) { MITK_WARN << "Saving a 2D image with 3D geometry information. Geometry information will be lost! You might " "consider using Convert2Dto3DImageFilter before saving."; // set matrix to identity mitk::AffineTransform3D::Pointer affTrans = mitk::AffineTransform3D::New(); affTrans->SetIdentity(); mitk::Vector3D spacing = geometry->GetSpacing(); mitk::Point3D origin = geometry->GetOrigin(); geometry->SetIndexToWorldTransform(affTrans); geometry->SetSpacing(spacing); geometry->SetOrigin(origin); } LocalFile localFile(this); const std::string path = localFile.GetFileName(); MITK_INFO << "Writing image: " << path << std::endl; try { // Implementation of writer using itkImageIO directly. This skips the use // of templated itkImageFileWriter, which saves the multiplexing on MITK side. const unsigned int dimension = image->GetDimension(); const unsigned int *const dimensions = image->GetDimensions(); const mitk::PixelType pixelType = image->GetPixelType(); const mitk::Vector3D mitkSpacing = geometry->GetSpacing(); const mitk::Point3D mitkOrigin = geometry->GetOrigin(); // Due to templating in itk, we are forced to save a 4D spacing and 4D Origin, // though they are not supported in MITK itk::Vector spacing4D; spacing4D[0] = mitkSpacing[0]; spacing4D[1] = mitkSpacing[1]; spacing4D[2] = mitkSpacing[2]; spacing4D[3] = 1; // There is no support for a 4D spacing. However, we should have a valid value here itk::Vector origin4D; origin4D[0] = mitkOrigin[0]; origin4D[1] = mitkOrigin[1]; origin4D[2] = mitkOrigin[2]; origin4D[3] = 0; // There is no support for a 4D origin. However, we should have a valid value here // Set the necessary information for imageIO m_ImageIO->SetNumberOfDimensions(dimension); m_ImageIO->SetPixelType(pixelType.GetPixelType()); m_ImageIO->SetComponentType(pixelType.GetComponentType() < PixelComponentUserType ? static_cast(pixelType.GetComponentType()) : itk::ImageIOBase::UNKNOWNCOMPONENTTYPE); m_ImageIO->SetNumberOfComponents(pixelType.GetNumberOfComponents()); itk::ImageIORegion ioRegion(dimension); for (unsigned int i = 0; i < dimension; i++) { m_ImageIO->SetDimensions(i, dimensions[i]); m_ImageIO->SetSpacing(i, spacing4D[i]); m_ImageIO->SetOrigin(i, origin4D[i]); mitk::Vector3D mitkDirection; mitkDirection.SetVnlVector(geometry->GetIndexToWorldTransform()->GetMatrix().GetVnlMatrix().get_column(i)); itk::Vector direction4D; direction4D[0] = mitkDirection[0]; direction4D[1] = mitkDirection[1]; direction4D[2] = mitkDirection[2]; // MITK only supports a 3x3 direction matrix. Due to templating in itk, however, we must // save a 4x4 matrix for 4D images. in this case, add an homogneous component to the matrix. if (i == 3) { direction4D[3] = 1; // homogenous component } else { direction4D[3] = 0; } vnl_vector axisDirection(dimension); for (unsigned int j = 0; j < dimension; j++) { axisDirection[j] = direction4D[j] / spacing4D[i]; } m_ImageIO->SetDirection(i, axisDirection); ioRegion.SetSize(i, image->GetLargestPossibleRegion().GetSize(i)); ioRegion.SetIndex(i, image->GetLargestPossibleRegion().GetIndex(i)); } // use compression if available m_ImageIO->UseCompressionOn(); m_ImageIO->SetIORegion(ioRegion); m_ImageIO->SetFileName(path); // Handle time geometry - const ArbitraryTimeGeometry *arbitraryTG = dynamic_cast(image->GetTimeGeometry()); + const auto *arbitraryTG = dynamic_cast(image->GetTimeGeometry()); if (arbitraryTG) { itk::EncapsulateMetaData(m_ImageIO->GetMetaDataDictionary(), PROPERTY_KEY_TIMEGEOMETRY_TYPE, ArbitraryTimeGeometry::GetStaticNameOfClass()); std::stringstream stream; stream << arbitraryTG->GetTimeBounds(0)[0]; for (TimeStepType pos = 0; pos < arbitraryTG->CountTimeSteps(); ++pos) { stream << " " << arbitraryTG->GetTimeBounds(pos)[1]; } std::string data = stream.str(); itk::EncapsulateMetaData( m_ImageIO->GetMetaDataDictionary(), PROPERTY_KEY_TIMEGEOMETRY_TIMEPOINTS, data); } // Handle properties mitk::PropertyList::Pointer imagePropertyList = image->GetPropertyList(); for (const auto &property : *imagePropertyList->GetMap()) { IPropertyPersistence::InfoResultType infoList = mitk::CoreServices::GetPropertyPersistence()->GetInfo(property.first, GetMimeType()->GetName(), true); if (infoList.empty()) { continue; } std::string value = infoList.front()->GetSerializationFunction()(property.second); if (value == mitk::BaseProperty::VALUE_CANNOT_BE_CONVERTED_TO_STRING) { continue; } std::string key = infoList.front()->GetKey(); itk::EncapsulateMetaData(m_ImageIO->GetMetaDataDictionary(), key, value); } ImageReadAccessor imageAccess(image); m_ImageIO->Write(imageAccess.GetData()); } catch (const std::exception &e) { mitkThrow() << e.what(); } } AbstractFileIO::ConfidenceLevel ItkImageIO::GetWriterConfidenceLevel() const { // Check if the image dimension is supported - const Image *image = dynamic_cast(this->GetInput()); + const auto *image = dynamic_cast(this->GetInput()); if (image == nullptr) { // We cannot write a null object, DUH! return IFileWriter::Unsupported; } if (!m_ImageIO->SupportsDimension(image->GetDimension())) { // okay, dimension is not supported. We have to look at a special case: // 3D-Image with one slice. We can treat that as a 2D image. if ((image->GetDimension() == 3) && (image->GetSlicedGeometry()->GetSlices() == 1)) return IFileWriter::Supported; else return IFileWriter::Unsupported; } // Check if geometry information will be lost if (image->GetDimension() == 2 && !image->GetGeometry()->Is2DConvertable()) { return IFileWriter::PartiallySupported; } return IFileWriter::Supported; } ItkImageIO *ItkImageIO::IOClone() const { return new ItkImageIO(*this); } void ItkImageIO::InitializeDefaultMetaDataKeys() { this->m_DefaultMetaDataKeys.push_back("NRRD.space"); this->m_DefaultMetaDataKeys.push_back("NRRD.kinds"); this->m_DefaultMetaDataKeys.push_back(PROPERTY_NAME_TIMEGEOMETRY_TYPE); this->m_DefaultMetaDataKeys.push_back(PROPERTY_NAME_TIMEGEOMETRY_TIMEPOINTS); this->m_DefaultMetaDataKeys.push_back("ITK.InputFilterName"); } } diff --git a/Modules/Core/src/IO/mitkLegacyFileReaderService.cpp b/Modules/Core/src/IO/mitkLegacyFileReaderService.cpp index 930322db50..dde4675791 100644 --- a/Modules/Core/src/IO/mitkLegacyFileReaderService.cpp +++ b/Modules/Core/src/IO/mitkLegacyFileReaderService.cpp @@ -1,118 +1,118 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkLegacyFileReaderService.h" #include #include #include #include #include mitk::LegacyFileReaderService::LegacyFileReaderService(const mitk::LegacyFileReaderService &other) : mitk::AbstractFileReader(other) { } mitk::LegacyFileReaderService::LegacyFileReaderService(const std::vector &extensions, const std::string &category) : AbstractFileReader() { this->SetMimeTypePrefix(IOMimeTypes::DEFAULT_BASE_NAME() + ".legacy."); CustomMimeType customMimeType; customMimeType.SetCategory(category); for (auto extension : extensions) { if (!extension.empty() && extension[0] == '.') { extension.assign(extension.begin() + 1, extension.end()); } customMimeType.AddExtension(extension); } this->SetDescription(category); this->SetMimeType(customMimeType); m_ServiceReg = this->RegisterService(); } mitk::LegacyFileReaderService::~LegacyFileReaderService() { } ////////////////////// Reading ///////////////////////// std::vector> mitk::LegacyFileReaderService::Read() { std::vector result; std::list possibleIOAdapter; std::list allobjects = itk::ObjectFactoryBase::CreateAllInstance("mitkIOAdapter"); - for (std::list::iterator i = allobjects.begin(); i != allobjects.end(); ++i) + for (auto i = allobjects.begin(); i != allobjects.end(); ++i) { - IOAdapterBase *io = dynamic_cast(i->GetPointer()); + auto *io = dynamic_cast(i->GetPointer()); if (io) { possibleIOAdapter.push_back(io); } else { MITK_ERROR << "Error BaseDataIO factory did not return an IOAdapterBase: " << (*i)->GetNameOfClass() << std::endl; } } const std::string path = this->GetLocalFileName(); - for (std::list::iterator k = possibleIOAdapter.begin(); k != possibleIOAdapter.end(); ++k) + for (auto k = possibleIOAdapter.begin(); k != possibleIOAdapter.end(); ++k) { bool canReadFile = (*k)->CanReadFile(path, "", ""); // they could read the file if (canReadFile) { BaseDataSource::Pointer ioObject = (*k)->CreateIOProcessObject(path, "", ""); ioObject->Update(); - int numberOfContents = static_cast(ioObject->GetNumberOfOutputs()); + auto numberOfContents = static_cast(ioObject->GetNumberOfOutputs()); if (numberOfContents > 0) { BaseData::Pointer baseData; for (int i = 0; i < numberOfContents; ++i) { baseData = dynamic_cast(ioObject->GetOutputs()[i].GetPointer()); if (baseData) // this is what's wanted, right? { result.push_back(baseData); } } } break; } } if (result.empty()) { mitkThrow() << "Could not read file '" << path << "'"; } return result; } mitk::LegacyFileReaderService *mitk::LegacyFileReaderService::Clone() const { return new LegacyFileReaderService(*this); } diff --git a/Modules/Core/src/IO/mitkLegacyFileWriterService.cpp b/Modules/Core/src/IO/mitkLegacyFileWriterService.cpp index 1edf507a38..8c7ff85c0f 100644 --- a/Modules/Core/src/IO/mitkLegacyFileWriterService.cpp +++ b/Modules/Core/src/IO/mitkLegacyFileWriterService.cpp @@ -1,88 +1,88 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkLegacyFileWriterService.h" #include #include #include mitk::LegacyFileWriterService::LegacyFileWriterService(mitk::FileWriter::Pointer legacyWriter, const std::string &description) : AbstractFileWriter(legacyWriter->GetSupportedBaseData()), m_LegacyWriter(legacyWriter) { this->SetMimeTypePrefix(IOMimeTypes::DEFAULT_BASE_NAME() + ".legacy."); this->SetDescription(description); CustomMimeType customMimeType; std::vector extensions = legacyWriter->GetPossibleFileExtensions(); - for (std::vector::iterator ext = extensions.begin(); ext != extensions.end(); ++ext) + for (auto ext = extensions.begin(); ext != extensions.end(); ++ext) { if (ext->empty()) continue; std::string extension = *ext; if (extension.size() > 1 && extension[0] == '*') { // remove "*" extension = extension.substr(1); } if (extension.size() > 1 && extension[0] == '.') { // remove "." extension = extension.substr(1); } std::transform(extension.begin(), extension.end(), extension.begin(), ::tolower); customMimeType.AddExtension(extension); } this->SetMimeType(customMimeType); m_ServiceRegistration = RegisterService(); } mitk::LegacyFileWriterService::~LegacyFileWriterService() { } ////////////////////// Writing ///////////////////////// void mitk::LegacyFileWriterService::Write() { if (m_LegacyWriter.IsNull()) mitkThrow() << "LegacyFileWriterService was incorrectly initialized: Has no LegacyFileWriter."; ValidateOutputLocation(); LocalFile localFile(this); m_LegacyWriter->SetFileName(localFile.GetFileName().c_str()); m_LegacyWriter->SetInput(const_cast(this->GetInput())); m_LegacyWriter->Write(); } mitk::IFileWriter::ConfidenceLevel mitk::LegacyFileWriterService::GetConfidenceLevel() const { if (mitk::AbstractFileWriter::GetConfidenceLevel() == Unsupported) return Unsupported; DataNode::Pointer node = DataNode::New(); node->SetData(const_cast(this->GetInput())); return m_LegacyWriter->CanWriteDataType(node) ? Supported : Unsupported; } mitk::LegacyFileWriterService *mitk::LegacyFileWriterService::Clone() const { return new LegacyFileWriterService(*this); } diff --git a/Modules/Core/src/IO/mitkMimeTypeProvider.cpp b/Modules/Core/src/IO/mitkMimeTypeProvider.cpp index 866b3323ed..379148a8dc 100644 --- a/Modules/Core/src/IO/mitkMimeTypeProvider.cpp +++ b/Modules/Core/src/IO/mitkMimeTypeProvider.cpp @@ -1,171 +1,171 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkMimeTypeProvider.h" #include "mitkLogMacros.h" #include #include #include #ifdef _MSC_VER #pragma warning(disable : 4503) // decorated name length exceeded, name was truncated #pragma warning(disable : 4355) #endif namespace mitk { MimeTypeProvider::MimeTypeProvider() : m_Tracker(nullptr) {} MimeTypeProvider::~MimeTypeProvider() { delete m_Tracker; } void MimeTypeProvider::Start() { if (m_Tracker == nullptr) { m_Tracker = new us::ServiceTracker(us::GetModuleContext(), this); } m_Tracker->Open(); } void MimeTypeProvider::Stop() { m_Tracker->Close(); } std::vector MimeTypeProvider::GetMimeTypes() const { std::vector result; for (const auto &elem : m_NameToMimeType) { result.push_back(elem.second); } return result; } std::vector MimeTypeProvider::GetMimeTypesForFile(const std::string &filePath) const { std::vector result; for (const auto &elem : m_NameToMimeType) { if (elem.second.AppliesTo(filePath)) { result.push_back(elem.second); } } std::sort(result.begin(), result.end()); std::reverse(result.begin(), result.end()); return result; } std::vector MimeTypeProvider::GetMimeTypesForCategory(const std::string &category) const { std::vector result; for (const auto &elem : m_NameToMimeType) { if (elem.second.GetCategory() == category) { result.push_back(elem.second); } } return result; } MimeType MimeTypeProvider::GetMimeTypeForName(const std::string &name) const { - std::map::const_iterator iter = m_NameToMimeType.find(name); + auto iter = m_NameToMimeType.find(name); if (iter != m_NameToMimeType.end()) return iter->second; return MimeType(); } std::vector MimeTypeProvider::GetCategories() const { std::vector result; for (const auto &elem : m_NameToMimeType) { std::string category = elem.second.GetCategory(); if (!category.empty()) { result.push_back(category); } } std::sort(result.begin(), result.end()); result.erase(std::unique(result.begin(), result.end()), result.end()); return result; } MimeTypeProvider::TrackedType MimeTypeProvider::AddingService(const ServiceReferenceType &reference) { MimeType result = this->GetMimeType(reference); if (result.IsValid()) { std::string name = result.GetName(); m_NameToMimeTypes[name].insert(result); // get the highest ranked mime-type m_NameToMimeType[name] = *(m_NameToMimeTypes[name].rbegin()); } return result; } void MimeTypeProvider::ModifiedService(const ServiceReferenceType & /*reference*/, TrackedType /*mimetype*/) { // should we track changes in the ranking property? } void MimeTypeProvider::RemovedService(const ServiceReferenceType & /*reference*/, TrackedType mimeType) { std::string name = mimeType.GetName(); std::set &mimeTypes = m_NameToMimeTypes[name]; mimeTypes.erase(mimeType); if (mimeTypes.empty()) { m_NameToMimeTypes.erase(name); m_NameToMimeType.erase(name); } else { // get the highest ranked mime-type m_NameToMimeType[name] = *(mimeTypes.rbegin()); } } MimeType MimeTypeProvider::GetMimeType(const ServiceReferenceType &reference) const { MimeType result; if (!reference) return result; CustomMimeType *mimeType = us::GetModuleContext()->GetService(reference); if (mimeType != nullptr) { try { int rank = 0; us::Any rankProp = reference.GetProperty(us::ServiceConstants::SERVICE_RANKING()); if (!rankProp.Empty()) { rank = us::any_cast(rankProp); } - long id = us::any_cast(reference.GetProperty(us::ServiceConstants::SERVICE_ID())); + auto id = us::any_cast(reference.GetProperty(us::ServiceConstants::SERVICE_ID())); result = MimeType(*mimeType, rank, id); } catch (const us::BadAnyCastException &e) { MITK_WARN << "Unexpected exception: " << e.what(); } us::GetModuleContext()->UngetService(reference); } return result; } } diff --git a/Modules/Core/src/IO/mitkPointSetReaderService.cpp b/Modules/Core/src/IO/mitkPointSetReaderService.cpp index f10d962ee5..07a47cd3f6 100644 --- a/Modules/Core/src/IO/mitkPointSetReaderService.cpp +++ b/Modules/Core/src/IO/mitkPointSetReaderService.cpp @@ -1,279 +1,279 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ // MITK #include "mitkPointSetReaderService.h" #include "mitkGeometry3DToXML.h" #include "mitkIOMimeTypes.h" #include "mitkProportionalTimeGeometry.h" // STL #include #include #include #include mitk::PointSetReaderService::PointSetReaderService() : AbstractFileReader(CustomMimeType(IOMimeTypes::POINTSET_MIMETYPE()), "MITK Point Set Reader") { RegisterService(); } mitk::PointSetReaderService::~PointSetReaderService() { } std::vector> mitk::PointSetReaderService::Read() { // Switch the current locale to "C" LocaleSwitch localeSwitch("C"); std::vector> result; InputStream stream(this); TiXmlDocument doc; stream >> doc; if (!doc.Error()) { TiXmlHandle docHandle(&doc); // unsigned int pointSetCounter(0); for (TiXmlElement *currentPointSetElement = docHandle.FirstChildElement("point_set_file").FirstChildElement("point_set").ToElement(); currentPointSetElement != nullptr; currentPointSetElement = currentPointSetElement->NextSiblingElement()) { mitk::PointSet::Pointer newPointSet = mitk::PointSet::New(); // time geometry assembled for addition after all points // else the SetPoint method would already transform the points that we provide it mitk::ProportionalTimeGeometry::Pointer timeGeometry = mitk::ProportionalTimeGeometry::New(); if (currentPointSetElement->FirstChildElement("time_series") != nullptr) { for (TiXmlElement *currentTimeSeries = currentPointSetElement->FirstChildElement("time_series")->ToElement(); currentTimeSeries != nullptr; currentTimeSeries = currentTimeSeries->NextSiblingElement()) { unsigned int currentTimeStep(0); TiXmlElement *currentTimeSeriesID = currentTimeSeries->FirstChildElement("time_series_id"); currentTimeStep = atoi(currentTimeSeriesID->GetText()); timeGeometry->Expand(currentTimeStep + 1); // expand (default to identity) in any case TiXmlElement *geometryElem = currentTimeSeries->FirstChildElement("Geometry3D"); if (geometryElem) { Geometry3D::Pointer geometry = Geometry3DToXML::FromXML(geometryElem); if (geometry.IsNotNull()) { timeGeometry->SetTimeStepGeometry(geometry, currentTimeStep); } else { MITK_ERROR << "Could not deserialize Geometry3D element."; } } else { MITK_WARN << "Fallback to legacy behavior: defining PointSet geometry as identity"; } newPointSet = this->ReadPoints(newPointSet, currentTimeSeries, currentTimeStep); } } else { newPointSet = this->ReadPoints(newPointSet, currentPointSetElement, 0); } newPointSet->SetTimeGeometry(timeGeometry); result.push_back(newPointSet.GetPointer()); } } else { mitkThrow() << "Parsing error at line " << doc.ErrorRow() << ", col " << doc.ErrorCol() << ": " << doc.ErrorDesc(); } return result; } mitk::BaseGeometry::Pointer mitk::PointSetReaderService::ReadGeometry(TiXmlElement *parentElement) { TiXmlElement *geometryElem = parentElement->FirstChildElement("geometry3d"); if (!geometryElem) return nullptr; // data to generate AffineTransform3D::MatrixType matrix; AffineTransform3D::OffsetType offset; bool isImageGeometry(false); unsigned int frameOfReferenceID(0); BaseGeometry::BoundsArrayType bounds; bool somethingMissing(false); // find data in xml structure TiXmlElement *imageGeometryElem = geometryElem->FirstChildElement("image_geometry"); if (imageGeometryElem) { std::string igs = imageGeometryElem->GetText(); isImageGeometry = igs == "true" || igs == "TRUE" || igs == "1"; } else somethingMissing = true; TiXmlElement *frameOfReferenceElem = geometryElem->FirstChildElement("frame_of_reference_id"); if (frameOfReferenceElem) { frameOfReferenceID = atoi(frameOfReferenceElem->GetText()); } else somethingMissing = true; TiXmlElement *indexToWorldElem = geometryElem->FirstChildElement("index_to_world"); if (indexToWorldElem) { TiXmlElement *matrixElem = indexToWorldElem->FirstChildElement("matrix3x3"); TiXmlElement *offsetElem = indexToWorldElem->FirstChildElement("offset"); if (indexToWorldElem && offsetElem) { TiXmlElement *col0 = matrixElem->FirstChildElement("column_0"); TiXmlElement *col1 = matrixElem->FirstChildElement("column_1"); TiXmlElement *col2 = matrixElem->FirstChildElement("column_2"); if (col0 && col1 && col2) { somethingMissing |= TIXML_SUCCESS != col0->QueryDoubleAttribute("x", &matrix[0][0]); somethingMissing |= TIXML_SUCCESS != col0->QueryDoubleAttribute("y", &matrix[1][0]); somethingMissing |= TIXML_SUCCESS != col0->QueryDoubleAttribute("z", &matrix[2][0]); somethingMissing |= TIXML_SUCCESS != col1->QueryDoubleAttribute("x", &matrix[0][1]); somethingMissing |= TIXML_SUCCESS != col1->QueryDoubleAttribute("y", &matrix[1][1]); somethingMissing |= TIXML_SUCCESS != col1->QueryDoubleAttribute("z", &matrix[2][1]); somethingMissing |= TIXML_SUCCESS != col2->QueryDoubleAttribute("x", &matrix[0][2]); somethingMissing |= TIXML_SUCCESS != col2->QueryDoubleAttribute("y", &matrix[1][2]); somethingMissing |= TIXML_SUCCESS != col2->QueryDoubleAttribute("z", &matrix[2][2]); } else somethingMissing = true; somethingMissing |= TIXML_SUCCESS != offsetElem->QueryDoubleAttribute("x", &offset[0]); somethingMissing |= TIXML_SUCCESS != offsetElem->QueryDoubleAttribute("y", &offset[1]); somethingMissing |= TIXML_SUCCESS != offsetElem->QueryDoubleAttribute("z", &offset[2]); } else somethingMissing = true; TiXmlElement *boundsElem = geometryElem->FirstChildElement("bounds"); if (boundsElem) { TiXmlElement *minBoundsElem = boundsElem->FirstChildElement("min"); TiXmlElement *maxBoundsElem = boundsElem->FirstChildElement("max"); if (minBoundsElem && maxBoundsElem) { somethingMissing |= TIXML_SUCCESS != minBoundsElem->QueryDoubleAttribute("x", &bounds[0]); somethingMissing |= TIXML_SUCCESS != minBoundsElem->QueryDoubleAttribute("y", &bounds[2]); somethingMissing |= TIXML_SUCCESS != minBoundsElem->QueryDoubleAttribute("z", &bounds[4]); somethingMissing |= TIXML_SUCCESS != maxBoundsElem->QueryDoubleAttribute("x", &bounds[1]); somethingMissing |= TIXML_SUCCESS != maxBoundsElem->QueryDoubleAttribute("y", &bounds[3]); somethingMissing |= TIXML_SUCCESS != maxBoundsElem->QueryDoubleAttribute("z", &bounds[5]); } else somethingMissing = true; } else somethingMissing = true; } else somethingMissing = true; if (somethingMissing) { MITK_ERROR << "XML structure of geometry inside a PointSet file broken. Refusing to build Geometry3D"; return nullptr; } else { Geometry3D::Pointer g = Geometry3D::New(); g->SetImageGeometry(isImageGeometry); g->SetFrameOfReferenceID(frameOfReferenceID); g->SetBounds(bounds); AffineTransform3D::Pointer transform = AffineTransform3D::New(); transform->SetMatrix(matrix); transform->SetOffset(offset); g->SetIndexToWorldTransform(transform); return g.GetPointer(); } } mitk::PointSet::Pointer mitk::PointSetReaderService::ReadPoints(mitk::PointSet::Pointer newPointSet, TiXmlElement *currentTimeSeries, unsigned int currentTimeStep) { if (currentTimeSeries->FirstChildElement("point") != nullptr) { for (TiXmlElement *currentPoint = currentTimeSeries->FirstChildElement("point")->ToElement(); currentPoint != nullptr; currentPoint = currentPoint->NextSiblingElement()) { unsigned int id(0); - mitk::PointSpecificationType spec((mitk::PointSpecificationType)0); + auto spec((mitk::PointSpecificationType)0); double x(0.0); double y(0.0); double z(0.0); id = atoi(currentPoint->FirstChildElement("id")->GetText()); if (currentPoint->FirstChildElement("specification") != nullptr) { spec = (mitk::PointSpecificationType)atoi(currentPoint->FirstChildElement("specification")->GetText()); } x = atof(currentPoint->FirstChildElement("x")->GetText()); y = atof(currentPoint->FirstChildElement("y")->GetText()); z = atof(currentPoint->FirstChildElement("z")->GetText()); mitk::Point3D point; mitk::FillVector3D(point, x, y, z); newPointSet->SetPoint(id, point, spec, currentTimeStep); } } else { if (currentTimeStep != newPointSet->GetTimeSteps() + 1) { newPointSet->Expand(currentTimeStep + 1); // expand time step series with empty time step } } return newPointSet; } mitk::PointSetReaderService::PointSetReaderService(const mitk::PointSetReaderService &other) : mitk::AbstractFileReader(other) { } mitk::PointSetReaderService *mitk::PointSetReaderService::Clone() const { return new mitk::PointSetReaderService(*this); } diff --git a/Modules/Core/src/IO/mitkPointSetWriterService.cpp b/Modules/Core/src/IO/mitkPointSetWriterService.cpp index 3acd9a6361..888170b4ff 100644 --- a/Modules/Core/src/IO/mitkPointSetWriterService.cpp +++ b/Modules/Core/src/IO/mitkPointSetWriterService.cpp @@ -1,186 +1,186 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkPointSetWriterService.h" #include "mitkGeometry3DToXML.h" #include "mitkIOMimeTypes.h" #include "mitkLocaleSwitch.h" #include "mitkGeometry3D.h" #include #include #include #include // // Initialization of the xml tags. // const std::string mitk::PointSetWriterService::XML_POINT_SET_FILE = "point_set_file"; const std::string mitk::PointSetWriterService::XML_FILE_VERSION = "file_version"; const std::string mitk::PointSetWriterService::XML_POINT_SET = "point_set"; const std::string mitk::PointSetWriterService::XML_TIME_SERIES = "time_series"; const std::string mitk::PointSetWriterService::XML_TIME_SERIES_ID = "time_series_id"; const std::string mitk::PointSetWriterService::XML_POINT = "point"; const std::string mitk::PointSetWriterService::XML_ID = "id"; const std::string mitk::PointSetWriterService::XML_SPEC = "specification"; const std::string mitk::PointSetWriterService::XML_X = "x"; const std::string mitk::PointSetWriterService::XML_Y = "y"; const std::string mitk::PointSetWriterService::XML_Z = "z"; const std::string mitk::PointSetWriterService::VERSION_STRING = "0.1"; mitk::PointSetWriterService::PointSetWriterService() : AbstractFileWriter( PointSet::GetStaticNameOfClass(), CustomMimeType(IOMimeTypes::POINTSET_MIMETYPE()), "MITK Point Set Writer") { RegisterService(); } mitk::PointSetWriterService::PointSetWriterService(const mitk::PointSetWriterService &other) : AbstractFileWriter(other) { } mitk::PointSetWriterService::~PointSetWriterService() { } void mitk::PointSetWriterService::Write() { mitk::LocaleSwitch localeC("C"); TiXmlDocument doc; - TiXmlDeclaration *decl = new TiXmlDeclaration( + auto *decl = new TiXmlDeclaration( "1.0", "UTF-8", ""); // TODO what to write here? encoding? standalone would mean that we provide a DTD somewhere... doc.LinkEndChild(decl); - TiXmlElement *rootNode = new TiXmlElement(XML_POINT_SET_FILE); + auto *rootNode = new TiXmlElement(XML_POINT_SET_FILE); doc.LinkEndChild(rootNode); - TiXmlElement *versionNode = new TiXmlElement(XML_FILE_VERSION); - TiXmlText *versionText = new TiXmlText(VERSION_STRING); + auto *versionNode = new TiXmlElement(XML_FILE_VERSION); + auto *versionText = new TiXmlText(VERSION_STRING); versionNode->LinkEndChild(versionText); rootNode->LinkEndChild(versionNode); TiXmlElement *pointSetNode = ToXML(static_cast(this->GetInput())); if (!pointSetNode) { mitkThrow() << "Serialization error during PointSet writing."; } rootNode->LinkEndChild(pointSetNode); // out << doc; // streaming of TinyXML write no new-lines, // rendering XML files unreadable (for humans) LocalFile f(this); if (!doc.SaveFile(f.GetFileName())) { mitkThrow() << "Some error during point set writing."; } } mitk::PointSetWriterService *mitk::PointSetWriterService::Clone() const { return new PointSetWriterService(*this); } TiXmlElement *mitk::PointSetWriterService::ToXML(const mitk::PointSet *pointSet) { // the following is rather bloated and could be expressed in more compact XML // (e.g. using attributes instead of tags for x/y/z). The current format is // kept to be compatible with the previous writer. - TiXmlElement *pointSetElement = new TiXmlElement(XML_POINT_SET); + auto *pointSetElement = new TiXmlElement(XML_POINT_SET); unsigned int timecount = pointSet->GetTimeSteps(); for (unsigned int i = 0; i < timecount; i++) { - TiXmlElement *timeSeriesElement = new TiXmlElement(XML_TIME_SERIES); + auto *timeSeriesElement = new TiXmlElement(XML_TIME_SERIES); pointSetElement->LinkEndChild(timeSeriesElement); - TiXmlElement *timeSeriesIDElement = new TiXmlElement(XML_TIME_SERIES_ID); + auto *timeSeriesIDElement = new TiXmlElement(XML_TIME_SERIES_ID); timeSeriesElement->LinkEndChild(timeSeriesIDElement); TiXmlText *timeSeriesIDText = new TiXmlText(ConvertToString(i)); timeSeriesIDElement->LinkEndChild(timeSeriesIDText); PointSet::PointsContainer *pointsContainer = pointSet->GetPointSet(i)->GetPoints(); PointSet::PointsContainer::Iterator it; - Geometry3D *geometry = dynamic_cast(pointSet->GetGeometry(i)); + auto *geometry = dynamic_cast(pointSet->GetGeometry(i)); if (geometry == nullptr) { MITK_WARN << "Writing a PointSet with something other that a Geometry3D. This is not foreseen and not handled."; // we'll continue anyway, this imitates previous behavior } else { TiXmlElement *geometryElement = Geometry3DToXML::ToXML(geometry); timeSeriesElement->LinkEndChild(geometryElement); } for (it = pointsContainer->Begin(); it != pointsContainer->End(); ++it) { - TiXmlElement *pointElement = new TiXmlElement(XML_POINT); + auto *pointElement = new TiXmlElement(XML_POINT); timeSeriesElement->LinkEndChild(pointElement); - TiXmlElement *pointIDElement = new TiXmlElement(XML_ID); + auto *pointIDElement = new TiXmlElement(XML_ID); TiXmlText *pointIDText = new TiXmlText(ConvertToString(it->Index())); pointIDElement->LinkEndChild(pointIDText); pointElement->LinkEndChild(pointIDElement); mitk::PointSet::PointType point = it->Value(); - TiXmlElement *pointSpecElement = new TiXmlElement(XML_SPEC); + auto *pointSpecElement = new TiXmlElement(XML_SPEC); TiXmlText *pointSpecText = new TiXmlText(ConvertToString(pointSet->GetSpecificationTypeInfo(it->Index(), i))); pointSpecElement->LinkEndChild(pointSpecText); pointElement->LinkEndChild(pointSpecElement); - TiXmlElement *pointXElement = new TiXmlElement(XML_X); + auto *pointXElement = new TiXmlElement(XML_X); TiXmlText *pointXText = new TiXmlText(ConvertToString(point[0])); pointXElement->LinkEndChild(pointXText); pointElement->LinkEndChild(pointXElement); - TiXmlElement *pointYElement = new TiXmlElement(XML_Y); + auto *pointYElement = new TiXmlElement(XML_Y); TiXmlText *pointYText = new TiXmlText(ConvertToString(point[1])); pointYElement->LinkEndChild(pointYText); pointElement->LinkEndChild(pointYElement); - TiXmlElement *pointZElement = new TiXmlElement(XML_Z); + auto *pointZElement = new TiXmlElement(XML_Z); TiXmlText *pointZText = new TiXmlText(ConvertToString(point[2])); pointZElement->LinkEndChild(pointZText); pointElement->LinkEndChild(pointZElement); } } return pointSetElement; } template std::string mitk::PointSetWriterService::ConvertToString(T value) { std::ostringstream o; std::locale I("C"); o.imbue(I); if (o << std::setprecision(12) << value) { return o.str(); } else { return "conversion error"; } } \ No newline at end of file diff --git a/Modules/Core/src/IO/mitkProportionalTimeGeometryToXML.cpp b/Modules/Core/src/IO/mitkProportionalTimeGeometryToXML.cpp index 65db5367f6..58594fe397 100644 --- a/Modules/Core/src/IO/mitkProportionalTimeGeometryToXML.cpp +++ b/Modules/Core/src/IO/mitkProportionalTimeGeometryToXML.cpp @@ -1,166 +1,166 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkProportionalTimeGeometryToXML.h" #include "mitkGeometry3DToXML.h" #include #include TiXmlElement *mitk::ProportionalTimeGeometryToXML::ToXML(const ProportionalTimeGeometry *timeGeom) { assert(timeGeom); - TiXmlElement *timeGeomElem = new TiXmlElement("ProportionalTimeGeometry"); + auto *timeGeomElem = new TiXmlElement("ProportionalTimeGeometry"); timeGeomElem->SetAttribute("NumberOfTimeSteps", timeGeom->CountTimeSteps()); // TinyXML cannot serialize infinity (default value for time step) // So we guard this value and the first time point against serialization problems // by not writing them. The reader can then tell that absence of those values // means "keep the default values" if (timeGeom->GetFirstTimePoint() != -std::numeric_limits::max()) timeGeomElem->SetAttribute("FirstTimePoint", boost::lexical_cast(timeGeom->GetFirstTimePoint())); if (timeGeom->GetStepDuration() != std::numeric_limits::infinity()) timeGeomElem->SetAttribute("StepDuration", boost::lexical_cast(timeGeom->GetStepDuration())); for (TimeStepType t = 0; t < timeGeom->CountTimeSteps(); ++t) { // add a node for the geometry of each time step const Geometry3D *geom3D(nullptr); if ((geom3D = dynamic_cast(timeGeom->GetGeometryForTimeStep(t).GetPointer()))) { TiXmlElement *geom3DElement = Geometry3DToXML::ToXML(geom3D); geom3DElement->SetAttribute("TimeStep", t); // mark order for us timeGeomElem->LinkEndChild(geom3DElement); } else { MITK_WARN << "Serializing a ProportionalTimeGeometry that contains something other than Geometry3D!" << " (in time step " << t << ")" << " File will miss information!"; } } return timeGeomElem; } mitk::ProportionalTimeGeometry::Pointer mitk::ProportionalTimeGeometryToXML::FromXML(TiXmlElement *timeGeometryElement) { if (!timeGeometryElement) { MITK_ERROR << "Cannot deserialize ProportionalTimeGeometry from nullptr."; return nullptr; } int numberOfTimeSteps = 0; if (TIXML_SUCCESS != timeGeometryElement->QueryIntAttribute("NumberOfTimeSteps", &numberOfTimeSteps)) { MITK_WARN << " found without NumberOfTimeSteps attribute. Counting..."; } // might be missing! TimePointType firstTimePoint; std::string firstTimePoint_s; TimePointType stepDuration; std::string stepDuration_s; try { if (TIXML_SUCCESS == timeGeometryElement->QueryStringAttribute("FirstTimePoint", &firstTimePoint_s)) { firstTimePoint = boost::lexical_cast(firstTimePoint_s); } else { firstTimePoint = -std::numeric_limits::max(); } if (TIXML_SUCCESS == timeGeometryElement->QueryStringAttribute("StepDuration", &stepDuration_s)) { stepDuration = boost::lexical_cast(stepDuration_s); } else { stepDuration = std::numeric_limits::infinity(); } } catch (boost::bad_lexical_cast &e) { MITK_ERROR << "Could not parse string as number: " << e.what(); return nullptr; } // list of all geometries with their time steps std::multimap allReadGeometries; int indexForUnlabeledTimeStep(-1); for (TiXmlElement *currentElement = timeGeometryElement->FirstChildElement(); currentElement != nullptr; currentElement = currentElement->NextSiblingElement()) { // different geometries could have been inside a ProportionalTimeGeometry. // By now, we only support Geometry3D std::string tagName = currentElement->Value(); if (tagName == "Geometry3D") { Geometry3D::Pointer restoredGeometry = Geometry3DToXML::FromXML(currentElement); if (restoredGeometry.IsNotNull()) { int timeStep(-1); if (TIXML_SUCCESS != currentElement->QueryIntAttribute("TimeStep", &timeStep)) { timeStep = indexForUnlabeledTimeStep--; // decrement index for next one MITK_WARN << "Found without 'TimeStep' attribute in . No guarantees " "on order anymore."; } if (allReadGeometries.count(static_cast(timeStep)) > 0) { MITK_WARN << "Found tags with identical 'TimeStep' attribute in . No " "guarantees on order anymore."; } allReadGeometries.insert(std::make_pair(static_cast(timeStep), restoredGeometry.GetPointer())); } } else { MITK_WARN << "Found unsupported tag <" << tagName << "> inside . Ignoring."; } } // now add all BaseGeometries that were read to a new instance // of ProportionalTimeGeometry ProportionalTimeGeometry::Pointer newTimeGeometry = ProportionalTimeGeometry::New(); newTimeGeometry->SetFirstTimePoint(firstTimePoint); newTimeGeometry->SetStepDuration(stepDuration); newTimeGeometry->ReserveSpaceForGeometries(allReadGeometries.size()); TimeStepType t(0); for (auto entry : allReadGeometries) { // We add items with newly assigned time steps. // This avoids great confusion when a file contains // bogus numbers. newTimeGeometry->SetTimeStepGeometry(entry.second, t++); } // Need to re-calculate global bounding box. // This is neither stored in a file, nor done by SetTimeStepGeometry newTimeGeometry->UpdateBoundingBox(); return newTimeGeometry; } diff --git a/Modules/Core/src/IO/mitkSurfaceStlIO.cpp b/Modules/Core/src/IO/mitkSurfaceStlIO.cpp index ce60138563..ee94362ebb 100644 --- a/Modules/Core/src/IO/mitkSurfaceStlIO.cpp +++ b/Modules/Core/src/IO/mitkSurfaceStlIO.cpp @@ -1,164 +1,164 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkSurfaceStlIO.h" #include "mitkIOMimeTypes.h" #include "mitkLocaleSwitch.h" #include "mitkSurface.h" #include #include #include #include #include #include #include namespace mitk { std::string SurfaceStlIO::OPTION_MERGE_POINTS() { static std::string s = "Merge points"; return s; } std::string SurfaceStlIO::OPTION_TAG_SOLIDS() { static std::string s = "Tag solids"; return s; } std::string SurfaceStlIO::OPTION_CLEAN() { static std::string s = "Clean poly data"; return s; } SurfaceStlIO::SurfaceStlIO() : SurfaceVtkIO(Surface::GetStaticNameOfClass(), IOMimeTypes::STEREOLITHOGRAPHY_MIMETYPE(), "Stereolithography") { Options defaultOptions; defaultOptions[OPTION_MERGE_POINTS()] = us::Any(true); defaultOptions[OPTION_TAG_SOLIDS()] = us::Any(false); defaultOptions[OPTION_CLEAN()] = us::Any(true); this->SetDefaultReaderOptions(defaultOptions); this->RegisterService(); } std::vector> SurfaceStlIO::Read() { LocaleSwitch localeSwitch("C"); Options options = this->GetReaderOptions(); mitk::Surface::Pointer output = mitk::Surface::New(); vtkSmartPointer stlReader = vtkSmartPointer::New(); stlReader->SetFileName(this->GetLocalFileName().c_str()); bool mergePoints = true; bool tagSolids = false; bool cleanData = true; try { mergePoints = us::any_cast(options[OPTION_MERGE_POINTS()]); tagSolids = us::any_cast(options[OPTION_TAG_SOLIDS()]); cleanData = us::any_cast(options[OPTION_CLEAN()]); } catch (const us::BadAnyCastException &e) { MITK_WARN << "Unexpected error: " << e.what(); } stlReader->SetMerging(mergePoints); stlReader->SetScalarTags(tagSolids); vtkSmartPointer normalsGenerator = vtkSmartPointer::New(); normalsGenerator->SetInputConnection(stlReader->GetOutputPort()); vtkSmartPointer algo = normalsGenerator; if (cleanData) { vtkSmartPointer cleanPolyDataFilter = vtkSmartPointer::New(); cleanPolyDataFilter->SetInputConnection(normalsGenerator->GetOutputPort()); cleanPolyDataFilter->PieceInvariantOff(); cleanPolyDataFilter->ConvertLinesToPointsOff(); cleanPolyDataFilter->ConvertPolysToLinesOff(); cleanPolyDataFilter->ConvertStripsToPolysOff(); if (mergePoints) { cleanPolyDataFilter->PointMergingOn(); } algo = cleanPolyDataFilter; } algo->Update(); if (algo->GetOutput() != nullptr) { vtkSmartPointer surfaceWithNormals = algo->GetOutput(); output->SetVtkPolyData(surfaceWithNormals); } std::vector result; result.push_back(output.GetPointer()); return result; } void SurfaceStlIO::Write() { LocaleSwitch localeSwitch("C"); ValidateOutputLocation(); - const Surface *input = dynamic_cast(this->GetInput()); + const auto *input = dynamic_cast(this->GetInput()); const unsigned int timesteps = input->GetTimeGeometry()->CountTimeSteps(); for (unsigned int t = 0; t < timesteps; ++t) { std::string fileName; vtkSmartPointer polyData = this->GetPolyData(t, fileName); vtkSmartPointer triangleFilter = vtkSmartPointer::New(); triangleFilter->SetInputData(polyData); vtkSmartPointer writer = vtkSmartPointer::New(); writer->SetInputConnection(triangleFilter->GetOutputPort()); // The vtk stl writer cannot write to streams LocalFile localFile(this); writer->SetFileName(localFile.GetFileName().c_str()); if (writer->Write() == 0 || writer->GetErrorCode() != 0) { mitkThrow() << "Error during surface writing" << (writer->GetErrorCode() ? std::string(": ") + vtkErrorCode::GetStringFromErrorCode(writer->GetErrorCode()) : std::string()); } if (this->GetOutputStream() && input->GetTimeGeometry()->CountTimeSteps() > 1) { MITK_WARN << "Writing multiple time-steps to output streams is not supported. " << "Only the first time-step will be written"; break; } } } SurfaceStlIO *SurfaceStlIO::IOClone() const { return new SurfaceStlIO(*this); } } diff --git a/Modules/Core/src/IO/mitkSurfaceVtkIO.cpp b/Modules/Core/src/IO/mitkSurfaceVtkIO.cpp index 35b9e3af9b..7af4ba9b80 100644 --- a/Modules/Core/src/IO/mitkSurfaceVtkIO.cpp +++ b/Modules/Core/src/IO/mitkSurfaceVtkIO.cpp @@ -1,101 +1,101 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkSurfaceVtkIO.h" #include "mitkSurface.h" #include #include #include #include #include namespace mitk { SurfaceVtkIO::SurfaceVtkIO(const std::string &baseDataType, const CustomMimeType &mimeType, const std::string &description) : AbstractFileIO(baseDataType, mimeType, description) { } vtkSmartPointer SurfaceVtkIO::GetPolyData(unsigned int t, std::string &fileName) { - const Surface *input = dynamic_cast(this->GetInput()); + const auto *input = dynamic_cast(this->GetInput()); vtkSmartPointer transformPolyData = vtkSmartPointer::New(); // surfaces do not have to exist in all timesteps; therefor, only write valid surfaces if (input->GetVtkPolyData(t) == nullptr) return vtkSmartPointer(); std::string baseName = this->GetOutputLocation(); std::string extension = itksys::SystemTools::GetFilenameExtension(baseName); if (!extension.empty()) { baseName = baseName.substr(0, baseName.size() - extension.size()); } std::ostringstream ss; ss.imbue(::std::locale::classic()); BaseGeometry *geometry = input->GetGeometry(t); if (input->GetTimeGeometry()->IsValidTimeStep(t)) { if (input->GetTimeGeometry()->CountTimeSteps() > 1) { const TimeBounds &timebounds = input->GetTimeGeometry()->GetTimeBounds(t); ss << baseName << "_S" << std::setprecision(0) << timebounds[0] << "_E" << std::setprecision(0) << timebounds[1] << "_T" << t << extension; } else { // use the original file name ss << this->GetOutputLocation(); } } else { MITK_WARN << "Error on write: TimeGeometry invalid of surface " << fileName << "."; return vtkSmartPointer(); } fileName = ss.str(); transformPolyData->SetInputData(input->GetVtkPolyData(t)); transformPolyData->SetTransform(geometry->GetVtkTransform()); transformPolyData->UpdateWholeExtent(); vtkSmartPointer polyData = transformPolyData->GetOutput(); return polyData; } IFileIO::ConfidenceLevel SurfaceVtkIO::GetWriterConfidenceLevel() const { if (AbstractFileIO::GetWriterConfidenceLevel() == Unsupported) return Unsupported; if (this->GetInput()->GetTimeGeometry()->CountTimeSteps() > 1) { // The VTK formats don't support multiple time points. // During writing, we write each time step into a separate file. // For output streams, we only write the first time-step and print a warning. return PartiallySupported; } return Supported; } } diff --git a/Modules/Core/src/IO/mitkSurfaceVtkLegacyIO.cpp b/Modules/Core/src/IO/mitkSurfaceVtkLegacyIO.cpp index c009c0ee02..5a9f57cc58 100644 --- a/Modules/Core/src/IO/mitkSurfaceVtkLegacyIO.cpp +++ b/Modules/Core/src/IO/mitkSurfaceVtkLegacyIO.cpp @@ -1,121 +1,121 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkSurfaceVtkLegacyIO.h" #include "mitkIOMimeTypes.h" #include "mitkSurface.h" #include #include #include #include #include #include namespace mitk { SurfaceVtkLegacyIO::SurfaceVtkLegacyIO() : SurfaceVtkIO(Surface::GetStaticNameOfClass(), IOMimeTypes::VTK_POLYDATA_LEGACY_MIMETYPE(), "VTK Legacy PolyData") { Options defaultOptions; defaultOptions["Save as binary file"] = false; this->SetDefaultWriterOptions(defaultOptions); this->RegisterService(); } std::vector> SurfaceVtkLegacyIO::Read() { mitk::Surface::Pointer output = mitk::Surface::New(); // The legay vtk reader cannot work with input streams const std::string fileName = this->GetLocalFileName(); vtkSmartPointer reader = vtkSmartPointer::New(); reader->SetFileName(fileName.c_str()); reader->Update(); if (reader->GetOutput() != nullptr) { output->SetVtkPolyData(reader->GetOutput()); } else { mitkThrow() << "vtkPolyDataReader error: " << vtkErrorCode::GetStringFromErrorCode(reader->GetErrorCode()); } std::vector result; result.push_back(output.GetPointer()); return result; } IFileIO::ConfidenceLevel SurfaceVtkLegacyIO::GetReaderConfidenceLevel() const { if (AbstractFileIO::GetReaderConfidenceLevel() == Unsupported) return Unsupported; vtkSmartPointer reader = vtkSmartPointer::New(); reader->SetFileName(this->GetLocalFileName().c_str()); if (reader->IsFilePolyData()) { if (std::strcmp(reader->GetHeader(), "vtk output") == 0) { return Supported; } else return PartiallySupported; } return Unsupported; } void SurfaceVtkLegacyIO::Write() { ValidateOutputLocation(); - const Surface *input = dynamic_cast(this->GetInput()); + const auto *input = dynamic_cast(this->GetInput()); const unsigned int timesteps = input->GetTimeGeometry()->CountTimeSteps(); for (unsigned int t = 0; t < timesteps; ++t) { std::string fileName; vtkSmartPointer polyData = this->GetPolyData(t, fileName); vtkSmartPointer writer = vtkSmartPointer::New(); writer->SetInputData(polyData); if (us::any_cast(GetWriterOption("Save as binary file"))) { writer->SetFileTypeToBinary(); } // The legacy vtk poly data writer cannot write to streams LocalFile localFile(this); writer->SetFileName(localFile.GetFileName().c_str()); if (writer->Write() == 0 || writer->GetErrorCode() != 0) { mitkThrow() << "Error during surface writing" << (writer->GetErrorCode() ? std::string(": ") + vtkErrorCode::GetStringFromErrorCode(writer->GetErrorCode()) : std::string()); } if (this->GetOutputStream() && input->GetTimeGeometry()->CountTimeSteps() > 1) { MITK_WARN << "Writing multiple time-steps to output streams is not supported. " << "Only the first time-step will be written"; break; } } } SurfaceVtkLegacyIO *SurfaceVtkLegacyIO::IOClone() const { return new SurfaceVtkLegacyIO(*this); } } diff --git a/Modules/Core/src/IO/mitkSurfaceVtkXmlIO.cpp b/Modules/Core/src/IO/mitkSurfaceVtkXmlIO.cpp index daa8d7d732..fae9bc27ac 100644 --- a/Modules/Core/src/IO/mitkSurfaceVtkXmlIO.cpp +++ b/Modules/Core/src/IO/mitkSurfaceVtkXmlIO.cpp @@ -1,156 +1,156 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkSurfaceVtkXmlIO.h" #include "mitkIOMimeTypes.h" #include "mitkSurface.h" #include #include #include #include namespace mitk { class VtkXMLPolyDataReader : public ::vtkXMLPolyDataReader { public: static VtkXMLPolyDataReader *New() { return new VtkXMLPolyDataReader(); } vtkTypeMacro(VtkXMLPolyDataReader, vtkXMLPolyDataReader) void SetStream(std::istream *is) { this->Stream = is; } std::istream *GetStream() const { return this->Stream; } }; class VtkXMLPolyDataWriter : public ::vtkXMLPolyDataWriter { public: static VtkXMLPolyDataWriter *New() { return new VtkXMLPolyDataWriter(); } vtkTypeMacro(VtkXMLPolyDataWriter, vtkXMLPolyDataWriter) void SetStream(std::ostream *os) { this->Stream = os; } std::ostream *GetStream() const { return this->Stream; } }; SurfaceVtkXmlIO::SurfaceVtkXmlIO() : SurfaceVtkIO(Surface::GetStaticNameOfClass(), IOMimeTypes::VTK_POLYDATA_MIMETYPE(), "VTK XML PolyData") { this->RegisterService(); } std::vector> SurfaceVtkXmlIO::Read() { mitk::Surface::Pointer output = mitk::Surface::New(); vtkSmartPointer reader = vtkSmartPointer::New(); if (this->GetInputStream()) { reader->SetStream(this->GetInputStream()); } else { reader->SetFileName(this->GetInputLocation().c_str()); } reader->Update(); if (reader->GetOutput() != nullptr) { output->SetVtkPolyData(reader->GetOutput()); } else { mitkThrow() << "vtkXMLPolyDataReader error: " << vtkErrorCode::GetStringFromErrorCode(reader->GetErrorCode()); } std::vector result; result.push_back(output.GetPointer()); return result; } IFileIO::ConfidenceLevel SurfaceVtkXmlIO::GetReaderConfidenceLevel() const { if (AbstractFileIO::GetReaderConfidenceLevel() == Unsupported) return Unsupported; if (this->GetInputStream() == nullptr) { // check if the xml vtk reader can handle the file vtkSmartPointer xmlReader = vtkSmartPointer::New(); if (xmlReader->CanReadFile(this->GetInputLocation().c_str()) != 0) { return Supported; } return Unsupported; } // in case of an input stream, VTK does not seem to have methods for // validating it return Supported; } void SurfaceVtkXmlIO::Write() { ValidateOutputLocation(); - const Surface *input = dynamic_cast(this->GetInput()); + const auto *input = dynamic_cast(this->GetInput()); const unsigned int timesteps = input->GetTimeGeometry()->CountTimeSteps(); for (unsigned int t = 0; t < timesteps; ++t) { std::string fileName; vtkSmartPointer polyData = this->GetPolyData(t, fileName); if (polyData.Get() == nullptr) { mitkThrow() << "Cannot write empty surface"; } vtkSmartPointer writer = vtkSmartPointer::New(); writer->SetInputData(polyData); if (this->GetOutputStream()) { if (input->GetTimeGeometry()->CountTimeSteps() > 1) { MITK_WARN << "Writing multiple time-steps to output streams is not supported. " << "Only the first time-step will be written"; } writer->SetStream(this->GetOutputStream()); } else { writer->SetFileName(fileName.c_str()); } if (writer->Write() == 0 || writer->GetErrorCode() != 0) { mitkThrow() << "Error during surface writing" << (writer->GetErrorCode() ? std::string(": ") + vtkErrorCode::GetStringFromErrorCode(writer->GetErrorCode()) : std::string()); } if (this->GetOutputStream()) break; } } SurfaceVtkXmlIO *SurfaceVtkXmlIO::IOClone() const { return new SurfaceVtkXmlIO(*this); } } diff --git a/Modules/Core/src/Interactions/mitkDispatcher.cpp b/Modules/Core/src/Interactions/mitkDispatcher.cpp index 16fbd7bdbd..66ec2b5e8c 100644 --- a/Modules/Core/src/Interactions/mitkDispatcher.cpp +++ b/Modules/Core/src/Interactions/mitkDispatcher.cpp @@ -1,281 +1,281 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkDispatcher.h" #include "mitkInteractionEvent.h" #include "mitkInteractionEventObserver.h" #include "mitkInternalEvent.h" #include "usGetModuleContext.h" namespace { struct cmp { bool operator()(mitk::DataInteractor *d1, mitk::DataInteractor *d2) { return (d1->GetLayer() > d2->GetLayer()); } }; } mitk::Dispatcher::Dispatcher(const std::string &rendererName) : m_ProcessingMode(REGULAR) { // LDAP filter string to find all listeners specific for the renderer // corresponding to this dispatcher std::string specificRenderer = "(rendererName=" + rendererName + ")"; // LDAP filter string to find all listeners that are not specific // to any renderer std::string anyRenderer = "(!(rendererName=*))"; // LDAP filter string to find only instances of InteractionEventObserver std::string classInteractionEventObserver = "(" + us::ServiceConstants::OBJECTCLASS() + "=" + us_service_interface_iid() + ")"; // Configure the LDAP filter to find all instances of InteractionEventObserver // that are specific to this dispatcher or unspecific to any dispatchers (real global listener) us::LDAPFilter filter("(&(|" + specificRenderer + anyRenderer + ")" + classInteractionEventObserver + ")"); // Give the filter to the ObserverTracker m_EventObserverTracker = new us::ServiceTracker(us::GetModuleContext(), filter); m_EventObserverTracker->Open(); } void mitk::Dispatcher::AddDataInteractor(const DataNode *dataNode) { RemoveDataInteractor(dataNode); RemoveOrphanedInteractors(); auto dataInteractor = dataNode->GetDataInteractor().GetPointer(); if (dataInteractor != nullptr) m_Interactors.push_back(dataInteractor); } /* * Note: One DataInteractor can only have one DataNode and vice versa, * BUT the m_Interactors list may contain another DataInteractor that is still connected to this DataNode, * in this case we have to remove >1 DataInteractor. (Some special case of switching DataNodes between DataInteractors * and registering a * DataNode to a DataStorage after assigning it to an DataInteractor) */ void mitk::Dispatcher::RemoveDataInteractor(const DataNode *dataNode) { - for (ListInteractorType::iterator it = m_Interactors.begin(); it != m_Interactors.end();) + for (auto it = m_Interactors.begin(); it != m_Interactors.end();) { if ((*it).IsNull() || (*it)->GetDataNode() == nullptr || (*it)->GetDataNode() == dataNode) { it = m_Interactors.erase(it); } else { ++it; } } } size_t mitk::Dispatcher::GetNumberOfInteractors() { return m_Interactors.size(); } mitk::Dispatcher::~Dispatcher() { m_EventObserverTracker->Close(); delete m_EventObserverTracker; m_Interactors.clear(); } bool mitk::Dispatcher::ProcessEvent(InteractionEvent *event) { InteractionEvent::Pointer p = event; bool eventIsHandled = false; /* Filter out and handle Internal Events separately */ - InternalEvent *internalEvent = dynamic_cast(event); + auto *internalEvent = dynamic_cast(event); if (internalEvent != nullptr) { eventIsHandled = HandleInternalEvent(internalEvent); // InternalEvents that are handled are not sent to the listeners if (eventIsHandled) { return true; } } switch (m_ProcessingMode) { case CONNECTEDMOUSEACTION: // finished connected mouse action if (std::strcmp(p->GetNameOfClass(), "MouseReleaseEvent") == 0) { m_ProcessingMode = REGULAR; if (m_SelectedInteractor.IsNotNull()) eventIsHandled = m_SelectedInteractor->HandleEvent(event, m_SelectedInteractor->GetDataNode()); m_SelectedInteractor = nullptr; } // give event to selected interactor if (eventIsHandled == false && m_SelectedInteractor.IsNotNull()) eventIsHandled = m_SelectedInteractor->HandleEvent(event, m_SelectedInteractor->GetDataNode()); break; case GRABINPUT: if (m_SelectedInteractor.IsNotNull()) { eventIsHandled = m_SelectedInteractor->HandleEvent(event, m_SelectedInteractor->GetDataNode()); SetEventProcessingMode(m_SelectedInteractor); } break; case PREFERINPUT: if (m_SelectedInteractor.IsNotNull() && m_SelectedInteractor->HandleEvent(event, m_SelectedInteractor->GetDataNode()) == true) { SetEventProcessingMode(m_SelectedInteractor); eventIsHandled = true; } break; case REGULAR: break; } // Standard behavior. Is executed in STANDARD mode and PREFERINPUT mode, if preferred interactor rejects event. if (m_ProcessingMode == REGULAR || (m_ProcessingMode == PREFERINPUT && eventIsHandled == false)) { if (std::strcmp(p->GetNameOfClass(), "MousePressEvent") == 0) event->GetSender()->GetRenderingManager()->SetRenderWindowFocus(event->GetSender()->GetRenderWindow()); m_Interactors.sort(cmp()); // sorts interactors by layer (descending); // copy the list to prevent iterator invalidation as executing actions // in HandleEvent() can cause the m_Interactors list to be updated const ListInteractorType tmpInteractorList(m_Interactors); ListInteractorType::const_iterator it; for (it = tmpInteractorList.cbegin(); it != tmpInteractorList.cend(); ++it) { if ((*it).IsNotNull() && (*it)->HandleEvent(event, (*it)->GetDataNode())) { // Interactor can be deleted during HandleEvent(), so check it again if ((*it).IsNotNull()) { // if an event is handled several properties are checked, in order to determine the processing mode of the // dispatcher SetEventProcessingMode(*it); } if (std::strcmp(p->GetNameOfClass(), "MousePressEvent") == 0 && m_ProcessingMode == REGULAR) { m_SelectedInteractor = *it; m_ProcessingMode = CONNECTEDMOUSEACTION; } eventIsHandled = true; break; } } } /* Notify InteractionEventObserver */ const std::vector> listEventObserver = m_EventObserverTracker->GetServiceReferences(); - for (std::vector>::const_iterator it = listEventObserver.cbegin(); + for (auto it = listEventObserver.cbegin(); it != listEventObserver.cend(); ++it) { InteractionEventObserver *interactionEventObserver = m_EventObserverTracker->GetService(*it); if (interactionEventObserver != nullptr) { if (interactionEventObserver->IsEnabled()) { interactionEventObserver->Notify(event, eventIsHandled); } } } // Process event queue if (!m_QueuedEvents.empty()) { InteractionEvent::Pointer e = m_QueuedEvents.front(); m_QueuedEvents.pop_front(); ProcessEvent(e); } return eventIsHandled; } /* * Checks if DataNodes associated with DataInteractors point back to them. * If not remove the DataInteractors. (This can happen when s.o. tries to set DataNodes to multiple DataInteractors) */ void mitk::Dispatcher::RemoveOrphanedInteractors() { - for (ListInteractorType::iterator it = m_Interactors.begin(); it != m_Interactors.end();) + for (auto it = m_Interactors.begin(); it != m_Interactors.end();) { if ((*it).IsNull()) { it = m_Interactors.erase(it); } else { DataNode::Pointer node = (*it)->GetDataNode(); if (node.IsNull()) { it = m_Interactors.erase(it); } else { DataInteractor::Pointer interactor = node->GetDataInteractor(); if (interactor != it->GetPointer()) { it = m_Interactors.erase(it); } else { ++it; } } } } } void mitk::Dispatcher::QueueEvent(InteractionEvent *event) { m_QueuedEvents.push_back(event); } void mitk::Dispatcher::SetEventProcessingMode(DataInteractor *dataInteractor) { m_ProcessingMode = dataInteractor->GetMode(); if (dataInteractor->GetMode() != REGULAR) { m_SelectedInteractor = dataInteractor; } } bool mitk::Dispatcher::HandleInternalEvent(InternalEvent *internalEvent) { if (internalEvent->GetSignalName() == DataInteractor::IntDeactivateMe && internalEvent->GetTargetInteractor() != nullptr) { internalEvent->GetTargetInteractor()->GetDataNode()->SetDataInteractor(nullptr); internalEvent->GetTargetInteractor()->SetDataNode(nullptr); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); return true; } return false; } diff --git a/Modules/Core/src/Interactions/mitkDisplayInteractor.cpp b/Modules/Core/src/Interactions/mitkDisplayInteractor.cpp index 354370e641..f3d5b7ef1c 100644 --- a/Modules/Core/src/Interactions/mitkDisplayInteractor.cpp +++ b/Modules/Core/src/Interactions/mitkDisplayInteractor.cpp @@ -1,958 +1,958 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkDisplayInteractor.h" #include "mitkBaseRenderer.h" #include "mitkCameraController.h" #include "mitkInteractionPositionEvent.h" #include "mitkPropertyList.h" #include #include #include // level window #include "mitkLevelWindow.h" #include "mitkLevelWindowProperty.h" #include "mitkLine.h" #include "mitkNodePredicateDataType.h" #include "mitkStandaloneDataStorage.h" #include "vtkRenderWindowInteractor.h" // Rotation #include "mitkInteractionConst.h" #include "rotate_cursor.xpm" #include #include #include "mitkImage.h" #include "mitkImagePixelReadAccessor.h" #include "mitkPixelTypeMultiplex.h" #include "mitkStatusBar.h" #include void mitk::DisplayInteractor::Notify(InteractionEvent *interactionEvent, bool isHandled) { // to use the state machine pattern, // the event is passed to the state machine interface to be handled if (!isHandled || m_AlwaysReact) { this->HandleEvent(interactionEvent, nullptr); } } void mitk::DisplayInteractor::ConnectActionsAndFunctions() { CONNECT_CONDITION("check_position_event", CheckPositionEvent); CONNECT_CONDITION("check_can_rotate", CheckRotationPossible); CONNECT_CONDITION("check_can_swivel", CheckSwivelPossible); CONNECT_FUNCTION("init", Init); CONNECT_FUNCTION("move", Move); CONNECT_FUNCTION("zoom", Zoom); CONNECT_FUNCTION("scroll", Scroll); CONNECT_FUNCTION("ScrollOneDown", ScrollOneDown); CONNECT_FUNCTION("ScrollOneUp", ScrollOneUp); CONNECT_FUNCTION("levelWindow", AdjustLevelWindow); CONNECT_FUNCTION("setCrosshair", SetCrosshair); CONNECT_FUNCTION("updateStatusbar", UpdateStatusbar) CONNECT_FUNCTION("startRotation", StartRotation); CONNECT_FUNCTION("endRotation", EndRotation); CONNECT_FUNCTION("rotate", Rotate); CONNECT_FUNCTION("swivel", Swivel); } mitk::DisplayInteractor::DisplayInteractor() : m_IndexToSliceModifier(4), m_AutoRepeat(false), m_InvertScrollDirection(false), m_InvertZoomDirection(false), m_InvertMoveDirection(false), m_InvertLevelWindowDirection(false), m_AlwaysReact(false), m_ZoomFactor(2), m_LinkPlanes(true) { m_StartCoordinateInMM.Fill(0); m_LastDisplayCoordinate.Fill(0); m_LastCoordinateInMM.Fill(0); m_CurrentDisplayCoordinate.Fill(0); } mitk::DisplayInteractor::~DisplayInteractor() { } bool mitk::DisplayInteractor::CheckPositionEvent(const InteractionEvent *interactionEvent) { - const InteractionPositionEvent *positionEvent = dynamic_cast(interactionEvent); + const auto *positionEvent = dynamic_cast(interactionEvent); if (positionEvent == nullptr) { return false; } return true; } bool mitk::DisplayInteractor::CheckRotationPossible(const mitk::InteractionEvent *interactionEvent) { // Decide between moving and rotation slices. /* Detailed logic: 1. Find the SliceNavigationController that has sent the event: this one defines our rendering plane and will NOT be rotated. Needs not even be counted or checked. 2. Inspect every other SliceNavigationController - calculate the line intersection of this SliceNavigationController's plane with our rendering plane - if there is NO interesection, ignore and continue - IF there is an intersection - check the mouse cursor's distance from that line. 0. if the line is NOT near the cursor, remember the plane as "one of the other planes" (which can be rotated in "locked" mode) 1. on first line near the cursor, just remember this intersection line as THE other plane that we want to rotate 2. on every consecutive line near the cursor, check if the line is geometrically identical to the line that we want to rotate - if yes, we just push this line to the "other" lines and rotate it along - if no, then we have a situation where the mouse is near two other lines (e.g. crossing point) and don't want to rotate */ - const InteractionPositionEvent *posEvent = dynamic_cast(interactionEvent); + const auto *posEvent = dynamic_cast(interactionEvent); if (posEvent == nullptr) return false; BaseRenderer *clickedRenderer = posEvent->GetSender(); const PlaneGeometry *ourViewportGeometry = (clickedRenderer->GetCurrentWorldPlaneGeometry()); if (!ourViewportGeometry) return false; Point3D cursorPosition = posEvent->GetPositionInWorld(); const auto spacing = ourViewportGeometry->GetSpacing(); const PlaneGeometry *geometryToBeRotated = nullptr; // this one is under the mouse cursor const PlaneGeometry *anyOtherGeometry = nullptr; // this is also visible (for calculation of intersection ONLY) Line3D intersectionLineWithGeometryToBeRotated; bool hitMultipleLines(false); m_SNCsToBeRotated.clear(); const double threshholdDistancePixels = 12.0; auto renWindows = interactionEvent->GetSender()->GetRenderingManager()->GetAllRegisteredRenderWindows(); for (auto renWin : renWindows) { SliceNavigationController *snc = BaseRenderer::GetInstance(renWin)->GetSliceNavigationController(); // If the mouse cursor is in 3D Renderwindow, do not check for intersecting planes. if (BaseRenderer::GetInstance(renWin)->GetMapperID() == BaseRenderer::Standard3D) continue; const PlaneGeometry *otherRenderersRenderPlane = snc->GetCurrentPlaneGeometry(); if (otherRenderersRenderPlane == nullptr) continue; // ignore, we don't see a plane // check if there is an intersection Line3D intersectionLine; // between rendered/clicked geometry and the one being analyzed if (!ourViewportGeometry->IntersectionLine(otherRenderersRenderPlane, intersectionLine)) { continue; // we ignore this plane, it's parallel to our plane } // check distance from intersection line const double distanceFromIntersectionLine = intersectionLine.Distance(cursorPosition) / spacing[snc->GetDefaultViewDirection()]; // far away line, only remember for linked rotation if necessary if (distanceFromIntersectionLine > threshholdDistancePixels) { anyOtherGeometry = otherRenderersRenderPlane; // we just take the last one, so overwrite each iteration (we just // need some crossing point) // TODO what about multiple crossings? NOW we have undefined behavior / random crossing point is used if (m_LinkPlanes) { m_SNCsToBeRotated.push_back(snc); } } else // close to cursor { if (geometryToBeRotated == nullptr) // first one close to the cursor { geometryToBeRotated = otherRenderersRenderPlane; intersectionLineWithGeometryToBeRotated = intersectionLine; m_SNCsToBeRotated.push_back(snc); } else { // compare to the line defined by geometryToBeRotated: if identical, just rotate this otherRenderersRenderPlane // together with the primary one // if different, DON'T rotate if (intersectionLine.IsParallel(intersectionLineWithGeometryToBeRotated) && intersectionLine.Distance(intersectionLineWithGeometryToBeRotated.GetPoint1()) < mitk::eps) { m_SNCsToBeRotated.push_back(snc); } else { hitMultipleLines = true; } } } } bool moveSlices(true); if (geometryToBeRotated && anyOtherGeometry && ourViewportGeometry && !hitMultipleLines) { // assure all three are valid, so calculation of center of rotation can be done moveSlices = false; } // question in state machine is: "rotate?" if (moveSlices) // i.e. NOT rotate { return false; } else { // we DO have enough information for rotation m_LastCursorPosition = intersectionLineWithGeometryToBeRotated.Project( cursorPosition); // remember where the last cursor position ON THE LINE has been observed if (anyOtherGeometry->IntersectionPoint( intersectionLineWithGeometryToBeRotated, m_CenterOfRotation)) // find center of rotation by intersection with any of the OTHER lines { return true; } else { return false; } } return false; } bool mitk::DisplayInteractor::CheckSwivelPossible(const mitk::InteractionEvent *interactionEvent) { const ScalarType ThresholdDistancePixels = 6.0; // Decide between moving and rotation: if we're close to the crossing // point of the planes, moving mode is entered, otherwise // rotation/swivel mode - const InteractionPositionEvent *posEvent = dynamic_cast(interactionEvent); + const auto *posEvent = dynamic_cast(interactionEvent); BaseRenderer *renderer = interactionEvent->GetSender(); if (!posEvent || !renderer) return false; const Point3D &cursor = posEvent->GetPositionInWorld(); m_SNCsToBeRotated.clear(); const PlaneGeometry *clickedGeometry(nullptr); const PlaneGeometry *otherGeometry1(nullptr); const PlaneGeometry *otherGeometry2(nullptr); auto renWindows = interactionEvent->GetSender()->GetRenderingManager()->GetAllRegisteredRenderWindows(); for (auto renWin : renWindows) { SliceNavigationController *snc = BaseRenderer::GetInstance(renWin)->GetSliceNavigationController(); // If the mouse cursor is in 3D Renderwindow, do not check for intersecting planes. if (BaseRenderer::GetInstance(renWin)->GetMapperID() == BaseRenderer::Standard3D) continue; // unsigned int slice = (*iter)->GetSlice()->GetPos(); // unsigned int time = (*iter)->GetTime()->GetPos(); const PlaneGeometry *planeGeometry = snc->GetCurrentPlaneGeometry(); if (!planeGeometry) continue; if (snc == renderer->GetSliceNavigationController()) { clickedGeometry = planeGeometry; m_SNCsToBeRotated.push_back(snc); } else { if (otherGeometry1 == nullptr) { otherGeometry1 = planeGeometry; } else { otherGeometry2 = planeGeometry; } if (m_LinkPlanes) { // If planes are linked, apply rotation to all planes m_SNCsToBeRotated.push_back(snc); } } } mitk::Line3D line; mitk::Point3D point; if ((clickedGeometry != nullptr) && (otherGeometry1 != nullptr) && (otherGeometry2 != nullptr) && clickedGeometry->IntersectionLine(otherGeometry1, line) && otherGeometry2->IntersectionPoint(line, point)) { m_CenterOfRotation = point; if (m_CenterOfRotation.EuclideanDistanceTo(cursor) < ThresholdDistancePixels) { return false; } else { m_ReferenceCursor = posEvent->GetPointerPositionOnScreen(); // Get main axes of rotation plane and store it for rotation step m_RotationPlaneNormal = clickedGeometry->GetNormal(); ScalarType xVector[] = {1.0, 0.0, 0.0}; ScalarType yVector[] = {0.0, 1.0, 0.0}; clickedGeometry->BaseGeometry::IndexToWorld(Vector3D(xVector), m_RotationPlaneXVector); clickedGeometry->BaseGeometry::IndexToWorld(Vector3D(yVector), m_RotationPlaneYVector); m_RotationPlaneNormal.Normalize(); m_RotationPlaneXVector.Normalize(); m_RotationPlaneYVector.Normalize(); m_PreviousRotationAxis.Fill(0.0); m_PreviousRotationAxis[2] = 1.0; m_PreviousRotationAngle = 0.0; return true; } } else { return false; } return false; } void mitk::DisplayInteractor::Init(StateMachineAction *, InteractionEvent *interactionEvent) { - InteractionPositionEvent *positionEvent = static_cast(interactionEvent); + auto *positionEvent = static_cast(interactionEvent); m_LastDisplayCoordinate = positionEvent->GetPointerPositionOnScreen(); m_CurrentDisplayCoordinate = m_LastDisplayCoordinate; positionEvent->GetSender()->DisplayToPlane(m_LastDisplayCoordinate, m_StartCoordinateInMM); m_LastCoordinateInMM = m_StartCoordinateInMM; } void mitk::DisplayInteractor::Move(StateMachineAction *, InteractionEvent *interactionEvent) { BaseRenderer *sender = interactionEvent->GetSender(); - InteractionPositionEvent *positionEvent = static_cast(interactionEvent); + auto *positionEvent = static_cast(interactionEvent); float invertModifier = -1.0; if (m_InvertMoveDirection) { invertModifier = 1.0; } // perform translation Vector2D moveVector = (positionEvent->GetPointerPositionOnScreen() - m_LastDisplayCoordinate) * invertModifier; moveVector *= sender->GetScaleFactorMMPerDisplayUnit(); sender->GetCameraController()->MoveBy(moveVector); sender->GetRenderingManager()->RequestUpdate(sender->GetRenderWindow()); m_LastDisplayCoordinate = positionEvent->GetPointerPositionOnScreen(); } void mitk::DisplayInteractor::SetCrosshair(mitk::StateMachineAction *, mitk::InteractionEvent *interactionEvent) { const BaseRenderer::Pointer sender = interactionEvent->GetSender(); auto renWindows = sender->GetRenderingManager()->GetAllRegisteredRenderWindows(); - InteractionPositionEvent *positionEvent = static_cast(interactionEvent); + auto *positionEvent = static_cast(interactionEvent); Point3D pos = positionEvent->GetPositionInWorld(); for (auto renWin : renWindows) { if (BaseRenderer::GetInstance(renWin)->GetMapperID() == BaseRenderer::Standard2D && renWin != sender->GetRenderWindow()) BaseRenderer::GetInstance(renWin)->GetSliceNavigationController()->SelectSliceByPoint(pos); } } void mitk::DisplayInteractor::Zoom(StateMachineAction *, InteractionEvent *interactionEvent) { const BaseRenderer::Pointer sender = interactionEvent->GetSender(); - InteractionPositionEvent *positionEvent = static_cast(interactionEvent); + auto *positionEvent = static_cast(interactionEvent); float factor = 1.0; float distance = 0; if (m_ZoomDirection == "updown") { distance = m_CurrentDisplayCoordinate[1] - m_LastDisplayCoordinate[1]; } else { distance = m_CurrentDisplayCoordinate[0] - m_LastDisplayCoordinate[0]; } if (m_InvertZoomDirection) { distance *= -1.0; } // set zooming speed if (distance < 0.0) { factor = 1.0 / m_ZoomFactor; } else if (distance > 0.0) { factor = 1.0 * m_ZoomFactor; } if (factor != 1.0) { sender->GetCameraController()->Zoom(factor, m_StartCoordinateInMM); sender->GetRenderingManager()->RequestUpdate(sender->GetRenderWindow()); } m_LastDisplayCoordinate = m_CurrentDisplayCoordinate; m_CurrentDisplayCoordinate = positionEvent->GetPointerPositionOnScreen(); } void mitk::DisplayInteractor::Scroll(StateMachineAction *, InteractionEvent *interactionEvent) { - InteractionPositionEvent *positionEvent = static_cast(interactionEvent); + auto *positionEvent = static_cast(interactionEvent); mitk::SliceNavigationController::Pointer sliceNaviController = interactionEvent->GetSender()->GetSliceNavigationController(); if (sliceNaviController) { int delta = 0; // Scrolling direction if (m_ScrollDirection == "updown") { delta = static_cast(m_LastDisplayCoordinate[1] - positionEvent->GetPointerPositionOnScreen()[1]); } else { delta = static_cast(m_LastDisplayCoordinate[0] - positionEvent->GetPointerPositionOnScreen()[0]); } if (m_InvertScrollDirection) { delta *= -1; } // Set how many pixels the mouse has to be moved to scroll one slice // if we moved less than 'm_IndexToSliceModifier' pixels slice ONE slice only if (delta > 0 && delta < m_IndexToSliceModifier) { delta = m_IndexToSliceModifier; } else if (delta < 0 && delta > -m_IndexToSliceModifier) { delta = -m_IndexToSliceModifier; } delta /= m_IndexToSliceModifier; int newPos = sliceNaviController->GetSlice()->GetPos() + delta; // if auto repeat is on, start at first slice if you reach the last slice and vice versa int maxSlices = sliceNaviController->GetSlice()->GetSteps(); if (m_AutoRepeat) { while (newPos < 0) { newPos += maxSlices; } while (newPos >= maxSlices) { newPos -= maxSlices; } } else { // if the new slice is below 0 we still show slice 0 // due to the stepper using unsigned int we have to do this ourselves if (newPos < 1) { newPos = 0; } } // set the new position sliceNaviController->GetSlice()->SetPos(newPos); m_LastDisplayCoordinate = m_CurrentDisplayCoordinate; m_CurrentDisplayCoordinate = positionEvent->GetPointerPositionOnScreen(); } } void mitk::DisplayInteractor::ScrollOneDown(StateMachineAction *, InteractionEvent *interactionEvent) { mitk::SliceNavigationController::Pointer sliceNaviController = interactionEvent->GetSender()->GetSliceNavigationController(); if (!sliceNaviController->GetSliceLocked()) { mitk::Stepper *stepper = sliceNaviController->GetSlice(); if (stepper->GetSteps() <= 1) { stepper = sliceNaviController->GetTime(); } stepper->Next(); } } void mitk::DisplayInteractor::ScrollOneUp(StateMachineAction *, InteractionEvent *interactionEvent) { mitk::SliceNavigationController::Pointer sliceNaviController = interactionEvent->GetSender()->GetSliceNavigationController(); if (!sliceNaviController->GetSliceLocked()) { mitk::Stepper *stepper = sliceNaviController->GetSlice(); if (stepper->GetSteps() <= 1) { stepper = sliceNaviController->GetTime(); } stepper->Previous(); } } void mitk::DisplayInteractor::AdjustLevelWindow(StateMachineAction *, InteractionEvent *interactionEvent) { BaseRenderer::Pointer sender = interactionEvent->GetSender(); - InteractionPositionEvent *positionEvent = static_cast(interactionEvent); + auto *positionEvent = static_cast(interactionEvent); m_LastDisplayCoordinate = m_CurrentDisplayCoordinate; m_CurrentDisplayCoordinate = positionEvent->GetPointerPositionOnScreen(); // search for active image mitk::DataStorage::Pointer storage = sender->GetDataStorage(); mitk::DataNode::Pointer node = nullptr; mitk::DataStorage::SetOfObjects::ConstPointer allImageNodes = storage->GetSubset(mitk::NodePredicateDataType::New("Image")); for (unsigned int i = 0; i < allImageNodes->size(); i++) { bool isActiveImage = false; bool propFound = allImageNodes->at(i)->GetBoolProperty("imageForLevelWindow", isActiveImage); if (propFound && isActiveImage) { node = allImageNodes->at(i); continue; } } if (node.IsNull()) { node = storage->GetNode(mitk::NodePredicateDataType::New("Image")); } if (node.IsNull()) { return; } mitk::LevelWindow lv = mitk::LevelWindow(); node->GetLevelWindow(lv); ScalarType level = lv.GetLevel(); ScalarType window = lv.GetWindow(); int levelIndex = 0; int windowIndex = 1; if (m_LevelDirection != "leftright") { levelIndex = 1; windowIndex = 0; } int directionModifier = 1; if (m_InvertLevelWindowDirection) { directionModifier = -1; } // calculate adjustments from mouse movements level += (m_CurrentDisplayCoordinate[levelIndex] - m_LastDisplayCoordinate[levelIndex]) * static_cast(2) * directionModifier; window += (m_CurrentDisplayCoordinate[windowIndex] - m_LastDisplayCoordinate[windowIndex]) * static_cast(2) * directionModifier; lv.SetLevelWindow(level, window); dynamic_cast(node->GetProperty("levelwindow"))->SetLevelWindow(lv); sender->GetRenderingManager()->RequestUpdateAll(); } void mitk::DisplayInteractor::StartRotation(mitk::StateMachineAction *, mitk::InteractionEvent *) { this->SetMouseCursor(rotate_cursor_xpm, 0, 0); } void mitk::DisplayInteractor::EndRotation(mitk::StateMachineAction *, mitk::InteractionEvent *) { this->ResetMouseCursor(); } void mitk::DisplayInteractor::Rotate(mitk::StateMachineAction *, mitk::InteractionEvent *event) { - const InteractionPositionEvent *posEvent = dynamic_cast(event); + const auto *posEvent = dynamic_cast(event); if (posEvent == nullptr) return; Point3D cursor = posEvent->GetPositionInWorld(); Vector3D toProjected = m_LastCursorPosition - m_CenterOfRotation; Vector3D toCursor = cursor - m_CenterOfRotation; // cross product: | A x B | = |A| * |B| * sin(angle) Vector3D axisOfRotation; vnl_vector_fixed vnlDirection = vnl_cross_3d(toCursor.GetVnlVector(), toProjected.GetVnlVector()); axisOfRotation.SetVnlVector(vnlDirection); // scalar product: A * B = |A| * |B| * cos(angle) // tan = sin / cos ScalarType angle = -atan2((double)(axisOfRotation.GetNorm()), (double)(toCursor * toProjected)); angle *= 180.0 / vnl_math::pi; m_LastCursorPosition = cursor; // create RotationOperation and apply to all SNCs that should be rotated RotationOperation rotationOperation(OpROTATE, m_CenterOfRotation, axisOfRotation, angle); // iterate the OTHER slice navigation controllers: these are filled in DoDecideBetweenRotationAndSliceSelection - for (SNCVector::iterator iter = m_SNCsToBeRotated.begin(); iter != m_SNCsToBeRotated.end(); ++iter) + for (auto iter = m_SNCsToBeRotated.begin(); iter != m_SNCsToBeRotated.end(); ++iter) { TimeGeometry *timeGeometry = (*iter)->GetCreatedWorldGeometry(); if (!timeGeometry) continue; timeGeometry->ExecuteOperation(&rotationOperation); (*iter)->SendCreatedWorldGeometryUpdate(); } RenderingManager::GetInstance()->RequestUpdateAll(); } void mitk::DisplayInteractor::Swivel(mitk::StateMachineAction *, mitk::InteractionEvent *event) { - const InteractionPositionEvent *posEvent = dynamic_cast(event); + const auto *posEvent = dynamic_cast(event); if (!posEvent) return; // Determine relative mouse movement projected onto world space Point2D cursor = posEvent->GetPointerPositionOnScreen(); Vector2D relativeCursor = cursor - m_ReferenceCursor; Vector3D relativeCursorAxis = m_RotationPlaneXVector * relativeCursor[0] + m_RotationPlaneYVector * relativeCursor[1]; // Determine rotation axis (perpendicular to rotation plane and cursor // movement) Vector3D rotationAxis = itk::CrossProduct(m_RotationPlaneNormal, relativeCursorAxis); ScalarType rotationAngle = relativeCursor.GetNorm() / 2.0; // Restore the initial plane pose by undoing the previous rotation // operation RotationOperation op(OpROTATE, m_CenterOfRotation, m_PreviousRotationAxis, -m_PreviousRotationAngle); SNCVector::iterator iter; for (iter = m_SNCsToBeRotated.begin(); iter != m_SNCsToBeRotated.end(); ++iter) { if (!(*iter)->GetSliceRotationLocked()) { TimeGeometry *timeGeometry = (*iter)->GetCreatedWorldGeometry(); if (!timeGeometry) continue; timeGeometry->ExecuteOperation(&op); (*iter)->SendCreatedWorldGeometryUpdate(); } } // Apply new rotation operation to all relevant SNCs RotationOperation op2(OpROTATE, m_CenterOfRotation, rotationAxis, rotationAngle); for (iter = m_SNCsToBeRotated.begin(); iter != m_SNCsToBeRotated.end(); ++iter) { if (!(*iter)->GetSliceRotationLocked()) { // Retrieve the TimeGeometry of this SliceNavigationController TimeGeometry *timeGeometry = (*iter)->GetCreatedWorldGeometry(); if (!timeGeometry) continue; // Execute the new rotation timeGeometry->ExecuteOperation(&op2); // Notify listeners (*iter)->SendCreatedWorldGeometryUpdate(); } } m_PreviousRotationAxis = rotationAxis; m_PreviousRotationAngle = rotationAngle; RenderingManager::GetInstance()->RequestUpdateAll(); return; } void mitk::DisplayInteractor::UpdateStatusbar(mitk::StateMachineAction *, mitk::InteractionEvent *event) { - const InteractionPositionEvent *posEvent = dynamic_cast(event); + const auto *posEvent = dynamic_cast(event); if (!posEvent) return; std::string statusText; TNodePredicateDataType::Pointer isImageData = TNodePredicateDataType::New(); mitk::DataStorage::SetOfObjects::ConstPointer nodes = posEvent->GetSender()->GetDataStorage()->GetSubset(isImageData).GetPointer(); // posEvent->GetPositionInWorld() would return the world position at the // time of initiating the interaction. However, we need to update the // status bar with the position after changing slice. Therefore, we // translate the same display position with the renderer again to // get the new world position. Point3D worldposition; event->GetSender()->DisplayToWorld(posEvent->GetPointerPositionOnScreen(), worldposition); mitk::Image::Pointer image3D; mitk::DataNode::Pointer node; mitk::DataNode::Pointer topSourceNode; int component = 0; node = this->GetTopLayerNode(nodes, worldposition, posEvent->GetSender()); if (node.IsNotNull()) { bool isBinary(false); node->GetBoolProperty("binary", isBinary); if (isBinary) { mitk::DataStorage::SetOfObjects::ConstPointer sourcenodes = posEvent->GetSender()->GetDataStorage()->GetSources(node, nullptr, true); if (!sourcenodes->empty()) { topSourceNode = this->GetTopLayerNode(sourcenodes, worldposition, posEvent->GetSender()); } if (topSourceNode.IsNotNull()) { image3D = dynamic_cast(topSourceNode->GetData()); topSourceNode->GetIntProperty("Image.Displayed Component", component); } else { image3D = dynamic_cast(node->GetData()); node->GetIntProperty("Image.Displayed Component", component); } } else { image3D = dynamic_cast(node->GetData()); node->GetIntProperty("Image.Displayed Component", component); } } // get the position and gray value from the image and build up status bar text auto statusBar = StatusBar::GetInstance(); if (image3D.IsNotNull() && statusBar != nullptr) { itk::Index<3> p; image3D->GetGeometry()->WorldToIndex(worldposition, p); auto pixelType = image3D->GetChannelDescriptor().GetPixelType().GetPixelType(); if (pixelType == itk::ImageIOBase::RGB || pixelType == itk::ImageIOBase::RGBA) { std::string pixelValue = "Pixel RGB(A) value: "; pixelValue.append(ConvertCompositePixelValueToString(image3D, p)); statusBar->DisplayImageInfo(worldposition, p, posEvent->GetSender()->GetTime(), pixelValue.c_str()); } else if ( pixelType == itk::ImageIOBase::DIFFUSIONTENSOR3D || pixelType == itk::ImageIOBase::SYMMETRICSECONDRANKTENSOR ) { std::string pixelValue = "See ODF Details view. "; statusBar->DisplayImageInfo(worldposition, p, posEvent->GetSender()->GetTime(), pixelValue.c_str()); } else { mitk::ScalarType pixelValue; mitkPixelTypeMultiplex5(mitk::FastSinglePixelAccess, image3D->GetChannelDescriptor().GetPixelType(), image3D, image3D->GetVolumeData(posEvent->GetSender()->GetTimeStep()), p, pixelValue, component); statusBar->DisplayImageInfo(worldposition, p, posEvent->GetSender()->GetTime(), pixelValue); } } else { statusBar->DisplayImageInfoInvalid(); } } void mitk::DisplayInteractor::ConfigurationChanged() { mitk::PropertyList::Pointer properties = GetAttributes(); // auto repeat std::string strAutoRepeat = ""; if (properties->GetStringProperty("autoRepeat", strAutoRepeat)) { if (strAutoRepeat == "true") { m_AutoRepeat = true; } else { m_AutoRepeat = false; } } // pixel movement for scrolling one slice std::string strPixelPerSlice = ""; if (properties->GetStringProperty("pixelPerSlice", strPixelPerSlice)) { m_IndexToSliceModifier = atoi(strPixelPerSlice.c_str()); } else { m_IndexToSliceModifier = 4; } // scroll direction if (!properties->GetStringProperty("scrollDirection", m_ScrollDirection)) { m_ScrollDirection = "updown"; } m_InvertScrollDirection = GetBoolProperty(properties, "invertScrollDirection", false); // zoom direction if (!properties->GetStringProperty("zoomDirection", m_ZoomDirection)) { m_ZoomDirection = "updown"; } m_InvertZoomDirection = GetBoolProperty(properties, "invertZoomDirection", false); m_InvertMoveDirection = GetBoolProperty(properties, "invertMoveDirection", false); if (!properties->GetStringProperty("levelWindowDirection", m_LevelDirection)) { m_LevelDirection = "leftright"; } m_InvertLevelWindowDirection = GetBoolProperty(properties, "invertLevelWindowDirection", false); // coupled rotation std::string strCoupled = ""; if (properties->GetStringProperty("coupled", strCoupled)) { if (strCoupled == "true") m_LinkPlanes = true; else m_LinkPlanes = false; } // zoom factor std::string strZoomFactor = ""; properties->GetStringProperty("zoomFactor", strZoomFactor); m_ZoomFactor = .05; if (atoi(strZoomFactor.c_str()) > 0) { m_ZoomFactor = 1.0 + (atoi(strZoomFactor.c_str()) / 100.0); } // allwaysReact std::string strAlwaysReact = ""; if (properties->GetStringProperty("alwaysReact", strAlwaysReact)) { if (strAlwaysReact == "true") { m_AlwaysReact = true; } else { m_AlwaysReact = false; } } else { m_AlwaysReact = false; } } bool mitk::DisplayInteractor::FilterEvents(InteractionEvent *interactionEvent, DataNode * /*dataNode*/) { if (interactionEvent->GetSender() == nullptr) return false; if (interactionEvent->GetSender()->GetMapperID() == BaseRenderer::Standard3D) return false; return true; } bool mitk::DisplayInteractor::GetBoolProperty(mitk::PropertyList::Pointer propertyList, const char *propertyName, bool defaultValue) { std::string valueAsString; if (!propertyList->GetStringProperty(propertyName, valueAsString)) { return defaultValue; } else { if (valueAsString == "true") { return true; } else { return false; } } } mitk::DataNode::Pointer mitk::DisplayInteractor::GetTopLayerNode(mitk::DataStorage::SetOfObjects::ConstPointer nodes, mitk::Point3D worldposition, BaseRenderer *ren) { mitk::DataNode::Pointer node; if (nodes.IsNotNull()) { int maxlayer = -32768; bool isHelper(false); for (unsigned int x = 0; x < nodes->size(); x++) { nodes->at(x)->GetBoolProperty("helper object", isHelper); if (nodes->at(x)->GetData()->GetGeometry()->IsInside(worldposition) && isHelper == false) { int layer = 0; if (!(nodes->at(x)->GetIntProperty("layer", layer))) continue; if (layer > maxlayer) { if (static_cast(nodes->at(x))->IsVisible(ren)) { node = nodes->at(x); maxlayer = layer; } } } } } return node; } diff --git a/Modules/Core/src/Interactions/mitkEventConfig.cpp b/Modules/Core/src/Interactions/mitkEventConfig.cpp index 0240e2a8d7..f159b27ae0 100755 --- a/Modules/Core/src/Interactions/mitkEventConfig.cpp +++ b/Modules/Core/src/Interactions/mitkEventConfig.cpp @@ -1,410 +1,410 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkEventConfig.h" #include "mitkEventFactory.h" #include "mitkInteractionEvent.h" #include "mitkInteractionEventConst.h" #include "mitkInteractionKeyEvent.h" #include "mitkInternalEvent.h" // VTK #include #include // us #include "usGetModuleContext.h" #include "usModule.h" #include "usModuleResource.h" #include "usModuleResourceStream.h" namespace mitk { class EventConfigXMLParser : public vtkXMLParser { public: EventConfigXMLParser(EventConfigPrivate *d); protected: /** * @brief Derived from XMLReader **/ void StartElement(const char *elementName, const char **atts) override; /** * @brief Derived from XMLReader **/ void EndElement(const char *elementName) override; std::string ReadXMLStringAttribute(const std::string &name, const char **atts); bool ReadXMLBooleanAttribute(const std::string &name, const char **atts); private: EventConfigPrivate *const d; }; struct EventConfigPrivate : public us::SharedData { EventConfigPrivate(); EventConfigPrivate(const EventConfigPrivate &other); struct EventMapping { std::string variantName; InteractionEvent::ConstPointer interactionEvent; }; typedef std::list EventListType; /** * Checks if mapping with the same parameters already exists, if so, it is replaced, * else the new mapping added */ void InsertMapping(const EventMapping &mapping); void CopyMapping(const EventListType); /** * @brief List of all global properties of the config object. */ PropertyList::Pointer m_PropertyList; /** * @brief Temporal list of all prMousePressEventoperties of a Event. Used to parse an Input-Event and collect all * parameters between the two * and tags. */ PropertyList::Pointer m_EventPropertyList; EventMapping m_CurrEventMapping; /** * Stores InteractionEvents and their corresponding VariantName */ EventListType m_EventList; bool m_Errors; // use member, because of inheritance from vtkXMLParser we can't return a success value for parsing the // file. EventConfigXMLParser m_XmlParser; }; } mitk::EventConfigPrivate::EventConfigPrivate() : m_PropertyList(PropertyList::New()), m_EventPropertyList(PropertyList::New()), m_Errors(false), m_XmlParser(this) { // Avoid VTK warning: Trying to delete object with non-zero reference count. m_XmlParser.SetReferenceCount(0); } mitk::EventConfigPrivate::EventConfigPrivate(const EventConfigPrivate &other) : us::SharedData(other), m_PropertyList(other.m_PropertyList->Clone()), m_EventPropertyList(other.m_EventPropertyList->Clone()), m_CurrEventMapping(other.m_CurrEventMapping), m_EventList(other.m_EventList), m_Errors(other.m_Errors), m_XmlParser(this) { // Avoid VTK warning: Trying to delete object with non-zero reference count. m_XmlParser.SetReferenceCount(0); } void mitk::EventConfigPrivate::InsertMapping(const EventMapping &mapping) { - for (EventListType::iterator it = m_EventList.begin(); it != m_EventList.end(); ++it) + for (auto it = m_EventList.begin(); it != m_EventList.end(); ++it) { if (*(it->interactionEvent) == *mapping.interactionEvent) { // MITK_INFO<< "Configuration overwritten:" << (*it).variantName; m_EventList.erase(it); break; } } m_EventList.push_back(mapping); } void mitk::EventConfigPrivate::CopyMapping(const EventListType eventList) { EventListType::const_iterator iter; for (iter = eventList.begin(); iter != eventList.end(); ++iter) { InsertMapping(*(iter)); } } mitk::EventConfigXMLParser::EventConfigXMLParser(EventConfigPrivate *d) : d(d) { } void mitk::EventConfigXMLParser::StartElement(const char *elementName, const char **atts) { std::string name(elementName); if (name == InteractionEventConst::xmlTagConfigRoot()) { // } else if (name == InteractionEventConst::xmlTagParam()) { const std::string name = ReadXMLStringAttribute(InteractionEventConst::xmlParameterName(), atts); const std::string value = ReadXMLStringAttribute(InteractionEventConst::xmlParameterValue(), atts); d->m_PropertyList->SetStringProperty(name.c_str(), value.c_str()); } else if (name == InteractionEventConst::xmlTagEventVariant()) { const std::string eventClass = ReadXMLStringAttribute(InteractionEventConst::xmlParameterEventClass(), atts); const std::string eventVariant = ReadXMLStringAttribute(InteractionEventConst::xmlParameterName(), atts); // New list in which all parameters are stored that are given within the tag d->m_EventPropertyList = PropertyList::New(); d->m_EventPropertyList->SetStringProperty(InteractionEventConst::xmlParameterEventClass().c_str(), eventClass.c_str()); d->m_EventPropertyList->SetStringProperty(InteractionEventConst::xmlParameterEventVariant().c_str(), eventVariant.c_str()); d->m_CurrEventMapping.variantName = eventVariant; } else if (name == InteractionEventConst::xmlTagAttribute()) { // Attributes that describe an Input Event, such as which MouseButton triggered the event,or which modifier keys are // pressed const std::string name = ReadXMLStringAttribute(InteractionEventConst::xmlParameterName(), atts); const std::string value = ReadXMLStringAttribute(InteractionEventConst::xmlParameterValue(), atts); d->m_EventPropertyList->SetStringProperty(name.c_str(), value.c_str()); } } void mitk::EventConfigXMLParser::EndElement(const char *elementName) { const std::string name(elementName); // At end of input section, all necessary infos are collected to created an interaction event. if (name == InteractionEventConst::xmlTagEventVariant()) { InteractionEvent::Pointer event = EventFactory::CreateEvent(d->m_EventPropertyList); if (event.IsNotNull()) { d->m_CurrEventMapping.interactionEvent = event; d->InsertMapping(d->m_CurrEventMapping); } else { MITK_WARN << "EventConfig: Unknown Event-Type in config. Entry skipped: " << name; } } } std::string mitk::EventConfigXMLParser::ReadXMLStringAttribute(const std::string &name, const char **atts) { if (atts) { const char **attsIter = atts; while (*attsIter) { if (name == *attsIter) { attsIter++; return *attsIter; } attsIter += 2; } } return std::string(); } bool mitk::EventConfigXMLParser::ReadXMLBooleanAttribute(const std::string &name, const char **atts) { std::string s = ReadXMLStringAttribute(name, atts); std::transform(s.begin(), s.end(), s.begin(), ::toupper); return s == "TRUE"; } mitk::EventConfig::EventConfig() : d(new EventConfigPrivate) { } mitk::EventConfig::EventConfig(const EventConfig &other) : d(other.d) { } mitk::EventConfig::EventConfig(const std::string &filename, const us::Module *module) : d(new EventConfigPrivate) { if (module == nullptr) { module = us::GetModuleContext()->GetModule(); } us::ModuleResource resource = module->GetResource("Interactions/" + filename); if (!resource.IsValid()) { MITK_ERROR << "Resource not valid. State machine pattern in module " << module->GetName() << " not found: /Interactions/" << filename; return; } EventConfig newConfig; us::ModuleResourceStream stream(resource); newConfig.d->m_XmlParser.SetStream(&stream); bool success = newConfig.d->m_XmlParser.Parse() && !newConfig.d->m_Errors; if (success) { *this = newConfig; } } mitk::EventConfig::EventConfig(std::istream &inputStream) : d(new EventConfigPrivate) { EventConfig newConfig; newConfig.d->m_XmlParser.SetStream(&inputStream); bool success = newConfig.d->m_XmlParser.Parse() && !newConfig.d->m_Errors; if (success) { *this = newConfig; } } mitk::EventConfig::EventConfig(const std::vector &configDescription) : d(new EventConfigPrivate) { - std::vector::const_iterator it_end = configDescription.end(); - for (std::vector::const_iterator it = configDescription.begin(); it != it_end; ++it) + auto it_end = configDescription.end(); + for (auto it = configDescription.begin(); it != it_end; ++it) { std::string typeVariant; (*it)->GetStringProperty(InteractionEventConst::xmlTagEventVariant().c_str(), typeVariant); if (typeVariant != "") { InteractionEvent::Pointer event = EventFactory::CreateEvent(*it); if (event.IsNotNull()) { d->m_CurrEventMapping.interactionEvent = event; std::string eventVariant; (*it)->GetStringProperty(InteractionEventConst::xmlTagEventVariant().c_str(), eventVariant); d->m_CurrEventMapping.variantName = eventVariant; d->InsertMapping(d->m_CurrEventMapping); } else { MITK_WARN << "EventConfig: Unknown Event-Type in config. When constructing from PropertyList."; } } else { (*it)->GetStringProperty(InteractionEventConst::xmlTagParam().c_str(), typeVariant); if (typeVariant != "") { std::string name, value; (*it)->GetStringProperty(InteractionEventConst::xmlParameterName().c_str(), name); (*it)->GetStringProperty(InteractionEventConst::xmlParameterValue().c_str(), value); d->m_PropertyList->SetStringProperty(name.c_str(), value.c_str()); } } } } mitk::EventConfig &mitk::EventConfig::operator=(const mitk::EventConfig &other) { d = other.d; return *this; } mitk::EventConfig::~EventConfig() { } bool mitk::EventConfig::IsValid() const { return !(d->m_EventList.empty() && d->m_PropertyList->IsEmpty()); } bool mitk::EventConfig::AddConfig(const std::string &fileName, const us::Module *module) { if (module == nullptr) { module = us::GetModuleContext()->GetModule(); } us::ModuleResource resource = module->GetResource("Interactions/" + fileName); if (!resource.IsValid()) { MITK_ERROR << "Resource not valid. State machine pattern in module " << module->GetName() << " not found: /Interactions/" << fileName; return false; } EventConfig newConfig(*this); us::ModuleResourceStream stream(resource); newConfig.d->m_XmlParser.SetStream(&stream); bool success = newConfig.d->m_XmlParser.Parse() && !newConfig.d->m_Errors; if (success) { *this = newConfig; } return success; } bool mitk::EventConfig::AddConfig(const EventConfig &config) { if (!config.IsValid()) return false; d->m_PropertyList->ConcatenatePropertyList(config.d->m_PropertyList->Clone(), true); d->m_EventPropertyList = config.d->m_EventPropertyList->Clone(); d->m_CurrEventMapping = config.d->m_CurrEventMapping; d->CopyMapping(config.d->m_EventList); return true; } mitk::PropertyList::Pointer mitk::EventConfig::GetAttributes() const { return d->m_PropertyList; } std::string mitk::EventConfig::GetMappedEvent(const EventType &interactionEvent) const { // internal events are excluded from mapping if (std::strcmp(interactionEvent->GetNameOfClass(), "InternalEvent") == 0) { - InternalEvent *internalEvent = dynamic_cast(interactionEvent.GetPointer()); + auto *internalEvent = dynamic_cast(interactionEvent.GetPointer()); return internalEvent->GetSignalName(); } - for (EventConfigPrivate::EventListType::const_iterator it = d->m_EventList.begin(); it != d->m_EventList.end(); ++it) + for (auto it = d->m_EventList.begin(); it != d->m_EventList.end(); ++it) { if (*(it->interactionEvent) == *interactionEvent) { return (*it).variantName; } } // if this part is reached, no mapping has been found, // so here we handle key events and map a key event to the string "Std" + letter/code // so "A" will be returned as "StdA" if (std::strcmp(interactionEvent->GetNameOfClass(), "InteractionKeyEvent") == 0) { - InteractionKeyEvent *keyEvent = dynamic_cast(interactionEvent.GetPointer()); + auto *keyEvent = dynamic_cast(interactionEvent.GetPointer()); return ("Std" + keyEvent->GetKey()); } return ""; } void mitk::EventConfig::ClearConfig() { d->m_PropertyList->Clear(); d->m_EventPropertyList->Clear(); d->m_CurrEventMapping.variantName.clear(); d->m_CurrEventMapping.interactionEvent = nullptr; d->m_EventList.clear(); d->m_Errors = false; } diff --git a/Modules/Core/src/Interactions/mitkEventFactory.cpp b/Modules/Core/src/Interactions/mitkEventFactory.cpp index 744dc3109d..86012729eb 100755 --- a/Modules/Core/src/Interactions/mitkEventFactory.cpp +++ b/Modules/Core/src/Interactions/mitkEventFactory.cpp @@ -1,579 +1,579 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkEventFactory.h" #include #include #include #include #include #include #include #include #include #include #include #include namespace { std::vector &split(const std::string &s, char delim, std::vector &elems) { std::stringstream ss(s); std::string item; while (std::getline(ss, item, delim)) { elems.push_back(item); } return elems; } std::vector split(const std::string &s, char delim) { std::vector elems; return split(s, delim, elems); } } /** * @brief GetEventButton Return EventButton as String * @param event * @return */ static std::string GetButtonState(mitk::InteractionEvent *event) { mitk::InteractionEvent::MouseButtons buttonState = mitk::InteractionEvent::NoButton; std::string eventClass = event->GetNameOfClass(); std::transform(eventClass.cbegin(), eventClass.cend(), eventClass.begin(), ::toupper); std::string strButtonState = ""; if (eventClass == "MOUSEPRESSEVENT") { - mitk::MousePressEvent *mme = dynamic_cast(event); + auto *mme = dynamic_cast(event); buttonState = mme->GetButtonStates(); } if (eventClass == "MOUSERELEASEEVENT") { - mitk::MouseReleaseEvent *mme = dynamic_cast(event); + auto *mme = dynamic_cast(event); buttonState = mme->GetButtonStates(); } if (eventClass == "MOUSEDOUBLECLICKEVENT") { - mitk::MouseDoubleClickEvent *mme = dynamic_cast(event); + auto *mme = dynamic_cast(event); buttonState = mme->GetButtonStates(); } if (eventClass == "MOUSEMOVEEVENT") { - mitk::MouseMoveEvent *mme = dynamic_cast(event); + auto *mme = dynamic_cast(event); buttonState = mme->GetButtonStates(); } if (eventClass == "MOUSEWHEELEVENT") { - mitk::MouseWheelEvent *mme = dynamic_cast(event); + auto *mme = dynamic_cast(event); buttonState = mme->GetButtonStates(); } if (buttonState & mitk::InteractionEvent::LeftMouseButton) { strButtonState = "LeftMouseButton"; } if (buttonState & mitk::InteractionEvent::RightMouseButton) { if (strButtonState != "") strButtonState += ","; strButtonState += "RightMouseButton"; } if (buttonState & mitk::InteractionEvent::MiddleMouseButton) { if (strButtonState != "") strButtonState += ","; strButtonState += "MiddleMouseButton"; } return strButtonState; } /** * @brief GetModifierState Return ModifierState as String * @param event * @return */ static std::string GetModifierState(mitk::InteractionEvent *event) { mitk::InteractionEvent::ModifierKeys modifierKeys = mitk::InteractionEvent::NoKey; std::string eventClass = event->GetNameOfClass(); std::transform(eventClass.cbegin(), eventClass.cend(), eventClass.begin(), ::toupper); std::string strModKeys = ""; // TODO Add InteractionKey if (eventClass == "MOUSEPRESSEVENT") { - mitk::MousePressEvent *mme = dynamic_cast(event); + auto *mme = dynamic_cast(event); modifierKeys = mme->GetModifiers(); } if (eventClass == "MOUSERELEASEEVENT") { - mitk::MouseReleaseEvent *mme = dynamic_cast(event); + auto *mme = dynamic_cast(event); modifierKeys = mme->GetModifiers(); } if (eventClass == "MOUSEDOUBLECLICKEVENT") { - mitk::MouseDoubleClickEvent *mme = dynamic_cast(event); + auto *mme = dynamic_cast(event); modifierKeys = mme->GetModifiers(); } if (eventClass == "MOUSEMOVEEVENT") { - mitk::MouseMoveEvent *mme = dynamic_cast(event); + auto *mme = dynamic_cast(event); modifierKeys = mme->GetModifiers(); } if (eventClass == "MOUSEWHEELEVENT") { - mitk::MouseWheelEvent *mme = dynamic_cast(event); + auto *mme = dynamic_cast(event); modifierKeys = mme->GetModifiers(); } if (modifierKeys & mitk::InteractionEvent::ShiftKey) { strModKeys = "SHIFT"; } if (modifierKeys & mitk::InteractionEvent::ControlKey) { if (strModKeys != "") strModKeys += ","; strModKeys += "CTRL"; } if (modifierKeys & mitk::InteractionEvent::AltKey) { if (strModKeys != "") strModKeys += ","; strModKeys += "ALT"; } return strModKeys; } /** * @brief GetEventButton Return EventButton as String * @param event * @return */ static std::string GetEventButton(mitk::InteractionEvent *event) { mitk::InteractionEvent::MouseButtons button = mitk::InteractionEvent::NoButton; std::string eventClass = event->GetNameOfClass(); std::transform(eventClass.cbegin(), eventClass.cend(), eventClass.begin(), ::toupper); std::string stdButton = ""; // TODO Add InteractionKey if (eventClass == "MOUSEPRESSEVENT") { - mitk::MousePressEvent *mme = dynamic_cast(event); + auto *mme = dynamic_cast(event); button = mme->GetEventButton(); } if (eventClass == "MOUSERELEASEEVENT") { - mitk::MouseReleaseEvent *mme = dynamic_cast(event); + auto *mme = dynamic_cast(event); button = mme->GetEventButton(); } if (eventClass == "MOUSEDOUBLECLICKEVENT") { - mitk::MouseDoubleClickEvent *mme = dynamic_cast(event); + auto *mme = dynamic_cast(event); button = mme->GetEventButton(); } if (button & mitk::InteractionEvent::LeftMouseButton) { stdButton = "LeftMouseButton"; } if (button & mitk::InteractionEvent::RightMouseButton) { stdButton = "RightMouseButton"; } if (button & mitk::InteractionEvent::MiddleMouseButton) { stdButton = "MiddleMouseButton"; } return stdButton; } /** * @brief GetPosition Return World Position as String * @param event * @return */ static std::string GetPositionInWorld(mitk::InteractionEvent *event) { std::stringstream ss; - mitk::InteractionPositionEvent *pe = dynamic_cast(event); + auto *pe = dynamic_cast(event); if (pe != nullptr) { mitk::Point3D p = pe->GetPositionInWorld(); ss << p[0] << "," << p[1] << "," << p[2]; } return ss.str(); } /** * @brief GetPositionOnScreen Return PositionOnScreen as String * @param event * @return */ static std::string GetPositionOnScreen(mitk::InteractionEvent *event) { std::stringstream ss; - mitk::InteractionPositionEvent *pe = dynamic_cast(event); + auto *pe = dynamic_cast(event); if (pe != nullptr) { mitk::Point2D p = pe->GetPointerPositionOnScreen(); ss << p[0] << "," << p[1]; } return ss.str(); } mitk::InteractionEvent::Pointer mitk::EventFactory::CreateEvent(PropertyList::Pointer list) { // std::string eventClass, eventVariant; list->GetStringProperty(InteractionEventConst::xmlParameterEventClass().c_str(), eventClass); list->GetStringProperty(InteractionEventConst::xmlParameterEventVariant().c_str(), eventVariant); // Query all possible attributes, if they are not present, set their default values. // Position Events & Key Events std::string strModifiers; InteractionEvent::ModifierKeys modifiers = InteractionEvent::NoKey; std::string strEventButton; InteractionEvent::MouseButtons eventButton = InteractionEvent::NoButton; std::string strButtonState; InteractionEvent::MouseButtons buttonState = InteractionEvent::NoButton; std::string strKey; std::string key; std::string strWheelDelta; int wheelDelta; std::string strSignalName = ""; Point2D pos; pos.Fill(0); std::string strPos; // Position on screen if (list->GetStringProperty(InteractionEventConst::xmlEventPropertyPositionOnScreen().c_str(), strPos)) { // split comma separated string int commaPos; commaPos = strPos.find_first_of(','); pos[0] = static_cast(std::atof(strPos.substr(0, commaPos).c_str())); pos[1] = static_cast(std::atof(strPos.substr(commaPos + 1, strPos.length()).c_str())); } std::string strWorld; Point3D worldPos; worldPos.Fill(0); // Position in world coordinates if (list->GetStringProperty(InteractionEventConst::xmlEventPropertyPositionInWorld().c_str(), strWorld)) { const std::vector coords = split(strWorld, ','); int i = 0; - for (std::vector::const_iterator it = coords.cbegin(); it != coords.cend(); ++it, ++i) + for (auto it = coords.cbegin(); it != coords.cend(); ++it, ++i) { worldPos[i] = atof((*it).c_str()); } } // Parse modifier information if (list->GetStringProperty(InteractionEventConst::xmlEventPropertyModifier().c_str(), strModifiers)) { std::vector mods = split(strModifiers, ','); - for (std::vector::iterator it = mods.begin(); it != mods.end(); ++it) + for (auto it = mods.begin(); it != mods.end(); ++it) { std::transform((*it).cbegin(), (*it).cend(), (*it).begin(), ::toupper); if (*it == "CTRL") { modifiers = modifiers | InteractionEvent::ControlKey; } else if (*it == "ALT") { modifiers = modifiers | InteractionEvent::AltKey; } else if (*it == "SHIFT") { modifiers = modifiers | InteractionEvent::ShiftKey; } else { MITK_WARN << "mitkEventFactory: Invalid event modifier in config file :" << (*it); } } } // Set EventButton if (list->GetStringProperty(InteractionEventConst::xmlEventPropertyEventButton().c_str(), strEventButton)) { std::transform(strEventButton.cbegin(), strEventButton.cend(), strEventButton.begin(), ::toupper); if (strEventButton == "MIDDLEMOUSEBUTTON") { eventButton = InteractionEvent::MiddleMouseButton; } else if (strEventButton == "LEFTMOUSEBUTTON") { eventButton = InteractionEvent::LeftMouseButton; } else if (strEventButton == "RIGHTMOUSEBUTTON") { eventButton = InteractionEvent::RightMouseButton; } else { MITK_WARN << "mitkEventFactory: Invalid event button in config file: " << strEventButton; } } // Parse ButtonStates if (list->GetStringProperty(InteractionEventConst::xmlEventPropertyButtonState().c_str(), strButtonState)) { std::vector mods = split(strButtonState, ','); - for (std::vector::iterator it = mods.begin(); it != mods.end(); ++it) + for (auto it = mods.begin(); it != mods.end(); ++it) { std::transform((*it).cbegin(), (*it).cend(), (*it).begin(), ::toupper); if (*it == "MIDDLEMOUSEBUTTON") { buttonState = buttonState | InteractionEvent::MiddleMouseButton; } else if (*it == "LEFTMOUSEBUTTON") { buttonState = buttonState | InteractionEvent::LeftMouseButton; } else if (*it == "RIGHTMOUSEBUTTON") { buttonState = buttonState | InteractionEvent::RightMouseButton; } else { MITK_WARN << "mitkEventFactory: Invalid event buttonstate in config file:" << (*it); } } } // Key if (!list->GetStringProperty(InteractionEventConst::xmlEventPropertyKey().c_str(), strKey)) { key = ""; } else { key = strKey; } // WheelDelta if (!list->GetStringProperty(InteractionEventConst::xmlEventPropertyScrollDirection().c_str(), strWheelDelta)) { wheelDelta = 0; } else { std::transform(strWheelDelta.cbegin(), strWheelDelta.cend(), strWheelDelta.begin(), ::toupper); if (strWheelDelta == "DOWN") { wheelDelta = -1; } else { wheelDelta = 1; } } // Internal Signals Name list->GetStringProperty(InteractionEventConst::xmlEventPropertySignalName().c_str(), strSignalName); // Get BaseRenderer by name mitk::BaseRenderer *renderer = nullptr; std::string strRenderer; // only search for a renderer if there is at least one renderer registered if (mitk::BaseRenderer::baseRendererMap.size() > 0) { if (list->GetStringProperty(mitk::InteractionEventConst::xmlEventPropertyRendererName().c_str(), strRenderer)) { // look up for renderer registered with the name in xml file renderer = mitk::BaseRenderer::GetByName(strRenderer); } // if not found always use first registered renderer if (renderer == nullptr) renderer = (*(mitk::BaseRenderer::baseRendererMap.cbegin())).second; } /* * Here the objects are created */ mitk::InteractionEvent::Pointer event; std::transform(eventClass.cbegin(), eventClass.cend(), eventClass.begin(), ::toupper); if (eventClass == "MOUSEPRESSEVENT") { // buttonstates incorporate the event button (as in Qt) buttonState = buttonState | eventButton; event = MousePressEvent::New(renderer, pos, buttonState, modifiers, eventButton); } else if (eventClass == "MOUSEDOUBLECLICKEVENT") { buttonState = buttonState | eventButton; event = MouseDoubleClickEvent::New(renderer, pos, buttonState, modifiers, eventButton); } else if (eventClass == "MOUSEMOVEEVENT") { event = MouseMoveEvent::New(renderer, pos, buttonState, modifiers); } else if (eventClass == "MOUSERELEASEEVENT") { event = MouseReleaseEvent::New(renderer, pos, buttonState, modifiers, eventButton); } else if (eventClass == "INTERACTIONKEYEVENT") { event = InteractionKeyEvent::New(renderer, key, modifiers); } else if (eventClass == "MOUSEWHEELEVENT") { event = MouseWheelEvent::New(renderer, pos, buttonState, modifiers, wheelDelta); } else if (eventClass == "INTERACTIONPOSITIONEVENT") { event = InteractionPositionEvent::New(renderer, pos); } else if (eventClass == "INTERNALEVENT") { event = InternalEvent::New(renderer, nullptr, strSignalName); } else if (eventClass == "INTERACTIONEVENT") { event = InteractionEvent::New(renderer); } if (event.IsNull()) { MITK_WARN << "Event couldn't be constructed. Please check your StateMachine patterns and config files\n for the " "following event class, which is not valid: " << eventClass; return nullptr; } return event; } std::string mitk::EventFactory::EventToXML(mitk::InteractionEvent *event) { - InternalEvent *ie = dynamic_cast(event); + auto *ie = dynamic_cast(event); if (ie != nullptr) return ""; std::string eventClass = event->GetNameOfClass(); std::string eventXML = "<" + InteractionEventConst::xmlTagEventVariant() + " " + InteractionEventConst::xmlParameterEventClass() + "=\""; std::transform(eventClass.cbegin(), eventClass.cend(), eventClass.begin(), ::toupper); eventXML += eventClass + "\" >\n"; // here follow event specific attributes if (eventClass == "MOUSEPRESSEVENT" || eventClass == "MOUSERELEASEEVENT" || eventClass == "MOUSEDOUBLECLICKEVENT" || eventClass == "MOUSEMOVEEVENT" || eventClass == "MOUSEWHEELEVENT") { if (!(eventClass == "MOUSEMOVEEVENT") && !(eventClass == "MOUSEWHEELEVENT")) { // EventButton eventXML += " <" + InteractionEventConst::xmlTagAttribute() + " " + InteractionEventConst::xmlParameterName() + "=\"" + InteractionEventConst::xmlEventPropertyEventButton() + "\" "; eventXML += InteractionEventConst::xmlParameterValue() + "=\""; eventXML += GetEventButton(event); eventXML += "\"/>\n"; } // ButtonState if (GetButtonState(event) != "") { eventXML += " <" + InteractionEventConst::xmlTagAttribute() + " " + InteractionEventConst::xmlParameterName() + "=\"" + InteractionEventConst::xmlEventPropertyButtonState() + "\" "; eventXML += InteractionEventConst::xmlParameterValue() + "=\""; eventXML += GetButtonState(event); eventXML += "\"/>\n"; } // Modifiers if (GetModifierState(event) != "") { eventXML += " <" + InteractionEventConst::xmlTagAttribute() + " " + InteractionEventConst::xmlParameterName() + "=\"" + InteractionEventConst::xmlEventPropertyModifier() + "\" "; eventXML += InteractionEventConst::xmlParameterValue() + "=\""; eventXML += GetModifierState(event); eventXML += "\"/>\n"; } // Position on Screen eventXML += " <" + InteractionEventConst::xmlTagAttribute() + " " + InteractionEventConst::xmlParameterName() + "=\"" + InteractionEventConst::xmlEventPropertyPositionOnScreen() + "\" "; eventXML += InteractionEventConst::xmlParameterValue() + "=\""; eventXML += GetPositionOnScreen(event); eventXML += "\"/>\n"; // Position in World eventXML += " <" + InteractionEventConst::xmlTagAttribute() + " " + InteractionEventConst::xmlParameterName() + "=\"" + InteractionEventConst::xmlEventPropertyPositionInWorld() + "\" "; eventXML += InteractionEventConst::xmlParameterValue() + "=\""; eventXML += GetPositionInWorld(event); eventXML += "\"/>\n"; } else if (eventClass == "INTERACTIONKEYEVENT") { - mitk::InteractionKeyEvent *ke = dynamic_cast(event); + auto *ke = dynamic_cast(event); // key eventXML += " <" + InteractionEventConst::xmlTagAttribute() + " " + InteractionEventConst::xmlParameterName() + "=\"" + InteractionEventConst::xmlEventPropertyKey() + "\" "; eventXML += InteractionEventConst::xmlParameterValue() + "=\""; eventXML += ke->GetKey(); eventXML += "\"/>\n"; } else { MITK_WARN << "Event not recognized, discarding event of type " << event->GetNameOfClass(); } if (eventClass == "MOUSEWHEELEVENT") { - MouseWheelEvent *we = dynamic_cast(event); + auto *we = dynamic_cast(event); int delta = we->GetWheelDelta(); std::stringstream ss; ss << delta; eventXML += " <" + InteractionEventConst::xmlTagAttribute() + " " + InteractionEventConst::xmlParameterName() + "=\"" + InteractionEventConst::xmlEventPropertyWheelDelta() + "\" "; eventXML += InteractionEventConst::xmlParameterValue() + "=\""; eventXML += ss.str(); eventXML += "\"/>\n"; eventXML += " <" + InteractionEventConst::xmlTagAttribute() + " " + InteractionEventConst::xmlParameterName() + "=\"" + InteractionEventConst::xmlEventPropertyScrollDirection() + "\" "; eventXML += InteractionEventConst::xmlParameterValue() + "=\""; eventXML += delta < 0 ? "DOWN" : "UP"; eventXML += "\"/>\n"; } // Renderer name eventXML += " <" + InteractionEventConst::xmlTagAttribute() + " " + InteractionEventConst::xmlParameterName() + "=\"" + InteractionEventConst::xmlEventPropertyRendererName() + "\" "; eventXML += InteractionEventConst::xmlParameterValue() + "=\""; eventXML += event->GetSender()->GetName(); eventXML += "\"/>\n"; // closing tag: eventXML += ""; return eventXML; } diff --git a/Modules/Core/src/Interactions/mitkEventRecorder.cpp b/Modules/Core/src/Interactions/mitkEventRecorder.cpp index 85164e96d5..3ccb25974f 100644 --- a/Modules/Core/src/Interactions/mitkEventRecorder.cpp +++ b/Modules/Core/src/Interactions/mitkEventRecorder.cpp @@ -1,192 +1,192 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkEventRecorder.h" #include "mitkEventFactory.h" #include "mitkInteractionEvent.h" #include "mitkInteractionEventConst.h" #include "vtkCamera.h" #include "mitkBaseRenderer.h" static void WriteEventXMLHeader(std::ofstream &stream) { stream << mitk::InteractionEventConst::xmlHead() << "\n"; } static void WriteEventXMLConfig(std::ofstream &stream) { // stream << " <" << mitk::InteractionEventConst::xmlTagConfigRoot() << ">\n"; // write renderer config // for all registered 2D renderers write name and viewdirection. - mitk::BaseRenderer::BaseRendererMapType::iterator rendererIterator = mitk::BaseRenderer::baseRendererMap.begin(); - mitk::BaseRenderer::BaseRendererMapType::iterator end = mitk::BaseRenderer::baseRendererMap.end(); + auto rendererIterator = mitk::BaseRenderer::baseRendererMap.begin(); + auto end = mitk::BaseRenderer::baseRendererMap.end(); for (; rendererIterator != end; ++rendererIterator) { std::string rendererName = (*rendererIterator).second->GetName(); mitk::SliceNavigationController::ViewDirection viewDirection = (*rendererIterator).second->GetSliceNavigationController()->GetDefaultViewDirection(); mitk::BaseRenderer::MapperSlotId mapperID = (*rendererIterator).second->GetMapperID(); // stream << " <" << mitk::InteractionEventConst::xmlTagRenderer() << " " << mitk::InteractionEventConst::xmlEventPropertyRendererName() << "=\"" << rendererName << "\" " << mitk::InteractionEventConst::xmlEventPropertyViewDirection() << "=\"" << viewDirection << "\" " << mitk::InteractionEventConst::xmlEventPropertyMapperID() << "=\"" << mapperID << "\" " << mitk::InteractionEventConst::xmlRenderSizeX() << "=\"" << (*rendererIterator).second->GetSize()[0] << "\" " << mitk::InteractionEventConst::xmlRenderSizeY() << "=\"" << (*rendererIterator).second->GetSize()[1] << "\" " << mitk::InteractionEventConst::xmlRenderSizeZ() << "=\"" << (*rendererIterator).second->GetSize()[2] << "\" "; ; if ((*rendererIterator).second->GetMapperID() == mitk::BaseRenderer::Standard3D) { // For a 3D render window, rotation and zoom settings are determined by the vtkCamera parameters // these are recorded here: stream << mitk::InteractionEventConst::xmlViewUpX() << "=\"" << (*rendererIterator).second->GetVtkRenderer()->GetActiveCamera()->GetViewUp()[0] << "\" " << mitk::InteractionEventConst::xmlViewUpY() << "=\"" << (*rendererIterator).second->GetVtkRenderer()->GetActiveCamera()->GetViewUp()[1] << "\" " << mitk::InteractionEventConst::xmlViewUpZ() << "=\"" << (*rendererIterator).second->GetVtkRenderer()->GetActiveCamera()->GetViewUp()[2] << "\" " << mitk::InteractionEventConst::xmlCameraFocalPointX() << "=\"" << (*rendererIterator).second->GetVtkRenderer()->GetActiveCamera()->GetFocalPoint()[0] << "\" " << mitk::InteractionEventConst::xmlCameraFocalPointY() << "=\"" << (*rendererIterator).second->GetVtkRenderer()->GetActiveCamera()->GetFocalPoint()[1] << "\" " << mitk::InteractionEventConst::xmlCameraFocalPointZ() << "=\"" << (*rendererIterator).second->GetVtkRenderer()->GetActiveCamera()->GetFocalPoint()[2] << "\" " << mitk::InteractionEventConst::xmlCameraPositionX() << "=\"" << (*rendererIterator).second->GetVtkRenderer()->GetActiveCamera()->GetPosition()[0] << "\" " << mitk::InteractionEventConst::xmlCameraPositionY() << "=\"" << (*rendererIterator).second->GetVtkRenderer()->GetActiveCamera()->GetPosition()[1] << "\" " << mitk::InteractionEventConst::xmlCameraPositionZ() << "=\"" << (*rendererIterator).second->GetVtkRenderer()->GetActiveCamera()->GetPosition()[2] << "\" "; } stream << "/>\n"; } // stream << " \n"; } static void WriteEventXMLEventsOpen(std::ofstream &stream) { stream << " <" << mitk::InteractionEventConst::xmlTagEvents() << ">\n"; } static void WriteEventXMLEventsClose(std::ofstream &stream) { stream << " \n"; } static void WriteEventXMLInteractionsOpen(std::ofstream &stream) { stream << "<" << mitk::InteractionEventConst::xmlTagInteractions() << ">\n"; } static void WriteEventXMLInteractionsClose(std::ofstream &stream) { stream << ""; } static void WriteEventXMLClose(std::ofstream &stream) { WriteEventXMLEventsClose(stream); WriteEventXMLInteractionsClose(stream); } mitk::EventRecorder::EventRecorder() : m_Active(false) { } mitk::EventRecorder::~EventRecorder() { if (m_FileStream.is_open()) { m_FileStream.flush(); m_FileStream.close(); } } void mitk::EventRecorder::Notify(mitk::InteractionEvent *interactionEvent, bool /*isHandled*/) { if (m_FileStream.is_open()) m_FileStream << EventFactory::EventToXML(interactionEvent) << "\n"; } void mitk::EventRecorder::SetEventIgnoreList(std::vector list) { m_IgnoreList = list; } void mitk::EventRecorder::StartRecording() { if (m_FileName == "") { MITK_ERROR << "EventRecorder::StartRecording - Filename needs to be set first."; return; } if (m_FileStream.is_open()) { MITK_ERROR << "EventRecorder::StartRecording - Still recording. Stop recording before starting it again."; return; } m_FileStream.open(m_FileName.c_str(), std::ofstream::out); if (!m_FileStream.good()) { MITK_ERROR << "File " << m_FileName << " could not be opened!"; m_FileStream.close(); return; } m_Active = true; // write head and config // // // // // // ... // // WriteEventXMLHeader(m_FileStream); WriteEventXMLInteractionsOpen(m_FileStream); WriteEventXMLConfig(m_FileStream); WriteEventXMLEventsOpen(m_FileStream); } void mitk::EventRecorder::StopRecording() { if (m_FileStream.is_open()) { // write end tag // // WriteEventXMLClose(m_FileStream); m_FileStream.flush(); m_FileStream.close(); m_Active = false; } } diff --git a/Modules/Core/src/Interactions/mitkEventStateMachine.cpp b/Modules/Core/src/Interactions/mitkEventStateMachine.cpp index eecc488207..930e0a8354 100644 --- a/Modules/Core/src/Interactions/mitkEventStateMachine.cpp +++ b/Modules/Core/src/Interactions/mitkEventStateMachine.cpp @@ -1,349 +1,349 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkEventStateMachine.h" #include "mitkApplicationCursor.h" #include "mitkInteractionEvent.h" #include "mitkStateMachineAction.h" #include "mitkStateMachineCondition.h" #include "mitkStateMachineContainer.h" #include "mitkStateMachineState.h" #include "mitkStateMachineTransition.h" #include "mitkUndoController.h" mitk::EventStateMachine::EventStateMachine() : m_IsActive(true), m_UndoController(nullptr), m_StateMachineContainer(nullptr), m_CurrentState(nullptr), m_MouseCursorSet(false) { if (!m_UndoController) { m_UndoController = new UndoController(UndoController::VERBOSE_LIMITEDLINEARUNDO); // switch to LLU or add LLU /** * here the Undo mechanism is enabled / disabled for all interactors. **/ m_UndoEnabled = true; } } bool mitk::EventStateMachine::LoadStateMachine(const std::string &filename, const us::Module *module) { if (m_StateMachineContainer != nullptr) { m_StateMachineContainer->Delete(); } m_StateMachineContainer = StateMachineContainer::New(); if (m_StateMachineContainer->LoadBehavior(filename, module)) { m_CurrentState = m_StateMachineContainer->GetStartState(); - for (ConditionDelegatesMapType::iterator i = m_ConditionDelegatesMap.begin(); i != m_ConditionDelegatesMap.end(); + for (auto i = m_ConditionDelegatesMap.begin(); i != m_ConditionDelegatesMap.end(); ++i) { delete i->second; } m_ConditionDelegatesMap.clear(); // clear actions map ,and connect all actions as declared in sub-class - for (std::map::iterator i = m_ActionFunctionsMap.begin(); + for (auto i = m_ActionFunctionsMap.begin(); i != m_ActionFunctionsMap.end(); ++i) { delete i->second; } m_ActionFunctionsMap.clear(); - for (ActionDelegatesMapType::iterator i = m_ActionDelegatesMap.begin(); i != m_ActionDelegatesMap.end(); ++i) + for (auto i = m_ActionDelegatesMap.begin(); i != m_ActionDelegatesMap.end(); ++i) { delete i->second; } m_ActionDelegatesMap.clear(); ConnectActionsAndFunctions(); return true; } else { MITK_WARN << "Unable to load StateMachine from file: " << filename; return false; } } mitk::EventStateMachine::~EventStateMachine() { if (m_StateMachineContainer != nullptr) { m_StateMachineContainer->Delete(); } } void mitk::EventStateMachine::AddActionFunction(const std::string &action, mitk::TActionFunctor *functor) { if (!functor) return; // make sure double calls for same action won't cause memory leaks delete m_ActionFunctionsMap[action]; - ActionDelegatesMapType::iterator i = m_ActionDelegatesMap.find(action); + auto i = m_ActionDelegatesMap.find(action); if (i != m_ActionDelegatesMap.end()) { delete i->second; m_ActionDelegatesMap.erase(i); } m_ActionFunctionsMap[action] = functor; } void mitk::EventStateMachine::AddActionFunction(const std::string &action, const ActionFunctionDelegate &delegate) { - std::map::iterator i = m_ActionFunctionsMap.find(action); + auto i = m_ActionFunctionsMap.find(action); if (i != m_ActionFunctionsMap.end()) { delete i->second; m_ActionFunctionsMap.erase(i); } delete m_ActionDelegatesMap[action]; m_ActionDelegatesMap[action] = delegate.Clone(); } void mitk::EventStateMachine::AddConditionFunction(const std::string &condition, const ConditionFunctionDelegate &delegate) { m_ConditionDelegatesMap[condition] = delegate.Clone(); } bool mitk::EventStateMachine::HandleEvent(InteractionEvent *event, DataNode *dataNode) { if (!m_IsActive) return false; if (!FilterEvents(event, dataNode)) { return false; } // Get the transition that can be executed mitk::StateMachineTransition::Pointer transition = GetExecutableTransition(event); // check if the current state holds a transition that works with the given event. if (transition.IsNotNull()) { // all conditions are fulfilled so we can continue with the actions m_CurrentState = transition->GetNextState(); // iterate over all actions in this transition and execute them const ActionVectorType actions = transition->GetActions(); - for (ActionVectorType::const_iterator it = actions.cbegin(); it != actions.cend(); ++it) + for (auto it = actions.cbegin(); it != actions.cend(); ++it) { try { ExecuteAction(*it, event); } catch (const std::exception &e) { MITK_ERROR << "Unhandled excaption caught in ExecuteAction(): " << e.what(); return false; } catch (...) { MITK_ERROR << "Unhandled excaption caught in ExecuteAction()"; return false; } } return true; } return false; } void mitk::EventStateMachine::ConnectActionsAndFunctions() { MITK_WARN << "ConnectActionsAndFunctions in DataInteractor not implemented.\n DataInteractor will not be able to " "process any events."; } bool mitk::EventStateMachine::CheckCondition(const StateMachineCondition &condition, const InteractionEvent *event) { bool retVal = false; ConditionDelegatesMapType::const_iterator delegateIter = m_ConditionDelegatesMap.find(condition.GetConditionName()); if (delegateIter != m_ConditionDelegatesMap.cend()) { retVal = delegateIter->second->Execute(event); } else { MITK_WARN << "No implementation of condition '" << condition.GetConditionName() << "' has been found."; } return retVal; } void mitk::EventStateMachine::ExecuteAction(StateMachineAction *action, InteractionEvent *event) { if (action == nullptr) { return; } // Maps Action-Name to Functor and executes the Functor. ActionDelegatesMapType::const_iterator delegateIter = m_ActionDelegatesMap.find(action->GetActionName()); if (delegateIter != m_ActionDelegatesMap.cend()) { delegateIter->second->Execute(action, event); } else { // try the legacy system std::map::const_iterator functionIter = m_ActionFunctionsMap.find(action->GetActionName()); if (functionIter != m_ActionFunctionsMap.cend()) { functionIter->second->DoAction(action, event); } else { MITK_WARN << "No implementation of action '" << action->GetActionName() << "' has been found."; } } } mitk::StateMachineState *mitk::EventStateMachine::GetCurrentState() const { return m_CurrentState.GetPointer(); } bool mitk::EventStateMachine::FilterEvents(InteractionEvent *interactionEvent, DataNode *dataNode) { if (dataNode == nullptr) { MITK_WARN << "EventStateMachine: Empty DataNode received along with this Event " << interactionEvent; return false; } bool visible = false; if (dataNode->GetBoolProperty("visible", visible, interactionEvent->GetSender()) == false) { // property doesn't exist return false; } return visible; } mitk::StateMachineTransition *mitk::EventStateMachine::GetExecutableTransition(mitk::InteractionEvent *event) { // Map that will contain all conditions that are possibly used by the // transitions std::map conditionsMap; // Get a list of all transitions that match the given event const mitk::StateMachineState::TransitionVector transitionList = m_CurrentState->GetTransitionList(event->GetNameOfClass(), MapToEventVariant(event)); // if there are not transitions, we can return nullptr here. if (transitionList.empty()) { return nullptr; } StateMachineState::TransitionVector::const_iterator transitionIter; ConditionVectorType::const_iterator conditionIter; for (transitionIter = transitionList.cbegin(); transitionIter != transitionList.cend(); ++transitionIter) { bool allConditionsFulfilled(true); // Get all conditions for the current transition const ConditionVectorType conditions = (*transitionIter)->GetConditions(); for (conditionIter = conditions.cbegin(); conditionIter != conditions.cend(); ++conditionIter) { bool currentConditionFulfilled(false); // sequentially check all conditions that we have evaluated above const std::string conditionName = (*conditionIter).GetConditionName(); // Check if the condition has already been evaluated if (conditionsMap.find(conditionName) == conditionsMap.cend()) { // if the condition has not been evaluated yet, do it now and store // the result in the map try { currentConditionFulfilled = CheckCondition((*conditionIter), event); conditionsMap.insert(std::pair(conditionName, currentConditionFulfilled)); } catch (const std::exception &e) { MITK_ERROR << "Unhandled excaption caught in CheckCondition(): " << e.what(); currentConditionFulfilled = false; break; } catch (...) { MITK_ERROR << "Unhandled excaption caught in CheckCondition()"; currentConditionFulfilled = false; break; } } else { // if the condition has been evaluated before, use that result currentConditionFulfilled = conditionsMap[conditionName]; } // set 'allConditionsFulfilled' under consideration of a possible // inversion of the condition if (currentConditionFulfilled == (*conditionIter).IsInverted()) { allConditionsFulfilled = false; break; } } // If all conditions are fulfilled, we execute this transition if (allConditionsFulfilled) { return (*transitionIter); } } // We have found no transition that can be executed, return nullptr return nullptr; } void mitk::EventStateMachine::ResetToStartState() { m_CurrentState = m_StateMachineContainer->GetStartState(); } void mitk::EventStateMachine::SetMouseCursor(const char *xpm[], int hotspotX, int hotspotY) { // Remove previously set mouse cursor if (m_MouseCursorSet) { ApplicationCursor::GetInstance()->PopCursor(); } ApplicationCursor::GetInstance()->PushCursor(xpm, hotspotX, hotspotY); m_MouseCursorSet = true; } void mitk::EventStateMachine::ResetMouseCursor() { if (m_MouseCursorSet) { ApplicationCursor::GetInstance()->PopCursor(); m_MouseCursorSet = false; } } diff --git a/Modules/Core/src/Interactions/mitkInteractionKeyEvent.cpp b/Modules/Core/src/Interactions/mitkInteractionKeyEvent.cpp index 0b806234ce..dbb77c0ee4 100644 --- a/Modules/Core/src/Interactions/mitkInteractionKeyEvent.cpp +++ b/Modules/Core/src/Interactions/mitkInteractionKeyEvent.cpp @@ -1,50 +1,50 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkInteractionKeyEvent.h" mitk::InteractionKeyEvent::InteractionKeyEvent(mitk::BaseRenderer *baseRenderer, const std::string &key, ModifierKeys modifiers = ControlKey) : InteractionEvent(baseRenderer), m_Key(key), m_Modifiers(modifiers) { } mitk::InteractionEvent::ModifierKeys mitk::InteractionKeyEvent::GetModifiers() const { return m_Modifiers; } std::string mitk::InteractionKeyEvent::GetKey() const { return m_Key; } mitk::InteractionKeyEvent::~InteractionKeyEvent() { } bool mitk::InteractionKeyEvent::IsEqual(const mitk::InteractionEvent &interactionEvent) const { - const mitk::InteractionKeyEvent &keyEvent = static_cast(interactionEvent); + const auto &keyEvent = static_cast(interactionEvent); return (this->GetModifiers() == keyEvent.GetModifiers() && this->GetKey() == keyEvent.GetKey() && Superclass::IsEqual(interactionEvent)); } bool mitk::InteractionKeyEvent::IsSuperClassOf(const InteractionEvent::Pointer &baseClass) const { return (dynamic_cast(baseClass.GetPointer()) != nullptr); } diff --git a/Modules/Core/src/Interactions/mitkInternalEvent.cpp b/Modules/Core/src/Interactions/mitkInternalEvent.cpp index 63a8cbcf4f..1a304180c0 100644 --- a/Modules/Core/src/Interactions/mitkInternalEvent.cpp +++ b/Modules/Core/src/Interactions/mitkInternalEvent.cpp @@ -1,50 +1,50 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkInternalEvent.h" #include "mitkDataInteractor.h" mitk::InternalEvent::InternalEvent(mitk::BaseRenderer *baseRenderer, DataInteractor *sourceInteractor, const std::string &signalName) : InteractionEvent(baseRenderer), m_DataInteractor(sourceInteractor), m_SignalName(signalName) { } bool mitk::InternalEvent::IsEqual(const mitk::InteractionEvent &interactionEvent) const { - const mitk::InternalEvent &internalEvent = static_cast(interactionEvent); + const auto &internalEvent = static_cast(interactionEvent); return (m_SignalName == internalEvent.GetSignalName() && Superclass::IsEqual(interactionEvent)); } mitk::InternalEvent::~InternalEvent() { } std::string mitk::InternalEvent::GetSignalName() const { return m_SignalName; } mitk::DataInteractor *mitk::InternalEvent::GetTargetInteractor() const { return m_DataInteractor.GetPointer(); } bool mitk::InternalEvent::IsSuperClassOf(const InteractionEvent::Pointer &baseClass) const { return (nullptr != dynamic_cast(baseClass.GetPointer())); } diff --git a/Modules/Core/src/Interactions/mitkMouseDoubleClickEvent.cpp b/Modules/Core/src/Interactions/mitkMouseDoubleClickEvent.cpp index 34c5fc7888..24664f9c18 100644 --- a/Modules/Core/src/Interactions/mitkMouseDoubleClickEvent.cpp +++ b/Modules/Core/src/Interactions/mitkMouseDoubleClickEvent.cpp @@ -1,76 +1,76 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkMouseDoubleClickEvent.h" #include "mitkException.h" mitk::MouseDoubleClickEvent::MouseDoubleClickEvent(mitk::BaseRenderer *baseRenderer, const mitk::Point2D &mousePosition, MouseButtons buttonStates, ModifierKeys modifiers, MouseButtons eventButton) : InteractionPositionEvent(baseRenderer, mousePosition), m_EventButton(eventButton), m_ButtonStates(buttonStates), m_Modifiers(modifiers) { } mitk::InteractionEvent::MouseButtons mitk::MouseDoubleClickEvent::GetEventButton() const { return m_EventButton; } void mitk::MouseDoubleClickEvent::SetEventButton(MouseButtons buttons) { m_EventButton = buttons; } mitk::InteractionEvent::ModifierKeys mitk::MouseDoubleClickEvent::GetModifiers() const { return m_Modifiers; } mitk::InteractionEvent::MouseButtons mitk::MouseDoubleClickEvent::GetButtonStates() const { return m_ButtonStates; } void mitk::MouseDoubleClickEvent::SetModifiers(ModifierKeys modifiers) { m_Modifiers = modifiers; } void mitk::MouseDoubleClickEvent::SetButtonStates(MouseButtons buttons) { m_ButtonStates = buttons; } mitk::MouseDoubleClickEvent::~MouseDoubleClickEvent() { } bool mitk::MouseDoubleClickEvent::IsEqual(const mitk::InteractionEvent &interactionEvent) const { - const mitk::MouseDoubleClickEvent &mpe = static_cast(interactionEvent); + const auto &mpe = static_cast(interactionEvent); return (this->GetEventButton() == mpe.GetEventButton() && this->GetModifiers() == mpe.GetModifiers() && this->GetButtonStates() == mpe.GetButtonStates() && Superclass::IsEqual(interactionEvent)); } bool mitk::MouseDoubleClickEvent::IsSuperClassOf(const InteractionEvent::Pointer &baseClass) const { return (dynamic_cast(baseClass.GetPointer()) != nullptr); } diff --git a/Modules/Core/src/Interactions/mitkMouseMoveEvent.cpp b/Modules/Core/src/Interactions/mitkMouseMoveEvent.cpp index a909d2d66c..ce700eae42 100644 --- a/Modules/Core/src/Interactions/mitkMouseMoveEvent.cpp +++ b/Modules/Core/src/Interactions/mitkMouseMoveEvent.cpp @@ -1,62 +1,62 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkMouseMoveEvent.h" #include "mitkException.h" mitk::MouseMoveEvent::MouseMoveEvent(mitk::BaseRenderer *baseRenderer, const mitk::Point2D &mousePosition, MouseButtons buttonStates, ModifierKeys modifiers) : InteractionPositionEvent(baseRenderer, mousePosition), m_ButtonStates(buttonStates), m_Modifiers(modifiers) { } mitk::InteractionEvent::ModifierKeys mitk::MouseMoveEvent::GetModifiers() const { return m_Modifiers; } mitk::InteractionEvent::MouseButtons mitk::MouseMoveEvent::GetButtonStates() const { return m_ButtonStates; } void mitk::MouseMoveEvent::SetModifiers(ModifierKeys modifiers) { m_Modifiers = modifiers; } void mitk::MouseMoveEvent::SetButtonStates(MouseButtons buttons) { m_ButtonStates = buttons; } mitk::MouseMoveEvent::~MouseMoveEvent() { } bool mitk::MouseMoveEvent::IsEqual(const mitk::InteractionEvent &interactionEvent) const { - const mitk::MouseMoveEvent &mpe = static_cast(interactionEvent); + const auto &mpe = static_cast(interactionEvent); return (this->GetModifiers() == mpe.GetModifiers() && this->GetButtonStates() == mpe.GetButtonStates() && Superclass::IsEqual(interactionEvent)); } bool mitk::MouseMoveEvent::IsSuperClassOf(const InteractionEvent::Pointer &baseClass) const { return (dynamic_cast(baseClass.GetPointer()) != nullptr); } diff --git a/Modules/Core/src/Interactions/mitkMousePressEvent.cpp b/Modules/Core/src/Interactions/mitkMousePressEvent.cpp index 5f9bc0b87f..96704966fc 100644 --- a/Modules/Core/src/Interactions/mitkMousePressEvent.cpp +++ b/Modules/Core/src/Interactions/mitkMousePressEvent.cpp @@ -1,76 +1,76 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkMousePressEvent.h" #include "mitkException.h" mitk::MousePressEvent::MousePressEvent(mitk::BaseRenderer *baseRenderer, const mitk::Point2D &mousePosition, MouseButtons buttonStates, ModifierKeys modifiers, MouseButtons eventButton) : InteractionPositionEvent(baseRenderer, mousePosition), m_EventButton(eventButton), m_ButtonStates(buttonStates), m_Modifiers(modifiers) { } mitk::InteractionEvent::MouseButtons mitk::MousePressEvent::GetEventButton() const { return m_EventButton; } void mitk::MousePressEvent::SetEventButton(MouseButtons buttons) { m_EventButton = buttons; } mitk::InteractionEvent::ModifierKeys mitk::MousePressEvent::GetModifiers() const { return m_Modifiers; } mitk::InteractionEvent::MouseButtons mitk::MousePressEvent::GetButtonStates() const { return m_ButtonStates; } void mitk::MousePressEvent::SetModifiers(ModifierKeys modifiers) { m_Modifiers = modifiers; } void mitk::MousePressEvent::SetButtonStates(MouseButtons buttons) { m_ButtonStates = buttons; } mitk::MousePressEvent::~MousePressEvent() { } bool mitk::MousePressEvent::IsEqual(const mitk::InteractionEvent &interactionEvent) const { - const mitk::MousePressEvent &mpe = static_cast(interactionEvent); + const auto &mpe = static_cast(interactionEvent); return (this->GetEventButton() == mpe.GetEventButton() && this->GetModifiers() == mpe.GetModifiers() && this->GetButtonStates() == mpe.GetButtonStates() && Superclass::IsEqual(interactionEvent)); } bool mitk::MousePressEvent::IsSuperClassOf(const InteractionEvent::Pointer &baseClass) const { return (dynamic_cast(baseClass.GetPointer()) != nullptr); } diff --git a/Modules/Core/src/Interactions/mitkMouseReleaseEvent.cpp b/Modules/Core/src/Interactions/mitkMouseReleaseEvent.cpp index 5e97114f3e..ccb15e88bc 100644 --- a/Modules/Core/src/Interactions/mitkMouseReleaseEvent.cpp +++ b/Modules/Core/src/Interactions/mitkMouseReleaseEvent.cpp @@ -1,76 +1,76 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkMouseReleaseEvent.h" #include "mitkException.h" mitk::MouseReleaseEvent::MouseReleaseEvent(mitk::BaseRenderer *baseRenderer, const mitk::Point2D &mousePosition, MouseButtons buttonStates, ModifierKeys modifiers, MouseButtons eventButton) : InteractionPositionEvent(baseRenderer, mousePosition), m_EventButton(eventButton), m_ButtonStates(buttonStates), m_Modifiers(modifiers) { } mitk::InteractionEvent::MouseButtons mitk::MouseReleaseEvent::GetEventButton() const { return m_EventButton; } void mitk::MouseReleaseEvent::SetEventButton(MouseButtons buttons) { m_EventButton = buttons; } mitk::InteractionEvent::ModifierKeys mitk::MouseReleaseEvent::GetModifiers() const { return m_Modifiers; } mitk::InteractionEvent::MouseButtons mitk::MouseReleaseEvent::GetButtonStates() const { return m_ButtonStates; } void mitk::MouseReleaseEvent::SetModifiers(ModifierKeys modifiers) { m_Modifiers = modifiers; } void mitk::MouseReleaseEvent::SetButtonStates(MouseButtons buttons) { m_ButtonStates = buttons; } mitk::MouseReleaseEvent::~MouseReleaseEvent() { } bool mitk::MouseReleaseEvent::IsEqual(const mitk::InteractionEvent &interactionEvent) const { - const mitk::MouseReleaseEvent &mre = static_cast(interactionEvent); + const auto &mre = static_cast(interactionEvent); return (this->GetEventButton() == mre.GetEventButton() && this->GetModifiers() == mre.GetModifiers() && this->GetButtonStates() == mre.GetButtonStates() && Superclass::IsEqual(interactionEvent)); } bool mitk::MouseReleaseEvent::IsSuperClassOf(const InteractionEvent::Pointer &baseClass) const { return (dynamic_cast(baseClass.GetPointer()) != nullptr); } diff --git a/Modules/Core/src/Interactions/mitkMouseWheelEvent.cpp b/Modules/Core/src/Interactions/mitkMouseWheelEvent.cpp index 2b9203ce1b..1f8a8aaa42 100644 --- a/Modules/Core/src/Interactions/mitkMouseWheelEvent.cpp +++ b/Modules/Core/src/Interactions/mitkMouseWheelEvent.cpp @@ -1,78 +1,78 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkMouseWheelEvent.h" mitk::MouseWheelEvent::MouseWheelEvent(BaseRenderer *baseRenderer, const Point2D &mousePosition, MouseButtons buttonStates, ModifierKeys modifiers, int wheelDelta) : InteractionPositionEvent(baseRenderer, mousePosition), m_WheelDelta(wheelDelta), m_ButtonStates(buttonStates), m_Modifiers(modifiers) { } int mitk::MouseWheelEvent::GetWheelDelta() const { return m_WheelDelta; } void mitk::MouseWheelEvent::SetWheelDelta(int delta) { m_WheelDelta = delta; } mitk::InteractionEvent::ModifierKeys mitk::MouseWheelEvent::GetModifiers() const { return m_Modifiers; } mitk::InteractionEvent::MouseButtons mitk::MouseWheelEvent::GetButtonStates() const { return m_ButtonStates; } void mitk::MouseWheelEvent::SetModifiers(ModifierKeys modifiers) { m_Modifiers = modifiers; } void mitk::MouseWheelEvent::SetButtonStates(MouseButtons buttons) { m_ButtonStates = buttons; } mitk::MouseWheelEvent::~MouseWheelEvent() { } bool mitk::MouseWheelEvent::IsEqual(const mitk::InteractionEvent &interactionEvent) const { - const mitk::MouseWheelEvent &mwe = static_cast(interactionEvent); + const auto &mwe = static_cast(interactionEvent); return ((this->GetWheelDelta() * mwe.GetWheelDelta() >= 0) // Consider WheelEvents to be equal if the scrolling is done in the same direction. && this->GetModifiers() == mwe.GetModifiers() && this->GetButtonStates() == mwe.GetButtonStates() && Superclass::IsEqual(interactionEvent)); } bool mitk::MouseWheelEvent::IsSuperClassOf(const InteractionEvent::Pointer &baseClass) const { return (dynamic_cast(baseClass.GetPointer()) != nullptr); } diff --git a/Modules/Core/src/Interactions/mitkPointSetDataInteractor.cpp b/Modules/Core/src/Interactions/mitkPointSetDataInteractor.cpp index c271bc7735..4c23fa04e4 100644 --- a/Modules/Core/src/Interactions/mitkPointSetDataInteractor.cpp +++ b/Modules/Core/src/Interactions/mitkPointSetDataInteractor.cpp @@ -1,626 +1,626 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkPointSetDataInteractor.h" #include "mitkMouseMoveEvent.h" #include "mitkInteractionConst.h" // TODO: refactor file #include "mitkInternalEvent.h" #include "mitkOperationEvent.h" #include "mitkRenderingManager.h" #include // #include "mitkBaseRenderer.h" #include "mitkDispatcher.h" #include "mitkUndoController.h" void mitk::PointSetDataInteractor::ConnectActionsAndFunctions() { // Condition which is evaluated before transition is taken // following actions in the statemachine are only executed if it returns TRUE CONNECT_CONDITION("isoverpoint", CheckSelection); CONNECT_FUNCTION("addpoint", AddPoint); CONNECT_FUNCTION("selectpoint", SelectPoint); CONNECT_FUNCTION("unselect", UnSelectPointAtPosition); CONNECT_FUNCTION("unselectAll", UnSelectAll); CONNECT_FUNCTION("initMove", InitMove); CONNECT_FUNCTION("movePoint", MovePoint); CONNECT_FUNCTION("finishMovement", FinishMove); CONNECT_FUNCTION("removePoint", RemovePoint); } void mitk::PointSetDataInteractor::AddPoint(StateMachineAction *stateMachineAction, InteractionEvent *interactionEvent) { unsigned int timeStep = interactionEvent->GetSender()->GetTimeStep(GetDataNode()->GetData()); ScalarType timeInMs = interactionEvent->GetSender()->GetTime(); // disallow adding of new points if maximum number of points is reached if (m_MaxNumberOfPoints > 1 && m_PointSet->GetSize(timeStep) >= m_MaxNumberOfPoints) { return; } // To add a point the minimal information is the position, this method accepts all InteractionsPositionEvents - InteractionPositionEvent *positionEvent = dynamic_cast(interactionEvent); + auto *positionEvent = dynamic_cast(interactionEvent); if (positionEvent != nullptr) { mitk::Point3D itkPoint = positionEvent->GetPositionInWorld(); this->UnselectAll(timeStep, timeInMs); int lastPosition = 0; mitk::PointSet::PointsIterator it, end; it = m_PointSet->Begin(timeStep); end = m_PointSet->End(timeStep); while (it != end) { if (!m_PointSet->IndexExists(lastPosition, timeStep)) break; ++it; ++lastPosition; } // Insert a Point to the PointSet // 2) Create the Operation inserting the point if (m_PointSet->IsEmpty()) { lastPosition = 0; } - PointOperation *doOp = new mitk::PointOperation(OpINSERT, timeInMs, itkPoint, lastPosition); + auto *doOp = new mitk::PointOperation(OpINSERT, timeInMs, itkPoint, lastPosition); // 3) If Undo is enabled, also create the inverse Operation if (m_UndoEnabled) { - PointOperation *undoOp = new mitk::PointOperation(OpREMOVE, timeInMs, itkPoint, lastPosition); + auto *undoOp = new mitk::PointOperation(OpREMOVE, timeInMs, itkPoint, lastPosition); // 4) Do and Undo Operations are combined in an Operation event which also contains the target of the operations // (here m_PointSet) OperationEvent *operationEvent = new OperationEvent(m_PointSet, doOp, undoOp, "Add point"); // 5) Store the Operation in the UndoController OperationEvent::IncCurrObjectEventId(); m_UndoController->SetOperationEvent(operationEvent); } // 6) Execute the Operation performs the actual insertion of the point into the PointSet m_PointSet->ExecuteOperation(doOp); // 7) If Undo is not enabled the Do-Operation is to be dropped to prevent memory leaks. if (!m_UndoEnabled) delete doOp; // Request update interactionEvent->GetSender()->GetRenderingManager()->RequestUpdateAll(); // Check if points form a closed contour now, if so fire an InternalEvent IsClosedContour(stateMachineAction, interactionEvent); if (m_MaxNumberOfPoints > 0 && m_PointSet->GetSize(timeStep) >= m_MaxNumberOfPoints) { // Signal that DataNode is fully filled this->NotifyResultReady(); // Send internal event that can be used by StateMachines to switch in a different state InternalEvent::Pointer event = InternalEvent::New(nullptr, this, "MaximalNumberOfPoints"); positionEvent->GetSender()->GetDispatcher()->QueueEvent(event.GetPointer()); } } } void mitk::PointSetDataInteractor::SelectPoint(StateMachineAction *, InteractionEvent *interactionEvent) { unsigned int timeStep = interactionEvent->GetSender()->GetTimeStep(GetDataNode()->GetData()); ScalarType timeInMs = interactionEvent->GetSender()->GetTime(); - InteractionPositionEvent *positionEvent = dynamic_cast(interactionEvent); + auto *positionEvent = dynamic_cast(interactionEvent); if (positionEvent != nullptr) { Point3D point = positionEvent->GetPositionInWorld(); // iterate over point set and check if it contains a point close enough to the pointer to be selected int index = GetPointIndexByPosition(point, timeStep); if (index != -1) { // first deselect the other points // undoable deselect of all points in the DataList this->UnselectAll(timeStep, timeInMs); - PointOperation *doOp = new mitk::PointOperation(OpSELECTPOINT, timeInMs, point, index); + auto *doOp = new mitk::PointOperation(OpSELECTPOINT, timeInMs, point, index); /*if (m_UndoEnabled) { PointOperation* undoOp = new mitk::PointOperation(OpDESELECTPOINT,timeInMs,point, index); OperationEvent *operationEvent = new OperationEvent(m_PointSet, doOp, undoOp, "Select Point"); OperationEvent::IncCurrObjectEventId(); m_UndoController->SetOperationEvent(operationEvent); }*/ // execute the Operation m_PointSet->ExecuteOperation(doOp); if (!m_UndoEnabled) delete doOp; interactionEvent->GetSender()->GetRenderingManager()->RequestUpdateAll(); } } } mitk::PointSetDataInteractor::PointSetDataInteractor() : m_MaxNumberOfPoints(0), m_SelectionAccuracy(3.5) { } mitk::PointSetDataInteractor::~PointSetDataInteractor() { } void mitk::PointSetDataInteractor::RemovePoint(StateMachineAction *, InteractionEvent *interactionEvent) { unsigned int timeStep = interactionEvent->GetSender()->GetTimeStep(GetDataNode()->GetData()); ScalarType timeInMs = interactionEvent->GetSender()->GetTime(); - InteractionPositionEvent *positionEvent = dynamic_cast(interactionEvent); + auto *positionEvent = dynamic_cast(interactionEvent); if (positionEvent != nullptr) { mitk::Point3D itkPoint = positionEvent->GetPositionInWorld(); // search the point in the list int position = m_PointSet->SearchPoint(itkPoint, m_SelectionAccuracy, timeStep); if (position >= 0) // found a point { PointSet::PointType pt = m_PointSet->GetPoint(position, timeStep); itkPoint[0] = pt[0]; itkPoint[1] = pt[1]; itkPoint[2] = pt[2]; - PointOperation *doOp = new mitk::PointOperation(OpREMOVE, timeInMs, itkPoint, position); + auto *doOp = new mitk::PointOperation(OpREMOVE, timeInMs, itkPoint, position); if (m_UndoEnabled) // write to UndoMechanism { - PointOperation *undoOp = new mitk::PointOperation(OpINSERT, timeInMs, itkPoint, position); + auto *undoOp = new mitk::PointOperation(OpINSERT, timeInMs, itkPoint, position); OperationEvent *operationEvent = new OperationEvent(m_PointSet, doOp, undoOp, "Remove point"); mitk::OperationEvent::IncCurrObjectEventId(); m_UndoController->SetOperationEvent(operationEvent); } // execute the Operation m_PointSet->ExecuteOperation(doOp); if (!m_UndoEnabled) delete doOp; /*now select the point "position-1", and if it is the first in list, then continue at the last in list*/ // only then a select of a point is possible! if (m_PointSet->GetSize(timeStep) > 0) { this->SelectPoint(m_PointSet->Begin(timeStep)->Index(), timeStep, timeInMs); } } interactionEvent->GetSender()->GetRenderingManager()->RequestUpdateAll(); } } void mitk::PointSetDataInteractor::IsClosedContour(StateMachineAction *, InteractionEvent *interactionEvent) { unsigned int timeStep = interactionEvent->GetSender()->GetTimeStep(GetDataNode()->GetData()); - InteractionPositionEvent *positionEvent = dynamic_cast(interactionEvent); + auto *positionEvent = dynamic_cast(interactionEvent); if (positionEvent != nullptr) { Point3D point = positionEvent->GetPositionInWorld(); // iterate over point set and check if it contains a point close enough to the pointer to be selected if (GetPointIndexByPosition(point, timeStep) != -1 && m_PointSet->GetSize(timeStep) >= 3) { InternalEvent::Pointer event = InternalEvent::New(nullptr, this, "ClosedContour"); positionEvent->GetSender()->GetDispatcher()->QueueEvent(event.GetPointer()); } } } void mitk::PointSetDataInteractor::MovePoint(StateMachineAction *stateMachineAction, InteractionEvent *interactionEvent) { unsigned int timeStep = interactionEvent->GetSender()->GetTimeStep(GetDataNode()->GetData()); ScalarType timeInMs = interactionEvent->GetSender()->GetTime(); - InteractionPositionEvent *positionEvent = dynamic_cast(interactionEvent); + auto *positionEvent = dynamic_cast(interactionEvent); if (positionEvent != nullptr) { IsClosedContour(stateMachineAction, interactionEvent); mitk::Point3D newPoint, resultPoint; newPoint = positionEvent->GetPositionInWorld(); // search the elements in the list that are selected then calculate the // vector, because only with the vector we can move several elements in // the same direction // newPoint - lastPoint = vector // then move all selected and set the lastPoint = newPoint. // then add all vectors to a summeryVector (to be able to calculate the // startpoint for undoOperation) mitk::Vector3D dirVector = newPoint - m_LastPoint; // sum up all Movement for Undo in FinishMovement m_SumVec = m_SumVec + dirVector; mitk::PointSet::PointsIterator it, end; it = m_PointSet->Begin(timeStep); end = m_PointSet->End(timeStep); while (it != end) { int position = it->Index(); if (m_PointSet->GetSelectInfo(position, timeStep)) // if selected { PointSet::PointType pt = m_PointSet->GetPoint(position, timeStep); mitk::Point3D sumVec; sumVec[0] = pt[0]; sumVec[1] = pt[1]; sumVec[2] = pt[2]; resultPoint = sumVec + dirVector; - PointOperation *doOp = new mitk::PointOperation(OpMOVE, timeInMs, resultPoint, position); + auto *doOp = new mitk::PointOperation(OpMOVE, timeInMs, resultPoint, position); // execute the Operation // here no undo is stored, because the movement-steps aren't interesting. // only the start and the end is interisting to store for undo. m_PointSet->ExecuteOperation(doOp); delete doOp; } ++it; } m_LastPoint = newPoint; // for calculation of the direction vector // Update the display interactionEvent->GetSender()->GetRenderingManager()->RequestUpdateAll(); IsClosedContour(stateMachineAction, interactionEvent); } } void mitk::PointSetDataInteractor::UnSelectPointAtPosition(StateMachineAction *, InteractionEvent *interactionEvent) { unsigned int timeStep = interactionEvent->GetSender()->GetTimeStep(GetDataNode()->GetData()); ScalarType timeInMs = interactionEvent->GetSender()->GetTime(); - InteractionPositionEvent *positionEvent = dynamic_cast(interactionEvent); + auto *positionEvent = dynamic_cast(interactionEvent); if (positionEvent != nullptr) { Point3D point = positionEvent->GetPositionInWorld(); // iterate over point set and check if it contains a point close enough to the pointer to be selected int index = GetPointIndexByPosition(point, timeStep); // here it is ensured that we don't switch from one point being selected to another one being selected, // without accepting the unselect of the current point if (index != -1) { - PointOperation *doOp = new mitk::PointOperation(OpDESELECTPOINT, timeInMs, point, index); + auto *doOp = new mitk::PointOperation(OpDESELECTPOINT, timeInMs, point, index); /*if (m_UndoEnabled) { PointOperation* undoOp = new mitk::PointOperation(OpSELECTPOINT,timeInMs, point, index); OperationEvent *operationEvent = new OperationEvent(m_PointSet, doOp, undoOp, "Unselect Point"); OperationEvent::IncCurrObjectEventId(); m_UndoController->SetOperationEvent(operationEvent); }*/ m_PointSet->ExecuteOperation(doOp); if (!m_UndoEnabled) delete doOp; interactionEvent->GetSender()->GetRenderingManager()->RequestUpdateAll(); } } } void mitk::PointSetDataInteractor::UnSelectAll(mitk::StateMachineAction *, mitk::InteractionEvent *interactionEvent) { unsigned int timeStep = interactionEvent->GetSender()->GetTimeStep(GetDataNode()->GetData()); ScalarType timeInMs = interactionEvent->GetSender()->GetTime(); - InteractionPositionEvent *positionEvent = dynamic_cast(interactionEvent); + auto *positionEvent = dynamic_cast(interactionEvent); if (positionEvent != nullptr) { Point3D positioninWorld = positionEvent->GetPositionInWorld(); PointSet::PointsContainer::Iterator it, end; PointSet::DataType *itkPointSet = m_PointSet->GetPointSet(timeStep); end = itkPointSet->GetPoints()->End(); for (it = itkPointSet->GetPoints()->Begin(); it != end; it++) { int position = it->Index(); // then declare an operation which unselects this point; // UndoOperation as well! if (m_PointSet->GetSelectInfo(position, timeStep)) { float distance = sqrt(positioninWorld.SquaredEuclideanDistanceTo(m_PointSet->GetPoint(position, timeStep))); if (distance > m_SelectionAccuracy) { mitk::Point3D noPoint; noPoint.Fill(0); - mitk::PointOperation *doOp = new mitk::PointOperation(OpDESELECTPOINT, timeInMs, noPoint, position); + auto *doOp = new mitk::PointOperation(OpDESELECTPOINT, timeInMs, noPoint, position); /*if ( m_UndoEnabled ) { mitk::PointOperation* undoOp = new mitk::PointOperation(OpSELECTPOINT, timeInMs, noPoint, position); OperationEvent *operationEvent = new OperationEvent( m_PointSet, doOp, undoOp, "Unselect Point" ); OperationEvent::IncCurrObjectEventId(); m_UndoController->SetOperationEvent( operationEvent ); }*/ m_PointSet->ExecuteOperation(doOp); if (!m_UndoEnabled) delete doOp; } } } } else { this->UnselectAll(timeStep, timeInMs); } interactionEvent->GetSender()->GetRenderingManager()->RequestUpdateAll(); } void mitk::PointSetDataInteractor::UpdatePointSet(mitk::StateMachineAction *, mitk::InteractionEvent *) { - mitk::PointSet *pointSet = dynamic_cast(this->GetDataNode()->GetData()); + auto *pointSet = dynamic_cast(this->GetDataNode()->GetData()); if (pointSet == nullptr) { MITK_ERROR << "PointSetDataInteractor:: No valid point set ."; return; } m_PointSet = pointSet; } void mitk::PointSetDataInteractor::Abort(StateMachineAction *, InteractionEvent *interactionEvent) { InternalEvent::Pointer event = InternalEvent::New(nullptr, this, IntDeactivateMe); interactionEvent->GetSender()->GetDispatcher()->QueueEvent(event.GetPointer()); } /* * Check whether the DataNode contains a pointset, if not create one and add it. */ void mitk::PointSetDataInteractor::DataNodeChanged() { if (GetDataNode() != nullptr) { - PointSet *points = dynamic_cast(GetDataNode()->GetData()); + auto *points = dynamic_cast(GetDataNode()->GetData()); if (points == nullptr) { m_PointSet = PointSet::New(); GetDataNode()->SetData(m_PointSet); } else { m_PointSet = points; } // load config file parameter: maximal number of points mitk::PropertyList::Pointer properties = GetAttributes(); std::string strNumber; if (properties->GetStringProperty("MaxPoints", strNumber)) { m_MaxNumberOfPoints = atoi(strNumber.c_str()); } } } void mitk::PointSetDataInteractor::InitMove(StateMachineAction *, InteractionEvent *interactionEvent) { - InteractionPositionEvent *positionEvent = dynamic_cast(interactionEvent); + auto *positionEvent = dynamic_cast(interactionEvent); if (positionEvent == nullptr) return; // start of the Movement is stored to calculate the undoKoordinate // in FinishMovement m_LastPoint = positionEvent->GetPositionInWorld(); // initialize a value to calculate the movement through all // MouseMoveEvents from MouseClick to MouseRelease m_SumVec.Fill(0); GetDataNode()->SetProperty("contourcolor", ColorProperty::New(1.0, 1.0, 1.0)); } void mitk::PointSetDataInteractor::FinishMove(StateMachineAction *, InteractionEvent *interactionEvent) { unsigned int timeStep = interactionEvent->GetSender()->GetTimeStep(GetDataNode()->GetData()); ScalarType timeInMs = interactionEvent->GetSender()->GetTime(); - InteractionPositionEvent *positionEvent = dynamic_cast(interactionEvent); + auto *positionEvent = dynamic_cast(interactionEvent); if (positionEvent != nullptr) { // finish the movement: // the final point is m_LastPoint // m_SumVec stores the movement in a vector // the operation would not be necessary, but we need it for the undo Operation. // m_LastPoint is for the Operation // the point for undoOperation calculates from all selected // elements (point) - m_SumVec // search all selected elements and move them with undo-functionality. mitk::PointSet::PointsIterator it, end; it = m_PointSet->Begin(timeStep); end = m_PointSet->End(timeStep); while (it != end) { int position = it->Index(); if (m_PointSet->GetSelectInfo(position, timeStep)) // if selected { PointSet::PointType pt = m_PointSet->GetPoint(position, timeStep); Point3D itkPoint; itkPoint[0] = pt[0]; itkPoint[1] = pt[1]; itkPoint[2] = pt[2]; - PointOperation *doOp = new mitk::PointOperation(OpMOVE, timeInMs, itkPoint, position); + auto *doOp = new mitk::PointOperation(OpMOVE, timeInMs, itkPoint, position); if (m_UndoEnabled) //&& (posEvent->GetType() == mitk::Type_MouseButtonRelease) { // set the undo-operation, so the final position is undo-able // calculate the old Position from the already moved position - m_SumVec mitk::Point3D undoPoint = (itkPoint - m_SumVec); - PointOperation *undoOp = new mitk::PointOperation(OpMOVE, timeInMs, undoPoint, position); + auto *undoOp = new mitk::PointOperation(OpMOVE, timeInMs, undoPoint, position); OperationEvent *operationEvent = new OperationEvent(m_PointSet, doOp, undoOp, "Move point"); OperationEvent::IncCurrObjectEventId(); m_UndoController->SetOperationEvent(operationEvent); } // execute the Operation m_PointSet->ExecuteOperation(doOp); if (!m_UndoEnabled) delete doOp; } ++it; } // Update the display interactionEvent->GetSender()->GetRenderingManager()->RequestUpdateAll(); } else { return; } this->NotifyResultReady(); } void mitk::PointSetDataInteractor::SetAccuracy(float accuracy) { m_SelectionAccuracy = accuracy; } void mitk::PointSetDataInteractor::SetMaxPoints(unsigned int maxNumber) { m_MaxNumberOfPoints = maxNumber; } int mitk::PointSetDataInteractor::GetPointIndexByPosition(Point3D position, unsigned int time, float accuracy) { // iterate over point set and check if it contains a point close enough to the pointer to be selected - PointSet *points = dynamic_cast(GetDataNode()->GetData()); + auto *points = dynamic_cast(GetDataNode()->GetData()); int index = -1; if (points == nullptr) { return index; } if (points->GetPointSet(time) == nullptr) return -1; PointSet::PointsContainer *pointsContainer = points->GetPointSet(time)->GetPoints(); float minDistance = m_SelectionAccuracy; if (accuracy != -1) minDistance = accuracy; for (PointSet::PointsIterator it = pointsContainer->Begin(); it != pointsContainer->End(); it++) { float distance = sqrt(position.SquaredEuclideanDistanceTo(points->GetPoint(it->Index(), time))); if (distance < minDistance) // if several points fall within the margin, choose the one with minimal distance to position { index = it->Index(); } } return index; } bool mitk::PointSetDataInteractor::CheckSelection(const mitk::InteractionEvent *interactionEvent) { - const InteractionPositionEvent *positionEvent = dynamic_cast(interactionEvent); + const auto *positionEvent = dynamic_cast(interactionEvent); if (positionEvent != nullptr) { int timeStep = positionEvent->GetSender()->GetTimeStep(); Point3D point = positionEvent->GetPositionInWorld(); // iterate over point set and check if it contains a point close enough to the pointer to be selected int index = GetPointIndexByPosition(point, timeStep); if (index != -1) return true; } return false; } void mitk::PointSetDataInteractor::UnselectAll(unsigned int timeStep, ScalarType timeInMs) { - mitk::PointSet *pointSet = dynamic_cast(GetDataNode()->GetData()); + auto *pointSet = dynamic_cast(GetDataNode()->GetData()); if (pointSet == nullptr) { return; } mitk::PointSet::DataType *itkPointSet = pointSet->GetPointSet(timeStep); if (itkPointSet == nullptr) { return; } mitk::PointSet::PointsContainer::Iterator it, end; end = itkPointSet->GetPoints()->End(); for (it = itkPointSet->GetPoints()->Begin(); it != end; it++) { int position = it->Index(); PointSet::PointDataType pointData = {0, false, PTUNDEFINED}; itkPointSet->GetPointData(position, &pointData); // then declare an operation which unselects this point; // UndoOperation as well! if (pointData.selected) { mitk::Point3D noPoint; noPoint.Fill(0); - mitk::PointOperation *doOp = new mitk::PointOperation(OpDESELECTPOINT, timeInMs, noPoint, position); + auto *doOp = new mitk::PointOperation(OpDESELECTPOINT, timeInMs, noPoint, position); /*if ( m_UndoEnabled ) { mitk::PointOperation *undoOp = new mitk::PointOperation(OpSELECTPOINT, timeInMs, noPoint, position); OperationEvent *operationEvent = new OperationEvent( pointSet, doOp, undoOp, "Unselect Point" ); OperationEvent::IncCurrObjectEventId(); m_UndoController->SetOperationEvent( operationEvent ); }*/ pointSet->ExecuteOperation(doOp); if (!m_UndoEnabled) delete doOp; } } } void mitk::PointSetDataInteractor::SelectPoint(int position, unsigned int timeStep, ScalarType timeInMS) { - mitk::PointSet *pointSet = dynamic_cast(this->GetDataNode()->GetData()); + auto *pointSet = dynamic_cast(this->GetDataNode()->GetData()); // if List is empty, then no selection of a point can be done! if ((pointSet == nullptr) || (pointSet->GetSize(timeStep) <= 0)) { return; } // dummyPoint... not needed anyway mitk::Point3D noPoint; noPoint.Fill(0); - mitk::PointOperation *doOp = new mitk::PointOperation(OpSELECTPOINT, timeInMS, noPoint, position); + auto *doOp = new mitk::PointOperation(OpSELECTPOINT, timeInMS, noPoint, position); /*if ( m_UndoEnabled ) { mitk::PointOperation* undoOp = new mitk::PointOperation(OpDESELECTPOINT,timeInMS, noPoint, position); OperationEvent *operationEvent = new OperationEvent(pointSet, doOp, undoOp, "Select Point"); OperationEvent::IncCurrObjectEventId(); m_UndoController->SetOperationEvent(operationEvent); }*/ pointSet->ExecuteOperation(doOp); if (!m_UndoEnabled) delete doOp; } diff --git a/Modules/Core/src/Interactions/mitkSinglePointDataInteractor.cpp b/Modules/Core/src/Interactions/mitkSinglePointDataInteractor.cpp index 857aab00fe..70e751b24a 100644 --- a/Modules/Core/src/Interactions/mitkSinglePointDataInteractor.cpp +++ b/Modules/Core/src/Interactions/mitkSinglePointDataInteractor.cpp @@ -1,107 +1,107 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkSinglePointDataInteractor.h" #include "mitkMouseMoveEvent.h" #include "mitkInteractionConst.h" // TODO: refactor file #include "mitkInternalEvent.h" #include "mitkOperationEvent.h" #include "mitkRenderingManager.h" #include // #include "mitkBaseRenderer.h" #include "mitkDispatcher.h" #include "mitkUndoController.h" mitk::SinglePointDataInteractor::SinglePointDataInteractor() { this->SetMaxPoints(1); } mitk::SinglePointDataInteractor::~SinglePointDataInteractor() { } void mitk::SinglePointDataInteractor::AddPoint(StateMachineAction * /*stateMachineAction*/, InteractionEvent *interactionEvent) { unsigned int timeStep = interactionEvent->GetSender()->GetTimeStep(GetDataNode()->GetData()); ScalarType timeInMs = interactionEvent->GetSender()->GetTime(); // To add a point the minimal information is the position, this method accepts all InteractionsPositionEvents - InteractionPositionEvent *positionEvent = dynamic_cast(interactionEvent); + auto *positionEvent = dynamic_cast(interactionEvent); if (positionEvent != nullptr) { PointOperation *doOp; PointOperation *undoOp; if (m_PointSet->IndexExists(0, timeStep)) { PointSet::PointType pt = m_PointSet->GetPoint(0, timeStep); Point3D itkPoint; itkPoint[0] = pt[0]; itkPoint[1] = pt[1]; itkPoint[2] = pt[2]; doOp = new mitk::PointOperation(OpMOVE, timeInMs, positionEvent->GetPositionInWorld(), 0); undoOp = new mitk::PointOperation(OpMOVE, timeInMs, itkPoint, 0); } else { doOp = new mitk::PointOperation(OpINSERT, timeInMs, positionEvent->GetPositionInWorld(), 0); undoOp = new mitk::PointOperation(OpREMOVE, timeInMs, positionEvent->GetPositionInWorld(), 0); } if (m_UndoEnabled) { OperationEvent *operationEvent = new OperationEvent(m_PointSet, doOp, undoOp, "Move point"); OperationEvent::IncCurrObjectEventId(); m_UndoController->SetOperationEvent(operationEvent); } // execute the Operation m_PointSet->ExecuteOperation(doOp); if (!m_UndoEnabled) delete doOp; // Request update interactionEvent->GetSender()->GetRenderingManager()->RequestUpdateAll(); } } /* * Check whether the DataNode contains a pointset, if not create one and add it. */ void mitk::SinglePointDataInteractor::DataNodeChanged() { if (GetDataNode() != nullptr) { - PointSet *points = dynamic_cast(GetDataNode()->GetData()); + auto *points = dynamic_cast(GetDataNode()->GetData()); if (points == nullptr) { m_PointSet = PointSet::New(); GetDataNode()->SetData(m_PointSet); } else { points->Clear(); m_PointSet = points; } } } diff --git a/Modules/Core/src/Interactions/mitkStateMachineContainer.cpp b/Modules/Core/src/Interactions/mitkStateMachineContainer.cpp index 8e99407a88..183cf0f968 100755 --- a/Modules/Core/src/Interactions/mitkStateMachineContainer.cpp +++ b/Modules/Core/src/Interactions/mitkStateMachineContainer.cpp @@ -1,243 +1,243 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkStateMachineContainer.h" #include #include #include #include // us #include "usGetModuleContext.h" #include "usModule.h" #include "usModuleResource.h" #include "usModuleResourceStream.h" /** * @brief This class builds up all the necessary structures for a statemachine. * and stores one start-state for all built statemachines. **/ // XML StateMachine Tags const std::string NAME = "name"; const std::string CONFIG = "statemachine"; const std::string STATE = "state"; const std::string STATEMODE = "state_mode"; const std::string TRANSITION = "transition"; const std::string EVENTCLASS = "event_class"; const std::string EVENTVARIANT = "event_variant"; const std::string STARTSTATE = "startstate"; const std::string TARGET = "target"; const std::string ACTION = "action"; const std::string CONDITION = "condition"; const std::string INVERTED = "inverted"; namespace mitk { vtkStandardNewMacro(StateMachineContainer); } mitk::StateMachineContainer::StateMachineContainer() : m_StartStateFound(false), m_errors(false) { } mitk::StateMachineContainer::~StateMachineContainer() { } /** * @brief Loads the xml file filename and generates the necessary instances. **/ bool mitk::StateMachineContainer::LoadBehavior(const std::string &fileName, const us::Module *module) { if (module == nullptr) { module = us::GetModuleContext()->GetModule(); } us::ModuleResource resource = module->GetResource("Interactions/" + fileName); if (!resource.IsValid()) { mitkThrow() << ("Resource not valid. State machine pattern not found:" + fileName); } us::ModuleResourceStream stream(resource); this->SetStream(&stream); m_Filename = fileName; return this->Parse() && !m_errors; } mitk::StateMachineState::Pointer mitk::StateMachineContainer::GetStartState() const { return m_StartState; } /** * @brief sets the pointers in Transition (setNextState(..)) according to the extracted xml-file content **/ void mitk::StateMachineContainer::ConnectStates() { - for (StateMachineCollectionType::iterator it = m_States.begin(); it != m_States.end(); ++it) + for (auto it = m_States.begin(); it != m_States.end(); ++it) { if ((*it)->ConnectTransitions(&m_States) == false) m_errors = true; } } void mitk::StateMachineContainer::StartElement(const char *elementName, const char **atts) { std::string name(elementName); if (name == CONFIG) { // } else if (name == STATE) { std::string stateName = ReadXMLStringAttribut(NAME, atts); std::transform(stateName.begin(), stateName.end(), stateName.begin(), ::toupper); std::string stateMode = ReadXMLStringAttribut(STATEMODE, atts); std::transform(stateMode.begin(), stateMode.end(), stateMode.begin(), ::toupper); bool isStartState = ReadXMLBooleanAttribut(STARTSTATE, atts); if (isStartState) { m_StartStateFound = true; } // sanitize state modes if (stateMode == "" || stateMode == "REGULAR") { stateMode = "REGULAR"; } else if (stateMode != "GRAB_INPUT" && stateMode != "PREFER_INPUT") { MITK_WARN << "Invalid State Modus " << stateMode << ". Mode assumed to be REGULAR"; stateMode = "REGULAR"; } m_CurrState = mitk::StateMachineState::New(stateName, stateMode); if (isStartState) m_StartState = m_CurrState; } else if (name == TRANSITION) { std::string eventClass = ReadXMLStringAttribut(EVENTCLASS, atts); std::string eventVariant = ReadXMLStringAttribut(EVENTVARIANT, atts); std::string target = ReadXMLStringAttribut(TARGET, atts); std::transform(target.begin(), target.end(), target.begin(), ::toupper); mitk::StateMachineTransition::Pointer transition = mitk::StateMachineTransition::New(target, eventClass, eventVariant); if (m_CurrState) { m_CurrState->AddTransition(transition); } else { MITK_WARN << "Malformed Statemachine Pattern. Transition has no origin. \n Will be ignored."; MITK_WARN << "Malformed Transition details: target=" << target << ", event class:" << eventClass << ", event variant:" << eventVariant; } m_CurrTransition = transition; } else if (name == ACTION) { std::string actionName = ReadXMLStringAttribut(NAME, atts); mitk::StateMachineAction::Pointer action = mitk::StateMachineAction::New(actionName); if (m_CurrTransition) m_CurrTransition->AddAction(action); else MITK_WARN << "Malformed state machine Pattern. Action without transition. \n Will be ignored."; } else if (name == CONDITION) { if (!m_CurrTransition) MITK_WARN << "Malformed state machine Pattern. Condition without transition. \n Will be ignored."; std::string conditionName = ReadXMLStringAttribut(NAME, atts); std::string inverted = ReadXMLStringAttribut(INVERTED, atts); if (inverted == "" || inverted == "false") { m_CurrTransition->AddCondition(mitk::StateMachineCondition(conditionName, false)); } else { m_CurrTransition->AddCondition(mitk::StateMachineCondition(conditionName, true)); } } } void mitk::StateMachineContainer::EndElement(const char *elementName) { std::string name(elementName); if (name == CONFIG) { if (m_StartState.IsNull()) { MITK_ERROR << "State machine pattern has no start state and cannot be used: " << m_Filename; } ConnectStates(); } else if (name == TRANSITION) { m_CurrTransition = nullptr; } else if (name == ACTION) { // } else if (name == CONDITION) { // } else if (name == STATE) { m_States.push_back(m_CurrState); m_CurrState = nullptr; } } std::string mitk::StateMachineContainer::ReadXMLStringAttribut(std::string name, const char **atts) { if (atts) { const char **attsIter = atts; while (*attsIter) { if (name == *attsIter) { attsIter++; return *attsIter; } attsIter++; attsIter++; } } return std::string(); } bool mitk::StateMachineContainer::ReadXMLBooleanAttribut(std::string name, const char **atts) { std::string s = ReadXMLStringAttribut(name, atts); std::transform(s.begin(), s.end(), s.begin(), ::toupper); if (s == "TRUE") return true; else return false; } diff --git a/Modules/Core/src/Interactions/mitkStateMachineState.cpp b/Modules/Core/src/Interactions/mitkStateMachineState.cpp index e5a054baa4..6ba9fd82b7 100755 --- a/Modules/Core/src/Interactions/mitkStateMachineState.cpp +++ b/Modules/Core/src/Interactions/mitkStateMachineState.cpp @@ -1,109 +1,109 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkStateMachineState.h" mitk::StateMachineState::StateMachineState(const std::string &stateName, const std::string &stateMode) : m_Name(stateName), m_StateMode(stateMode) { } std::string mitk::StateMachineState::GetMode() const { return m_StateMode; } mitk::StateMachineState::~StateMachineState() { m_Transitions.clear(); } bool mitk::StateMachineState::AddTransition(StateMachineTransition::Pointer transition) { - for (TransitionVector::iterator it = m_Transitions.begin(); it != m_Transitions.end(); ++it) + for (auto it = m_Transitions.begin(); it != m_Transitions.end(); ++it) { if (transition.GetPointer() == (*it).GetPointer()) return false; } m_Transitions.push_back(transition); return true; } mitk::StateMachineTransition::Pointer mitk::StateMachineState::GetTransition(const std::string &eventClass, const std::string &eventVariant) { TransitionVector transitions = this->GetTransitionList(eventClass, eventVariant); if (transitions.size() > 1) { MITK_WARN << "Multiple transitions have been found for event. Use non-deprecated method " "StateMachineState::GetTransitionList() instead!"; } if (transitions.empty()) { return nullptr; } else { return transitions.at(0); } } mitk::StateMachineState::TransitionVector mitk::StateMachineState::GetTransitionList(const std::string &eventClass, const std::string &eventVariant) { TransitionVector transitions; mitk::StateMachineTransition::Pointer t = mitk::StateMachineTransition::New("", eventClass, eventVariant); - for (TransitionVector::iterator it = m_Transitions.begin(); it != m_Transitions.end(); ++it) + for (auto it = m_Transitions.begin(); it != m_Transitions.end(); ++it) { if (**it == *t) // do not switch it and t, order matters, see mitk::StateMachineTransition == operator transitions.push_back(*it); } return transitions; } std::string mitk::StateMachineState::GetName() const { return m_Name; } //##Documentation //## Post-processing step, when builing StateMachine from XML. //## Parse all transitions and find the State that matches the String-Name. bool mitk::StateMachineState::ConnectTransitions(StateMap *allStates) { - for (TransitionVector::iterator transIt = m_Transitions.begin(); transIt != m_Transitions.end(); ++transIt) + for (auto transIt = m_Transitions.begin(); transIt != m_Transitions.end(); ++transIt) { bool found = false; - for (StateMap::iterator stateIt = allStates->begin(); stateIt != allStates->end(); ++stateIt) + for (auto stateIt = allStates->begin(); stateIt != allStates->end(); ++stateIt) { if ((*stateIt)->GetName() == (*transIt)->GetNextStateName()) { (*transIt)->SetNextState(*stateIt); found = true; break; } } if (!found) { MITK_WARN << "Target State not found in StateMachine."; return false; // only reached if no state matching the string is found } } return true; } diff --git a/Modules/Core/src/Rendering/mitkAnnotationUtils.cpp b/Modules/Core/src/Rendering/mitkAnnotationUtils.cpp index ef21056540..f47ae70eae 100644 --- a/Modules/Core/src/Rendering/mitkAnnotationUtils.cpp +++ b/Modules/Core/src/Rendering/mitkAnnotationUtils.cpp @@ -1,126 +1,126 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkAnnotationUtils.h" #include "mitkAnnotation.h" #include "vtkCallbackCommand.h" #include "vtkCommand.h" #include namespace mitk { AnnotationUtils::AnnotationUtils() {} AnnotationUtils::~AnnotationUtils() {} AbstractAnnotationRenderer *AnnotationUtils::GetAnnotationRenderer(const std::string &arTypeID, const std::string &rendererID) { // get the context us::ModuleContext *context = us::GetModuleContext(); // specify a filter that defines the requested type std::string filter = "(&(" + AbstractAnnotationRenderer::US_PROPKEY_ID + "=" + arTypeID + ")(" + AbstractAnnotationRenderer::US_PROPKEY_RENDERER_ID + "=" + rendererID + "))"; // find the fitting service std::vector serviceReferences = context->GetServiceReferences(AbstractAnnotationRenderer::US_INTERFACE_NAME, filter); // check if a service reference was found. It is also possible that several // services were found. This is not checked here, just the first one is taken. AbstractAnnotationRenderer *ar = nullptr; if (serviceReferences.size()) { ar = context->GetService(serviceReferences.front()); } // no service reference was found or found service reference has no valid source return ar; } void AnnotationUtils::RegisterAnnotationRenderer(AbstractAnnotationRenderer *annotationRenderer) { static AnnotationRendererServices AnnotationRendererServices; // Define ServiceProps us::ServiceProperties props; props[AbstractAnnotationRenderer::US_PROPKEY_RENDERER_ID] = annotationRenderer->GetRendererID(); props[AbstractAnnotationRenderer::US_PROPKEY_ID] = annotationRenderer->GetID(); us::GetModuleContext()->RegisterService(annotationRenderer, props); AnnotationRendererServices.push_back(std::unique_ptr(annotationRenderer)); } void AnnotationUtils::UpdateAnnotationRenderer(const std::string &rendererID) { for (AbstractAnnotationRenderer *annotationRenderer : GetAnnotationRenderer(rendererID)) { annotationRenderer->Update(); } } void AnnotationUtils::BaseRendererChanged(BaseRenderer *renderer) { for (AbstractAnnotationRenderer *annotationRenderer : GetAnnotationRenderer(renderer->GetName())) { annotationRenderer->CurrentBaseRendererChanged(); } vtkCallbackCommand *renderCallbackCommand = vtkCallbackCommand::New(); renderCallbackCommand->SetCallback(AnnotationUtils::RenderWindowCallback); renderer->GetRenderWindow()->AddObserver(vtkCommand::ModifiedEvent, renderCallbackCommand); renderCallbackCommand->Delete(); } void AnnotationUtils::RenderWindowCallback(vtkObject *caller, unsigned long, void *, void *) { - vtkRenderWindow *renderWindow = dynamic_cast(caller); + auto *renderWindow = dynamic_cast(caller); if (!renderWindow) return; BaseRenderer *renderer = BaseRenderer::GetInstance(renderWindow); for (AbstractAnnotationRenderer *annotationRenderer : GetAnnotationRenderer(renderer->GetName())) { annotationRenderer->OnRenderWindowModified(); } } Annotation *AnnotationUtils::GetAnnotation(const std::string &AnnotationID) { std::string ldapFilter = "(" + Annotation::US_PROPKEY_ID + "=" + AnnotationID + ")"; us::ModuleContext *context = us::GetModuleContext(); std::vector> annotations = context->GetServiceReferences(ldapFilter); Annotation *annotation = nullptr; if (!annotations.empty()) { annotation = us::GetModuleContext()->GetService(annotations.front()); } return annotation; } std::vector AnnotationUtils::GetAnnotationRenderer(const std::string &rendererID) { us::ModuleContext *context = us::GetModuleContext(); // specify a filter that defines the requested type std::string filter = "(&(" + AbstractAnnotationRenderer::US_PROPKEY_ID + "=*)(" + AbstractAnnotationRenderer::US_PROPKEY_RENDERER_ID + "=" + rendererID + "))"; // find the fitting service std::vector serviceReferences = context->GetServiceReferences(AbstractAnnotationRenderer::US_INTERFACE_NAME, filter); std::vector arList; for (us::ServiceReferenceU service : serviceReferences) { arList.push_back(context->GetService(service)); } return arList; } } diff --git a/Modules/Core/src/Rendering/mitkBaseRenderer.cpp b/Modules/Core/src/Rendering/mitkBaseRenderer.cpp index cde6d18e19..53d5d4b073 100644 --- a/Modules/Core/src/Rendering/mitkBaseRenderer.cpp +++ b/Modules/Core/src/Rendering/mitkBaseRenderer.cpp @@ -1,787 +1,787 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkBaseRenderer.h" #include "mitkMapper.h" #include "mitkResliceMethodProperty.h" // Geometries #include "mitkPlaneGeometry.h" #include "mitkSlicedGeometry3D.h" // Controllers #include "mitkCameraController.h" #include "mitkCameraRotationController.h" #include "mitkSliceNavigationController.h" #include "mitkVtkLayerController.h" #include "mitkInteractionConst.h" #include "mitkProperties.h" #include "mitkWeakPointerProperty.h" // VTK #include #include #include #include #include #include #include mitk::BaseRenderer::BaseRendererMapType mitk::BaseRenderer::baseRendererMap; mitk::BaseRenderer *mitk::BaseRenderer::GetInstance(vtkRenderWindow *renWin) { - for (BaseRendererMapType::iterator mapit = baseRendererMap.begin(); mapit != baseRendererMap.end(); ++mapit) + for (auto mapit = baseRendererMap.begin(); mapit != baseRendererMap.end(); ++mapit) { if ((*mapit).first == renWin) return (*mapit).second; } return nullptr; } void mitk::BaseRenderer::AddInstance(vtkRenderWindow *renWin, BaseRenderer *baseRenderer) { if (renWin == nullptr || baseRenderer == nullptr) return; // ensure that no BaseRenderer is managed twice mitk::BaseRenderer::RemoveInstance(renWin); baseRendererMap.insert(BaseRendererMapType::value_type(renWin, baseRenderer)); } void mitk::BaseRenderer::RemoveInstance(vtkRenderWindow *renWin) { - BaseRendererMapType::iterator mapit = baseRendererMap.find(renWin); + auto mapit = baseRendererMap.find(renWin); if (mapit != baseRendererMap.end()) baseRendererMap.erase(mapit); } mitk::BaseRenderer *mitk::BaseRenderer::GetByName(const std::string &name) { - for (BaseRendererMapType::iterator mapit = baseRendererMap.begin(); mapit != baseRendererMap.end(); ++mapit) + for (auto mapit = baseRendererMap.begin(); mapit != baseRendererMap.end(); ++mapit) { if ((*mapit).second->m_Name == name) return (*mapit).second; } return nullptr; } vtkRenderWindow *mitk::BaseRenderer::GetRenderWindowByName(const std::string &name) { - for (BaseRendererMapType::iterator mapit = baseRendererMap.begin(); mapit != baseRendererMap.end(); ++mapit) + for (auto mapit = baseRendererMap.begin(); mapit != baseRendererMap.end(); ++mapit) { if ((*mapit).second->m_Name == name) return (*mapit).first; } return nullptr; } mitk::BaseRenderer::BaseRenderer(const char *name, vtkRenderWindow *renWin, mitk::RenderingManager *rm, RenderingMode::Type renderingMode) : m_RenderWindow(nullptr), m_VtkRenderer(nullptr), m_MapperID(defaultMapper), m_DataStorage(nullptr), m_RenderingManager(rm), m_LastUpdateTime(0), m_CameraController(nullptr), m_SliceNavigationController(nullptr), m_CameraRotationController(nullptr), m_WorldTimeGeometry(nullptr), m_CurrentWorldGeometry(nullptr), m_CurrentWorldPlaneGeometry(nullptr), m_Slice(0), m_TimeStep(), m_CurrentWorldPlaneGeometryUpdateTime(), m_TimeStepUpdateTime(), m_KeepDisplayedRegion(true), m_CurrentWorldPlaneGeometryData(nullptr), m_CurrentWorldPlaneGeometryNode(nullptr), m_CurrentWorldPlaneGeometryTransformTime(0), m_Name(name), m_EmptyWorldGeometry(true), m_NumberOfVisibleLODEnabledMappers(0) { m_Bounds[0] = 0; m_Bounds[1] = 0; m_Bounds[2] = 0; m_Bounds[3] = 0; m_Bounds[4] = 0; m_Bounds[5] = 0; if (name != nullptr) { m_Name = name; } else { m_Name = "unnamed renderer"; itkWarningMacro(<< "Created unnamed renderer. Bad for serialization. Please choose a name."); } if (renWin != nullptr) { m_RenderWindow = renWin; m_RenderWindow->Register(nullptr); } else { itkWarningMacro(<< "Created mitkBaseRenderer without vtkRenderWindow present."); } // instances.insert( this ); // adding this BaseRenderer to the List of all BaseRenderer m_BindDispatcherInteractor = new mitk::BindDispatcherInteractor(GetName()); WeakPointerProperty::Pointer rendererProp = WeakPointerProperty::New((itk::Object *)this); m_CurrentWorldPlaneGeometry = mitk::PlaneGeometry::New(); m_CurrentWorldPlaneGeometryData = mitk::PlaneGeometryData::New(); m_CurrentWorldPlaneGeometryData->SetPlaneGeometry(m_CurrentWorldPlaneGeometry); m_CurrentWorldPlaneGeometryNode = mitk::DataNode::New(); m_CurrentWorldPlaneGeometryNode->SetData(m_CurrentWorldPlaneGeometryData); m_CurrentWorldPlaneGeometryNode->GetPropertyList()->SetProperty("renderer", rendererProp); m_CurrentWorldPlaneGeometryNode->GetPropertyList()->SetProperty("layer", IntProperty::New(1000)); m_CurrentWorldPlaneGeometryNode->SetProperty("reslice.thickslices", mitk::ResliceMethodProperty::New()); m_CurrentWorldPlaneGeometryNode->SetProperty("reslice.thickslices.num", mitk::IntProperty::New(1)); m_CurrentWorldPlaneGeometryTransformTime = m_CurrentWorldPlaneGeometryNode->GetVtkTransform()->GetMTime(); mitk::SliceNavigationController::Pointer sliceNavigationController = mitk::SliceNavigationController::New(); sliceNavigationController->SetRenderer(this); sliceNavigationController->ConnectGeometrySliceEvent(this); sliceNavigationController->ConnectGeometryUpdateEvent(this); sliceNavigationController->ConnectGeometryTimeEvent(this, false); m_SliceNavigationController = sliceNavigationController; m_CameraRotationController = mitk::CameraRotationController::New(); m_CameraRotationController->SetRenderWindow(m_RenderWindow); m_CameraRotationController->AcquireCamera(); m_CameraController = mitk::CameraController::New(); m_CameraController->SetRenderer(this); m_VtkRenderer = vtkRenderer::New(); if (renderingMode == RenderingMode::DepthPeeling) { m_VtkRenderer->SetUseDepthPeeling(1); m_VtkRenderer->SetMaximumNumberOfPeels(8); m_VtkRenderer->SetOcclusionRatio(0.0); } if (mitk::VtkLayerController::GetInstance(m_RenderWindow) == nullptr) { mitk::VtkLayerController::AddInstance(m_RenderWindow, m_VtkRenderer); } mitk::VtkLayerController::GetInstance(m_RenderWindow)->InsertSceneRenderer(m_VtkRenderer); } mitk::BaseRenderer::~BaseRenderer() { if (m_VtkRenderer != nullptr) { m_VtkRenderer->Delete(); m_VtkRenderer = nullptr; } if (m_CameraController.IsNotNull()) m_CameraController->SetRenderer(nullptr); mitk::VtkLayerController::RemoveInstance(m_RenderWindow); RemoveAllLocalStorages(); m_DataStorage = nullptr; if (m_BindDispatcherInteractor != nullptr) { delete m_BindDispatcherInteractor; } if (m_RenderWindow != nullptr) { m_RenderWindow->Delete(); m_RenderWindow = nullptr; } } void mitk::BaseRenderer::RemoveAllLocalStorages() { this->InvokeEvent(mitk::BaseRenderer::RendererResetEvent()); std::list::iterator it; for (it = m_RegisteredLocalStorageHandlers.begin(); it != m_RegisteredLocalStorageHandlers.end(); ++it) (*it)->ClearLocalStorage(this, false); m_RegisteredLocalStorageHandlers.clear(); } void mitk::BaseRenderer::RegisterLocalStorageHandler(mitk::BaseLocalStorageHandler *lsh) { m_RegisteredLocalStorageHandlers.push_back(lsh); } mitk::Dispatcher::Pointer mitk::BaseRenderer::GetDispatcher() const { return m_BindDispatcherInteractor->GetDispatcher(); } void mitk::BaseRenderer::UnregisterLocalStorageHandler(mitk::BaseLocalStorageHandler *lsh) { m_RegisteredLocalStorageHandlers.remove(lsh); } void mitk::BaseRenderer::SetDataStorage(DataStorage *storage) { if (storage != m_DataStorage && storage != nullptr) { m_DataStorage = storage; m_BindDispatcherInteractor->SetDataStorage(m_DataStorage); this->Modified(); } } const mitk::BaseRenderer::MapperSlotId mitk::BaseRenderer::defaultMapper = 1; void mitk::BaseRenderer::Paint() { } void mitk::BaseRenderer::Initialize() { } void mitk::BaseRenderer::Resize(int w, int h) { this->m_RenderWindow->SetSize(w, h); } void mitk::BaseRenderer::InitRenderer(vtkRenderWindow *renderwindow) { if (m_RenderWindow != renderwindow) { if (m_RenderWindow != nullptr) { m_RenderWindow->Delete(); } m_RenderWindow = renderwindow; if (m_RenderWindow != nullptr) { m_RenderWindow->Register(nullptr); } } RemoveAllLocalStorages(); if (m_CameraController.IsNotNull()) { m_CameraController->SetRenderer(this); } } void mitk::BaseRenderer::InitSize(int w, int h) { this->m_RenderWindow->SetSize(w, h); } void mitk::BaseRenderer::SetSlice(unsigned int slice) { if (m_Slice != slice) { m_Slice = slice; if (m_WorldTimeGeometry.IsNotNull()) { // get world geometry which may be rotated, for the current time step SlicedGeometry3D *slicedWorldGeometry = dynamic_cast(m_WorldTimeGeometry->GetGeometryForTimeStep(m_TimeStep).GetPointer()); if (slicedWorldGeometry != nullptr) { // if slice position is part of the world geometry... if (m_Slice >= slicedWorldGeometry->GetSlices()) // set the current worldplanegeomety as the selected 2D slice of the world geometry m_Slice = slicedWorldGeometry->GetSlices() - 1; SetCurrentWorldPlaneGeometry(slicedWorldGeometry->GetPlaneGeometry(m_Slice)); SetCurrentWorldGeometry(slicedWorldGeometry); } } else Modified(); } } void mitk::BaseRenderer::SetTimeStep(unsigned int timeStep) { if (m_TimeStep != timeStep) { m_TimeStep = timeStep; m_TimeStepUpdateTime.Modified(); if (m_WorldTimeGeometry.IsNotNull()) { if (m_TimeStep >= m_WorldTimeGeometry->CountTimeSteps()) m_TimeStep = m_WorldTimeGeometry->CountTimeSteps() - 1; SlicedGeometry3D *slicedWorldGeometry = dynamic_cast(m_WorldTimeGeometry->GetGeometryForTimeStep(m_TimeStep).GetPointer()); if (slicedWorldGeometry != nullptr) { SetCurrentWorldPlaneGeometry(slicedWorldGeometry->GetPlaneGeometry(m_Slice)); SetCurrentWorldGeometry(slicedWorldGeometry); } } else Modified(); } } int mitk::BaseRenderer::GetTimeStep(const mitk::BaseData *data) const { if ((data == nullptr) || (data->IsInitialized() == false)) { return -1; } return data->GetTimeGeometry()->TimePointToTimeStep(GetTime()); } mitk::ScalarType mitk::BaseRenderer::GetTime() const { if (m_WorldTimeGeometry.IsNull()) { return 0; } else { ScalarType timeInMS = m_WorldTimeGeometry->TimeStepToTimePoint(GetTimeStep()); if (timeInMS == itk::NumericTraits::NonpositiveMin()) return 0; else return timeInMS; } } void mitk::BaseRenderer::SetWorldTimeGeometry(mitk::TimeGeometry *geometry) { assert(geometry != nullptr); itkDebugMacro("setting WorldTimeGeometry to " << geometry); if (m_WorldTimeGeometry != geometry) { if (geometry->GetBoundingBoxInWorld()->GetDiagonalLength2() == 0) return; m_WorldTimeGeometry = geometry; itkDebugMacro("setting WorldTimeGeometry to " << m_WorldTimeGeometry); if (m_TimeStep >= m_WorldTimeGeometry->CountTimeSteps()) m_TimeStep = m_WorldTimeGeometry->CountTimeSteps() - 1; BaseGeometry *geometry3d; geometry3d = m_WorldTimeGeometry->GetGeometryForTimeStep(m_TimeStep); SetWorldGeometry3D(geometry3d); } } void mitk::BaseRenderer::SetWorldGeometry3D(mitk::BaseGeometry *geometry) { itkDebugMacro("setting WorldGeometry3D to " << geometry); if (geometry->GetBoundingBox()->GetDiagonalLength2() == 0) return; SlicedGeometry3D *slicedWorldGeometry; slicedWorldGeometry = dynamic_cast(geometry); PlaneGeometry::Pointer geometry2d; if (slicedWorldGeometry != nullptr) { if (m_Slice >= slicedWorldGeometry->GetSlices() && (m_Slice != 0)) m_Slice = slicedWorldGeometry->GetSlices() - 1; geometry2d = slicedWorldGeometry->GetPlaneGeometry(m_Slice); if (geometry2d.IsNull()) { PlaneGeometry::Pointer plane = mitk::PlaneGeometry::New(); plane->InitializeStandardPlane(slicedWorldGeometry); geometry2d = plane; } SetCurrentWorldGeometry(slicedWorldGeometry); } else { geometry2d = dynamic_cast(geometry); if (geometry2d.IsNull()) { PlaneGeometry::Pointer plane = PlaneGeometry::New(); plane->InitializeStandardPlane(geometry); geometry2d = plane; } SetCurrentWorldGeometry(geometry); } SetCurrentWorldPlaneGeometry(geometry2d); // calls Modified() if (m_CurrentWorldPlaneGeometry.IsNull()) itkWarningMacro("m_CurrentWorldPlaneGeometry is nullptr"); } void mitk::BaseRenderer::SetCurrentWorldPlaneGeometry(mitk::PlaneGeometry *geometry2d) { if (m_CurrentWorldPlaneGeometry != geometry2d) { m_CurrentWorldPlaneGeometry = geometry2d; m_CurrentWorldPlaneGeometryData->SetPlaneGeometry(m_CurrentWorldPlaneGeometry); m_CurrentWorldPlaneGeometryUpdateTime.Modified(); Modified(); } } void mitk::BaseRenderer::SendUpdateSlice() { m_CurrentWorldPlaneGeometryUpdateTime.Modified(); } int *mitk::BaseRenderer::GetSize() const { return this->m_RenderWindow->GetSize(); } int *mitk::BaseRenderer::GetViewportSize() const { return this->m_VtkRenderer->GetSize(); } void mitk::BaseRenderer::SetCurrentWorldGeometry(mitk::BaseGeometry *geometry) { m_CurrentWorldGeometry = geometry; if (geometry == nullptr) { m_Bounds[0] = 0; m_Bounds[1] = 0; m_Bounds[2] = 0; m_Bounds[3] = 0; m_Bounds[4] = 0; m_Bounds[5] = 0; m_EmptyWorldGeometry = true; return; } BoundingBox::Pointer boundingBox = m_CurrentWorldGeometry->CalculateBoundingBoxRelativeToTransform(nullptr); const BoundingBox::BoundsArrayType &worldBounds = boundingBox->GetBounds(); m_Bounds[0] = worldBounds[0]; m_Bounds[1] = worldBounds[1]; m_Bounds[2] = worldBounds[2]; m_Bounds[3] = worldBounds[3]; m_Bounds[4] = worldBounds[4]; m_Bounds[5] = worldBounds[5]; if (boundingBox->GetDiagonalLength2() <= mitk::eps) m_EmptyWorldGeometry = true; else m_EmptyWorldGeometry = false; } void mitk::BaseRenderer::SetGeometry(const itk::EventObject &geometrySendEvent) { - const SliceNavigationController::GeometrySendEvent *sendEvent = + const auto *sendEvent = dynamic_cast(&geometrySendEvent); assert(sendEvent != nullptr); SetWorldTimeGeometry(sendEvent->GetTimeGeometry()); } void mitk::BaseRenderer::UpdateGeometry(const itk::EventObject &geometryUpdateEvent) { - const SliceNavigationController::GeometryUpdateEvent *updateEvent = + const auto *updateEvent = dynamic_cast(&geometryUpdateEvent); if (updateEvent == nullptr) return; if (m_CurrentWorldGeometry.IsNotNull()) { - SlicedGeometry3D *slicedWorldGeometry = dynamic_cast(m_CurrentWorldGeometry.GetPointer()); + auto *slicedWorldGeometry = dynamic_cast(m_CurrentWorldGeometry.GetPointer()); if (slicedWorldGeometry) { PlaneGeometry *geometry2D = slicedWorldGeometry->GetPlaneGeometry(m_Slice); SetCurrentWorldPlaneGeometry(geometry2D); // calls Modified() } } } void mitk::BaseRenderer::SetGeometrySlice(const itk::EventObject &geometrySliceEvent) { - const SliceNavigationController::GeometrySliceEvent *sliceEvent = + const auto *sliceEvent = dynamic_cast(&geometrySliceEvent); assert(sliceEvent != nullptr); SetSlice(sliceEvent->GetPos()); } void mitk::BaseRenderer::SetGeometryTime(const itk::EventObject &geometryTimeEvent) { - const SliceNavigationController::GeometryTimeEvent *timeEvent = + const auto *timeEvent = dynamic_cast(&geometryTimeEvent); assert(timeEvent != nullptr); SetTimeStep(timeEvent->GetPos()); } const double *mitk::BaseRenderer::GetBounds() const { return m_Bounds; } void mitk::BaseRenderer::DrawOverlayMouse(mitk::Point2D &itkNotUsed(p2d)) { MITK_INFO << "BaseRenderer::DrawOverlayMouse()- should be inconcret implementation OpenGLRenderer." << std::endl; } void mitk::BaseRenderer::RequestUpdate() { SetConstrainZoomingAndPanning(true); m_RenderingManager->RequestUpdate(this->m_RenderWindow); } void mitk::BaseRenderer::ForceImmediateUpdate() { m_RenderingManager->ForceImmediateUpdate(this->m_RenderWindow); } unsigned int mitk::BaseRenderer::GetNumberOfVisibleLODEnabledMappers() const { return m_NumberOfVisibleLODEnabledMappers; } mitk::RenderingManager *mitk::BaseRenderer::GetRenderingManager() const { return m_RenderingManager.GetPointer(); } /*! Sets the new Navigation controller */ void mitk::BaseRenderer::SetSliceNavigationController(mitk::SliceNavigationController *SlicenavigationController) { if (SlicenavigationController == nullptr) return; // copy worldgeometry SlicenavigationController->SetInputWorldTimeGeometry(SlicenavigationController->GetCreatedWorldGeometry()); SlicenavigationController->Update(); // set new m_SliceNavigationController = SlicenavigationController; m_SliceNavigationController->SetRenderer(this); if (m_SliceNavigationController.IsNotNull()) { m_SliceNavigationController->ConnectGeometrySliceEvent(this); m_SliceNavigationController->ConnectGeometryUpdateEvent(this); m_SliceNavigationController->ConnectGeometryTimeEvent(this, false); } } void mitk::BaseRenderer::DisplayToWorld(const Point2D &displayPoint, Point3D &worldIndex) const { if (m_MapperID == BaseRenderer::Standard2D) { double display[3], *world; // For the rigth z-position in display coordinates, take the focal point, convert it to display and use it for // correct depth. double *displayCoord; double cameraFP[4]; // Get camera focal point and position. Convert to display (screen) // coordinates. We need a depth value for z-buffer. this->GetVtkRenderer()->GetActiveCamera()->GetFocalPoint(cameraFP); cameraFP[3] = 0.0; this->GetVtkRenderer()->SetWorldPoint(cameraFP[0], cameraFP[1], cameraFP[2], cameraFP[3]); this->GetVtkRenderer()->WorldToDisplay(); displayCoord = this->GetVtkRenderer()->GetDisplayPoint(); // now convert the display point to world coordinates display[0] = displayPoint[0]; display[1] = displayPoint[1]; display[2] = displayCoord[2]; this->GetVtkRenderer()->SetDisplayPoint(display); this->GetVtkRenderer()->DisplayToWorld(); world = this->GetVtkRenderer()->GetWorldPoint(); for (int i = 0; i < 3; i++) { worldIndex[i] = world[i] / world[3]; } } else if (m_MapperID == BaseRenderer::Standard3D) { PickWorldPoint( displayPoint, worldIndex); // Seems to be the same code as above, but subclasses may contain different implementations. } return; } void mitk::BaseRenderer::DisplayToPlane(const Point2D &displayPoint, Point2D &planePointInMM) const { if (m_MapperID == BaseRenderer::Standard2D) { Point3D worldPoint; this->DisplayToWorld(displayPoint, worldPoint); this->m_CurrentWorldPlaneGeometry->Map(worldPoint, planePointInMM); } else if (m_MapperID == BaseRenderer::Standard3D) { MITK_WARN << "No conversion possible with 3D mapper."; return; } return; } void mitk::BaseRenderer::WorldToDisplay(const Point3D &worldIndex, Point2D &displayPoint) const { double world[4], *display; world[0] = worldIndex[0]; world[1] = worldIndex[1]; world[2] = worldIndex[2]; world[3] = 1.0; this->GetVtkRenderer()->SetWorldPoint(world); this->GetVtkRenderer()->WorldToDisplay(); display = this->GetVtkRenderer()->GetDisplayPoint(); displayPoint[0] = display[0]; displayPoint[1] = display[1]; return; } void mitk::BaseRenderer::WorldToView(const mitk::Point3D &worldIndex, mitk::Point2D &viewPoint) const { double world[4], *view; world[0] = worldIndex[0]; world[1] = worldIndex[1]; world[2] = worldIndex[2]; world[3] = 1.0; this->GetVtkRenderer()->SetWorldPoint(world); this->GetVtkRenderer()->WorldToView(); view = this->GetVtkRenderer()->GetViewPoint(); this->GetVtkRenderer()->ViewToNormalizedViewport(view[0], view[1], view[2]); viewPoint[0] = view[0] * this->GetViewportSize()[0]; viewPoint[1] = view[1] * this->GetViewportSize()[1]; return; } void mitk::BaseRenderer::PlaneToDisplay(const Point2D &planePointInMM, Point2D &displayPoint) const { Point3D worldPoint; this->m_CurrentWorldPlaneGeometry->Map(planePointInMM, worldPoint); this->WorldToDisplay(worldPoint, displayPoint); return; } void mitk::BaseRenderer::PlaneToView(const Point2D &planePointInMM, Point2D &viewPoint) const { Point3D worldPoint; this->m_CurrentWorldPlaneGeometry->Map(planePointInMM, worldPoint); this->WorldToView(worldPoint,viewPoint); return; } double mitk::BaseRenderer::GetScaleFactorMMPerDisplayUnit() const { if (this->GetMapperID() == BaseRenderer::Standard2D) { // GetParallelScale returns half of the height of the render window in mm. // Divided by the half size of the Display size in pixel givest the mm per pixel. return this->GetVtkRenderer()->GetActiveCamera()->GetParallelScale() * 2.0 / GetViewportSize()[1]; } else return 1.0; } mitk::Point2D mitk::BaseRenderer::GetDisplaySizeInMM() const { Point2D dispSizeInMM; dispSizeInMM[0] = GetSizeX() * GetScaleFactorMMPerDisplayUnit(); dispSizeInMM[1] = GetSizeY() * GetScaleFactorMMPerDisplayUnit(); return dispSizeInMM; } mitk::Point2D mitk::BaseRenderer::GetViewportSizeInMM() const { Point2D dispSizeInMM; dispSizeInMM[0] = GetViewportSize()[0] * GetScaleFactorMMPerDisplayUnit(); dispSizeInMM[1] = GetViewportSize()[1] * GetScaleFactorMMPerDisplayUnit(); return dispSizeInMM; } mitk::Point2D mitk::BaseRenderer::GetOriginInMM() const { Point2D originPx; originPx[0] = m_VtkRenderer->GetOrigin()[0]; originPx[1] = m_VtkRenderer->GetOrigin()[1]; Point2D displayGeometryOriginInMM; DisplayToPlane(originPx, displayGeometryOriginInMM); // top left of the render window (Origin) return displayGeometryOriginInMM; } void mitk::BaseRenderer::SetConstrainZoomingAndPanning(bool constrain) { m_ConstrainZoomingAndPanning = constrain; if (m_ConstrainZoomingAndPanning) { this->GetCameraController()->AdjustCameraToPlane(); } } mitk::Point3D mitk::BaseRenderer::Map2DRendererPositionTo3DWorldPosition(const Point2D &mousePosition) const { // DEPRECATED: Map2DRendererPositionTo3DWorldPosition is deprecated. use DisplayToWorldInstead Point3D position; DisplayToWorld(mousePosition, position); return position; } void mitk::BaseRenderer::PrintSelf(std::ostream &os, itk::Indent indent) const { os << indent << " MapperID: " << m_MapperID << std::endl; os << indent << " Slice: " << m_Slice << std::endl; os << indent << " TimeStep: " << m_TimeStep << std::endl; os << indent << " CurrentWorldPlaneGeometry: "; if (m_CurrentWorldPlaneGeometry.IsNull()) os << "nullptr" << std::endl; else m_CurrentWorldPlaneGeometry->Print(os, indent); os << indent << " CurrentWorldPlaneGeometryUpdateTime: " << m_CurrentWorldPlaneGeometryUpdateTime << std::endl; os << indent << " CurrentWorldPlaneGeometryTransformTime: " << m_CurrentWorldPlaneGeometryTransformTime << std::endl; Superclass::PrintSelf(os, indent); } diff --git a/Modules/Core/src/Rendering/mitkImageVtkMapper2D.cpp b/Modules/Core/src/Rendering/mitkImageVtkMapper2D.cpp index 7d10db6d7c..853cd1a06c 100644 --- a/Modules/Core/src/Rendering/mitkImageVtkMapper2D.cpp +++ b/Modules/Core/src/Rendering/mitkImageVtkMapper2D.cpp @@ -1,1130 +1,1130 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ // MITK #include #include #include #include #include #include #include #include #include #include #include //#include #include "mitkImageStatisticsHolder.h" #include "mitkPlaneClipping.h" #include // MITK Rendering #include "mitkImageVtkMapper2D.h" #include "vtkMitkLevelWindowFilter.h" #include "vtkMitkThickSlicesFilter.h" #include "vtkNeverTranslucentTexture.h" // VTK #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include // ITK #include #include mitk::ImageVtkMapper2D::ImageVtkMapper2D() { } mitk::ImageVtkMapper2D::~ImageVtkMapper2D() { // The 3D RW Mapper (PlaneGeometryDataVtkMapper3D) is listening to this event, // in order to delete the images from the 3D RW. this->InvokeEvent(itk::DeleteEvent()); } // set the two points defining the textured plane according to the dimension and spacing void mitk::ImageVtkMapper2D::GeneratePlane(mitk::BaseRenderer *renderer, double planeBounds[6]) { LocalStorage *localStorage = m_LSH.GetLocalStorage(renderer); float depth = this->CalculateLayerDepth(renderer); // Set the origin to (xMin; yMin; depth) of the plane. This is necessary for obtaining the correct // plane size in crosshair rotation and swivel mode. localStorage->m_Plane->SetOrigin(planeBounds[0], planeBounds[2], depth); // These two points define the axes of the plane in combination with the origin. // Point 1 is the x-axis and point 2 the y-axis. // Each plane is transformed according to the view (axial, coronal and saggital) afterwards. localStorage->m_Plane->SetPoint1(planeBounds[1], planeBounds[2], depth); // P1: (xMax, yMin, depth) localStorage->m_Plane->SetPoint2(planeBounds[0], planeBounds[3], depth); // P2: (xMin, yMax, depth) } float mitk::ImageVtkMapper2D::CalculateLayerDepth(mitk::BaseRenderer *renderer) { // get the clipping range to check how deep into z direction we can render images double maxRange = renderer->GetVtkRenderer()->GetActiveCamera()->GetClippingRange()[1]; // Due to a VTK bug, we cannot use the whole clipping range. /100 is empirically determined float depth = -maxRange * 0.01; // divide by 100 int layer = 0; GetDataNode()->GetIntProperty("layer", layer, renderer); // add the layer property for each image to render images with a higher layer on top of the others depth += layer * 10; //*10: keep some room for each image (e.g. for ODFs in between) if (depth > 0.0f) { depth = 0.0f; MITK_WARN << "Layer value exceeds clipping range. Set to minimum instead."; } return depth; } const mitk::Image *mitk::ImageVtkMapper2D::GetInput(void) { return static_cast(GetDataNode()->GetData()); } vtkProp *mitk::ImageVtkMapper2D::GetVtkProp(mitk::BaseRenderer *renderer) { // return the actor corresponding to the renderer return m_LSH.GetLocalStorage(renderer)->m_Actors; } void mitk::ImageVtkMapper2D::GenerateDataForRenderer(mitk::BaseRenderer *renderer) { LocalStorage *localStorage = m_LSH.GetLocalStorage(renderer); SetVtkMapperImmediateModeRendering(localStorage->m_Mapper); - mitk::Image *image = const_cast(this->GetInput()); + auto *image = const_cast(this->GetInput()); mitk::DataNode *datanode = this->GetDataNode(); if (nullptr == image || !image->IsInitialized()) { return; } // check if there is a valid worldGeometry const PlaneGeometry *worldGeometry = renderer->GetCurrentWorldPlaneGeometry(); if (nullptr == worldGeometry || !worldGeometry->IsValid() || !worldGeometry->HasReferenceGeometry()) { return; } image->Update(); // early out if there is no intersection of the current rendering geometry // and the geometry of the image that is to be rendered. if (!RenderingGeometryIntersectsImage(worldGeometry, image->GetSlicedGeometry())) { // set image to nullptr, to clear the texture in 3D, because // the latest image is used there if the plane is out of the geometry // see bug-13275 localStorage->m_ReslicedImage = nullptr; localStorage->m_Mapper->SetInputData(localStorage->m_EmptyPolyData); return; } // set main input for ExtractSliceFilter localStorage->m_Reslicer->SetInput(image); localStorage->m_Reslicer->SetWorldGeometry(worldGeometry); localStorage->m_Reslicer->SetTimeStep(this->GetTimestep()); // set the transformation of the image to adapt reslice axis localStorage->m_Reslicer->SetResliceTransformByGeometry( image->GetTimeGeometry()->GetGeometryForTimeStep(this->GetTimestep())); // is the geometry of the slice based on the input image or the worldgeometry? bool inPlaneResampleExtentByGeometry = false; datanode->GetBoolProperty("in plane resample extent by geometry", inPlaneResampleExtentByGeometry, renderer); localStorage->m_Reslicer->SetInPlaneResampleExtentByGeometry(inPlaneResampleExtentByGeometry); // Initialize the interpolation mode for resampling; switch to nearest // neighbor if the input image is too small. if ((image->GetDimension() >= 3) && (image->GetDimension(2) > 1)) { VtkResliceInterpolationProperty *resliceInterpolationProperty; datanode->GetProperty(resliceInterpolationProperty, "reslice interpolation", renderer); int interpolationMode = VTK_RESLICE_NEAREST; if (resliceInterpolationProperty != nullptr) { interpolationMode = resliceInterpolationProperty->GetInterpolation(); } switch (interpolationMode) { case VTK_RESLICE_NEAREST: localStorage->m_Reslicer->SetInterpolationMode(ExtractSliceFilter::RESLICE_NEAREST); break; case VTK_RESLICE_LINEAR: localStorage->m_Reslicer->SetInterpolationMode(ExtractSliceFilter::RESLICE_LINEAR); break; case VTK_RESLICE_CUBIC: localStorage->m_Reslicer->SetInterpolationMode(ExtractSliceFilter::RESLICE_CUBIC); break; } } else { localStorage->m_Reslicer->SetInterpolationMode(ExtractSliceFilter::RESLICE_NEAREST); } // set the vtk output property to true, makes sure that no unneeded mitk image convertion // is done. localStorage->m_Reslicer->SetVtkOutputRequest(true); // Thickslicing int thickSlicesMode = 0; int thickSlicesNum = 1; // Thick slices parameters if (image->GetPixelType().GetNumberOfComponents() == 1) // for now only single component are allowed { DataNode *dn = renderer->GetCurrentWorldPlaneGeometryNode(); if (dn) { ResliceMethodProperty *resliceMethodEnumProperty = nullptr; if (dn->GetProperty(resliceMethodEnumProperty, "reslice.thickslices", renderer) && resliceMethodEnumProperty) thickSlicesMode = resliceMethodEnumProperty->GetValueAsId(); IntProperty *intProperty = nullptr; if (dn->GetProperty(intProperty, "reslice.thickslices.num", renderer) && intProperty) { thickSlicesNum = intProperty->GetValue(); if (thickSlicesNum < 1) thickSlicesNum = 1; } } else { MITK_WARN << "no associated widget plane data tree node found"; } } - const PlaneGeometry *planeGeometry = dynamic_cast(worldGeometry); + const auto *planeGeometry = dynamic_cast(worldGeometry); if (thickSlicesMode > 0) { double dataZSpacing = 1.0; Vector3D normInIndex, normal; - const mitk::AbstractTransformGeometry *abstractGeometry = + const auto *abstractGeometry = dynamic_cast(worldGeometry); if (abstractGeometry != nullptr) normal = abstractGeometry->GetPlane()->GetNormal(); else { if (planeGeometry != nullptr) { normal = planeGeometry->GetNormal(); } else return; // no fitting geometry set } normal.Normalize(); image->GetTimeGeometry()->GetGeometryForTimeStep(this->GetTimestep())->WorldToIndex(normal, normInIndex); dataZSpacing = 1.0 / normInIndex.GetNorm(); localStorage->m_Reslicer->SetOutputDimensionality(3); localStorage->m_Reslicer->SetOutputSpacingZDirection(dataZSpacing); localStorage->m_Reslicer->SetOutputExtentZDirection(-thickSlicesNum, 0 + thickSlicesNum); // Do the reslicing. Modified() is called to make sure that the reslicer is // executed even though the input geometry information did not change; this // is necessary when the input /em data, but not the /em geometry changes. localStorage->m_TSFilter->SetThickSliceMode(thickSlicesMode - 1); localStorage->m_TSFilter->SetInputData(localStorage->m_Reslicer->GetVtkOutput()); // vtkFilter=>mitkFilter=>vtkFilter update mechanism will fail without calling manually localStorage->m_Reslicer->Modified(); localStorage->m_Reslicer->Update(); localStorage->m_TSFilter->Modified(); localStorage->m_TSFilter->Update(); localStorage->m_ReslicedImage = localStorage->m_TSFilter->GetOutput(); } else { // this is needed when thick mode was enable bevore. These variable have to be reset to default values localStorage->m_Reslicer->SetOutputDimensionality(2); localStorage->m_Reslicer->SetOutputSpacingZDirection(1.0); localStorage->m_Reslicer->SetOutputExtentZDirection(0, 0); localStorage->m_Reslicer->Modified(); // start the pipeline with updating the largest possible, needed if the geometry of the input has changed localStorage->m_Reslicer->UpdateLargestPossibleRegion(); localStorage->m_ReslicedImage = localStorage->m_Reslicer->GetVtkOutput(); } // Bounds information for reslicing (only reuqired if reference geometry // is present) // this used for generating a vtkPLaneSource with the right size double sliceBounds[6]; for (auto &sliceBound : sliceBounds) { sliceBound = 0.0; } localStorage->m_Reslicer->GetClippedPlaneBounds(sliceBounds); // get the spacing of the slice localStorage->m_mmPerPixel = localStorage->m_Reslicer->GetOutputSpacing(); // calculate minimum bounding rect of IMAGE in texture { double textureClippingBounds[6]; for (auto &textureClippingBound : textureClippingBounds) { textureClippingBound = 0.0; } // Calculate the actual bounds of the transformed plane clipped by the // dataset bounding box; this is required for drawing the texture at the // correct position during 3D mapping. mitk::PlaneClipping::CalculateClippedPlaneBounds(image->GetGeometry(), planeGeometry, textureClippingBounds); textureClippingBounds[0] = static_cast(textureClippingBounds[0] / localStorage->m_mmPerPixel[0] + 0.5); textureClippingBounds[1] = static_cast(textureClippingBounds[1] / localStorage->m_mmPerPixel[0] + 0.5); textureClippingBounds[2] = static_cast(textureClippingBounds[2] / localStorage->m_mmPerPixel[1] + 0.5); textureClippingBounds[3] = static_cast(textureClippingBounds[3] / localStorage->m_mmPerPixel[1] + 0.5); // clipping bounds for cutting the image localStorage->m_LevelWindowFilter->SetClippingBounds(textureClippingBounds); } // get the number of scalar components to distinguish between different image types int numberOfComponents = localStorage->m_ReslicedImage->GetNumberOfScalarComponents(); // get the binary property bool binary = false; bool binaryOutline = false; datanode->GetBoolProperty("binary", binary, renderer); if (binary) // binary image { datanode->GetBoolProperty("outline binary", binaryOutline, renderer); if (binaryOutline) // contour rendering { // get pixel type of vtk image itk::ImageIOBase::IOComponentType componentType = static_cast(image->GetPixelType().GetComponentType()); switch (componentType) { case itk::ImageIOBase::UCHAR: // generate contours/outlines localStorage->m_OutlinePolyData = CreateOutlinePolyData(renderer); break; case itk::ImageIOBase::USHORT: // generate contours/outlines localStorage->m_OutlinePolyData = CreateOutlinePolyData(renderer); break; default: binaryOutline = false; this->ApplyLookuptable(renderer); MITK_WARN << "Type of all binary images should be unsigned char or unsigned short. Outline does not work on other pixel types!"; } if (binaryOutline) // binary outline is still true --> add outline { float binaryOutlineWidth = 1.0; if (datanode->GetFloatProperty("outline width", binaryOutlineWidth, renderer)) { if (localStorage->m_Actors->GetNumberOfPaths() > 1) { float binaryOutlineShadowWidth = 1.5; datanode->GetFloatProperty("outline shadow width", binaryOutlineShadowWidth, renderer); dynamic_cast(localStorage->m_Actors->GetParts()->GetItemAsObject(0)) ->GetProperty() ->SetLineWidth(binaryOutlineWidth * binaryOutlineShadowWidth); } localStorage->m_Actor->GetProperty()->SetLineWidth(binaryOutlineWidth); } } } else // standard binary image { if (numberOfComponents != 1) { MITK_ERROR << "Rendering Error: Binary Images with more then 1 component are not supported!"; } } } this->ApplyOpacity(renderer); this->ApplyRenderingMode(renderer); // do not use a VTK lookup table (we do that ourselves in m_LevelWindowFilter) localStorage->m_Texture->MapColorScalarsThroughLookupTableOff(); int displayedComponent = 0; if (datanode->GetIntProperty("Image.Displayed Component", displayedComponent, renderer) && numberOfComponents > 1) { localStorage->m_VectorComponentExtractor->SetComponents(displayedComponent); localStorage->m_VectorComponentExtractor->SetInputData(localStorage->m_ReslicedImage); localStorage->m_LevelWindowFilter->SetInputConnection(localStorage->m_VectorComponentExtractor->GetOutputPort(0)); } else { // connect the input with the levelwindow filter localStorage->m_LevelWindowFilter->SetInputData(localStorage->m_ReslicedImage); } // check for texture interpolation property bool textureInterpolation = false; GetDataNode()->GetBoolProperty("texture interpolation", textureInterpolation, renderer); // set the interpolation modus according to the property localStorage->m_Texture->SetInterpolate(textureInterpolation); // connect the texture with the output of the levelwindow filter localStorage->m_Texture->SetInputConnection(localStorage->m_LevelWindowFilter->GetOutputPort()); this->TransformActor(renderer); - vtkActor *contourShadowActor = dynamic_cast(localStorage->m_Actors->GetParts()->GetItemAsObject(0)); + auto *contourShadowActor = dynamic_cast(localStorage->m_Actors->GetParts()->GetItemAsObject(0)); if (binary && binaryOutline) // connect the mapper with the polyData which contains the lines { // We need the contour for the binary outline property as actor localStorage->m_Mapper->SetInputData(localStorage->m_OutlinePolyData); localStorage->m_Actor->SetTexture(nullptr); // no texture for contours bool binaryOutlineShadow = false; datanode->GetBoolProperty("outline binary shadow", binaryOutlineShadow, renderer); if (binaryOutlineShadow) { contourShadowActor->SetVisibility(true); } else { contourShadowActor->SetVisibility(false); } } else { // Connect the mapper with the input texture. This is the standard case. // setup the textured plane this->GeneratePlane(renderer, sliceBounds); // set the plane as input for the mapper localStorage->m_Mapper->SetInputConnection(localStorage->m_Plane->GetOutputPort()); // set the texture for the actor localStorage->m_Actor->SetTexture(localStorage->m_Texture); contourShadowActor->SetVisibility(false); } // We have been modified => save this for next Update() localStorage->m_LastUpdateTime.Modified(); } void mitk::ImageVtkMapper2D::ApplyLevelWindow(mitk::BaseRenderer *renderer) { LocalStorage *localStorage = this->GetLocalStorage(renderer); LevelWindow levelWindow; this->GetDataNode()->GetLevelWindow(levelWindow, renderer, "levelwindow"); localStorage->m_LevelWindowFilter->GetLookupTable()->SetRange(levelWindow.GetLowerWindowBound(), levelWindow.GetUpperWindowBound()); mitk::LevelWindow opacLevelWindow; if (this->GetDataNode()->GetLevelWindow(opacLevelWindow, renderer, "opaclevelwindow")) { // pass the opaque level window to the filter localStorage->m_LevelWindowFilter->SetMinOpacity(opacLevelWindow.GetLowerWindowBound()); localStorage->m_LevelWindowFilter->SetMaxOpacity(opacLevelWindow.GetUpperWindowBound()); } else { // no opaque level window localStorage->m_LevelWindowFilter->SetMinOpacity(0.0); localStorage->m_LevelWindowFilter->SetMaxOpacity(255.0); } } void mitk::ImageVtkMapper2D::ApplyColor(mitk::BaseRenderer *renderer) { LocalStorage *localStorage = this->GetLocalStorage(renderer); float rgb[3] = {1.0f, 1.0f, 1.0f}; // check for color prop and use it for rendering if it exists // binary image hovering & binary image selection bool hover = false; bool selected = false; bool binary = false; GetDataNode()->GetBoolProperty("binaryimage.ishovering", hover, renderer); GetDataNode()->GetBoolProperty("selected", selected, renderer); GetDataNode()->GetBoolProperty("binary", binary, renderer); if (binary && hover && !selected) { mitk::ColorProperty::Pointer colorprop = dynamic_cast(GetDataNode()->GetProperty("binaryimage.hoveringcolor", renderer)); if (colorprop.IsNotNull()) { memcpy(rgb, colorprop->GetColor().GetDataPointer(), 3 * sizeof(float)); } else { GetDataNode()->GetColor(rgb, renderer, "color"); } } if (binary && selected) { mitk::ColorProperty::Pointer colorprop = dynamic_cast(GetDataNode()->GetProperty("binaryimage.selectedcolor", renderer)); if (colorprop.IsNotNull()) { memcpy(rgb, colorprop->GetColor().GetDataPointer(), 3 * sizeof(float)); } else { GetDataNode()->GetColor(rgb, renderer, "color"); } } if (!binary || (!hover && !selected)) { GetDataNode()->GetColor(rgb, renderer, "color"); } double rgbConv[3] = {(double)rgb[0], (double)rgb[1], (double)rgb[2]}; // conversion to double for VTK dynamic_cast(localStorage->m_Actors->GetParts()->GetItemAsObject(0))->GetProperty()->SetColor(rgbConv); localStorage->m_Actor->GetProperty()->SetColor(rgbConv); if (localStorage->m_Actors->GetParts()->GetNumberOfItems() > 1) { float rgb[3] = {1.0f, 1.0f, 1.0f}; mitk::ColorProperty::Pointer colorprop = dynamic_cast(GetDataNode()->GetProperty("outline binary shadow color", renderer)); if (colorprop.IsNotNull()) { memcpy(rgb, colorprop->GetColor().GetDataPointer(), 3 * sizeof(float)); } double rgbConv[3] = {(double)rgb[0], (double)rgb[1], (double)rgb[2]}; // conversion to double for VTK dynamic_cast(localStorage->m_Actors->GetParts()->GetItemAsObject(0))->GetProperty()->SetColor(rgbConv); } } void mitk::ImageVtkMapper2D::ApplyOpacity(mitk::BaseRenderer *renderer) { LocalStorage *localStorage = this->GetLocalStorage(renderer); float opacity = 1.0f; // check for opacity prop and use it for rendering if it exists GetDataNode()->GetOpacity(opacity, renderer, "opacity"); // set the opacity according to the properties localStorage->m_Actor->GetProperty()->SetOpacity(opacity); if (localStorage->m_Actors->GetParts()->GetNumberOfItems() > 1) { dynamic_cast(localStorage->m_Actors->GetParts()->GetItemAsObject(0)) ->GetProperty() ->SetOpacity(opacity); } } void mitk::ImageVtkMapper2D::ApplyRenderingMode(mitk::BaseRenderer *renderer) { LocalStorage *localStorage = m_LSH.GetLocalStorage(renderer); bool binary = false; this->GetDataNode()->GetBoolProperty("binary", binary, renderer); if (binary) // is it a binary image? { // for binary images, we always use our default LuT and map every value to (0,1) // the opacity of 0 will always be 0.0. We never a apply a LuT/TfF nor a level window. localStorage->m_LevelWindowFilter->SetLookupTable(localStorage->m_BinaryLookupTable); } else { // all other image types can make use of the rendering mode int renderingMode = mitk::RenderingModeProperty::LOOKUPTABLE_LEVELWINDOW_COLOR; mitk::RenderingModeProperty::Pointer mode = dynamic_cast(this->GetDataNode()->GetProperty("Image Rendering.Mode", renderer)); if (mode.IsNotNull()) { renderingMode = mode->GetRenderingMode(); } switch (renderingMode) { case mitk::RenderingModeProperty::LOOKUPTABLE_LEVELWINDOW_COLOR: MITK_DEBUG << "'Image Rendering.Mode' = LevelWindow_LookupTable_Color"; this->ApplyLookuptable(renderer); this->ApplyLevelWindow(renderer); break; case mitk::RenderingModeProperty::COLORTRANSFERFUNCTION_LEVELWINDOW_COLOR: MITK_DEBUG << "'Image Rendering.Mode' = LevelWindow_ColorTransferFunction_Color"; this->ApplyColorTransferFunction(renderer); this->ApplyLevelWindow(renderer); break; case mitk::RenderingModeProperty::LOOKUPTABLE_COLOR: MITK_DEBUG << "'Image Rendering.Mode' = LookupTable_Color"; this->ApplyLookuptable(renderer); break; case mitk::RenderingModeProperty::COLORTRANSFERFUNCTION_COLOR: MITK_DEBUG << "'Image Rendering.Mode' = ColorTransferFunction_Color"; this->ApplyColorTransferFunction(renderer); break; default: MITK_ERROR << "No valid 'Image Rendering.Mode' set. Using LOOKUPTABLE_LEVELWINDOW_COLOR instead."; this->ApplyLookuptable(renderer); this->ApplyLevelWindow(renderer); break; } } // we apply color for all images (including binaries). this->ApplyColor(renderer); } void mitk::ImageVtkMapper2D::ApplyLookuptable(mitk::BaseRenderer *renderer) { LocalStorage *localStorage = m_LSH.GetLocalStorage(renderer); vtkLookupTable *usedLookupTable = localStorage->m_ColorLookupTable; // If lookup table or transferfunction use is requested... mitk::LookupTableProperty::Pointer lookupTableProp = dynamic_cast(this->GetDataNode()->GetProperty("LookupTable")); if (lookupTableProp.IsNotNull()) // is a lookuptable set? { usedLookupTable = lookupTableProp->GetLookupTable()->GetVtkLookupTable(); } else { //"Image Rendering.Mode was set to use a lookup table but there is no property 'LookupTable'. // A default (rainbow) lookup table will be used. // Here have to do nothing. Warning for the user has been removed, due to unwanted console output // in every interation of the rendering. } localStorage->m_LevelWindowFilter->SetLookupTable(usedLookupTable); } void mitk::ImageVtkMapper2D::ApplyColorTransferFunction(mitk::BaseRenderer *renderer) { mitk::TransferFunctionProperty::Pointer transferFunctionProp = dynamic_cast( this->GetDataNode()->GetProperty("Image Rendering.Transfer Function", renderer)); if (transferFunctionProp.IsNull()) { MITK_ERROR << "'Image Rendering.Mode'' was set to use a color transfer function but there is no property 'Image " "Rendering.Transfer Function'. Nothing will be done."; return; } LocalStorage *localStorage = m_LSH.GetLocalStorage(renderer); // pass the transfer function to our level window filter localStorage->m_LevelWindowFilter->SetLookupTable(transferFunctionProp->GetValue()->GetColorTransferFunction()); localStorage->m_LevelWindowFilter->SetOpacityPiecewiseFunction( transferFunctionProp->GetValue()->GetScalarOpacityFunction()); } void mitk::ImageVtkMapper2D::Update(mitk::BaseRenderer *renderer) { bool visible = true; GetDataNode()->GetVisibility(visible, renderer, "visible"); if (!visible) { return; } - mitk::Image *data = const_cast(this->GetInput()); + auto *data = const_cast(this->GetInput()); if (data == nullptr) { return; } // Calculate time step of the input data for the specified renderer (integer value) this->CalculateTimeStep(renderer); // Check if time step is valid const TimeGeometry *dataTimeGeometry = data->GetTimeGeometry(); if ((dataTimeGeometry == nullptr) || (dataTimeGeometry->CountTimeSteps() == 0) || (!dataTimeGeometry->IsValidTimeStep(this->GetTimestep()))) { return; } const DataNode *node = this->GetDataNode(); data->UpdateOutputInformation(); LocalStorage *localStorage = m_LSH.GetLocalStorage(renderer); // check if something important has changed and we need to rerender if ((localStorage->m_LastUpdateTime < node->GetMTime()) // was the node modified? || (localStorage->m_LastUpdateTime < data->GetPipelineMTime()) // Was the data modified? || (localStorage->m_LastUpdateTime < renderer->GetCurrentWorldPlaneGeometryUpdateTime()) // was the geometry modified? || (localStorage->m_LastUpdateTime < renderer->GetCurrentWorldPlaneGeometry()->GetMTime()) || (localStorage->m_LastUpdateTime < node->GetPropertyList()->GetMTime()) // was a property modified? || (localStorage->m_LastUpdateTime < node->GetPropertyList(renderer)->GetMTime())) { this->GenerateDataForRenderer(renderer); } // since we have checked that nothing important has changed, we can set // m_LastUpdateTime to the current time localStorage->m_LastUpdateTime.Modified(); } void mitk::ImageVtkMapper2D::SetDefaultProperties(mitk::DataNode *node, mitk::BaseRenderer *renderer, bool overwrite) { mitk::Image::Pointer image = dynamic_cast(node->GetData()); // Properties common for both images and segmentations node->AddProperty("depthOffset", mitk::FloatProperty::New(0.0), renderer, overwrite); node->AddProperty("outline binary", mitk::BoolProperty::New(false), renderer, overwrite); node->AddProperty("outline width", mitk::FloatProperty::New(1.0), renderer, overwrite); node->AddProperty("outline binary shadow", mitk::BoolProperty::New(false), renderer, overwrite); node->AddProperty("outline binary shadow color", ColorProperty::New(0.0, 0.0, 0.0), renderer, overwrite); node->AddProperty("outline shadow width", mitk::FloatProperty::New(1.5), renderer, overwrite); if (image->IsRotated()) node->AddProperty("reslice interpolation", mitk::VtkResliceInterpolationProperty::New(VTK_RESLICE_CUBIC)); else node->AddProperty("reslice interpolation", mitk::VtkResliceInterpolationProperty::New()); node->AddProperty("texture interpolation", mitk::BoolProperty::New(false)); node->AddProperty("in plane resample extent by geometry", mitk::BoolProperty::New(false)); node->AddProperty("bounding box", mitk::BoolProperty::New(false)); mitk::RenderingModeProperty::Pointer renderingModeProperty = mitk::RenderingModeProperty::New(); node->AddProperty("Image Rendering.Mode", renderingModeProperty); // Set default grayscale look-up table mitk::LookupTable::Pointer mitkLut = mitk::LookupTable::New(); mitkLut->SetType(mitk::LookupTable::GRAYSCALE); mitk::LookupTableProperty::Pointer mitkLutProp = mitk::LookupTableProperty::New(); mitkLutProp->SetLookupTable(mitkLut); node->SetProperty("LookupTable", mitkLutProp); std::string photometricInterpretation; // DICOM tag telling us how pixel values should be displayed if (node->GetStringProperty("dicom.pixel.PhotometricInterpretation", photometricInterpretation)) { // modality provided by DICOM or other reader if (photometricInterpretation.find("MONOCHROME1") != std::string::npos) // meaning: display MINIMUM pixels as WHITE { // Set inverse grayscale look-up table mitkLut->SetType(mitk::LookupTable::INVERSE_GRAYSCALE); mitkLutProp->SetLookupTable(mitkLut); node->SetProperty("LookupTable", mitkLutProp); renderingModeProperty->SetValue(mitk::RenderingModeProperty::LOOKUPTABLE_LEVELWINDOW_COLOR); // USE lookuptable } // Otherwise do nothing - the default grayscale look-up table has already been set } bool isBinaryImage(false); if (!node->GetBoolProperty("binary", isBinaryImage) && image->GetPixelType().GetNumberOfComponents() == 1) { // ok, property is not set, use heuristic to determine if this // is a binary image mitk::Image::Pointer centralSliceImage; ScalarType minValue = 0.0; ScalarType maxValue = 0.0; ScalarType min2ndValue = 0.0; ScalarType max2ndValue = 0.0; mitk::ImageSliceSelector::Pointer sliceSelector = mitk::ImageSliceSelector::New(); sliceSelector->SetInput(image); sliceSelector->SetSliceNr(image->GetDimension(2) / 2); sliceSelector->SetTimeNr(image->GetDimension(3) / 2); sliceSelector->SetChannelNr(image->GetDimension(4) / 2); sliceSelector->Update(); centralSliceImage = sliceSelector->GetOutput(); if (centralSliceImage.IsNotNull() && centralSliceImage->IsInitialized()) { minValue = centralSliceImage->GetStatistics()->GetScalarValueMin(); maxValue = centralSliceImage->GetStatistics()->GetScalarValueMax(); min2ndValue = centralSliceImage->GetStatistics()->GetScalarValue2ndMin(); max2ndValue = centralSliceImage->GetStatistics()->GetScalarValue2ndMax(); } if ((maxValue == min2ndValue && minValue == max2ndValue) || minValue == maxValue) { // centralSlice is strange, lets look at all data minValue = image->GetStatistics()->GetScalarValueMin(); maxValue = image->GetStatistics()->GetScalarValueMaxNoRecompute(); min2ndValue = image->GetStatistics()->GetScalarValue2ndMinNoRecompute(); max2ndValue = image->GetStatistics()->GetScalarValue2ndMaxNoRecompute(); } isBinaryImage = (maxValue == min2ndValue && minValue == max2ndValue); } std::string className = image->GetNameOfClass(); if (className != "TensorImage" && className != "OdfImage") { PixelType pixelType = image->GetPixelType(); size_t numComponents = pixelType.GetNumberOfComponents(); if ((pixelType.GetPixelType() == itk::ImageIOBase::VECTOR && numComponents > 1) || numComponents == 2 || numComponents > 4) { node->AddProperty("Image.Displayed Component", mitk::IntProperty::New(0), renderer, overwrite); } } // some more properties specific for a binary... if (isBinaryImage) { node->AddProperty("opacity", mitk::FloatProperty::New(0.3f), renderer, overwrite); node->AddProperty("color", ColorProperty::New(1.0, 0.0, 0.0), renderer, overwrite); node->AddProperty("binaryimage.selectedcolor", ColorProperty::New(1.0, 0.0, 0.0), renderer, overwrite); node->AddProperty("binaryimage.selectedannotationcolor", ColorProperty::New(1.0, 0.0, 0.0), renderer, overwrite); node->AddProperty("binaryimage.hoveringcolor", ColorProperty::New(1.0, 0.0, 0.0), renderer, overwrite); node->AddProperty("binaryimage.hoveringannotationcolor", ColorProperty::New(1.0, 0.0, 0.0), renderer, overwrite); node->AddProperty("binary", mitk::BoolProperty::New(true), renderer, overwrite); node->AddProperty("layer", mitk::IntProperty::New(10), renderer, overwrite); } else //...or image type object { node->AddProperty("opacity", mitk::FloatProperty::New(1.0f), renderer, overwrite); node->AddProperty("color", ColorProperty::New(1.0, 1.0, 1.0), renderer, overwrite); node->AddProperty("binary", mitk::BoolProperty::New(false), renderer, overwrite); node->AddProperty("layer", mitk::IntProperty::New(0), renderer, overwrite); } if (image.IsNotNull() && image->IsInitialized()) { if ((overwrite) || (node->GetProperty("levelwindow", renderer) == nullptr)) { /* initialize level/window from DICOM tags */ std::string sLevel = ""; std::string sWindow = ""; if (GetBackwardsCompatibleDICOMProperty( 0x0028, 0x1050, "dicom.voilut.WindowCenter", image->GetPropertyList(), sLevel) && GetBackwardsCompatibleDICOMProperty( 0x0028, 0x1051, "dicom.voilut.WindowWidth", image->GetPropertyList(), sWindow)) { float level = atof(sLevel.c_str()); float window = atof(sWindow.c_str()); mitk::LevelWindow contrast; std::string sSmallestPixelValueInSeries; std::string sLargestPixelValueInSeries; if (GetBackwardsCompatibleDICOMProperty(0x0028, 0x0108, "dicom.series.SmallestPixelValueInSeries", image->GetPropertyList(), sSmallestPixelValueInSeries) && GetBackwardsCompatibleDICOMProperty(0x0028, 0x0109, "dicom.series.LargestPixelValueInSeries", image->GetPropertyList(), sLargestPixelValueInSeries)) { float smallestPixelValueInSeries = atof(sSmallestPixelValueInSeries.c_str()); float largestPixelValueInSeries = atof(sLargestPixelValueInSeries.c_str()); contrast.SetRangeMinMax(smallestPixelValueInSeries - 1, largestPixelValueInSeries + 1); // why not a little buffer? // might remedy some l/w widget challenges } else { contrast.SetAuto(static_cast(node->GetData()), false, true); // we need this as a fallback } contrast.SetLevelWindow(level, window, true); node->SetProperty("levelwindow", LevelWindowProperty::New(contrast), renderer); } } if (((overwrite) || (node->GetProperty("opaclevelwindow", renderer) == nullptr)) && (image->GetPixelType().GetPixelType() == itk::ImageIOBase::RGBA) && (image->GetPixelType().GetComponentType() == itk::ImageIOBase::UCHAR)) { mitk::LevelWindow opaclevwin; opaclevwin.SetRangeMinMax(0, 255); opaclevwin.SetWindowBounds(0, 255); mitk::LevelWindowProperty::Pointer prop = mitk::LevelWindowProperty::New(opaclevwin); node->SetProperty("opaclevelwindow", prop, renderer); } } Superclass::SetDefaultProperties(node, renderer, overwrite); } mitk::ImageVtkMapper2D::LocalStorage *mitk::ImageVtkMapper2D::GetLocalStorage(mitk::BaseRenderer *renderer) { return m_LSH.GetLocalStorage(renderer); } template vtkSmartPointer mitk::ImageVtkMapper2D::CreateOutlinePolyData(mitk::BaseRenderer *renderer) { LocalStorage *localStorage = this->GetLocalStorage(renderer); // get the min and max index values of each direction int *extent = localStorage->m_ReslicedImage->GetExtent(); int xMin = extent[0]; int xMax = extent[1]; int yMin = extent[2]; int yMax = extent[3]; int *dims = localStorage->m_ReslicedImage->GetDimensions(); // dimensions of the image int line = dims[0]; // how many pixels per line? int x = xMin; // pixel index x int y = yMin; // pixel index y // get the depth for each contour float depth = CalculateLayerDepth(renderer); vtkSmartPointer points = vtkSmartPointer::New(); // the points to draw vtkSmartPointer lines = vtkSmartPointer::New(); // the lines to connect the points // We take the pointer to the first pixel of the image - TPixel* currentPixel = static_cast(localStorage->m_ReslicedImage->GetScalarPointer()); + auto* currentPixel = static_cast(localStorage->m_ReslicedImage->GetScalarPointer()); while (y <= yMax) { // if the current pixel value is set to something if ((currentPixel) && (*currentPixel != 0)) { // check in which direction a line is necessary // a line is added if the neighbor of the current pixel has the value 0 // and if the pixel is located at the edge of the image // if vvvvv not the first line vvvvv if (y > yMin && *(currentPixel - line) == 0) { // x direction - bottom edge of the pixel // add the 2 points vtkIdType p1 = points->InsertNextPoint(x * localStorage->m_mmPerPixel[0], y * localStorage->m_mmPerPixel[1], depth); vtkIdType p2 = points->InsertNextPoint((x + 1) * localStorage->m_mmPerPixel[0], y * localStorage->m_mmPerPixel[1], depth); // add the line between both points lines->InsertNextCell(2); lines->InsertCellPoint(p1); lines->InsertCellPoint(p2); } // if vvvvv not the last line vvvvv if (y < yMax && *(currentPixel + line) == 0) { // x direction - top edge of the pixel vtkIdType p1 = points->InsertNextPoint(x * localStorage->m_mmPerPixel[0], (y + 1) * localStorage->m_mmPerPixel[1], depth); vtkIdType p2 = points->InsertNextPoint( (x + 1) * localStorage->m_mmPerPixel[0], (y + 1) * localStorage->m_mmPerPixel[1], depth); lines->InsertNextCell(2); lines->InsertCellPoint(p1); lines->InsertCellPoint(p2); } // if vvvvv not the first pixel vvvvv if ((x > xMin || y > yMin) && *(currentPixel - 1) == 0) { // y direction - left edge of the pixel vtkIdType p1 = points->InsertNextPoint(x * localStorage->m_mmPerPixel[0], y * localStorage->m_mmPerPixel[1], depth); vtkIdType p2 = points->InsertNextPoint(x * localStorage->m_mmPerPixel[0], (y + 1) * localStorage->m_mmPerPixel[1], depth); lines->InsertNextCell(2); lines->InsertCellPoint(p1); lines->InsertCellPoint(p2); } // if vvvvv not the last pixel vvvvv if ((y < yMax || (x < xMax)) && *(currentPixel + 1) == 0) { // y direction - right edge of the pixel vtkIdType p1 = points->InsertNextPoint((x + 1) * localStorage->m_mmPerPixel[0], y * localStorage->m_mmPerPixel[1], depth); vtkIdType p2 = points->InsertNextPoint( (x + 1) * localStorage->m_mmPerPixel[0], (y + 1) * localStorage->m_mmPerPixel[1], depth); lines->InsertNextCell(2); lines->InsertCellPoint(p1); lines->InsertCellPoint(p2); } /* now consider pixels at the edge of the image */ // if vvvvv left edge of image vvvvv if (x == xMin) { // draw left edge of the pixel vtkIdType p1 = points->InsertNextPoint(x * localStorage->m_mmPerPixel[0], y * localStorage->m_mmPerPixel[1], depth); vtkIdType p2 = points->InsertNextPoint(x * localStorage->m_mmPerPixel[0], (y + 1) * localStorage->m_mmPerPixel[1], depth); lines->InsertNextCell(2); lines->InsertCellPoint(p1); lines->InsertCellPoint(p2); } // if vvvvv right edge of image vvvvv if (x == xMax) { // draw right edge of the pixel vtkIdType p1 = points->InsertNextPoint((x + 1) * localStorage->m_mmPerPixel[0], y * localStorage->m_mmPerPixel[1], depth); vtkIdType p2 = points->InsertNextPoint( (x + 1) * localStorage->m_mmPerPixel[0], (y + 1) * localStorage->m_mmPerPixel[1], depth); lines->InsertNextCell(2); lines->InsertCellPoint(p1); lines->InsertCellPoint(p2); } // if vvvvv bottom edge of image vvvvv if (y == yMin) { // draw bottom edge of the pixel vtkIdType p1 = points->InsertNextPoint(x * localStorage->m_mmPerPixel[0], y * localStorage->m_mmPerPixel[1], depth); vtkIdType p2 = points->InsertNextPoint((x + 1) * localStorage->m_mmPerPixel[0], y * localStorage->m_mmPerPixel[1], depth); lines->InsertNextCell(2); lines->InsertCellPoint(p1); lines->InsertCellPoint(p2); } // if vvvvv top edge of image vvvvv if (y == yMax) { // draw top edge of the pixel vtkIdType p1 = points->InsertNextPoint(x * localStorage->m_mmPerPixel[0], (y + 1) * localStorage->m_mmPerPixel[1], depth); vtkIdType p2 = points->InsertNextPoint( (x + 1) * localStorage->m_mmPerPixel[0], (y + 1) * localStorage->m_mmPerPixel[1], depth); lines->InsertNextCell(2); lines->InsertCellPoint(p1); lines->InsertCellPoint(p2); } } // end if currentpixel is set x++; if (x > xMax) { // reached end of line x = xMin; y++; } // Increase the pointer-position to the next pixel. // This is safe, as the while-loop and the x-reset logic above makes // sure we do not exceed the bounds of the image currentPixel++; } // end of while // Create a polydata to store everything in vtkSmartPointer polyData = vtkSmartPointer::New(); // Add the points to the dataset polyData->SetPoints(points); // Add the lines to the dataset polyData->SetLines(lines); return polyData; } void mitk::ImageVtkMapper2D::TransformActor(mitk::BaseRenderer *renderer) { LocalStorage *localStorage = m_LSH.GetLocalStorage(renderer); // get the transformation matrix of the reslicer in order to render the slice as axial, coronal or saggital vtkSmartPointer trans = vtkSmartPointer::New(); vtkSmartPointer matrix = localStorage->m_Reslicer->GetResliceAxes(); trans->SetMatrix(matrix); // transform the plane/contour (the actual actor) to the corresponding view (axial, coronal or saggital) localStorage->m_Actor->SetUserTransform(trans); // transform the origin to center based coordinates, because MITK is center based. localStorage->m_Actor->SetPosition(-0.5 * localStorage->m_mmPerPixel[0], -0.5 * localStorage->m_mmPerPixel[1], 0.0); if (localStorage->m_Actors->GetNumberOfPaths() > 1) { - vtkActor *secondaryActor = dynamic_cast(localStorage->m_Actors->GetParts()->GetItemAsObject(0)); + auto *secondaryActor = dynamic_cast(localStorage->m_Actors->GetParts()->GetItemAsObject(0)); secondaryActor->SetUserTransform(trans); secondaryActor->SetPosition(-0.5 * localStorage->m_mmPerPixel[0], -0.5 * localStorage->m_mmPerPixel[1], 0.0); } } bool mitk::ImageVtkMapper2D::RenderingGeometryIntersectsImage(const PlaneGeometry *renderingGeometry, SlicedGeometry3D *imageGeometry) { // if either one of the two geometries is nullptr we return true // for safety reasons if (renderingGeometry == nullptr || imageGeometry == nullptr) return true; // get the distance for the first cornerpoint ScalarType initialDistance = renderingGeometry->SignedDistance(imageGeometry->GetCornerPoint(0)); for (int i = 1; i < 8; i++) { mitk::Point3D cornerPoint = imageGeometry->GetCornerPoint(i); // get the distance to the other cornerpoints ScalarType distance = renderingGeometry->SignedDistance(cornerPoint); // if it has not the same signing as the distance of the first point if (initialDistance * distance < 0) { // we have an intersection and return true return true; } } // all distances have the same sign, no intersection and we return false return false; } mitk::ImageVtkMapper2D::LocalStorage::~LocalStorage() { } mitk::ImageVtkMapper2D::LocalStorage::LocalStorage() : m_VectorComponentExtractor(vtkSmartPointer::New()) { m_LevelWindowFilter = vtkSmartPointer::New(); // Do as much actions as possible in here to avoid double executions. m_Plane = vtkSmartPointer::New(); m_Texture = vtkSmartPointer::New().GetPointer(); m_DefaultLookupTable = vtkSmartPointer::New(); m_BinaryLookupTable = vtkSmartPointer::New(); m_ColorLookupTable = vtkSmartPointer::New(); m_Mapper = vtkSmartPointer::New(); m_Actor = vtkSmartPointer::New(); m_Actors = vtkSmartPointer::New(); m_Reslicer = mitk::ExtractSliceFilter::New(); m_TSFilter = vtkSmartPointer::New(); m_OutlinePolyData = vtkSmartPointer::New(); m_ReslicedImage = vtkSmartPointer::New(); m_EmptyPolyData = vtkSmartPointer::New(); // the following actions are always the same and thus can be performed // in the constructor for each image (i.e. the image-corresponding local storage) m_TSFilter->ReleaseDataFlagOn(); mitk::LookupTable::Pointer mitkLUT = mitk::LookupTable::New(); // built a default lookuptable mitkLUT->SetType(mitk::LookupTable::GRAYSCALE); m_DefaultLookupTable = mitkLUT->GetVtkLookupTable(); mitkLUT->SetType(mitk::LookupTable::LEGACY_BINARY); m_BinaryLookupTable = mitkLUT->GetVtkLookupTable(); mitkLUT->SetType(mitk::LookupTable::LEGACY_RAINBOW_COLOR); m_ColorLookupTable = mitkLUT->GetVtkLookupTable(); // do not repeat the texture (the image) m_Texture->RepeatOff(); // set the mapper for the actor m_Actor->SetMapper(m_Mapper); vtkSmartPointer outlineShadowActor = vtkSmartPointer::New(); outlineShadowActor->SetMapper(m_Mapper); m_Actors->AddPart(outlineShadowActor); m_Actors->AddPart(m_Actor); } diff --git a/Modules/Core/src/Rendering/mitkMapper.cpp b/Modules/Core/src/Rendering/mitkMapper.cpp index 9fd50203db..a917acf960 100644 --- a/Modules/Core/src/Rendering/mitkMapper.cpp +++ b/Modules/Core/src/Rendering/mitkMapper.cpp @@ -1,152 +1,152 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkMapper.h" #include "mitkBaseRenderer.h" #include "mitkDataNode.h" #include "mitkProperties.h" mitk::Mapper::Mapper() : m_DataNode(nullptr), m_TimeStep(0) { } mitk::Mapper::~Mapper() { } mitk::BaseData *mitk::Mapper::GetData() const { return m_DataNode == nullptr ? nullptr : m_DataNode->GetData(); } mitk::DataNode *mitk::Mapper::GetDataNode() const { return this->m_DataNode; } bool mitk::Mapper::GetColor(float rgb[3], mitk::BaseRenderer *renderer, const char *name) const { const mitk::DataNode *node = GetDataNode(); if (node == nullptr) return false; return node->GetColor(rgb, renderer, name); } bool mitk::Mapper::GetVisibility(bool &visible, mitk::BaseRenderer *renderer, const char *name) const { const mitk::DataNode *node = GetDataNode(); if (node == nullptr) return false; return node->GetVisibility(visible, renderer, name); } bool mitk::Mapper::GetOpacity(float &opacity, mitk::BaseRenderer *renderer, const char *name) const { const mitk::DataNode *node = GetDataNode(); if (node == nullptr) return false; return node->GetOpacity(opacity, renderer, name); } bool mitk::Mapper::GetLevelWindow(mitk::LevelWindow &levelWindow, mitk::BaseRenderer *renderer, const char *name) const { const mitk::DataNode *node = GetDataNode(); if (node == nullptr) return false; return node->GetLevelWindow(levelWindow, renderer, name); } bool mitk::Mapper::IsVisible(mitk::BaseRenderer *renderer, const char *name) const { bool visible = true; GetDataNode()->GetVisibility(visible, renderer, name); return visible; } void mitk::Mapper::CalculateTimeStep(mitk::BaseRenderer *renderer) { if ((renderer != nullptr) && (m_DataNode != nullptr)) { m_TimeStep = renderer->GetTimeStep(m_DataNode->GetData()); } else { m_TimeStep = 0; } } void mitk::Mapper::Update(mitk::BaseRenderer *renderer) { const DataNode *node = GetDataNode(); assert(node != nullptr); - mitk::BaseData *data = static_cast(node->GetData()); + auto *data = static_cast(node->GetData()); if (!data) return; // Calculate time step of the input data for the specified renderer (integer value) this->CalculateTimeStep(renderer); // Check if time step is valid const TimeGeometry *dataTimeGeometry = data->GetTimeGeometry(); if ((dataTimeGeometry == nullptr) || (dataTimeGeometry->CountTimeSteps() == 0) || (!dataTimeGeometry->IsValidTimeStep(m_TimeStep))) { // TimeGeometry or time step is not valid for this data: // reset mapper so that nothing is displayed this->ResetMapper(renderer); return; } this->GenerateDataForRenderer(renderer); } bool mitk::Mapper::BaseLocalStorage::IsGenerateDataRequired(mitk::BaseRenderer *renderer, mitk::Mapper *mapper, mitk::DataNode *dataNode) const { if (mapper && m_LastGenerateDataTime < mapper->GetMTime()) return true; if (dataNode) { if (m_LastGenerateDataTime < dataNode->GetDataReferenceChangedTime()) return true; mitk::BaseData *data = dataNode->GetData(); if (data && m_LastGenerateDataTime < data->GetMTime()) return true; } if (renderer && m_LastGenerateDataTime < renderer->GetTimeStepUpdateTime()) return true; return false; } void mitk::Mapper::SetDefaultProperties(mitk::DataNode *node, mitk::BaseRenderer *renderer, bool overwrite) { node->AddProperty("visible", mitk::BoolProperty::New(true), renderer, overwrite); node->AddProperty("layer", mitk::IntProperty::New(0), renderer, overwrite); node->AddProperty("name", mitk::StringProperty::New("No Name!"), renderer, overwrite); } diff --git a/Modules/Core/src/Rendering/mitkPlaneGeometryDataMapper2D.cpp b/Modules/Core/src/Rendering/mitkPlaneGeometryDataMapper2D.cpp index 64b3135288..75149b8742 100644 --- a/Modules/Core/src/Rendering/mitkPlaneGeometryDataMapper2D.cpp +++ b/Modules/Core/src/Rendering/mitkPlaneGeometryDataMapper2D.cpp @@ -1,710 +1,710 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkPlaneGeometryDataMapper2D.h" // mitk includes #include "mitkVtkPropRenderer.h" #include #include #include #include #include #include #include #include #include // vtk includes #include #include #include #include #include #include #include #include #include #include /// #include #include #include #include namespace { /// Some simple interval arithmetic template class SimpleInterval { public: SimpleInterval(T start = T(), T end = T()) : m_LowerBoundary(std::min(start, end)), m_UpperBoundary(std::max(start, end)) { } T GetLowerBoundary() const { return m_LowerBoundary; } T GetUpperBoundary() const { return m_UpperBoundary; } bool empty() const { return m_LowerBoundary == m_UpperBoundary; } bool operator<(const SimpleInterval &otherInterval) const { return this->m_UpperBoundary < otherInterval.GetLowerBoundary(); } private: T m_LowerBoundary; T m_UpperBoundary; }; template class IntervalSet { public: typedef SimpleInterval IntervalType; IntervalSet(IntervalType startingInterval) { m_IntervalsContainer.insert(std::move(startingInterval)); } void operator-=(const IntervalType &interval) { // equal_range will find all the intervals in the interval set which intersect with the input interval // due to the nature of operator< of SimpleInterval auto range = m_IntervalsContainer.equal_range(interval); for (auto iter = range.first; iter != range.second;) { auto subtractionResult = SubtractIntervals(*iter, interval); // Remove the old interval from the set iter = m_IntervalsContainer.erase(iter); for (auto &&interval : subtractionResult) { if (!interval.empty()) { // Add the new interval to the set // emplace_hint adds the element at the closest valid place before the hint iterator, // which is exactly where the new interval should be iter = m_IntervalsContainer.insert(iter, std::move(interval)); ++iter; } } } } IntervalSet operator-(const IntervalType &interval) { IntervalSet result = *this; result -= interval; return result; } typedef std::set IntervalsContainer; const IntervalsContainer &getIntervals() const { return m_IntervalsContainer; } private: IntervalsContainer m_IntervalsContainer; std::array SubtractIntervals(const IntervalType &firstInterval, const IntervalType &secondInterval) { assert(secondInterval.GetUpperBoundary() >= firstInterval.GetLowerBoundary() && firstInterval.GetUpperBoundary() >= secondInterval.GetLowerBoundary()); // Non-intersecting intervals should never reach here if (secondInterval.GetLowerBoundary() < firstInterval.GetLowerBoundary()) { if (firstInterval.GetUpperBoundary() < secondInterval.GetUpperBoundary()) { std::array result = {{IntervalType(), IntervalType()}}; return result; // firstInterval completely enclosed } std::array result = { {IntervalType(firstInterval.GetUpperBoundary(), secondInterval.GetUpperBoundary()), IntervalType()}}; return result; // secondInterval removes the beginning of firstInterval } if (firstInterval.GetUpperBoundary() < secondInterval.GetUpperBoundary()) { std::array result = { {IntervalType(firstInterval.GetLowerBoundary(), secondInterval.GetLowerBoundary()), IntervalType()}}; return result; // secondInterval removes the end of firstInterval } std::array result = { {IntervalType(firstInterval.GetLowerBoundary(), secondInterval.GetLowerBoundary()), IntervalType(secondInterval.GetUpperBoundary(), firstInterval.GetUpperBoundary())}}; return result; // secondInterval is completely enclosed in firstInterval and removes the middle } }; } mitk::PlaneGeometryDataMapper2D::AllInstancesContainer mitk::PlaneGeometryDataMapper2D::s_AllInstances; // input for this mapper ( = PlaneGeometryData) const mitk::PlaneGeometryData *mitk::PlaneGeometryDataMapper2D::GetInput() const { return static_cast(GetDataNode()->GetData()); } mitk::PlaneGeometryDataMapper2D::PlaneGeometryDataMapper2D() : m_RenderOrientationArrows(false), m_ArrowOrientationPositive(true), m_DepthValue(1.0f) { s_AllInstances.insert(this); } mitk::PlaneGeometryDataMapper2D::~PlaneGeometryDataMapper2D() { s_AllInstances.erase(this); } vtkProp *mitk::PlaneGeometryDataMapper2D::GetVtkProp(mitk::BaseRenderer *renderer) { LocalStorage *ls = m_LSH.GetLocalStorage(renderer); return ls->m_CrosshairAssembly; } void mitk::PlaneGeometryDataMapper2D::GenerateDataForRenderer(mitk::BaseRenderer *renderer) { BaseLocalStorage *ls = m_LSH.GetLocalStorage(renderer); // The PlaneGeometryDataMapper2D mapper is special in that the rendering of // OTHER PlaneGeometryDatas affects how we render THIS PlaneGeometryData // (for the gap at the point where they intersect). A change in any of the // other PlaneGeometryData nodes could mean that we render ourself // differently, so we check for that here. - for (AllInstancesContainer::iterator it = s_AllInstances.begin(); it != s_AllInstances.end(); ++it) + for (auto it = s_AllInstances.begin(); it != s_AllInstances.end(); ++it) { bool generateDataRequired = ls->IsGenerateDataRequired(renderer, this, (*it)->GetDataNode()); if (generateDataRequired) break; } ls->UpdateGenerateDataTime(); // Collect all other PlaneGeometryDatas that are being mapped by this mapper m_OtherPlaneGeometries.clear(); - for (AllInstancesContainer::iterator it = s_AllInstances.begin(); it != s_AllInstances.end(); ++it) + for (auto it = s_AllInstances.begin(); it != s_AllInstances.end(); ++it) { Self *otherInstance = *it; // Skip ourself if (otherInstance == this) continue; mitk::DataNode *otherNode = otherInstance->GetDataNode(); if (!otherNode) continue; // Skip other PlaneGeometryData nodes that are not visible on this renderer if (!otherNode->IsVisible(renderer)) continue; - PlaneGeometryData *otherData = dynamic_cast(otherNode->GetData()); + auto *otherData = dynamic_cast(otherNode->GetData()); if (!otherData) continue; - PlaneGeometry *otherGeometry = dynamic_cast(otherData->GetPlaneGeometry()); + auto *otherGeometry = dynamic_cast(otherData->GetPlaneGeometry()); if (otherGeometry && !dynamic_cast(otherData->GetPlaneGeometry())) { m_OtherPlaneGeometries.push_back(otherNode); } } CreateVtkCrosshair(renderer); ApplyAllProperties(renderer); } void mitk::PlaneGeometryDataMapper2D::CreateVtkCrosshair(mitk::BaseRenderer *renderer) { bool visible = true; LocalStorage *ls = m_LSH.GetLocalStorage(renderer); ls->m_CrosshairActor->SetVisibility(0); ls->m_ArrowActor->SetVisibility(0); ls->m_CrosshairHelperLineActor->SetVisibility(0); GetDataNode()->GetVisibility(visible, renderer, "visible"); if (!visible) { return; } PlaneGeometryData::Pointer input = const_cast(this->GetInput()); mitk::DataNode *geometryDataNode = renderer->GetCurrentWorldPlaneGeometryNode(); const PlaneGeometryData *rendererWorldPlaneGeometryData = dynamic_cast(geometryDataNode->GetData()); // intersecting with ourself? if (input.IsNull() || input.GetPointer() == rendererWorldPlaneGeometryData) { return; // nothing to do in this case } - const PlaneGeometry *inputPlaneGeometry = dynamic_cast(input->GetPlaneGeometry()); + const auto *inputPlaneGeometry = dynamic_cast(input->GetPlaneGeometry()); - const PlaneGeometry *worldPlaneGeometry = + const auto *worldPlaneGeometry = dynamic_cast(rendererWorldPlaneGeometryData->GetPlaneGeometry()); if (worldPlaneGeometry && dynamic_cast(worldPlaneGeometry) == nullptr && inputPlaneGeometry && dynamic_cast(input->GetPlaneGeometry()) == nullptr) { const BaseGeometry *referenceGeometry = inputPlaneGeometry->GetReferenceGeometry(); // calculate intersection of the plane data with the border of the // world geometry rectangle Point3D point1, point2; Line3D crossLine; // Calculate the intersection line of the input plane with the world plane if (worldPlaneGeometry->IntersectionLine(inputPlaneGeometry, crossLine)) { bool hasIntersection = referenceGeometry ? CutCrossLineWithReferenceGeometry(referenceGeometry, crossLine) : CutCrossLineWithPlaneGeometry(inputPlaneGeometry, crossLine); if (!hasIntersection) { return; } point1 = crossLine.GetPoint1(); point2 = crossLine.GetPoint2(); vtkSmartPointer lines = vtkSmartPointer::New(); vtkSmartPointer points = vtkSmartPointer::New(); vtkSmartPointer linesPolyData = vtkSmartPointer::New(); // Now iterate through all other lines displayed in this window and // calculate the positions of intersection with the line to be // rendered; these positions will be stored in lineParams to form a // gap afterwards. - NodesVectorType::iterator otherPlanesIt = m_OtherPlaneGeometries.begin(); - NodesVectorType::iterator otherPlanesEnd = m_OtherPlaneGeometries.end(); + auto otherPlanesIt = m_OtherPlaneGeometries.begin(); + auto otherPlanesEnd = m_OtherPlaneGeometries.end(); int gapSize = 32; this->GetDataNode()->GetPropertyValue("Crosshair.Gap Size", gapSize, nullptr); auto intervals = IntervalSet(SimpleInterval(0, 1)); ScalarType lineLength = point1.EuclideanDistanceTo(point2); ScalarType gapInMM = gapSize * renderer->GetScaleFactorMMPerDisplayUnit(); float gapSizeParam = gapInMM / lineLength; if (gapSize != 0) { while (otherPlanesIt != otherPlanesEnd) { bool ignorePlane = false; (*otherPlanesIt)->GetPropertyValue("Crosshair.Ignore", ignorePlane); if (ignorePlane) { ++otherPlanesIt; continue; } - PlaneGeometry *otherPlaneGeometry = static_cast( + auto *otherPlaneGeometry = static_cast( static_cast((*otherPlanesIt)->GetData())->GetPlaneGeometry()); if (otherPlaneGeometry != inputPlaneGeometry && otherPlaneGeometry != worldPlaneGeometry) { double intersectionParam; if (otherPlaneGeometry->IntersectionPointParam(crossLine, intersectionParam) && intersectionParam > 0 && intersectionParam < 1) { Point3D point = crossLine.GetPoint() + intersectionParam * crossLine.GetDirection(); bool intersectionPointInsideOtherPlane = otherPlaneGeometry->HasReferenceGeometry() ? TestPointInReferenceGeometry(otherPlaneGeometry->GetReferenceGeometry(), point) : TestPointInPlaneGeometry(otherPlaneGeometry, point); if (intersectionPointInsideOtherPlane) { intervals -= SimpleInterval(intersectionParam - gapSizeParam, intersectionParam + gapSizeParam); } } } ++otherPlanesIt; } } for (const auto &interval : intervals.getIntervals()) { this->DrawLine(crossLine.GetPoint(interval.GetLowerBoundary()), crossLine.GetPoint(interval.GetUpperBoundary()), lines, points); } // Add the points to the dataset linesPolyData->SetPoints(points); // Add the lines to the dataset linesPolyData->SetLines(lines); Vector3D orthogonalVector; orthogonalVector = inputPlaneGeometry->GetNormal(); worldPlaneGeometry->Project(orthogonalVector, orthogonalVector); orthogonalVector.Normalize(); // Visualize ls->m_Mapper->SetInputData(linesPolyData); ls->m_CrosshairActor->SetMapper(ls->m_Mapper); // Determine if we should draw the area covered by the thick slicing, default is false. // This will also show the area of slices that do not have thick slice mode enabled bool showAreaOfThickSlicing = false; GetDataNode()->GetBoolProperty("reslice.thickslices.showarea", showAreaOfThickSlicing); // determine the pixelSpacing in that direction double thickSliceDistance = SlicedGeometry3D::CalculateSpacing( referenceGeometry ? referenceGeometry->GetSpacing() : inputPlaneGeometry->GetSpacing(), orthogonalVector); IntProperty *intProperty = nullptr; if (GetDataNode()->GetProperty(intProperty, "reslice.thickslices.num") && intProperty) thickSliceDistance *= intProperty->GetValue() + 0.5; else showAreaOfThickSlicing = false; // not the nicest place to do it, but we have the width of the visible bloc in MM here // so we store it in this fancy property GetDataNode()->SetFloatProperty("reslice.thickslices.sizeinmm", thickSliceDistance * 2); ls->m_CrosshairActor->SetVisibility(1); vtkSmartPointer arrowPolyData = vtkSmartPointer::New(); ls->m_Arrowmapper->SetInputData(arrowPolyData); if (this->m_RenderOrientationArrows) { ScalarType triangleSizeMM = 7.0 * renderer->GetScaleFactorMMPerDisplayUnit(); vtkSmartPointer triangles = vtkSmartPointer::New(); vtkSmartPointer triPoints = vtkSmartPointer::New(); DrawOrientationArrow(triangles, triPoints, triangleSizeMM, orthogonalVector, point1, point2); DrawOrientationArrow(triangles, triPoints, triangleSizeMM, orthogonalVector, point2, point1); arrowPolyData->SetPoints(triPoints); arrowPolyData->SetPolys(triangles); ls->m_ArrowActor->SetVisibility(1); } // Visualize vtkSmartPointer helperlinesPolyData = vtkSmartPointer::New(); ls->m_HelperLinesmapper->SetInputData(helperlinesPolyData); if (showAreaOfThickSlicing) { vtkSmartPointer helperlines = vtkSmartPointer::New(); // vectorToHelperLine defines how to reach the helperLine from the mainLine // got the right direction, so we multiply the width Vector3D vecToHelperLine = orthogonalVector * thickSliceDistance; this->DrawLine(point1 - vecToHelperLine, point2 - vecToHelperLine, helperlines, points); this->DrawLine(point1 + vecToHelperLine, point2 + vecToHelperLine, helperlines, points); // Add the points to the dataset helperlinesPolyData->SetPoints(points); // Add the lines to the dataset helperlinesPolyData->SetLines(helperlines); ls->m_CrosshairActor->GetProperty()->SetLineStipplePattern(0xf0f0); ls->m_CrosshairActor->GetProperty()->SetLineStippleRepeatFactor(1); ls->m_CrosshairHelperLineActor->SetVisibility(1); } } } } bool mitk::PlaneGeometryDataMapper2D::TestPointInPlaneGeometry(const PlaneGeometry *planeGeometry, const Point3D &point) { Point2D mappedPoint; planeGeometry->Map(point, mappedPoint); planeGeometry->WorldToIndex(mappedPoint, mappedPoint); return (planeGeometry->GetBounds()[0] < mappedPoint[0] && mappedPoint[0] < planeGeometry->GetBounds()[1] && planeGeometry->GetBounds()[2] < mappedPoint[1] && mappedPoint[1] < planeGeometry->GetBounds()[3]); } bool mitk::PlaneGeometryDataMapper2D::TestPointInReferenceGeometry(const BaseGeometry *referenceGeometry, const Point3D &point) { return referenceGeometry->IsInside(point); } bool mitk::PlaneGeometryDataMapper2D::CutCrossLineWithPlaneGeometry(const PlaneGeometry *planeGeometry, Line3D &crossLine) { Point2D indexLinePoint; Vector2D indexLineDirection; planeGeometry->Map(crossLine.GetPoint(), indexLinePoint); planeGeometry->Map(crossLine.GetPoint(), crossLine.GetDirection(), indexLineDirection); planeGeometry->WorldToIndex(indexLinePoint, indexLinePoint); planeGeometry->WorldToIndex(indexLineDirection, indexLineDirection); mitk::Point2D intersectionPoints[2]; // Then, clip this line with the (transformed) bounding box of the // reference geometry. int nIntersections = Line3D::RectangleLineIntersection(planeGeometry->GetBounds()[0], planeGeometry->GetBounds()[2], planeGeometry->GetBounds()[1], planeGeometry->GetBounds()[3], indexLinePoint, indexLineDirection, intersectionPoints[0], intersectionPoints[1]); if (nIntersections < 2) { return false; } planeGeometry->IndexToWorld(intersectionPoints[0], intersectionPoints[0]); planeGeometry->IndexToWorld(intersectionPoints[1], intersectionPoints[1]); Point3D point1, point2; planeGeometry->Map(intersectionPoints[0], point1); planeGeometry->Map(intersectionPoints[1], point2); crossLine.SetPoints(point1, point2); return true; } bool mitk::PlaneGeometryDataMapper2D::CutCrossLineWithReferenceGeometry(const BaseGeometry *referenceGeometry, Line3D &crossLine) { Point3D boundingBoxMin, boundingBoxMax; boundingBoxMin = referenceGeometry->GetCornerPoint(0); boundingBoxMax = referenceGeometry->GetCornerPoint(7); Point3D indexLinePoint; Vector3D indexLineDirection; referenceGeometry->WorldToIndex(crossLine.GetPoint(), indexLinePoint); referenceGeometry->WorldToIndex(crossLine.GetDirection(), indexLineDirection); referenceGeometry->WorldToIndex(boundingBoxMin, boundingBoxMin); referenceGeometry->WorldToIndex(boundingBoxMax, boundingBoxMax); Point3D point1, point2; // Then, clip this line with the (transformed) bounding box of the // reference geometry. int nIntersections = Line3D::BoxLineIntersection(boundingBoxMin[0], boundingBoxMin[1], boundingBoxMin[2], boundingBoxMax[0], boundingBoxMax[1], boundingBoxMax[2], indexLinePoint, indexLineDirection, point1, point2); if (nIntersections < 2) { return false; } referenceGeometry->IndexToWorld(point1, point1); referenceGeometry->IndexToWorld(point2, point2); crossLine.SetPoints(point1, point2); return true; } void mitk::PlaneGeometryDataMapper2D::DrawLine(mitk::Point3D p0, mitk::Point3D p1, vtkCellArray *lines, vtkPoints *points) { vtkIdType pidStart = points->InsertNextPoint(p0[0], p0[1], p0[2]); vtkIdType pidEnd = points->InsertNextPoint(p1[0], p1[1], p1[2]); vtkSmartPointer lineVtk = vtkSmartPointer::New(); lineVtk->GetPointIds()->SetId(0, pidStart); lineVtk->GetPointIds()->SetId(1, pidEnd); lines->InsertNextCell(lineVtk); } void mitk::PlaneGeometryDataMapper2D::DrawOrientationArrow(vtkSmartPointer triangles, vtkSmartPointer triPoints, double triangleSizeMM, Vector3D &orthogonalVector, Point3D &point1, Point3D &point2) { // Draw arrows to indicate plane orientation // Vector along line Vector3D v1 = point2 - point1; v1.Normalize(); v1 *= triangleSizeMM; // Orthogonal vector Vector3D v2 = orthogonalVector; v2 *= triangleSizeMM; if (!this->m_ArrowOrientationPositive) v2 *= -1.0; // Initialize remaining triangle coordinates accordingly Point3D p1 = point1 + v1 * 2.0; Point3D p2 = point1 + v1 + v2; vtkIdType t0 = triPoints->InsertNextPoint(point1[0], point1[1], point1[2]); // start of the line vtkIdType t1 = triPoints->InsertNextPoint(p1[0], p1[1], p1[2]); // point on line vtkIdType t2 = triPoints->InsertNextPoint(p2[0], p2[1], p2[2]); // direction point vtkSmartPointer triangle = vtkSmartPointer::New(); triangle->GetPointIds()->SetId(0, t0); triangle->GetPointIds()->SetId(1, t1); triangle->GetPointIds()->SetId(2, t2); triangles->InsertNextCell(triangle); } int mitk::PlaneGeometryDataMapper2D::DetermineThickSliceMode(DataNode *dn, int &thickSlicesNum) { int thickSlicesMode = 0; // determine the state and the extend of the thick-slice mode mitk::ResliceMethodProperty *resliceMethodEnumProperty = nullptr; if (dn->GetProperty(resliceMethodEnumProperty, "reslice.thickslices") && resliceMethodEnumProperty) thickSlicesMode = resliceMethodEnumProperty->GetValueAsId(); IntProperty *intProperty = nullptr; if (dn->GetProperty(intProperty, "reslice.thickslices.num") && intProperty) { thickSlicesNum = intProperty->GetValue(); if (thickSlicesNum < 1) thickSlicesNum = 0; if (thickSlicesNum > 10) thickSlicesNum = 10; } if (thickSlicesMode == 0) thickSlicesNum = 0; return thickSlicesMode; } void mitk::PlaneGeometryDataMapper2D::ApplyAllProperties(BaseRenderer *renderer) { LocalStorage *ls = m_LSH.GetLocalStorage(renderer); ApplyColorAndOpacityProperties2D(renderer, ls->m_CrosshairActor); ApplyColorAndOpacityProperties2D(renderer, ls->m_CrosshairHelperLineActor); ApplyColorAndOpacityProperties2D(renderer, ls->m_ArrowActor); float thickness; this->GetDataNode()->GetFloatProperty("Line width", thickness, renderer); ls->m_CrosshairActor->GetProperty()->SetLineWidth(thickness); ls->m_CrosshairHelperLineActor->GetProperty()->SetLineWidth(thickness); PlaneOrientationProperty *decorationProperty; this->GetDataNode()->GetProperty(decorationProperty, "decoration", renderer); if (decorationProperty != nullptr) { if (decorationProperty->GetPlaneDecoration() == PlaneOrientationProperty::PLANE_DECORATION_POSITIVE_ORIENTATION) { m_RenderOrientationArrows = true; m_ArrowOrientationPositive = true; } else if (decorationProperty->GetPlaneDecoration() == PlaneOrientationProperty::PLANE_DECORATION_NEGATIVE_ORIENTATION) { m_RenderOrientationArrows = true; m_ArrowOrientationPositive = false; } else { m_RenderOrientationArrows = false; } } } void mitk::PlaneGeometryDataMapper2D::ApplyColorAndOpacityProperties2D(BaseRenderer *renderer, vtkActor2D *actor) { float rgba[4] = {1.0f, 1.0f, 1.0f, 1.0f}; DataNode *node = GetDataNode(); // check for color prop and use it for rendering if it exists node->GetColor(rgba, renderer, "color"); // check for opacity prop and use it for rendering if it exists node->GetOpacity(rgba[3], renderer, "opacity"); double drgba[4] = {rgba[0], rgba[1], rgba[2], rgba[3]}; actor->GetProperty()->SetColor(drgba); actor->GetProperty()->SetOpacity(drgba[3]); } void mitk::PlaneGeometryDataMapper2D::SetDefaultProperties(mitk::DataNode *node, mitk::BaseRenderer *renderer, bool overwrite) { mitk::IPropertyAliases *aliases = mitk::CoreServices::GetPropertyAliases(); node->AddProperty("Line width", mitk::FloatProperty::New(1), renderer, overwrite); aliases->AddAlias("line width", "Crosshair.Line Width", ""); node->AddProperty("Crosshair.Gap Size", mitk::IntProperty::New(32), renderer, overwrite); node->AddProperty("decoration", mitk::PlaneOrientationProperty::New(PlaneOrientationProperty::PLANE_DECORATION_NONE), renderer, overwrite); aliases->AddAlias("decoration", "Crosshair.Orientation Decoration", ""); Superclass::SetDefaultProperties(node, renderer, overwrite); } void mitk::PlaneGeometryDataMapper2D::UpdateVtkTransform(mitk::BaseRenderer * /*renderer*/) { } mitk::PlaneGeometryDataMapper2D::LocalStorage::LocalStorage() { m_CrosshairAssembly = vtkSmartPointer::New(); m_CrosshairActor = vtkSmartPointer::New(); m_ArrowActor = vtkSmartPointer::New(); m_CrosshairHelperLineActor = vtkSmartPointer::New(); m_HelperLinesmapper = vtkSmartPointer::New(); m_Mapper = vtkSmartPointer::New(); m_Arrowmapper = vtkSmartPointer::New(); m_CrosshairActor->SetMapper(m_Mapper); m_ArrowActor->SetMapper(m_Arrowmapper); m_CrosshairHelperLineActor->SetMapper(m_HelperLinesmapper); m_CrosshairActor->SetVisibility(0); m_ArrowActor->SetVisibility(0); m_CrosshairHelperLineActor->SetVisibility(0); m_CrosshairAssembly->AddPart(m_CrosshairActor); m_CrosshairAssembly->AddPart(m_ArrowActor); m_CrosshairAssembly->AddPart(m_CrosshairHelperLineActor); vtkCoordinate *tcoord = vtkCoordinate::New(); tcoord->SetCoordinateSystemToWorld(); m_HelperLinesmapper->SetTransformCoordinate(tcoord); m_Mapper->SetTransformCoordinate(tcoord); // tcoord->SetCoordinateSystemToNormalizedDisplay(); m_Arrowmapper->SetTransformCoordinate(tcoord); tcoord->Delete(); } mitk::PlaneGeometryDataMapper2D::LocalStorage::~LocalStorage() { } diff --git a/Modules/Core/src/Rendering/mitkPlaneGeometryDataVtkMapper3D.cpp b/Modules/Core/src/Rendering/mitkPlaneGeometryDataVtkMapper3D.cpp index 79f1d68759..d3c4e18a87 100644 --- a/Modules/Core/src/Rendering/mitkPlaneGeometryDataVtkMapper3D.cpp +++ b/Modules/Core/src/Rendering/mitkPlaneGeometryDataVtkMapper3D.cpp @@ -1,589 +1,589 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkPlaneGeometryDataVtkMapper3D.h" #include "mitkImageVtkMapper2D.h" #include "mitkNodePredicateDataType.h" #include "mitkNodePredicateOr.h" #include "mitkSmartPointerProperty.h" #include "mitkSurface.h" #include "mitkVtkRepresentationProperty.h" #include "mitkWeakPointerProperty.h" #include "vtkMitkLevelWindowFilter.h" #include "vtkNeverTranslucentTexture.h" #include #include #include #include #include #include #include #include #include #include #include #include namespace mitk { PlaneGeometryDataVtkMapper3D::PlaneGeometryDataVtkMapper3D() : m_NormalsActorAdded(false), m_DataStorage(nullptr) { m_EdgeTuber = vtkTubeFilter::New(); m_EdgeMapper = vtkPolyDataMapper::New(); m_SurfaceCreator = PlaneGeometryDataToSurfaceFilter::New(); m_SurfaceCreatorBoundingBox = BoundingBox::New(); m_SurfaceCreatorPointsContainer = BoundingBox::PointsContainer::New(); m_Edges = vtkFeatureEdges::New(); m_Edges->BoundaryEdgesOn(); m_Edges->FeatureEdgesOff(); m_Edges->NonManifoldEdgesOff(); m_Edges->ManifoldEdgesOff(); m_EdgeTransformer = vtkTransformPolyDataFilter::New(); m_NormalsTransformer = vtkTransformPolyDataFilter::New(); m_EdgeActor = vtkActor::New(); m_BackgroundMapper = vtkPolyDataMapper::New(); m_BackgroundActor = vtkActor::New(); m_Prop3DAssembly = vtkAssembly::New(); m_ImageAssembly = vtkAssembly::New(); m_SurfaceCreatorBoundingBox->SetPoints(m_SurfaceCreatorPointsContainer); m_Cleaner = vtkCleanPolyData::New(); m_Cleaner->PieceInvariantOn(); m_Cleaner->ConvertLinesToPointsOn(); m_Cleaner->ConvertPolysToLinesOn(); m_Cleaner->ConvertStripsToPolysOn(); m_Cleaner->PointMergingOn(); // Make sure that the FeatureEdge algorithm is initialized with a "valid" // (though empty) input vtkPolyData *emptyPolyData = vtkPolyData::New(); m_Cleaner->SetInputData(emptyPolyData); emptyPolyData->Delete(); m_Edges->SetInputConnection(m_Cleaner->GetOutputPort()); m_EdgeTransformer->SetInputConnection(m_Edges->GetOutputPort()); m_EdgeTuber->SetInputConnection(m_EdgeTransformer->GetOutputPort()); m_EdgeTuber->SetVaryRadiusToVaryRadiusOff(); m_EdgeTuber->SetNumberOfSides(12); m_EdgeTuber->CappingOn(); m_EdgeMapper->SetInputConnection(m_EdgeTuber->GetOutputPort()); m_EdgeMapper->ScalarVisibilityOff(); m_BackgroundMapper->SetInputData(emptyPolyData); m_BackgroundMapper->Update(); m_EdgeActor->SetMapper(m_EdgeMapper); m_BackgroundActor->GetProperty()->SetAmbient(0.5); m_BackgroundActor->GetProperty()->SetColor(0.0, 0.0, 0.0); m_BackgroundActor->GetProperty()->SetOpacity(0.0); m_BackgroundActor->SetMapper(m_BackgroundMapper); vtkProperty *backfaceProperty = m_BackgroundActor->MakeProperty(); backfaceProperty->SetColor(0.0, 0.0, 0.0); m_BackgroundActor->SetBackfaceProperty(backfaceProperty); backfaceProperty->Delete(); m_FrontHedgeHog = vtkHedgeHog::New(); m_BackHedgeHog = vtkHedgeHog::New(); m_FrontNormalsMapper = vtkPolyDataMapper::New(); m_FrontNormalsMapper->SetInputConnection(m_FrontHedgeHog->GetOutputPort()); m_BackNormalsMapper = vtkPolyDataMapper::New(); m_Prop3DAssembly->AddPart(m_EdgeActor); m_Prop3DAssembly->AddPart(m_ImageAssembly); m_FrontNormalsActor = vtkActor::New(); m_FrontNormalsActor->SetMapper(m_FrontNormalsMapper); m_BackNormalsActor = vtkActor::New(); m_BackNormalsActor->SetMapper(m_BackNormalsMapper); m_ImageMapperDeletedCommand = MemberCommandType::New(); m_ImageMapperDeletedCommand->SetCallbackFunction(this, &PlaneGeometryDataVtkMapper3D::ImageMapperDeletedCallback); } PlaneGeometryDataVtkMapper3D::~PlaneGeometryDataVtkMapper3D() { m_ImageAssembly->Delete(); m_Prop3DAssembly->Delete(); m_EdgeTuber->Delete(); m_EdgeMapper->Delete(); m_EdgeTransformer->Delete(); m_Cleaner->Delete(); m_Edges->Delete(); m_NormalsTransformer->Delete(); m_EdgeActor->Delete(); m_BackgroundMapper->Delete(); m_BackgroundActor->Delete(); m_FrontNormalsMapper->Delete(); m_FrontNormalsActor->Delete(); m_FrontHedgeHog->Delete(); m_BackNormalsMapper->Delete(); m_BackNormalsActor->Delete(); m_BackHedgeHog->Delete(); - for (ActorList::iterator it = m_ImageActors.begin(); it != m_ImageActors.end(); ++it) + for (auto it = m_ImageActors.begin(); it != m_ImageActors.end(); ++it) it->second.m_Actor->ReleaseGraphicsResources(nullptr); // Delete entries in m_ImageActors list one by one m_ImageActors.clear(); m_DataStorage = nullptr; } vtkProp *PlaneGeometryDataVtkMapper3D::GetVtkProp(mitk::BaseRenderer * /*renderer*/) { if ((this->GetDataNode() != nullptr) && (m_ImageAssembly != nullptr)) { // Do not transform the entire Prop3D assembly, but only the image part // here. The colored frame is transformed elsewhere (via m_EdgeTransformer), // since only vertices should be transformed there, not the poly data // itself, to avoid distortion for anisotropic datasets. m_ImageAssembly->SetUserTransform(this->GetDataNode()->GetVtkTransform()); } return m_Prop3DAssembly; } void PlaneGeometryDataVtkMapper3D::UpdateVtkTransform(mitk::BaseRenderer * /*renderer*/) { m_ImageAssembly->SetUserTransform(this->GetDataNode()->GetVtkTransform(this->GetTimestep())); } const PlaneGeometryData *PlaneGeometryDataVtkMapper3D::GetInput() { return static_cast(GetDataNode()->GetData()); } void PlaneGeometryDataVtkMapper3D::SetDataStorageForTexture(mitk::DataStorage *storage) { if (storage != nullptr && m_DataStorage != storage) { m_DataStorage = storage; this->Modified(); } } void PlaneGeometryDataVtkMapper3D::ImageMapperDeletedCallback(itk::Object *caller, const itk::EventObject & /*event*/) { - ImageVtkMapper2D *imageMapper = dynamic_cast(caller); + auto *imageMapper = dynamic_cast(caller); if ((imageMapper != nullptr)) { if (m_ImageActors.count(imageMapper) > 0) { m_ImageActors[imageMapper].m_Sender = nullptr; // sender is already destroying itself m_ImageActors.erase(imageMapper); } } } void PlaneGeometryDataVtkMapper3D::GenerateDataForRenderer(BaseRenderer *renderer) { SetVtkMapperImmediateModeRendering(m_EdgeMapper); SetVtkMapperImmediateModeRendering(m_BackgroundMapper); // Remove all actors from the assembly, and re-initialize it with the // edge actor m_ImageAssembly->GetParts()->RemoveAllItems(); bool visible = true; GetDataNode()->GetVisibility(visible, renderer, "visible"); if (!visible) { // visibility has explicitly to be set in the single actors // due to problems when using cell picking: // even if the assembly is invisible, the renderer contains // references to the assemblies parts. During picking the // visibility of each part is checked, and not only for the // whole assembly. m_ImageAssembly->VisibilityOff(); m_EdgeActor->VisibilityOff(); return; } // visibility has explicitly to be set in the single actors // due to problems when using cell picking: // even if the assembly is invisible, the renderer contains // references to the assemblies parts. During picking the // visibility of each part is checked, and not only for the // whole assembly. m_ImageAssembly->VisibilityOn(); bool drawEdges = true; this->GetDataNode()->GetBoolProperty("draw edges", drawEdges, renderer); m_EdgeActor->SetVisibility(drawEdges); PlaneGeometryData::Pointer input = const_cast(this->GetInput()); if (input.IsNotNull() && (input->GetPlaneGeometry() != nullptr)) { SmartPointerProperty::Pointer surfacecreatorprop; surfacecreatorprop = dynamic_cast(GetDataNode()->GetProperty("surfacegeometry", renderer)); if ((surfacecreatorprop.IsNull()) || (surfacecreatorprop->GetSmartPointer().IsNull()) || ((m_SurfaceCreator = dynamic_cast(surfacecreatorprop->GetSmartPointer().GetPointer())) .IsNull())) { m_SurfaceCreator->PlaceByGeometryOn(); surfacecreatorprop = SmartPointerProperty::New(m_SurfaceCreator); GetDataNode()->SetProperty("surfacegeometry", surfacecreatorprop); } m_SurfaceCreator->SetInput(input); int res; if (GetDataNode()->GetIntProperty("xresolution", res, renderer)) { m_SurfaceCreator->SetXResolution(res); } if (GetDataNode()->GetIntProperty("yresolution", res, renderer)) { m_SurfaceCreator->SetYResolution(res); } double tubeRadius = 1.0; // Radius of tubular edge surrounding plane // Clip the PlaneGeometry with the reference geometry bounds (if available) if (input->GetPlaneGeometry()->HasReferenceGeometry()) { const BaseGeometry *referenceGeometry = input->GetPlaneGeometry()->GetReferenceGeometry(); BoundingBox::PointType boundingBoxMin, boundingBoxMax; boundingBoxMin = referenceGeometry->GetBoundingBox()->GetMinimum(); boundingBoxMax = referenceGeometry->GetBoundingBox()->GetMaximum(); if (referenceGeometry->GetImageGeometry()) { for (unsigned int i = 0; i < 3; ++i) { boundingBoxMin[i] -= 0.5; boundingBoxMax[i] -= 0.5; } } m_SurfaceCreatorPointsContainer->CreateElementAt(0) = boundingBoxMin; m_SurfaceCreatorPointsContainer->CreateElementAt(1) = boundingBoxMax; m_SurfaceCreatorBoundingBox->ComputeBoundingBox(); m_SurfaceCreator->SetBoundingBox(m_SurfaceCreatorBoundingBox); tubeRadius = referenceGeometry->GetDiagonalLength() / 450.0; } // If no reference geometry is available, clip with the current global // bounds else if (m_DataStorage.IsNotNull()) { m_SurfaceCreator->SetBoundingBox(m_DataStorage->ComputeVisibleBoundingBox(nullptr, "includeInBoundingBox")); tubeRadius = sqrt(m_SurfaceCreator->GetBoundingBox()->GetDiagonalLength2()) / 450.0; } // Calculate the surface of the PlaneGeometry m_SurfaceCreator->Update(); Surface *surface = m_SurfaceCreator->GetOutput(); // Check if there's something to display, otherwise return if ((surface->GetVtkPolyData() == nullptr) || (surface->GetVtkPolyData()->GetNumberOfCells() == 0)) { m_ImageAssembly->VisibilityOff(); return; } // add a graphical representation of the surface normals if requested DataNode *node = this->GetDataNode(); bool displayNormals = false; bool colorTwoSides = false; bool invertNormals = false; node->GetBoolProperty("draw normals 3D", displayNormals, renderer); node->GetBoolProperty("color two sides", colorTwoSides, renderer); node->GetBoolProperty("invert normals", invertNormals, renderer); // if we want to draw the display normals or render two sides we have to get the colors if (displayNormals || colorTwoSides) { // get colors float frontColor[3] = {0.0, 0.0, 1.0}; node->GetColor(frontColor, renderer, "front color"); float backColor[3] = {1.0, 0.0, 0.0}; node->GetColor(backColor, renderer, "back color"); if (displayNormals) { m_NormalsTransformer->SetInputData(surface->GetVtkPolyData()); m_NormalsTransformer->SetTransform(node->GetVtkTransform(this->GetTimestep())); m_FrontHedgeHog->SetInputConnection(m_NormalsTransformer->GetOutputPort()); m_FrontHedgeHog->SetVectorModeToUseNormal(); m_FrontHedgeHog->SetScaleFactor(invertNormals ? 1.0 : -1.0); m_FrontHedgeHog->Update(); m_FrontNormalsActor->GetProperty()->SetColor(frontColor[0], frontColor[1], frontColor[2]); m_BackHedgeHog->SetInputConnection(m_NormalsTransformer->GetOutputPort()); m_BackHedgeHog->SetVectorModeToUseNormal(); m_BackHedgeHog->SetScaleFactor(invertNormals ? -1.0 : 1.0); m_BackHedgeHog->Update(); m_BackNormalsActor->GetProperty()->SetColor(backColor[0], backColor[1], backColor[2]); // if there is no actor added yet, add one if (!m_NormalsActorAdded) { m_Prop3DAssembly->AddPart(m_FrontNormalsActor); m_Prop3DAssembly->AddPart(m_BackNormalsActor); m_NormalsActorAdded = true; } } // if we don't want to display normals AND there is an actor added remove the actor else if (m_NormalsActorAdded) { m_Prop3DAssembly->RemovePart(m_FrontNormalsActor); m_Prop3DAssembly->RemovePart(m_BackNormalsActor); m_NormalsActorAdded = false; } if (colorTwoSides) { if (!invertNormals) { m_BackgroundActor->GetProperty()->SetColor(backColor[0], backColor[1], backColor[2]); m_BackgroundActor->GetBackfaceProperty()->SetColor(frontColor[0], frontColor[1], frontColor[2]); } else { m_BackgroundActor->GetProperty()->SetColor(frontColor[0], frontColor[1], frontColor[2]); m_BackgroundActor->GetBackfaceProperty()->SetColor(backColor[0], backColor[1], backColor[2]); } } } // Add black background for all images (which may be transparent) m_BackgroundMapper->SetInputData(surface->GetVtkPolyData()); // m_ImageAssembly->AddPart(m_BackgroundActor); LayerSortedActorList layerSortedActors; // Traverse the data tree to find nodes resliced by ImageMapperGL2D // use a predicate to get all data nodes which are "images" or inherit from mitk::Image mitk::TNodePredicateDataType::Pointer predicateAllImages = mitk::TNodePredicateDataType::New(); mitk::DataStorage::SetOfObjects::ConstPointer all = m_DataStorage->GetSubset(predicateAllImages); // process all found images for (mitk::DataStorage::SetOfObjects::ConstIterator it = all->Begin(); it != all->End(); ++it) { DataNode *node = it->Value(); if (node != nullptr) this->ProcessNode(node, renderer, surface, layerSortedActors); } // Add all image actors to the assembly, sorted according to // layer property LayerSortedActorList::iterator actorIt; for (actorIt = layerSortedActors.begin(); actorIt != layerSortedActors.end(); ++actorIt) { m_ImageAssembly->AddPart(actorIt->second); } // Configurate the tube-shaped frame: size according to the surface // bounds, color as specified in the plane's properties vtkPolyData *surfacePolyData = surface->GetVtkPolyData(); m_Cleaner->SetInputData(surfacePolyData); m_EdgeTransformer->SetTransform(this->GetDataNode()->GetVtkTransform(this->GetTimestep())); // Adjust the radius according to extent m_EdgeTuber->SetRadius(tubeRadius); // Get the plane's color and set the tube properties accordingly ColorProperty::Pointer colorProperty; colorProperty = dynamic_cast(this->GetDataNode()->GetProperty("color")); if (colorProperty.IsNotNull()) { const Color &color = colorProperty->GetColor(); m_EdgeActor->GetProperty()->SetColor(color.GetRed(), color.GetGreen(), color.GetBlue()); } else { m_EdgeActor->GetProperty()->SetColor(1.0, 1.0, 1.0); } m_ImageAssembly->SetUserTransform(this->GetDataNode()->GetVtkTransform(this->GetTimestep())); } VtkRepresentationProperty *representationProperty; this->GetDataNode()->GetProperty(representationProperty, "material.representation", renderer); if (representationProperty != nullptr) m_BackgroundActor->GetProperty()->SetRepresentation(representationProperty->GetVtkRepresentation()); } void PlaneGeometryDataVtkMapper3D::ProcessNode(DataNode *node, BaseRenderer *renderer, Surface *surface, LayerSortedActorList &layerSortedActors) { if (node != nullptr) { // we need to get the information from the 2D mapper to render the texture on the 3D plane - ImageVtkMapper2D *imageMapper = + auto *imageMapper = dynamic_cast(node->GetMapper(1)); // GetMapper(1) provides the 2D mapper for the data node // if there is a 2D mapper, which is not the standard image mapper... if (!imageMapper && node->GetMapper(1)) { //... check if it is the composite mapper std::string cname(node->GetMapper(1)->GetNameOfClass()); if (!cname.compare("CompositeMapper")) // string.compare returns 0 if the two strings are equal. { // get the standard image mapper. // This is a special case in MITK and does only work for the CompositeMapper. imageMapper = dynamic_cast(node->GetMapper(3)); } } if ((node->IsVisible(renderer)) && imageMapper) { WeakPointerProperty::Pointer rendererProp = dynamic_cast(GetDataNode()->GetPropertyList()->GetProperty("renderer")); if (rendererProp.IsNotNull()) { BaseRenderer::Pointer planeRenderer = dynamic_cast(rendererProp->GetWeakPointer().GetPointer()); // Retrieve and update image to be mapped const ImageVtkMapper2D::LocalStorage *localStorage = imageMapper->GetLocalStorage(planeRenderer); if (planeRenderer.IsNotNull()) { // perform update of imagemapper if needed (maybe the respective 2D renderwindow is not rendered/update // before) imageMapper->Update(planeRenderer); // If it has not been initialized already in a previous pass, // generate an actor and a texture object to // render the image associated with the ImageVtkMapper2D. vtkActor *imageActor; vtkDataSetMapper *dataSetMapper = nullptr; vtkTexture *texture; if (m_ImageActors.count(imageMapper) == 0) { dataSetMapper = vtkDataSetMapper::New(); // Enable rendering without copying the image. dataSetMapper->ImmediateModeRenderingOn(); texture = vtkNeverTranslucentTexture::New(); texture->RepeatOff(); imageActor = vtkActor::New(); imageActor->SetMapper(dataSetMapper); imageActor->SetTexture(texture); imageActor->GetProperty()->SetOpacity( 0.999); // HACK! otherwise VTK wouldn't recognize this as translucent // surface (if LUT values map to alpha < 255 // improvement: apply "opacity" property onle HERE and also in 2D image mapper. DO NOT change LUT to // achieve // translucent images (see method ChangeOpacity in image mapper 2D) // Make imageActor the sole owner of the mapper and texture // objects dataSetMapper->UnRegister(nullptr); texture->UnRegister(nullptr); // Store the actor so that it may be accessed in following // passes. m_ImageActors[imageMapper].Initialize(imageActor, imageMapper, m_ImageMapperDeletedCommand); } else { // Else, retrieve the actor and associated objects from the // previous pass. imageActor = m_ImageActors[imageMapper].m_Actor; dataSetMapper = (vtkDataSetMapper *)imageActor->GetMapper(); texture = imageActor->GetTexture(); } // Set poly data new each time its object changes (e.g. when // switching between planar and curved geometries) if ((dataSetMapper != nullptr) && (dataSetMapper->GetInput() != surface->GetVtkPolyData())) { dataSetMapper->SetInputData(surface->GetVtkPolyData()); } dataSetMapper->Update(); // Check if the m_ReslicedImage is nullptr. // This is the case when no image geometry is met by // the reslicer. In that case, the texture has to be // empty (black) and we don't have to do anything. // See fixed bug #13275 if (localStorage->m_ReslicedImage != nullptr) { texture->SetInputConnection(localStorage->m_LevelWindowFilter->GetOutputPort()); // do not use a VTK lookup table (we do that ourselves in m_LevelWindowFilter) texture->MapColorScalarsThroughLookupTableOff(); // re-use properties from the 2D image mapper imageActor->SetProperty(localStorage->m_Actor->GetProperty()); imageActor->GetProperty()->SetAmbient(0.5); // Set texture interpolation on/off bool textureInterpolation = node->IsOn("texture interpolation", renderer); texture->SetInterpolate(textureInterpolation); // Store this actor to be added to the actor assembly, sort // by layer int layer = 1; node->GetIntProperty("layer", layer); layerSortedActors.insert(std::pair(layer, imageActor)); } } } } } } void PlaneGeometryDataVtkMapper3D::ActorInfo::Initialize(vtkActor *actor, itk::Object *sender, itk::Command *command) { m_Actor = actor; m_Sender = sender; // Get informed when ImageMapper object is deleted, so that // the data structures built here can be deleted as well m_ObserverID = sender->AddObserver(itk::DeleteEvent(), command); } PlaneGeometryDataVtkMapper3D::ActorInfo::ActorInfo() : m_Actor(nullptr), m_Sender(nullptr), m_ObserverID(0) {} PlaneGeometryDataVtkMapper3D::ActorInfo::~ActorInfo() { if (m_Sender != nullptr) { m_Sender->RemoveObserver(m_ObserverID); } if (m_Actor != nullptr) { m_Actor->ReleaseGraphicsResources(nullptr); m_Actor->Delete(); } } } // namespace mitk diff --git a/Modules/Core/src/Rendering/mitkPointSetVtkMapper3D.cpp b/Modules/Core/src/Rendering/mitkPointSetVtkMapper3D.cpp index 9259fd473c..0a21261991 100644 --- a/Modules/Core/src/Rendering/mitkPointSetVtkMapper3D.cpp +++ b/Modules/Core/src/Rendering/mitkPointSetVtkMapper3D.cpp @@ -1,705 +1,705 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkPointSetVtkMapper3D.h" #include "mitkColorProperty.h" #include "mitkDataNode.h" #include "mitkPointSet.h" #include "mitkProperties.h" #include "mitkVtkPropRenderer.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include const mitk::PointSet *mitk::PointSetVtkMapper3D::GetInput() { return static_cast(GetDataNode()->GetData()); } mitk::PointSetVtkMapper3D::PointSetVtkMapper3D() : m_vtkSelectedPointList(nullptr), m_vtkUnselectedPointList(nullptr), m_VtkSelectedPolyDataMapper(nullptr), m_VtkUnselectedPolyDataMapper(nullptr), m_vtkTextList(nullptr), m_NumberOfSelectedAdded(0), m_NumberOfUnselectedAdded(0), m_PointSize(1.0), m_ContourRadius(0.5), m_VertexRendering(false) { // propassembly m_PointsAssembly = vtkSmartPointer::New(); // creating actors to be able to set transform m_SelectedActor = vtkSmartPointer::New(); m_UnselectedActor = vtkSmartPointer::New(); m_ContourActor = vtkSmartPointer::New(); } mitk::PointSetVtkMapper3D::~PointSetVtkMapper3D() { } void mitk::PointSetVtkMapper3D::ReleaseGraphicsResources(vtkWindow *renWin) { m_PointsAssembly->ReleaseGraphicsResources(renWin); m_SelectedActor->ReleaseGraphicsResources(renWin); m_UnselectedActor->ReleaseGraphicsResources(renWin); m_ContourActor->ReleaseGraphicsResources(renWin); } void mitk::PointSetVtkMapper3D::ReleaseGraphicsResources(mitk::BaseRenderer *renderer) { m_PointsAssembly->ReleaseGraphicsResources(renderer->GetRenderWindow()); m_SelectedActor->ReleaseGraphicsResources(renderer->GetRenderWindow()); m_UnselectedActor->ReleaseGraphicsResources(renderer->GetRenderWindow()); m_ContourActor->ReleaseGraphicsResources(renderer->GetRenderWindow()); } void mitk::PointSetVtkMapper3D::CreateVTKRenderObjects() { m_vtkSelectedPointList = vtkSmartPointer::New(); m_vtkUnselectedPointList = vtkSmartPointer::New(); m_PointsAssembly->VisibilityOn(); if (m_PointsAssembly->GetParts()->IsItemPresent(m_SelectedActor)) m_PointsAssembly->RemovePart(m_SelectedActor); if (m_PointsAssembly->GetParts()->IsItemPresent(m_UnselectedActor)) m_PointsAssembly->RemovePart(m_UnselectedActor); if (m_PointsAssembly->GetParts()->IsItemPresent(m_ContourActor)) m_PointsAssembly->RemovePart(m_ContourActor); // exceptional displaying for PositionTracker -> MouseOrientationTool int mapperID; bool isInputDevice = false; if (this->GetDataNode()->GetBoolProperty("inputdevice", isInputDevice) && isInputDevice) { if (this->GetDataNode()->GetIntProperty("BaseRendererMapperID", mapperID) && mapperID == BaseRenderer::Standard3D) return; // The event for the PositionTracker came from the 3d widget and not needs to be displayed } // get and update the PointSet mitk::PointSet::Pointer input = const_cast(this->GetInput()); /* only update the input data, if the property tells us to */ bool update = true; this->GetDataNode()->GetBoolProperty("updateDataOnRender", update); if (update == true) input->Update(); int timestep = this->GetTimestep(); mitk::PointSet::DataType::Pointer itkPointSet = input->GetPointSet(timestep); if (itkPointSet.GetPointer() == nullptr) { m_PointsAssembly->VisibilityOff(); return; } // now fill selected and unselected pointList // get size of Points in Property m_PointSize = 2; mitk::FloatProperty::Pointer pointSizeProp = dynamic_cast(this->GetDataNode()->GetProperty("pointsize")); if (pointSizeProp.IsNotNull()) m_PointSize = pointSizeProp->GetValue(); // get the property for creating a label onto every point only once bool showLabel = true; this->GetDataNode()->GetBoolProperty("show label", showLabel); const char *pointLabel = nullptr; if (showLabel) { if (dynamic_cast(this->GetDataNode()->GetPropertyList()->GetProperty("label")) != nullptr) pointLabel = dynamic_cast(this->GetDataNode()->GetPropertyList()->GetProperty("label"))->GetValue(); else showLabel = false; } // whether or not to creat a "contour" - connecting lines between all the points int nbPoints = itkPointSet->GetPointData()->Size(); bool makeContour = false; this->GetDataNode()->GetBoolProperty("show contour", makeContour); bool closeContour = false; this->GetDataNode()->GetBoolProperty("close contour", closeContour); int contourPointLimit = 0; // NO contour if (makeContour) { if (closeContour) contourPointLimit = nbPoints; else contourPointLimit = nbPoints - 1; } // build list of all positions for later transform in one go mitk::PointSet::PointsContainer::Iterator pointsIter; int ptIdx; m_NumberOfSelectedAdded = 0; m_NumberOfUnselectedAdded = 0; vtkSmartPointer localPoints = vtkSmartPointer::New(); m_WorldPositions = vtkSmartPointer::New(); m_PointConnections = vtkSmartPointer::New(); // m_PointConnections between points for (ptIdx = 0, pointsIter = itkPointSet->GetPoints()->Begin(); pointsIter != itkPointSet->GetPoints()->End(); pointsIter++, ptIdx++) { itk::Point currentPoint = pointsIter->Value(); localPoints->InsertPoint(ptIdx, currentPoint[0], currentPoint[1], currentPoint[2]); if (makeContour && ptIdx < contourPointLimit) { vtkIdType cell[2] = {(ptIdx + 1) % nbPoints, ptIdx}; m_PointConnections->InsertNextCell(2, cell); } } vtkSmartPointer vtktransform = this->GetDataNode()->GetVtkTransform(this->GetTimestep()); vtktransform->TransformPoints(localPoints, m_WorldPositions); // create contour if (makeContour) { this->CreateContour(m_WorldPositions, m_PointConnections); } // check if the list for the PointDataContainer is the same size as the PointsContainer. Is not, then the points were // inserted manually and can not be visualized according to the PointData (selected/unselected) bool pointDataBroken = (itkPointSet->GetPointData()->Size() != itkPointSet->GetPoints()->Size()); // now add an object for each point in data mitk::PointSet::PointDataContainer::Iterator pointDataIter = itkPointSet->GetPointData()->Begin(); for (ptIdx = 0; ptIdx < nbPoints; ++ptIdx) // pointDataIter moved at end of loop { double currentPoint[3]; m_WorldPositions->GetPoint(ptIdx, currentPoint); vtkSmartPointer source; // check for the pointtype in data and decide which geom-object to take and then add to the selected or unselected // list int pointType; if (itkPointSet->GetPointData()->size() == 0 || pointDataBroken) pointType = mitk::PTUNDEFINED; else pointType = pointDataIter.Value().pointSpec; switch (pointType) { case mitk::PTUNDEFINED: { vtkSmartPointer sphere = vtkSmartPointer::New(); sphere->SetRadius(m_PointSize / 2.0f); sphere->SetCenter(currentPoint); // sphere->SetCenter(pointsIter.Value()[0],pointsIter.Value()[1],pointsIter.Value()[2]); // MouseOrientation Tool (PositionTracker) if (isInputDevice) { sphere->SetThetaResolution(10); sphere->SetPhiResolution(10); } else { sphere->SetThetaResolution(20); sphere->SetPhiResolution(20); } source = sphere; } break; case mitk::PTSTART: { vtkSmartPointer cube = vtkSmartPointer::New(); cube->SetXLength(m_PointSize / 2); cube->SetYLength(m_PointSize / 2); cube->SetZLength(m_PointSize / 2); cube->SetCenter(currentPoint); source = cube; } break; case mitk::PTCORNER: { vtkSmartPointer cone = vtkSmartPointer::New(); cone->SetRadius(m_PointSize / 2.0f); cone->SetCenter(currentPoint); cone->SetResolution(20); source = cone; } break; case mitk::PTEDGE: { vtkSmartPointer cylinder = vtkSmartPointer::New(); cylinder->SetRadius(m_PointSize / 2.0f); cylinder->SetCenter(currentPoint); cylinder->SetResolution(20); source = cylinder; } break; case mitk::PTEND: { vtkSmartPointer sphere = vtkSmartPointer::New(); sphere->SetRadius(m_PointSize / 2.0f); // no SetCenter?? this functionality should be explained! // otherwise: join with default block! sphere->SetThetaResolution(20); sphere->SetPhiResolution(20); source = sphere; } break; default: { vtkSmartPointer sphere = vtkSmartPointer::New(); sphere->SetRadius(m_PointSize / 2.0f); sphere->SetCenter(currentPoint); sphere->SetThetaResolution(20); sphere->SetPhiResolution(20); source = sphere; } break; } if (pointDataIter.Value().selected && !pointDataBroken) { m_vtkSelectedPointList->AddInputConnection(source->GetOutputPort()); ++m_NumberOfSelectedAdded; } else { m_vtkUnselectedPointList->AddInputConnection(source->GetOutputPort()); ++m_NumberOfUnselectedAdded; } if (showLabel) { char buffer[20]; std::string l = pointLabel; if (input->GetSize() > 1) { sprintf(buffer, "%d", ptIdx + 1); l.append(buffer); } // Define the text for the label vtkSmartPointer label = vtkSmartPointer::New(); label->SetText(l.c_str()); //# Set up a transform to move the label to a new position. vtkSmartPointer aLabelTransform = vtkSmartPointer::New(); aLabelTransform->Identity(); aLabelTransform->Translate(currentPoint[0] + 2, currentPoint[1] + 2, currentPoint[2]); aLabelTransform->Scale(5.7, 5.7, 5.7); //# Move the label to a new position. vtkSmartPointer labelTransform = vtkSmartPointer::New(); labelTransform->SetTransform(aLabelTransform); labelTransform->SetInputConnection(label->GetOutputPort()); // add it to the wright PointList if (pointType) { m_vtkSelectedPointList->AddInputConnection(labelTransform->GetOutputPort()); ++m_NumberOfSelectedAdded; } else { m_vtkUnselectedPointList->AddInputConnection(labelTransform->GetOutputPort()); ++m_NumberOfUnselectedAdded; } } if (pointDataIter != itkPointSet->GetPointData()->End()) pointDataIter++; } // end FOR // now according to number of elements added to selected or unselected, build up the rendering pipeline if (m_NumberOfSelectedAdded > 0) { m_VtkSelectedPolyDataMapper = vtkSmartPointer::New(); m_VtkSelectedPolyDataMapper->SetInputConnection(m_vtkSelectedPointList->GetOutputPort()); // create a new instance of the actor m_SelectedActor = vtkSmartPointer::New(); m_SelectedActor->SetMapper(m_VtkSelectedPolyDataMapper); m_PointsAssembly->AddPart(m_SelectedActor); } if (m_NumberOfUnselectedAdded > 0) { m_VtkUnselectedPolyDataMapper = vtkSmartPointer::New(); m_VtkUnselectedPolyDataMapper->SetInputConnection(m_vtkUnselectedPointList->GetOutputPort()); // create a new instance of the actor m_UnselectedActor = vtkSmartPointer::New(); m_UnselectedActor->SetMapper(m_VtkUnselectedPolyDataMapper); m_PointsAssembly->AddPart(m_UnselectedActor); } } void mitk::PointSetVtkMapper3D::VertexRendering() { // get and update the PointSet mitk::PointSet::Pointer input = const_cast(this->GetInput()); /* only update the input data, if the property tells us to */ bool update = true; this->GetDataNode()->GetBoolProperty("updateDataOnRender", update); if (update == true) input->Update(); int timestep = this->GetTimestep(); mitk::PointSet::DataType::Pointer itkPointSet = input->GetPointSet(timestep); // turn off standard actors m_UnselectedActor->VisibilityOff(); m_SelectedActor->VisibilityOff(); // point size m_PointSize = 2.0; mitk::FloatProperty::Pointer pointSizeProp = dynamic_cast(this->GetDataNode()->GetProperty("pointsize")); if (pointSizeProp.IsNotNull()) m_PointSize = pointSizeProp->GetValue(); double *color = m_UnselectedActor->GetProperty()->GetColor(); double opacity = m_UnselectedActor->GetProperty()->GetOpacity(); glClearColor(0.0, 0.0, 0.0, 0.0); glDisable(GL_COLOR_MATERIAL); glDisable(GL_LIGHTING); glEnable(GL_POINT_SMOOTH); glPointSize(m_PointSize); glBegin(GL_POINTS); glColor4d(color[0], color[1], color[2], opacity); for (auto pointsIter = itkPointSet->GetPoints()->Begin(); pointsIter != itkPointSet->GetPoints()->End(); pointsIter++) { const itk::Point &point = pointsIter->Value(); glVertex3d(point[0], point[1], point[2]); } glEnd(); // reset context glPointSize(1.0); glDisable(GL_POINT_SMOOTH); glEnable(GL_COLOR_MATERIAL); glEnable(GL_LIGHTING); } void mitk::PointSetVtkMapper3D::GenerateDataForRenderer(mitk::BaseRenderer *renderer) { bool visible = true; GetDataNode()->GetVisibility(visible, renderer, "visible"); if (!visible) { m_UnselectedActor->VisibilityOff(); m_SelectedActor->VisibilityOff(); m_ContourActor->VisibilityOff(); return; } // create new vtk render objects (e.g. sphere for a point) SetVtkMapperImmediateModeRendering(m_VtkSelectedPolyDataMapper); SetVtkMapperImmediateModeRendering(m_VtkUnselectedPolyDataMapper); BaseLocalStorage *ls = m_LSH.GetLocalStorage(renderer); bool needGenerateData = ls->IsGenerateDataRequired(renderer, this, GetDataNode()); if (!needGenerateData) { - mitk::FloatProperty *pointSizeProp = + auto *pointSizeProp = dynamic_cast(this->GetDataNode()->GetProperty("pointsize")); - mitk::FloatProperty *contourSizeProp = + auto *contourSizeProp = dynamic_cast(this->GetDataNode()->GetProperty("contoursize")); bool useVertexRendering = false; this->GetDataNode()->GetBoolProperty("Vertex Rendering", useVertexRendering); // only create new vtk render objects if property values were changed if (pointSizeProp && m_PointSize != pointSizeProp->GetValue()) needGenerateData = true; if (contourSizeProp && m_ContourRadius != contourSizeProp->GetValue()) needGenerateData = true; // when vertex rendering is enabled the pointset is always // drawn with opengl, thus we leave needGenerateData always false if (useVertexRendering && m_VertexRendering != useVertexRendering) { needGenerateData = false; m_VertexRendering = true; } else if (!useVertexRendering && m_VertexRendering) { m_VertexRendering = false; needGenerateData = true; } } if (needGenerateData) { this->CreateVTKRenderObjects(); ls->UpdateGenerateDataTime(); } this->ApplyAllProperties(renderer, m_ContourActor); bool showPoints = true; this->GetDataNode()->GetBoolProperty("show points", showPoints); m_UnselectedActor->SetVisibility(showPoints && !m_VertexRendering); m_SelectedActor->SetVisibility(showPoints && !m_VertexRendering); if (false && dynamic_cast(this->GetDataNode()->GetProperty("opacity")) != nullptr) { mitk::FloatProperty::Pointer pointOpacity = dynamic_cast(this->GetDataNode()->GetProperty("opacity")); float opacity = pointOpacity->GetValue(); m_ContourActor->GetProperty()->SetOpacity(opacity); m_UnselectedActor->GetProperty()->SetOpacity(opacity); m_SelectedActor->GetProperty()->SetOpacity(opacity); } bool showContour = false; this->GetDataNode()->GetBoolProperty("show contour", showContour); m_ContourActor->SetVisibility(showContour); // use vertex rendering if (m_VertexRendering) { VertexRendering(); ls->UpdateGenerateDataTime(); } } void mitk::PointSetVtkMapper3D::ResetMapper(BaseRenderer * /*renderer*/) { m_PointsAssembly->VisibilityOff(); } vtkProp *mitk::PointSetVtkMapper3D::GetVtkProp(mitk::BaseRenderer * /*renderer*/) { return m_PointsAssembly; } void mitk::PointSetVtkMapper3D::UpdateVtkTransform(mitk::BaseRenderer * /*renderer*/) { } void mitk::PointSetVtkMapper3D::ApplyAllProperties(mitk::BaseRenderer *renderer, vtkActor *actor) { Superclass::ApplyColorAndOpacityProperties(renderer, actor); // check for color props and use it for rendering of selected/unselected points and contour // due to different params in VTK (double/float) we have to convert! // vars to convert to double unselectedColor[4] = {1.0f, 1.0f, 0.0f, 1.0f}; // yellow double selectedColor[4] = {1.0f, 0.0f, 0.0f, 1.0f}; // red double contourColor[4] = {1.0f, 0.0f, 0.0f, 1.0f}; // red // different types for color!!! mitk::Color tmpColor; double opacity = 1.0; // check if there is an unselected property if (dynamic_cast( this->GetDataNode()->GetPropertyList(renderer)->GetProperty("unselectedcolor")) != nullptr) { tmpColor = dynamic_cast( this->GetDataNode()->GetPropertyList(renderer)->GetProperty("unselectedcolor")) ->GetValue(); unselectedColor[0] = tmpColor[0]; unselectedColor[1] = tmpColor[1]; unselectedColor[2] = tmpColor[2]; unselectedColor[3] = 1.0f; //!!define a new ColorProp to be able to pass alpha value } else if (dynamic_cast( this->GetDataNode()->GetPropertyList(nullptr)->GetProperty("unselectedcolor")) != nullptr) { tmpColor = dynamic_cast(this->GetDataNode()->GetPropertyList(nullptr)->GetProperty("unselectedcolor")) ->GetValue(); unselectedColor[0] = tmpColor[0]; unselectedColor[1] = tmpColor[1]; unselectedColor[2] = tmpColor[2]; unselectedColor[3] = 1.0f; //!!define a new ColorProp to be able to pass alpha value } else { // check if the node has a color float unselectedColorTMP[4] = {1.0f, 1.0f, 0.0f, 1.0f}; // yellow m_DataNode->GetColor(unselectedColorTMP, nullptr); unselectedColor[0] = unselectedColorTMP[0]; unselectedColor[1] = unselectedColorTMP[1]; unselectedColor[2] = unselectedColorTMP[2]; // unselectedColor[3] stays 1.0f } // get selected property if (dynamic_cast( this->GetDataNode()->GetPropertyList(renderer)->GetProperty("selectedcolor")) != nullptr) { tmpColor = dynamic_cast(this->GetDataNode()->GetPropertyList(renderer)->GetProperty("selectedcolor")) ->GetValue(); selectedColor[0] = tmpColor[0]; selectedColor[1] = tmpColor[1]; selectedColor[2] = tmpColor[2]; selectedColor[3] = 1.0f; } else if (dynamic_cast( this->GetDataNode()->GetPropertyList(nullptr)->GetProperty("selectedcolor")) != nullptr) { tmpColor = dynamic_cast(this->GetDataNode()->GetPropertyList(nullptr)->GetProperty("selectedcolor")) ->GetValue(); selectedColor[0] = tmpColor[0]; selectedColor[1] = tmpColor[1]; selectedColor[2] = tmpColor[2]; selectedColor[3] = 1.0f; } // get contour property if (dynamic_cast( this->GetDataNode()->GetPropertyList(renderer)->GetProperty("contourcolor")) != nullptr) { tmpColor = dynamic_cast(this->GetDataNode()->GetPropertyList(renderer)->GetProperty("contourcolor")) ->GetValue(); contourColor[0] = tmpColor[0]; contourColor[1] = tmpColor[1]; contourColor[2] = tmpColor[2]; contourColor[3] = 1.0f; } else if (dynamic_cast( this->GetDataNode()->GetPropertyList(nullptr)->GetProperty("contourcolor")) != nullptr) { tmpColor = dynamic_cast(this->GetDataNode()->GetPropertyList(nullptr)->GetProperty("contourcolor")) ->GetValue(); contourColor[0] = tmpColor[0]; contourColor[1] = tmpColor[1]; contourColor[2] = tmpColor[2]; contourColor[3] = 1.0f; } if (dynamic_cast(this->GetDataNode()->GetPropertyList(renderer)->GetProperty("opacity")) != nullptr) { mitk::FloatProperty::Pointer pointOpacity = dynamic_cast(this->GetDataNode()->GetPropertyList(renderer)->GetProperty("opacity")); opacity = pointOpacity->GetValue(); } else if (dynamic_cast(this->GetDataNode()->GetPropertyList(nullptr)->GetProperty("opacity")) != nullptr) { mitk::FloatProperty::Pointer pointOpacity = dynamic_cast(this->GetDataNode()->GetPropertyList(nullptr)->GetProperty("opacity")); opacity = pointOpacity->GetValue(); } // finished color / opacity fishing! // check if a contour shall be drawn bool showContour = false; this->GetDataNode()->GetBoolProperty("show contour", showContour, renderer); if (showContour && (m_ContourActor != nullptr)) { this->CreateContour(m_WorldPositions, m_PointConnections); m_ContourActor->GetProperty()->SetColor(contourColor); m_ContourActor->GetProperty()->SetOpacity(opacity); } m_SelectedActor->GetProperty()->SetColor(selectedColor); m_SelectedActor->GetProperty()->SetOpacity(opacity); m_UnselectedActor->GetProperty()->SetColor(unselectedColor); m_UnselectedActor->GetProperty()->SetOpacity(opacity); } void mitk::PointSetVtkMapper3D::CreateContour(vtkPoints *points, vtkCellArray *m_PointConnections) { vtkSmartPointer vtkContourPolyData = vtkSmartPointer::New(); vtkSmartPointer vtkContourPolyDataMapper = vtkSmartPointer::New(); vtkSmartPointer contour = vtkSmartPointer::New(); contour->SetPoints(points); contour->SetLines(m_PointConnections); vtkSmartPointer tubeFilter = vtkSmartPointer::New(); tubeFilter->SetNumberOfSides(12); tubeFilter->SetInputData(contour); // check for property contoursize. m_ContourRadius = 0.5; mitk::FloatProperty::Pointer contourSizeProp = dynamic_cast(this->GetDataNode()->GetProperty("contoursize")); if (contourSizeProp.IsNotNull()) m_ContourRadius = contourSizeProp->GetValue(); tubeFilter->SetRadius(m_ContourRadius); tubeFilter->Update(); // add to pipeline vtkContourPolyData->AddInputConnection(tubeFilter->GetOutputPort()); vtkContourPolyDataMapper->SetInputConnection(vtkContourPolyData->GetOutputPort()); m_ContourActor->SetMapper(vtkContourPolyDataMapper); m_PointsAssembly->AddPart(m_ContourActor); } void mitk::PointSetVtkMapper3D::SetDefaultProperties(mitk::DataNode *node, mitk::BaseRenderer *renderer, bool overwrite) { node->AddProperty("line width", mitk::IntProperty::New(2), renderer, overwrite); node->AddProperty("pointsize", mitk::FloatProperty::New(1.0), renderer, overwrite); node->AddProperty("selectedcolor", mitk::ColorProperty::New(1.0f, 0.0f, 0.0f), renderer, overwrite); // red node->AddProperty("color", mitk::ColorProperty::New(1.0f, 1.0f, 0.0f), renderer, overwrite); // yellow node->AddProperty("opacity", mitk::FloatProperty::New(1.0f), renderer, overwrite); node->AddProperty("show contour", mitk::BoolProperty::New(false), renderer, overwrite); node->AddProperty("close contour", mitk::BoolProperty::New(false), renderer, overwrite); node->AddProperty("contourcolor", mitk::ColorProperty::New(1.0f, 0.0f, 0.0f), renderer, overwrite); node->AddProperty("contoursize", mitk::FloatProperty::New(0.5), renderer, overwrite); node->AddProperty("show points", mitk::BoolProperty::New(true), renderer, overwrite); node->AddProperty("updateDataOnRender", mitk::BoolProperty::New(true), renderer, overwrite); node->AddProperty("Vertex Rendering", mitk::BoolProperty::New(false), renderer, overwrite); Superclass::SetDefaultProperties(node, renderer, overwrite); } diff --git a/Modules/Core/src/Rendering/mitkSurfaceVtkMapper2D.cpp b/Modules/Core/src/Rendering/mitkSurfaceVtkMapper2D.cpp index f64a34b96b..fdcba029f7 100644 --- a/Modules/Core/src/Rendering/mitkSurfaceVtkMapper2D.cpp +++ b/Modules/Core/src/Rendering/mitkSurfaceVtkMapper2D.cpp @@ -1,406 +1,406 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkSurfaceVtkMapper2D.h" // MITK includes #include "mitkVtkPropRenderer.h" #include #include #include #include #include #include #include #include #include // VTK includes #include #include #include #include #include #include #include #include #include #include #include // constructor LocalStorage mitk::SurfaceVtkMapper2D::LocalStorage::LocalStorage() { m_Mapper = vtkSmartPointer::New(); m_Mapper->ScalarVisibilityOff(); m_Actor = vtkSmartPointer::New(); m_PropAssembly = vtkSmartPointer::New(); m_PropAssembly->AddPart(m_Actor); m_CuttingPlane = vtkSmartPointer::New(); m_Cutter = vtkSmartPointer::New(); m_Cutter->SetCutFunction(m_CuttingPlane); m_Mapper->SetInputConnection(m_Cutter->GetOutputPort()); m_NormalGlyph = vtkSmartPointer::New(); m_InverseNormalGlyph = vtkSmartPointer::New(); // Source for the glyph filter m_ArrowSource = vtkSmartPointer::New(); // set small default values for fast rendering m_ArrowSource->SetTipRadius(0.05); m_ArrowSource->SetTipLength(0.20); m_ArrowSource->SetTipResolution(5); m_ArrowSource->SetShaftResolution(5); m_ArrowSource->SetShaftRadius(0.01); m_NormalGlyph->SetSourceConnection(m_ArrowSource->GetOutputPort()); m_NormalGlyph->SetVectorModeToUseNormal(); m_NormalGlyph->OrientOn(); m_InverseNormalGlyph->SetSourceConnection(m_ArrowSource->GetOutputPort()); m_InverseNormalGlyph->SetVectorModeToUseNormal(); m_InverseNormalGlyph->OrientOn(); m_NormalMapper = vtkSmartPointer::New(); m_NormalMapper->SetInputConnection(m_NormalGlyph->GetOutputPort()); m_NormalMapper->ScalarVisibilityOff(); m_InverseNormalMapper = vtkSmartPointer::New(); m_InverseNormalMapper->SetInputConnection(m_NormalGlyph->GetOutputPort()); m_InverseNormalMapper->ScalarVisibilityOff(); m_NormalActor = vtkSmartPointer::New(); m_NormalActor->SetMapper(m_NormalMapper); m_InverseNormalActor = vtkSmartPointer::New(); m_InverseNormalActor->SetMapper(m_InverseNormalMapper); m_ReverseSense = vtkSmartPointer::New(); } // destructor LocalStorage mitk::SurfaceVtkMapper2D::LocalStorage::~LocalStorage() { } const mitk::Surface *mitk::SurfaceVtkMapper2D::GetInput() const { return static_cast(GetDataNode()->GetData()); } // constructor PointSetVtkMapper2D mitk::SurfaceVtkMapper2D::SurfaceVtkMapper2D() { } mitk::SurfaceVtkMapper2D::~SurfaceVtkMapper2D() { } // reset mapper so that nothing is displayed e.g. toggle visiblity of the propassembly void mitk::SurfaceVtkMapper2D::ResetMapper(BaseRenderer *renderer) { LocalStorage *ls = m_LSH.GetLocalStorage(renderer); ls->m_PropAssembly->VisibilityOff(); } vtkProp *mitk::SurfaceVtkMapper2D::GetVtkProp(mitk::BaseRenderer *renderer) { LocalStorage *ls = m_LSH.GetLocalStorage(renderer); return ls->m_PropAssembly; } void mitk::SurfaceVtkMapper2D::Update(mitk::BaseRenderer *renderer) { const mitk::DataNode *node = GetDataNode(); if (node == nullptr) return; bool visible = true; node->GetVisibility(visible, renderer, "visible"); if (!visible) return; - mitk::Surface *surface = static_cast(node->GetData()); + auto *surface = static_cast(node->GetData()); if (surface == nullptr) return; // Calculate time step of the input data for the specified renderer (integer value) this->CalculateTimeStep(renderer); // Check if time step is valid const mitk::TimeGeometry *dataTimeGeometry = surface->GetTimeGeometry(); if ((dataTimeGeometry == nullptr) || (dataTimeGeometry->CountTimeSteps() == 0) || (!dataTimeGeometry->IsValidTimeStep(this->GetTimestep()))) { return; } surface->UpdateOutputInformation(); LocalStorage *localStorage = m_LSH.GetLocalStorage(renderer); // check if something important has changed and we need to rerender if ((localStorage->m_LastUpdateTime < node->GetMTime()) // was the node modified? || (localStorage->m_LastUpdateTime < surface->GetPipelineMTime()) // Was the data modified? || (localStorage->m_LastUpdateTime < renderer->GetCurrentWorldPlaneGeometryUpdateTime()) // was the geometry modified? || (localStorage->m_LastUpdateTime < renderer->GetCurrentWorldPlaneGeometry()->GetMTime()) || (localStorage->m_LastUpdateTime < node->GetPropertyList()->GetMTime()) // was a property modified? || (localStorage->m_LastUpdateTime < node->GetPropertyList(renderer)->GetMTime())) { this->GenerateDataForRenderer(renderer); } // since we have checked that nothing important has changed, we can set // m_LastUpdateTime to the current time localStorage->m_LastUpdateTime.Modified(); } void mitk::SurfaceVtkMapper2D::GenerateDataForRenderer(mitk::BaseRenderer *renderer) { const DataNode *node = GetDataNode(); - Surface *surface = static_cast(node->GetData()); + auto *surface = static_cast(node->GetData()); const TimeGeometry *dataTimeGeometry = surface->GetTimeGeometry(); LocalStorage *localStorage = m_LSH.GetLocalStorage(renderer); ScalarType time = renderer->GetTime(); int timestep = 0; if (time > itk::NumericTraits::NonpositiveMin()) timestep = dataTimeGeometry->TimePointToTimeStep(time); vtkSmartPointer inputPolyData = surface->GetVtkPolyData(timestep); if ((inputPolyData == nullptr) || (inputPolyData->GetNumberOfPoints() < 1)) return; // apply color and opacity read from the PropertyList this->ApplyAllProperties(renderer); const PlaneGeometry *planeGeometry = renderer->GetCurrentWorldPlaneGeometry(); if ((planeGeometry == nullptr) || (!planeGeometry->IsValid()) || (!planeGeometry->HasReferenceGeometry())) { return; } if (localStorage->m_Actor->GetMapper() == nullptr) localStorage->m_Actor->SetMapper(localStorage->m_Mapper); double origin[3]; origin[0] = planeGeometry->GetOrigin()[0]; origin[1] = planeGeometry->GetOrigin()[1]; origin[2] = planeGeometry->GetOrigin()[2]; double normal[3]; normal[0] = planeGeometry->GetNormal()[0]; normal[1] = planeGeometry->GetNormal()[1]; normal[2] = planeGeometry->GetNormal()[2]; localStorage->m_CuttingPlane->SetOrigin(origin); localStorage->m_CuttingPlane->SetNormal(normal); // Transform the data according to its geometry. // See UpdateVtkTransform documentation for details. vtkSmartPointer vtktransform = GetDataNode()->GetVtkTransform(this->GetTimestep()); vtkSmartPointer filter = vtkSmartPointer::New(); filter->SetTransform(vtktransform); filter->SetInputData(inputPolyData); localStorage->m_Cutter->SetInputConnection(filter->GetOutputPort()); localStorage->m_Cutter->Update(); bool generateNormals = false; node->GetBoolProperty("draw normals 2D", generateNormals); if (generateNormals) { localStorage->m_NormalGlyph->SetInputConnection(localStorage->m_Cutter->GetOutputPort()); localStorage->m_NormalGlyph->Update(); localStorage->m_NormalMapper->SetInputConnection(localStorage->m_NormalGlyph->GetOutputPort()); localStorage->m_PropAssembly->AddPart(localStorage->m_NormalActor); } else { localStorage->m_NormalGlyph->SetInputConnection(nullptr); localStorage->m_PropAssembly->RemovePart(localStorage->m_NormalActor); } bool generateInverseNormals = false; node->GetBoolProperty("invert normals", generateInverseNormals); if (generateInverseNormals) { localStorage->m_ReverseSense->SetInputConnection(localStorage->m_Cutter->GetOutputPort()); localStorage->m_ReverseSense->ReverseCellsOff(); localStorage->m_ReverseSense->ReverseNormalsOn(); localStorage->m_InverseNormalGlyph->SetInputConnection(localStorage->m_ReverseSense->GetOutputPort()); localStorage->m_InverseNormalGlyph->Update(); localStorage->m_InverseNormalMapper->SetInputConnection(localStorage->m_InverseNormalGlyph->GetOutputPort()); localStorage->m_PropAssembly->AddPart(localStorage->m_InverseNormalActor); } else { localStorage->m_ReverseSense->SetInputConnection(nullptr); localStorage->m_PropAssembly->RemovePart(localStorage->m_InverseNormalActor); } } void mitk::SurfaceVtkMapper2D::FixupLegacyProperties(PropertyList *properties) { // Before bug 18528, "line width" was an IntProperty, now it is a FloatProperty float lineWidth = 1.0f; if (!properties->GetFloatProperty("line width", lineWidth)) { int legacyLineWidth = lineWidth; if (properties->GetIntProperty("line width", legacyLineWidth)) { properties->ReplaceProperty("line width", FloatProperty::New(static_cast(legacyLineWidth))); } } } void mitk::SurfaceVtkMapper2D::ApplyAllProperties(mitk::BaseRenderer *renderer) { const DataNode *node = GetDataNode(); if (node == nullptr) { return; } FixupLegacyProperties(node->GetPropertyList(renderer)); FixupLegacyProperties(node->GetPropertyList()); float lineWidth = 1.0f; node->GetFloatProperty("line width", lineWidth, renderer); LocalStorage *localStorage = m_LSH.GetLocalStorage(renderer); // check for color and opacity properties, use it for rendering if they exists float color[3] = {1.0f, 1.0f, 1.0f}; node->GetColor(color, renderer, "color"); float opacity = 1.0f; node->GetOpacity(opacity, renderer, "opacity"); // Pass properties to VTK localStorage->m_Actor->GetProperty()->SetColor(color[0], color[1], color[2]); localStorage->m_Actor->GetProperty()->SetOpacity(opacity); localStorage->m_NormalActor->GetProperty()->SetOpacity(opacity); localStorage->m_InverseNormalActor->GetProperty()->SetOpacity(opacity); localStorage->m_Actor->GetProperty()->SetLineWidth(lineWidth); // By default, the cutter will also copy/compute normals of the cut // to the output polydata. The normals will influence the // vtkPolyDataMapper lightning. To view a clean cut the lighting has // to be disabled. localStorage->m_Actor->GetProperty()->SetLighting(0); // same block for scalar data rendering as in 3D mapper mitk::TransferFunctionProperty::Pointer transferFuncProp; this->GetDataNode()->GetProperty(transferFuncProp, "Surface.TransferFunction", renderer); if (transferFuncProp.IsNotNull()) { localStorage->m_Mapper->SetLookupTable(transferFuncProp->GetValue()->GetColorTransferFunction()); } mitk::LookupTableProperty::Pointer lookupTableProp; this->GetDataNode()->GetProperty(lookupTableProp, "LookupTable", renderer); if (lookupTableProp.IsNotNull()) { localStorage->m_Mapper->SetLookupTable(lookupTableProp->GetLookupTable()->GetVtkLookupTable()); } mitk::LevelWindow levelWindow; if (this->GetDataNode()->GetLevelWindow(levelWindow, renderer, "levelWindow")) { localStorage->m_Mapper->SetScalarRange(levelWindow.GetLowerWindowBound(), levelWindow.GetUpperWindowBound()); } else if (this->GetDataNode()->GetLevelWindow(levelWindow, renderer)) { localStorage->m_Mapper->SetScalarRange(levelWindow.GetLowerWindowBound(), levelWindow.GetUpperWindowBound()); } bool scalarVisibility = false; this->GetDataNode()->GetBoolProperty("scalar visibility", scalarVisibility); localStorage->m_Mapper->SetScalarVisibility((scalarVisibility ? 1 : 0)); if (scalarVisibility) { mitk::VtkScalarModeProperty *scalarMode; if (this->GetDataNode()->GetProperty(scalarMode, "scalar mode", renderer)) localStorage->m_Mapper->SetScalarMode(scalarMode->GetVtkScalarMode()); else localStorage->m_Mapper->SetScalarModeToDefault(); bool colorMode = false; this->GetDataNode()->GetBoolProperty("color mode", colorMode); localStorage->m_Mapper->SetColorMode((colorMode ? 1 : 0)); double scalarsMin = 0; this->GetDataNode()->GetDoubleProperty("ScalarsRangeMinimum", scalarsMin, renderer); double scalarsMax = 1.0; this->GetDataNode()->GetDoubleProperty("ScalarsRangeMaximum", scalarsMax, renderer); localStorage->m_Mapper->SetScalarRange(scalarsMin, scalarsMax); } // color for inverse normals float inverseNormalsColor[3] = {1.0f, 0.0f, 0.0f}; node->GetColor(inverseNormalsColor, renderer, "back color"); localStorage->m_InverseNormalActor->GetProperty()->SetColor( inverseNormalsColor[0], inverseNormalsColor[1], inverseNormalsColor[2]); // color for normals float normalsColor[3] = {0.0f, 1.0f, 0.0f}; node->GetColor(normalsColor, renderer, "front color"); localStorage->m_NormalActor->GetProperty()->SetColor(normalsColor[0], normalsColor[1], normalsColor[2]); // normals scaling float normalScaleFactor = 10.0f; node->GetFloatProperty("front normal lenth (px)", normalScaleFactor, renderer); localStorage->m_NormalGlyph->SetScaleFactor(normalScaleFactor); // inverse normals scaling float inverseNormalScaleFactor = 10.0f; node->GetFloatProperty("back normal lenth (px)", inverseNormalScaleFactor, renderer); localStorage->m_InverseNormalGlyph->SetScaleFactor(inverseNormalScaleFactor); } void mitk::SurfaceVtkMapper2D::SetDefaultProperties(mitk::DataNode *node, mitk::BaseRenderer *renderer, bool overwrite) { mitk::IPropertyAliases *aliases = mitk::CoreServices::GetPropertyAliases(); node->AddProperty("line width", FloatProperty::New(2.0f), renderer, overwrite); aliases->AddAlias("line width", "Surface.2D.Line Width", "Surface"); node->AddProperty("scalar mode", VtkScalarModeProperty::New(), renderer, overwrite); node->AddProperty("draw normals 2D", BoolProperty::New(false), renderer, overwrite); aliases->AddAlias("draw normals 2D", "Surface.2D.Normals.Draw Normals", "Surface"); node->AddProperty("invert normals", BoolProperty::New(false), renderer, overwrite); aliases->AddAlias("invert normals", "Surface.2D.Normals.Draw Inverse Normals", "Surface"); node->AddProperty("front color", ColorProperty::New(0.0, 1.0, 0.0), renderer, overwrite); aliases->AddAlias("front color", "Surface.2D.Normals.Normals Color", "Surface"); node->AddProperty("back color", ColorProperty::New(1.0, 0.0, 0.0), renderer, overwrite); aliases->AddAlias("back color", "Surface.2D.Normals.Inverse Normals Color", "Surface"); node->AddProperty("front normal lenth (px)", FloatProperty::New(10.0), renderer, overwrite); aliases->AddAlias("front normal lenth (px)", "Surface.2D.Normals.Normals Scale Factor", "Surface"); node->AddProperty("back normal lenth (px)", FloatProperty::New(10.0), renderer, overwrite); aliases->AddAlias("back normal lenth (px)", "Surface.2D.Normals.Inverse Normals Scale Factor", "Surface"); node->AddProperty("layer", IntProperty::New(100), renderer, overwrite); Superclass::SetDefaultProperties(node, renderer, overwrite); } diff --git a/Modules/Core/src/Rendering/mitkSurfaceVtkMapper3D.cpp b/Modules/Core/src/Rendering/mitkSurfaceVtkMapper3D.cpp index f8031d0a11..8fea49adbf 100644 --- a/Modules/Core/src/Rendering/mitkSurfaceVtkMapper3D.cpp +++ b/Modules/Core/src/Rendering/mitkSurfaceVtkMapper3D.cpp @@ -1,522 +1,522 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkSurfaceVtkMapper3D.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include // VTK #include #include #include #include #include #include #include #include const mitk::Surface *mitk::SurfaceVtkMapper3D::GetInput() { return static_cast(GetDataNode()->GetData()); } mitk::SurfaceVtkMapper3D::SurfaceVtkMapper3D() { m_GenerateNormals = false; } mitk::SurfaceVtkMapper3D::~SurfaceVtkMapper3D() { } void mitk::SurfaceVtkMapper3D::GenerateDataForRenderer(mitk::BaseRenderer *renderer) { LocalStorage *ls = m_LSH.GetLocalStorage(renderer); bool visible = true; GetDataNode()->GetVisibility(visible, renderer, "visible"); if (!visible) { ls->m_Actor->VisibilityOff(); return; } // // set the input-object at time t for the mapper // mitk::Surface::Pointer input = const_cast(this->GetInput()); vtkSmartPointer polydata = input->GetVtkPolyData(this->GetTimestep()); if (polydata == nullptr) { ls->m_Actor->VisibilityOff(); return; } if (m_GenerateNormals) { ls->m_VtkPolyDataNormals->SetInputData(polydata); ls->m_VtkPolyDataMapper->SetInputConnection(ls->m_VtkPolyDataNormals->GetOutputPort()); } else { bool depthsorting = false; GetDataNode()->GetBoolProperty("Depth Sorting", depthsorting); if (depthsorting) { ls->m_DepthSort->SetInputData(polydata); ls->m_DepthSort->SetCamera(renderer->GetVtkRenderer()->GetActiveCamera()); ls->m_DepthSort->SetDirectionToBackToFront(); ls->m_DepthSort->Update(); ls->m_VtkPolyDataMapper->SetInputConnection(ls->m_DepthSort->GetOutputPort()); } else { ls->m_VtkPolyDataMapper->SetInputData(polydata); } } // // apply properties read from the PropertyList // ApplyAllProperties(renderer, ls->m_Actor); if (visible) ls->m_Actor->VisibilityOn(); } void mitk::SurfaceVtkMapper3D::ResetMapper(BaseRenderer *renderer) { LocalStorage *ls = m_LSH.GetLocalStorage(renderer); ls->m_Actor->VisibilityOff(); } void mitk::SurfaceVtkMapper3D::ApplyMitkPropertiesToVtkProperty(mitk::DataNode *node, vtkProperty *property, mitk::BaseRenderer *renderer) { // Backface culling { mitk::BoolProperty::Pointer p; node->GetProperty(p, "Backface Culling", renderer); bool useCulling = false; if (p.IsNotNull()) useCulling = p->GetValue(); property->SetBackfaceCulling(useCulling); } // Colors { double ambient[3] = {0.5, 0.5, 0.0}; double diffuse[3] = {0.5, 0.5, 0.0}; double specular[3] = {1.0, 1.0, 1.0}; float coeff_ambient = 0.5f; float coeff_diffuse = 0.5f; float coeff_specular = 0.5f; float power_specular = 10.0f; // Color { mitk::ColorProperty::Pointer p; node->GetProperty(p, "color", renderer); if (p.IsNotNull()) { mitk::Color c = p->GetColor(); ambient[0] = c.GetRed(); ambient[1] = c.GetGreen(); ambient[2] = c.GetBlue(); diffuse[0] = c.GetRed(); diffuse[1] = c.GetGreen(); diffuse[2] = c.GetBlue(); // Setting specular color to the same, make physically no real sense, however vtk rendering slows down, if these // colors are different. specular[0] = c.GetRed(); specular[1] = c.GetGreen(); specular[2] = c.GetBlue(); } } // Ambient { mitk::ColorProperty::Pointer p; node->GetProperty(p, "material.ambientColor", renderer); if (p.IsNotNull()) { mitk::Color c = p->GetColor(); ambient[0] = c.GetRed(); ambient[1] = c.GetGreen(); ambient[2] = c.GetBlue(); } } // Diffuse { mitk::ColorProperty::Pointer p; node->GetProperty(p, "material.diffuseColor", renderer); if (p.IsNotNull()) { mitk::Color c = p->GetColor(); diffuse[0] = c.GetRed(); diffuse[1] = c.GetGreen(); diffuse[2] = c.GetBlue(); } } // Specular { mitk::ColorProperty::Pointer p; node->GetProperty(p, "material.specularColor", renderer); if (p.IsNotNull()) { mitk::Color c = p->GetColor(); specular[0] = c.GetRed(); specular[1] = c.GetGreen(); specular[2] = c.GetBlue(); } } // Ambient coeff { node->GetFloatProperty("material.ambientCoefficient", coeff_ambient, renderer); } // Diffuse coeff { node->GetFloatProperty("material.diffuseCoefficient", coeff_diffuse, renderer); } // Specular coeff { node->GetFloatProperty("material.specularCoefficient", coeff_specular, renderer); } // Specular power { node->GetFloatProperty("material.specularPower", power_specular, renderer); } property->SetAmbient(coeff_ambient); property->SetDiffuse(coeff_diffuse); property->SetSpecular(coeff_specular); property->SetSpecularPower(power_specular); property->SetAmbientColor(ambient); property->SetDiffuseColor(diffuse); property->SetSpecularColor(specular); } // Render mode { // Opacity { float opacity = 1.0f; if (node->GetOpacity(opacity, renderer)) property->SetOpacity(opacity); } // Wireframe line width { float lineWidth = 1; node->GetFloatProperty("material.wireframeLineWidth", lineWidth, renderer); property->SetLineWidth(lineWidth); } // Point size { float pointSize = 1.0f; node->GetFloatProperty("material.pointSize", pointSize, renderer); property->SetPointSize(pointSize); } // Representation { mitk::VtkRepresentationProperty::Pointer p; node->GetProperty(p, "material.representation", renderer); if (p.IsNotNull()) property->SetRepresentation(p->GetVtkRepresentation()); } // Interpolation { mitk::VtkInterpolationProperty::Pointer p; node->GetProperty(p, "material.interpolation", renderer); if (p.IsNotNull()) property->SetInterpolation(p->GetVtkInterpolation()); } } } void mitk::SurfaceVtkMapper3D::ApplyAllProperties(mitk::BaseRenderer *renderer, vtkActor * /*actor*/) { LocalStorage *ls = m_LSH.GetLocalStorage(renderer); Superclass::ApplyColorAndOpacityProperties(renderer, ls->m_Actor); // VTK Properties ApplyMitkPropertiesToVtkProperty(this->GetDataNode(), ls->m_Actor->GetProperty(), renderer); mitk::TransferFunctionProperty::Pointer transferFuncProp; this->GetDataNode()->GetProperty(transferFuncProp, "Surface.TransferFunction", renderer); if (transferFuncProp.IsNotNull()) { ls->m_VtkPolyDataMapper->SetLookupTable(transferFuncProp->GetValue()->GetColorTransferFunction()); } mitk::LookupTableProperty::Pointer lookupTableProp; this->GetDataNode()->GetProperty(lookupTableProp, "LookupTable", renderer); if (lookupTableProp.IsNotNull()) { ls->m_VtkPolyDataMapper->SetLookupTable(lookupTableProp->GetLookupTable()->GetVtkLookupTable()); } mitk::LevelWindow levelWindow; if (this->GetDataNode()->GetLevelWindow(levelWindow, renderer, "levelWindow")) { ls->m_VtkPolyDataMapper->SetScalarRange(levelWindow.GetLowerWindowBound(), levelWindow.GetUpperWindowBound()); } else if (this->GetDataNode()->GetLevelWindow(levelWindow, renderer)) { ls->m_VtkPolyDataMapper->SetScalarRange(levelWindow.GetLowerWindowBound(), levelWindow.GetUpperWindowBound()); } bool scalarVisibility = false; this->GetDataNode()->GetBoolProperty("scalar visibility", scalarVisibility); ls->m_VtkPolyDataMapper->SetScalarVisibility((scalarVisibility ? 1 : 0)); if (scalarVisibility) { mitk::VtkScalarModeProperty *scalarMode; if (this->GetDataNode()->GetProperty(scalarMode, "scalar mode", renderer)) ls->m_VtkPolyDataMapper->SetScalarMode(scalarMode->GetVtkScalarMode()); else ls->m_VtkPolyDataMapper->SetScalarModeToDefault(); bool colorMode = false; this->GetDataNode()->GetBoolProperty("color mode", colorMode); ls->m_VtkPolyDataMapper->SetColorMode((colorMode ? 1 : 0)); double scalarsMin = 0; this->GetDataNode()->GetDoubleProperty("ScalarsRangeMinimum", scalarsMin, renderer); double scalarsMax = 1.0; this->GetDataNode()->GetDoubleProperty("ScalarsRangeMaximum", scalarsMax, renderer); ls->m_VtkPolyDataMapper->SetScalarRange(scalarsMin, scalarsMax); } mitk::SmartPointerProperty::Pointer imagetextureProp = dynamic_cast(GetDataNode()->GetProperty("Surface.Texture", renderer)); if (imagetextureProp.IsNotNull()) { mitk::Image *miktTexture = dynamic_cast(imagetextureProp->GetSmartPointer().GetPointer()); vtkSmartPointer vtkTxture = vtkSmartPointer::New(); // Either select the first slice of a volume if (miktTexture->GetDimension(2) > 1) { MITK_WARN << "3D Textures are not supported by VTK and MITK. The first slice of the volume will be used instead!"; mitk::ImageSliceSelector::Pointer sliceselector = mitk::ImageSliceSelector::New(); sliceselector->SetSliceNr(0); sliceselector->SetChannelNr(0); sliceselector->SetTimeNr(0); sliceselector->SetInput(miktTexture); sliceselector->Update(); vtkTxture->SetInputData(sliceselector->GetOutput()->GetVtkImageData()); } else // or just use the 2D image { vtkTxture->SetInputData(miktTexture->GetVtkImageData()); } // pass the texture to the actor ls->m_Actor->SetTexture(vtkTxture); if (ls->m_VtkPolyDataMapper->GetInput()->GetPointData()->GetTCoords() == nullptr) { MITK_ERROR << "Surface.Texture property was set, but there are no texture coordinates. Please provide texture " "coordinates for the vtkPolyData via vtkPolyData->GetPointData()->SetTCoords()."; } // if no texture is set, this will also remove a previously used texture // and reset the actor to it's default behaviour } else { ls->m_Actor->SetTexture(nullptr); } // deprecated settings bool deprecatedUseCellData = false; this->GetDataNode()->GetBoolProperty("deprecated useCellDataForColouring", deprecatedUseCellData); bool deprecatedUsePointData = false; this->GetDataNode()->GetBoolProperty("deprecated usePointDataForColouring", deprecatedUsePointData); if (deprecatedUseCellData) { ls->m_VtkPolyDataMapper->SetColorModeToDefault(); ls->m_VtkPolyDataMapper->SetScalarRange(0, 255); ls->m_VtkPolyDataMapper->ScalarVisibilityOn(); ls->m_VtkPolyDataMapper->SetScalarModeToUseCellData(); ls->m_Actor->GetProperty()->SetSpecular(1); ls->m_Actor->GetProperty()->SetSpecularPower(50); ls->m_Actor->GetProperty()->SetInterpolationToPhong(); } else if (deprecatedUsePointData) { float scalarsMin = 0; if (dynamic_cast(this->GetDataNode()->GetProperty("ScalarsRangeMinimum")) != nullptr) scalarsMin = dynamic_cast(this->GetDataNode()->GetProperty("ScalarsRangeMinimum"))->GetValue(); float scalarsMax = 0.1; if (dynamic_cast(this->GetDataNode()->GetProperty("ScalarsRangeMaximum")) != nullptr) scalarsMax = dynamic_cast(this->GetDataNode()->GetProperty("ScalarsRangeMaximum"))->GetValue(); ls->m_VtkPolyDataMapper->SetScalarRange(scalarsMin, scalarsMax); ls->m_VtkPolyDataMapper->SetColorModeToMapScalars(); ls->m_VtkPolyDataMapper->ScalarVisibilityOn(); ls->m_Actor->GetProperty()->SetSpecular(1); ls->m_Actor->GetProperty()->SetSpecularPower(50); ls->m_Actor->GetProperty()->SetInterpolationToPhong(); } int deprecatedScalarMode = VTK_COLOR_MODE_DEFAULT; if (this->GetDataNode()->GetIntProperty("deprecated scalar mode", deprecatedScalarMode, renderer)) { ls->m_VtkPolyDataMapper->SetScalarMode(deprecatedScalarMode); ls->m_VtkPolyDataMapper->ScalarVisibilityOn(); ls->m_Actor->GetProperty()->SetSpecular(1); ls->m_Actor->GetProperty()->SetSpecularPower(50); } // Check whether one or more ClippingProperty objects have been defined for // this node. Check both renderer specific and global property lists, since // properties in both should be considered. const PropertyList::PropertyMap *rendererProperties = this->GetDataNode()->GetPropertyList(renderer)->GetMap(); const PropertyList::PropertyMap *globalProperties = this->GetDataNode()->GetPropertyList(nullptr)->GetMap(); // Add clipping planes (if any) ls->m_ClippingPlaneCollection->RemoveAllItems(); PropertyList::PropertyMap::const_iterator it; for (it = rendererProperties->begin(); it != rendererProperties->end(); ++it) { this->CheckForClippingProperty(renderer, (*it).second.GetPointer()); } for (it = globalProperties->begin(); it != globalProperties->end(); ++it) { this->CheckForClippingProperty(renderer, (*it).second.GetPointer()); } if (ls->m_ClippingPlaneCollection->GetNumberOfItems() > 0) { ls->m_VtkPolyDataMapper->SetClippingPlanes(ls->m_ClippingPlaneCollection); } else { ls->m_VtkPolyDataMapper->RemoveAllClippingPlanes(); } } vtkProp *mitk::SurfaceVtkMapper3D::GetVtkProp(mitk::BaseRenderer *renderer) { LocalStorage *ls = m_LSH.GetLocalStorage(renderer); return ls->m_Actor; } void mitk::SurfaceVtkMapper3D::CheckForClippingProperty(mitk::BaseRenderer *renderer, mitk::BaseProperty *property) { LocalStorage *ls = m_LSH.GetLocalStorage(renderer); - ClippingProperty *clippingProperty = dynamic_cast(property); + auto *clippingProperty = dynamic_cast(property); if ((clippingProperty != nullptr) && (clippingProperty->GetClippingEnabled())) { const Point3D &origin = clippingProperty->GetOrigin(); const Vector3D &normal = clippingProperty->GetNormal(); vtkSmartPointer clippingPlane = vtkSmartPointer::New(); clippingPlane->SetOrigin(origin[0], origin[1], origin[2]); clippingPlane->SetNormal(normal[0], normal[1], normal[2]); ls->m_ClippingPlaneCollection->AddItem(clippingPlane); } } void mitk::SurfaceVtkMapper3D::SetDefaultPropertiesForVtkProperty(mitk::DataNode *node, mitk::BaseRenderer *renderer, bool overwrite) { // Shading { node->AddProperty("material.wireframeLineWidth", mitk::FloatProperty::New(1.0f), renderer, overwrite); node->AddProperty("material.pointSize", mitk::FloatProperty::New(1.0f), renderer, overwrite); node->AddProperty("material.ambientCoefficient", mitk::FloatProperty::New(0.05f), renderer, overwrite); node->AddProperty("material.diffuseCoefficient", mitk::FloatProperty::New(0.9f), renderer, overwrite); node->AddProperty("material.specularCoefficient", mitk::FloatProperty::New(1.0f), renderer, overwrite); node->AddProperty("material.specularPower", mitk::FloatProperty::New(16.0f), renderer, overwrite); node->AddProperty("material.representation", mitk::VtkRepresentationProperty::New(), renderer, overwrite); node->AddProperty("material.interpolation", mitk::VtkInterpolationProperty::New(), renderer, overwrite); } } void mitk::SurfaceVtkMapper3D::SetDefaultProperties(mitk::DataNode *node, mitk::BaseRenderer *renderer, bool overwrite) { node->AddProperty("color", mitk::ColorProperty::New(1.0f, 1.0f, 1.0f), renderer, overwrite); node->AddProperty("opacity", mitk::FloatProperty::New(1.0), renderer, overwrite); mitk::SurfaceVtkMapper3D::SetDefaultPropertiesForVtkProperty(node, renderer, overwrite); // Shading node->AddProperty("scalar visibility", mitk::BoolProperty::New(false), renderer, overwrite); node->AddProperty("color mode", mitk::BoolProperty::New(false), renderer, overwrite); node->AddProperty("scalar mode", mitk::VtkScalarModeProperty::New(), renderer, overwrite); mitk::Surface::Pointer surface = dynamic_cast(node->GetData()); if (surface.IsNotNull()) { if ((surface->GetVtkPolyData() != nullptr) && (surface->GetVtkPolyData()->GetPointData() != nullptr) && (surface->GetVtkPolyData()->GetPointData()->GetScalars() != nullptr)) { node->AddProperty("scalar visibility", mitk::BoolProperty::New(true), renderer, overwrite); node->AddProperty("color mode", mitk::BoolProperty::New(true), renderer, overwrite); } } // Backface culling node->AddProperty("Backface Culling", mitk::BoolProperty::New(false), renderer, overwrite); node->AddProperty("Depth Sorting", mitk::BoolProperty::New(false), renderer, overwrite); mitk::CoreServices::GetPropertyDescriptions()->AddDescription( "Depth Sorting", "Enables correct rendering for transparent objects by ordering polygons according to the distance " "to the camera. It is not recommended to enable this property for large surfaces (rendering might " "be slow)."); Superclass::SetDefaultProperties(node, renderer, overwrite); } diff --git a/Modules/Core/src/Rendering/mitkVtkEventProvider.cpp b/Modules/Core/src/Rendering/mitkVtkEventProvider.cpp index 3ece9ccc4e..60d9692b9b 100644 --- a/Modules/Core/src/Rendering/mitkVtkEventProvider.cpp +++ b/Modules/Core/src/Rendering/mitkVtkEventProvider.cpp @@ -1,244 +1,244 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkVtkEventProvider.h" #include "mitkVtkEventAdapter.h" #include #include #include #include #include #include "mitkInteractionEvent.h" #define VTKEVENTPROVIDER_INFO MBI_INFO("mitk.core.vtkeventprovider") #define VTKEVENTPROVIDER_WARN MBI_WARN("mitk.core.vtkeventprovider") #define VTKEVENTPROVIDER_ERROR MBI_ERROR("mitk.core.vtkeventprovider") #define VTKEVENTPROVIDER_DEBUG MBI_DEBUG("mitk.core.vtkeventprovider") namespace mitk { vtkStandardNewMacro(vtkEventProvider); } //---------------------------------------------------------------------------- mitk::vtkEventProvider::vtkEventProvider() { // priority of the observer/command; we want MITK events processed in the very beginning this->Priority = 99999.99; // take over the processing of delete and keypress events from the superclass this->EventCallbackCommand->SetCallback(vtkEventProvider::ProcessEvents); // Set/Get the passive observer flag. If this is set to true, this // indicates that this command does not change the state of the // system in any way. Passive observers are processed first, and // are not called even when another command has focus. this->EventCallbackCommand->SetPassiveObserver(1); // get events first // mouse move AddInteractionEvent(vtkCommand::MouseMoveEvent); // mouse press AddInteractionEvent(vtkCommand::LeftButtonPressEvent); AddInteractionEvent(vtkCommand::MiddleButtonPressEvent); AddInteractionEvent(vtkCommand::RightButtonPressEvent); // mouse release AddInteractionEvent(vtkCommand::LeftButtonReleaseEvent); AddInteractionEvent(vtkCommand::MiddleButtonReleaseEvent); AddInteractionEvent(vtkCommand::RightButtonReleaseEvent); // wheel event AddInteractionEvent(vtkCommand::MouseWheelBackwardEvent); AddInteractionEvent(vtkCommand::MouseWheelForwardEvent); // key press event AddInteractionEvent(vtkCommand::KeyPressEvent); } mitk::vtkEventProvider::~vtkEventProvider() { this->SetInteractor(nullptr); } void mitk::vtkEventProvider::SetMitkRenderWindow(mitk::RenderWindow *renWin) { m_RenderWindow = renWin; } mitk::RenderWindow *mitk::vtkEventProvider::GetRenderWindow() { return m_RenderWindow; } void mitk::vtkEventProvider::SetEnabled(int enabling) { if (!this->Interactor) { VTKEVENTPROVIDER_ERROR << "The interactor must be set prior to enabling/disabling widget"; return; } if (enabling) //---------------------------------------------------------- { VTKEVENTPROVIDER_DEBUG << "Enabling widget"; if (this->Enabled) // already enabled, just return { return; } this->Enabled = 1; // listen to all event types specified in m_InteractionEventsVector vtkRenderWindowInteractor *i = this->Interactor; InteractionEventsVectorType::iterator it; for (it = m_InteractionEventsVector.begin(); it != m_InteractionEventsVector.end(); ++it) { // add observer to interactorStyle i->GetInteractorStyle()->AddObserver((vtkCommand::EventIds)(*it), this->EventCallbackCommand, this->Priority); } this->InvokeEvent(vtkCommand::EnableEvent, nullptr); } else // disabling----------------------------------------------------------- { VTKEVENTPROVIDER_DEBUG << "Disabling widget"; if (!this->Enabled) // already disabled, just return { return; } this->Enabled = 0; // don't listen for events any more this->Interactor->RemoveObserver(this->EventCallbackCommand); // this->Interactor->HandleEventLoop = 0; this->InvokeEvent(vtkCommand::DisableEvent, nullptr); } } //---------------------------------------------------------------------------- // This adds the keypress event observer and the delete event observer void mitk::vtkEventProvider::SetInteractor(vtkRenderWindowInteractor *i) { if (i == this->Interactor) { return; } // if we already have an Interactor then stop observing it if (this->Interactor) this->SetEnabled(0); // disable the old interactor this->Interactor = i; this->Modified(); } //---------------------------------------------------------------------------- void mitk::vtkEventProvider::ProcessEvents(vtkObject *object, unsigned long event, void *clientData, void *vtkNotUsed(callData)) { - vtkEventProvider *self = reinterpret_cast(clientData); + auto *self = reinterpret_cast(clientData); vtkRenderWindowInteractor *rwi = static_cast(object)->GetInteractor(); // base renderer mitk::BaseRenderer *baseRenderer = mitk::BaseRenderer::GetInstance(self->GetRenderWindow()->GetVtkRenderWindow()); switch (event) { // key press case vtkCommand::KeyPressEvent: { VTKEVENTPROVIDER_DEBUG << "key press event"; InteractionEvent::Pointer adaptedEvent = VtkEventAdapter::AdaptInteractionKeyEvent(baseRenderer, event, rwi).GetPointer(); self->GetRenderWindow()->HandleEvent(adaptedEvent.GetPointer()); break; } // mouse events case vtkCommand::MouseMoveEvent: { VTKEVENTPROVIDER_DEBUG << "mouse move event"; InteractionEvent::Pointer adaptedEvent = VtkEventAdapter::AdaptMouseMoveEvent(baseRenderer, event, rwi).GetPointer(); self->GetRenderWindow()->HandleEvent(adaptedEvent.GetPointer()); break; } case vtkCommand::LeftButtonPressEvent: case vtkCommand::MiddleButtonPressEvent: case vtkCommand::RightButtonPressEvent: { VTKEVENTPROVIDER_DEBUG << "mouse press event"; InteractionEvent::Pointer adaptedEvent = VtkEventAdapter::AdaptMousePressEvent(baseRenderer, event, rwi).GetPointer(); self->GetRenderWindow()->HandleEvent(adaptedEvent.GetPointer()); break; } case vtkCommand::LeftButtonReleaseEvent: case vtkCommand::MiddleButtonReleaseEvent: case vtkCommand::RightButtonReleaseEvent: { VTKEVENTPROVIDER_DEBUG << "mouse release event"; InteractionEvent::Pointer adaptedEvent = VtkEventAdapter::AdaptMouseReleaseEvent(baseRenderer, event, rwi).GetPointer(); self->GetRenderWindow()->HandleEvent(adaptedEvent.GetPointer()); break; } // mouse WHEEL case vtkCommand::MouseWheelForwardEvent: case vtkCommand::MouseWheelBackwardEvent: { VTKEVENTPROVIDER_DEBUG << "mouse wheel event"; InteractionEvent::Pointer adaptedEvent = VtkEventAdapter::AdaptMouseWheelEvent(baseRenderer, event, rwi).GetPointer(); self->GetRenderWindow()->HandleEvent(adaptedEvent.GetPointer()); break; } default: VTKEVENTPROVIDER_INFO << "VTK event not mapped properly."; break; } } void mitk::vtkEventProvider::RemoveInteractionEvent(unsigned long ievent) { InteractionEventsVectorType::iterator it; if (m_InteractionEventsVector.size() > 0) { it = std::find(m_InteractionEventsVector.begin(), m_InteractionEventsVector.end(), ievent); if (it != m_InteractionEventsVector.end()) { m_InteractionEventsVector.erase(it); return; } } } void mitk::vtkEventProvider::AddInteractionEvent(unsigned long ievent) { // Remove event if it already exists RemoveInteractionEvent(ievent); m_InteractionEventsVector.push_back(ievent); } diff --git a/Modules/Core/src/Rendering/mitkVtkMapper.cpp b/Modules/Core/src/Rendering/mitkVtkMapper.cpp index 6dd8eb2cba..6f5eac9772 100644 --- a/Modules/Core/src/Rendering/mitkVtkMapper.cpp +++ b/Modules/Core/src/Rendering/mitkVtkMapper.cpp @@ -1,140 +1,140 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkVtkMapper.h" mitk::VtkMapper::VtkMapper() { } mitk::VtkMapper::~VtkMapper() { } void mitk::VtkMapper::MitkRender(mitk::BaseRenderer *renderer, mitk::VtkPropRenderer::RenderType type) { switch (type) { case mitk::VtkPropRenderer::Opaque: this->MitkRenderOpaqueGeometry(renderer); break; case mitk::VtkPropRenderer::Translucent: this->MitkRenderTranslucentGeometry(renderer); break; case mitk::VtkPropRenderer::Overlay: this->MitkRenderOverlay(renderer); break; case mitk::VtkPropRenderer::Volumetric: this->MitkRenderVolumetricGeometry(renderer); break; } } bool mitk::VtkMapper::IsVtkBased() const { return true; } void mitk::VtkMapper::MitkRenderOverlay(BaseRenderer *renderer) { bool visible = true; GetDataNode()->GetVisibility(visible, renderer, "visible"); if (!visible) return; if (this->GetVtkProp(renderer)->GetVisibility()) { GetVtkProp(renderer)->RenderOverlay(renderer->GetVtkRenderer()); } } void mitk::VtkMapper::MitkRenderOpaqueGeometry(BaseRenderer *renderer) { bool visible = true; GetDataNode()->GetVisibility(visible, renderer, "visible"); if (!visible) return; if (this->GetVtkProp(renderer)->GetVisibility()) { GetVtkProp(renderer)->RenderOpaqueGeometry(renderer->GetVtkRenderer()); } } void mitk::VtkMapper::MitkRenderTranslucentGeometry(BaseRenderer *renderer) { bool visible = true; GetDataNode()->GetVisibility(visible, renderer, "visible"); if (!visible) return; if (this->GetVtkProp(renderer)->GetVisibility()) { GetVtkProp(renderer)->RenderTranslucentPolygonalGeometry(renderer->GetVtkRenderer()); } } void mitk::VtkMapper::MitkRenderVolumetricGeometry(BaseRenderer *renderer) { bool visible = true; GetDataNode()->GetVisibility(visible, renderer, "visible"); if (!visible) return; if (GetVtkProp(renderer)->GetVisibility()) { GetVtkProp(renderer)->RenderVolumetricGeometry(renderer->GetVtkRenderer()); } } bool mitk::VtkMapper::HasVtkProp(const vtkProp *prop, BaseRenderer *renderer) { vtkProp *myProp = this->GetVtkProp(renderer); // TODO: check if myProp is a vtkAssembly and if so, check if prop is contained in its leafs return (prop == myProp); } void mitk::VtkMapper::SetVtkMapperImmediateModeRendering(vtkMapper *mapper) { if (mapper) mapper->SetImmediateModeRendering(mitk::VtkPropRenderer::useImmediateModeRendering()); } void mitk::VtkMapper::UpdateVtkTransform(mitk::BaseRenderer *renderer) { vtkLinearTransform *vtktransform = GetDataNode()->GetVtkTransform(this->GetTimestep()); - vtkProp3D *prop = dynamic_cast(GetVtkProp(renderer)); + auto *prop = dynamic_cast(GetVtkProp(renderer)); if (prop) prop->SetUserTransform(vtktransform); } void mitk::VtkMapper::ApplyColorAndOpacityProperties(BaseRenderer *renderer, vtkActor *actor) { float rgba[4] = {1.0f, 1.0f, 1.0f, 1.0f}; DataNode *node = GetDataNode(); // check for color prop and use it for rendering if it exists node->GetColor(rgba, renderer, "color"); // check for opacity prop and use it for rendering if it exists node->GetOpacity(rgba[3], renderer, "opacity"); double drgba[4] = {rgba[0], rgba[1], rgba[2], rgba[3]}; actor->GetProperty()->SetColor(drgba); actor->GetProperty()->SetOpacity(drgba[3]); } diff --git a/Modules/Core/src/Rendering/mitkVtkPropRenderer.cpp b/Modules/Core/src/Rendering/mitkVtkPropRenderer.cpp index 15d12c26fb..e1e62686a8 100644 --- a/Modules/Core/src/Rendering/mitkVtkPropRenderer.cpp +++ b/Modules/Core/src/Rendering/mitkVtkPropRenderer.cpp @@ -1,680 +1,680 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkVtkPropRenderer.h" // MAPPERS #include "mitkCameraController.h" #include "mitkImageVtkMapper2D.h" #include "mitkMapper.h" #include "mitkPlaneGeometryDataVtkMapper3D.h" #include "mitkVtkMapper.h" #include #include #include #include #include #include #include #include #include #include // VTK #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include mitk::VtkPropRenderer::VtkPropRenderer(const char *name, vtkRenderWindow *renWin, mitk::RenderingManager *rm, mitk::BaseRenderer::RenderingMode::Type renderingMode) : BaseRenderer(name, renWin, rm, renderingMode), m_CameraInitializedForMapperID(0) { didCount = false; m_WorldPointPicker = vtkWorldPointPicker::New(); m_PointPicker = vtkPointPicker::New(); m_PointPicker->SetTolerance(0.0025); m_CellPicker = vtkCellPicker::New(); m_CellPicker->SetTolerance(0.0025); mitk::PlaneGeometryDataVtkMapper3D::Pointer geometryMapper = mitk::PlaneGeometryDataVtkMapper3D::New(); m_CurrentWorldPlaneGeometryMapper = geometryMapper; m_CurrentWorldPlaneGeometryNode->SetMapper(2, geometryMapper); m_LightKit = vtkLightKit::New(); m_LightKit->AddLightsToRenderer(m_VtkRenderer); m_PickingMode = WorldPointPicking; m_TextRenderer = vtkRenderer::New(); m_TextRenderer->SetRenderWindow(renWin); m_TextRenderer->SetInteractive(0); m_TextRenderer->SetErase(0); } /*! \brief Destructs the VtkPropRenderer. */ mitk::VtkPropRenderer::~VtkPropRenderer() { // Workaround for GLDisplayList Bug { m_MapperID = 0; checkState(); } if (m_LightKit != nullptr) m_LightKit->Delete(); if (m_VtkRenderer != nullptr) { m_CameraController = nullptr; m_VtkRenderer->Delete(); m_VtkRenderer = nullptr; } else m_CameraController = nullptr; if (m_WorldPointPicker != nullptr) m_WorldPointPicker->Delete(); if (m_PointPicker != nullptr) m_PointPicker->Delete(); if (m_CellPicker != nullptr) m_CellPicker->Delete(); if (m_TextRenderer != nullptr) m_TextRenderer->Delete(); } void mitk::VtkPropRenderer::SetDataStorage(mitk::DataStorage *storage) { if (storage == nullptr || storage == m_DataStorage) return; BaseRenderer::SetDataStorage(storage); static_cast(m_CurrentWorldPlaneGeometryMapper.GetPointer()) ->SetDataStorageForTexture(m_DataStorage.GetPointer()); // Compute the geometry from the current data tree bounds and set it as world geometry this->SetWorldGeometryToDataStorageBounds(); } bool mitk::VtkPropRenderer::SetWorldGeometryToDataStorageBounds() { if (m_DataStorage.IsNull()) return false; // initialize world geometry mitk::TimeGeometry::Pointer geometry = m_DataStorage->ComputeVisibleBoundingGeometry3D(nullptr, "includeInBoundingBox"); if (geometry.IsNull()) return false; this->SetWorldTimeGeometry(geometry); this->GetVtkRenderer()->ResetCamera(); this->GetCameraController()->Fit(); this->Modified(); return true; } /*! \brief Called by the vtkMitkRenderProp in order to start MITK rendering process. */ int mitk::VtkPropRenderer::Render(mitk::VtkPropRenderer::RenderType type) { // Do we have objects to render? if (this->GetEmptyWorldGeometry()) return 0; if (m_DataStorage.IsNull()) return 0; // Update mappers and prepare mapper queue if (type == VtkPropRenderer::Opaque) this->PrepareMapperQueue(); // go through the generated list and let the sorted mappers paint for (auto it = m_MappersMap.cbegin(); it != m_MappersMap.cend(); it++) { Mapper *mapper = (*it).second; mapper->MitkRender(this, type); } // Render text if (type == VtkPropRenderer::Overlay) { if (m_TextCollection.size() > 0) { m_TextRenderer->SetViewport(this->GetVtkRenderer()->GetViewport()); - for (TextMapType::iterator it = m_TextCollection.begin(); it != m_TextCollection.end(); ++it) + for (auto it = m_TextCollection.begin(); it != m_TextCollection.end(); ++it) m_TextRenderer->AddViewProp((*it).second); m_TextRenderer->Render(); } } return 1; } /*! \brief PrepareMapperQueue iterates the datatree PrepareMapperQueue iterates the datatree in order to find mappers which shall be rendered. Also, it sortes the mappers wrt to their layer. */ void mitk::VtkPropRenderer::PrepareMapperQueue() { // variable for counting LOD-enabled mappers m_NumberOfVisibleLODEnabledMappers = 0; // Do we have to update the mappers ? if (m_LastUpdateTime < GetMTime() || m_LastUpdateTime < this->GetCurrentWorldPlaneGeometry()->GetMTime()) { Update(); } else if (m_MapperID >= 1 && m_MapperID < 6) Update(); // remove all text properties before mappers will add new ones m_TextRenderer->RemoveAllViewProps(); for (unsigned int i = 0; i < m_TextCollection.size(); i++) { m_TextCollection[i]->Delete(); } m_TextCollection.clear(); // clear priority_queue m_MappersMap.clear(); int mapperNo = 0; // DataStorage if (m_DataStorage.IsNull()) return; DataStorage::SetOfObjects::ConstPointer allObjects = m_DataStorage->GetAll(); for (DataStorage::SetOfObjects::ConstIterator it = allObjects->Begin(); it != allObjects->End(); ++it) { const DataNode::Pointer node = it->Value(); if (node.IsNull()) continue; const mitk::Mapper::Pointer mapper = node->GetMapper(m_MapperID); if (mapper.IsNull()) continue; bool visible = true; node->GetVisibility(visible, this, "visible"); // The information about LOD-enabled mappers is required by RenderingManager if (mapper->IsLODEnabled(this) && visible) { ++m_NumberOfVisibleLODEnabledMappers; } // mapper without a layer property get layer number 1 int layer = 1; node->GetIntProperty("layer", layer, this); int nr = (layer << 16) + mapperNo; m_MappersMap.insert(std::pair(nr, mapper)); mapperNo++; } } void mitk::VtkPropRenderer::Update(mitk::DataNode *datatreenode) { if (datatreenode != nullptr) { mitk::Mapper::Pointer mapper = datatreenode->GetMapper(m_MapperID); if (mapper.IsNotNull()) { if (GetCurrentWorldPlaneGeometry()->IsValid()) { mapper->Update(this); { - VtkMapper *vtkmapper = dynamic_cast(mapper.GetPointer()); + auto *vtkmapper = dynamic_cast(mapper.GetPointer()); if (vtkmapper != nullptr) { vtkmapper->UpdateVtkTransform(this); } } } } } } void mitk::VtkPropRenderer::Update() { if (m_DataStorage.IsNull()) return; mitk::DataStorage::SetOfObjects::ConstPointer all = m_DataStorage->GetAll(); for (mitk::DataStorage::SetOfObjects::ConstIterator it = all->Begin(); it != all->End(); ++it) Update(it->Value()); Modified(); m_LastUpdateTime = GetMTime(); } /*! \brief This method is called from the two Constructors */ void mitk::VtkPropRenderer::InitRenderer(vtkRenderWindow *renderWindow) { BaseRenderer::InitRenderer(renderWindow); vtkCallbackCommand *renderCallbackCommand = vtkCallbackCommand::New(); renderCallbackCommand->SetCallback(VtkPropRenderer::RenderingCallback); renderWindow->GetInteractor()->AddObserver(vtkCommand::RenderEvent, renderCallbackCommand); renderCallbackCommand->Delete(); if (renderWindow == nullptr) { m_InitNeeded = false; m_ResizeNeeded = false; return; } m_InitNeeded = true; m_ResizeNeeded = true; m_LastUpdateTime = 0; } void mitk::VtkPropRenderer::RenderingCallback(vtkObject *caller, unsigned long, void *, void *) { - vtkRenderWindowInteractor *renderWindowInteractor = dynamic_cast(caller); + auto *renderWindowInteractor = dynamic_cast(caller); if (!renderWindowInteractor) return; mitk::BaseRenderer *renderer = mitk::BaseRenderer::GetInstance(renderWindowInteractor->GetRenderWindow()); if (renderer) renderer->RequestUpdate(); } /*! \brief Resize the OpenGL Window */ void mitk::VtkPropRenderer::Resize(int w, int h) { BaseRenderer::Resize(w, h); m_RenderingManager->RequestUpdate(this->GetRenderWindow()); } void mitk::VtkPropRenderer::InitSize(int w, int h) { m_RenderWindow->SetSize(w, h); Superclass::InitSize(w, h); Modified(); Update(); if (m_VtkRenderer != nullptr) { int w = vtkObject::GetGlobalWarningDisplay(); vtkObject::GlobalWarningDisplayOff(); m_VtkRenderer->ResetCamera(); vtkObject::SetGlobalWarningDisplay(w); } this->GetCameraController()->Fit(); } int mitk::VtkPropRenderer::WriteSimpleText( std::string text, double posX, double posY, double color1, double color2, double color3, float opacity) { this->GetVtkRenderer()->ViewToDisplay(); if (!text.empty()) { Point2D p; vtkTextActor *textActor = vtkTextActor::New(); textActor->SetDisplayPosition(posX, posY); textActor->SetInput(text.c_str()); textActor->SetTextScaleModeToNone(); textActor->GetTextProperty()->SetColor(color1, color2, color3); // TODO: Read color from node property textActor->GetTextProperty()->SetOpacity(opacity); int text_id = m_TextCollection.size(); m_TextCollection.insert(TextMapType::value_type(text_id, textActor)); return text_id; } else { return -1; } } void mitk::VtkPropRenderer::SetMapperID(const MapperSlotId mapperId) { if (m_MapperID != mapperId) Superclass::SetMapperID(mapperId); // Workaround for GL Displaylist Bug checkState(); } /*! \brief Activates the current renderwindow. */ void mitk::VtkPropRenderer::MakeCurrent() { if (m_RenderWindow != nullptr) m_RenderWindow->MakeCurrent(); } void mitk::VtkPropRenderer::PickWorldPoint(const mitk::Point2D &displayPoint, mitk::Point3D &worldPoint) const { if (this->GetRenderWindow()->GetNeverRendered() != 0) return; // somebody called picking before we ever rendered; cannot have enough information yet switch (m_PickingMode) { case (WorldPointPicking): { m_WorldPointPicker->Pick(displayPoint[0], displayPoint[1], 0, m_VtkRenderer); vtk2itk(m_WorldPointPicker->GetPickPosition(), worldPoint); break; } case (PointPicking): { m_PointPicker->Pick(displayPoint[0], displayPoint[1], 0, m_VtkRenderer); vtk2itk(m_PointPicker->GetPickPosition(), worldPoint); break; } case (CellPicking): { m_CellPicker->Pick(displayPoint[0], displayPoint[1], 0, m_VtkRenderer); vtk2itk(m_CellPicker->GetPickPosition(), worldPoint); break; } } // todo: is this picking in 2D renderwindows? // Superclass::PickWorldPoint(displayPoint, worldPoint); } mitk::DataNode *mitk::VtkPropRenderer::PickObject(const Point2D &displayPosition, Point3D &worldPosition) const { m_CellPicker->InitializePickList(); // Iterate over all DataStorage objects to determine all vtkProps intended // for picking DataStorage::SetOfObjects::ConstPointer allObjects = m_DataStorage->GetAll(); for (DataStorage::SetOfObjects::ConstIterator it = allObjects->Begin(); it != allObjects->End(); ++it) { const DataNode *node = it->Value(); if (node == nullptr) continue; bool pickable = false; node->GetBoolProperty("pickable", pickable); if (!pickable) continue; - VtkMapper *mapper = dynamic_cast(node->GetMapper(m_MapperID)); + auto *mapper = dynamic_cast(node->GetMapper(m_MapperID)); if (mapper == nullptr) continue; vtkProp *prop = mapper->GetVtkProp((mitk::BaseRenderer *)this); if (prop == nullptr) continue; m_CellPicker->AddPickList(prop); } // Do the picking and retrieve the picked vtkProp (if any) m_CellPicker->PickFromListOn(); m_CellPicker->Pick(displayPosition[0], displayPosition[1], 0.0, m_VtkRenderer); m_CellPicker->PickFromListOff(); vtk2itk(m_CellPicker->GetPickPosition(), worldPosition); vtkProp *prop = m_CellPicker->GetViewProp(); if (prop == nullptr) { return nullptr; } // Iterate over all DataStorage objects to determine if the retrieved // vtkProp is owned by any associated mapper. for (DataStorage::SetOfObjects::ConstIterator it = allObjects->Begin(); it != allObjects->End(); ++it) { DataNode::Pointer node = it->Value(); if (node.IsNull()) continue; mitk::Mapper *mapper = node->GetMapper(m_MapperID); if (mapper == nullptr) continue; - mitk::VtkMapper *vtkmapper = dynamic_cast(mapper); + auto *vtkmapper = dynamic_cast(mapper); if (vtkmapper) { // if vtk-based, then ... if (vtkmapper->HasVtkProp(prop, const_cast(this))) { return node; } } } return nullptr; } // todo: is this 2D renderwindow picking? // return Superclass::PickObject( displayPosition, worldPosition ); vtkTextProperty *mitk::VtkPropRenderer::GetTextLabelProperty(int text_id) { return this->m_TextCollection[text_id]->GetTextProperty(); } void mitk::VtkPropRenderer::InitPathTraversal() { if (m_DataStorage.IsNotNull()) { this->UpdatePaths(); this->m_Paths->InitTraversal(); } } void mitk::VtkPropRenderer::UpdatePaths() { if (m_DataStorage.IsNull()) { return; } if (GetMTime() > m_PathTime || (m_Paths != nullptr && m_Paths->GetMTime() > m_PathTime)) { // Create the list to hold all the paths m_Paths = vtkSmartPointer::New(); DataStorage::SetOfObjects::ConstPointer objects = m_DataStorage->GetAll(); - for (DataStorage::SetOfObjects::const_iterator iter = objects->begin(); iter != objects->end(); ++iter) + for (auto iter = objects->begin(); iter != objects->end(); ++iter) { vtkSmartPointer onePath = vtkSmartPointer::New(); Mapper *mapper = (*iter)->GetMapper(BaseRenderer::Standard3D); if (mapper) { - VtkMapper *vtkmapper = dynamic_cast(mapper); + auto *vtkmapper = dynamic_cast(mapper); { vtkProp *prop = vtkmapper->GetVtkProp(this); if (prop && prop->GetVisibility()) { // add to assembly path onePath->AddNode(prop, prop->GetMatrix()); m_Paths->AddItem(onePath); } } } } m_PathTime.Modified(); } } int mitk::VtkPropRenderer::GetNumberOfPaths() { UpdatePaths(); return m_Paths->GetNumberOfItems(); } vtkAssemblyPath *mitk::VtkPropRenderer::GetNextPath() { return m_Paths ? m_Paths->GetNextItem() : nullptr; } void mitk::VtkPropRenderer::ReleaseGraphicsResources(vtkWindow * /*renWin*/) { if (m_DataStorage.IsNull()) return; DataStorage::SetOfObjects::ConstPointer allObjects = m_DataStorage->GetAll(); - for (DataStorage::SetOfObjects::const_iterator iter = allObjects->begin(); iter != allObjects->end(); ++iter) + for (auto iter = allObjects->begin(); iter != allObjects->end(); ++iter) { DataNode::Pointer node = *iter; if (node.IsNull()) continue; Mapper *mapper = node->GetMapper(m_MapperID); if (mapper) { - VtkMapper *vtkmapper = dynamic_cast(mapper); + auto *vtkmapper = dynamic_cast(mapper); if (vtkmapper) vtkmapper->ReleaseGraphicsResources(this); } } } const vtkWorldPointPicker *mitk::VtkPropRenderer::GetWorldPointPicker() const { return m_WorldPointPicker; } const vtkPointPicker *mitk::VtkPropRenderer::GetPointPicker() const { return m_PointPicker; } const vtkCellPicker *mitk::VtkPropRenderer::GetCellPicker() const { return m_CellPicker; } mitk::VtkPropRenderer::MappersMapType mitk::VtkPropRenderer::GetMappersMap() const { return m_MappersMap; } // Workaround for GL Displaylist bug static int glWorkAroundGlobalCount = 0; bool mitk::VtkPropRenderer::useImmediateModeRendering() { return glWorkAroundGlobalCount > 1; } void mitk::VtkPropRenderer::checkState() { if (m_MapperID == Standard3D) { if (!didCount) { didCount = true; glWorkAroundGlobalCount++; if (glWorkAroundGlobalCount == 2) { MITK_INFO << "Multiple 3D Renderwindows active...: turning Immediate Rendering ON for legacy mappers"; // vtkMapper::GlobalImmediateModeRenderingOn(); } } } else { if (didCount) { didCount = false; glWorkAroundGlobalCount--; if (glWorkAroundGlobalCount == 1) { MITK_INFO << "Single 3D Renderwindow active...: turning Immediate Rendering OFF for legacy mappers"; // vtkMapper::GlobalImmediateModeRenderingOff(); } } } } //### Contains all methods which are neceassry before each VTK Render() call void mitk::VtkPropRenderer::PrepareRender() { if (this->GetMapperID() != m_CameraInitializedForMapperID) { Initialize2DvtkCamera(); // Set parallel projection etc. } GetCameraController()->AdjustCameraToPlane(); } bool mitk::VtkPropRenderer::Initialize2DvtkCamera() { if (this->GetMapperID() == Standard3D) { // activate parallel projection for 2D this->GetVtkRenderer()->GetActiveCamera()->SetParallelProjection(false); vtkSmartPointer style = vtkSmartPointer::New(); this->GetRenderWindow()->GetInteractor()->SetInteractorStyle(style); this->GetRenderWindow()->GetInteractor()->EnableRenderOff(); m_CameraInitializedForMapperID = Standard3D; } else if (this->GetMapperID() == Standard2D) { // activate parallel projection for 2D this->GetVtkRenderer()->GetActiveCamera()->SetParallelProjection(true); // turn the light out in the scene in order to render correct grey values. // TODO Implement a property for light in the 2D render windows (in another method) this->GetVtkRenderer()->RemoveAllLights(); vtkSmartPointer style = vtkSmartPointer::New(); this->GetRenderWindow()->GetInteractor()->SetInteractorStyle(style); this->GetRenderWindow()->GetInteractor()->EnableRenderOff(); m_CameraInitializedForMapperID = Standard2D; } return true; } diff --git a/Modules/Core/src/Rendering/vtkMitkLevelWindowFilter.cpp b/Modules/Core/src/Rendering/vtkMitkLevelWindowFilter.cpp index 1fca3768d1..961b52f6ee 100644 --- a/Modules/Core/src/Rendering/vtkMitkLevelWindowFilter.cpp +++ b/Modules/Core/src/Rendering/vtkMitkLevelWindowFilter.cpp @@ -1,589 +1,589 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "vtkMitkLevelWindowFilter.h" #include "vtkObjectFactory.h" #include #include #include #include #include #include #include #include // used for acos etc. #include // used for PI #include #include static const double PI = itk::Math::pi; vtkStandardNewMacro(vtkMitkLevelWindowFilter); vtkMitkLevelWindowFilter::vtkMitkLevelWindowFilter() : m_LookupTable(nullptr), m_OpacityFunction(nullptr), m_MinOpacity(0.0), m_MaxOpacity(255.0) { // MITK_INFO << "mitk level/window filter uses " << GetNumberOfThreads() << " thread(s)"; } vtkMitkLevelWindowFilter::~vtkMitkLevelWindowFilter() { } vtkMTimeType vtkMitkLevelWindowFilter::GetMTime() { vtkMTimeType mTime = this->vtkObject::GetMTime(); vtkMTimeType time; if (this->m_LookupTable != nullptr) { time = this->m_LookupTable->GetMTime(); mTime = (time > mTime ? time : mTime); } return mTime; } void vtkMitkLevelWindowFilter::SetLookupTable(vtkScalarsToColors *lookupTable) { if (m_LookupTable != lookupTable) { m_LookupTable = lookupTable; this->Modified(); } } vtkScalarsToColors *vtkMitkLevelWindowFilter::GetLookupTable() { return m_LookupTable; } void vtkMitkLevelWindowFilter::SetOpacityPiecewiseFunction(vtkPiecewiseFunction *opacityFunction) { if (m_OpacityFunction != opacityFunction) { m_OpacityFunction = opacityFunction; this->Modified(); } } // This code was copied from the iil. The template works only for float and double. // Internal method which should never be used anywhere else and should not be in th header. // Convert color pixels from (R,G,B) to (H,S,I). // Reference: "Digital Image Processing, 2nd. edition", R. Gonzalez and R. Woods. Prentice Hall, 2002. template void RGBtoHSI(T *RGB, T *HSI) { T R = RGB[0], G = RGB[1], B = RGB[2], nR = (R < 0 ? 0 : (R > 255 ? 255 : R)) / 255, nG = (G < 0 ? 0 : (G > 255 ? 255 : G)) / 255, nB = (B < 0 ? 0 : (B > 255 ? 255 : B)) / 255, m = nR < nG ? (nR < nB ? nR : nB) : (nG < nB ? nG : nB), theta = (T)(std::acos(0.5f * ((nR - nG) + (nR - nB)) / std::sqrt(std::pow(nR - nG, 2) + (nR - nB) * (nG - nB))) * 180 / PI), sum = nR + nG + nB; T H = 0, S = 0, I = 0; if (theta > 0) H = (nB <= nG) ? theta : 360 - theta; if (sum > 0) S = 1 - 3 / sum * m; I = sum / 3; HSI[0] = (T)H; HSI[1] = (T)S; HSI[2] = (T)I; } // This code was copied from the iil. The template works only for float and double. // Internal method which should never be used anywhere else and should not be in th header. // Convert color pixels from (H,S,I) to (R,G,B). template void HSItoRGB(T *HSI, T *RGB) { T H = (T)HSI[0], S = (T)HSI[1], I = (T)HSI[2], a = I * (1 - S), R = 0, G = 0, B = 0; if (H < 120) { B = a; R = (T)(I * (1 + S * std::cos(H * PI / 180) / std::cos((60 - H) * PI / 180))); G = 3 * I - (R + B); } else if (H < 240) { H -= 120; R = a; G = (T)(I * (1 + S * std::cos(H * PI / 180) / std::cos((60 - H) * PI / 180))); B = 3 * I - (R + G); } else { H -= 240; G = a; B = (T)(I * (1 + S * std::cos(H * PI / 180) / std::cos((60 - H) * PI / 180))); R = 3 * I - (G + B); } R *= 255; G *= 255; B *= 255; RGB[0] = (T)(R < 0 ? 0 : (R > 255 ? 255 : R)); RGB[1] = (T)(G < 0 ? 0 : (G > 255 ? 255 : G)); RGB[2] = (T)(B < 0 ? 0 : (B > 255 ? 255 : B)); } // Internal method which should never be used anywhere else and should not be in th header. //---------------------------------------------------------------------------- // This templated function executes the filter for any type of data. template void vtkApplyLookupTableOnRGBA(vtkMitkLevelWindowFilter *self, vtkImageData *inData, vtkImageData *outData, int outExt[6], double *clippingBounds, T *) { vtkImageIterator inputIt(inData, outExt); vtkImageIterator outputIt(outData, outExt); vtkLookupTable *lookupTable; const int maxC = inData->GetNumberOfScalarComponents(); double tableRange[2]; lookupTable = dynamic_cast(self->GetLookupTable()); lookupTable->GetTableRange(tableRange); // parameters for RGB level window const double scale = (tableRange[1] - tableRange[0] > 0 ? 255.0 / (tableRange[1] - tableRange[0]) : 0.0); const double bias = tableRange[0] * scale; // parameters for opaque level window const double scaleOpac = (self->GetMaxOpacity() - self->GetMinOpacity() > 0 ? 255.0 / (self->GetMaxOpacity() - self->GetMinOpacity()) : 0.0); const double biasOpac = self->GetMinOpacity() * scaleOpac; int y = outExt[2]; // Loop through ouput pixels while (!outputIt.IsAtEnd()) { T *inputSI = inputIt.BeginSpan(); T *outputSI = outputIt.BeginSpan(); T *outputSIEnd = outputIt.EndSpan(); if (y >= clippingBounds[2] && y < clippingBounds[3]) { int x = outExt[0]; while (outputSI != outputSIEnd) { if (x >= clippingBounds[0] && x < clippingBounds[1]) { double rgb[3], alpha, hsi[3]; // level/window mechanism for intensity in HSI space rgb[0] = static_cast(*inputSI); inputSI++; rgb[1] = static_cast(*inputSI); inputSI++; rgb[2] = static_cast(*inputSI); inputSI++; RGBtoHSI(rgb, hsi); hsi[2] = hsi[2] * 255.0 * scale - bias; hsi[2] = (hsi[2] > 255.0 ? 255 : (hsi[2] < 0.0 ? 0 : hsi[2])); hsi[2] /= 255.0; HSItoRGB(hsi, rgb); *outputSI = static_cast(rgb[0]); outputSI++; *outputSI = static_cast(rgb[1]); outputSI++; *outputSI = static_cast(rgb[2]); outputSI++; unsigned char finalAlpha = 255; // RGBA case if (maxC >= 4) { // level/window mechanism for opacity alpha = static_cast(*inputSI); inputSI++; alpha = alpha * scaleOpac - biasOpac; if (alpha > 255.0) { alpha = 255.0; } else if (alpha < 0.0) { alpha = 0.0; } finalAlpha = static_cast(alpha); for (int c = 4; c < maxC; c++) inputSI++; } *outputSI = static_cast(finalAlpha); outputSI++; } else { inputSI += maxC; *outputSI = 0; outputSI++; *outputSI = 0; outputSI++; *outputSI = 0; outputSI++; *outputSI = 0; outputSI++; } x++; } } else { while (outputSI != outputSIEnd) { *outputSI = 0; outputSI++; *outputSI = 0; outputSI++; *outputSI = 0; outputSI++; *outputSI = 0; outputSI++; } } inputIt.NextSpan(); outputIt.NextSpan(); y++; } } // Internal method which should never be used anywhere else and should not be in th header. //---------------------------------------------------------------------------- // This templated function executes the filter for any type of data. template void vtkApplyLookupTableOnScalarsFast( vtkMitkLevelWindowFilter *self, vtkImageData *inData, vtkImageData *outData, int outExt[6], T *) { vtkImageIterator inputIt(inData, outExt); vtkImageIterator outputIt(outData, outExt); double tableRange[2]; // access vtkLookupTable - vtkLookupTable *lookupTable = dynamic_cast(self->GetLookupTable()); + auto *lookupTable = dynamic_cast(self->GetLookupTable()); lookupTable->GetTableRange(tableRange); // access elements of the vtkLookupTable const int *realLookupTable = reinterpret_cast(lookupTable->GetTable()->GetPointer(0)); int maxIndex = lookupTable->GetNumberOfColors() - 1; const float scale = (tableRange[1] - tableRange[0] > 0 ? (maxIndex + 1) / (tableRange[1] - tableRange[0]) : 0.0); // ensuring that starting point is zero float bias = -tableRange[0] * scale; // due to later conversion to int for rounding bias += 0.5f; // Loop through ouput pixels while (!outputIt.IsAtEnd()) { unsigned char *outputSI = outputIt.BeginSpan(); unsigned char *outputSIEnd = outputIt.EndSpan(); T *inputSI = inputIt.BeginSpan(); while (outputSI != outputSIEnd) { // map to an index - int idx = static_cast(*inputSI * scale + bias); + auto idx = static_cast(*inputSI * scale + bias); if (idx < 0) idx = 0; else if (idx > maxIndex) idx = maxIndex; *reinterpret_cast(outputSI) = realLookupTable[idx]; inputSI++; outputSI += 4; } inputIt.NextSpan(); outputIt.NextSpan(); } } // Internal method which should never be used anywhere else and should not be in th header. //---------------------------------------------------------------------------- // This templated function executes the filter for any type of data. template void vtkApplyLookupTableOnScalars(vtkMitkLevelWindowFilter *self, vtkImageData *inData, vtkImageData *outData, int outExt[6], double *clippingBounds, T *) { vtkImageIterator inputIt(inData, outExt); vtkImageIterator outputIt(outData, outExt); vtkScalarsToColors *lookupTable = self->GetLookupTable(); int y = outExt[2]; // Loop through ouput pixels while (!outputIt.IsAtEnd()) { unsigned char *outputSI = outputIt.BeginSpan(); unsigned char *outputSIEnd = outputIt.EndSpan(); // do we iterate over the inner vertical clipping bounds if (y >= clippingBounds[2] && y < clippingBounds[3]) { T *inputSI = inputIt.BeginSpan(); int x = outExt[0]; while (outputSI != outputSIEnd) { // is this pixel within horizontal clipping bounds if (x >= clippingBounds[0] && x < clippingBounds[1]) { // fetching original value - double grayValue = static_cast(*inputSI); + auto grayValue = static_cast(*inputSI); // applying lookuptable - copy the 4 (RGBA) chars as a single int *reinterpret_cast(outputSI) = *reinterpret_cast(lookupTable->MapValue(grayValue)); } else { // outer horizontal clipping bounds - write a transparent RGBA pixel as a single int *reinterpret_cast(outputSI) = 0; } inputSI++; outputSI += 4; x++; } } else { // outer vertical clipping bounds - write a transparent RGBA line as ints while (outputSI != outputSIEnd) { *reinterpret_cast(outputSI) = 0; outputSI += 4; } } inputIt.NextSpan(); outputIt.NextSpan(); y++; } } // Internal method which should never be used anywhere else and should not be in th header. //---------------------------------------------------------------------------- // This templated function executes the filter for any type of data. template void vtkApplyLookupTableOnScalarsCTF(vtkMitkLevelWindowFilter *self, vtkImageData *inData, vtkImageData *outData, int outExt[6], double *clippingBounds, T *) { vtkImageIterator inputIt(inData, outExt); vtkImageIterator outputIt(outData, outExt); - vtkColorTransferFunction *lookupTable = dynamic_cast(self->GetLookupTable()); + auto *lookupTable = dynamic_cast(self->GetLookupTable()); vtkPiecewiseFunction *opacityFunction = self->GetOpacityPiecewiseFunction(); int y = outExt[2]; // Loop through ouput pixels while (!outputIt.IsAtEnd()) { unsigned char *outputSI = outputIt.BeginSpan(); unsigned char *outputSIEnd = outputIt.EndSpan(); // do we iterate over the inner vertical clipping bounds if (y >= clippingBounds[2] && y < clippingBounds[3]) { T *inputSI = inputIt.BeginSpan(); int x = outExt[0]; while (outputSI != outputSIEnd) { // is this pixel within horizontal clipping bounds if (x >= clippingBounds[0] && x < clippingBounds[1]) { // fetching original value - double grayValue = static_cast(*inputSI); + auto grayValue = static_cast(*inputSI); // applying directly colortransferfunction // because vtkColorTransferFunction::MapValue is not threadsafe double rgba[4]; lookupTable->GetColor(grayValue, rgba); // RGB mapping rgba[3] = 1.0; if (opacityFunction) rgba[3] = opacityFunction->GetValue(grayValue); // Alpha mapping for (int i = 0; i < 4; ++i) { outputSI[i] = static_cast(255.0 * rgba[i] + 0.5); } } else { // outer horizontal clipping bounds - write a transparent RGBA pixel as a single int *reinterpret_cast(outputSI) = 0; } inputSI++; outputSI += 4; x++; } } else { // outer vertical clipping bounds - write a transparent RGBA line as ints while (outputSI != outputSIEnd) { *reinterpret_cast(outputSI) = 0; outputSI += 4; } } inputIt.NextSpan(); outputIt.NextSpan(); y++; } } int vtkMitkLevelWindowFilter::RequestInformation(vtkInformation *request, vtkInformationVector **inputVector, vtkInformationVector *outputVector) { vtkInformation *outInfo = outputVector->GetInformationObject(0); // do nothing except copy scalar type info this->CopyInputArrayAttributesToOutput(request, inputVector, outputVector); vtkDataObject::SetPointDataActiveScalarInfo(outInfo, VTK_UNSIGNED_CHAR, 4); return 1; } // Method to run the filter in different threads. void vtkMitkLevelWindowFilter::ThreadedExecute(vtkImageData *inData, vtkImageData *outData, int extent[6], int /*id*/) { if (inData->GetNumberOfScalarComponents() > 2) { switch (inData->GetScalarType()) { vtkTemplateMacro( vtkApplyLookupTableOnRGBA(this, inData, outData, extent, m_ClippingBounds, static_cast(nullptr))); default: vtkErrorMacro(<< "Execute: Unknown ScalarType"); return; } } else { bool dontClip = extent[2] >= m_ClippingBounds[2] && extent[3] <= m_ClippingBounds[3] && extent[0] >= m_ClippingBounds[0] && extent[1] <= m_ClippingBounds[1]; if (this->GetLookupTable()) this->GetLookupTable()->Build(); - vtkLookupTable *vlt = dynamic_cast(this->GetLookupTable()); - vtkColorTransferFunction *ctf = dynamic_cast(this->GetLookupTable()); + auto *vlt = dynamic_cast(this->GetLookupTable()); + auto *ctf = dynamic_cast(this->GetLookupTable()); bool linearLookupTable = vlt && vlt->GetScale() == VTK_SCALE_LINEAR; bool useFast = dontClip && linearLookupTable; if (ctf) { switch (inData->GetScalarType()) { vtkTemplateMacro(vtkApplyLookupTableOnScalarsCTF( this, inData, outData, extent, m_ClippingBounds, static_cast(nullptr))); default: vtkErrorMacro(<< "Execute: Unknown ScalarType"); return; } } else if (useFast) { switch (inData->GetScalarType()) { vtkTemplateMacro( vtkApplyLookupTableOnScalarsFast(this, inData, outData, extent, static_cast(nullptr))); default: vtkErrorMacro(<< "Execute: Unknown ScalarType"); return; } } else { switch (inData->GetScalarType()) { vtkTemplateMacro(vtkApplyLookupTableOnScalars( this, inData, outData, extent, m_ClippingBounds, static_cast(nullptr))); default: vtkErrorMacro(<< "Execute: Unknown ScalarType"); return; } } } } // void vtkMitkLevelWindowFilter::ExecuteInformation( // vtkImageData *vtkNotUsed(inData), vtkImageData *vtkNotUsed(outData)) //{ //} void vtkMitkLevelWindowFilter::SetMinOpacity(double minOpacity) { m_MinOpacity = minOpacity; } inline double vtkMitkLevelWindowFilter::GetMinOpacity() const { return m_MinOpacity; } void vtkMitkLevelWindowFilter::SetMaxOpacity(double maxOpacity) { m_MaxOpacity = maxOpacity; } inline double vtkMitkLevelWindowFilter::GetMaxOpacity() const { return m_MaxOpacity; } void vtkMitkLevelWindowFilter::SetClippingBounds(double *bounds) // TODO does double[4] work?? { for (unsigned int i = 0; i < 4; ++i) m_ClippingBounds[i] = bounds[i]; } diff --git a/Modules/Core/src/Rendering/vtkMitkRenderProp.cpp b/Modules/Core/src/Rendering/vtkMitkRenderProp.cpp index 24b91f6062..2b3933c745 100644 --- a/Modules/Core/src/Rendering/vtkMitkRenderProp.cpp +++ b/Modules/Core/src/Rendering/vtkMitkRenderProp.cpp @@ -1,110 +1,110 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "vtkMitkRenderProp.h" #include #include #include #include "mitkVtkMapper.h" //#include "mitkGLMapper.h" vtkStandardNewMacro(vtkMitkRenderProp); vtkMitkRenderProp::vtkMitkRenderProp() { } vtkMitkRenderProp::~vtkMitkRenderProp() { } double *vtkMitkRenderProp::GetBounds() { return const_cast(m_VtkPropRenderer->GetBounds()); } void vtkMitkRenderProp::SetPropRenderer(mitk::VtkPropRenderer::Pointer propRenderer) { this->m_VtkPropRenderer = propRenderer; } int vtkMitkRenderProp::RenderOpaqueGeometry(vtkViewport * /*viewport*/) { return m_VtkPropRenderer->Render(mitk::VtkPropRenderer::Opaque); } int vtkMitkRenderProp::RenderOverlay(vtkViewport * /*viewport*/) { return m_VtkPropRenderer->Render(mitk::VtkPropRenderer::Overlay); } void vtkMitkRenderProp::ReleaseGraphicsResources(vtkWindow *window) { m_VtkPropRenderer->ReleaseGraphicsResources(window); } void vtkMitkRenderProp::InitPathTraversal() { m_VtkPropRenderer->InitPathTraversal(); } vtkAssemblyPath *vtkMitkRenderProp::GetNextPath() { return m_VtkPropRenderer->GetNextPath(); } int vtkMitkRenderProp::GetNumberOfPaths() { return m_VtkPropRenderer->GetNumberOfPaths(); } // BUG (#1551) added method depth peeling int vtkMitkRenderProp::HasTranslucentPolygonalGeometry() { typedef std::map MappersMapType; const MappersMapType mappersMap = m_VtkPropRenderer->GetMappersMap(); - for (MappersMapType::const_iterator it = mappersMap.cbegin(); it != mappersMap.cend(); ++it) + for (auto it = mappersMap.cbegin(); it != mappersMap.cend(); ++it) { mitk::Mapper *mapper = (*it).second; const mitk::VtkMapper::Pointer vtkMapper = dynamic_cast(mapper); if (vtkMapper) { // Due to VTK 5.2 bug, we need to initialize the Paths object in vtkPropAssembly // manually (see issue #8186 committed to VTK's Mantis issue tracker) // --> VTK bug resolved on 2008-12-01 - vtkPropAssembly *propAssembly = dynamic_cast(vtkMapper->GetVtkProp(m_VtkPropRenderer)); + auto *propAssembly = dynamic_cast(vtkMapper->GetVtkProp(m_VtkPropRenderer)); if (propAssembly) { propAssembly->InitPathTraversal(); } if (vtkMapper->GetVtkProp(m_VtkPropRenderer)->HasTranslucentPolygonalGeometry() == 1) return 1; } } return 0; } int vtkMitkRenderProp::RenderTranslucentPolygonalGeometry(vtkViewport *) { return m_VtkPropRenderer->Render(mitk::VtkPropRenderer::Translucent); } int vtkMitkRenderProp::RenderVolumetricGeometry(vtkViewport *) { return m_VtkPropRenderer->Render(mitk::VtkPropRenderer::Volumetric); } diff --git a/Modules/Core/src/mitkCoreActivator.cpp b/Modules/Core/src/mitkCoreActivator.cpp index 9c4f779b06..2fc89343a5 100644 --- a/Modules/Core/src/mitkCoreActivator.cpp +++ b/Modules/Core/src/mitkCoreActivator.cpp @@ -1,324 +1,324 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkCoreActivator.h" // File IO #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "mitkDicomSeriesReaderService.h" #include "mitkLegacyFileWriterService.h" #include #include #include // Micro Services #include #include #include #include #include #include #include #include #include #include // ITK "injects" static initialization code for IO factories // via the itkImageIOFactoryRegisterManager.h header (which // is generated in the application library build directory). // To ensure that the code is called *before* the CppMicroServices // static initialization code (which triggers the Activator::Start // method), we include the ITK header here. #include void HandleMicroServicesMessages(us::MsgType type, const char *msg) { switch (type) { case us::DebugMsg: MITK_DEBUG << msg; break; case us::InfoMsg: MITK_INFO << msg; break; case us::WarningMsg: MITK_WARN << msg; break; case us::ErrorMsg: MITK_ERROR << msg; break; } } void AddMitkAutoLoadPaths(const std::string &programPath) { us::ModuleSettings::AddAutoLoadPath(programPath); #ifdef __APPLE__ // Walk up three directories since that is where the .dylib files are located // for build trees. std::string additionalPath = programPath; bool addPath = true; for (int i = 0; i < 3; ++i) { std::size_t index = additionalPath.find_last_of('/'); if (index != std::string::npos) { additionalPath = additionalPath.substr(0, index); } else { addPath = false; break; } } if (addPath) { us::ModuleSettings::AddAutoLoadPath(additionalPath); } #endif } class FixedNiftiImageIO : public itk::NiftiImageIO { public: /** Standard class typedefs. */ typedef FixedNiftiImageIO Self; typedef itk::NiftiImageIO Superclass; typedef itk::SmartPointer Pointer; /** Method for creation through the object factory. */ itkNewMacro(Self) /** Run-time type information (and related methods). */ itkTypeMacro(FixedNiftiImageIO, Superclass) bool SupportsDimension(unsigned long dim) override { return dim > 1 && dim < 5; } }; void MitkCoreActivator::Load(us::ModuleContext *context) { // Handle messages from CppMicroServices us::installMsgHandler(HandleMicroServicesMessages); this->m_Context = context; // Add the current application directory to the auto-load paths. // This is useful for third-party executables. std::string programPath = mitk::IOUtil::GetProgramPath(); if (programPath.empty()) { MITK_WARN << "Could not get the program path."; } else { AddMitkAutoLoadPaths(programPath); } // m_RenderingManager = mitk::RenderingManager::New(); // context->RegisterService(renderingManager.GetPointer()); m_PlanePositionManager.reset(new mitk::PlanePositionManagerService); context->RegisterService(m_PlanePositionManager.get()); m_PropertyAliases.reset(new mitk::PropertyAliases); context->RegisterService(m_PropertyAliases.get()); m_PropertyDescriptions.reset(new mitk::PropertyDescriptions); context->RegisterService(m_PropertyDescriptions.get()); m_PropertyExtensions.reset(new mitk::PropertyExtensions); context->RegisterService(m_PropertyExtensions.get()); m_PropertyFilters.reset(new mitk::PropertyFilters); context->RegisterService(m_PropertyFilters.get()); m_PropertyPersistence.reset(new mitk::PropertyPersistence); context->RegisterService(m_PropertyPersistence.get()); m_MimeTypeProvider.reset(new mitk::MimeTypeProvider); m_MimeTypeProvider->Start(); m_MimeTypeProviderReg = context->RegisterService(m_MimeTypeProvider.get()); this->RegisterDefaultMimeTypes(); this->RegisterItkReaderWriter(); this->RegisterVtkReaderWriter(); // Add custom Reader / Writer Services m_FileReaders.push_back(new mitk::PointSetReaderService()); m_FileWriters.push_back(new mitk::PointSetWriterService()); m_FileReaders.push_back(new mitk::GeometryDataReaderService()); m_FileWriters.push_back(new mitk::GeometryDataWriterService()); m_FileReaders.push_back(new mitk::DicomSeriesReaderService()); m_FileReaders.push_back(new mitk::RawImageFileReaderService()); /* There IS an option to exchange ALL vtkTexture instances against vtkNeverTranslucentTextureFactory. This code is left here as a reminder, just in case we might need to do that some time. vtkNeverTranslucentTextureFactory* textureFactory = vtkNeverTranslucentTextureFactory::New(); vtkObjectFactory::RegisterFactory( textureFactory ); textureFactory->Delete(); */ this->RegisterLegacyWriter(); } void MitkCoreActivator::Unload(us::ModuleContext *) { for (auto &elem : m_FileReaders) { delete elem; } for (auto &elem : m_FileWriters) { delete elem; } for (auto &elem : m_FileIOs) { delete elem; } for (auto &elem : m_LegacyWriters) { delete elem; } // The mitk::ModuleContext* argument of the Unload() method // will always be 0 for the Mitk library. It makes no sense // to use it at this stage anyway, since all libraries which // know about the module system have already been unloaded. // we need to close the internal service tracker of the // MimeTypeProvider class here. Otherwise it // would hold on to the ModuleContext longer than it is // actually valid. m_MimeTypeProviderReg.Unregister(); m_MimeTypeProvider->Stop(); for (std::vector::const_iterator mimeTypeIter = m_DefaultMimeTypes.begin(), iterEnd = m_DefaultMimeTypes.end(); mimeTypeIter != iterEnd; ++mimeTypeIter) { delete *mimeTypeIter; } } void MitkCoreActivator::RegisterDefaultMimeTypes() { // Register some default mime-types std::vector mimeTypes = mitk::IOMimeTypes::Get(); for (std::vector::const_iterator mimeTypeIter = mimeTypes.begin(), iterEnd = mimeTypes.end(); mimeTypeIter != iterEnd; ++mimeTypeIter) { m_DefaultMimeTypes.push_back(*mimeTypeIter); m_Context->RegisterService(m_DefaultMimeTypes.back()); } } void MitkCoreActivator::RegisterItkReaderWriter() { std::list allobjects = itk::ObjectFactoryBase::CreateAllInstance("itkImageIOBase"); for (auto &allobject : allobjects) { - itk::ImageIOBase *io = dynamic_cast(allobject.GetPointer()); + auto *io = dynamic_cast(allobject.GetPointer()); // NiftiImageIO does not provide a correct "SupportsDimension()" methods // and the supported read/write extensions are not ordered correctly if (dynamic_cast(io)) continue; // Use a custom mime-type for GDCMImageIO below if (dynamic_cast(allobject.GetPointer())) { // MITK provides its own DICOM reader (which internally uses GDCMImageIO). continue; } if (io) { m_FileIOs.push_back(new mitk::ItkImageIO(io)); } else { MITK_WARN << "Error ImageIO factory did not return an ImageIOBase: " << (allobject)->GetNameOfClass(); } } FixedNiftiImageIO::Pointer itkNiftiIO = FixedNiftiImageIO::New(); mitk::ItkImageIO *niftiIO = new mitk::ItkImageIO(mitk::IOMimeTypes::NIFTI_MIMETYPE(), itkNiftiIO.GetPointer(), 0); m_FileIOs.push_back(niftiIO); } void MitkCoreActivator::RegisterVtkReaderWriter() { m_FileIOs.push_back(new mitk::SurfaceVtkXmlIO()); m_FileIOs.push_back(new mitk::SurfaceStlIO()); m_FileIOs.push_back(new mitk::SurfaceVtkLegacyIO()); m_FileIOs.push_back(new mitk::ImageVtkXmlIO()); m_FileIOs.push_back(new mitk::ImageVtkLegacyIO()); } void MitkCoreActivator::RegisterLegacyWriter() { std::list allobjects = itk::ObjectFactoryBase::CreateAllInstance("IOWriter"); - for (std::list::iterator i = allobjects.begin(); i != allobjects.end(); ++i) + for (auto i = allobjects.begin(); i != allobjects.end(); ++i) { mitk::FileWriter::Pointer io = dynamic_cast(i->GetPointer()); if (io) { std::string description = std::string("Legacy ") + io->GetNameOfClass() + " Writer"; mitk::IFileWriter *writer = new mitk::LegacyFileWriterService(io, description); m_LegacyWriters.push_back(writer); } else { MITK_ERROR << "Error IOWriter override is not of type mitk::FileWriter: " << (*i)->GetNameOfClass() << std::endl; } } } US_EXPORT_MODULE_ACTIVATOR(MitkCoreActivator) // Call CppMicroservices initialization code at the end of the file. // This especially ensures that VTK object factories have already // been registered (VTK initialization code is injected by implicitly // include VTK header files at the top of this file). US_INITIALIZE_MODULE diff --git a/Modules/Core/src/mitkCoreObjectFactory.cpp b/Modules/Core/src/mitkCoreObjectFactory.cpp index d7e1993759..5ee3a790aa 100644 --- a/Modules/Core/src/mitkCoreObjectFactory.cpp +++ b/Modules/Core/src/mitkCoreObjectFactory.cpp @@ -1,458 +1,458 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkCoreObjectFactory.h" #include "mitkConfig.h" #include "mitkColorProperty.h" #include "mitkDataNode.h" #include "mitkEnumerationProperty.h" #include "mitkGeometry3D.h" #include "mitkGeometryData.h" #include "mitkImage.h" #include "mitkLevelWindowProperty.h" #include "mitkLookupTable.h" #include "mitkLookupTableProperty.h" #include "mitkPlaneGeometry.h" #include "mitkPlaneGeometryData.h" #include "mitkPlaneGeometryDataMapper2D.h" #include "mitkPlaneGeometryDataVtkMapper3D.h" #include "mitkPointSet.h" #include "mitkPointSetVtkMapper2D.h" #include "mitkPointSetVtkMapper3D.h" #include "mitkProperties.h" #include "mitkPropertyList.h" #include "mitkSlicedGeometry3D.h" #include "mitkSmartPointerProperty.h" #include "mitkStringProperty.h" #include "mitkSurface.h" #include "mitkSurface.h" #include "mitkSurfaceVtkMapper2D.h" #include "mitkSurfaceVtkMapper3D.h" #include "mitkTimeGeometry.h" #include "mitkTransferFunctionProperty.h" #include "mitkVtkInterpolationProperty.h" #include "mitkVtkRepresentationProperty.h" #include "mitkVtkResliceInterpolationProperty.h" #include // Legacy Support: #include #include #include void mitk::CoreObjectFactory::RegisterExtraFactory(CoreObjectFactoryBase *factory) { MITK_DEBUG << "CoreObjectFactory: registering extra factory of type " << factory->GetNameOfClass(); m_ExtraFactories.insert(CoreObjectFactoryBase::Pointer(factory)); // Register Legacy Reader and Writer this->RegisterLegacyReaders(factory); this->RegisterLegacyWriters(factory); } void mitk::CoreObjectFactory::UnRegisterExtraFactory(CoreObjectFactoryBase *factory) { MITK_DEBUG << "CoreObjectFactory: un-registering extra factory of type " << factory->GetNameOfClass(); this->UnRegisterLegacyWriters(factory); this->UnRegisterLegacyReaders(factory); try { m_ExtraFactories.erase(factory); } catch (std::exception const &e) { MITK_ERROR << "Caugt exception while unregistering: " << e.what(); } } mitk::CoreObjectFactory::Pointer mitk::CoreObjectFactory::GetInstance() { static mitk::CoreObjectFactory::Pointer instance; if (instance.IsNull()) { instance = mitk::CoreObjectFactory::New(); } return instance; } mitk::CoreObjectFactory::~CoreObjectFactory() { - for (std::map>::iterator iter = + for (auto iter = m_LegacyReaders.begin(); iter != m_LegacyReaders.end(); ++iter) { for (auto &elem : iter->second) { delete elem; } } - for (std::map>::iterator iter = + for (auto iter = m_LegacyWriters.begin(); iter != m_LegacyWriters.end(); ++iter) { for (auto &elem : iter->second) { delete elem; } } } void mitk::CoreObjectFactory::SetDefaultProperties(mitk::DataNode *node) { if (node == nullptr) return; mitk::DataNode::Pointer nodePointer = node; mitk::Image::Pointer image = dynamic_cast(node->GetData()); if (image.IsNotNull() && image->IsInitialized()) { mitk::ImageVtkMapper2D::SetDefaultProperties(node); } mitk::PlaneGeometryData::Pointer planeGeometry = dynamic_cast(node->GetData()); if (planeGeometry.IsNotNull()) { mitk::PlaneGeometryDataMapper2D::SetDefaultProperties(node); } mitk::Surface::Pointer surface = dynamic_cast(node->GetData()); if (surface.IsNotNull()) { mitk::SurfaceVtkMapper2D::SetDefaultProperties(node); mitk::SurfaceVtkMapper3D::SetDefaultProperties(node); } mitk::PointSet::Pointer pointSet = dynamic_cast(node->GetData()); if (pointSet.IsNotNull()) { mitk::PointSetVtkMapper2D::SetDefaultProperties(node); mitk::PointSetVtkMapper3D::SetDefaultProperties(node); } - for (ExtraFactoriesContainer::iterator it = m_ExtraFactories.begin(); it != m_ExtraFactories.end(); ++it) + for (auto it = m_ExtraFactories.begin(); it != m_ExtraFactories.end(); ++it) { (*it)->SetDefaultProperties(node); } } mitk::CoreObjectFactory::CoreObjectFactory() { static bool alreadyDone = false; if (!alreadyDone) { CreateFileExtensionsMap(); // RegisterLegacyReaders(this); // RegisterLegacyWriters(this); alreadyDone = true; } } mitk::Mapper::Pointer mitk::CoreObjectFactory::CreateMapper(mitk::DataNode *node, MapperSlotId id) { mitk::Mapper::Pointer newMapper = nullptr; mitk::Mapper::Pointer tmpMapper = nullptr; // check whether extra factories provide mapper - for (ExtraFactoriesContainer::iterator it = m_ExtraFactories.begin(); it != m_ExtraFactories.end(); ++it) + for (auto it = m_ExtraFactories.begin(); it != m_ExtraFactories.end(); ++it) { tmpMapper = (*it)->CreateMapper(node, id); if (tmpMapper.IsNotNull()) newMapper = tmpMapper; } if (newMapper.IsNull()) { mitk::BaseData *data = node->GetData(); if (id == mitk::BaseRenderer::Standard2D) { if ((dynamic_cast(data) != nullptr)) { newMapper = mitk::ImageVtkMapper2D::New(); newMapper->SetDataNode(node); } else if ((dynamic_cast(data) != nullptr)) { newMapper = mitk::PlaneGeometryDataMapper2D::New(); newMapper->SetDataNode(node); } else if ((dynamic_cast(data) != nullptr)) { newMapper = mitk::SurfaceVtkMapper2D::New(); // cast because SetDataNode is not virtual - mitk::SurfaceVtkMapper2D *castedMapper = dynamic_cast(newMapper.GetPointer()); + auto *castedMapper = dynamic_cast(newMapper.GetPointer()); castedMapper->SetDataNode(node); } else if ((dynamic_cast(data) != nullptr)) { newMapper = mitk::PointSetVtkMapper2D::New(); newMapper->SetDataNode(node); } } else if (id == mitk::BaseRenderer::Standard3D) { if ((dynamic_cast(data) != nullptr)) { newMapper = mitk::PlaneGeometryDataVtkMapper3D::New(); newMapper->SetDataNode(node); } else if ((dynamic_cast(data) != nullptr)) { newMapper = mitk::SurfaceVtkMapper3D::New(); newMapper->SetDataNode(node); } else if ((dynamic_cast(data) != nullptr)) { newMapper = mitk::PointSetVtkMapper3D::New(); newMapper->SetDataNode(node); } } } return newMapper; } const char *mitk::CoreObjectFactory::GetFileExtensions() { MultimapType aMap; - for (ExtraFactoriesContainer::iterator it = m_ExtraFactories.begin(); it != m_ExtraFactories.end(); ++it) + for (auto it = m_ExtraFactories.begin(); it != m_ExtraFactories.end(); ++it) { aMap = (*it)->GetFileExtensionsMap(); this->MergeFileExtensions(m_FileExtensionsMap, aMap); } this->CreateFileExtensions(m_FileExtensionsMap, m_FileExtensions); return m_FileExtensions.c_str(); } void mitk::CoreObjectFactory::MergeFileExtensions(MultimapType &fileExtensionsMap, MultimapType inputMap) { std::pair pairOfIter; - for (MultimapType::iterator it = inputMap.begin(); it != inputMap.end(); ++it) + for (auto it = inputMap.begin(); it != inputMap.end(); ++it) { bool duplicateFound = false; pairOfIter = fileExtensionsMap.equal_range((*it).first); - for (MultimapType::iterator it2 = pairOfIter.first; it2 != pairOfIter.second; ++it2) + for (auto it2 = pairOfIter.first; it2 != pairOfIter.second; ++it2) { // cout << " [" << (*it).first << ", " << (*it).second << "]" << endl; std::string aString = (*it2).second; if (aString.compare((*it).second) == 0) { // cout << " DUP!! [" << (*it).first << ", " << (*it).second << "]" << endl; duplicateFound = true; break; } } if (!duplicateFound) { fileExtensionsMap.insert(std::pair((*it).first, (*it).second)); } } } mitk::CoreObjectFactoryBase::MultimapType mitk::CoreObjectFactory::GetFileExtensionsMap() { return m_FileExtensionsMap; } void mitk::CoreObjectFactory::CreateFileExtensionsMap() { /* m_FileExtensionsMap.insert(std::pair("*.dcm", "DICOM files")); m_FileExtensionsMap.insert(std::pair("*.DCM", "DICOM files")); m_FileExtensionsMap.insert(std::pair("*.dc3", "DICOM files")); m_FileExtensionsMap.insert(std::pair("*.DC3", "DICOM files")); m_FileExtensionsMap.insert(std::pair("*.gdcm", "DICOM files")); m_FileExtensionsMap.insert(std::pair("*.seq", "DKFZ Pic")); m_FileExtensionsMap.insert(std::pair("*.seq.gz", "DKFZ Pic")); m_FileExtensionsMap.insert(std::pair("*.dcm", "Sets of 2D slices")); m_FileExtensionsMap.insert(std::pair("*.gdcm", "Sets of 2D slices")); */ } const char *mitk::CoreObjectFactory::GetSaveFileExtensions() { MultimapType aMap; - for (ExtraFactoriesContainer::iterator it = m_ExtraFactories.begin(); it != m_ExtraFactories.end(); ++it) + for (auto it = m_ExtraFactories.begin(); it != m_ExtraFactories.end(); ++it) { aMap = (*it)->GetSaveFileExtensionsMap(); this->MergeFileExtensions(m_SaveFileExtensionsMap, aMap); } this->CreateFileExtensions(m_SaveFileExtensionsMap, m_SaveFileExtensions); return m_SaveFileExtensions.c_str(); } mitk::CoreObjectFactoryBase::MultimapType mitk::CoreObjectFactory::GetSaveFileExtensionsMap() { return m_SaveFileExtensionsMap; } mitk::CoreObjectFactory::FileWriterList mitk::CoreObjectFactory::GetFileWriters() { FileWriterList allWriters = m_FileWriters; // sort to merge lists later on typedef std::set FileWriterSet; FileWriterSet fileWritersSet; fileWritersSet.insert(allWriters.begin(), allWriters.end()); // collect all extra factories - for (ExtraFactoriesContainer::iterator it = m_ExtraFactories.begin(); it != m_ExtraFactories.end(); ++it) + for (auto it = m_ExtraFactories.begin(); it != m_ExtraFactories.end(); ++it) { FileWriterList list2 = (*it)->GetFileWriters(); // add them to the sorted set fileWritersSet.insert(list2.begin(), list2.end()); } // write back to allWriters to return a list allWriters.clear(); allWriters.insert(allWriters.end(), fileWritersSet.begin(), fileWritersSet.end()); return allWriters; } void mitk::CoreObjectFactory::MapEvent(const mitk::Event *, const int) { } std::string mitk::CoreObjectFactory::GetDescriptionForExtension(const std::string &extension) { std::multimap fileExtensionMap = GetSaveFileExtensionsMap(); - for (std::multimap::iterator it = fileExtensionMap.begin(); it != fileExtensionMap.end(); + for (auto it = fileExtensionMap.begin(); it != fileExtensionMap.end(); ++it) if (it->first == extension) return it->second; return ""; // If no matching extension was found, return emtpy string } void mitk::CoreObjectFactory::RegisterLegacyReaders(mitk::CoreObjectFactoryBase *factory) { // We are not really interested in the string, just call the method since // many readers initialize the map the first time when this method is called factory->GetFileExtensions(); std::map> extensionsByCategories; std::multimap fileExtensionMap = factory->GetFileExtensionsMap(); - for (std::multimap::iterator it = fileExtensionMap.begin(); it != fileExtensionMap.end(); + for (auto it = fileExtensionMap.begin(); it != fileExtensionMap.end(); ++it) { std::string extension = it->first; // remove "*." extension = extension.erase(0, 2); extensionsByCategories[it->second].push_back(extension); } for (auto &extensionsByCategorie : extensionsByCategories) { m_LegacyReaders[factory].push_back( new mitk::LegacyFileReaderService(extensionsByCategorie.second, extensionsByCategorie.first)); } } void mitk::CoreObjectFactory::UnRegisterLegacyReaders(mitk::CoreObjectFactoryBase *factory) { - std::map>::iterator iter = + auto iter = m_LegacyReaders.find(factory); if (iter != m_LegacyReaders.end()) { for (auto &elem : iter->second) { delete elem; } m_LegacyReaders.erase(iter); } } void mitk::CoreObjectFactory::RegisterLegacyWriters(mitk::CoreObjectFactoryBase *factory) { // Get all external Writers mitk::CoreObjectFactory::FileWriterList writers = factory->GetFileWriters(); // We are not really interested in the string, just call the method since // many writers initialize the map the first time when this method is called factory->GetSaveFileExtensions(); MultimapType fileExtensionMap = factory->GetSaveFileExtensionsMap(); - for (mitk::CoreObjectFactory::FileWriterList::iterator it = writers.begin(); it != writers.end(); ++it) + for (auto it = writers.begin(); it != writers.end(); ++it) { std::vector extensions = (*it)->GetPossibleFileExtensions(); if (extensions.empty()) continue; std::string description; - for (std::vector::iterator ext = extensions.begin(); ext != extensions.end(); ++ext) + for (auto ext = extensions.begin(); ext != extensions.end(); ++ext) { if (ext->empty()) continue; std::string extension = *ext; std::string extensionWithStar = extension; if (extension.find_first_of('*') == 0) { // remove "*." extension = extension.substr(0, extension.size() - 2); } else { extensionWithStar.insert(extensionWithStar.begin(), '*'); } - for (MultimapType::iterator fileExtensionIter = fileExtensionMap.begin(); + for (auto fileExtensionIter = fileExtensionMap.begin(); fileExtensionIter != fileExtensionMap.end(); ++fileExtensionIter) { if (fileExtensionIter->first == extensionWithStar) { description = fileExtensionIter->second; break; } } if (!description.empty()) break; } if (description.empty()) { description = std::string("Legacy ") + (*it)->GetNameOfClass() + " Reader"; } mitk::FileWriter::Pointer fileWriter(it->GetPointer()); mitk::LegacyFileWriterService *lfws = new mitk::LegacyFileWriterService(fileWriter, description); m_LegacyWriters[factory].push_back(lfws); } } void mitk::CoreObjectFactory::UnRegisterLegacyWriters(mitk::CoreObjectFactoryBase *factory) { - std::map>::iterator iter = + auto iter = m_LegacyWriters.find(factory); if (iter != m_LegacyWriters.end()) { for (auto &elem : iter->second) { delete elem; } m_LegacyWriters.erase(iter); } } diff --git a/Modules/Core/src/mitkCoreObjectFactoryBase.cpp b/Modules/Core/src/mitkCoreObjectFactoryBase.cpp index efa0946c5d..18f826fcdf 100644 --- a/Modules/Core/src/mitkCoreObjectFactoryBase.cpp +++ b/Modules/Core/src/mitkCoreObjectFactoryBase.cpp @@ -1,71 +1,71 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkCoreObjectFactoryBase.h" void mitk::CoreObjectFactoryBase::CreateFileExtensions(MultimapType fileExtensionsMap, std::string &fileExtensions) { std::map aMap; // group the extensions by extension-group // e.g. aMap["DICOM files"] = "*.dcm *.DCM *.dc3 *.DC3 *.gdcm" - for (MultimapType::iterator it = fileExtensionsMap.begin(); it != fileExtensionsMap.end(); ++it) + for (auto it = fileExtensionsMap.begin(); it != fileExtensionsMap.end(); ++it) { std::string aValue = aMap[(*it).second]; if (aValue.compare("") != 0) { aValue.append(" "); } aValue.append((*it).first); aMap[(*it).second] = aValue; } // build the "all" entry (it contains all the extensions) // and add it to the string in the first position // e.g. "all (*.dcm *.DCM *.dc3 *.DC3 *.gdcm *.ima *.mhd ... *.vti *.hdr *.nrrd *.nhdr );;" fileExtensions = "known extensions ("; std::string lastKey = ""; - for (MultimapType::iterator it = fileExtensionsMap.begin(); it != fileExtensionsMap.end(); ++it) + for (auto it = fileExtensionsMap.begin(); it != fileExtensionsMap.end(); ++it) { std::string aKey = (*it).first; if (aKey.compare(lastKey) != 0) { if (lastKey.compare("") != 0) { fileExtensions.append(" "); } fileExtensions.append(aKey); } lastKey = aKey; } fileExtensions.append(");;all (*);;"); // build the entry for each extension-group // e.g. "Sets of 2D slices (*.pic *.pic.gz *.bmp *.png *.dcm *.gdcm *.ima *.tiff);;" - for (std::map::iterator it = aMap.begin(); it != aMap.end(); ++it) + for (auto it = aMap.begin(); it != aMap.end(); ++it) { // cout << " [" << (*it).first << ", " << (*it).second << "]" << endl; std::string aKey = (*it).first; if (aKey.compare("") != 0) { fileExtensions.append((*it).first); fileExtensions.append(" ("); fileExtensions.append((*it).second); fileExtensions.append(");;"); } } } diff --git a/Modules/Core/src/mitkCoreServices.cpp b/Modules/Core/src/mitkCoreServices.cpp index 030ce40ecd..808796a19c 100644 --- a/Modules/Core/src/mitkCoreServices.cpp +++ b/Modules/Core/src/mitkCoreServices.cpp @@ -1,126 +1,126 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkCoreServices.h" #include #include #include #include #include #include #include #include #include #include #include #include namespace mitk { itk::SimpleFastMutexLock &s_ContextToServicesMapMutex() { static itk::SimpleFastMutexLock mutex; return mutex; } std::map> &s_ContextToServicesMap() { static std::map> serviceMap; return serviceMap; } template static S *GetCoreService(us::ModuleContext *context) { if (context == nullptr) context = us::GetModuleContext(); S *coreService = nullptr; us::ServiceReference serviceRef = context->GetServiceReference(); if (serviceRef) { coreService = context->GetService(serviceRef); } assert(coreService && "Asserting non-nullptr MITK core service"); { itk::MutexLockHolder l(s_ContextToServicesMapMutex()); s_ContextToServicesMap()[context].insert(std::make_pair(coreService, serviceRef)); } return coreService; } IPropertyAliases *CoreServices::GetPropertyAliases(us::ModuleContext *context) { return GetCoreService(context); } IPropertyDescriptions *CoreServices::GetPropertyDescriptions(us::ModuleContext *context) { return GetCoreService(context); } IPropertyExtensions *CoreServices::GetPropertyExtensions(us::ModuleContext *context) { return GetCoreService(context); } IPropertyFilters *CoreServices::GetPropertyFilters(us::ModuleContext *context) { return GetCoreService(context); } IPropertyPersistence *CoreServices::GetPropertyPersistence(us::ModuleContext *context) { return GetCoreService(context); } IMimeTypeProvider *CoreServices::GetMimeTypeProvider(us::ModuleContext *context) { return GetCoreService(context); } bool CoreServices::Unget(us::ModuleContext *context, const std::string & /*interfaceId*/, void *service) { bool success = false; itk::MutexLockHolder l(s_ContextToServicesMapMutex()); - std::map>::iterator iter = + auto iter = s_ContextToServicesMap().find(context); if (iter != s_ContextToServicesMap().end()) { - std::map::iterator iter2 = iter->second.find(service); + auto iter2 = iter->second.find(service); if (iter2 != iter->second.end()) { us::ServiceReferenceU serviceRef = iter2->second; if (serviceRef) { success = context->UngetService(serviceRef); if (success) { iter->second.erase(iter2); } } } } return success; } } diff --git a/Modules/Core/test/mitkDicomSeriesReaderTest.cpp b/Modules/Core/test/mitkDicomSeriesReaderTest.cpp index aa8ebb5b98..1e33d95313 100644 --- a/Modules/Core/test/mitkDicomSeriesReaderTest.cpp +++ b/Modules/Core/test/mitkDicomSeriesReaderTest.cpp @@ -1,178 +1,178 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkTestingMacros.h" #include #include "mitkDicomSeriesReader.h" #include "mitkImage.h" #include "mitkProperties.h" static std::map> GetTagInformationFromFile( mitk::DicomSeriesReader::StringContainer files) { gdcm::Scanner scanner; std::map> tagInformations; const gdcm::Tag tagSliceLocation(0x0020, 0x1041); // slice location scanner.AddTag(tagSliceLocation); const gdcm::Tag tagInstanceNumber(0x0020, 0x0013); // (image) instance number scanner.AddTag(tagInstanceNumber); const gdcm::Tag tagSOPInstanceNumber(0x0008, 0x0018); // SOP instance number scanner.AddTag(tagSOPInstanceNumber); // unsigned int slice(0); scanner.Scan(files); // return const_cast(scanner.GetMappings()); - gdcm::Scanner::MappingType &tagValueMappings = const_cast(scanner.GetMappings()); + auto &tagValueMappings = const_cast(scanner.GetMappings()); for (std::vector::const_iterator fIter = files.begin(); fIter != files.end(); ++fIter) { std::map tags; tags.insert( std::pair(tagSliceLocation, tagValueMappings[fIter->c_str()][tagSliceLocation])); tags.insert( std::pair(tagInstanceNumber, tagValueMappings[fIter->c_str()][tagInstanceNumber])); tags.insert( std::pair(tagSOPInstanceNumber, tagValueMappings[fIter->c_str()][tagSOPInstanceNumber])); tagInformations.insert(std::pair>(fIter->c_str(), tags)); } return tagInformations; } int mitkDicomSeriesReaderTest(int argc, char *argv[]) { // always start with this! MITK_TEST_BEGIN("DicomSeriesReader") if (argc < 1) { MITK_ERROR << "No directory given!"; return -1; } char *dir; dir = argv[1]; // check if DICOMTags have been set as property for mitk::Image mitk::DicomSeriesReader::FileNamesGrouping seriesInFiles = mitk::DicomSeriesReader::GetSeries(dir, true); std::list images; std::map fileMap; // TODO sort series UIDs, implementation of map iterator might differ on different platforms (or verify this is a // standard topic??) for (mitk::DicomSeriesReader::FileNamesGrouping::const_iterator seriesIter = seriesInFiles.begin(); seriesIter != seriesInFiles.end(); ++seriesIter) { mitk::DicomSeriesReader::StringContainer files = seriesIter->second.GetFilenames(); mitk::DataNode::Pointer node = mitk::DicomSeriesReader::LoadDicomSeries(files); MITK_TEST_CONDITION_REQUIRED(node.IsNotNull(), "Testing node") if (node.IsNotNull()) { mitk::Image::Pointer image = dynamic_cast(node->GetData()); images.push_back(image); fileMap.insert(std::pair(image, files)); } } // Test if DICOM tags have been added correctly to the mitk::image properties const gdcm::Tag tagSliceLocation(0x0020, 0x1041); // slice location const gdcm::Tag tagInstanceNumber(0x0020, 0x0013); // (image) instance number const gdcm::Tag tagSOPInstanceNumber(0x0008, 0x0018); // SOP instance number for (std::list::const_iterator imageIter = images.begin(); imageIter != images.end(); ++imageIter) { const mitk::Image::Pointer image = *imageIter; // Get tag information for all dicom files of this image std::map> tagInformations = GetTagInformationFromFile((*fileMap.find(image)).second); mitk::StringLookupTableProperty *sliceLocation = dynamic_cast(image->GetProperty("dicom.image.0020.1041").GetPointer()); mitk::StringLookupTableProperty *instanceNumber = dynamic_cast(image->GetProperty("dicom.image.0020.0013").GetPointer()); mitk::StringLookupTableProperty *SOPInstnaceNumber = dynamic_cast(image->GetProperty("dicom.image.0008.0018").GetPointer()); mitk::StringLookupTableProperty *files = dynamic_cast(image->GetProperty("files").GetPointer()); MITK_TEST_CONDITION(sliceLocation != nullptr, "Test if tag for slice location has been set to mitk image"); if (sliceLocation != nullptr) { for (int i = 0; i < (int)sliceLocation->GetValue().GetLookupTable().size(); i++) { if (i < (int)files->GetValue().GetLookupTable().size()) { MITK_INFO << "Table value: " << sliceLocation->GetValue().GetTableValue(i) << " and File value: " << tagInformations[files->GetValue().GetTableValue(i).c_str()][tagSliceLocation] << std::endl; MITK_INFO << "Filename: " << files->GetValue().GetTableValue(i).c_str() << std::endl; MITK_TEST_CONDITION(sliceLocation->GetValue().GetTableValue(i) == tagInformations[files->GetValue().GetTableValue(i).c_str()][tagSliceLocation], "Test if value for slice location is correct"); } } } MITK_TEST_CONDITION(instanceNumber != nullptr, "Test if tag for image instance number has been set to mitk image"); if (instanceNumber != nullptr) { for (int i = 0; i < (int)instanceNumber->GetValue().GetLookupTable().size(); i++) { if (i < (int)files->GetValue().GetLookupTable().size()) { MITK_INFO << "Table value: " << instanceNumber->GetValue().GetTableValue(i) << " and File value: " << tagInformations[files->GetValue().GetTableValue(i).c_str()][tagInstanceNumber] << std::endl; MITK_INFO << "Filename: " << files->GetValue().GetTableValue(i).c_str() << std::endl; MITK_TEST_CONDITION(instanceNumber->GetValue().GetTableValue(i) == tagInformations[files->GetValue().GetTableValue(i).c_str()][tagInstanceNumber], "Test if value for instance number is correct"); } } } MITK_TEST_CONDITION(SOPInstnaceNumber != nullptr, "Test if tag for SOP instance number has been set to mitk image"); if (SOPInstnaceNumber != nullptr) { for (int i = 0; i < (int)SOPInstnaceNumber->GetValue().GetLookupTable().size(); i++) { if (i < (int)files->GetValue().GetLookupTable().size()) { MITK_INFO << "Table value: " << instanceNumber->GetValue().GetTableValue(i) << " and File value: " << tagInformations[files->GetValue().GetTableValue(i).c_str()][tagSOPInstanceNumber] << std::endl; MITK_INFO << "Filename: " << files->GetValue().GetTableValue(i).c_str() << std::endl; MITK_TEST_CONDITION(SOPInstnaceNumber->GetValue().GetTableValue(i) == tagInformations[files->GetValue().GetTableValue(i).c_str()][tagSOPInstanceNumber], "Test if value for SOP instance number is correct"); } } } } MITK_TEST_END() } diff --git a/Modules/Core/test/mitkFloatToStringTest.cpp b/Modules/Core/test/mitkFloatToStringTest.cpp index 24b98c8f09..58c06374fe 100644 --- a/Modules/Core/test/mitkFloatToStringTest.cpp +++ b/Modules/Core/test/mitkFloatToStringTest.cpp @@ -1,146 +1,146 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkTestFixture.h" #include "mitkTestingMacros.h" #include #include "mitkEqual.h" #include #include "mitkLog.h" #include #include //! //! Verifies boost::lexical for MITK's serialization purposes //! //! Verifies: //! - special numbers behavior: //! - infinity converted to "inf", parsed from "inf" or "infinity" (case-independent) //! - not-a-number converted to "nan", parsed from "nan" (case-independent) //! - Default round-trip conversion double-to-string-to-double compares equal given //! the MITK default epsilon value (mitk::Equal) //! class mitkFloatToStringTestSuite : public mitk::TestFixture { CPPUNIT_TEST_SUITE(mitkFloatToStringTestSuite); MITK_TEST(ConfirmStringValues); MITK_TEST(ConfirmStringValues); MITK_TEST(TestConversions); MITK_TEST(TestConversions); CPPUNIT_TEST_SUITE_END(); public: template void ConfirmNumberToString(DATATYPE number, const std::string &s) { CPPUNIT_ASSERT_EQUAL(boost::lexical_cast(number), s); } template void ConfirmStringToNumber(const std::string &s, DATATYPE number) { CPPUNIT_ASSERT_EQUAL(number, boost::lexical_cast(s)); } template void ConfirmStringValues() { // we want to make sure that the following strings will be accepted and returned // by our conversion functions. This must not change in the future to ensure compatibility - DATATYPE nan = boost::lexical_cast("nan"); + auto nan = boost::lexical_cast("nan"); CPPUNIT_ASSERT_MESSAGE("nan==nan must be false", !(nan == nan)); nan = boost::lexical_cast("NAN"); CPPUNIT_ASSERT_MESSAGE("NAN==NAN must be false", !(nan == nan)); std::string s_nan = boost::lexical_cast(nan); CPPUNIT_ASSERT_EQUAL(std::string("nan"), s_nan); ConfirmStringToNumber("inf", std::numeric_limits::infinity()); ConfirmStringToNumber("INF", std::numeric_limits::infinity()); ConfirmStringToNumber("infinity", std::numeric_limits::infinity()); ConfirmStringToNumber("INFINITY", std::numeric_limits::infinity()); ConfirmStringToNumber("-inf", -std::numeric_limits::infinity()); ConfirmStringToNumber("-INF", -std::numeric_limits::infinity()); ConfirmStringToNumber("-infinity", -std::numeric_limits::infinity()); ConfirmStringToNumber("-INFINITY", -std::numeric_limits::infinity()); ConfirmNumberToString(std::numeric_limits::infinity(), "inf"); ConfirmNumberToString(-std::numeric_limits::infinity(), "-inf"); } template void CheckRoundTrip(DATATYPE number, DATATYPE tolerance) { std::string s = boost::lexical_cast(number); - DATATYPE number2 = boost::lexical_cast(s); + auto number2 = boost::lexical_cast(s); CPPUNIT_ASSERT_MESSAGE(std::string("Must not parse string ") + s + " as NaN", number2 == number2); if (tolerance == 0) { CPPUNIT_ASSERT_EQUAL(number, number2); } else // mitk::Equal cannot take 0 as tolerance { CPPUNIT_ASSERT(mitk::Equal(number, number2, tolerance)); } } template void CheckRoundTrip(const std::string &input) { - DATATYPE number = boost::lexical_cast(input); + auto number = boost::lexical_cast(input); std::string result = boost::lexical_cast(number); // There are normal imprecisions when converting to string // We do only compare if the numeric values match "close enough" - DATATYPE number2 = boost::lexical_cast(result); + auto number2 = boost::lexical_cast(result); CPPUNIT_ASSERT(mitk::Equal(number, number2)); } template void TestConversions() { // we cannot test the NaN roundtrip because nan == nan will never be true CheckRoundTrip(std::numeric_limits::infinity(), 0.0); CheckRoundTrip(-std::numeric_limits::infinity(), 0.0); CheckRoundTrip(std::numeric_limits::denorm_min(), mitk::eps); CheckRoundTrip(std::numeric_limits::epsilon(), mitk::eps); CheckRoundTrip(std::numeric_limits::lowest(), mitk::eps); CheckRoundTrip(std::numeric_limits::min(), mitk::eps); CheckRoundTrip(std::numeric_limits::max(), mitk::eps); CheckRoundTrip(sqrt(2), mitk::eps); CheckRoundTrip(0.000000042, mitk::eps); CheckRoundTrip(422345678.2345678, mitk::eps); CheckRoundTrip(0.0, 0); CheckRoundTrip(-0.0, 0); CheckRoundTrip("1"); CheckRoundTrip("1.1"); CheckRoundTrip("1.12121212"); CheckRoundTrip("1.1e-2"); } }; // class MITK_TEST_SUITE_REGISTRATION(mitkFloatToString) diff --git a/Modules/Core/test/mitkGeometryDataToSurfaceFilterTest.cpp b/Modules/Core/test/mitkGeometryDataToSurfaceFilterTest.cpp index b49513c66e..281fe4c55f 100644 --- a/Modules/Core/test/mitkGeometryDataToSurfaceFilterTest.cpp +++ b/Modules/Core/test/mitkGeometryDataToSurfaceFilterTest.cpp @@ -1,274 +1,274 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkPlaneGeometryDataToSurfaceFilter.h" #include "mitkPlaneGeometry.h" #include "mitkPlaneGeometryData.h" #include "mitkSurface.h" #include "vtkPolyData.h" #include template int testExpectedIndexBoundingBox(mitk::BaseGeometry *geometry, TScalarType expectedIndexBounds[6]) { - mitk::BoundingBox *bb = const_cast(geometry->GetBoundingBox()); + auto *bb = const_cast(geometry->GetBoundingBox()); mitk::BoundingBox::BoundsArrayType bounds = bb->GetBounds(); int i; for (i = 0; i < 6; ++i) { if (mitk::Equal(bounds[i], expectedIndexBounds[i]) == false) { std::cout << "[FAILED]" << std::endl; return EXIT_FAILURE; } } std::cout << "[PASSED]" << std::endl; return EXIT_SUCCESS; } template int testExpectedAxisParallelBoundingBox(mitk::BaseGeometry *geometry, TScalarType expectedAxisParallelBounds[6]) { mitk::BoundingBox::Pointer bb = geometry->CalculateBoundingBoxRelativeToTransform(nullptr); mitk::BoundingBox::BoundsArrayType bounds = bb->GetBounds(); int i; for (i = 0; i < 6; ++i) { if (mitk::Equal(bounds[i], expectedAxisParallelBounds[i]) == false) { std::cout << "[FAILED]" << std::endl; return EXIT_FAILURE; } } std::cout << "[PASSED]" << std::endl; return EXIT_SUCCESS; } int testSurfaceBoundingBoxConsistency(mitk::Surface *surface, bool expectIdentityTransform) { std::cout << " Testing surface contents: "; if ((surface == nullptr) || (surface->GetVtkPolyData() == nullptr) || (surface->GetVtkPolyData()->GetNumberOfPoints() == 0)) { std::cout << "[FAILED]" << std::endl; return EXIT_FAILURE; } else { std::cout << "[PASSED]" << std::endl; } double bounds[6] = {0.0, 0.0, 0.0, 0.0, 0.0, 0.0}; vtkPolyData *polys = surface->GetVtkPolyData(); polys->ComputeBounds(); polys->GetBounds(bounds); if (expectIdentityTransform == false) { mitk::PlaneGeometry::Pointer geometry = mitk::PlaneGeometry::New(); geometry->SetFloatBounds(bounds); geometry->SetIndexToWorldTransform(surface->GetGeometry()->GetIndexToWorldTransform()); mitk::BoundingBox::BoundsArrayType bb = const_cast(geometry->GetBoundingBox())->GetBounds(); for (int i = 0; i < 6; ++i) bounds[i] = bb[i]; } std::cout << " Testing GetBoundingBox() "; if ((testExpectedIndexBoundingBox(surface->GetGeometry(), bounds)) != EXIT_SUCCESS) { std::cout << "[FAILED]" << std::endl; return EXIT_FAILURE; } std::cout << "[PASSED]" << std::endl; return EXIT_SUCCESS; } int testGeometryDataToSurfaceFilter(mitk::PlaneGeometryDataToSurfaceFilter *geometryToSurfaceFilter, mitk::ScalarType expectedIndexBounds[6], mitk::ScalarType expectedAxisParallelBounds[6], bool expectIdentityTransform) { int result; std::cout << "Testing SetRequestedRegionToLargestPossibleRegion(): "; geometryToSurfaceFilter->GetOutput()->SetRequestedRegionToLargestPossibleRegion(); std::cout << "[PASSED]" << std::endl; std::cout << "Testing UpdateOutputInformation(): "; geometryToSurfaceFilter->UpdateOutputInformation(); std::cout << "[PASSED]" << std::endl; std::cout << "Testing correctness of bounding-box after UpdateOutputInformation(): "; if ((result = testExpectedIndexBoundingBox(geometryToSurfaceFilter->GetOutput()->GetGeometry(), expectedIndexBounds)) != EXIT_SUCCESS) { return result; } std::cout << "Testing correctness of axis-parallel bounding-box after UpdateOutputInformation(): "; if ((result = testExpectedAxisParallelBoundingBox(geometryToSurfaceFilter->GetOutput()->GetGeometry(), expectedAxisParallelBounds)) != EXIT_SUCCESS) { return result; } std::cout << "Testing Update(): "; geometryToSurfaceFilter->Update(); std::cout << "[PASSED]" << std::endl; std::cout << "Testing correctness of bounding-box after Update(): "; if ((result = testExpectedIndexBoundingBox(geometryToSurfaceFilter->GetOutput()->GetGeometry(), expectedIndexBounds)) != EXIT_SUCCESS) { return result; } std::cout << "Testing correctness of axis-parallel bounding-box after UpdateOutputInformation(): "; if ((result = testExpectedAxisParallelBoundingBox(geometryToSurfaceFilter->GetOutput()->GetGeometry(), expectedAxisParallelBounds)) != EXIT_SUCCESS) { return result; } std::cout << "Testing bounding-box consistency: " << std::endl; if ((result = testSurfaceBoundingBoxConsistency(geometryToSurfaceFilter->GetOutput(), expectIdentityTransform)) != EXIT_SUCCESS) { std::cout << "[FAILED]" << std::endl; return result; } else { std::cout << "[PASSED]" << std::endl; } return EXIT_SUCCESS; } int mitkGeometryDataToSurfaceFilterTest(int /*argc*/, char * /*argv*/ []) { int result; std::cout << "Testing mitk::PlaneGeometryDataToSurfaceFilter: " << std::endl; mitk::PlaneGeometryDataToSurfaceFilter::Pointer geometryToSurfaceFilter; std::cout << "Testing PlaneGeometryDataToSurfaceFilter::New(): "; geometryToSurfaceFilter = mitk::PlaneGeometryDataToSurfaceFilter::New(); if (geometryToSurfaceFilter.IsNull()) { std::cout << "[FAILED]" << std::endl; return EXIT_FAILURE; } else { std::cout << "[PASSED]" << std::endl; } mitk::Point3D origin; mitk::PlaneGeometry::Pointer plane = mitk::PlaneGeometry::New(); plane->InitializeStandardPlane(50, 100); mitk::FillVector3D(origin, 1.0, 2.0, 3.0); plane->SetOrigin(origin); mitk::PlaneGeometryData::Pointer geometryData = mitk::PlaneGeometryData::New(); geometryData->SetPlaneGeometry(plane); std::cout << "Testing SetInput(): "; geometryToSurfaceFilter->SetInput(geometryData); std::cout << "[PASSED]" << std::endl; std::cout << "Testing GetInput(): "; if (geometryToSurfaceFilter->GetInput() != geometryData) { std::cout << "[FAILED]" << std::endl; return EXIT_FAILURE; } else { std::cout << "[PASSED]" << std::endl; } std::cout << "Testing default of PlaneGeometryDataToSurfaceFilter::m_PlaceByGeometry (expected is false): "; if (geometryToSurfaceFilter->GetPlaceByGeometry() != false) { std::cout << "[FAILED]" << std::endl; return EXIT_FAILURE; } else { std::cout << "[PASSED]" << std::endl; } // test with m_PlaceByGeometry==false mitk::ScalarType expectedBoundsFinal[6] = {1.0, 51.0, 2.0, 102.0, 3.0, 3.0}; if ((result = testGeometryDataToSurfaceFilter( geometryToSurfaceFilter, expectedBoundsFinal, expectedBoundsFinal, true)) != EXIT_SUCCESS) { return result; } std::cout << "Testing PlaneGeometryDataToSurfaceFilter::SetPlaceByGeometry(true): "; geometryToSurfaceFilter->SetPlaceByGeometry(true); if (geometryToSurfaceFilter->GetPlaceByGeometry() != true) { std::cout << "[FAILED]" << std::endl; return EXIT_FAILURE; } else { std::cout << "[PASSED]" << std::endl; } // test with m_PlaceByGeometry==true mitk::ScalarType expectedIndexBounds[6] = {0.0, 50.0, 0.0, 100.0, 0.0, 0.0}; mitk::ScalarType expectedAxisParallelBounds[6] = {1.0, 51.0, 2.0, 102.0, 3.0, 3.0}; if ((result = testGeometryDataToSurfaceFilter( geometryToSurfaceFilter, expectedIndexBounds, expectedAxisParallelBounds, true)) != EXIT_SUCCESS) { return result; } // test with specified BoundingBox (m_PlaceByGeometry is irrelevant for this test) mitk::BoundingBox::Pointer boundingBox = mitk::BoundingBox::New(); mitk::Point3D bbMin, bbMax; mitk::FillVector3D(bbMin, 10.0, 10.0, -6.0); mitk::FillVector3D(bbMax, 40.0, 90.0, 6.0); mitk::BoundingBox::PointsContainer::Pointer pointsContainer = mitk::BoundingBox::PointsContainer::New(); pointsContainer->InsertElement(0, bbMin); pointsContainer->InsertElement(1, bbMax); boundingBox->SetPoints(pointsContainer); boundingBox->ComputeBoundingBox(); geometryToSurfaceFilter->SetPlaceByGeometry(true); geometryToSurfaceFilter->SetBoundingBox(boundingBox); mitk::ScalarType expectedIndexBoundsWithBB[6] = {9.0, 39.0, 8.0, 88.0, 0.0, 0.0}; mitk::ScalarType expectedAxisParallelBoundsWithBB[6] = {10.0, 40.0, 10.0, 90.0, 3.0, 3.0}; if ((result = testGeometryDataToSurfaceFilter( geometryToSurfaceFilter, expectedIndexBoundsWithBB, expectedAxisParallelBoundsWithBB, true)) != EXIT_SUCCESS) { return result; } std::cout << "[TEST DONE]" << std::endl; return EXIT_SUCCESS; } diff --git a/Modules/Core/test/mitkImageAccessorTest.cpp b/Modules/Core/test/mitkImageAccessorTest.cpp index a7dda27857..de81b5037c 100644 --- a/Modules/Core/test/mitkImageAccessorTest.cpp +++ b/Modules/Core/test/mitkImageAccessorTest.cpp @@ -1,251 +1,251 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "itkBarrier.h" #include "mitkIOUtil.h" #include "mitkImage.h" #include "mitkImagePixelReadAccessor.h" #include "mitkImagePixelWriteAccessor.h" #include "mitkImageReadAccessor.h" #include "mitkImageTimeSelector.h" #include "mitkImageWriteAccessor.h" #include #include #include #include #include #include struct ThreadData { itk::Barrier::Pointer m_Barrier; // holds a pointer to the used barrier mitk::Image::Pointer data; // some random data int m_NoOfThreads; // holds the number of generated threads bool m_Successful; // to check if everything worked }; itk::SimpleFastMutexLock testMutex; ITK_THREAD_RETURN_TYPE ThreadMethod(void *data) { /* extract data pointer from Thread Info structure */ - struct itk::MultiThreader::ThreadInfoStruct *pInfo = (struct itk::MultiThreader::ThreadInfoStruct *)data; + auto *pInfo = (struct itk::MultiThreader::ThreadInfoStruct *)data; // some data validity checking if (pInfo == nullptr) { return ITK_THREAD_RETURN_VALUE; } if (pInfo->UserData == nullptr) { return ITK_THREAD_RETURN_VALUE; } // obtain user data for processing - ThreadData *threadData = (ThreadData *)pInfo->UserData; + auto *threadData = (ThreadData *)pInfo->UserData; srand(pInfo->ThreadID); mitk::Image::Pointer im = threadData->data; int nrSlices = im->GetDimension(2); // Create randomly a PixelRead- or PixelWriteAccessor for a slice and access all pixels in it. try { if (rand() % 2) { testMutex.Lock(); mitk::ImageDataItem *iDi = im->GetSliceData(rand() % nrSlices); testMutex.Unlock(); while (!iDi->IsComplete()) { } // MITK_INFO << "pixeltype: " << im->GetPixelType().GetComponentTypeAsString(); if ((im->GetPixelType().GetComponentTypeAsString() == "short") && (im->GetDimension() == 3)) { // Use pixeltype&dimension specific read accessor int xlength = im->GetDimension(0); int ylength = im->GetDimension(1); mitk::ImagePixelReadAccessor readAccessor(im, iDi); itk::Index<2> idx; for (int i = 0; i < xlength; ++i) { for (int j = 0; j < ylength; ++j) { idx[0] = i; idx[1] = j; readAccessor.GetPixelByIndexSafe(idx); } } } else { // use general accessor mitk::ImageReadAccessor *iRA = new mitk::ImageReadAccessor(im, iDi); delete iRA; } } else { testMutex.Lock(); mitk::ImageDataItem *iDi = im->GetSliceData(rand() % nrSlices); testMutex.Unlock(); while (!iDi->IsComplete()) { } if ((im->GetPixelType().GetComponentTypeAsString() == "short") && (im->GetDimension() == 3)) { // Use pixeltype&dimension specific read accessor int xlength = im->GetDimension(0); int ylength = im->GetDimension(1); mitk::ImagePixelWriteAccessor writeAccessor(im, iDi); itk::Index<2> idx; for (int i = 0; i < xlength; ++i) { for (int j = 0; j < ylength; ++j) { idx[0] = i; idx[1] = j; short newVal = rand() % 16000; writeAccessor.SetPixelByIndexSafe(idx, newVal); short val = writeAccessor.GetPixelByIndexSafe(idx); if (val != newVal) { threadData->m_Successful = false; } } } } else { // use general accessor mitk::ImageWriteAccessor iB(im, iDi); void *pointer = iB.GetData(); *((char *)pointer) = 0; } } } catch (mitk::MemoryIsLockedException &e) { threadData->m_Successful = false; e.Print(std::cout); } catch (mitk::Exception &e) { threadData->m_Successful = false; e.Print(std::cout); } // data processing end! threadData->m_Barrier->Wait(); return ITK_THREAD_RETURN_VALUE; } int mitkImageAccessorTest(int argc, char *argv[]) { MITK_TEST_BEGIN("mitkImageAccessorTest"); std::cout << "Loading file: "; if (argc == 0) { std::cout << "no file specified [FAILED]" << std::endl; return EXIT_FAILURE; } mitk::Image::Pointer image = nullptr; try { image = dynamic_cast(mitk::IOUtil::Load(std::string(argv[1]))[0].GetPointer()); if (image.IsNull()) { MITK_TEST_FAILED_MSG(<< "file could not be loaded [FAILED]") } } catch (itk::ExceptionObject &ex) { MITK_TEST_FAILED_MSG(<< "Exception: " << ex << "[FAILED]") } // CHECK INAPPROPRIATE AND SPECIAL USAGE // recursive mutex lock MITK_TEST_OUTPUT(<< "Testing a recursive mutex lock attempt, should end in an exception ..."); MITK_TEST_FOR_EXCEPTION_BEGIN(mitk::Exception) mitk::ImageWriteAccessor first(image); mitk::ImageReadAccessor second(image); MITK_TEST_FOR_EXCEPTION_END(mitk::Exception) // ignore lock mechanism in read accessor try { mitk::ImageWriteAccessor first(image); mitk::ImageReadAccessor second(image, nullptr, mitk::ImageAccessorBase::IgnoreLock); MITK_TEST_CONDITION_REQUIRED(true, "Testing the option flag \"IgnoreLock\" in ReadAccessor"); } catch (const mitk::Exception & /*e*/) { MITK_TEST_CONDITION_REQUIRED(false, "Ignoring the lock mechanism leads to exception."); } // CREATE THREADS image->GetGeometry()->Initialize(); itk::MultiThreader::Pointer threader = itk::MultiThreader::New(); unsigned int noOfThreads = 100; // initialize barrier itk::Barrier::Pointer barrier = itk::Barrier::New(); barrier->Initialize(noOfThreads + 1); // add one for we stop the base thread when the worker threads are processing - ThreadData *threadData = new ThreadData; + auto *threadData = new ThreadData; threadData->m_Barrier = barrier; threadData->m_NoOfThreads = noOfThreads; threadData->data = image; threadData->m_Successful = true; // spawn threads for (unsigned int i = 0; i < noOfThreads; ++i) { threader->SpawnThread(ThreadMethod, threadData); } // stop the base thread during worker thread execution barrier->Wait(); // terminate threads for (unsigned int j = 0; j < noOfThreads; ++j) { threader->TerminateThread(j); } bool TestSuccessful = threadData->m_Successful; delete threadData; MITK_TEST_CONDITION_REQUIRED(TestSuccessful, "Testing image access from multiple threads"); MITK_TEST_END(); } diff --git a/Modules/Core/test/mitkImageTest.cpp b/Modules/Core/test/mitkImageTest.cpp index ed7d32a0d0..a44828fe07 100644 --- a/Modules/Core/test/mitkImageTest.cpp +++ b/Modules/Core/test/mitkImageTest.cpp @@ -1,556 +1,556 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ // mitk includes #include "mitkException.h" #include "mitkIOUtil.h" #include "mitkImageGenerator.h" #include "mitkImagePixelReadAccessor.h" #include "mitkImageReadAccessor.h" #include "mitkPixelTypeMultiplex.h" #include #include #include #include #include #include "mitkImageSliceSelector.h" // itk includes #include #include // stl includes #include // vtk includes #include // Checks if reference count is correct after using GetVtkImageData() bool ImageVtkDataReferenceCheck(const char *fname) { const std::string filename = std::string(fname); try { mitk::Image::Pointer image = dynamic_cast(mitk::IOUtil::Load(filename)[0].GetPointer()); MITK_TEST_CONDITION_REQUIRED(image.IsNotNull(), "Non-nullptr image") vtkImageData *vtk = image->GetVtkImageData(); if (vtk == nullptr) return false; } catch (...) { MITK_TEST_FAILED_MSG(<< "Could not read file for testing: " << filename); return false; } return true; } template void TestRandomPixelAccess(const mitk::PixelType /*ptype*/, mitk::Image::Pointer image, mitk::Point3D &point, mitk::ScalarType &value) { // generate a random point in world coordinates mitk::Point3D xMax, yMax, zMax, xMaxIndex, yMaxIndex, zMaxIndex; xMaxIndex.Fill(0.0f); yMaxIndex.Fill(0.0f); zMaxIndex.Fill(0.0f); xMaxIndex[0] = image->GetLargestPossibleRegion().GetSize()[0]; yMaxIndex[1] = image->GetLargestPossibleRegion().GetSize()[1]; zMaxIndex[2] = image->GetLargestPossibleRegion().GetSize()[2]; image->GetGeometry()->IndexToWorld(xMaxIndex, xMax); image->GetGeometry()->IndexToWorld(yMaxIndex, yMax); image->GetGeometry()->IndexToWorld(zMaxIndex, zMax); MITK_INFO << "Origin " << image->GetGeometry()->GetOrigin()[0] << " " << image->GetGeometry()->GetOrigin()[1] << " " << image->GetGeometry()->GetOrigin()[2] << ""; MITK_INFO << "MaxExtend " << xMax[0] << " " << yMax[1] << " " << zMax[2] << ""; itk::Statistics::MersenneTwisterRandomVariateGenerator::Pointer randomGenerator = itk::Statistics::MersenneTwisterRandomVariateGenerator::New(); randomGenerator->Initialize(std::rand()); // initialize with random value, to get sensible random points for the image point[0] = randomGenerator->GetUniformVariate(image->GetGeometry()->GetOrigin()[0], xMax[0]); point[1] = randomGenerator->GetUniformVariate(image->GetGeometry()->GetOrigin()[1], yMax[1]); point[2] = randomGenerator->GetUniformVariate(image->GetGeometry()->GetOrigin()[2], zMax[2]); MITK_INFO << "RandomPoint " << point[0] << " " << point[1] << " " << point[2] << ""; // test values and max/min mitk::ScalarType imageMin = image->GetStatistics()->GetScalarValueMin(); mitk::ScalarType imageMax = image->GetStatistics()->GetScalarValueMax(); // test accessing PixelValue with coordinate leading to a negative index const mitk::Point3D geom_origin = image->GetGeometry()->GetOrigin(); const mitk::Point3D geom_center = image->GetGeometry()->GetCenter(); // shift position from origin outside of the image ( in the opposite direction to [center-origin] vector which points // in the inside) mitk::Point3D position = geom_origin + (geom_origin - geom_center); MITK_INFO << "Testing access outside of the image"; unsigned int dim = image->GetDimension(); if (dim == 3 || dim == 4) { mitk::ImagePixelReadAccessor imAccess3(image, image->GetVolumeData(0)); // Comparison ?>=0 not needed since all position[i] and timestep are unsigned int // (position[0]>=0 && position[1] >=0 && position[2]>=0 && timestep>=0) // bug-11978 : we still need to catch index with negative values if (point[0] < 0 || point[1] < 0 || point[2] < 0) { MITK_WARN << "Given position (" << point << ") is out of image range, returning 0."; } else { value = static_cast(imAccess3.GetPixelByWorldCoordinates(point)); MITK_TEST_CONDITION((value >= imageMin && value <= imageMax), "Value returned is between max/min"); } itk::Index<3> itkIndex; image->GetGeometry()->WorldToIndex(position, itkIndex); MITK_TEST_FOR_EXCEPTION_BEGIN(mitk::Exception); imAccess3.GetPixelByIndexSafe(itkIndex); MITK_TEST_FOR_EXCEPTION_END(mitk::Exception); } MITK_INFO << imageMin << " " << imageMax << " " << value << ""; } class mitkImageTestClass { public: void SetClonedGeometry_None_ClonedEqualInput() { mitk::Image::Pointer image = mitk::ImageGenerator::GenerateRandomImage(100, 100, 100, 1, 0.2, 0.3, 0.4); //----------------- // geometry information for image mitk::Point3D origin; mitk::Vector3D right, bottom; mitk::Vector3D spacing; mitk::FillVector3D(origin, 17.0, 19.92, 7.83); mitk::FillVector3D(right, 1.0, 2.0, 3.0); mitk::FillVector3D(bottom, 0.0, -3.0, 2.0); mitk::FillVector3D(spacing, 0.78, 0.91, 2.23); // InitializeStandardPlane(rightVector, downVector, spacing) mitk::PlaneGeometry::Pointer planegeometry = mitk::PlaneGeometry::New(); planegeometry->InitializeStandardPlane(100, 100, right, bottom, &spacing); planegeometry->SetOrigin(origin); planegeometry->ChangeImageGeometryConsideringOriginOffset(true); image->SetClonedGeometry(planegeometry); mitk::BaseGeometry::Pointer imageGeometry = image->GetGeometry(); MITK_ASSERT_EQUAL(imageGeometry, planegeometry, "Matrix elements of cloned matrix equal original matrix"); } }; int mitkImageTest(int argc, char *argv[]) { MITK_TEST_BEGIN(mitkImageTest); mitkImageTestClass tester; tester.SetClonedGeometry_None_ClonedEqualInput(); // Create Image out of nowhere mitk::Image::Pointer imgMem = mitk::Image::New(); mitk::PixelType pt = mitk::MakeScalarPixelType(); unsigned int dim[] = {100, 100, 20}; MITK_TEST_CONDITION_REQUIRED(imgMem.IsNotNull(), "An image was created. "); // Initialize image imgMem->Initialize(pt, 3, dim); MITK_TEST_CONDITION_REQUIRED(imgMem->IsInitialized(), "Image::IsInitialized() ?"); MITK_TEST_CONDITION_REQUIRED(imgMem->GetPixelType() == pt, "PixelType was set correctly."); int *p = nullptr; int *p2 = nullptr; try { mitk::ImageReadAccessor imgMemAcc(imgMem); p = (int *)imgMemAcc.GetData(); } catch (mitk::Exception &e) { MITK_ERROR << e.what(); } MITK_TEST_CONDITION(p != nullptr, "GetData() returned not-nullptr pointer."); // filling image const unsigned int size = dim[0] * dim[1] * dim[2]; for (unsigned int i = 0; i < size; ++i, ++p) *p = (signed int)i; // Getting it again and compare with filled values: try { mitk::ImageReadAccessor imgMemAcc(imgMem); p2 = (int *)imgMemAcc.GetData(); } catch (mitk::Exception &e) { MITK_ERROR << e.what(); } MITK_TEST_CONDITION(p2 != nullptr, "GetData() returned not-nullptr pointer."); bool isEqual = true; for (unsigned int i = 0; i < size; ++i, ++p2) { if (*p2 != (signed int)i) { isEqual = false; } } MITK_TEST_CONDITION(isEqual, "The values previously set as data are correct [pixelwise comparison]."); // Testing GetSliceData() and compare with filled values: try { mitk::ImageReadAccessor imgMemAcc(imgMem, imgMem->GetSliceData(dim[2] / 2)); p2 = (int *)imgMemAcc.GetData(); } catch (mitk::Exception &e) { MITK_ERROR << e.what(); } MITK_TEST_CONDITION_REQUIRED(p2 != nullptr, "Valid slice data returned"); unsigned int xy_size = dim[0] * dim[1]; unsigned int start_mid_slice = (dim[2] / 2) * xy_size; isEqual = true; for (unsigned int i = 0; i < xy_size; ++i, ++p2) { if (*p2 != (signed int)(i + start_mid_slice)) { isEqual = false; } } MITK_TEST_CONDITION(isEqual, "The SliceData are correct [pixelwise comparison]. "); imgMem = mitk::Image::New(); // testing re-initialization of test image mitk::PixelType pType = mitk::MakePixelType(); imgMem->Initialize(pType, 3, dim); MITK_TEST_CONDITION_REQUIRED(imgMem->GetDimension() == 3, "Testing initialization parameter dimension!"); MITK_TEST_CONDITION_REQUIRED(imgMem->GetPixelType() == pType, "Testing initialization parameter pixeltype!"); MITK_TEST_CONDITION_REQUIRED( imgMem->GetDimension(0) == dim[0] && imgMem->GetDimension(1) == dim[1] && imgMem->GetDimension(2) == dim[2], "Testing initialization of dimensions!"); MITK_TEST_CONDITION(imgMem->IsInitialized(), "Image is initialized."); // Setting volume again: try { mitk::ImageReadAccessor imgMemAcc(imgMem); imgMem->SetVolume(imgMemAcc.GetData()); } catch (mitk::Exception &e) { MITK_ERROR << e.what(); } //----------------- // geometry information for image mitk::Point3D origin; mitk::Vector3D right, bottom; mitk::Vector3D spacing; mitk::FillVector3D(origin, 17.0, 19.92, 7.83); mitk::FillVector3D(right, 1.0, 2.0, 3.0); mitk::FillVector3D(bottom, 0.0, -3.0, 2.0); mitk::FillVector3D(spacing, 0.78, 0.91, 2.23); // InitializeStandardPlane(rightVector, downVector, spacing) mitk::PlaneGeometry::Pointer planegeometry = mitk::PlaneGeometry::New(); planegeometry->InitializeStandardPlane(100, 100, right, bottom, &spacing); planegeometry->SetOrigin(origin); planegeometry->SetImageGeometry(true); // Testing Initialize(const mitk::PixelType& type, const mitk::Geometry3D& geometry, unsigned int slices) with // PlaneGeometry and GetData(): "; imgMem->Initialize(mitk::MakePixelType(), *planegeometry); MITK_TEST_CONDITION_REQUIRED( imgMem->GetGeometry()->GetOrigin() == static_cast(planegeometry)->GetOrigin(), "Testing correct setting of geometry via initialize!"); try { mitk::ImageReadAccessor imgMemAcc(imgMem); p = (int *)imgMemAcc.GetData(); } catch (mitk::Exception &e) { MITK_ERROR << e.what(); } MITK_TEST_CONDITION_REQUIRED(p != nullptr, "GetData() returned valid pointer."); // Testing Initialize(const mitk::PixelType& type, int sDim, const mitk::PlaneGeometry& geometry) and GetData(): "; imgMem->Initialize(mitk::MakePixelType(), 40, *planegeometry); try { mitk::ImageReadAccessor imgMemAcc(imgMem); p = (int *)imgMemAcc.GetData(); } catch (mitk::Exception &e) { MITK_ERROR << e.what(); } MITK_TEST_CONDITION_REQUIRED(p != nullptr, "GetData() returned valid pointer."); //----------------- // testing origin information and methods MITK_TEST_CONDITION_REQUIRED(mitk::Equal(imgMem->GetGeometry()->GetOrigin(), origin), "Testing correctness of origin via GetGeometry()->GetOrigin(): "); // Setting origin via SetOrigin(origin): "; mitk::FillVector3D(origin, 37.0, 17.92, 27.83); imgMem->SetOrigin(origin); // Test origin MITK_TEST_CONDITION_REQUIRED(mitk::Equal(imgMem->GetGeometry()->GetOrigin(), origin), "Testing correctness of changed origin via GetGeometry()->GetOrigin(): "); MITK_TEST_CONDITION_REQUIRED( mitk::Equal(imgMem->GetSlicedGeometry()->GetPlaneGeometry(0)->GetOrigin(), origin), "Testing correctness of changed origin via GetSlicedGeometry()->GetPlaneGeometry(0)->GetOrigin(): "); //----------------- // testing spacing information and methodsunsigned int dim[]={100,100,20}; MITK_TEST_CONDITION_REQUIRED(mitk::Equal(imgMem->GetGeometry()->GetSpacing(), spacing), "Testing correct spacing from Geometry3D!"); mitk::FillVector3D(spacing, 7.0, 0.92, 1.83); imgMem->SetSpacing(spacing); MITK_TEST_CONDITION_REQUIRED(mitk::Equal(imgMem->GetGeometry()->GetSpacing(), spacing), "Testing correctness of changed spacing via GetGeometry()->GetSpacing(): "); MITK_TEST_CONDITION_REQUIRED( mitk::Equal(imgMem->GetSlicedGeometry()->GetPlaneGeometry(0)->GetSpacing(), spacing), "Testing correctness of changed spacing via GetSlicedGeometry()->GetPlaneGeometry(0)->GetSpacing(): "); mitk::Image::Pointer vecImg = mitk::Image::New(); try { mitk::ImageReadAccessor imgMemAcc(imgMem); vecImg->Initialize(imgMem->GetPixelType(), *imgMem->GetGeometry(), 2 /* #channels */, 0 /*tDim*/); vecImg->SetImportChannel(const_cast(imgMemAcc.GetData()), 0, mitk::Image::CopyMemory); vecImg->SetImportChannel(const_cast(imgMemAcc.GetData()), 1, mitk::Image::CopyMemory); mitk::ImageReadAccessor vecImgAcc(vecImg); mitk::ImageReadAccessor vecImgAcc0(vecImg, vecImg->GetChannelData(0)); mitk::ImageReadAccessor vecImgAcc1(vecImg, vecImg->GetChannelData(1)); MITK_TEST_CONDITION_REQUIRED(vecImgAcc0.GetData() != nullptr && vecImgAcc1.GetData() != nullptr, "Testing set and return of channel data!"); MITK_TEST_CONDITION_REQUIRED(vecImg->IsValidSlice(0, 0, 1), ""); MITK_TEST_OUTPUT(<< " Testing whether CopyMemory worked"); MITK_TEST_CONDITION_REQUIRED(imgMemAcc.GetData() != vecImgAcc.GetData(), ""); MITK_TEST_OUTPUT(<< " Testing destruction after SetImportChannel"); vecImg = nullptr; MITK_TEST_CONDITION_REQUIRED(vecImg.IsNull(), "testing destruction!"); } catch (mitk::Exception &e) { MITK_ERROR << e.what(); } //----------------- MITK_TEST_OUTPUT(<< "Testing initialization via vtkImageData"); MITK_TEST_OUTPUT(<< " Setting up vtkImageData"); vtkImageData *vtkimage = vtkImageData::New(); vtkimage->Initialize(); vtkimage->SetDimensions(2, 3, 4); double vtkorigin[] = {-350, -358.203, -1363.5}; vtkimage->SetOrigin(vtkorigin); mitk::Point3D vtkoriginAsMitkPoint; mitk::vtk2itk(vtkorigin, vtkoriginAsMitkPoint); double vtkspacing[] = {1.367, 1.367, 2}; vtkimage->SetSpacing(vtkspacing); vtkimage->AllocateScalars(VTK_SHORT, 1); std::cout << "[PASSED]" << std::endl; MITK_TEST_OUTPUT(<< " Testing mitk::Image::Initialize(vtkImageData*, ...)"); mitk::Image::Pointer mitkByVtkImage = mitk::Image::New(); mitkByVtkImage->Initialize(vtkimage); MITK_TEST_CONDITION_REQUIRED(mitkByVtkImage->IsInitialized(), ""); vtkimage->Delete(); MITK_TEST_OUTPUT(<< " Testing whether spacing has been correctly initialized from vtkImageData"); mitk::Vector3D spacing2 = mitkByVtkImage->GetGeometry()->GetSpacing(); mitk::Vector3D vtkspacingAsMitkVector; mitk::vtk2itk(vtkspacing, vtkspacingAsMitkVector); MITK_TEST_CONDITION_REQUIRED(mitk::Equal(spacing2, vtkspacingAsMitkVector), ""); MITK_TEST_OUTPUT( << " Testing whether GetSlicedGeometry(0)->GetOrigin() has been correctly initialized from vtkImageData"); mitk::Point3D origin2 = mitkByVtkImage->GetSlicedGeometry(0)->GetOrigin(); MITK_TEST_CONDITION_REQUIRED(mitk::Equal(origin2, vtkoriginAsMitkPoint), ""); MITK_TEST_OUTPUT(<< " Testing whether GetGeometry()->GetOrigin() has been correctly initialized from vtkImageData"); origin2 = mitkByVtkImage->GetGeometry()->GetOrigin(); MITK_TEST_CONDITION_REQUIRED(mitk::Equal(origin2, vtkoriginAsMitkPoint), ""); MITK_TEST_OUTPUT(<< " Testing if vtkOrigin is (0, 0, 0). This behaviour is due to historical development of MITK. " "Aslo see bug 5050!"); vtkImageData *vtkImage = imgMem->GetVtkImageData(); auto vtkOrigin = vtkImage->GetOrigin(); MITK_TEST_CONDITION_REQUIRED(mitk::Equal(vtkOrigin[0], 0), "testing vtkOrigin[0] to be 0"); MITK_TEST_CONDITION_REQUIRED(mitk::Equal(vtkOrigin[1], 0), "testing vtkOrigin[1] to be 0"); MITK_TEST_CONDITION_REQUIRED(mitk::Equal(vtkOrigin[2], 0), "testing vtkOrigin[2] to be 0"); // TODO test the following initializers on channel-incorporation // void mitk::Image::Initialize(const mitk::PixelType& type, unsigned int dimension, unsigned int *dimensions, // unsigned int channels) // void mitk::Image::Initialize(const mitk::PixelType& type, int sDim, const mitk::PlaneGeometry& geometry2d, bool // flipped, unsigned int channels, int tDim ) // void mitk::Image::Initialize(const mitk::Image* image) // void mitk::Image::Initialize(const mitkIpPicDescriptor* pic, int channels, int tDim, int sDim) // mitk::Image::Pointer vecImg = mitk::Image::New(); // vecImg->Initialize(PixelType(typeid(float), 6, itk::ImageIOBase::SYMMETRICSECONDRANKTENSOR), // *imgMem->GetGeometry(), 2 /* #channels */, 0 /*tDim*/, false /*shiftBoundingBoxMinimumToZero*/ ); // vecImg->Initialize(PixelType(typeid(itk::Vector)), *imgMem->GetGeometry(), 2 /* #channels */, 0 /*tDim*/, // false /*shiftBoundingBoxMinimumToZero*/ ); // testing access by index coordinates and by world coordinates MITK_TEST_CONDITION_REQUIRED(argc == 2, "Check if test image is accessible!"); const std::string filename = std::string(argv[1]); mitk::Image::Pointer image; try { image = dynamic_cast(mitk::IOUtil::Load(filename)[0].GetPointer()); MITK_TEST_CONDITION_REQUIRED(image.IsNotNull(), "Non-nullptr image") } catch (...) { MITK_TEST_FAILED_MSG(<< "Could not read file for testing: " << filename); return 0; } mitk::Point3D point; mitk::ScalarType value = -1.; mitkPixelTypeMultiplex3( TestRandomPixelAccess, image->GetImageDescriptor()->GetChannelTypeById(0), image, point, value) { // testing the clone method of mitk::Image mitk::Image::Pointer cloneImage = image->Clone(); MITK_TEST_CONDITION_REQUIRED(cloneImage->GetDimension() == image->GetDimension(), "Clone (testing dimension)"); MITK_TEST_CONDITION_REQUIRED(cloneImage->GetPixelType() == image->GetPixelType(), "Clone (testing pixel type)"); // After cloning an image the geometry of both images should be equal too MITK_TEST_CONDITION_REQUIRED(cloneImage->GetGeometry()->GetOrigin() == image->GetGeometry()->GetOrigin(), "Clone (testing origin)"); MITK_TEST_CONDITION_REQUIRED(cloneImage->GetGeometry()->GetSpacing() == image->GetGeometry()->GetSpacing(), "Clone (testing spacing)"); MITK_TEST_CONDITION_REQUIRED( mitk::MatrixEqualElementWise(cloneImage->GetGeometry()->GetIndexToWorldTransform()->GetMatrix(), image->GetGeometry()->GetIndexToWorldTransform()->GetMatrix()), "Clone (testing transformation matrix)"); MITK_TEST_CONDITION_REQUIRED( mitk::MatrixEqualElementWise(cloneImage->GetTimeGeometry() ->GetGeometryForTimeStep(cloneImage->GetDimension(3) - 1) ->GetIndexToWorldTransform() ->GetMatrix(), cloneImage->GetTimeGeometry() ->GetGeometryForTimeStep(image->GetDimension(3) - 1) ->GetIndexToWorldTransform() ->GetMatrix()), "Clone(testing time sliced geometry)"); for (unsigned int i = 0u; i < cloneImage->GetDimension(); ++i) { MITK_TEST_CONDITION_REQUIRED(cloneImage->GetDimension(i) == image->GetDimension(i), "Clone (testing dimension " << i << ")"); } } // access via itk if (image->GetDimension() > 3) // CastToItk only works with 3d images so we need to check for 4d images { mitk::ImageTimeSelector::Pointer selector = mitk::ImageTimeSelector::New(); selector->SetTimeNr(0); selector->SetInput(image); selector->Update(); image = selector->GetOutput(); } if (image->GetDimension() == 3) { typedef itk::Image ItkFloatImage3D; ItkFloatImage3D::Pointer itkimage; try { mitk::CastToItkImage(image, itkimage); MITK_TEST_CONDITION_REQUIRED(itkimage.IsNotNull(), "Test conversion to itk::Image!"); } catch (std::exception &e) { MITK_INFO << e.what(); } mitk::Point3D itkPhysicalPoint; image->GetGeometry()->WorldToItkPhysicalPoint(point, itkPhysicalPoint); MITK_INFO << "ITKPoint " << itkPhysicalPoint[0] << " " << itkPhysicalPoint[1] << " " << itkPhysicalPoint[2] << ""; mitk::Point3D backTransformedPoint; image->GetGeometry()->ItkPhysicalPointToWorld(itkPhysicalPoint, backTransformedPoint); MITK_TEST_CONDITION_REQUIRED(mitk::Equal(point, backTransformedPoint), "Testing world->itk-physical->world consistency"); itk::Index<3> idx; bool status = itkimage->TransformPhysicalPointToIndex(itkPhysicalPoint, idx); MITK_INFO << "ITK Index " << idx[0] << " " << idx[1] << " " << idx[2] << ""; if (status && value != -1.) { float valByItk = itkimage->GetPixel(idx); MITK_TEST_CONDITION_REQUIRED(mitk::Equal(valByItk, value), "Compare value of pixel returned by mitk in comparison to itk"); } else { MITK_WARN << "Index is out buffered region!"; } } else { MITK_INFO << "Image does not contain three dimensions, some test cases are skipped!"; } // clone generated 3D image with one slice in z direction (cf. bug 11058) - unsigned int *threeDdim = new unsigned int[3]; + auto *threeDdim = new unsigned int[3]; threeDdim[0] = 100; threeDdim[1] = 200; threeDdim[2] = 1; mitk::Image::Pointer threeDImage = mitk::Image::New(); threeDImage->Initialize(mitk::MakeScalarPixelType(), 3, threeDdim); mitk::Image::Pointer cloneThreeDImage = threeDImage->Clone(); // check that the clone image has the same dimensionality as the source image MITK_TEST_CONDITION_REQUIRED(cloneThreeDImage->GetDimension() == 3, "Testing if the clone image initializes with 3D!"); MITK_TEST_CONDITION_REQUIRED(ImageVtkDataReferenceCheck(argv[1]), "Checking reference count of Image after using GetVtkImageData()"); MITK_TEST_END(); } diff --git a/Modules/Core/test/mitkImageTimeSelectorTest.cpp b/Modules/Core/test/mitkImageTimeSelectorTest.cpp index 4232c35558..2bea7c99fa 100644 --- a/Modules/Core/test/mitkImageTimeSelectorTest.cpp +++ b/Modules/Core/test/mitkImageTimeSelectorTest.cpp @@ -1,113 +1,113 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkImage.h" #include "mitkImageGenerator.h" #include "mitkImageTimeSelector.h" #include "mitkTestingMacros.h" #include "mitkIOUtil.h" #include #include /** Global members common for all subtests */ namespace { std::string m_Filename; mitk::Image::Pointer m_Image; } // end of anonymous namespace /** @brief Global test setup */ static void Setup() { try { m_Image = dynamic_cast(mitk::IOUtil::Load(m_Filename)[0].GetPointer()); } catch (const itk::ExceptionObject &e) { MITK_TEST_FAILED_MSG(<< "(Setup) Caught exception from IOUtil while loading input : " << m_Filename << "\n" << e.what()) } } static void Valid_AllInputTimesteps_ReturnsTrue() { Setup(); mitk::ImageTimeSelector::Pointer timeSelector = mitk::ImageTimeSelector::New(); timeSelector->SetInput(m_Image); // test all timesteps const unsigned int maxTimeStep = m_Image->GetTimeSteps(); for (unsigned int t = 0; t < maxTimeStep; t++) { timeSelector->SetTimeNr(t); timeSelector->Update(); mitk::Image::Pointer currentTimestepImage = timeSelector->GetOutput(); std::stringstream ss; ss << " : Valid image in timestep " << t; MITK_TEST_CONDITION_REQUIRED(currentTimestepImage.IsNotNull(), ss.str().c_str()); } } static void Valid_ImageExpandedByTimestep_ReturnsTrue() { Setup(); mitk::ImageTimeSelector::Pointer timeSelector = mitk::ImageTimeSelector::New(); const unsigned int maxTimeStep = m_Image->GetTimeSteps(); mitk::TimeGeometry *tsg = m_Image->GetTimeGeometry(); - mitk::ProportionalTimeGeometry *ptg = dynamic_cast(tsg); + auto *ptg = dynamic_cast(tsg); ptg->Expand(maxTimeStep + 1); ptg->SetTimeStepGeometry(ptg->GetGeometryForTimeStep(0), maxTimeStep); mitk::Image::Pointer expandedImage = mitk::Image::New(); expandedImage->Initialize(m_Image->GetPixelType(0), *tsg); timeSelector->SetInput(expandedImage); for (unsigned int t = 0; t < maxTimeStep + 1; t++) { timeSelector->SetTimeNr(t); timeSelector->Update(); mitk::Image::Pointer currentTimestepImage = timeSelector->GetOutput(); std::stringstream ss; ss << " : Valid image in timestep " << t; MITK_TEST_CONDITION_REQUIRED(currentTimestepImage.IsNotNull(), ss.str().c_str()); } } int mitkImageTimeSelectorTest(int /*argc*/, char *argv[]) { MITK_TEST_BEGIN(mitkImageTimeSelectorTest); m_Filename = std::string(argv[1]); Valid_AllInputTimesteps_ReturnsTrue(); Valid_ImageExpandedByTimestep_ReturnsTrue(); MITK_TEST_END(); } diff --git a/Modules/Core/test/mitkLevelWindowTest.cpp b/Modules/Core/test/mitkLevelWindowTest.cpp index 433bae6ba6..a4f855ed80 100644 --- a/Modules/Core/test/mitkLevelWindowTest.cpp +++ b/Modules/Core/test/mitkLevelWindowTest.cpp @@ -1,970 +1,970 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkImageWriteAccessor.h" #include "mitkLevelWindow.h" #include /* * Reseting the Levelwindow to default values: * i.e. Range = -1000..1000, Level = 0 and Window = 500 */ void resetLevelWindow(mitk::LevelWindow &lw) { // Default window bounds lw.SetRangeMinMax(-10000, 10000); lw.SetLevelWindow(0, 500, false); if (lw.GetRangeMin() != -10000 || lw.GetRangeMax() != 10000 || lw.GetLevel() != 0 || lw.GetWindow() != 500) { std::cout << "[Failed] To reset Levelwindow" << std::endl; } } int mitkLevelWindowTest(int, char *[]) { std::cout << "Testing mitk::LevelWindow " << std::endl; std::cout << "Testing mitk::LevelWindow constructor with Level and Window "; auto levWin = new mitk::LevelWindow(256, 500); std::cout << "[PASSED]" << std::endl; std::cout << "Testing mitk::LevelWindow GetDefaultWindow "; mitk::ScalarType defaultWindow = levWin->GetDefaultWindow(); if (!(defaultWindow == 500)) { std::cout << defaultWindow << " [FAILED]" << std::endl; return EXIT_FAILURE; } std::cout << "[PASSED]" << std::endl; std::cout << "Testing mitk::LevelWindow GetDefaultLevel "; mitk::ScalarType defaultLevel = levWin->GetDefaultLevel(); if (!(defaultLevel == 256)) { std::cout << "[FAILED]" << std::endl; return EXIT_FAILURE; } std::cout << "[PASSED]" << std::endl; std::cout << "Testing mitk::LevelWindow GetWindow "; mitk::ScalarType window = levWin->GetWindow(); if (!(window == 500)) { std::cout << "[FAILED]" << std::endl; return EXIT_FAILURE; } std::cout << "[PASSED]" << std::endl; std::cout << "Testing mitk::LevelWindow GetMin "; if (!(levWin->GetLowerWindowBound() == 6)) { std::cout << "[FAILED]" << std::endl; return EXIT_FAILURE; } std::cout << "[PASSED]" << std::endl; std::cout << "Testing mitk::LevelWindow GetMax "; if (!(levWin->GetUpperWindowBound() == 506)) { std::cout << "[FAILED]" << std::endl; return EXIT_FAILURE; } std::cout << "[PASSED]" << std::endl; std::cout << "Testing mitk::LevelWindow GetLevel "; mitk::ScalarType level = levWin->GetLevel(); if (!(level == 256)) { std::cout << "[FAILED]" << std::endl; return EXIT_FAILURE; } std::cout << "[PASSED]" << std::endl; std::cout << "Testing mitk::LevelWindow GetWindow : GetDefaultWindow "; if (!(defaultWindow == window)) { std::cout << "[FAILED]" << std::endl; return EXIT_FAILURE; } std::cout << "[PASSED]" << std::endl; std::cout << "Testing mitk::LevelWindow GetLevel : GetDefaultLevel "; if (!(defaultLevel == level)) { std::cout << "[FAILED]" << std::endl; return EXIT_FAILURE; } std::cout << "[PASSED]" << std::endl; std::cout << "Testing mitk::LevelWindow SetLevelWindow "; levWin->SetLevelWindow(20, 100); if (!(levWin->GetLevel() == 20)) { std::cout << "[FAILED]" << std::endl; return EXIT_FAILURE; } if (!(levWin->GetWindow() == 100)) { std::cout << "[FAILED]" << std::endl; return EXIT_FAILURE; } std::cout << "[PASSED]" << std::endl; std::cout << "Testing mitk::LevelWindow SetLevelWindow "; levWin->SetLevelWindow(levWin->GetDefaultLevel(), levWin->GetDefaultWindow()); if (!(levWin->GetLevel() == 256) && !(levWin->GetWindow() == 500)) { std::cout << "[FAILED]" << std::endl; return EXIT_FAILURE; } std::cout << "[PASSED]" << std::endl; std::cout << "Testing mitk::LevelWindow SetDefaultLevelWindow "; levWin->SetDefaultLevelWindow(20, 200); if (!(levWin->GetDefaultLevel() == 20) && !(levWin->GetDefaultWindow() == 200)) { std::cout << "[FAILED]" << std::endl; return EXIT_FAILURE; } std::cout << "[PASSED]" << std::endl; std::cout << "Testing mitk::LevelWindow ResetDefaultLevelWindow "; levWin->SetLevelWindow(100, 50); levWin->ResetDefaultLevelWindow(); // double a = levWin->GetLevel(); // double d = levWin->GetWindow(); if (!((levWin->GetLevel() == 20) && (levWin->GetWindow() == 200))) { std::cout << "[FAILED]" << std::endl; return EXIT_FAILURE; } std::cout << "[PASSED]" << std::endl; std::cout << "Testing mitk::LevelWindow SetWindowBounds "; levWin->SetWindowBounds(0, 2); if (!((levWin->GetLowerWindowBound() == 0) && (levWin->GetUpperWindowBound() == 2) && (levWin->GetLevel() == 1) && (levWin->GetWindow() == 2))) { std::cout << "[FAILED]" << std::endl; return EXIT_FAILURE; } levWin->ResetDefaultLevelWindow(); std::cout << "[PASSED]" << std::endl; std::cout << "Testing mitk::LevelWindow SetRangeMinMax with rangemin = rangemax"; levWin->SetRangeMinMax(2000, 2000); if (!(levWin->GetRangeMin() == 1999 && levWin->GetRangeMax() == 2000)) { std::cout << "[FAILED]" << std::endl; return EXIT_FAILURE; } std::cout << "[PASSED]" << std::endl; std::cout << "Testing mitk::LevelWindow SetRangeMinMax with rangemin > rangemax"; levWin->SetRangeMinMax(2100, 2000); if (!(levWin->GetRangeMin() == 2000 && levWin->GetRangeMax() == 2100)) { std::cout << "[FAILED]" << std::endl; return EXIT_FAILURE; } std::cout << "[PASSED]" << std::endl; std::cout << "Testing mitk::LevelWindow SetRangeMinMax "; levWin->SetRangeMinMax(-1000, 2000); std::cout << "[PASSED]" << std::endl; std::cout << "Testing mitk::LevelWindow GetRangeMin "; if (!(levWin->GetRangeMin() == -1000)) { std::cout << "[FAILED]" << std::endl; return EXIT_FAILURE; } std::cout << "[PASSED]" << std::endl; std::cout << "Testing mitk::LevelWindow GetRangeMax "; if (!(levWin->GetRangeMax() == 2000)) { std::cout << "[FAILED]" << std::endl; return EXIT_FAILURE; } std::cout << "[PASSED]" << std::endl; std::cout << "Testing mitk::LevelWindow GetRange "; if (!((levWin->GetRangeMax() - levWin->GetRangeMin()) == levWin->GetRange())) { std::cout << "[FAILED]" << std::endl; return EXIT_FAILURE; } std::cout << "[PASSED]" << std::endl; std::cout << "Testing mitk::LevelWindow SetDefaultBoundaries with rangemin = rangemax"; levWin->SetDefaultBoundaries(2000, 2000); if (!(levWin->GetDefaultLowerBound() == 1999 && levWin->GetDefaultUpperBound() == 2000)) { std::cout << "[FAILED]" << std::endl; return EXIT_FAILURE; } std::cout << "[PASSED]" << std::endl; std::cout << "Testing mitk::LevelWindow SetDefaultBoundaries with rangemin > rangemax"; levWin->SetDefaultBoundaries(2100, 2000); if (!(levWin->GetDefaultLowerBound() == 2000 && levWin->GetDefaultUpperBound() == 2100)) { std::cout << "[FAILED]" << std::endl; return EXIT_FAILURE; } std::cout << "[PASSED]" << std::endl; std::cout << "Testing mitk::LevelWindow SetDefaultBoundaries "; levWin->SetDefaultBoundaries(-2000, 8000); std::cout << "[PASSED]" << std::endl; std::cout << "Testing mitk::LevelWindow GetDefaultLowerBound "; if (!(levWin->GetDefaultLowerBound() == -2000)) { std::cout << "[FAILED]" << std::endl; return EXIT_FAILURE; } std::cout << "[PASSED]" << std::endl; std::cout << "Testing mitk::LevelWindow GetDefaultUpperBound "; if (!(levWin->GetDefaultUpperBound() == 8000)) { std::cout << "[FAILED]" << std::endl; return EXIT_FAILURE; } std::cout << "[PASSED]" << std::endl; std::cout << "Testing mitk::LevelWindow ResetDefaultRangeMinMax "; levWin->ResetDefaultRangeMinMax(); if (!((levWin->GetRangeMin() == levWin->GetDefaultLowerBound()) && (levWin->GetRangeMax() == levWin->GetDefaultUpperBound()))) { std::cout << "[FAILED]" << std::endl; return EXIT_FAILURE; } std::cout << "[PASSED]" << std::endl; std::cout << "Testing mitk::LevelWindow minRange > maxRange "; levWin->SetRangeMinMax(2000, 1000); if (!((levWin->GetRangeMin() == 1000) && (levWin->GetRangeMax() == 2000))) { std::cout << "[FAILED]" << std::endl; return EXIT_FAILURE; } levWin->SetRangeMinMax(2000, -1000); if (!((levWin->GetRangeMin() == -1000) && (levWin->GetRangeMax() == 2000))) { std::cout << "[FAILED]" << std::endl; return EXIT_FAILURE; } levWin->SetRangeMinMax(-2000, -3000); if (!((levWin->GetRangeMin() == -3000) && (levWin->GetRangeMax() == -2000))) { std::cout << "[FAILED]" << std::endl; return EXIT_FAILURE; } levWin->SetRangeMinMax(0, -1000); if (!((levWin->GetRangeMin() == -1000) && (levWin->GetRangeMax() == 0))) { std::cout << "[FAILED]" << std::endl; return EXIT_FAILURE; } levWin->SetRangeMinMax(2000, 0); if (!((levWin->GetRangeMin() == 0) && (levWin->GetRangeMax() == 2000))) { std::cout << "[FAILED]" << std::endl; return EXIT_FAILURE; } levWin->SetRangeMinMax(-10000, 10000); std::cout << "[PASSED]" << std::endl; std::cout << "Testing mitk::LevelWindow defaultMinRange > defaultMaxRange "; levWin->SetDefaultBoundaries(2000, 1000); if (!((levWin->GetDefaultLowerBound() == 1000) && (levWin->GetDefaultUpperBound() == 2000))) { std::cout << "[FAILED]" << std::endl; return EXIT_FAILURE; } levWin->SetDefaultBoundaries(2000, -1000); if (!((levWin->GetDefaultLowerBound() == -1000) && (levWin->GetDefaultUpperBound() == 2000))) { std::cout << "[FAILED]" << std::endl; return EXIT_FAILURE; } levWin->SetDefaultBoundaries(-2000, -3000); if (!((levWin->GetDefaultLowerBound() == -3000) && (levWin->GetDefaultUpperBound() == -2000))) { std::cout << "[FAILED]" << std::endl; return EXIT_FAILURE; } levWin->SetDefaultBoundaries(0, -1000); if (!((levWin->GetDefaultLowerBound() == -1000) && (levWin->GetDefaultUpperBound() == 0))) { std::cout << "[FAILED]" << std::endl; return EXIT_FAILURE; } levWin->SetDefaultBoundaries(2000, 0); if (!((levWin->GetDefaultLowerBound() == 0) && (levWin->GetDefaultUpperBound() == 2000))) { std::cout << "[FAILED]" << std::endl; return EXIT_FAILURE; } levWin->SetDefaultBoundaries(-10000, 10000); std::cout << "[PASSED]" << std::endl; std::cout << "Testing mitk::LevelWindow min > max "; levWin->SetWindowBounds(2000, 1000); if (!((levWin->GetLowerWindowBound() == 1000) && (levWin->GetUpperWindowBound() == 2000))) { std::cout << "[FAILED]" << std::endl; return EXIT_FAILURE; } levWin->SetWindowBounds(2000, -1000); if (!((levWin->GetLowerWindowBound() == -1000) && (levWin->GetUpperWindowBound() == 2000))) { std::cout << "[FAILED]" << std::endl; return EXIT_FAILURE; } levWin->SetWindowBounds(-2000, -3000); if (!((levWin->GetLowerWindowBound() == -3000) && (levWin->GetUpperWindowBound() == -2000))) { std::cout << "[FAILED]" << std::endl; return EXIT_FAILURE; } levWin->SetWindowBounds(0, -1000); if (!((levWin->GetLowerWindowBound() == -1000) && (levWin->GetUpperWindowBound() == 0))) { std::cout << "[FAILED]" << std::endl; return EXIT_FAILURE; } levWin->SetWindowBounds(2000, 0); if (!((levWin->GetLowerWindowBound() == 0) && (levWin->GetUpperWindowBound() == 2000))) { std::cout << "[FAILED]" << std::endl; return EXIT_FAILURE; } levWin->ResetDefaultLevelWindow(); std::cout << "[PASSED]" << std::endl; // minmax > maxrange, minmax < minrange, minmaxrange, min < minrange & max > minrange // max < minrange & min > minrange, min > maxrange & max < maxrange, min < minrange & max > maxrange // min > maxrange & max < minrange std::cout << "Testing mitk::LevelWindow max > min > maxrange autoexpand = FALSE"; levWin->SetWindowBounds(11000, 12000, false); if (!((levWin->GetLowerWindowBound() == 9999) && (levWin->GetUpperWindowBound() == 10000))) { std::cout << "[FAILED]" << levWin->GetLowerWindowBound() << "," << levWin->GetUpperWindowBound() << std::endl; return EXIT_FAILURE; } levWin->ResetDefaultLevelWindow(); std::cout << "[PASSED]" << std::endl; std::cout << "Testing mitk::LevelWindow max > min > maxrange autoexpand = TRUE"; levWin->SetWindowBounds(11000, 12000); if (!((levWin->GetLowerWindowBound() == 11000) && (levWin->GetUpperWindowBound() == 12000))) { std::cout << "[FAILED]" << levWin->GetLowerWindowBound() << "," << levWin->GetUpperWindowBound() << std::endl; return EXIT_FAILURE; } levWin->ResetDefaultLevelWindow(); std::cout << "[PASSED]" << std::endl; // Reset default window resetLevelWindow(*levWin); std::cout << "Testing mitk::LevelWindow min > max > maxrange autoexpand = FALSE"; levWin->SetWindowBounds(12000, 11000, false); if (!((levWin->GetLowerWindowBound() == 9999) && (levWin->GetUpperWindowBound() == 10000))) { std::cout << "[FAILED]" << std::endl; return EXIT_FAILURE; } levWin->ResetDefaultLevelWindow(); std::cout << "[PASSED]" << std::endl; std::cout << "Testing mitk::LevelWindow min > max > maxrange autoexpand = TRUE"; levWin->SetWindowBounds(12000, 11000); if (!((levWin->GetLowerWindowBound() == 11000) && (levWin->GetUpperWindowBound() == 12000))) { std::cout << "[FAILED]" << std::endl; return EXIT_FAILURE; } levWin->ResetDefaultLevelWindow(); std::cout << "[PASSED]" << std::endl; resetLevelWindow(*levWin); std::cout << "Testing mitk::LevelWindow min < max < minrange autoexpand = FALSE"; levWin->SetWindowBounds(-12000, -11000, false); if (!((levWin->GetLowerWindowBound() == -10000) && (levWin->GetUpperWindowBound() == -9999))) { std::cout << "[FAILED]" << std::endl; return EXIT_FAILURE; } levWin->ResetDefaultLevelWindow(); std::cout << "[PASSED]" << std::endl; std::cout << "Testing mitk::LevelWindow min < max < minrange autoexpand = TRUE"; levWin->SetWindowBounds(-12000, -11000); if (!((levWin->GetLowerWindowBound() == -12000) && (levWin->GetUpperWindowBound() == -11000))) { std::cout << "[FAILED]" << std::endl; return EXIT_FAILURE; } levWin->ResetDefaultLevelWindow(); std::cout << "[PASSED]" << std::endl; resetLevelWindow(*levWin); std::cout << "Testing mitk::LevelWindow max < min < minrange autoexpand = FALSE"; levWin->SetWindowBounds(-11000, -12000, false); if (!((levWin->GetLowerWindowBound() == -10000) && (levWin->GetUpperWindowBound() == -9999))) { std::cout << "[FAILED]: Expected (-10000, -9999): " << levWin->GetLowerWindowBound() << " - " << levWin->GetUpperWindowBound() << std::endl; return EXIT_FAILURE; } levWin->ResetDefaultLevelWindow(); std::cout << "[PASSED]" << std::endl; std::cout << "Testing mitk::LevelWindow max < min < minrange autoexpand = TRUE"; levWin->SetWindowBounds(-11000, -12000); if (!((levWin->GetLowerWindowBound() == -12000) && (levWin->GetUpperWindowBound() == -11000))) { std::cout << "[FAILED]" << std::endl; return EXIT_FAILURE; } levWin->ResetDefaultLevelWindow(); std::cout << "[PASSED]" << std::endl; resetLevelWindow(*levWin); std::cout << "Testing mitk::LevelWindow min < maxrang & max > maxrange autoexpand = FALSE"; levWin->SetWindowBounds(9999, 12000, false); if (!((levWin->GetLowerWindowBound() == 9999) && (levWin->GetUpperWindowBound() == 10000))) { std::cout << "[FAILED]" << std::endl; return EXIT_FAILURE; } levWin->ResetDefaultLevelWindow(); std::cout << "[PASSED]" << std::endl; std::cout << "Testing mitk::LevelWindow min < maxrang & max > maxrange autoexpand = TRUE"; levWin->SetWindowBounds(9999, 12000); if (!((levWin->GetLowerWindowBound() == 9999) && (levWin->GetUpperWindowBound() == 12000))) { std::cout << "[FAILED]" << std::endl; return EXIT_FAILURE; } levWin->ResetDefaultLevelWindow(); std::cout << "[PASSED]" << std::endl; resetLevelWindow(*levWin); std::cout << "Testing mitk::LevelWindow min < minrange & max > minrange autoexpand = FALSE"; levWin->SetWindowBounds(-11000, -9999, false); if (!((levWin->GetLowerWindowBound() == -10000) && (levWin->GetUpperWindowBound() == -9999))) { std::cout << "[FAILED]" << std::endl; return EXIT_FAILURE; } levWin->ResetDefaultLevelWindow(); std::cout << "[PASSED]" << std::endl; std::cout << "Testing mitk::LevelWindow min < minrange & max > minrange autoexpand = TRUE"; levWin->SetWindowBounds(-11000, -9999); if (!((levWin->GetLowerWindowBound() == -11000) && (levWin->GetUpperWindowBound() == -9999))) { std::cout << "[FAILED]" << std::endl; return EXIT_FAILURE; } levWin->ResetDefaultLevelWindow(); std::cout << "[PASSED]" << std::endl; resetLevelWindow(*levWin); std::cout << "Testing mitk::LevelWindow min < minrange & max > maxrange autoexpand = FALSE"; levWin->SetWindowBounds(-11000, 11000, false); if (!((levWin->GetLowerWindowBound() == -10000) && (levWin->GetUpperWindowBound() == 10000))) { std::cout << "[FAILED]" << std::endl; return EXIT_FAILURE; } levWin->ResetDefaultLevelWindow(); std::cout << "[PASSED]" << std::endl; std::cout << "Testing mitk::LevelWindow min < minrange & max > maxrange autoexpand = TRUE"; levWin->SetWindowBounds(-11000, 11000); if (!((levWin->GetLowerWindowBound() == -11000) && (levWin->GetUpperWindowBound() == 11000))) { std::cout << "[FAILED]" << std::endl; return EXIT_FAILURE; } levWin->ResetDefaultLevelWindow(); std::cout << "[PASSED]" << std::endl; resetLevelWindow(*levWin); std::cout << "Testing mitk::LevelWindow maxrange > min = max > minrange autoexpand = FALSE"; levWin->SetWindowBounds(5000, 5000, false); if (!((levWin->GetLowerWindowBound() == 4999.5) && (levWin->GetUpperWindowBound() == 5000.5))) { std::cout << "[FAILED]: Expected (4999.5, 5000.5): " << levWin->GetLowerWindowBound() << " - " << levWin->GetUpperWindowBound() << std::endl; return EXIT_FAILURE; } levWin->ResetDefaultLevelWindow(); std::cout << "[PASSED]" << std::endl; std::cout << "Testing mitk::LevelWindow maxrange > min = max > minrange autoexpand = TRUE"; levWin->SetWindowBounds(5000, 5000); if (!((levWin->GetLowerWindowBound() == 4999.5) && (levWin->GetUpperWindowBound() == 5000.5))) { std::cout << "[FAILED]" << std::endl; return EXIT_FAILURE; } levWin->ResetDefaultLevelWindow(); std::cout << "[PASSED]" << std::endl; resetLevelWindow(*levWin); std::cout << "Testing mitk::LevelWindow min = max = minrange autoexpand = FALSE"; levWin->SetWindowBounds(-10000, -10000, false); if (!((levWin->GetLowerWindowBound() == -10000) && (levWin->GetUpperWindowBound() == -9999))) { std::cout << "[FAILED]: Expected (-10000, -9999): " << levWin->GetLowerWindowBound() << " - " << levWin->GetUpperWindowBound() << " . " << levWin->GetRangeMin() << std::endl; return EXIT_FAILURE; } levWin->ResetDefaultLevelWindow(); std::cout << "[PASSED]" << std::endl; std::cout << "Testing mitk::LevelWindow min = max = minrange autoexpand = TRUE"; levWin->SetWindowBounds(-10000, -10000); if (!((levWin->GetLowerWindowBound() == -10000) && (levWin->GetUpperWindowBound() == -9999))) { std::cout << "[FAILED]" << std::endl; return EXIT_FAILURE; } levWin->ResetDefaultLevelWindow(); std::cout << "[PASSED]" << std::endl; resetLevelWindow(*levWin); std::cout << "Testing mitk::LevelWindow min = max = maxrange autoexpand = FALSE"; levWin->SetWindowBounds(10000, 10000, false); if (!((levWin->GetLowerWindowBound() == 9999) && (levWin->GetUpperWindowBound() == 10000))) { std::cout << "[FAILED]" << std::endl; return EXIT_FAILURE; } levWin->ResetDefaultLevelWindow(); std::cout << "[PASSED]" << std::endl; std::cout << "Testing mitk::LevelWindow min = max = maxrange autoexpand = TRUE"; levWin->SetWindowBounds(10000, 10000); if (!((levWin->GetLowerWindowBound() == 9999) && (levWin->GetUpperWindowBound() == 10000))) { std::cout << "[FAILED]" << std::endl; return EXIT_FAILURE; } levWin->ResetDefaultLevelWindow(); std::cout << "[PASSED]" << std::endl; resetLevelWindow(*levWin); std::cout << "Testing mitk::LevelWindow min = max > maxrange autoexpand = FALSE"; levWin->SetWindowBounds(11000, 11000, false); if (!((levWin->GetLowerWindowBound() == 9999) && (levWin->GetUpperWindowBound() == 10000))) { std::cout << "[FAILED]" << std::endl; return EXIT_FAILURE; } levWin->ResetDefaultLevelWindow(); std::cout << "[PASSED]" << std::endl; std::cout << "Testing mitk::LevelWindow min = max > maxrange autoexpand = TRUE"; levWin->SetWindowBounds(11000, 11000); if (!((levWin->GetLowerWindowBound() == 10999) && (levWin->GetUpperWindowBound() == 11000))) { std::cout << "[FAILED]" << std::endl; return EXIT_FAILURE; } levWin->ResetDefaultLevelWindow(); std::cout << "[PASSED]" << std::endl; resetLevelWindow(*levWin); std::cout << "Testing mitk::LevelWindow min = max < minrange autoexpand = FALSE"; levWin->SetWindowBounds(-11000, -11000, false); if (!((levWin->GetLowerWindowBound() == -10000) && (levWin->GetUpperWindowBound() == -9999))) { std::cout << "[FAILED]" << std::endl; return EXIT_FAILURE; } levWin->ResetDefaultLevelWindow(); std::cout << "[PASSED]" << std::endl; std::cout << "Testing mitk::LevelWindow min = max < minrange autoexpand = TRUE"; levWin->SetWindowBounds(-11000, -11000); if (!((levWin->GetLowerWindowBound() == -11000) && (levWin->GetUpperWindowBound() == -10999))) { std::cout << "[FAILED]" << std::endl; return EXIT_FAILURE; } levWin->ResetDefaultLevelWindow(); std::cout << "[PASSED]" << std::endl; resetLevelWindow(*levWin); std::cout << "Testing mitk::LevelWindow maxrange > min > minrange > max autoexpand = FALSE"; levWin->SetWindowBounds(-9000, -11000, false); if (!((levWin->GetLowerWindowBound() == -10000) && (levWin->GetUpperWindowBound() == -9000))) { std::cout << "[FAILED]" << std::endl; return EXIT_FAILURE; } levWin->ResetDefaultLevelWindow(); std::cout << "[PASSED]" << std::endl; std::cout << "Testing mitk::LevelWindow maxrange > min > minrange > max autoexpand = TRUE"; levWin->SetWindowBounds(-9000, -11000, true); if (!((levWin->GetLowerWindowBound() == -11000) && (levWin->GetUpperWindowBound() == -9000))) { std::cout << "[FAILED]" << std::endl; return EXIT_FAILURE; } levWin->ResetDefaultLevelWindow(); std::cout << "[PASSED]" << std::endl; resetLevelWindow(*levWin); std::cout << "Testing mitk::LevelWindow min > maxrange > minrange > max autoexpand = FALSE"; levWin->SetWindowBounds(11000, -11000, false); if (!((levWin->GetLowerWindowBound() == -10000) && (levWin->GetUpperWindowBound() == 10000))) { std::cout << "[FAILED]" << std::endl; return EXIT_FAILURE; } levWin->ResetDefaultLevelWindow(); std::cout << "[PASSED]" << std::endl; std::cout << "Testing mitk::LevelWindow min > maxrange > minrange > max autoexpand = TRUE"; levWin->SetWindowBounds(11000, -11000); if (!((levWin->GetLowerWindowBound() == -11000) && (levWin->GetUpperWindowBound() == 11000))) { std::cout << "[FAILED]" << std::endl; return EXIT_FAILURE; } levWin->ResetDefaultLevelWindow(); std::cout << "[PASSED]" << std::endl; resetLevelWindow(*levWin); std::cout << "Testing mitk::LevelWindow SetRangeMinMax with maxrange < min < max autoexpand = FALSE"; levWin->SetRangeMinMax(-20000, -15000); if (!((levWin->GetLowerWindowBound() == -15001) && (levWin->GetUpperWindowBound() == -15000))) { std::cout << "[FAILED]" << std::endl; return EXIT_FAILURE; } levWin->ResetDefaultRangeMinMax(); levWin->ResetDefaultLevelWindow(); std::cout << "[PASSED]" << std::endl; std::cout << "Testing mitk::LevelWindow SetRangeMinMax with minrange > maxrange & maxrange < min < max "; levWin->ResetDefaultLevelWindow(); levWin->SetRangeMinMax(-15000, -20000); if (!((levWin->GetLowerWindowBound() == -15001) && (levWin->GetUpperWindowBound() == -15000))) { std::cout << "[FAILED]" << std::endl; return EXIT_FAILURE; } levWin->ResetDefaultRangeMinMax(); levWin->ResetDefaultLevelWindow(); std::cout << "[PASSED]" << std::endl; std::cout << "Testing mitk::LevelWindow SetRangeMinMax with minrange < min < maxrange < max autoexpand = FALSE"; levWin->SetRangeMinMax(-80, 1000); levWin->SetWindowBounds(-1000, 110, false); if (!((levWin->GetLowerWindowBound() == -80) && (levWin->GetUpperWindowBound() == 110))) { std::cout << "[FAILED]" << std::endl; return EXIT_FAILURE; } levWin->ResetDefaultRangeMinMax(); levWin->ResetDefaultLevelWindow(); std::cout << "[PASSED]" << std::endl; std::cout << "Testing mitk::LevelWindow SetRangeMinMax with minrange < min < maxrange < max autoexpand = TRUE"; levWin->SetRangeMinMax(-80, 1000); levWin->SetWindowBounds(-1000, 110); if (!((levWin->GetLowerWindowBound() == -1000) && (levWin->GetUpperWindowBound() == 110))) { std::cout << "[FAILED]" << std::endl; return EXIT_FAILURE; } levWin->ResetDefaultRangeMinMax(); levWin->ResetDefaultLevelWindow(); std::cout << "[PASSED]" << std::endl; resetLevelWindow(*levWin); std::cout << "Testing mitk::LevelWindow SetRangeMinMax with maxrange < minrange & minrange < min < maxrange < max " "autoexpand = FALSE"; levWin->SetRangeMinMax(1000, -80); levWin->SetWindowBounds(-1000, 110, false); if (!((levWin->GetLowerWindowBound() == -80) && (levWin->GetUpperWindowBound() == 110))) { std::cout << "[FAILED]" << std::endl; return EXIT_FAILURE; } levWin->ResetDefaultRangeMinMax(); levWin->ResetDefaultLevelWindow(); std::cout << "[PASSED]" << std::endl; std::cout << "Testing mitk::LevelWindow SetRangeMinMax with maxrange < minrange & minrange < min < maxrange < max " "autoexpand = TRUE"; levWin->SetRangeMinMax(1000, -80); levWin->SetWindowBounds(-1000, 110); if (!((levWin->GetLowerWindowBound() == -1000) && (levWin->GetUpperWindowBound() == 110))) { std::cout << "[FAILED]" << std::endl; return EXIT_FAILURE; } levWin->ResetDefaultRangeMinMax(); levWin->ResetDefaultLevelWindow(); std::cout << "[PASSED]" << std::endl; resetLevelWindow(*levWin); std::cout << "Testing mitk::LevelWindow SetRangeMinMax with min < minrange < maxrange SetRangeMinMax(20, 110); if (!((levWin->GetLowerWindowBound() == 20) && (levWin->GetUpperWindowBound() == 110))) { std::cout << "[FAILED]" << std::endl; return EXIT_FAILURE; } levWin->ResetDefaultRangeMinMax(); levWin->ResetDefaultLevelWindow(); std::cout << "[PASSED]" << std::endl; std::cout << "Testing mitk::LevelWindow SetRangeMinMax with minRange > maxRange & min < maxrange < max "; levWin->SetWindowBounds(-90, 1000); levWin->SetRangeMinMax(100, -80); if (!((levWin->GetLowerWindowBound() == -80) && (levWin->GetUpperWindowBound() == 100))) { std::cout << "[FAILED]" << std::endl; return EXIT_FAILURE; } levWin->ResetDefaultRangeMinMax(); levWin->ResetDefaultLevelWindow(); std::cout << "[PASSED]" << std::endl; std::cout << "Testing mitk::LevelWindow SetRangeMinMax with minRange > maxRange & min < minrange < maxrange SetRangeMinMax(20, 100); if (!((levWin->GetLowerWindowBound() == 20) && (levWin->GetUpperWindowBound() == 100))) { std::cout << "[FAILED]" << std::endl; return EXIT_FAILURE; } levWin->ResetDefaultRangeMinMax(); levWin->ResetDefaultLevelWindow(); std::cout << "[PASSED]" << std::endl; std::cout << "Testing mitk::LevelWindow SetRangeMinMax with min < max < minrange "; levWin->SetRangeMinMax(20000, 15000); if (!((levWin->GetLowerWindowBound() == 15000) && (levWin->GetUpperWindowBound() == 15001))) { std::cout << "[FAILED]" << std::endl; return EXIT_FAILURE; } levWin->ResetDefaultRangeMinMax(); levWin->ResetDefaultLevelWindow(); std::cout << "[PASSED]" << std::endl; std::cout << "Testing mitk::LevelWindow SetRangeMinMax with minrange > maxrange & min < max < minrange "; levWin->SetRangeMinMax(20000, 15000); if (!((levWin->GetLowerWindowBound() == 15000) && (levWin->GetUpperWindowBound() == 15001))) { std::cout << "[FAILED]" << std::endl; return EXIT_FAILURE; } levWin->ResetDefaultRangeMinMax(); levWin->ResetDefaultLevelWindow(); std::cout << "[PASSED]" << std::endl; std::cout << "Testing mitk::LevelWindow SetRangeMinMax with min < minrange SetRangeMinMax(-20000, -15000); if (!((levWin->GetLowerWindowBound() == -15001) && (levWin->GetUpperWindowBound() == -15000))) { std::cout << "[FAILED]" << std::endl; return EXIT_FAILURE; } levWin->ResetDefaultRangeMinMax(); levWin->ResetDefaultLevelWindow(); std::cout << "[PASSED]" << std::endl; // auch für default levelwindow und default range // Create Image out of nowhere mitk::Image::Pointer image; // mitk::PixelType pt(typeid(int)); unsigned int dim[] = {100, 100, 20}; std::cout << "Creating image: "; image = mitk::Image::New(); // image->DebugOn(); image->Initialize(mitk::MakePixelType(), 3, dim); mitk::ImageWriteAccessor imAccess(image); - int *p = (int *)imAccess.GetData(); + auto *p = (int *)imAccess.GetData(); int size = dim[0] * dim[1] * dim[2]; int i; for (i = 0; i < size; ++i, ++p) *p = i; std::cout << "[PASSED]" << std::endl; std::cout << "Testing mitk::LevelWindow SetAuto "; mitk::LevelWindow levelwindow; levelwindow.SetAuto(image); std::cout << "[PASSED]" << std::endl; std::cout << "Testing mitk::LevelWindow constructor with mitkLevelWindow "; const mitk::LevelWindow *lw = new mitk::LevelWindow(levelwindow); if (!(lw->GetRange() == levelwindow.GetRange())) { std::cout << "[FAILED]" << std::endl; delete lw; return EXIT_FAILURE; } std::cout << "[PASSED]" << std::endl; std::cout << "Testing mitk::LevelWindow destructor "; delete levWin; delete lw; std::cout << "[PASSED]" << std::endl; mitk::LevelWindow levelWindow(50, 100); levelWindow.SetRangeMinMax(0, 100); // test range restriction/adaption for SetLevelWindow and SetWindowBounds std::cout << "Testing range restriction of mitk::LevelWindow::SetWindowBounds() autoexpand = FALSE"; mitk::ScalarType initialUpperBound = levelWindow.GetUpperWindowBound(); mitk::ScalarType initialLowerBound = levelWindow.GetLowerWindowBound(); levelWindow.SetWindowBounds(-10, 110, false); if (levelWindow.GetUpperWindowBound() != initialUpperBound || levelWindow.GetLowerWindowBound() != initialLowerBound) { std::cout << "[FAILED]" << std::endl; return EXIT_FAILURE; } std::cout << "[PASSED]" << std::endl; std::cout << "Testing range restriction of mitk::LevelWindow::SetWindowBounds() autoexpand = TRUE"; levelWindow.SetWindowBounds(-10, 110); if (levelWindow.GetUpperWindowBound() != 110 || levelWindow.GetLowerWindowBound() != -10) { std::cout << "[FAILED]" << std::endl; return EXIT_FAILURE; } std::cout << "[PASSED]" << std::endl; levelWindow.SetRangeMinMax(0, 100); levelWindow.SetLevelWindow(50, 100); std::cout << "Testing range restriction of mitk::LevelWindow::SetLevelWindow() autoexpand = FALSE"; levelWindow.SetLevelWindow(60, 100, false); if (levelWindow.GetUpperWindowBound() != initialUpperBound) { std::cout << "[FAILED]" << std::endl; return EXIT_FAILURE; } std::cout << "[PASSED]" << std::endl; std::cout << "Testing range restriction of mitk::LevelWindow::SetLevelWindow() autoexpand = TRUE"; levelWindow.SetLevelWindow(60, 100); if (levelWindow.GetUpperWindowBound() != initialUpperBound + 10) { std::cout << "[FAILED]" << std::endl; return EXIT_FAILURE; } std::cout << "[PASSED]" << std::endl; levelWindow.SetRangeMinMax(0, 100); levelWindow.SetLevelWindow(50, 100); std::cout << "Testing range restriction of mitk::LevelWindow::SetLevelWindow() autoexpand = FALSE"; levelWindow.SetLevelWindow(40, 100, false); if (levelWindow.GetLowerWindowBound() != initialLowerBound) { std::cout << "[FAILED]" << std::endl; return EXIT_FAILURE; } std::cout << "[PASSED]" << std::endl; std::cout << "Testing range restriction of mitk::LevelWindow::SetLevelWindow() autoexpand = TRUE"; levelWindow.SetLevelWindow(40, 100); if (levelWindow.GetLowerWindowBound() != initialLowerBound - 10) { std::cout << "[FAILED]" << std::endl; return EXIT_FAILURE; } std::cout << "[PASSED]" << std::endl; std::cout << "Testing range adaption of mitk::LevelWindow::SetWindowBounds()"; levelWindow.SetWindowBounds(-10, 90, true); // ture == force if (levelWindow.GetUpperWindowBound() != 90.0 || levelWindow.GetLowerWindowBound() != -10.0) { std::cout << "[FAILED]" << std::endl; return EXIT_FAILURE; } std::cout << "[PASSED]" << std::endl; std::cout << "Testing range adaption of mitk::LevelWindow::SetWindowBounds()"; levelWindow.SetWindowBounds(-20, 110, true); // ture == force if (levelWindow.GetUpperWindowBound() != 110.0 || levelWindow.GetLowerWindowBound() != -20.0) { std::cout << "[FAILED]" << std::endl; return EXIT_FAILURE; } std::cout << "[PASSED]" << std::endl; std::cout << "Testing range adaption of mitk::LevelWindow::SetLevelWindow()"; levelWindow.SetLevelWindow(50, 140, true); // ture == force if (levelWindow.GetUpperWindowBound() != 120.0 || levelWindow.GetLowerWindowBound() != -20.0) { std::cout << "[FAILED]" << std::endl; return EXIT_FAILURE; } std::cout << "[PASSED]" << std::endl; std::cout << "Testing c'tor with x-ray values (formerly did not expand range enough)"; mitk::LevelWindow crLevelWindow(16352, 16444); if (crLevelWindow.GetLevel() != 16352.0 || crLevelWindow.GetWindow() != 16444.0) { std::cout << "[FAILED]" << std::endl; return EXIT_FAILURE; } std::cout << "[PASSED]" << std::endl; std::cout << "[PASSED]" << std::endl; std::cout << "[TEST DONE]" << std::endl; return EXIT_SUCCESS; } \ No newline at end of file diff --git a/Modules/Core/test/mitkLogTest.cpp b/Modules/Core/test/mitkLogTest.cpp index e4a3e8b479..d84f86b759 100644 --- a/Modules/Core/test/mitkLogTest.cpp +++ b/Modules/Core/test/mitkLogTest.cpp @@ -1,316 +1,316 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkCommon.h" #include "mitkTestingMacros.h" #include #include #include #include #include /** Documentation * * @brief this class provides an accessible BackendCout to determine whether this backend was * used to process a message or not. * It is needed for the disable / enable backend test. */ class TestBackendCout : public mbilog::BackendCout { public: TestBackendCout() { m_Called = false; mbilog::BackendCout(); } void ProcessMessage(const mbilog::LogMessage &l) override { m_Called = true; mbilog::BackendCout::ProcessMessage(l); } bool WasCalled() { return m_Called; } private: bool m_Called; }; /** Documentation * * @brief Objects of this class can start an internal thread by calling the Start() method. * The thread is then logging messages until the method Stop() is called. The class * can be used to test if logging is thread-save by using multiple objects and let * them log simuntanously. */ class mitkTestLoggingThread : public itk::Object { public: mitkClassMacroItkParent(mitkTestLoggingThread, itk::Object); mitkNewMacro1Param(mitkTestLoggingThread, itk::MultiThreader::Pointer); int NumberOfMessages; protected: mitkTestLoggingThread(itk::MultiThreader::Pointer MultiThreader) { ThreadID = -1; NumberOfMessages = 0; m_MultiThreader = MultiThreader; LoggingRunning = true; } bool LoggingRunning; int ThreadID; itk::MultiThreader::Pointer m_MultiThreader; void LogMessages() { while (LoggingRunning) { MITK_INFO << "Test info stream in thread" << ThreadID << "\n even with newlines"; MITK_WARN << "Test warning stream in thread " << ThreadID << ". " << "Even with a very long text, even without meaning or implied meaning or content, just a long " "sentence to see whether something has problems with long sentences or output in files or into " "windows or commandlines or whatever."; MITK_DEBUG << "Test debugging stream in thread " << ThreadID; MITK_ERROR << "Test error stream in thread " << ThreadID; MITK_FATAL << "Test fatal stream in thread " << ThreadID; NumberOfMessages += 5; } } static ITK_THREAD_RETURN_TYPE ThreadStartTracking(void *pInfoStruct) { /* extract this pointer from Thread Info structure */ - struct itk::MultiThreader::ThreadInfoStruct *pInfo = (struct itk::MultiThreader::ThreadInfoStruct *)pInfoStruct; + auto *pInfo = (struct itk::MultiThreader::ThreadInfoStruct *)pInfoStruct; if (pInfo == nullptr) { return ITK_THREAD_RETURN_VALUE; } if (pInfo->UserData == nullptr) { return ITK_THREAD_RETURN_VALUE; } - mitkTestLoggingThread *thisthread = (mitkTestLoggingThread *)pInfo->UserData; + auto *thisthread = (mitkTestLoggingThread *)pInfo->UserData; if (thisthread != nullptr) thisthread->LogMessages(); return ITK_THREAD_RETURN_VALUE; } public: int Start() { LoggingRunning = true; this->ThreadID = m_MultiThreader->SpawnThread(this->ThreadStartTracking, this); return ThreadID; } void Stop() { LoggingRunning = false; } }; /** Documentation * * @brief This class holds static test methods to sturcture the test of the mitk logging mechanism. */ class mitkLogTestClass { public: static void TestSimpleLog() { bool testSucceded = true; try { MITK_INFO << "Test info stream."; MITK_WARN << "Test warning stream."; MITK_DEBUG << "Test debugging stream."; // only activated if cmake variable is on! // so no worries if you see no output for this line MITK_ERROR << "Test error stream."; MITK_FATAL << "Test fatal stream."; } catch (const mitk::Exception &) { testSucceded = false; } MITK_TEST_CONDITION_REQUIRED(testSucceded, "Test logging streams."); } static void TestObjectInfoLogging() { bool testSucceded = true; try { int i = 123; float f = .32234; double d = 123123; std::string testString = "testString"; std::stringstream testStringStream; testStringStream << "test" << "String" << "Stream"; mitk::Point3D testMitkPoint; testMitkPoint.Fill(2); MITK_INFO << i; MITK_INFO << f; MITK_INFO << d; MITK_INFO << testString; MITK_INFO << testStringStream.str(); MITK_INFO << testMitkPoint; } catch (const mitk::Exception &) { testSucceded = false; } MITK_TEST_CONDITION_REQUIRED(testSucceded, "Test logging of object information."); } static void TestThreadSaveLog(bool toFile) { bool testSucceded = true; try { if (toFile) { std::string filename = mitk::StandardFileLocations::GetInstance()->GetOptionDirectory() + "/testthreadlog.log"; itksys::SystemTools::RemoveFile(filename.c_str()); // remove old file, we do not want to append to large files mitk::LoggingBackend::SetLogFile(filename.c_str()); } unsigned int numberOfThreads = 20; unsigned int threadRuntimeInMilliseconds = 2000; std::vector threadIDs; std::vector threads; itk::MultiThreader::Pointer multiThreader = itk::MultiThreader::New(); for (unsigned int threadIdx = 0; threadIdx < numberOfThreads; ++threadIdx) { // initialize threads... mitkTestLoggingThread::Pointer newThread = mitkTestLoggingThread::New(multiThreader); threads.push_back(newThread); std::cout << "Created " << threadIdx << ". thread." << std::endl; } for (unsigned int threadIdx = 0; threadIdx < numberOfThreads; ++threadIdx) { // start them std::cout << "Start " << threadIdx << ". thread." << std::endl; threadIDs.push_back(threads[threadIdx]->Start()); std::cout << threadIdx << ". thread has ID " << threadIDs[threadIdx] << std::endl; } // wait for some time (milliseconds) itksys::SystemTools::Delay(threadRuntimeInMilliseconds); for (unsigned int threadIdx = 0; threadIdx < numberOfThreads; ++threadIdx) { // stop them std::cout << "Stop " << threadIdx << ". thread." << std::endl; threads[threadIdx]->Stop(); } for (unsigned int threadIdx = 0; threadIdx < numberOfThreads; ++threadIdx) { // Wait for all threads to end multiThreader->TerminateThread(threadIDs[threadIdx]); std::cout << "Terminated " << threadIdx << ". thread (" << threads[threadIdx]->NumberOfMessages << " messages)." << std::endl; } } catch (std::exception &e) { MITK_ERROR << "exception during 'TestThreadSaveLog': " << e.what(); testSucceded = false; } catch (...) { MITK_ERROR << "unknown exception during 'TestThreadSaveLog'"; testSucceded = false; } // if no error occured until now, everything is ok MITK_TEST_CONDITION_REQUIRED(testSucceded, "Test logging in different threads."); } static void TestLoggingToFile() { std::string filename = mitk::StandardFileLocations::GetInstance()->GetOptionDirectory() + "/testlog.log"; mitk::LoggingBackend::SetLogFile(filename.c_str()); MITK_INFO << "Test logging to default filename: " << mitk::LoggingBackend::GetLogFile(); MITK_TEST_CONDITION_REQUIRED(itksys::SystemTools::FileExists(filename.c_str()), "Testing if log file exists."); // TODO delete log file? } static void TestAddAndRemoveBackends() { mbilog::BackendCout myBackend = mbilog::BackendCout(); mbilog::RegisterBackend(&myBackend); MITK_INFO << "Test logging"; mbilog::UnregisterBackend(&myBackend); // if no error occured until now, everything is ok MITK_TEST_CONDITION_REQUIRED(true, "Test add/remove logging backend."); } static void TestDefaultBackend() { // not possible now, because we cannot unregister the mitk logging backend in the moment. If such a method is added // to mbilog utility one may add this test. } static void TestEnableDisableBackends() { TestBackendCout myCoutBackend = TestBackendCout(); mbilog::RegisterBackend(&myCoutBackend); mbilog::DisableBackends(mbilog::Console); MITK_INFO << "There should be no output!"; bool success = !myCoutBackend.WasCalled(); mbilog::EnableBackends(mbilog::Console); MITK_INFO << "Now there should be an output."; success &= myCoutBackend.WasCalled(); mbilog::UnregisterBackend(&myCoutBackend); MITK_TEST_CONDITION_REQUIRED(success, "Test disable / enable logging backends.") } }; int mitkLogTest(int /* argc */, char * /*argv*/ []) { // always start with this! MITK_TEST_BEGIN("Log") MITK_TEST_OUTPUT(<< "TESTING ALL LOGGING OUTPUTS, ERROR MESSAGES ARE ALSO TESTED AND NOT MEANING AN ERROR OCCURED!") mitkLogTestClass::TestSimpleLog(); mitkLogTestClass::TestObjectInfoLogging(); mitkLogTestClass::TestLoggingToFile(); mitkLogTestClass::TestAddAndRemoveBackends(); mitkLogTestClass::TestThreadSaveLog(false); // false = to console mitkLogTestClass::TestThreadSaveLog(true); // true = to file mitkLogTestClass::TestEnableDisableBackends(); // TODO actually test file somehow? // always end with this! MITK_TEST_END() } diff --git a/Modules/Core/test/mitkMultiComponentImageDataComparisonFilterTest.cpp b/Modules/Core/test/mitkMultiComponentImageDataComparisonFilterTest.cpp index 5d3450a6c7..0f52599061 100644 --- a/Modules/Core/test/mitkMultiComponentImageDataComparisonFilterTest.cpp +++ b/Modules/Core/test/mitkMultiComponentImageDataComparisonFilterTest.cpp @@ -1,81 +1,81 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ // mitk includes #include "mitkIOUtil.h" #include "mitkImageReadAccessor.h" #include "mitkMultiComponentImageDataComparisonFilter.h" #include "mitkTestingMacros.h" #include "itkNumericTraits.h" int mitkMultiComponentImageDataComparisonFilterTest(int /*argc*/, char *argv[]) { MITK_TEST_BEGIN("MultiComponentImageDataComparisonFilter"); // instantiation mitk::MultiComponentImageDataComparisonFilter::Pointer testObject = mitk::MultiComponentImageDataComparisonFilter::New(); MITK_TEST_CONDITION_REQUIRED(testObject.IsNotNull(), "Testing instantiation of test class!"); MITK_TEST_CONDITION_REQUIRED(testObject->GetCompareFilterResult() == nullptr, "Testing initialization of result struct"); MITK_TEST_CONDITION_REQUIRED(testObject->GetTolerance() == 0.0f, "Testing initialization of tolerance member"); MITK_TEST_CONDITION_REQUIRED(testObject->GetResult() == false, "Testing initialization of CompareResult member"); // initialize compare result struct and pass it to the filter mitk::CompareFilterResults compareResult; compareResult.m_MaximumDifference = 0.0f; compareResult.m_MinimumDifference = itk::NumericTraits::max(); compareResult.m_MeanDifference = 0.0f; compareResult.m_FilterCompleted = false; compareResult.m_TotalDifference = 0.0f; compareResult.m_PixelsWithDifference = 0; testObject->SetCompareFilterResult(&compareResult); MITK_TEST_CONDITION_REQUIRED(testObject->GetCompareFilterResult() != nullptr, "Testing set/get of compare result struct"); MITK_TEST_CONDITION_REQUIRED(testObject->GetResult() == false, "CompareResult still false"); // now load an image with several components and present it to the filter mitk::Image::Pointer testImg = dynamic_cast(mitk::IOUtil::Load(argv[1])[0].GetPointer()); mitk::Image::Pointer testImg2 = testImg->Clone(); testObject->SetValidImage(testImg); testObject->SetTestImage(testImg2); MITK_TEST_CONDITION_REQUIRED(testObject->GetNumberOfIndexedInputs() == 2, "Testing correct handling of input images"); testObject->Update(); MITK_TEST_CONDITION_REQUIRED(testObject->GetResult(), "Testing filter processing with equal image data"); // now change some of the data and check if the response is correct mitk::ImageReadAccessor imgAcc(testImg2); - unsigned char *imgData = (unsigned char *)imgAcc.GetData(); + auto *imgData = (unsigned char *)imgAcc.GetData(); imgData[10] += 1; imgData[20] += 2; imgData[30] += 3; testObject->Update(); MITK_TEST_CONDITION_REQUIRED(testObject->GetResult() == false, "Testing filter processing with unequal image data"); MITK_TEST_CONDITION_REQUIRED( mitk::Equal((int)testObject->GetCompareFilterResult()->m_PixelsWithDifference, (int)3) && mitk::Equal((double)testObject->GetCompareFilterResult()->m_MaximumDifference, (double)3.0) && mitk::Equal((double)testObject->GetCompareFilterResult()->m_MeanDifference, (double)2.0), "Assessing calculated image differences"); MITK_TEST_END(); } diff --git a/Modules/Core/test/mitkPointSetLocaleTest.cpp b/Modules/Core/test/mitkPointSetLocaleTest.cpp index 50f722c89f..f5974259d7 100644 --- a/Modules/Core/test/mitkPointSetLocaleTest.cpp +++ b/Modules/Core/test/mitkPointSetLocaleTest.cpp @@ -1,162 +1,162 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkIOUtil.h" #include "mitkPointSet.h" #include "mitkStandardFileLocations.h" #include "mitkTestingMacros.h" #include #include #include #include bool ChangeLocale(const std::string &locale) { try { MITK_TEST_OUTPUT(<< "\n** Changing locale from " << setlocale(LC_ALL, nullptr) << " to '" << locale << "'"); setlocale(LC_ALL, locale.c_str()); std::locale l(locale.c_str()); std::cin.imbue(l); std::cout.imbue(l); return true; } catch (...) { MITK_TEST_OUTPUT(<< "Could not activate locale " << locale << "\n"); return false; } } void ReaderLocaleTest(mitk::Point3D &refPoint, std::string filename) { MITK_TEST_OUTPUT(<< "---- Reader Test ---- "); mitk::PointSet::Pointer pointSet = dynamic_cast(mitk::IOUtil::Load(filename)[0].GetPointer()); mitk::Point3D point; if (pointSet->GetPointIfExists(0, &point)) { MITK_TEST_CONDITION_REQUIRED(fabs(refPoint[0] - point[0]) < 0.00001, "read x correct"); MITK_TEST_CONDITION_REQUIRED(fabs(refPoint[1] - point[1]) < 0.00001, "read y correct"); MITK_TEST_CONDITION_REQUIRED(fabs(refPoint[2] - point[2]) < 0.00001, "read z correct"); } else { MITK_TEST_FAILED_MSG(<< "File " << filename << " can not be read - test will not applied."); return; } } void WriterLocaleTest(mitk::Point3D &refPoint, std::string filename) { MITK_TEST_OUTPUT(<< "---- Writer Test---- "); // create pointset mitk::PointSet::Pointer refPointSet = mitk::PointSet::New(); refPointSet->InsertPoint(0, refPoint); // SetPoint(0, refPoint); std::string tmpFilePath = mitk::IOUtil::CreateTemporaryFile("testPointSet_XXXXXX.mps"); // write point set mitk::IOUtil::Save(refPointSet, tmpFilePath); std::ifstream stream(tmpFilePath.c_str()); // compare two .mps files std::ifstream refStream(filename.c_str()); MITK_TEST_CONDITION_REQUIRED(refStream, "Read reference point set"); MITK_TEST_CONDITION_REQUIRED(stream, "Read point set"); bool differ = false; if (stream.is_open() && refStream.is_open()) { std::string streamLine; std::string refStreamLine; while (!stream.eof() && !refStream.eof()) { getline(stream, streamLine); getline(refStream, refStreamLine); if (streamLine.compare(refStreamLine) != 0) { differ = true; break; } } stream.close(); refStream.close(); } MITK_TEST_CONDITION_REQUIRED(!differ, "Write point set correct"); } int mitkPointSetLocaleTest(int, char *[]) { MITK_TEST_BEGIN("PointSetLocaleTest"); // create reference point set mitk::PointSet::Pointer refPointSet = mitk::PointSet::New(); mitk::Point3D refPoint; refPoint[0] = 32.2946; refPoint[1] = -17.7359; refPoint[2] = 29.6502; refPointSet->SetPoint(0, refPoint); // create locale list typedef std::list StringList; StringList alllocales; alllocales.push_back("de_DE"); alllocales.push_back("de_DE.utf8"); alllocales.push_back("de_DE.UTF-8"); alllocales.push_back("de_DE@euro"); alllocales.push_back("German_Germany"); // QuickFix for MAC OS X // See for more the Bug #3894 comments #if defined(__APPLE__) || defined(MACOSX) alllocales.push_back("C"); #endif // write a reference file using the "C" locale once ChangeLocale("C"); std::string referenceFilePath = mitk::IOUtil::CreateTemporaryFile("refPointSet_XXXXXX.mps"); MITK_INFO << "Reference PointSet in " << referenceFilePath; // write point set mitk::IOUtil::Save(refPointSet, referenceFilePath); unsigned int numberOfTestedGermanLocales(0); - for (StringList::iterator iter = alllocales.begin(); iter != alllocales.end(); ++iter) + for (auto iter = alllocales.begin(); iter != alllocales.end(); ++iter) { if (ChangeLocale(*iter)) { ++numberOfTestedGermanLocales; WriterLocaleTest(refPoint, referenceFilePath); ReaderLocaleTest(refPoint, referenceFilePath); } } if (numberOfTestedGermanLocales == 0) { MITK_TEST_OUTPUT(<< "Warning: No German locale was found on the system."); } // MITK_TEST_CONDITION_REQUIRED( numberOfTestedGermanLocales > 0, "Verify that at least one German locale has been // tested."); MITK_TEST_END(); } diff --git a/Modules/Core/test/mitkPropertyAliasesTest.cpp b/Modules/Core/test/mitkPropertyAliasesTest.cpp index 492c88f840..506b263029 100644 --- a/Modules/Core/test/mitkPropertyAliasesTest.cpp +++ b/Modules/Core/test/mitkPropertyAliasesTest.cpp @@ -1,68 +1,67 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include #include #include #include int mitkPropertyAliasesTest(int, char *[]) { MITK_TEST_BEGIN("mitkPropertyAliasesTest"); mitk::IPropertyAliases *propertyAliases = mitk::CoreServices::GetPropertyAliases(); MITK_TEST_CONDITION_REQUIRED(propertyAliases != nullptr, "Get property aliases service"); propertyAliases->AddAlias("propertyName1", "alias1a"); propertyAliases->AddAlias("propertyName1", "alias1b"); propertyAliases->AddAlias("propertyName2", "alias2a"); propertyAliases->AddAlias("propertyName2", "alias2b", "className"); typedef std::vector Aliases; - typedef Aliases::iterator AliasesIterator; Aliases aliases = propertyAliases->GetAliases("propertyName1"); - AliasesIterator it1 = std::find(aliases.begin(), aliases.end(), "alias1a"); - AliasesIterator it2 = std::find(aliases.begin(), aliases.end(), "alias1b"); + auto it1 = std::find(aliases.begin(), aliases.end(), "alias1a"); + auto it2 = std::find(aliases.begin(), aliases.end(), "alias1b"); MITK_TEST_CONDITION(aliases.size() == 2 && it1 != aliases.end() && it2 != aliases.end(), "Get aliases of \"propertyName1\""); aliases = propertyAliases->GetAliases("propertyName2"); it1 = std::find(aliases.begin(), aliases.end(), "alias2a"); MITK_TEST_CONDITION(aliases.size() == 1 && it1 != aliases.end(), "Get aliases of \"propertyName2\""); aliases = propertyAliases->GetAliases("propertyName2", "className"); it1 = std::find(aliases.begin(), aliases.end(), "alias2b"); MITK_TEST_CONDITION(aliases.size() == 1 && it1 != aliases.end(), "Get aliases of \"propertyName2\" restricted to \"className\""); std::string propertyName = propertyAliases->GetPropertyName("alias1b"); MITK_TEST_CONDITION(propertyName == "propertyName1", "Get property name of \"alias1b\""); propertyName = propertyAliases->GetPropertyName("alias2b"); MITK_TEST_CONDITION(propertyName.empty(), "Get property name of non-existing unrestricted \"alias2b\""); propertyName = propertyAliases->GetPropertyName("alias2b", "className"); MITK_TEST_CONDITION(propertyName == "propertyName2", "Get property name of restricted \"alias2b\""); MITK_TEST_END(); } diff --git a/Modules/Core/test/mitkRenderingManagerTest.cpp b/Modules/Core/test/mitkRenderingManagerTest.cpp index fac7a4c1e5..b581280440 100644 --- a/Modules/Core/test/mitkRenderingManagerTest.cpp +++ b/Modules/Core/test/mitkRenderingManagerTest.cpp @@ -1,216 +1,216 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkProperties.h" #include "mitkRenderingManager.h" #include "mitkStandaloneDataStorage.h" #include "mitkVtkPropRenderer.h" #include "vtkRenderWindow.h" #include "mitkTestingMacros.h" #include "mitkSurface.h" #include // Propertylist Test /** * Simple example for a test for the class "RenderingManager". * * argc and argv are the command line parameters which were passed to * the ADD_TEST command in the CMakeLists.txt file. For the automatic * tests, argv is either empty for the simple tests or contains the filename * of a test image for the image tests (see CMakeLists.txt). */ class mitkRenderingManagerTestClass { public: static void TestPropertyList(mitk::RenderingManager::Pointer renderingManager) { mitk::PropertyList::Pointer propertyList = renderingManager->GetPropertyList(); MITK_TEST_CONDITION(renderingManager->GetPropertyList().IsNotNull(), "Testing if the constructor set the propertylist") // check if the default properties are set renderingManager->SetProperty("booltest", mitk::BoolProperty::New(true)); - mitk::BoolProperty *prop = dynamic_cast(renderingManager->GetProperty("booltest")); + auto *prop = dynamic_cast(renderingManager->GetProperty("booltest")); MITK_TEST_CONDITION(prop->GetValue(), "Testing if getting the bool property") MITK_TEST_CONDITION(propertyList == renderingManager->GetPropertyList(), "Testing if the propertylist has changed during the last tests") } static void TestSurfaceLoading(mitk::RenderingManager::Pointer renderingManager) { // create and render two dimensional surface vtkCubeSource *plane = vtkCubeSource::New(); double planeBounds[] = {-1.0, 1.0, -1.0, 1.0, 0.0, 0.0}; double cubeBounds[] = {-0.5, 0.5, -0.5, 0.5, -0.5, 0.5}; plane->SetBounds(planeBounds); plane->SetCenter(0.0, 0.0, 0.0); vtkPolyData *polys = plane->GetOutput(); mitk::Surface::Pointer mitkPlane = mitk::Surface::New(); mitkPlane->SetVtkPolyData(polys); plane->Delete(); mitk::DataNode::Pointer planeNode = mitk::DataNode::New(); planeNode->SetData(mitkPlane); renderingManager->GetDataStorage()->Add(planeNode); mitk::Geometry3D::Pointer planeGeometry = mitk::Geometry3D::New(); planeGeometry->SetFloatBounds(planeBounds); MITK_TEST_CONDITION(renderingManager->InitializeViews(planeGeometry), "Testing if two dimensional Geometry3Ds can be displayed") // clear rendering renderingManager->GetDataStorage()->Remove(planeNode); renderingManager->InitializeViews(); // create and render three dimensional surface vtkCubeSource *cube = vtkCubeSource::New(); cube->SetBounds(cubeBounds); cube->SetCenter(0.0, 0.0, 0.0); vtkPolyData *polyCube = cube->GetOutput(); mitk::Surface::Pointer mitkCube = mitk::Surface::New(); mitkCube->SetVtkPolyData(polyCube); cube->Delete(); mitk::DataNode::Pointer cubeNode = mitk::DataNode::New(); cubeNode->SetData(mitkCube); renderingManager->GetDataStorage()->Add(cubeNode); mitk::Geometry3D::Pointer cubeGeometry = mitk::Geometry3D::New(); cubeGeometry->SetFloatBounds(cubeBounds); MITK_TEST_CONDITION(renderingManager->InitializeViews(cubeGeometry), "Testing if three dimensional Geometry3Ds can be displayed") // clear rendering renderingManager->GetDataStorage()->Remove(cubeNode); renderingManager->InitializeViews(); } static void TestAddRemoveRenderWindow() { mitk::RenderingManager::Pointer myRenderingManager = mitk::RenderingManager::New(); // mitk::StandaloneDataStorage::Pointer ds = mitk::StandaloneDataStorage::New(); // myRenderingManager->SetDataStorage(ds); { vtkRenderWindow *vtkRenWin = vtkRenderWindow::New(); MITK_TEST_CONDITION_REQUIRED(myRenderingManager->GetAllRegisteredRenderWindows().size() == 0, "Render window list must be empty") // Add Render Window myRenderingManager->AddRenderWindow(vtkRenWin); MITK_TEST_CONDITION_REQUIRED(myRenderingManager->GetAllRegisteredRenderWindows().size() == 1, "Render window list must contain one item") // Remove Render Window myRenderingManager->RemoveRenderWindow(vtkRenWin); MITK_TEST_CONDITION_REQUIRED(myRenderingManager->GetAllRegisteredRenderWindows().size() == 0, "Render window list must be empty") // Test if the Render Window was removed properly. This should not do anything MITK_TEST_OUTPUT(<< "Call RequestUpdate on removed render window") myRenderingManager->RequestUpdate(vtkRenWin); MITK_TEST_OUTPUT(<< "Call ForceImmediateUpdate on removed render window") myRenderingManager->ForceImmediateUpdate(vtkRenWin); MITK_TEST_CONDITION_REQUIRED(myRenderingManager->GetAllRegisteredRenderWindows().size() == 0, "Render window list must be empty") // Delete vtk variable correctly vtkRenWin->Delete(); } // Check that the previous calls to RequestUpdate and ForceImmediateUpdate // did not modify the internal vtkRenderWindow list. This should not crash. MITK_TEST_OUTPUT(<< "Call RequestUpdateAll after deleting render window") myRenderingManager->RequestUpdateAll(); MITK_TEST_OUTPUT(<< "Call ForceImmediateUpdateAll after deleting render window") myRenderingManager->ForceImmediateUpdateAll(); } }; // mitkDataNodeTestClass int mitkRenderingManagerTest(int /* argc */, char * /*argv*/ []) { // always start with this! MITK_TEST_BEGIN("RenderingManager") mitkRenderingManagerTestClass::TestAddRemoveRenderWindow(); mitk::RenderingManager::Pointer globalRenderingManager = mitk::RenderingManager::GetInstance(); MITK_TEST_CONDITION_REQUIRED(globalRenderingManager.IsNotNull(), "Testing instantiation of global static instance") mitk::RenderingManager::Pointer myRenderingManager = mitk::RenderingManager::New(); MITK_TEST_CONDITION_REQUIRED(myRenderingManager.IsNotNull(), "Testing instantiation of second 'local' instance") MITK_TEST_CONDITION_REQUIRED(myRenderingManager != globalRenderingManager, "Testing whether global instance equals new local instance (must not be!)") mitk::StandaloneDataStorage::Pointer ds = mitk::StandaloneDataStorage::New(); mitk::StandaloneDataStorage::Pointer ds2 = mitk::StandaloneDataStorage::New(); myRenderingManager->SetDataStorage(ds); vtkRenderWindow *vtkRenWin = vtkRenderWindow::New(); mitk::VtkPropRenderer::Pointer br = mitk::VtkPropRenderer::New("testingBR", vtkRenWin, myRenderingManager); mitk::BaseRenderer::AddInstance(vtkRenWin, br); myRenderingManager->AddRenderWindow(vtkRenWin); MITK_TEST_CONDITION_REQUIRED(br->GetDataStorage() == ds, "Testing if internal DataStorage has been set correctly for registered BaseRenderer") myRenderingManager->SetDataStorage(ds2); MITK_TEST_CONDITION_REQUIRED( br->GetDataStorage() == ds2, "Testing if change of internal DataStorage has been forwarded correctly to registered BaseRenderer") mitkRenderingManagerTestClass::TestPropertyList(myRenderingManager); mitkRenderingManagerTestClass::TestSurfaceLoading(myRenderingManager); // write your own tests here and use the macros from mitkTestingMacros.h !!! // do not write to std::cout and do not return from this function yourself! // Remove Render Window myRenderingManager->RemoveRenderWindow(vtkRenWin); // Delete vtk variable correctly vtkRenWin->Delete(); // always end with this! MITK_TEST_END() } diff --git a/Modules/Core/test/mitkRotatedSlice4DTest.cpp b/Modules/Core/test/mitkRotatedSlice4DTest.cpp index 3622bb64bd..f5c18c5764 100644 --- a/Modules/Core/test/mitkRotatedSlice4DTest.cpp +++ b/Modules/Core/test/mitkRotatedSlice4DTest.cpp @@ -1,87 +1,87 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkExtractSliceFilter.h" #include "mitkIOUtil.h" #include "mitkImagePixelReadAccessor.h" #include "mitkImageTimeSelector.h" #include "mitkInteractionConst.h" #include "mitkRotationOperation.h" #include "mitkTestingMacros.h" #include "time.h" /* * The mitkRotatedSlice4DTest loads a 4D image and extracts a specifically rotated slice in each time step's volume. */ int mitkRotatedSlice4DTest(int, char *argv[]) { MITK_TEST_BEGIN("mitkRotatedSlice4DTest"); std::string filename = argv[1]; // load 4D image mitk::Image::Pointer image4D = dynamic_cast(mitk::IOUtil::Load(filename)[0].GetPointer()); // check inputs if (image4D.IsNull()) { MITK_INFO << "Could not load the file"; return false; } // for each time step... for (unsigned int ts = 0; ts < image4D->GetTimeSteps(); ts++) { mitk::ImageTimeSelector::Pointer timeSelector = mitk::ImageTimeSelector::New(); timeSelector->SetInput(image4D); timeSelector->SetTimeNr(ts); timeSelector->Update(); mitk::Image::Pointer image3D = timeSelector->GetOutput(); int sliceNumber = 5; mitk::PlaneGeometry::Pointer plane = mitk::PlaneGeometry::New(); plane->InitializeStandardPlane(image3D->GetGeometry(), mitk::PlaneGeometry::Frontal, sliceNumber, true, false); // rotate about an arbitrary point and axis... float angle = 30; mitk::Point3D point; point.Fill(sliceNumber); mitk::Vector3D rotationAxis; rotationAxis[0] = 1; rotationAxis[1] = 2; rotationAxis[2] = 3; rotationAxis.Normalize(); // Create Rotation Operation - mitk::RotationOperation *op = new mitk::RotationOperation(mitk::OpROTATE, point, rotationAxis, angle); + auto *op = new mitk::RotationOperation(mitk::OpROTATE, point, rotationAxis, angle); plane->ExecuteOperation(op); delete op; // Now extract mitk::ExtractSliceFilter::Pointer extractor = mitk::ExtractSliceFilter::New(); extractor->SetInput(image3D); extractor->SetWorldGeometry(plane); extractor->Update(); mitk::Image::Pointer extractedPlane; extractedPlane = extractor->GetOutput(); std::stringstream ss; ss << " : Valid slice in timestep " << ts; MITK_TEST_CONDITION_REQUIRED(extractedPlane.IsNotNull(), ss.str().c_str()); } MITK_TEST_END(); } diff --git a/Modules/Core/test/mitkSurfaceTest.cpp b/Modules/Core/test/mitkSurfaceTest.cpp index 5f2d6a8757..63389c8cba 100644 --- a/Modules/Core/test/mitkSurfaceTest.cpp +++ b/Modules/Core/test/mitkSurfaceTest.cpp @@ -1,149 +1,149 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkCommon.h" #include "mitkNumericTypes.h" #include "mitkSurface.h" #include "mitkTestingMacros.h" #include "vtkPolyData.h" #include "vtkSphereSource.h" #include int mitkSurfaceTest(int /*argc*/, char * /*argv*/ []) { MITK_TEST_BEGIN("Surface"); mitk::Surface::Pointer surface = mitk::Surface::New(); MITK_TEST_CONDITION_REQUIRED(surface.GetPointer(), "Testing initialization!"); mitk::Surface::Pointer cloneSurface = surface->Clone(); MITK_TEST_CONDITION_REQUIRED(cloneSurface.GetPointer(), "Testing clone surface initialization!"); vtkSphereSource *sphereSource = vtkSphereSource::New(); sphereSource->SetCenter(0, 0, 0); sphereSource->SetRadius(5.0); sphereSource->SetThetaResolution(10); sphereSource->SetPhiResolution(10); sphereSource->Update(); vtkPolyData *polys = sphereSource->GetOutput(); MITK_TEST_CONDITION_REQUIRED(surface->GetVtkPolyData() == nullptr, "Testing initial state of vtkPolyData"); surface->SetVtkPolyData(polys); sphereSource->Delete(); MITK_TEST_CONDITION_REQUIRED(surface->GetVtkPolyData() != nullptr, "Testing set vtkPolyData"); cloneSurface = surface->Clone(); MITK_TEST_CONDITION_REQUIRED(cloneSurface->GetVtkPolyData() != nullptr, "Testing set vtkPolyData of cloned surface!"); cloneSurface = nullptr; double bounds[6] = {0.0, 0.0, 0.0, 0.0, 0.0, 0.0}; polys->ComputeBounds(); polys->GetBounds(bounds); surface->UpdateOutputInformation(); surface->SetRequestedRegionToLargestPossibleRegion(); - mitk::BoundingBox *bb = const_cast(surface->GetGeometry()->GetBoundingBox()); + auto *bb = const_cast(surface->GetGeometry()->GetBoundingBox()); mitk::BoundingBox::BoundsArrayType surfBounds = bb->GetBounds(); bool passed = false; if (bounds[0] == surfBounds[0] && bounds[1] == surfBounds[1] && bounds[2] == surfBounds[2] && bounds[3] == surfBounds[3] && bounds[4] == surfBounds[4] && bounds[5] == surfBounds[5]) { passed = true; } MITK_TEST_CONDITION_REQUIRED(passed, "Testing GetBoundingBox()!"); surface->Expand(5); surface->Update(); surface->SetRequestedRegionToLargestPossibleRegion(); mitk::Surface::RegionType requestedRegion = surface->GetRequestedRegion(); MITK_TEST_CONDITION_REQUIRED(requestedRegion.GetSize(3) == 5, "Testing mitk::Surface::Expand( timesteps ): "); double boundsMat[5][6]; for (int i = 0; i < 5; i++) { vtkSphereSource *sphereSource = vtkSphereSource::New(); sphereSource->SetCenter(0, 0, 0); sphereSource->SetRadius(1.0 * (i + 1.0)); sphereSource->SetThetaResolution(10); sphereSource->SetPhiResolution(10); sphereSource->Update(); sphereSource->GetOutput()->ComputeBounds(); sphereSource->GetOutput()->GetBounds(boundsMat[i]); surface->SetVtkPolyData(sphereSource->GetOutput(), i); sphereSource->Delete(); } surface->UpdateOutputInformation(); surface->SetRequestedRegionToLargestPossibleRegion(); passed = true; for (int i = 0; i < 5; i++) { mitk::BoundingBox::BoundsArrayType surfBounds = (const_cast(surface->GetTimeGeometry()->GetGeometryForTimeStep(i)->GetBoundingBox())) ->GetBounds(); if (boundsMat[i][0] != surfBounds[0] || boundsMat[i][1] != surfBounds[1] || boundsMat[i][2] != surfBounds[2] || boundsMat[i][3] != surfBounds[3] || boundsMat[i][4] != surfBounds[4] || boundsMat[i][5] != surfBounds[5]) { passed = false; break; } } MITK_TEST_CONDITION_REQUIRED(passed, "Testing mitk::Surface::Testing 4D surface data creation!"); const mitk::TimeGeometry *inputTimeGeometry = surface->GetUpdatedTimeGeometry(); int time = 3; int timestep = 0; timestep = inputTimeGeometry->TimePointToTimeStep(time); MITK_TEST_CONDITION_REQUIRED(time == timestep, "Testing correctness of geometry for surface->GetUpdatedTimeGeometry()!"); sphereSource = vtkSphereSource::New(); sphereSource->SetCenter(0, 0, 0); sphereSource->SetRadius(100.0); sphereSource->SetThetaResolution(10); sphereSource->SetPhiResolution(10); sphereSource->Update(); surface->SetVtkPolyData(sphereSource->GetOutput(), 3); sphereSource->Delete(); inputTimeGeometry = surface->GetUpdatedTimeGeometry(); time = 3; timestep = inputTimeGeometry->TimePointToTimeStep(time); MITK_TEST_CONDITION_REQUIRED( time == timestep, "Explicitly changing the data of timestep 3 and checking for timebounds correctness of surface's geometry again!"); unsigned int numberoftimesteps = surface->GetTimeSteps(); mitk::Surface::Pointer dummy = mitk::Surface::New(); dummy->Graft(surface); MITK_TEST_CONDITION_REQUIRED(dummy->GetVtkPolyData() != nullptr, "Testing copying a Surface with Graft()!"); MITK_TEST_CONDITION_REQUIRED( dummy->GetTimeSteps() == numberoftimesteps, "orig-numberofTimeSteps:" << numberoftimesteps << " copy-numberofTimeSteps:" << dummy->GetTimeSteps()); surface = nullptr; MITK_TEST_CONDITION_REQUIRED(surface.IsNull(), "Testing destruction of surface!"); MITK_TEST_END(); } diff --git a/Modules/Core/test/mitkSurfaceToImageFilterTest.cpp b/Modules/Core/test/mitkSurfaceToImageFilterTest.cpp index da56eacb39..46a2e01938 100644 --- a/Modules/Core/test/mitkSurfaceToImageFilterTest.cpp +++ b/Modules/Core/test/mitkSurfaceToImageFilterTest.cpp @@ -1,257 +1,257 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include #include #include #include #include "mitkSurfaceToImageFilter.h" #include "mitkTestFixture.h" #include class mitkSurfaceToImageFilterTestSuite : public mitk::TestFixture { CPPUNIT_TEST_SUITE(mitkSurfaceToImageFilterTestSuite); MITK_TEST(test3DSurfaceValidOutput); MITK_TEST(test3DSurfaceCorrect); MITK_TEST(test3DSurfaceIn4DImage); CPPUNIT_TEST_SUITE_END(); private: /** Members used inside the different test methods. All members are initialized via setUp().*/ mitk::Surface::Pointer m_Surface; public: /** * @brief Setup Always call this method before each Test-case to ensure correct and new intialization of the used * members for a new test case. (If the members are not used in a test, the method does not need to be called). */ void setUp() override { m_Surface = dynamic_cast(mitk::IOUtil::Load(GetTestDataFilePath("ball.stl"))[0].GetPointer()); } void tearDown() override {} void test3DSurfaceValidOutput() { mitk::SurfaceToImageFilter::Pointer surfaceToImageFilter = mitk::SurfaceToImageFilter::New(); mitk::Image::Pointer additionalInputImage = mitk::Image::New(); additionalInputImage->Initialize(mitk::MakeScalarPixelType(), *m_Surface->GetTimeGeometry()); // Arrange the filter surfaceToImageFilter->MakeOutputBinaryOn(); surfaceToImageFilter->SetInput(m_Surface); surfaceToImageFilter->SetImage(additionalInputImage); surfaceToImageFilter->Update(); CPPUNIT_ASSERT_MESSAGE( "SurfaceToImageFilter_AnyInputImageAndModeSetToBinary_ResultIsImageWithUCHARPixelType", surfaceToImageFilter->GetOutput()->GetPixelType().GetComponentType() == itk::ImageIOBase::UCHAR); surfaceToImageFilter->SetUShortBinaryPixelType(true); surfaceToImageFilter->Update(); CPPUNIT_ASSERT_MESSAGE( "SurfaceToImageFilter_AnyInputImageAndModeSetToBinary_ResultIsImageWithUCHARPixelType", surfaceToImageFilter->GetOutput()->GetPixelType().GetComponentType() == itk::ImageIOBase::USHORT); } void test3DSurfaceCorrect() { mitk::SurfaceToImageFilter::Pointer surfaceToImageFilter = mitk::SurfaceToImageFilter::New(); // todo I don't know if this image is always needed. There is no documentation of the filter. Use git blame and ask // the author. mitk::Image::Pointer additionalInputImage = mitk::Image::New(); - unsigned int *dims = new unsigned int[3]; + auto *dims = new unsigned int[3]; dims[0] = 32; dims[1] = 32; dims[2] = 32; additionalInputImage->Initialize(mitk::MakeScalarPixelType(), 3, dims); additionalInputImage->SetOrigin(m_Surface->GetGeometry()->GetOrigin()); additionalInputImage->GetGeometry()->SetIndexToWorldTransform(m_Surface->GetGeometry()->GetIndexToWorldTransform()); // Arrange the filter // The docu does not really tell if this is always needed. Could we skip SetImage in any case? surfaceToImageFilter->MakeOutputBinaryOn(); surfaceToImageFilter->SetInput(m_Surface); surfaceToImageFilter->SetImage(additionalInputImage); surfaceToImageFilter->Update(); mitk::ImagePixelReadAccessor outputReader(surfaceToImageFilter->GetOutput()); itk::Index<3> idx; bool valuesCorrect = true; // Values outside the ball should be 0 idx[0] = 0; idx[1] = 0, idx[2] = 0; valuesCorrect = valuesCorrect && (outputReader.GetPixelByIndex(idx) == 0); idx[0] = 0; idx[1] = 15, idx[2] = 15; valuesCorrect = valuesCorrect && (outputReader.GetPixelByIndex(idx) == 0); idx[0] = 15; idx[1] = 15, idx[2] = 0; valuesCorrect = valuesCorrect && (outputReader.GetPixelByIndex(idx) == 0); idx[0] = 15; idx[1] = 0, idx[2] = 15; valuesCorrect = valuesCorrect && (outputReader.GetPixelByIndex(idx) == 0); idx[0] = 5; idx[1] = 9, idx[2] = 23; valuesCorrect = valuesCorrect && (outputReader.GetPixelByIndex(idx) == 0); // Values inside the ball should be 1 idx[0] = 15; idx[1] = 15, idx[2] = 15; valuesCorrect = valuesCorrect && (outputReader.GetPixelByIndex(idx) == 1); idx[0] = 31; idx[1] = 15, idx[2] = 15; valuesCorrect = valuesCorrect && (outputReader.GetPixelByIndex(idx) == 1); idx[0] = 2; idx[1] = 15, idx[2] = 15; valuesCorrect = valuesCorrect && (outputReader.GetPixelByIndex(idx) == 1); idx[0] = 15; idx[1] = 15, idx[2] = 2; valuesCorrect = valuesCorrect && (outputReader.GetPixelByIndex(idx) == 1); idx[0] = 15; idx[1] = 2, idx[2] = 15; valuesCorrect = valuesCorrect && (outputReader.GetPixelByIndex(idx) == 1); idx[0] = 6; idx[1] = 9, idx[2] = 23; valuesCorrect = valuesCorrect && (outputReader.GetPixelByIndex(idx) == 1); CPPUNIT_ASSERT_MESSAGE("SurfaceToImageFilter_BallSurfaceAsInput_OutputCorrect", valuesCorrect == true); } void test3DSurfaceIn4DImage() { mitk::SurfaceToImageFilter::Pointer surfaceToImageFilter = mitk::SurfaceToImageFilter::New(); mitk::Image::Pointer additionalInputImage = mitk::Image::New(); - unsigned int *dims = new unsigned int[4]; + auto *dims = new unsigned int[4]; dims[0] = 32; dims[1] = 32; dims[2] = 32; dims[3] = 2; additionalInputImage->Initialize(mitk::MakeScalarPixelType(), 4, dims); additionalInputImage->SetOrigin(m_Surface->GetGeometry()->GetOrigin()); additionalInputImage->GetGeometry()->SetIndexToWorldTransform(m_Surface->GetGeometry()->GetIndexToWorldTransform()); mitk::Image::Pointer secondStep = additionalInputImage->Clone(); unsigned int size = sizeof(unsigned char); for (unsigned int i = 0; i < secondStep->GetDimension(); ++i) { size *= secondStep->GetDimension(i); } { mitk::ImageWriteAccessor accessor(secondStep); memset(accessor.GetData(), 1, size); } additionalInputImage->GetTimeGeometry()->Expand(2); additionalInputImage->GetGeometry(1)->SetSpacing(secondStep->GetGeometry()->GetSpacing()); additionalInputImage->GetGeometry(1)->SetOrigin(secondStep->GetGeometry()->GetOrigin()); additionalInputImage->GetGeometry(1)->SetIndexToWorldTransform( secondStep->GetGeometry()->GetIndexToWorldTransform()); { mitk::ImageReadAccessor readAccess(secondStep); additionalInputImage->SetImportVolume(readAccess.GetData(), 0); additionalInputImage->SetImportVolume(readAccess.GetData(), 1); } // Arrange the filter surfaceToImageFilter->MakeOutputBinaryOn(); surfaceToImageFilter->SetInput(m_Surface); surfaceToImageFilter->SetImage(additionalInputImage); surfaceToImageFilter->Update(); mitk::ImagePixelReadAccessor outputReader(surfaceToImageFilter->GetOutput()); itk::Index<4> idx; bool valuesCorrect = true; // Values outside the ball should be 0 idx[0] = 0; idx[1] = 0, idx[2] = 0; idx[3] = 0; valuesCorrect = valuesCorrect && (outputReader.GetPixelByIndex(idx) == 0); idx[0] = 0; idx[1] = 15, idx[2] = 15; idx[3] = 0; valuesCorrect = valuesCorrect && (outputReader.GetPixelByIndex(idx) == 0); idx[0] = 15; idx[1] = 15, idx[2] = 0; idx[3] = 0; valuesCorrect = valuesCorrect && (outputReader.GetPixelByIndex(idx) == 0); idx[0] = 15; idx[1] = 0, idx[2] = 15; idx[3] = 0; valuesCorrect = valuesCorrect && (outputReader.GetPixelByIndex(idx) == 0); idx[0] = 5; idx[1] = 9, idx[2] = 23; idx[3] = 0; valuesCorrect = valuesCorrect && (outputReader.GetPixelByIndex(idx) == 0); // Values inside the ball should be 1 hould be 1 idx[0] = 15; idx[1] = 15, idx[2] = 15; idx[3] = 0; valuesCorrect = valuesCorrect && (outputReader.GetPixelByIndex(idx) == 1); idx[0] = 31; idx[1] = 15, idx[2] = 15; idx[3] = 0; valuesCorrect = valuesCorrect && (outputReader.GetPixelByIndex(idx) == 1); idx[0] = 2; idx[1] = 15, idx[2] = 15; idx[3] = 0; valuesCorrect = valuesCorrect && (outputReader.GetPixelByIndex(idx) == 1); idx[0] = 15; idx[1] = 15, idx[2] = 2; idx[3] = 0; valuesCorrect = valuesCorrect && (outputReader.GetPixelByIndex(idx) == 1); idx[0] = 15; idx[1] = 2, idx[2] = 15; idx[3] = 0; valuesCorrect = valuesCorrect && (outputReader.GetPixelByIndex(idx) == 1); idx[0] = 6; idx[1] = 9, idx[2] = 23; idx[3] = 0; valuesCorrect = valuesCorrect && (outputReader.GetPixelByIndex(idx) == 1); // Values inside the ball but in the second timestep hould be 0 idx[0] = 15; idx[1] = 15, idx[2] = 15; idx[3] = 1; valuesCorrect = valuesCorrect && (outputReader.GetPixelByIndex(idx) == 0); idx[0] = 31; idx[1] = 15, idx[2] = 15; idx[3] = 1; valuesCorrect = valuesCorrect && (outputReader.GetPixelByIndex(idx) == 0); idx[0] = 2; idx[1] = 15, idx[2] = 15; idx[3] = 1; valuesCorrect = valuesCorrect && (outputReader.GetPixelByIndex(idx) == 0); idx[0] = 15; idx[1] = 15, idx[2] = 2; idx[3] = 1; valuesCorrect = valuesCorrect && (outputReader.GetPixelByIndex(idx) == 0); idx[0] = 15; idx[1] = 2, idx[2] = 15; idx[3] = 1; valuesCorrect = valuesCorrect && (outputReader.GetPixelByIndex(idx) == 0); idx[0] = 6; idx[1] = 9, idx[2] = 23; idx[3] = 1; valuesCorrect = valuesCorrect && (outputReader.GetPixelByIndex(idx) == 0); CPPUNIT_ASSERT_MESSAGE("SurfaceToImageFilter_BallSurfaceAsInput_Output4DCorrect", valuesCorrect == true); } }; MITK_TEST_SUITE_REGISTRATION(mitkSurfaceToImageFilter) diff --git a/Modules/Core/test/mitkTemporoSpatialStringPropertyTest.cpp b/Modules/Core/test/mitkTemporoSpatialStringPropertyTest.cpp index d29109afbd..b2923fdd7d 100644 --- a/Modules/Core/test/mitkTemporoSpatialStringPropertyTest.cpp +++ b/Modules/Core/test/mitkTemporoSpatialStringPropertyTest.cpp @@ -1,150 +1,150 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkTemporoSpatialStringProperty.h" #include "mitkTestFixture.h" #include "mitkTestingMacros.h" #include class mitkTemporoSpatialStringPropertyTestSuite : public mitk::TestFixture { CPPUNIT_TEST_SUITE(mitkTemporoSpatialStringPropertyTestSuite); MITK_TEST(GetValue); MITK_TEST(HasValue); MITK_TEST(SetValue); MITK_TEST(serializeTemporoSpatialStringPropertyToJSON); MITK_TEST(deserializeJSONToTemporoSpatialStringProperty); CPPUNIT_TEST_SUITE_END(); private: std::string refJSON; mitk::TemporoSpatialStringProperty::Pointer refProp; public: void setUp() override { refJSON = "{\"values\":[{\"t\":0, \"z\":0, \"value\":\"v_0_0\"}, {\"t\":3, \"z\":0, \"value\":\"v_3_0\"}, " "{\"t\":3, \"z\":2, \"value\":\"v_3_2\"}, {\"t\":6, \"z\":1, \"value\":\"v_6_1\"}]}"; refProp = mitk::TemporoSpatialStringProperty::New(); refProp->SetValue(0, 0, "v_0_0"); refProp->SetValue(3, 0, "v_3_0"); refProp->SetValue(3, 2, "v_3_2"); refProp->SetValue(6, 1, "v_6_1"); } void tearDown() override {} void GetValue() { CPPUNIT_ASSERT(refProp->GetValue() == "v_0_0"); CPPUNIT_ASSERT(refProp->GetValue(3, 0) == "v_3_0"); CPPUNIT_ASSERT(refProp->GetValue(3, 2) == "v_3_2"); CPPUNIT_ASSERT(refProp->GetValue(3, 1, false, true) == "v_3_0"); CPPUNIT_ASSERT(refProp->GetValue(3, 5, false, true) == "v_3_2"); CPPUNIT_ASSERT(refProp->GetValueBySlice(0) == "v_0_0"); CPPUNIT_ASSERT(refProp->GetValueBySlice(4, true) == "v_0_0"); CPPUNIT_ASSERT(refProp->GetValueByTimeStep(3) == "v_3_0"); CPPUNIT_ASSERT(refProp->GetValueByTimeStep(6) == "v_6_1"); CPPUNIT_ASSERT(refProp->GetValueByTimeStep(5, true) == "v_3_0"); CPPUNIT_ASSERT(refProp->GetValueAsString() == "v_0_0"); CPPUNIT_ASSERT(refProp->GetAvailableTimeSteps().size() == 3); CPPUNIT_ASSERT(refProp->GetAvailableTimeSteps()[0] == 0); CPPUNIT_ASSERT(refProp->GetAvailableTimeSteps()[1] == 3); CPPUNIT_ASSERT(refProp->GetAvailableTimeSteps()[2] == 6); CPPUNIT_ASSERT(refProp->GetAvailableSlices(0).size() == 1); CPPUNIT_ASSERT(refProp->GetAvailableSlices(0)[0] == 0); CPPUNIT_ASSERT(refProp->GetAvailableSlices(3).size() == 2); CPPUNIT_ASSERT(refProp->GetAvailableSlices(3)[0] == 0); CPPUNIT_ASSERT(refProp->GetAvailableSlices(3)[1] == 2); CPPUNIT_ASSERT(refProp->GetAvailableSlices(2).size() == 0); } void HasValue() { CPPUNIT_ASSERT(refProp->HasValue()); CPPUNIT_ASSERT(refProp->HasValue(3, 0)); CPPUNIT_ASSERT(refProp->HasValue(3, 2)); CPPUNIT_ASSERT(refProp->HasValue(3, 1, false, true)); CPPUNIT_ASSERT(refProp->HasValue(3, 5, false, true)); CPPUNIT_ASSERT(!refProp->HasValue(3, 1)); CPPUNIT_ASSERT(!refProp->HasValue(3, 5)); CPPUNIT_ASSERT(refProp->HasValue(4, 2, true, true)); CPPUNIT_ASSERT(refProp->HasValue(4, 2, true, false)); CPPUNIT_ASSERT(!refProp->HasValue(4, 2, false, true)); CPPUNIT_ASSERT(refProp->HasValueBySlice(0)); CPPUNIT_ASSERT(refProp->HasValueBySlice(4, true)); CPPUNIT_ASSERT(!refProp->HasValueBySlice(4)); CPPUNIT_ASSERT(refProp->HasValueByTimeStep(3)); CPPUNIT_ASSERT(refProp->HasValueByTimeStep(6)); CPPUNIT_ASSERT(refProp->HasValueByTimeStep(5, true)); CPPUNIT_ASSERT(!refProp->HasValueByTimeStep(5)); } void SetValue() { CPPUNIT_ASSERT_NO_THROW(refProp->SetValue(8, 9, "v_8_9")); CPPUNIT_ASSERT(refProp->GetValue(8, 9) == "v_8_9"); CPPUNIT_ASSERT_NO_THROW(refProp->SetValue("newValue")); CPPUNIT_ASSERT(refProp->GetValue(0, 0) == "newValue"); CPPUNIT_ASSERT(refProp->GetAvailableTimeSteps().size() == 1); CPPUNIT_ASSERT(refProp->GetAvailableSlices(0).size() == 1); } void serializeTemporoSpatialStringPropertyToJSON() { std::string data = mitk::PropertyPersistenceSerialization::serializeTemporoSpatialStringPropertyToJSON(refProp); CPPUNIT_ASSERT(refJSON == data); //"Testing serializeTemporoSpatialStringPropertyToJSON() producing correct string."); } void deserializeJSONToTemporoSpatialStringProperty() { mitk::BaseProperty::Pointer prop = mitk::PropertyPersistenceDeserialization::deserializeJSONToTemporoSpatialStringProperty(refJSON); - mitk::TemporoSpatialStringProperty *tsProp = dynamic_cast(prop.GetPointer()); + auto *tsProp = dynamic_cast(prop.GetPointer()); CPPUNIT_ASSERT( tsProp->GetValue(0, 0) == "v_0_0"); //"Testing deserializeJSONToTemporoSpatialStringProperty() producing property with correct value 1"); CPPUNIT_ASSERT( tsProp->GetValue(3, 0) == "v_3_0"); //"Testing deserializeJSONToTemporoSpatialStringProperty() producing property with correct value 2"); CPPUNIT_ASSERT( tsProp->GetValue(3, 2) == "v_3_2"); //"Testing deserializeJSONToTemporoSpatialStringProperty() producing property with correct value 3"); CPPUNIT_ASSERT( tsProp->GetValue(6, 1) == "v_6_1"); //"Testing deserializeJSONToTemporoSpatialStringProperty() producing property with correct value 4"); CPPUNIT_ASSERT(*tsProp == *refProp); //"Testing deserializeJSONToTemporoSpatialStringProperty()"); } }; MITK_TEST_SUITE_REGISTRATION(mitkTemporoSpatialStringProperty) diff --git a/Modules/Core/test/mitkVolumeCalculatorTest.cpp b/Modules/Core/test/mitkVolumeCalculatorTest.cpp index c7951c26b5..9c23430bbb 100644 --- a/Modules/Core/test/mitkVolumeCalculatorTest.cpp +++ b/Modules/Core/test/mitkVolumeCalculatorTest.cpp @@ -1,62 +1,62 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkIOUtil.h" #include "mitkImage.h" #include "mitkTestingMacros.h" #include "mitkVolumeCalculator.h" #include int mitkVolumeCalculatorTest(int /*argc*/, char *argv[]) { MITK_TEST_BEGIN("VolumeCalculator") const char *filename = argv[1]; const char *filename3D = argv[2]; mitk::VolumeCalculator::Pointer volumeCalculator = mitk::VolumeCalculator::New(); //********************************************************************* // Part I: Testing calculated volume. // The correct values have been manually calculated using external software. //********************************************************************* mitk::Image::Pointer image = dynamic_cast(mitk::IOUtil::Load(filename)[0].GetPointer()); MITK_TEST_CONDITION_REQUIRED(image.IsNotNull(), "01 Check if test image could be loaded"); volumeCalculator->SetImage(image); volumeCalculator->SetThreshold(0); volumeCalculator->ComputeVolume(); float volume = volumeCalculator->GetVolume(); MITK_TEST_CONDITION_REQUIRED(volume == 1600, "02 Test Volume Result. Expected 1600 actual value " << volume); volumeCalculator->SetThreshold(255); volumeCalculator->ComputeVolume(); volume = volumeCalculator->GetVolume(); MITK_TEST_CONDITION_REQUIRED(volume == 1272.50, "03 Test Volume Result. Expected 1272.50 actual value " << volume); image = dynamic_cast(mitk::IOUtil::Load(filename3D)[0].GetPointer()); volumeCalculator->SetImage(image); volumeCalculator->SetThreshold(-1023); volumeCalculator->ComputeVolume(); std::vector volumes = volumeCalculator->GetVolumes(); - for (std::vector::iterator it = volumes.begin(); it != volumes.end(); ++it) + for (auto it = volumes.begin(); it != volumes.end(); ++it) { MITK_TEST_CONDITION_REQUIRED((*it) == 24.576f, "04 Test Volume Result."); } MITK_TEST_END() } diff --git a/Modules/Core/test/vtkMitkThickSlicesFilterTest.cpp b/Modules/Core/test/vtkMitkThickSlicesFilterTest.cpp index 7bbcbabe27..636bcc088d 100644 --- a/Modules/Core/test/vtkMitkThickSlicesFilterTest.cpp +++ b/Modules/Core/test/vtkMitkThickSlicesFilterTest.cpp @@ -1,159 +1,159 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkTestingMacros.h" #include #include "mitkImage.h" #include "mitkImageWriteAccessor.h" #include #include #include class vtkMitkThickSlicesFilterTestHelper { public: static mitk::Image::Pointer CreateTestImage(int min, int max) { mitk::PixelType pixelType(mitk::MakeScalarPixelType()); mitk::Image::Pointer testImage = mitk::Image::New(); auto dim = new unsigned int[3]; dim[0] = 10; dim[1] = 10; dim[2] = max + 1 - min; testImage->Initialize(pixelType, 3, dim); size_t buffer_size = dim[0] * dim[1] * sizeof(unsigned char); for (int i = min; i <= max; ++i) { mitk::ImageWriteAccessor writeAccess(testImage, testImage->GetSliceData(i - min)); memset(writeAccess.GetData(), i, buffer_size); } return testImage; } static void EvaluateResult(unsigned char expectedValue, vtkImageData *image, const char *projection) { MITK_TEST_CONDITION_REQUIRED( image->GetDimensions()[0] == 10 && image->GetDimensions()[1] == 10 && image->GetDimensions()[2] == 1, "Resulting image has correct size"); - unsigned char *value = static_cast(image->GetScalarPointer(0, 0, 0)); + auto *value = static_cast(image->GetScalarPointer(0, 0, 0)); MITK_INFO << "Evaluating projection mode: " << projection; MITK_INFO << "expected value: " << static_cast(expectedValue); MITK_INFO << "actual value: " << static_cast(value[0]); MITK_TEST_CONDITION_REQUIRED(value[0] == expectedValue, "Resulting image has correct pixel-value"); } }; /** * Test for vtkMitkThickSlicesFilter. * */ int vtkMitkThickSlicesFilterTest(int, char *[]) { // always start with this! MITK_TEST_BEGIN("vtkMitkThickSlicesFilterTest") vtkMitkThickSlicesFilter *thickSliceFilter = vtkMitkThickSlicesFilter::New(); ////////////////////////////////////////////////////////////////////////// // Image looks like: // 000000000 // 111111111 // 222222222 mitk::Image::Pointer testImage1 = vtkMitkThickSlicesFilterTestHelper::CreateTestImage(0, 2); thickSliceFilter->SetInputData(testImage1->GetVtkImageData()); // MaxIP thickSliceFilter->SetThickSliceMode(0); thickSliceFilter->Modified(); thickSliceFilter->Update(); vtkMitkThickSlicesFilterTestHelper::EvaluateResult(2, thickSliceFilter->GetOutput(), "MaxIP"); // Sum thickSliceFilter->SetThickSliceMode(1); thickSliceFilter->Modified(); thickSliceFilter->Update(); vtkMitkThickSlicesFilterTestHelper::EvaluateResult(1, thickSliceFilter->GetOutput(), "Sum"); // Weighted thickSliceFilter->SetThickSliceMode(2); thickSliceFilter->Modified(); thickSliceFilter->Update(); vtkMitkThickSlicesFilterTestHelper::EvaluateResult(1, thickSliceFilter->GetOutput(), "Weighted"); // MinIP thickSliceFilter->SetThickSliceMode(3); thickSliceFilter->Modified(); thickSliceFilter->Update(); vtkMitkThickSlicesFilterTestHelper::EvaluateResult(0, thickSliceFilter->GetOutput(), "MinIP"); // Mean thickSliceFilter->SetThickSliceMode(4); thickSliceFilter->Modified(); thickSliceFilter->Update(); vtkMitkThickSlicesFilterTestHelper::EvaluateResult(1, thickSliceFilter->GetOutput(), "Mean"); ////////////////////////////////////////////////////////////////////////// // Image looks like: // 333333333 // 444444444 // 555555555 // 666666666 // 777777777 // 888888888 mitk::Image::Pointer testImage2 = vtkMitkThickSlicesFilterTestHelper::CreateTestImage(3, 8); thickSliceFilter->SetInputData(testImage2->GetVtkImageData()); // MaxIP thickSliceFilter->SetThickSliceMode(0); thickSliceFilter->Modified(); thickSliceFilter->Update(); vtkMitkThickSlicesFilterTestHelper::EvaluateResult(8, thickSliceFilter->GetOutput(), "MaxIP"); // Sum thickSliceFilter->SetThickSliceMode(1); thickSliceFilter->Modified(); thickSliceFilter->Update(); vtkMitkThickSlicesFilterTestHelper::EvaluateResult(5, thickSliceFilter->GetOutput(), "Sum"); // Weighted thickSliceFilter->SetThickSliceMode(2); thickSliceFilter->Modified(); thickSliceFilter->Update(); vtkMitkThickSlicesFilterTestHelper::EvaluateResult(4, thickSliceFilter->GetOutput(), "Weighted"); // MinIP thickSliceFilter->SetThickSliceMode(3); thickSliceFilter->Modified(); thickSliceFilter->Update(); vtkMitkThickSlicesFilterTestHelper::EvaluateResult(3, thickSliceFilter->GetOutput(), "MinIP"); // Mean thickSliceFilter->SetThickSliceMode(4); thickSliceFilter->Modified(); thickSliceFilter->Update(); vtkMitkThickSlicesFilterTestHelper::EvaluateResult(6, thickSliceFilter->GetOutput(), "Mean"); thickSliceFilter->Delete(); MITK_TEST_END() } diff --git a/Modules/DICOMReader/src/mitkDICOMDatasetAccessingImageFrameInfo.cpp b/Modules/DICOMReader/src/mitkDICOMDatasetAccessingImageFrameInfo.cpp index 732ac20c0a..926cc34da0 100644 --- a/Modules/DICOMReader/src/mitkDICOMDatasetAccessingImageFrameInfo.cpp +++ b/Modules/DICOMReader/src/mitkDICOMDatasetAccessingImageFrameInfo.cpp @@ -1,76 +1,76 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkDICOMDatasetAccessingImageFrameInfo.h" mitk::DICOMDatasetAccessingImageFrameInfo ::DICOMDatasetAccessingImageFrameInfo(const std::string& filename, unsigned int frameNo) :DICOMImageFrameInfo(filename, frameNo) { } mitk::DICOMDatasetAccessingImageFrameInfo ::~DICOMDatasetAccessingImageFrameInfo() { } mitk::DICOMImageFrameList mitk::ConvertToDICOMImageFrameList(const DICOMDatasetAccessingImageFrameList& input) { DICOMImageFrameList output; output.reserve(input.size()); for (auto& inputIter : input) { DICOMImageFrameInfo* fi = inputIter.GetPointer(); assert(fi); output.push_back(fi); } return output; } mitk::DICOMDatasetList mitk::ConvertToDICOMDatasetList(const DICOMDatasetAccessingImageFrameList& input) { DICOMDatasetList output; output.reserve(input.size()); for (auto& inputIter : input) { DICOMDatasetAccess* da = inputIter.GetPointer(); assert(da); output.push_back(da); } return output; } mitk::DICOMDatasetAccessingImageFrameList mitk::ConvertToDICOMDatasetAccessingImageFrameList(const DICOMDatasetList& input) { DICOMDatasetAccessingImageFrameList output; output.reserve(input.size()); for (auto& inputIter : input) { - DICOMDatasetAccessingImageFrameInfo* afi = dynamic_cast(inputIter); + auto* afi = dynamic_cast(inputIter); assert(afi); output.push_back(afi); } return output; } diff --git a/Modules/DICOMReader/src/mitkDICOMITKSeriesGDCMReader.cpp b/Modules/DICOMReader/src/mitkDICOMITKSeriesGDCMReader.cpp index 2961b9cb2b..518207e729 100644 --- a/Modules/DICOMReader/src/mitkDICOMITKSeriesGDCMReader.cpp +++ b/Modules/DICOMReader/src/mitkDICOMITKSeriesGDCMReader.cpp @@ -1,622 +1,622 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ //#define MBILOG_ENABLE_DEBUG #define ENABLE_TIMING #include #include #include "mitkDICOMITKSeriesGDCMReader.h" #include "mitkITKDICOMSeriesReaderHelper.h" #include "mitkGantryTiltInformation.h" #include "mitkDICOMTagBasedSorter.h" #include "mitkDICOMGDCMTagScanner.h" itk::MutexLock::Pointer mitk::DICOMITKSeriesGDCMReader::s_LocaleMutex = itk::MutexLock::New(); mitk::DICOMITKSeriesGDCMReader::DICOMITKSeriesGDCMReader( unsigned int decimalPlacesForOrientation, bool simpleVolumeImport ) : DICOMFileReader() , m_FixTiltByShearing( true ) , m_SimpleVolumeReading( simpleVolumeImport ) , m_DecimalPlacesForOrientation( decimalPlacesForOrientation ) , m_ExternalCache(false) { this->EnsureMandatorySortersArePresent( decimalPlacesForOrientation, simpleVolumeImport ); } mitk::DICOMITKSeriesGDCMReader::DICOMITKSeriesGDCMReader( const DICOMITKSeriesGDCMReader& other ) : DICOMFileReader( other ) , m_FixTiltByShearing( false ) , m_SortingResultInProgress( other.m_SortingResultInProgress ) , m_Sorter( other.m_Sorter ) , m_EquiDistantBlocksSorter( other.m_EquiDistantBlocksSorter->Clone() ) , m_NormalDirectionConsistencySorter( other.m_NormalDirectionConsistencySorter->Clone() ) , m_ReplacedCLocales( other.m_ReplacedCLocales ) , m_ReplacedCinLocales( other.m_ReplacedCinLocales ) , m_DecimalPlacesForOrientation( other.m_DecimalPlacesForOrientation ) , m_TagCache( other.m_TagCache ) , m_ExternalCache(other.m_ExternalCache) { } mitk::DICOMITKSeriesGDCMReader::~DICOMITKSeriesGDCMReader() { } mitk::DICOMITKSeriesGDCMReader& mitk::DICOMITKSeriesGDCMReader:: operator=( const DICOMITKSeriesGDCMReader& other ) { if ( this != &other ) { DICOMFileReader::operator =( other ); this->m_FixTiltByShearing = other.m_FixTiltByShearing; this->m_SortingResultInProgress = other.m_SortingResultInProgress; this->m_Sorter = other.m_Sorter; // TODO should clone the list items this->m_EquiDistantBlocksSorter = other.m_EquiDistantBlocksSorter->Clone(); this->m_NormalDirectionConsistencySorter = other.m_NormalDirectionConsistencySorter->Clone(); this->m_ReplacedCLocales = other.m_ReplacedCLocales; this->m_ReplacedCinLocales = other.m_ReplacedCinLocales; this->m_DecimalPlacesForOrientation = other.m_DecimalPlacesForOrientation; this->m_TagCache = other.m_TagCache; } return *this; } bool mitk::DICOMITKSeriesGDCMReader::operator==( const DICOMFileReader& other ) const { - if ( const Self* otherSelf = dynamic_cast( &other ) ) + if ( const auto* otherSelf = dynamic_cast( &other ) ) { if ( this->m_FixTiltByShearing == otherSelf->m_FixTiltByShearing && *( this->m_EquiDistantBlocksSorter ) == *( otherSelf->m_EquiDistantBlocksSorter ) && ( fabs( this->m_DecimalPlacesForOrientation - otherSelf->m_DecimalPlacesForOrientation ) < eps ) ) { // test sorters for equality if ( this->m_Sorter.size() != otherSelf->m_Sorter.size() ) return false; auto mySorterIter = this->m_Sorter.cbegin(); auto oSorterIter = otherSelf->m_Sorter.cbegin(); for ( ; mySorterIter != this->m_Sorter.cend() && oSorterIter != otherSelf->m_Sorter.cend(); ++mySorterIter, ++oSorterIter ) { if ( !( **mySorterIter == **oSorterIter ) ) return false; // this sorter differs } // nothing differs ==> all is equal return true; } else { return false; } } else { return false; } } void mitk::DICOMITKSeriesGDCMReader::SetFixTiltByShearing( bool on ) { this->Modified(); m_FixTiltByShearing = on; } bool mitk::DICOMITKSeriesGDCMReader::GetFixTiltByShearing() const { return m_FixTiltByShearing; } void mitk::DICOMITKSeriesGDCMReader::SetAcceptTwoSlicesGroups( bool accept ) const { this->Modified(); m_EquiDistantBlocksSorter->SetAcceptTwoSlicesGroups( accept ); } bool mitk::DICOMITKSeriesGDCMReader::GetAcceptTwoSlicesGroups() const { return m_EquiDistantBlocksSorter->GetAcceptTwoSlicesGroups(); } void mitk::DICOMITKSeriesGDCMReader::InternalPrintConfiguration( std::ostream& os ) const { unsigned int sortIndex( 1 ); for ( auto sorterIter = m_Sorter.cbegin(); sorterIter != m_Sorter.cend(); ++sortIndex, ++sorterIter ) { os << "Sorting step " << sortIndex << ":" << std::endl; ( *sorterIter )->PrintConfiguration( os, " " ); } os << "Sorting step " << sortIndex << ":" << std::endl; m_EquiDistantBlocksSorter->PrintConfiguration( os, " " ); } std::string mitk::DICOMITKSeriesGDCMReader::GetActiveLocale() { return setlocale( LC_NUMERIC, nullptr ); } void mitk::DICOMITKSeriesGDCMReader::PushLocale() const { s_LocaleMutex->Lock(); std::string currentCLocale = setlocale( LC_NUMERIC, nullptr ); m_ReplacedCLocales.push( currentCLocale ); setlocale( LC_NUMERIC, "C" ); std::locale currentCinLocale( std::cin.getloc() ); m_ReplacedCinLocales.push( currentCinLocale ); std::locale l( "C" ); std::cin.imbue( l ); s_LocaleMutex->Unlock(); } void mitk::DICOMITKSeriesGDCMReader::PopLocale() const { s_LocaleMutex->Lock(); if ( !m_ReplacedCLocales.empty() ) { setlocale( LC_NUMERIC, m_ReplacedCLocales.top().c_str() ); m_ReplacedCLocales.pop(); } else { MITK_WARN << "Mismatched PopLocale on DICOMITKSeriesGDCMReader."; } if ( !m_ReplacedCinLocales.empty() ) { std::cin.imbue( m_ReplacedCinLocales.top() ); m_ReplacedCinLocales.pop(); } else { MITK_WARN << "Mismatched PopLocale on DICOMITKSeriesGDCMReader."; } s_LocaleMutex->Unlock(); } mitk::DICOMITKSeriesGDCMReader::SortingBlockList mitk::DICOMITKSeriesGDCMReader::Condense3DBlocks( SortingBlockList& input ) { return input; // to be implemented differently by sub-classes } #if defined( MBILOG_ENABLE_DEBUG ) || defined( ENABLE_TIMING ) #define timeStart( part ) timer.Start( part ); #define timeStop( part ) timer.Stop( part ); #else #define timeStart( part ) #define timeStop( part ) #endif void mitk::DICOMITKSeriesGDCMReader::AnalyzeInputFiles() { itk::TimeProbesCollectorBase timer; timeStart( "Reset" ); this->ClearOutputs(); timeStop( "Reset" ); // prepare initial sorting (== list of input files) const StringList inputFilenames = this->GetInputFiles(); timeStart( "Check input for DCM" ); if ( inputFilenames.empty() || !this->CanHandleFile( inputFilenames.front() ) // first || !this->CanHandleFile( inputFilenames.back() ) // last || !this->CanHandleFile( inputFilenames[inputFilenames.size() / 2] ) // roughly central file ) { // TODO a read-as-many-as-possible fallback could be implemented here MITK_DEBUG << "Reader unable to process files.."; return; } timeStop( "Check input for DCM" ); // scan files for sorting-relevant tags if ( m_TagCache.IsNull() || ( m_TagCache->GetMTime()GetMTime() && !m_ExternalCache )) { timeStart( "Tag scanning" ); DICOMGDCMTagScanner::Pointer filescanner = DICOMGDCMTagScanner::New(); filescanner->SetInputFiles( inputFilenames ); filescanner->AddTagPaths( this->GetTagsOfInterest() ); PushLocale(); filescanner->Scan(); PopLocale(); m_TagCache = filescanner->GetScanCache(); // keep alive and make accessible to sub-classes timeStop("Tag scanning"); } else { // ensure that the tag cache contains our required tags AND files and has scanned! } m_SortingResultInProgress.clear(); m_SortingResultInProgress.push_back(m_TagCache->GetFrameInfoList()); // sort and split blocks as configured timeStart( "Sorting frames" ); unsigned int sorterIndex = 0; for ( auto sorterIter = m_Sorter.cbegin(); sorterIter != m_Sorter.cend(); ++sorterIndex, ++sorterIter ) { std::stringstream ss; ss << "Sorting step " << sorterIndex; timeStart( ss.str().c_str() ); m_SortingResultInProgress = this->InternalExecuteSortingStep( sorterIndex, *sorterIter, m_SortingResultInProgress ); timeStop( ss.str().c_str() ); } if ( !m_SimpleVolumeReading ) { // a last extra-sorting step: ensure equidistant slices timeStart( "EquiDistantBlocksSorter" ); m_SortingResultInProgress = this->InternalExecuteSortingStep( sorterIndex++, m_EquiDistantBlocksSorter.GetPointer(), m_SortingResultInProgress ); timeStop( "EquiDistantBlocksSorter" ); } timeStop( "Sorting frames" ); timeStart( "Condensing 3D blocks" ); m_SortingResultInProgress = this->Condense3DBlocks( m_SortingResultInProgress ); timeStop( "Condensing 3D blocks" ); // provide final result as output timeStart( "Output" ); unsigned int o = this->GetNumberOfOutputs(); this->SetNumberOfOutputs( o + m_SortingResultInProgress.size() ); // Condense3DBlocks may already have added outputs! for ( auto blockIter = m_SortingResultInProgress.cbegin(); blockIter != m_SortingResultInProgress.cend(); ++o, ++blockIter ) { const DICOMDatasetAccessingImageFrameList& gdcmFrameInfoList = *blockIter; assert( !gdcmFrameInfoList.empty() ); // reverse frames if necessary // update tilt information from absolute last sorting const DICOMDatasetList datasetList = ConvertToDICOMDatasetList( gdcmFrameInfoList ); m_NormalDirectionConsistencySorter->SetInput( datasetList ); m_NormalDirectionConsistencySorter->Sort(); const DICOMDatasetAccessingImageFrameList sortedGdcmInfoFrameList = ConvertToDICOMDatasetAccessingImageFrameList( m_NormalDirectionConsistencySorter->GetOutput( 0 ) ); const GantryTiltInformation& tiltInfo = m_NormalDirectionConsistencySorter->GetTiltInformation(); // set frame list for current block const DICOMImageFrameList frameList = ConvertToDICOMImageFrameList( sortedGdcmInfoFrameList ); assert( !frameList.empty() ); DICOMImageBlockDescriptor block; block.SetTagCache( this->GetTagCache() ); // important: this must be before SetImageFrameList(), because // SetImageFrameList will trigger reading of lots of interesting // tags! block.SetAdditionalTagsOfInterest( GetAdditionalTagsOfInterest() ); block.SetTagLookupTableToPropertyFunctor( GetTagLookupTableToPropertyFunctor() ); block.SetImageFrameList( frameList ); block.SetTiltInformation( tiltInfo ); block.SetReaderImplementationLevel( this->GetReaderImplementationLevel( block.GetSOPClassUID() ) ); this->SetOutput( o, block ); } timeStop( "Output" ); #if defined( MBILOG_ENABLE_DEBUG ) || defined( ENABLE_TIMING ) std::cout << "---------------------------------------------------------------" << std::endl; timer.Report( std::cout ); std::cout << "---------------------------------------------------------------" << std::endl; #endif } mitk::DICOMITKSeriesGDCMReader::SortingBlockList mitk::DICOMITKSeriesGDCMReader::InternalExecuteSortingStep( unsigned int sortingStepIndex, const DICOMDatasetSorter::Pointer& sorter, const SortingBlockList& input ) { SortingBlockList nextStepSorting; // we should not modify our input list while processing it std::stringstream ss; ss << "Sorting step " << sortingStepIndex << " '"; #if defined( MBILOG_ENABLE_DEBUG ) sorter->PrintConfiguration( ss ); #endif ss << "'"; nextStepSorting.clear(); MITK_DEBUG << "================================================================================"; MITK_DEBUG << "DICOMITKSeriesGDCMReader: " << ss.str() << ": " << input.size() << " groups input"; unsigned int groupIndex = 0; for ( auto blockIter = input.cbegin(); blockIter != input.cend(); ++groupIndex, ++blockIter ) { const DICOMDatasetAccessingImageFrameList& gdcmInfoFrameList = *blockIter; const DICOMDatasetList datasetList = ConvertToDICOMDatasetList( gdcmInfoFrameList ); #if defined( MBILOG_ENABLE_DEBUG ) MITK_DEBUG << "--------------------------------------------------------------------------------"; MITK_DEBUG << "DICOMITKSeriesGDCMReader: " << ss.str() << ", dataset group " << groupIndex << " (" << datasetList.size() << " datasets): "; for ( auto oi = datasetList.cbegin(); oi != datasetList.cend(); ++oi ) { MITK_DEBUG << " INPUT : " << ( *oi )->GetFilenameIfAvailable(); } #endif sorter->SetInput( datasetList ); sorter->Sort(); unsigned int numberOfResultingBlocks = sorter->GetNumberOfOutputs(); for ( unsigned int b = 0; b < numberOfResultingBlocks; ++b ) { const DICOMDatasetList blockResult = sorter->GetOutput( b ); for ( auto oi = blockResult.cbegin(); oi != blockResult.cend(); ++oi ) { MITK_DEBUG << " OUTPUT(" << b << ") :" << ( *oi )->GetFilenameIfAvailable(); } DICOMDatasetAccessingImageFrameList sortedGdcmInfoFrameList = ConvertToDICOMDatasetAccessingImageFrameList( blockResult ); nextStepSorting.push_back( sortedGdcmInfoFrameList ); } } return nextStepSorting; } mitk::ReaderImplementationLevel mitk::DICOMITKSeriesGDCMReader::GetReaderImplementationLevel( const std::string sopClassUID ) { if ( sopClassUID.empty() ) { return SOPClassUnknown; } gdcm::UIDs uidKnowledge; uidKnowledge.SetFromUID( sopClassUID.c_str() ); gdcm::UIDs::TSType gdcmType = uidKnowledge; switch ( gdcmType ) { case gdcm::UIDs::CTImageStorage: case gdcm::UIDs::MRImageStorage: case gdcm::UIDs::PositronEmissionTomographyImageStorage: case gdcm::UIDs::ComputedRadiographyImageStorage: case gdcm::UIDs::DigitalXRayImageStorageForPresentation: case gdcm::UIDs::DigitalXRayImageStorageForProcessing: return SOPClassSupported; case gdcm::UIDs::NuclearMedicineImageStorage: return SOPClassPartlySupported; case gdcm::UIDs::SecondaryCaptureImageStorage: return SOPClassImplemented; default: return SOPClassUnsupported; } } // void AllocateOutputImages(); bool mitk::DICOMITKSeriesGDCMReader::LoadImages() { bool success = true; unsigned int numberOfOutputs = this->GetNumberOfOutputs(); for ( unsigned int o = 0; o < numberOfOutputs; ++o ) { success &= this->LoadMitkImageForOutput( o ); } return success; } bool mitk::DICOMITKSeriesGDCMReader::LoadMitkImageForImageBlockDescriptor( DICOMImageBlockDescriptor& block ) const { PushLocale(); const DICOMImageFrameList& frames = block.GetImageFrameList(); const GantryTiltInformation tiltInfo = block.GetTiltInformation(); bool hasTilt = tiltInfo.IsRegularGantryTilt(); ITKDICOMSeriesReaderHelper::StringContainer filenames; filenames.reserve( frames.size() ); for ( auto frameIter = frames.cbegin(); frameIter != frames.cend(); ++frameIter ) { filenames.push_back( ( *frameIter )->Filename ); } mitk::ITKDICOMSeriesReaderHelper helper; bool success( true ); try { mitk::Image::Pointer mitkImage = helper.Load( filenames, m_FixTiltByShearing && hasTilt, tiltInfo ); block.SetMitkImage( mitkImage ); } catch ( const std::exception& e ) { success = false; MITK_ERROR << "Exception during image loading: " << e.what(); } PopLocale(); return success; } bool mitk::DICOMITKSeriesGDCMReader::LoadMitkImageForOutput( unsigned int o ) { DICOMImageBlockDescriptor& block = this->InternalGetOutput( o ); return this->LoadMitkImageForImageBlockDescriptor( block ); } bool mitk::DICOMITKSeriesGDCMReader::CanHandleFile( const std::string& filename ) { return ITKDICOMSeriesReaderHelper::CanHandleFile( filename ); } void mitk::DICOMITKSeriesGDCMReader::AddSortingElement( DICOMDatasetSorter* sorter, bool atFront ) { assert( sorter ); if ( atFront ) { m_Sorter.push_front( sorter ); } else { m_Sorter.push_back( sorter ); } this->Modified(); } mitk::DICOMITKSeriesGDCMReader::ConstSorterList mitk::DICOMITKSeriesGDCMReader::GetFreelyConfiguredSortingElements() const { std::list result; unsigned int sortIndex( 0 ); for ( auto sorterIter = m_Sorter.begin(); sorterIter != m_Sorter.end(); ++sortIndex, ++sorterIter ) { if ( sortIndex > 0 ) // ignore first element (see EnsureMandatorySortersArePresent) { result.push_back( ( *sorterIter ).GetPointer() ); } } return result; } void mitk::DICOMITKSeriesGDCMReader::EnsureMandatorySortersArePresent( unsigned int decimalPlacesForOrientation, bool simpleVolumeImport ) { DICOMTagBasedSorter::Pointer splitter = DICOMTagBasedSorter::New(); splitter->AddDistinguishingTag( DICOMTag(0x0028, 0x0010) ); // Number of Rows splitter->AddDistinguishingTag( DICOMTag(0x0028, 0x0011) ); // Number of Columns splitter->AddDistinguishingTag( DICOMTag(0x0028, 0x0030) ); // Pixel Spacing splitter->AddDistinguishingTag( DICOMTag(0x0018, 0x1164) ); // Imager Pixel Spacing splitter->AddDistinguishingTag( DICOMTag(0x0020, 0x0037), new mitk::DICOMTagBasedSorter::CutDecimalPlaces(decimalPlacesForOrientation) ); // Image Orientation (Patient) splitter->AddDistinguishingTag( DICOMTag(0x0018, 0x0050) ); // Slice Thickness if ( !simpleVolumeImport ) { std::cout << "Simple volume reading: ignoring number of frames" << std::endl; splitter->AddDistinguishingTag( DICOMTag(0x0028, 0x0008) ); // Number of Frames } this->AddSortingElement( splitter, true ); // true = at front if ( m_EquiDistantBlocksSorter.IsNull() ) { m_EquiDistantBlocksSorter = mitk::EquiDistantBlocksSorter::New(); } m_EquiDistantBlocksSorter->SetAcceptTilt( m_FixTiltByShearing ); if ( m_NormalDirectionConsistencySorter.IsNull() ) { m_NormalDirectionConsistencySorter = mitk::NormalDirectionConsistencySorter::New(); } } void mitk::DICOMITKSeriesGDCMReader::SetToleratedOriginOffsetToAdaptive( double fractionOfInterSliceDistance ) const { assert( m_EquiDistantBlocksSorter.IsNotNull() ); m_EquiDistantBlocksSorter->SetToleratedOriginOffsetToAdaptive( fractionOfInterSliceDistance ); this->Modified(); } void mitk::DICOMITKSeriesGDCMReader::SetToleratedOriginOffset( double millimeters ) const { assert( m_EquiDistantBlocksSorter.IsNotNull() ); m_EquiDistantBlocksSorter->SetToleratedOriginOffset( millimeters ); this->Modified(); } double mitk::DICOMITKSeriesGDCMReader::GetToleratedOriginError() const { assert( m_EquiDistantBlocksSorter.IsNotNull() ); return m_EquiDistantBlocksSorter->GetToleratedOriginOffset(); } bool mitk::DICOMITKSeriesGDCMReader::IsToleratedOriginOffsetAbsolute() const { assert( m_EquiDistantBlocksSorter.IsNotNull() ); return m_EquiDistantBlocksSorter->IsToleratedOriginOffsetAbsolute(); } double mitk::DICOMITKSeriesGDCMReader::GetDecimalPlacesForOrientation() const { return m_DecimalPlacesForOrientation; } mitk::DICOMTagCache::Pointer mitk::DICOMITKSeriesGDCMReader::GetTagCache() const { return m_TagCache; } void mitk::DICOMITKSeriesGDCMReader::SetTagCache( const DICOMTagCache::Pointer& tagCache ) { m_TagCache = tagCache; m_ExternalCache = tagCache.IsNotNull(); } mitk::DICOMTagPathList mitk::DICOMITKSeriesGDCMReader::GetTagsOfInterest() const { DICOMTagPathList completeList; // check all configured sorters for ( auto sorterIter = m_Sorter.cbegin(); sorterIter != m_Sorter.cend(); ++sorterIter ) { assert( sorterIter->IsNotNull() ); const DICOMTagList tags = ( *sorterIter )->GetTagsOfInterest(); completeList.insert( completeList.end(), tags.cbegin(), tags.cend() ); } // check our own forced sorters DICOMTagList tags = m_EquiDistantBlocksSorter->GetTagsOfInterest(); completeList.insert( completeList.end(), tags.cbegin(), tags.cend() ); tags = m_NormalDirectionConsistencySorter->GetTagsOfInterest(); completeList.insert( completeList.end(), tags.cbegin(), tags.cend() ); // add the tags for DICOMImageBlockDescriptor tags = DICOMImageBlockDescriptor::GetTagsOfInterest(); completeList.insert( completeList.end(), tags.cbegin(), tags.cend() ); const AdditionalTagsMapType tagList = GetAdditionalTagsOfInterest(); for ( auto iter = tagList.cbegin(); iter != tagList.cend(); ++iter ) { completeList.push_back( iter->first ) ; } return completeList; } diff --git a/Modules/DICOMReader/src/mitkDICOMImageBlockDescriptor.cpp b/Modules/DICOMReader/src/mitkDICOMImageBlockDescriptor.cpp index e3587efdeb..a777d79ce9 100644 --- a/Modules/DICOMReader/src/mitkDICOMImageBlockDescriptor.cpp +++ b/Modules/DICOMReader/src/mitkDICOMImageBlockDescriptor.cpp @@ -1,863 +1,863 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkDICOMImageBlockDescriptor.h" #include "mitkStringProperty.h" #include "mitkLevelWindowProperty.h" #include mitk::DICOMImageBlockDescriptor::DICOMImageBlockDescriptor() : m_ReaderImplementationLevel( SOPClassUnknown ) , m_PropertyList( PropertyList::New() ) , m_TagCache( nullptr ) , m_PropertiesOutOfDate( true ) { m_PropertyFunctor = &mitk::DICOMImageBlockDescriptor::GetPropertyForDICOMValues; } mitk::DICOMImageBlockDescriptor::~DICOMImageBlockDescriptor() { } mitk::DICOMImageBlockDescriptor::DICOMImageBlockDescriptor( const DICOMImageBlockDescriptor& other ) : m_ImageFrameList( other.m_ImageFrameList ) , m_MitkImage( other.m_MitkImage ) , m_SliceIsLoaded( other.m_SliceIsLoaded ) , m_ReaderImplementationLevel( other.m_ReaderImplementationLevel ) , m_TiltInformation( other.m_TiltInformation ) , m_PropertyList( other.m_PropertyList->Clone() ) , m_TagCache( other.m_TagCache ) , m_PropertiesOutOfDate( other.m_PropertiesOutOfDate ) , m_AdditionalTagMap(other.m_AdditionalTagMap) , m_FoundAdditionalTags(other.m_FoundAdditionalTags) , m_PropertyFunctor(other.m_PropertyFunctor) { if ( m_MitkImage ) { m_MitkImage = m_MitkImage->Clone(); } m_PropertyFunctor = &mitk::DICOMImageBlockDescriptor::GetPropertyForDICOMValues; } mitk::DICOMImageBlockDescriptor& mitk::DICOMImageBlockDescriptor:: operator=( const DICOMImageBlockDescriptor& other ) { if ( this != &other ) { m_ImageFrameList = other.m_ImageFrameList; m_MitkImage = other.m_MitkImage; m_SliceIsLoaded = other.m_SliceIsLoaded; m_ReaderImplementationLevel = other.m_ReaderImplementationLevel; m_TiltInformation = other.m_TiltInformation; m_AdditionalTagMap = other.m_AdditionalTagMap; m_FoundAdditionalTags = other.m_FoundAdditionalTags; m_PropertyFunctor = other.m_PropertyFunctor; if ( other.m_PropertyList ) { m_PropertyList = other.m_PropertyList->Clone(); } if ( other.m_MitkImage ) { m_MitkImage = other.m_MitkImage->Clone(); } m_TagCache = other.m_TagCache; m_PropertiesOutOfDate = other.m_PropertiesOutOfDate; } return *this; } mitk::DICOMTagList mitk::DICOMImageBlockDescriptor::GetTagsOfInterest() { DICOMTagList completeList; completeList.push_back( DICOMTag( 0x0018, 0x1164 ) ); // pixel spacing completeList.push_back( DICOMTag( 0x0028, 0x0030 ) ); // imager pixel spacing completeList.push_back( DICOMTag( 0x0008, 0x0018 ) ); // sop instance UID completeList.push_back( DICOMTag( 0x0008, 0x0016 ) ); // sop class UID completeList.push_back( DICOMTag( 0x0020, 0x0011 ) ); // series number completeList.push_back( DICOMTag( 0x0008, 0x1030 ) ); // study description completeList.push_back( DICOMTag( 0x0008, 0x103e ) ); // series description completeList.push_back( DICOMTag( 0x0008, 0x0060 ) ); // modality completeList.push_back( DICOMTag( 0x0018, 0x0024 ) ); // sequence name completeList.push_back( DICOMTag( 0x0020, 0x0037 ) ); // image orientation completeList.push_back( DICOMTag( 0x0020, 0x1041 ) ); // slice location completeList.push_back( DICOMTag( 0x0020, 0x0012 ) ); // acquisition number completeList.push_back( DICOMTag( 0x0020, 0x0013 ) ); // instance number completeList.push_back( DICOMTag( 0x0020, 0x0032 ) ); // image position patient completeList.push_back( DICOMTag( 0x0028, 0x1050 ) ); // window center completeList.push_back( DICOMTag( 0x0028, 0x1051 ) ); // window width completeList.push_back( DICOMTag( 0x0008, 0x0008 ) ); // image type completeList.push_back( DICOMTag( 0x0028, 0x0004 ) ); // photometric interpretation return completeList; } void mitk::DICOMImageBlockDescriptor::SetAdditionalTagsOfInterest( const AdditionalTagsMapType& tagMap) { m_AdditionalTagMap = tagMap; } void mitk::DICOMImageBlockDescriptor::SetTiltInformation( const GantryTiltInformation& info ) { m_TiltInformation = info; } const mitk::GantryTiltInformation mitk::DICOMImageBlockDescriptor::GetTiltInformation() const { return m_TiltInformation; } void mitk::DICOMImageBlockDescriptor::SetImageFrameList( const DICOMImageFrameList& framelist ) { m_ImageFrameList = framelist; m_SliceIsLoaded.resize( framelist.size() ); m_SliceIsLoaded.assign( framelist.size(), false ); m_PropertiesOutOfDate = true; } const mitk::DICOMImageFrameList& mitk::DICOMImageBlockDescriptor::GetImageFrameList() const { return m_ImageFrameList; } void mitk::DICOMImageBlockDescriptor::SetMitkImage( Image::Pointer image ) { if ( m_MitkImage != image ) { if ( m_TagCache.IsNull() ) { MITK_ERROR << "Unable to describe MITK image with properties without a tag-cache object!"; m_MitkImage = nullptr; return; } if ( m_ImageFrameList.empty() ) { MITK_ERROR << "Unable to describe MITK image with properties without a frame list!"; m_MitkImage = nullptr; return; } // Should verify that the image matches m_ImageFrameList and m_TagCache // however, this is hard to do without re-analyzing all // TODO we should at least make sure that the number of frames is identical (plus rows/columns, // orientation) // without gantry tilt correction, we can also check image origin m_MitkImage = this->DescribeImageWithProperties( this->FixupSpacing( image ) ); } } mitk::Image::Pointer mitk::DICOMImageBlockDescriptor::GetMitkImage() const { return m_MitkImage; } mitk::Image::Pointer mitk::DICOMImageBlockDescriptor::FixupSpacing( Image* mitkImage ) { if ( mitkImage ) { Vector3D imageSpacing = mitkImage->GetGeometry()->GetSpacing(); ScalarType desiredSpacingX = imageSpacing[0]; ScalarType desiredSpacingY = imageSpacing[1]; this->GetDesiredMITKImagePixelSpacing( desiredSpacingX, desiredSpacingY ); // prefer pixel spacing over imager pixel spacing if ( desiredSpacingX <= 0 || desiredSpacingY <= 0 ) { return mitkImage; } MITK_DEBUG << "Loaded image with spacing " << imageSpacing[0] << ", " << imageSpacing[1]; MITK_DEBUG << "Found correct spacing info " << desiredSpacingX << ", " << desiredSpacingY; imageSpacing[0] = desiredSpacingX; imageSpacing[1] = desiredSpacingY; mitkImage->GetGeometry()->SetSpacing( imageSpacing ); } return mitkImage; } void mitk::DICOMImageBlockDescriptor::SetSliceIsLoaded( unsigned int index, bool isLoaded ) { if ( index < m_SliceIsLoaded.size() ) { m_SliceIsLoaded[index] = isLoaded; } else { std::stringstream ss; ss << "Index " << index << " out of range (" << m_SliceIsLoaded.size() << " indices reserved)"; throw std::invalid_argument( ss.str() ); } } bool mitk::DICOMImageBlockDescriptor::IsSliceLoaded( unsigned int index ) const { if ( index < m_SliceIsLoaded.size() ) { return m_SliceIsLoaded[index]; } else { std::stringstream ss; ss << "Index " << index << " out of range (" << m_SliceIsLoaded.size() << " indices reserved)"; throw std::invalid_argument( ss.str() ); } } bool mitk::DICOMImageBlockDescriptor::AllSlicesAreLoaded() const { bool allLoaded = true; for ( auto iter = m_SliceIsLoaded.cbegin(); iter != m_SliceIsLoaded.cend(); ++iter ) { allLoaded &= *iter; } return allLoaded; } /* PS defined IPS defined PS==IPS 0 0 --> UNKNOWN spacing, loader will invent 0 1 --> spacing as at detector surface 1 0 --> spacing as in patient 1 1 0 --> detector surface spacing CORRECTED for geometrical magnifications: spacing as in patient 1 1 1 --> detector surface spacing NOT corrected for geometrical magnifications: spacing as at detector */ mitk::PixelSpacingInterpretation mitk::DICOMImageBlockDescriptor::GetPixelSpacingInterpretation() const { if ( m_ImageFrameList.empty() || m_TagCache.IsNull() ) { MITK_ERROR << "Invalid call to GetPixelSpacingInterpretation. Need to have initialized tag-cache!"; return SpacingUnknown; } const std::string pixelSpacing = this->GetPixelSpacing(); const std::string imagerPixelSpacing = this->GetImagerPixelSpacing(); if ( pixelSpacing.empty() ) { if ( imagerPixelSpacing.empty() ) { return SpacingUnknown; } else { return SpacingAtDetector; } } else // Pixel Spacing defined { if ( imagerPixelSpacing.empty() ) { return SpacingInPatient; } else if ( pixelSpacing != imagerPixelSpacing ) { return SpacingInPatient; } else { return SpacingAtDetector; } } } std::string mitk::DICOMImageBlockDescriptor::GetPixelSpacing() const { if ( m_ImageFrameList.empty() || m_TagCache.IsNull() ) { MITK_ERROR << "Invalid call to GetPixelSpacing. Need to have initialized tag-cache!"; return std::string( "" ); } static const DICOMTag tagPixelSpacing( 0x0028, 0x0030 ); return m_TagCache->GetTagValue( m_ImageFrameList.front(), tagPixelSpacing ).value; } std::string mitk::DICOMImageBlockDescriptor::GetImagerPixelSpacing() const { if ( m_ImageFrameList.empty() || m_TagCache.IsNull() ) { MITK_ERROR << "Invalid call to GetImagerPixelSpacing. Need to have initialized tag-cache!"; return std::string( "" ); } static const DICOMTag tagImagerPixelSpacing( 0x0018, 0x1164 ); return m_TagCache->GetTagValue( m_ImageFrameList.front(), tagImagerPixelSpacing ).value; } void mitk::DICOMImageBlockDescriptor::GetDesiredMITKImagePixelSpacing( ScalarType& spacingX, ScalarType& spacingY ) const { const std::string pixelSpacing = this->GetPixelSpacing(); // preference for "in patient" pixel spacing if ( !DICOMStringToSpacing( pixelSpacing, spacingX, spacingY ) ) { const std::string imagerPixelSpacing = this->GetImagerPixelSpacing(); // fallback to "on detector" spacing if ( !DICOMStringToSpacing( imagerPixelSpacing, spacingX, spacingY ) ) { // at this point we have no hints whether the spacing is correct // do a quick sanity check and either trust in the input or set both to 1 // We assume neither spacing to be negative, zero or unexpectedly large for // medical images if (spacingX < mitk::eps || spacingX > 1000 || spacingY < mitk::eps || spacingY > 1000) { spacingX = spacingY = 1.0; } } } } void mitk::DICOMImageBlockDescriptor::SetProperty( const std::string& key, BaseProperty* value ) { m_PropertyList->SetProperty( key, value ); } mitk::BaseProperty* mitk::DICOMImageBlockDescriptor::GetProperty( const std::string& key ) const { this->UpdateImageDescribingProperties(); return m_PropertyList->GetProperty( key ); } std::string mitk::DICOMImageBlockDescriptor::GetPropertyAsString( const std::string& key ) const { this->UpdateImageDescribingProperties(); const mitk::BaseProperty::Pointer property = m_PropertyList->GetProperty( key ); if ( property.IsNotNull() ) { return property->GetValueAsString(); } else { return std::string( "" ); } } void mitk::DICOMImageBlockDescriptor::SetFlag( const std::string& key, bool value ) { m_PropertyList->ReplaceProperty( key, BoolProperty::New( value ) ); } bool mitk::DICOMImageBlockDescriptor::GetFlag( const std::string& key, bool defaultValue ) const { this->UpdateImageDescribingProperties(); BoolProperty::ConstPointer boolProp = dynamic_cast( this->GetProperty( key ) ); if ( boolProp.IsNotNull() ) { return boolProp->GetValue(); } else { return defaultValue; } } void mitk::DICOMImageBlockDescriptor::SetIntProperty( const std::string& key, int value ) { m_PropertyList->ReplaceProperty( key, IntProperty::New( value ) ); } int mitk::DICOMImageBlockDescriptor::GetIntProperty( const std::string& key, int defaultValue ) const { this->UpdateImageDescribingProperties(); IntProperty::ConstPointer intProp = dynamic_cast( this->GetProperty( key ) ); if ( intProp.IsNotNull() ) { return intProp->GetValue(); } else { return defaultValue; } } double mitk::DICOMImageBlockDescriptor::stringtodouble( const std::string& str ) const { double d; std::string trimmedstring( str ); try { trimmedstring = trimmedstring.erase( trimmedstring.find_last_not_of( " \n\r\t" ) + 1 ); } catch ( ... ) { // no last not of } std::string firstcomponent( trimmedstring ); try { firstcomponent = trimmedstring.erase( trimmedstring.find_first_of( "\\" ) ); } catch ( ... ) { // no last not of } std::istringstream converter( firstcomponent ); if ( !firstcomponent.empty() && ( converter >> d ) && converter.eof() ) { return d; } else { throw std::invalid_argument( "Argument is not a convertable number" ); } } mitk::Image::Pointer mitk::DICOMImageBlockDescriptor::DescribeImageWithProperties( Image* mitkImage ) { // TODO: this is a collection of properties that have been provided by the // legacy DicomSeriesReader. // We should at some point clean up this collection and name them in a more // consistent way! if ( !mitkImage ) return mitkImage; // first part: add some tags that describe individual slices // these propeties are defined at analysis time (see UpdateImageDescribingProperties()) const char* propertyKeySliceLocation = "dicom.image.0020.1041"; const char* propertyKeyInstanceNumber = "dicom.image.0020.0013"; const char* propertyKeySOPInstanceUID = "dicom.image.0008.0018"; mitkImage->SetProperty( propertyKeySliceLocation, this->GetProperty( "sliceLocationForSlices" ) ); mitkImage->SetProperty( propertyKeyInstanceNumber, this->GetProperty( "instanceNumberForSlices" ) ); mitkImage->SetProperty( propertyKeySOPInstanceUID, this->GetProperty( "SOPInstanceUIDForSlices" ) ); mitkImage->SetProperty( "files", this->GetProperty( "filenamesForSlices" ) ); // second part: add properties that describe the whole image block mitkImage->SetProperty( "dicomseriesreader.SOPClassUID", StringProperty::New( this->GetSOPClassUID() ) ); mitkImage->SetProperty( "dicomseriesreader.SOPClass", StringProperty::New( this->GetSOPClassUIDAsName() ) ); mitkImage->SetProperty( "dicomseriesreader.PixelSpacingInterpretationString", StringProperty::New( PixelSpacingInterpretationToString( this->GetPixelSpacingInterpretation() ) ) ); mitkImage->SetProperty( "dicomseriesreader.PixelSpacingInterpretation", GenericProperty::New( this->GetPixelSpacingInterpretation() ) ); mitkImage->SetProperty( "dicomseriesreader.ReaderImplementationLevelString", StringProperty::New( ReaderImplementationLevelToString( m_ReaderImplementationLevel ) ) ); mitkImage->SetProperty( "dicomseriesreader.ReaderImplementationLevel", GenericProperty::New( m_ReaderImplementationLevel ) ); mitkImage->SetProperty( "dicomseriesreader.GantyTiltCorrected", BoolProperty::New( this->GetTiltInformation().IsRegularGantryTilt() ) ); mitkImage->SetProperty( "dicomseriesreader.3D+t", BoolProperty::New( this->GetFlag( "3D+t", false ) ) ); // level window const std::string windowCenter = this->GetPropertyAsString( "windowCenter" ); const std::string windowWidth = this->GetPropertyAsString( "windowWidth" ); try { const double level = stringtodouble( windowCenter ); const double window = stringtodouble( windowWidth ); mitkImage->SetProperty( "levelwindow", LevelWindowProperty::New( LevelWindow( level, window ) ) ); } catch ( ... ) { // nothing, no levelwindow to be predicted... } const std::string modality = this->GetPropertyAsString( "modality" ); mitkImage->SetProperty( "modality", StringProperty::New( modality ) ); mitkImage->SetProperty( "dicom.pixel.PhotometricInterpretation", this->GetProperty( "photometricInterpretation" ) ); mitkImage->SetProperty( "dicom.image.imagetype", this->GetProperty( "imagetype" ) ); mitkImage->SetProperty( "dicom.study.StudyDescription", this->GetProperty( "studyDescription" ) ); mitkImage->SetProperty( "dicom.series.SeriesDescription", this->GetProperty( "seriesDescription" ) ); mitkImage->SetProperty( "dicom.pixel.Rows", this->GetProperty( "rows" ) ); mitkImage->SetProperty( "dicom.pixel.Columns", this->GetProperty( "columns" ) ); // third part: get all found additional tags of interest for (auto tag : m_FoundAdditionalTags) { BaseProperty* prop = this->GetProperty(tag); if (prop) { mitkImage->SetProperty(tag.c_str(), prop); } } // fourth part: get something from ImageIO. BUT this needs to be created elsewhere. or not at all! return mitkImage; } void mitk::DICOMImageBlockDescriptor::SetReaderImplementationLevel( const ReaderImplementationLevel& level ) { m_ReaderImplementationLevel = level; } mitk::ReaderImplementationLevel mitk::DICOMImageBlockDescriptor::GetReaderImplementationLevel() const { return m_ReaderImplementationLevel; } std::string mitk::DICOMImageBlockDescriptor::GetSOPClassUID() const { if ( !m_ImageFrameList.empty() && m_TagCache.IsNotNull() ) { static const DICOMTag tagSOPClassUID( 0x0008, 0x0016 ); return m_TagCache->GetTagValue( m_ImageFrameList.front(), tagSOPClassUID ).value; } else { MITK_ERROR << "Invalid call to DICOMImageBlockDescriptor::GetSOPClassUID(). Need to have initialized tag-cache!"; return std::string( "" ); } } std::string mitk::DICOMImageBlockDescriptor::GetSOPClassUIDAsName() const { if ( !m_ImageFrameList.empty() && m_TagCache.IsNotNull() ) { gdcm::UIDs uidKnowledge; uidKnowledge.SetFromUID( this->GetSOPClassUID().c_str() ); const char* name = uidKnowledge.GetName(); if ( name ) { return std::string( name ); } else { return std::string( "" ); } } else { MITK_ERROR << "Invalid call to DICOMImageBlockDescriptor::GetSOPClassUIDAsName(). Need to have " "initialized tag-cache!"; return std::string( "" ); } } int mitk::DICOMImageBlockDescriptor::GetNumberOfTimeSteps() const { int result = 1; this->m_PropertyList->GetIntProperty("timesteps", result); return result; }; int mitk::DICOMImageBlockDescriptor::GetNumberOfFramesPerTimeStep() const { const int numberOfTimesteps = this->GetNumberOfTimeSteps(); int numberOfFramesPerTimestep = this->m_ImageFrameList.size() / numberOfTimesteps; assert(int(double((double)this->m_ImageFrameList.size() / (double)numberOfTimesteps)) == numberOfFramesPerTimestep); // this should hold return numberOfFramesPerTimestep; }; void mitk::DICOMImageBlockDescriptor::SetTagCache( DICOMTagCache* privateCache ) { // this must only be used during loading and never afterwards m_TagCache = privateCache; } #define printPropertyRange( label, property_name ) \ \ { \ const std::string first = this->GetPropertyAsString( #property_name "First" ); \ const std::string last = this->GetPropertyAsString( #property_name "Last" ); \ if ( !first.empty() || !last.empty() ) \ { \ if ( first == last ) \ { \ os << " " label ": '" << first << "'" << std::endl; \ } \ else \ { \ os << " " label ": '" << first << "' - '" << last << "'" << std::endl; \ } \ } \ \ } #define printProperty( label, property_name ) \ \ { \ const std::string first = this->GetPropertyAsString( #property_name ); \ if ( !first.empty() ) \ { \ os << " " label ": '" << first << "'" << std::endl; \ } \ \ } #define printBool( label, commands ) \ \ { \ os << " " label ": '" << ( commands ? "yes" : "no" ) << "'" << std::endl; \ \ } void mitk::DICOMImageBlockDescriptor::Print(std::ostream& os, bool filenameDetails) const { os << " Number of Frames: '" << m_ImageFrameList.size() << "'" << std::endl; os << " SOP class: '" << this->GetSOPClassUIDAsName() << "'" << std::endl; printProperty( "Series Number", seriesNumber ); printProperty( "Study Description", studyDescription ); printProperty( "Series Description", seriesDescription ); printProperty( "Modality", modality ); printProperty( "Sequence Name", sequenceName ); printPropertyRange( "Slice Location", sliceLocation ); printPropertyRange( "Acquisition Number", acquisitionNumber ); printPropertyRange( "Instance Number", instanceNumber ); printPropertyRange( "Image Position", imagePositionPatient ); printProperty( "Image Orientation", orientation ); os << " Pixel spacing interpretation: '" << PixelSpacingInterpretationToString( this->GetPixelSpacingInterpretation() ) << "'" << std::endl; printBool( "Gantry Tilt", this->GetTiltInformation().IsRegularGantryTilt() ) // printBool("3D+t", this->GetFlag("3D+t",false)) // os << " MITK image loaded: '" << (this->GetMitkImage().IsNotNull() ? "yes" : "no") << "'" << // std::endl; if ( filenameDetails ) { os << " Files in this image block:" << std::endl; for ( auto frameIter = m_ImageFrameList.begin(); frameIter != m_ImageFrameList.end(); ++frameIter ) { os << " " << ( *frameIter )->Filename; if ( ( *frameIter )->FrameNo > 0 ) { os << ", " << ( *frameIter )->FrameNo; } os << std::endl; } } } #define storeTagValueToProperty( tag_name, tag_g, tag_e ) \ \ { \ const DICOMTag t( tag_g, tag_e ); \ const std::string tagValue = m_TagCache->GetTagValue( firstFrame, t ).value; \ const_cast( this ) \ ->SetProperty( #tag_name, StringProperty::New( tagValue ) ); \ \ } #define storeTagValueRangeToProperty( tag_name, tag_g, tag_e ) \ \ { \ const DICOMTag t( tag_g, tag_e ); \ const std::string tagValueFirst = m_TagCache->GetTagValue( firstFrame, t ).value; \ const std::string tagValueLast = m_TagCache->GetTagValue( lastFrame, t ).value; \ const_cast( this ) \ ->SetProperty( #tag_name "First", StringProperty::New( tagValueFirst ) ); \ const_cast( this ) \ ->SetProperty( #tag_name "Last", StringProperty::New( tagValueLast ) ); \ \ } void mitk::DICOMImageBlockDescriptor::UpdateImageDescribingProperties() const { if ( !m_PropertiesOutOfDate ) return; if ( !m_ImageFrameList.empty() ) { if ( m_TagCache.IsNull() ) { MITK_ERROR << "Invalid call to DICOMImageBlockDescriptor::UpdateImageDescribingProperties(). Need to " "have initialized tag-cache!"; return; } const DICOMImageFrameInfo::Pointer firstFrame = m_ImageFrameList.front(); const DICOMImageFrameInfo::Pointer lastFrame = m_ImageFrameList.back(); // see macros above storeTagValueToProperty( seriesNumber, 0x0020, 0x0011 ); storeTagValueToProperty( studyDescription, 0x0008, 0x1030 ); storeTagValueToProperty( seriesDescription, 0x0008, 0x103e ); storeTagValueToProperty( modality, 0x0008, 0x0060 ); storeTagValueToProperty( sequenceName, 0x0018, 0x0024 ); storeTagValueToProperty( orientation, 0x0020, 0x0037 ); storeTagValueToProperty( rows, 0x0028, 0x0010 ); storeTagValueToProperty( columns, 0x0028, 0x0011 ); storeTagValueRangeToProperty( sliceLocation, 0x0020, 0x1041 ); storeTagValueRangeToProperty( acquisitionNumber, 0x0020, 0x0012 ); storeTagValueRangeToProperty( instanceNumber, 0x0020, 0x0013 ); storeTagValueRangeToProperty( imagePositionPatient, 0x0020, 0x0032 ); storeTagValueToProperty( windowCenter, 0x0028, 0x1050 ); storeTagValueToProperty( windowWidth, 0x0028, 0x1051 ); storeTagValueToProperty( imageType, 0x0008, 0x0008 ); storeTagValueToProperty( photometricInterpretation, 0x0028, 0x0004 ); // some per-image attributes // frames are just numbered starting from 0. timestep 1 (the second time-step) has frames starting at // (number-of-frames-per-timestep) // std::string propertyKeySliceLocation = "dicom.image.0020.1041"; // std::string propertyKeyInstanceNumber = "dicom.image.0020.0013"; // std::string propertyKeySOPInstanceNumber = "dicom.image.0008.0018"; StringLookupTable sliceLocationForSlices; StringLookupTable instanceNumberForSlices; StringLookupTable SOPInstanceUIDForSlices; StringLookupTable filenamesForSlices; const DICOMTag tagSliceLocation( 0x0020, 0x1041 ); const DICOMTag tagInstanceNumber( 0x0020, 0x0013 ); const DICOMTag tagSOPInstanceNumber( 0x0008, 0x0018 ); std::unordered_map additionalTagResultList; unsigned int slice(0); int timePoint(-1); const int framesPerTimeStep = this->GetNumberOfFramesPerTimeStep(); for ( auto frameIter = m_ImageFrameList.begin(); frameIter != m_ImageFrameList.end(); ++slice, ++frameIter ) { unsigned int zSlice = slice%framesPerTimeStep; if ( zSlice == 0) { timePoint++; } const std::string sliceLocation = m_TagCache->GetTagValue( *frameIter, tagSliceLocation ).value; sliceLocationForSlices.SetTableValue( slice, sliceLocation ); const std::string instanceNumber = m_TagCache->GetTagValue( *frameIter, tagInstanceNumber ).value; instanceNumberForSlices.SetTableValue( slice, instanceNumber ); const std::string sopInstanceUID = m_TagCache->GetTagValue( *frameIter, tagSOPInstanceNumber ).value; SOPInstanceUIDForSlices.SetTableValue( slice, sopInstanceUID ); const std::string filename = ( *frameIter )->Filename; filenamesForSlices.SetTableValue( slice, filename ); MITK_DEBUG << "Tag info for slice " << slice << ": SL '" << sliceLocation << "' IN '" << instanceNumber << "' SOP instance UID '" << sopInstanceUID << "'"; for (const auto& tag : m_AdditionalTagMap) { const DICOMTagCache::FindingsListType findings = m_TagCache->GetTagValue( *frameIter, tag.first ); for (const auto& finding : findings) { if (finding.isValid) { std::string propKey = (tag.second.empty()) ? DICOMTagPathToPropertyName(finding.path) : tag.second; DICOMCachedValueInfo info{ static_cast(timePoint), zSlice, finding.value }; additionalTagResultList[propKey].SetTableValue(slice, info); } } } } // add property or properties with proper names - DICOMImageBlockDescriptor* thisInstance = const_cast( this ); + auto* thisInstance = const_cast( this ); thisInstance->SetProperty( "sliceLocationForSlices", StringLookupTableProperty::New( sliceLocationForSlices ) ); thisInstance->SetProperty( "instanceNumberForSlices", StringLookupTableProperty::New( instanceNumberForSlices ) ); thisInstance->SetProperty( "SOPInstanceUIDForSlices", StringLookupTableProperty::New( SOPInstanceUIDForSlices ) ); thisInstance->SetProperty( "filenamesForSlices", StringLookupTableProperty::New( filenamesForSlices ) ); //add properties for additional tags of interest for ( auto iter = additionalTagResultList.cbegin(); iter != additionalTagResultList.cend(); ++iter ) { thisInstance->SetProperty( iter->first, m_PropertyFunctor( iter->second ) ); thisInstance->m_FoundAdditionalTags.insert(m_FoundAdditionalTags.cend(),iter->first); } m_PropertiesOutOfDate = false; } } mitk::BaseProperty::Pointer mitk::DICOMImageBlockDescriptor::GetPropertyForDICOMValues(const DICOMCachedValueLookupTable& cacheLookupTable) { const auto& lookupTable = cacheLookupTable.GetLookupTable(); typedef std::pair PairType; if ( std::adjacent_find( lookupTable.cbegin(), lookupTable.cend(), []( const PairType& lhs, const PairType& rhs ) { return lhs.second.Value != rhs.second.Value; } ) == lookupTable.cend() ) { return static_cast( mitk::StringProperty::New(cacheLookupTable.GetTableValue(0).Value).GetPointer()); } StringLookupTable stringTable; for (auto element : lookupTable) { stringTable.SetTableValue(element.first, element.second.Value); } return static_cast( mitk::StringLookupTableProperty::New(stringTable).GetPointer()); } void mitk::DICOMImageBlockDescriptor::SetTagLookupTableToPropertyFunctor( TagLookupTableToPropertyFunctor functor ) { if ( functor != nullptr ) { m_PropertyFunctor = functor; } } diff --git a/Modules/DICOMReader/src/mitkDICOMReaderConfigurator.cpp b/Modules/DICOMReader/src/mitkDICOMReaderConfigurator.cpp index c460533882..9190c0ad8f 100644 --- a/Modules/DICOMReader/src/mitkDICOMReaderConfigurator.cpp +++ b/Modules/DICOMReader/src/mitkDICOMReaderConfigurator.cpp @@ -1,693 +1,693 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkDICOMReaderConfigurator.h" #include "mitkDICOMSortByTag.h" #include "mitkSortByImagePositionPatient.h" mitk::DICOMReaderConfigurator ::DICOMReaderConfigurator() { } mitk::DICOMReaderConfigurator ::~DICOMReaderConfigurator() { } mitk::DICOMFileReader::Pointer mitk::DICOMReaderConfigurator ::CreateFromConfigFile(const std::string& filename) const { TiXmlDocument doc (filename); if (doc.LoadFile()) { return this->CreateFromTiXmlDocument( doc ); } else { MITK_ERROR << "Unable to load file at '" << filename <<"'"; return DICOMFileReader::Pointer(); } } mitk::DICOMFileReader::Pointer mitk::DICOMReaderConfigurator ::CreateFromUTF8ConfigString(const std::string& xmlContents) const { TiXmlDocument doc; doc.Parse(xmlContents.c_str(), nullptr, TIXML_ENCODING_UTF8); return this->CreateFromTiXmlDocument( doc ); } mitk::DICOMFileReader::Pointer mitk::DICOMReaderConfigurator ::CreateFromTiXmlDocument(TiXmlDocument& doc) const { TiXmlHandle root(doc.RootElement()); if (TiXmlElement* rootElement = root.ToElement()) { if (strcmp(rootElement->Value(), "DICOMFileReader")) // :-( no std::string methods { MITK_ERROR << "File should contain a tag at top-level! Found '" << (rootElement->Value() ? std::string(rootElement->Value()) : std::string("!nothing!")) << "' instead"; return nullptr; } const char* classnameC = rootElement->Attribute("class"); if (!classnameC) { MITK_ERROR << "File should name a reader class in the class attribute: . Found nothing instead"; return nullptr; } int version(1); if ( rootElement->QueryIntAttribute("version", &version) == TIXML_SUCCESS) { if (version != 1) { MITK_WARN << "This reader is only capable of creating DICOMFileReaders of version 1. " << "Will not continue, because given configuration is meant for version " << version << "."; return nullptr; } } else { MITK_ERROR << "File should name the version of the reader class in the version attribute: ." << " Found nothing instead, assuming version 1!"; version = 1; } std::string classname(classnameC); double decimalPlacesForOrientation(5); bool useDecimalPlacesForOrientation(false); useDecimalPlacesForOrientation = rootElement->QueryDoubleAttribute("decimalPlacesForOrientation", &decimalPlacesForOrientation) == TIXML_SUCCESS; // attribute present and a double value if (classname == "ClassicDICOMSeriesReader") { mitk::ClassicDICOMSeriesReader::Pointer reader = mitk::ClassicDICOMSeriesReader::New(); this->ConfigureCommonPropertiesOfDICOMITKSeriesGDCMReader(reader.GetPointer(), rootElement); this->ConfigureCommonPropertiesOfThreeDnTDICOMSeriesReader(reader.GetPointer(), rootElement); return reader.GetPointer(); } if (classname == "ThreeDnTDICOMSeriesReader") { mitk::ThreeDnTDICOMSeriesReader::Pointer reader; if (useDecimalPlacesForOrientation) reader = mitk::ThreeDnTDICOMSeriesReader::New(decimalPlacesForOrientation); else reader = mitk::ThreeDnTDICOMSeriesReader::New(); return ConfigureThreeDnTDICOMSeriesReader(reader, rootElement).GetPointer(); } else if (classname == "DICOMITKSeriesGDCMReader") { const char* simpleVolumeImportC = rootElement->Attribute("simpleVolumeImport"); bool simpleVolumeImport = simpleVolumeImportC ? true : false; mitk::DICOMITKSeriesGDCMReader::Pointer reader; if (useDecimalPlacesForOrientation) reader = mitk::DICOMITKSeriesGDCMReader::New( decimalPlacesForOrientation, simpleVolumeImport ); else reader = mitk::DICOMITKSeriesGDCMReader::New( mitk::DICOMITKSeriesGDCMReader::GetDefaultDecimalPlacesForOrientation(), simpleVolumeImport ); // simple volume import that ignores number of frames and inter slice distance return ConfigureDICOMITKSeriesGDCMReader(reader, rootElement).GetPointer(); } else { MITK_ERROR << "DICOMFileReader tag names unknown class '" << classname << "'"; return nullptr; } } else { MITK_ERROR << "Great confusion: no root element in XML document. Expecting a DICOMFileReader tag at top-level."; return nullptr; } } #define boolStringTrue(s) \ ( s == "true" || s == "on" || s == "1" \ || s == "TRUE" || s == "ON") void mitk::DICOMReaderConfigurator ::ConfigureCommonPropertiesOfThreeDnTDICOMSeriesReader(ThreeDnTDICOMSeriesReader::Pointer reader, TiXmlElement* element) const { // add the "group3DnT" flag bool group3DnT(true); const char* group3DnTC = element->Attribute("group3DnT"); if (group3DnTC) { std::string group3DnTS(group3DnTC); group3DnT = boolStringTrue(group3DnTS); } reader->SetGroup3DandT( group3DnT ); } mitk::ThreeDnTDICOMSeriesReader::Pointer mitk::DICOMReaderConfigurator ::ConfigureThreeDnTDICOMSeriesReader(ThreeDnTDICOMSeriesReader::Pointer reader, TiXmlElement* element) const { assert(element); // use all the base class configuration if (this->ConfigureDICOMITKSeriesGDCMReader( reader.GetPointer(), element ).IsNull()) { return nullptr; } this->ConfigureCommonPropertiesOfThreeDnTDICOMSeriesReader(reader,element); return reader; } void mitk::DICOMReaderConfigurator ::ConfigureCommonPropertiesOfDICOMITKSeriesGDCMReader(DICOMITKSeriesGDCMReader::Pointer reader, TiXmlElement* element) const { assert(element); const char* configLabelC = element->Attribute("label"); if (configLabelC) { std::string configLabel(configLabelC); reader->SetConfigurationLabel(configLabel); } const char* configDescriptionC = element->Attribute("description"); if (configDescriptionC) { reader->SetConfigurationDescription(configDescriptionC); } // "fixTiltByShearing" flag bool fixTiltByShearing(false); const char* fixTiltByShearingC = element->Attribute("fixTiltByShearing"); if (fixTiltByShearingC) { std::string fixTiltByShearingS(fixTiltByShearingC); fixTiltByShearing = boolStringTrue(fixTiltByShearingS); } reader->SetFixTiltByShearing( fixTiltByShearing ); } mitk::DICOMITKSeriesGDCMReader::Pointer mitk::DICOMReaderConfigurator ::ConfigureDICOMITKSeriesGDCMReader(DICOMITKSeriesGDCMReader::Pointer reader, TiXmlElement* element) const { assert(element); this->ConfigureCommonPropertiesOfDICOMITKSeriesGDCMReader(reader, element); // "acceptTwoSlicesGroups" flag bool acceptTwoSlicesGroups(true); const char* acceptTwoSlicesGroupsC = element->Attribute("acceptTwoSlicesGroups"); if (acceptTwoSlicesGroupsC) { std::string acceptTwoSlicesGroupsS(acceptTwoSlicesGroupsC); acceptTwoSlicesGroups = boolStringTrue(acceptTwoSlicesGroupsS); } reader->SetAcceptTwoSlicesGroups( acceptTwoSlicesGroups ); // "toleratedOriginError" attribute (double) bool toleratedOriginErrorIsAbsolute(false); const char* toleratedOriginErrorIsAbsoluteC = element->Attribute("toleratedOriginErrorIsAbsolute"); if (toleratedOriginErrorIsAbsoluteC) { std::string toleratedOriginErrorIsAbsoluteS(toleratedOriginErrorIsAbsoluteC); toleratedOriginErrorIsAbsolute = boolStringTrue(toleratedOriginErrorIsAbsoluteS); } double toleratedOriginError(0.3); if (element->QueryDoubleAttribute("toleratedOriginError", &toleratedOriginError) == TIXML_SUCCESS) // attribute present and a double value { if (toleratedOriginErrorIsAbsolute) { reader->SetToleratedOriginOffset( toleratedOriginError ); } else { reader->SetToleratedOriginOffsetToAdaptive( toleratedOriginError ); } } // DICOMTagBasedSorters are the only thing we create at this point // TODO for-loop over all child elements of type DICOMTagBasedSorter, BUT actually a single sorter of this type is enough. TiXmlElement* dElement = element->FirstChildElement("DICOMDatasetSorter"); if (dElement) { const char* classnameC = dElement->Attribute("class"); if (!classnameC) { MITK_ERROR << "File should name a DICOMDatasetSorter class in the class attribute of . Found nothing instead"; return nullptr; } std::string classname(classnameC); if (classname == "DICOMTagBasedSorter") { DICOMTagBasedSorter::Pointer tagSorter = CreateDICOMTagBasedSorter(dElement); if (tagSorter.IsNotNull()) { reader->AddSortingElement( tagSorter ); } } else { MITK_ERROR << "DICOMDatasetSorter tag names unknown class '" << classname << "'"; return nullptr; } } return reader; } mitk::DICOMTagBasedSorter::Pointer mitk::DICOMReaderConfigurator ::CreateDICOMTagBasedSorter(TiXmlElement* element) const { mitk::DICOMTagBasedSorter::Pointer tagSorter = mitk::DICOMTagBasedSorter::New(); // "strictSorting" parameter! bool strictSorting(true); const char* strictSortingC = element->Attribute("strictSorting"); if (strictSortingC) { std::string strictSortingS(strictSortingC); strictSorting = boolStringTrue(strictSortingS); } tagSorter->SetStrictSorting(strictSorting); // "strictSorting" parameter! bool expectDistanceOne(true); const char* expectDistanceOneC = element->Attribute("expectDistanceOne"); if (expectDistanceOneC) { std::string expectDistanceOneS(expectDistanceOneC); expectDistanceOne = boolStringTrue(expectDistanceOneS); } tagSorter->SetExpectDistanceOne(expectDistanceOne); TiXmlElement* dElement = element->FirstChildElement("Distinguishing"); if (dElement) { for ( TiXmlElement* tChild = dElement->FirstChildElement(); tChild != nullptr; tChild = tChild->NextSiblingElement() ) { try { mitk::DICOMTag tag = tagFromXMLElement(tChild); int i(5); if (tChild->QueryIntAttribute("cutDecimalPlaces", &i) == TIXML_SUCCESS) { tagSorter->AddDistinguishingTag( tag, new mitk::DICOMTagBasedSorter::CutDecimalPlaces(i) ); } else { tagSorter->AddDistinguishingTag( tag ); } } catch(...) { return nullptr; } } } // "sorting tags" TiXmlElement* sElement = element->FirstChildElement("Sorting"); if (sElement) { DICOMSortCriterion::Pointer previousCriterion; DICOMSortCriterion::Pointer currentCriterion; for ( TiXmlNode* tChildNode = sElement->LastChild(); tChildNode != nullptr; tChildNode = tChildNode->PreviousSibling() ) { TiXmlElement* tChild = tChildNode->ToElement(); if (!tChild) continue; if (!strcmp(tChild->Value(), "Tag")) { try { currentCriterion = this->CreateDICOMSortByTag(tChild, previousCriterion); } catch(...) { std::stringstream ss; ss << "Could not parse element at (input line " << tChild->Row() << ", col. " << tChild->Column() << ")!"; MITK_ERROR << ss.str(); return nullptr; } } else if (!strcmp(tChild->Value(), "ImagePositionPatient")) { try { currentCriterion = this->CreateSortByImagePositionPatient(tChild, previousCriterion); } catch(...) { std::stringstream ss; ss << "Could not parse element at (input line " << tChild->Row() << ", col. " << tChild->Column() << ")!"; MITK_ERROR << ss.str(); return nullptr; } } else { MITK_ERROR << "File contain unknown tag <" << tChild->Value() << "> tag as child to ! Cannot interpret..."; } previousCriterion = currentCriterion; } tagSorter->SetSortCriterion( currentCriterion.GetPointer() ); } return tagSorter; } std::string mitk::DICOMReaderConfigurator ::requiredStringAttribute(TiXmlElement* xmlElement, const std::string& key) const { assert(xmlElement); const char* gC = xmlElement->Attribute(key.c_str()); if (gC) { std::string gS(gC); return gS; } else { std::stringstream ss; ss << "Expected an attribute '" << key << "' at this position " "(input line " << xmlElement->Row() << ", col. " << xmlElement->Column() << ")!"; MITK_ERROR << ss.str(); throw std::invalid_argument( ss.str() ); } } unsigned int mitk::DICOMReaderConfigurator ::hexStringToUInt(const std::string& s) const { std::stringstream converter(s); unsigned int ui; converter >> std::hex >> ui; MITK_DEBUG << "Converted string '" << s << "' to unsigned int " << ui; return ui; } mitk::DICOMTag mitk::DICOMReaderConfigurator ::tagFromXMLElement(TiXmlElement* xmlElement) const { assert(xmlElement); if (strcmp(xmlElement->Value(), "Tag")) // :-( no std::string methods { std::stringstream ss; ss << "Expected a tag at this position " "(input line " << xmlElement->Row() << ", col. " << xmlElement->Column() << ")!"; MITK_ERROR << ss.str(); throw std::invalid_argument( ss.str() ); } std::string groupS = requiredStringAttribute(xmlElement, "group"); std::string elementS = requiredStringAttribute(xmlElement, "element"); try { // convert string to int (assuming string is in hex format with leading "0x" like "0x0020") unsigned int group = hexStringToUInt(groupS); unsigned int element = hexStringToUInt(elementS); return DICOMTag(group, element); } catch(...) { std::stringstream ss; ss << "Expected group and element values in to be hexadecimal with leading 0x, e.g. '0x0020'" "(input line " << xmlElement->Row() << ", col. " << xmlElement->Column() << ")!"; MITK_ERROR << ss.str(); throw std::invalid_argument( ss.str() ); } } mitk::DICOMSortCriterion::Pointer mitk::DICOMReaderConfigurator ::CreateDICOMSortByTag(TiXmlElement* xmlElement, DICOMSortCriterion::Pointer secondaryCriterion) const { mitk::DICOMTag tag = tagFromXMLElement(xmlElement); return DICOMSortByTag::New(tag, secondaryCriterion).GetPointer(); } mitk::DICOMSortCriterion::Pointer mitk::DICOMReaderConfigurator ::CreateSortByImagePositionPatient(TiXmlElement*, DICOMSortCriterion::Pointer secondaryCriterion) const { return SortByImagePositionPatient::New(secondaryCriterion).GetPointer(); } std::string mitk::DICOMReaderConfigurator ::CreateConfigStringFromReader(DICOMFileReader::ConstPointer reader) const { // check possible sub-classes from the most-specific one up to the most generic one const DICOMFileReader* cPointer = reader; TiXmlElement* root; - if (const ClassicDICOMSeriesReader* specificReader = dynamic_cast(cPointer)) + if (const auto* specificReader = dynamic_cast(cPointer)) { root = this->CreateConfigStringFromReader(specificReader); } else - if (const ThreeDnTDICOMSeriesReader* specificReader = dynamic_cast(cPointer)) + if (const auto* specificReader = dynamic_cast(cPointer)) { root = this->CreateConfigStringFromReader(specificReader); } else - if (const DICOMITKSeriesGDCMReader* specificReader = dynamic_cast(cPointer)) + if (const auto* specificReader = dynamic_cast(cPointer)) { root = this->CreateConfigStringFromReader(specificReader); } else { MITK_WARN << "Unknown reader class passed to DICOMReaderConfigurator::CreateConfigStringFromReader(). Cannot serialize."; return ""; // no serialization, what a pity } if (root) { TiXmlDocument document; document.LinkEndChild( root ); TiXmlPrinter printer; printer.SetIndent( " " ); document.Accept( &printer ); std::string xmltext = printer.CStr(); return xmltext; } else { MITK_WARN << "DICOMReaderConfigurator::CreateConfigStringFromReader() created empty serialization. Problem?"; return ""; } } TiXmlElement* mitk::DICOMReaderConfigurator ::CreateConfigStringFromReader(const DICOMITKSeriesGDCMReader* reader) const { TiXmlElement* root = this->CreateDICOMFileReaderTag(reader); assert(root); root->SetAttribute("fixTiltByShearing", toString(reader->GetFixTiltByShearing())); root->SetAttribute("acceptTwoSlicesGroups", toString(reader->GetAcceptTwoSlicesGroups())); root->SetDoubleAttribute("toleratedOriginError", reader->GetToleratedOriginError()); root->SetAttribute("toleratedOriginErrorIsAbsolute", toString(reader->IsToleratedOriginOffsetAbsolute())); root->SetDoubleAttribute("decimalPlacesForOrientation", reader->GetDecimalPlacesForOrientation()); // iterate DICOMDatasetSorter objects DICOMITKSeriesGDCMReader::ConstSorterList sorterList = reader->GetFreelyConfiguredSortingElements(); for(auto sorterIter = sorterList.begin(); sorterIter != sorterList.end(); ++sorterIter) { const DICOMDatasetSorter* sorter = *sorterIter; - if (const DICOMTagBasedSorter* specificSorter = dynamic_cast(sorter)) + if (const auto* specificSorter = dynamic_cast(sorter)) { TiXmlElement* sorterTag = this->CreateConfigStringFromDICOMDatasetSorter(specificSorter); root->LinkEndChild(sorterTag); } else { MITK_WARN << "Unknown DICOMDatasetSorter class passed to DICOMReaderConfigurator::CreateConfigStringFromReader(). Cannot serialize."; return nullptr; } } return root; } TiXmlElement* mitk::DICOMReaderConfigurator ::CreateConfigStringFromDICOMDatasetSorter(const DICOMTagBasedSorter* sorter) const { assert(sorter); auto sorterTag = new TiXmlElement("DICOMDatasetSorter"); sorterTag->SetAttribute("class", sorter->GetNameOfClass()); sorterTag->SetAttribute("strictSorting", toString(sorter->GetStrictSorting())); sorterTag->SetAttribute("expectDistanceOne", toString(sorter->GetExpectDistanceOne())); auto distinguishingTagsElement = new TiXmlElement("Distinguishing"); sorterTag->LinkEndChild(distinguishingTagsElement); mitk::DICOMTagList distinguishingTags = sorter->GetDistinguishingTags(); for (auto tagIter = distinguishingTags.begin(); tagIter != distinguishingTags.end(); ++tagIter) { TiXmlElement* tag = this->CreateConfigStringFromDICOMTag(*tagIter); distinguishingTagsElement->LinkEndChild(tag); const DICOMTagBasedSorter::TagValueProcessor* processor = sorter->GetTagValueProcessorForDistinguishingTag(*tagIter); - if (const DICOMTagBasedSorter::CutDecimalPlaces* specificProcessor = dynamic_cast(processor)) + if (const auto* specificProcessor = dynamic_cast(processor)) { tag->SetDoubleAttribute("cutDecimalPlaces", specificProcessor->GetPrecision()); } } auto sortingElement = new TiXmlElement("Sorting"); sorterTag->LinkEndChild(sortingElement); mitk::DICOMSortCriterion::ConstPointer sortCriterion = sorter->GetSortCriterion(); while (sortCriterion.IsNotNull()) { std::string classname = sortCriterion->GetNameOfClass(); if (classname == "SortByImagePositionPatient") { sortingElement->LinkEndChild( new TiXmlElement("ImagePositionPatient") ); // no parameters } else if (classname == "DICOMSortByTag") { DICOMTagList pseudoTagList = sortCriterion->GetTagsOfInterest(); if (pseudoTagList.size() == 1) { DICOMTag firstTag = pseudoTagList.front(); TiXmlElement* tagElement = this->CreateConfigStringFromDICOMTag(firstTag); sortingElement->LinkEndChild( tagElement ); } else { MITK_ERROR << "Encountered SortByTag class with MULTIPLE tag in CreateConfigStringFromDICOMDatasetSorter. Cannot serialize."; return nullptr; } } else { MITK_ERROR << "Encountered unknown class '" << classname << "' in CreateConfigStringFromDICOMDatasetSorter. Cannot serialize."; return nullptr; } sortCriterion = sortCriterion->GetSecondaryCriterion(); } return sorterTag; } TiXmlElement* mitk::DICOMReaderConfigurator ::CreateConfigStringFromDICOMTag(const DICOMTag& tag) const { auto tagElement = new TiXmlElement("Tag"); // name group element tagElement->SetAttribute("name", tag.GetName().c_str()); tagElement->SetAttribute("group", toHexString(tag.GetGroup())); tagElement->SetAttribute("element", toHexString(tag.GetElement())); return tagElement; } std::string mitk::DICOMReaderConfigurator ::toHexString(unsigned int i) const { std::stringstream ss; ss << "0x" << std::setfill ('0') << std::setw(4) << std::hex << i; return ss.str(); } TiXmlElement* mitk::DICOMReaderConfigurator ::CreateConfigStringFromReader(const ThreeDnTDICOMSeriesReader* reader) const { TiXmlElement* root = this->CreateConfigStringFromReader(static_cast(reader)); assert(root); root->SetAttribute("group3DnT", toString(reader->GetGroup3DandT())); return root; } const char* mitk::DICOMReaderConfigurator ::toString(bool b) const { return b ? "true" : "false"; } TiXmlElement* mitk::DICOMReaderConfigurator ::CreateConfigStringFromReader(const ClassicDICOMSeriesReader* reader) const { return this->CreateDICOMFileReaderTag(reader); } TiXmlElement* mitk::DICOMReaderConfigurator ::CreateDICOMFileReaderTag(const DICOMFileReader* reader) const { auto readerTag = new TiXmlElement("DICOMFileReader"); readerTag->SetAttribute("class", reader->GetNameOfClass()); readerTag->SetAttribute("label", reader->GetConfigurationLabel().c_str()); readerTag->SetAttribute("description", reader->GetConfigurationDescription().c_str()); readerTag->SetAttribute("version", "1"); return readerTag; } diff --git a/Modules/DICOMReader/src/mitkDICOMSortByTag.cpp b/Modules/DICOMReader/src/mitkDICOMSortByTag.cpp index e2a966f126..a8275d1483 100644 --- a/Modules/DICOMReader/src/mitkDICOMSortByTag.cpp +++ b/Modules/DICOMReader/src/mitkDICOMSortByTag.cpp @@ -1,181 +1,181 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkDICOMSortByTag.h" #include "dcmtk/ofstd/ofstd.h" mitk::DICOMSortByTag ::DICOMSortByTag(const DICOMTag& tag, DICOMSortCriterion::Pointer secondaryCriterion) :DICOMSortCriterion(secondaryCriterion) ,m_Tag(tag) { } mitk::DICOMSortByTag ::~DICOMSortByTag() { } mitk::DICOMSortByTag ::DICOMSortByTag(const DICOMSortByTag& other ) :DICOMSortCriterion(other) ,m_Tag(other.m_Tag) { } mitk::DICOMSortByTag& mitk::DICOMSortByTag ::operator=(const DICOMSortByTag& other) { if (this != &other) { DICOMSortCriterion::operator=(other); m_Tag = other.m_Tag; } return *this; } bool mitk::DICOMSortByTag ::operator==(const DICOMSortCriterion& other) const { - if (const DICOMSortByTag* otherSelf = dynamic_cast(&other)) + if (const auto* otherSelf = dynamic_cast(&other)) { if (!(this->m_Tag == otherSelf->m_Tag)) return false; if (this->m_SecondaryCriterion.IsNull() && otherSelf->m_SecondaryCriterion.IsNull()) return true; if (this->m_SecondaryCriterion.IsNull() || otherSelf->m_SecondaryCriterion.IsNull()) return false; return *(this->m_SecondaryCriterion) == *(otherSelf->m_SecondaryCriterion); } else { return false; } } void mitk::DICOMSortByTag ::Print(std::ostream& os) const { m_Tag.Print(os); } mitk::DICOMTagList mitk::DICOMSortByTag ::GetTagsOfInterest() const { DICOMTagList list; list.push_back(m_Tag); return list; } bool mitk::DICOMSortByTag ::IsLeftBeforeRight(const mitk::DICOMDatasetAccess* left, const mitk::DICOMDatasetAccess* right) const { return this->NumericCompare(left, right, m_Tag); } bool mitk::DICOMSortByTag ::StringCompare(const mitk::DICOMDatasetAccess* left, const mitk::DICOMDatasetAccess* right, const DICOMTag& tag) const { assert(left); assert(right); DICOMDatasetFinding leftFinding = left->GetTagValueAsString(tag); DICOMDatasetFinding rightFinding = right->GetTagValueAsString(tag); //Doesn't care if findings are valid or not. If they are not valid, //value is empty, thats enough. if (leftFinding.value != rightFinding.value) { return leftFinding.value.compare(rightFinding.value) < 0; } else { return this->NextLevelIsLeftBeforeRight(left, right); } } bool mitk::DICOMSortByTag ::NumericCompare(const mitk::DICOMDatasetAccess* left, const mitk::DICOMDatasetAccess* right, const DICOMTag& tag) const { assert(left); assert(right); const DICOMDatasetFinding leftFinding = left->GetTagValueAsString(tag); const DICOMDatasetFinding rightFinding = right->GetTagValueAsString(tag); //Doesn't care if findings are valid or not. If they are not valid, //value is empty, thats enough. double leftDouble( 0 ); double rightDouble( 0 ); try { leftDouble = OFStandard::atof( leftFinding.value.c_str() ); rightDouble = OFStandard::atof(rightFinding.value.c_str()); } catch ( const std::exception& /*e*/ ) { return this->StringCompare(left,right, tag); // fallback to string compare } if ( leftDouble != rightDouble ) // can we decide? { return leftDouble < rightDouble; } else // ask secondary criterion { return this->NextLevelIsLeftBeforeRight( left, right ); } } double mitk::DICOMSortByTag ::NumericDistance(const mitk::DICOMDatasetAccess* from, const mitk::DICOMDatasetAccess* to) const { assert(from); assert(to); const DICOMDatasetFinding fromFinding = from->GetTagValueAsString(m_Tag); const DICOMDatasetFinding toFinding = to->GetTagValueAsString(m_Tag); //Doesn't care if findings are valid or not. If they are not valid, //value is empty, thats enough. double fromDouble(0); double toDouble(0); try { fromDouble = OFStandard::atof(fromFinding.value.c_str()); toDouble = OFStandard::atof(toFinding.value.c_str()); } catch ( const std::exception& /*e*/ ) { MITK_WARN << "NO NUMERIC DISTANCE between '" << fromFinding.value << "' and '" << toFinding.value << "'"; return 0; } return toDouble - fromDouble; // TODO second-level compare? } diff --git a/Modules/DICOMReader/src/mitkDICOMTagBasedSorter.cpp b/Modules/DICOMReader/src/mitkDICOMTagBasedSorter.cpp index fd12412fa3..7cacc334e3 100644 --- a/Modules/DICOMReader/src/mitkDICOMTagBasedSorter.cpp +++ b/Modules/DICOMReader/src/mitkDICOMTagBasedSorter.cpp @@ -1,596 +1,596 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkDICOMTagBasedSorter.h" #include #include mitk::DICOMTagBasedSorter::CutDecimalPlaces ::CutDecimalPlaces(unsigned int precision) :m_Precision(precision) { } mitk::DICOMTagBasedSorter::CutDecimalPlaces ::CutDecimalPlaces(const CutDecimalPlaces& other) :m_Precision(other.m_Precision) { } std::string mitk::DICOMTagBasedSorter::CutDecimalPlaces ::operator()(const std::string& input) const { // be a bit tolerant for tags such as image orientation orienatation, let only the first few digits matter (http://bugs.mitk.org/show_bug.cgi?id=12263) // iterate all fields, convert each to a number, cut this number as configured, then return a concatenated string with all cut-off numbers std::ostringstream resultString; resultString.str(std::string()); resultString.clear(); resultString.setf(std::ios::fixed, std::ios::floatfield); resultString.precision(m_Precision); std::stringstream ss(input); ss.str(input); ss.clear(); std::string item; double number(0); std::istringstream converter(item); while (std::getline(ss, item, '\\')) { converter.str(item); converter.clear(); if (converter >> number && converter.eof()) { // converted to double resultString << number; } else { // did not convert to double resultString << item; // just paste the unmodified string } if (!ss.eof()) { resultString << "\\"; } } return resultString.str(); } mitk::DICOMTagBasedSorter::TagValueProcessor* mitk::DICOMTagBasedSorter::CutDecimalPlaces ::Clone() const { return new CutDecimalPlaces(*this); } unsigned int mitk::DICOMTagBasedSorter::CutDecimalPlaces ::GetPrecision() const { return m_Precision; } mitk::DICOMTagBasedSorter ::DICOMTagBasedSorter() :DICOMDatasetSorter() ,m_StrictSorting(false) ,m_ExpectDistanceOne(false) { } mitk::DICOMTagBasedSorter ::~DICOMTagBasedSorter() { for(auto ti = m_TagValueProcessor.cbegin(); ti != m_TagValueProcessor.cend(); ++ti) { delete ti->second; } } mitk::DICOMTagBasedSorter ::DICOMTagBasedSorter(const DICOMTagBasedSorter& other ) :DICOMDatasetSorter(other) ,m_DistinguishingTags( other.m_DistinguishingTags ) ,m_SortCriterion( other.m_SortCriterion ) ,m_StrictSorting( other.m_StrictSorting ) ,m_ExpectDistanceOne( other.m_ExpectDistanceOne ) { for(auto ti = other.m_TagValueProcessor.cbegin(); ti != other.m_TagValueProcessor.cend(); ++ti) { m_TagValueProcessor[ti->first] = ti->second->Clone(); } } mitk::DICOMTagBasedSorter& mitk::DICOMTagBasedSorter ::operator=(const DICOMTagBasedSorter& other) { if (this != &other) { DICOMDatasetSorter::operator=(other); m_DistinguishingTags = other.m_DistinguishingTags; m_SortCriterion = other.m_SortCriterion; m_StrictSorting = other.m_StrictSorting; m_ExpectDistanceOne = other.m_ExpectDistanceOne; for(auto ti = other.m_TagValueProcessor.cbegin(); ti != other.m_TagValueProcessor.cend(); ++ti) { m_TagValueProcessor[ti->first] = ti->second->Clone(); } } return *this; } bool mitk::DICOMTagBasedSorter ::operator==(const DICOMDatasetSorter& other) const { - if (const DICOMTagBasedSorter* otherSelf = dynamic_cast(&other)) + if (const auto* otherSelf = dynamic_cast(&other)) { if (this->m_StrictSorting != otherSelf->m_StrictSorting) return false; if (this->m_ExpectDistanceOne != otherSelf->m_ExpectDistanceOne) return false; bool allTagsPresentAndEqual(true); if (this->m_DistinguishingTags.size() != otherSelf->m_DistinguishingTags.size()) return false; for (auto myTag = this->m_DistinguishingTags.cbegin(); myTag != this->m_DistinguishingTags.cend(); ++myTag) { allTagsPresentAndEqual &= (std::find( otherSelf->m_DistinguishingTags.cbegin(), otherSelf->m_DistinguishingTags.cend(), *myTag ) != otherSelf->m_DistinguishingTags.cend()); // other contains this tags // since size is equal, we don't need to check the inverse } if (!allTagsPresentAndEqual) return false; if (this->m_SortCriterion.IsNotNull() && otherSelf->m_SortCriterion.IsNotNull()) { return *(this->m_SortCriterion) == *(otherSelf->m_SortCriterion); } else { return this->m_SortCriterion.IsNull() && otherSelf->m_SortCriterion.IsNull(); } } else { return false; } } void mitk::DICOMTagBasedSorter ::PrintConfiguration(std::ostream& os, const std::string& indent) const { os << indent << "Tag based sorting " << "(strict=" << (m_StrictSorting?"true":"false") << ", expectDistanceOne=" << (m_ExpectDistanceOne?"true":"false") << "):" << std::endl; for (auto tagIter = m_DistinguishingTags.begin(); tagIter != m_DistinguishingTags.end(); ++tagIter) { os << indent << " Split on "; tagIter->Print(os); os << std::endl; } DICOMSortCriterion::ConstPointer crit = m_SortCriterion.GetPointer(); while (crit.IsNotNull()) { os << indent << " Sort by "; crit->Print(os); os << std::endl; crit = crit->GetSecondaryCriterion(); } } void mitk::DICOMTagBasedSorter ::SetStrictSorting(bool strict) { m_StrictSorting = strict; } bool mitk::DICOMTagBasedSorter ::GetStrictSorting() const { return m_StrictSorting; } void mitk::DICOMTagBasedSorter ::SetExpectDistanceOne(bool strict) { m_ExpectDistanceOne = strict; } bool mitk::DICOMTagBasedSorter ::GetExpectDistanceOne() const { return m_ExpectDistanceOne; } mitk::DICOMTagList mitk::DICOMTagBasedSorter ::GetTagsOfInterest() { DICOMTagList allTags = m_DistinguishingTags; const DICOMTagList sortingRelevantTags = m_SortCriterion->GetAllTagsOfInterest(); allTags.insert( allTags.end(), sortingRelevantTags.cbegin(), sortingRelevantTags.cend() ); // append return allTags; } mitk::DICOMTagList mitk::DICOMTagBasedSorter ::GetDistinguishingTags() const { return m_DistinguishingTags; } const mitk::DICOMTagBasedSorter::TagValueProcessor* mitk::DICOMTagBasedSorter ::GetTagValueProcessorForDistinguishingTag(const DICOMTag& tag) const { auto loc = m_TagValueProcessor.find(tag); if (loc != m_TagValueProcessor.cend()) { return loc->second; } else { return nullptr; } } void mitk::DICOMTagBasedSorter ::AddDistinguishingTag( const DICOMTag& tag, TagValueProcessor* tagValueProcessor ) { m_DistinguishingTags.push_back(tag); m_TagValueProcessor[tag] = tagValueProcessor; } void mitk::DICOMTagBasedSorter ::SetSortCriterion( DICOMSortCriterion::ConstPointer criterion ) { m_SortCriterion = criterion; } mitk::DICOMSortCriterion::ConstPointer mitk::DICOMTagBasedSorter ::GetSortCriterion() const { return m_SortCriterion; } void mitk::DICOMTagBasedSorter ::Sort() { // 1. split // 2. sort each group GroupIDToListType groups = this->SplitInputGroups(); GroupIDToListType& sortedGroups = this->SortGroups( groups ); // 3. define output this->SetNumberOfOutputs(sortedGroups.size()); unsigned int outputIndex(0); for (auto groupIter = sortedGroups.cbegin(); groupIter != sortedGroups.cend(); ++outputIndex, ++groupIter) { this->SetOutput(outputIndex, groupIter->second); } } std::string mitk::DICOMTagBasedSorter ::BuildGroupID( DICOMDatasetAccess* dataset ) { // just concatenate all tag values assert(dataset); std::stringstream groupID; groupID << "g"; for (auto tagIter = m_DistinguishingTags.cbegin(); tagIter != m_DistinguishingTags.cend(); ++tagIter) { groupID << tagIter->GetGroup() << tagIter->GetElement(); // make group/element part of the id to cover empty tags DICOMDatasetFinding rawTagValue = dataset->GetTagValueAsString(*tagIter); std::string processedTagValue; if ( m_TagValueProcessor[*tagIter] != nullptr && rawTagValue.isValid) { processedTagValue = (*m_TagValueProcessor[*tagIter])(rawTagValue.value); } else { processedTagValue = rawTagValue.value; } groupID << processedTagValue; } // shorten ID? return groupID.str(); } mitk::DICOMTagBasedSorter::GroupIDToListType mitk::DICOMTagBasedSorter ::SplitInputGroups() { DICOMDatasetList input = GetInput(); // copy GroupIDToListType listForGroupID; for (auto dsIter = input.cbegin(); dsIter != input.cend(); ++dsIter) { DICOMDatasetAccess* dataset = *dsIter; assert(dataset); std::string groupID = this->BuildGroupID( dataset ); MITK_DEBUG << "Group ID for for " << dataset->GetFilenameIfAvailable() << ": " << groupID; listForGroupID[groupID].push_back(dataset); } MITK_DEBUG << "After tag based splitting: " << listForGroupID.size() << " groups"; return listForGroupID; } mitk::DICOMTagBasedSorter::GroupIDToListType& mitk::DICOMTagBasedSorter ::SortGroups(GroupIDToListType& groups) { if (m_SortCriterion.IsNotNull()) { /* Three steps here: 1. sort within each group - this may result in orders such as 1 2 3 4 6 7 8 10 12 13 14 2. create new groups by enforcing consecutive order within each group - resorts above example like 1 2 3 4 ; 6 7 8 ; 10 ; 12 13 14 3. sort all of the groups (not WITHIN each group) by their first frame - if earlier "distinguish" steps created groups like 6 7 8 ; 1 2 3 4 ; 10, then this step would sort them like 1 2 3 4 ; 6 7 8 ; 10 */ // Step 1: sort within the groups // for each output // sort by all configured tags, use secondary tags when equal or empty // make configurable: // - sorting order (ascending, descending) // - sort numerically // - ... ? unsigned int groupIndex(0); for (auto gIter = groups.begin(); gIter != groups.end(); ++groupIndex, ++gIter) { DICOMDatasetList& dsList = gIter->second; #ifdef MBILOG_ENABLE_DEBUG MITK_DEBUG << " --------------------------------------------------------------------------------"; MITK_DEBUG << " DICOMTagBasedSorter before sorting group : " << groupIndex; for (auto oi = dsList.begin(); oi != dsList.cend(); ++oi) { MITK_DEBUG << " INPUT : " << (*oi)->GetFilenameIfAvailable(); } #endif // #ifdef MBILOG_ENABLE_DEBUG std::sort( dsList.begin(), dsList.end(), ParameterizedDatasetSort( m_SortCriterion ) ); #ifdef MBILOG_ENABLE_DEBUG MITK_DEBUG << " --------------------------------------------------------------------------------"; MITK_DEBUG << " DICOMTagBasedSorter after sorting group : " << groupIndex; for (auto oi = dsList.cbegin(); oi != dsList.cend(); ++oi) { MITK_DEBUG << " OUTPUT : " << (*oi)->GetFilenameIfAvailable(); } MITK_DEBUG << " --------------------------------------------------------------------------------"; #endif // MBILOG_ENABLE_DEBUG } GroupIDToListType consecutiveGroups; if (m_StrictSorting) { // Step 2: create new groups by enforcing consecutive order within each group unsigned int groupIndex(0); for (auto gIter = groups.begin(); gIter != groups.end(); ++gIter) { std::stringstream groupKey; groupKey << std::setfill('0') << std::setw(6) << groupIndex++; DICOMDatasetList& dsList = gIter->second; DICOMDatasetAccess* previousDS(nullptr); unsigned int dsIndex(0); double constantDistance(0.0); bool constantDistanceInitialized(false); for (auto dataset = dsList.cbegin(); dataset != dsList.cend(); ++dsIndex, ++dataset) { if (dsIndex >0) // ignore the first dataset, we cannot check any distances yet.. { // for the second and every following dataset: // let the sorting criterion calculate a "distance" // if the distance is not 1, split off a new group! const double currentDistance = m_SortCriterion->NumericDistance(previousDS, *dataset); if (constantDistanceInitialized) { if (fabs(currentDistance - constantDistance) < fabs(constantDistance * 0.01)) // ok, deviation of up to 1% of distance is tolerated { // nothing to do, just ok MITK_DEBUG << "Checking currentDistance==" << currentDistance << ": small enough"; } //else if (currentDistance < mitk::eps) // close enough to 0 else { MITK_DEBUG << "Split consecutive group at index " << dsIndex << " (current distance " << currentDistance << ", constant distance " << constantDistance << ")"; // split! this is done by simply creating a new group (key) groupKey.str(std::string()); groupKey.clear(); groupKey << std::setfill('0') << std::setw(6) << groupIndex++; } } else { // second slice: learn about the expected distance! // heuristic: if distance is an integer, we check for a special case: // if the distance is integer and not 1/-1, then we assume // a missing slice right after the first slice // ==> split off slices // in all other cases: second dataset at this position, no need to split already, we are still learning about the images // addition to the above: when sorting by imagepositions, a distance other than 1 between the first two slices is // not unusual, actually expected... then we should not split if (m_ExpectDistanceOne) { if ((currentDistance - (int)currentDistance == 0.0) && fabs(currentDistance) != 1.0) // exact comparison. An integer should not be expressed as 1.000000000000000000000000001! { MITK_DEBUG << "Split consecutive group at index " << dsIndex << " (special case: expected distance 1 exactly)"; groupKey.str(std::string()); groupKey.clear(); groupKey << std::setfill('0') << std::setw(6) << groupIndex++; } } MITK_DEBUG << "Initialize strict distance to currentDistance=" << currentDistance; constantDistance = currentDistance; constantDistanceInitialized = true; } } consecutiveGroups[groupKey.str()].push_back(*dataset); previousDS = *dataset; } } } else { consecutiveGroups = groups; } // Step 3: sort all of the groups (not WITHIN each group) by their first frame /* build a list-1 of datasets with the first dataset one of each group sort this list-1 build a new result list-2: - iterate list-1, for each dataset - find the group that contains this dataset - add this group as the next element to list-2 return list-2 as the sorted output */ DICOMDatasetList firstSlices; for (auto gIter = consecutiveGroups.cbegin(); gIter != consecutiveGroups.cend(); ++gIter) { assert(!gIter->second.empty()); firstSlices.push_back(gIter->second.front()); } std::sort( firstSlices.begin(), firstSlices.end(), ParameterizedDatasetSort( m_SortCriterion ) ); GroupIDToListType sortedResultBlocks; unsigned int groupKeyValue(0); for (auto firstSlice = firstSlices.cbegin(); firstSlice != firstSlices.cend(); ++firstSlice) { for (auto gIter = consecutiveGroups.cbegin(); gIter != consecutiveGroups.cend(); ++groupKeyValue, ++gIter) { if (gIter->second.front() == *firstSlice) { std::stringstream groupKey; groupKey << std::setfill('0') << std::setw(6) << groupKeyValue; // try more than 999,999 groups and you are doomed (your application already is) sortedResultBlocks[groupKey.str()] = gIter->second; } } } groups = sortedResultBlocks; } #ifdef MBILOG_ENABLE_DEBUG unsigned int groupIndex( 0 ); for ( auto gIter = groups.begin(); gIter != groups.end(); ++groupIndex, ++gIter ) { DICOMDatasetList& dsList = gIter->second; MITK_DEBUG << " --------------------------------------------------------------------------------"; MITK_DEBUG << " DICOMTagBasedSorter after sorting group : " << groupIndex; for ( auto oi = dsList.begin(); oi != dsList.end(); ++oi ) { MITK_DEBUG << " OUTPUT : " << ( *oi )->GetFilenameIfAvailable(); } MITK_DEBUG << " --------------------------------------------------------------------------------"; } #endif // MBILOG_ENABLE_DEBUG return groups; } mitk::DICOMTagBasedSorter::ParameterizedDatasetSort ::ParameterizedDatasetSort(DICOMSortCriterion::ConstPointer criterion) :m_SortCriterion(criterion) { } bool mitk::DICOMTagBasedSorter::ParameterizedDatasetSort ::operator() (const mitk::DICOMDatasetAccess* left, const mitk::DICOMDatasetAccess* right) { assert(left); assert(right); assert(m_SortCriterion.IsNotNull()); return m_SortCriterion->IsLeftBeforeRight(left, right); } diff --git a/Modules/DICOMReader/src/mitkDICOMTagPath.cpp b/Modules/DICOMReader/src/mitkDICOMTagPath.cpp index a1f8f8f35d..8d28b4b8ea 100644 --- a/Modules/DICOMReader/src/mitkDICOMTagPath.cpp +++ b/Modules/DICOMReader/src/mitkDICOMTagPath.cpp @@ -1,729 +1,729 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include #include #include #include #include namespace { std::string GenerateRegExForNumber(unsigned int tagNumber) { std::ostringstream resultRegEx; std::ostringstream hexNumber; hexNumber << std::hex << tagNumber; // default std::hex output is lowercase std::regex reg_character("([a-f]+)"); // so only check for lowercase characters here if (std::regex_search(hexNumber.str(), reg_character)) { // hexNumber contains a characters from a-f // needs to be generated with lowercase and uppercase characters resultRegEx << "(" << std::setw(4) << std::setfill('0') << std::hex << tagNumber << "|" << std::setw(4) << std::setfill('0') << std::hex << std::uppercase << tagNumber << std::nouppercase << ")"; } else { // only decimal values (0-9) contained in hexNumber - no need to modify the result resultRegEx << std::setw(4) << std::setfill('0') << std::hex << tagNumber; } return resultRegEx.str(); } } namespace mitk { DICOMTagPath::NodeInfo:: NodeInfo() : type(NodeType::Invalid), tag(0, 0), selection(0) { ; }; DICOMTagPath::NodeInfo:: NodeInfo(const DICOMTag& aTag, NodeType aType, ItemSelectionIndex index) : type(aType), tag(aTag), selection(index) {}; bool DICOMTagPath::NodeInfo::operator == (const NodeInfo& right) const { if (!(this->tag == right.tag)) return false; if (this->type != right.type) return false; if (this->selection != right.selection) return false; return true; }; bool DICOMTagPath::NodeInfo:: Matches(const NodeInfo& right) const { if (type == NodeType::AnyElement || right.type == NodeType::AnyElement) { return true; } else if (tag == right.tag && type != NodeType::Invalid && right.type != NodeType::Invalid) { if (type == NodeType::Element && right.type == NodeType::Element) { return true; } else if(selection == right.selection || type == NodeType::AnySelection || right.type == NodeType::AnySelection) { return true; } } return false; }; bool DICOMTagPath::IsEmpty() const { return m_NodeInfos.empty(); }; bool DICOMTagPath:: IsExplicit() const { for (const auto & pos : m_NodeInfos) { if ((pos.type == NodeInfo::NodeType::AnySelection) || (pos.type == NodeInfo::NodeType::AnyElement)) return false; } return true; }; bool DICOMTagPath:: HasItemSelectionWildcardsOnly() const { bool result = false; for (const auto & pos : m_NodeInfos) { if (pos.type == NodeInfo::NodeType::AnyElement) return false; result = result || pos.type == NodeInfo::NodeType::AnySelection; } return result; }; DICOMTagPath::PathIndexType DICOMTagPath::Size() const { return m_NodeInfos.size(); } DICOMTagPath::PathIndexType DICOMTagPath:: AddNode(const NodeInfo& newNode) { m_NodeInfos.push_back(newNode); return m_NodeInfos.size() - 1; }; const DICOMTagPath::NodeInfo& DICOMTagPath:: GetNode(const PathIndexType& index) const { if (index >= Size()) { mitkThrow() << "Error. Cannot return info of path node. Node index is out of bound. Index: " << index << "; Path: " << this->ToStr(); } return m_NodeInfos[index]; }; DICOMTagPath::NodeInfo& DICOMTagPath:: GetNode(const PathIndexType& index) { if (index >= Size()) { mitkThrow() << "Error. Cannot return info of path node. Node index is out of bound. Index: " << index << "; Path: " << this->ToStr(); } return m_NodeInfos[index]; }; const DICOMTagPath::NodeInfo& DICOMTagPath:: GetFirstNode() const { return GetNode(0); }; const DICOMTagPath::NodeInfo& DICOMTagPath:: GetLastNode() const { return GetNode(Size() - 1); }; DICOMTagPath::NodeInfo& DICOMTagPath:: GetLastNode() { return GetNode(Size() - 1); }; const DICOMTagPath::NodeInfoVectorType& DICOMTagPath:: GetNodes() const { return m_NodeInfos; }; std::string DICOMTagPath:: ToStr() const { std::ostringstream nameStream; if (this->Size() == 0) return nameStream.str(); PathIndexType i = 0; for (const auto& node : m_NodeInfos) { if (i) { nameStream << "."; } ++i; if (node.type == NodeInfo::NodeType::AnyElement) { nameStream << "*"; } else if (node.type != NodeInfo::NodeType::Invalid) { nameStream << "(" << std::setw(4) << std::setfill('0') << std::hex << std::uppercase << node.tag.GetGroup() << std::nouppercase << "," << std::setw(4) << std::setfill('0') << std::hex << std::uppercase << node.tag.GetElement() << std::nouppercase << ")"; if (node.type == NodeInfo::NodeType::SequenceSelection) { nameStream << "[" << node.selection << "]"; } else if (node.type == NodeInfo::NodeType::AnySelection) { nameStream << "[*]"; } } else { nameStream << "INVALID_NODE"; } } return nameStream.str(); }; bool DICOMTagPath:: operator == (const DICOMTagPath& path) const { return this->m_NodeInfos == path.m_NodeInfos; }; bool DICOMTagPath:: operator < (const DICOMTagPath& right) const { auto rightIter = right.m_NodeInfos.cbegin(); const auto rightEnd = right.m_NodeInfos.cend(); for (const auto& leftPos : m_NodeInfos) { if (rightIter == rightEnd) return false; if (leftPos.tag.GetElement() < rightIter->tag.GetElement()) return true; if (rightIter->tag.GetElement() < leftPos.tag.GetElement()) return false; if (leftPos.tag.GetGroup() < rightIter->tag.GetGroup()) return true; if (rightIter->tag.GetGroup()< leftPos.tag.GetGroup()) return false; if (leftPos.type < rightIter->type) return true; if (rightIter->type< leftPos.type) return false; if (leftPos.selection < rightIter->selection) return true; if (rightIter->selection< leftPos.selection) return false; ++rightIter; } return rightIter != rightEnd; } bool DICOMTagPath:: Equals(const DICOMTagPath& path) const { return DICOMTagPathesMatch(*this, path); }; DICOMTagPath& DICOMTagPath:: operator = (const DICOMTagPath& path) { if (&path == this) return *this; this->m_NodeInfos = path.m_NodeInfos; return *this; }; DICOMTagPath& DICOMTagPath::AddAnyElement() { m_NodeInfos.emplace_back(DICOMTag(0,0), NodeInfo::NodeType::AnyElement); return *this; }; DICOMTagPath& DICOMTagPath::AddElement(unsigned int group, unsigned int element) { m_NodeInfos.emplace_back(DICOMTag(group, element), NodeInfo::NodeType::Element); return *this; }; DICOMTagPath& DICOMTagPath::AddAnySelection(unsigned int group, unsigned int element) { m_NodeInfos.emplace_back(DICOMTag(group, element), NodeInfo::NodeType::AnySelection); return *this; }; DICOMTagPath& DICOMTagPath::AddSelection(unsigned int group, unsigned int element, ItemSelectionIndex index) { m_NodeInfos.emplace_back(DICOMTag(group, element), NodeInfo::NodeType::SequenceSelection, index); return *this; }; DICOMTagPath& DICOMTagPath:: FromStr(const std::string& pathStr) { NodeInfoVectorType result; std::istringstream f(pathStr); std::string subStr; while (getline(f, subStr, '.')) { NodeInfo info; if (subStr == "*") { info.type = NodeInfo::NodeType::AnyElement; } else { std::regex reg_element("\\(([A - Fa - f\\d]{4}),([A - Fa - f\\d]{4})\\)"); std::regex reg_anySelection("\\(([A - Fa - f\\d]{4}),([A - Fa - f\\d]{4})\\)\\[\\*\\]"); std::regex reg_Selection("\\(([A - Fa - f\\d]{4}),([A - Fa - f\\d]{4})\\)\\[(\\d+)\\]"); std::smatch sm; if (std::regex_match(subStr, sm, reg_anySelection)) { info.type = NodeInfo::NodeType::AnySelection; info.tag = DICOMTag(std::stoul(sm[1], nullptr, 16), std::stoul(sm[2], nullptr, 16)); } else if (std::regex_match(subStr, sm, reg_Selection)) { info.type = NodeInfo::NodeType::SequenceSelection; info.tag = DICOMTag(std::stoul(sm[1], nullptr, 16), std::stoul(sm[2], nullptr, 16)); info.selection = std::stoi(sm[3]); } else if (std::regex_match(subStr, sm, reg_element)) { info.type = NodeInfo::NodeType::Element; info.tag = DICOMTag(std::stoul(sm[1], nullptr, 16), std::stoul(sm[2], nullptr, 16)); } } result.push_back(info); } this->m_NodeInfos.swap(result); return *this; }; DICOMTagPath::DICOMTagPath() { Reset(); }; DICOMTagPath:: DICOMTagPath(const DICOMTagPath& path) { *this = path; }; DICOMTagPath:: DICOMTagPath(const DICOMTag& tag) { m_NodeInfos.emplace_back(tag, NodeInfo::NodeType::Element); }; DICOMTagPath::DICOMTagPath(unsigned int group, unsigned int element) { m_NodeInfos.emplace_back(DICOMTag(group,element)); }; DICOMTagPath:: ~DICOMTagPath() {}; void DICOMTagPath:: Reset() { this->m_NodeInfos.clear(); }; bool DICOMTagPath:: DICOMTagPathesMatch(const DICOMTagPath& left, const DICOMTagPath& right) { - NodeInfoVectorType::const_iterator leftPos = left.GetNodes().cbegin(); - NodeInfoVectorType::const_iterator rightPos = right.GetNodes().cbegin(); - NodeInfoVectorType::const_iterator leftEnd = left.GetNodes().cend(); - NodeInfoVectorType::const_iterator rightEnd = right.GetNodes().cend(); + auto leftPos = left.GetNodes().cbegin(); + auto rightPos = right.GetNodes().cbegin(); + auto leftEnd = left.GetNodes().cend(); + auto rightEnd = right.GetNodes().cend(); while (leftPos != leftEnd && rightPos != rightEnd) { if (!leftPos->Matches(*rightPos)) break; ++leftPos; ++rightPos; } if (leftPos == leftEnd && rightPos == rightEnd) return true; else return false; }; std::ostream & operator<<(std::ostream &os, const DICOMTagPath &value) { os << value.ToStr(); return os; }; std::string DICOMTagPathToPropertyRegEx(const DICOMTagPath& tagPath) { std::ostringstream nameStream; nameStream << "DICOM"; for (const auto& node : tagPath.GetNodes()) { nameStream << "\\."; if (node.type == DICOMTagPath::NodeInfo::NodeType::AnyElement) { nameStream << "([A-Fa-f\\d]{4})\\.([A-Fa-f\\d]{4})"; } else if (node.type != DICOMTagPath::NodeInfo::NodeType::Invalid) { nameStream << GenerateRegExForNumber(node.tag.GetGroup()) << "\\." << GenerateRegExForNumber(node.tag.GetElement()); if (node.type == DICOMTagPath::NodeInfo::NodeType::SequenceSelection) { nameStream << "\\.\\[" << node.selection << "\\]"; } else if (node.type == DICOMTagPath::NodeInfo::NodeType::AnySelection) { nameStream << "\\.\\[(\\d*)\\]"; } } else { nameStream << "INVALIDNODE"; } } return nameStream.str(); }; std::string DICOMTagPathToPersistenceKeyRegEx(const DICOMTagPath& tagPath) { std::ostringstream nameStream; nameStream << "DICOM"; for (const auto& node : tagPath.GetNodes()) { nameStream << "_"; if (node.type == DICOMTagPath::NodeInfo::NodeType::AnyElement) { nameStream << "([A-Fa-f\\d]{4})_([A-Fa-f\\d]{4})"; } else if (node.type != DICOMTagPath::NodeInfo::NodeType::Invalid) { nameStream << GenerateRegExForNumber(node.tag.GetGroup()) << "_" << GenerateRegExForNumber(node.tag.GetElement()); if (node.type == DICOMTagPath::NodeInfo::NodeType::SequenceSelection) { nameStream << "_\\[" << node.selection << "\\]"; } else if (node.type == DICOMTagPath::NodeInfo::NodeType::AnySelection) { nameStream << "_\\[(\\d*)\\]"; } } else { nameStream << "INVALIDNODE"; } } return nameStream.str(); }; std::string DICOMTagPathToPersistenceKeyTemplate(const DICOMTagPath& tagPath) { std::ostringstream nameStream; nameStream << "DICOM"; int captureGroup = 1; for (const auto& node : tagPath.GetNodes()) { nameStream << "_"; if (node.type == DICOMTagPath::NodeInfo::NodeType::AnyElement) { nameStream << "$" << captureGroup++; nameStream << "_$" << captureGroup++; } else if (node.type != DICOMTagPath::NodeInfo::NodeType::Invalid) { nameStream << std::setw(4) << std::setfill('0') << std::hex << std::uppercase << node.tag.GetGroup() << std::nouppercase << "_" << std::setw(4) << std::setfill('0') << std::hex << std::uppercase << node.tag.GetElement(); if (node.type == DICOMTagPath::NodeInfo::NodeType::SequenceSelection) { nameStream << "_[" << node.selection << "]"; } else if (node.type == DICOMTagPath::NodeInfo::NodeType::AnySelection) { nameStream << "_[$" << captureGroup++ << "]"; } } else { nameStream << "INVALID_NODE"; } } return nameStream.str(); }; std::string DICOMTagPathToPersistenceNameTemplate(const DICOMTagPath& tagPath) { std::ostringstream nameStream; nameStream << "DICOM"; int captureGroup = 1; for (const auto& node : tagPath.GetNodes()) { nameStream << "."; if (node.type == DICOMTagPath::NodeInfo::NodeType::AnyElement) { nameStream << "$" << captureGroup++; nameStream << ".$" << captureGroup++; } else if (node.type != DICOMTagPath::NodeInfo::NodeType::Invalid) { nameStream << std::setw(4) << std::setfill('0') << std::hex << std::uppercase << node.tag.GetGroup() << std::nouppercase << "." << std::setw(4) << std::setfill('0') << std::hex << std::uppercase << node.tag.GetElement(); if (node.type == DICOMTagPath::NodeInfo::NodeType::SequenceSelection) { nameStream << ".[" << node.selection << "]"; } else if (node.type == DICOMTagPath::NodeInfo::NodeType::AnySelection) { nameStream << ".[$"< #include "usModuleContext.h" #include "usGetModuleContext.h" mitk::DICOMTagPathMapType::value_type MakeLegacyEntry(const std::string& propName, const mitk::DICOMTag& tag) { return std::make_pair(tag, propName); } mitk::DICOMTagPathMapType::value_type MakeEntry(const mitk::DICOMTagPath& tagPath) { return std::make_pair(tagPath, ""); } mitk::DICOMTagPathMapType mitk::GetCurrentDICOMTagsOfInterest() { mitk::DICOMTagPathMapType result; std::vector > toiRegisters = us::GetModuleContext()->GetServiceReferences(); if (toiRegisters.empty()) { // bad, no service found, cannot get tags of interest MITK_ERROR << "DICOM tag error: no service for DICOM tags of interest"; return result; } else if (toiRegisters.size() > 1) { MITK_WARN << "DICOM tag error: multiple service for DICOM tags of interest found. Using just one."; } - IDICOMTagsOfInterest* toiRegister = us::GetModuleContext()->GetService(toiRegisters.front()); + auto* toiRegister = us::GetModuleContext()->GetService(toiRegisters.front()); if (!toiRegister) { MITK_ERROR << "Service lookup error, cannot get DICOM tag of interest service "; } return toiRegister->GetTagsOfInterest(); } mitk::DICOMTagPathMapType mitk::GetDefaultDICOMTagsOfInterest() { DICOMTagPathMapType result; //These tags are copied from DICOMSeriesReader. The old naming style (deprecated) //is kept for backwards compatibility until it is removed. //Below we have also already added the properties with the new naming style // Patient module /*dicom.patient.PatientsName*/ result.insert(MakeEntry(DICOMTag(0x0010, 0x0010))); /*dicom.patient.PatientID*/ result.insert(MakeEntry(DICOMTag(0x0010, 0x0020))); /*dicom.patient.PatientsBirthDate*/ result.insert(MakeEntry(DICOMTag(0x0010, 0x0030))); /*dicom.patient.PatientsSex*/ result.insert(MakeEntry(DICOMTag(0x0010, 0x0040))); /*dicom.patient.PatientsBirthTime*/ result.insert(MakeEntry(DICOMTag(0x0010, 0x0032))); /*dicom.patient.OtherPatientIDs*/ result.insert(MakeEntry(DICOMTag(0x0010, 0x1000))); /*dicom.patient.OtherPatientNames*/ result.insert(MakeEntry(DICOMTag(0x0010, 0x1001))); /*dicom.patient.EthnicGroup*/ result.insert(MakeEntry(DICOMTag(0x0010, 0x2160))); /*dicom.patient.PatientComments*/ result.insert(MakeEntry(DICOMTag(0x0010, 0x4000))); /*dicom.patient.PatientIdentityRemoved*/ result.insert(MakeEntry(DICOMTag(0x0012, 0x0062))); /*dicom.patient.DeIdentificationMethod*/ result.insert(MakeEntry(DICOMTag(0x0012, 0x0063))); // General Study module /*dicom.study.StudyInstanceUID*/ result.insert(MakeEntry(DICOMTag(0x0020, 0x000d))); /*dicom.study.StudyDate*/ result.insert(MakeEntry(DICOMTag(0x0008, 0x0020))); /*dicom.study.StudyTime*/ result.insert(MakeEntry(DICOMTag(0x0008, 0x0030))); /*dicom.study.ReferringPhysiciansName*/ result.insert(MakeEntry(DICOMTag(0x0008, 0x0090))); /*dicom.study.StudyID*/ result.insert(MakeEntry(DICOMTag(0x0020, 0x0010))); /*dicom.study.AccessionNumber*/ result.insert(MakeEntry(DICOMTag(0x0008, 0x0050))); /*dicom.study.StudyDescription*/ result.insert(MakeEntry(DICOMTag(0x0008, 0x1030))); /*dicom.study.PhysiciansOfRecord*/ result.insert(MakeEntry(DICOMTag(0x0008, 0x1048))); /*dicom.study.NameOfPhysicianReadingStudy*/ result.insert(MakeEntry(DICOMTag(0x0008, 0x1060))); // General Series module /*dicom.series.Modality*/ result.insert(MakeEntry(DICOMTag(0x0008, 0x0060))); /*dicom.series.SeriesInstanceUID*/ result.insert(MakeEntry(DICOMTag(0x0020, 0x000e))); /*dicom.series.SeriesNumber*/ result.insert(MakeEntry(DICOMTag(0x0020, 0x0011))); /*dicom.series.Laterality*/ result.insert(MakeEntry(DICOMTag(0x0020, 0x0060))); /*dicom.series.SeriesDate*/ result.insert(MakeEntry(DICOMTag(0x0008, 0x0021))); /*dicom.series.SeriesTime*/ result.insert(MakeEntry(DICOMTag(0x0008, 0x0031))); /*dicom.series.PerformingPhysiciansName*/ result.insert(MakeEntry(DICOMTag(0x0008, 0x1050))); /*dicom.series.ProtocolName*/ result.insert(MakeEntry(DICOMTag(0x0018, 0x1030))); /*dicom.series.SeriesDescription*/ result.insert(MakeEntry(DICOMTag(0x0008, 0x103e))); /*dicom.series.OperatorsName*/ result.insert(MakeEntry(DICOMTag(0x0008, 0x1070))); /*dicom.series.BodyPartExamined*/ result.insert(MakeEntry(DICOMTag(0x0018, 0x0015))); /*dicom.series.PatientPosition*/ result.insert(MakeEntry(DICOMTag(0x0018, 0x5100))); /*dicom.series.SmallestPixelValueInSeries*/ result.insert(MakeEntry(DICOMTag(0x0028, 0x0108))); /*dicom.series.LargestPixelValueInSeries*/ result.insert(MakeEntry(DICOMTag(0x0028, 0x0109))); // VOI LUT module /*dicom.voilut.WindowCenter*/ result.insert(MakeEntry(DICOMTag(0x0028, 0x1050))); /*dicom.voilut.WindowWidth*/ result.insert(MakeEntry(DICOMTag(0x0028, 0x1051))); /*dicom.voilut.WindowCenterAndWidthExplanation*/ result.insert(MakeEntry(DICOMTag(0x0028, 0x1055))); // Image Pixel module /*dicom.pixel.PhotometricInterpretation*/ result.insert(MakeEntry(DICOMTag(0x0028, 0x0004))); /*dicom.pixel.Rows*/ result.insert(MakeEntry(DICOMTag(0x0028, 0x0010))); /*dicom.pixel.Columns*/ result.insert(MakeEntry(DICOMTag(0x0028, 0x0011))); // Image Plane module /*dicom.PixelSpacing*/ result.insert(MakeEntry(DICOMTag(0x0028, 0x0030))); /*dicom.ImagerPixelSpacing*/ result.insert(MakeEntry(DICOMTag(0x0018, 0x1164))); //additional for RT /*dicom.RescaleIntercept*/ result.insert(MakeEntry(DICOMTag(0x0028, 0x1052))); /*dicom.RescaleSlope*/ result.insert(MakeEntry(DICOMTag(0x0028, 0x1053))); /*dicom.ManufacturerModelName*/ result.insert(MakeEntry(DICOMTag(0x0008, 0x1090))); /*dicom.ManufacturerName*/ result.insert(MakeEntry(DICOMTag(0x0008, 0x0070))); /*dicom.InstitutionName*/ result.insert(MakeEntry(DICOMTag(0x0008, 0x0080))); /*dicom.StationName*/ result.insert(MakeEntry(DICOMTag(0x0008, 0x1010))); /*dicom.DoseGridScaling*/ result.insert(MakeEntry(DICOMTag(0x3004, 0x000e))); //Additions for RTPLAN DICOMTagPath doseReferenceSequence; doseReferenceSequence.AddAnySelection(0x300A, 0x0010); DICOMTagPath fractionGroupSequence; fractionGroupSequence.AddAnySelection(0x300A, 0x0070); DICOMTagPath beamSequence; beamSequence.AddAnySelection(0x300A, 0x00B0); DICOMTagPath referencedStructureSetSequence; referencedStructureSetSequence.AddAnySelection(0x300C, 0x0060); result.insert(MakeEntry(DICOMTagPath(doseReferenceSequence).AddElement(0x300A, 0x0013))); //dicom.DoseReferenceSequence.DoseReferenceUID result.insert(MakeEntry(DICOMTagPath(doseReferenceSequence).AddElement(0x300A, 0x0016))); //dicom.DoseReferenceSequence.DoseReferenceDescription result.insert(MakeEntry(DICOMTagPath(doseReferenceSequence).AddElement(0x300A, 0x0026))); //dicom.DoseReferenceSequence.TargetPrescriptionDose result.insert(MakeEntry(DICOMTagPath(fractionGroupSequence).AddElement(0x300A, 0x0078))); //dicom.FractionGroupSequence.NumberOfFractionsPlanned result.insert(MakeEntry(DICOMTagPath(fractionGroupSequence).AddElement(0x300A, 0x0080))); //dicom.FractionGroupSequence.NumberOfBeams result.insert(MakeEntry(DICOMTagPath(beamSequence).AddElement(0x300A, 0x00C6))); //dicom.BeamSequence.RadiationType result.insert(MakeEntry(DICOMTagPath(referencedStructureSetSequence).AddElement(0x0008, 0x1155))); //dicom.ReferencedStructureSetSequence.ReferencedSOPInstanceUID //Additions for RTSTRUCT DICOMTagPath structureSetROISequence; structureSetROISequence.AddAnySelection(0x3006, 0x0020); result.insert(MakeEntry(DICOMTagPath(structureSetROISequence).AddElement(0x3006, 0x0022))); //dicom.StructureSetROISequence.ROINumber result.insert(MakeEntry(DICOMTagPath(structureSetROISequence).AddElement(0x3006, 0x0026))); //dicom.StructureSetROISequence.ROIName result.insert(MakeEntry(DICOMTagPath(structureSetROISequence).AddElement(0x3006, 0x0024))); //dicom.StructureSetROISequence.ReferencedFrameOfReferenceUID //Additions for RTDOSE DICOMTagPath planReferenceSequence; planReferenceSequence.AddAnySelection(0x300C, 0x0002); result.insert(MakeEntry(DICOMTagPath(planReferenceSequence).AddElement(0x0008, 0x1155))); //dicom.PlanReferenceSequence.ReferencedSOPInstanceUID //Additions for PET DICOMTagPath radioPharmaRootTag; radioPharmaRootTag.AddAnySelection(0x0054, 0x0016); DICOMTagPath radioNuclideRootTag(radioPharmaRootTag); radioNuclideRootTag.AddAnySelection(0x0054, 0x0300); result.insert(MakeEntry(DICOMTagPath(radioPharmaRootTag).AddElement(0x0018, 0x0031))); //dicom.pet.Radiopharmaceutical result.insert(MakeEntry(DICOMTagPath(radioPharmaRootTag).AddElement(0x0018, 0x1072))); //dicom.pet.RadiopharmaceuticalStartTime result.insert(MakeEntry(DICOMTagPath(radioPharmaRootTag).AddElement(0x0018, 0x1074))); //dicom.pet.RadionuclideTotalDose result.insert(MakeEntry(DICOMTagPath(radioPharmaRootTag).AddElement(0x0018, 0x1075))); //dicom.pet.RadionuclideHalfLife result.insert(MakeEntry(DICOMTagPath(radioPharmaRootTag).AddElement(0x0018, 0x1076))); //dicom.pet.RadionuclidePositronFraction result.insert(MakeEntry(DICOMTagPath(radioNuclideRootTag).AddElement(0x0008, 0x0100))); //dicom.pet.Radionuclide.CodeValue result.insert(MakeEntry(DICOMTagPath(radioNuclideRootTag).AddElement(0x0008, 0x0102))); //dicom.pet.Radionuclide.CodingSchemeDesignator result.insert(MakeEntry(DICOMTagPath(radioNuclideRootTag).AddElement(0x0008, 0x0104))); //dicom.pet.Radionuclide.CodemManing result.insert(MakeEntry(DICOMTag(0x0054, 0x1001))); //dicom.pet.RadioactivityUnits result.insert(MakeEntry(DICOMTag(0x0054, 0x1102))); //dicom.pet.DecayCorrection result.insert(MakeEntry(DICOMTag(0x0054, 0x1321))); //dicom.pet.DecayFactor result.insert(MakeEntry(DICOMTag(0x0054, 0x1300))); //dicom.pet.FrameReferenceTime result.insert(MakeEntry(DICOMTag(0x0010, 0x1030))); //dicom.patient.PatientWeight result.insert(MakeEntry(DICOMTag(0x0010, 0x1020))); //dicom.patient.PatientSize //Other interesting acquisition correlated information result.insert(MakeEntry(DICOMTag(0x0008, 0x0022))); //dicom.acquisition date result.insert(MakeEntry(DICOMTag(0x0008, 0x0032))); //dicom.acquisition time result.insert(MakeEntry(DICOMTag(0x0008, 0x002a))); //dicom.acquisition datetime result.insert(MakeEntry(DICOMTag(0x0008, 0x0080))); //dicom.Modality result.insert(MakeEntry(DICOMTag(0x0018, 0x002a))); //dicom.Sequence Name result.insert(MakeEntry(DICOMTag(0x0018, 0x0020))); //dicom.Scanning Sequence result.insert(MakeEntry(DICOMTag(0x0018, 0x0021))); //dicom.Sequence Variant result.insert(MakeEntry(DICOMTag(0x0018, 0x0080))); //dicom.TR result.insert(MakeEntry(DICOMTag(0x0018, 0x0081))); //dicom.TE result.insert(MakeEntry(DICOMTag(0x0018, 0x1310))); //dicom.Acquisition Matrix result.insert(MakeEntry(DICOMTag(0x0018, 0x0087))); //dicom.Magnetic Field Strength //SOP result.insert(MakeEntry(DICOMTag(0x0008, 0x0018))); //SOP Instance UID result.insert(MakeEntry(DICOMTag(0x0020, 0x0013))); //Instance number result.insert(MakeEntry(DICOMTag(0x0020, 0x1041))); //Slice location return result; }; diff --git a/Modules/DICOMReader/src/mitkEquiDistantBlocksSorter.cpp b/Modules/DICOMReader/src/mitkEquiDistantBlocksSorter.cpp index 66a3823645..8212e45e78 100644 --- a/Modules/DICOMReader/src/mitkEquiDistantBlocksSorter.cpp +++ b/Modules/DICOMReader/src/mitkEquiDistantBlocksSorter.cpp @@ -1,573 +1,573 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ //#define MBILOG_ENABLE_DEBUG #include "mitkEquiDistantBlocksSorter.h" mitk::EquiDistantBlocksSorter::SliceGroupingAnalysisResult ::SliceGroupingAnalysisResult() { } mitk::DICOMDatasetList mitk::EquiDistantBlocksSorter::SliceGroupingAnalysisResult ::GetBlockDatasets() { return m_GroupedFiles; } mitk::DICOMDatasetList mitk::EquiDistantBlocksSorter::SliceGroupingAnalysisResult ::GetUnsortedDatasets() { return m_UnsortedFiles; } bool mitk::EquiDistantBlocksSorter::SliceGroupingAnalysisResult ::ContainsGantryTilt() { return m_TiltInfo.IsRegularGantryTilt(); } void mitk::EquiDistantBlocksSorter::SliceGroupingAnalysisResult ::AddFileToSortedBlock(DICOMDatasetAccess* dataset) { m_GroupedFiles.push_back( dataset ); } void mitk::EquiDistantBlocksSorter::SliceGroupingAnalysisResult ::AddFileToUnsortedBlock(DICOMDatasetAccess* dataset) { m_UnsortedFiles.push_back( dataset ); } void mitk::EquiDistantBlocksSorter::SliceGroupingAnalysisResult ::AddFilesToUnsortedBlock(const DICOMDatasetList& datasets) { m_UnsortedFiles.insert( m_UnsortedFiles.end(), datasets.begin(), datasets.end() ); } void mitk::EquiDistantBlocksSorter::SliceGroupingAnalysisResult ::SetFirstFilenameOfBlock(const std::string& filename) { m_FirstFilenameOfBlock = filename; } std::string mitk::EquiDistantBlocksSorter::SliceGroupingAnalysisResult ::GetFirstFilenameOfBlock() const { return m_FirstFilenameOfBlock; } void mitk::EquiDistantBlocksSorter::SliceGroupingAnalysisResult ::SetLastFilenameOfBlock(const std::string& filename) { m_LastFilenameOfBlock = filename; } std::string mitk::EquiDistantBlocksSorter::SliceGroupingAnalysisResult ::GetLastFilenameOfBlock() const { return m_LastFilenameOfBlock; } void mitk::EquiDistantBlocksSorter::SliceGroupingAnalysisResult ::FlagGantryTilt(const GantryTiltInformation& tiltInfo) { m_TiltInfo = tiltInfo; } const mitk::GantryTiltInformation& mitk::EquiDistantBlocksSorter::SliceGroupingAnalysisResult ::GetTiltInfo() const { return m_TiltInfo; } void mitk::EquiDistantBlocksSorter::SliceGroupingAnalysisResult ::UndoPrematureGrouping() { assert( !m_GroupedFiles.empty() ); m_UnsortedFiles.insert( m_UnsortedFiles.begin(), m_GroupedFiles.back() ); m_GroupedFiles.pop_back(); m_TiltInfo = GantryTiltInformation(); } // ------------------------ end helper class mitk::EquiDistantBlocksSorter ::EquiDistantBlocksSorter() :DICOMDatasetSorter() ,m_AcceptTilt(false) ,m_ToleratedOriginOffset(0.3) ,m_ToleratedOriginOffsetIsAbsolute(false) ,m_AcceptTwoSlicesGroups(true) { } mitk::EquiDistantBlocksSorter ::EquiDistantBlocksSorter(const EquiDistantBlocksSorter& other ) :DICOMDatasetSorter(other) ,m_AcceptTilt(other.m_AcceptTilt) ,m_ToleratedOriginOffset(other.m_ToleratedOriginOffset) ,m_ToleratedOriginOffsetIsAbsolute(other.m_ToleratedOriginOffsetIsAbsolute) ,m_AcceptTwoSlicesGroups(other.m_AcceptTwoSlicesGroups) { } mitk::EquiDistantBlocksSorter ::~EquiDistantBlocksSorter() { } bool mitk::EquiDistantBlocksSorter ::operator==(const DICOMDatasetSorter& other) const { - if (const EquiDistantBlocksSorter* otherSelf = dynamic_cast(&other)) + if (const auto* otherSelf = dynamic_cast(&other)) { return this->m_AcceptTilt == otherSelf->m_AcceptTilt && this->m_ToleratedOriginOffsetIsAbsolute == otherSelf->m_ToleratedOriginOffsetIsAbsolute && this->m_AcceptTwoSlicesGroups == otherSelf->m_AcceptTwoSlicesGroups && (fabs(this->m_ToleratedOriginOffset - otherSelf->m_ToleratedOriginOffset) < eps); } else { return false; } } void mitk::EquiDistantBlocksSorter ::PrintConfiguration(std::ostream& os, const std::string& indent) const { std::stringstream ts; if (!m_ToleratedOriginOffsetIsAbsolute) { ts << "adaptive"; } else { ts << m_ToleratedOriginOffset << "mm"; } os << indent << "Sort into blocks of equidistant, well-aligned (tolerance " << ts.str() << ") slices " << (m_AcceptTilt ? "(accepting a gantry tilt)" : "") << std::endl; } void mitk::EquiDistantBlocksSorter ::SetAcceptTilt(bool accept) { m_AcceptTilt = accept; } bool mitk::EquiDistantBlocksSorter ::GetAcceptTilt() const { return m_AcceptTilt; } void mitk::EquiDistantBlocksSorter ::SetAcceptTwoSlicesGroups(bool accept) { m_AcceptTwoSlicesGroups = accept; } bool mitk::EquiDistantBlocksSorter ::GetAcceptTwoSlicesGroups() const { return m_AcceptTwoSlicesGroups; } mitk::EquiDistantBlocksSorter& mitk::EquiDistantBlocksSorter ::operator=(const EquiDistantBlocksSorter& other) { if (this != &other) { DICOMDatasetSorter::operator=(other); m_AcceptTilt = other.m_AcceptTilt; m_ToleratedOriginOffset = other.m_ToleratedOriginOffset; m_ToleratedOriginOffsetIsAbsolute = other.m_ToleratedOriginOffsetIsAbsolute; m_AcceptTwoSlicesGroups = other.m_AcceptTwoSlicesGroups; } return *this; } mitk::DICOMTagList mitk::EquiDistantBlocksSorter ::GetTagsOfInterest() { DICOMTagList tags; tags.push_back( DICOMTag(0x0020, 0x0032) ); // ImagePositionPatient tags.push_back( DICOMTag(0x0020, 0x0037) ); // ImageOrientationPatient tags.push_back( DICOMTag(0x0018, 0x1120) ); // GantryDetectorTilt return tags; } void mitk::EquiDistantBlocksSorter ::Sort() { DICOMDatasetList remainingInput = GetInput(); // copy typedef std::list OutputListType; OutputListType outputs; m_SliceGroupingResults.clear(); while (!remainingInput.empty()) // repeat until all files are grouped somehow { SliceGroupingAnalysisResult regularBlock = this->AnalyzeFileForITKImageSeriesReaderSpacingAssumption( remainingInput, m_AcceptTilt ); #ifdef MBILOG_ENABLE_DEBUG DICOMDatasetList inBlock = regularBlock.GetBlockDatasets(); DICOMDatasetList laterBlock = regularBlock.GetUnsortedDatasets(); MITK_DEBUG << "Result: sorted 3D group with " << inBlock.size() << " files"; for (DICOMDatasetList::const_iterator diter = inBlock.cbegin(); diter != inBlock.cend(); ++diter) MITK_DEBUG << " IN " << (*diter)->GetFilenameIfAvailable(); for (DICOMDatasetList::const_iterator diter = laterBlock.cbegin(); diter != laterBlock.cend(); ++diter) MITK_DEBUG << " OUT " << (*diter)->GetFilenameIfAvailable(); #endif // MBILOG_ENABLE_DEBUG outputs.push_back( regularBlock.GetBlockDatasets() ); m_SliceGroupingResults.push_back( regularBlock ); remainingInput = regularBlock.GetUnsortedDatasets(); } unsigned int numberOfOutputs = outputs.size(); this->SetNumberOfOutputs(numberOfOutputs); unsigned int outputIndex(0); for (auto oIter = outputs.cbegin(); oIter != outputs.cend(); ++outputIndex, ++oIter) { this->SetOutput(outputIndex, *oIter); } } void mitk::EquiDistantBlocksSorter ::SetToleratedOriginOffsetToAdaptive(double fractionOfInterSliceDistance) { m_ToleratedOriginOffset = fractionOfInterSliceDistance; m_ToleratedOriginOffsetIsAbsolute = false; if (m_ToleratedOriginOffset < 0.0) { MITK_WARN << "Call SetToleratedOriginOffsetToAdaptive() only with positive numbers between 0.0 and 1.0, read documentation!"; } if (m_ToleratedOriginOffset > 0.5) { MITK_WARN << "EquiDistantBlocksSorter is now accepting large errors, take care of measurements, they could appear at unprecise locations!"; } } void mitk::EquiDistantBlocksSorter ::SetToleratedOriginOffset(double millimeters) { m_ToleratedOriginOffset = millimeters; m_ToleratedOriginOffsetIsAbsolute = true; if (m_ToleratedOriginOffset < 0.0) { MITK_WARN << "Negative tolerance set to SetToleratedOriginOffset()!"; } } double mitk::EquiDistantBlocksSorter ::GetToleratedOriginOffset() const { return m_ToleratedOriginOffset; } bool mitk::EquiDistantBlocksSorter ::IsToleratedOriginOffsetAbsolute() const { return m_ToleratedOriginOffsetIsAbsolute; } std::string mitk::EquiDistantBlocksSorter ::ConstCharStarToString(const char* s) { return s ? std::string(s) : std::string(); } mitk::EquiDistantBlocksSorter::SliceGroupingAnalysisResult mitk::EquiDistantBlocksSorter ::AnalyzeFileForITKImageSeriesReaderSpacingAssumption( const DICOMDatasetList& datasets, bool groupImagesWithGantryTilt) { // result.first = files that fit ITK's assumption // result.second = files that do not fit, should be run through AnalyzeFileForITKImageSeriesReaderSpacingAssumption() again SliceGroupingAnalysisResult result; // we const_cast here, because I could not use a map.at(), which would make the code much more readable const DICOMTag tagImagePositionPatient = DICOMTag(0x0020,0x0032); // Image Position (Patient) const DICOMTag tagImageOrientation = DICOMTag(0x0020, 0x0037); // Image Orientation Vector3D fromFirstToSecondOrigin; fromFirstToSecondOrigin.Fill(0.0); bool fromFirstToSecondOriginInitialized(false); Point3D thisOrigin; thisOrigin.Fill(0.0f); Point3D lastOrigin; lastOrigin.Fill(0.0f); Point3D lastDifferentOrigin; lastDifferentOrigin.Fill(0.0f); bool lastOriginInitialized(false); MITK_DEBUG << "--------------------------------------------------------------------------------"; MITK_DEBUG << "Analyzing " << datasets.size() << " files for z-spacing assumption of ITK's ImageSeriesReader (group tilted: " << groupImagesWithGantryTilt << ")"; unsigned int fileIndex(0); double toleratedOriginError(0.005); // default: max. 1/10mm error when measurement crosses 20 slices in z direction (too strict? we don't know better) for (auto dsIter = datasets.cbegin(); dsIter != datasets.cend(); ++dsIter, ++fileIndex) { bool fileFitsIntoPattern(false); std::string thisOriginString; // Read tag value into point3D. PLEASE replace this by appropriate GDCM code if you figure out how to do that thisOriginString = (*dsIter)->GetTagValueAsString(tagImagePositionPatient).value; if (thisOriginString.empty()) { // don't let such files be in a common group. Everything without position information will be loaded as a single slice: // with standard DICOM files this can happen to: CR, DX, SC MITK_DEBUG << " ==> Sort away " << *dsIter << " for later analysis (no position information)"; // we already have one occupying this position if ( result.GetBlockDatasets().empty() ) // nothing WITH position information yet { // ==> this is a group of its own, stop processing, come back later result.AddFileToSortedBlock( *dsIter ); DICOMDatasetList remainingFiles; remainingFiles.insert( remainingFiles.end(), dsIter+1, datasets.end() ); result.AddFilesToUnsortedBlock( remainingFiles ); fileFitsIntoPattern = false; break; // no files anymore } else { // ==> this does not match, consider later result.AddFileToUnsortedBlock( *dsIter ); // sort away for further analysis fileFitsIntoPattern = false; continue; // next file } } bool ignoredConversionError(-42); // hard to get here, no graceful way to react thisOrigin = DICOMStringToPoint3D( thisOriginString, ignoredConversionError ); MITK_DEBUG << " " << fileIndex << " " << (*dsIter)->GetFilenameIfAvailable() << " at " /* << thisOriginString */ << "(" << thisOrigin[0] << "," << thisOrigin[1] << "," << thisOrigin[2] << ")"; if ( lastOriginInitialized && (thisOrigin == lastOrigin) ) { MITK_DEBUG << " ==> Sort away " << *dsIter << " for separate time step"; // we already have one occupying this position result.AddFileToUnsortedBlock( *dsIter ); // sort away for further analysis fileFitsIntoPattern = false; } else { if (!fromFirstToSecondOriginInitialized && lastOriginInitialized) // calculate vector as soon as possible when we get a new position { fromFirstToSecondOrigin = thisOrigin - lastDifferentOrigin; fromFirstToSecondOriginInitialized = true; // classic mode without tolerance! if (!m_ToleratedOriginOffsetIsAbsolute) { MITK_DEBUG << "Distance of two slices: " << fromFirstToSecondOrigin.GetNorm() << "mm"; toleratedOriginError = fromFirstToSecondOrigin.GetNorm() * 0.3; // a third of the slice distance // (less than half, which would mean that a slice is displayed where another slice should actually be) } else { toleratedOriginError = m_ToleratedOriginOffset; } MITK_DEBUG << "Accepting errors in actual versus expected origin up to " << toleratedOriginError << "mm"; // Here we calculate if this slice and the previous one are well aligned, // i.e. we test if the previous origin is on a line through the current // origin, directed into the normal direction of the current slice. // If this is NOT the case, then we have a data set with a TILTED GANTRY geometry, // which cannot be simply loaded into a single mitk::Image at the moment. // For this case, we flag this finding in the result and DicomSeriesReader // can correct for that later. Vector3D right; right.Fill(0.0); Vector3D up; right.Fill(0.0); // might be down as well, but it is just a name at this point std::string orientationValue = (*dsIter)->GetTagValueAsString( tagImageOrientation ).value; DICOMStringToOrientationVectors( orientationValue, right, up, ignoredConversionError ); GantryTiltInformation tiltInfo( lastDifferentOrigin, thisOrigin, right, up, 1 ); if ( tiltInfo.IsSheared() ) { /* optimistic approach, accepting gantry tilt: save file for later, check all further files */ // at this point we have TWO slices analyzed! if they are the only two files, we still split, because there is no third to verify our tilting assumption. // later with a third being available, we must check if the initial tilting vector is still valid. if yes, continue. // if NO, we need to split the already sorted part (result.first) and the currently analyzed file (*dsIter) // tell apart gantry tilt from overall skewedness // sort out irregularly sheared slices, that IS NOT tilting if ( groupImagesWithGantryTilt && tiltInfo.IsRegularGantryTilt() ) { assert(!datasets.empty()); result.FlagGantryTilt(tiltInfo); result.AddFileToSortedBlock( *dsIter ); // this file is good for current block result.SetFirstFilenameOfBlock( datasets.front()->GetFilenameIfAvailable() ); result.SetLastFilenameOfBlock( datasets.back()->GetFilenameIfAvailable() ); fileFitsIntoPattern = true; } else // caller does not want tilt compensation OR shearing is more complicated than tilt { result.AddFileToUnsortedBlock( *dsIter ); // sort away for further analysis fileFitsIntoPattern = false; } } else // not sheared { result.AddFileToSortedBlock( *dsIter ); // this file is good for current block fileFitsIntoPattern = true; } } else if (fromFirstToSecondOriginInitialized) // we already know the offset between slices { Point3D assumedOrigin = lastDifferentOrigin + fromFirstToSecondOrigin; Vector3D originError = assumedOrigin - thisOrigin; double norm = originError.GetNorm(); if (norm > toleratedOriginError) { MITK_DEBUG << " File does not fit into the inter-slice distance pattern (diff = " << norm << ", allowed " << toleratedOriginError << ")."; MITK_DEBUG << " Expected position (" << assumedOrigin[0] << "," << assumedOrigin[1] << "," << assumedOrigin[2] << "), got position (" << thisOrigin[0] << "," << thisOrigin[1] << "," << thisOrigin[2] << ")"; MITK_DEBUG << " ==> Sort away " << *dsIter << " for later analysis"; // At this point we know we deviated from the expectation of ITK's ImageSeriesReader // We split the input file list at this point, i.e. all files up to this one (excluding it) // are returned as group 1, the remaining files (including the faulty one) are group 2 /* Optimistic approach: check if any of the remaining slices fits in */ result.AddFileToUnsortedBlock( *dsIter ); // sort away for further analysis fileFitsIntoPattern = false; } else { result.AddFileToSortedBlock( *dsIter ); // this file is good for current block fileFitsIntoPattern = true; } } else // this should be the very first slice { result.AddFileToSortedBlock( *dsIter ); // this file is good for current block fileFitsIntoPattern = true; } } // record current origin for reference in later iterations if ( !lastOriginInitialized || ( fileFitsIntoPattern && (thisOrigin != lastOrigin) ) ) { lastDifferentOrigin = thisOrigin; } lastOrigin = thisOrigin; lastOriginInitialized = true; } if ( result.ContainsGantryTilt() ) { // check here how many files were grouped. // IF it was only two files AND we assume tiltedness (e.g. save "distance") // THEN we would want to also split the two previous files (simple) because // we don't have any reason to assume they belong together // Above behavior can be configured via m_AcceptTwoSlicesGroups, the default being "do accept" if ( result.GetBlockDatasets().size() == 2 && !m_AcceptTwoSlicesGroups ) { result.UndoPrematureGrouping(); } } // update tilt info to get maximum precision // earlier, tilt was only calculated from first and second slice. // now that we know the whole range, we can re-calculate using the very first and last slice if ( result.ContainsGantryTilt() && result.GetBlockDatasets().size() > 1 ) { try { DICOMDatasetList datasets = result.GetBlockDatasets(); DICOMDatasetAccess* firstDataset = datasets.front(); DICOMDatasetAccess* lastDataset = datasets.back(); unsigned int numberOfSlicesApart = datasets.size() - 1; std::string orientationString = firstDataset->GetTagValueAsString( tagImageOrientation ).value; std::string firstOriginString = firstDataset->GetTagValueAsString( tagImagePositionPatient ).value; std::string lastOriginString = lastDataset->GetTagValueAsString( tagImagePositionPatient ).value; result.FlagGantryTilt( GantryTiltInformation::MakeFromTagValues( firstOriginString, lastOriginString, orientationString, numberOfSlicesApart )); } catch (...) { // just do not flag anything, we are ok } } return result; } diff --git a/Modules/DICOMReader/src/mitkITKDICOMSeriesReaderHelper.cpp b/Modules/DICOMReader/src/mitkITKDICOMSeriesReaderHelper.cpp index 995c1835ad..d299111837 100644 --- a/Modules/DICOMReader/src/mitkITKDICOMSeriesReaderHelper.cpp +++ b/Modules/DICOMReader/src/mitkITKDICOMSeriesReaderHelper.cpp @@ -1,458 +1,458 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ //#define MBILOG_ENABLE_DEBUG #include #define BOOST_DATE_TIME_NO_LIB //Prevent unnecessary/unwanted auto link in this compilation when activating boost libraries in the MITK superbuild //It is necessary because BOOST_ALL_DYN_LINK overwrites BOOST_DATE_TIME_NO_LIB #if defined(BOOST_ALL_DYN_LINK) #undef BOOST_ALL_DYN_LINK #endif #include #include "mitkITKDICOMSeriesReaderHelper.h" #include "mitkITKDICOMSeriesReaderHelper.txx" #include "mitkDICOMGDCMTagScanner.h" #include "mitkArbitraryTimeGeometry.h" #include "dcmtk/dcmdata/dcvrda.h" const mitk::DICOMTag mitk::ITKDICOMSeriesReaderHelper::AcquisitionDateTag = mitk::DICOMTag( 0x0008, 0x0022 ); const mitk::DICOMTag mitk::ITKDICOMSeriesReaderHelper::AcquisitionTimeTag = mitk::DICOMTag( 0x0008, 0x0032 ); const mitk::DICOMTag mitk::ITKDICOMSeriesReaderHelper::TriggerTimeTag = mitk::DICOMTag( 0x0018, 0x1060 ); #define switch3DCase( IOType, T ) \ case IOType: \ return LoadDICOMByITK( filenames, correctTilt, tiltInfo, io ); bool mitk::ITKDICOMSeriesReaderHelper::CanHandleFile( const std::string& filename ) { MITK_DEBUG << "ITKDICOMSeriesReaderHelper::CanHandleFile " << filename; itk::GDCMImageIO::Pointer tester = itk::GDCMImageIO::New(); return tester->CanReadFile( filename.c_str() ); } mitk::Image::Pointer mitk::ITKDICOMSeriesReaderHelper::Load( const StringContainer& filenames, bool correctTilt, const GantryTiltInformation& tiltInfo ) { if ( filenames.empty() ) { MITK_DEBUG << "Calling LoadDicomSeries with empty filename string container. Probably invalid application logic."; return nullptr; // this is not actually an error but the result is very simple } typedef itk::GDCMImageIO DcmIoType; DcmIoType::Pointer io = DcmIoType::New(); try { if ( io->CanReadFile( filenames.front().c_str() ) ) { io->SetFileName( filenames.front().c_str() ); io->ReadImageInformation(); if ( io->GetPixelType() == itk::ImageIOBase::SCALAR ) { switch ( io->GetComponentType() ) { switch3DCase(DcmIoType::UCHAR, unsigned char) switch3DCase(DcmIoType::CHAR, char) switch3DCase( DcmIoType::USHORT, unsigned short) switch3DCase(DcmIoType::SHORT, short) switch3DCase(DcmIoType::UINT, unsigned int) switch3DCase(DcmIoType::INT, int) switch3DCase( DcmIoType::ULONG, long unsigned int) switch3DCase(DcmIoType::LONG, long int) switch3DCase(DcmIoType::FLOAT, float) switch3DCase(DcmIoType::DOUBLE, double) default : MITK_ERROR << "Found unsupported DICOM scalar pixel type: (enum value) " << io->GetComponentType(); } } else if ( io->GetPixelType() == itk::ImageIOBase::RGB ) { switch ( io->GetComponentType() ) { switch3DCase(DcmIoType::UCHAR, itk::RGBPixel) switch3DCase( DcmIoType::CHAR, itk::RGBPixel) switch3DCase(DcmIoType::USHORT, itk::RGBPixel) switch3DCase(DcmIoType::SHORT, itk::RGBPixel) switch3DCase( DcmIoType::UINT, itk::RGBPixel) switch3DCase(DcmIoType::INT, itk::RGBPixel) switch3DCase(DcmIoType::ULONG, itk::RGBPixel) switch3DCase(DcmIoType::LONG, itk::RGBPixel) switch3DCase( DcmIoType::FLOAT, itk::RGBPixel) switch3DCase(DcmIoType::DOUBLE, itk::RGBPixel) default : MITK_ERROR << "Found unsupported DICOM scalar pixel type: (enum value) " << io->GetComponentType(); } } MITK_ERROR << "Unsupported DICOM pixel type"; return nullptr; } } catch ( const itk::MemoryAllocationError& e ) { MITK_ERROR << "Out of memory. Cannot load DICOM series: " << e.what(); } catch ( const std::exception& e ) { MITK_ERROR << "Error encountered when loading DICOM series:" << e.what(); } catch ( ... ) { MITK_ERROR << "Unspecified error encountered when loading DICOM series."; } return nullptr; } #define switch3DnTCase( IOType, T ) \ case IOType: \ return LoadDICOMByITK3DnT( filenamesLists, correctTilt, tiltInfo, io ); mitk::Image::Pointer mitk::ITKDICOMSeriesReaderHelper::Load3DnT( const StringContainerList& filenamesLists, bool correctTilt, const GantryTiltInformation& tiltInfo ) { if ( filenamesLists.empty() || filenamesLists.front().empty() ) { MITK_DEBUG << "Calling LoadDicomSeries with empty filename string container. Probably invalid application logic."; return nullptr; // this is not actually an error but the result is very simple } typedef itk::GDCMImageIO DcmIoType; DcmIoType::Pointer io = DcmIoType::New(); try { if ( io->CanReadFile( filenamesLists.front().front().c_str() ) ) { io->SetFileName( filenamesLists.front().front().c_str() ); io->ReadImageInformation(); if ( io->GetPixelType() == itk::ImageIOBase::SCALAR ) { switch ( io->GetComponentType() ) { switch3DnTCase(DcmIoType::UCHAR, unsigned char) switch3DnTCase(DcmIoType::CHAR, char) switch3DnTCase(DcmIoType::USHORT, unsigned short) switch3DnTCase( DcmIoType::SHORT, short) switch3DnTCase(DcmIoType::UINT, unsigned int) switch3DnTCase(DcmIoType::INT, int) switch3DnTCase(DcmIoType::ULONG, long unsigned int) switch3DnTCase(DcmIoType::LONG, long int) switch3DnTCase(DcmIoType::FLOAT, float) switch3DnTCase(DcmIoType::DOUBLE, double) default : MITK_ERROR << "Found unsupported DICOM scalar pixel type: (enum value) " << io->GetComponentType(); } } else if ( io->GetPixelType() == itk::ImageIOBase::RGB ) { switch ( io->GetComponentType() ) { switch3DnTCase(DcmIoType::UCHAR, itk::RGBPixel) switch3DnTCase(DcmIoType::CHAR, itk::RGBPixel) switch3DnTCase( DcmIoType::USHORT, itk::RGBPixel) switch3DnTCase(DcmIoType::SHORT, itk::RGBPixel) switch3DnTCase(DcmIoType::UINT, itk::RGBPixel) switch3DnTCase( DcmIoType::INT, itk::RGBPixel) switch3DnTCase(DcmIoType::ULONG, itk::RGBPixel) switch3DnTCase(DcmIoType::LONG, itk::RGBPixel) switch3DnTCase( DcmIoType::FLOAT, itk::RGBPixel) switch3DnTCase(DcmIoType::DOUBLE, itk::RGBPixel) default : MITK_ERROR << "Found unsupported DICOM scalar pixel type: (enum value) " << io->GetComponentType(); } } MITK_ERROR << "Unsupported DICOM pixel type"; return nullptr; } } catch ( const itk::MemoryAllocationError& e ) { MITK_ERROR << "Out of memory. Cannot load DICOM series: " << e.what(); } catch ( const std::exception& e ) { MITK_ERROR << "Error encountered when loading DICOM series:" << e.what(); } catch ( ... ) { MITK_ERROR << "Unspecified error encountered when loading DICOM series."; } return nullptr; } bool ConvertDICOMDateTimeString( const std::string& dateString, const std::string& timeString, OFDateTime& time ) { OFString content( timeString.c_str() ); if ( !dateString.empty() ) { content = OFString( dateString.c_str() ).append( content ); } else { // This is a workaround for DICOM data that has an AquisitionTime but no AquisitionDate. // In this case, we use the current date. That's not really nice, but is absolutely OK // as we're only interested in the time anyways... OFString currentDate; DcmDate::getCurrentDate( currentDate ); content = currentDate.append( content ); } const OFCondition result = DcmDateTime::getOFDateTimeFromString( content, time ); return result.good(); } boost::posix_time::ptime ConvertOFDateTimeToPTime( const OFDateTime& time ) { const boost::gregorian::date boostDate( time.getDate().getYear(), time.getDate().getMonth(), time.getDate().getDay() ); const boost::posix_time::time_duration boostTime = boost::posix_time::hours( time.getTime().getHour() ) + boost::posix_time::minutes( time.getTime().getMinute() ) + boost::posix_time::seconds( time.getTime().getSecond() ) + boost::posix_time::milliseconds( time.getTime().getMilliSecond() ); boost::posix_time::ptime result( boostDate, boostTime ); return result; } OFDateTime GetLowerDateTime( const OFDateTime& time1, const OFDateTime& time2 ) { OFDateTime result = time1; if ( ( time2.getDate() < time1.getDate() ) || ( ( time2.getDate() == time1.getDate() ) && ( time2.getTime() < time1.getTime() ) ) ) { result = time2; } return result; } OFDateTime GetUpperDateTime( const OFDateTime& time1, const OFDateTime& time2 ) { OFDateTime result = time1; if ( ( time2.getDate() > time1.getDate() ) || ( ( time2.getDate() == time1.getDate() ) && ( time2.getTime() > time1.getTime() ) ) ) { result = time2; } return result; } double ComputeMiliSecDuration( const OFDateTime& start, const OFDateTime& stop ) { const boost::posix_time::ptime startTime = ConvertOFDateTimeToPTime( start ); const boost::posix_time::ptime stopTime = ConvertOFDateTimeToPTime( stop ); ::boost::posix_time::time_duration duration = stopTime - startTime; return duration.total_milliseconds(); } bool mitk::ITKDICOMSeriesReaderHelper::ExtractDateTimeBoundsAndTriggerOfTimeStep( const StringContainer& filenamesOfTimeStep, DateTimeBounds& bounds, TimeBounds& triggerBounds) { DICOMGDCMTagScanner::Pointer filescanner = DICOMGDCMTagScanner::New(); filescanner->SetInputFiles(filenamesOfTimeStep); filescanner->AddTag(AcquisitionDateTag); filescanner->AddTag(AcquisitionTimeTag); filescanner->AddTag(TriggerTimeTag); filescanner->Scan(); const DICOMDatasetAccessingImageFrameList frameList = filescanner->GetFrameInfoList(); bool result = false; bool firstAq = true; bool firstTr = true; triggerBounds = TimeBounds(0.0); - for (DICOMDatasetAccessingImageFrameList::const_iterator pos = frameList.cbegin(); pos != frameList.cend(); ++pos) + for (auto pos = frameList.cbegin(); pos != frameList.cend(); ++pos) { const std::string aqDateStr = (*pos)->GetTagValueAsString(AcquisitionDateTag).value; const std::string aqTimeStr = (*pos)->GetTagValueAsString(AcquisitionTimeTag).value; const std::string triggerTimeStr = (*pos)->GetTagValueAsString(TriggerTimeTag).value; OFDateTime aqDateTime; const bool convertAqResult = ConvertDICOMDateTimeString(aqDateStr, aqTimeStr, aqDateTime); OFBool convertTriggerResult; mitk::ScalarType triggerTime = OFStandard::atof(triggerTimeStr.c_str(), &convertTriggerResult); if (convertAqResult) { if (firstAq) { bounds[0] = aqDateTime; bounds[1] = aqDateTime; firstAq = false; } else { bounds[0] = GetLowerDateTime(bounds[0], aqDateTime); bounds[1] = GetUpperDateTime(bounds[1], aqDateTime); } result = true; } if (convertTriggerResult) { if (firstTr) { triggerBounds[0] = triggerTime; triggerBounds[1] = triggerTime; firstTr = false; } else { triggerBounds[0] = std::min(triggerBounds[0], triggerTime); triggerBounds[1] = std::max(triggerBounds[1], triggerTime); } result = true; } } return result; }; bool mitk::ITKDICOMSeriesReaderHelper::ExtractTimeBoundsOfTimeStep( const StringContainer& filenamesOfTimeStep, TimeBounds& bounds, const OFDateTime& baselineDateTime ) { DateTimeBounds aqDTBounds; TimeBounds triggerBounds; bool result = ExtractDateTimeBoundsAndTriggerOfTimeStep(filenamesOfTimeStep, aqDTBounds, triggerBounds); mitk::ScalarType lowerBound = ComputeMiliSecDuration( baselineDateTime, aqDTBounds[0] ); mitk::ScalarType upperBound = ComputeMiliSecDuration( baselineDateTime, aqDTBounds[1] ); if ( lowerBound < mitk::eps || upperBound < mitk::eps ) { lowerBound = triggerBounds[0]; upperBound = triggerBounds[1]; } bounds[0] = lowerBound; bounds[1] = upperBound; return result; }; mitk::ITKDICOMSeriesReaderHelper::TimeBoundsList mitk::ITKDICOMSeriesReaderHelper::ExtractTimeBoundsOfTimeSteps( const StringContainerList& filenamesOfTimeSteps ) { TimeBoundsList result; OFDateTime baseLine; // extract the timebounds DateTimeBounds baselineDateTimeBounds; TimeBounds triggerBounds; - StringContainerList::const_iterator pos = filenamesOfTimeSteps.cbegin(); + auto pos = filenamesOfTimeSteps.cbegin(); ExtractDateTimeBoundsAndTriggerOfTimeStep(*pos, baselineDateTimeBounds, triggerBounds); baseLine = baselineDateTimeBounds[0]; // timebounds for baseline is 0 TimeBounds bounds( 0.0 ); result.push_back( bounds ); // iterate over the remaining timesteps for ( ++pos; pos != filenamesOfTimeSteps.cend(); ++pos ) { TimeBounds bounds( 0.0 ); TimeBounds dateTimeBounds; // extract the timebounds relative to the baseline if ( ExtractTimeBoundsOfTimeStep( *pos, dateTimeBounds, baseLine ) ) { bounds[0] = dateTimeBounds[0]; bounds[1] = dateTimeBounds[1]; } result.push_back( bounds ); } return result; }; mitk::TimeGeometry::Pointer mitk::ITKDICOMSeriesReaderHelper::GenerateTimeGeometry( const BaseGeometry* templateGeometry, const TimeBoundsList& boundsList ) { TimeGeometry::Pointer timeGeometry; double check = 0.0; const auto boundListSize = boundsList.size(); for ( std::size_t pos = 0; pos < boundListSize; ++pos ) { check += boundsList[pos][0]; check += boundsList[pos][1]; } if ( check < mitk::eps ) { // if all bounds are zero we assume that the bounds could not be correctly determined // and as a fallback generate a time geometry in the old mitk style ProportionalTimeGeometry::Pointer newTimeGeometry = ProportionalTimeGeometry::New(); newTimeGeometry->Initialize( templateGeometry, boundListSize ); timeGeometry = newTimeGeometry.GetPointer(); } else { ArbitraryTimeGeometry::Pointer newTimeGeometry = ArbitraryTimeGeometry::New(); newTimeGeometry->ClearAllGeometries(); newTimeGeometry->ReserveSpaceForGeometries( boundListSize ); for ( std::size_t pos = 0; pos < boundListSize; ++pos ) { TimeBounds bounds = boundsList[pos]; if ( pos + 1 < boundListSize ) { //Currently we do not explicitly support "gaps" in the time coverage //thus we set the max time bound of a time step to the min time bound //of its successor. bounds[1] = boundsList[pos + 1][0]; } newTimeGeometry->AppendNewTimeStepClone(templateGeometry, bounds[0], bounds[1]); } timeGeometry = newTimeGeometry.GetPointer(); } return timeGeometry; }; diff --git a/Modules/DICOMReader/src/mitkThreeDnTDICOMSeriesReader.cpp b/Modules/DICOMReader/src/mitkThreeDnTDICOMSeriesReader.cpp index eedf682eb0..00008c9dcd 100644 --- a/Modules/DICOMReader/src/mitkThreeDnTDICOMSeriesReader.cpp +++ b/Modules/DICOMReader/src/mitkThreeDnTDICOMSeriesReader.cpp @@ -1,264 +1,264 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkThreeDnTDICOMSeriesReader.h" #include "mitkITKDICOMSeriesReaderHelper.h" mitk::ThreeDnTDICOMSeriesReader ::ThreeDnTDICOMSeriesReader(unsigned int decimalPlacesForOrientation) :DICOMITKSeriesGDCMReader(decimalPlacesForOrientation) ,m_Group3DandT(true) { } mitk::ThreeDnTDICOMSeriesReader ::ThreeDnTDICOMSeriesReader(const ThreeDnTDICOMSeriesReader& other ) :DICOMITKSeriesGDCMReader(other) ,m_Group3DandT(true) { } mitk::ThreeDnTDICOMSeriesReader ::~ThreeDnTDICOMSeriesReader() { } mitk::ThreeDnTDICOMSeriesReader& mitk::ThreeDnTDICOMSeriesReader ::operator=(const ThreeDnTDICOMSeriesReader& other) { if (this != &other) { DICOMITKSeriesGDCMReader::operator=(other); this->m_Group3DandT = other.m_Group3DandT; } return *this; } bool mitk::ThreeDnTDICOMSeriesReader ::operator==(const DICOMFileReader& other) const { - if (const Self* otherSelf = dynamic_cast(&other)) + if (const auto* otherSelf = dynamic_cast(&other)) { return DICOMITKSeriesGDCMReader::operator==(other) && this->m_Group3DandT == otherSelf->m_Group3DandT; } else { return false; } } void mitk::ThreeDnTDICOMSeriesReader ::SetGroup3DandT(bool on) { m_Group3DandT = on; } bool mitk::ThreeDnTDICOMSeriesReader ::GetGroup3DandT() const { return m_Group3DandT; } mitk::DICOMITKSeriesGDCMReader::SortingBlockList mitk::ThreeDnTDICOMSeriesReader ::Condense3DBlocks(SortingBlockList& resultOf3DGrouping) { if (!m_Group3DandT) { return resultOf3DGrouping; // don't work if nobody asks us to } SortingBlockList remainingBlocks = resultOf3DGrouping; SortingBlockList non3DnTBlocks; SortingBlockList true3DnTBlocks; std::vector true3DnTBlocksTimeStepCount; // we should describe our need for this tag as needed via a function // (however, we currently know that the superclass will always need this tag) const DICOMTag tagImagePositionPatient(0x0020, 0x0032); while (!remainingBlocks.empty()) { // new block to fill up const DICOMDatasetAccessingImageFrameList& firstBlock = remainingBlocks.front(); DICOMDatasetAccessingImageFrameList current3DnTBlock = firstBlock; int current3DnTBlockNumberOfTimeSteps = 1; // get block characteristics of first block const unsigned int currentBlockNumberOfSlices = firstBlock.size(); const std::string currentBlockFirstOrigin = firstBlock.front()->GetTagValueAsString( tagImagePositionPatient ).value; const std::string currentBlockLastOrigin = firstBlock.back()->GetTagValueAsString( tagImagePositionPatient ).value; remainingBlocks.erase( remainingBlocks.begin() ); // compare all other blocks against the first one for (auto otherBlockIter = remainingBlocks.begin(); otherBlockIter != remainingBlocks.cend(); /*++otherBlockIter*/) // <-- inside loop { // get block characteristics from first block const DICOMDatasetAccessingImageFrameList otherBlock = *otherBlockIter; const unsigned int otherBlockNumberOfSlices = otherBlock.size(); const std::string otherBlockFirstOrigin = otherBlock.front()->GetTagValueAsString( tagImagePositionPatient ).value; const std::string otherBlockLastOrigin = otherBlock.back()->GetTagValueAsString( tagImagePositionPatient ).value; // add matching blocks to current3DnTBlock // keep other blocks for later if ( otherBlockNumberOfSlices == currentBlockNumberOfSlices && otherBlockFirstOrigin == currentBlockFirstOrigin && otherBlockLastOrigin == currentBlockLastOrigin ) { // matching block ++current3DnTBlockNumberOfTimeSteps; current3DnTBlock.insert( current3DnTBlock.end(), otherBlock.begin(), otherBlock.end() ); // append // remove this block from remainingBlocks otherBlockIter = remainingBlocks.erase(otherBlockIter); // make sure iterator otherBlockIter is valid afterwards } else { ++otherBlockIter; } } // in any case, we now now all about the first block of our list ... // ... and we wither call it 3D o 3D+t if (current3DnTBlockNumberOfTimeSteps > 1) { true3DnTBlocks.push_back(current3DnTBlock); true3DnTBlocksTimeStepCount.push_back(current3DnTBlockNumberOfTimeSteps); } else { non3DnTBlocks.push_back(current3DnTBlock); } } // create output for real 3D+t blocks (other outputs will be created by superclass) // set 3D+t flag on output block this->SetNumberOfOutputs( true3DnTBlocks.size() ); unsigned int o = 0; for (auto blockIter = true3DnTBlocks.cbegin(); blockIter != true3DnTBlocks.cend(); ++o, ++blockIter) { // bad copy&paste code from DICOMITKSeriesGDCMReader, should be handled in a better way DICOMDatasetAccessingImageFrameList gdcmFrameInfoList = *blockIter; assert(!gdcmFrameInfoList.empty()); // reverse frames if necessary // update tilt information from absolute last sorting const DICOMDatasetList datasetList = ConvertToDICOMDatasetList( gdcmFrameInfoList ); m_NormalDirectionConsistencySorter->SetInput( datasetList ); m_NormalDirectionConsistencySorter->Sort(); const DICOMDatasetAccessingImageFrameList sortedGdcmInfoFrameList = ConvertToDICOMDatasetAccessingImageFrameList( m_NormalDirectionConsistencySorter->GetOutput(0) ); const GantryTiltInformation& tiltInfo = m_NormalDirectionConsistencySorter->GetTiltInformation(); // set frame list for current block const DICOMImageFrameList frameList = ConvertToDICOMImageFrameList( sortedGdcmInfoFrameList ); assert(!frameList.empty()); DICOMImageBlockDescriptor block; block.SetTagCache( this->GetTagCache() ); // important: this must be before SetImageFrameList(), because SetImageFrameList will trigger reading of lots of interesting tags! block.SetAdditionalTagsOfInterest(GetAdditionalTagsOfInterest()); block.SetTagLookupTableToPropertyFunctor(GetTagLookupTableToPropertyFunctor()); block.SetImageFrameList( frameList ); block.SetTiltInformation( tiltInfo ); block.SetFlag("3D+t", true); block.SetIntProperty("timesteps", true3DnTBlocksTimeStepCount[o]); MITK_DEBUG << "Found " << true3DnTBlocksTimeStepCount[o] << " timesteps"; this->SetOutput( o, block ); } return non3DnTBlocks; } bool mitk::ThreeDnTDICOMSeriesReader ::LoadImages() { bool success = true; unsigned int numberOfOutputs = this->GetNumberOfOutputs(); for (unsigned int o = 0; o < numberOfOutputs; ++o) { const DICOMImageBlockDescriptor& block = this->InternalGetOutput(o); if (block.GetFlag("3D+t", false)) { success &= this->LoadMitkImageForOutput(o); } else { success &= DICOMITKSeriesGDCMReader::LoadMitkImageForOutput(o); // let superclass handle non-3D+t } } return success; } bool mitk::ThreeDnTDICOMSeriesReader ::LoadMitkImageForImageBlockDescriptor(DICOMImageBlockDescriptor& block) const { PushLocale(); const DICOMImageFrameList& frames = block.GetImageFrameList(); const GantryTiltInformation tiltInfo = block.GetTiltInformation(); const bool hasTilt = tiltInfo.IsRegularGantryTilt(); const int numberOfTimesteps = block.GetNumberOfTimeSteps(); if (numberOfTimesteps == 1) { return DICOMITKSeriesGDCMReader::LoadMitkImageForImageBlockDescriptor(block); } const int numberOfFramesPerTimestep = block.GetNumberOfFramesPerTimeStep(); ITKDICOMSeriesReaderHelper::StringContainerList filenamesPerTimestep; for (int timeStep = 0; timeStepFilename ); } filenamesPerTimestep.push_back( filenamesOfThisTimeStep ); } mitk::ITKDICOMSeriesReaderHelper helper; mitk::Image::Pointer mitkImage = helper.Load3DnT( filenamesPerTimestep, m_FixTiltByShearing && hasTilt, tiltInfo ); block.SetMitkImage( mitkImage ); PopLocale(); return true; } diff --git a/Modules/DataTypesExt/src/mitkAffineBaseDataInteractor3D.cpp b/Modules/DataTypesExt/src/mitkAffineBaseDataInteractor3D.cpp index 4d2295eded..8de28a66de 100644 --- a/Modules/DataTypesExt/src/mitkAffineBaseDataInteractor3D.cpp +++ b/Modules/DataTypesExt/src/mitkAffineBaseDataInteractor3D.cpp @@ -1,508 +1,508 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkAffineBaseDataInteractor3D.h" #include #include #include #include #include #include #include #include #include #include #include #include // Properties to allow the user to interact with the base data const char *translationStepSizePropertyName = "AffineBaseDataInteractor3D.Translation Step Size"; const char *selectedColorPropertyName = "AffineBaseDataInteractor3D.Selected Color"; const char *deselectedColorPropertyName = "AffineBaseDataInteractor3D.Deselected Color"; const char *priorPropertyName = "AffineBaseDataInteractor3D.Prior Color"; const char *rotationStepSizePropertyName = "AffineBaseDataInteractor3D.Rotation Step Size"; const char *scaleStepSizePropertyName = "AffineBaseDataInteractor3D.Scale Step Size"; const char *anchorPointX = "AffineBaseDataInteractor3D.Anchor Point X"; const char *anchorPointY = "AffineBaseDataInteractor3D.Anchor Point Y"; const char *anchorPointZ = "AffineBaseDataInteractor3D.Anchor Point Z"; mitk::AffineBaseDataInteractor3D::AffineBaseDataInteractor3D() { m_OriginalGeometry = mitk::Geometry3D::New(); } mitk::AffineBaseDataInteractor3D::~AffineBaseDataInteractor3D() { this->RestoreNodeProperties(); } void mitk::AffineBaseDataInteractor3D::ConnectActionsAndFunctions() { // **Conditions** that can be used in the state machine, to ensure that certain conditions are met, before actually // executing an action CONNECT_CONDITION("isOverObject", CheckOverObject); // **Function** in the statmachine patterns also referred to as **Actions** CONNECT_FUNCTION("selectObject", SelectObject); CONNECT_FUNCTION("deselectObject", DeselectObject); CONNECT_FUNCTION("initTranslate", InitTranslate); CONNECT_FUNCTION("initRotate", InitRotate); CONNECT_FUNCTION("translateObject", TranslateObject); CONNECT_FUNCTION("rotateObject", RotateObject); CONNECT_FUNCTION("scaleObject", ScaleObject); CONNECT_FUNCTION("translateUpKey", TranslateUpKey); CONNECT_FUNCTION("translateDownKey", TranslateDownKey); CONNECT_FUNCTION("translateLeftKey", TranslateLeftKey); CONNECT_FUNCTION("translateRightKey", TranslateRightKey); CONNECT_FUNCTION("translateUpModifierKey", TranslateUpModifierKey); CONNECT_FUNCTION("translateDownModifierKey", TranslateDownModifierKey); CONNECT_FUNCTION("scaleDownKey", ScaleDownKey); CONNECT_FUNCTION("scaleUpKey", ScaleUpKey); CONNECT_FUNCTION("rotateUpKey", RotateUpKey); CONNECT_FUNCTION("rotateDownKey", RotateDownKey); CONNECT_FUNCTION("rotateLeftKey", RotateLeftKey); CONNECT_FUNCTION("rotateRightKey", RotateRightKey); CONNECT_FUNCTION("rotateUpModifierKey", RotateUpModifierKey); CONNECT_FUNCTION("rotateDownModifierKey", RotateDownModifierKey); } void mitk::AffineBaseDataInteractor3D::TranslateUpKey(StateMachineAction *, InteractionEvent *interactionEvent) { float stepSize = 1.0f; this->GetDataNode()->GetFloatProperty(translationStepSizePropertyName, stepSize); mitk::Vector3D movementVector; movementVector.Fill(0.0); movementVector.SetElement(2, stepSize); this->TranslateGeometry(movementVector, this->GetUpdatedTimeGeometry(interactionEvent)); } void mitk::AffineBaseDataInteractor3D::TranslateDownKey(mitk::StateMachineAction *, mitk::InteractionEvent *interactionEvent) { float stepSize = 1.0f; this->GetDataNode()->GetFloatProperty(translationStepSizePropertyName, stepSize); mitk::Vector3D movementVector; movementVector.Fill(0.0); movementVector.SetElement(2, -stepSize); this->TranslateGeometry(movementVector, this->GetUpdatedTimeGeometry(interactionEvent)); } void mitk::AffineBaseDataInteractor3D::TranslateLeftKey(mitk::StateMachineAction *, mitk::InteractionEvent *interactionEvent) { float stepSize = 1.0f; this->GetDataNode()->GetFloatProperty(translationStepSizePropertyName, stepSize); mitk::Vector3D movementVector; movementVector.Fill(0.0); movementVector.SetElement(0, -stepSize); this->TranslateGeometry(movementVector, this->GetUpdatedTimeGeometry(interactionEvent)); } void mitk::AffineBaseDataInteractor3D::TranslateRightKey(mitk::StateMachineAction *, mitk::InteractionEvent *interactionEvent) { float stepSize = 1.0f; this->GetDataNode()->GetFloatProperty(translationStepSizePropertyName, stepSize); mitk::Vector3D movementVector; movementVector.Fill(0.0); movementVector.SetElement(0, stepSize); this->TranslateGeometry(movementVector, this->GetUpdatedTimeGeometry(interactionEvent)); } void mitk::AffineBaseDataInteractor3D::TranslateUpModifierKey(mitk::StateMachineAction *, mitk::InteractionEvent *interactionEvent) { float stepSize = 1.0f; this->GetDataNode()->GetFloatProperty(translationStepSizePropertyName, stepSize); mitk::Vector3D movementVector; movementVector.Fill(0.0); movementVector.SetElement(1, stepSize); this->TranslateGeometry(movementVector, this->GetUpdatedTimeGeometry(interactionEvent)); } void mitk::AffineBaseDataInteractor3D::TranslateDownModifierKey(mitk::StateMachineAction *, mitk::InteractionEvent *interactionEvent) { float stepSize = 1.0f; this->GetDataNode()->GetFloatProperty(translationStepSizePropertyName, stepSize); mitk::Vector3D movementVector; movementVector.Fill(0.0); movementVector.SetElement(1, -stepSize); this->TranslateGeometry(movementVector, this->GetUpdatedTimeGeometry(interactionEvent)); } void mitk::AffineBaseDataInteractor3D::RotateUpKey(mitk::StateMachineAction *, mitk::InteractionEvent *interactionEvent) { float stepSize = 1.0f; this->GetDataNode()->GetFloatProperty(rotationStepSizePropertyName, stepSize); this->RotateGeometry(-stepSize, 0, this->GetUpdatedTimeGeometry(interactionEvent)); } void mitk::AffineBaseDataInteractor3D::RotateDownKey(mitk::StateMachineAction *, mitk::InteractionEvent *interactionEvent) { float stepSize = 1.0f; this->GetDataNode()->GetFloatProperty(rotationStepSizePropertyName, stepSize); this->RotateGeometry(stepSize, 0, this->GetUpdatedTimeGeometry(interactionEvent)); return; } void mitk::AffineBaseDataInteractor3D::RotateLeftKey(mitk::StateMachineAction *, mitk::InteractionEvent *interactionEvent) { float stepSize = 1.0f; this->GetDataNode()->GetFloatProperty(rotationStepSizePropertyName, stepSize); this->RotateGeometry(-stepSize, 2, this->GetUpdatedTimeGeometry(interactionEvent)); } void mitk::AffineBaseDataInteractor3D::RotateRightKey(mitk::StateMachineAction *, mitk::InteractionEvent *interactionEvent) { float stepSize = 1.0f; this->GetDataNode()->GetFloatProperty(rotationStepSizePropertyName, stepSize); this->RotateGeometry(stepSize, 2, this->GetUpdatedTimeGeometry(interactionEvent)); } void mitk::AffineBaseDataInteractor3D::RotateUpModifierKey(mitk::StateMachineAction *, mitk::InteractionEvent *interactionEvent) { float stepSize = 1.0f; this->GetDataNode()->GetFloatProperty(rotationStepSizePropertyName, stepSize); this->RotateGeometry(stepSize, 1, this->GetUpdatedTimeGeometry(interactionEvent)); } void mitk::AffineBaseDataInteractor3D::RotateDownModifierKey(mitk::StateMachineAction *, mitk::InteractionEvent *interactionEvent) { float stepSize = 1.0f; this->GetDataNode()->GetFloatProperty(rotationStepSizePropertyName, stepSize); this->RotateGeometry(-stepSize, 1, this->GetUpdatedTimeGeometry(interactionEvent)); } void mitk::AffineBaseDataInteractor3D::ScaleUpKey(mitk::StateMachineAction *, mitk::InteractionEvent *interactionEvent) { float stepSize = 0.1f; this->GetDataNode()->GetFloatProperty(scaleStepSizePropertyName, stepSize); mitk::Point3D newScale; newScale.Fill(stepSize); this->ScaleGeometry(newScale, this->GetUpdatedTimeGeometry(interactionEvent)); } void mitk::AffineBaseDataInteractor3D::ScaleDownKey(mitk::StateMachineAction *, mitk::InteractionEvent *interactionEvent) { float stepSize = 0.1f; this->GetDataNode()->GetFloatProperty(scaleStepSizePropertyName, stepSize); mitk::Point3D newScale; newScale.Fill(-stepSize); this->ScaleGeometry(newScale, this->GetUpdatedTimeGeometry(interactionEvent)); } void mitk::AffineBaseDataInteractor3D::ScaleGeometry(mitk::Point3D newScale, mitk::BaseGeometry *geometry) { mitk::Point3D anchorPoint; float pointX = 0.0f; float pointY = 0.0f; float pointZ = 0.0f; anchorPoint.Fill(0.0); this->GetDataNode()->GetFloatProperty(anchorPointX, pointX); this->GetDataNode()->GetFloatProperty(anchorPointY, pointY); this->GetDataNode()->GetFloatProperty(anchorPointZ, pointZ); anchorPoint[0] = pointX; anchorPoint[1] = pointY; anchorPoint[2] = pointZ; - ScaleOperation *doOp = new mitk::ScaleOperation(OpSCALE, newScale, anchorPoint); + auto *doOp = new mitk::ScaleOperation(OpSCALE, newScale, anchorPoint); geometry->ExecuteOperation(doOp); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } void mitk::AffineBaseDataInteractor3D::RotateGeometry(mitk::ScalarType angle, int rotationaxis, mitk::BaseGeometry *geometry) { mitk::Vector3D rotationAxis = geometry->GetAxisVector(rotationaxis); float pointX = 0.0f; float pointY = 0.0f; float pointZ = 0.0f; mitk::Point3D pointOfRotation; pointOfRotation.Fill(0.0); this->GetDataNode()->GetFloatProperty(anchorPointX, pointX); this->GetDataNode()->GetFloatProperty(anchorPointY, pointY); this->GetDataNode()->GetFloatProperty(anchorPointZ, pointZ); pointOfRotation[0] = pointX; pointOfRotation[1] = pointY; pointOfRotation[2] = pointZ; - mitk::RotationOperation *doOp = new mitk::RotationOperation(OpROTATE, pointOfRotation, rotationAxis, angle); + auto *doOp = new mitk::RotationOperation(OpROTATE, pointOfRotation, rotationAxis, angle); geometry->ExecuteOperation(doOp); delete doOp; mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } void mitk::AffineBaseDataInteractor3D::TranslateGeometry(mitk::Vector3D translate, mitk::BaseGeometry *geometry) { geometry->Translate(translate); this->GetDataNode()->Modified(); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } mitk::BaseGeometry *mitk::AffineBaseDataInteractor3D::GetUpdatedTimeGeometry(mitk::InteractionEvent *interactionEvent) { // Get the correct time geometry to support 3D + t int timeStep = interactionEvent->GetSender()->GetTimeStep(this->GetDataNode()->GetData()); BaseGeometry *geometry = this->GetDataNode()->GetData()->GetUpdatedTimeGeometry()->GetGeometryForTimeStep(timeStep); if (geometry == nullptr) MITK_ERROR << "Geometry is nullptr. Cannot modify it."; return geometry; } void mitk::AffineBaseDataInteractor3D::DataNodeChanged() { mitk::DataNode::Pointer newInputNode = this->GetDataNode(); if (newInputNode.IsNotNull()) { // add default properties newInputNode->AddProperty(selectedColorPropertyName, mitk::ColorProperty::New(0.0, 1.0, 0.0)); newInputNode->AddProperty(deselectedColorPropertyName, mitk::ColorProperty::New(0.0, 0.0, 1.0)); newInputNode->AddProperty(translationStepSizePropertyName, mitk::FloatProperty::New(1.0f)); newInputNode->AddProperty(rotationStepSizePropertyName, mitk::FloatProperty::New(1.0f)); newInputNode->AddProperty(scaleStepSizePropertyName, mitk::FloatProperty::New(0.1f)); // save the previous color of the node, in order to restore it after the interactor is destroyed mitk::ColorProperty::Pointer priorColor = dynamic_cast(newInputNode->GetProperty("color")); if (priorColor.IsNotNull()) { mitk::ColorProperty::Pointer tmpCopyOfPriorColor = mitk::ColorProperty::New(); tmpCopyOfPriorColor->SetColor(priorColor->GetColor()); newInputNode->AddProperty(priorPropertyName, tmpCopyOfPriorColor); } newInputNode->SetColor(0.0, 0.0, 1.0); } mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } void mitk::AffineBaseDataInteractor3D::SetDataNode(DataNode *node) { this->RestoreNodeProperties(); // if there was another node set, restore it's color DataInteractor::SetDataNode(node); } bool mitk::AffineBaseDataInteractor3D::CheckOverObject(const InteractionEvent *interactionEvent) { - const InteractionPositionEvent *positionEvent = dynamic_cast(interactionEvent); + const auto *positionEvent = dynamic_cast(interactionEvent); if (positionEvent == nullptr) return false; Point3D currentWorldPoint; if (interactionEvent->GetSender()->PickObject(positionEvent->GetPointerPositionOnScreen(), currentWorldPoint) == this->GetDataNode()) return true; return false; } void mitk::AffineBaseDataInteractor3D::SelectObject(StateMachineAction *, InteractionEvent *interactionEvent) { DataNode::Pointer node = this->GetDataNode(); if (node.IsNull()) return; mitk::ColorProperty::Pointer selectedColor = dynamic_cast(node->GetProperty(selectedColorPropertyName)); if (selectedColor.IsNotNull()) { node->GetPropertyList()->SetProperty("color", selectedColor); } interactionEvent->GetSender()->GetRenderingManager()->RequestUpdateAll(); return; } void mitk::AffineBaseDataInteractor3D::DeselectObject(StateMachineAction *, InteractionEvent *interactionEvent) { DataNode::Pointer node = this->GetDataNode(); if (node.IsNull()) return; mitk::ColorProperty::Pointer selectedColor = dynamic_cast(node->GetProperty(deselectedColorPropertyName)); if (selectedColor.IsNotNull()) { node->GetPropertyList()->SetProperty("color", selectedColor); } interactionEvent->GetSender()->GetRenderingManager()->RequestUpdateAll(); return; } void mitk::AffineBaseDataInteractor3D::InitTranslate(StateMachineAction *, InteractionEvent *interactionEvent) { InitMembers(interactionEvent); } void mitk::AffineBaseDataInteractor3D::InitRotate(StateMachineAction *, InteractionEvent *interactionEvent) { InitMembers(interactionEvent); } bool mitk::AffineBaseDataInteractor3D::InitMembers(InteractionEvent *interactionEvent) { - InteractionPositionEvent *positionEvent = dynamic_cast(interactionEvent); + auto *positionEvent = dynamic_cast(interactionEvent); if (positionEvent == nullptr) return false; m_InitialPickedDisplayPoint = positionEvent->GetPointerPositionOnScreen(); m_InitialPickedWorldPoint = positionEvent->GetPositionInWorld(); // Get the timestep to also support 3D+t int timeStep = 0; if ((interactionEvent->GetSender()) != nullptr) timeStep = interactionEvent->GetSender()->GetTimeStep(this->GetDataNode()->GetData()); // Make deep copy of current Geometry3D of the plane this->GetDataNode()->GetData()->UpdateOutputInformation(); // make sure that the Geometry is up-to-date m_OriginalGeometry = static_cast(this->GetDataNode()->GetData()->GetGeometry(timeStep)->Clone().GetPointer()); return true; } void mitk::AffineBaseDataInteractor3D::TranslateObject(StateMachineAction *, InteractionEvent *interactionEvent) { - InteractionPositionEvent *positionEvent = dynamic_cast(interactionEvent); + auto *positionEvent = dynamic_cast(interactionEvent); if (positionEvent == nullptr) return; Point3D currentPickedPoint = positionEvent->GetPositionInWorld(); Vector3D interactionMove; interactionMove[0] = currentPickedPoint[0] - m_InitialPickedWorldPoint[0]; interactionMove[1] = currentPickedPoint[1] - m_InitialPickedWorldPoint[1]; interactionMove[2] = currentPickedPoint[2] - m_InitialPickedWorldPoint[2]; int timeStep = interactionEvent->GetSender()->GetTimeStep(this->GetDataNode()->GetData()); mitk::BaseGeometry::Pointer geometry = this->GetDataNode()->GetData()->GetUpdatedTimeGeometry()->GetGeometryForTimeStep(timeStep); geometry->SetOrigin(m_OriginalGeometry->GetOrigin()); this->TranslateGeometry(interactionMove, this->GetUpdatedTimeGeometry(interactionEvent)); return; } void mitk::AffineBaseDataInteractor3D::RotateObject(StateMachineAction *, InteractionEvent *interactionEvent) { - InteractionPositionEvent *positionEvent = dynamic_cast(interactionEvent); + auto *positionEvent = dynamic_cast(interactionEvent); if (positionEvent == nullptr) return; Point2D currentPickedDisplayPoint = positionEvent->GetPointerPositionOnScreen(); Point3D currentWorldPoint = positionEvent->GetPositionInWorld(); vtkCamera *camera = nullptr; vtkRenderer *currentVtkRenderer = nullptr; if ((interactionEvent->GetSender()) != nullptr) { camera = interactionEvent->GetSender()->GetVtkRenderer()->GetActiveCamera(); currentVtkRenderer = interactionEvent->GetSender()->GetVtkRenderer(); } if (camera && currentVtkRenderer) { double vpn[3]; camera->GetViewPlaneNormal(vpn); Vector3D viewPlaneNormal; viewPlaneNormal[0] = vpn[0]; viewPlaneNormal[1] = vpn[1]; viewPlaneNormal[2] = vpn[2]; Vector3D interactionMove; interactionMove[0] = currentWorldPoint[0] - m_InitialPickedWorldPoint[0]; interactionMove[1] = currentWorldPoint[1] - m_InitialPickedWorldPoint[1]; interactionMove[2] = currentWorldPoint[2] - m_InitialPickedWorldPoint[2]; if (interactionMove[0] == 0 && interactionMove[1] == 0 && interactionMove[2] == 0) return; Vector3D rotationAxis = itk::CrossProduct(viewPlaneNormal, interactionMove); rotationAxis.Normalize(); int *size = currentVtkRenderer->GetSize(); double l2 = (currentPickedDisplayPoint[0] - m_InitialPickedDisplayPoint[0]) * (currentPickedDisplayPoint[0] - m_InitialPickedDisplayPoint[0]) + (currentPickedDisplayPoint[1] - m_InitialPickedDisplayPoint[1]) * (currentPickedDisplayPoint[1] - m_InitialPickedDisplayPoint[1]); double rotationAngle = 360.0 * sqrt(l2 / (size[0] * size[0] + size[1] * size[1])); // Use center of data bounding box as center of rotation Point3D rotationCenter = m_OriginalGeometry->GetCenter(); int timeStep = 0; if ((interactionEvent->GetSender()) != nullptr) timeStep = interactionEvent->GetSender()->GetTimeStep(this->GetDataNode()->GetData()); // Reset current Geometry3D to original state (pre-interaction) and // apply rotation RotationOperation op(OpROTATE, rotationCenter, rotationAxis, rotationAngle); Geometry3D::Pointer newGeometry = static_cast(m_OriginalGeometry->Clone().GetPointer()); newGeometry->ExecuteOperation(&op); mitk::TimeGeometry::Pointer timeGeometry = this->GetDataNode()->GetData()->GetTimeGeometry(); if (timeGeometry.IsNotNull()) timeGeometry->SetTimeStepGeometry(newGeometry, timeStep); interactionEvent->GetSender()->GetRenderingManager()->RequestUpdateAll(); } } void mitk::AffineBaseDataInteractor3D::ScaleObject(StateMachineAction *, InteractionEvent * /*interactionEvent*/) { return; } void mitk::AffineBaseDataInteractor3D::RestoreNodeProperties() { mitk::DataNode::Pointer inputNode = this->GetDataNode(); if (inputNode.IsNull()) return; mitk::ColorProperty::Pointer color = dynamic_cast(inputNode->GetProperty(priorPropertyName)); if (color.IsNotNull()) { inputNode->GetPropertyList()->SetProperty("color", color); } inputNode->GetPropertyList()->DeleteProperty(selectedColorPropertyName); inputNode->GetPropertyList()->DeleteProperty(deselectedColorPropertyName); inputNode->GetPropertyList()->DeleteProperty(priorPropertyName); inputNode->GetPropertyList()->DeleteProperty(translationStepSizePropertyName); inputNode->GetPropertyList()->DeleteProperty(rotationStepSizePropertyName); inputNode->GetPropertyList()->DeleteProperty(scaleStepSizePropertyName); // update rendering mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } diff --git a/Modules/DataTypesExt/src/mitkAffineImageCropperInteractor.cpp b/Modules/DataTypesExt/src/mitkAffineImageCropperInteractor.cpp index e8330952bb..e60562a461 100644 --- a/Modules/DataTypesExt/src/mitkAffineImageCropperInteractor.cpp +++ b/Modules/DataTypesExt/src/mitkAffineImageCropperInteractor.cpp @@ -1,264 +1,264 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkAffineImageCropperInteractor.h" #include "mitkInteractionConst.h" #include "mitkInteractionPositionEvent.h" #include "mitkRotationOperation.h" #include "mitkSurface.h" #include "mitkVtkMapper.h" #include #include #include #include #include #include #include #include #include #include mitk::AffineImageCropperInteractor::AffineImageCropperInteractor() { m_OriginalGeometry = Geometry3D::New(); } mitk::AffineImageCropperInteractor::~AffineImageCropperInteractor() { } void mitk::AffineImageCropperInteractor::ConnectActionsAndFunctions() { // **Conditions** that can be used in the state machine, to ensure that certain conditions are met, before actually // executing an action CONNECT_CONDITION("isOverObject", CheckOverObject); // **Function** in the statmachine patterns also referred to as **Actions** CONNECT_FUNCTION("selectObject", SelectObject); CONNECT_FUNCTION("deselectObject", DeselectObject); CONNECT_FUNCTION("initTranslate", InitTranslate); CONNECT_FUNCTION("initRotate", InitRotate); CONNECT_FUNCTION("initDeformation", InitDeformation); CONNECT_FUNCTION("translateObject", TranslateObject); CONNECT_FUNCTION("rotateObject", RotateObject); CONNECT_FUNCTION("deformObject", DeformObject); CONNECT_FUNCTION("scaleRadius", ScaleRadius); } void mitk::AffineImageCropperInteractor::DataNodeChanged() { } bool mitk::AffineImageCropperInteractor::CheckOverObject(const InteractionEvent *interactionEvent) { mitk::DataNode::Pointer dn = this->GetDataNode(); if (dn.IsNull()) return false; - const InteractionPositionEvent *positionEvent = dynamic_cast(interactionEvent); + const auto *positionEvent = dynamic_cast(interactionEvent); Point3D currentPickedPoint = positionEvent->GetPositionInWorld(); - mitk::BoundingObject *object = dynamic_cast(dn->GetData()); + auto *object = dynamic_cast(dn->GetData()); object->GetGeometry()->WorldToIndex(currentPickedPoint, currentPickedPoint); return object && object->GetGeometry()->GetBoundingBox()->IsInside(currentPickedPoint); } void mitk::AffineImageCropperInteractor::SelectObject(StateMachineAction *, InteractionEvent *interactionEvent) { mitk::DataNode::Pointer dn = this->GetDataNode(); if (dn.IsNull()) return; m_SelectedNode = dn; interactionEvent->GetSender()->GetRenderingManager()->RequestUpdateAll(); } void mitk::AffineImageCropperInteractor::Deselect() { mitk::DataNode::Pointer dn = this->GetDataNode(); if (dn.IsNull()) return; m_SelectedNode = dn; } void mitk::AffineImageCropperInteractor::DeselectObject(StateMachineAction *, InteractionEvent *interactionEvent) { Deselect(); interactionEvent->GetSender()->GetRenderingManager()->RequestUpdateAll(); } void mitk::AffineImageCropperInteractor::ScaleRadius(StateMachineAction *, InteractionEvent *interactionEvent) { - const MouseWheelEvent *wheelEvent = dynamic_cast(interactionEvent); + const auto *wheelEvent = dynamic_cast(interactionEvent); if (wheelEvent == nullptr) return; if (m_SelectedNode.IsNull()) return; double scale = (double)(wheelEvent->GetWheelDelta()) / 64.0; mitk::Point3D newScale; newScale[0] = newScale[1] = newScale[2] = scale; mitk::Point3D anchorPoint = wheelEvent->GetPositionInWorld(); - ScaleOperation *doOp = new mitk::ScaleOperation(OpSCALE, newScale, anchorPoint); + auto *doOp = new mitk::ScaleOperation(OpSCALE, newScale, anchorPoint); m_SelectedNode->GetData()->GetGeometry()->ExecuteOperation(doOp); interactionEvent->GetSender()->GetRenderingManager()->RequestUpdateAll(); } void mitk::AffineImageCropperInteractor::InitTranslate(StateMachineAction *, InteractionEvent *interactionEvent) { if (m_SelectedNode.IsNull()) return; - InteractionPositionEvent *positionEvent = dynamic_cast(interactionEvent); + auto *positionEvent = dynamic_cast(interactionEvent); if (positionEvent == nullptr) return; m_InitialPickedPoint = positionEvent->GetPositionInWorld(); m_InitialPickedDisplayPoint = positionEvent->GetPointerPositionOnScreen(); mitk::Surface::Pointer surface = dynamic_cast(m_SelectedNode->GetData()); mitk::BaseGeometry::Pointer surGeo = surface->GetGeometry(); m_InitialOrigin = surGeo->GetOrigin(); } void mitk::AffineImageCropperInteractor::InitRotate(StateMachineAction *, InteractionEvent *interactionEvent) { - InteractionPositionEvent *positionEvent = dynamic_cast(interactionEvent); + auto *positionEvent = dynamic_cast(interactionEvent); if (positionEvent == nullptr) return; m_InitialPickedPoint = positionEvent->GetPositionInWorld(); m_InitialPickedDisplayPoint = positionEvent->GetPointerPositionOnScreen(); mitk::Surface::Pointer surface = dynamic_cast(m_SelectedNode->GetData()); mitk::BaseGeometry::Pointer surGeo = surface->GetGeometry(); m_OriginalGeometry = dynamic_cast(surGeo.GetPointer()); } void mitk::AffineImageCropperInteractor::InitDeformation(StateMachineAction *, InteractionEvent *interactionEvent) { - InteractionPositionEvent *positionEvent = dynamic_cast(interactionEvent); + auto *positionEvent = dynamic_cast(interactionEvent); if (positionEvent == nullptr) return; m_InitialPickedPoint = positionEvent->GetPositionInWorld(); m_InitialPickedDisplayPoint = positionEvent->GetPointerPositionOnScreen(); mitk::Surface::Pointer surface = dynamic_cast(m_SelectedNode->GetData()); mitk::BaseGeometry::Pointer surGeo = surface->GetGeometry(); m_OriginalGeometry = dynamic_cast(surGeo.GetPointer()); } void mitk::AffineImageCropperInteractor::TranslateObject(StateMachineAction *, InteractionEvent *interactionEvent) { - InteractionPositionEvent *positionEvent = dynamic_cast(interactionEvent); + auto *positionEvent = dynamic_cast(interactionEvent); if (positionEvent == nullptr) return; Point3D currentPickedPoint = positionEvent->GetPositionInWorld(); Vector3D interactionMove; interactionMove[0] = currentPickedPoint[0] - m_InitialPickedPoint[0]; interactionMove[1] = currentPickedPoint[1] - m_InitialPickedPoint[1]; interactionMove[2] = currentPickedPoint[2] - m_InitialPickedPoint[2]; mitk::Surface::Pointer surface = dynamic_cast(m_SelectedNode->GetData()); mitk::BaseGeometry::Pointer surGeo = surface->GetGeometry(); surGeo->SetOrigin(m_InitialOrigin); surGeo->Translate(interactionMove); interactionEvent->GetSender()->GetRenderingManager()->RequestUpdateAll(); } void mitk::AffineImageCropperInteractor::DeformObject(StateMachineAction *, InteractionEvent *interactionEvent) { - InteractionPositionEvent *positionEvent = dynamic_cast(interactionEvent); + auto *positionEvent = dynamic_cast(interactionEvent); if (positionEvent == nullptr) return; Point3D currentPickedPoint = positionEvent->GetPositionInWorld(); Vector3D interactionMove = currentPickedPoint - m_InitialPickedPoint; mitk::Surface::Pointer surface = dynamic_cast(m_SelectedNode->GetData()); surface->SetGeometry(m_OriginalGeometry); mitk::BaseGeometry::Pointer surGeo = surface->GetGeometry(); surGeo->WorldToIndex(interactionMove, interactionMove); Point3D scale; for (int i = 0; i < 3; ++i) { scale[i] = (interactionMove[i] * surGeo->GetMatrixColumn(i).magnitude()) - 1; } mitk::Point3D anchorPoint = surGeo->GetCenter(); - ScaleOperation *doOp = new mitk::ScaleOperation(OpSCALE, scale, anchorPoint); + auto *doOp = new mitk::ScaleOperation(OpSCALE, scale, anchorPoint); surGeo->ExecuteOperation(doOp); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } void mitk::AffineImageCropperInteractor::RotateObject(StateMachineAction *, InteractionEvent *interactionEvent) { - InteractionPositionEvent *positionEvent = dynamic_cast(interactionEvent); + auto *positionEvent = dynamic_cast(interactionEvent); if (positionEvent == nullptr) return; Point2D currentPickedDisplayPoint = positionEvent->GetPointerPositionOnScreen(); if (currentPickedDisplayPoint.EuclideanDistanceTo(m_InitialPickedDisplayPoint) < 1) return; vtkRenderer *currentVtkRenderer = interactionEvent->GetSender()->GetVtkRenderer(); if (currentVtkRenderer && currentVtkRenderer->GetActiveCamera()) { double vpn[3]; currentVtkRenderer->GetActiveCamera()->GetViewPlaneNormal(vpn); Vector3D rotationAxis; rotationAxis[0] = vpn[0]; rotationAxis[1] = vpn[1]; rotationAxis[2] = vpn[2]; rotationAxis.Normalize(); Vector2D move = currentPickedDisplayPoint - m_InitialPickedDisplayPoint; double rotationAngle = -57.3 * atan(move[0] / move[1]); if (move[1] < 0) rotationAngle += 180; // Use center of data bounding box as center of rotation Point3D rotationCenter = m_OriginalGeometry->GetCenter(); if (positionEvent->GetSender()->GetMapperID() == BaseRenderer::Standard2D) rotationCenter = m_InitialPickedPoint; // Reset current Geometry3D to original state (pre-interaction) and // apply rotation RotationOperation op(OpROTATE, rotationCenter, rotationAxis, rotationAngle); Geometry3D::Pointer newGeometry = static_cast(m_OriginalGeometry->Clone().GetPointer()); newGeometry->ExecuteOperation(&op); m_SelectedNode->GetData()->SetGeometry(newGeometry); interactionEvent->GetSender()->GetRenderingManager()->RequestUpdateAll(); } } diff --git a/Modules/DataTypesExt/src/mitkClippingPlaneInteractor3D.cpp b/Modules/DataTypesExt/src/mitkClippingPlaneInteractor3D.cpp index bb13002de7..2ac56950ac 100644 --- a/Modules/DataTypesExt/src/mitkClippingPlaneInteractor3D.cpp +++ b/Modules/DataTypesExt/src/mitkClippingPlaneInteractor3D.cpp @@ -1,337 +1,337 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkClippingPlaneInteractor3D.h" #include #include #include #include #include #include #include #include #include #include mitk::ClippingPlaneInteractor3D::ClippingPlaneInteractor3D() { m_OriginalGeometry = Geometry3D::New(); // Initialize vector arithmetic m_ObjectNormal[0] = 0.0; m_ObjectNormal[1] = 0.0; m_ObjectNormal[2] = 1.0; } mitk::ClippingPlaneInteractor3D::~ClippingPlaneInteractor3D() { } void mitk::ClippingPlaneInteractor3D::ConnectActionsAndFunctions() { // **Conditions** that can be used in the state machine, to ensure that certain conditions are met, before actually // executing an action CONNECT_CONDITION("isOverObject", CheckOverObject); // **Function** in the statmachine patterns also referred to as **Actions** CONNECT_FUNCTION("selectObject", SelectObject); CONNECT_FUNCTION("deselectObject", DeselectObject); CONNECT_FUNCTION("initTranslate", InitTranslate); CONNECT_FUNCTION("initRotate", InitRotate); CONNECT_FUNCTION("translateObject", TranslateObject); CONNECT_FUNCTION("rotateObject", RotateObject); } void mitk::ClippingPlaneInteractor3D::DataNodeChanged() { } bool mitk::ClippingPlaneInteractor3D::CheckOverObject(const InteractionEvent *interactionEvent) { - const InteractionPositionEvent *positionEvent = dynamic_cast(interactionEvent); + const auto *positionEvent = dynamic_cast(interactionEvent); if (positionEvent == nullptr) return false; Point3D currentWorldPoint; if (interactionEvent->GetSender()->PickObject(positionEvent->GetPointerPositionOnScreen(), currentWorldPoint) == this->GetDataNode()) return true; return false; } void mitk::ClippingPlaneInteractor3D::SelectObject(StateMachineAction *, InteractionEvent *interactionEvent) { DataNode::Pointer node = this->GetDataNode(); if (node.IsNull()) return; node->SetColor(1.0, 0.0, 0.0); // Colorize surface / wireframe dependend on distance from picked point this->ColorizeSurface(interactionEvent->GetSender(), 0.0); interactionEvent->GetSender()->GetRenderingManager()->RequestUpdateAll(); } void mitk::ClippingPlaneInteractor3D::DeselectObject(StateMachineAction *, InteractionEvent *interactionEvent) { DataNode::Pointer node = this->GetDataNode(); if (node.IsNull()) return; node->SetColor(1.0, 1.0, 1.0); // Colorize surface / wireframe as inactive this->ColorizeSurface(interactionEvent->GetSender(), -1.0); interactionEvent->GetSender()->GetRenderingManager()->RequestUpdateAll(); } void mitk::ClippingPlaneInteractor3D::InitTranslate(StateMachineAction *, InteractionEvent *interactionEvent) { - InteractionPositionEvent *positionEvent = dynamic_cast(interactionEvent); + auto *positionEvent = dynamic_cast(interactionEvent); if (positionEvent == nullptr) return; m_InitialPickedDisplayPoint = positionEvent->GetPointerPositionOnScreen(); vtkInteractorObserver::ComputeDisplayToWorld(interactionEvent->GetSender()->GetVtkRenderer(), m_InitialPickedDisplayPoint[0], m_InitialPickedDisplayPoint[1], 0.0, // m_InitialInteractionPickedPoint[2], m_InitialPickedWorldPoint); // Get the timestep to also support 3D+t int timeStep = 0; if ((interactionEvent->GetSender()) != nullptr) timeStep = interactionEvent->GetSender()->GetTimeStep(this->GetDataNode()->GetData()); // Make deep copy of current Geometry3D of the plane this->GetDataNode()->GetData()->UpdateOutputInformation(); // make sure that the Geometry is up-to-date m_OriginalGeometry = static_cast(this->GetDataNode()->GetData()->GetGeometry(timeStep)->Clone().GetPointer()); } void mitk::ClippingPlaneInteractor3D::InitRotate(StateMachineAction *, InteractionEvent *interactionEvent) { - InteractionPositionEvent *positionEvent = dynamic_cast(interactionEvent); + auto *positionEvent = dynamic_cast(interactionEvent); if (positionEvent == nullptr) return; m_InitialPickedDisplayPoint = positionEvent->GetPointerPositionOnScreen(); vtkInteractorObserver::ComputeDisplayToWorld(interactionEvent->GetSender()->GetVtkRenderer(), m_InitialPickedDisplayPoint[0], m_InitialPickedDisplayPoint[1], 0.0, // m_InitialInteractionPickedPoint[2], m_InitialPickedWorldPoint); // Get the timestep to also support 3D+t int timeStep = interactionEvent->GetSender()->GetTimeStep(this->GetDataNode()->GetData()); // Make deep copy of current Geometry3D of the plane this->GetDataNode()->GetData()->UpdateOutputInformation(); // make sure that the Geometry is up-to-date m_OriginalGeometry = static_cast(this->GetDataNode()->GetData()->GetGeometry(timeStep)->Clone().GetPointer()); } void mitk::ClippingPlaneInteractor3D::TranslateObject(StateMachineAction *, InteractionEvent *interactionEvent) { - InteractionPositionEvent *positionEvent = dynamic_cast(interactionEvent); + auto *positionEvent = dynamic_cast(interactionEvent); if (positionEvent == nullptr) return; double currentWorldPoint[4]; mitk::Point2D currentDisplayPoint = positionEvent->GetPointerPositionOnScreen(); vtkInteractorObserver::ComputeDisplayToWorld(interactionEvent->GetSender()->GetVtkRenderer(), currentDisplayPoint[0], currentDisplayPoint[1], 0.0, // m_InitialInteractionPickedPoint[2], currentWorldPoint); Vector3D interactionMove; interactionMove[0] = currentWorldPoint[0] - m_InitialPickedWorldPoint[0]; interactionMove[1] = currentWorldPoint[1] - m_InitialPickedWorldPoint[1]; interactionMove[2] = currentWorldPoint[2] - m_InitialPickedWorldPoint[2]; Point3D origin = m_OriginalGeometry->GetOrigin(); // Get the timestep to also support 3D+t int timeStep = interactionEvent->GetSender()->GetTimeStep(this->GetDataNode()->GetData()); // If data is an mitk::Surface, extract it Surface::Pointer surface = dynamic_cast(this->GetDataNode()->GetData()); vtkPolyData *polyData = nullptr; if (surface.IsNotNull()) { polyData = surface->GetVtkPolyData(timeStep); // Extract surface normal from surface (if existent, otherwise use default) vtkPointData *pointData = polyData->GetPointData(); if (pointData != nullptr) { vtkDataArray *normal = polyData->GetPointData()->GetVectors("planeNormal"); if (normal != nullptr) { m_ObjectNormal[0] = normal->GetComponent(0, 0); m_ObjectNormal[1] = normal->GetComponent(0, 1); m_ObjectNormal[2] = normal->GetComponent(0, 2); } } } Vector3D transformedObjectNormal; this->GetDataNode()->GetData()->GetGeometry(timeStep)->IndexToWorld(m_ObjectNormal, transformedObjectNormal); this->GetDataNode()->GetData()->GetGeometry(timeStep)->SetOrigin( origin + transformedObjectNormal * (interactionMove * transformedObjectNormal)); interactionEvent->GetSender()->GetRenderingManager()->RequestUpdateAll(); } void mitk::ClippingPlaneInteractor3D::RotateObject(StateMachineAction *, InteractionEvent *interactionEvent) { - InteractionPositionEvent *positionEvent = dynamic_cast(interactionEvent); + auto *positionEvent = dynamic_cast(interactionEvent); if (positionEvent == nullptr) return; double currentWorldPoint[4]; Point2D currentPickedDisplayPoint = positionEvent->GetPointerPositionOnScreen(); vtkInteractorObserver::ComputeDisplayToWorld(interactionEvent->GetSender()->GetVtkRenderer(), currentPickedDisplayPoint[0], currentPickedDisplayPoint[1], 0.0, // m_InitialInteractionPickedPoint[2], currentWorldPoint); vtkCamera *camera = nullptr; vtkRenderer *currentVtkRenderer = nullptr; if ((interactionEvent->GetSender()) != nullptr) { vtkRenderWindow *renderWindow = interactionEvent->GetSender()->GetRenderWindow(); if (renderWindow != nullptr) { vtkRenderWindowInteractor *renderWindowInteractor = renderWindow->GetInteractor(); if (renderWindowInteractor != nullptr) { currentVtkRenderer = renderWindowInteractor->GetInteractorStyle()->GetCurrentRenderer(); if (currentVtkRenderer != nullptr) camera = currentVtkRenderer->GetActiveCamera(); } } } if (camera) { double vpn[3]; camera->GetViewPlaneNormal(vpn); Vector3D viewPlaneNormal; viewPlaneNormal[0] = vpn[0]; viewPlaneNormal[1] = vpn[1]; viewPlaneNormal[2] = vpn[2]; Vector3D interactionMove; interactionMove[0] = currentWorldPoint[0] - m_InitialPickedWorldPoint[0]; interactionMove[1] = currentWorldPoint[1] - m_InitialPickedWorldPoint[1]; interactionMove[2] = currentWorldPoint[2] - m_InitialPickedWorldPoint[2]; if (interactionMove[0] == 0 && interactionMove[1] == 0 && interactionMove[2] == 0) return; Vector3D rotationAxis = itk::CrossProduct(viewPlaneNormal, interactionMove); rotationAxis.Normalize(); int *size = currentVtkRenderer->GetSize(); double l2 = (currentPickedDisplayPoint[0] - m_InitialPickedDisplayPoint[0]) * (currentPickedDisplayPoint[0] - m_InitialPickedDisplayPoint[0]) + (currentPickedDisplayPoint[1] - m_InitialPickedDisplayPoint[1]) * (currentPickedDisplayPoint[1] - m_InitialPickedDisplayPoint[1]); double rotationAngle = 360.0 * sqrt(l2 / (size[0] * size[0] + size[1] * size[1])); // Use center of data bounding box as center of rotation Point3D rotationCenter = m_OriginalGeometry->GetCenter(); int timeStep = 0; if ((interactionEvent->GetSender()) != nullptr) timeStep = interactionEvent->GetSender()->GetTimeStep(this->GetDataNode()->GetData()); // Reset current Geometry3D to original state (pre-interaction) and // apply rotation RotationOperation op(OpROTATE, rotationCenter, rotationAxis, rotationAngle); Geometry3D::Pointer newGeometry = static_cast(m_OriginalGeometry->Clone().GetPointer()); newGeometry->ExecuteOperation(&op); mitk::TimeGeometry::Pointer timeGeometry = this->GetDataNode()->GetData()->GetTimeGeometry(); if (timeGeometry.IsNotNull()) timeGeometry->SetTimeStepGeometry(newGeometry, timeStep); interactionEvent->GetSender()->GetRenderingManager()->RequestUpdateAll(); } } void mitk::ClippingPlaneInteractor3D::ColorizeSurface(BaseRenderer::Pointer renderer, double scalar) { BaseData::Pointer data = this->GetDataNode()->GetData(); if (data.IsNull()) { MITK_ERROR << "ClippingPlaneInteractor3D: No data object present!"; return; } // Get the timestep to also support 3D+t int timeStep = 0; if (renderer.IsNotNull()) timeStep = renderer->GetTimeStep(data); // If data is an mitk::Surface, extract it Surface::Pointer surface = dynamic_cast(data.GetPointer()); vtkPolyData *polyData = nullptr; if (surface.IsNotNull()) polyData = surface->GetVtkPolyData(timeStep); if (polyData == nullptr) { MITK_ERROR << "ClippingPlaneInteractor3D: No poly data present!"; return; } vtkPointData *pointData = polyData->GetPointData(); if (pointData == nullptr) { MITK_ERROR << "ClippingPlaneInteractor3D: No point data present!"; return; } vtkDataArray *scalars = pointData->GetScalars(); if (scalars == nullptr) { MITK_ERROR << "ClippingPlaneInteractor3D: No scalars for point data present!"; return; } for (vtkIdType i = 0; i < pointData->GetNumberOfTuples(); ++i) { scalars->SetComponent(i, 0, scalar); } polyData->Modified(); pointData->Update(); } diff --git a/Modules/DataTypesExt/src/mitkColorConversions.cpp b/Modules/DataTypesExt/src/mitkColorConversions.cpp index 5f52ce8ae0..1b4aac1240 100644 --- a/Modules/DataTypesExt/src/mitkColorConversions.cpp +++ b/Modules/DataTypesExt/src/mitkColorConversions.cpp @@ -1,164 +1,164 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkColorConversions.h" namespace mitk { namespace ColorConversions { /* Convert HSV color to RGB color (pseudocode from wikipedia) H from 0 to 360 S from 0 to 1 V from 0 to 1 R, G, and B from 0 to 1 */ void Hsv2Rgb(float h, float s, float v, float &r, float &g, float &b) { if (s == 0.0) { r = g = b = v; // gray return; } int Hi = (int)(h / 60.0) % 6; if (h >= 360) Hi = 6; - float f = (float)(h / 60.0 - (float)Hi); - float p = (float)(v * (1.0 - s)); - float q = (float)(v * (1.0 - s * f)); - float t = (float)(v * (1.0 - s * (1.0 - f))); + auto f = (float)(h / 60.0 - (float)Hi); + auto p = (float)(v * (1.0 - s)); + auto q = (float)(v * (1.0 - s * f)); + auto t = (float)(v * (1.0 - s * (1.0 - f))); switch (Hi) { case 0: case 6: r = v; g = t; b = p; break; case 1: r = q; g = v; b = p; break; case 2: r = p; g = v; b = t; break; case 3: r = p; g = q; b = v; break; case 4: r = t; g = p; b = v; break; case 5: r = v; g = p; b = q; break; } } /* Convert RGB color to HSV color R from 0 to 1 G from 0 to 1 B from 0 to 1 H from 0 to 360 and -1 for undefined color (pure white) S from 0 to 1 V from 0 to 1 */ void Rgb2Hsv(float r, float g, float b, float &h, float &s, float &v) { /* r = r/255; b = b/255; g = g/255;*/ float mn = r, mx = r; int maxVal = 0; if (g > mx) { mx = g; maxVal = 1; } if (b > mx) { mx = b; maxVal = 2; } if (g < mn) mn = g; if (b < mn) mn = b; float delta = mx - mn; v = mx; if (mx != 0) s = delta / mx; else { s = 0; h = 0; return; } if (s == 0.0f) { h = -1; return; } else { switch (maxVal) { case 0: { h = (g - b) / delta; break; } // yel < h < mag case 1: { h = 2 + (b - r) / delta; break; } // cyan < h < yel case 2: { h = 4 + (r - g) / delta; break; } // mag < h < cyan } } h *= 60; if (h < 0) h += 360; } } // ColorConversion } // mitk diff --git a/Modules/DataTypesExt/src/mitkCompressedImageContainer.cpp b/Modules/DataTypesExt/src/mitkCompressedImageContainer.cpp index 4c7f87ef20..3f18908bbd 100644 --- a/Modules/DataTypesExt/src/mitkCompressedImageContainer.cpp +++ b/Modules/DataTypesExt/src/mitkCompressedImageContainer.cpp @@ -1,182 +1,182 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkCompressedImageContainer.h" #include "mitkImageReadAccessor.h" #include "itk_zlib.h" #include mitk::CompressedImageContainer::CompressedImageContainer() : m_PixelType(nullptr), m_ImageGeometry(nullptr) { } mitk::CompressedImageContainer::~CompressedImageContainer() { for (auto iter = m_ByteBuffers.begin(); iter != m_ByteBuffers.end(); ++iter) { free(iter->first); } delete m_PixelType; } void mitk::CompressedImageContainer::SetImage(Image *image) { for (auto iter = m_ByteBuffers.begin(); iter != m_ByteBuffers.end(); ++iter) { free(iter->first); } m_ByteBuffers.clear(); // Compress diff image using zlib (will be restored on demand) // determine memory size occupied by voxel data m_ImageDimension = image->GetDimension(); m_ImageDimensions.clear(); m_PixelType = new mitk::PixelType(image->GetPixelType()); m_OneTimeStepImageSizeInBytes = m_PixelType->GetSize(); // bits per element divided by 8 for (unsigned int i = 0; i < m_ImageDimension; ++i) { unsigned int currentImageDimension = image->GetDimension(i); m_ImageDimensions.push_back(currentImageDimension); if (i < 3) { m_OneTimeStepImageSizeInBytes *= currentImageDimension; // only the 3D memory size } } m_ImageGeometry = image->GetGeometry(); m_NumberOfTimeSteps = 1; if (m_ImageDimension > 3) { m_NumberOfTimeSteps = image->GetDimension(3); } for (unsigned int timestep = 0; timestep < m_NumberOfTimeSteps; ++timestep) { // allocate a buffer as specified by zlib unsigned long bufferSize = m_OneTimeStepImageSizeInBytes + static_cast(m_OneTimeStepImageSizeInBytes * 0.2) + 12; - unsigned char *byteBuffer = (unsigned char *)malloc(bufferSize); + auto *byteBuffer = (unsigned char *)malloc(bufferSize); if (itk::Object::GetDebug()) { // compress image here into a buffer MITK_INFO << "Using ZLib version: '" << zlibVersion() << "'" << std::endl << "Attempting to compress " << m_OneTimeStepImageSizeInBytes << " image bytes into a buffer of size " << bufferSize << std::endl; } ImageReadAccessor imgAcc(image, image->GetVolumeData(timestep)); ::Bytef *dest(byteBuffer); ::uLongf destLen(bufferSize); - ::Bytef *source((unsigned char *)imgAcc.GetData()); + auto *source((unsigned char *)imgAcc.GetData()); ::uLongf sourceLen(m_OneTimeStepImageSizeInBytes); int zlibRetVal = ::compress(dest, &destLen, source, sourceLen); if (itk::Object::GetDebug()) { if (zlibRetVal == Z_OK) { MITK_INFO << "Success, using " << destLen << " bytes of the buffer (ratio " << ((double)destLen / (double)sourceLen) << ")" << std::endl; } else { switch (zlibRetVal) { case Z_MEM_ERROR: MITK_ERROR << "not enough memory" << std::endl; break; case Z_BUF_ERROR: MITK_ERROR << "output buffer too small" << std::endl; break; default: MITK_ERROR << "other, unspecified error" << std::endl; break; } } } // only use the neccessary amount of memory, realloc the buffer! byteBuffer = (unsigned char *)realloc(byteBuffer, destLen); bufferSize = destLen; // MITK_INFO << "Using " << bufferSize << " bytes to store compressed image (" << destLen << " needed)" << // std::endl; m_ByteBuffers.push_back(std::pair(byteBuffer, bufferSize)); } } mitk::Image::Pointer mitk::CompressedImageContainer::GetImage() { if (m_ByteBuffers.empty()) return nullptr; // uncompress image data, create an Image Image::Pointer image = Image::New(); unsigned int dims[20]; // more than 20 dimensions and bang for (unsigned int dim = 0; dim < m_ImageDimension; ++dim) dims[dim] = m_ImageDimensions[dim]; image->Initialize(*m_PixelType, m_ImageDimension, dims); // this IS needed, right ?? But it does allocate memory -> // does create one big lump of memory (also in windows) unsigned int timeStep(0); for (auto iter = m_ByteBuffers.begin(); iter != m_ByteBuffers.end(); ++iter, ++timeStep) { ImageReadAccessor imgAcc(image, image->GetVolumeData(timeStep)); - ::Bytef *dest((unsigned char *)imgAcc.GetData()); + auto *dest((unsigned char *)imgAcc.GetData()); ::uLongf destLen(m_OneTimeStepImageSizeInBytes); ::Bytef *source(iter->first); ::uLongf sourceLen(iter->second); int zlibRetVal = ::uncompress(dest, &destLen, source, sourceLen); if (itk::Object::GetDebug()) { if (zlibRetVal == Z_OK) { MITK_INFO << "Success, destLen now " << destLen << " bytes" << std::endl; } else { switch (zlibRetVal) { case Z_DATA_ERROR: MITK_ERROR << "compressed data corrupted" << std::endl; break; case Z_MEM_ERROR: MITK_ERROR << "not enough memory" << std::endl; break; case Z_BUF_ERROR: MITK_ERROR << "output buffer too small" << std::endl; break; default: MITK_ERROR << "other, unspecified error" << std::endl; break; } } } } image->SetGeometry(m_ImageGeometry); image->Modified(); return image; } diff --git a/Modules/DataTypesExt/src/mitkDataStorageSelection.cpp b/Modules/DataTypesExt/src/mitkDataStorageSelection.cpp index 2a41d5d474..dda2fbe839 100644 --- a/Modules/DataTypesExt/src/mitkDataStorageSelection.cpp +++ b/Modules/DataTypesExt/src/mitkDataStorageSelection.cpp @@ -1,329 +1,329 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkDataStorageSelection.h" #include #include #include namespace mitk { DataStorageSelection::DataStorageSelection(mitk::DataStorage *_DataStorage, bool _AutoAddNodes) : m_DataStorage(nullptr), m_Predicate(nullptr), m_SelfCall(false), m_AutoAddNodes(_AutoAddNodes) { this->SetDataStorage(_DataStorage); } DataStorageSelection::DataStorageSelection(mitk::DataStorage *_DataStorage, mitk::NodePredicateBase *_Predicate, bool _AutoAddNodes) : m_DataStorage(nullptr), m_Predicate(_Predicate), m_SelfCall(false), m_AutoAddNodes(_AutoAddNodes) { this->SetDataStorage(_DataStorage); } DataStorageSelection::~DataStorageSelection() { // kick datastorage and all nodes and all listeners this->SetDataStorage(nullptr); } mitk::DataStorage::Pointer DataStorageSelection::GetDataStorage() const { return m_DataStorage; } mitk::NodePredicateBase::Pointer DataStorageSelection::GetPredicate() const { return m_Predicate; } unsigned int DataStorageSelection::GetSize() const { return m_Nodes.size(); } mitk::DataNode::Pointer DataStorageSelection::GetNode() const { return this->GetNode(0); } mitk::DataNode::Pointer DataStorageSelection::GetNode(unsigned int index) const { return (index < m_Nodes.size()) ? m_Nodes.at(index) : nullptr; } std::vector DataStorageSelection::GetNodes() const { return m_Nodes; } bool DataStorageSelection::DoesAutoAddNodes() const { return m_AutoAddNodes; } DataStorageSelection &DataStorageSelection::operator=(mitk::DataNode *node) { this->RemoveAllNodes(); this->AddNode(node); return *this; } DataStorageSelection &DataStorageSelection::operator=(mitk::DataNode::Pointer node) { *this = node.GetPointer(); return *this; } void DataStorageSelection::SetDataStorage(mitk::DataStorage *_DataStorage) { // only proceed if we have a new datastorage if (m_DataStorage != _DataStorage) { // if a data storage was set before remove old event listeners if (m_DataStorage != nullptr) { if (m_AutoAddNodes) this->m_DataStorage->AddNodeEvent.RemoveListener( mitk::MessageDelegate1(this, &DataStorageSelection::AddNode)); this->m_DataStorage->RemoveNodeEvent.RemoveListener( mitk::MessageDelegate1(this, &DataStorageSelection::RemoveNode)); m_DataStorage->RemoveObserver(m_DataStorageDeletedTag); m_DataStorageDeletedTag = 0; } // set new data storage m_DataStorage = _DataStorage; // if new storage is not 0 subscribe for events if (m_DataStorage != nullptr) { // subscribe for node added/removed events if (m_AutoAddNodes) this->m_DataStorage->AddNodeEvent.AddListener( mitk::MessageDelegate1(this, &DataStorageSelection::AddNode)); this->m_DataStorage->RemoveNodeEvent.AddListener( mitk::MessageDelegate1(this, &DataStorageSelection::RemoveNode)); itk::MemberCommand::Pointer ObjectChangedCommand = itk::MemberCommand::New(); ObjectChangedCommand->SetCallbackFunction(this, &DataStorageSelection::ObjectChanged); m_DataStorageDeletedTag = m_DataStorage->AddObserver(itk::DeleteEvent(), ObjectChangedCommand); } // Reset model (even if datastorage is 0->will be checked in Reset()) this->Reset(); } } void DataStorageSelection::SetPredicate(mitk::NodePredicateBase *_Predicate) { // ensure that a new predicate is set in order to avoid unnecessary changed events if (m_Predicate != _Predicate) { m_Predicate = _Predicate; this->Reset(); } } void DataStorageSelection::AddNode(const mitk::DataNode *node) { // garantuee no recursions when a new node event is thrown if (m_SelfCall) return; // if we have a predicate, check node against predicate first if (m_Predicate.IsNotNull() && !m_Predicate->CheckNode(node)) return; // no duplicates if (std::find(m_Nodes.begin(), m_Nodes.end(), node) != m_Nodes.end()) return; - mitk::DataNode *nonConstNode = const_cast(node); + auto *nonConstNode = const_cast(node); // add listener this->AddListener(nonConstNode); // add node m_Nodes.push_back(nonConstNode); NodeAdded.Send(node); } void DataStorageSelection::RemoveNode(const mitk::DataNode *node) { if (m_SelfCall) return; // find corresponding node auto nodeIt = std::find(m_Nodes.begin(), m_Nodes.end(), node); if (nodeIt == m_Nodes.end()) return; - mitk::DataNode *nonConstNode = const_cast(node); + auto *nonConstNode = const_cast(node); // add listener this->RemoveListener(nonConstNode); // remove node m_Nodes.erase(nodeIt); NodeRemoved.Send(node); } void DataStorageSelection::RemoveAllNodes() { // remove all nodes now (dont use iterators because removing elements // would invalidate the iterator) // start at the last element: first in, last out unsigned int i = m_Nodes.size(); while (!m_Nodes.empty()) { --i; this->RemoveNode(m_Nodes.at(i)); } } void DataStorageSelection::ObjectChanged(const itk::Object *caller, const itk::EventObject & /*event*/) { if (m_SelfCall) return; /* const itk::DeleteEvent* delEvent = 0; const itk::ModifiedEvent* modifiedEvent = dynamic_cast(&event); if(!modifiedEvent) delEvent = dynamic_cast(&event); */ const mitk::BaseProperty *prop = nullptr; const mitk::PropertyList *propList = nullptr; - const mitk::DataNode *node = dynamic_cast(caller); + const auto *node = dynamic_cast(caller); if (!node) { if ((prop = dynamic_cast(caller))) { node = this->FindNode(prop); } else if ((propList = dynamic_cast(caller))) { node = this->FindNode(propList); } else if (dynamic_cast(caller)) { this->SetDataStorage(nullptr); } } if (prop && node) { PropertyChanged.Send(node, prop); } else if (node) { NodeChanged.Send(node); } } //# protected mitk::DataNode::Pointer DataStorageSelection::FindNode(const mitk::BaseProperty *prop) const { mitk::DataNode *node = nullptr; for (auto it = m_Nodes.begin(); it != m_Nodes.end(); ++it) { for (auto it2 = (*it)->GetPropertyList()->GetMap()->begin(); it2 != (*it)->GetPropertyList()->GetMap()->end(); ++it2) { if (it2->second == prop) { node = *it; break; } } } return node; } mitk::DataNode::Pointer DataStorageSelection::FindNode(const mitk::PropertyList *propList) const { mitk::DataNode *node = nullptr; for (auto it = m_Nodes.begin(); it != m_Nodes.end(); ++it) { if ((*it)->GetPropertyList() == propList) { node = *it; break; } } return node; } void DataStorageSelection::Reset() { this->RemoveAllNodes(); // the whole reset depends on the fact if a data storage is set or not if (m_DataStorage) { mitk::DataStorage::SetOfObjects::ConstPointer _NodeSet = nullptr; if (m_AutoAddNodes && m_Predicate.IsNotNull()) // get subset _NodeSet = m_DataStorage->GetSubset(m_Predicate); // if predicate is nullptr, select all nodes else if (m_AutoAddNodes) { _NodeSet = m_DataStorage->GetAll(); } else return; // finally add all nodes to the model for (auto it = _NodeSet->begin(); it != _NodeSet->end(); it++) { // save node this->AddNode(*it); } } } void DataStorageSelection::RemoveListener(mitk::DataNode *node) { // remove node listener node->RemoveObserver(m_NodeModifiedObserverTags[node]); m_NodeModifiedObserverTags.erase(node); // remove propertylist listener mitk::PropertyList *propList = node->GetPropertyList(); propList->RemoveObserver(m_PropertyListModifiedObserverTags[propList]); m_PropertyListModifiedObserverTags.erase(propList); propList->RemoveObserver(m_PropertyListDeletedObserverTags[propList]); m_PropertyListDeletedObserverTags.erase(propList); mitk::BaseProperty *prop = nullptr; // do the same for each property for (auto it = propList->GetMap()->begin(); it != propList->GetMap()->end(); ++it) { prop = it->second; prop->RemoveObserver(m_PropertyModifiedObserverTags[prop]); m_PropertyModifiedObserverTags.erase(prop); prop->RemoveObserver(m_PropertyDeletedObserverTags[prop]); m_PropertyDeletedObserverTags.erase(prop); } } void DataStorageSelection::AddListener(mitk::DataNode *node) { // node listener itk::MemberCommand::Pointer ObjectChangedCommand = itk::MemberCommand::New(); ObjectChangedCommand->SetCallbackFunction(this, &DataStorageSelection::ObjectChanged); m_NodeModifiedObserverTags[node] = node->AddObserver(itk::ModifiedEvent(), ObjectChangedCommand); // create propertylist listener mitk::PropertyList *propList = node->GetPropertyList(); m_PropertyListModifiedObserverTags[propList] = propList->AddObserver(itk::ModifiedEvent(), ObjectChangedCommand); m_PropertyListDeletedObserverTags[propList] = propList->AddObserver(itk::DeleteEvent(), ObjectChangedCommand); mitk::BaseProperty *prop = nullptr; // do the same for each property for (auto it = propList->GetMap()->begin(); it != propList->GetMap()->end(); ++it) { prop = it->second; m_PropertyModifiedObserverTags[prop] = prop->AddObserver(itk::ModifiedEvent(), ObjectChangedCommand); m_PropertyDeletedObserverTags[prop] = prop->AddObserver(itk::ModifiedEvent(), ObjectChangedCommand); } } } diff --git a/Modules/DataTypesExt/src/mitkLabeledImageLookupTable.cpp b/Modules/DataTypesExt/src/mitkLabeledImageLookupTable.cpp index 8e7ac1325a..0876657cd1 100644 --- a/Modules/DataTypesExt/src/mitkLabeledImageLookupTable.cpp +++ b/Modules/DataTypesExt/src/mitkLabeledImageLookupTable.cpp @@ -1,143 +1,143 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkLabeledImageLookupTable.h" #include #include /** * Default constructor. Protected to prevent "normal" creation */ mitk::LabeledImageLookupTable::LabeledImageLookupTable() : m_LevelWindow(128, 256) { if (m_LookupTable == nullptr) { itkWarningMacro( "LookupTable is nullptr, it should have been initialized by the default constructor of mitk::LookupTable"); m_LookupTable = vtkLookupTable::New(); } m_LookupTable->SetNumberOfTableValues(256); // set the background to black and fully transparent m_LookupTable->SetTableValue(0, 0.0, 0.0, 0.0, 0.0); // initialize the remaining 255 colors with random values and // an alpha value of 1.0 double r, g, b; // // Initialize the random number generator with an arbitrary seed. // This way, the generated colors are random, but always the same... std::srand(2); for (vtkIdType index = 1; index < 256; ++index) { GenerateRandomColor(r, g, b); m_LookupTable->SetTableValue(index, r, g, b); } // initialize the default level/window settings, // which can be accessed via GetLevelWindow(); m_LevelWindow.SetRangeMinMax(0, 255); m_LevelWindow.SetWindowBounds(0, 255); m_LevelWindow.SetFixed(true); } mitk::LabeledImageLookupTable::LabeledImageLookupTable(const mitk::LabeledImageLookupTable &other) : LookupTable(other), m_LevelWindow(other.m_LevelWindow) { } /** * Virtual destructor */ mitk::LabeledImageLookupTable::~LabeledImageLookupTable() { } mitk::LabeledImageLookupTable &mitk::LabeledImageLookupTable::operator=(const mitk::LookupTable &other) { LookupTable::operator=(other); - if (const LabeledImageLookupTable *lut = dynamic_cast(&other)) + if (const auto *lut = dynamic_cast(&other)) { this->m_LevelWindow = lut->m_LevelWindow; } return *this; } /** * Sets the color for a given label * @param label The pixel value used as a label in the image * @param r The red component of the rgba color value. Values sould be given in the range [0,1] * @param g The green component of the rgba color value. Values sould be given in the range [0,1] * @param b The blue component of the rgba color value. Values sould be given in the range [0,1] * @param a The alpha component of the rgba color value. Values sould be given in the range [0,1]. Default is 1. */ void mitk::LabeledImageLookupTable::SetColorForLabel(const mitk::LabeledImageLookupTable::LabelType &label, const double &r, const double &g, const double &b, const double a) { if (m_LookupTable == nullptr) { itkWarningMacro("LookupTable is nullptr, but it should have been initialized by the constructor"); return; } m_LookupTable->SetTableValue(label, r, g, b, a); } /** * Determines the color which will be used for coloring a given label. * @param label the label for which the color should be returned * @returns an rgba array containing the color information for the given label * Color components are expressed as [0,1] double values. */ double *mitk::LabeledImageLookupTable::GetColorForLabel(const mitk::LabeledImageLookupTable::LabelType &label) { if (m_LookupTable == nullptr) { itkWarningMacro("LookupTable is nullptr, but it should have been initialized by the constructor"); return nullptr; } return m_LookupTable->GetTableValue(label); } /** * Generates a random rgb color value. Values for rgb are in the range * [0,1] */ void mitk::LabeledImageLookupTable::GenerateRandomColor(double &r, double &g, double &b) { r = GenerateRandomNumber(); g = GenerateRandomNumber(); b = GenerateRandomNumber(); } /** * Generates a radnom number drawn from a uniform * distribution in the range [0,1]. */ double mitk::LabeledImageLookupTable::GenerateRandomNumber() { return (((double)(std::rand())) / ((double)(RAND_MAX))); } itk::LightObject::Pointer mitk::LabeledImageLookupTable::InternalClone() const { itk::LightObject::Pointer result(new Self(*this)); result->UnRegister(); return result; } diff --git a/Modules/DataTypesExt/src/mitkLabeledImageVolumeCalculator.cpp b/Modules/DataTypesExt/src/mitkLabeledImageVolumeCalculator.cpp index 49a60a5a0a..83179c06bb 100644 --- a/Modules/DataTypesExt/src/mitkLabeledImageVolumeCalculator.cpp +++ b/Modules/DataTypesExt/src/mitkLabeledImageVolumeCalculator.cpp @@ -1,127 +1,127 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkLabeledImageVolumeCalculator.h" #include "mitkImageAccessByItk.h" #include namespace mitk { LabeledImageVolumeCalculator::LabeledImageVolumeCalculator() { m_InputTimeSelector = ImageTimeSelector::New(); m_DummyPoint.Fill(0.0); } LabeledImageVolumeCalculator::~LabeledImageVolumeCalculator() {} double LabeledImageVolumeCalculator::GetVolume(unsigned int label) const { if (label < m_VolumeVector.size()) return m_VolumeVector[label]; else return 0.0; } const Point3D &LabeledImageVolumeCalculator::GetCentroid(unsigned int label) const { if (label < m_CentroidVector.size()) return m_CentroidVector[label]; else return m_DummyPoint; } const LabeledImageVolumeCalculator::VolumeVector &LabeledImageVolumeCalculator::GetVolumes() const { return m_VolumeVector; } const LabeledImageVolumeCalculator::PointVector &LabeledImageVolumeCalculator::GetCentroids() const { return m_CentroidVector; } void LabeledImageVolumeCalculator::Calculate() { if (m_Image.IsNull()) { itkExceptionMacro(<< "Image not set!"); return; } m_InputTimeSelector->SetInput(m_Image); m_InputTimeSelector->SetTimeNr(0); m_InputTimeSelector->UpdateLargestPossibleRegion(); AccessByItk_2(m_InputTimeSelector->GetOutput(), _InternalCalculateVolumes, this, m_Image->GetGeometry(0)); //} } template void LabeledImageVolumeCalculator::_InternalCalculateVolumes(itk::Image *image, LabeledImageVolumeCalculator * /*volumeCalculator*/, BaseGeometry *geometry) { typedef itk::Image ImageType; typedef typename ImageType::IndexType IndexType; typedef itk::ImageRegionConstIteratorWithIndex IteratorType; // Reset volume and centroid vectors m_VolumeVector.clear(); m_CentroidVector.clear(); // Iterate over image and determine number of voxels and centroid // per label IteratorType it(image, image->GetBufferedRegion()); for (it.GoToBegin(); !it.IsAtEnd(); ++it) { const IndexType &index = it.GetIndex(); - unsigned int pixel = static_cast(it.Get()); + auto pixel = static_cast(it.Get()); if (m_VolumeVector.size() <= pixel) { m_VolumeVector.resize(pixel + 1); m_CentroidVector.resize(pixel + 1); } m_VolumeVector[pixel] += 1.0; m_CentroidVector[pixel][0] += index[0]; m_CentroidVector[pixel][1] += index[1]; m_CentroidVector[pixel][2] += index[2]; } // Calculate voxel volume from spacing const Vector3D &spacing = geometry->GetSpacing(); double voxelVolume = spacing[0] * spacing[1] * spacing[2]; // Calculate centroid (in world coordinates) and volumes for all labels for (unsigned int i = 0; i < m_VolumeVector.size(); ++i) { if (m_VolumeVector[i] > 0.0) { m_CentroidVector[i][0] /= m_VolumeVector[i]; m_CentroidVector[i][1] /= m_VolumeVector[i]; m_CentroidVector[i][2] /= m_VolumeVector[i]; geometry->IndexToWorld(m_CentroidVector[i], m_CentroidVector[i]); m_VolumeVector[i] *= voxelVolume; } } } } diff --git a/Modules/DataTypesExt/src/mitkMesh.cpp b/Modules/DataTypesExt/src/mitkMesh.cpp index 8086f83471..995561188d 100644 --- a/Modules/DataTypesExt/src/mitkMesh.cpp +++ b/Modules/DataTypesExt/src/mitkMesh.cpp @@ -1,809 +1,809 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkMesh.h" #include "mitkInteractionConst.h" #include "mitkLine.h" #include "mitkLineOperation.h" #include "mitkLineOperation.h" #include "mitkNumericTypes.h" #include "mitkOperation.h" #include "mitkOperationActor.h" #include "mitkPointOperation.h" #include "mitkRenderingManager.h" #include "mitkStatusBar.h" mitk::Mesh::Mesh() { } mitk::Mesh::~Mesh() { } const mitk::Mesh::DataType *mitk::Mesh::GetMesh(int t) const { return m_PointSetSeries[t]; } mitk::Mesh::DataType *mitk::Mesh::GetMesh(int t) { return m_PointSetSeries[t]; } void mitk::Mesh::SetMesh(DataType *mesh, int t) { this->Expand(t + 1); m_PointSetSeries[t] = mesh; } unsigned long mitk::Mesh::GetNumberOfCells(int t) { return m_PointSetSeries[t]->GetNumberOfCells(); } // search a line that is close enough according to the given position bool mitk::Mesh::SearchLine(Point3D point, float distance, unsigned long &lineId, unsigned long &cellId, int t) { // returns true if a line is found ScalarType bestDist = distance; // iterate through all cells. ConstCellIterator cellIt = m_PointSetSeries[t]->GetCells()->Begin(); ConstCellIterator cellEnd = m_PointSetSeries[t]->GetCells()->End(); while (cellIt != cellEnd) { if (cellIt.Value()->GetNumberOfPoints() > 1) { // then iterate through all indexes of points in it->Value() PointIdIterator inAIt = cellIt.Value()->PointIdsBegin(); // first point PointIdIterator inBIt = cellIt.Value()->PointIdsBegin(); // second point PointIdIterator inEnd = cellIt.Value()->PointIdsEnd(); ++inAIt; // so it points to the point before inBIt int currentLineId = 0; while (inAIt != inEnd) { mitk::PointSet::PointType pointA, pointB; if (m_PointSetSeries[t]->GetPoint((*inAIt), &pointA) && m_PointSetSeries[t]->GetPoint((*inBIt), &pointB)) { auto line = new Line(); line->SetPoints(pointA, pointB); double thisDistance = line->Distance(point); if (thisDistance < bestDist) { cellId = cellIt->Index(); lineId = currentLineId; bestDist = thisDistance; } } ++inAIt; ++inBIt; ++currentLineId; } // If the cell is closed, then check the line from the last index to // the first index if inAIt points to inEnd, then inBIt points to the // last index. CellDataType cellData; bool dataOk = m_PointSetSeries[t]->GetCellData(cellIt->Index(), &cellData); if (dataOk) { if (cellData.closed) { // get the points PointIdIterator inAIt = cellIt.Value()->PointIdsBegin(); // first point // inBIt points to last. mitk::PointSet::PointType pointA, pointB; if (m_PointSetSeries[t]->GetPoint((*inAIt), &pointA) && m_PointSetSeries[t]->GetPoint((*inBIt), &pointB)) { auto line = new Line(); line->SetPoints(pointA, pointB); double thisDistance = line->Distance(point); if (thisDistance < bestDist) { cellId = cellIt->Index(); lineId = currentLineId; bestDist = thisDistance; } } } } } ++cellIt; } return (bestDist < distance); } int mitk::Mesh::SearchFirstCell(unsigned long pointId, int t) { // iterate through all cells and find the cell the given pointId is inside ConstCellIterator it = m_PointSetSeries[t]->GetCells()->Begin(); ConstCellIterator end = m_PointSetSeries[t]->GetCells()->End(); while (it != end) { PointIdIterator position = std::find(it->Value()->PointIdsBegin(), it->Value()->PointIdsEnd(), pointId); if (position != it->Value()->PointIdsEnd()) { return it->Index(); } ++it; } return -1; } // Due to not implemented itk::CellInterface::EvaluatePosition and errors in // using vtkCell::EvaluatePosition (changing iterator!) we must implement // it in mitk::Mesh // make it easy and look for hit points and hit lines: needs to be done anyway! bool mitk::Mesh::EvaluatePosition(mitk::Point3D point, unsigned long &cellId, float precision, int t) { int pointId = this->SearchPoint(point, precision, t); if (pointId > -1) { // search the cell the point lies inside cellId = this->SearchFirstCell(pointId, t); return true; } unsigned long lineId = 0; if (this->SearchLine(point, precision, lineId, cellId, t)) { return true; } return false; } unsigned long mitk::Mesh::GetNewCellId(int t) { long nextCellId = -1; ConstCellIterator it = m_PointSetSeries[t]->GetCells()->Begin(); ConstCellIterator end = m_PointSetSeries[t]->GetCells()->End(); while (it != end) { nextCellId = it.Index(); ++it; } ++nextCellId; return nextCellId; } int mitk::Mesh::SearchSelectedCell(int t) { CellDataIterator cellDataIt, cellDataEnd; cellDataEnd = m_PointSetSeries[t]->GetCellData()->End(); for (cellDataIt = m_PointSetSeries[t]->GetCellData()->Begin(); cellDataIt != cellDataEnd; cellDataIt++) { // then declare an operation which unselects this line; UndoOperation as well! if (cellDataIt->Value().selected) { return cellDataIt->Index(); } } return -1; } // get the cell; then iterate through the Ids times lineId. Then IdA ist the // one, IdB ist ne next.don't forget the last closing line if the cell is // closed bool mitk::Mesh::GetPointIds(unsigned long cellId, unsigned long lineId, int &idA, int &idB, int t) { CellAutoPointer cellAutoPointer; bool ok = m_PointSetSeries[t]->GetCell(cellId, cellAutoPointer); if (ok) { CellType *cell = cellAutoPointer.GetPointer(); // Get the cellData to also check the closing line CellDataType cellData; m_PointSetSeries[t]->GetCellData(cellId, &cellData); bool closed = cellData.closed; PointIdIterator pointIdIt = cell->PointIdsBegin(); PointIdIterator pointIdEnd = cell->PointIdsEnd(); unsigned int counter = 0; bool found = false; while (pointIdIt != pointIdEnd) { if (counter == lineId) { idA = (*pointIdIt); ++pointIdIt; found = true; break; } ++counter; ++pointIdIt; } if (found) { // if in the middle if (pointIdIt != pointIdEnd) { idB = (*pointIdIt); } // if found but on the end, then it is the closing connection, so the // last and the first point else if (closed) { pointIdIt = cell->PointIdsBegin(); idB = (*pointIdIt); } } else ok = false; } return ok; } void mitk::Mesh::ExecuteOperation(Operation *operation) { // adding only the operations, that aren't implemented by the pointset. switch (operation->GetOperationType()) { case OpNOTHING: break; case OpNEWCELL: { - mitk::LineOperation *lineOp = dynamic_cast(operation); + auto *lineOp = dynamic_cast(operation); // if no lineoperation, then call superclass pointSet if (lineOp == nullptr) { Superclass::ExecuteOperation(operation); } bool ok; int cellId = lineOp->GetCellId(); CellAutoPointer cellAutoPointer; ok = m_PointSetSeries[0]->GetCell(cellId, cellAutoPointer); // if it doesn't already exist if (!ok) { cellAutoPointer.TakeOwnership(new PolygonType); m_PointSetSeries[0]->SetCell(cellId, cellAutoPointer); CellDataType cellData; cellData.selected = true; cellData.selectedLines.clear(); cellData.closed = false; m_PointSetSeries[0]->SetCellData(cellId, cellData); } } break; case OpDELETECELL: { - mitk::LineOperation *lineOp = dynamic_cast(operation); + auto *lineOp = dynamic_cast(operation); if (lineOp == nullptr) // if no lineoperation, then call superclass pointSet { Superclass::ExecuteOperation(operation); } m_PointSetSeries[0]->GetCells()->DeleteIndex((unsigned)lineOp->GetCellId()); m_PointSetSeries[0]->GetCellData()->DeleteIndex((unsigned)lineOp->GetCellId()); } break; case OpCLOSECELL: // sets the bolean flag closed from a specified cell to true. { - mitk::LineOperation *lineOp = dynamic_cast(operation); + auto *lineOp = dynamic_cast(operation); if (lineOp == nullptr) // if no lineoperation, then call superclass pointSet { // then search the selected cell!//TODO Superclass::ExecuteOperation(operation); } bool ok; int cellId = lineOp->GetCellId(); if (cellId < 0) // cellId isn't set { cellId = this->SearchSelectedCell(0); if (cellId < 0) // still not found return; } CellAutoPointer cellAutoPointer; // get directly the celldata!TODO ok = m_PointSetSeries[0]->GetCell(cellId, cellAutoPointer); if (ok) { CellDataType cellData; m_PointSetSeries[0]->GetCellData(cellId, &cellData); cellData.closed = true; m_PointSetSeries[0]->SetCellData(cellId, cellData); } } break; case OpOPENCELL: { - mitk::LineOperation *lineOp = dynamic_cast(operation); + auto *lineOp = dynamic_cast(operation); if (lineOp == nullptr) // if no lineoperation, then call superclass pointSet { Superclass::ExecuteOperation(operation); } bool ok; int cellId = lineOp->GetCellId(); CellAutoPointer cellAutoPointer; ok = m_PointSetSeries[0]->GetCell(cellId, cellAutoPointer); if (ok) { CellDataType cellData; m_PointSetSeries[0]->GetCellData(cellId, &cellData); cellData.closed = false; ; m_PointSetSeries[0]->SetCellData(cellId, cellData); } } break; case OpADDLINE: // inserts the ID of the selected point into the indexes of lines in the // selected cell afterwars the added line is selected { - mitk::LineOperation *lineOp = dynamic_cast(operation); + auto *lineOp = dynamic_cast(operation); int cellId = -1; int pId = -1; if (lineOp == nullptr) { cellId = this->SearchSelectedCell(0); if (cellId == -1) return; pId = this->SearchSelectedPoint(0); if (pId == -1) return; } else { cellId = lineOp->GetCellId(); if (cellId == -1) return; pId = lineOp->GetPIdA(); if (pId == -1) return; } bool ok; CellAutoPointer cellAutoPointer; ok = m_PointSetSeries[0]->GetCell(cellId, cellAutoPointer); if (ok) { CellType *cell = cellAutoPointer.GetPointer(); if (cell->GetType() == CellType::POLYGON_CELL) { - PolygonType *polygon = static_cast(cell); + auto *polygon = static_cast(cell); // add the pointId to the Cell. filling the empty cell with // one id doesn't mean to add a line, it means, that the // initilal PointId is set. The next addition of a pointId adds // a line polygon->AddPointId(pId); // select the line, if we really added a line, so now have more than // 1 pointID in the cell CellDataType cellData; ok = m_PointSetSeries[0]->GetCellData(cellId, &cellData); if (ok) { // A line between point 0 and 1 has the Id 0. A line between // 1 and 2 has a Id = 1. So we add getnumberofpoints-2. if (polygon->GetNumberOfPoints() > 1) cellData.selectedLines.push_back(polygon->GetNumberOfPoints() - 2); } m_PointSetSeries[0]->SetCellData(cellId, cellData); m_PointSetSeries[0]->SetCell(cellId, cellAutoPointer); } } } break; case OpDELETELINE: { // deleted the last line through removing the index PIdA // (if set to -1, use the last point) in the given cellId - mitk::LineOperation *lineOp = dynamic_cast(operation); + auto *lineOp = dynamic_cast(operation); int cellId = -1; int pId = -1; if (lineOp == nullptr) { cellId = this->SearchSelectedCell(0); if (cellId == -1) return; pId = this->SearchSelectedPoint(0); } else { cellId = lineOp->GetCellId(); if (cellId == -1) return; pId = lineOp->GetPIdA(); } bool ok; CellAutoPointer cellAutoPointer; ok = m_PointSetSeries[0]->GetCell(cellId, cellAutoPointer); if (ok) { CellType *cell = cellAutoPointer.GetPointer(); if (cell->GetType() == CellType::POLYGON_CELL) { - PolygonType *oldPolygon = static_cast(cell); + auto *oldPolygon = static_cast(cell); auto newPolygonCell = new PolygonType; CellAutoPointer newCell; newCell.TakeOwnership(newPolygonCell); PointIdConstIterator it, oldend; oldend = oldPolygon->PointIdsEnd(); if (pId >= 0) { for (it = oldPolygon->PointIdsBegin(); it != oldend; ++it) { if ((*it) != (MeshType::PointIdentifier)pId) { newPolygonCell->AddPointId(*it); } } } else { --oldend; for (it = oldPolygon->PointIdsBegin(); it != oldend; ++it) newPolygonCell->AddPointId(*it); } oldPolygon->SetPointIds(0, newPolygonCell->GetNumberOfPoints(), newPolygonCell->PointIdsBegin()); } } } break; case OpREMOVELINE: // Remove the given Index in the given cell through copying everything // into a new cell accept the one that has to be deleted. { - mitk::LineOperation *lineOp = dynamic_cast(operation); + auto *lineOp = dynamic_cast(operation); if (lineOp == nullptr) // if no lineoperation, then call superclass pointSet { Superclass::ExecuteOperation(operation); } bool ok; CellAutoPointer cellAutoPointer; int cellId = lineOp->GetCellId(); ok = m_PointSetSeries[0]->GetCell(cellId, cellAutoPointer); if (!ok) return; CellType *cell = cellAutoPointer.GetPointer(); CellAutoPointer newCellAutoPointer; newCellAutoPointer.TakeOwnership(new PolygonType); - PolygonType *newPolygon = static_cast(cell); + auto *newPolygon = static_cast(cell); PointIdIterator it = cell->PointIdsBegin(); PointIdIterator end = cell->PointIdsEnd(); int pointId = lineOp->GetPIdA(); if (pointId < 0) // if not initialized!! return; while (it != end) { if ((*it) == (unsigned int)pointId) { break; } else { newPolygon->AddPointId(*it); } ++it; } while (it != end) { newPolygon->AddPointId(*it); it++; } m_PointSetSeries[0]->SetCell(cellId, newCellAutoPointer); } break; case OpINSERTLINE: // //insert line between two other points. ////before A--B after A--C--B // //the points A, B and C have to be in the pointset. // //needed: CellId, Id of C , Id A and Id B ////the cell has to exist! //{ // mitk::LineOperation *lineOp = dynamic_cast(operation); // if (lineOp == nullptr)//if no lineoperation, then call superclass pointSet // { // Superclass::ExecuteOperation(operation); // } // int cellId = lineOp->GetCellId(); // int pIdC = lineOp->GetPIdC(); // int pIdA = lineOp->GetPIdA(); // int pIdB = lineOp->GetPIdB(); // //the points of the given PointIds have to exist in the PointSet // bool ok; // ok = m_PointSetSeries[0]->GetPoints()->IndexExists(pIdA); // if (!ok) // return; // ok = m_PointSetSeries[0]->GetPoints()->IndexExists(pIdB); // if (!ok) // return; // ok = m_PointSetSeries[0]->GetPoints()->IndexExists(pIdC); // if (!ok) // return; // // so the points do exist. So now check, if there is already a cell // // with the given Id // DataType::CellAutoPointer cell; // ok = m_PointSetSeries[0]->GetCell(cellId, cell); // if (!ok) // return; // //pIdA and pIdB should exist in the cell // // PointIdIterator pit = cell->PointIdsBegin(); // PointIdIterator end = cell->PointIdsEnd(); // // //now arrange the new Ids in the cell like desired; pIdC between // // pIdA and pIdB // unsigned int nuPoints = cell->GetNumberOfPoints(); // std::vector newPoints; // pit = cell->PointIdsBegin(); // end = cell->PointIdsEnd(); // int i = 0; // while( pit != end ) // { // if ((*pit) = pIdA) // { // //now we have found the place to add pIdC after // newPoints[i] = (*pit); // i++; // newPoints[i] = pIdC; // } // else // newPoints[i] = (*pit); // pit++; // } // //now we have the Ids, that existed before combined with the new ones // //so delete the old cell // //doesn't seem to be necessary! // //cell->ClearPoints(); // pit = cell->PointIdsBegin(); // cell->SetPointIds(pit); //} break; case OpMOVELINE: //(moves two points) { - mitk::LineOperation *lineOp = dynamic_cast(operation); + auto *lineOp = dynamic_cast(operation); if (lineOp == nullptr) { mitk::StatusBar::GetInstance()->DisplayText( "Message from mitkMesh: Recieved wrong type of operation! See mitkMeshInteractor.cpp", 10000); return; } // create two operations out of the one operation and call superclass // through the transmitted pointIds get the koordinates of the points. // then add the transmitted vestor to them // create two operations and send them to superclass Point3D pointA, pointB; pointA.Fill(0.0); pointB.Fill(0.0); m_PointSetSeries[0]->GetPoint(lineOp->GetPIdA(), &pointA); m_PointSetSeries[0]->GetPoint(lineOp->GetPIdB(), &pointB); pointA[0] += lineOp->GetVector()[0]; pointA[1] += lineOp->GetVector()[1]; pointA[2] += lineOp->GetVector()[2]; pointB[0] += lineOp->GetVector()[0]; pointB[1] += lineOp->GetVector()[1]; pointB[2] += lineOp->GetVector()[2]; auto operationA = new mitk::PointOperation(OpMOVE, pointA, lineOp->GetPIdA()); auto operationB = new mitk::PointOperation(OpMOVE, pointB, lineOp->GetPIdB()); Superclass::ExecuteOperation(operationA); Superclass::ExecuteOperation(operationB); } break; case OpSELECTLINE: //(select the given line) { - mitk::LineOperation *lineOp = dynamic_cast(operation); + auto *lineOp = dynamic_cast(operation); if (lineOp == nullptr) // if no lineoperation, then call superclass pointSet { Superclass::ExecuteOperation(operation); } int cellId = lineOp->GetCellId(); CellAutoPointer cellAutoPointer; bool ok = m_PointSetSeries[0]->GetCell(cellId, cellAutoPointer); if (ok) { CellDataType cellData; m_PointSetSeries[0]->GetCellData(cellId, &cellData); SelectedLinesType *selectedLines = &(cellData.selectedLines); auto position = std::find(selectedLines->begin(), selectedLines->end(), (unsigned int)lineOp->GetId()); if (position == selectedLines->end()) // if not alsready selected { cellData.selectedLines.push_back(lineOp->GetId()); } m_PointSetSeries[0]->SetCellData(lineOp->GetCellId(), cellData); } } break; case OpDESELECTLINE: //(deselect the given line) { - mitk::LineOperation *lineOp = dynamic_cast(operation); + auto *lineOp = dynamic_cast(operation); if (lineOp == nullptr) { Superclass::ExecuteOperation(operation); } int cellId = lineOp->GetCellId(); CellAutoPointer cellAutoPointer; bool ok = m_PointSetSeries[0]->GetCell(cellId, cellAutoPointer); if (ok) { CellDataType cellData; m_PointSetSeries[0]->GetCellData(cellId, &cellData); SelectedLinesType *selectedLines = &(cellData.selectedLines); auto position = std::find(selectedLines->begin(), selectedLines->end(), (unsigned int)lineOp->GetId()); if (position != selectedLines->end()) // if found { selectedLines->erase(position); } m_PointSetSeries[0]->SetCellData(cellId, cellData); } } break; case OpSELECTCELL: //(select the given cell) { - mitk::LineOperation *lineOp = dynamic_cast(operation); + auto *lineOp = dynamic_cast(operation); if (lineOp == nullptr) // if no lineoperation, then call superclass pointSet { Superclass::ExecuteOperation(operation); } int cellId = lineOp->GetCellId(); CellAutoPointer cellAutoPointer; // directly get the data!//TODO bool ok = m_PointSetSeries[0]->GetCell(cellId, cellAutoPointer); if (ok) { CellDataType cellData; m_PointSetSeries[0]->GetCellData(cellId, &cellData); cellData.selected = true; m_PointSetSeries[0]->SetCellData(cellId, cellData); } } break; case OpDESELECTCELL: //(deselect the given cell) { - mitk::LineOperation *lineOp = dynamic_cast(operation); + auto *lineOp = dynamic_cast(operation); if (lineOp == nullptr) // if no lineoperation, then call superclass pointSet { Superclass::ExecuteOperation(operation); } int cellId = lineOp->GetCellId(); CellAutoPointer cellAutoPointer; bool ok = m_PointSetSeries[0]->GetCell(cellId, cellAutoPointer); if (ok) { CellDataType cellData; m_PointSetSeries[0]->GetCellData(cellId, &cellData); cellData.selected = false; m_PointSetSeries[0]->SetCellData(cellId, cellData); } } break; case OpMOVECELL: // moves all Points of one cell according to the given vector { - mitk::CellOperation *lineOp = dynamic_cast(operation); + auto *lineOp = dynamic_cast(operation); if (lineOp == nullptr) // if no celloperation, then call superclass pointSet { Superclass::ExecuteOperation(operation); } int cellId = lineOp->GetCellId(); Vector3D vector = lineOp->GetVector(); // get the cell CellAutoPointer cellAutoPointer; bool ok = m_PointSetSeries[0]->GetCell(cellId, cellAutoPointer); if (!ok) return; CellDataType cellData; m_PointSetSeries[0]->GetCellData(cellId, &cellData); // iterate through the pointIds of the CellData and move those points in // the pointset PointIdIterator it = cellAutoPointer->PointIdsBegin(); PointIdIterator end = cellAutoPointer->PointIdsEnd(); while (it != end) { unsigned int position = (*it); PointType point; point.Fill(0); m_PointSetSeries[0]->GetPoint(position, &point); point = point + vector; m_PointSetSeries[0]->SetPoint(position, point); ++it; } } break; default: // if the operation couldn't be handled here, then send it to superclass Superclass::ExecuteOperation(operation); return; } // to tell the mappers, that the data is modifierd and has to be updated this->Modified(); mitk::OperationEndEvent endevent(operation); ((const itk::Object *)this)->InvokeEvent(endevent); // As discussed lately, don't mess with rendering from inside data structures //*todo has to be done here, cause of update-pipeline not working yet // mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } mitk::Mesh::DataType::BoundingBoxPointer mitk::Mesh::GetBoundingBoxFromCell(unsigned long cellId, int t) { // itk::CellInterface has also a GetBoundingBox, but it // returns CoordRepType [PointDimension *2] DataType::BoundingBoxPointer bBoxPointer = nullptr; CellAutoPointer cellAutoPointer; if (m_PointSetSeries[t]->GetCell(cellId, cellAutoPointer)) { DataType::PointsContainerPointer pointsContainer = DataType::PointsContainer::New(); PointIdIterator bbIt = cellAutoPointer.GetPointer()->PointIdsBegin(); PointIdIterator bbEnd = cellAutoPointer.GetPointer()->PointIdsEnd(); while (bbIt != bbEnd) { mitk::PointSet::PointType point; bool pointOk = m_PointSetSeries[t]->GetPoint((*bbIt), &point); if (pointOk) pointsContainer->SetElement((*bbIt), point); ++bbIt; } bBoxPointer = DataType::BoundingBoxType::New(); bBoxPointer->SetPoints(pointsContainer); bBoxPointer->ComputeBoundingBox(); } return bBoxPointer; } diff --git a/Modules/DataTypesExt/src/mitkOrganTypeProperty.cpp b/Modules/DataTypesExt/src/mitkOrganTypeProperty.cpp index e159c02e9b..dc8cdce685 100644 --- a/Modules/DataTypesExt/src/mitkOrganTypeProperty.cpp +++ b/Modules/DataTypesExt/src/mitkOrganTypeProperty.cpp @@ -1,120 +1,120 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkOrganTypeProperty.h" mitk::OrganTypeProperty::OrganTypeProperty() { AddEnumerationTypes(); } mitk::OrganTypeProperty::OrganTypeProperty(const IdType &value) { AddEnumerationTypes(); if (IsValidEnumerationValue(value)) { SetValue(value); } else { SetValue(0); } } mitk::OrganTypeProperty::OrganTypeProperty(const std::string &value) { AddEnumerationTypes(); if (IsValidEnumerationValue(value)) { SetValue(value); } else { SetValue("undefined"); } } mitk::OrganTypeProperty::~OrganTypeProperty() { } void mitk::OrganTypeProperty::AddEnumerationTypes() { - IdType newId = static_cast(EnumerationProperty::Size()); + auto newId = static_cast(EnumerationProperty::Size()); // On changes, please also change mitk::DataNodeFactory::DefaultColorForOrgan() AddEnum("undefined", newId++); AddEnum("Ankle", newId++); AddEnum("Appendix", newId++); AddEnum("Blood vessels", newId++); AddEnum("Bone", newId++); AddEnum("Brain", newId++); AddEnum("Bronchial tree", newId++); AddEnum("Coccyx", newId++); AddEnum("Colon", newId++); AddEnum("Cyst", newId++); AddEnum("Elbow", newId++); AddEnum("Eye", newId++); AddEnum("Fallopian tube", newId++); AddEnum("Fat", newId++); AddEnum("Gall bladder", newId++); AddEnum("Hand", newId++); AddEnum("Heart", newId++); AddEnum("Hip", newId++); AddEnum("Hippocampus", newId++); AddEnum("Kidney", newId++); AddEnum("Knee", newId++); AddEnum("Larynx", newId++); AddEnum("Liver", newId++); AddEnum("Lung", newId++); AddEnum("Lymph node", newId++); AddEnum("Muscle", newId++); AddEnum("Nerve", newId++); AddEnum("Nose", newId++); AddEnum("Oesophagus", newId++); AddEnum("Ovaries", newId++); AddEnum("Pancreas", newId++); AddEnum("Pelvis", newId++); AddEnum("Penis", newId++); AddEnum("Pharynx", newId++); AddEnum("Prostate", newId++); AddEnum("Rectum", newId++); AddEnum("Sacrum", newId++); AddEnum("Seminal vesicle", newId++); AddEnum("Shoulder", newId++); AddEnum("Spinal cord", newId++); AddEnum("Spleen", newId++); AddEnum("Stomach", newId++); AddEnum("Teeth", newId++); AddEnum("Testicles", newId++); AddEnum("Thyroid", newId++); AddEnum("Tongue", newId++); AddEnum("Tumor", newId++); AddEnum("Urethra", newId++); AddEnum("Urinary bladder", newId++); AddEnum("Uterus", newId++); AddEnum("Vagina", newId++); AddEnum("Vertebra", newId++); AddEnum("Wrist", newId++); } itk::LightObject::Pointer mitk::OrganTypeProperty::InternalClone() const { itk::LightObject::Pointer result(new Self(*this)); result->UnRegister(); return result; } diff --git a/Modules/DataTypesExt/src/mitkSurfaceDeformationDataInteractor3D.cpp b/Modules/DataTypesExt/src/mitkSurfaceDeformationDataInteractor3D.cpp index bf4c365a11..580aebd20a 100644 --- a/Modules/DataTypesExt/src/mitkSurfaceDeformationDataInteractor3D.cpp +++ b/Modules/DataTypesExt/src/mitkSurfaceDeformationDataInteractor3D.cpp @@ -1,305 +1,305 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkSurfaceDeformationDataInteractor3D.h" #include "mitkMouseWheelEvent.h" #include #include #include mitk::SurfaceDeformationDataInteractor3D::SurfaceDeformationDataInteractor3D() : m_GaussSigma(30.0) { m_OriginalPolyData = vtkPolyData::New(); // Initialize vector arithmetic m_ObjectNormal[0] = 0.0; m_ObjectNormal[1] = 0.0; m_ObjectNormal[2] = 1.0; } mitk::SurfaceDeformationDataInteractor3D::~SurfaceDeformationDataInteractor3D() { m_OriginalPolyData->Delete(); } void mitk::SurfaceDeformationDataInteractor3D::ConnectActionsAndFunctions() { // **Conditions** that can be used in the state machine, to ensure that certain conditions are met, before // actually executing an action CONNECT_CONDITION("isOverObject", CheckOverObject); // **Function** in the statmachine patterns also referred to as **Actions** CONNECT_FUNCTION("selectObject", SelectObject); CONNECT_FUNCTION("deselectObject", DeselectObject); CONNECT_FUNCTION("initDeformation", InitDeformation); CONNECT_FUNCTION("deformObject", DeformObject); CONNECT_FUNCTION("scaleRadius", ScaleRadius); } void mitk::SurfaceDeformationDataInteractor3D::DataNodeChanged() { if (this->GetDataNode() != nullptr) { m_Surface = dynamic_cast(this->GetDataNode()->GetData()); if (m_Surface == nullptr) MITK_ERROR << "SurfaceDeformationDataInteractor3D::DataNodeChanged(): DataNode has to contain a surface."; } else m_Surface = nullptr; } bool mitk::SurfaceDeformationDataInteractor3D::CheckOverObject(const InteractionEvent *interactionEvent) { - const InteractionPositionEvent *positionEvent = dynamic_cast(interactionEvent); + const auto *positionEvent = dynamic_cast(interactionEvent); if (positionEvent == nullptr) return false; Point2D currentPickedDisplayPoint = positionEvent->GetPointerPositionOnScreen(); Point3D currentPickedPoint; if (interactionEvent->GetSender()->PickObject(currentPickedDisplayPoint, currentPickedPoint) == this->GetDataNode()) { // Colorized surface at current picked position m_SurfaceColorizationCenter = currentPickedPoint; return true; } return false; } void mitk::SurfaceDeformationDataInteractor3D::SelectObject(StateMachineAction *, InteractionEvent *interactionEvent) { - const InteractionPositionEvent *positionEvent = dynamic_cast(interactionEvent); + const auto *positionEvent = dynamic_cast(interactionEvent); if (positionEvent == nullptr) return; int timeStep = interactionEvent->GetSender()->GetTimeStep(this->GetDataNode()->GetData()); vtkPolyData *polyData = m_Surface->GetVtkPolyData(timeStep); this->GetDataNode()->SetColor(1.0, 0.0, 0.0); // Colorize surface / wireframe dependend on distance from picked point this->ColorizeSurface(polyData, timeStep, m_SurfaceColorizationCenter, COLORIZATION_GAUSS); interactionEvent->GetSender()->GetRenderingManager()->RequestUpdateAll(); } void mitk::SurfaceDeformationDataInteractor3D::DeselectObject(StateMachineAction *, InteractionEvent *interactionEvent) { - const InteractionPositionEvent *positionEvent = dynamic_cast(interactionEvent); + const auto *positionEvent = dynamic_cast(interactionEvent); if (positionEvent == nullptr) return; int timeStep = interactionEvent->GetSender()->GetTimeStep(this->GetDataNode()->GetData()); vtkPolyData *polyData = m_Surface->GetVtkPolyData(timeStep); this->GetDataNode()->SetColor(1.0, 1.0, 1.0); // Colorize surface / wireframe as inactive this->ColorizeSurface(polyData, timeStep, m_SurfaceColorizationCenter, COLORIZATION_CONSTANT, -1.0); interactionEvent->GetSender()->GetRenderingManager()->RequestUpdateAll(); } void mitk::SurfaceDeformationDataInteractor3D::InitDeformation(StateMachineAction *, InteractionEvent *interactionEvent) { - const InteractionPositionEvent *positionEvent = dynamic_cast(interactionEvent); + const auto *positionEvent = dynamic_cast(interactionEvent); if (positionEvent == nullptr) return; int timeStep = interactionEvent->GetSender()->GetTimeStep(this->GetDataNode()->GetData()); vtkPolyData *polyData = m_Surface->GetVtkPolyData(timeStep); // Store current picked point mitk::Point2D currentPickedDisplayPoint = positionEvent->GetPointerPositionOnScreen(); interactionEvent->GetSender()->PickObject(currentPickedDisplayPoint, m_InitialPickedPoint); vtkInteractorObserver::ComputeDisplayToWorld(interactionEvent->GetSender()->GetVtkRenderer(), currentPickedDisplayPoint[0], currentPickedDisplayPoint[1], 0.0, // m_InitialInteractionPickedPoint[2], m_InitialPickedWorldPoint); // Make deep copy of vtkPolyData interacted on m_OriginalPolyData->DeepCopy(polyData); } void mitk::SurfaceDeformationDataInteractor3D::DeformObject(StateMachineAction *, InteractionEvent *interactionEvent) { - const InteractionPositionEvent *positionEvent = dynamic_cast(interactionEvent); + const auto *positionEvent = dynamic_cast(interactionEvent); if (positionEvent == nullptr) return; int timeStep = interactionEvent->GetSender()->GetTimeStep(this->GetDataNode()->GetData()); vtkPolyData *polyData = m_Surface->GetVtkPolyData(timeStep); BaseGeometry::Pointer geometry = this->GetDataNode()->GetData()->GetGeometry(timeStep); double currentWorldPoint[4]; mitk::Point2D currentDisplayPoint = positionEvent->GetPointerPositionOnScreen(); vtkInteractorObserver::ComputeDisplayToWorld(interactionEvent->GetSender()->GetVtkRenderer(), currentDisplayPoint[0], currentDisplayPoint[1], 0.0, // m_InitialInteractionPickedPoint[2], currentWorldPoint); // Calculate mouse move in 3D space Vector3D interactionMove; interactionMove[0] = currentWorldPoint[0] - m_InitialPickedWorldPoint[0]; interactionMove[1] = currentWorldPoint[1] - m_InitialPickedWorldPoint[1]; interactionMove[2] = currentWorldPoint[2] - m_InitialPickedWorldPoint[2]; // Transform mouse move into geometry space this->GetDataNode()->GetData()->UpdateOutputInformation(); // make sure that the Geometry is up-to-date Vector3D interactionMoveIndex; geometry->WorldToIndex(interactionMove, interactionMoveIndex); // Get picked point and transform into local coordinates Point3D pickedPoint; geometry->WorldToIndex(m_InitialPickedPoint, pickedPoint); Vector3D v1 = pickedPoint.GetVectorFromOrigin(); vtkDataArray *normal = polyData->GetPointData()->GetVectors("planeNormal"); if (normal != nullptr) { m_ObjectNormal[0] = normal->GetComponent(0, 0); m_ObjectNormal[1] = normal->GetComponent(0, 1); m_ObjectNormal[2] = normal->GetComponent(0, 2); } Vector3D v2 = m_ObjectNormal * (interactionMoveIndex * m_ObjectNormal); vtkPoints *originalPoints = m_OriginalPolyData->GetPoints(); vtkPoints *deformedPoints = polyData->GetPoints(); double denom = m_GaussSigma * m_GaussSigma * 2; double point[3]; for (vtkIdType i = 0; i < deformedPoints->GetNumberOfPoints(); ++i) { // Get original point double *originalPoint = originalPoints->GetPoint(i); Vector3D v0; v0[0] = originalPoint[0]; v0[1] = originalPoint[1]; v0[2] = originalPoint[2]; // Calculate distance of this point from line through picked point double d = itk::CrossProduct(m_ObjectNormal, (v1 - v0)).GetNorm(); Vector3D t = v2 * exp(-d * d / denom); point[0] = originalPoint[0] + t[0]; point[1] = originalPoint[1] + t[1]; point[2] = originalPoint[2] + t[2]; deformedPoints->SetPoint(i, point); } // Make sure that surface is colorized at initial picked position as long as we are in deformation state m_SurfaceColorizationCenter = m_InitialPickedPoint; polyData->Modified(); m_Surface->Modified(); interactionEvent->GetSender()->GetRenderingManager()->RequestUpdateAll(); } void mitk::SurfaceDeformationDataInteractor3D::ScaleRadius(StateMachineAction *, InteractionEvent *interactionEvent) { - const MouseWheelEvent *wheelEvent = dynamic_cast(interactionEvent); + const auto *wheelEvent = dynamic_cast(interactionEvent); if (wheelEvent == nullptr) return; m_GaussSigma += (double)(wheelEvent->GetWheelDelta()) / 20; if (m_GaussSigma < 10.0) { m_GaussSigma = 10.0; } else if (m_GaussSigma > 128.0) { m_GaussSigma = 128.0; } int timeStep = interactionEvent->GetSender()->GetTimeStep(this->GetDataNode()->GetData()); vtkPolyData *polyData = m_Surface->GetVtkPolyData(timeStep); // Colorize surface / wireframe dependend on sigma and distance from picked point this->ColorizeSurface(polyData, timeStep, m_SurfaceColorizationCenter, COLORIZATION_GAUSS); interactionEvent->GetSender()->GetRenderingManager()->RequestUpdateAll(); } void mitk::SurfaceDeformationDataInteractor3D::ColorizeSurface( vtkPolyData *polyData, int timeStep, const Point3D &pickedPoint, int mode, double scalar) { if (polyData == nullptr) return; vtkPoints *points = polyData->GetPoints(); vtkPointData *pointData = polyData->GetPointData(); if (pointData == nullptr) return; vtkDataArray *scalars = pointData->GetScalars(); if (scalars == nullptr) return; if (mode == COLORIZATION_GAUSS) { // Get picked point and transform into local coordinates Point3D localPickedPoint; BaseGeometry::Pointer geometry = this->GetDataNode()->GetData()->GetGeometry(timeStep); geometry->WorldToIndex(pickedPoint, localPickedPoint); Vector3D v1 = localPickedPoint.GetVectorFromOrigin(); vtkDataArray *normal = polyData->GetPointData()->GetVectors("planeNormal"); if (normal != nullptr) { m_ObjectNormal[0] = normal->GetComponent(0, 0); m_ObjectNormal[1] = normal->GetComponent(0, 1); m_ObjectNormal[2] = normal->GetComponent(0, 2); } double denom = m_GaussSigma * m_GaussSigma * 2; for (vtkIdType i = 0; i < points->GetNumberOfPoints(); ++i) { // Get original point double *point = points->GetPoint(i); Vector3D v0; v0[0] = point[0]; v0[1] = point[1]; v0[2] = point[2]; // Calculate distance of this point from line through picked point double d = itk::CrossProduct(m_ObjectNormal, (v1 - v0)).GetNorm(); double t = exp(-d * d / denom); scalars->SetComponent(i, 0, t); } } else if (mode == COLORIZATION_CONSTANT) { for (vtkIdType i = 0; i < pointData->GetNumberOfTuples(); ++i) { scalars->SetComponent(i, 0, scalar); } } polyData->Modified(); pointData->Update(); } diff --git a/Modules/DataTypesExt/src/mitkUnstructuredGrid.cpp b/Modules/DataTypesExt/src/mitkUnstructuredGrid.cpp index bab9b2bce7..a6bb8c2171 100644 --- a/Modules/DataTypesExt/src/mitkUnstructuredGrid.cpp +++ b/Modules/DataTypesExt/src/mitkUnstructuredGrid.cpp @@ -1,269 +1,269 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkUnstructuredGrid.h" #include void mitk::UnstructuredGrid::SetVtkUnstructuredGrid(vtkUnstructuredGrid *grid, unsigned int t) { this->Expand(t + 1); if (m_GridSeries[t] != nullptr) { m_GridSeries[t]->Delete(); } m_GridSeries[t] = grid; // call m_VtkPolyData->Register(nullptr) to tell the reference counting that we // want to keep a reference on the object if (m_GridSeries[t] != nullptr) m_GridSeries[t]->Register(grid); this->Modified(); m_CalculateBoundingBox = true; } void mitk::UnstructuredGrid::Expand(unsigned int timeSteps) { // check if the vector is long enough to contain the new element // at the given position. If not, expand it with sufficient zero-filled elements. if (timeSteps > m_GridSeries.size()) { Superclass::Expand(timeSteps); vtkUnstructuredGrid *pdnull = nullptr; m_GridSeries.resize(timeSteps, pdnull); m_CalculateBoundingBox = true; } } void mitk::UnstructuredGrid::ClearData() { for (auto it = m_GridSeries.begin(); it != m_GridSeries.end(); ++it) { if ((*it) != nullptr) (*it)->Delete(); } m_GridSeries.clear(); Superclass::ClearData(); } void mitk::UnstructuredGrid::InitializeEmpty() { vtkUnstructuredGrid *pdnull = nullptr; m_GridSeries.resize(1, pdnull); Superclass::InitializeTimeGeometry(1); m_Initialized = true; } vtkUnstructuredGrid *mitk::UnstructuredGrid::GetVtkUnstructuredGrid(unsigned int t) { if (t < m_GridSeries.size()) { vtkUnstructuredGrid *grid = m_GridSeries[t]; if ((grid == nullptr) && (GetSource().GetPointer() != nullptr)) { RegionType requestedregion; requestedregion.SetIndex(3, t); requestedregion.SetSize(3, 1); SetRequestedRegion(&requestedregion); GetSource()->Update(); } grid = m_GridSeries[t]; return grid; } else return nullptr; } void mitk::UnstructuredGrid::Graft(const DataObject *data) { - const UnstructuredGrid *grid = dynamic_cast(data); + const auto *grid = dynamic_cast(data); if (grid == nullptr) mitkThrow() << "Data object used to graft surface is not a mitk::Surface."; this->CopyInformation(data); m_GridSeries.clear(); for (unsigned int i = 0; i < grid->m_GridSeries.size(); ++i) { m_GridSeries.push_back(vtkUnstructuredGrid::New()); m_GridSeries.back()->DeepCopy(const_cast(grid)->GetVtkUnstructuredGrid(i)); } } mitk::UnstructuredGrid::UnstructuredGrid() : m_CalculateBoundingBox(false) { this->InitializeEmpty(); } mitk::UnstructuredGrid::UnstructuredGrid(const mitk::UnstructuredGrid &other) : BaseData(other), m_LargestPossibleRegion(other.m_LargestPossibleRegion), m_CalculateBoundingBox(other.m_CalculateBoundingBox) { if (!other.m_Initialized) { this->InitializeEmpty(); } else { m_GridSeries = other.m_GridSeries; m_Initialized = other.m_Initialized; } this->SetRequestedRegion(const_cast(&other)); } mitk::UnstructuredGrid::~UnstructuredGrid() { this->ClearData(); } void mitk::UnstructuredGrid::UpdateOutputInformation() { if (this->GetSource()) { this->GetSource()->UpdateOutputInformation(); } if ((m_CalculateBoundingBox) && (m_GridSeries.size() > 0)) CalculateBoundingBox(); else GetTimeGeometry()->Update(); } void mitk::UnstructuredGrid::CalculateBoundingBox() { // // first make sure, that the associated time sliced geometry has // the same number of geometry 3d's as vtkUnstructuredGrids are present // TimeGeometry *timeGeometry = GetTimeGeometry(); if (timeGeometry->CountTimeSteps() != m_GridSeries.size()) { itkExceptionMacro(<< "timeGeometry->CountTimeSteps() != m_GridSeries.size() -- use Initialize(timeSteps) with " "correct number of timeSteps!"); } // // Iterate over the vtkUnstructuredGrids and update the Geometry // information of each of the items. // for (unsigned int i = 0; i < m_GridSeries.size(); ++i) { vtkUnstructuredGrid *grid = m_GridSeries[i]; double bounds[] = {0.0, 0.0, 0.0, 0.0, 0.0, 0.0}; if ((grid != nullptr) && (grid->GetNumberOfCells() > 0)) { // grid->Update(); //VTK6_TODO grid->ComputeBounds(); grid->GetBounds(bounds); } mitk::BaseGeometry::Pointer g3d = timeGeometry->GetGeometryForTimeStep(i); assert(g3d.IsNotNull()); g3d->SetFloatBounds(bounds); } timeGeometry->Update(); mitk::BoundingBox::Pointer bb = const_cast(timeGeometry->GetBoundingBoxInWorld()); itkDebugMacro(<< "boundingbox min: " << bb->GetMinimum()); itkDebugMacro(<< "boundingbox max: " << bb->GetMaximum()); m_CalculateBoundingBox = false; } void mitk::UnstructuredGrid::SetRequestedRegionToLargestPossibleRegion() { m_RequestedRegion = GetLargestPossibleRegion(); } bool mitk::UnstructuredGrid::RequestedRegionIsOutsideOfTheBufferedRegion() { RegionType::IndexValueType end = m_RequestedRegion.GetIndex(3) + m_RequestedRegion.GetSize(3); if (((RegionType::IndexValueType)m_GridSeries.size()) < end) return true; for (RegionType::IndexValueType t = m_RequestedRegion.GetIndex(3); t < end; ++t) if (m_GridSeries[t] == nullptr) return true; return false; } bool mitk::UnstructuredGrid::VerifyRequestedRegion() { if ((m_RequestedRegion.GetIndex(3) >= 0) && (m_RequestedRegion.GetIndex(3) + m_RequestedRegion.GetSize(3) <= m_GridSeries.size())) return true; return false; } void mitk::UnstructuredGrid::SetRequestedRegion(const itk::DataObject *data) { const mitk::UnstructuredGrid *gridData; gridData = dynamic_cast(data); if (gridData) { m_RequestedRegion = gridData->GetRequestedRegion(); } else { // pointer could not be cast back down itkExceptionMacro(<< "mitk::UnstructuredGrid::SetRequestedRegion(DataObject*) cannot cast " << typeid(data).name() << " to " << typeid(UnstructuredGrid *).name()); } } void mitk::UnstructuredGrid::SetRequestedRegion(UnstructuredGrid::RegionType *region) // by arin { if (region != nullptr) { m_RequestedRegion = *region; } else { // pointer could not be cast back down itkExceptionMacro(<< "mitk::UnstructuredGrid::SetRequestedRegion(UnstructuredGrid::RegionType*) cannot cast " << typeid(region).name() << " to " << typeid(UnstructuredGrid *).name()); } } void mitk::UnstructuredGrid::CopyInformation(const itk::DataObject *data) { Superclass::CopyInformation(data); } void mitk::UnstructuredGrid::Update() { if (GetSource().IsNull()) { for (auto it = m_GridSeries.begin(); it != m_GridSeries.end(); ++it) { // if ( ( *it ) != 0 ) // ( *it )->Update(); //VTK6_TODO } } Superclass::Update(); } diff --git a/Modules/DataTypesExt/test/mitkCompressedImageContainerTest.cpp b/Modules/DataTypesExt/test/mitkCompressedImageContainerTest.cpp index 22a511fe6f..137f5e57f8 100644 --- a/Modules/DataTypesExt/test/mitkCompressedImageContainerTest.cpp +++ b/Modules/DataTypesExt/test/mitkCompressedImageContainerTest.cpp @@ -1,186 +1,186 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkCompressedImageContainer.h" #include "mitkCoreObjectFactory.h" #include "mitkIOUtil.h" #include "mitkImageDataItem.h" #include "mitkImageReadAccessor.h" class mitkCompressedImageContainerTestClass { public: static void Test(mitk::CompressedImageContainer *container, mitk::Image *image, unsigned int &numberFailed) { container->SetImage(image); // compress mitk::Image::Pointer uncompressedImage = container->GetImage(); // uncompress // check dimensions if (image->GetDimension() != uncompressedImage->GetDimension()) { ++numberFailed; std::cerr << " (EE) Number of image dimensions wrong after uncompression (was: " << image->GetDimension() << ", now: " << uncompressedImage->GetDimension() << ")" << std::endl; } for (unsigned int dim = 0; dim < image->GetDimension(); ++dim) { if (image->GetDimension(dim) != uncompressedImage->GetDimension(dim)) { ++numberFailed; std::cerr << " (EE) Image dimension " << dim << " differs after uncompression (was: " << image->GetDimension(dim) << ", now: " << uncompressedImage->GetDimension(dim) << ")" << std::endl; } } // check pixel type if (image->GetPixelType() != uncompressedImage->GetPixelType()) { ++numberFailed; std::cerr << " (EE) Pixel type wrong after uncompression:" << std::endl; mitk::PixelType m_PixelType = image->GetPixelType(); std::cout << "Original pixel type:" << std::endl; std::cout << " PixelType: " << m_PixelType.GetTypeAsString() << std::endl; std::cout << " BitsPerElement: " << m_PixelType.GetBpe() << std::endl; std::cout << " NumberOfComponents: " << m_PixelType.GetNumberOfComponents() << std::endl; std::cout << " BitsPerComponent: " << m_PixelType.GetBitsPerComponent() << std::endl; // m_PixelType = uncompressedImage->GetPixelType(); std::cout << "Uncompressed pixel type:" << std::endl; std::cout << " PixelType: " << uncompressedImage->GetPixelType().GetTypeAsString() << std::endl; std::cout << " BitsPerElement: " << uncompressedImage->GetPixelType().GetBpe() << std::endl; std::cout << " NumberOfComponents: " << uncompressedImage->GetPixelType().GetNumberOfComponents() << std::endl; std::cout << " BitsPerComponent: " << uncompressedImage->GetPixelType().GetBitsPerComponent() << std::endl; } // check data mitk::PixelType m_PixelType = image->GetPixelType(); unsigned long oneTimeStepSizeInBytes = m_PixelType.GetBpe() >> 3; // bits per element divided by 8 for (unsigned int dim = 0; dim < image->GetDimension(); ++dim) { if (dim < 3) { oneTimeStepSizeInBytes *= image->GetDimension(dim); } } unsigned int numberOfTimeSteps(1); if (image->GetDimension() > 3) { numberOfTimeSteps = image->GetDimension(3); } for (unsigned int timeStep = 0; timeStep < numberOfTimeSteps; ++timeStep) { mitk::ImageReadAccessor origImgAcc(image, image->GetVolumeData(timeStep)); mitk::ImageReadAccessor unCompImgAcc(uncompressedImage, uncompressedImage->GetVolumeData(timeStep)); - unsigned char *originalData((unsigned char *)origImgAcc.GetData()); - unsigned char *uncompressedData((unsigned char *)unCompImgAcc.GetData()); + auto *originalData((unsigned char *)origImgAcc.GetData()); + auto *uncompressedData((unsigned char *)unCompImgAcc.GetData()); unsigned long difference(0); for (unsigned long byte = 0; byte < oneTimeStepSizeInBytes; ++byte) { if (originalData[byte] != uncompressedData[byte]) { ++difference; } } if (difference > 0) { ++numberFailed; std::cerr << " (EE) Pixel data in timestep " << timeStep << " not identical after uncompression. " << difference << " pixels different." << std::endl; break; // break "for timeStep" } } } }; /// ctest entry point int mitkCompressedImageContainerTest(int argc, char *argv[]) { // one big variable to tell if anything went wrong unsigned int numberFailed(0); // need one parameter (image filename) if (argc == 0) { std::cerr << "No file specified [FAILED]" << std::endl; return EXIT_FAILURE; } // load the image mitk::Image::Pointer image = nullptr; try { std::cout << "Testing with parameter '" << argv[1] << "'" << std::endl; image = dynamic_cast(mitk::IOUtil::Load(argv[1])[0].GetPointer()); } catch (const mitk::Exception &) { std::cout << "File not an image - test will not be applied [PASSED]" << std::endl; std::cout << "[TEST DONE]" << std::endl; return EXIT_SUCCESS; } catch (itk::ExceptionObject &ex) { ++numberFailed; std::cerr << "Exception: " << ex << "[FAILED]" << std::endl; return EXIT_FAILURE; } std::cout << " (II) Could load image." << std::endl; std::cout << "Testing instantiation" << std::endl; // instantiation mitk::CompressedImageContainer::Pointer container = mitk::CompressedImageContainer::New(); if (container.IsNotNull()) { std::cout << " (II) Instantiation works." << std::endl; } else { ++numberFailed; std::cout << "Test failed, and it's the ugliest one!" << std::endl; return EXIT_FAILURE; } // some real work mitkCompressedImageContainerTestClass::Test(container, image, numberFailed); std::cout << "Testing destruction" << std::endl; // freeing container = nullptr; std::cout << " (II) Freeing works." << std::endl; if (numberFailed > 0) { std::cerr << numberFailed << " tests failed." << std::endl; return EXIT_FAILURE; } else { std::cout << "PASSED all tests." << std::endl; return EXIT_SUCCESS; } } diff --git a/Modules/Gizmo/src/mitkGizmo.cpp b/Modules/Gizmo/src/mitkGizmo.cpp index a53477551d..ea68f71a9e 100644 --- a/Modules/Gizmo/src/mitkGizmo.cpp +++ b/Modules/Gizmo/src/mitkGizmo.cpp @@ -1,546 +1,546 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkGizmo.h" #include "mitkGizmoInteractor.h" // MITK includes #include #include #include #include #include // VTK includes #include #include #include #include #include #include #include #include #include #include #include #include #include // ITK includes #include // MicroServices #include namespace { const char *PROPERTY_KEY_ORIGINAL_OBJECT_OPACITY = "gizmo.originalObjectOpacity"; } namespace mitk { //! Private object, removing the Gizmo from data storage along with its manipulated object. class GizmoRemover { public: GizmoRemover() : m_Storage(nullptr), m_GizmoNode(nullptr), m_ManipulatedNode(nullptr), m_StorageObserverTag(0) {} //! Tell the object about the storage that contains both the nodes //! containing the gizmo and the manipulated object. //! //! The method sets up observation of //! - removal of the manipulated node from storage //! - destruction of storage itself void UpdateStorageObservation(mitk::DataStorage *storage, mitk::DataNode *gizmo_node, mitk::DataNode *manipulated_node) { if (m_Storage != nullptr) { m_Storage->RemoveNodeEvent.RemoveListener( mitk::MessageDelegate1(this, &GizmoRemover::OnDataNodeHasBeenRemoved)); m_Storage->RemoveObserver(m_StorageObserverTag); } m_Storage = storage; m_GizmoNode = gizmo_node; m_ManipulatedNode = manipulated_node; if (m_Storage != nullptr) { m_Storage->RemoveNodeEvent.AddListener( mitk::MessageDelegate1(this, &GizmoRemover::OnDataNodeHasBeenRemoved)); itk::SimpleMemberCommand::Pointer command = itk::SimpleMemberCommand::New(); command->SetCallbackFunction(this, &mitk::GizmoRemover::OnDataStorageDeleted); m_StorageObserverTag = m_Storage->AddObserver(itk::ModifiedEvent(), command); } } //! Callback notified on destruction of DataStorage void OnDataStorageDeleted() { m_Storage = nullptr; } //! Callback notified on removal of _any_ object from data storage void OnDataNodeHasBeenRemoved(const mitk::DataNode *node) { if (node == m_ManipulatedNode) { // m_Storage is still alive because it is the emitter if (m_Storage->Exists(m_GizmoNode)) { m_Storage->Remove(m_GizmoNode); // normally, gizmo will be deleted here (unless somebody // still holds a reference to it) } } } //! Clean up our observer registrations ~GizmoRemover() { if (m_Storage) { m_Storage->RemoveNodeEvent.RemoveListener( mitk::MessageDelegate1(this, &GizmoRemover::OnDataNodeHasBeenRemoved)); m_Storage->RemoveObserver(m_StorageObserverTag); } } private: mitk::DataStorage *m_Storage; mitk::DataNode *m_GizmoNode; mitk::DataNode *m_ManipulatedNode; unsigned long m_StorageObserverTag; }; } // namespace MITK bool mitk::Gizmo::HasGizmoAttached(DataNode *node, DataStorage *storage) { auto typeCondition = TNodePredicateDataType::New(); auto gizmoChildren = storage->GetDerivations(node, typeCondition); return !gizmoChildren->empty(); } bool mitk::Gizmo::RemoveGizmoFromNode(DataNode *node, DataStorage *storage) { if (node == nullptr || storage == nullptr) { return false; } auto typeCondition = TNodePredicateDataType::New(); auto gizmoChildren = storage->GetDerivations(node, typeCondition); for (auto &gizmoChild : *gizmoChildren) { - Gizmo *gizmo = dynamic_cast(gizmoChild->GetData()); + auto *gizmo = dynamic_cast(gizmoChild->GetData()); if (gizmo) { storage->Remove(gizmoChild); gizmo->m_GizmoRemover->UpdateStorageObservation(nullptr, nullptr, nullptr); } } //-------------------------------------------------------------- // Restore original opacity if we changed it //-------------------------------------------------------------- float originalOpacity = 1.0; if (node->GetFloatProperty(PROPERTY_KEY_ORIGINAL_OBJECT_OPACITY, originalOpacity)) { node->SetOpacity(originalOpacity); node->GetPropertyList()->DeleteProperty(PROPERTY_KEY_ORIGINAL_OBJECT_OPACITY); } return !gizmoChildren->empty(); } mitk::DataNode::Pointer mitk::Gizmo::AddGizmoToNode(DataNode *node, DataStorage *storage) { assert(node); if (node->GetData() == nullptr || node->GetData()->GetGeometry() == nullptr) { return nullptr; } //-------------------------------------------------------------- // Add visual gizmo that follows the node to be manipulated //-------------------------------------------------------------- auto gizmo = Gizmo::New(); auto gizmoNode = DataNode::New(); gizmoNode->SetName("Gizmo"); gizmoNode->SetData(gizmo); gizmo->FollowGeometry(node->GetData()->GetGeometry()); //-------------------------------------------------------------- // Add interaction to the gizmo //-------------------------------------------------------------- mitk::GizmoInteractor::Pointer interactor = mitk::GizmoInteractor::New(); interactor->LoadStateMachine("Gizmo3DStates.xml", us::GetModuleContext()->GetModule()); interactor->SetEventConfig("Gizmo3DConfig.xml", us::ModuleRegistry::GetModule("MitkGizmo")); interactor->SetGizmoNode(gizmoNode); interactor->SetManipulatedObjectNode(node); //-------------------------------------------------------------- // Note current opacity for later restore and lower it //-------------------------------------------------------------- float currentNodeOpacity = 1.0; if (node->GetOpacity(currentNodeOpacity, nullptr)) { if (currentNodeOpacity > 0.5f) { node->SetFloatProperty(PROPERTY_KEY_ORIGINAL_OBJECT_OPACITY, currentNodeOpacity); node->SetOpacity(0.5f); } } if (storage) { storage->Add(gizmoNode, node); gizmo->m_GizmoRemover->UpdateStorageObservation(storage, gizmoNode, node); } return gizmoNode; } mitk::Gizmo::Gizmo() : Surface(), m_AllowTranslation(true), m_AllowRotation(true), m_AllowScaling(true), m_GizmoRemover(new GizmoRemover()) { m_Center.Fill(0); m_AxisX.Fill(0); m_AxisX[0] = 1; m_AxisY.Fill(0); m_AxisY[1] = 1; m_AxisZ.Fill(0); m_AxisZ[2] = 1; m_Radius.Fill(1); UpdateRepresentation(); } mitk::Gizmo::~Gizmo() { if (m_FollowedGeometry.IsNotNull()) { m_FollowedGeometry->RemoveObserver(m_FollowerTag); } } void mitk::Gizmo::UpdateRepresentation() { /* bounding box around the unscaled bounding object */ ScalarType bounds[6] = {-m_Radius[0] * 1.2, +m_Radius[0] * 1.2, -m_Radius[1] * 1.2, +m_Radius[1] * 1.2, -m_Radius[2] * 1.2, +m_Radius[2] * 1.2}; GetGeometry()->SetBounds(bounds); GetTimeGeometry()->Update(); SetVtkPolyData(BuildGizmo()); } namespace { void AssignScalarValueTo(vtkPolyData *polydata, char value) { vtkSmartPointer pointData = vtkSmartPointer::New(); int numberOfPoints = polydata->GetNumberOfPoints(); pointData->SetNumberOfComponents(1); pointData->SetNumberOfTuples(numberOfPoints); pointData->FillComponent(0, value); polydata->GetPointData()->SetScalars(pointData); } vtkSmartPointer BuildAxis(const mitk::Point3D ¢er, const mitk::Vector3D &axis, double halflength, bool drawRing, char vertexValueAxis, char vertexValueRing, char vertexValueScale) { // Define all sizes relative to absolute size (thus that the gizmo will appear // in the same relative size for huge (size >> 1) and tiny (size << 1) objects). // This means that the gizmo will appear very different when a scene contains _both_ // huge and tiny objects at the same time, but when the users zooms in on his // object of interest, the gizmo will always have the same relative size. const double shaftRadius = halflength * 0.02; const double arrowHeight = shaftRadius * 6; const int tubeSides = 15; // poly data appender to collect cones and tube that make up the axis vtkSmartPointer axisSource = vtkSmartPointer::New(); // build two cones at the end of axis for (double sign = -1.0; sign < 3.0; sign += 2) { vtkSmartPointer cone = vtkConeSource::New(); // arrow tips at 110% of radius cone->SetCenter(center[0] + sign * axis[0] * (halflength * 1.1 + arrowHeight * 0.5), center[1] + sign * axis[1] * (halflength * 1.1 + arrowHeight * 0.5), center[2] + sign * axis[2] * (halflength * 1.1 + arrowHeight * 0.5)); cone->SetDirection(sign * axis[0], sign * axis[1], sign * axis[2]); cone->SetRadius(shaftRadius * 3); cone->SetHeight(arrowHeight); cone->SetResolution(tubeSides); cone->CappingOn(); cone->Update(); AssignScalarValueTo(cone->GetOutput(), vertexValueScale); axisSource->AddInputData(cone->GetOutput()); } // build the axis itself (as a tube around the line defining the axis) vtkSmartPointer shaftSkeleton = vtkSmartPointer::New(); vtkSmartPointer shaftPoints = vtkSmartPointer::New(); shaftPoints->InsertPoint(0, (center - axis * halflength * 1.1).GetDataPointer()); shaftPoints->InsertPoint(1, (center + axis * halflength * 1.1).GetDataPointer()); shaftSkeleton->SetPoints(shaftPoints); vtkSmartPointer shaftLines = vtkSmartPointer::New(); vtkIdType shaftLinePoints[] = {0, 1}; shaftLines->InsertNextCell(2, shaftLinePoints); shaftSkeleton->SetLines(shaftLines); vtkSmartPointer shaftSource = vtkSmartPointer::New(); shaftSource->SetInputData(shaftSkeleton); shaftSource->SetNumberOfSides(tubeSides); shaftSource->SetVaryRadiusToVaryRadiusOff(); shaftSource->SetRadius(shaftRadius); shaftSource->Update(); AssignScalarValueTo(shaftSource->GetOutput(), vertexValueAxis); axisSource->AddInputData(shaftSource->GetOutput()); axisSource->Update(); vtkSmartPointer ringSource; // used after if block, so declare it here if (drawRing) { // build the ring orthogonal to the axis (as another tube) vtkSmartPointer ringSkeleton = vtkSmartPointer::New(); vtkSmartPointer ringPoints = vtkSmartPointer::New(); ringPoints->SetDataTypeToDouble(); // just some decision (see cast below) unsigned int numberOfRingPoints = 100; vtkSmartPointer ringLines = vtkSmartPointer::New(); ringLines->InsertNextCell(numberOfRingPoints + 1); mitk::Vector3D ringPointer; for (unsigned int segment = 0; segment < numberOfRingPoints; ++segment) { ringPointer[0] = 0; ringPointer[1] = std::cos((double)(segment) / (double)numberOfRingPoints * 2.0 * vtkMath::Pi()); ringPointer[2] = std::sin((double)(segment) / (double)numberOfRingPoints * 2.0 * vtkMath::Pi()); ringPoints->InsertPoint(segment, (ringPointer * halflength).GetDataPointer()); ringLines->InsertCellPoint(segment); } ringLines->InsertCellPoint(0); // transform ring points (copied from vtkConeSource) vtkSmartPointer t = vtkSmartPointer::New(); t->Translate(center.GetDataPointer()); double vMag = vtkMath::Norm(axis.GetDataPointer()); if (axis[0] < 0.0) { // flip x -> -x to avoid instability t->RotateWXYZ(180.0, (axis[0] - vMag) / 2.0, axis[1] / 2.0, axis[2] / 2.0); t->RotateWXYZ(180.0, 0, 1, 0); } else { t->RotateWXYZ(180.0, (axis[0] + vMag) / 2.0, axis[1] / 2.0, axis[2] / 2.0); } double thisPoint[3]; for (unsigned int i = 0; i < numberOfRingPoints; ++i) { ringPoints->GetPoint(i, thisPoint); t->TransformPoint(thisPoint, thisPoint); ringPoints->SetPoint(i, thisPoint); } ringSkeleton->SetPoints(ringPoints); ringSkeleton->SetLines(ringLines); ringSource = vtkSmartPointer::New(); ringSource->SetInputData(ringSkeleton); ringSource->SetNumberOfSides(tubeSides); ringSource->SetVaryRadiusToVaryRadiusOff(); ringSource->SetRadius(shaftRadius); ringSource->Update(); AssignScalarValueTo(ringSource->GetOutput(), vertexValueRing); } // assemble axis and ring vtkSmartPointer appenderGlobal = vtkSmartPointer::New(); appenderGlobal->AddInputData(axisSource->GetOutput()); if (drawRing) { appenderGlobal->AddInputData(ringSource->GetOutput()); } appenderGlobal->Update(); // make everything shiny by adding normals vtkSmartPointer normalsSource = vtkSmartPointer::New(); normalsSource->SetInputConnection(appenderGlobal->GetOutputPort()); normalsSource->ComputePointNormalsOn(); normalsSource->ComputeCellNormalsOff(); normalsSource->SplittingOn(); normalsSource->Update(); vtkSmartPointer result = normalsSource->GetOutput(); return result; } } // unnamed namespace double mitk::Gizmo::GetLongestRadius() const { double longestAxis = std::max(m_Radius[0], m_Radius[1]); longestAxis = std::max(longestAxis, m_Radius[2]); return longestAxis; } vtkSmartPointer mitk::Gizmo::BuildGizmo() { double longestAxis = GetLongestRadius(); vtkSmartPointer appender = vtkSmartPointer::New(); appender->AddInputData(BuildAxis(m_Center, m_AxisX, longestAxis, m_AllowRotation, m_AllowTranslation ? MoveAlongAxisX : NoHandle, m_AllowRotation ? RotateAroundAxisX : NoHandle, m_AllowScaling ? ScaleX : NoHandle)); appender->AddInputData(BuildAxis(m_Center, m_AxisY, longestAxis, m_AllowRotation, m_AllowTranslation ? MoveAlongAxisY : NoHandle, m_AllowRotation ? RotateAroundAxisY : NoHandle, m_AllowScaling ? ScaleY : NoHandle)); appender->AddInputData(BuildAxis(m_Center, m_AxisZ, longestAxis, m_AllowRotation, m_AllowTranslation ? MoveAlongAxisZ : NoHandle, m_AllowRotation ? RotateAroundAxisZ : NoHandle, m_AllowScaling ? ScaleZ : NoHandle)); auto sphereSource = vtkSmartPointer::New(); sphereSource->SetCenter(m_Center[0], m_Center[1], m_Center[2]); sphereSource->SetRadius(longestAxis * 0.06); sphereSource->Update(); AssignScalarValueTo(sphereSource->GetOutput(), MoveFreely); appender->AddInputData(sphereSource->GetOutput()); appender->Update(); return appender->GetOutput(); } void mitk::Gizmo::FollowGeometry(BaseGeometry *geom) { auto observer = itk::SimpleMemberCommand::New(); observer->SetCallbackFunction(this, &Gizmo::OnFollowedGeometryModified); if (m_FollowedGeometry.IsNotNull()) { m_FollowedGeometry->RemoveObserver(m_FollowerTag); } m_FollowedGeometry = geom; m_FollowerTag = m_FollowedGeometry->AddObserver(itk::ModifiedEvent(), observer); // initial adjustment OnFollowedGeometryModified(); } void mitk::Gizmo::OnFollowedGeometryModified() { m_Center = m_FollowedGeometry->GetCenter(); m_AxisX = m_FollowedGeometry->GetAxisVector(0); m_AxisY = m_FollowedGeometry->GetAxisVector(1); m_AxisZ = m_FollowedGeometry->GetAxisVector(2); m_AxisX.Normalize(); m_AxisY.Normalize(); m_AxisZ.Normalize(); for (int dim = 0; dim < 3; ++dim) { m_Radius[dim] = 0.5 * m_FollowedGeometry->GetExtentInMM(dim); } UpdateRepresentation(); } mitk::Gizmo::HandleType mitk::Gizmo::GetHandleFromPointDataValue(double value) { #define CheckHandleType(type) \ if (static_cast(value) == static_cast(type)) \ return type; CheckHandleType(MoveFreely); CheckHandleType(MoveAlongAxisX); CheckHandleType(MoveAlongAxisY); CheckHandleType(MoveAlongAxisZ); CheckHandleType(RotateAroundAxisX); CheckHandleType(RotateAroundAxisY); CheckHandleType(RotateAroundAxisZ); CheckHandleType(ScaleX); CheckHandleType(ScaleY); CheckHandleType(ScaleZ); return NoHandle; #undef CheckHandleType } mitk::Gizmo::HandleType mitk::Gizmo::GetHandleFromPointID(vtkIdType id) { assert(GetVtkPolyData()); assert(GetVtkPolyData()->GetPointData()); assert(GetVtkPolyData()->GetPointData()->GetScalars()); double dataValue = GetVtkPolyData()->GetPointData()->GetScalars()->GetTuple1(id); return GetHandleFromPointDataValue(dataValue); } std::string mitk::Gizmo::HandleTypeToString(HandleType type) { #define CheckHandleType(candidateType) \ if (type == candidateType) \ return std::string(#candidateType); CheckHandleType(MoveFreely); CheckHandleType(MoveAlongAxisX); CheckHandleType(MoveAlongAxisY); CheckHandleType(MoveAlongAxisZ); CheckHandleType(RotateAroundAxisX); CheckHandleType(RotateAroundAxisY); CheckHandleType(RotateAroundAxisZ); CheckHandleType(ScaleX); CheckHandleType(ScaleY); CheckHandleType(ScaleZ); CheckHandleType(NoHandle); return "InvalidHandleType"; #undef CheckHandleType } diff --git a/Modules/IOExt/Internal/mitkPlyFileWriterService.cpp b/Modules/IOExt/Internal/mitkPlyFileWriterService.cpp index 09bc6d9e6d..3dbfaf2eb7 100644 --- a/Modules/IOExt/Internal/mitkPlyFileWriterService.cpp +++ b/Modules/IOExt/Internal/mitkPlyFileWriterService.cpp @@ -1,59 +1,59 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ // MITK #include "mitkPlyFileWriterService.h" #include #include // VTK #include #include #include #include mitk::PlyFileWriterService::PlyFileWriterService() : AbstractFileWriter(mitk::Surface::GetStaticNameOfClass(), CustomMimeType(IOMimeTypes::STANFORD_PLY_MIMETYPE()), "Stanford Triangle PLY Writer") { RegisterService(); } mitk::PlyFileWriterService::PlyFileWriterService(const mitk::PlyFileWriterService &other) : AbstractFileWriter(other) { } mitk::PlyFileWriterService::~PlyFileWriterService() { } void mitk::PlyFileWriterService::Write() { vtkSmartPointer writer = vtkSmartPointer::New(); writer->SetFileTypeToBinary(); writer->SetFileName(GetOutputLocation().c_str()); - const mitk::Surface *surface = static_cast(this->GetInput()); + const auto *surface = static_cast(this->GetInput()); writer->SetInputDataObject(surface->GetVtkPolyData()); writer->Write(); } mitk::PlyFileWriterService *mitk::PlyFileWriterService::Clone() const { return new PlyFileWriterService(*this); } diff --git a/Modules/ImageExtraction/mitkExtractDirectedPlaneImageFilter.cpp b/Modules/ImageExtraction/mitkExtractDirectedPlaneImageFilter.cpp index 94978bfb18..fcb734e783 100644 --- a/Modules/ImageExtraction/mitkExtractDirectedPlaneImageFilter.cpp +++ b/Modules/ImageExtraction/mitkExtractDirectedPlaneImageFilter.cpp @@ -1,481 +1,481 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkExtractDirectedPlaneImageFilter.h" #include "mitkAbstractTransformGeometry.h" //#include "mitkImageMapperGL2D.h" #include "vtkMitkThickSlicesFilter.h" #include #include #include #include #include #include #include #include #include #include mitk::ExtractDirectedPlaneImageFilter::ExtractDirectedPlaneImageFilter() : m_WorldGeometry(nullptr) { MITK_WARN << "Class ExtractDirectedPlaneImageFilter is deprecated! Use ExtractSliceFilter instead."; m_Reslicer = vtkImageReslice::New(); m_TargetTimestep = 0; m_InPlaneResampleExtentByGeometry = true; m_ResliceInterpolationProperty = nullptr; // VtkResliceInterpolationProperty::New(); //TODO initial with value m_ThickSlicesMode = 0; m_ThickSlicesNum = 1; } mitk::ExtractDirectedPlaneImageFilter::~ExtractDirectedPlaneImageFilter() { if (m_ResliceInterpolationProperty != nullptr) m_ResliceInterpolationProperty->Delete(); m_Reslicer->Delete(); } void mitk::ExtractDirectedPlaneImageFilter::GenerateData() { // A world geometry must be set... if (m_WorldGeometry == nullptr) { itkWarningMacro(<< "No world geometry has been set. Returning."); return; } - Image *input = const_cast(this->GetInput()); + auto *input = const_cast(this->GetInput()); input->Update(); if (input == nullptr) { itkWarningMacro(<< "No input set."); return; } const TimeGeometry *inputTimeGeometry = input->GetTimeGeometry(); if ((inputTimeGeometry == nullptr) || (inputTimeGeometry->CountTimeSteps() == 0)) { itkWarningMacro(<< "Error reading input image geometry."); return; } // Get the target timestep; if none is set, use the lowest given. unsigned int timestep = m_TargetTimestep; if (inputTimeGeometry->IsValidTimeStep(timestep) == false) { itkWarningMacro(<< "This is not a valid timestep: " << timestep); return; } // check if there is something to display. if (!input->IsVolumeSet(timestep)) { itkWarningMacro(<< "No volume data existent at given timestep " << timestep); return; } Image::RegionType requestedRegion = input->GetLargestPossibleRegion(); requestedRegion.SetIndex(3, timestep); requestedRegion.SetSize(3, 1); requestedRegion.SetSize(4, 1); input->SetRequestedRegion(&requestedRegion); input->Update(); vtkImageData *inputData = input->GetVtkImageData(timestep); if (inputData == nullptr) { itkWarningMacro(<< "Could not extract vtk image data for given timestep" << timestep); return; } double spacing[3]; inputData->GetSpacing(spacing); // how big the area is in physical coordinates: widthInMM x heightInMM pixels mitk::ScalarType widthInMM, heightInMM; // where we want to sample Point3D origin; Vector3D right, bottom, normal; Vector3D rightInIndex, bottomInIndex; assert(input->GetTimeGeometry() == inputTimeGeometry); // take transform of input image into account BaseGeometry *inputGeometry = inputTimeGeometry->GetGeometryForTimeStep(timestep); if (inputGeometry == nullptr) { itkWarningMacro(<< "There is no Geometry3D at given timestep " << timestep); return; } ScalarType mmPerPixel[2]; // Bounds information for reslicing (only required if reference geometry // is present) double bounds[6]; bool boundsInitialized = false; for (auto &bound : bounds) { bound = 0.0; } Vector2D extent; extent.Fill(0.0); // Do we have a simple PlaneGeometry? if (dynamic_cast(m_WorldGeometry) != nullptr && dynamic_cast(m_WorldGeometry) == nullptr) { - const PlaneGeometry *planeGeometry = static_cast(m_WorldGeometry); + const auto *planeGeometry = static_cast(m_WorldGeometry); origin = planeGeometry->GetOrigin(); right = planeGeometry->GetAxisVector(0); bottom = planeGeometry->GetAxisVector(1); normal = planeGeometry->GetNormal(); if (m_InPlaneResampleExtentByGeometry) { // Resampling grid corresponds to the current world geometry. This // means that the spacing of the output 2D image depends on the // currently selected world geometry, and *not* on the image itself. extent[0] = m_WorldGeometry->GetExtent(0); extent[1] = m_WorldGeometry->GetExtent(1); } else { // Resampling grid corresponds to the input geometry. This means that // the spacing of the output 2D image is directly derived from the // associated input image, regardless of the currently selected world // geometry. inputGeometry->WorldToIndex(right, rightInIndex); inputGeometry->WorldToIndex(bottom, bottomInIndex); extent[0] = rightInIndex.GetNorm(); extent[1] = bottomInIndex.GetNorm(); } // Get the extent of the current world geometry and calculate resampling // spacing therefrom. widthInMM = m_WorldGeometry->GetExtentInMM(0); heightInMM = m_WorldGeometry->GetExtentInMM(1); mmPerPixel[0] = widthInMM / extent[0]; mmPerPixel[1] = heightInMM / extent[1]; right.Normalize(); bottom.Normalize(); normal.Normalize(); // origin += right * ( mmPerPixel[0] * 0.5 ); // origin += bottom * ( mmPerPixel[1] * 0.5 ); // widthInMM -= mmPerPixel[0]; // heightInMM -= mmPerPixel[1]; // Use inverse transform of the input geometry for reslicing the 3D image m_Reslicer->SetResliceTransform(inputGeometry->GetVtkTransform()->GetLinearInverse()); // Set background level to TRANSLUCENT (see PlaneGeometryDataVtkMapper3D) m_Reslicer->SetBackgroundLevel(-32768); // Check if a reference geometry does exist (as would usually be the case for // PlaneGeometry). // Note: this is currently not strictly required, but could facilitate // correct plane clipping. if (m_WorldGeometry->GetReferenceGeometry()) { // Calculate the actual bounds of the transformed plane clipped by the // dataset bounding box; this is required for drawing the texture at the // correct position during 3D mapping. boundsInitialized = this->CalculateClippedPlaneBounds(m_WorldGeometry->GetReferenceGeometry(), planeGeometry, bounds); } } // Do we have an AbstractTransformGeometry? else if (dynamic_cast(m_WorldGeometry)) { - const mitk::AbstractTransformGeometry *abstractGeometry = + const auto *abstractGeometry = dynamic_cast(m_WorldGeometry); extent[0] = abstractGeometry->GetParametricExtent(0); extent[1] = abstractGeometry->GetParametricExtent(1); widthInMM = abstractGeometry->GetParametricExtentInMM(0); heightInMM = abstractGeometry->GetParametricExtentInMM(1); mmPerPixel[0] = widthInMM / extent[0]; mmPerPixel[1] = heightInMM / extent[1]; origin = abstractGeometry->GetPlane()->GetOrigin(); right = abstractGeometry->GetPlane()->GetAxisVector(0); right.Normalize(); bottom = abstractGeometry->GetPlane()->GetAxisVector(1); bottom.Normalize(); normal = abstractGeometry->GetPlane()->GetNormal(); normal.Normalize(); // Use a combination of the InputGeometry *and* the possible non-rigid // AbstractTransformGeometry for reslicing the 3D Image vtkGeneralTransform *composedResliceTransform = vtkGeneralTransform::New(); composedResliceTransform->Identity(); composedResliceTransform->Concatenate(inputGeometry->GetVtkTransform()->GetLinearInverse()); composedResliceTransform->Concatenate(abstractGeometry->GetVtkAbstractTransform()); m_Reslicer->SetResliceTransform(composedResliceTransform); // Set background level to BLACK instead of translucent, to avoid // boundary artifacts (see PlaneGeometryDataVtkMapper3D) m_Reslicer->SetBackgroundLevel(-1023); composedResliceTransform->Delete(); } else { itkWarningMacro(<< "World Geometry has to be a PlaneGeometry or an AbstractTransformGeometry."); return; } // Make sure that the image to be resliced has a certain minimum size. if ((extent[0] <= 2) && (extent[1] <= 2)) { itkWarningMacro(<< "Image is too small to be resliced..."); return; } vtkSmartPointer unitSpacingImageFilter = vtkImageChangeInformation::New(); unitSpacingImageFilter->SetOutputSpacing(1.0, 1.0, 1.0); unitSpacingImageFilter->SetInputData(inputData); m_Reslicer->SetInputConnection(unitSpacingImageFilter->GetOutputPort()); // m_Reslicer->SetInput( inputData ); m_Reslicer->SetOutputDimensionality(2); m_Reslicer->SetOutputOrigin(0.0, 0.0, 0.0); Vector2D pixelsPerMM; pixelsPerMM[0] = 1.0 / mmPerPixel[0]; pixelsPerMM[1] = 1.0 / mmPerPixel[1]; // calulate the originArray and the orientations for the reslice-filter double originArray[3]; itk2vtk(origin, originArray); m_Reslicer->SetResliceAxesOrigin(originArray); double cosines[9]; // direction of the X-axis of the sampled result vnl2vtk(right.GetVnlVector(), cosines); // direction of the Y-axis of the sampled result vnl2vtk(bottom.GetVnlVector(), cosines + 3); // normal of the plane vnl2vtk(normal.GetVnlVector(), cosines + 6); m_Reslicer->SetResliceAxesDirectionCosines(cosines); int xMin, xMax, yMin, yMax; if (boundsInitialized) { xMin = static_cast(bounds[0] / mmPerPixel[0]); //+ 0.5 ); xMax = static_cast(bounds[1] / mmPerPixel[0]); //+ 0.5 ); yMin = static_cast(bounds[2] / mmPerPixel[1]); //+ 0.5); yMax = static_cast(bounds[3] / mmPerPixel[1]); //+ 0.5 ); } else { // If no reference geometry is available, we also don't know about the // maximum plane size; so the overlap is just ignored xMin = yMin = 0; xMax = static_cast(extent[0] - pixelsPerMM[0]); //+ 0.5 ); yMax = static_cast(extent[1] - pixelsPerMM[1]); //+ 0.5 ); } m_Reslicer->SetOutputSpacing(mmPerPixel[0], mmPerPixel[1], 1.0); // xMax and yMax are meant exclusive until now, whereas // SetOutputExtent wants an inclusive bound. Thus, we need // to subtract 1. m_Reslicer->SetOutputExtent(xMin, xMax - 1, yMin, yMax - 1, 0, 1); // Do the reslicing. Modified() is called to make sure that the reslicer is // executed even though the input geometry information did not change; this // is necessary when the input /em data, but not the /em geometry changes. m_Reslicer->Modified(); m_Reslicer->ReleaseDataFlagOn(); m_Reslicer->Update(); // 1. Check the result vtkImageData *reslicedImage = m_Reslicer->GetOutput(); if ((reslicedImage == nullptr) || (reslicedImage->GetDataDimension() < 1)) { itkWarningMacro(<< "Reslicer returned empty image"); return; } unsigned int dimensions[2]; dimensions[0] = (unsigned int)extent[0]; dimensions[1] = (unsigned int)extent[1]; Vector3D spacingVector; FillVector3D(spacingVector, mmPerPixel[0], mmPerPixel[1], 1.0); mitk::Image::Pointer resultImage = this->GetOutput(); resultImage->Initialize(input->GetPixelType(), 2, dimensions); resultImage->SetSpacing(spacingVector); } void mitk::ExtractDirectedPlaneImageFilter::GenerateOutputInformation() { Superclass::GenerateOutputInformation(); } bool mitk::ExtractDirectedPlaneImageFilter::CalculateClippedPlaneBounds(const BaseGeometry *boundingGeometry, const PlaneGeometry *planeGeometry, double *bounds) { // Clip the plane with the bounding geometry. To do so, the corner points // of the bounding box are transformed by the inverse transformation // matrix, and the transformed bounding box edges derived therefrom are // clipped with the plane z=0. The resulting min/max values are taken as // bounds for the image reslicer. const BoundingBox *boundingBox = boundingGeometry->GetBoundingBox(); BoundingBox::PointType bbMin = boundingBox->GetMinimum(); BoundingBox::PointType bbMax = boundingBox->GetMaximum(); vtkPoints *points = vtkPoints::New(); if (boundingGeometry->GetImageGeometry()) { points->InsertPoint(0, bbMin[0] - 0.5, bbMin[1] - 0.5, bbMin[2] - 0.5); points->InsertPoint(1, bbMin[0] - 0.5, bbMin[1] - 0.5, bbMax[2] - 0.5); points->InsertPoint(2, bbMin[0] - 0.5, bbMax[1] - 0.5, bbMax[2] - 0.5); points->InsertPoint(3, bbMin[0] - 0.5, bbMax[1] - 0.5, bbMin[2] - 0.5); points->InsertPoint(4, bbMax[0] - 0.5, bbMin[1] - 0.5, bbMin[2] - 0.5); points->InsertPoint(5, bbMax[0] - 0.5, bbMin[1] - 0.5, bbMax[2] - 0.5); points->InsertPoint(6, bbMax[0] - 0.5, bbMax[1] - 0.5, bbMax[2] - 0.5); points->InsertPoint(7, bbMax[0] - 0.5, bbMax[1] - 0.5, bbMin[2] - 0.5); } else { points->InsertPoint(0, bbMin[0], bbMin[1], bbMin[2]); points->InsertPoint(1, bbMin[0], bbMin[1], bbMax[2]); points->InsertPoint(2, bbMin[0], bbMax[1], bbMax[2]); points->InsertPoint(3, bbMin[0], bbMax[1], bbMin[2]); points->InsertPoint(4, bbMax[0], bbMin[1], bbMin[2]); points->InsertPoint(5, bbMax[0], bbMin[1], bbMax[2]); points->InsertPoint(6, bbMax[0], bbMax[1], bbMax[2]); points->InsertPoint(7, bbMax[0], bbMax[1], bbMin[2]); } vtkPoints *newPoints = vtkPoints::New(); vtkTransform *transform = vtkTransform::New(); transform->Identity(); transform->Concatenate(planeGeometry->GetVtkTransform()->GetLinearInverse()); transform->Concatenate(boundingGeometry->GetVtkTransform()); transform->TransformPoints(points, newPoints); transform->Delete(); bounds[0] = bounds[2] = 10000000.0; bounds[1] = bounds[3] = -10000000.0; bounds[4] = bounds[5] = 0.0; this->LineIntersectZero(newPoints, 0, 1, bounds); this->LineIntersectZero(newPoints, 1, 2, bounds); this->LineIntersectZero(newPoints, 2, 3, bounds); this->LineIntersectZero(newPoints, 3, 0, bounds); this->LineIntersectZero(newPoints, 0, 4, bounds); this->LineIntersectZero(newPoints, 1, 5, bounds); this->LineIntersectZero(newPoints, 2, 6, bounds); this->LineIntersectZero(newPoints, 3, 7, bounds); this->LineIntersectZero(newPoints, 4, 5, bounds); this->LineIntersectZero(newPoints, 5, 6, bounds); this->LineIntersectZero(newPoints, 6, 7, bounds); this->LineIntersectZero(newPoints, 7, 4, bounds); // clean up vtk data points->Delete(); newPoints->Delete(); if ((bounds[0] > 9999999.0) || (bounds[2] > 9999999.0) || (bounds[1] < -9999999.0) || (bounds[3] < -9999999.0)) { return false; } else { // The resulting bounds must be adjusted by the plane spacing, since we // we have so far dealt with index coordinates const mitk::Vector3D planeSpacing = planeGeometry->GetSpacing(); bounds[0] *= planeSpacing[0]; bounds[1] *= planeSpacing[0]; bounds[2] *= planeSpacing[1]; bounds[3] *= planeSpacing[1]; bounds[4] *= planeSpacing[2]; bounds[5] *= planeSpacing[2]; return true; } } bool mitk::ExtractDirectedPlaneImageFilter::LineIntersectZero(vtkPoints *points, int p1, int p2, double *bounds) { double point1[3]; double point2[3]; points->GetPoint(p1, point1); points->GetPoint(p2, point2); if ((point1[2] * point2[2] <= 0.0) && (point1[2] != point2[2])) { double x, y; x = (point1[0] * point2[2] - point1[2] * point2[0]) / (point2[2] - point1[2]); y = (point1[1] * point2[2] - point1[2] * point2[1]) / (point2[2] - point1[2]); if (x < bounds[0]) { bounds[0] = x; } if (x > bounds[1]) { bounds[1] = x; } if (y < bounds[2]) { bounds[2] = y; } if (y > bounds[3]) { bounds[3] = y; } bounds[4] = bounds[5] = 0.0; return true; } return false; } diff --git a/Modules/ImageStatistics/mitkImageStatisticsCalculator.cpp b/Modules/ImageStatistics/mitkImageStatisticsCalculator.cpp index 71034570bd..7e7e598cbe 100644 --- a/Modules/ImageStatistics/mitkImageStatisticsCalculator.cpp +++ b/Modules/ImageStatistics/mitkImageStatisticsCalculator.cpp @@ -1,629 +1,629 @@ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "itkImageFileWriter.h" namespace mitk { void ImageStatisticsCalculator::SetInputImage(mitk::Image::Pointer image) { if (image != m_Image) { m_Image = image; m_StatisticsByTimeStep.resize(m_Image->GetTimeSteps()); m_StatisticsUpdateTimePerTimeStep.resize(m_Image->GetTimeSteps()); std::fill(m_StatisticsUpdateTimePerTimeStep.begin(), m_StatisticsUpdateTimePerTimeStep.end(), 0); this->Modified(); } } void ImageStatisticsCalculator::SetMask(mitk::MaskGenerator::Pointer mask) { if (mask != m_MaskGenerator) { m_MaskGenerator = mask; this->Modified(); } } void ImageStatisticsCalculator::SetSecondaryMask(mitk::MaskGenerator::Pointer mask) { if (mask != m_SecondaryMaskGenerator) { m_SecondaryMaskGenerator = mask; this->Modified(); } } void ImageStatisticsCalculator::SetNBinsForHistogramStatistics(unsigned int nBins) { if (nBins != m_nBinsForHistogramStatistics) { m_nBinsForHistogramStatistics = nBins; this->Modified(); this->m_UseBinSizeOverNBins = false; } if (m_UseBinSizeOverNBins) { this->Modified(); this->m_UseBinSizeOverNBins = false; } } unsigned int ImageStatisticsCalculator::GetNBinsForHistogramStatistics() const { return m_nBinsForHistogramStatistics; } void ImageStatisticsCalculator::SetBinSizeForHistogramStatistics(double binSize) { if (binSize != m_binSizeForHistogramStatistics) { m_binSizeForHistogramStatistics = binSize; this->Modified(); this->m_UseBinSizeOverNBins = true; } if (!m_UseBinSizeOverNBins) { this->Modified(); this->m_UseBinSizeOverNBins = true; } } double ImageStatisticsCalculator::GetBinSizeForHistogramStatistics() const { return m_binSizeForHistogramStatistics; } ImageStatisticsCalculator::StatisticsContainer::Pointer ImageStatisticsCalculator::GetStatistics(unsigned int timeStep, unsigned int label) { if (timeStep >= m_StatisticsByTimeStep.size()) { mitkThrow() << "invalid timeStep in ImageStatisticsCalculator_v2::GetStatistics"; } if (m_Image.IsNull()) { mitkThrow() << "no image"; } if (!m_Image->IsInitialized()) { mitkThrow() << "Image not initialized!"; } if (IsUpdateRequired(timeStep)) { if (m_MaskGenerator.IsNotNull()) { m_MaskGenerator->SetTimeStep(timeStep); m_InternalMask = m_MaskGenerator->GetMask(); if (m_MaskGenerator->GetReferenceImage().IsNotNull()) { m_InternalImageForStatistics = m_MaskGenerator->GetReferenceImage(); } else { m_InternalImageForStatistics = m_Image; } } else { m_InternalImageForStatistics = m_Image; } if (m_SecondaryMaskGenerator.IsNotNull()) { m_SecondaryMaskGenerator->SetTimeStep(timeStep); m_SecondaryMask = m_SecondaryMaskGenerator->GetMask(); } ImageTimeSelector::Pointer imgTimeSel = ImageTimeSelector::New(); imgTimeSel->SetInput(m_InternalImageForStatistics); imgTimeSel->SetTimeNr(timeStep); imgTimeSel->UpdateLargestPossibleRegion(); m_ImageTimeSlice = imgTimeSel->GetOutput(); // Calculate statistics with/without mask if (m_MaskGenerator.IsNull() && m_SecondaryMaskGenerator.IsNull()) { // 1) calculate statistics unmasked: AccessByItk_1(m_ImageTimeSlice, InternalCalculateStatisticsUnmasked, timeStep) } else { // 2) calculate statistics masked AccessByItk_1(m_ImageTimeSlice, InternalCalculateStatisticsMasked, timeStep) } //this->Modified(); } m_StatisticsUpdateTimePerTimeStep[timeStep] = m_StatisticsByTimeStep[timeStep][m_StatisticsByTimeStep[timeStep].size()-1]->GetMTime(); - for (std::vector::iterator it = m_StatisticsByTimeStep[timeStep].begin(); it != m_StatisticsByTimeStep[timeStep].end(); ++it) + for (auto it = m_StatisticsByTimeStep[timeStep].begin(); it != m_StatisticsByTimeStep[timeStep].end(); ++it) { StatisticsContainer::Pointer statCont = *it; if (statCont->GetLabel() == label) { return statCont->Clone(); } } // these lines will ony be executed if the requested label could not be found! MITK_WARN << "Invalid label: " << label << " in time step: " << timeStep; return StatisticsContainer::New(); } template < typename TPixel, unsigned int VImageDimension > void ImageStatisticsCalculator::InternalCalculateStatisticsUnmasked( typename itk::Image< TPixel, VImageDimension >* image, unsigned int timeStep) { typedef typename itk::Image< TPixel, VImageDimension > ImageType; typedef typename itk::ExtendedStatisticsImageFilter ImageStatisticsFilterType; typedef typename itk::MinMaxImageFilterWithIndex MinMaxFilterType; StatisticsContainer::Pointer statisticsResult = StatisticsContainer::New(); typename ImageStatisticsFilterType::Pointer statisticsFilter = ImageStatisticsFilterType::New(); statisticsFilter->SetInput(image); statisticsFilter->SetCoordinateTolerance(0.001); statisticsFilter->SetDirectionTolerance(0.001); // TODO: this is single threaded. Implement our own image filter that does this multi threaded // typename itk::MinimumMaximumImageCalculator::Pointer imgMinMaxFilter = itk::MinimumMaximumImageCalculator::New(); // imgMinMaxFilter->SetImage(image); // imgMinMaxFilter->Compute(); vnl_vector minIndex, maxIndex; typename MinMaxFilterType::Pointer minMaxFilter = MinMaxFilterType::New(); minMaxFilter->SetInput(image); minMaxFilter->UpdateLargestPossibleRegion(); typename ImageType::PixelType minval = minMaxFilter->GetMin(); typename ImageType::PixelType maxval = minMaxFilter->GetMax(); typename ImageType::IndexType tmpMinIndex = minMaxFilter->GetMinIndex(); typename ImageType::IndexType tmpMaxIndex = minMaxFilter->GetMaxIndex(); // typename ImageType::IndexType tmpMinIndex = imgMinMaxFilter->GetIndexOfMinimum(); // typename ImageType::IndexType tmpMaxIndex = imgMinMaxFilter->GetIndexOfMaximum(); minIndex.set_size(tmpMaxIndex.GetIndexDimension()); maxIndex.set_size(tmpMaxIndex.GetIndexDimension()); for (unsigned int i=0; i < tmpMaxIndex.GetIndexDimension(); i++) { minIndex[i] = tmpMinIndex[i]; maxIndex[i] = tmpMaxIndex[i]; } statisticsResult->SetMinIndex(minIndex); statisticsResult->SetMaxIndex(maxIndex); //convert m_binSize in m_nBins if necessary unsigned int nBinsForHistogram; if (m_UseBinSizeOverNBins) { nBinsForHistogram = std::max(static_cast(std::ceil(maxval - minval)) / m_binSizeForHistogramStatistics, 10.); // do not allow less than 10 bins } else { nBinsForHistogram = m_nBinsForHistogramStatistics; } statisticsFilter->SetHistogramParameters(nBinsForHistogram, minval, maxval); try { statisticsFilter->Update(); } catch (const itk::ExceptionObject& e) { mitkThrow() << "Image statistics calculation failed due to following ITK Exception: \n " << e.what(); } // no mask, therefore just one label = the whole image m_StatisticsByTimeStep[timeStep].resize(1); statisticsResult->SetLabel(1); statisticsResult->SetN(image->GetLargestPossibleRegion().GetNumberOfPixels()); statisticsResult->SetMean(statisticsFilter->GetMean()); statisticsResult->SetMin(statisticsFilter->GetMinimum()); statisticsResult->SetMax(statisticsFilter->GetMaximum()); statisticsResult->SetVariance(statisticsFilter->GetVariance()); statisticsResult->SetStd(statisticsFilter->GetSigma()); statisticsResult->SetSkewness(statisticsFilter->GetSkewness()); statisticsResult->SetKurtosis(statisticsFilter->GetKurtosis()); statisticsResult->SetRMS(std::sqrt(std::pow(statisticsFilter->GetMean(), 2.) + statisticsFilter->GetVariance())); // variance = sigma^2 statisticsResult->SetMPP(statisticsFilter->GetMPP()); statisticsResult->SetEntropy(statisticsFilter->GetEntropy()); statisticsResult->SetMedian(statisticsFilter->GetMedian()); statisticsResult->SetUniformity(statisticsFilter->GetUniformity()); statisticsResult->SetUPP(statisticsFilter->GetUPP()); statisticsResult->SetHistogram(statisticsFilter->GetHistogram()); m_StatisticsByTimeStep[timeStep][0] = statisticsResult; } template < typename TPixel, unsigned int VImageDimension > void ImageStatisticsCalculator::InternalCalculateStatisticsMasked( typename itk::Image< TPixel, VImageDimension >* image, unsigned int timeStep) { typedef itk::Image< TPixel, VImageDimension > ImageType; typedef itk::Image< MaskPixelType, VImageDimension > MaskType; typedef typename MaskType::PixelType LabelPixelType; typedef itk::ExtendedLabelStatisticsImageFilter< ImageType, MaskType > ImageStatisticsFilterType; typedef MaskUtilities< TPixel, VImageDimension > MaskUtilType; typedef typename itk::MinMaxLabelImageFilterWithIndex MinMaxLabelFilterType; typedef typename ImageType::PixelType InputImgPixelType; // workaround: if m_SecondaryMaskGenerator ist not null but m_MaskGenerator is! (this is the case if we request a 'ignore zuero valued pixels' // mask in the gui but do not define a primary mask) bool swapMasks = false; if (m_SecondaryMask.IsNotNull() && m_InternalMask.IsNull()) { m_InternalMask = m_SecondaryMask; m_SecondaryMask = nullptr; swapMasks = true; } // maskImage has to have the same dimension as image typename MaskType::Pointer maskImage = MaskType::New(); try { // try to access the pixel values directly (no copying or casting). Only works if mask pixels are of pixelType unsigned short maskImage = ImageToItkImage< MaskPixelType, VImageDimension >(m_InternalMask); } catch (const itk::ExceptionObject &) { // if the pixel type of the mask is not short, then we have to make a copy of m_InternalMask (and cast the values) CastToItkImage(m_InternalMask, maskImage); } // if we have a secondary mask (say a ignoreZeroPixelMask) we need to combine the masks (corresponds to AND) if (m_SecondaryMask.IsNotNull()) { // dirty workaround for a bug when pf mask + any other mask is used in conjunction. We need a proper fix for this (Fabian Isensee is responsible and probably working on it!) if (m_InternalMask->GetDimension() == 2 && (m_SecondaryMask->GetDimension() == 3 || m_SecondaryMask->GetDimension() == 4)) { mitk::Image::Pointer old_img = m_SecondaryMaskGenerator->GetReferenceImage(); m_SecondaryMaskGenerator->SetInputImage(m_MaskGenerator->GetReferenceImage()); m_SecondaryMask = m_SecondaryMaskGenerator->GetMask(); m_SecondaryMaskGenerator->SetInputImage(old_img); } typename MaskType::Pointer secondaryMaskImage = MaskType::New(); secondaryMaskImage = ImageToItkImage< MaskPixelType, VImageDimension >(m_SecondaryMask); // secondary mask should be a ignore zero value pixel mask derived from image. it has to be cropped to the mask region (which may be planar or simply smaller) typename MaskUtilities::Pointer secondaryMaskMaskUtil = MaskUtilities::New(); secondaryMaskMaskUtil->SetImage(secondaryMaskImage.GetPointer()); secondaryMaskMaskUtil->SetMask(maskImage.GetPointer()); typename MaskType::Pointer adaptedSecondaryMaskImage = secondaryMaskMaskUtil->ExtractMaskImageRegion(); typename itk::MaskImageFilter2::Pointer maskFilter = itk::MaskImageFilter2::New(); maskFilter->SetInput1(maskImage); maskFilter->SetInput2(adaptedSecondaryMaskImage); maskFilter->SetMaskingValue(1); // all pixels of maskImage where secondaryMaskImage==1 will be kept, all the others are set to 0 maskFilter->UpdateLargestPossibleRegion(); maskImage = maskFilter->GetOutput(); } typename MaskUtilType::Pointer maskUtil = MaskUtilType::New(); maskUtil->SetImage(image); maskUtil->SetMask(maskImage.GetPointer()); // if mask is smaller than image, extract the image region where the mask is typename ImageType::Pointer adaptedImage = ImageType::New(); adaptedImage = maskUtil->ExtractMaskImageRegion(); // this also checks mask sanity // find min, max, minindex and maxindex typename MinMaxLabelFilterType::Pointer minMaxFilter = MinMaxLabelFilterType::New(); minMaxFilter->SetInput(adaptedImage); minMaxFilter->SetLabelInput(maskImage); minMaxFilter->UpdateLargestPossibleRegion(); // set histogram parameters for each label individually (min/max may be different for each label) typedef typename std::map MapType; typedef typename std::pair PairType; std::vector relevantLabels = minMaxFilter->GetRelevantLabels(); MapType minVals; MapType maxVals; std::map nBins; for (LabelPixelType label:relevantLabels) { minVals.insert(PairType(label, minMaxFilter->GetMin(label))); maxVals.insert(PairType(label, minMaxFilter->GetMax(label))); unsigned int nBinsForHistogram; if (m_UseBinSizeOverNBins) { nBinsForHistogram = std::max(static_cast(std::ceil(minMaxFilter->GetMax(label) - minMaxFilter->GetMin(label))) / m_binSizeForHistogramStatistics, 10.); // do not allow less than 10 bins } else { nBinsForHistogram = m_nBinsForHistogramStatistics; } nBins.insert(typename std::pair(label, nBinsForHistogram)); } typename ImageStatisticsFilterType::Pointer imageStatisticsFilter = ImageStatisticsFilterType::New(); imageStatisticsFilter->SetDirectionTolerance(0.001); imageStatisticsFilter->SetCoordinateTolerance(0.001); imageStatisticsFilter->SetInput(adaptedImage); imageStatisticsFilter->SetLabelInput(maskImage); imageStatisticsFilter->SetHistogramParametersForLabels(nBins, minVals, maxVals); imageStatisticsFilter->Update(); std::list labels = imageStatisticsFilter->GetRelevantLabels(); - std::list::iterator it = labels.begin(); + auto it = labels.begin(); m_StatisticsByTimeStep[timeStep].resize(0); while(it != labels.end()) { StatisticsContainer::Pointer statisticsResult = StatisticsContainer::New(); // find min, max, minindex and maxindex // make sure to only look in the masked region, use a masker for this vnl_vector minIndex, maxIndex; mitk::Point3D worldCoordinateMin; mitk::Point3D worldCoordinateMax; mitk::Point3D indexCoordinateMin; mitk::Point3D indexCoordinateMax; m_InternalImageForStatistics->GetGeometry()->IndexToWorld(minMaxFilter->GetMinIndex(*it), worldCoordinateMin); m_InternalImageForStatistics->GetGeometry()->IndexToWorld(minMaxFilter->GetMaxIndex(*it), worldCoordinateMax); m_Image->GetGeometry()->WorldToIndex(worldCoordinateMin, indexCoordinateMin); m_Image->GetGeometry()->WorldToIndex(worldCoordinateMax, indexCoordinateMax); minIndex.set_size(3); maxIndex.set_size(3); //for (unsigned int i=0; i < tmpMaxIndex.GetIndexDimension(); i++) for (unsigned int i=0; i < 3; i++) { minIndex[i] = indexCoordinateMin[i]; maxIndex[i] = indexCoordinateMax[i]; } statisticsResult->SetMinIndex(minIndex); statisticsResult->SetMaxIndex(maxIndex); assert(abs(minMaxFilter->GetMax(*it) - imageStatisticsFilter->GetMaximum(*it)) < mitk::eps); assert(abs(minMaxFilter->GetMin(*it) - imageStatisticsFilter->GetMinimum(*it)) < mitk::eps); statisticsResult->SetN(imageStatisticsFilter->GetSum(*it) / (double) imageStatisticsFilter->GetMean(*it)); statisticsResult->SetMean(imageStatisticsFilter->GetMean(*it)); statisticsResult->SetMin(imageStatisticsFilter->GetMinimum(*it)); statisticsResult->SetMax(imageStatisticsFilter->GetMaximum(*it)); statisticsResult->SetVariance(imageStatisticsFilter->GetVariance(*it)); statisticsResult->SetStd(imageStatisticsFilter->GetSigma(*it)); statisticsResult->SetSkewness(imageStatisticsFilter->GetSkewness(*it)); statisticsResult->SetKurtosis(imageStatisticsFilter->GetKurtosis(*it)); statisticsResult->SetRMS(std::sqrt(std::pow(imageStatisticsFilter->GetMean(*it), 2.) + imageStatisticsFilter->GetVariance(*it))); // variance = sigma^2 statisticsResult->SetMPP(imageStatisticsFilter->GetMPP(*it)); statisticsResult->SetLabel(*it); statisticsResult->SetEntropy(imageStatisticsFilter->GetEntropy(*it)); statisticsResult->SetMedian(imageStatisticsFilter->GetMedian(*it)); statisticsResult->SetUniformity(imageStatisticsFilter->GetUniformity(*it)); statisticsResult->SetUPP(imageStatisticsFilter->GetUPP(*it)); statisticsResult->SetHistogram(imageStatisticsFilter->GetHistogram(*it)); m_StatisticsByTimeStep[timeStep].push_back(statisticsResult); ++it; } // swap maskGenerators back if (swapMasks) { m_SecondaryMask = m_InternalMask; m_InternalMask = nullptr; } } bool ImageStatisticsCalculator::IsUpdateRequired(unsigned int timeStep) const { unsigned long thisClassTimeStamp = this->GetMTime(); unsigned long inputImageTimeStamp = m_Image->GetMTime(); unsigned long statisticsTimeStamp = m_StatisticsUpdateTimePerTimeStep[timeStep]; if (thisClassTimeStamp > statisticsTimeStamp) // inputs have changed { return true; } if (inputImageTimeStamp > statisticsTimeStamp) // image has changed { return true; } if (m_MaskGenerator.IsNotNull()) { unsigned long maskGeneratorTimeStamp = m_MaskGenerator->GetMTime(); if (maskGeneratorTimeStamp > statisticsTimeStamp) // there is a mask generator and it has changed { return true; } } if (m_SecondaryMaskGenerator.IsNotNull()) { unsigned long maskGeneratorTimeStamp = m_SecondaryMaskGenerator->GetMTime(); if (maskGeneratorTimeStamp > statisticsTimeStamp) // there is a secondary mask generator and it has changed { return true; } } return false; } ImageStatisticsCalculator::StatisticsContainer::StatisticsContainer(): m_N(0), m_Mean(nan("")), m_Min(nan("")), m_Max(nan("")), m_Std(nan("")), m_Variance(nan("")), m_Skewness(nan("")), m_Kurtosis(nan("")), m_RMS(nan("")), m_MPP(nan("")), m_Median(nan("")), m_Uniformity(nan("")), m_UPP(nan("")), m_Entropy(nan("")) { m_minIndex.set_size(0); m_maxIndex.set_size(0); } ImageStatisticsCalculator::statisticsMapType ImageStatisticsCalculator::StatisticsContainer::GetStatisticsAsMap() { ImageStatisticsCalculator::statisticsMapType statisticsAsMap; statisticsAsMap["N"] = m_N; statisticsAsMap["Mean"] = m_Mean; statisticsAsMap["Min"] = m_Min; statisticsAsMap["Max"] = m_Max; statisticsAsMap["StandardDeviation"] = m_Std; statisticsAsMap["Variance"] = m_Variance; statisticsAsMap["Skewness"] = m_Skewness; statisticsAsMap["Kurtosis"] = m_Kurtosis; statisticsAsMap["RMS"] = m_RMS; statisticsAsMap["MPP"] = m_MPP; statisticsAsMap["Median"] = m_Median; statisticsAsMap["Uniformity"] = m_Uniformity; statisticsAsMap["UPP"] = m_UPP; statisticsAsMap["Entropy"] = m_Entropy; statisticsAsMap["Label"] = m_Label; return statisticsAsMap; } void ImageStatisticsCalculator::StatisticsContainer::Reset() { m_N = 0; m_Mean = nan(""); m_Min = nan(""); m_Max = nan(""); m_Std = nan(""); m_Variance = nan(""); m_Skewness = nan(""); m_Kurtosis = nan(""); m_RMS = nan(""); m_MPP = nan(""); m_Median = nan(""); m_Uniformity = nan(""); m_UPP = nan(""); m_Entropy = nan(""); m_Histogram = HistogramType::New(); m_minIndex.set_size(0); m_maxIndex.set_size(0); m_Label = 0; } void ImageStatisticsCalculator::StatisticsContainer::Print() { ImageStatisticsCalculator::statisticsMapType statMap = this->GetStatisticsAsMap(); // print all map key value pairs // const auto& val:statMap for (auto it = statMap.begin(); it != statMap.end(); ++it) { std::cout << it->first << ": " << it->second << std::endl; } // print the min and max index std::cout << "Min Index:" << std::endl; for (auto it = this->GetMinIndex().begin(); it != this->GetMinIndex().end(); ++it) { std::cout << *it << " "; } std::cout << std::endl; // print the min and max index std::cout << "Max Index:" << std::endl; for (auto it = this->GetMaxIndex().begin(); it != this->GetMaxIndex().end(); ++it) { std::cout << *it << " "; } std::cout << std::endl; } std::string ImageStatisticsCalculator::StatisticsContainer::GetAsString() { std::string res = ""; ImageStatisticsCalculator::statisticsMapType statMap = this->GetStatisticsAsMap(); // print all map key value pairs // const auto& val:statMap for (auto it = statMap.begin(); it != statMap.end(); ++it) { res += std::string(it->first) + ": " + std::to_string(it->second) + "\n"; } // print the min and max index res += "Min Index:" + std::string("\n"); for (auto it = this->GetMinIndex().begin(); it != this->GetMinIndex().end(); it++) { res += std::to_string(*it) + std::string(" "); } res += "\n"; // print the min and max index res += "Max Index:" + std::string("\n"); for (auto it = this->GetMaxIndex().begin(); it != this->GetMaxIndex().end(); it++) { res += std::to_string(*it) + " "; } res += "\n"; return res; } } diff --git a/Modules/ImageStatistics/mitkPlanarFigureMaskGenerator.cpp b/Modules/ImageStatistics/mitkPlanarFigureMaskGenerator.cpp index c0f4cf513f..257bf37e91 100644 --- a/Modules/ImageStatistics/mitkPlanarFigureMaskGenerator.cpp +++ b/Modules/ImageStatistics/mitkPlanarFigureMaskGenerator.cpp @@ -1,529 +1,529 @@ #include #include #include #include "mitkImageAccessByItk.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace mitk { void PlanarFigureMaskGenerator::SetPlanarFigure(mitk::PlanarFigure::Pointer planarFigure) { if ( planarFigure.IsNull() ) { throw std::runtime_error( "Error: planar figure empty!" ); } const PlaneGeometry *planarFigurePlaneGeometry = planarFigure->GetPlaneGeometry(); if ( planarFigurePlaneGeometry == nullptr ) { throw std::runtime_error( "Planar-Figure not yet initialized!" ); } - const PlaneGeometry *planarFigureGeometry = + const auto *planarFigureGeometry = dynamic_cast< const PlaneGeometry * >( planarFigurePlaneGeometry ); if ( planarFigureGeometry == nullptr ) { throw std::runtime_error( "Non-planar planar figures not supported!" ); } if (planarFigure != m_PlanarFigure) { this->Modified(); m_PlanarFigure = planarFigure; } } mitk::Image::Pointer PlanarFigureMaskGenerator::GetReferenceImage() { if (IsUpdateRequired()) { this->CalculateMask(); } return m_ReferenceImage; } template < typename TPixel, unsigned int VImageDimension > void PlanarFigureMaskGenerator::InternalCalculateMaskFromPlanarFigure( const itk::Image< TPixel, VImageDimension > *image, unsigned int axis ) { typedef itk::Image< unsigned short, 2 > MaskImage2DType; typename MaskImage2DType::Pointer maskImage = MaskImage2DType::New(); maskImage->SetOrigin(image->GetOrigin()); maskImage->SetSpacing(image->GetSpacing()); maskImage->SetLargestPossibleRegion(image->GetLargestPossibleRegion()); maskImage->SetBufferedRegion(image->GetBufferedRegion()); maskImage->SetDirection(image->GetDirection()); maskImage->SetNumberOfComponentsPerPixel(image->GetNumberOfComponentsPerPixel()); maskImage->Allocate(); maskImage->FillBuffer(1); // all PolylinePoints of the PlanarFigure are stored in a vtkPoints object. // These points are used by the vtkLassoStencilSource to create // a vtkImageStencil. const mitk::PlaneGeometry *planarFigurePlaneGeometry = m_PlanarFigure->GetPlaneGeometry(); const typename PlanarFigure::PolyLineType planarFigurePolyline = m_PlanarFigure->GetPolyLine( 0 ); const mitk::BaseGeometry *imageGeometry3D = m_inputImage->GetGeometry( 0 ); // If there is a second poly line in a closed planar figure, treat it as a hole. PlanarFigure::PolyLineType planarFigureHolePolyline; if (m_PlanarFigure->GetPolyLinesSize() == 2) planarFigureHolePolyline = m_PlanarFigure->GetPolyLine(1); // Determine x- and y-dimensions depending on principal axis // TODO use plane geometry normal to determine that automatically, then check whether the PF is aligned with one of the three principal axis int i0, i1; switch ( axis ) { case 0: i0 = 1; i1 = 2; break; case 1: i0 = 0; i1 = 2; break; case 2: default: i0 = 0; i1 = 1; break; } // store the polyline contour as vtkPoints object bool outOfBounds = false; vtkSmartPointer points = vtkSmartPointer::New(); typename PlanarFigure::PolyLineType::const_iterator it; for ( it = planarFigurePolyline.begin(); it != planarFigurePolyline.end(); ++it ) { Point3D point3D; // Convert 2D point back to the local index coordinates of the selected // image // Fabian: From PlaneGeometry documentation: // Converts a 2D point given in mm (pt2d_mm) relative to the upper-left corner of the geometry into the corresponding world-coordinate (a 3D point in mm, pt3d_mm). // To convert a 2D point given in units (e.g., pixels in case of an image) into a 2D point given in mm (as required by this method), use IndexToWorld. planarFigurePlaneGeometry->Map( *it, point3D ); // Polygons (partially) outside of the image bounds can not be processed // further due to a bug in vtkPolyDataToImageStencil if ( !imageGeometry3D->IsInside( point3D ) ) { outOfBounds = true; } imageGeometry3D->WorldToIndex( point3D, point3D ); points->InsertNextPoint( point3D[i0], point3D[i1], 0 ); } vtkSmartPointer holePoints = nullptr; if (!planarFigureHolePolyline.empty()) { holePoints = vtkSmartPointer::New(); Point3D point3D; PlanarFigure::PolyLineType::const_iterator end = planarFigureHolePolyline.end(); for (it = planarFigureHolePolyline.begin(); it != end; ++it) { // Fabian: same as above planarFigurePlaneGeometry->Map(*it, point3D); imageGeometry3D->WorldToIndex(point3D, point3D); holePoints->InsertNextPoint(point3D[i0], point3D[i1], 0); } } // mark a malformed 2D planar figure ( i.e. area = 0 ) as out of bounds // this can happen when all control points of a rectangle lie on the same line = two of the three extents are zero double bounds[6] = {0, 0, 0, 0, 0, 0}; points->GetBounds( bounds ); bool extent_x = (fabs(bounds[0] - bounds[1])) < mitk::eps; bool extent_y = (fabs(bounds[2] - bounds[3])) < mitk::eps; bool extent_z = (fabs(bounds[4] - bounds[5])) < mitk::eps; // throw an exception if a closed planar figure is deformed, i.e. has only one non-zero extent if ( m_PlanarFigure->IsClosed() && ((extent_x && extent_y) || (extent_x && extent_z) || (extent_y && extent_z))) { mitkThrow() << "Figure has a zero area and cannot be used for masking."; } if ( outOfBounds ) { throw std::runtime_error( "Figure at least partially outside of image bounds!" ); } // create a vtkLassoStencilSource and set the points of the Polygon vtkSmartPointer lassoStencil = vtkSmartPointer::New(); lassoStencil->SetShapeToPolygon(); lassoStencil->SetPoints( points ); vtkSmartPointer holeLassoStencil = nullptr; if (holePoints.GetPointer() != nullptr) { holeLassoStencil = vtkSmartPointer::New(); holeLassoStencil->SetShapeToPolygon(); holeLassoStencil->SetPoints(holePoints); } // Export from ITK to VTK (to use a VTK filter) typedef itk::VTKImageImport< MaskImage2DType > ImageImportType; typedef itk::VTKImageExport< MaskImage2DType > ImageExportType; typename ImageExportType::Pointer itkExporter = ImageExportType::New(); itkExporter->SetInput( maskImage ); // itkExporter->SetInput( castFilter->GetOutput() ); vtkSmartPointer vtkImporter = vtkSmartPointer::New(); this->ConnectPipelines( itkExporter, vtkImporter ); // Apply the generated image stencil to the input image vtkSmartPointer imageStencilFilter = vtkSmartPointer::New(); imageStencilFilter->SetInputConnection( vtkImporter->GetOutputPort() ); imageStencilFilter->SetStencilConnection(lassoStencil->GetOutputPort()); imageStencilFilter->ReverseStencilOff(); imageStencilFilter->SetBackgroundValue( 0 ); imageStencilFilter->Update(); vtkSmartPointer holeStencilFilter = nullptr; if (holeLassoStencil.GetPointer() != nullptr) { holeStencilFilter = vtkSmartPointer::New(); holeStencilFilter->SetInputConnection(imageStencilFilter->GetOutputPort()); holeStencilFilter->SetStencilConnection(holeLassoStencil->GetOutputPort()); holeStencilFilter->ReverseStencilOn(); holeStencilFilter->SetBackgroundValue(0); holeStencilFilter->Update(); } // Export from VTK back to ITK vtkSmartPointer vtkExporter = vtkSmartPointer::New(); vtkExporter->SetInputConnection( holeStencilFilter.GetPointer() == nullptr ? imageStencilFilter->GetOutputPort() : holeStencilFilter->GetOutputPort()); vtkExporter->Update(); typename ImageImportType::Pointer itkImporter = ImageImportType::New(); this->ConnectPipelines( vtkExporter, itkImporter ); itkImporter->Update(); typedef itk::ImageDuplicator< ImageImportType::OutputImageType > DuplicatorType; DuplicatorType::Pointer duplicator = DuplicatorType::New(); duplicator->SetInputImage( itkImporter->GetOutput() ); duplicator->Update(); // Store mask m_InternalITKImageMask2D = duplicator->GetOutput(); } template < typename TPixel, unsigned int VImageDimension > void PlanarFigureMaskGenerator::InternalCalculateMaskFromOpenPlanarFigure( const itk::Image< TPixel, VImageDimension > *image, unsigned int axis ) { typedef itk::Image< unsigned short, 2 > MaskImage2DType; typedef itk::LineIterator< MaskImage2DType > LineIteratorType; typedef MaskImage2DType::IndexType IndexType2D; typedef std::vector< IndexType2D > IndexVecType; typename MaskImage2DType::Pointer maskImage = MaskImage2DType::New(); maskImage->SetOrigin(image->GetOrigin()); maskImage->SetSpacing(image->GetSpacing()); maskImage->SetLargestPossibleRegion(image->GetLargestPossibleRegion()); maskImage->SetBufferedRegion(image->GetBufferedRegion()); maskImage->SetDirection(image->GetDirection()); maskImage->SetNumberOfComponentsPerPixel(image->GetNumberOfComponentsPerPixel()); maskImage->Allocate(); maskImage->FillBuffer(0); // all PolylinePoints of the PlanarFigure are stored in a vtkPoints object. const mitk::PlaneGeometry *planarFigurePlaneGeometry = m_PlanarFigure->GetPlaneGeometry(); const typename PlanarFigure::PolyLineType planarFigurePolyline = m_PlanarFigure->GetPolyLine( 0 ); const mitk::BaseGeometry *imageGeometry3D = m_inputImage->GetGeometry( 0 ); // Determine x- and y-dimensions depending on principal axis // TODO use plane geometry normal to determine that automatically, then check whether the PF is aligned with one of the three principal axis int i0, i1; switch ( axis ) { case 0: i0 = 1; i1 = 2; break; case 1: i0 = 0; i1 = 2; break; case 2: default: i0 = 0; i1 = 1; break; } int numPolyLines = m_PlanarFigure->GetPolyLinesSize(); for ( int lineId = 0; lineId < numPolyLines; ++lineId ) { // store the polyline contour as vtkPoints object bool outOfBounds = false; IndexVecType pointIndices; typename PlanarFigure::PolyLineType::const_iterator it; for ( it = planarFigurePolyline.begin(); it != planarFigurePolyline.end(); ++it ) { Point3D point3D; planarFigurePlaneGeometry->Map( *it, point3D ); if ( !imageGeometry3D->IsInside( point3D ) ) { outOfBounds = true; } imageGeometry3D->WorldToIndex( point3D, point3D ); IndexType2D index2D; index2D[0] = point3D[i0]; index2D[1] = point3D[i1]; pointIndices.push_back( index2D ); } if ( outOfBounds ) { throw std::runtime_error( "Figure at least partially outside of image bounds!" ); } for ( IndexVecType::const_iterator it = pointIndices.begin(); it != pointIndices.end()-1; ++it ) { IndexType2D ind1 = *it; IndexType2D ind2 = *(it+1); LineIteratorType lineIt( maskImage, ind1, ind2 ); while ( !lineIt.IsAtEnd() ) { lineIt.Set( 1 ); ++lineIt; } } } // Store mask m_InternalITKImageMask2D = maskImage; } bool PlanarFigureMaskGenerator::GetPrincipalAxis( const BaseGeometry *geometry, Vector3D vector, unsigned int &axis ) { vector.Normalize(); for ( unsigned int i = 0; i < 3; ++i ) { Vector3D axisVector = geometry->GetAxisVector( i ); axisVector.Normalize(); if ( fabs( fabs( axisVector * vector ) - 1.0) < mitk::eps ) { axis = i; return true; } } return false; } void PlanarFigureMaskGenerator::CalculateMask() { if (m_inputImage.IsNull()) { MITK_ERROR << "Image is not set."; } if (m_PlanarFigure.IsNull()) { MITK_ERROR << "PlanarFigure is not set."; } if (m_TimeStep != 0) { MITK_WARN << "Multiple TimeSteps are not supported in PlanarFigureMaskGenerator (yet)."; } const BaseGeometry *imageGeometry = m_inputImage->GetGeometry(); if ( imageGeometry == nullptr ) { throw std::runtime_error( "Image geometry invalid!" ); } if (m_inputImage->GetTimeSteps() > 0) { mitk::ImageTimeSelector::Pointer imgTimeSel = mitk::ImageTimeSelector::New(); imgTimeSel->SetInput(m_inputImage); imgTimeSel->SetTimeNr(m_TimeStep); imgTimeSel->UpdateLargestPossibleRegion(); m_InternalTimeSliceImage = imgTimeSel->GetOutput(); } else { m_InternalTimeSliceImage = m_inputImage; } m_InternalITKImageMask2D = nullptr; const PlaneGeometry *planarFigurePlaneGeometry = m_PlanarFigure->GetPlaneGeometry(); - const PlaneGeometry *planarFigureGeometry = dynamic_cast< const PlaneGeometry * >( planarFigurePlaneGeometry ); + const auto *planarFigureGeometry = dynamic_cast< const PlaneGeometry * >( planarFigurePlaneGeometry ); //const BaseGeometry *imageGeometry = m_inputImage->GetGeometry(); // Find principal direction of PlanarFigure in input image unsigned int axis; if ( !this->GetPrincipalAxis( imageGeometry, planarFigureGeometry->GetNormal(), axis ) ) { throw std::runtime_error( "Non-aligned planar figures not supported!" ); } m_PlanarFigureAxis = axis; // Find slice number corresponding to PlanarFigure in input image itk::Image< unsigned short, 3 >::IndexType index; imageGeometry->WorldToIndex( planarFigureGeometry->GetOrigin(), index ); unsigned int slice = index[axis]; // extract image slice which corresponds to the planarFigure and store it in m_InternalImageSlice mitk::Image::Pointer inputImageSlice = extract2DImageSlice(axis, slice); //mitk::IOUtil::Save(inputImageSlice, "/home/fabian/inputSliceImage.nrrd"); // Compute mask from PlanarFigure // rastering for open planar figure: if ( !m_PlanarFigure->IsClosed() ) { AccessFixedDimensionByItk_1(inputImageSlice, InternalCalculateMaskFromOpenPlanarFigure, 2, axis) } else//for closed planar figure { AccessFixedDimensionByItk_1(inputImageSlice, InternalCalculateMaskFromPlanarFigure, 2, axis) } //convert itk mask to mitk::Image::Pointer and return it mitk::Image::Pointer planarFigureMaskImage; planarFigureMaskImage = mitk::GrabItkImageMemory(m_InternalITKImageMask2D); //mitk::IOUtil::Save(planarFigureMaskImage, "/home/fabian/planarFigureMaskImage.nrrd"); //Convert2Dto3DImageFilter::Pointer sliceTo3DImageConverter = Convert2Dto3DImageFilter::New(); //sliceTo3DImageConverter->SetInput(planarFigureMaskImage); //sliceTo3DImageConverter->Update(); //mitk::IOUtil::Save(sliceTo3DImageConverter->GetOutput(), "/home/fabian/3DsliceImage.nrrd"); m_ReferenceImage = inputImageSlice; //mitk::IOUtil::Save(m_ReferenceImage, "/home/fabian/referenceImage.nrrd"); m_InternalMask = planarFigureMaskImage; } void PlanarFigureMaskGenerator::SetTimeStep(unsigned int timeStep) { if (timeStep != m_TimeStep) { m_TimeStep = timeStep; } } mitk::Image::Pointer PlanarFigureMaskGenerator::GetMask() { if (IsUpdateRequired()) { this->CalculateMask(); this->Modified(); } m_InternalMaskUpdateTime = this->GetMTime(); return m_InternalMask; } mitk::Image::Pointer PlanarFigureMaskGenerator::extract2DImageSlice(unsigned int axis, unsigned int slice) { // Extract slice with given position and direction from image unsigned int dimension = m_InternalTimeSliceImage->GetDimension(); mitk::Image::Pointer imageSlice = mitk::Image::New(); if (dimension == 3) { ExtractImageFilter::Pointer imageExtractor = ExtractImageFilter::New(); imageExtractor->SetInput( m_InternalTimeSliceImage ); imageExtractor->SetSliceDimension( axis ); imageExtractor->SetSliceIndex( slice ); imageExtractor->Update(); imageSlice = imageExtractor->GetOutput(); } else if(dimension == 2) { imageSlice = m_InternalTimeSliceImage; } else { MITK_ERROR << "Unsupported image dimension. Dimension is: " << dimension << ". Only 2D and 3D images are supported."; } return imageSlice; } bool PlanarFigureMaskGenerator::IsUpdateRequired() const { unsigned long thisClassTimeStamp = this->GetMTime(); unsigned long internalMaskTimeStamp = m_InternalMask->GetMTime(); unsigned long planarFigureTimeStamp = m_PlanarFigure->GetMTime(); unsigned long inputImageTimeStamp = m_inputImage->GetMTime(); if (thisClassTimeStamp > m_InternalMaskUpdateTime) // inputs have changed { return true; } if (m_InternalMaskUpdateTime < planarFigureTimeStamp || m_InternalMaskUpdateTime < inputImageTimeStamp) // mask image has changed outside of this class { return true; } if (internalMaskTimeStamp > m_InternalMaskUpdateTime) // internal mask has been changed outside of this class { return true; } return false; } } diff --git a/Modules/IpPicSupportIO/Internal/mitkPicFileReader.cpp b/Modules/IpPicSupportIO/Internal/mitkPicFileReader.cpp index 373e5027ca..d40a0e49b6 100644 --- a/Modules/IpPicSupportIO/Internal/mitkPicFileReader.cpp +++ b/Modules/IpPicSupportIO/Internal/mitkPicFileReader.cpp @@ -1,303 +1,303 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkPicFileReader.h" #include "mitkPicHelper.h" #include "mitkCustomMimeType.h" #include "mitkImageWriteAccessor.h" #include static mitk::PixelType CastToPixelType(mitkIpPicType_t pictype, size_t bpe) { const bool isSignedIntegralType = (pictype == mitkIpPicInt); const bool isUnsignedIntegralType = (pictype == mitkIpPicUInt); if (isSignedIntegralType) { switch (bpe) { case sizeof(char) : return mitk::MakeScalarPixelType(); case sizeof(short) : return mitk::MakeScalarPixelType(); default: return mitk::MakeScalarPixelType(); } } else if (isUnsignedIntegralType) { switch (bpe) { case sizeof(unsigned char) : return mitk::MakeScalarPixelType(); case sizeof(unsigned short) : return mitk::MakeScalarPixelType(); default: return mitk::MakeScalarPixelType(); } } else // is floating point type { switch (bpe) { case sizeof(float) : return mitk::MakeScalarPixelType(); default: return mitk::MakeScalarPixelType(); } } } static mitk::ImageDescriptor::Pointer CastToImageDescriptor(mitkIpPicDescriptor *desc) { mitk::ImageDescriptor::Pointer imDescriptor = mitk::ImageDescriptor::New(); imDescriptor->Initialize(desc->n, desc->dim); mitk::PixelType ptype = ::CastToPixelType(desc->type, (desc->bpe / 8)); imDescriptor->AddNewChannel(ptype, "imported by pic"); return imDescriptor; } static mitkIpPicType_t CastToIpPicType(int intype) { const bool isSignedIntegralType = (intype == itk::ImageIOBase::INT || intype == itk::ImageIOBase::SHORT || intype == itk::ImageIOBase::CHAR || intype == itk::ImageIOBase::LONG); const bool isUnsignedIntegralType = (intype == itk::ImageIOBase::UINT || intype == itk::ImageIOBase::USHORT || intype == itk::ImageIOBase::UCHAR || intype == itk::ImageIOBase::ULONG); const bool isFloatingPointType = (intype == itk::ImageIOBase::FLOAT || intype == itk::ImageIOBase::DOUBLE); if (isSignedIntegralType) return mitkIpPicInt; if (isUnsignedIntegralType) return mitkIpPicUInt; if (isFloatingPointType) return mitkIpPicFloat; return mitkIpPicUnknown; } static mitkIpPicDescriptor * CastToIpPicDescriptor(mitk::Image::Pointer refImg, mitk::ImageWriteAccessor *imageAccess, mitkIpPicDescriptor *picDesc) { const mitk::ImageDescriptor::Pointer imDesc = refImg->GetImageDescriptor(); // initialize dimension information for (unsigned int i = 0; i < 8; i++) { picDesc->n[i] = 1; } // set dimension information picDesc->dim = refImg->GetDimension(); memcpy(picDesc->n, imDesc->GetDimensions(), picDesc->dim * sizeof(unsigned int)); picDesc->type = ::CastToIpPicType(refImg->GetPixelType().GetComponentType()); picDesc->bpe = refImg->GetPixelType().GetBpe(); if (imageAccess != nullptr) { picDesc->data = imageAccess->GetData(); } return picDesc; } mitk::PicFileReader::PicFileReader() : AbstractFileReader() { CustomMimeType mimeType(this->GetMimeTypePrefix() + "mbipic"); mimeType.AddExtension("pic"); mimeType.AddExtension("pic.gz"); mimeType.AddExtension("PIC"); mimeType.AddExtension("PIC.gz"); mimeType.SetCategory("Images"); mimeType.SetComment("DKFZ Legacy PIC Format"); this->SetMimeType(mimeType); this->SetDescription("DKFZ PIC"); this->RegisterService(); } std::vector mitk::PicFileReader::Read() { mitk::Image::Pointer image = this->CreateImage(); this->FillImage(image); std::vector result; result.push_back(image.GetPointer()); return result; } mitk::Image::Pointer mitk::PicFileReader::CreateImage() { Image::Pointer output = Image::New(); std::string fileName = this->GetLocalFileName(); mitkIpPicDescriptor *header = mitkIpPicGetHeader(const_cast(fileName.c_str()), nullptr); if (!header) { mitkThrow() << "File could not be read."; } header = mitkIpPicGetTags(const_cast(fileName.c_str()), header); int channels = 1; mitkIpPicTSV_t *tsv; if ((tsv = mitkIpPicQueryTag(header, "SOURCE HEADER")) != nullptr) { if (tsv->n[0] > 1e+06) { mitkIpPicTSV_t *tsvSH; tsvSH = mitkIpPicDelTag(header, "SOURCE HEADER"); mitkIpPicFreeTag(tsvSH); } } if ((tsv = mitkIpPicQueryTag(header, "ICON80x80")) != nullptr) { mitkIpPicTSV_t *tsvSH; tsvSH = mitkIpPicDelTag(header, "ICON80x80"); mitkIpPicFreeTag(tsvSH); } if ((tsv = mitkIpPicQueryTag(header, "VELOCITY")) != nullptr) { ++channels; mitkIpPicDelTag(header, "VELOCITY"); } if (header == nullptr || header->bpe == 0) { mitkThrow() << " Could not read file " << fileName; } // if pic image only 2D, the n[2] value is not initialized unsigned int slices = 1; if (header->dim == 2) { header->n[2] = slices; } // First initialize the geometry of the output image by the pic-header SlicedGeometry3D::Pointer slicedGeometry = mitk::SlicedGeometry3D::New(); PicHelper::InitializeEvenlySpaced(header, header->n[2], slicedGeometry); // if pic image only 3D, the n[3] value is not initialized unsigned int timesteps = 1; if (header->dim > 3) { timesteps = header->n[3]; } slicedGeometry->ImageGeometryOn(); ProportionalTimeGeometry::Pointer timeGeometry = ProportionalTimeGeometry::New(); timeGeometry->Initialize(slicedGeometry, timesteps); // Cast the pic descriptor to ImageDescriptor and initialize the output output->Initialize(CastToImageDescriptor(header)); output->SetTimeGeometry(timeGeometry); mitkIpPicFree(header); return output; } void mitk::PicFileReader::ConvertHandedness(mitkIpPicDescriptor *pic) { // left to right handed conversion if (pic->dim >= 3) { mitkIpPicDescriptor *slice = mitkIpPicCopyHeader(pic, nullptr); slice->dim = 2; size_t size = _mitkIpPicSize(slice); slice->data = malloc(size); size_t v, volumes = (pic->dim > 3 ? pic->n[3] : 1); size_t volume_size = size * pic->n[2]; for (v = 0; v < volumes; ++v) { - unsigned char *p_first = (unsigned char *)pic->data; - unsigned char *p_last = (unsigned char *)pic->data; + auto *p_first = (unsigned char *)pic->data; + auto *p_last = (unsigned char *)pic->data; p_first += v * volume_size; p_last += size * (pic->n[2] - 1) + v * volume_size; size_t i, smid = pic->n[2] / 2; for (i = 0; i < smid; ++i, p_last -= size, p_first += size) { memcpy(slice->data, p_last, size); memcpy(p_last, p_first, size); memcpy(p_first, slice->data, size); } } mitkIpPicFree(slice); } } mitk::PicFileReader *mitk::PicFileReader::Clone() const { return new PicFileReader(*this); } void mitk::PicFileReader::FillImage(Image::Pointer output) { mitkIpPicDescriptor *outputPic = mitkIpPicNew(); outputPic = CastToIpPicDescriptor(output, nullptr, outputPic); mitkIpPicDescriptor *pic = mitkIpPicGet(const_cast(this->GetLocalFileName().c_str()), outputPic); // comes upside-down (in MITK coordinates) from PIC file ConvertHandedness(pic); mitkIpPicTSV_t *tsv; if ((tsv = mitkIpPicQueryTag(pic, "SOURCE HEADER")) != nullptr) { if (tsv->n[0] > 1e+06) { mitkIpPicTSV_t *tsvSH; tsvSH = mitkIpPicDelTag(pic, "SOURCE HEADER"); mitkIpPicFreeTag(tsvSH); } } if ((tsv = mitkIpPicQueryTag(pic, "ICON80x80")) != nullptr) { mitkIpPicTSV_t *tsvSH; tsvSH = mitkIpPicDelTag(pic, "ICON80x80"); mitkIpPicFreeTag(tsvSH); } if ((tsv = mitkIpPicQueryTag(pic, "VELOCITY")) != nullptr) { mitkIpPicDescriptor *header = mitkIpPicCopyHeader(pic, nullptr); header->data = tsv->value; ConvertHandedness(header); output->SetChannel(header->data, 1); header->data = nullptr; mitkIpPicFree(header); mitkIpPicDelTag(pic, "VELOCITY"); } // Copy the memory to avoid mismatches of malloc() and delete[]. // mitkIpPicGet will always allocate a new memory block with malloc(), // but MITK Images delete the data via delete[]. output->SetImportChannel(pic->data, 0, Image::CopyMemory); pic->data = nullptr; mitkIpPicFree(pic); } diff --git a/Modules/IpPicSupportIO/Internal/mitkPicHelper.cpp b/Modules/IpPicSupportIO/Internal/mitkPicHelper.cpp index 8e7bfd6edd..867dd117d9 100644 --- a/Modules/IpPicSupportIO/Internal/mitkPicHelper.cpp +++ b/Modules/IpPicSupportIO/Internal/mitkPicHelper.cpp @@ -1,295 +1,295 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkPicHelper.h" #include "mitkConfig.h" #include "mitkPlaneGeometry.h" #include "mitkSlicedGeometry3D.h" #ifdef HAVE_IPDICOM #include "ipDicom/ipDicom.h" #endif /* HAVE_IPDICOM */ bool mitk::PicHelper::GetSpacing(const mitkIpPicDescriptor *aPic, Vector3D &spacing) { - mitkIpPicDescriptor *pic = const_cast(aPic); + auto *pic = const_cast(aPic); mitkIpPicTSV_t *tsv; bool pixelSize = false; tsv = mitkIpPicQueryTag(pic, "REAL PIXEL SIZE"); if (tsv == nullptr) { tsv = mitkIpPicQueryTag(pic, "PIXEL SIZE"); pixelSize = true; } if (tsv) { bool tagFound = false; if ((tsv->dim * tsv->n[0] >= 3) && (tsv->type == mitkIpPicFloat)) { if (tsv->bpe == 32) { FillVector3D(spacing, ((mitkIpFloat4_t *)tsv->value)[0], ((mitkIpFloat4_t *)tsv->value)[1], ((mitkIpFloat4_t *)tsv->value)[2]); tagFound = true; } else if (tsv->bpe == 64) { FillVector3D(spacing, ((mitkIpFloat8_t *)tsv->value)[0], ((mitkIpFloat8_t *)tsv->value)[1], ((mitkIpFloat8_t *)tsv->value)[2]); tagFound = true; } } if (tagFound && pixelSize) { tsv = mitkIpPicQueryTag(pic, "PIXEL SPACING"); if (tsv) { mitk::ScalarType zSpacing = 0; if ((tsv->dim * tsv->n[0] >= 3) && (tsv->type == mitkIpPicFloat)) { if (tsv->bpe == 32) { zSpacing = ((mitkIpFloat4_t *)tsv->value)[2]; } else if (tsv->bpe == 64) { zSpacing = ((mitkIpFloat8_t *)tsv->value)[2]; } if (zSpacing != 0) { spacing[2] = zSpacing; } } } } if (tagFound) return true; } #ifdef HAVE_IPDICOM tsv = mitkIpPicQueryTag(pic, "SOURCE HEADER"); if (tsv) { void *data; mitkIpUInt4_t len; mitkIpFloat8_t spacing_z = 0; mitkIpFloat8_t thickness = 1; mitkIpFloat8_t fx = 1; mitkIpFloat8_t fy = 1; bool ok = false; if (dicomFindElement((unsigned char *)tsv->value, 0x0018, 0x0088, &data, &len)) { ok = true; sscanf((char *)data, "%lf", &spacing_z); // itkGenericOutputMacro( "spacing: " << spacing_z << " mm"); } if (dicomFindElement((unsigned char *)tsv->value, 0x0018, 0x0050, &data, &len)) { ok = true; sscanf((char *)data, "%lf", &thickness); // itkGenericOutputMacro( "thickness: " << thickness << " mm"); if (thickness == 0) thickness = 1; } if (dicomFindElement((unsigned char *)tsv->value, 0x0028, 0x0030, &data, &len) && len > 0 && ((char *)data)[0]) { sscanf((char *)data, "%lf\\%lf", &fy, &fx); // row / column value // itkGenericOutputMacro( "fx, fy: " << fx << "/" << fy << " mm"); } else ok = false; if (ok) FillVector3D(spacing, fx, fy, (spacing_z > 0 ? spacing_z : thickness)); return ok; } #endif /* HAVE_IPDICOM */ if (spacing[0] <= 0 || spacing[1] <= 0 || spacing[2] <= 0) { itkGenericOutputMacro(<< "illegal spacing by pic tag: " << spacing << ". Setting spacing to (1,1,1)."); spacing.Fill(1); } return false; } bool mitk::PicHelper::GetTimeSpacing(const mitkIpPicDescriptor *aPic, float &timeSpacing) { - mitkIpPicDescriptor *pic = const_cast(aPic); + auto *pic = const_cast(aPic); mitkIpPicTSV_t *tsv; tsv = mitkIpPicQueryTag(pic, "PIXEL SIZE"); if (tsv) { timeSpacing = ((mitkIpFloat4_t *)tsv->value)[3]; if (timeSpacing <= 0) timeSpacing = 1; } else timeSpacing = 1; return true; } bool mitk::PicHelper::SetSpacing(const mitkIpPicDescriptor *aPic, SlicedGeometry3D *slicedgeometry) { - mitkIpPicDescriptor *pic = const_cast(aPic); + auto *pic = const_cast(aPic); Vector3D spacing(slicedgeometry->GetSpacing()); mitkIpPicTSV_t *tsv; if ((tsv = mitkIpPicQueryTag(pic, "REAL PIXEL SIZES")) != nullptr) { int count = tsv->n[1]; - float *value = (float *)tsv->value; + auto *value = (float *)tsv->value; mitk::Vector3D pixelSize; spacing.Fill(0); for (int s = 0; s < count; s++) { pixelSize[0] = (ScalarType)*value++; pixelSize[1] = (ScalarType)*value++; pixelSize[2] = (ScalarType)*value++; spacing += pixelSize; } spacing *= 1.0f / count; slicedgeometry->SetSpacing(spacing); itkGenericOutputMacro(<< "the slices are inhomogeneous"); } else if (GetSpacing(pic, spacing)) { slicedgeometry->SetSpacing(spacing); return true; } return false; } void mitk::PicHelper::InitializeEvenlySpaced(const mitkIpPicDescriptor *pic, unsigned int slices, SlicedGeometry3D *slicedgeometry) { assert(pic != nullptr); assert(slicedgeometry != nullptr); mitk::PlaneGeometry::Pointer planegeometry = mitk::PlaneGeometry::New(); mitkIpPicTSV_t *geometryTag; if ((geometryTag = mitkIpPicQueryTag(const_cast(pic), "ISG")) != nullptr) { mitk::Point3D origin; mitk::Vector3D rightVector; mitk::Vector3D downVector; mitk::Vector3D spacing; mitk::vtk2itk(((float *)geometryTag->value + 0), origin); mitk::vtk2itk(((float *)geometryTag->value + 3), rightVector); mitk::vtk2itk(((float *)geometryTag->value + 6), downVector); mitk::vtk2itk(((float *)geometryTag->value + 9), spacing); mitk::PlaneGeometry::Pointer planegeometry = PlaneGeometry::New(); planegeometry->InitializeStandardPlane(pic->n[0], pic->n[1], rightVector, downVector, &spacing); planegeometry->SetOrigin(origin); slicedgeometry->InitializeEvenlySpaced(planegeometry, slices); } else { Vector3D spacing; spacing.Fill(1); GetSpacing(pic, spacing); planegeometry->InitializeStandardPlane(pic->n[0], pic->n[1], spacing); slicedgeometry->InitializeEvenlySpaced(planegeometry, spacing[2], slices); } } bool mitk::PicHelper::SetPlaneGeometry(const mitkIpPicDescriptor *aPic, int s, SlicedGeometry3D *slicedgeometry) { - mitkIpPicDescriptor *pic = const_cast(aPic); + auto *pic = const_cast(aPic); if ((pic != nullptr) && (slicedgeometry->IsValidSlice(s))) { // construct standard view mitk::Point3D origin; mitk::Vector3D rightDV, bottomDV; mitkIpPicTSV_t *tsv; if ((tsv = mitkIpPicQueryTag(pic, "REAL PIXEL SIZES")) != nullptr) { - unsigned int count = (unsigned int)tsv->n[1]; - float *value = (float *)tsv->value; + auto count = (unsigned int)tsv->n[1]; + auto *value = (float *)tsv->value; mitk::Vector3D pixelSize; ScalarType zPosition = 0.0f; if (s >= 0) { if (count < (unsigned int)s) return false; count = s; } else { if (count < slicedgeometry->GetSlices()) return false; count = slicedgeometry->GetSlices(); } unsigned int slice; for (slice = 0; slice < count; ++slice) { pixelSize[0] = (ScalarType)*value++; pixelSize[1] = (ScalarType)*value++; pixelSize[2] = (ScalarType)*value++; zPosition += pixelSize[2] / 2.0f; // first half slice thickness if ((s == -1) || (slice == (unsigned int)s)) { Vector3D spacing; spacing = pixelSize; FillVector3D(origin, 0, 0, zPosition); FillVector3D(rightDV, pic->n[0], 0, 0); FillVector3D(bottomDV, 0, pic->n[1], 0); mitk::PlaneGeometry::Pointer planegeometry = mitk::PlaneGeometry::New(); planegeometry->InitializeStandardPlane( pic->n[0], pic->n[1], rightDV.GetVnlVector(), bottomDV.GetVnlVector(), &spacing); planegeometry->SetOrigin(origin); slicedgeometry->SetPlaneGeometry(planegeometry, s); } zPosition += pixelSize[2] / 2.0f; // second half slice thickness } } else { FillVector3D(origin, 0, 0, s); slicedgeometry->IndexToWorld(origin, origin); FillVector3D(rightDV, pic->n[0], 0, 0); FillVector3D(bottomDV, 0, pic->n[1], 0); mitk::PlaneGeometry::Pointer planegeometry = mitk::PlaneGeometry::New(); Vector3D spacing = slicedgeometry->GetSpacing(); planegeometry->InitializeStandardPlane( pic->n[0], pic->n[1], rightDV.GetVnlVector(), bottomDV.GetVnlVector(), &spacing); planegeometry->SetOrigin(origin); slicedgeometry->SetPlaneGeometry(planegeometry, s); } return true; } return false; } diff --git a/Modules/LegacyIO/mitkDataNodeFactory.cpp b/Modules/LegacyIO/mitkDataNodeFactory.cpp index 31c213483c..fe662ea8e8 100644 --- a/Modules/LegacyIO/mitkDataNodeFactory.cpp +++ b/Modules/LegacyIO/mitkDataNodeFactory.cpp @@ -1,496 +1,496 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include #include #include #include #include #include // C-Standard library includes #include #include // STL-related includes #include #include #include #include #include // VTK-related includes #include #include #include #include #include #include #include #include #include #include #include // ITK-related includes #include #include #include #include #include #include #include #include #include #ifdef NOMINMAX #define DEF_NOMINMAX #undef NOMINMAX #endif #include #ifdef DEF_NOMINMAX #ifndef NOMINMAX #define NOMINMAX #endif #undef DEF_NOMINMAX #endif #include #include // MITK-related includes #include "mitkPointSet.h" #include "mitkProperties.h" #include "mitkStringProperty.h" #include "mitkSurface.h" //#include "mitkMaterialProperty.h" #include "mitkCoreObjectFactory.h" #include "mitkImage.h" #include "mitkImageChannelSelector.h" #include "mitkImageSliceSelector.h" #include "mitkLevelWindowProperty.h" #include "mitkLookupTable.h" #include "mitkLookupTableProperty.h" #include "mitkProgressBar.h" #include "mitkPropertyNameHelper.h" #include "mitkTransferFunctionProperty.h" #include "mitkVtkInterpolationProperty.h" #include "mitkVtkRepresentationProperty.h" #include "mitkVtkResliceInterpolationProperty.h" #include "mitkVtkScalarModeProperty.h" #include bool mitk::DataNodeFactory::m_TextureInterpolationActive = false; // default value for texture interpolation if nothing is defined in global options (see QmitkMainTemplate.ui.h) mitk::DataNodeFactory::DataNodeFactory() { m_Serie = false; m_OldProgress = 0; this->Modified(); // ensure that a CoreObjectFactory has been instantiated mitk::CoreObjectFactory::GetInstance(); } mitk::DataNodeFactory::~DataNodeFactory() { } void mitk::DataNodeFactory::SetImageSerie(bool serie) { m_Serie = serie; } void mitk::DataNodeFactory::GenerateData() { // IF filename is something.pic, and something.pic does not exist, try to read something.pic.gz // if there are both, something.pic and something.pic.gz, only the requested file is read // not only for images, but for all formats std::ifstream exists(m_FileName.c_str()); if (!exists) { std::string testfilename = m_FileName + ".gz"; std::ifstream exists(testfilename.c_str()); if (exists.good()) { m_FileName += ".gz"; } else { testfilename = m_FileName + ".GZ"; std::ifstream exists(testfilename.c_str()); if (exists.good()) { m_FileName += ".GZ"; } else { std::string message("File does not exist, or cannot be read. Filename = "); message += m_FileName; MITK_ERROR << message; itkExceptionMacro(<< message.str()); } } } // part for DICOM // const char *numbers = "0123456789."; // std::string::size_type first_non_number; // first_non_number = itksys::SystemTools::GetFilenameName(m_FileName).find_first_not_of ( numbers ); if (DicomSeriesReader::IsDicom(this->m_FileName) /*|| first_non_number == std::string::npos*/) { this->ReadFileSeriesTypeDCM(); } else { bool usedNewDTNF = false; // the mitkBaseDataIO class returns a pointer of a vector of BaseData objects std::vector baseDataVector = mitk::BaseDataIO::LoadBaseDataFromFile(m_FileName, m_FilePrefix, m_FilePattern, m_Serie); if (!baseDataVector.empty()) this->ResizeOutputs((unsigned int)baseDataVector.size()); for (int i = 0; i < (int)baseDataVector.size(); i++) { mitk::BaseData::Pointer baseData = baseDataVector.at(i); if (baseData.IsNotNull()) { usedNewDTNF = true; mitk::DataNode::Pointer node = mitk::DataNode::New(); node->SetData(baseData); this->SetDefaultCommonProperties(node); this->SetOutput(this->MakeNameFromOutputIndex(i), node); } } if (!usedNewDTNF && (m_FileName != "") && !(m_Serie == false)) ReadFileSeriesTypeITKImageSeriesReader(); } } void mitk::DataNodeFactory::ResizeOutputs(const unsigned int &num) { unsigned int prevNum = this->GetNumberOfOutputs(); this->SetNumberOfIndexedOutputs(num); for (unsigned int i = prevNum; i < num; ++i) { this->SetNthOutput(i, this->MakeOutput(i).GetPointer()); } } bool mitk::DataNodeFactory::FileNameEndsWith(const std::string &name) { if (m_FileName.size() < name.size()) return false; return m_FileName.substr(m_FileName.size() - name.size()) == name; } bool mitk::DataNodeFactory::FilePatternEndsWith(const std::string &name) { return m_FilePattern.find(name) != std::string::npos; } std::string mitk::DataNodeFactory::GetBaseFileName() { return itksys::SystemTools::GetFilenameName(m_FileName); } std::string mitk::DataNodeFactory::GetBaseFilePrefix() { return itksys::SystemTools::GetFilenameName(m_FilePrefix); } std::string mitk::DataNodeFactory::GetDirectory() { if (!m_FileName.empty()) return itksys::SystemTools::GetFilenamePath(m_FileName); if (!m_FilePrefix.empty()) return itksys::SystemTools::GetFilenamePath(m_FilePrefix); return std::string(); } void mitk::DataNodeFactory::ReadFileSeriesTypeDCM() { mitk::LocaleSwitch localeSwitch("C"); std::locale previousCppLocale(std::cin.getloc()); std::locale l("C"); std::cin.imbue(l); if (DicomSeriesReader::IsPhilips3DDicom(this->GetFileName())) { MITK_INFO << "it is a Philips3D US Dicom file" << std::endl; this->ResizeOutputs(1); DataNode::Pointer node = this->GetOutput(); mitk::DicomSeriesReader::StringContainer stringvec; stringvec.push_back(this->GetFileName()); if (DicomSeriesReader::LoadDicomSeries(stringvec, *node)) { node->SetName(this->GetBaseFileName()); } std::cin.imbue(previousCppLocale); return; } DicomSeriesReader::FileNamesGrouping imageBlocks = DicomSeriesReader::GetSeries( this->GetDirectory(), true, this->m_SeriesRestrictions); // true = group gantry tilt images const unsigned int size = imageBlocks.size(); this->ResizeOutputs(size); ProgressBar::GetInstance()->AddStepsToDo(size); ProgressBar::GetInstance()->Progress(); unsigned int outputIndex = 0u; const DicomSeriesReader::FileNamesGrouping::const_iterator n_end = imageBlocks.end(); for (DicomSeriesReader::FileNamesGrouping::const_iterator n_it = imageBlocks.begin(); n_it != n_end; ++n_it) { const std::string &uid = n_it->first; DataNode::Pointer node = this->GetOutput(outputIndex); const DicomSeriesReader::ImageBlockDescriptor &imageBlockDescriptor(n_it->second); MITK_INFO << "--------------------------------------------------------------------------------"; MITK_INFO << "DataNodeFactory: Loading DICOM series " << outputIndex << ": Series UID " << imageBlockDescriptor.GetSeriesInstanceUID() << std::endl; MITK_INFO << " " << imageBlockDescriptor.GetFilenames().size() << " '" << imageBlockDescriptor.GetModality() << "' files (" << imageBlockDescriptor.GetSOPClassUIDAsString() << ") loaded into 1 mitk::Image"; MITK_INFO << " multi-frame: " << (imageBlockDescriptor.IsMultiFrameImage() ? "Yes" : "No"); MITK_INFO << " reader support: " << DicomSeriesReader::ReaderImplementationLevelToString( imageBlockDescriptor.GetReaderImplementationLevel()); MITK_INFO << " pixel spacing type: " << DicomSeriesReader::PixelSpacingInterpretationToString(imageBlockDescriptor.GetPixelSpacingType()); MITK_INFO << " gantry tilt corrected: " << (imageBlockDescriptor.HasGantryTiltCorrected() ? "Yes" : "No"); MITK_INFO << " 3D+t: " << (imageBlockDescriptor.HasMultipleTimePoints() ? "Yes" : "No"); MITK_INFO << "--------------------------------------------------------------------------------"; if (DicomSeriesReader::LoadDicomSeries(n_it->second.GetFilenames(), *node, true, true, true)) { std::string nodeName(uid); std::string studyDescription; if (GetBackwardsCompatibleDICOMProperty( 0x0008, 0x1030, "dicom.study.StudyDescription", node->GetPropertyList(), studyDescription)) { nodeName = studyDescription; std::string seriesDescription; if (GetBackwardsCompatibleDICOMProperty( 0x0008, 0x103e, "dicom.study.SeriesDescription", node->GetPropertyList(), seriesDescription)) { nodeName += "/" + seriesDescription; } } node->SetName(nodeName); ++outputIndex; } else { MITK_ERROR << "DataNodeFactory: Skipping series " << outputIndex << " due to some unspecified error..." << std::endl; } ProgressBar::GetInstance()->Progress(); } std::cin.imbue(previousCppLocale); } void mitk::DataNodeFactory::ReadFileSeriesTypeITKImageSeriesReader() { typedef itk::Image ImageType; typedef itk::ImageSeriesReader ReaderType; if (!this->GenerateFileList()) { itkWarningMacro("Sorry, file list could not be generated!"); return; } if (m_MatchedFileNames.size() == 0) { itkWarningMacro("Sorry, no files matched the given filename (" << m_FileName << ")!"); return; } // // Finally, initialize the ITK-reader and load the files! // ReaderType::Pointer reader = ReaderType::New(); reader->SetFileNames(m_MatchedFileNames); try { reader->Update(); ResizeOutputs(reader->GetNumberOfOutputs()); for (unsigned int i = 0; i < reader->GetNumberOfOutputs(); ++i) { // Initialize mitk image from itk mitk::Image::Pointer image = mitk::Image::New(); image->InitializeByItk(reader->GetOutput(i)); image->SetVolume(reader->GetOutput(i)->GetBufferPointer()); // add the mitk image to the node mitk::DataNode::Pointer node = this->GetOutput(i); node->SetData(image); mitk::StringProperty::Pointer nameProp = mitk::StringProperty::New(m_FileName); node->SetProperty("name", nameProp); } } catch (const std::exception &e) { itkWarningMacro(<< e.what()); return; } } mitk::ColorProperty::Pointer mitk::DataNodeFactory::DefaultColorForOrgan(const std::string &organ) { static bool initialized = false; static std::map s_ColorMap; if (!initialized) { // all lowercase here, please! s_ColorMap.insert(std::make_pair("ankle", "0xe38686")); s_ColorMap.insert(std::make_pair("appendix", "0xe38686")); s_ColorMap.insert(std::make_pair("blood vessels", "0xff3131")); s_ColorMap.insert(std::make_pair("bronchial tree", "0x3168ff")); s_ColorMap.insert(std::make_pair("bone", "0xd5d5d5")); s_ColorMap.insert(std::make_pair("brain", "0xff9cca")); s_ColorMap.insert(std::make_pair("coccyx", "0xe38686")); s_ColorMap.insert(std::make_pair("colon", "0xe38686")); s_ColorMap.insert(std::make_pair("cyst", "0xe38686")); s_ColorMap.insert(std::make_pair("elbow", "0xe38686")); s_ColorMap.insert(std::make_pair("eye", "0xe38686")); s_ColorMap.insert(std::make_pair("fallopian tube", "0xe38686")); s_ColorMap.insert(std::make_pair("fat", "0xff2bee")); s_ColorMap.insert(std::make_pair("hand", "0xe38686")); s_ColorMap.insert(std::make_pair("gall bladder", "0x567f18")); s_ColorMap.insert(std::make_pair("heart", "0xeb1d32")); s_ColorMap.insert(std::make_pair("hip", "0xe38686")); s_ColorMap.insert(std::make_pair("kidney", "0xd33f00")); s_ColorMap.insert(std::make_pair("knee", "0xe38686")); s_ColorMap.insert(std::make_pair("larynx", "0xe38686")); s_ColorMap.insert(std::make_pair("liver", "0xffcc3d")); s_ColorMap.insert(std::make_pair("lung", "0x6bdcff")); s_ColorMap.insert(std::make_pair("lymph node", "0xff0000")); s_ColorMap.insert(std::make_pair("muscle", "0xff456a")); s_ColorMap.insert(std::make_pair("nerve", "0xffea4f")); s_ColorMap.insert(std::make_pair("nose", "0xe38686")); s_ColorMap.insert(std::make_pair("oesophagus", "0xe38686")); s_ColorMap.insert(std::make_pair("ovaries", "0xe38686")); s_ColorMap.insert(std::make_pair("pancreas", "0xf9ab3d")); s_ColorMap.insert(std::make_pair("pelvis", "0xe38686")); s_ColorMap.insert(std::make_pair("penis", "0xe38686")); s_ColorMap.insert(std::make_pair("pharynx", "0xe38686")); s_ColorMap.insert(std::make_pair("prostate", "0xe38686")); s_ColorMap.insert(std::make_pair("rectum", "0xe38686")); s_ColorMap.insert(std::make_pair("sacrum", "0xe38686")); s_ColorMap.insert(std::make_pair("seminal vesicle", "0xe38686")); s_ColorMap.insert(std::make_pair("shoulder", "0xe38686")); s_ColorMap.insert(std::make_pair("spinal cord", "0xf5f93d")); s_ColorMap.insert(std::make_pair("spleen", "0xf96c3d")); s_ColorMap.insert(std::make_pair("stomach", "0xf96c3d")); s_ColorMap.insert(std::make_pair("teeth", "0xfffcd8")); s_ColorMap.insert(std::make_pair("testicles", "0xe38686")); s_ColorMap.insert(std::make_pair("thyroid", "0xfff694")); s_ColorMap.insert(std::make_pair("tongue", "0xe38686")); s_ColorMap.insert(std::make_pair("tumor", "0x937011")); s_ColorMap.insert(std::make_pair("urethra", "0xf8ff32")); s_ColorMap.insert(std::make_pair("urinary bladder", "0xf8ff32")); s_ColorMap.insert(std::make_pair("uterus", "0xe38686")); s_ColorMap.insert(std::make_pair("vagina", "0xe38686")); s_ColorMap.insert(std::make_pair("vertebra", "0xe38686")); s_ColorMap.insert(std::make_pair("wrist", "0xe38686")); initialized = true; } std::string lowercaseOrgan(organ); for (unsigned int i = 0; i < organ.length(); i++) { lowercaseOrgan[i] = tolower(lowercaseOrgan[i]); } - std::map::iterator iter = s_ColorMap.find(lowercaseOrgan); + auto iter = s_ColorMap.find(lowercaseOrgan); if (iter != s_ColorMap.end()) { std::string hexColor = iter->second; std::string hexRed = std::string("0x") + hexColor.substr(2, 2); std::string hexGreen = std::string("0x") + hexColor.substr(4, 2); std::string hexBlue = std::string("0x") + hexColor.substr(6, 2); long int red = strtol(hexRed.c_str(), nullptr, 16); long int green = strtol(hexGreen.c_str(), nullptr, 16); long int blue = strtol(hexBlue.c_str(), nullptr, 16); return ColorProperty::New((float)red / 255.0, (float)green / 255.0, (float)blue / 255.0); } else { // a default color (green) return ColorProperty::New(0.0, 1.0, 0.0); } } void mitk::DataNodeFactory::SetDefaultCommonProperties(mitk::DataNode::Pointer &node) { // path mitk::StringProperty::Pointer pathProp = mitk::StringProperty::New(itksys::SystemTools::GetFilenamePath(m_FileName)); node->SetProperty(StringProperty::PATH, pathProp); // name already defined? mitk::StringProperty::Pointer nameProp = dynamic_cast(node->GetProperty("name")); if (nameProp.IsNull() || (strcmp(nameProp->GetValue(), "No Name!") == 0)) { // name already defined in BaseData mitk::StringProperty::Pointer baseDataNameProp = dynamic_cast(node->GetData()->GetProperty("name").GetPointer()); if (baseDataNameProp.IsNull() || (strcmp(baseDataNameProp->GetValue(), "No Name!") == 0)) { // name neither defined in node, nor in BaseData -> name = filename if (FileNameEndsWith(".gz")) m_FileName = m_FileName.substr(0, m_FileName.length() - 3); nameProp = mitk::StringProperty::New(itksys::SystemTools::GetFilenameWithoutLastExtension(m_FileName)); node->SetProperty("name", nameProp); } else { // name defined in BaseData! nameProp = mitk::StringProperty::New(baseDataNameProp->GetValue()); node->SetProperty("name", nameProp); } } // visibility if (!node->GetProperty("visible")) node->SetVisibility(true); } diff --git a/Modules/LegacyIO/mitkItkImageFileReader.cpp b/Modules/LegacyIO/mitkItkImageFileReader.cpp index bad828651b..e53474c766 100644 --- a/Modules/LegacyIO/mitkItkImageFileReader.cpp +++ b/Modules/LegacyIO/mitkItkImageFileReader.cpp @@ -1,218 +1,218 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkItkImageFileReader.h" #include "mitkConfig.h" #include "mitkException.h" #include #include #include #include #include #include //#include #include #include #include #include //#include //#include //#include //#include //#include //#include void mitk::ItkImageFileReader::GenerateData() { mitk::LocaleSwitch localeSwitch("C"); mitk::Image::Pointer image = this->GetOutput(); const unsigned int MINDIM = 2; const unsigned int MAXDIM = 4; MITK_INFO("mitkItkImageFileReader") << "loading " << m_FileName << " via itk::ImageIOFactory... " << std::endl; // Check to see if we can read the file given the name or prefix if (m_FileName == "") { mitkThrow() << "Empty filename in mitk::ItkImageFileReader "; return; } itk::ImageIOBase::Pointer imageIO = itk::ImageIOFactory::CreateImageIO(m_FileName.c_str(), itk::ImageIOFactory::ReadMode); if (imageIO.IsNull()) { // itkWarningMacro( << "File Type not supported!" ); mitkThrow() << "Could not create itk::ImageIOBase object for filename " << m_FileName; return; } // Got to allocate space for the image. Determine the characteristics of // the image. imageIO->SetFileName(m_FileName.c_str()); imageIO->ReadImageInformation(); unsigned int ndim = imageIO->GetNumberOfDimensions(); if (ndim < MINDIM || ndim > MAXDIM) { itkWarningMacro(<< "Sorry, only dimensions 2, 3 and 4 are supported. The given file has " << ndim << " dimensions! Reading as 4D."); ndim = MAXDIM; } itk::ImageIORegion ioRegion(ndim); itk::ImageIORegion::SizeType ioSize = ioRegion.GetSize(); itk::ImageIORegion::IndexType ioStart = ioRegion.GetIndex(); unsigned int dimensions[MAXDIM]; dimensions[0] = 0; dimensions[1] = 0; dimensions[2] = 0; dimensions[3] = 0; ScalarType spacing[MAXDIM]; spacing[0] = 1.0f; spacing[1] = 1.0f; spacing[2] = 1.0f; spacing[3] = 1.0f; Point3D origin; origin.Fill(0); unsigned int i; for (i = 0; i < ndim; ++i) { ioStart[i] = 0; ioSize[i] = imageIO->GetDimensions(i); if (i < MAXDIM) { dimensions[i] = imageIO->GetDimensions(i); spacing[i] = imageIO->GetSpacing(i); if (spacing[i] <= 0) spacing[i] = 1.0f; } if (i < 3) { origin[i] = imageIO->GetOrigin(i); } } ioRegion.SetSize(ioSize); ioRegion.SetIndex(ioStart); MITK_INFO("mitkItkImageFileReader") << "ioRegion: " << ioRegion << std::endl; imageIO->SetIORegion(ioRegion); void *buffer = new unsigned char[imageIO->GetImageSizeInBytes()]; imageIO->Read(buffer); image->Initialize(MakePixelType(imageIO), ndim, dimensions); image->SetImportChannel(buffer, 0, Image::ManageMemory); // access direction of itk::Image and include spacing mitk::Matrix3D matrix; matrix.SetIdentity(); unsigned int j, itkDimMax3 = (ndim >= 3 ? 3 : ndim); for (i = 0; i < itkDimMax3; ++i) for (j = 0; j < itkDimMax3; ++j) matrix[i][j] = imageIO->GetDirection(j)[i]; // re-initialize PlaneGeometry with origin and direction - PlaneGeometry *planeGeometry = static_cast(image->GetSlicedGeometry(0)->GetPlaneGeometry(0)); + auto *planeGeometry = static_cast(image->GetSlicedGeometry(0)->GetPlaneGeometry(0)); planeGeometry->SetOrigin(origin); planeGeometry->GetIndexToWorldTransform()->SetMatrix(matrix); // re-initialize SlicedGeometry3D SlicedGeometry3D *slicedGeometry = image->GetSlicedGeometry(0); slicedGeometry->InitializeEvenlySpaced(planeGeometry, image->GetDimension(2)); slicedGeometry->SetSpacing(spacing); MITK_INFO("mitkItkImageFileReader") << slicedGeometry->GetCornerPoint(false, false, false); MITK_INFO("mitkItkImageFileReader") << slicedGeometry->GetCornerPoint(true, true, true); // re-initialize TimeGeometry ProportionalTimeGeometry::Pointer timeGeometry = ProportionalTimeGeometry::New(); timeGeometry->Initialize(slicedGeometry, image->GetDimension(3)); image->SetTimeGeometry(timeGeometry); buffer = nullptr; MITK_INFO("mitkItkImageFileReader") << "number of image components: " << image->GetPixelType().GetNumberOfComponents() << std::endl; // mitk::DataNode::Pointer node = this->GetOutput(); // node->SetData( image ); // add level-window property // if ( image->GetPixelType().GetNumberOfComponents() == 1 ) //{ // SetDefaultImageProperties( node ); //} MITK_INFO("mitkItkImageFileReader") << "...finished!" << std::endl; } bool mitk::ItkImageFileReader::CanReadFile(const std::string filename, const std::string filePrefix, const std::string filePattern) { // First check the extension if (filename == "") return false; // check if image is serie if (filePattern != "" && filePrefix != "") return false; itk::ImageIOBase::Pointer imageIO = itk::ImageIOFactory::CreateImageIO(filename.c_str(), itk::ImageIOFactory::ReadMode); if (imageIO.IsNull()) return false; try { imageIO->SetFileName(filename.c_str()); imageIO->ReadImageInformation(); itk::MetaDataDictionary imgMetaDictionary = imageIO->GetMetaDataDictionary(); std::vector imgMetaKeys = imgMetaDictionary.GetKeys(); std::vector::const_iterator itKey = imgMetaKeys.begin(); std::string metaString; for (; itKey != imgMetaKeys.end(); itKey++) { itk::ExposeMetaData(imgMetaDictionary, *itKey, metaString); if (itKey->find("modality") != std::string::npos) { if (metaString.find("DWMRI") != std::string::npos) { return false; // DiffusionImageReader should handle this } } } } catch (...) { MITK_INFO("mitkItkImageFileReader") << "Could not read ImageInformation "; } return true; } mitk::ItkImageFileReader::ItkImageFileReader() : m_FileName(""), m_FilePrefix(""), m_FilePattern("") { } mitk::ItkImageFileReader::~ItkImageFileReader() { } diff --git a/Modules/LegacyIO/mitkPointSetReader.cpp b/Modules/LegacyIO/mitkPointSetReader.cpp index 8d1e0045ea..4c0638533f 100644 --- a/Modules/LegacyIO/mitkPointSetReader.cpp +++ b/Modules/LegacyIO/mitkPointSetReader.cpp @@ -1,199 +1,199 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkPointSetReader.h" #include #include #include mitk::PointSetReader::PointSetReader() { m_Success = false; } mitk::PointSetReader::~PointSetReader() { } void mitk::PointSetReader::GenerateData() { // Switch the current locale to "C" LocaleSwitch localeSwitch("C"); m_Success = false; if (m_FileName == "") { itkWarningMacro(<< "Sorry, filename has not been set!"); return; } if (!this->CanReadFile(m_FileName.c_str())) { itkWarningMacro(<< "Sorry, can't read file " << m_FileName << "!"); return; } try { TiXmlDocument doc(m_FileName.c_str()); bool loadOkay = doc.LoadFile(); if (loadOkay) { TiXmlHandle docHandle(&doc); unsigned int pointSetCounter(0); for (TiXmlElement *currentPointSetElement = docHandle.FirstChildElement("point_set_file").FirstChildElement("point_set").ToElement(); currentPointSetElement != nullptr; currentPointSetElement = currentPointSetElement->NextSiblingElement()) { mitk::PointSet::Pointer newPointSet = mitk::PointSet::New(); if (currentPointSetElement->FirstChildElement("time_series") != nullptr) { for (TiXmlElement *currentTimeSeries = currentPointSetElement->FirstChildElement("time_series")->ToElement(); currentTimeSeries != nullptr; currentTimeSeries = currentTimeSeries->NextSiblingElement()) { unsigned int currentTimeStep(0); TiXmlElement *currentTimeSeriesID = currentTimeSeries->FirstChildElement("time_series_id"); currentTimeStep = atoi(currentTimeSeriesID->GetText()); newPointSet = this->ReadPoint(newPointSet, currentTimeSeries, currentTimeStep); } } else { newPointSet = this->ReadPoint(newPointSet, currentPointSetElement, 0); } this->SetNthOutput(pointSetCounter, newPointSet); pointSetCounter++; } } else { MITK_WARN << "XML parser error!"; } } catch (...) { MITK_ERROR << "Cannot read point set."; m_Success = false; } m_Success = true; } mitk::PointSet::Pointer mitk::PointSetReader::ReadPoint(mitk::PointSet::Pointer newPointSet, TiXmlElement *currentTimeSeries, unsigned int currentTimeStep) { if (currentTimeSeries->FirstChildElement("point") != nullptr) { for (TiXmlElement *currentPoint = currentTimeSeries->FirstChildElement("point")->ToElement(); currentPoint != nullptr; currentPoint = currentPoint->NextSiblingElement()) { unsigned int id(0); - mitk::PointSpecificationType spec((mitk::PointSpecificationType)0); + auto spec((mitk::PointSpecificationType)0); double x(0.0); double y(0.0); double z(0.0); id = atoi(currentPoint->FirstChildElement("id")->GetText()); if (currentPoint->FirstChildElement("specification") != nullptr) { spec = (mitk::PointSpecificationType)atoi(currentPoint->FirstChildElement("specification")->GetText()); } x = atof(currentPoint->FirstChildElement("x")->GetText()); y = atof(currentPoint->FirstChildElement("y")->GetText()); z = atof(currentPoint->FirstChildElement("z")->GetText()); mitk::Point3D point; mitk::FillVector3D(point, x, y, z); newPointSet->SetPoint(id, point, spec, currentTimeStep); } } else { if (currentTimeStep != newPointSet->GetTimeSteps() + 1) { newPointSet->Expand(currentTimeStep + 1); // expand time step series with empty time step } } return newPointSet; } void mitk::PointSetReader::GenerateOutputInformation() { } int mitk::PointSetReader::CanReadFile(const char *name) { std::ifstream in(name); bool isGood = in.good(); in.close(); return isGood; } bool mitk::PointSetReader::CanReadFile(const std::string filename, const std::string filePrefix, const std::string filePattern) { // First check the extension if (filename == "") { // MITK_INFO<<"No filename specified."<GetNumberOfOutputs(); this->SetNumberOfIndexedOutputs(num); for (unsigned int i = prevNum; i < num; ++i) { this->SetNthOutput(i, this->MakeOutput(i).GetPointer()); } } bool mitk::PointSetReader::GetSuccess() const { return m_Success; } diff --git a/Modules/MapperExt/src/mitkEnhancedPointSetVtkMapper3D.cpp b/Modules/MapperExt/src/mitkEnhancedPointSetVtkMapper3D.cpp index 96a799c151..d3e7727a55 100644 --- a/Modules/MapperExt/src/mitkEnhancedPointSetVtkMapper3D.cpp +++ b/Modules/MapperExt/src/mitkEnhancedPointSetVtkMapper3D.cpp @@ -1,435 +1,435 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkEnhancedPointSetVtkMapper3D.h" //#include #include #include "mitkDataNode.h" #include "mitkLookupTables.h" #include "mitkProperties.h" #include "mitkColorProperty.h" //#include "mitkVtkPropRenderer.h" #include #include #include #include #include #include #include #include #include #include #include #include #include const mitk::PointSet *mitk::EnhancedPointSetVtkMapper3D::GetInput() { return static_cast(GetDataNode()->GetData()); } mitk::EnhancedPointSetVtkMapper3D::EnhancedPointSetVtkMapper3D() { m_Contour = vtkActor::New(); m_ContourSource = vtkTubeFilter::New(); m_PropAssembly = vtkAssembly::New(); } vtkProp *mitk::EnhancedPointSetVtkMapper3D::GetVtkProp(mitk::BaseRenderer * /*renderer*/) { return m_PropAssembly; } mitk::EnhancedPointSetVtkMapper3D::~EnhancedPointSetVtkMapper3D() { m_Contour->Delete(); m_ContourSource->Delete(); m_PropAssembly->Delete(); // TODO: do cleanup correctly // Clean up all remaining actors and poly-data sources // std::for_each(m_PointActors.begin(), m_PointActors.end(), &mitk::EnhancedPointSetVtkMapper3D::DeleteVtkObject); // std::for_each(m_SphereSources.begin(), m_SphereSources.end(), // &mitk::EnhancedPointSetVtgkMapper3D::DeleteVtkObject); // std::for_each(m_CubeSources.begin(), m_CubeSources.end(), &mitk::EnhancedPointSetVtkMapper3D::DeleteVtkObject); // std::for_each(m_ConeSources.begin(), m_ConeSources.end(), &mitk::EnhancedPointSetVtkMapper3D::DeleteVtkObject); // std::for_each(m_CylinderSources.begin(), m_CylinderSources.end(), // &mitk::EnhancedPointSetVtkMapper3D::DeleteVtkObject); // } void mitk::EnhancedPointSetVtkMapper3D::UpdateVtkObjects() { // get and update the PointSet const mitk::PointSet *pointset = this->GetInput(); // pointset->Update(); int timestep = this->GetTimestep(); mitk::PointSet::DataType *itkPointSet = pointset->GetPointSet(timestep); mitk::PointSet::PointsContainer *points = itkPointSet->GetPoints(); mitk::PointSet::PointDataContainer *pointData = itkPointSet->GetPointData(); assert(points->Size() == pointData->Size()); mitk::PointSet::PointsIterator pIt; mitk::PointSet::PointDataIterator pdIt; /* search removed points and delete the corresponding source/actor/mapper objects */ - for (ActorMap::iterator it = m_PointActors.begin(); it != m_PointActors.end();) + for (auto it = m_PointActors.begin(); it != m_PointActors.end();) { PointIdentifier id = it->first; if (!points->IndexExists(id)) { this->RemoveEntryFromSourceMaps(id); m_PropAssembly->GetParts()->RemoveItem(it->second.first); // remove from prop assembly if (it->second.first != nullptr) it->second.first->Delete(); // Delete actor, which deletes mapper too (reference count) - ActorMap::iterator er = it; // save iterator for deleting + auto er = it; // save iterator for deleting ++it; // advance iterator to next object m_PointActors.erase( er); // erase element from map. This invalidates er, therefore we had to advance it before deletion. } else ++it; } /* iterate over each point in the pointset and create corresponding vtk objects */ for (pIt = points->Begin(), pdIt = pointData->Begin(); pIt != itkPointSet->GetPoints()->End(); ++pIt, ++pdIt) { PointIdentifier pointID = pIt->Index(); assert(pointID == pdIt->Index()); mitk::PointSet::PointDataType data = pdIt->Value(); - ActorMap::iterator aIt = m_PointActors.find(pointID); // Does an actor exist for the point? + auto aIt = m_PointActors.find(pointID); // Does an actor exist for the point? /* Create/Update sources for the point */ vtkActor *a = nullptr; bool newPoint = (aIt == m_PointActors.end()); // current point is new bool specChanged = (!newPoint && data.pointSpec != aIt->second.second); // point spec of current point has changed if (newPoint) // point did not exist before, we have to create vtk objects for it { // create actor and mapper for the new point a = vtkActor::New(); vtkPolyDataMapper *m = vtkPolyDataMapper::New(); a->SetMapper(m); m->UnRegister(nullptr); aIt = m_PointActors.insert(std::make_pair(pointID, std::make_pair(a, data.pointSpec))) .first; // insert element and update actormap iterator to point to new element m_PropAssembly->AddPart(a); } else { a = aIt->second.first; if (specChanged) // point exists, but point spec has changed { this->RemoveEntryFromSourceMaps(pointID); } } if (newPoint || specChanged) // new point OR existing point but point spec changed { vtkPolyDataAlgorithm *source = nullptr; // works only in VTK 5+ switch (data.pointSpec) // add to new map { // TODO: look up representation in a representationlookuptable case PTSTART: // cube m_CubeSources[pointID] = vtkCubeSource::New(); source = m_CubeSources[pointID]; break; case PTCORNER: // cone m_ConeSources[pointID] = vtkConeSource::New(); source = m_ConeSources[pointID]; break; case PTEDGE: // cylinder m_CylinderSources[pointID] = vtkCylinderSource::New(); source = m_CylinderSources[pointID]; break; case PTUNDEFINED: // sphere case PTEND: default: m_SphereSources[pointID] = vtkSphereSource::New(); source = m_SphereSources[pointID]; break; } - vtkPolyDataMapper *m = dynamic_cast(a->GetMapper()); + auto *m = dynamic_cast(a->GetMapper()); assert(m != nullptr); m->SetInputConnection(source->GetOutputPort()); aIt->second.second = data.pointSpec; // update point spec in actormap } } // for each point } void mitk::EnhancedPointSetVtkMapper3D::ApplyColorAndOpacityProperties(mitk::BaseRenderer *renderer, vtkActor * /*actor*/) { this->UpdateVtkObjects(); /* iterate over all points in pointset and apply properties to corresponding vtk objects */ // get and update the PointSet const mitk::PointSet *pointset = this->GetInput(); int timestep = this->GetTimestep(); mitk::PointSet::DataType *itkPointSet = pointset->GetPointSet(timestep); mitk::PointSet::PointsContainer *points = itkPointSet->GetPoints(); mitk::PointSet::PointDataContainer *pointData = itkPointSet->GetPointData(); assert(points->Size() == pointData->Size()); mitk::PointSet::PointsIterator pIt; mitk::PointSet::PointDataIterator pdIt; mitk::DataNode *n = this->GetDataNode(); assert(n != nullptr); for (pIt = points->Begin(), pdIt = pointData->Begin(); pIt != itkPointSet->GetPoints()->End(); ++pIt, ++pdIt) // for each point in the pointset { PointIdentifier pointID = pIt->Index(); assert(pointID == pdIt->Index()); mitk::PointSet::PointDataType data = pdIt->Value(); - ActorMap::iterator aIt = m_PointActors.find(pointID); // Does an actor exist for the point? + auto aIt = m_PointActors.find(pointID); // Does an actor exist for the point? assert(aIt != m_PointActors.end()); // UpdateVtkObjects() must ensure that actor exists vtkActor *a = aIt->second.first; assert(a != nullptr); SetVtkMapperImmediateModeRendering(a->GetMapper()); /* update properties */ // visibility bool pointVisibility = true; bool visValueFound = false; mitk::BaseProperty *visProp = n->GetProperty("visibility", renderer); - mitk::BoolLookupTableProperty *visLTProp = dynamic_cast(visProp); + auto *visLTProp = dynamic_cast(visProp); if (visLTProp != nullptr) { mitk::BoolLookupTable visLookupTable = visLTProp->GetValue(); // if (visLookupTable != nullptr) //{ try { pointVisibility = visLookupTable.GetTableValue(pointID); visValueFound = true; } catch (...) { } //} } if (visValueFound == false) { pointVisibility = n->IsVisible(renderer, "show points"); // use BoolProperty instead } a->SetVisibility(pointVisibility); // opacity float opacity = 1.0; bool opValueFound = false; mitk::BaseProperty *opProp = n->GetProperty("opacity", renderer); - mitk::FloatLookupTableProperty *opLTProp = dynamic_cast(opProp); + auto *opLTProp = dynamic_cast(opProp); if (opLTProp != nullptr) { mitk::FloatLookupTable opLookupTable = opLTProp->GetValue(); // if (opLookupTable != nullptr) //{ try { opacity = opLookupTable.GetTableValue(pointID); opValueFound = true; } catch (...) { } //} } if (opValueFound == false) { n->GetOpacity(opacity, renderer); } a->GetProperty()->SetOpacity(opacity); ////////////////////// continue here /////////////////// // pointsize & point position float pointSize = 1.0; n->GetFloatProperty("pointsize", pointSize, renderer); switch (data.pointSpec) { // TODO: look up representation in a representationlookuptable case PTSTART: // cube m_CubeSources[pointID]->SetXLength(pointSize); m_CubeSources[pointID]->SetYLength(pointSize); m_CubeSources[pointID]->SetZLength(pointSize); // m_CubeSources[pointID]->SetCenter(pos[0], pos[1], pos[2]); break; case PTCORNER: // cone m_ConeSources[pointID]->SetRadius(pointSize / 2); m_ConeSources[pointID]->SetHeight(pointSize); m_ConeSources[pointID]->SetResolution(2); // two crossed triangles. Maybe introduce an extra property for // m_ConeSources[pointID]->SetCenter(pos[0], pos[1], pos[2]); break; case PTEDGE: // cylinder m_CylinderSources[pointID]->SetRadius(pointSize / 2); m_CylinderSources[pointID]->SetHeight(pointSize); m_CylinderSources[pointID]->CappingOn(); m_CylinderSources[pointID]->SetResolution(6); // m_CylinderSources[pointID]->SetCenter(pos[0], pos[1], pos[2]); break; case PTUNDEFINED: // sphere case PTEND: default: m_SphereSources[pointID]->SetRadius(pointSize / 2); m_SphereSources[pointID]->SetThetaResolution(10); m_SphereSources[pointID]->SetPhiResolution(10); // m_SphereSources[pointID]->SetCenter(pos[0], pos[1], pos[2]); break; } // set position mitk::Point3D pos = pIt->Value(); aIt->second.first->SetPosition(pos[0], pos[1], pos[2]); // selectedcolor & color float color[3]; if (data.selected) { if (!n->GetColor(color, renderer, "selectedcolor")) n->GetColor(color, renderer); } else { mitk::BaseProperty *a = n->GetProperty("colorLookupTable", renderer); - mitk::LookupTableProperty *b = dynamic_cast(a); + auto *b = dynamic_cast(a); if (b != nullptr) { mitk::LookupTable::Pointer c = b->GetLookupTable(); vtkLookupTable *d = c->GetVtkLookupTable(); double *e = d->GetTableValue(pointID); color[0] = e[0]; color[1] = e[1]; color[2] = e[2]; } else { if (!n->GetColor(color, renderer, "unselectedcolor")) n->GetColor(color, renderer); } } // TODO: What about "color" property? 2D Mapper only uses unselected and selected color properties a->GetProperty()->SetColor(color[0], color[1], color[2]); // TODO: label property } // TODO test different pointSpec // TODO "line width" "show contour" "contourcolor" "contoursize" "close contour" "show label", "label" // TODO "show points" vs "visibility" - is visibility evaluated at all? in a superclass maybe? // TODO create lookup tables for all properties that should be evaluated per point. also create editor widgets for // these lookup tables! // TODO check if property changes and pointset changes are reflected in the render window immediately. // TODO check behavior with large PointSets // TODO check for memory leaks on adding/deleting points } void mitk::EnhancedPointSetVtkMapper3D::GenerateDataForRenderer(mitk::BaseRenderer *renderer) { BaseLocalStorage *ls = m_LSH.GetLocalStorage(renderer); bool needGenerateData = ls->IsGenerateDataRequired(renderer, this, GetDataNode()); if (needGenerateData) { ls->UpdateGenerateDataTime(); this->UpdateVtkObjects(); } ApplyColorAndOpacityProperties(renderer, nullptr); } void mitk::EnhancedPointSetVtkMapper3D::UpdateVtkTransform(mitk::BaseRenderer * /*renderer*/) { // TODO: apply new transform if time step changed // vtkLinearTransform * vtktransform = // this->GetDataNode()->GetVtkTransform(this->GetTimestep()); // m_SelectedActor->SetUserTransform(vtktransform); // m_UnselectedActor->SetUserTransform(vtktransform); // m_ContourActor->SetUserTransform(vtktransform); } void mitk::EnhancedPointSetVtkMapper3D::SetDefaultProperties(mitk::DataNode *node, mitk::BaseRenderer *renderer, bool overwrite) { node->AddProperty("line width", mitk::IntProperty::New(2), renderer, overwrite); node->AddProperty("pointsize", mitk::FloatProperty::New(1.0), renderer, overwrite); node->AddProperty( "selectedcolor", mitk::ColorProperty::New(1.0f, 1.0f, 0.0f), renderer, overwrite); // yellow for selected node->AddProperty( "unselectedcolor", mitk::ColorProperty::New(0.5f, 1.0f, 0.5f), renderer, overwrite); // middle green for unselected node->AddProperty("color", mitk::ColorProperty::New(1.0f, 0.0f, 0.0f), renderer, overwrite); // red as standard node->AddProperty("show contour", mitk::BoolProperty::New(false), renderer, overwrite); node->AddProperty("contourcolor", mitk::ColorProperty::New(1.0f, 0.0f, 0.0f), renderer, overwrite); node->AddProperty("contoursize", mitk::FloatProperty::New(0.5), renderer, overwrite); node->AddProperty("show points", mitk::BoolProperty::New(true), renderer, overwrite); node->AddProperty("show label", mitk::BoolProperty::New(false), renderer, overwrite); node->AddProperty("label", mitk::StringProperty::New("P"), renderer, overwrite); node->AddProperty("opacity", mitk::FloatProperty::New(1.0), renderer, overwrite); Superclass::SetDefaultProperties(node, renderer, overwrite); } void mitk::EnhancedPointSetVtkMapper3D::DeleteVtkObject(vtkObject *o) { if (o != nullptr) o->Delete(); } void mitk::EnhancedPointSetVtkMapper3D::RemoveEntryFromSourceMaps(mitk::PointSet::PointIdentifier pointID) { - ActorMap::iterator aIt = m_PointActors.find(pointID); + auto aIt = m_PointActors.find(pointID); if (aIt == m_PointActors.end()) return; switch (aIt->second.second) // erase in old map { // TODO: look up representation in a representationlookuptable case PTSTART: // cube m_CubeSources[pointID]->Delete(); m_CubeSources.erase(pointID); break; case PTCORNER: // cone m_ConeSources[pointID]->Delete(); m_ConeSources.erase(pointID); break; case PTEDGE: // cylinder m_CylinderSources[pointID]->Delete(); m_CylinderSources.erase(pointID); break; case PTUNDEFINED: // sphere case PTEND: default: m_SphereSources[pointID]->Delete(); m_SphereSources.erase(pointID); break; } } diff --git a/Modules/MapperExt/src/mitkGPUVolumeMapper3D.cpp b/Modules/MapperExt/src/mitkGPUVolumeMapper3D.cpp index fb12fd9cde..30d42b37c1 100644 --- a/Modules/MapperExt/src/mitkGPUVolumeMapper3D.cpp +++ b/Modules/MapperExt/src/mitkGPUVolumeMapper3D.cpp @@ -1,592 +1,592 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #define GPU_INFO MITK_INFO("mapper.vr") #define GPU_WARN MITK_WARN("mapper.vr") #define GPU_ERROR MITK_ERROR("mapper.vr") #include "mitkGPUVolumeMapper3D.h" #include "mitkDataNode.h" #include "mitkColorProperty.h" #include "mitkColorProperty.h" #include "mitkLevelWindow.h" #include "mitkLevelWindowProperty.h" #include "mitkLookupTableProperty.h" #include "mitkProperties.h" #include "mitkRenderingManager.h" #include "mitkTransferFunctionInitializer.h" #include "mitkTransferFunctionProperty.h" #include "mitkVtkPropRenderer.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "vtkMitkGPUVolumeRayCastMapper.h" #include "vtkOpenGLGPUVolumeRayCastMapper.h" const mitk::Image *mitk::GPUVolumeMapper3D::GetInput() { return static_cast(GetDataNode()->GetData()); } void mitk::GPUVolumeMapper3D::MitkRenderVolumetricGeometry(mitk::BaseRenderer *renderer) { VtkMapper::MitkRenderVolumetricGeometry(renderer); // if(ls->m_gpuInitialized) // ls->m_MapperGPU->UpdateMTime(); } bool mitk::GPUVolumeMapper3D::InitGPU(mitk::BaseRenderer * /*renderer*/) { return false; } void mitk::GPUVolumeMapper3D::InitCPU(mitk::BaseRenderer *renderer) { LocalStorage *ls = m_LSH.GetLocalStorage(renderer); if (ls->m_cpuInitialized) return; ls->m_VtkRenderWindow = renderer->GetVtkRenderer()->GetRenderWindow(); ls->m_MapperCPU = vtkSmartPointer::New(); int numThreads = ls->m_MapperCPU->GetNumberOfThreads(); GPU_INFO << "initializing cpu-raycast-vr (vtkFixedPointVolumeRayCastMapper) (" << numThreads << " threads)"; ls->m_MapperCPU->SetSampleDistance(1.0); // ls->m_MapperCPU->LockSampleDistanceToInputSpacingOn(); ls->m_MapperCPU->SetImageSampleDistance(1.0); ls->m_MapperCPU->IntermixIntersectingGeometryOn(); ls->m_MapperCPU->SetAutoAdjustSampleDistances(0); ls->m_VolumePropertyCPU = vtkSmartPointer::New(); ls->m_VolumePropertyCPU->ShadeOn(); ls->m_VolumePropertyCPU->SetAmbient(0.10f); // 0.05f ls->m_VolumePropertyCPU->SetDiffuse(0.50f); // 0.45f ls->m_VolumePropertyCPU->SetSpecular(0.40f); // 0.50f ls->m_VolumePropertyCPU->SetSpecularPower(16.0f); ls->m_VolumePropertyCPU->SetInterpolationTypeToLinear(); ls->m_VolumeCPU = vtkSmartPointer::New(); ls->m_VolumeCPU->SetMapper(ls->m_MapperCPU); ls->m_VolumeCPU->SetProperty(ls->m_VolumePropertyCPU); ls->m_VolumeCPU->VisibilityOn(); ls->m_MapperCPU->SetInputConnection(m_UnitSpacingImageFilter->GetOutputPort()); // m_Resampler->GetOutput()); ls->m_cpuInitialized = true; } void mitk::GPUVolumeMapper3D::DeinitCPU(mitk::BaseRenderer *renderer) { LocalStorage *ls = m_LSH.GetLocalStorage(renderer); if (!ls->m_cpuInitialized) return; GPU_INFO << "deinitializing cpu-raycast-vr"; ls->m_VolumePropertyCPU = nullptr; ls->m_MapperCPU = nullptr; ls->m_VolumeCPU = nullptr; ls->m_cpuInitialized = false; } mitk::GPUVolumeMapper3D::GPUVolumeMapper3D() { m_Volumenullptr = nullptr; m_commonInitialized = false; } mitk::GPUVolumeMapper3D::~GPUVolumeMapper3D() { DeinitCommon(); } void mitk::GPUVolumeMapper3D::InitCommon() { if (m_commonInitialized) return; m_UnitSpacingImageFilter = vtkSmartPointer::New(); m_UnitSpacingImageFilter->SetOutputSpacing(1.0, 1.0, 1.0); CreateDefaultTransferFunctions(); m_commonInitialized = true; } void mitk::GPUVolumeMapper3D::DeinitCommon() { if (!m_commonInitialized) return; m_commonInitialized = false; } bool mitk::GPUVolumeMapper3D::IsRenderable(mitk::BaseRenderer *renderer) { if (!GetDataNode()) return false; DataNode *node = GetDataNode(); bool visible = true; node->GetVisibility(visible, renderer, "visible"); if (!visible) return false; bool value = false; if (!node->GetBoolProperty("volumerendering", value, renderer)) return false; if (!value) return false; - mitk::Image *input = const_cast(this->GetInput()); + auto *input = const_cast(this->GetInput()); if (!input || !input->IsInitialized()) return false; vtkImageData *inputData = input->GetVtkImageData(this->GetTimestep()); if (inputData == nullptr) return false; return true; } void mitk::GPUVolumeMapper3D::InitVtkMapper(mitk::BaseRenderer *renderer) { if (IsRAYEnabled(renderer)) { DeinitCPU(renderer); if (!InitRAY(renderer)) { GPU_WARN << "hardware renderer can't initialize ... falling back to software renderer"; goto fallback; } } else if (IsGPUEnabled(renderer)) { DeinitCPU(renderer); DeinitRAY(renderer); if (!InitGPU(renderer)) { GPU_WARN << "hardware renderer can't initialize ... falling back to software renderer"; goto fallback; } } else { fallback: DeinitRAY(renderer); InitCPU(renderer); } } vtkProp *mitk::GPUVolumeMapper3D::GetVtkProp(mitk::BaseRenderer *renderer) { if (!IsRenderable(renderer)) { if (!m_Volumenullptr) { m_Volumenullptr = vtkSmartPointer::New(); m_Volumenullptr->VisibilityOff(); } return m_Volumenullptr; } InitCommon(); InitVtkMapper(renderer); LocalStorage *ls = m_LSH.GetLocalStorage(renderer); if (ls->m_rayInitialized) return ls->m_VolumeRAY; if (ls->m_gpuInitialized) return ls->m_VolumeGPU; return ls->m_VolumeCPU; } void mitk::GPUVolumeMapper3D::GenerateDataForRenderer(mitk::BaseRenderer *renderer) { if (!IsRenderable(renderer)) return; InitCommon(); InitVtkMapper(renderer); - mitk::Image *input = const_cast(this->GetInput()); + auto *input = const_cast(this->GetInput()); vtkImageData *inputData = input->GetVtkImageData(this->GetTimestep()); m_UnitSpacingImageFilter->SetInputData(inputData); LocalStorage *ls = m_LSH.GetLocalStorage(renderer); if (ls->m_rayInitialized) { GenerateDataRAY(renderer); } else if (ls->m_gpuInitialized) { GenerateDataGPU(renderer); } else { GenerateDataCPU(renderer); } // UpdateTransferFunctions UpdateTransferFunctions(renderer); } void mitk::GPUVolumeMapper3D::GenerateDataGPU(mitk::BaseRenderer * /*renderer*/) { } void mitk::GPUVolumeMapper3D::GenerateDataCPU(mitk::BaseRenderer *renderer) { LocalStorage *ls = m_LSH.GetLocalStorage(renderer); int nextLod = mitk::RenderingManager::GetInstance()->GetNextLOD(renderer); if (IsLODEnabled(renderer) && nextLod == 0) { ls->m_MapperCPU->SetImageSampleDistance(3.5); ls->m_MapperCPU->SetSampleDistance(1.25); ls->m_VolumePropertyCPU->SetInterpolationTypeToNearest(); } else { ls->m_MapperCPU->SetImageSampleDistance(1.0); ls->m_MapperCPU->SetSampleDistance(1.0); ls->m_VolumePropertyCPU->SetInterpolationTypeToLinear(); } // Check raycasting mode if (IsMIPEnabled(renderer)) ls->m_MapperCPU->SetBlendModeToMaximumIntensity(); else ls->m_MapperCPU->SetBlendModeToComposite(); // Updating shadings { float value = 0; if (GetDataNode()->GetFloatProperty("volumerendering.cpu.ambient", value, renderer)) ls->m_VolumePropertyCPU->SetAmbient(value); if (GetDataNode()->GetFloatProperty("volumerendering.cpu.diffuse", value, renderer)) ls->m_VolumePropertyCPU->SetDiffuse(value); if (GetDataNode()->GetFloatProperty("volumerendering.cpu.specular", value, renderer)) ls->m_VolumePropertyCPU->SetSpecular(value); if (GetDataNode()->GetFloatProperty("volumerendering.cpu.specular.power", value, renderer)) ls->m_VolumePropertyCPU->SetSpecularPower(value); } } void mitk::GPUVolumeMapper3D::CreateDefaultTransferFunctions() { m_DefaultOpacityTransferFunction = vtkSmartPointer::New(); m_DefaultOpacityTransferFunction->AddPoint(0.0, 0.0); m_DefaultOpacityTransferFunction->AddPoint(255.0, 0.8); m_DefaultOpacityTransferFunction->ClampingOn(); m_DefaultGradientTransferFunction = vtkSmartPointer::New(); m_DefaultGradientTransferFunction->AddPoint(0.0, 0.0); m_DefaultGradientTransferFunction->AddPoint(255.0, 0.8); m_DefaultGradientTransferFunction->ClampingOn(); m_DefaultColorTransferFunction = vtkSmartPointer::New(); m_DefaultColorTransferFunction->AddRGBPoint(0.0, 0.0, 0.0, 0.0); m_DefaultColorTransferFunction->AddRGBPoint(127.5, 1, 1, 0.0); m_DefaultColorTransferFunction->AddRGBPoint(255.0, 0.8, 0.2, 0); m_DefaultColorTransferFunction->ClampingOn(); m_BinaryOpacityTransferFunction = vtkSmartPointer::New(); m_BinaryOpacityTransferFunction->AddPoint(0, 0.0); m_BinaryOpacityTransferFunction->AddPoint(1, 1.0); m_BinaryGradientTransferFunction = vtkSmartPointer::New(); m_BinaryGradientTransferFunction->AddPoint(0.0, 1.0); m_BinaryColorTransferFunction = vtkSmartPointer::New(); } void mitk::GPUVolumeMapper3D::UpdateTransferFunctions(mitk::BaseRenderer *renderer) { LocalStorage *ls = m_LSH.GetLocalStorage(renderer); vtkPiecewiseFunction *opacityTransferFunction = m_DefaultOpacityTransferFunction; vtkPiecewiseFunction *gradientTransferFunction = m_DefaultGradientTransferFunction; vtkColorTransferFunction *colorTransferFunction = m_DefaultColorTransferFunction; bool isBinary = false; GetDataNode()->GetBoolProperty("binary", isBinary, renderer); if (isBinary) { opacityTransferFunction = m_BinaryOpacityTransferFunction; gradientTransferFunction = m_BinaryGradientTransferFunction; colorTransferFunction = m_BinaryColorTransferFunction; colorTransferFunction->RemoveAllPoints(); float rgb[3]; if (!GetDataNode()->GetColor(rgb, renderer)) rgb[0] = rgb[1] = rgb[2] = 1; colorTransferFunction->AddRGBPoint(0, rgb[0], rgb[1], rgb[2]); colorTransferFunction->Modified(); } else { - mitk::TransferFunctionProperty *transferFunctionProp = + auto *transferFunctionProp = dynamic_cast(this->GetDataNode()->GetProperty("TransferFunction", renderer)); if (transferFunctionProp) { opacityTransferFunction = transferFunctionProp->GetValue()->GetScalarOpacityFunction(); gradientTransferFunction = transferFunctionProp->GetValue()->GetGradientOpacityFunction(); colorTransferFunction = transferFunctionProp->GetValue()->GetColorTransferFunction(); } } if (ls->m_gpuInitialized) { ls->m_VolumePropertyGPU->SetColor(colorTransferFunction); ls->m_VolumePropertyGPU->SetScalarOpacity(opacityTransferFunction); ls->m_VolumePropertyGPU->SetGradientOpacity(gradientTransferFunction); } if (ls->m_rayInitialized) { ls->m_VolumePropertyRAY->SetColor(colorTransferFunction); ls->m_VolumePropertyRAY->SetScalarOpacity(opacityTransferFunction); ls->m_VolumePropertyRAY->SetGradientOpacity(gradientTransferFunction); } if (ls->m_cpuInitialized) { ls->m_VolumePropertyCPU->SetColor(colorTransferFunction); ls->m_VolumePropertyCPU->SetScalarOpacity(opacityTransferFunction); ls->m_VolumePropertyCPU->SetGradientOpacity(gradientTransferFunction); } } void mitk::GPUVolumeMapper3D::ApplyProperties(vtkActor * /*actor*/, mitk::BaseRenderer * /*renderer*/) { // GPU_INFO << "ApplyProperties"; } void mitk::GPUVolumeMapper3D::SetDefaultProperties(mitk::DataNode *node, mitk::BaseRenderer *renderer, bool overwrite) { // GPU_INFO << "SetDefaultProperties"; node->AddProperty("volumerendering", mitk::BoolProperty::New(false), renderer, overwrite); node->AddProperty("volumerendering.usemip", mitk::BoolProperty::New(false), renderer, overwrite); node->AddProperty("volumerendering.uselod", mitk::BoolProperty::New(false), renderer, overwrite); node->AddProperty("volumerendering.cpu.ambient", mitk::FloatProperty::New(0.10f), renderer, overwrite); node->AddProperty("volumerendering.cpu.diffuse", mitk::FloatProperty::New(0.50f), renderer, overwrite); node->AddProperty("volumerendering.cpu.specular", mitk::FloatProperty::New(0.40f), renderer, overwrite); node->AddProperty("volumerendering.cpu.specular.power", mitk::FloatProperty::New(16.0f), renderer, overwrite); bool usegpu = true; #ifdef __APPLE__ usegpu = false; node->AddProperty("volumerendering.uselod", mitk::BoolProperty::New(true), renderer, overwrite); #endif node->AddProperty("volumerendering.usegpu", mitk::BoolProperty::New(usegpu), renderer, overwrite); node->AddProperty("volumerendering.useray", mitk::BoolProperty::New(false), renderer, overwrite); node->AddProperty("volumerendering.ray.ambient", mitk::FloatProperty::New(0.25f), renderer, overwrite); node->AddProperty("volumerendering.ray.diffuse", mitk::FloatProperty::New(0.50f), renderer, overwrite); node->AddProperty("volumerendering.ray.specular", mitk::FloatProperty::New(0.40f), renderer, overwrite); node->AddProperty("volumerendering.ray.specular.power", mitk::FloatProperty::New(16.0f), renderer, overwrite); node->AddProperty("volumerendering.gpu.ambient", mitk::FloatProperty::New(0.25f), renderer, overwrite); node->AddProperty("volumerendering.gpu.diffuse", mitk::FloatProperty::New(0.50f), renderer, overwrite); node->AddProperty("volumerendering.gpu.specular", mitk::FloatProperty::New(0.40f), renderer, overwrite); node->AddProperty("volumerendering.gpu.specular.power", mitk::FloatProperty::New(16.0f), renderer, overwrite); node->AddProperty("volumerendering.gpu.usetexturecompression", mitk::BoolProperty::New(false), renderer, overwrite); node->AddProperty("volumerendering.gpu.reducesliceartifacts", mitk::BoolProperty::New(false), renderer, overwrite); node->AddProperty("binary", mitk::BoolProperty::New(false), renderer, overwrite); mitk::Image::Pointer image = dynamic_cast(node->GetData()); if (image.IsNotNull() && image->IsInitialized()) { if ((overwrite) || (node->GetProperty("levelwindow", renderer) == nullptr)) { mitk::LevelWindowProperty::Pointer levWinProp = mitk::LevelWindowProperty::New(); mitk::LevelWindow levelwindow; levelwindow.SetAuto(image); levWinProp->SetLevelWindow(levelwindow); node->SetProperty("levelwindow", levWinProp, renderer); } if ((overwrite) || (node->GetProperty("TransferFunction", renderer) == nullptr)) { // add a default transfer function mitk::TransferFunction::Pointer tf = mitk::TransferFunction::New(); mitk::TransferFunctionInitializer::Pointer tfInit = mitk::TransferFunctionInitializer::New(tf); tfInit->SetTransferFunctionMode(0); node->SetProperty("TransferFunction", mitk::TransferFunctionProperty::New(tf.GetPointer())); } } Superclass::SetDefaultProperties(node, renderer, overwrite); } bool mitk::GPUVolumeMapper3D::IsLODEnabled(mitk::BaseRenderer *renderer) const { bool value = false; return GetDataNode()->GetBoolProperty("volumerendering.uselod", value, renderer) && value; } bool mitk::GPUVolumeMapper3D::IsMIPEnabled(mitk::BaseRenderer *renderer) { bool value = false; return GetDataNode()->GetBoolProperty("volumerendering.usemip", value, renderer) && value; } bool mitk::GPUVolumeMapper3D::IsGPUEnabled(mitk::BaseRenderer *renderer) { LocalStorage *ls = m_LSH.GetLocalStorage(renderer); bool value = false; return ls->m_gpuSupported && GetDataNode()->GetBoolProperty("volumerendering.usegpu", value, renderer) && value; } bool mitk::GPUVolumeMapper3D::InitRAY(mitk::BaseRenderer *renderer) { LocalStorage *ls = m_LSH.GetLocalStorage(renderer); if (ls->m_rayInitialized) return ls->m_raySupported; ls->m_VtkRenderWindow = renderer->GetVtkRenderer()->GetRenderWindow(); GPU_INFO << "initializing gpu-raycast-vr (vtkOpenGLGPUVolumeRayCastMapper)"; ls->m_MapperRAY = vtkSmartPointer::New(); ls->m_MapperRAY->SetAutoAdjustSampleDistances(0); ls->m_MapperRAY->SetSampleDistance(1.0); ls->m_VolumePropertyRAY = vtkSmartPointer::New(); ls->m_VolumePropertyRAY->ShadeOn(); ls->m_VolumePropertyRAY->SetAmbient(0.25f); // 0.05f ls->m_VolumePropertyRAY->SetDiffuse(0.50f); // 0.45f ls->m_VolumePropertyRAY->SetSpecular(0.40f); // 0.50f ls->m_VolumePropertyRAY->SetSpecularPower(16.0f); ls->m_VolumePropertyRAY->SetInterpolationTypeToLinear(); ls->m_VolumeRAY = vtkSmartPointer::New(); ls->m_VolumeRAY->SetMapper(ls->m_MapperRAY); ls->m_VolumeRAY->SetProperty(ls->m_VolumePropertyRAY); ls->m_VolumeRAY->VisibilityOn(); ls->m_MapperRAY->SetInputConnection(this->m_UnitSpacingImageFilter->GetOutputPort()); ls->m_raySupported = ls->m_MapperRAY->IsRenderSupported(renderer->GetRenderWindow(), ls->m_VolumePropertyRAY); ls->m_rayInitialized = true; return ls->m_raySupported; } void mitk::GPUVolumeMapper3D::DeinitRAY(mitk::BaseRenderer *renderer) { LocalStorage *ls = m_LSH.GetLocalStorage(renderer); if (ls->m_rayInitialized) { GPU_INFO << "deinitializing gpu-raycast-vr"; ls->m_MapperRAY = nullptr; ls->m_VolumePropertyRAY = nullptr; // Here ReleaseGraphicsResources has to be called to avoid VTK error messages. // This seems like a VTK bug, because ReleaseGraphicsResources() is ment for internal use, // but you cannot just delete the object (last smartpointer reference) without getting the // VTK error. ls->m_VolumeRAY->ReleaseGraphicsResources(renderer->GetVtkRenderer()->GetRenderWindow()); ls->m_VolumeRAY = nullptr; ls->m_rayInitialized = false; } } void mitk::GPUVolumeMapper3D::GenerateDataRAY(mitk::BaseRenderer *renderer) { LocalStorage *ls = m_LSH.GetLocalStorage(renderer); if (IsLODEnabled(renderer) && mitk::RenderingManager::GetInstance()->GetNextLOD(renderer) == 0) ls->m_MapperRAY->SetImageSampleDistance(4.0); else ls->m_MapperRAY->SetImageSampleDistance(1.0); // Check raycasting mode if (IsMIPEnabled(renderer)) ls->m_MapperRAY->SetBlendModeToMaximumIntensity(); else ls->m_MapperRAY->SetBlendModeToComposite(); // Updating shadings { float value = 0; if (GetDataNode()->GetFloatProperty("volumerendering.ray.ambient", value, renderer)) ls->m_VolumePropertyRAY->SetAmbient(value); if (GetDataNode()->GetFloatProperty("volumerendering.ray.diffuse", value, renderer)) ls->m_VolumePropertyRAY->SetDiffuse(value); if (GetDataNode()->GetFloatProperty("volumerendering.ray.specular", value, renderer)) ls->m_VolumePropertyRAY->SetSpecular(value); if (GetDataNode()->GetFloatProperty("volumerendering.ray.specular.power", value, renderer)) ls->m_VolumePropertyRAY->SetSpecularPower(value); } } bool mitk::GPUVolumeMapper3D::IsRAYEnabled(mitk::BaseRenderer *renderer) { LocalStorage *ls = m_LSH.GetLocalStorage(renderer); bool value = false; return ls->m_raySupported && GetDataNode()->GetBoolProperty("volumerendering.useray", value, renderer) && value; } diff --git a/Modules/MapperExt/src/mitkMeshMapper2D.cpp b/Modules/MapperExt/src/mitkMeshMapper2D.cpp index 44b7765800..0865e33d27 100644 --- a/Modules/MapperExt/src/mitkMeshMapper2D.cpp +++ b/Modules/MapperExt/src/mitkMeshMapper2D.cpp @@ -1,476 +1,476 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkMeshMapper2D.h" #include "mitkBaseRenderer.h" #include "mitkColorProperty.h" #include "mitkGL.h" #include "mitkLine.h" #include "mitkMesh.h" #include "mitkPlaneGeometry.h" #include "mitkProperties.h" #include #include const float selectedColor[] = {1.0f, 0.0f, 0.6f}; // for selected! mitk::MeshMapper2D::MeshMapper2D() { } mitk::MeshMapper2D::~MeshMapper2D() { } const mitk::Mesh *mitk::MeshMapper2D::GetInput(void) { return static_cast(GetDataNode()->GetData()); } // Return whether a point is "smaller" than the second static bool point3DSmaller(const mitk::Point3D &elem1, const mitk::Point3D &elem2) { if (elem1[0] != elem2[0]) return elem1[0] < elem2[0]; if (elem1[1] != elem2[1]) return elem1[1] < elem2[1]; return elem1[2] < elem2[2]; } void mitk::MeshMapper2D::Paint(mitk::BaseRenderer *renderer) { bool visible = true; GetDataNode()->GetVisibility(visible, renderer, "visible"); if (!visible) return; // @FIXME: Logik fuer update bool updateNeccesary = true; if (updateNeccesary) { // aus GenerateData mitk::Mesh::Pointer input = const_cast(this->GetInput()); // Get the TimeGeometry of the input object const TimeGeometry *inputTimeGeometry = input->GetTimeGeometry(); if ((inputTimeGeometry == nullptr) || (inputTimeGeometry->CountTimeSteps() == 0)) { return; } // // get the world time // ScalarType time = renderer->GetTime(); // // convert the world time in time steps of the input object // int timeStep = 0; if (time > itk::NumericTraits::NonpositiveMin()) timeStep = inputTimeGeometry->TimePointToTimeStep(time); if (inputTimeGeometry->IsValidTimeStep(timeStep) == false) { return; } mitk::Mesh::MeshType::Pointer itkMesh = input->GetMesh(timeStep); if (itkMesh.GetPointer() == nullptr) { return; } const PlaneGeometry *worldplanegeometry = (renderer->GetCurrentWorldPlaneGeometry()); // apply color and opacity read from the PropertyList ApplyColorAndOpacityProperties(renderer); vtkLinearTransform *transform = GetDataNode()->GetVtkTransform(); // List of the Points Mesh::DataType::PointsContainerConstIterator it, end; it = itkMesh->GetPoints()->Begin(); end = itkMesh->GetPoints()->End(); // iterator on the additional data of each point Mesh::PointDataIterator dataIt; //, dataEnd; dataIt = itkMesh->GetPointData()->Begin(); // for switching back to old color after using selected color float unselectedColor[4]; glGetFloatv(GL_CURRENT_COLOR, unselectedColor); while (it != end) { mitk::Point3D p, projected_p; float vtkp[3]; itk2vtk(it->Value(), vtkp); transform->TransformPoint(vtkp, vtkp); vtk2itk(vtkp, p); renderer->GetCurrentWorldPlaneGeometry()->Project(p, projected_p); Vector3D diff = p - projected_p; if (diff.GetSquaredNorm() < 4.0) { Point2D pt2d, tmp; renderer->WorldToDisplay(p, pt2d); Vector2D horz, vert; horz[0] = 5; horz[1] = 0; vert[0] = 0; vert[1] = 5; // check if the point is to be marked as selected if (dataIt->Value().selected) { horz[0] = 8; vert[1] = 8; glColor3f(selectedColor[0], selectedColor[1], selectedColor[2]); // red switch (dataIt->Value().pointSpec) { case PTSTART: // a quad glBegin(GL_LINE_LOOP); tmp = pt2d - horz + vert; glVertex2dv(&tmp[0]); tmp = pt2d + horz + vert; glVertex2dv(&tmp[0]); tmp = pt2d + horz - vert; glVertex2dv(&tmp[0]); tmp = pt2d - horz - vert; glVertex2dv(&tmp[0]); glEnd(); break; case PTUNDEFINED: // a diamond around the point glBegin(GL_LINE_LOOP); tmp = pt2d - horz; glVertex2dv(&tmp[0]); tmp = pt2d + vert; glVertex2dv(&tmp[0]); tmp = pt2d + horz; glVertex2dv(&tmp[0]); tmp = pt2d - vert; glVertex2dv(&tmp[0]); glEnd(); break; default: break; } // switch // the actual point glBegin(GL_POINTS); tmp = pt2d; glVertex2dv(&tmp[0]); glEnd(); } else // if not selected { glColor3f(unselectedColor[0], unselectedColor[1], unselectedColor[2]); switch (dataIt->Value().pointSpec) { case PTSTART: // a quad glBegin(GL_LINE_LOOP); tmp = pt2d - horz + vert; glVertex2dv(&tmp[0]); tmp = pt2d + horz + vert; glVertex2dv(&tmp[0]); tmp = pt2d + horz - vert; glVertex2dv(&tmp[0]); tmp = pt2d - horz - vert; glVertex2dv(&tmp[0]); glEnd(); break; case PTUNDEFINED: // drawing crosses glBegin(GL_LINES); tmp = pt2d - horz; glVertex2dv(&tmp[0]); tmp = pt2d + horz; glVertex2dv(&tmp[0]); tmp = pt2d - vert; glVertex2dv(&tmp[0]); tmp = pt2d + vert; glVertex2dv(&tmp[0]); glEnd(); break; default: { break; } } // switch } // else } ++it; ++dataIt; } // now connect the lines inbetween mitk::Mesh::PointType thisPoint; thisPoint.Fill(0); Point2D *firstOfCell = nullptr; Point2D *lastPoint = nullptr; unsigned int lastPointId = 0; bool lineSelected = false; Point3D firstOfCell3D; Point3D lastPoint3D; bool first; mitk::Line line; std::vector intersectionPoints; double t; // iterate through all cells and then iterate through all indexes of points in that cell Mesh::CellIterator cellIt, cellEnd; Mesh::CellDataIterator cellDataIt; //, cellDataEnd; Mesh::PointIdIterator cellIdIt, cellIdEnd; cellIt = itkMesh->GetCells()->Begin(); cellEnd = itkMesh->GetCells()->End(); cellDataIt = itkMesh->GetCellData()->Begin(); while (cellIt != cellEnd) { unsigned int numOfPointsInCell = cellIt->Value()->GetNumberOfPoints(); if (numOfPointsInCell > 1) { // iterate through all id's in the cell cellIdIt = cellIt->Value()->PointIdsBegin(); cellIdEnd = cellIt->Value()->PointIdsEnd(); firstOfCell3D = input->GetPoint(*cellIdIt, timeStep); intersectionPoints.clear(); intersectionPoints.reserve(numOfPointsInCell); first = true; while (cellIdIt != cellIdEnd) { lastPoint3D = thisPoint; thisPoint = input->GetPoint(*cellIdIt, timeStep); // search in data (vector<> selectedLines) if the index of the point is set. if so, then the line is selected. lineSelected = false; Mesh::SelectedLinesType selectedLines = cellDataIt->Value().selectedLines; // a line between 1(lastPoint) and 2(pt2d) has the Id 1, so look for the Id of lastPoint // since we only start, if we have more than one point in the cell, lastPointId is initiated with 0 - Mesh::SelectedLinesIter position = std::find(selectedLines.begin(), selectedLines.end(), lastPointId); + auto position = std::find(selectedLines.begin(), selectedLines.end(), lastPointId); if (position != selectedLines.end()) { lineSelected = true; } mitk::Point3D p, projected_p; float vtkp[3]; itk2vtk(thisPoint, vtkp); transform->TransformPoint(vtkp, vtkp); vtk2itk(vtkp, p); renderer->GetCurrentWorldPlaneGeometry()->Project(p, projected_p); Vector3D diff = p - projected_p; if (diff.GetSquaredNorm() < 4.0) { Point2D pt2d, tmp; renderer->WorldToDisplay(p, pt2d); if (lastPoint == nullptr) { // set the first point in the cell. This point in needed to close the polygon firstOfCell = new Point2D; *firstOfCell = pt2d; lastPoint = new Point2D; *lastPoint = pt2d; lastPointId = *cellIdIt; } else { if (lineSelected) { glColor3f(selectedColor[0], selectedColor[1], selectedColor[2]); // red // a line from lastPoint to thisPoint glBegin(GL_LINES); glVertex2dv(&(*lastPoint)[0]); glVertex2dv(&pt2d[0]); glEnd(); } else // if not selected { glColor3f(unselectedColor[0], unselectedColor[1], unselectedColor[2]); // drawing crosses glBegin(GL_LINES); glVertex2dv(&(*lastPoint)[0]); glVertex2dv(&pt2d[0]); glEnd(); } // to draw the line to the next in iteration step *lastPoint = pt2d; // and to search for the selection state of the line lastPointId = *cellIdIt; } // if..else } // if <4.0 // fill off-plane polygon part 1 if ((!first) && (worldplanegeometry != nullptr)) { line.SetPoints(lastPoint3D, thisPoint); if (worldplanegeometry->IntersectionPointParam(line, t) && ((t >= 0) && (t <= 1))) { intersectionPoints.push_back(line.GetPoint(t)); } } ++cellIdIt; first = false; } // while cellIdIter // closed polygon? if (cellDataIt->Value().closed) { // close the polygon if needed if (firstOfCell != nullptr) { lineSelected = false; Mesh::SelectedLinesType selectedLines = cellDataIt->Value().selectedLines; - Mesh::SelectedLinesIter position = std::find(selectedLines.begin(), selectedLines.end(), lastPointId); + auto position = std::find(selectedLines.begin(), selectedLines.end(), lastPointId); if (position != selectedLines.end()) // found the index { glColor3f(selectedColor[0], selectedColor[1], selectedColor[2]); // red // a line from lastPoint to firstPoint glBegin(GL_LINES); glVertex2dv(&(*lastPoint)[0]); glVertex2dv(&(*firstOfCell)[0]); glEnd(); } else { glColor3f(unselectedColor[0], unselectedColor[1], unselectedColor[2]); glBegin(GL_LINES); glVertex2dv(&(*lastPoint)[0]); glVertex2dv(&(*firstOfCell)[0]); glEnd(); } } } // if closed // Axis-aligned bounding box(AABB) around the cell if selected and set in Property bool showBoundingBox; if (dynamic_cast(this->GetDataNode()->GetProperty("showBoundingBox")) == nullptr) showBoundingBox = false; else showBoundingBox = dynamic_cast(this->GetDataNode()->GetProperty("showBoundingBox"))->GetValue(); if (showBoundingBox) { if (cellDataIt->Value().selected) { mitk::Mesh::DataType::BoundingBoxPointer aABB = input->GetBoundingBoxFromCell(cellIt->Index()); if (aABB.IsNotNull()) { mitk::Mesh::PointType min, max; min = aABB->GetMinimum(); max = aABB->GetMaximum(); // project to the displayed geometry Point2D min2D, max2D; Point3D p, projected_p; float vtkp[3]; itk2vtk(min, vtkp); transform->TransformPoint(vtkp, vtkp); vtk2itk(vtkp, p); renderer->WorldToDisplay(p, min2D); itk2vtk(max, vtkp); transform->TransformPoint(vtkp, vtkp); vtk2itk(vtkp, p); renderer->GetCurrentWorldPlaneGeometry()->Project(p, projected_p); Vector3D diff = p - projected_p; if (diff.GetSquaredNorm() < 4.0) { renderer->WorldToDisplay(p, max2D); // draw the BoundingBox glColor3f(selectedColor[0], selectedColor[1], selectedColor[2]); // red // a line from lastPoint to firstPoint glBegin(GL_LINE_LOOP); glVertex2f(min2D[0], min2D[1]); glVertex2f(min2D[0], max2D[1]); glVertex2f(max2D[0], max2D[1]); glVertex2f(max2D[0], min2D[1]); glEnd(); } // draw bounding-box } // bounding-box exists } // cell selected } // show bounding-box // fill off-plane polygon part 2 if (worldplanegeometry != nullptr) { // consider line from last to first line.SetPoints(thisPoint, firstOfCell3D); if (worldplanegeometry->IntersectionPointParam(line, t) && ((t >= 0) && (t <= 1))) { intersectionPoints.push_back(line.GetPoint(t)); } std::sort(intersectionPoints.begin(), intersectionPoints.end(), point3DSmaller); std::vector::iterator it, end; end = intersectionPoints.end(); if ((intersectionPoints.size() % 2) != 0) { --end; // ensure even number of intersection-points } Point2D pt2d; for (it = intersectionPoints.begin(); it != end; ++it) { glBegin(GL_LINES); renderer->WorldToDisplay(*it, pt2d); glVertex2dv(pt2d.GetDataPointer()); ++it; renderer->WorldToDisplay(*it, pt2d); glVertex2dv(pt2d.GetDataPointer()); glEnd(); } if (it != intersectionPoints.end()) { glBegin(GL_LINES); renderer->WorldToDisplay(*it, pt2d); glVertex2dv(pt2d.GetDataPointer()); glVertex2dv(pt2d.GetDataPointer()); glEnd(); } } // fill off-plane polygon part 2 } // if numOfPointsInCell>1 delete firstOfCell; delete lastPoint; lastPoint = nullptr; firstOfCell = nullptr; lastPointId = 0; ++cellIt; ++cellDataIt; } } } diff --git a/Modules/MapperExt/src/mitkUnstructuredGridMapper2D.cpp b/Modules/MapperExt/src/mitkUnstructuredGridMapper2D.cpp index 95a64b1d88..d1187d31d4 100644 --- a/Modules/MapperExt/src/mitkUnstructuredGridMapper2D.cpp +++ b/Modules/MapperExt/src/mitkUnstructuredGridMapper2D.cpp @@ -1,559 +1,559 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkUnstructuredGridMapper2D.h" #include #include "mitkAbstractTransformGeometry.h" #include "mitkBaseRenderer.h" #include "mitkColorProperty.h" #include "mitkPlaneGeometry.h" #include "mitkProperties.h" #include "mitkTransferFunction.h" #include "mitkTransferFunctionProperty.h" #include "mitkUnstructuredGrid.h" #include "mitkVtkMapper3D.h" #include "mitkVtkScalarModeProperty.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "vtkPointSetSlicer.h" void mitk::UnstructuredGridMapper2D::GenerateDataForRenderer(mitk::BaseRenderer *renderer) { BaseLocalStorage *ls = m_LSH.GetLocalStorage(renderer); bool needGenerateData = ls->IsGenerateDataRequired(renderer, this, GetDataNode()); if (needGenerateData) { ls->UpdateGenerateDataTime(); mitk::DataNode::ConstPointer node = this->GetDataNode(); if (node.IsNull()) return; if (!node->GetProperty(m_ScalarMode, "scalar mode")) { m_ScalarMode = mitk::VtkScalarModeProperty::New(0); } if (!node->GetProperty(m_ScalarVisibility, "scalar visibility")) { m_ScalarVisibility = mitk::BoolProperty::New(true); } if (!node->GetProperty(m_Outline, "outline polygons")) { m_Outline = mitk::BoolProperty::New(false); } if (!node->GetProperty(m_Color, "color")) { m_Color = mitk::ColorProperty::New(1.0f, 1.0f, 1.0f); } if (!node->GetProperty(m_LineWidth, "line width")) { m_LineWidth = mitk::IntProperty::New(1); } } mitk::BaseData::Pointer input = const_cast(GetDataNode()->GetData()); assert(input); input->Update(); if (m_VtkPointSet) m_VtkPointSet->UnRegister(nullptr); m_VtkPointSet = this->GetVtkPointSet(renderer, this->GetTimestep()); assert(m_VtkPointSet); m_VtkPointSet->Register(nullptr); if (m_ScalarVisibility->GetValue()) { mitk::DataNode::ConstPointer node = this->GetDataNode(); mitk::TransferFunctionProperty::Pointer transferFuncProp; node->GetProperty(transferFuncProp, "TransferFunction", renderer); if (transferFuncProp.IsNotNull()) { mitk::TransferFunction::Pointer tf = transferFuncProp->GetValue(); if (m_ScalarsToColors) m_ScalarsToColors->UnRegister(nullptr); m_ScalarsToColors = static_cast(tf->GetColorTransferFunction()); m_ScalarsToColors->Register(nullptr); if (m_ScalarsToOpacity) m_ScalarsToOpacity->UnRegister(nullptr); m_ScalarsToOpacity = tf->GetScalarOpacityFunction(); m_ScalarsToOpacity->Register(nullptr); } else { if (m_ScalarsToColors) m_ScalarsToColors->UnRegister(nullptr); m_ScalarsToColors = this->GetVtkLUT(renderer); assert(m_ScalarsToColors); m_ScalarsToColors->Register(nullptr); float opacity; node->GetOpacity(opacity, renderer); if (m_ScalarsToOpacity) m_ScalarsToOpacity->UnRegister(nullptr); m_ScalarsToOpacity = vtkPiecewiseFunction::New(); double range[2]; m_VtkPointSet->GetScalarRange(range); m_ScalarsToOpacity->AddSegment(range[0], opacity, range[1], opacity); } } } void mitk::UnstructuredGridMapper2D::Paint(mitk::BaseRenderer *renderer) { bool visible = true; GetDataNode()->GetVisibility(visible, renderer, "visible"); if (!visible) return; vtkLinearTransform *vtktransform = GetDataNode()->GetVtkTransform(); vtkLinearTransform *inversetransform = vtktransform->GetLinearInverse(); PlaneGeometry::ConstPointer worldGeometry = renderer->GetCurrentWorldPlaneGeometry(); PlaneGeometry::ConstPointer worldPlaneGeometry = dynamic_cast(worldGeometry.GetPointer()); Point3D point; Vector3D normal; if (worldPlaneGeometry.IsNotNull()) { // set up vtkPlane according to worldGeometry point = worldPlaneGeometry->GetOrigin(); normal = worldPlaneGeometry->GetNormal(); normal.Normalize(); m_Plane->SetTransform((vtkAbstractTransform *)nullptr); } else { //@FIXME: does not work correctly. Does m_Plane->SetTransform really transforms a "plane plane" into a "curved //plane"? return; AbstractTransformGeometry::ConstPointer worldAbstractGeometry = dynamic_cast(renderer->GetCurrentWorldPlaneGeometry()); if (worldAbstractGeometry.IsNotNull()) { // set up vtkPlane according to worldGeometry point = const_cast(worldAbstractGeometry->GetParametricBoundingBox())->GetMinimum(); FillVector3D(normal, 0, 0, 1); m_Plane->SetTransform(worldAbstractGeometry->GetVtkAbstractTransform()->GetInverse()); } else return; } double vp[3], vnormal[3]; vnl2vtk(point.GetVnlVector(), vp); vnl2vtk(normal.GetVnlVector(), vnormal); // normally, we would need to transform the surface and cut the transformed surface with the cutter. // This might be quite slow. Thus, the idea is, to perform an inverse transform of the plane instead. //@todo It probably does not work for scaling operations yet:scaling operations have to be // dealed with after the cut is performed by scaling the contour. inversetransform->TransformPoint(vp, vp); inversetransform->TransformNormalAtPoint(vp, vnormal, vnormal); m_Plane->SetOrigin(vp); m_Plane->SetNormal(vnormal); // set data into cutter m_Slicer->SetInputData(m_VtkPointSet); // m_Cutter->GenerateCutScalarsOff(); // m_Cutter->SetSortByToSortByCell(); // calculate the cut m_Slicer->Update(); // apply color and opacity read from the PropertyList ApplyColorAndOpacityProperties(renderer); // traverse the cut contour vtkPolyData *contour = m_Slicer->GetOutput(); vtkPoints *vpoints = contour->GetPoints(); vtkCellArray *vlines = contour->GetLines(); vtkCellArray *vpolys = contour->GetPolys(); vtkPointData *vpointdata = contour->GetPointData(); vtkDataArray *vscalars = vpointdata->GetScalars(); vtkCellData *vcelldata = contour->GetCellData(); vtkDataArray *vcellscalars = vcelldata->GetScalars(); const int numberOfLines = contour->GetNumberOfLines(); const int numberOfPolys = contour->GetNumberOfPolys(); const bool useCellData = m_ScalarMode->GetVtkScalarMode() == VTK_SCALAR_MODE_DEFAULT || m_ScalarMode->GetVtkScalarMode() == VTK_SCALAR_MODE_USE_CELL_DATA; const bool usePointData = m_ScalarMode->GetVtkScalarMode() == VTK_SCALAR_MODE_USE_POINT_DATA; Point3D p; Point2D p2d; vlines->InitTraversal(); vpolys->InitTraversal(); mitk::Color outlineColor = m_Color->GetColor(); glLineWidth((float)m_LineWidth->GetValue()); for (int i = 0; i < numberOfLines; ++i) { vtkIdType *cell(nullptr); vtkIdType cellSize(0); vlines->GetNextCell(cellSize, cell); float rgba[4] = {outlineColor[0], outlineColor[1], outlineColor[2], 1.0f}; if (m_ScalarVisibility->GetValue() && vcellscalars) { if (useCellData) { // color each cell according to cell data double scalar = vcellscalars->GetComponent(i, 0); double rgb[3] = {1.0f, 1.0f, 1.0f}; m_ScalarsToColors->GetColor(scalar, rgb); rgba[0] = (float)rgb[0]; rgba[1] = (float)rgb[1]; rgba[2] = (float)rgb[2]; rgba[3] = (float)m_ScalarsToOpacity->GetValue(scalar); } else if (usePointData) { double scalar = vscalars->GetComponent(i, 0); double rgb[3] = {1.0f, 1.0f, 1.0f}; m_ScalarsToColors->GetColor(scalar, rgb); rgba[0] = (float)rgb[0]; rgba[1] = (float)rgb[1]; rgba[2] = (float)rgb[2]; rgba[3] = (float)m_ScalarsToOpacity->GetValue(scalar); } } glColor4fv(rgba); glBegin(GL_LINE_LOOP); for (int j = 0; j < cellSize; ++j) { vpoints->GetPoint(cell[j], vp); // take transformation via vtktransform into account vtktransform->TransformPoint(vp, vp); vtk2itk(vp, p); // convert 3D point (in mm) to display coordinates (units ) renderer->WorldToDisplay(p, p2d); // convert display coordinates ( (0,0) is top-left ) in GL coordinates ( (0,0) is bottom-left ) // p2d[1]=toGL-p2d[1]; // add the current vertex to the line glVertex2f(p2d[0], p2d[1]); } glEnd(); } bool polyOutline = m_Outline->GetValue(); bool scalarVisibility = m_ScalarVisibility->GetValue(); // cache the transformed points // a fixed size array is way faster than 'new' // slices through 3d cells usually do not generated // polygons with more than 6 vertices const int maxPolySize = 10; - Point2D *cachedPoints = new Point2D[maxPolySize * numberOfPolys]; + auto *cachedPoints = new Point2D[maxPolySize * numberOfPolys]; glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); // only draw polygons if there are cell scalars // or the outline property is set to true if (scalarVisibility && vcellscalars) { glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); for (int i = 0; i < numberOfPolys; ++i) { vtkIdType *cell(nullptr); vtkIdType cellSize(0); vpolys->GetNextCell(cellSize, cell); float rgba[4] = {1.0f, 1.0f, 1.0f, 0}; if (scalarVisibility && vcellscalars) { if (useCellData) { // color each cell according to cell data double scalar = vcellscalars->GetComponent(i + numberOfLines, 0); double rgb[3] = {1.0f, 1.0f, 1.0f}; m_ScalarsToColors->GetColor(scalar, rgb); rgba[0] = (float)rgb[0]; rgba[1] = (float)rgb[1]; rgba[2] = (float)rgb[2]; rgba[3] = (float)m_ScalarsToOpacity->GetValue(scalar); } else if (usePointData) { double scalar = vscalars->GetComponent(i, 0); double rgb[3] = {1.0f, 1.0f, 1.0f}; m_ScalarsToColors->GetColor(scalar, rgb); rgba[0] = (float)rgb[0]; rgba[1] = (float)rgb[1]; rgba[2] = (float)rgb[2]; rgba[3] = (float)m_ScalarsToOpacity->GetValue(scalar); } } glColor4fv(rgba); glBegin(GL_POLYGON); for (int j = 0; j < cellSize; ++j) { vpoints->GetPoint(cell[j], vp); // take transformation via vtktransform into account vtktransform->TransformPoint(vp, vp); vtk2itk(vp, p); // convert 3D point (in mm) to display coordinates (units ) renderer->WorldToDisplay(p, p2d); // convert display coordinates ( (0,0) is top-left ) in GL coordinates ( (0,0) is bottom-left ) // p2d[1]=toGL-p2d[1]; cachedPoints[i * 10 + j][0] = p2d[0]; cachedPoints[i * 10 + j][1] = p2d[1]; // add the current vertex to the line glVertex2f(p2d[0], p2d[1]); } glEnd(); } if (polyOutline) { vpolys->InitTraversal(); glColor4f(outlineColor[0], outlineColor[1], outlineColor[2], 1.0f); glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); for (int i = 0; i < numberOfPolys; ++i) { vtkIdType *cell(nullptr); vtkIdType cellSize(0); vpolys->GetNextCell(cellSize, cell); glBegin(GL_POLYGON); // glPolygonOffset(1.0, 1.0); for (int j = 0; j < cellSize; ++j) { // add the current vertex to the line glVertex2f(cachedPoints[i * 10 + j][0], cachedPoints[i * 10 + j][1]); } glEnd(); } } } glDisable(GL_BLEND); delete[] cachedPoints; } vtkAbstractMapper3D *mitk::UnstructuredGridMapper2D::GetVtkAbstractMapper3D(mitk::BaseRenderer *renderer) { // MITK_INFO << "GETVTKABSTRACTMAPPER3D\n"; mitk::DataNode::ConstPointer node = this->GetDataNode(); if (node.IsNull()) return nullptr; mitk::VtkMapper::Pointer mitkMapper = dynamic_cast(node->GetMapper(2)); if (mitkMapper.IsNull()) { return nullptr; } mitkMapper->Update(renderer); - vtkAssembly *assembly = dynamic_cast(mitkMapper->GetVtkProp(renderer)); + auto *assembly = dynamic_cast(mitkMapper->GetVtkProp(renderer)); if (assembly) { vtkProp3DCollection *collection = assembly->GetParts(); collection->InitTraversal(); vtkProp3D *prop3d = nullptr; do { prop3d = collection->GetNextProp3D(); - vtkActor *actor = dynamic_cast(prop3d); + auto *actor = dynamic_cast(prop3d); if (actor) { return dynamic_cast(actor->GetMapper()); } - vtkVolume *volume = dynamic_cast(prop3d); + auto *volume = dynamic_cast(prop3d); if (volume) { return dynamic_cast(volume->GetMapper()); } } while (prop3d != collection->GetLastProp3D()); } else { - vtkActor *actor = dynamic_cast(mitkMapper->GetVtkProp(renderer)); + auto *actor = dynamic_cast(mitkMapper->GetVtkProp(renderer)); if (actor) { return dynamic_cast(actor->GetMapper()); } - vtkVolume *volume = dynamic_cast(mitkMapper->GetVtkProp(renderer)); + auto *volume = dynamic_cast(mitkMapper->GetVtkProp(renderer)); if (volume) { return dynamic_cast(volume->GetMapper()); } } return nullptr; } vtkPointSet *mitk::UnstructuredGridMapper2D::GetVtkPointSet(mitk::BaseRenderer *renderer, int time) { // MITK_INFO << "GETVTKPOINTSET\n"; vtkAbstractMapper3D *abstractMapper = GetVtkAbstractMapper3D(renderer); if (abstractMapper == nullptr) { // try to get data from the node mitk::DataNode::ConstPointer node = this->GetDataNode(); if (node.IsNull()) return nullptr; mitk::BaseData::Pointer data = node->GetData(); mitk::UnstructuredGrid::Pointer grid = dynamic_cast(data.GetPointer()); if (!grid.IsNull()) return static_cast(grid->GetVtkUnstructuredGrid(time)); return nullptr; } else { - vtkMapper *mapper = dynamic_cast(abstractMapper); + auto *mapper = dynamic_cast(abstractMapper); if (mapper) { return dynamic_cast(mapper->GetInput()); } - vtkAbstractVolumeMapper *volMapper = dynamic_cast(abstractMapper); + auto *volMapper = dynamic_cast(abstractMapper); if (volMapper) { return dynamic_cast(volMapper->GetDataSetInput()); } } return nullptr; } vtkScalarsToColors *mitk::UnstructuredGridMapper2D::GetVtkLUT(mitk::BaseRenderer *renderer) { // MITK_INFO << "GETVTKLUT\n"; - vtkMapper *mapper = dynamic_cast(GetVtkAbstractMapper3D(renderer)); + auto *mapper = dynamic_cast(GetVtkAbstractMapper3D(renderer)); if (mapper) return mapper->GetLookupTable(); else { mitk::DataNode::ConstPointer node = this->GetDataNode(); if (node.IsNull()) return nullptr; mitk::VtkMapper::Pointer mitkMapper = dynamic_cast(node->GetMapper(2)); if (mitkMapper.IsNull()) { // MITK_INFO << "mitkMapper is null\n"; return nullptr; } mitkMapper->Update(renderer); - vtkVolume *volume = dynamic_cast(mitkMapper->GetVtkProp(renderer)); + auto *volume = dynamic_cast(mitkMapper->GetVtkProp(renderer)); if (volume) { // MITK_INFO << "found volume prop\n"; return static_cast(volume->GetProperty()->GetRGBTransferFunction()); } - vtkAssembly *assembly = dynamic_cast(mitkMapper->GetVtkProp(renderer)); + auto *assembly = dynamic_cast(mitkMapper->GetVtkProp(renderer)); if (assembly) { // MITK_INFO << "found assembly prop\n"; mitk::TransferFunctionProperty::Pointer transferFuncProp; node->GetProperty(transferFuncProp, "TransferFunction", nullptr); if (transferFuncProp.IsNotNull()) { MITK_INFO << "return colortransferfunction\n"; return static_cast(transferFuncProp->GetValue()->GetColorTransferFunction()); } } return nullptr; } } bool mitk::UnstructuredGridMapper2D::IsConvertibleToVtkPointSet(mitk::BaseRenderer *renderer) { return (GetVtkPointSet(renderer, this->GetTimestep()) != nullptr); } mitk::UnstructuredGridMapper2D::UnstructuredGridMapper2D() { m_Plane = vtkPlane::New(); m_Slicer = vtkPointSetSlicer::New(); m_Slicer->SetSlicePlane(m_Plane); m_ScalarsToColors = nullptr; m_ScalarsToOpacity = nullptr; m_VtkPointSet = nullptr; // m_LUT = vtkLookupTable::New(); // m_LUT->SetTableRange( 0, 255 ); // m_LUT->SetNumberOfColors( 255 ); // m_LUT->SetRampToLinear (); // m_LUT->Build(); } mitk::UnstructuredGridMapper2D::~UnstructuredGridMapper2D() { m_Slicer->Delete(); m_Plane->Delete(); if (m_ScalarsToOpacity != nullptr) m_ScalarsToOpacity->UnRegister(nullptr); if (m_ScalarsToColors != nullptr) m_ScalarsToColors->UnRegister(nullptr); if (m_VtkPointSet != nullptr) m_VtkPointSet->UnRegister(nullptr); } diff --git a/Modules/MapperExt/src/mitkVolumeMapperVtkSmart3D.cpp b/Modules/MapperExt/src/mitkVolumeMapperVtkSmart3D.cpp index f80a1fb416..fa505e97f4 100644 --- a/Modules/MapperExt/src/mitkVolumeMapperVtkSmart3D.cpp +++ b/Modules/MapperExt/src/mitkVolumeMapperVtkSmart3D.cpp @@ -1,255 +1,255 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkVolumeMapperVtkSmart3D.h" #include "mitkTransferFunctionProperty.h" #include "mitkTransferFunctionInitializer.h" #include "mitkLevelWindowProperty.h" #include #include #include #include #include void mitk::VolumeMapperVtkSmart3D::GenerateDataForRenderer(mitk::BaseRenderer *renderer) { if (this->GetDataNode()->GetMTime() < this->GetMTime()) { return; } bool value; this->GetDataNode()->GetBoolProperty("volumerendering", value, renderer); if (!value) { m_Volume->VisibilityOff(); return; } else { m_Volume->VisibilityOn(); } UpdateTransferFunctions(renderer); UpdateRenderMode(renderer); this->Modified(); } vtkProp* mitk::VolumeMapperVtkSmart3D::GetVtkProp(mitk::BaseRenderer *) { if (!m_Volume->GetMapper()) { createMapper(GetInputImage()); createVolume(); createVolumeProperty(); } return m_Volume; } void mitk::VolumeMapperVtkSmart3D::ApplyProperties(vtkActor *, mitk::BaseRenderer *) { } void mitk::VolumeMapperVtkSmart3D::SetDefaultProperties(mitk::DataNode *node, mitk::BaseRenderer *renderer, bool overwrite) { // GPU_INFO << "SetDefaultProperties"; node->AddProperty("volumerendering", mitk::BoolProperty::New(false), renderer, overwrite); node->AddProperty("volumerendering.usemip", mitk::BoolProperty::New(false), renderer, overwrite); node->AddProperty("volumerendering.cpu.ambient", mitk::FloatProperty::New(0.10f), renderer, overwrite); node->AddProperty("volumerendering.cpu.diffuse", mitk::FloatProperty::New(0.50f), renderer, overwrite); node->AddProperty("volumerendering.cpu.specular", mitk::FloatProperty::New(0.40f), renderer, overwrite); node->AddProperty("volumerendering.cpu.specular.power", mitk::FloatProperty::New(16.0f), renderer, overwrite); node->AddProperty("volumerendering.usegpu", mitk::BoolProperty::New(false), renderer, overwrite); node->AddProperty("volumerendering.useray", mitk::BoolProperty::New(false), renderer, overwrite); node->AddProperty("volumerendering.gpu.ambient", mitk::FloatProperty::New(0.25f), renderer, overwrite); node->AddProperty("volumerendering.gpu.diffuse", mitk::FloatProperty::New(0.50f), renderer, overwrite); node->AddProperty("volumerendering.gpu.specular", mitk::FloatProperty::New(0.40f), renderer, overwrite); node->AddProperty("volumerendering.gpu.specular.power", mitk::FloatProperty::New(16.0f), renderer, overwrite); node->AddProperty("binary", mitk::BoolProperty::New(false), renderer, overwrite); mitk::Image::Pointer image = dynamic_cast(node->GetData()); if (image.IsNotNull() && image->IsInitialized()) { if ((overwrite) || (node->GetProperty("levelwindow", renderer) == nullptr)) { mitk::LevelWindowProperty::Pointer levWinProp = mitk::LevelWindowProperty::New(); mitk::LevelWindow levelwindow; levelwindow.SetAuto(image); levWinProp->SetLevelWindow(levelwindow); node->SetProperty("levelwindow", levWinProp, renderer); } if ((overwrite) || (node->GetProperty("TransferFunction", renderer) == nullptr)) { // add a default transfer function mitk::TransferFunction::Pointer tf = mitk::TransferFunction::New(); mitk::TransferFunctionInitializer::Pointer tfInit = mitk::TransferFunctionInitializer::New(tf); tfInit->SetTransferFunctionMode(0); node->SetProperty("TransferFunction", mitk::TransferFunctionProperty::New(tf.GetPointer())); } } Superclass::SetDefaultProperties(node, renderer, overwrite); } vtkImageData* mitk::VolumeMapperVtkSmart3D::GetInputImage() { - mitk::Image *input = const_cast(static_cast(this->GetDataNode()->GetData())); + auto *input = const_cast(static_cast(this->GetDataNode()->GetData())); vtkImageData* img = input->GetVtkImageData(this->GetTimestep()); img->SetSpacing(1,1,1); return img; } void mitk::VolumeMapperVtkSmart3D::createMapper(vtkImageData* imageData) { m_SmartVolumeMapper->SetBlendModeToComposite(); m_SmartVolumeMapper->SetInputData(imageData); } void mitk::VolumeMapperVtkSmart3D::createVolume() { m_Volume->VisibilityOff(); m_Volume->SetMapper(m_SmartVolumeMapper); m_Volume->SetProperty(m_VolumeProperty); } void mitk::VolumeMapperVtkSmart3D::createVolumeProperty() { m_VolumeProperty->ShadeOn(); m_VolumeProperty->SetInterpolationType(VTK_LINEAR_INTERPOLATION); } void mitk::VolumeMapperVtkSmart3D::UpdateTransferFunctions(mitk::BaseRenderer *renderer) { vtkSmartPointer opacityTransferFunction; vtkSmartPointer gradientTransferFunction; vtkSmartPointer colorTransferFunction; bool isBinary = false; this->GetDataNode()->GetBoolProperty("binary", isBinary, renderer); if (isBinary) { colorTransferFunction = vtkSmartPointer::New(); float rgb[3]; if (!GetDataNode()->GetColor(rgb, renderer)) rgb[0] = rgb[1] = rgb[2] = 1; colorTransferFunction->AddRGBPoint(0, rgb[0], rgb[1], rgb[2]); colorTransferFunction->Modified(); opacityTransferFunction = vtkSmartPointer::New(); gradientTransferFunction = vtkSmartPointer::New(); } else { - mitk::TransferFunctionProperty *transferFunctionProp = + auto *transferFunctionProp = dynamic_cast(this->GetDataNode()->GetProperty("TransferFunction", renderer)); if (transferFunctionProp) { opacityTransferFunction = transferFunctionProp->GetValue()->GetScalarOpacityFunction(); gradientTransferFunction = transferFunctionProp->GetValue()->GetGradientOpacityFunction(); colorTransferFunction = transferFunctionProp->GetValue()->GetColorTransferFunction(); } else { opacityTransferFunction = vtkSmartPointer::New(); gradientTransferFunction = vtkSmartPointer::New(); colorTransferFunction = vtkSmartPointer::New(); } } m_VolumeProperty->SetColor(colorTransferFunction); m_VolumeProperty->SetScalarOpacity(opacityTransferFunction); m_VolumeProperty->SetGradientOpacity(gradientTransferFunction); } void mitk::VolumeMapperVtkSmart3D::UpdateRenderMode(mitk::BaseRenderer *renderer) { bool usegpu = false; bool useray = false; bool usemip = false; this->GetDataNode()->GetBoolProperty("volumerendering.usegpu", usegpu); this->GetDataNode()->GetBoolProperty("volumerendering.useray", useray); this->GetDataNode()->GetBoolProperty("volumerendering.usemip", usemip); if (usegpu) m_SmartVolumeMapper->SetRequestedRenderModeToGPU(); else if (useray) m_SmartVolumeMapper->SetRequestedRenderModeToRayCast(); else m_SmartVolumeMapper->SetRequestedRenderModeToDefault(); int blendMode; if (this->GetDataNode()->GetIntProperty("volumerendering.blendmode", blendMode)) m_SmartVolumeMapper->SetBlendMode(blendMode); else if (usemip) m_SmartVolumeMapper->SetBlendMode(vtkSmartVolumeMapper::MAXIMUM_INTENSITY_BLEND); // shading parameter if (m_SmartVolumeMapper->GetRequestedRenderMode() == vtkSmartVolumeMapper::GPURenderMode) { float value = 0; if (this->GetDataNode()->GetFloatProperty("volumerendering.gpu.ambient", value, renderer)) m_VolumeProperty->SetAmbient(value); if (this->GetDataNode()->GetFloatProperty("volumerendering.gpu.diffuse", value, renderer)) m_VolumeProperty->SetDiffuse(value); if (this->GetDataNode()->GetFloatProperty("volumerendering.gpu.specular", value, renderer)) m_VolumeProperty->SetSpecular(value); if (this->GetDataNode()->GetFloatProperty("volumerendering.gpu.specular.power", value, renderer)) m_VolumeProperty->SetSpecularPower(value); } else { float value = 0; if (this->GetDataNode()->GetFloatProperty("volumerendering.cpu.ambient", value, renderer)) m_VolumeProperty->SetAmbient(value); if (this->GetDataNode()->GetFloatProperty("volumerendering.cpu.diffuse", value, renderer)) m_VolumeProperty->SetDiffuse(value); if (this->GetDataNode()->GetFloatProperty("volumerendering.cpu.specular", value, renderer)) m_VolumeProperty->SetSpecular(value); if (this->GetDataNode()->GetFloatProperty("volumerendering.cpu.specular.power", value, renderer)) m_VolumeProperty->SetSpecularPower(value); } } mitk::VolumeMapperVtkSmart3D::VolumeMapperVtkSmart3D() { vtkObjectFactory::RegisterFactory(vtkRenderingOpenGL2ObjectFactory::New()); vtkObjectFactory::RegisterFactory(vtkRenderingVolumeOpenGL2ObjectFactory::New()); m_SmartVolumeMapper = vtkSmartPointer::New(); m_SmartVolumeMapper->SetBlendModeToComposite(); m_VolumeProperty = vtkSmartPointer::New(); m_Volume = vtkSmartPointer::New(); } mitk::VolumeMapperVtkSmart3D::~VolumeMapperVtkSmart3D() { } diff --git a/Modules/Multilabel/autoload/IO/mitkLabelSetImageIO.cpp b/Modules/Multilabel/autoload/IO/mitkLabelSetImageIO.cpp index a925ca69c9..abff08cfc5 100644 --- a/Modules/Multilabel/autoload/IO/mitkLabelSetImageIO.cpp +++ b/Modules/Multilabel/autoload/IO/mitkLabelSetImageIO.cpp @@ -1,456 +1,456 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #ifndef __mitkLabelSetImageWriter__cpp #define __mitkLabelSetImageWriter__cpp #include "mitkLabelSetImageIO.h" #include "mitkBasePropertySerializer.h" #include "mitkIOMimeTypes.h" #include "mitkImageAccessByItk.h" #include "mitkLabelSetIOHelper.h" #include "mitkLabelSetImageConverter.h" #include // itk #include "itkImageFileReader.h" #include "itkImageFileWriter.h" #include "itkMetaDataDictionary.h" #include "itkMetaDataObject.h" #include "itkNrrdImageIO.h" namespace mitk { LabelSetImageIO::LabelSetImageIO() : AbstractFileIO(LabelSetImage::GetStaticNameOfClass(), IOMimeTypes::NRRD_MIMETYPE(), "MITK Multilabel Image") { AbstractFileWriter::SetRanking(10); AbstractFileReader::SetRanking(10); this->RegisterService(); } IFileIO::ConfidenceLevel LabelSetImageIO::GetWriterConfidenceLevel() const { if (AbstractFileIO::GetWriterConfidenceLevel() == Unsupported) return Unsupported; - const LabelSetImage *input = static_cast(this->GetInput()); + const auto *input = static_cast(this->GetInput()); if (input) return Supported; else return Unsupported; } void LabelSetImageIO::Write() { ValidateOutputLocation(); auto input = dynamic_cast(this->GetInput()); mitk::LocaleSwitch localeSwitch("C"); mitk::Image::Pointer inputVector = mitk::ConvertLabelSetImageToImage(input); // image write if (inputVector.IsNull()) { mitkThrow() << "Cannot write non-image data"; } itk::NrrdImageIO::Pointer nrrdImageIo = itk::NrrdImageIO::New(); // Clone the image geometry, because we might have to change it // for writing purposes BaseGeometry::Pointer geometry = inputVector->GetGeometry()->Clone(); // Check if geometry information will be lost if (inputVector->GetDimension() == 2 && !geometry->Is2DConvertable()) { MITK_WARN << "Saving a 2D image with 3D geometry information. Geometry information will be lost! You might " "consider using Convert2Dto3DImageFilter before saving."; // set matrix to identity mitk::AffineTransform3D::Pointer affTrans = mitk::AffineTransform3D::New(); affTrans->SetIdentity(); mitk::Vector3D spacing = geometry->GetSpacing(); mitk::Point3D origin = geometry->GetOrigin(); geometry->SetIndexToWorldTransform(affTrans); geometry->SetSpacing(spacing); geometry->SetOrigin(origin); } LocalFile localFile(this); const std::string path = localFile.GetFileName(); MITK_INFO << "Writing image: " << path << std::endl; try { // Implementation of writer using itkImageIO directly. This skips the use // of templated itkImageFileWriter, which saves the multiplexing on MITK side. const unsigned int dimension = inputVector->GetDimension(); const unsigned int *const dimensions = inputVector->GetDimensions(); const mitk::PixelType pixelType = inputVector->GetPixelType(); const mitk::Vector3D mitkSpacing = geometry->GetSpacing(); const mitk::Point3D mitkOrigin = geometry->GetOrigin(); // Due to templating in itk, we are forced to save a 4D spacing and 4D Origin, // though they are not supported in MITK itk::Vector spacing4D; spacing4D[0] = mitkSpacing[0]; spacing4D[1] = mitkSpacing[1]; spacing4D[2] = mitkSpacing[2]; spacing4D[3] = 1; // There is no support for a 4D spacing. However, we should have a valid value here itk::Vector origin4D; origin4D[0] = mitkOrigin[0]; origin4D[1] = mitkOrigin[1]; origin4D[2] = mitkOrigin[2]; origin4D[3] = 0; // There is no support for a 4D origin. However, we should have a valid value here // Set the necessary information for imageIO nrrdImageIo->SetNumberOfDimensions(dimension); nrrdImageIo->SetPixelType(pixelType.GetPixelType()); nrrdImageIo->SetComponentType(pixelType.GetComponentType() < PixelComponentUserType ? static_cast(pixelType.GetComponentType()) : itk::ImageIOBase::UNKNOWNCOMPONENTTYPE); nrrdImageIo->SetNumberOfComponents(pixelType.GetNumberOfComponents()); itk::ImageIORegion ioRegion(dimension); for (unsigned int i = 0; i < dimension; i++) { nrrdImageIo->SetDimensions(i, dimensions[i]); nrrdImageIo->SetSpacing(i, spacing4D[i]); nrrdImageIo->SetOrigin(i, origin4D[i]); mitk::Vector3D mitkDirection; mitkDirection.SetVnlVector(geometry->GetIndexToWorldTransform()->GetMatrix().GetVnlMatrix().get_column(i)); itk::Vector direction4D; direction4D[0] = mitkDirection[0]; direction4D[1] = mitkDirection[1]; direction4D[2] = mitkDirection[2]; // MITK only supports a 3x3 direction matrix. Due to templating in itk, however, we must // save a 4x4 matrix for 4D images. in this case, add an homogneous component to the matrix. if (i == 3) { direction4D[3] = 1; // homogenous component } else { direction4D[3] = 0; } vnl_vector axisDirection(dimension); for (unsigned int j = 0; j < dimension; j++) { axisDirection[j] = direction4D[j] / spacing4D[i]; } nrrdImageIo->SetDirection(i, axisDirection); ioRegion.SetSize(i, inputVector->GetLargestPossibleRegion().GetSize(i)); ioRegion.SetIndex(i, inputVector->GetLargestPossibleRegion().GetIndex(i)); } // use compression if available nrrdImageIo->UseCompressionOn(); nrrdImageIo->SetIORegion(ioRegion); nrrdImageIo->SetFileName(path); // label set specific meta data char keybuffer[512]; char valbuffer[512]; sprintf(keybuffer, "modality"); sprintf(valbuffer, "org.mitk.image.multilabel"); itk::EncapsulateMetaData( nrrdImageIo->GetMetaDataDictionary(), std::string(keybuffer), std::string(valbuffer)); sprintf(keybuffer, "layers"); sprintf(valbuffer, "%1d", input->GetNumberOfLayers()); itk::EncapsulateMetaData( nrrdImageIo->GetMetaDataDictionary(), std::string(keybuffer), std::string(valbuffer)); for (unsigned int layerIdx = 0; layerIdx < input->GetNumberOfLayers(); layerIdx++) { sprintf(keybuffer, "layer_%03d", layerIdx); // layer idx sprintf(valbuffer, "%1d", input->GetNumberOfLabels(layerIdx)); // number of labels for the layer itk::EncapsulateMetaData( nrrdImageIo->GetMetaDataDictionary(), std::string(keybuffer), std::string(valbuffer)); - mitk::LabelSet::LabelContainerConstIteratorType iter = input->GetLabelSet(layerIdx)->IteratorConstBegin(); + auto iter = input->GetLabelSet(layerIdx)->IteratorConstBegin(); unsigned int count(0); while (iter != input->GetLabelSet(layerIdx)->IteratorConstEnd()) { std::unique_ptr document; document.reset(new TiXmlDocument()); - TiXmlDeclaration *decl = new TiXmlDeclaration("1.0", "", ""); // TODO what to write here? encoding? etc.... + auto *decl = new TiXmlDeclaration("1.0", "", ""); // TODO what to write here? encoding? etc.... document->LinkEndChild(decl); TiXmlElement *labelElem = mitk::LabelSetIOHelper::GetLabelAsTiXmlElement(iter->second); document->LinkEndChild(labelElem); TiXmlPrinter printer; printer.SetIndent(""); printer.SetLineBreak(""); document->Accept(&printer); sprintf(keybuffer, "org.mitk.label_%03u_%05u", layerIdx, count); itk::EncapsulateMetaData( nrrdImageIo->GetMetaDataDictionary(), std::string(keybuffer), printer.Str()); ++iter; ++count; } } // end label set specific meta data ImageReadAccessor imageAccess(inputVector); nrrdImageIo->Write(imageAccess.GetData()); } catch (const std::exception &e) { mitkThrow() << e.what(); } // end image write } IFileIO::ConfidenceLevel LabelSetImageIO::GetReaderConfidenceLevel() const { if (AbstractFileIO::GetReaderConfidenceLevel() == Unsupported) return Unsupported; const std::string fileName = this->GetLocalFileName(); itk::NrrdImageIO::Pointer io = itk::NrrdImageIO::New(); io->SetFileName(fileName); io->ReadImageInformation(); itk::MetaDataDictionary imgMetaDataDictionary = io->GetMetaDataDictionary(); std::string value(""); itk::ExposeMetaData(imgMetaDataDictionary, "modality", value); if (value.compare("org.mitk.image.multilabel") == 0) { return Supported; } else return Unsupported; } std::vector LabelSetImageIO::Read() { mitk::LocaleSwitch localeSwitch("C"); // begin regular image loading, adapted from mitkItkImageIO itk::NrrdImageIO::Pointer nrrdImageIO = itk::NrrdImageIO::New(); Image::Pointer image = Image::New(); const unsigned int MINDIM = 2; const unsigned int MAXDIM = 4; const std::string path = this->GetLocalFileName(); MITK_INFO << "loading " << path << " via itk::ImageIOFactory... " << std::endl; // Check to see if we can read the file given the name or prefix if (path.empty()) { mitkThrow() << "Empty filename in mitk::ItkImageIO "; } // Got to allocate space for the image. Determine the characteristics of // the image. nrrdImageIO->SetFileName(path); nrrdImageIO->ReadImageInformation(); unsigned int ndim = nrrdImageIO->GetNumberOfDimensions(); if (ndim < MINDIM || ndim > MAXDIM) { MITK_WARN << "Sorry, only dimensions 2, 3 and 4 are supported. The given file has " << ndim << " dimensions! Reading as 4D."; ndim = MAXDIM; } itk::ImageIORegion ioRegion(ndim); itk::ImageIORegion::SizeType ioSize = ioRegion.GetSize(); itk::ImageIORegion::IndexType ioStart = ioRegion.GetIndex(); unsigned int dimensions[MAXDIM]; dimensions[0] = 0; dimensions[1] = 0; dimensions[2] = 0; dimensions[3] = 0; ScalarType spacing[MAXDIM]; spacing[0] = 1.0f; spacing[1] = 1.0f; spacing[2] = 1.0f; spacing[3] = 1.0f; Point3D origin; origin.Fill(0); unsigned int i; for (i = 0; i < ndim; ++i) { ioStart[i] = 0; ioSize[i] = nrrdImageIO->GetDimensions(i); if (i < MAXDIM) { dimensions[i] = nrrdImageIO->GetDimensions(i); spacing[i] = nrrdImageIO->GetSpacing(i); if (spacing[i] <= 0) spacing[i] = 1.0f; } if (i < 3) { origin[i] = nrrdImageIO->GetOrigin(i); } } ioRegion.SetSize(ioSize); ioRegion.SetIndex(ioStart); MITK_INFO << "ioRegion: " << ioRegion << std::endl; nrrdImageIO->SetIORegion(ioRegion); void *buffer = new unsigned char[nrrdImageIO->GetImageSizeInBytes()]; nrrdImageIO->Read(buffer); image->Initialize(MakePixelType(nrrdImageIO), ndim, dimensions); image->SetImportChannel(buffer, 0, Image::ManageMemory); // access direction of itk::Image and include spacing mitk::Matrix3D matrix; matrix.SetIdentity(); unsigned int j, itkDimMax3 = (ndim >= 3 ? 3 : ndim); for (i = 0; i < itkDimMax3; ++i) for (j = 0; j < itkDimMax3; ++j) matrix[i][j] = nrrdImageIO->GetDirection(j)[i]; // re-initialize PlaneGeometry with origin and direction PlaneGeometry *planeGeometry = image->GetSlicedGeometry(0)->GetPlaneGeometry(0); planeGeometry->SetOrigin(origin); planeGeometry->GetIndexToWorldTransform()->SetMatrix(matrix); // re-initialize SlicedGeometry3D SlicedGeometry3D *slicedGeometry = image->GetSlicedGeometry(0); slicedGeometry->InitializeEvenlySpaced(planeGeometry, image->GetDimension(2)); slicedGeometry->SetSpacing(spacing); MITK_INFO << slicedGeometry->GetCornerPoint(false, false, false); MITK_INFO << slicedGeometry->GetCornerPoint(true, true, true); // re-initialize TimeGeometry ProportionalTimeGeometry::Pointer timeGeometry = ProportionalTimeGeometry::New(); timeGeometry->Initialize(slicedGeometry, image->GetDimension(3)); image->SetTimeGeometry(timeGeometry); buffer = nullptr; MITK_INFO << "number of image components: " << image->GetPixelType().GetNumberOfComponents() << std::endl; const itk::MetaDataDictionary &dictionary = nrrdImageIO->GetMetaDataDictionary(); - for (itk::MetaDataDictionary::ConstIterator iter = dictionary.Begin(), iterEnd = dictionary.End(); iter != iterEnd; + for (auto iter = dictionary.Begin(), iterEnd = dictionary.End(); iter != iterEnd; ++iter) { std::string key = std::string("meta.") + iter->first; if (iter->second->GetMetaDataObjectTypeInfo() == typeid(std::string)) { std::string value = dynamic_cast *>(iter->second.GetPointer())->GetMetaDataObjectValue(); image->SetProperty(key.c_str(), mitk::StringProperty::New(value)); } } // end regular image loading LabelSetImage::Pointer output = ConvertImageToLabelSetImage(image); // get labels and add them as properties to the image char keybuffer[256]; unsigned int numberOfLayers = GetIntByKey(dictionary, "layers"); std::string _xmlStr; mitk::Label::Pointer label; for (unsigned int layerIdx = 0; layerIdx < numberOfLayers; layerIdx++) { sprintf(keybuffer, "layer_%03d", layerIdx); int numberOfLabels = GetIntByKey(dictionary, keybuffer); mitk::LabelSet::Pointer labelSet = mitk::LabelSet::New(); for (int labelIdx = 0; labelIdx < numberOfLabels; labelIdx++) { TiXmlDocument doc; sprintf(keybuffer, "label_%03d_%05d", layerIdx, labelIdx); _xmlStr = GetStringByKey(dictionary, keybuffer); doc.Parse(_xmlStr.c_str()); TiXmlElement *labelElem = doc.FirstChildElement("Label"); if (labelElem == nullptr) mitkThrow() << "Error parsing NRRD header for mitk::LabelSetImage IO"; label = mitk::LabelSetIOHelper::LoadLabelFromTiXmlDocument(labelElem); if (label->GetValue() == 0) // set exterior label is needed to hold exterior information output->SetExteriorLabel(label); labelSet->AddLabel(label); labelSet->SetLayer(layerIdx); } output->AddLabelSetToLayer(layerIdx, labelSet); } MITK_INFO << "...finished!" << std::endl; std::vector result; result.push_back(output.GetPointer()); return result; } int LabelSetImageIO::GetIntByKey(const itk::MetaDataDictionary &dic, const std::string &str) { std::vector imgMetaKeys = dic.GetKeys(); std::vector::const_iterator itKey = imgMetaKeys.begin(); std::string metaString(""); for (; itKey != imgMetaKeys.end(); itKey++) { itk::ExposeMetaData(dic, *itKey, metaString); if (itKey->find(str.c_str()) != std::string::npos) { return atoi(metaString.c_str()); } } return 0; } std::string LabelSetImageIO::GetStringByKey(const itk::MetaDataDictionary &dic, const std::string &str) { std::vector imgMetaKeys = dic.GetKeys(); std::vector::const_iterator itKey = imgMetaKeys.begin(); std::string metaString(""); for (; itKey != imgMetaKeys.end(); itKey++) { itk::ExposeMetaData(dic, *itKey, metaString); if (itKey->find(str.c_str()) != std::string::npos) { return metaString; } } return metaString; } LabelSetImageIO *LabelSetImageIO::IOClone() const { return new LabelSetImageIO(*this); } } // namespace #endif //__mitkLabelSetImageWriter__cpp diff --git a/Modules/Multilabel/autoload/IO/mitkLabelSetImageSerializer.cpp b/Modules/Multilabel/autoload/IO/mitkLabelSetImageSerializer.cpp index 85c2da5173..39cad5704f 100644 --- a/Modules/Multilabel/autoload/IO/mitkLabelSetImageSerializer.cpp +++ b/Modules/Multilabel/autoload/IO/mitkLabelSetImageSerializer.cpp @@ -1,68 +1,68 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkLabelSetImageSerializer.h" #include "mitkLabelSetImage.h" #include #include MITK_REGISTER_SERIALIZER(LabelSetImageSerializer) mitk::LabelSetImageSerializer::LabelSetImageSerializer() { } mitk::LabelSetImageSerializer::~LabelSetImageSerializer() { } std::string mitk::LabelSetImageSerializer::Serialize() { - const LabelSetImage *image = dynamic_cast(m_Data.GetPointer()); + const auto *image = dynamic_cast(m_Data.GetPointer()); if (image == nullptr) { MITK_ERROR << " Object at " << (const void *)this->m_Data << " is not an mitk::LabelSetImage. Cannot serialize as LabelSetImage."; return ""; } std::string filename(this->GetUniqueFilenameInWorkingDirectory()); filename += "_"; filename += m_FilenameHint; filename += ".nrrd"; std::string fullname(m_WorkingDirectory); fullname += "/"; fullname += itksys::SystemTools::ConvertToOutputPath(filename.c_str()); try { mitk::IOUtil::Save(const_cast(image), fullname); // LabelSetImageWriter::Pointer writer = LabelSetImageWriter::New(); // writer->SetFileName(fullname); // writer->SetInput(const_cast(image)); // writer->Write(); } catch (std::exception &e) { MITK_ERROR << " Error serializing object at " << (const void *)this->m_Data << " to " << fullname << ": " << e.what(); return ""; } return filename; } diff --git a/Modules/Multilabel/mitkLabel.cpp b/Modules/Multilabel/mitkLabel.cpp index 2a55527a8c..fd8624c558 100644 --- a/Modules/Multilabel/mitkLabel.cpp +++ b/Modules/Multilabel/mitkLabel.cpp @@ -1,301 +1,301 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkLabel.h" #include "itkProcessObject.h" #include "tinyxml.h" #include #include #include const mitk::Label::PixelType mitk::Label::MAX_LABEL_VALUE = std::numeric_limits::max(); mitk::Label::Label() : PropertyList() { if (GetProperty("locked") == nullptr) SetLocked(true); if (GetProperty("visible") == nullptr) SetVisible(true); if (GetProperty("opacity") == nullptr) SetOpacity(0.6); if (GetProperty("center.coordinates") == nullptr) { mitk::Point3D pnt; pnt.SetElement(0, 0); pnt.SetElement(1, 0); pnt.SetElement(2, 0); SetCenterOfMassCoordinates(pnt); } if (GetProperty("center.index") == nullptr) { mitk::Point3D pnt; pnt.SetElement(0, 0); pnt.SetElement(1, 0); pnt.SetElement(2, 0); SetCenterOfMassIndex(pnt); } if (GetProperty("color") == nullptr) { mitk::Color col; col.Set(0, 0, 0); SetColor(col); } if (GetProperty("name") == nullptr) SetName("noName!"); if (GetProperty("value") == nullptr) SetValue(0); if (GetProperty("layer") == nullptr) SetLayer(0); } mitk::Label::Label(const Label &other) : PropertyList(other) // copyconstructer of property List handles the coping action { - mitk::PropertyList::PropertyMap *map = const_cast(this->GetMap()); + auto *map = const_cast(this->GetMap()); auto it = map->begin(); auto end = map->end(); for (; it != end; ++it) { itk::SimpleMemberCommand