diff --git a/Modules/AlgorithmsExt/include/itkIntelligentBinaryClosingFilter.h b/Modules/AlgorithmsExt/include/itkIntelligentBinaryClosingFilter.h index 846211b1f2..20423e6434 100644 --- a/Modules/AlgorithmsExt/include/itkIntelligentBinaryClosingFilter.h +++ b/Modules/AlgorithmsExt/include/itkIntelligentBinaryClosingFilter.h @@ -1,139 +1,137 @@ /*=================================================================== 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 __itkIntelligentBinaryClosingFilter_h #define __itkIntelligentBinaryClosingFilter_h #if defined(_MSC_VER) #pragma warning(disable : 4786) #endif #include #include #include #include #include #include #include #include #include #include namespace itk { /** \class itkIntelligentBinaryClosingFilter * WARNING: This filtezr needs at least ITK version 3.2 * or above to run and compile!!! * */ template class ITK_EXPORT IntelligentBinaryClosingFilter : public ImageToImageFilter { public: /** Standard "Self" typedef. */ typedef IntelligentBinaryClosingFilter Self; /** The type of input image. */ typedef TInputImage InputImageType; /** Dimension of the input and output images. */ itkStaticConstMacro(ImageDimension, unsigned int, TInputImage::ImageDimension); /** The type of output image. */ typedef TOutputImage OutputImageType; /** Standard super class typedef support. */ typedef ImageToImageFilter Superclass; /** Smart pointer typedef support */ typedef SmartPointer Pointer; /** Run-time type information (and related methods) */ itkTypeMacro(IntelligentBinaryClosingFilter, ImageToImageFilter); /** Method for creation through the object factory. */ itkFactorylessNewMacro(Self) itkCloneMacro(Self) /** Standard process object method. This filter is not multithreaded. */ void GenerateData() override; /** Overloaded to link the input to this filter with the input of the mini-pipeline */ void SetInput(const InputImageType *input) override { // processObject is not const-correct so a const_cast is needed here this->ProcessObject::SetNthInput(0, const_cast(input)); m_DilateImageFilter->SetInput(input); } using Superclass::SetInput; void SetInput(unsigned int i, const TInputImage *image) override { if (i != 0) { itkExceptionMacro(<< "Filter has only one input."); } else { this->SetInput(image); } } itkGetMacro(ClosingRadius, float); itkSetMacro(ClosingRadius, float); itkGetMacro(SurfaceRatio, float); itkSetMacro(SurfaceRatio, float); protected: IntelligentBinaryClosingFilter(); ~IntelligentBinaryClosingFilter() override {} - IntelligentBinaryClosingFilter(const Self &) {} - void operator=(const Self &) {} void PrintSelf(std::ostream &os, Indent indent) const override; private: typedef typename InputImageType::PixelType InputPixelType; typedef BinaryBallStructuringElement StructuringElementType; typedef BinaryErodeImageFilter BinaryErodeImageFilterType; typedef BinaryDilateImageFilter BinaryDilateImageFilterType; typedef SubtractImageFilter SubtractImageFilterType; typedef ConnectedComponentImageFilter ConnectedComponentImageFilterType; typedef RelabelComponentImageFilter RelabelComponentImageFilterType; typedef GrayscaleDilateImageFilter DilateComponentImageFilterType; typedef ImageRegionIterator InputIteratorType; typedef ImageRegionConstIterator ConstInputIteratorType; typedef ImageRegionIterator OutputIteratorType; typename BinaryErodeImageFilterType::Pointer m_ErodeImageFilter; typename BinaryDilateImageFilterType::Pointer m_DilateImageFilter; typename SubtractImageFilterType::Pointer m_SubtractImageFilter; typename ConnectedComponentImageFilterType::Pointer m_ConnectedComponentImageFilter; typename RelabelComponentImageFilterType::Pointer m_RelabelComponentImageFilter; typename DilateComponentImageFilterType::Pointer m_BorderDetectionDilateFilter; // typename BinaryDilateImageFilterType::Pointer m_BorderAdjacencyDilateFilter; float m_ClosingRadius; float m_SurfaceRatio; }; } // end namespace itk #ifndef ITK_MANUAL_INSTANTIATION #include "itkIntelligentBinaryClosingFilter.txx" #endif #endif // IntelligentBinaryClosingFilter_h diff --git a/Modules/AlgorithmsExt/include/itkIntelligentBinaryClosingFilter.txx b/Modules/AlgorithmsExt/include/itkIntelligentBinaryClosingFilter.txx index 1e51338c9b..1b8aec5ef5 100644 --- a/Modules/AlgorithmsExt/include/itkIntelligentBinaryClosingFilter.txx +++ b/Modules/AlgorithmsExt/include/itkIntelligentBinaryClosingFilter.txx @@ -1,160 +1,158 @@ /*=================================================================== 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 _itkIntelligentBinaryClosingFilter_txx #define _itkIntelligentBinaryClosingFilter_txx #include "itkIntelligentBinaryClosingFilter.h" #include namespace itk { template - IntelligentBinaryClosingFilter::IntelligentBinaryClosingFilter() + IntelligentBinaryClosingFilter::IntelligentBinaryClosingFilter(): m_ClosingRadius(4.0), m_SurfaceRatio(70) { m_ErodeImageFilter = BinaryErodeImageFilterType::New(); m_DilateImageFilter = BinaryDilateImageFilterType::New(); m_SubtractImageFilter = SubtractImageFilterType::New(); m_ConnectedComponentImageFilter = ConnectedComponentImageFilterType::New(); m_RelabelComponentImageFilter = RelabelComponentImageFilterType::New(); m_RelabelComponentImageFilter->SetInPlace(true); m_BorderDetectionDilateFilter = DilateComponentImageFilterType::New(); - m_ClosingRadius = 4.0; - m_SurfaceRatio = 70; } template void IntelligentBinaryClosingFilter::GenerateData() { const InputImageType *input = this->GetInput(); // Allocate the output image. typename OutputImageType::Pointer output = this->GetOutput(); output->SetRequestedRegion(input->GetRequestedRegion()); output->SetBufferedRegion(input->GetBufferedRegion()); output->SetLargestPossibleRegion(input->GetLargestPossibleRegion()); output->Allocate(); // set up structuring element for closing StructuringElementType seClosing; itk::SizeValueType radius[ImageDimension]; const typename InputImageType::SpacingType spacing = input->GetSpacing(); for (unsigned int d = 0; d < ImageDimension; d++) { // closing works in voxel coordinates, so use spacing (and add 0.5 for correct rounding - cast just truncates) radius[d] = (unsigned long)(m_ClosingRadius / spacing[d] + 0.5); } MITK_INFO << " Closing kernel size = [" << radius[0] << ", " << radius[1] << ", " << radius[2] << "]" << std::endl; seClosing.SetRadius(radius); seClosing.CreateStructuringElement(); // closing m_DilateImageFilter->SetInput(this->GetInput()); m_DilateImageFilter->SetKernel(seClosing); m_DilateImageFilter->SetDilateValue(1); m_ErodeImageFilter->SetInput(m_DilateImageFilter->GetOutput()); m_ErodeImageFilter->SetKernel(seClosing); m_ErodeImageFilter->SetErodeValue(1); // subtraction m_SubtractImageFilter->SetInput1(m_ErodeImageFilter->GetOutput()); m_SubtractImageFilter->SetInput2(this->GetInput()); // connected components m_ConnectedComponentImageFilter->SetInput(m_SubtractImageFilter->GetOutput()); m_RelabelComponentImageFilter->SetInput(m_ConnectedComponentImageFilter->GetOutput()); m_RelabelComponentImageFilter->Update(); // set up structuring element for border voxel detection StructuringElementType seBorder; for (auto &radiu : radius) { radiu = 1; } seBorder.SetRadius(radius); seBorder.CreateStructuringElement(); // dilate all components to detect border voxels m_BorderDetectionDilateFilter->SetInput(m_RelabelComponentImageFilter->GetOutput()); m_BorderDetectionDilateFilter->SetKernel(seBorder); m_BorderDetectionDilateFilter->Update(); // count volumes and border voxels for all components OutputIteratorType itComp(m_RelabelComponentImageFilter->GetOutput(), m_RelabelComponentImageFilter->GetOutput()->GetLargestPossibleRegion()); OutputIteratorType itBorder(m_BorderDetectionDilateFilter->GetOutput(), m_BorderDetectionDilateFilter->GetOutput()->GetLargestPossibleRegion()); ConstInputIteratorType itIn(input, input->GetLargestPossibleRegion()); std::vector volume(m_RelabelComponentImageFilter->GetNumberOfObjects() + 1, 0); std::vector border(m_RelabelComponentImageFilter->GetNumberOfObjects() + 1, 0); std::vector adjacent(m_RelabelComponentImageFilter->GetNumberOfObjects() + 1, 0); typename OutputImageType::ValueType borderId, compId; for (itComp.GoToBegin(), itBorder.GoToBegin(), itIn.GoToBegin(); !itComp.IsAtEnd(); ++itComp, ++itBorder, ++itIn) { borderId = itBorder.Get(); if (borderId != 0) { compId = itComp.Get(); if (compId != 0) { volume[compId]++; } else { // this is border country border[borderId]++; if (itIn.Get() != 0) adjacent[borderId]++; } } } // calculate ratios std::vector ratio(m_RelabelComponentImageFilter->GetNumberOfObjects() + 1, 0); MITK_INFO << " " << m_RelabelComponentImageFilter->GetNumberOfObjects() << " components found" << std::endl; for (unsigned int i = 0; i < ratio.size(); i++) { if (border[i] != 0) ratio[i] = 100.0 * (float)(adjacent[i]) / (float)(border[i]); } // fill output OutputIteratorType itOut(output, output->GetLargestPossibleRegion()); for (itOut.GoToBegin(), itIn.GoToBegin(), itComp.GoToBegin(); !itOut.IsAtEnd(); ++itOut, ++itIn, ++itComp) { if (itIn.Get() != 0) { itOut.Set(1); } else { compId = itComp.Get(); if (ratio[compId] > m_SurfaceRatio) itOut.Set(1); else itOut.Set(0); } } } template void IntelligentBinaryClosingFilter::PrintSelf(std::ostream &os, Indent indent) const { Superclass::PrintSelf(os, indent); } } // end namespace itk #endif diff --git a/Modules/BoundingShape/src/DataManagement/mitkBoundingShapeUtil.cpp b/Modules/BoundingShape/src/DataManagement/mitkBoundingShapeUtil.cpp index c52aed2c90..e5aafa8cfd 100644 --- a/Modules/BoundingShape/src/DataManagement/mitkBoundingShapeUtil.cpp +++ b/Modules/BoundingShape/src/DataManagement/mitkBoundingShapeUtil.cpp @@ -1,217 +1,217 @@ /*=================================================================== 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 "mitkBoundingShapeUtil.h" #include "mitkGeometry3D.h" #include "vtkDoubleArray.h" #include "vtkMath.h" #include #include -mitk::Handle::Handle() : m_IsActive(false), m_FaceIndices(4) +mitk::Handle::Handle() : m_IsActive(false), m_FaceIndices(4), m_Index(0) { m_Position.Fill(0.0); } mitk::Handle::Handle(mitk::Point3D pos, int index, std::vector faceIndices, bool active) : m_IsActive(active), m_Position(pos), m_FaceIndices(faceIndices), m_Index(index) { } mitk::Handle::~Handle() { } bool mitk::Handle::IsActive() { return m_IsActive; } bool mitk::Handle::IsNotActive() { return !m_IsActive; }; void mitk::Handle::SetActive(bool status) { m_IsActive = status; }; void mitk::Handle::SetPosition(mitk::Point3D pos) { m_Position = pos; }; mitk::Point3D mitk::Handle::GetPosition() { return m_Position; }; void mitk::Handle::SetIndex(int index) { m_Index = index; }; int mitk::Handle::GetIndex() { return m_Index; }; std::vector mitk::Handle::GetFaceIndices() { return m_FaceIndices; }; mitk::Point3D mitk::CalcAvgPoint(mitk::Point3D a, mitk::Point3D b) { mitk::Point3D c; c[0] = (a[0] + b[0]) / 2.0; c[1] = (a[1] + b[1]) / 2.0; c[2] = (a[2] + b[2]) / 2.0; return c; } std::vector mitk::GetCornerPoints(mitk::BaseGeometry::Pointer geometry, bool visualizationOffset) { if (geometry == nullptr) mitkThrow() << "Geometry is not valid."; mitk::BoundingBox::ConstPointer boundingBox = geometry->GetBoundingBox(); mitk::Point3D BBmin = boundingBox->GetMinimum(); mitk::Point3D BBmax = boundingBox->GetMaximum(); // use 0.5 offset because the vtkCubeSource is not center pixel based (only for visualization purpose) if (visualizationOffset) { BBmin -= 0.5; BBmax -= 0.5; } mitk::Point3D p0; p0[0] = BBmin[0]; p0[1] = BBmin[1]; p0[2] = BBmin[2]; // bottom - left - back corner mitk::Point3D p1; p1[0] = BBmin[0]; p1[1] = BBmin[1]; p1[2] = BBmax[2]; // top - left - back corner mitk::Point3D p2; p2[0] = BBmin[0]; p2[1] = BBmax[1]; p2[2] = BBmin[2]; // bottom - left - front corner mitk::Point3D p3; p3[0] = BBmin[0]; p3[1] = BBmax[1]; p3[2] = BBmax[2]; // top - left - front corner mitk::Point3D p4; p4[0] = BBmax[0]; p4[1] = BBmin[1]; p4[2] = BBmin[2]; // bottom - right - back corner mitk::Point3D p5; p5[0] = BBmax[0]; p5[1] = BBmin[1]; p5[2] = BBmax[2]; // top - right - back corner mitk::Point3D p6; p6[0] = BBmax[0]; p6[1] = BBmax[1]; p6[2] = BBmin[2]; // bottom - right - front corner mitk::Point3D p7; p7[0] = BBmax[0]; p7[1] = BBmax[1]; p7[2] = BBmax[2]; // top - right - front corner std::vector cornerPoints; cornerPoints.push_back(geometry->GetIndexToWorldTransform()->TransformPoint(p0)); cornerPoints.push_back(geometry->GetIndexToWorldTransform()->TransformPoint(p1)); cornerPoints.push_back(geometry->GetIndexToWorldTransform()->TransformPoint(p2)); cornerPoints.push_back(geometry->GetIndexToWorldTransform()->TransformPoint(p3)); cornerPoints.push_back(geometry->GetIndexToWorldTransform()->TransformPoint(p4)); cornerPoints.push_back(geometry->GetIndexToWorldTransform()->TransformPoint(p5)); cornerPoints.push_back(geometry->GetIndexToWorldTransform()->TransformPoint(p6)); cornerPoints.push_back(geometry->GetIndexToWorldTransform()->TransformPoint(p7)); return cornerPoints; } std::vector mitk::GetHandleIndices(int index) { std::vector faceIndices; faceIndices.resize(4); // +------+ // / /| // +------+ | // | | + // | |/ // +------+ switch (index) { case 0: { faceIndices[0] = 0; faceIndices[1] = 1; faceIndices[2] = 2; faceIndices[3] = 3; } break; case 1: { faceIndices[0] = 4; faceIndices[1] = 5; faceIndices[2] = 6; faceIndices[3] = 7; } break; case 3: { faceIndices[0] = 0; faceIndices[1] = 2; faceIndices[2] = 4; faceIndices[3] = 6; } break; case 2: { faceIndices[0] = 1; faceIndices[1] = 3; faceIndices[2] = 5; faceIndices[3] = 7; } break; case 4: { faceIndices[0] = 0; faceIndices[1] = 1; faceIndices[2] = 4; faceIndices[3] = 5; } break; case 5: { faceIndices[0] = 2; faceIndices[1] = 3; faceIndices[2] = 6; faceIndices[3] = 7; } break; default: { faceIndices[0] = 0; faceIndices[1] = 0; faceIndices[2] = 0; faceIndices[3] = 0; } break; } return faceIndices; } \ No newline at end of file diff --git a/Modules/DicomRT/src/mitkDoseImageVtkMapper2D.cpp b/Modules/DicomRT/src/mitkDoseImageVtkMapper2D.cpp index f6e435fa77..4e48d9095f 100644 --- a/Modules/DicomRT/src/mitkDoseImageVtkMapper2D.cpp +++ b/Modules/DicomRT/src/mitkDoseImageVtkMapper2D.cpp @@ -1,1163 +1,1167 @@ /*=================================================================== 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 "mitkImageStatisticsHolder.h" #include "mitkPlaneClipping.h" #include "mitkPropertyNameHelper.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include // MITK Rendering #include "mitkDoseImageVtkMapper2D.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 #include // ITK #include mitk::DoseImageVtkMapper2D::DoseImageVtkMapper2D() { } mitk::DoseImageVtkMapper2D::~DoseImageVtkMapper2D() { // 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::DoseImageVtkMapper2D::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::DoseImageVtkMapper2D::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::DoseImageVtkMapper2D::GetInput(void) { return static_cast(GetDataNode()->GetData()); } vtkProp *mitk::DoseImageVtkMapper2D::GetVtkProp(mitk::BaseRenderer *renderer) { // return the actor corresponding to the renderer return m_LSH.GetLocalStorage(renderer)->m_Actors; } void mitk::DoseImageVtkMapper2D::GenerateDataForRenderer(mitk::BaseRenderer *renderer) { LocalStorage *localStorage = m_LSH.GetLocalStorage(renderer); mitk::Image *input = const_cast(this->GetInput()); mitk::DataNode *datanode = this->GetDataNode(); if (input == nullptr || input->IsInitialized() == false) { return; } // check if there is a valid worldGeometry const PlaneGeometry *worldGeometry = renderer->GetCurrentWorldPlaneGeometry(); if ((worldGeometry == nullptr) || (!worldGeometry->IsValid()) || (!worldGeometry->HasReferenceGeometry())) { return; } input->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, input->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(input); 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( input->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 ((input->GetDimension() >= 3) && (input->GetDimension(2) > 1)) { VtkResliceInterpolationProperty *resliceInterpolationProperty; datanode->GetProperty(resliceInterpolationProperty, "reslice interpolation"); 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 (input->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") && resliceMethodEnumProperty) thickSlicesMode = resliceMethodEnumProperty->GetValueAsId(); IntProperty *intProperty = nullptr; if (dn->GetProperty(intProperty, "reslice.thickslices.num") && intProperty) { thickSlicesNum = intProperty->GetValue(); if (thickSlicesNum < 1) thickSlicesNum = 1; if (thickSlicesNum > 10) thickSlicesNum = 10; } } else { MITK_WARN << "no associated widget plane data tree node found"; } } const PlaneGeometry *planeGeometry = dynamic_cast(worldGeometry); if (thickSlicesMode > 0) { double dataZSpacing = 1.0; Vector3D normInIndex, normal; if (planeGeometry != nullptr) { normal = planeGeometry->GetNormal(); } else { const mitk::AbstractTransformGeometry *abstractGeometry = dynamic_cast(worldGeometry); if (abstractGeometry != nullptr) normal = abstractGeometry->GetPlane()->GetNormal(); else return; // no fitting geometry set } normal.Normalize(); input->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 (int i = 0; i < 6; ++i) { sliceBounds[i] = 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 (int i = 0; i < 6; ++i) { textureClippingBounds[i] = 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(input->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 showIsoLines property bool showIsoLines = false; datanode->GetBoolProperty("dose.showIsoLines", showIsoLines, renderer); if (showIsoLines) // contour rendering { // generate contours/outlines localStorage->m_OutlinePolyData = CreateOutlinePolyData(renderer); 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 { localStorage->m_ReslicedImage = nullptr; localStorage->m_Mapper->SetInputData(localStorage->m_EmptyPolyData); return; } this->ApplyOpacity(renderer); this->ApplyRenderingMode(renderer); // do not use a VTK lookup table (we do that ourselves in m_LevelWindowFilter) localStorage->m_Texture->SetColorModeToDirectScalars(); 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)); if (showIsoLines) // 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::DoseImageVtkMapper2D::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::DoseImageVtkMapper2D::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; GetDataNode()->GetBoolProperty("binaryimage.ishovering", hover, renderer); GetDataNode()->GetBoolProperty("selected", selected, renderer); if (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 (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 (!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::DoseImageVtkMapper2D::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::DoseImageVtkMapper2D::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::DoseImageVtkMapper2D::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::DoseImageVtkMapper2D::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()); } void mitk::DoseImageVtkMapper2D::Update(mitk::BaseRenderer *renderer) { bool visible = true; GetDataNode()->GetVisibility(visible, renderer, "visible"); if (!visible) { return; } mitk::Image *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::DoseImageVtkMapper2D::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)); // default value 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); } // Otherwise do nothing - the default grayscale look-up table has already been set } bool isBinaryImage(false); if (!node->GetBoolProperty("binary", isBinaryImage)) { // 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); } // 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); std::string className = image->GetNameOfClass(); if (className != "TensorImage" && className != "OdfImage" && className != "ShImage") { PixelType pixelType = image->GetPixelType(); size_t numComponents = pixelType.GetNumberOfComponents(); if ((pixelType.GetPixelTypeAsString() == "vector" && numComponents > 1) || numComponents == 2 || numComponents > 4) node->AddProperty("Image.Displayed Component", 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::DoseImageVtkMapper2D::LocalStorage *mitk::DoseImageVtkMapper2D::GetLocalStorage(mitk::BaseRenderer *renderer) { return m_LSH.GetLocalStorage(renderer); } vtkSmartPointer mitk::DoseImageVtkMapper2D::CreateOutlinePolyData(mitk::BaseRenderer *renderer) { vtkSmartPointer points = vtkSmartPointer::New(); // the points to draw vtkSmartPointer lines = vtkSmartPointer::New(); // the lines to connect the points vtkSmartPointer colors = vtkSmartPointer::New(); colors->SetNumberOfComponents(3); colors->SetName("Colors"); float pref; this->GetDataNode()->GetFloatProperty(mitk::RTConstants::REFERENCE_DOSE_PROPERTY_NAME.c_str(), pref); mitk::IsoDoseLevelSetProperty::Pointer propIsoSet = dynamic_cast( GetDataNode()->GetProperty(mitk::RTConstants::DOSE_ISO_LEVELS_PROPERTY_NAME.c_str())); mitk::IsoDoseLevelSet::Pointer isoDoseLevelSet = propIsoSet->GetValue(); for (mitk::IsoDoseLevelSet::ConstIterator doseIT = isoDoseLevelSet->Begin(); doseIT != isoDoseLevelSet->End(); ++doseIT) { if (doseIT->GetVisibleIsoLine()) { this->CreateLevelOutline(renderer, &(doseIT.Value()), pref, points, lines, colors); } // end of if visible dose value } // end of loop over all does values mitk::IsoDoseLevelVectorProperty::Pointer propfreeIsoVec = dynamic_cast( GetDataNode()->GetProperty(mitk::RTConstants::DOSE_FREE_ISO_VALUES_PROPERTY_NAME.c_str())); mitk::IsoDoseLevelVector::Pointer frereIsoDoseLevelVec = propfreeIsoVec->GetValue(); for (mitk::IsoDoseLevelVector::ConstIterator freeDoseIT = frereIsoDoseLevelVec->Begin(); freeDoseIT != frereIsoDoseLevelVec->End(); ++freeDoseIT) { if (freeDoseIT->Value()->GetVisibleIsoLine()) { this->CreateLevelOutline(renderer, freeDoseIT->Value(), pref, points, lines, colors); } // end of if visible dose value } // end of loop over all does values // 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); polyData->GetCellData()->SetScalars(colors); return polyData; } void mitk::DoseImageVtkMapper2D::CreateLevelOutline(mitk::BaseRenderer *renderer, const mitk::IsoDoseLevel *level, float pref, vtkSmartPointer points, vtkSmartPointer lines, vtkSmartPointer colors) { 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? // get the depth for each contour float depth = CalculateLayerDepth(renderer); double doseValue = level->GetDoseValue() * pref; mitk::IsoDoseLevel::ColorType isoColor = level->GetColor(); unsigned char colorLine[3] = {static_cast(isoColor.GetRed() * 255), static_cast(isoColor.GetGreen() * 255), static_cast(isoColor.GetBlue() * 255)}; int x = xMin; // pixel index x int y = yMin; // pixel index y float *currentPixel; // We take the pointer to the first pixel of the image currentPixel = static_cast(localStorage->m_ReslicedImage->GetScalarPointer()); + if (!currentPixel){ + mitkThrow() << "currentPixel invalid"; + } + while (y <= yMax) { // if the current pixel value is set to something if ((currentPixel) && (*currentPixel >= doseValue)) { // 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) < doseValue) { // 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); colors->InsertNextTypedTuple(colorLine); } // if vvvvv not the last line vvvvv if (y < yMax && *(currentPixel + line) < doseValue) { // 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); colors->InsertNextTypedTuple(colorLine); } // if vvvvv not the first pixel vvvvv if ((x > xMin || y > yMin) && *(currentPixel - 1) < doseValue) { // 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); colors->InsertNextTypedTuple(colorLine); } // if vvvvv not the last pixel vvvvv if ((y < yMax || (x < xMax)) && *(currentPixel + 1) < doseValue) { // 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); colors->InsertNextTypedTuple(colorLine); } /* 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); colors->InsertNextTypedTuple(colorLine); } // 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); colors->InsertNextTypedTuple(colorLine); } // 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); colors->InsertNextTypedTuple(colorLine); } // 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); colors->InsertNextTypedTuple(colorLine); } } // 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 } void mitk::DoseImageVtkMapper2D::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)); secondaryActor->SetUserTransform(trans); secondaryActor->SetPosition(-0.5 * localStorage->m_mmPerPixel[0], -0.5 * localStorage->m_mmPerPixel[1], 0.0); } } bool mitk::DoseImageVtkMapper2D::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::DoseImageVtkMapper2D::LocalStorage::~LocalStorage() { } mitk::DoseImageVtkMapper2D::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/GraphAlgorithms/itkShortestPathCostFunctionLiveWire.h b/Modules/GraphAlgorithms/itkShortestPathCostFunctionLiveWire.h index 723c63d641..984ec5a818 100644 --- a/Modules/GraphAlgorithms/itkShortestPathCostFunctionLiveWire.h +++ b/Modules/GraphAlgorithms/itkShortestPathCostFunctionLiveWire.h @@ -1,174 +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. ===================================================================*/ #ifndef __itkShortestPathCostFunctionLiveWire_h #define __itkShortestPathCostFunctionLiveWire_h #include "itkShortestPathCostFunction.h" #include "itkImageRegionConstIterator.h" namespace itk { /** \brief Cost function for LiveWire purposes. Specific features are considered to calculate cummulative costs of a link between two pixels. These are: - Gradient Magnitude - Gradient Direction - Laplacian Zero Crossing By default the Gradient Magnitude is mapped linearly to cost values between 0 (good) and 1 (bad). Via SetDynamicCostMap( std::map< int, int > &costMap) a cost map can be set to dynamically map Gradient Magnitude (non linear). Thus, lower values can be considered with lower costs than higher values of gradient magnitudes. To compute the costs of the gradient magnitude dynamically an iverted map of the histogram of gradient magnitude image is used. */ template class ITK_EXPORT ShortestPathCostFunctionLiveWire : public ShortestPathCostFunction { public: /** Standard class typedefs. */ typedef ShortestPathCostFunctionLiveWire Self; typedef ShortestPathCostFunction Superclass; typedef SmartPointer Pointer; typedef SmartPointer ConstPointer; typedef itk::ImageRegionConstIterator ConstIteratorType; /** Method for creation through the object factory. */ itkFactorylessNewMacro(Self) itkCloneMacro(Self) /** Run-time type information (and related methods). */ itkTypeMacro(ShortestPathCostFunctionLiveWire, ShortestPathCostFunction); typedef itk::Image UnsignedCharImageType; typedef itk::Image FloatImageType; typedef float ComponentType; typedef itk::CovariantVector OutputPixelType; typedef itk::Image VectorOutputImageType; typedef typename TInputImageType::IndexType IndexType; typedef TInputImageType ImageType; typedef itk::ImageRegion<2> RegionType; /** \brief calculates the costs for going from p1 to p2*/ double GetCost(IndexType p1, IndexType p2) override; /** \brief returns the minimal costs possible (needed for A*)*/ double GetMinCost() override; /** \brief Initialize the metric*/ void Initialize() override; /** \brief Add void pixel in cost map*/ virtual void AddRepulsivePoint(const IndexType &index); /** \brief Remove void pixel in cost map*/ virtual void RemoveRepulsivePoint(const IndexType &index); /** \brief Clear repulsive points in cost function*/ virtual void ClearRepulsivePoints(); itkSetMacro(RequestedRegion, RegionType); itkGetMacro(RequestedRegion, RegionType); - // Set/Get function for sigma parameter - itkSetMacro(UseApproximateGradient, bool); - itkGetMacro(UseApproximateGradient, bool); - void SetImage(const TInputImageType *_arg) override; void SetDynamicCostMap(std::map &costMap) { this->m_CostMap = costMap; this->m_UseCostMap = true; this->m_MaxMapCosts = -1; this->Modified(); } void SetUseCostMap(bool useCostMap) { this->m_UseCostMap = useCostMap; } /** \brief Set the maximum of the dynamic cost map to save computation time. */ void SetCostMapMaximum(double max) { this->m_MaxMapCosts = max; } enum Constants { MAPSCALEFACTOR = 10 }; /** \brief Returns the y value of gaussian with given offset and amplitude gaussian approximation f(x) = v(bin) * e^ ( -1/2 * (|x-k(bin)| / sigma)^2 ) \param x \param xOfGaussian - offset \param yOfGaussian - amplitude */ static double Gaussian(double x, double xOfGaussian, double yOfGaussian); const UnsignedCharImageType *GetMaskImage() { return this->m_MaskImage.GetPointer(); }; const FloatImageType *GetGradientMagnitudeImage() { return this->m_GradientMagnitudeImage.GetPointer(); }; const FloatImageType *GetEdgeImage() { return this->m_EdgeImage.GetPointer(); }; const VectorOutputImageType *GetGradientImage() { return this->m_GradientImage.GetPointer(); }; protected: ShortestPathCostFunctionLiveWire(); ~ShortestPathCostFunctionLiveWire() override{}; FloatImageType::Pointer m_GradientMagnitudeImage; FloatImageType::Pointer m_EdgeImage; UnsignedCharImageType::Pointer m_MaskImage; VectorOutputImageType::Pointer m_GradientImage; - double minCosts; + double m_MinCosts; bool m_UseRepulsivePoints; typename Superclass::PixelType val; typename Superclass::PixelType startValue; typename Superclass::PixelType endValue; double m_GradientMax; RegionType m_RequestedRegion; - bool m_UseApproximateGradient; - bool m_Initialized; std::map m_CostMap; bool m_UseCostMap; double m_MaxMapCosts; private: double SigmoidFunction(double I, double max, double min, double alpha, double beta); }; } // end namespace itk #ifndef ITK_MANUAL_INSTANTIATION #include "itkShortestPathCostFunctionLiveWire.txx" #endif #endif /* __itkShortestPathCostFunctionLiveWire_h */ diff --git a/Modules/GraphAlgorithms/itkShortestPathCostFunctionLiveWire.txx b/Modules/GraphAlgorithms/itkShortestPathCostFunctionLiveWire.txx index 9de02b41ec..95c5939050 100644 --- a/Modules/GraphAlgorithms/itkShortestPathCostFunctionLiveWire.txx +++ b/Modules/GraphAlgorithms/itkShortestPathCostFunctionLiveWire.txx @@ -1,408 +1,403 @@ /*=================================================================== 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 __itkShortestPathCostFunctionLiveWire_txx #define __itkShortestPathCostFunctionLiveWire_txx #include "itkShortestPathCostFunctionLiveWire.h" #include #include #include #include #include #include #include #include namespace itk { // Constructor template - ShortestPathCostFunctionLiveWire::ShortestPathCostFunctionLiveWire() + ShortestPathCostFunctionLiveWire::ShortestPathCostFunctionLiveWire(): m_MinCosts(0.0), m_UseRepulsivePoints(false), m_GradientMax(0.0), m_Initialized(false), m_UseCostMap(false), m_MaxMapCosts(-1.0) { - m_UseRepulsivePoints = false; - m_GradientMax = 0.0; - m_Initialized = false; - m_UseCostMap = false; - m_MaxMapCosts = -1.0; } template void ShortestPathCostFunctionLiveWire::AddRepulsivePoint(const IndexType &index) { this->m_MaskImage->SetPixel(index, 255); m_UseRepulsivePoints = true; } template void ShortestPathCostFunctionLiveWire::RemoveRepulsivePoint(const IndexType &index) { this->m_MaskImage->SetPixel(index, 0); } template void ShortestPathCostFunctionLiveWire::SetImage(const TInputImageType *_arg) { if (this->m_Image != _arg) { this->m_Image = _arg; // initialize mask image this->m_MaskImage = UnsignedCharImageType::New(); this->m_MaskImage->SetRegions(this->m_Image->GetLargestPossibleRegion()); this->m_MaskImage->SetOrigin(this->m_Image->GetOrigin()); this->m_MaskImage->SetSpacing(this->m_Image->GetSpacing()); this->m_MaskImage->SetDirection(this->m_Image->GetDirection()); this->m_MaskImage->Allocate(); this->m_MaskImage->FillBuffer(0); this->Modified(); this->m_Initialized = false; } } template void ShortestPathCostFunctionLiveWire::ClearRepulsivePoints() { m_UseRepulsivePoints = false; this->m_MaskImage->FillBuffer(0); } template double ShortestPathCostFunctionLiveWire::GetCost(IndexType p1, IndexType p2) { // local component costs // weights double w1; double w2; double w3; double costs = 0.0; // if we are on the mask, return asap if (m_UseRepulsivePoints) { if ((this->m_MaskImage->GetPixel(p1) != 0) || (this->m_MaskImage->GetPixel(p2) != 0)) return 1000; } double gradientX, gradientY; gradientX = gradientY = 0.0; double gradientCost; double gradientMagnitude; // Gradient Magnitude costs gradientMagnitude = this->m_GradientMagnitudeImage->GetPixel(p2); gradientX = m_GradientImage->GetPixel(p2)[0]; gradientY = m_GradientImage->GetPixel(p2)[1]; if (m_UseCostMap && !m_CostMap.empty()) { std::map::iterator end = m_CostMap.end(); std::map::iterator last = --(m_CostMap.end()); // current position std::map::iterator x; // std::map< int, int >::key_type keyOfX = static_cast::key_type>(gradientMagnitude * 1000); int keyOfX = static_cast(gradientMagnitude /* ShortestPathCostFunctionLiveWire::MAPSCALEFACTOR*/); x = m_CostMap.find(keyOfX); std::map::iterator left2; std::map::iterator left1; std::map::iterator right1; std::map::iterator right2; if (x == end) { // x can also be == end if the key is not in the map but between two other keys // search next key within map from x upwards right1 = m_CostMap.lower_bound(keyOfX); } else { right1 = x; } if (right1 == end || right1 == last) { right2 = end; } else //( right1 != (end-1) ) { auto temp = right1; right2 = ++right1; // rght1 + 1 right1 = temp; } if (right1 == m_CostMap.begin()) { left1 = end; left2 = end; } else if (right1 == (++(m_CostMap.begin()))) { auto temp = right1; left1 = --right1; // rght1 - 1 right1 = temp; left2 = end; } else { auto temp = right1; left1 = --right1; // rght1 - 1 left2 = --right1; // rght1 - 2 right1 = temp; } double partRight1, partRight2, partLeft1, partLeft2; partRight1 = partRight2 = partLeft1 = partLeft2 = 0.0; /* f(x) = v(bin) * e^ ( -1/2 * (|x-k(bin)| / sigma)^2 ) gaussian approximation where v(bin) is the value in the map k(bin) is the key */ if (left2 != end) { partLeft2 = ShortestPathCostFunctionLiveWire::Gaussian(keyOfX, left2->first, left2->second); } if (left1 != end) { partLeft1 = ShortestPathCostFunctionLiveWire::Gaussian(keyOfX, left1->first, left1->second); } if (right1 != end) { partRight1 = ShortestPathCostFunctionLiveWire::Gaussian(keyOfX, right1->first, right1->second); } if (right2 != end) { partRight2 = ShortestPathCostFunctionLiveWire::Gaussian(keyOfX, right2->first, right2->second); } if (m_MaxMapCosts > 0.0) { gradientCost = 1.0 - ((partRight1 + partRight2 + partLeft1 + partLeft2) / m_MaxMapCosts); } else { // use linear mapping gradientCost = 1.0 - (gradientMagnitude / m_GradientMax); } } else { // use linear mapping // value between 0 (good) and 1 (bad) gradientCost = 1.0 - (gradientMagnitude / m_GradientMax); } // Laplacian zero crossing costs // f(p) = 0; if I(p)=0 // or 1; if I(p)!=0 double laplacianCost; typename Superclass::PixelType laplaceImageValue; laplaceImageValue = m_EdgeImage->GetPixel(p2); if (laplaceImageValue < 0 || laplaceImageValue > 0) { laplacianCost = 1.0; } else { laplacianCost = 0.0; } // gradient vector at p1 double nGradientAtP1[2]; nGradientAtP1[0] = gradientX; // previously computed for gradient magnitude nGradientAtP1[1] = gradientY; // gradient direction unit vector of p1 nGradientAtP1[0] /= gradientMagnitude; nGradientAtP1[1] /= gradientMagnitude; //------- // gradient vector at p1 double nGradientAtP2[2]; nGradientAtP2[0] = m_GradientImage->GetPixel(p2)[0]; nGradientAtP2[1] = m_GradientImage->GetPixel(p2)[1]; nGradientAtP2[0] /= m_GradientMagnitudeImage->GetPixel(p2); nGradientAtP2[1] /= m_GradientMagnitudeImage->GetPixel(p2); double scalarProduct = (nGradientAtP1[0] * nGradientAtP2[0]) + (nGradientAtP1[1] * nGradientAtP2[1]); if (std::abs(scalarProduct) >= 1.0) { // this should probably not happen; make sure the input for acos is valid scalarProduct = 0.999999999; } double gradientDirectionCost = acos(scalarProduct) / 3.14159265; if (this->m_UseCostMap) { w1 = 0.43; w2 = 0.43; w3 = 0.14; } else { w1 = 0.10; w2 = 0.85; w3 = 0.05; } costs = w1 * laplacianCost + w2 * gradientCost + w3 * gradientDirectionCost; // scale by euclidian distance double costScale; if (p1[0] == p2[0] || p1[1] == p2[1]) { // horizontal or vertical neighbor costScale = 1.0; } else { // diagonal neighbor costScale = sqrt(2.0); } costs *= costScale; return costs; } template double ShortestPathCostFunctionLiveWire::GetMinCost() { - return minCosts; + return m_MinCosts; } template void ShortestPathCostFunctionLiveWire::Initialize() { if (!m_Initialized) { typedef itk::CastImageFilter CastFilterType; typename CastFilterType::Pointer castFilter = CastFilterType::New(); castFilter->SetInput(this->m_Image); // init gradient magnitude image typedef itk::GradientMagnitudeImageFilter GradientMagnitudeFilterType; typename GradientMagnitudeFilterType::Pointer gradientFilter = GradientMagnitudeFilterType::New(); gradientFilter->SetInput(castFilter->GetOutput()); // gradientFilter->SetNumberOfThreads(4); // gradientFilter->GetOutput()->SetRequestedRegion(m_RequestedRegion); gradientFilter->Update(); this->m_GradientMagnitudeImage = gradientFilter->GetOutput(); typedef itk::StatisticsImageFilter StatisticsImageFilterType; typename StatisticsImageFilterType::Pointer statisticsImageFilter = StatisticsImageFilterType::New(); statisticsImageFilter->SetInput(this->m_GradientMagnitudeImage); statisticsImageFilter->Update(); m_GradientMax = statisticsImageFilter->GetMaximum(); typedef itk::GradientImageFilter GradientFilterType; typename GradientFilterType::Pointer filter = GradientFilterType::New(); // sigma is specified in millimeters // filter->SetSigma( 1.5 ); filter->SetInput(castFilter->GetOutput()); filter->Update(); m_GradientImage = filter->GetOutput(); // init zero crossings // typedef itk::ZeroCrossingImageFilter< TInputImageType, UnsignedCharImageType > ZeroCrossingImageFilterType; // ZeroCrossingImageFilterType::Pointer zeroCrossingImageFilter = ZeroCrossingImageFilterType::New(); // zeroCrossingImageFilter->SetInput(this->m_Image); // zeroCrossingImageFilter->SetBackgroundValue(1); // zeroCrossingImageFilter->SetForegroundValue(0); // zeroCrossingImageFilter->SetNumberOfThreads(4); // zeroCrossingImageFilter->Update(); // m_EdgeImage = zeroCrossingImageFilter->GetOutput(); // cast image to float to apply canny edge dection filter /*typedef itk::CastImageFilter< TInputImageType, FloatImageType > CastFilterType; CastFilterType::Pointer castFilter = CastFilterType::New(); castFilter->SetInput(this->m_Image);*/ // typedef itk::LaplacianImageFilter filterType; // filterType::Pointer laplacianFilter = filterType::New(); // laplacianFilter->SetInput( castFilter->GetOutput() ); // NOTE: input image type must be double or float // laplacianFilter->Update(); // m_EdgeImage = laplacianFilter->GetOutput(); // init canny edge detection typedef itk::CannyEdgeDetectionImageFilter CannyEdgeDetectionImageFilterType; typename CannyEdgeDetectionImageFilterType::Pointer cannyEdgeDetectionfilter = CannyEdgeDetectionImageFilterType::New(); cannyEdgeDetectionfilter->SetInput(castFilter->GetOutput()); cannyEdgeDetectionfilter->SetUpperThreshold(30); cannyEdgeDetectionfilter->SetLowerThreshold(15); cannyEdgeDetectionfilter->SetVariance(4); cannyEdgeDetectionfilter->SetMaximumError(.01f); cannyEdgeDetectionfilter->Update(); m_EdgeImage = cannyEdgeDetectionfilter->GetOutput(); // set minCosts - minCosts = 0.0; // The lower, the more thouroughly! 0 = dijkstra. If estimate costs are lower than actual costs + m_MinCosts = 0.0; // The lower, the more thouroughly! 0 = dijkstra. If estimate costs are lower than actual costs // everything is fine. If estimation is higher than actual costs, you might not get the shortest // but a different path. m_Initialized = true; } // check start/end point value startValue = this->m_Image->GetPixel(this->m_StartIndex); endValue = this->m_Image->GetPixel(this->m_EndIndex); } template double ShortestPathCostFunctionLiveWire::SigmoidFunction( double I, double max, double min, double alpha, double beta) { // Using the SIgmoid formula from ITK Software Guide 6.3.2 Non Linear Mappings double Exponent = -1 * ((I - beta) / alpha); double Factor = 1 / (1 + exp(Exponent)); double newI = (max - min) * Factor + min; return newI; } template double ShortestPathCostFunctionLiveWire::Gaussian(double x, double xOfGaussian, double yOfGaussian) { return yOfGaussian * exp(-0.5 * pow((x - xOfGaussian), 2)); } } // end namespace itk #endif // __itkShortestPathCostFunctionLiveWire_txx diff --git a/Modules/GraphAlgorithms/itkShortestPathImageFilter.h b/Modules/GraphAlgorithms/itkShortestPathImageFilter.h index de08cc8c6d..1d960ddaad 100644 --- a/Modules/GraphAlgorithms/itkShortestPathImageFilter.h +++ b/Modules/GraphAlgorithms/itkShortestPathImageFilter.h @@ -1,235 +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. ===================================================================*/ #ifndef __itkShortestPathImageFilter_h #define __itkShortestPathImageFilter_h #include "itkImageToImageFilter.h" #include "itkShortestPathCostFunction.h" #include "itkShortestPathNode.h" #include #include // ------- INFORMATION ---------- /// SET FUNCTIONS // void SetInput( ItkImage ) // Compulsory // void SetStartIndex (const IndexType & StartIndex); // Compulsory // void SetEndIndex(const IndexType & EndIndex); // Compulsory // void SetFullNeighborsMode(bool) // Optional (default=false), if false N4, if true N26 // void SetActivateTimeOut(bool) // Optional (default=false), for debug issues: after 30s algorithms terminates. You can // have a look at the VectorOrderImage to see how far it came // void SetMakeOutputImage(bool) // Optional (default=true), Generate an outputimage of the path. You can also get the // path directoy with GetVectorPath() // void SetCalcAllDistances(bool) // Optional (default=false), Calculate Distances over the whole image. CAREFUL, // algorithm time extends a lot. Necessary for GetDistanceImage // void SetStoreVectorOrder(bool) // Optional (default=false), Stores in which order the pixels were checked. Necessary // for GetVectorOrderImage // void AddEndIndex(const IndexType & EndIndex) //Optional. By calling this function you can add several endpoints! The // algorithm will look for several shortest Pathes. From Start to all Endpoints. // /// GET FUNCTIONS // std::vector< itk::Index<3> > GetVectorPath(); // returns the shortest path as vector // std::vector< std::vector< itk::Index<3> > GetMultipleVectorPathe(); // returns a vector of shortest Pathes (which are // vectors of points) // GetDistanceImage // Returns the distance image // GetVectorOrderIMage // Returns the Vector Order image // // EXAMPLE USE // pleae see qmitkmitralvalvesegmentation4dtee bundle namespace itk { template class ShortestPathImageFilter : public ImageToImageFilter { public: // Standard Typedefs typedef ShortestPathImageFilter Self; typedef ImageToImageFilter Superclass; typedef SmartPointer Pointer; typedef SmartPointer ConstPointer; // Typdefs for metric typedef ShortestPathCostFunction CostFunctionType; typedef typename CostFunctionType::Pointer CostFunctionTypePointer; // More typdefs for convenience typedef TInputImageType InputImageType; typedef typename TInputImageType::Pointer InputImagePointer; typedef typename TInputImageType::PixelType InputImagePixelType; typedef typename TInputImageType::SizeType InputImageSizeType; typedef typename TInputImageType::IndexType IndexType; typedef typename itk::ImageRegionIteratorWithIndex InputImageIteratorType; typedef TOutputImageType OutputImageType; typedef typename TOutputImageType::Pointer OutputImagePointer; typedef typename TOutputImageType::PixelType OutputImagePixelType; typedef typename TOutputImageType::IndexType OutputImageIndexType; typedef ImageRegionIteratorWithIndex OutputImageIteratorType; typedef itk::ShapedNeighborhoodIterator itkShapedNeighborhoodIteratorType; // New Macro for smartpointer instantiation itkFactorylessNewMacro(Self) itkCloneMacro(Self) // Run-time type information itkTypeMacro(ShortestPathImageFilter, ImageToImageFilter) // Display void PrintSelf(std::ostream &os, Indent indent) const override; // Compare function for A_STAR struct CompareNodeStar { bool operator()(ShortestPathNode *a, ShortestPathNode *b) { return (a->distAndEst > b->distAndEst); } }; // \brief Set Starpoint for ShortestPath Calculation void SetStartIndex(const IndexType &StartIndex); // \brief Adds Endpoint for multiple ShortestPath Calculation void AddEndIndex(const IndexType &index); // \brief Set Endpoint for ShortestPath Calculation void SetEndIndex(const IndexType &EndIndex); // \brief Set FullNeighborsMode. false = no diagonal neighbors, in 2D this means N4 Neigborhood. true = would be N8 // in 2D itkSetMacro(FullNeighborsMode, bool); itkGetMacro(FullNeighborsMode, bool); // \brief Set Graph_fullNeighbors. false = no diagonal neighbors, in 2D this means N4 Neigborhood. true = would be // N8 in 2D itkSetMacro(Graph_fullNeighbors, bool) // \brief (default=true), Produce output image, which shows the shortest path. But you can also get the shortest // Path directly as vector with the function GetVectorPath itkSetMacro(MakeOutputImage, bool); itkGetMacro(MakeOutputImage, bool); // \brief (default=false), Store an Vector of Order, so you can call getVectorOrderImage after update itkSetMacro(StoreVectorOrder, bool); itkGetMacro(StoreVectorOrder, bool); // \brief (default=false), // Calculate all Distances to all pixels, so you can call getDistanceImage after update // (warning algo will take a long time) itkSetMacro(CalcAllDistances, bool); itkGetMacro(CalcAllDistances, bool); // \brief (default=false), for debug issues: after 30s algorithms terminates. You can have a look at the // VectorOrderImage to see how far it came itkSetMacro(ActivateTimeOut, bool); itkGetMacro(ActivateTimeOut, bool); // \brief returns shortest Path as vector std::vector GetVectorPath(); // \brief returns Multiple shortest Paths. You can call this function, when u performed a multiple shortest path // search (one start, several ends) std::vector> GetMultipleVectorPaths(); // \brief returns the vector order image. It shows in which order the pixels were checked. good for debugging. Be // sure to have m_StoreVectorOrder=true OutputImagePointer GetVectorOrderImage(); // \brief returns the distance image. It shows the distances from the startpoint to all other pixels. Be sure to // have m_CalcAllDistances=true OutputImagePointer GetDistanceImage(); // \brief Fill m_VectorPath void MakeShortestPathVector(); // \brief cleans up the filter void CleanUp(); itkSetObjectMacro(CostFunction, CostFunctionType); // itkSetObjectMacro = set function that uses pointer as parameter itkGetObjectMacro(CostFunction, CostFunctionType); protected: std::vector m_endPoints; // if you fill this vector, the algo will not rest until all endPoints have been reached std::vector m_endPointsClosed; ShortestPathNode *m_Nodes; // main list that contains all nodes NodeNumType m_Graph_NumberOfNodes; NodeNumType m_Graph_StartNode; NodeNumType m_Graph_EndNode; - unsigned int m_ImageDimensions; bool m_Graph_fullNeighbors; std::vector m_Graph_DiscoveredNodeList; ShortestPathImageFilter(Self &); // intentionally not implemented void operator=(const Self &); // intentionally not implemented const static int BACKGROUND = 0; const static int FOREGROUND = 255; bool m_FullNeighborsMode; bool m_MakeOutputImage; bool m_StoreVectorOrder; // Store an Vector of Order, so you can call getVectorOrderImage after update bool m_CalcAllDistances; // Calculate all Distances, so you can call getDistanceImage after update (warning algo // will take a long time) bool multipleEndPoints; bool m_ActivateTimeOut; // if true, then i search max. 30 secs. then abort bool m_Initialized; CostFunctionTypePointer m_CostFunction; IndexType m_StartIndex, m_EndIndex; std::vector m_VectorPath; std::vector> m_MultipleVectorPaths; std::vector m_VectorOrder; ShortestPathImageFilter(); ~ShortestPathImageFilter() override; // \brief Create all the outputs void MakeOutputs(); // \brief Generate Data void GenerateData() override; // \brief gets the estimate costs from pixel a to target. double getEstimatedCostsToTarget(const IndexType &a); typename InputImageType::Pointer m_magnitudeImage; // \brief Convert a indexnumber of a node in m_Nodes to image coordinates typename TInputImageType::IndexType NodeToCoord(NodeNumType); // \brief Convert image coordinate to a indexnumber of a node in m_Nodes unsigned int CoordToNode(IndexType); // \brief Returns the neighbors of a node std::vector GetNeighbors(NodeNumType nodeNum, bool FullNeighbors); // \brief Check if coords are in bounds of image bool CoordIsInBounds(IndexType); // \brief Initializes the graph void InitGraph(); // \brief Start ShortestPathSearch void StartShortestPathSearch(); }; } // end of namespace itk #include "itkShortestPathImageFilter.txx" #endif diff --git a/Modules/GraphAlgorithms/itkShortestPathImageFilter.txx b/Modules/GraphAlgorithms/itkShortestPathImageFilter.txx index 027b74dae7..720c1cb9db 100644 --- a/Modules/GraphAlgorithms/itkShortestPathImageFilter.txx +++ b/Modules/GraphAlgorithms/itkShortestPathImageFilter.txx @@ -1,893 +1,894 @@ /*=================================================================== 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 __itkShortestPathImageFilter_txx #define __itkShortestPathImageFilter_txx #include "itkShortestPathImageFilter.h" #include "mitkMemoryUtilities.h" #include #include #include #include namespace itk { // Constructor (initialize standard values) template ShortestPathImageFilter::ShortestPathImageFilter() : m_Nodes(nullptr), m_Graph_NumberOfNodes(0), + m_Graph_fullNeighbors(false), m_FullNeighborsMode(false), m_MakeOutputImage(true), m_StoreVectorOrder(false), m_CalcAllDistances(false), multipleEndPoints(false), m_ActivateTimeOut(false), m_Initialized(false) { m_endPoints.clear(); m_endPointsClosed.clear(); if (m_MakeOutputImage) { this->SetNumberOfRequiredOutputs(1); this->SetNthOutput(0, OutputImageType::New()); } } template ShortestPathImageFilter::~ShortestPathImageFilter() { delete[] m_Nodes; } template inline typename ShortestPathImageFilter::IndexType ShortestPathImageFilter::NodeToCoord(NodeNumType node) { const InputImageSizeType &size = this->GetInput()->GetRequestedRegion().GetSize(); int dim = InputImageType::ImageDimension; IndexType coord; if (dim == 2) { coord[1] = node / size[0]; coord[0] = node % size[0]; if (((unsigned long)coord[0] >= size[0]) || ((unsigned long)coord[1] >= size[1])) { coord[0] = 0; coord[1] = 0; } } if (dim == 3) { coord[2] = node / (size[0] * size[1]); coord[1] = (node % (size[0] * size[1])) / size[0]; coord[0] = (node % (size[0] * size[1])) % size[0]; if (((unsigned long)coord[0] >= size[0]) || ((unsigned long)coord[1] >= size[1]) || ((unsigned long)coord[2] >= size[2])) { coord[0] = 0; coord[1] = 0; coord[2] = 0; } } return coord; } template inline typename itk::NodeNumType ShortestPathImageFilter::CoordToNode( IndexType coord) { const InputImageSizeType &size = this->GetInput()->GetRequestedRegion().GetSize(); int dim = InputImageType::ImageDimension; NodeNumType node = 0; if (dim == 2) { node = (coord[1] * size[0]) + coord[0]; } if (dim == 3) { node = (coord[2] * size[0] * size[1]) + (coord[1] * size[0]) + coord[0]; } if ((m_Graph_NumberOfNodes > 0) && (node >= m_Graph_NumberOfNodes)) { /*MITK_INFO << "WARNING! Coordinates outside image!"; MITK_INFO << "Coords = " << coord ; MITK_INFO << "ImageDim = " << dim ; MITK_INFO << "RequestedRegionSize = " << size ;*/ node = 0; } return node; } template inline bool ShortestPathImageFilter::CoordIsInBounds(IndexType coord) { const InputImageSizeType &size = this->GetInput()->GetRequestedRegion().GetSize(); int dim = InputImageType::ImageDimension; if (dim == 2) { if ((coord[0] >= 0) && ((unsigned long)coord[0] < size[0]) && (coord[1] >= 0) && ((unsigned long)coord[1] < size[1])) { return true; } } if (dim == 3) { if ((coord[0] >= 0) && ((unsigned long)coord[0] < size[0]) && (coord[1] >= 0) && ((unsigned long)coord[1] < size[1]) && (coord[2] >= 0) && ((unsigned long)coord[2] < size[2])) { return true; } } return false; } template inline std::vector ShortestPathImageFilter::GetNeighbors( unsigned int nodeNum, bool FullNeighbors) { // returns a vector of nodepointers.. these nodes are the neighbors int dim = InputImageType::ImageDimension; IndexType Coord = NodeToCoord(nodeNum); IndexType NeighborCoord; std::vector nodeList; int neighborDistance = 1; // if i increase that, i might not hit the endnote // maybe use itkNeighborhoodIterator here, might be faster if (dim == 2) { // N4 NeighborCoord[0] = Coord[0]; NeighborCoord[1] = Coord[1] - neighborDistance; if (CoordIsInBounds(NeighborCoord)) nodeList.push_back(&m_Nodes[CoordToNode(NeighborCoord)]); NeighborCoord[0] = Coord[0] + neighborDistance; NeighborCoord[1] = Coord[1]; if (CoordIsInBounds(NeighborCoord)) nodeList.push_back(&m_Nodes[CoordToNode(NeighborCoord)]); NeighborCoord[0] = Coord[0]; NeighborCoord[1] = Coord[1] + neighborDistance; if (CoordIsInBounds(NeighborCoord)) nodeList.push_back(&m_Nodes[CoordToNode(NeighborCoord)]); NeighborCoord[0] = Coord[0] - neighborDistance; NeighborCoord[1] = Coord[1]; if (CoordIsInBounds(NeighborCoord)) nodeList.push_back(&m_Nodes[CoordToNode(NeighborCoord)]); if (FullNeighbors) { // N8 NeighborCoord[0] = Coord[0] - neighborDistance; NeighborCoord[1] = Coord[1] - neighborDistance; if (CoordIsInBounds(NeighborCoord)) nodeList.push_back(&m_Nodes[CoordToNode(NeighborCoord)]); NeighborCoord[0] = Coord[0] + neighborDistance; NeighborCoord[1] = Coord[1] - neighborDistance; if (CoordIsInBounds(NeighborCoord)) nodeList.push_back(&m_Nodes[CoordToNode(NeighborCoord)]); NeighborCoord[0] = Coord[0] - neighborDistance; NeighborCoord[1] = Coord[1] + neighborDistance; if (CoordIsInBounds(NeighborCoord)) nodeList.push_back(&m_Nodes[CoordToNode(NeighborCoord)]); NeighborCoord[0] = Coord[0] + neighborDistance; NeighborCoord[1] = Coord[1] + neighborDistance; if (CoordIsInBounds(NeighborCoord)) nodeList.push_back(&m_Nodes[CoordToNode(NeighborCoord)]); } } if (dim == 3) { // N6 NeighborCoord[0] = Coord[0]; NeighborCoord[1] = Coord[1] - neighborDistance; NeighborCoord[2] = Coord[2]; if (CoordIsInBounds(NeighborCoord)) nodeList.push_back(&m_Nodes[CoordToNode(NeighborCoord)]); NeighborCoord[0] = Coord[0] + neighborDistance; NeighborCoord[1] = Coord[1]; NeighborCoord[2] = Coord[2]; if (CoordIsInBounds(NeighborCoord)) nodeList.push_back(&m_Nodes[CoordToNode(NeighborCoord)]); NeighborCoord[0] = Coord[0]; NeighborCoord[1] = Coord[1] + neighborDistance; NeighborCoord[2] = Coord[2]; if (CoordIsInBounds(NeighborCoord)) nodeList.push_back(&m_Nodes[CoordToNode(NeighborCoord)]); NeighborCoord[0] = Coord[0] - neighborDistance; NeighborCoord[1] = Coord[1]; NeighborCoord[2] = Coord[2]; if (CoordIsInBounds(NeighborCoord)) nodeList.push_back(&m_Nodes[CoordToNode(NeighborCoord)]); NeighborCoord[0] = Coord[0]; NeighborCoord[1] = Coord[1]; NeighborCoord[2] = Coord[2] + neighborDistance; if (CoordIsInBounds(NeighborCoord)) nodeList.push_back(&m_Nodes[CoordToNode(NeighborCoord)]); NeighborCoord[0] = Coord[0]; NeighborCoord[1] = Coord[1]; NeighborCoord[2] = Coord[2] - neighborDistance; if (CoordIsInBounds(NeighborCoord)) nodeList.push_back(&m_Nodes[CoordToNode(NeighborCoord)]); if (FullNeighbors) { // N26 // Middle Slice NeighborCoord[0] = Coord[0] - neighborDistance; NeighborCoord[1] = Coord[1] - neighborDistance; NeighborCoord[2] = Coord[2]; if (CoordIsInBounds(NeighborCoord)) nodeList.push_back(&m_Nodes[CoordToNode(NeighborCoord)]); NeighborCoord[0] = Coord[0] + neighborDistance; NeighborCoord[1] = Coord[1] - neighborDistance; NeighborCoord[2] = Coord[2]; if (CoordIsInBounds(NeighborCoord)) nodeList.push_back(&m_Nodes[CoordToNode(NeighborCoord)]); NeighborCoord[0] = Coord[0] - neighborDistance; NeighborCoord[1] = Coord[1] + neighborDistance; NeighborCoord[2] = Coord[2]; if (CoordIsInBounds(NeighborCoord)) nodeList.push_back(&m_Nodes[CoordToNode(NeighborCoord)]); NeighborCoord[0] = Coord[0] + neighborDistance; NeighborCoord[1] = Coord[1] + neighborDistance; NeighborCoord[2] = Coord[2]; if (CoordIsInBounds(NeighborCoord)) nodeList.push_back(&m_Nodes[CoordToNode(NeighborCoord)]); // BackSlice (Diagonal) NeighborCoord[0] = Coord[0] - neighborDistance; NeighborCoord[1] = Coord[1] - neighborDistance; NeighborCoord[2] = Coord[2] - neighborDistance; if (CoordIsInBounds(NeighborCoord)) nodeList.push_back(&m_Nodes[CoordToNode(NeighborCoord)]); NeighborCoord[0] = Coord[0] + neighborDistance; NeighborCoord[1] = Coord[1] - neighborDistance; NeighborCoord[2] = Coord[2] - neighborDistance; if (CoordIsInBounds(NeighborCoord)) nodeList.push_back(&m_Nodes[CoordToNode(NeighborCoord)]); NeighborCoord[0] = Coord[0] - neighborDistance; NeighborCoord[1] = Coord[1] + neighborDistance; NeighborCoord[2] = Coord[2] - neighborDistance; if (CoordIsInBounds(NeighborCoord)) nodeList.push_back(&m_Nodes[CoordToNode(NeighborCoord)]); NeighborCoord[0] = Coord[0] + neighborDistance; NeighborCoord[1] = Coord[1] + neighborDistance; NeighborCoord[2] = Coord[2] - neighborDistance; if (CoordIsInBounds(NeighborCoord)) nodeList.push_back(&m_Nodes[CoordToNode(NeighborCoord)]); // BackSlice (Non-Diag) NeighborCoord[0] = Coord[0]; NeighborCoord[1] = Coord[1] - neighborDistance; NeighborCoord[2] = Coord[2] - neighborDistance; if (CoordIsInBounds(NeighborCoord)) nodeList.push_back(&m_Nodes[CoordToNode(NeighborCoord)]); NeighborCoord[0] = Coord[0] + neighborDistance; NeighborCoord[1] = Coord[1]; NeighborCoord[2] = Coord[2] - neighborDistance; if (CoordIsInBounds(NeighborCoord)) nodeList.push_back(&m_Nodes[CoordToNode(NeighborCoord)]); NeighborCoord[0] = Coord[0]; NeighborCoord[1] = Coord[1] + neighborDistance; NeighborCoord[2] = Coord[2] - neighborDistance; if (CoordIsInBounds(NeighborCoord)) nodeList.push_back(&m_Nodes[CoordToNode(NeighborCoord)]); NeighborCoord[0] = Coord[0] - neighborDistance; NeighborCoord[1] = Coord[1]; NeighborCoord[2] = Coord[2] - neighborDistance; if (CoordIsInBounds(NeighborCoord)) nodeList.push_back(&m_Nodes[CoordToNode(NeighborCoord)]); // FrontSlice (Diagonal) NeighborCoord[0] = Coord[0] - neighborDistance; NeighborCoord[1] = Coord[1] - neighborDistance; NeighborCoord[2] = Coord[2] + neighborDistance; if (CoordIsInBounds(NeighborCoord)) nodeList.push_back(&m_Nodes[CoordToNode(NeighborCoord)]); NeighborCoord[0] = Coord[0] + neighborDistance; NeighborCoord[1] = Coord[1] - neighborDistance; NeighborCoord[2] = Coord[2] + neighborDistance; if (CoordIsInBounds(NeighborCoord)) nodeList.push_back(&m_Nodes[CoordToNode(NeighborCoord)]); NeighborCoord[0] = Coord[0] - neighborDistance; NeighborCoord[1] = Coord[1] + neighborDistance; NeighborCoord[2] = Coord[2] + neighborDistance; if (CoordIsInBounds(NeighborCoord)) nodeList.push_back(&m_Nodes[CoordToNode(NeighborCoord)]); NeighborCoord[0] = Coord[0] + neighborDistance; NeighborCoord[1] = Coord[1] + neighborDistance; NeighborCoord[2] = Coord[2] + neighborDistance; if (CoordIsInBounds(NeighborCoord)) nodeList.push_back(&m_Nodes[CoordToNode(NeighborCoord)]); // FrontSlice(Non-Diag) NeighborCoord[0] = Coord[0]; NeighborCoord[1] = Coord[1] - neighborDistance; NeighborCoord[2] = Coord[2] + neighborDistance; if (CoordIsInBounds(NeighborCoord)) nodeList.push_back(&m_Nodes[CoordToNode(NeighborCoord)]); NeighborCoord[0] = Coord[0] + neighborDistance; NeighborCoord[1] = Coord[1]; NeighborCoord[2] = Coord[2] + neighborDistance; if (CoordIsInBounds(NeighborCoord)) nodeList.push_back(&m_Nodes[CoordToNode(NeighborCoord)]); NeighborCoord[0] = Coord[0]; NeighborCoord[1] = Coord[1] + neighborDistance; NeighborCoord[2] = Coord[2] + neighborDistance; if (CoordIsInBounds(NeighborCoord)) nodeList.push_back(&m_Nodes[CoordToNode(NeighborCoord)]); NeighborCoord[0] = Coord[0] - neighborDistance; NeighborCoord[1] = Coord[1]; NeighborCoord[2] = Coord[2] + neighborDistance; if (CoordIsInBounds(NeighborCoord)) nodeList.push_back(&m_Nodes[CoordToNode(NeighborCoord)]); } } return nodeList; } template void ShortestPathImageFilter::SetStartIndex( const typename TInputImageType::IndexType &StartIndex) { for (unsigned int i = 0; i < TInputImageType::ImageDimension; ++i) { m_StartIndex[i] = StartIndex[i]; } m_Graph_StartNode = CoordToNode(m_StartIndex); // MITK_INFO << "StartIndex = " << StartIndex; // MITK_INFO << "StartNode = " << m_Graph_StartNode; m_Initialized = false; } template void ShortestPathImageFilter::SetEndIndex( const typename TInputImageType::IndexType &EndIndex) { for (unsigned int i = 0; i < TInputImageType::ImageDimension; ++i) { m_EndIndex[i] = EndIndex[i]; } m_Graph_EndNode = CoordToNode(m_EndIndex); // MITK_INFO << "EndNode = " << m_Graph_EndNode; } template void ShortestPathImageFilter::AddEndIndex( const typename TInputImageType::IndexType &index) { // ONLY FOR MULTIPLE END POINTS SEARCH IndexType newEndIndex; for (unsigned int i = 0; i < TInputImageType::ImageDimension; ++i) { newEndIndex[i] = index[i]; } m_endPoints.push_back(newEndIndex); SetEndIndex(m_endPoints[0]); multipleEndPoints = true; } template inline double ShortestPathImageFilter::getEstimatedCostsToTarget( const typename TInputImageType::IndexType &a) { // Returns the minimal possible costs for a path from "a" to targetnode. itk::Vector v; v[0] = m_EndIndex[0] - a[0]; v[1] = m_EndIndex[1] - a[1]; v[2] = m_EndIndex[2] - a[2]; return m_CostFunction->GetMinCost() * v.GetNorm(); } template void ShortestPathImageFilter::InitGraph() { if (!m_Initialized) { // Clean up previous stuff CleanUp(); // Calc Number of nodes - m_ImageDimensions = TInputImageType::ImageDimension; + auto imageDimensions = TInputImageType::ImageDimension; const InputImageSizeType &size = this->GetInput()->GetRequestedRegion().GetSize(); m_Graph_NumberOfNodes = 1; - for (NodeNumType i = 0; i < m_ImageDimensions; ++i) + for (NodeNumType i = 0; i < imageDimensions; ++i) m_Graph_NumberOfNodes = m_Graph_NumberOfNodes * size[i]; // Initialize mainNodeList with that number m_Nodes = new ShortestPathNode[m_Graph_NumberOfNodes]; // Initialize each node in nodelist for (NodeNumType i = 0; i < m_Graph_NumberOfNodes; i++) { m_Nodes[i].distAndEst = -1; m_Nodes[i].distance = -1; m_Nodes[i].prevNode = -1; m_Nodes[i].mainListIndex = i; m_Nodes[i].closed = false; } m_Initialized = true; } // In the beginning, the Startnode needs a distance of 0 m_Nodes[m_Graph_StartNode].distance = 0; m_Nodes[m_Graph_StartNode].distAndEst = 0; // initalize cost function m_CostFunction->Initialize(); } template void ShortestPathImageFilter::StartShortestPathSearch() { // Setup Timer clock_t startAll = clock(); clock_t stopAll = clock(); // init variables double durationAll = 0; bool timeout = false; NodeNumType mainNodeListIndex = 0; DistanceType curNodeDistance = 0; NodeNumType numberOfNodesChecked = 0; // Create Multimap (tree structure for fast searching) std::multimap myMap; std::pair::iterator, std::multimap::iterator> ret; std::multimap::iterator it; // At first, only startNote is discovered. myMap.insert( std::pair(m_Nodes[m_Graph_StartNode].distAndEst, &m_Nodes[m_Graph_StartNode])); // While there are discovered Nodes, pick the one with lowest distance, // update its neighbors and eventually delete it from the discovered Nodes list. while (!myMap.empty()) { numberOfNodesChecked++; /*if ( (numberOfNodesChecked % (m_Graph_NumberOfNodes/100)) == 0) { MITK_INFO << "Checked " << ( numberOfNodesChecked / (m_Graph_NumberOfNodes/100) ) << "% List: " << myMap.size() << "\n"; }*/ // Get element with lowest score mainNodeListIndex = myMap.begin()->second->mainListIndex; curNodeDistance = myMap.begin()->second->distance; myMap.begin()->second->closed = true; // close it // Debug: // MITK_INFO << "INFO: size " << myMap.size(); /* for (it = myMap.begin(); it != myMap.end(); ++it) { MITK_INFO << "(1) " << it->first << "|" << it->second->distAndEst << "|"<second->mainListIndex; } */ // Kicks out element with lowest score myMap.erase(myMap.begin()); // if wanted, store vector order if (m_StoreVectorOrder) { m_VectorOrder.push_back(mainNodeListIndex); } // Check neighbors std::vector neighborNodes = GetNeighbors(mainNodeListIndex, m_Graph_fullNeighbors); for (NodeNumType i = 0; i < neighborNodes.size(); i++) { if (neighborNodes[i]->closed) continue; // this nodes is already closed, go to next neighbor IndexType coordCurNode = NodeToCoord(mainNodeListIndex); IndexType coordNeighborNode = NodeToCoord(neighborNodes[i]->mainListIndex); // calculate the new Distance to the current neighbor double newDistance = curNodeDistance + (m_CostFunction->GetCost(coordCurNode, coordNeighborNode)); // if it is shorter than any yet known path to this neighbor, than the current path is better. Save that! if ((newDistance < neighborNodes[i]->distance) || (neighborNodes[i]->distance == -1)) { // if that neighbornode is not in discoverednodeList yet, Push it there and update if (neighborNodes[i]->distance == -1) { neighborNodes[i]->distance = newDistance; neighborNodes[i]->distAndEst = newDistance + getEstimatedCostsToTarget(coordNeighborNode); neighborNodes[i]->prevNode = mainNodeListIndex; myMap.insert(std::pair(m_Nodes[neighborNodes[i]->mainListIndex].distAndEst, &m_Nodes[neighborNodes[i]->mainListIndex])); /* MITK_INFO << "Inserted: " << m_Nodes[neighborNodes[i]->mainListIndex].distAndEst << "|" << m_Nodes[neighborNodes[i]->mainListIndex].mainListIndex; MITK_INFO << "INFO: size " << myMap.size(); for (it = myMap.begin(); it != myMap.end(); ++it) { MITK_INFO << "(1) " << it->first << "|" << it->second->distAndEst << "|"<second->mainListIndex; } */ } // or if is already in discoverednodelist, update else { /* it = myMap.find(neighborNodes[i]->distAndEst); if (it == myMap.end() ) { MITK_INFO << "Nothing!"; // look further for (it = myMap.begin(); it != myMap.end(); ++it) { if ((*it).second->mainListIndex == lookForId) { MITK_INFO << "But it is there!!!"; MITK_INFO << "Searched for: " << lookFor << " but had: " << (*it).second->distAndEst; } } } */ // 1st : find and delete old element bool found = false; ret = myMap.equal_range(neighborNodes[i]->distAndEst); if ((ret.first == ret.second)) { /*+++++++++++++ no exact match +++++++++++++*/ // MITK_INFO << "No exact match!"; // if this happens, you are screwed /* MITK_INFO << "Was looking for: " << lookFor << " ID: " << lookForId; if (ret.first != myMap.end() ) { it = ret.first; MITK_INFO << "Found: " << it->first << " ID: " << it->second->mainListIndex; ++it; MITK_INFO << "Found: " << it->first << " ID: " << it->second->mainListIndex; --it; --it; MITK_INFO << "Found: " << it->first << " ID: " << it->second->mainListIndex; } // look if that ID is found in the map for (it = myMap.begin(); it != myMap.end(); ++it) { if ((*it).second->mainListIndex == lookForId) { MITK_INFO << "But it is there!!!"; MITK_INFO << "Searched dist: " << lookFor << " found dist: " << (*it).second->distAndEst; } } */ } else { for (it = ret.first; it != ret.second; ++it) { if (it->second->mainListIndex == neighborNodes[i]->mainListIndex) { found = true; myMap.erase(it); /* MITK_INFO << "INFO: size " << myMap.size(); MITK_INFO << "Erase: " << it->first << "|" << it->second->mainListIndex; MITK_INFO << "INFO: size " << myMap.size(); for (it = myMap.begin(); it != myMap.end(); ++it) { MITK_INFO << "(1) " << it->first << "|" << it->second->distAndEst << "|"<second->mainListIndex; } */ break; } } } if (!found) { // MITK_INFO << "Could not find it! :("; continue; } // 2nd: update and insert new element neighborNodes[i]->distance = newDistance; neighborNodes[i]->distAndEst = newDistance + getEstimatedCostsToTarget(coordNeighborNode); neighborNodes[i]->prevNode = mainNodeListIndex; // myMap.insert( std::pair (neighborNodes[i]->distAndEst, neighborNodes[i])); myMap.insert(std::pair(m_Nodes[neighborNodes[i]->mainListIndex].distAndEst, &m_Nodes[neighborNodes[i]->mainListIndex])); // MITK_INFO << "Re-Inserted: " << m_Nodes[neighborNodes[i]->mainListIndex].distAndEst << "|" << // m_Nodes[neighborNodes[i]->mainListIndex].mainListIndex; // MITK_INFO << "INFO: size " << myMap.size(); /*for (it = myMap.begin(); it != myMap.end(); ++it) { MITK_INFO << "(1) " << it->first << "|" << it->second->distAndEst << "|"<second->mainListIndex; }*/ } } } // finished with checking all neighbors. // Check Timeout, if activated if (m_ActivateTimeOut) { stopAll = clock(); durationAll = (double)(stopAll - startAll) / CLOCKS_PER_SEC; if (durationAll >= 30) { // MITK_INFO << "TIMEOUT!! Search took over 30 seconds"; timeout = true; } } // Check end criteria: // For multiple points if (multipleEndPoints) { // super slow, make it faster for (unsigned int i = 0; i < m_endPoints.size(); i++) { if (CoordToNode(m_endPoints[i]) == mainNodeListIndex) { m_endPointsClosed.push_back(NodeToCoord(mainNodeListIndex)); m_endPoints.erase(m_endPoints.begin() + i); if (m_endPoints.empty()) { // Finished! break return; } if (m_Graph_EndNode == mainNodeListIndex) { // set new end SetEndIndex(m_endPoints[0]); } } } } // if single end point, then end, if this one is reached or timeout happened. else if ((mainNodeListIndex == m_Graph_EndNode || timeout) && !m_CalcAllDistances) { /*if (m_StoreVectorOrder) MITK_INFO << "Number of Nodes checked: " << m_VectorOrder.size() ;*/ return; } } } template void ShortestPathImageFilter::MakeOutputs() { // MITK_INFO << "Make Output"; if (m_MakeOutputImage) { OutputImagePointer output0 = this->GetOutput(0); output0->SetRegions(this->GetInput()->GetLargestPossibleRegion()); output0->Allocate(); OutputImageIteratorType shortestPathImageIt(output0, output0->GetRequestedRegion()); // Create ShortestPathImage (Output 0) for (shortestPathImageIt.GoToBegin(); !shortestPathImageIt.IsAtEnd(); ++shortestPathImageIt) { // First intialize with background color shortestPathImageIt.Set(BACKGROUND); } if (!multipleEndPoints) // Only one path was calculated { for (unsigned int i = 0; i < m_VectorPath.size(); i++) { shortestPathImageIt.SetIndex(m_VectorPath[i]); shortestPathImageIt.Set(FOREGROUND); } } else // multiple pathes has been calculated, draw all { for (unsigned int i = 0; i < m_MultipleVectorPaths.size(); i++) { for (unsigned int j = 0; j < m_MultipleVectorPaths[i].size(); j++) { shortestPathImageIt.SetIndex(m_MultipleVectorPaths[i][j]); shortestPathImageIt.Set(FOREGROUND); } } } } } template typename ShortestPathImageFilter::OutputImagePointer ShortestPathImageFilter::GetVectorOrderImage() { // Create Vector Order Image // Return it OutputImagePointer image = OutputImageType::New(); image->SetRegions(this->GetInput()->GetLargestPossibleRegion()); image->Allocate(); OutputImageIteratorType vectorOrderImageIt(image, image->GetRequestedRegion()); // MITK_INFO << "GetVectorOrderImage"; for (vectorOrderImageIt.GoToBegin(); !vectorOrderImageIt.IsAtEnd(); ++vectorOrderImageIt) { // First intialize with background color vectorOrderImageIt.Value() = BACKGROUND; } for (int i = 0; i < m_VectorOrder.size(); i++) { vectorOrderImageIt.SetIndex(NodeToCoord(m_VectorOrder[i])); vectorOrderImageIt.Set(BACKGROUND + i); } return image; } template typename ShortestPathImageFilter::OutputImagePointer ShortestPathImageFilter::GetDistanceImage() { // Create Distance Image // Return it OutputImagePointer image = OutputImageType::New(); image->SetRegions(this->GetInput()->GetLargestPossibleRegion()); image->Allocate(); ; OutputImageIteratorType distanceImageIt(image, image->GetRequestedRegion()); // Create Distance Image (Output 1) NodeNumType myNodeNum; for (distanceImageIt.GoToBegin(); !distanceImageIt.IsAtEnd(); ++distanceImageIt) { IndexType index = distanceImageIt.GetIndex(); myNodeNum = CoordToNode(index); double newVal = m_Nodes[myNodeNum].distance; distanceImageIt.Set(newVal); } } template std::vector::IndexType> ShortestPathImageFilter::GetVectorPath() { return m_VectorPath; } template std::vector::IndexType>> ShortestPathImageFilter::GetMultipleVectorPaths() { return m_MultipleVectorPaths; } template void ShortestPathImageFilter::MakeShortestPathVector() { // MITK_INFO << "Make ShortestPath Vec"; // single end point if (!multipleEndPoints) { // fill m_VectorPath with the Shortest Path m_VectorPath.clear(); // Go backwards from endnote to startnode NodeNumType prevNode = m_Graph_EndNode; while (prevNode != m_Graph_StartNode) { m_VectorPath.push_back(NodeToCoord(prevNode)); prevNode = m_Nodes[prevNode].prevNode; } m_VectorPath.push_back(NodeToCoord(prevNode)); // reverse it std::reverse(m_VectorPath.begin(), m_VectorPath.end()); } // Multiple end end points and pathes else { for (unsigned int i = 0; i < m_endPointsClosed.size(); i++) { m_VectorPath.clear(); // Go backwards from endnote to startnode NodeNumType prevNode = CoordToNode(m_endPointsClosed[i]); while (prevNode != m_Graph_StartNode) { m_VectorPath.push_back(NodeToCoord(prevNode)); prevNode = m_Nodes[prevNode].prevNode; } m_VectorPath.push_back(NodeToCoord(prevNode)); // reverse it std::reverse(m_VectorPath.begin(), m_VectorPath.end()); // push_back m_MultipleVectorPaths.push_back(m_VectorPath); } } } template void ShortestPathImageFilter::CleanUp() { m_VectorOrder.clear(); m_VectorPath.clear(); // TODO: if multiple Path, clear all multiple Paths if (m_Nodes) delete[] m_Nodes; } template void ShortestPathImageFilter::GenerateData() { // Build Graph InitGraph(); // Calc Shortest Parth StartShortestPathSearch(); // Fill Shortest Path MakeShortestPathVector(); // Make Outputs MakeOutputs(); } template void ShortestPathImageFilter::PrintSelf(std::ostream &os, Indent indent) const { Superclass::PrintSelf(os, indent); } } /* end namespace itk */ #endif // __itkShortestPathImageFilter_txx diff --git a/Modules/IOExt/Internal/mitkParRecFileReader.h b/Modules/IOExt/Internal/mitkParRecFileReader.h index 0cfc3292f3..c4654ebe49 100644 --- a/Modules/IOExt/Internal/mitkParRecFileReader.h +++ b/Modules/IOExt/Internal/mitkParRecFileReader.h @@ -1,73 +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. ===================================================================*/ #ifndef PARRECFILEREADER_H_HEADER_INCLUDED_C1F48A22 #define PARRECFILEREADER_H_HEADER_INCLUDED_C1F48A22 #include "mitkCommon.h" #include "mitkFileReader.h" #include "mitkImageSource.h" namespace mitk { //##Documentation //## @brief Reader to read files in Philips PAR/REC file format class ParRecFileReader : public ImageSource, public FileReader { public: mitkClassMacro(ParRecFileReader, FileReader); /** Method for creation through the object factory. */ itkFactorylessNewMacro(Self) itkCloneMacro(Self) itkSetStringMacro(FileName); itkGetStringMacro(FileName); itkSetStringMacro(FilePrefix); itkGetStringMacro(FilePrefix); itkSetStringMacro(FilePattern); itkGetStringMacro(FilePattern); static bool CanReadFile(const std::string filename, const std::string filePrefix, const std::string filePattern); protected: void GenerateData() override; void GenerateOutputInformation() override; ParRecFileReader(); ~ParRecFileReader() override; //##Description //## @brief Time when Header was last read itk::TimeStamp m_ReadHeaderTime; - int m_StartFileIndex; - protected: std::string m_FileName; std::string m_RecFileName; std::string m_FilePrefix; std::string m_FilePattern; }; } // namespace mitk #endif /* PARRECFILEREADER_H_HEADER_INCLUDED_C1F48A22 */ diff --git a/Modules/ImageDenoising/itkTotalVariationDenoisingImageFilter.txx b/Modules/ImageDenoising/itkTotalVariationDenoisingImageFilter.txx index 9f6c4945dc..acbadbb11c 100644 --- a/Modules/ImageDenoising/itkTotalVariationDenoisingImageFilter.txx +++ b/Modules/ImageDenoising/itkTotalVariationDenoisingImageFilter.txx @@ -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. ===================================================================*/ /*=================================================================== This file is based heavily on a corresponding ITK filter. ===================================================================*/ #ifndef _itkTotalVariationDenoisingImageFilter_txx #define _itkTotalVariationDenoisingImageFilter_txx #include "itkTotalVariationDenoisingImageFilter.h" #include "itkConstShapedNeighborhoodIterator.h" #include "itkImageRegionConstIterator.h" #include "itkImageRegionIterator.h" #include "itkLocalVariationImageFilter.h" #include "itkNeighborhoodAlgorithm.h" #include "itkNeighborhoodInnerProduct.h" #include "itkOffset.h" #include "itkProgressReporter.h" #include "itkZeroFluxNeumannBoundaryCondition.h" #include #include namespace itk { template TotalVariationDenoisingImageFilter::TotalVariationDenoisingImageFilter() + : m_Lambda(1.0), m_NumberIterations(0) { - m_Lambda = 1.0; } template void TotalVariationDenoisingImageFilter::GenerateData() { // first we cast the input image to match output type typename CastType::Pointer infilter = CastType::New(); infilter->SetInput(this->GetInput()); infilter->Update(); typename TOutputImage::Pointer image = infilter->GetOutput(); // a second copy of the input image is saved as reference infilter = CastType::New(); infilter->SetInput(this->GetInput()); infilter->Update(); typename TOutputImage::Pointer origImage = infilter->GetOutput(); typename SingleIterationFilterType::Pointer filter; for (int i = 0; i < m_NumberIterations; i++) { filter = SingleIterationFilterType::New(); filter->SetInput(image); filter->SetOriginalImage(origImage); filter->SetLambda(m_Lambda); filter->SetNumberOfThreads(this->GetNumberOfThreads()); filter->UpdateLargestPossibleRegion(); image = filter->GetOutput(); std::cout << "Iteration " << i + 1 << "/" << m_NumberIterations << std::endl; } typename OutputImageType::Pointer output = this->GetOutput(); output->SetSpacing(image->GetSpacing()); typename OutputImageType::RegionType largestPossibleRegion; largestPossibleRegion.SetSize(image->GetLargestPossibleRegion().GetSize()); largestPossibleRegion.SetIndex(image->GetLargestPossibleRegion().GetIndex()); output->SetLargestPossibleRegion(image->GetLargestPossibleRegion()); output->SetBufferedRegion(image->GetLargestPossibleRegion()); output->Allocate(); itk::ImageRegionIterator oit(output, output->GetLargestPossibleRegion()); oit.GoToBegin(); itk::ImageRegionConstIterator iit(image, image->GetLargestPossibleRegion()); iit.GoToBegin(); while (!oit.IsAtEnd()) { oit.Set(iit.Get()); ++iit; ++oit; } } /** * Standard "PrintSelf" method */ template void TotalVariationDenoisingImageFilter::PrintSelf(std::ostream &os, Indent indent) const { Superclass::PrintSelf(os, indent); } } // end namespace itk #endif diff --git a/Modules/ImageExtraction/mitkExtractDirectedPlaneImageFilterNew.cpp b/Modules/ImageExtraction/mitkExtractDirectedPlaneImageFilterNew.cpp index 7ec1e222a8..b8b98cdc13 100644 --- a/Modules/ImageExtraction/mitkExtractDirectedPlaneImageFilterNew.cpp +++ b/Modules/ImageExtraction/mitkExtractDirectedPlaneImageFilterNew.cpp @@ -1,288 +1,288 @@ /*=================================================================== 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 "mitkExtractDirectedPlaneImageFilterNew.h" #include "itkImageRegionIterator.h" #include "mitkImageCast.h" #include "mitkImageTimeSelector.h" #include mitk::ExtractDirectedPlaneImageFilterNew::ExtractDirectedPlaneImageFilterNew() - : m_CurrentWorldPlaneGeometry(nullptr), m_ActualInputTimestep(0) + : m_CurrentWorldPlaneGeometry(nullptr), m_ImageGeometry(nullptr), m_ActualInputTimestep(0) { MITK_WARN << "Class ExtractDirectedPlaneImageFilterNew is deprecated! Use ExtractSliceFilter instead."; } mitk::ExtractDirectedPlaneImageFilterNew::~ExtractDirectedPlaneImageFilterNew() { } void mitk::ExtractDirectedPlaneImageFilterNew::GenerateData() { mitk::Image::ConstPointer inputImage = ImageToImageFilter::GetInput(0); if (!inputImage) { MITK_ERROR << "mitk::ExtractDirectedPlaneImageFilterNew: No input available. Please set the input!" << std::endl; itkExceptionMacro("mitk::ExtractDirectedPlaneImageFilterNew: No input available. Please set the input!"); return; } m_ImageGeometry = inputImage->GetGeometry(); // If no timestep is set, the lowest given will be selected // const mitk::TimeGeometry* inputTimeGeometry = this->GetInput()->GetTimeGeometry(); if (inputImage->GetDimension() > 4 || inputImage->GetDimension() < 2) { MITK_ERROR << "mitk::ExtractDirectedPlaneImageFilterNew:GenerateData works only with 3D and 3D+t images, sorry." << std::endl; itkExceptionMacro("mitk::ExtractDirectedPlaneImageFilterNew works only with 3D and 3D+t images, sorry."); return; } else if (inputImage->GetDimension() == 4) { mitk::ImageTimeSelector::Pointer timeselector = mitk::ImageTimeSelector::New(); timeselector->SetInput(inputImage); timeselector->SetTimeNr(m_ActualInputTimestep); timeselector->UpdateLargestPossibleRegion(); inputImage = timeselector->GetOutput(); } else if (inputImage->GetDimension() == 2) { mitk::Image::Pointer resultImage = ImageToImageFilter::GetOutput(); resultImage = const_cast(inputImage.GetPointer()); ImageToImageFilter::SetNthOutput(0, resultImage); return; } if (!m_CurrentWorldPlaneGeometry) { MITK_ERROR << "mitk::ExtractDirectedPlaneImageFilterNew::GenerateData has no CurrentWorldPlaneGeometry set" << std::endl; return; } AccessFixedDimensionByItk(inputImage, ItkSliceExtraction, 3); } // Generate Data void mitk::ExtractDirectedPlaneImageFilterNew::GenerateOutputInformation() { Superclass::GenerateOutputInformation(); } /* * The desired slice is extracted by filling the image`s corresponding pixel values in an empty 2 dimensional itk::Image * Therefor the itk image`s extent in pixel (in each direction) is doubled and its spacing (also in each direction) is * divided by two * (similar to the shannon theorem). */ template void mitk::ExtractDirectedPlaneImageFilterNew::ItkSliceExtraction(const itk::Image *inputImage) { typedef itk::Image InputImageType; typedef itk::Image SliceImageType; typedef itk::ImageRegionConstIterator SliceIterator; // Creating an itk::Image that represents the sampled slice typename SliceImageType::Pointer resultSlice = SliceImageType::New(); typename SliceImageType::IndexType start; start[0] = 0; start[1] = 0; Point3D origin = m_CurrentWorldPlaneGeometry->GetOrigin(); Vector3D right = m_CurrentWorldPlaneGeometry->GetAxisVector(0); Vector3D bottom = m_CurrentWorldPlaneGeometry->GetAxisVector(1); // Calculation the sample-spacing, i.e the half of the smallest spacing existing in the original image Vector3D newPixelSpacing = m_ImageGeometry->GetSpacing(); float minSpacing = newPixelSpacing[0]; for (unsigned int i = 1; i < newPixelSpacing.Size(); i++) { if (newPixelSpacing[i] < minSpacing) { minSpacing = newPixelSpacing[i]; } } newPixelSpacing[0] = 0.5 * minSpacing; newPixelSpacing[1] = 0.5 * minSpacing; newPixelSpacing[2] = 0.5 * minSpacing; float pixelSpacing[2]; pixelSpacing[0] = newPixelSpacing[0]; pixelSpacing[1] = newPixelSpacing[1]; // Calculating the size of the sampled slice typename SliceImageType::SizeType size; Vector2D extentInMM; extentInMM[0] = m_CurrentWorldPlaneGeometry->GetExtentInMM(0); extentInMM[1] = m_CurrentWorldPlaneGeometry->GetExtentInMM(1); // The maximum extent is the lenght of the diagonal of the considered plane double maxExtent = sqrt(extentInMM[0] * extentInMM[0] + extentInMM[1] * extentInMM[1]); unsigned int xTranlation = (maxExtent - extentInMM[0]); unsigned int yTranlation = (maxExtent - extentInMM[1]); size[0] = (maxExtent + xTranlation) / newPixelSpacing[0]; size[1] = (maxExtent + yTranlation) / newPixelSpacing[1]; // Creating an ImageRegion Object typename SliceImageType::RegionType region; region.SetSize(size); region.SetIndex(start); // Defining the image`s extent and origin by passing the region to it and allocating memory for it resultSlice->SetRegions(region); resultSlice->SetSpacing(pixelSpacing); resultSlice->Allocate(); /* * Here we create an new geometry so that the transformations are calculated correctly (our resulting slice has a * different bounding box and spacing) * The original current worldgeometry must be cloned because we have to keep the directions of the axis vector which * represents the rotation */ right.Normalize(); bottom.Normalize(); // Here we translate the origin to adapt the new geometry to the previous calculated extent origin[0] -= xTranlation * right[0] + yTranlation * bottom[0]; origin[1] -= xTranlation * right[1] + yTranlation * bottom[1]; origin[2] -= xTranlation * right[2] + yTranlation * bottom[2]; // Putting it together for the new geometry mitk::BaseGeometry::Pointer newSliceGeometryTest = dynamic_cast(m_CurrentWorldPlaneGeometry->Clone().GetPointer()); newSliceGeometryTest->ChangeImageGeometryConsideringOriginOffset(true); // Workaround because of BUG (#6505) newSliceGeometryTest->GetIndexToWorldTransform()->SetMatrix( m_CurrentWorldPlaneGeometry->GetIndexToWorldTransform()->GetMatrix()); // Workaround end newSliceGeometryTest->SetOrigin(origin); ScalarType bounds[6] = {0, static_cast(size[0]), 0, static_cast(size[1]), 0, 1}; newSliceGeometryTest->SetBounds(bounds); newSliceGeometryTest->SetSpacing(newPixelSpacing); newSliceGeometryTest->Modified(); // Workaround because of BUG (#6505) itk::MatrixOffsetTransformBase::MatrixType tempTransform = newSliceGeometryTest->GetIndexToWorldTransform()->GetMatrix(); // Workaround end /* * Now we iterate over the recently created slice. * For each slice - pixel we check whether there is an according * pixel in the input - image which can be set in the slice. * In this way a slice is sampled out of the input - image regrading to the given PlaneGeometry */ Point3D currentSliceIndexPointIn2D; Point3D currentImageWorldPointIn3D; typename InputImageType::IndexType inputIndex; SliceIterator sliceIterator(resultSlice, resultSlice->GetLargestPossibleRegion()); sliceIterator.GoToBegin(); while (!sliceIterator.IsAtEnd()) { /* * Here we add 0.5 to to assure that the indices are correctly transformed. * (Because of the 0.5er Bug) */ currentSliceIndexPointIn2D[0] = sliceIterator.GetIndex()[0] + 0.5; currentSliceIndexPointIn2D[1] = sliceIterator.GetIndex()[1] + 0.5; currentSliceIndexPointIn2D[2] = 0; newSliceGeometryTest->IndexToWorld(currentSliceIndexPointIn2D, currentImageWorldPointIn3D); m_ImageGeometry->WorldToIndex(currentImageWorldPointIn3D, inputIndex); if (m_ImageGeometry->IsIndexInside(inputIndex)) { resultSlice->SetPixel(sliceIterator.GetIndex(), inputImage->GetPixel(inputIndex)); } else { resultSlice->SetPixel(sliceIterator.GetIndex(), 0); } ++sliceIterator; } Image::Pointer resultImage = ImageToImageFilter::GetOutput(); GrabItkImageMemory(resultSlice, resultImage, nullptr, false); resultImage->SetClonedGeometry(newSliceGeometryTest); // Workaround because of BUG (#6505) resultImage->GetGeometry()->GetIndexToWorldTransform()->SetMatrix(tempTransform); // Workaround end } ///**TEST** May ba a little bit more efficient but doesn`t already work/ // right.Normalize(); // bottom.Normalize(); // Point3D currentImagePointIn3D = origin /*+ bottom*newPixelSpacing*/; // unsigned int columns ( 0 ); /**ENDE**/ /****TEST***/ // SliceImageType::IndexType index = sliceIterator.GetIndex(); // if ( columns == (extentInPixel[0]) ) //{ // If we are at the end of a row, then we have to go to the beginning of the next row // currentImagePointIn3D = origin; // currentImagePointIn3D += newPixelSpacing[1]*bottom*index[1]; // columns = 0; // m_ImageGeometry->WorldToIndex(currentImagePointIn3D, inputIndex); //} // else //{ //// // if ( columns != 0 ) //{ // currentImagePointIn3D += newPixelSpacing[0]*right; //} // m_ImageGeometry->WorldToIndex(currentImagePointIn3D, inputIndex); //} // if ( m_ImageGeometry->IsIndexInside( inputIndex )) //{ // resultSlice->SetPixel( sliceIterator.GetIndex(), inputImage->GetPixel(inputIndex) ); //} // else if (currentImagePointIn3D == origin) //{ // Point3D temp; // temp[0] = bottom[0]*newPixelSpacing[0]*0.5; // temp[1] = bottom[1]*newPixelSpacing[1]*0.5; // temp[2] = bottom[2]*newPixelSpacing[2]*0.5; // origin[0] += temp[0]; // origin[1] += temp[1]; // origin[2] += temp[2]; // currentImagePointIn3D = origin; // m_ImageGeometry->WorldToIndex(currentImagePointIn3D, inputIndex); // if ( m_ImageGeometry->IsIndexInside( inputIndex )) //{ // resultSlice->SetPixel( sliceIterator.GetIndex(), inputImage->GetPixel(inputIndex) ); //} //} /****TEST ENDE****/ diff --git a/Modules/ImageStatistics/mitkPlanarFigureMaskGenerator.h b/Modules/ImageStatistics/mitkPlanarFigureMaskGenerator.h index 36911bfc06..3598e7d533 100644 --- a/Modules/ImageStatistics/mitkPlanarFigureMaskGenerator.h +++ b/Modules/ImageStatistics/mitkPlanarFigureMaskGenerator.h @@ -1,135 +1,133 @@ #ifndef MITKPLANARFIGUREMASKGENERATOR #define MITKPLANARFIGUREMASKGENERATOR #include -#include -#include #include -#include -#include #include #include -#include +#include +#include +#include #include +#include +#include namespace mitk { -/** -* \class PlanarFigureMaskGenerator -* \brief Derived from MaskGenerator. This class is used to convert a mitk::PlanarFigure into a binary image mask -*/ -class MITKIMAGESTATISTICS_EXPORT PlanarFigureMaskGenerator: public MaskGenerator - { - public: + /** + * \class PlanarFigureMaskGenerator + * \brief Derived from MaskGenerator. This class is used to convert a mitk::PlanarFigure into a binary image mask + */ + class MITKIMAGESTATISTICS_EXPORT PlanarFigureMaskGenerator : public MaskGenerator + { + public: /** Standard Self typedef */ - typedef PlanarFigureMaskGenerator Self; - typedef MaskGenerator Superclass; - typedef itk::SmartPointer< Self > Pointer; - typedef itk::SmartPointer< const Self > ConstPointer; + typedef PlanarFigureMaskGenerator Self; + typedef MaskGenerator Superclass; + typedef itk::SmartPointer Pointer; + typedef itk::SmartPointer ConstPointer; /** Method for creation through the object factory. */ itkNewMacro(Self) - /** Runtime information support. */ - itkTypeMacro(PlanarFigureMaskGenerator, MaskGenerator) + /** Runtime information support. */ + itkTypeMacro(PlanarFigureMaskGenerator, MaskGenerator) - /** - * @brief GetMask Computes and returns the mask - * @return mitk::Image::Pointer of the generated mask - */ - mitk::Image::Pointer GetMask() override; + /** + * @brief GetMask Computes and returns the mask + * @return mitk::Image::Pointer of the generated mask + */ + mitk::Image::Pointer GetMask() override; void SetPlanarFigure(mitk::PlanarFigure::Pointer planarFigure); mitk::Image::Pointer GetReferenceImage() override; /** * @brief SetTimeStep is used to set the time step for which the mask is to be generated * @param timeStep */ void SetTimeStep(unsigned int timeStep) override; itkGetConstMacro(PlanarFigureAxis, unsigned int); itkGetConstMacro(PlanarFigureSlice, unsigned int); - protected: - PlanarFigureMaskGenerator():Superclass(){ - m_InternalMaskUpdateTime = 0; - m_InternalMask = mitk::Image::New(); - m_ReferenceImage = nullptr; - m_PlanarFigureAxis = 0; + protected: + PlanarFigureMaskGenerator() + : Superclass(), + m_ReferenceImage(nullptr), + m_PlanarFigureAxis(0), + m_InternalMaskUpdateTime(0), + m_PlanarFigureSlice(0) + { + m_InternalMask = mitk::Image::New(); } - - private: + private: void CalculateMask(); - template < typename TPixel, unsigned int VImageDimension > - void InternalCalculateMaskFromPlanarFigure( - const itk::Image< TPixel, VImageDimension > *image, unsigned int axis ); + template + void InternalCalculateMaskFromPlanarFigure(const itk::Image *image, unsigned int axis); - template < typename TPixel, unsigned int VImageDimension > - void InternalCalculateMaskFromOpenPlanarFigure( - const itk::Image< TPixel, VImageDimension > *image, unsigned int axis ); + template + void InternalCalculateMaskFromOpenPlanarFigure(const itk::Image *image, unsigned int axis); - mitk::Image::Pointer extract2DImageSlice(unsigned int axis, unsigned int slice); + mitk::Image::Pointer extract2DImageSlice(unsigned int axis, unsigned int slice); - bool GetPrincipalAxis(const BaseGeometry *geometry, Vector3D vector, - unsigned int &axis ); + bool GetPrincipalAxis(const BaseGeometry *geometry, Vector3D vector, unsigned int &axis); /** Connection from ITK to VTK */ template void ConnectPipelines(ITK_Exporter exporter, vtkSmartPointer importer) { importer->SetUpdateInformationCallback(exporter->GetUpdateInformationCallback()); importer->SetPipelineModifiedCallback(exporter->GetPipelineModifiedCallback()); importer->SetWholeExtentCallback(exporter->GetWholeExtentCallback()); importer->SetSpacingCallback(exporter->GetSpacingCallback()); importer->SetOriginCallback(exporter->GetOriginCallback()); importer->SetScalarTypeCallback(exporter->GetScalarTypeCallback()); importer->SetNumberOfComponentsCallback(exporter->GetNumberOfComponentsCallback()); importer->SetPropagateUpdateExtentCallback(exporter->GetPropagateUpdateExtentCallback()); importer->SetUpdateDataCallback(exporter->GetUpdateDataCallback()); importer->SetDataExtentCallback(exporter->GetDataExtentCallback()); importer->SetBufferPointerCallback(exporter->GetBufferPointerCallback()); importer->SetCallbackUserData(exporter->GetCallbackUserData()); } /** Connection from VTK to ITK */ template void ConnectPipelines(vtkSmartPointer exporter, ITK_Importer importer) { importer->SetUpdateInformationCallback(exporter->GetUpdateInformationCallback()); importer->SetPipelineModifiedCallback(exporter->GetPipelineModifiedCallback()); importer->SetWholeExtentCallback(exporter->GetWholeExtentCallback()); importer->SetSpacingCallback(exporter->GetSpacingCallback()); importer->SetOriginCallback(exporter->GetOriginCallback()); importer->SetScalarTypeCallback(exporter->GetScalarTypeCallback()); importer->SetNumberOfComponentsCallback(exporter->GetNumberOfComponentsCallback()); importer->SetPropagateUpdateExtentCallback(exporter->GetPropagateUpdateExtentCallback()); importer->SetUpdateDataCallback(exporter->GetUpdateDataCallback()); importer->SetDataExtentCallback(exporter->GetDataExtentCallback()); importer->SetBufferPointerCallback(exporter->GetBufferPointerCallback()); importer->SetCallbackUserData(exporter->GetCallbackUserData()); } bool IsUpdateRequired() const; mitk::PlanarFigure::Pointer m_PlanarFigure; itk::Image::Pointer m_InternalITKImageMask2D; mitk::Image::Pointer m_InternalTimeSliceImage; mitk::Image::Pointer m_ReferenceImage; unsigned int m_PlanarFigureAxis; unsigned long m_InternalMaskUpdateTime; unsigned int m_PlanarFigureSlice; - }; -} + }; +} // namespace mitk #endif // MITKPLANARFIGUREMASKGENERATOR - diff --git a/Modules/MapperExt/src/vtkMitkOpenGLVolumeTextureMapper3D.cpp b/Modules/MapperExt/src/vtkMitkOpenGLVolumeTextureMapper3D.cpp index 46a79cc79b..f3ea2ef526 100644 --- a/Modules/MapperExt/src/vtkMitkOpenGLVolumeTextureMapper3D.cpp +++ b/Modules/MapperExt/src/vtkMitkOpenGLVolumeTextureMapper3D.cpp @@ -1,2443 +1,2447 @@ /*=================================================================== 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. ===================================================================*/ #ifdef _OPENMP #include #endif #include "vtkWindows.h" #include "mitkCommon.h" #include "vtkMitkOpenGLVolumeTextureMapper3D.h" #define GPU_INFO MITK_INFO("mapper.vr") #define GPU_WARN MITK_WARN("mapper.vr") #include "vtkCamera.h" #include "vtkDataArray.h" #include "vtkImageData.h" #include "vtkLight.h" #include "vtkLightCollection.h" #include "vtkMath.h" #include "vtkMatrix4x4.h" #include "vtkObjectFactory.h" #include "vtkOpenGLExtensionManager.h" #include "vtkPlane.h" #include "vtkPlaneCollection.h" #include "vtkPointData.h" #include "vtkRenderWindow.h" #include "vtkRenderer.h" #include "vtkTimerLog.h" #include "vtkTransform.h" #include "vtkVolumeProperty.h" #include "vtkgl.h" #include "vtkOpenGLRenderWindow.h" #define myGL_COMPRESSED_RGB_S3TC_DXT1_EXT 0x83F0 #define myGL_COMPRESSED_LUMINANCE_ALPHA_LATC2_EXT 0x8C72 #define myGL_COMPRESSED_RGBA_S3TC_DXT5_EXT 0x83F3 const char *vtkMitkVolumeTextureMapper3D_FourDependentShadeFP = "!!ARBfp1.0\n" //# We need some temporary variables "TEMP index, normal, finalColor;\n" "TEMP temp,temp1, temp2, temp3,temp4; \n" "TEMP sampleColor;\n" "TEMP ndotl, ndoth, ndotv; \n" "TEMP lightInfo, lightResult;\n" //# We are going to use the first //# texture coordinate "ATTRIB tex0 = fragment.texcoord[0];\n" //# This is the lighting information "PARAM lightDirection = program.local[0];\n" "PARAM halfwayVector = program.local[1];\n" "PARAM coefficient = program.local[2];\n" "PARAM lightDiffColor = program.local[3]; \n" "PARAM lightSpecColor = program.local[4]; \n" "PARAM viewVector = program.local[5];\n" "PARAM constants = program.local[6];\n" //# This is our output color "OUTPUT out = result.color;\n" //# Look up the gradient direction //# in the third volume "TEX temp2, tex0, texture[0], 3D;\n" //# This normal is stored 0 to 1, change to -1 to 1 //# by multiplying by 2.0 then adding -1.0. "MAD normal, temp2, constants.x, constants.y;\n" "DP3 temp4, normal, normal;\n" "RSQ temp, temp4.x;\n" "MUL normal, normal, temp;\n" //"RCP temp4,temp.x;\n" //"MUL temp2.w,temp2.w,temp4.x;\n" //"MUL_SAT temp2.w,temp2.w,6.0;\n" "TEX sampleColor, tex0, texture[1], 3D;\n" //# Take the dot product of the light //# direction and the normal "DP3 ndotl, normal, lightDirection;\n" //# Take the dot product of the halfway //# vector and the normal "DP3 ndoth, normal, halfwayVector;\n" "DP3 ndotv, normal, viewVector;\n" //# flip if necessary for two sided lighting "MUL temp3, ndotl, constants.y; \n" "CMP ndotl, ndotv, ndotl, temp3;\n" "MUL temp3, ndoth, constants.y; \n" "CMP ndoth, ndotv, ndoth, temp3;\n" //# put the pieces together for a LIT operation "MOV lightInfo.x, ndotl.x; \n" "MOV lightInfo.y, ndoth.x; \n" "MOV lightInfo.w, coefficient.w; \n" //# compute the lighting "LIT lightResult, lightInfo;\n" //# COLOR FIX "MUL lightResult, lightResult, 4.0;\n" //# This is the ambient contribution "MUL finalColor, coefficient.x, sampleColor;\n" //# This is the diffuse contribution "MUL temp3, lightDiffColor, sampleColor;\n" "MUL temp3, temp3, lightResult.y;\n" "ADD finalColor, finalColor, temp3;\n" //# This is th specular contribution "MUL temp3, lightSpecColor, lightResult.z; \n" //# Add specular into result so far, and replace //# with the original alpha. "ADD out, finalColor, temp3;\n" "MOV out.w, temp2.w;\n" "END\n"; const char *vtkMitkVolumeTextureMapper3D_OneComponentShadeFP = "!!ARBfp1.0\n" //# This is the fragment program for one //# component data with shading //# We need some temporary variables "TEMP index, normal, finalColor;\n" "TEMP temp,temp1, temp2, temp3,temp4; \n" "TEMP sampleColor;\n" "TEMP ndotl, ndoth, ndotv; \n" "TEMP lightInfo, lightResult;\n" //# We are going to use the first //# texture coordinate "ATTRIB tex0 = fragment.texcoord[0];\n" //# This is the lighting information "PARAM lightDirection = program.local[0];\n" "PARAM halfwayVector = program.local[1];\n" "PARAM coefficient = program.local[2];\n" "PARAM lightDiffColor = program.local[3]; \n" "PARAM lightSpecColor = program.local[4]; \n" "PARAM viewVector = program.local[5];\n" "PARAM constants = program.local[6];\n" //# This is our output color "OUTPUT out = result.color;\n" //# Look up the gradient direction //# in the third volume "TEX temp2, tex0, texture[0], 3D;\n" // Gradient Compution //# Look up the scalar value / gradient //# magnitude in the first volume //"TEX temp1, tex0, texture[0], 3D;\n" /* "ADD temp3,tex0,{-0.005,0,0};\n" "TEX temp2,temp3, texture[0], 3D;\n" //"ADD temp3,tex0,{ 0.005,0,0};\n" //"TEX temp1,temp3, texture[0], 3D;\n" "SUB normal.x,temp2.y,temp1.y;\n" "ADD temp3,tex0,{0,-0.005,0};\n" "TEX temp2,temp3, texture[0], 3D;\n" //"ADD temp3,tex0,{0, 0.005,0};\n" //"TEX temp1,temp3, texture[0], 3D;\n" "SUB normal.y,temp2.y,temp1.y;\n" "ADD temp3,tex0,{0,0,-0.005};\n" "TEX temp2,temp3, texture[0], 3D;\n" //"ADD temp3,tex0,{0,0, 0.005};\n" //"TEX temp1,temp3, texture[0], 3D;\n" "SUB normal.z,temp2.y,temp1.y;\n" */ //"MOV normal,{1,1,1};\n" "MOV index.x,temp2.a;\n" //# This normal is stored 0 to 1, change to -1 to 1 //# by multiplying by 2.0 then adding -1.0. "MAD normal, temp2, constants.x, constants.y;\n" //# Swizzle this to use (a,r) as texture //# coordinates //"SWZ index, temp1, a, r, 1, 1;\n" //# Use this coordinate to look up a //# final color in the third texture //# (this is a 2D texture) "DP3 temp4, normal, normal;\n" "RSQ temp, temp4.x;\n" "RCP temp4,temp.x;\n" "MUL normal, normal, temp;\n" "MOV index.y, temp4.x;\n" "TEX sampleColor, index, texture[1], 2D;\n" //"MUL sampleColor.w,sampleColor.w,temp4.x;\n" //# Take the dot product of the light //# direction and the normal "DP3 ndotl, normal, lightDirection;\n" //# Take the dot product of the halfway //# vector and the normal "DP3 ndoth, normal, halfwayVector;\n" "DP3 ndotv, normal, viewVector;\n" //# flip if necessary for two sided lighting "MUL temp3, ndotl, constants.y; \n" "CMP ndotl, ndotv, ndotl, temp3;\n" "MUL temp3, ndoth, constants.y; \n" "CMP ndoth, ndotv, ndoth, temp3;\n" //# put the pieces together for a LIT operation "MOV lightInfo.x, ndotl.x; \n" "MOV lightInfo.y, ndoth.x; \n" "MOV lightInfo.w, coefficient.w; \n" //# compute the lighting "LIT lightResult, lightInfo;\n" //# COLOR FIX "MUL lightResult, lightResult, 4.0;\n" //# This is the ambient contribution "MUL finalColor, coefficient.x, sampleColor;\n" //# This is the diffuse contribution "MUL temp3, lightDiffColor, sampleColor;\n" "MUL temp3, temp3, lightResult.y;\n" "ADD finalColor, finalColor, temp3;\n" //# This is th specular contribution "MUL temp3, lightSpecColor, lightResult.z; \n" //# Add specular into result so far, and replace //# with the original alpha. "ADD out, finalColor, temp3;\n" "MOV out.w, sampleColor.w;\n" "END\n"; //#ifndef VTK_IMPLEMENT_MESA_CXX vtkStandardNewMacro(vtkMitkOpenGLVolumeTextureMapper3D); //#endif vtkMitkOpenGLVolumeTextureMapper3D::vtkMitkOpenGLVolumeTextureMapper3D() { // GPU_INFO << "vtkMitkOpenGLVolumeTextureMapper3D"; this->Initialized = 0; this->Volume1Index = 0; this->Volume2Index = 0; this->Volume3Index = 0; this->ColorLookupIndex = 0; this->AlphaLookupIndex = 0; this->RenderWindow = nullptr; this->SupportsCompressedTexture = false; prgOneComponentShade = 0; prgRGBAShade = 0; } vtkMitkOpenGLVolumeTextureMapper3D::~vtkMitkOpenGLVolumeTextureMapper3D() { // GPU_INFO << "~vtkMitkOpenGLVolumeTextureMapper3D"; if (prgOneComponentShade) vtkgl::DeleteProgramsARB(1, &prgOneComponentShade); if (prgRGBAShade) vtkgl::DeleteProgramsARB(1, &prgRGBAShade); } // Release the graphics resources used by this texture. void vtkMitkOpenGLVolumeTextureMapper3D::ReleaseGraphicsResources(vtkWindow *renWin) { // GPU_INFO << "ReleaseGraphicsResources"; if ((this->Volume1Index || this->Volume2Index || this->Volume3Index || this->ColorLookupIndex) && renWin) { static_cast(renWin)->MakeCurrent(); #ifdef GL_VERSION_1_1 // free any textures this->DeleteTextureIndex(&this->Volume1Index); this->DeleteTextureIndex(&this->Volume2Index); this->DeleteTextureIndex(&this->Volume3Index); this->DeleteTextureIndex(&this->ColorLookupIndex); this->DeleteTextureIndex(&this->AlphaLookupIndex); #endif } this->Volume1Index = 0; this->Volume2Index = 0; this->Volume3Index = 0; this->ColorLookupIndex = 0; this->RenderWindow = nullptr; this->SupportsCompressedTexture = false; this->SupportsNonPowerOfTwoTextures = false; this->Modified(); } // Release the graphics resources used by this texture. void vtkMitkOpenGLVolumeTextureMapper3D::ReleaseGraphicsResources(mitk::BaseRenderer *renderer) { // GPU_INFO << "ReleaseGraphicsResources"; vtkWindow *renWin = renderer->GetVtkRenderer()->GetRenderWindow(); if ((this->Volume1Index || this->Volume2Index || this->Volume3Index || this->ColorLookupIndex) && renWin) { static_cast(renWin)->MakeCurrent(); #ifdef GL_VERSION_1_1 // free any textures this->DeleteTextureIndex(&this->Volume1Index); this->DeleteTextureIndex(&this->Volume2Index); this->DeleteTextureIndex(&this->Volume3Index); this->DeleteTextureIndex(&this->ColorLookupIndex); this->DeleteTextureIndex(&this->AlphaLookupIndex); #endif } this->Volume1Index = 0; this->Volume2Index = 0; this->Volume3Index = 0; this->ColorLookupIndex = 0; this->RenderWindow = nullptr; this->SupportsCompressedTexture = false; this->SupportsNonPowerOfTwoTextures = false; this->Modified(); } void vtkMitkOpenGLVolumeTextureMapper3D::Render(vtkRenderer *ren, vtkVolume *vol) { // GPU_INFO << "Render"; ren->GetRenderWindow()->MakeCurrent(); if (!this->Initialized) { // this->Initialize(); this->Initialize(ren); } if (!this->RenderPossible) { vtkErrorMacro("required extensions not supported"); return; } vtkMatrix4x4 *matrix = vtkMatrix4x4::New(); vtkPlaneCollection *clipPlanes; vtkPlane *plane; int numClipPlanes = 0; double planeEquation[4]; // build transformation vol->GetMatrix(matrix); matrix->Transpose(); glPushAttrib(GL_ENABLE_BIT | GL_COLOR_BUFFER_BIT | GL_STENCIL_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_POLYGON_BIT | GL_TEXTURE_BIT); int i; // Use the OpenGL clip planes clipPlanes = this->ClippingPlanes; if (clipPlanes) { numClipPlanes = clipPlanes->GetNumberOfItems(); if (numClipPlanes > 6) { vtkErrorMacro(<< "OpenGL guarantees only 6 additional clipping planes"); } for (i = 0; i < numClipPlanes; i++) { glEnable(static_cast(GL_CLIP_PLANE0 + i)); plane = static_cast(clipPlanes->GetItemAsObject(i)); planeEquation[0] = plane->GetNormal()[0]; planeEquation[1] = plane->GetNormal()[1]; planeEquation[2] = plane->GetNormal()[2]; planeEquation[3] = -(planeEquation[0] * plane->GetOrigin()[0] + planeEquation[1] * plane->GetOrigin()[1] + planeEquation[2] * plane->GetOrigin()[2]); glClipPlane(static_cast(GL_CLIP_PLANE0 + i), planeEquation); } } // insert model transformation glMatrixMode(GL_MODELVIEW); glPushMatrix(); glMultMatrixd(matrix->Element[0]); glColor4f(1.0, 1.0, 1.0, 1.0); // Turn lighting off - the polygon textures already have illumination glDisable(GL_LIGHTING); // vtkGraphicErrorMacro(ren->GetRenderWindow(),"Before actual render method"); this->RenderFP(ren, vol); // pop transformation matrix glMatrixMode(GL_MODELVIEW); glPopMatrix(); matrix->Delete(); glPopAttrib(); } void vtkMitkOpenGLVolumeTextureMapper3D::RenderFP(vtkRenderer *ren, vtkVolume *vol) { // GPU_INFO << "RenderFP"; /* glAlphaFunc (GL_GREATER, static_cast(1.0/255.0)); glEnable (GL_ALPHA_TEST); */ glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); int components = this->GetInput()->GetNumberOfScalarComponents(); switch (components) { case 1: this->RenderOneIndependentShadeFP(ren, vol); break; case 4: this->RenderRGBAShadeFP(ren, vol); break; } vtkgl::ActiveTexture(vtkgl::TEXTURE2); glDisable(GL_TEXTURE_2D); glDisable(vtkgl::TEXTURE_3D); vtkgl::ActiveTexture(vtkgl::TEXTURE1); glDisable(GL_TEXTURE_2D); glDisable(vtkgl::TEXTURE_3D); vtkgl::ActiveTexture(vtkgl::TEXTURE0); glDisable(GL_TEXTURE_2D); glDisable(vtkgl::TEXTURE_3D); glDisable(GL_BLEND); } void vtkMitkOpenGLVolumeTextureMapper3D::DeleteTextureIndex(GLuint *index) { // GPU_INFO << "DeleteTextureIndex"; if (glIsTexture(*index)) { GLuint tempIndex; tempIndex = *index; glDeleteTextures(1, &tempIndex); *index = 0; } } void vtkMitkOpenGLVolumeTextureMapper3D::CreateTextureIndex(GLuint *index) { // GPU_INFO << "CreateTextureIndex"; GLuint tempIndex = 0; glGenTextures(1, &tempIndex); *index = static_cast(tempIndex); } void vtkMitkOpenGLVolumeTextureMapper3D::RenderPolygons(vtkRenderer *ren, vtkVolume *vol, int stages[4]) { // GPU_INFO << "RenderPolygons"; vtkRenderWindow *renWin = ren->GetRenderWindow(); if (renWin->CheckAbortStatus()) { return; } double bounds[27][6]; float distance2[27]; int numIterations; int i, j, k; // No cropping case - render the whole thing if (!this->Cropping) { // Use the input data bounds - we'll take care of the volume's // matrix during rendering this->GetInput()->GetBounds(bounds[0]); numIterations = 1; } // Simple cropping case - render the subvolume else if (this->CroppingRegionFlags == 0x2000) { this->GetCroppingRegionPlanes(bounds[0]); numIterations = 1; } // Complex cropping case - render each region in back-to-front order else { // Get the camera position double camPos[4]; ren->GetActiveCamera()->GetPosition(camPos); double volBounds[6]; this->GetInput()->GetBounds(volBounds); // Pass camera through inverse volume matrix // so that we are in the same coordinate system vtkMatrix4x4 *volMatrix = vtkMatrix4x4::New(); vol->GetMatrix(volMatrix); camPos[3] = 1.0; volMatrix->Invert(); volMatrix->MultiplyPoint(camPos, camPos); volMatrix->Delete(); if (camPos[3]) { camPos[0] /= camPos[3]; camPos[1] /= camPos[3]; camPos[2] /= camPos[3]; } // These are the region limits for x (first four), y (next four) and // z (last four). The first region limit is the lower bound for // that axis, the next two are the region planes along that axis, and // the final one in the upper bound for that axis. float limit[12]; for (i = 0; i < 3; i++) { limit[i * 4] = volBounds[i * 2]; limit[i * 4 + 1] = this->CroppingRegionPlanes[i * 2]; limit[i * 4 + 2] = this->CroppingRegionPlanes[i * 2 + 1]; limit[i * 4 + 3] = volBounds[i * 2 + 1]; } // For each of the 27 possible regions, find out if it is enabled, // and if so, compute the bounds and the distance from the camera // to the center of the region. int numRegions = 0; int region; for (region = 0; region < 27; region++) { int regionFlag = 1 << region; if (this->CroppingRegionFlags & regionFlag) { // what is the coordinate in the 3x3x3 grid int loc[3]; loc[0] = region % 3; loc[1] = (region / 3) % 3; loc[2] = (region / 9) % 3; // compute the bounds and center float center[3]; for (i = 0; i < 3; i++) { bounds[numRegions][i * 2] = limit[4 * i + loc[i]]; bounds[numRegions][i * 2 + 1] = limit[4 * i + loc[i] + 1]; center[i] = (bounds[numRegions][i * 2] + bounds[numRegions][i * 2 + 1]) / 2.0; } // compute the distance squared to the center distance2[numRegions] = (camPos[0] - center[0]) * (camPos[0] - center[0]) + (camPos[1] - center[1]) * (camPos[1] - center[1]) + (camPos[2] - center[2]) * (camPos[2] - center[2]); // we've added one region numRegions++; } } // Do a quick bubble sort on distance for (i = 1; i < numRegions; i++) { for (j = i; j > 0 && distance2[j] > distance2[j - 1]; j--) { float tmpBounds[6]; float tmpDistance2; for (k = 0; k < 6; k++) { tmpBounds[k] = bounds[j][k]; } tmpDistance2 = distance2[j]; for (k = 0; k < 6; k++) { bounds[j][k] = bounds[j - 1][k]; } distance2[j] = distance2[j - 1]; for (k = 0; k < 6; k++) { bounds[j - 1][k] = tmpBounds[k]; } distance2[j - 1] = tmpDistance2; } } numIterations = numRegions; } // loop over all regions we need to render for (int loop = 0; loop < numIterations; loop++) { // Compute the set of polygons for this region // according to the bounds this->ComputePolygons(ren, vol, bounds[loop]); // Loop over the polygons for (i = 0; i < this->NumberOfPolygons; i++) { if (renWin->CheckAbortStatus()) { return; } float *ptr = this->PolygonBuffer + 36 * i; glBegin(GL_TRIANGLE_FAN); for (j = 0; j < 6; j++) { if (ptr[0] < 0.0) { break; } for (k = 0; k < 4; k++) { if (stages[k]) { vtkgl::MultiTexCoord3fv(vtkgl::TEXTURE0 + k, ptr); } } glVertex3fv(ptr + 3); ptr += 6; } glEnd(); } } } // This method moves the scalars from the input volume into volume1 (and // possibly volume2) which are the 3D texture maps used for rendering. // // In the case where our volume is a power of two, the copy is done // directly. If we need to resample, then trilinear interpolation is used. // // A shift/scale is applied to the input scalar value to produce an 8 bit // value for the texture volume. // // When the input data is one component, the scalar value is placed in the // second component of the two component volume1. The first component is // filled in later with the gradient magnitude. // // When the input data is two component non-independent, the first component // of the input data is placed in the first component of volume1, and the // second component of the input data is placed in the third component of // volume1. Volume1 has three components - the second is filled in later with // the gradient magnitude. // // When the input data is four component non-independent, the first three // components of the input data are placed in volume1 (which has three // components), and the fourth component is placed in the second component // of volume2. The first component of volume2 is later filled in with the // gradient magnitude. template class ScalarGradientCompute { T *dataPtr; unsigned char *tmpPtr; unsigned char *tmpPtr2; int sizeX; int sizeY; int sizeZ; int sizeXY; int sizeXm1; int sizeYm1; int sizeZm1; int fullX; int fullY; int fullZ; int fullXY; int currentChunkStart; int currentChunkEnd; int offZ; float offset; float scale; public: ScalarGradientCompute(T *_dataPtr, unsigned char *_tmpPtr, unsigned char *_tmpPtr2, int _sizeX, int _sizeY, int _sizeZ, int _fullX, int _fullY, int _fullZ, float _offset, float _scale) { dataPtr = _dataPtr; tmpPtr = _tmpPtr; tmpPtr2 = _tmpPtr2; sizeX = _sizeX; sizeY = _sizeY; sizeZ = _sizeZ; fullX = _fullX; fullY = _fullY; fullZ = _fullZ; offset = _offset; scale = _scale; sizeXY = sizeX * sizeY; sizeXm1 = sizeX - 1; sizeYm1 = sizeY - 1; sizeZm1 = sizeZ - 1; fullXY = fullX * fullY; + currentChunkStart = 0; + currentChunkEnd = 0; + offZ = 0; } inline float sample(int x, int y, int z) { return float(dataPtr[x + y * sizeX + z * sizeXY]); } inline void fill(int x, int y, int z) { int doff = x + y * fullX + (z - offZ) * fullXY; tmpPtr[doff * 4 + 0] = 0; tmpPtr[doff * 4 + 1] = 0; tmpPtr[doff * 4 + 2] = 0; tmpPtr[doff * 4 + 3] = 0; /* tmpPtr2[doff*3+0]= 0; tmpPtr2[doff*3+1]= 0; tmpPtr2[doff*3+2]= 0; */ } inline int clamp(int x) { if (x < 0) x = 0; else if (x > 255) x = 255; return x; } inline void write(int x, int y, int z, float grayValue, float gx, float gy, float gz) { /* gx /= aspect[0]; gy /= aspect[1]; gz /= aspect[2]; */ // Compute the gradient magnitude int iGrayValue = static_cast((grayValue + offset) * scale + 0.5f); gx *= scale; gy *= scale; gz *= scale; float t = sqrtf(gx * gx + gy * gy + gz * gz); if (t > 0.01f) { if (t < 2.0f) { float fac = 2.0f / t; gx *= fac; gy *= fac; gz *= fac; } else if (t > 255.0f) { float fac = 255.0f / t; gx *= fac; gy *= fac; gz *= fac; } } else { gx = gy = gz = 0.0f; } int nx = static_cast(0.5f * gx + 127.5f); int ny = static_cast(0.5f * gy + 127.5f); int nz = static_cast(0.5f * gz + 127.5f); int doff = x + y * fullX + (z - offZ) * fullXY; // tmpPtr[doff*2+0]= 0; tmpPtr[doff * 4 + 0] = clamp(nx); tmpPtr[doff * 4 + 1] = clamp(ny); tmpPtr[doff * 4 + 2] = clamp(nz); tmpPtr[doff * 4 + 3] = clamp(iGrayValue); /* if( z == fullZ/2 ) if( y == fullY/2 ) MITK_INFO << x << " " << y << " " << z << " : " << iGrayValue << " : " << iGradient; */ } inline void compute(int x, int y, int z) { float grayValue = sample(x, y, z); float gx, gy, gz; gx = sample(x + 1, y, z) - sample(x - 1, y, z); gy = sample(x, y + 1, z) - sample(x, y - 1, z); gz = sample(x, y, z + 1) - sample(x, y, z - 1); write(x, y, z, grayValue, gx, gy, gz); } inline void computeClamp(int x, int y, int z) { float grayValue = sample(x, y, z); float gx, gy, gz; if (x == 0) gx = 2.0f * (sample(x + 1, y, z) - grayValue); else if (x == sizeXm1) gx = 2.0f * (grayValue - sample(x - 1, y, z)); else gx = sample(x + 1, y, z) - sample(x - 1, y, z); if (y == 0) gy = 2.0f * (sample(x, y + 1, z) - grayValue); else if (y == sizeYm1) gy = 2.0f * (grayValue - sample(x, y - 1, z)); else gy = sample(x, y + 1, z) - sample(x, y - 1, z); if (z == 0) gz = 2.0f * (sample(x, y, z + 1) - grayValue); else if (z == sizeZm1) gz = 2.0f * (grayValue - sample(x, y, z - 1)); else gz = sample(x, y, z + 1) - sample(x, y, z - 1); write(x, y, z, grayValue, gx, gy, gz); } inline void compute1D(int y, int z) { int x; x = 0; computeClamp(x, y, z); x++; while (x < sizeX - 1) { compute(x, y, z); x++; } if (x < sizeX) { computeClamp(x, y, z); x++; } while (x < fullX) { fill(x, y, z); x++; } } inline void fill1D(int y, int z) { int x; x = 0; while (x < fullX) { fill(x, y, z); x++; } } inline void computeClamp1D(int y, int z) { int x; x = 0; while (x < sizeX) { computeClamp(x, y, z); x++; } while (x < fullX) { fill(x, y, z); x++; } } inline void computeClamp2D(int z) { int y; y = 0; while (y < sizeY) { computeClamp1D(y, z); y++; } while (y < fullY) { fill1D(y, z); y++; } } inline void compute2D(int z) { int y; y = 0; computeClamp1D(y, z); y++; while (y < sizeY - 1) { compute1D(y, z); y++; } if (y < sizeY) { computeClamp1D(y, z); y++; } while (y < fullY) { fill1D(y, z); y++; } } inline void fill2D(int z) { int y; y = 0; while (y < fullY) { fill1D(y, z); y++; } } inline void fillSlices(int currentChunkStart, int currentChunkEnd) { offZ = currentChunkStart; /* int num = omp_get_num_procs(); MITK_INFO << "omp uses " << num << " processors"; */ #pragma omp parallel for for (int z = currentChunkStart; z <= currentChunkEnd; z++) { if (z == 0 || z == sizeZ - 1) computeClamp2D(z); else if (z >= sizeZ) fill2D(z); else compute2D(z); } } }; template void vtkVolumeTextureMapper3DComputeScalars( T *dataPtr, vtkMitkVolumeTextureMapper3D *me, float offset, float scale, GLuint volume1, GLuint /*volume2*/) { // T *inPtr; // unsigned char *outPtr, *outPtr2; // int i, j, k; // int idx; int inputDimensions[3]; double inputSpacing[3]; vtkImageData *input = me->GetInput(); input->GetDimensions(inputDimensions); input->GetSpacing(inputSpacing); int outputDimensions[3]; float outputSpacing[3]; me->GetVolumeDimensions(outputDimensions); me->GetVolumeSpacing(outputSpacing); // int components = input->GetNumberOfScalarComponents(); // double wx, wy, wz; // double fx, fy, fz; // int x, y, z; // double sampleRate[3]; // sampleRate[0] = outputSpacing[0] / static_cast(inputSpacing[0]); // sampleRate[1] = outputSpacing[1] / static_cast(inputSpacing[1]); // sampleRate[2] = outputSpacing[2] / static_cast(inputSpacing[2]); int fullX = outputDimensions[0]; int fullY = outputDimensions[1]; int fullZ = outputDimensions[2]; int sizeX = inputDimensions[0]; int sizeY = inputDimensions[1]; int sizeZ = inputDimensions[2]; int chunkSize = 64; if (fullZ < chunkSize) chunkSize = fullZ; int numChunks = (fullZ + (chunkSize - 1)) / chunkSize; // inPtr = dataPtr; unsigned char *tmpPtr = new unsigned char[fullX * fullY * chunkSize * 4]; unsigned char *tmpPtr2 = 0; // new unsigned char[fullX*fullY*chunkSize*3]; // For each Chunk { ScalarGradientCompute sgc(dataPtr, tmpPtr, tmpPtr2, sizeX, sizeY, sizeZ, fullX, fullY, fullZ, offset, scale); int currentChunk = 0; while (currentChunk < numChunks) { int currentChunkStart = currentChunk * chunkSize; int currentChunkEnd = currentChunkStart + chunkSize - 1; if (currentChunkEnd > (fullZ - 1)) currentChunkEnd = (fullZ - 1); int currentChunkSize = currentChunkEnd - currentChunkStart + 1; sgc.fillSlices(currentChunkStart, currentChunkEnd); glBindTexture(vtkgl::TEXTURE_3D, volume1); vtkgl::TexSubImage3D(vtkgl::TEXTURE_3D, 0, 0, 0, currentChunkStart, fullX, fullY, currentChunkSize, GL_RGBA, GL_UNSIGNED_BYTE, tmpPtr); currentChunk++; } } delete[] tmpPtr; } class RGBACompute { unsigned char *dataPtr; unsigned char *tmpPtr; unsigned char *tmpPtr2; int sizeX; int sizeY; int sizeZ; int sizeXY; int sizeXm1; int sizeYm1; int sizeZm1; int fullX; int fullY; int fullZ; int fullXY; // int currentChunkStart; // int currentChunkEnd; int offZ; public: RGBACompute(unsigned char *_dataPtr, unsigned char *_tmpPtr, unsigned char *_tmpPtr2, int _sizeX, int _sizeY, int _sizeZ, int _fullX, int _fullY, int _fullZ) { dataPtr = _dataPtr; tmpPtr = _tmpPtr; tmpPtr2 = _tmpPtr2; sizeX = _sizeX; sizeY = _sizeY; sizeZ = _sizeZ; fullX = _fullX; fullY = _fullY; fullZ = _fullZ; sizeXY = sizeX * sizeY; sizeXm1 = sizeX - 1; sizeYm1 = sizeY - 1; sizeZm1 = sizeZ - 1; fullXY = fullX * fullY; + offZ = 0; } inline int sample(int x, int y, int z) { return dataPtr[(x + y * sizeX + z * sizeXY) * 4 + 3]; } inline void fill(int x, int y, int z) { int doff = x + y * fullX + (z - offZ) * fullXY; tmpPtr[doff * 4 + 0] = 0; tmpPtr[doff * 4 + 1] = 0; tmpPtr[doff * 4 + 2] = 0; tmpPtr[doff * 4 + 3] = 0; tmpPtr2[doff * 3 + 0] = 0; tmpPtr2[doff * 3 + 1] = 0; tmpPtr2[doff * 3 + 2] = 0; } inline int clamp(int x) { if (x < 0) x = 0; else if (x > 255) x = 255; return x; } inline void write(int x, int y, int z, int iGrayValue, int gx, int gy, int gz) { /* gx /= aspect[0]; gy /= aspect[1]; gz /= aspect[2]; */ int nx = static_cast(0.5f * gx + 127.5f); int ny = static_cast(0.5f * gy + 127.5f); int nz = static_cast(0.5f * gz + 127.5f); int doff = x + y * fullX + (z - offZ) * fullXY; // tmpPtr[doff*2+0]= 0; tmpPtr[doff * 4 + 0] = clamp(nx); tmpPtr[doff * 4 + 1] = clamp(ny); tmpPtr[doff * 4 + 2] = clamp(nz); tmpPtr[doff * 4 + 3] = clamp(iGrayValue); int soff = x + y * sizeX + z * sizeXY; tmpPtr2[doff * 3 + 0] = dataPtr[soff * 4 + 0]; tmpPtr2[doff * 3 + 1] = dataPtr[soff * 4 + 1]; tmpPtr2[doff * 3 + 2] = dataPtr[soff * 4 + 2]; /* if( z == fullZ/2 ) if( y == fullY/2 ) MITK_INFO << x << " " << y << " " << z << " : " << iGrayValue << " : " << iGradient; */ } inline void compute(int x, int y, int z) { int grayValue = sample(x, y, z); int gx, gy, gz; gx = sample(x + 1, y, z) - sample(x - 1, y, z); gy = sample(x, y + 1, z) - sample(x, y - 1, z); gz = sample(x, y, z + 1) - sample(x, y, z - 1); write(x, y, z, grayValue, gx, gy, gz); } inline void computeClamp(int x, int y, int z) { int grayValue = sample(x, y, z); int gx, gy, gz; if (x == 0) gx = 2 * (sample(x + 1, y, z) - grayValue); else if (x == sizeXm1) gx = 2 * (grayValue - sample(x - 1, y, z)); else gx = sample(x + 1, y, z) - sample(x - 1, y, z); if (y == 0) gy = 2 * (sample(x, y + 1, z) - grayValue); else if (y == sizeYm1) gy = 2 * (grayValue - sample(x, y - 1, z)); else gy = sample(x, y + 1, z) - sample(x, y - 1, z); if (z == 0) gz = 2 * (sample(x, y, z + 1) - grayValue); else if (z == sizeZm1) gz = 2 * (grayValue - sample(x, y, z - 1)); else gz = sample(x, y, z + 1) - sample(x, y, z - 1); write(x, y, z, grayValue, gx, gy, gz); } inline void compute1D(int y, int z) { int x = 0; computeClamp(x, y, z); x++; while (x < sizeX - 1) { compute(x, y, z); x++; } if (x < sizeX) { computeClamp(x, y, z); x++; } while (x < fullX) { fill(x, y, z); x++; } } inline void fill1D(int y, int z) { int x = 0; while (x < fullX) { fill(x, y, z); x++; } } inline void computeClamp1D(int y, int z) { int x = 0; while (x < sizeX) { computeClamp(x, y, z); x++; } while (x < fullX) { fill(x, y, z); x++; } } inline void computeClamp2D(int z) { int y = 0; while (y < sizeY) { computeClamp1D(y, z); y++; } while (y < fullY) { fill1D(y, z); y++; } } inline void compute2D(int z) { int y = 0; computeClamp1D(y, z); y++; while (y < sizeY - 1) { compute1D(y, z); y++; } if (y < sizeY) { computeClamp1D(y, z); y++; } while (y < fullY) { fill1D(y, z); y++; } } inline void fill2D(int z) { int y = 0; while (y < fullY) { fill1D(y, z); y++; } } inline void fillSlices(int currentChunkStart, int currentChunkEnd) { offZ = currentChunkStart; #pragma omp parallel for for (int z = currentChunkStart; z <= currentChunkEnd; z++) { if (z == 0 || z == sizeZ - 1) computeClamp2D(z); else if (z >= sizeZ) fill2D(z); else compute2D(z); } } }; void vtkVolumeTextureMapper3DComputeRGBA(unsigned char *dataPtr, vtkMitkVolumeTextureMapper3D *me, GLuint volume1, GLuint volume2) { // unsigned char *inPtr; // unsigned char *outPtr, *outPtr2; // int i, j, k; // int idx; int inputDimensions[3]; double inputSpacing[3]; vtkImageData *input = me->GetInput(); input->GetDimensions(inputDimensions); input->GetSpacing(inputSpacing); int outputDimensions[3]; float outputSpacing[3]; me->GetVolumeDimensions(outputDimensions); me->GetVolumeSpacing(outputSpacing); int components = input->GetNumberOfScalarComponents(); MITK_INFO << "components are " << components; // double wx, wy, wz; // double fx, fy, fz; // int x, y, z; // double sampleRate[3]; // sampleRate[0] = outputSpacing[0] / static_cast(inputSpacing[0]); // sampleRate[1] = outputSpacing[1] / static_cast(inputSpacing[1]); // sampleRate[2] = outputSpacing[2] / static_cast(inputSpacing[2]); int fullX = outputDimensions[0]; int fullY = outputDimensions[1]; int fullZ = outputDimensions[2]; int sizeX = inputDimensions[0]; int sizeY = inputDimensions[1]; int sizeZ = inputDimensions[2]; int chunkSize = 64; if (fullZ < chunkSize) chunkSize = fullZ; int numChunks = (fullZ + (chunkSize - 1)) / chunkSize; // inPtr = dataPtr; unsigned char *tmpPtr = new unsigned char[fullX * fullY * chunkSize * 4]; unsigned char *tmpPtr2 = new unsigned char[fullX * fullY * chunkSize * 3]; // For each Chunk { RGBACompute sgc(dataPtr, tmpPtr, tmpPtr2, sizeX, sizeY, sizeZ, fullX, fullY, fullZ); int currentChunk = 0; while (currentChunk < numChunks) { // MITK_INFO << "processing chunk " << currentChunk; int currentChunkStart = currentChunk * chunkSize; int currentChunkEnd = currentChunkStart + chunkSize - 1; if (currentChunkEnd > (fullZ - 1)) currentChunkEnd = (fullZ - 1); int currentChunkSize = currentChunkEnd - currentChunkStart + 1; sgc.fillSlices(currentChunkStart, currentChunkEnd); glBindTexture(vtkgl::TEXTURE_3D, volume1); vtkgl::TexSubImage3D(vtkgl::TEXTURE_3D, 0, 0, 0, currentChunkStart, fullX, fullY, currentChunkSize, GL_RGBA, GL_UNSIGNED_BYTE, tmpPtr); glBindTexture(vtkgl::TEXTURE_3D, volume2); vtkgl::TexSubImage3D(vtkgl::TEXTURE_3D, 0, 0, 0, currentChunkStart, fullX, fullY, currentChunkSize, GL_RGB, GL_UNSIGNED_BYTE, tmpPtr2); currentChunk++; } } delete[] tmpPtr; delete[] tmpPtr2; } //----------------------------------------------------------------------------- void vtkMitkOpenGLVolumeTextureMapper3D::ComputeVolumeDimensions() { // Get the image data vtkImageData *input = this->GetInput(); // How big does the Volume need to be? int dim[3]; input->GetDimensions(dim); int powerOfTwoDim[3]; if (this->SupportsNonPowerOfTwoTextures) { for (int i = 0; i < 3; i++) powerOfTwoDim[i] = (dim[i] + 1) & ~1; // MITK_INFO << "using non-power-two even textures (" << // (1.0-double(dim[0]*dim[1]*dim[2])/double(powerOfTwoDim[0]*powerOfTwoDim[1]*powerOfTwoDim[2])) * 100.0 << "% // memory wasted)"; } else { for (int i = 0; i < 3; i++) { powerOfTwoDim[i] = 4; while (powerOfTwoDim[i] < dim[i]) powerOfTwoDim[i] *= 2; } MITK_WARN << "using power-two textures (" << (1.0 - double(dim[0] * dim[1] * dim[2]) / double(powerOfTwoDim[0] * powerOfTwoDim[1] * powerOfTwoDim[2])) * 100.0 << "% memory wasted)"; } // Save the volume size this->VolumeDimensions[0] = powerOfTwoDim[0]; this->VolumeDimensions[1] = powerOfTwoDim[1]; this->VolumeDimensions[2] = powerOfTwoDim[2]; // What is the spacing? double spacing[3]; input->GetSpacing(spacing); // Compute the new spacing this->VolumeSpacing[0] = (dim[0] - 1.01) * spacing[0] / static_cast(this->VolumeDimensions[0] - 1); this->VolumeSpacing[1] = (dim[1] - 1.01) * spacing[1] / static_cast(this->VolumeDimensions[1] - 1); this->VolumeSpacing[2] = ((dim[2]) - 1.01) * spacing[2] / static_cast(this->VolumeDimensions[2] - 1); } //----------------------------------------------------------------------------- bool vtkMitkOpenGLVolumeTextureMapper3D::UpdateVolumes(vtkVolume *vtkNotUsed(vol)) { // Get the image data vtkImageData *input = this->GetInput(); // input->Update(); //VTK6_TODO bool needUpdate = false; // Has the volume changed in some way? if (this->SavedTextureInput != input || this->SavedTextureMTime.GetMTime() < input->GetMTime()) needUpdate = true; // Do we have any volume on the gpu already? if (!this->Volume1Index) needUpdate = true; if (!needUpdate) return true; ComputeVolumeDimensions(); int components = input->GetNumberOfScalarComponents(); // Find the scalar range double scalarRange[2]; input->GetPointData()->GetScalars()->GetRange(scalarRange, components - 1); // Is the difference between max and min less than 4096? If so, and if // the data is not of float or double type, use a simple offset mapping. // If the difference between max and min is 4096 or greater, or the data // is of type float or double, we must use an offset / scaling mapping. // In this case, the array size will be 4096 - we need to figure out the // offset and scale factor. float offset; float scale; int arraySizeNeeded; int scalarType = input->GetScalarType(); if (scalarType == VTK_FLOAT || scalarType == VTK_DOUBLE || scalarRange[1] - scalarRange[0] > 255) { arraySizeNeeded = 256; offset = -scalarRange[0]; scale = 255.0 / (scalarRange[1] - scalarRange[0]); } else { arraySizeNeeded = static_cast(scalarRange[1] - scalarRange[0] + 1); offset = -scalarRange[0]; scale = 1.0; } this->ColorTableSize = arraySizeNeeded; this->ColorTableOffset = offset; this->ColorTableScale = scale; // Allocating volume on gpu { // Deleting old textures this->DeleteTextureIndex(&this->Volume1Index); this->DeleteTextureIndex(&this->Volume2Index); this->DeleteTextureIndex(&this->Volume3Index); this->CreateTextureIndex(&this->Volume1Index); // this->CreateTextureIndex(&this->Volume2Index); int dim[3]; this->GetVolumeDimensions(dim); vtkgl::ActiveTexture(vtkgl::TEXTURE0); MITK_INFO << "allocating volume on gpu"; GLint gradientScalarTextureFormat = GL_RGBA8; if (this->UseCompressedTexture && SupportsCompressedTexture) gradientScalarTextureFormat = myGL_COMPRESSED_RGBA_S3TC_DXT5_EXT; glBindTexture(vtkgl::TEXTURE_3D, this->Volume1Index); vtkgl::TexImage3D( vtkgl::TEXTURE_3D, 0, gradientScalarTextureFormat, dim[0], dim[1], dim[2], 0, GL_RGBA, GL_UNSIGNED_BYTE, 0); this->Setup3DTextureParameters(true); } // Transfer the input volume to the RGBA volume void *dataPtr = input->GetScalarPointer(); switch (scalarType) { vtkTemplateMacro(vtkVolumeTextureMapper3DComputeScalars( static_cast(dataPtr), this, offset, scale, this->Volume1Index, this->Volume2Index)); } this->SavedTextureInput = input; this->SavedTextureMTime.Modified(); return true; } //----------------------------------------------------------------------------- bool vtkMitkOpenGLVolumeTextureMapper3D::UpdateVolumesRGBA(vtkVolume *vtkNotUsed(vol)) { // Get the image data vtkImageData *input = this->GetInput(); // input->Update(); //VTK6_TODO bool needUpdate = false; // Has the volume changed in some way? if (this->SavedTextureInput != input || this->SavedTextureMTime.GetMTime() < input->GetMTime()) needUpdate = true; // Do we have any volume on the gpu already? if (!this->Volume1Index) needUpdate = true; if (!needUpdate) return true; MITK_INFO << "updating rgba volume"; ComputeVolumeDimensions(); // Allocating volume on gpu { // Deleting old textures this->DeleteTextureIndex(&this->Volume1Index); this->DeleteTextureIndex(&this->Volume2Index); this->DeleteTextureIndex(&this->Volume3Index); this->CreateTextureIndex(&this->Volume1Index); this->CreateTextureIndex(&this->Volume2Index); int dim[3]; this->GetVolumeDimensions(dim); MITK_INFO << "allocating volume on gpu"; GLint gradientScalarTextureFormat = GL_RGBA8; GLint colorTextureFormat = GL_RGB8; if (this->UseCompressedTexture && SupportsCompressedTexture) { gradientScalarTextureFormat = myGL_COMPRESSED_RGBA_S3TC_DXT5_EXT; colorTextureFormat = myGL_COMPRESSED_RGB_S3TC_DXT1_EXT; } vtkgl::ActiveTexture(vtkgl::TEXTURE0); glBindTexture(vtkgl::TEXTURE_3D, this->Volume1Index); vtkgl::TexImage3D( vtkgl::TEXTURE_3D, 0, gradientScalarTextureFormat, dim[0], dim[1], dim[2], 0, GL_RGBA, GL_UNSIGNED_BYTE, 0); this->Setup3DTextureParameters(true); glBindTexture(vtkgl::TEXTURE_3D, this->Volume2Index); vtkgl::TexImage3D(vtkgl::TEXTURE_3D, 0, colorTextureFormat, dim[0], dim[1], dim[2], 0, GL_RGB, GL_UNSIGNED_BYTE, 0); this->Setup3DTextureParameters(true); } // Transfer the input volume to the RGBA volume unsigned char *dataPtr = (unsigned char *)input->GetScalarPointer(); vtkVolumeTextureMapper3DComputeRGBA(dataPtr, this, this->Volume1Index, this->Volume2Index); this->SavedTextureInput = input; this->SavedTextureMTime.Modified(); return true; } void vtkMitkOpenGLVolumeTextureMapper3D::Setup3DTextureParameters(bool linear) { // GPU_INFO << "Setup3DTextureParameters"; if (linear) { glTexParameterf(vtkgl::TEXTURE_3D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameterf(vtkgl::TEXTURE_3D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); } else { glTexParameterf(vtkgl::TEXTURE_3D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexParameterf(vtkgl::TEXTURE_3D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); } glTexParameterf(vtkgl::TEXTURE_3D, GL_TEXTURE_WRAP_S, GL_CLAMP); glTexParameterf(vtkgl::TEXTURE_3D, GL_TEXTURE_WRAP_T, GL_CLAMP); } void vtkMitkOpenGLVolumeTextureMapper3D::SetupOneIndependentTextures(vtkRenderer *vtkNotUsed(ren), vtkVolume *vol) { // Update the volume containing the 2 byte scalar / gradient magnitude this->UpdateVolumes(vol); // Update the dependent 2D color table mapping scalar value and // gradient magnitude to RGBA if (this->UpdateColorLookup(vol) || !this->ColorLookupIndex) { this->DeleteTextureIndex(&this->ColorLookupIndex); this->DeleteTextureIndex(&this->AlphaLookupIndex); this->CreateTextureIndex(&this->ColorLookupIndex); vtkgl::ActiveTexture(vtkgl::TEXTURE1); glBindTexture(GL_TEXTURE_2D, this->ColorLookupIndex); glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP); glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP); // MITK_INFO << "uploading transferfunction"; GLint colorLookupTextureFormat = GL_RGBA8; if (this->UseCompressedTexture && SupportsCompressedTexture) colorLookupTextureFormat = myGL_COMPRESSED_RGBA_S3TC_DXT5_EXT; glTexImage2D(GL_TEXTURE_2D, 0, colorLookupTextureFormat, 256, 256, 0, GL_RGBA, GL_UNSIGNED_BYTE, this->ColorLookup); } } void vtkMitkOpenGLVolumeTextureMapper3D::SetupRGBATextures(vtkRenderer *vtkNotUsed(ren), vtkVolume *vol) { MITK_INFO << "SetupFourDependentTextures"; this->UpdateVolumesRGBA(vol); /* vtkgl::ActiveTexture( vtkgl::TEXTURE0 ); glDisable( GL_TEXTURE_2D ); glEnable( vtkgl::TEXTURE_3D ); vtkgl::ActiveTexture( vtkgl::TEXTURE1 ); glDisable( GL_TEXTURE_2D ); glEnable( vtkgl::TEXTURE_3D ); vtkgl::ActiveTexture( vtkgl::TEXTURE2 ); glDisable( GL_TEXTURE_2D ); glEnable( vtkgl::TEXTURE_3D ); // Update the volume containing the 3 byte scalars / gradient magnitude if ( this->UpdateVolumes( vol ) || !this->Volume1Index || !this->Volume2Index || !this->Volume3Index ) { int dim[3]; this->GetVolumeDimensions(dim); vtkgl::ActiveTexture( vtkgl::TEXTURE0 ); glBindTexture(vtkgl::TEXTURE_3D,0); this->DeleteTextureIndex(&this->Volume1Index); this->CreateTextureIndex(&this->Volume1Index); glBindTexture(vtkgl::TEXTURE_3D, this->Volume1Index); vtkgl::TexImage3D(vtkgl::TEXTURE_3D,0,this->InternalRGB,dim[0],dim[1], dim[2],0,GL_RGB,GL_UNSIGNED_BYTE,this->Volume1); vtkgl::ActiveTexture( vtkgl::TEXTURE1 ); glBindTexture(vtkgl::TEXTURE_3D,0); this->DeleteTextureIndex(&this->Volume2Index); this->CreateTextureIndex(&this->Volume2Index); glBindTexture(vtkgl::TEXTURE_3D, this->Volume2Index); vtkgl::TexImage3D(vtkgl::TEXTURE_3D,0,this->InternalLA,dim[0],dim[1], dim[2],0,GL_LUMINANCE_ALPHA,GL_UNSIGNED_BYTE, this->Volume2); vtkgl::ActiveTexture( vtkgl::TEXTURE2 ); glBindTexture(vtkgl::TEXTURE_3D,0); this->DeleteTextureIndex(&this->Volume3Index); this->CreateTextureIndex(&this->Volume3Index); glBindTexture(vtkgl::TEXTURE_3D, this->Volume3Index); vtkgl::TexImage3D(vtkgl::TEXTURE_3D,0,this->InternalRGB,dim[0],dim[1], dim[2],0,GL_RGB,GL_UNSIGNED_BYTE,this->Volume3); } vtkgl::ActiveTexture( vtkgl::TEXTURE0 ); glBindTexture(vtkgl::TEXTURE_3D, this->Volume1Index); this->Setup3DTextureParameters( true ); vtkgl::ActiveTexture( vtkgl::TEXTURE1 ); glBindTexture(vtkgl::TEXTURE_3D, this->Volume2Index); this->Setup3DTextureParameters( true ); vtkgl::ActiveTexture( vtkgl::TEXTURE2 ); glBindTexture(vtkgl::TEXTURE_3D_EXT, this->Volume3Index); this->Setup3DTextureParameters( true ); vtkgl::ActiveTexture( vtkgl::TEXTURE3 ); glEnable( GL_TEXTURE_2D ); glDisable( vtkgl::TEXTURE_3D ); // Update the dependent 2D table mapping scalar value and // gradient magnitude to opacity if ( this->UpdateColorLookup( vol ) || !this->AlphaLookupIndex ) { this->DeleteTextureIndex(&this->ColorLookupIndex); vtkgl::ActiveTexture( vtkgl::TEXTURE3 ); glBindTexture(GL_TEXTURE_2D,0); this->DeleteTextureIndex(&this->AlphaLookupIndex); this->CreateTextureIndex(&this->AlphaLookupIndex); glBindTexture(GL_TEXTURE_2D, this->AlphaLookupIndex); glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST ); glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP ); glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP ); //MITK_INFO << "uploading transferfunction"; glTexImage2D(GL_TEXTURE_2D,0,this->InternalAlpha, 256, 256, 0, GL_ALPHA, GL_UNSIGNED_BYTE, this->AlphaLookup ); } vtkgl::ActiveTexture( vtkgl::TEXTURE3 ); glBindTexture(GL_TEXTURE_2D, this->AlphaLookupIndex); */ } void vtkMitkOpenGLVolumeTextureMapper3D::RenderOneIndependentShadeFP(vtkRenderer *ren, vtkVolume *vol) { // GPU_INFO << "RenderOneIndependentShadeFP"; this->SetupOneIndependentTextures(ren, vol); glEnable(vtkgl::FRAGMENT_PROGRAM_ARB); vtkgl::BindProgramARB(vtkgl::FRAGMENT_PROGRAM_ARB, prgOneComponentShade); this->SetupProgramLocalsForShadingFP(ren, vol); // Bind Textures { vtkgl::ActiveTexture(vtkgl::TEXTURE0); glDisable(GL_TEXTURE_2D); glEnable(vtkgl::TEXTURE_3D); glBindTexture(vtkgl::TEXTURE_3D, this->Volume1Index); vtkgl::ActiveTexture(vtkgl::TEXTURE1); glEnable(GL_TEXTURE_2D); glDisable(vtkgl::TEXTURE_3D); glBindTexture(GL_TEXTURE_2D, this->ColorLookupIndex); vtkgl::ActiveTexture(vtkgl::TEXTURE2); glDisable(GL_TEXTURE_2D); glEnable(vtkgl::TEXTURE_3D); glBindTexture(vtkgl::TEXTURE_3D, this->Volume2Index); } int stages[4] = {1, 1, 1, 0}; this->RenderPolygons(ren, vol, stages); glDisable(vtkgl::FRAGMENT_PROGRAM_ARB); } void vtkMitkOpenGLVolumeTextureMapper3D::RenderRGBAShadeFP(vtkRenderer *ren, vtkVolume *vol) { this->SetupRGBATextures(ren, vol); glEnable(vtkgl::FRAGMENT_PROGRAM_ARB); vtkgl::BindProgramARB(vtkgl::FRAGMENT_PROGRAM_ARB, prgRGBAShade); this->SetupProgramLocalsForShadingFP(ren, vol); // Bind Textures { vtkgl::ActiveTexture(vtkgl::TEXTURE0); glDisable(GL_TEXTURE_2D); glEnable(vtkgl::TEXTURE_3D); glBindTexture(vtkgl::TEXTURE_3D, this->Volume1Index); vtkgl::ActiveTexture(vtkgl::TEXTURE1); glDisable(GL_TEXTURE_2D); glEnable(vtkgl::TEXTURE_3D); glBindTexture(vtkgl::TEXTURE_3D, this->Volume2Index); } int stages[4] = {1, 1, 1, 0}; this->RenderPolygons(ren, vol, stages); glDisable(vtkgl::FRAGMENT_PROGRAM_ARB); } void vtkMitkOpenGLVolumeTextureMapper3D::GetLightInformation(vtkRenderer *ren, vtkVolume *vol, GLfloat lightDirection[2][4], GLfloat lightDiffuseColor[2][4], GLfloat lightSpecularColor[2][4], GLfloat halfwayVector[2][4], GLfloat ambientColor[4]) { // GPU_INFO << "GetLightInformation"; float ambient = vol->GetProperty()->GetAmbient(); float diffuse = vol->GetProperty()->GetDiffuse(); float specular = vol->GetProperty()->GetSpecular(); vtkTransform *volumeTransform = vtkTransform::New(); volumeTransform->SetMatrix(vol->GetMatrix()); volumeTransform->Inverse(); vtkLightCollection *lights = ren->GetLights(); lights->InitTraversal(); vtkLight *light[2]; light[0] = lights->GetNextItem(); light[1] = lights->GetNextItem(); int lightIndex = 0; double cameraPosition[3]; double cameraFocalPoint[3]; ren->GetActiveCamera()->GetPosition(cameraPosition); ren->GetActiveCamera()->GetFocalPoint(cameraFocalPoint); double viewDirection[3]; volumeTransform->TransformPoint(cameraPosition, cameraPosition); volumeTransform->TransformPoint(cameraFocalPoint, cameraFocalPoint); viewDirection[0] = cameraFocalPoint[0] - cameraPosition[0]; viewDirection[1] = cameraFocalPoint[1] - cameraPosition[1]; viewDirection[2] = cameraFocalPoint[2] - cameraPosition[2]; vtkMath::Normalize(viewDirection); ambientColor[0] = 0.0; ambientColor[1] = 0.0; ambientColor[2] = 0.0; ambientColor[3] = 0.0; for (lightIndex = 0; lightIndex < 2; lightIndex++) { float dir[3] = {0, 0, 0}; float half[3] = {0, 0, 0}; if (light[lightIndex] == nullptr || light[lightIndex]->GetSwitch() == 0) { lightDiffuseColor[lightIndex][0] = 0.0; lightDiffuseColor[lightIndex][1] = 0.0; lightDiffuseColor[lightIndex][2] = 0.0; lightDiffuseColor[lightIndex][3] = 0.0; lightSpecularColor[lightIndex][0] = 0.0; lightSpecularColor[lightIndex][1] = 0.0; lightSpecularColor[lightIndex][2] = 0.0; lightSpecularColor[lightIndex][3] = 0.0; } else { float lightIntensity = light[lightIndex]->GetIntensity(); double lightColor[3]; light[lightIndex]->GetDiffuseColor(lightColor); double lightPosition[3]; double lightFocalPoint[3]; light[lightIndex]->GetTransformedPosition(lightPosition); light[lightIndex]->GetTransformedFocalPoint(lightFocalPoint); volumeTransform->TransformPoint(lightPosition, lightPosition); volumeTransform->TransformPoint(lightFocalPoint, lightFocalPoint); dir[0] = lightPosition[0] - lightFocalPoint[0]; dir[1] = lightPosition[1] - lightFocalPoint[1]; dir[2] = lightPosition[2] - lightFocalPoint[2]; vtkMath::Normalize(dir); lightDiffuseColor[lightIndex][0] = lightColor[0] * diffuse * lightIntensity; lightDiffuseColor[lightIndex][1] = lightColor[1] * diffuse * lightIntensity; lightDiffuseColor[lightIndex][2] = lightColor[2] * diffuse * lightIntensity; lightDiffuseColor[lightIndex][3] = 1.0; lightSpecularColor[lightIndex][0] = lightColor[0] * specular * lightIntensity; lightSpecularColor[lightIndex][1] = lightColor[1] * specular * lightIntensity; lightSpecularColor[lightIndex][2] = lightColor[2] * specular * lightIntensity; lightSpecularColor[lightIndex][3] = 0.0; half[0] = dir[0] - viewDirection[0]; half[1] = dir[1] - viewDirection[1]; half[2] = dir[2] - viewDirection[2]; vtkMath::Normalize(half); ambientColor[0] += ambient * lightColor[0]; ambientColor[1] += ambient * lightColor[1]; ambientColor[2] += ambient * lightColor[2]; } lightDirection[lightIndex][0] = (dir[0] + 1.0) / 2.0; lightDirection[lightIndex][1] = (dir[1] + 1.0) / 2.0; lightDirection[lightIndex][2] = (dir[2] + 1.0) / 2.0; lightDirection[lightIndex][3] = 0.0; halfwayVector[lightIndex][0] = (half[0] + 1.0) / 2.0; halfwayVector[lightIndex][1] = (half[1] + 1.0) / 2.0; halfwayVector[lightIndex][2] = (half[2] + 1.0) / 2.0; halfwayVector[lightIndex][3] = 0.0; } volumeTransform->Delete(); } void vtkMitkOpenGLVolumeTextureMapper3D::SetupProgramLocalsForShadingFP(vtkRenderer *ren, vtkVolume *vol) { // GPU_INFO << "SetupProgramLocalsForShadingFP"; GLfloat lightDirection[2][4]; GLfloat lightDiffuseColor[2][4]; GLfloat lightSpecularColor[2][4]; GLfloat halfwayVector[2][4]; GLfloat ambientColor[4]; float ambient = vol->GetProperty()->GetAmbient(); float diffuse = vol->GetProperty()->GetDiffuse(); float specular = vol->GetProperty()->GetSpecular(); float specularPower = vol->GetProperty()->GetSpecularPower(); vtkTransform *volumeTransform = vtkTransform::New(); volumeTransform->SetMatrix(vol->GetMatrix()); volumeTransform->Inverse(); vtkLightCollection *lights = ren->GetLights(); lights->InitTraversal(); vtkLight *light[2]; light[0] = lights->GetNextItem(); light[1] = lights->GetNextItem(); int lightIndex = 0; double cameraPosition[3]; double cameraFocalPoint[3]; ren->GetActiveCamera()->GetPosition(cameraPosition); ren->GetActiveCamera()->GetFocalPoint(cameraFocalPoint); volumeTransform->TransformPoint(cameraPosition, cameraPosition); volumeTransform->TransformPoint(cameraFocalPoint, cameraFocalPoint); double viewDirection[4]; viewDirection[0] = cameraFocalPoint[0] - cameraPosition[0]; viewDirection[1] = cameraFocalPoint[1] - cameraPosition[1]; viewDirection[2] = cameraFocalPoint[2] - cameraPosition[2]; viewDirection[3] = 0.0; vtkMath::Normalize(viewDirection); ambientColor[0] = 0.0; ambientColor[1] = 0.0; ambientColor[2] = 0.0; ambientColor[3] = 0.0; for (lightIndex = 0; lightIndex < 2; lightIndex++) { float dir[3] = {0, 0, 0}; float half[3] = {0, 0, 0}; if (light[lightIndex] == nullptr || light[lightIndex]->GetSwitch() == 0) { lightDiffuseColor[lightIndex][0] = 0.0; lightDiffuseColor[lightIndex][1] = 0.0; lightDiffuseColor[lightIndex][2] = 0.0; lightDiffuseColor[lightIndex][3] = 0.0; lightSpecularColor[lightIndex][0] = 0.0; lightSpecularColor[lightIndex][1] = 0.0; lightSpecularColor[lightIndex][2] = 0.0; lightSpecularColor[lightIndex][3] = 0.0; } else { float lightIntensity = light[lightIndex]->GetIntensity(); double lightColor[3]; light[lightIndex]->GetDiffuseColor(lightColor); double lightPosition[3]; double lightFocalPoint[3]; light[lightIndex]->GetTransformedPosition(lightPosition); light[lightIndex]->GetTransformedFocalPoint(lightFocalPoint); volumeTransform->TransformPoint(lightPosition, lightPosition); volumeTransform->TransformPoint(lightFocalPoint, lightFocalPoint); dir[0] = lightPosition[0] - lightFocalPoint[0]; dir[1] = lightPosition[1] - lightFocalPoint[1]; dir[2] = lightPosition[2] - lightFocalPoint[2]; vtkMath::Normalize(dir); lightDiffuseColor[lightIndex][0] = lightColor[0] * diffuse * lightIntensity; lightDiffuseColor[lightIndex][1] = lightColor[1] * diffuse * lightIntensity; lightDiffuseColor[lightIndex][2] = lightColor[2] * diffuse * lightIntensity; lightDiffuseColor[lightIndex][3] = 0.0; lightSpecularColor[lightIndex][0] = lightColor[0] * specular * lightIntensity; lightSpecularColor[lightIndex][1] = lightColor[1] * specular * lightIntensity; lightSpecularColor[lightIndex][2] = lightColor[2] * specular * lightIntensity; lightSpecularColor[lightIndex][3] = 0.0; half[0] = dir[0] - viewDirection[0]; half[1] = dir[1] - viewDirection[1]; half[2] = dir[2] - viewDirection[2]; vtkMath::Normalize(half); ambientColor[0] += ambient * lightColor[0]; ambientColor[1] += ambient * lightColor[1]; ambientColor[2] += ambient * lightColor[2]; } lightDirection[lightIndex][0] = dir[0]; lightDirection[lightIndex][1] = dir[1]; lightDirection[lightIndex][2] = dir[2]; lightDirection[lightIndex][3] = 0.0; halfwayVector[lightIndex][0] = half[0]; halfwayVector[lightIndex][1] = half[1]; halfwayVector[lightIndex][2] = half[2]; halfwayVector[lightIndex][3] = 0.0; } volumeTransform->Delete(); vtkgl::ProgramLocalParameter4fARB(vtkgl::FRAGMENT_PROGRAM_ARB, 0, lightDirection[0][0], lightDirection[0][1], lightDirection[0][2], lightDirection[0][3]); vtkgl::ProgramLocalParameter4fARB( vtkgl::FRAGMENT_PROGRAM_ARB, 1, halfwayVector[0][0], halfwayVector[0][1], halfwayVector[0][2], halfwayVector[0][3]); vtkgl::ProgramLocalParameter4fARB(vtkgl::FRAGMENT_PROGRAM_ARB, 2, ambient, diffuse, specular, specularPower); vtkgl::ProgramLocalParameter4fARB(vtkgl::FRAGMENT_PROGRAM_ARB, 3, lightDiffuseColor[0][0], lightDiffuseColor[0][1], lightDiffuseColor[0][2], lightDiffuseColor[0][3]); vtkgl::ProgramLocalParameter4fARB(vtkgl::FRAGMENT_PROGRAM_ARB, 4, lightSpecularColor[0][0], lightSpecularColor[0][1], lightSpecularColor[0][2], lightSpecularColor[0][3]); vtkgl::ProgramLocalParameter4fARB( vtkgl::FRAGMENT_PROGRAM_ARB, 5, viewDirection[0], viewDirection[1], viewDirection[2], viewDirection[3]); vtkgl::ProgramLocalParameter4fARB(vtkgl::FRAGMENT_PROGRAM_ARB, 6, 2.0, -1.0, 0.0, 0.0); } int vtkMitkOpenGLVolumeTextureMapper3D::IsRenderSupported(vtkRenderer *renderer, vtkVolumeProperty * /*property*/) { // GPU_INFO << "IsRenderSupported"; if (!this->Initialized) { // this->Initialize(); this->Initialize(renderer); } if (!this->RenderPossible) { MITK_WARN << "vtkMitkOpenGLVolumeTextureMapper3D::IsRenderSupported Rendering not possible"; return 0; } if (!this->GetInput()) { MITK_WARN << "vtkMitkOpenGLVolumeTextureMapper3D::IsRenderSupported No input available"; return 0; } return 1; } void vtkMitkOpenGLVolumeTextureMapper3D::Initialize(vtkRenderer *renderer) { // GPU_INFO << "Initialize"; this->Initialized = 1; // vtkOpenGLExtensionManager * extensions = vtkOpenGLExtensionManager::New(); // extensions->SetRenderWindow(nullptr); // set render window to the current one. vtkOpenGLExtensionManager *extensions = static_cast(renderer->GetRenderWindow())->GetExtensionManager(); int supports_texture3D = extensions->ExtensionSupported("GL_VERSION_1_2"); if (supports_texture3D) { extensions->LoadExtension("GL_VERSION_1_2"); } else { supports_texture3D = extensions->ExtensionSupported("GL_EXT_texture3D"); if (supports_texture3D) { extensions->LoadCorePromotedExtension("GL_EXT_texture3D"); } } int supports_multitexture = extensions->ExtensionSupported("GL_VERSION_1_3"); if (supports_multitexture) { extensions->LoadExtension("GL_VERSION_1_3"); } else { supports_multitexture = extensions->ExtensionSupported("GL_ARB_multitexture"); if (supports_multitexture) { extensions->LoadCorePromotedExtension("GL_ARB_multitexture"); } } this->SupportsCompressedTexture = extensions->ExtensionSupported("GL_VERSION_1_3") == 1; if (!this->SupportsCompressedTexture) { this->SupportsCompressedTexture = extensions->ExtensionSupported("GL_ARB_texture_compression") == 1; if (this->SupportsCompressedTexture) { extensions->LoadCorePromotedExtension("GL_ARB_texture_compression"); } } // GPU_INFO(this->SupportsCompressedTexture) << "supporting compressed textures"; this->SupportsNonPowerOfTwoTextures = extensions->ExtensionSupported("GL_VERSION_2_0") || extensions->ExtensionSupported("GL_ARB_texture_non_power_of_two"); // GPU_INFO << "np2: " << (this->SupportsNonPowerOfTwoTextures?1:0); int supports_GL_ARB_fragment_program = extensions->ExtensionSupported("GL_ARB_fragment_program"); if (supports_GL_ARB_fragment_program) { extensions->LoadExtension("GL_ARB_fragment_program"); } int supports_GL_ARB_vertex_program = extensions->ExtensionSupported("GL_ARB_vertex_program"); if (supports_GL_ARB_vertex_program) { extensions->LoadExtension("GL_ARB_vertex_program"); } RenderPossible = 0; if (supports_texture3D && supports_multitexture && supports_GL_ARB_fragment_program && supports_GL_ARB_vertex_program && vtkgl::TexImage3D && vtkgl::ActiveTexture && vtkgl::MultiTexCoord3fv && vtkgl::GenProgramsARB && vtkgl::DeleteProgramsARB && vtkgl::BindProgramARB && vtkgl::ProgramStringARB && vtkgl::ProgramLocalParameter4fARB) { RenderPossible = 1; } else { std::string errString = "no gpu-acceleration possible cause following extensions/methods are missing or unsupported:"; if (!supports_texture3D) errString += " EXT_TEXTURE3D"; if (!supports_multitexture) errString += " EXT_MULTITEXTURE"; if (!supports_GL_ARB_fragment_program) errString += " ARB_FRAGMENT_PROGRAM"; if (!supports_GL_ARB_vertex_program) errString += " ARB_VERTEX_PROGRAM"; if (!vtkgl::TexImage3D) errString += " glTexImage3D"; if (!vtkgl::ActiveTexture) errString += " glActiveTexture"; if (!vtkgl::MultiTexCoord3fv) errString += " glMultiTexCoord3fv"; if (!vtkgl::GenProgramsARB) errString += " glGenProgramsARB"; if (!vtkgl::DeleteProgramsARB) errString += " glDeleteProgramsARB"; if (!vtkgl::BindProgramARB) errString += " glBindProgramARB"; if (!vtkgl::ProgramStringARB) errString += " glProgramStringARB"; if (!vtkgl::ProgramLocalParameter4fARB) errString += " glProgramLocalParameter4fARB"; GPU_WARN << errString; }; if (RenderPossible) { vtkgl::GenProgramsARB(1, &prgOneComponentShade); vtkgl::BindProgramARB(vtkgl::FRAGMENT_PROGRAM_ARB, prgOneComponentShade); vtkgl::ProgramStringARB(vtkgl::FRAGMENT_PROGRAM_ARB, vtkgl::PROGRAM_FORMAT_ASCII_ARB, static_cast(strlen(vtkMitkVolumeTextureMapper3D_OneComponentShadeFP)), vtkMitkVolumeTextureMapper3D_OneComponentShadeFP); vtkgl::GenProgramsARB(1, &prgRGBAShade); vtkgl::BindProgramARB(vtkgl::FRAGMENT_PROGRAM_ARB, prgRGBAShade); vtkgl::ProgramStringARB(vtkgl::FRAGMENT_PROGRAM_ARB, vtkgl::PROGRAM_FORMAT_ASCII_ARB, static_cast(strlen(vtkMitkVolumeTextureMapper3D_FourDependentShadeFP)), vtkMitkVolumeTextureMapper3D_FourDependentShadeFP); } } // ---------------------------------------------------------------------------- // Print the vtkMitkOpenGLVolumeTextureMapper3D void vtkMitkOpenGLVolumeTextureMapper3D::PrintSelf(ostream &os, vtkIndent indent) { // vtkOpenGLExtensionManager * extensions = vtkOpenGLExtensionManager::New(); // extensions->SetRenderWindow(nullptr); // set render window to current render window os << indent << "Initialized " << this->Initialized << endl; /* if ( this->Initialized ) { os << indent << "Supports GL_VERSION_1_2:" << extensions->ExtensionSupported( "GL_VERSION_1_2" ) << endl; os << indent << "Supports GL_EXT_texture3D:" << extensions->ExtensionSupported( "GL_EXT_texture3D" ) << endl; os << indent << "Supports GL_VERSION_1_3:" << extensions->ExtensionSupported( "GL_VERSION_1_3" ) << endl; os << indent << "Supports GL_ARB_multitexture: " << extensions->ExtensionSupported( "GL_ARB_multitexture" ) << endl; os << indent << "Supports GL_NV_texture_shader2: " << extensions->ExtensionSupported( "GL_NV_texture_shader2" ) << endl; os << indent << "Supports GL_NV_register_combiners2: " << extensions->ExtensionSupported( "GL_NV_register_combiners2" ) << endl; os << indent << "Supports GL_ATI_fragment_shader: " << extensions->ExtensionSupported( "GL_ATI_fragment_shader" ) << endl; os << indent << "Supports GL_ARB_fragment_program: " << extensions->ExtensionSupported( "GL_ARB_fragment_program" ) << endl; os << indent << "Supports GL_ARB_texture_compression: " << extensions->ExtensionSupported( "GL_ARB_texture_compression" ) << endl; os << indent << "Supports GL_VERSION_2_0:" << extensions->ExtensionSupported( "GL_VERSION_2_0" ) << endl; os << indent << "Supports GL_ARB_texture_non_power_of_two:" << extensions->ExtensionSupported( "GL_ARB_texture_non_power_of_two" ) << endl; } extensions->Delete(); */ if (this->RenderWindow != 0) { vtkOpenGLExtensionManager *extensions = static_cast(this->RenderWindow)->GetExtensionManager(); if (this->Initialized) { os << indent << "Supports GL_VERSION_1_2:" << extensions->ExtensionSupported("GL_VERSION_1_2") << endl; os << indent << "Supports GL_EXT_texture3D:" << extensions->ExtensionSupported("GL_EXT_texture3D") << endl; os << indent << "Supports GL_VERSION_1_3:" << extensions->ExtensionSupported("GL_VERSION_1_3") << endl; os << indent << "Supports GL_ARB_multitexture: " << extensions->ExtensionSupported("GL_ARB_multitexture") << endl; os << indent << "Supports GL_NV_texture_shader2: " << extensions->ExtensionSupported("GL_NV_texture_shader2") << endl; os << indent << "Supports GL_NV_register_combiners2: " << extensions->ExtensionSupported("GL_NV_register_combiners2") << endl; os << indent << "Supports GL_ATI_fragment_shader: " << extensions->ExtensionSupported("GL_ATI_fragment_shader") << endl; os << indent << "Supports GL_ARB_fragment_program: " << extensions->ExtensionSupported("GL_ARB_fragment_program") << endl; os << indent << "Supports GL_ARB_texture_compression: " << extensions->ExtensionSupported("GL_ARB_texture_compression") << endl; os << indent << "Supports GL_VERSION_2_0:" << extensions->ExtensionSupported("GL_VERSION_2_0") << endl; os << indent << "Supports GL_ARB_texture_non_power_of_two:" << extensions->ExtensionSupported("GL_ARB_texture_non_power_of_two") << endl; } } this->Superclass::PrintSelf(os, indent); } diff --git a/Modules/MatchPointRegistration/Helper/mitkAlgorithmHelper.cpp b/Modules/MatchPointRegistration/Helper/mitkAlgorithmHelper.cpp index 9db48085b7..46264a8a2a 100644 --- a/Modules/MatchPointRegistration/Helper/mitkAlgorithmHelper.cpp +++ b/Modules/MatchPointRegistration/Helper/mitkAlgorithmHelper.cpp @@ -1,388 +1,388 @@ /*=================================================================== 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 "mitkAlgorithmHelper.h" //itk #include // Mitk #include #include // MatchPoint #include #include #include #include #include namespace mitk { - MITKAlgorithmHelper::MITKAlgorithmHelper(map::algorithm::RegistrationAlgorithmBase* algorithm) : - m_AlgorithmBase(algorithm) + MITKAlgorithmHelper::MITKAlgorithmHelper(map::algorithm::RegistrationAlgorithmBase *algorithm) + : m_AlgorithmBase(algorithm), m_Error(CheckError::none) { m_AllowImageCasting = true; } map::core::RegistrationBase::Pointer MITKAlgorithmHelper:: GetRegistration() const { map::core::RegistrationBase::Pointer spResult; unsigned int movingDim = m_AlgorithmBase->getMovingDimensions(); unsigned int targetDim = m_AlgorithmBase->getTargetDimensions(); if (movingDim != targetDim) { mapDefaultExceptionStaticMacro( << "Error, algorithm instance has unequal dimensionality and is therefore not supported in the current version of MITKAlgorithmHelper."); } if (movingDim > 3) { mapDefaultExceptionStaticMacro( << "Error, algorithm instance has a dimensionality larger than 3 and is therefore not supported in the current version of MITKAlgorithmHelper."); } typedef ::map::algorithm::facet::RegistrationAlgorithmInterface<2, 2> RegistrationAlg2D2DInterface; typedef ::map::algorithm::facet::RegistrationAlgorithmInterface<3, 3> RegistrationAlg3D3DInterface; RegistrationAlg2D2DInterface* pRegAlgorithm2D2D = dynamic_cast (m_AlgorithmBase.GetPointer()); RegistrationAlg3D3DInterface* pRegAlgorithm3D3D = dynamic_cast (m_AlgorithmBase.GetPointer()); if (pRegAlgorithm2D2D) { spResult = pRegAlgorithm2D2D->getRegistration(); } if (pRegAlgorithm3D3D) { spResult = pRegAlgorithm3D3D->getRegistration(); } return spResult; } mitk::MAPRegistrationWrapper::Pointer MITKAlgorithmHelper:: GetMITKRegistrationWrapper() const { map::core::RegistrationBase::Pointer spInternalResult = GetRegistration(); mitk::MAPRegistrationWrapper::Pointer spResult = mitk::MAPRegistrationWrapper::New(); spResult->SetRegistration(spInternalResult); return spResult; }; static const mitk::Image* GetDataAsImage(const mitk::BaseData* data) { return dynamic_cast(data); }; static const mitk::PointSet* GetDataAsPointSet(const mitk::BaseData* data) { return dynamic_cast(data); }; bool MITKAlgorithmHelper:: CheckData(const mitk::BaseData* moving, const mitk::BaseData* target, CheckError::Type& error) const { if (! m_AlgorithmBase) { mapDefaultExceptionStaticMacro( << "Error, cannot check data. Helper has no algorithm defined."); } if (! moving) { mapDefaultExceptionStaticMacro( << "Error, cannot check data. Moving data pointer is nullptr."); } if (! target) { mapDefaultExceptionStaticMacro( << "Error, cannot check data. Target data pointer is nullptr."); } bool result = false; m_Error = CheckError::unsupportedDataType; unsigned int movingDim = m_AlgorithmBase->getMovingDimensions(); unsigned int targetDim = m_AlgorithmBase->getTargetDimensions(); if (movingDim != targetDim) { m_Error = CheckError::wrongDimension; } else { //First check if data are point sets or images if (GetDataAsPointSet(target) && GetDataAsPointSet(moving)) { typedef ::map::core::continuous::Elements<3>::InternalPointSetType InternalDefaultPointSetType; typedef ::map::algorithm::facet::PointSetRegistrationAlgorithmInterface PointSetRegInterface; PointSetRegInterface* pPointSetInterface = dynamic_cast (m_AlgorithmBase.GetPointer()); if (!pPointSetInterface) { result = false; m_Error = CheckError::unsupportedDataType; } } else if (GetDataAsImage(moving) && GetDataAsImage(target)) { if (movingDim == 2) { AccessTwoImagesFixedDimensionByItk(GetDataAsImage(moving), GetDataAsImage(target), DoCheckImages, 2); } else if (movingDim == 3) { AccessTwoImagesFixedDimensionByItk(GetDataAsImage(moving), GetDataAsImage(target), DoCheckImages, 3); } else { m_Error = CheckError::wrongDimension; } if (m_Error == CheckError::none || (m_AllowImageCasting && m_Error == CheckError::onlyByCasting)) { result = true; } } } error = m_Error; return result; }; void MITKAlgorithmHelper::SetAllowImageCasting(bool allowCasting) { this->m_AllowImageCasting = allowCasting; }; bool MITKAlgorithmHelper::GetAllowImageCasting() const { return this->m_AllowImageCasting; }; void MITKAlgorithmHelper::SetData(const mitk::BaseData* moving, const mitk::BaseData* target) { if (! m_AlgorithmBase) { mapDefaultExceptionStaticMacro( << "Error, cannot check data. Helper has no algorithm defined."); } if (! moving) { mapDefaultExceptionStaticMacro( << "Error, cannot check data. Moving data pointer is nullptr."); } if (! target) { mapDefaultExceptionStaticMacro( << "Error, cannot check data. Target data pointer is nullptr."); } unsigned int movingDim = m_AlgorithmBase->getMovingDimensions(); unsigned int targetDim = m_AlgorithmBase->getTargetDimensions(); if (movingDim != targetDim) { mapDefaultExceptionStaticMacro( << "Error, cannot set data. Current version of MITKAlgorithmHelper only supports images/point sets with same dimensionality."); } if (GetDataAsPointSet(target) && GetDataAsPointSet(moving)) { typedef ::map::core::continuous::Elements<3>::InternalPointSetType InternalDefaultPointSetType; typedef ::map::algorithm::facet::PointSetRegistrationAlgorithmInterface PointSetRegInterface; PointSetRegInterface* pPointSetInterface = dynamic_cast (m_AlgorithmBase.GetPointer()); pPointSetInterface->setMovingPointSet(mitk::PointSetMappingHelper::ConvertPointSetMITKtoMAP( GetDataAsPointSet(moving)->GetPointSet())); pPointSetInterface->setTargetPointSet(mitk::PointSetMappingHelper::ConvertPointSetMITKtoMAP( GetDataAsPointSet(target)->GetPointSet())); } else if (GetDataAsImage(moving) && GetDataAsImage(target)) { if (movingDim == 2) { AccessTwoImagesFixedDimensionByItk(GetDataAsImage(moving), GetDataAsImage(target), DoSetImages, 2); } else if (movingDim == 3) { AccessTwoImagesFixedDimensionByItk(GetDataAsImage(moving), GetDataAsImage(target), DoSetImages, 3); } } }; template typename TOutImageType::Pointer MITKAlgorithmHelper::CastImage(const TInImageType* input) const { typedef itk::CastImageFilter< TInImageType, TOutImageType > CastFilterType; typename CastFilterType::Pointer spImageCaster = CastFilterType::New(); spImageCaster->SetInput(input); typename TOutImageType::Pointer spImage = spImageCaster->GetOutput(); spImageCaster->Update(); return spImage; } template void MITKAlgorithmHelper::DoSetImages(const itk::Image* moving, const itk::Image* target) { typedef itk::Image MovingImageType; typedef itk::Image TargetImageType; typedef itk::Image InternalDefaultMovingImageType; typedef itk::Image InternalDefaultTargetImageType; typedef ::map::algorithm::facet::ImageRegistrationAlgorithmInterface ImageRegInterface; typedef ::map::algorithm::facet::ImageRegistrationAlgorithmInterface DefaultImageRegInterface; ImageRegInterface* pImageInterface = dynamic_cast(m_AlgorithmBase.GetPointer()); DefaultImageRegInterface* pDefaultImageInterface = dynamic_cast (m_AlgorithmBase.GetPointer()); if (pImageInterface) { //just set directly and you are done /**@todo the duplication work arround is needed due to a insufficuence in the AccessTwoImagesFixedDimensionByItk macro. The macro always cast the passed image into non const (even if tha image was passed as const). This behavior enforces the unnecessary use of an writeaccessor, which as a consequence will lead to redundant access exceptions as long as the algorithm exists; e.g. in the typical scenario with the MatchPoint Plugins*/ typedef itk::ImageDuplicator< MovingImageType > MovingDuplicatorType; typedef itk::ImageDuplicator< TargetImageType > TargetDuplicatorType; typename MovingDuplicatorType::Pointer mDuplicator = MovingDuplicatorType::New(); mDuplicator->SetInputImage(moving); mDuplicator->Update(); typename TargetDuplicatorType::Pointer tDuplicator = TargetDuplicatorType::New(); tDuplicator->SetInputImage(target); tDuplicator->Update(); typename MovingImageType::Pointer clonedMoving = mDuplicator->GetOutput(); typename TargetImageType::Pointer clonedTarget = tDuplicator->GetOutput(); pImageInterface->setTargetImage(clonedTarget); pImageInterface->setMovingImage(clonedMoving); } else if (pDefaultImageInterface) { //you may convert it to the default image type and use it then if (! m_AllowImageCasting) { mapDefaultExceptionStaticMacro( << "Error, cannot set images. MITKAlgorithmHelper has to convert them into MatchPoint default images, but is not allowed. Please reconfigure helper."); } typename InternalDefaultTargetImageType::Pointer spCastedTarget = CastImage(target); typename InternalDefaultMovingImageType::Pointer spCastedMoving = CastImage(moving); pDefaultImageInterface->setTargetImage(spCastedTarget); pDefaultImageInterface->setMovingImage(spCastedMoving); } else { mapDefaultExceptionStaticMacro( << "Error, algorithm is not able to use the based images."); } } template void MITKAlgorithmHelper::DoCheckImages(const itk::Image* /*moving*/, const itk::Image* /*target*/) const { typedef itk::Image MovingImageType; typedef itk::Image TargetImageType; typedef itk::Image InternalDefaultMovingImageType; typedef itk::Image InternalDefaultTargetImageType; typedef ::map::algorithm::facet::ImageRegistrationAlgorithmInterface ImageRegInterface; typedef ::map::algorithm::facet::ImageRegistrationAlgorithmInterface DefaultImageRegInterface; ImageRegInterface* pImageInterface = dynamic_cast(m_AlgorithmBase.GetPointer()); DefaultImageRegInterface* pDefaultImageInterface = dynamic_cast (m_AlgorithmBase.GetPointer()); if (pImageInterface) { //just set directly and you are done m_Error = CheckError::none; } else if (pDefaultImageInterface) { //you may convert it to the default image type and use it then m_Error = CheckError::onlyByCasting; } else { m_Error = CheckError::unsupportedDataType; } } mapGenerateAlgorithmUIDPolicyMacro(DummyRegIDPolicy, "de.dkfz.dipp", "Identity", "1.0.0", ""); mitk::MAPRegistrationWrapper::Pointer GenerateIdentityRegistration3D() { typedef map::algorithm::DummyImageRegistrationAlgorithm::InternalImageType, map::core::discrete::Elements<3>::InternalImageType, DummyRegIDPolicy> DummyRegType; DummyRegType::Pointer regAlg = DummyRegType::New(); mitk::MITKAlgorithmHelper helper(regAlg); map::core::discrete::Elements<3>::InternalImageType::Pointer dummyImg = map::core::discrete::Elements<3>::InternalImageType::New(); dummyImg->Allocate(); regAlg->setTargetImage(dummyImg); regAlg->setMovingImage(dummyImg); mitk::MAPRegistrationWrapper::Pointer dummyReg = mitk::MAPRegistrationWrapper::New(); dummyReg->SetRegistration(regAlg->getRegistration()); return dummyReg; } } diff --git a/Modules/MatchPointRegistration/Rendering/mitkRegEvalStyleProperty.h b/Modules/MatchPointRegistration/Rendering/mitkRegEvalStyleProperty.h index 00113c15ca..56de51dda9 100644 --- a/Modules/MatchPointRegistration/Rendering/mitkRegEvalStyleProperty.h +++ b/Modules/MatchPointRegistration/Rendering/mitkRegEvalStyleProperty.h @@ -1,100 +1,100 @@ /*=================================================================== 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 _MITK_REG_EVAL_STYLE_PROPERTY__H_ #define _MITK_REG_EVAL_STYLE_PROPERTY__H_ // MITK #include // MITK #include "MitkMatchPointRegistrationExports.h" namespace mitk { #ifdef _MSC_VER # pragma warning(push) # pragma warning(disable: 4522) #endif /** * Encapsulates the enumeration for visualization styles. Valid values are: * 0/Blend 1/Color Blend 2/Checkerboard, 3/Wipe, 4/Difference, 5/Contour * Default is "Blend" */ class MITKMATCHPOINTREGISTRATION_EXPORT RegEvalStyleProperty : public EnumerationProperty { public: mitkClassMacro( RegEvalStyleProperty, EnumerationProperty ); itkNewMacro(RegEvalStyleProperty); mitkNewMacro1Param(RegEvalStyleProperty, const IdType&); mitkNewMacro1Param(RegEvalStyleProperty, const std::string&); using BaseProperty::operator=; protected: /** * Constructor. Sets the representation to a default value of 0 */ RegEvalStyleProperty( ); /** * Constructor. Sets the enumeration to the given value. If it is not * valid, the enumeration is set to 0 */ RegEvalStyleProperty( const IdType& value ); /** * Constructor. Sets the enumeration to the given value. If it is not * valid, the enumeration is set to 0 */ RegEvalStyleProperty( const std::string& value ); /** * this function is overridden as protected, so that the user may not add * additional invalid interpolation types. */ bool AddEnum( const std::string& name, const IdType& id ) override; /** * Adds the enumeration types */ - virtual void AddTypes(); + void AddTypes(); private: // purposely not implemented RegEvalStyleProperty(const RegEvalStyleProperty&); RegEvalStyleProperty& operator=(const RegEvalStyleProperty&); }; #ifdef _MSC_VER # pragma warning(pop) #endif } // end of namespace mitk #endif diff --git a/Modules/MatchPointRegistration/Rendering/mitkRegEvalWipeStyleProperty.h b/Modules/MatchPointRegistration/Rendering/mitkRegEvalWipeStyleProperty.h index f50f6df220..4f73bc8059 100644 --- a/Modules/MatchPointRegistration/Rendering/mitkRegEvalWipeStyleProperty.h +++ b/Modules/MatchPointRegistration/Rendering/mitkRegEvalWipeStyleProperty.h @@ -1,100 +1,100 @@ /*=================================================================== 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 _MITK_REG_EVAL_WIPE_STYLE_PROPERTY__H_ #define _MITK_REG_EVAL_WIPE_STYLE_PROPERTY__H_ // MITK #include // MITK #include "MitkMatchPointRegistrationExports.h" namespace mitk { #ifdef _MSC_VER # pragma warning(push) # pragma warning(disable: 4522) #endif /** * Encapsulates the enumeration for visualization styles. Valid values are: * 0/Cross 1/horizontal wipe 2/vertical wipe * Default is "Cross" */ class MITKMATCHPOINTREGISTRATION_EXPORT RegEvalWipeStyleProperty : public EnumerationProperty { public: mitkClassMacro( RegEvalWipeStyleProperty, EnumerationProperty ); itkNewMacro(RegEvalWipeStyleProperty); mitkNewMacro1Param(RegEvalWipeStyleProperty, const IdType&); mitkNewMacro1Param(RegEvalWipeStyleProperty, const std::string&); using BaseProperty::operator=; protected: /** * Constructor. Sets the representation to a default value of 0 */ RegEvalWipeStyleProperty( ); /** * Constructor. Sets the enumeration to the given value. If it is not * valid, the enumeration is set to 0 */ RegEvalWipeStyleProperty( const IdType& value ); /** * Constructor. Sets the enumeration to the given value. If it is not * valid, the enumeration is set to 0 */ RegEvalWipeStyleProperty( const std::string& value ); /** * this function is overridden as protected, so that the user may not add * additional invalid interpolation types. */ bool AddEnum( const std::string& name, const IdType& id ) override; /** * Adds the enumeration types */ - virtual void AddTypes(); + void AddTypes(); private: // purposely not implemented RegEvalWipeStyleProperty(const RegEvalWipeStyleProperty&); RegEvalWipeStyleProperty& operator=(const RegEvalWipeStyleProperty&); }; #ifdef _MSC_VER # pragma warning(pop) #endif } // end of namespace mitk #endif diff --git a/Modules/MatchPointRegistration/Rendering/mitkRegEvaluationMapper2D.cpp b/Modules/MatchPointRegistration/Rendering/mitkRegEvaluationMapper2D.cpp index ea6be768d4..e58cd97045 100644 --- a/Modules/MatchPointRegistration/Rendering/mitkRegEvaluationMapper2D.cpp +++ b/Modules/MatchPointRegistration/Rendering/mitkRegEvaluationMapper2D.cpp @@ -1,840 +1,840 @@ /*=================================================================== 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 "mitkImageStatisticsHolder.h" #include "mitkPlaneClipping.h" #include "mitkRegVisPropertyTags.h" #include "mitkRegVisHelper.h" #include "mitkRegEvalStyleProperty.h" #include "mitkRegEvalWipeStyleProperty.h" //MITK Rendering #include "mitkRegEvaluationMapper2D.h" #include "vtkMitkThickSlicesFilter.h" #include "vtkMitkLevelWindowFilter.h" #include "vtkNeverTranslucentTexture.h" //VTK #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include //ITK #include #include //MatchPoint #include #include mitk::RegEvaluationMapper2D::RegEvaluationMapper2D() { } mitk::RegEvaluationMapper2D::~RegEvaluationMapper2D() { } //set the two points defining the textured plane according to the dimension and spacing void mitk::RegEvaluationMapper2D::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::RegEvaluationMapper2D::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::RegEvaluationMapper2D::GetTargetImage( void ) { const mitk::RegEvaluationObject* evalObj = dynamic_cast< const mitk::RegEvaluationObject* >( GetDataNode()->GetData() ); if (evalObj) { return evalObj->GetTargetImage(); } return nullptr; } const mitk::Image* mitk::RegEvaluationMapper2D::GetMovingImage( void ) { const mitk::RegEvaluationObject* evalObj = dynamic_cast< const mitk::RegEvaluationObject* >( GetDataNode()->GetData() ); if (evalObj) { return evalObj->GetMovingImage(); } return nullptr; } const mitk::DataNode* mitk::RegEvaluationMapper2D::GetTargetNode(void) { const mitk::RegEvaluationObject* evalObj = dynamic_cast< const mitk::RegEvaluationObject* >(GetDataNode()->GetData()); if (evalObj) { return evalObj->GetTargetNode(); } return nullptr; } const mitk::DataNode* mitk::RegEvaluationMapper2D::GetMovingNode(void) { const mitk::RegEvaluationObject* evalObj = dynamic_cast< const mitk::RegEvaluationObject* >(GetDataNode()->GetData()); if (evalObj) { return evalObj->GetMovingNode(); } return nullptr; } const mitk::MAPRegistrationWrapper* mitk::RegEvaluationMapper2D::GetRegistration( void ) { const mitk::RegEvaluationObject* evalObj = dynamic_cast< const mitk::RegEvaluationObject* >( GetDataNode()->GetData() ); if (evalObj) { return evalObj->GetRegistration(); } return nullptr; } vtkProp* mitk::RegEvaluationMapper2D::GetVtkProp(mitk::BaseRenderer* renderer) { //return the actor corresponding to the renderer return m_LSH.GetLocalStorage(renderer)->m_Actors; } void mitk::RegEvaluationMapper2D::GenerateDataForRenderer( mitk::BaseRenderer *renderer ) { bool updated = false; LocalStorage *localStorage = m_LSH.GetLocalStorage(renderer); mitk::Image::Pointer targetInput = const_cast< mitk::Image * >( this->GetTargetImage() ); mitk::DataNode* datanode = this->GetDataNode(); if ( targetInput.IsNull() || targetInput->IsInitialized() == false ) { return; } mitk::Image::ConstPointer movingInput = this->GetMovingImage(); if ( movingInput.IsNull() || movingInput->IsInitialized() == false ) { return; } mitk::MAPRegistrationWrapper::ConstPointer reg = this->GetRegistration(); //check if there is a valid worldGeometry const PlaneGeometry *worldGeometry = renderer->GetCurrentWorldPlaneGeometry(); if( ( worldGeometry == nullptr ) || ( !worldGeometry->IsValid() ) || ( !worldGeometry->HasReferenceGeometry() )) { return; } if(targetInput->GetMTime()>localStorage->m_LastUpdateTime || (localStorage->m_LastUpdateTime < renderer->GetCurrentWorldPlaneGeometryUpdateTime()) //was the geometry modified? || (localStorage->m_LastUpdateTime < renderer->GetCurrentWorldPlaneGeometry()->GetMTime())) { //target input has been modified -> reslice target input targetInput->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, targetInput->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_EvaluationImage = nullptr; localStorage->m_Mapper->SetInputData( localStorage->m_EmptyPolyData ); return; } //set main input for ExtractSliceFilter localStorage->m_Reslicer->SetInput(targetInput); 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( targetInput->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 ( (targetInput->GetDimension() >= 3) && (targetInput->GetDimension(2) > 1) ) { VtkResliceInterpolationProperty *resliceInterpolationProperty; datanode->GetProperty( resliceInterpolationProperty, "reslice interpolation" ); 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); } //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_slicedTargetImage = localStorage->m_Reslicer->GetOutput(); updated = true; } if(updated || movingInput->GetMTime() > localStorage->m_LastUpdateTime || reg->GetMTime() > localStorage->m_LastUpdateTime) { //Map moving image localStorage->m_slicedMappedImage = mitk::ImageMappingHelper::map(movingInput,reg,false,0,localStorage->m_slicedTargetImage->GetGeometry(),false,0); updated = true; } // Bounds information for reslicing (only required if reference geometry // is present) //this used for generating a vtkPLaneSource with the right size double sliceBounds[6] = { 0.0, 0.0, 0.0, 0.0, 0.0, 0.0 }; if (updated || (localStorage->m_LastUpdateTime < datanode->GetPropertyList()->GetMTime()) //was a property modified? || (localStorage->m_LastUpdateTime < datanode->GetPropertyList(renderer)->GetMTime()) || (localStorage->m_LastUpdateTime < this->GetTargetNode()->GetMTime()) || (localStorage->m_LastUpdateTime < this->GetMovingNode()->GetMTime())) { 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] = { 0.0, 0.0, 0.0, 0.0, 0.0, 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. const PlaneGeometry *planeGeometry = dynamic_cast(worldGeometry); mitk::PlaneClipping::CalculateClippedPlaneBounds(targetInput->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_TargetLevelWindowFilter->SetClippingBounds(textureClippingBounds); localStorage->m_MappedLevelWindowFilter->SetClippingBounds(textureClippingBounds); } this->ApplyLookuptable(renderer, this->GetTargetNode(), localStorage->m_TargetLevelWindowFilter); this->ApplyLookuptable(renderer, this->GetMovingNode(), localStorage->m_MappedLevelWindowFilter); this->ApplyLevelWindow(renderer, this->GetTargetNode(), localStorage->m_TargetLevelWindowFilter); this->ApplyLevelWindow(renderer, this->GetMovingNode(), localStorage->m_MappedLevelWindowFilter); //connect the input with the levelwindow filter localStorage->m_TargetLevelWindowFilter->SetInputData(localStorage->m_slicedTargetImage->GetVtkImageData()); localStorage->m_MappedLevelWindowFilter->SetInputData(localStorage->m_slicedMappedImage->GetVtkImageData()); localStorage->m_TargetExtractFilter->SetInputConnection(localStorage->m_TargetLevelWindowFilter->GetOutputPort()); localStorage->m_MappedExtractFilter->SetInputConnection(localStorage->m_MappedLevelWindowFilter->GetOutputPort()); localStorage->m_TargetExtractFilter->SetComponents(0); localStorage->m_MappedExtractFilter->SetComponents(0); updated = true; } //Generate evaluation image bool isStyleOutdated = mitk::PropertyIsOutdated(datanode,mitk::nodeProp_RegEvalStyle,localStorage->m_LastUpdateTime); bool isBlendOutdated = mitk::PropertyIsOutdated(datanode,mitk::nodeProp_RegEvalBlendFactor,localStorage->m_LastUpdateTime); bool isCheckerOutdated = mitk::PropertyIsOutdated(datanode,mitk::nodeProp_RegEvalCheckerCount,localStorage->m_LastUpdateTime); bool isWipeStyleOutdated = mitk::PropertyIsOutdated(datanode,mitk::nodeProp_RegEvalWipeStyle,localStorage->m_LastUpdateTime); bool isContourOutdated = mitk::PropertyIsOutdated(datanode,mitk::nodeProp_RegEvalTargetContour,localStorage->m_LastUpdateTime); bool isPositionOutdated = mitk::PropertyIsOutdated(datanode, mitk::nodeProp_RegEvalCurrentPosition, localStorage->m_LastUpdateTime); if (updated || isStyleOutdated || isBlendOutdated || isCheckerOutdated || isWipeStyleOutdated || isContourOutdated || isPositionOutdated) { mitk::RegEvalStyleProperty::Pointer evalStyleProp = mitk::RegEvalStyleProperty::New(); datanode->GetProperty(evalStyleProp, mitk::nodeProp_RegEvalStyle); switch (evalStyleProp->GetValueAsId()) { case 0 : { PrepareBlend(datanode, localStorage); break; } case 1 : { PrepareColorBlend(localStorage); break; } case 2 : { PrepareCheckerBoard(datanode, localStorage); break; } case 3 : { const PlaneGeometry *worldGeometry = renderer->GetCurrentWorldPlaneGeometry(); Point3D currentPos3D; datanode->GetPropertyValue(mitk::nodeProp_RegEvalCurrentPosition, currentPos3D); Point2D currentPos2D; worldGeometry->Map(currentPos3D, currentPos2D); Point2D currentIndex2D; worldGeometry->WorldToIndex(currentPos2D, currentIndex2D); PrepareWipe(datanode, localStorage, currentIndex2D); break; } case 4 : { PrepareDifference(localStorage); break; } case 5 : { PrepareContour(datanode, localStorage); break; } } updated = true; } if(updated || (localStorage->m_LastUpdateTime < datanode->GetPropertyList()->GetMTime()) //was a property modified? || (localStorage->m_LastUpdateTime < datanode->GetPropertyList(renderer)->GetMTime()) ) { this->ApplyOpacity( renderer ); // do not use a VTK lookup table (we do that ourselves in m_LevelWindowFilter) localStorage->m_Texture->SetColorModeToDirectScalars(); // 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->SetInputData(localStorage->m_EvaluationImage); this->TransformActor( renderer ); vtkActor* contourShadowActor = dynamic_cast (localStorage->m_Actors->GetParts()->GetItemAsObject(0)); //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::RegEvaluationMapper2D::PrepareContour( mitk::DataNode* datanode, LocalStorage * localStorage ) { bool targetContour = true; datanode->GetBoolProperty(mitk::nodeProp_RegEvalTargetContour,targetContour); vtkSmartPointer magFilter = vtkSmartPointer::New(); if(targetContour) { magFilter->SetInputConnection(localStorage->m_TargetExtractFilter->GetOutputPort()); } else { magFilter->SetInputConnection(localStorage->m_MappedExtractFilter->GetOutputPort()); } vtkSmartPointer appendFilter = vtkSmartPointer::New(); appendFilter->AddInputConnection(magFilter->GetOutputPort()); appendFilter->AddInputConnection(magFilter->GetOutputPort()); if(targetContour) { appendFilter->AddInputConnection(localStorage->m_MappedExtractFilter->GetOutputPort()); } else { appendFilter->AddInputConnection(localStorage->m_TargetExtractFilter->GetOutputPort()); } appendFilter->Update(); localStorage->m_EvaluationImage = appendFilter->GetOutput(); } void mitk::RegEvaluationMapper2D::PrepareDifference( LocalStorage * localStorage ) { vtkSmartPointer diffFilter = vtkSmartPointer::New(); vtkSmartPointer minFilter = vtkSmartPointer::New(); vtkSmartPointer maxFilter = vtkSmartPointer::New(); minFilter->SetInputConnection(0, localStorage->m_TargetExtractFilter->GetOutputPort()); minFilter->SetInputConnection(1, localStorage->m_MappedExtractFilter->GetOutputPort()); minFilter->SetOperationToMin(); maxFilter->SetInputConnection(0, localStorage->m_TargetExtractFilter->GetOutputPort()); maxFilter->SetInputConnection(1, localStorage->m_MappedExtractFilter->GetOutputPort()); maxFilter->SetOperationToMax(); diffFilter->SetInputConnection(0, maxFilter->GetOutputPort()); diffFilter->SetInputConnection(1, minFilter->GetOutputPort()); diffFilter->SetOperationToSubtract(); diffFilter->Update(); localStorage->m_EvaluationImage = diffFilter->GetOutput(); } void mitk::RegEvaluationMapper2D::PrepareWipe(mitk::DataNode* datanode, LocalStorage * localStorage, const Point2D& currentIndex2D) { mitk::RegEvalWipeStyleProperty::Pointer evalWipeStyleProp = mitk::RegEvalWipeStyleProperty::New(); datanode->GetProperty(evalWipeStyleProp, mitk::nodeProp_RegEvalWipeStyle); vtkSmartPointer wipedFilter = vtkSmartPointer::New(); wipedFilter->SetInputConnection(0, localStorage->m_TargetLevelWindowFilter->GetOutputPort()); wipedFilter->SetInputConnection(1, localStorage->m_MappedLevelWindowFilter->GetOutputPort()); wipedFilter->SetPosition(currentIndex2D[0], currentIndex2D[1]); if (evalWipeStyleProp->GetValueAsId() == 0) { wipedFilter->SetWipeToQuad(); } else if (evalWipeStyleProp->GetValueAsId() == 1) { wipedFilter->SetWipeToHorizontal(); } else if (evalWipeStyleProp->GetValueAsId() == 2) { wipedFilter->SetWipeToVertical(); } wipedFilter->Update(); localStorage->m_EvaluationImage = wipedFilter->GetOutput(); } void mitk::RegEvaluationMapper2D::PrepareCheckerBoard( mitk::DataNode* datanode, LocalStorage * localStorage ) { int checkerCount = 5; datanode->GetIntProperty(mitk::nodeProp_RegEvalCheckerCount,checkerCount); vtkSmartPointer checkerboardFilter = vtkSmartPointer::New(); checkerboardFilter->SetInputConnection(0, localStorage->m_TargetLevelWindowFilter->GetOutputPort()); checkerboardFilter->SetInputConnection(1, localStorage->m_MappedLevelWindowFilter->GetOutputPort()); checkerboardFilter->SetNumberOfDivisions(checkerCount, checkerCount, 1); checkerboardFilter->Update(); localStorage->m_EvaluationImage = checkerboardFilter->GetOutput(); } void mitk::RegEvaluationMapper2D::PrepareColorBlend( LocalStorage * localStorage ) { vtkSmartPointer appendFilter = vtkSmartPointer::New(); //red channel appendFilter->AddInputConnection(localStorage->m_MappedExtractFilter->GetOutputPort()); //green channel appendFilter->AddInputConnection(localStorage->m_MappedExtractFilter->GetOutputPort()); //blue channel appendFilter->AddInputConnection(localStorage->m_TargetExtractFilter->GetOutputPort()); appendFilter->Update(); localStorage->m_EvaluationImage = appendFilter->GetOutput(); } void mitk::RegEvaluationMapper2D::PrepareBlend( mitk::DataNode* datanode, LocalStorage * localStorage ) { int blendfactor = 50; datanode->GetIntProperty(mitk::nodeProp_RegEvalBlendFactor,blendfactor); vtkSmartPointer blendFilter = vtkSmartPointer::New(); blendFilter->AddInputConnection(localStorage->m_TargetExtractFilter->GetOutputPort()); blendFilter->AddInputConnection(localStorage->m_MappedExtractFilter->GetOutputPort()); blendFilter->SetWeight(0, (100 - blendfactor) / 100.); blendFilter->SetWeight(1,blendfactor/100.); blendFilter->Update(); localStorage->m_EvaluationImage = blendFilter->GetOutput(); } void mitk::RegEvaluationMapper2D::ApplyLevelWindow(mitk::BaseRenderer *renderer, const mitk::DataNode* dataNode, vtkMitkLevelWindowFilter* levelFilter) { LevelWindow levelWindow; dataNode->GetLevelWindow(levelWindow, renderer, "levelwindow"); levelFilter->GetLookupTable()->SetRange(levelWindow.GetLowerWindowBound(), levelWindow.GetUpperWindowBound()); mitk::LevelWindow opacLevelWindow; if (dataNode->GetLevelWindow(opacLevelWindow, renderer, "opaclevelwindow")) { //pass the opaque level window to the filter levelFilter->SetMinOpacity(opacLevelWindow.GetLowerWindowBound()); levelFilter->SetMaxOpacity(opacLevelWindow.GetUpperWindowBound()); } else { //no opaque level window levelFilter->SetMinOpacity(0.0); levelFilter->SetMaxOpacity(255.0); } } void mitk::RegEvaluationMapper2D::ApplyLookuptable(mitk::BaseRenderer* renderer, const mitk::DataNode* dataNode, vtkMitkLevelWindowFilter* levelFilter) { 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(dataNode->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. } levelFilter->SetLookupTable(usedLookupTable); } void mitk::RegEvaluationMapper2D::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::RegEvaluationMapper2D::Update(mitk::BaseRenderer* renderer) { bool visible = true; GetDataNode()->GetVisibility(visible, renderer, "visible"); if ( !visible ) { return; } mitk::Image* data = const_cast( this->GetTargetImage() ); 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()) || (localStorage->m_LastUpdateTime < this->GetTargetNode()->GetMTime()) //was the target node modified? || (localStorage->m_LastUpdateTime < this->GetMovingNode()->GetMTime()) //was the moving node modified? || (localStorage->m_LastUpdateTime < this->GetTargetNode()->GetPropertyList()->GetMTime()) //was a target node property modified? || (localStorage->m_LastUpdateTime < this->GetTargetNode()->GetPropertyList(renderer)->GetMTime()) || (localStorage->m_LastUpdateTime < this->GetMovingNode()->GetPropertyList()->GetMTime()) //was a moving node property modified? || (localStorage->m_LastUpdateTime < this->GetMovingNode()->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::RegEvaluationMapper2D::SetDefaultProperties(mitk::DataNode* node, mitk::BaseRenderer* renderer, bool overwrite) { mitk::RegEvaluationObject* regEval = dynamic_cast(node->GetData()); if(!regEval) { return; } // Properties common for both images and segmentations node->AddProperty( "depthOffset", mitk::FloatProperty::New( 0.0 ), renderer, overwrite ); if(regEval->GetTargetImage() && regEval->GetTargetImage()->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 ) ); // set to user configurable default value (see global options) 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(); mitk::LookupTableProperty::Pointer mitkLutProp = mitk::LookupTableProperty::New(); mitkLutProp->SetLookupTable(mitkLut); node->SetProperty("LookupTable", mitkLutProp); 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); node->AddProperty(mitk::nodeProp_RegEvalStyle, mitk::RegEvalStyleProperty::New(0), renderer, overwrite); node->AddProperty(mitk::nodeProp_RegEvalBlendFactor, mitk::IntProperty::New(50), renderer, overwrite); node->AddProperty(mitk::nodeProp_RegEvalCheckerCount, mitk::IntProperty::New(3), renderer, overwrite); node->AddProperty(mitk::nodeProp_RegEvalTargetContour, mitk::BoolProperty::New(true), renderer, overwrite); node->AddProperty(mitk::nodeProp_RegEvalWipeStyle, mitk::RegEvalWipeStyleProperty::New(0), renderer, overwrite); node->AddProperty(mitk::nodeProp_RegEvalCurrentPosition, mitk::GenericProperty::New(mitk::Point3D()), renderer, overwrite); Superclass::SetDefaultProperties(node, renderer, overwrite); } mitk::RegEvaluationMapper2D::LocalStorage* mitk::RegEvaluationMapper2D::GetLocalStorage(mitk::BaseRenderer* renderer) { return m_LSH.GetLocalStorage(renderer); } void mitk::RegEvaluationMapper2D::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) ); secondaryActor->SetUserTransform(trans); secondaryActor->SetPosition( -0.5*localStorage->m_mmPerPixel[0], -0.5*localStorage->m_mmPerPixel[1], 0.0); } } bool mitk::RegEvaluationMapper2D::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::RegEvaluationMapper2D::LocalStorage::~LocalStorage() { } mitk::RegEvaluationMapper2D::LocalStorage::LocalStorage() { m_TargetLevelWindowFilter = vtkSmartPointer::New(); m_MappedLevelWindowFilter = vtkSmartPointer::New(); m_TargetExtractFilter = vtkSmartPointer::New(); m_MappedExtractFilter = vtkSmartPointer::New(); - + m_mmPerPixel = nullptr; //Do as much actions as possible in here to avoid double executions. m_Plane = vtkSmartPointer::New(); //m_Texture = vtkSmartPointer::New().GetPointer(); m_Texture = vtkSmartPointer::New().GetPointer(); m_DefaultLookupTable = vtkSmartPointer::New(); m_ColorLookupTable = vtkSmartPointer::New(); m_Mapper = vtkSmartPointer::New(); m_Actor = vtkSmartPointer::New(); m_Actors = vtkSmartPointer::New(); m_Reslicer = mitk::ExtractSliceFilter::New(); m_EvaluationImage = vtkSmartPointer::New(); m_EmptyPolyData = vtkSmartPointer::New(); mitk::LookupTable::Pointer mitkLUT = mitk::LookupTable::New(); //built a default lookuptable mitkLUT->SetType(mitk::LookupTable::GRAYSCALE); m_DefaultLookupTable = mitkLUT->GetVtkLookupTable(); mitkLUT->SetType(mitk::LookupTable::JET); 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/MatchPointRegistration/Rendering/mitkRegVisColorStyleProperty.h b/Modules/MatchPointRegistration/Rendering/mitkRegVisColorStyleProperty.h index 9dcb291ceb..f504ce3dac 100644 --- a/Modules/MatchPointRegistration/Rendering/mitkRegVisColorStyleProperty.h +++ b/Modules/MatchPointRegistration/Rendering/mitkRegVisColorStyleProperty.h @@ -1,98 +1,98 @@ /*=================================================================== 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 _MITK_REG_VIS_COLOR_STYLE_PROPERTY__H_ #define _MITK_REG_VIS_COLOR_STYLE_PROPERTY__H_ // MITK #include // MITK #include "MitkMatchPointRegistrationExports.h" namespace mitk { #ifdef _MSC_VER # pragma warning(push) # pragma warning(disable: 4522) #endif /** * Encapsulates the enumeration for direction for the registration visualization. Valid values are: * 0/UniColor, 1/VectorMagnitude * Default is "UniColor" */ class MITKMATCHPOINTREGISTRATION_EXPORT RegVisColorStyleProperty : public EnumerationProperty { public: mitkClassMacro( RegVisColorStyleProperty, EnumerationProperty ); itkNewMacro(RegVisColorStyleProperty); mitkNewMacro1Param(RegVisColorStyleProperty, const IdType&); mitkNewMacro1Param(RegVisColorStyleProperty, const std::string&); using BaseProperty::operator=; protected: /** * Constructor. Sets the representation to a default value of 0 */ RegVisColorStyleProperty( ); /** * Constructor. Sets the enumeration to the given value. If it is not * valid, the enumeration is set to 0 */ RegVisColorStyleProperty( const IdType& value ); /** * Constructor. Sets the enumeration to the given value. If it is not * valid, the enumeration is set to 0 */ RegVisColorStyleProperty( const std::string& value ); /** * this function is overridden as protected, so that the user may not add * additional invalid interpolation types. */ bool AddEnum( const std::string& name, const IdType& id ) override; /** * Adds the enumeration types */ - virtual void AddTypes(); + void AddTypes(); private: // purposely not implemented RegVisColorStyleProperty(const RegVisColorStyleProperty&); RegVisColorStyleProperty& operator=(const RegVisColorStyleProperty&); }; #ifdef _MSC_VER # pragma warning(pop) #endif } // end of namespace mitk #endif diff --git a/Modules/MatchPointRegistration/Rendering/mitkRegVisDirectionProperty.h b/Modules/MatchPointRegistration/Rendering/mitkRegVisDirectionProperty.h index 4d1d9d6ec9..94c012c539 100644 --- a/Modules/MatchPointRegistration/Rendering/mitkRegVisDirectionProperty.h +++ b/Modules/MatchPointRegistration/Rendering/mitkRegVisDirectionProperty.h @@ -1,98 +1,98 @@ /*=================================================================== 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 _MITK_REG_VIS_DIRECTION_PROPERTY__H_ #define _MITK_REG_VIS_DIRECTION_PROPERTY__H_ // MITK #include // MITK #include "MitkMatchPointRegistrationExports.h" namespace mitk { #ifdef _MSC_VER # pragma warning(push) # pragma warning(disable: 4522) #endif /** * Encapsulates the enumeration for direction for the registration visualization. Valid values are: * 0/Direct, 1/Inverse * Default is "Inverse" */ class MITKMATCHPOINTREGISTRATION_EXPORT RegVisDirectionProperty : public EnumerationProperty { public: mitkClassMacro( RegVisDirectionProperty, EnumerationProperty ); itkNewMacro(RegVisDirectionProperty); mitkNewMacro1Param(RegVisDirectionProperty, const IdType&); mitkNewMacro1Param(RegVisDirectionProperty, const std::string&); using BaseProperty::operator=; protected: /** * Constructor. Sets the representation to a default value of 1 */ RegVisDirectionProperty( ); /** * Constructor. Sets the enumeration to the given value. If it is not * valid, the enumeration is set to 1 */ RegVisDirectionProperty( const IdType& value ); /** * Constructor. Sets the enumeration to the given value. If it is not * valid, the enumeration is set to 1 */ RegVisDirectionProperty( const std::string& value ); /** * this function is overridden as protected, so that the user may not add * additional invalid interpolation types. */ bool AddEnum( const std::string& name, const IdType& id ) override; /** * Adds the enumeration types */ - virtual void AddTypes(); + void AddTypes(); private: // purposely not implemented RegVisDirectionProperty(const RegVisDirectionProperty&); RegVisDirectionProperty& operator=(const RegVisDirectionProperty&); }; #ifdef _MSC_VER # pragma warning(pop) #endif } // end of namespace mitk #endif diff --git a/Modules/MatchPointRegistration/Rendering/mitkRegVisStyleProperty.h b/Modules/MatchPointRegistration/Rendering/mitkRegVisStyleProperty.h index 046cd78df7..11f9b1ab0e 100644 --- a/Modules/MatchPointRegistration/Rendering/mitkRegVisStyleProperty.h +++ b/Modules/MatchPointRegistration/Rendering/mitkRegVisStyleProperty.h @@ -1,100 +1,100 @@ /*=================================================================== 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 _MITK_REG_VIS_STYLE_PROPERTY__H_ #define _MITK_REG_VIS_STYLE_PROPERTY__H_ // MITK #include // MITK #include "MitkMatchPointRegistrationExports.h" namespace mitk { #ifdef _MSC_VER # pragma warning(push) # pragma warning(disable: 4522) #endif /** * Encapsulates the enumeration for visualization styles. Valid values are: * 0/Grid, 1/Glyph, 2/Points * Default is "Grid" */ class MITKMATCHPOINTREGISTRATION_EXPORT RegVisStyleProperty : public EnumerationProperty { public: mitkClassMacro( RegVisStyleProperty, EnumerationProperty ); itkNewMacro(RegVisStyleProperty); mitkNewMacro1Param(RegVisStyleProperty, const IdType&); mitkNewMacro1Param(RegVisStyleProperty, const std::string&); using BaseProperty::operator=; protected: /** * Constructor. Sets the representation to a default value of 0 */ RegVisStyleProperty( ); /** * Constructor. Sets the enumeration to the given value. If it is not * valid, the enumeration is set to 0 */ RegVisStyleProperty( const IdType& value ); /** * Constructor. Sets the enumeration to the given value. If it is not * valid, the enumeration is set to 0 */ RegVisStyleProperty( const std::string& value ); /** * this function is overridden as protected, so that the user may not add * additional invalid interpolation types. */ bool AddEnum( const std::string& name, const IdType& id ) override; /** * Adds the enumeration types */ - virtual void AddTypes(); + void AddTypes(); private: // purposely not implemented RegVisStyleProperty(const RegVisStyleProperty&); RegVisStyleProperty& operator=(const RegVisStyleProperty&); }; #ifdef _MSC_VER # pragma warning(pop) #endif } // end of namespace mitk #endif diff --git a/Modules/ModelFit/include/mitkModelFitProviderBase.tpp b/Modules/ModelFit/include/mitkModelFitProviderBase.tpp index 7554a4c4e7..3401d50ce7 100644 --- a/Modules/ModelFit/include/mitkModelFitProviderBase.tpp +++ b/Modules/ModelFit/include/mitkModelFitProviderBase.tpp @@ -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 #include #include #include #include #include #include namespace mitk { template class ModelFitProviderBase::Impl { public: - Impl() : m_ReferenceFactory(TModelFactory::New()) + Impl() : m_Ranking(0), m_ReferenceFactory(TModelFactory::New()) { }; Impl(const Impl &other) = default; void SetRanking(int ranking) { m_Ranking = ranking; }; int GetRanking() const { return m_Ranking; }; us::ServiceRegistration m_Reg; int m_Ranking; using ModelFactoryType = TModelFactory; typename ModelFactoryType::Pointer m_ReferenceFactory; }; template ModelFitProviderBase::ModelFitProviderBase() : d() { } template ModelFitProviderBase::~ModelFitProviderBase() { UnregisterService(); } template ModelFitProviderBase::ModelFitProviderBase(const ModelFitProviderBase &other) : IModelFitProvider(), d(new Impl(*other.d.get())) { } template itk::SmartPointer ModelFitProviderBase::GenerateFactory() const { return TModelFactory::New().GetPointer(); }; template ModelBase::TimeGridType ModelFitProviderBase::GetVariableGrid(const modelFit::ModelFitInfo* fitInfo) const { if (!fitInfo) { mitkThrow() << "Error. Cannot get variable grid for model. Passed model fit info is null."; } if (!fitInfo->inputImage.IsNotNull()) { mitkThrow() << "Error. Cannot get variable grid for model. Passed model fit info has no input image."; } return mitk::ExtractTimeGrid(fitInfo->inputImage); }; template us::ServiceRegistration ModelFitProviderBase::RegisterService(us::ModuleContext *context) { if (d) return d->m_Reg; if (context == nullptr) { context = us::GetModuleContext(); } d.reset(new Impl()); us::ServiceProperties props = this->GetServiceProperties(); d->m_Reg = context->RegisterService(this, props); return d->m_Reg; } template void ModelFitProviderBase::UnregisterService() { try { d->m_Reg.Unregister(); } catch (const std::exception &) { } } template us::ServiceProperties ModelFitProviderBase::GetServiceProperties() const { us::ServiceProperties result; result[IModelFitProvider::PROP_MODEL_CLASS_ID()] = this->d->m_ReferenceFactory->GetClassID(); result[IModelFitProvider::PROP_MODEL_TYPE()] = this->d->m_ReferenceFactory->GetModelType(); result[us::ServiceConstants::SERVICE_RANKING()] = this->GetRanking(); return result; } template void ModelFitProviderBase::SetRanking(int ranking) { d->SetRanking(ranking); } template int ModelFitProviderBase::GetRanking() const { return d->GetRanking(); } } diff --git a/Modules/ModelFit/src/Common/mitkTimeGridHelper.cpp b/Modules/ModelFit/src/Common/mitkTimeGridHelper.cpp index 685cef5d67..91143d85ca 100644 --- a/Modules/ModelFit/src/Common/mitkTimeGridHelper.cpp +++ b/Modules/ModelFit/src/Common/mitkTimeGridHelper.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 "mitkTimeGridHelper.h" #include "itkExceptionObject.h" bool mitk::TimeGridIsMonotonIncreasing(const mitk::ModelBase::TimeGridType timeGrid) { const auto beginPos = timeGrid.begin(); const auto endPos = timeGrid.end(); for(mitk::ModelBase::TimeGridType::const_iterator posTime = beginPos; posTime != endPos; ++posTime) { if (posTime != beginPos && *(posTime-1)<*posTime) return false; } return true; }; mitk::ModelBase::ModelResultType mitk::InterpolateSignalToNewTimeGrid(const ModelBase::ModelResultType& inputSignal, const ModelBase::TimeGridType& inputGrid, const ModelBase::TimeGridType& outputGrid) { mitk::ModelBase::ModelResultType result(outputGrid.GetSize()); if (! inputSignal.GetSize()) { return result; } if (inputSignal.GetSize() != inputGrid.GetSize()) { itkGenericExceptionMacro("Input signal and input time grid have not the same size."); } mitk::ModelBase::ModelResultType::ValueType lastValue = inputSignal[0]; mitk::ModelBase::TimeGridType::ValueType lastTime = itk::NumericTraits::NonpositiveMin(); mitk::ModelBase::TimeGridType::const_iterator posITime = inputGrid.begin(); mitk::ModelBase::ModelResultType::const_iterator posValue = inputSignal.begin(); mitk::ModelBase::ModelResultType::iterator posResult = result.begin(); for(mitk::ModelBase::TimeGridType::const_iterator posOTime = outputGrid.begin(); posOTime != outputGrid.end(); ++posResult, ++posOTime) { - while(*posOTime > *posITime && posITime!=inputGrid.end()) + while(posITime!=inputGrid.end() && *posOTime > *posITime) { //forward in the input grid until the current output point //is between last and the current input point. lastValue = *posValue; lastTime = *posITime; ++posValue; ++posITime; } double weightLast = 1 - (*posOTime - lastTime)/(*posITime - lastTime); double weightNext = 1 - (*posITime - *posOTime)/(*posITime - lastTime); *posResult = weightLast * lastValue + weightNext * (*posValue); } return result; }; mitk::ModelBase::TimeGridType mitk::GenerateSupersampledTimeGrid(const mitk::ModelBase::TimeGridType& grid, const unsigned int samplingRate) { unsigned int origGridSize = grid.size(); mitk::ModelBase::TimeGridType interpolatedTimeGrid(((origGridSize - 1) * samplingRate) + 1); for (unsigned int t = 0; t < origGridSize - 1; ++t) { double delta = (grid[t + 1] - grid[t]) / samplingRate; for (unsigned int i = 0; i < samplingRate; ++i) { interpolatedTimeGrid[(t * samplingRate) + i] = grid[t] + i * delta; } } interpolatedTimeGrid[interpolatedTimeGrid.size() - 1] = grid[grid.size() - 1]; return interpolatedTimeGrid; }; diff --git a/Modules/Multilabel/autoload/IO/mitkLabelSetImageIO.cpp b/Modules/Multilabel/autoload/IO/mitkLabelSetImageIO.cpp index abff08cfc5..658d175cb7 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 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 + sprintf(keybuffer, "layer_%03u", layerIdx); // layer idx + sprintf(valbuffer, "%1u", input->GetNumberOfLabels(layerIdx)); // number of labels for the layer itk::EncapsulateMetaData( nrrdImageIo->GetMetaDataDictionary(), std::string(keybuffer), std::string(valbuffer)); auto iter = input->GetLabelSet(layerIdx)->IteratorConstBegin(); unsigned int count(0); while (iter != input->GetLabelSet(layerIdx)->IteratorConstEnd()) { std::unique_ptr document; document.reset(new TiXmlDocument()); 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 (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); + sprintf(keybuffer, "layer_%03u", 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); + sprintf(keybuffer, "label_%03u_%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/mitkLabelSetImageSurfaceStampFilter.cpp b/Modules/Multilabel/mitkLabelSetImageSurfaceStampFilter.cpp index 822f53363d..a9f9676f78 100644 --- a/Modules/Multilabel/mitkLabelSetImageSurfaceStampFilter.cpp +++ b/Modules/Multilabel/mitkLabelSetImageSurfaceStampFilter.cpp @@ -1,112 +1,112 @@ /*=================================================================== 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 "mitkLabelSetImageSurfaceStampFilter.h" #include "mitkImageAccessByItk.h" #include "mitkImageCast.h" #include #include #include #include -mitk::LabelSetImageSurfaceStampFilter::LabelSetImageSurfaceStampFilter() +mitk::LabelSetImageSurfaceStampFilter::LabelSetImageSurfaceStampFilter() : m_ForceOverwrite(false) { this->SetNumberOfIndexedInputs(1); this->SetNumberOfRequiredInputs(1); } mitk::LabelSetImageSurfaceStampFilter::~LabelSetImageSurfaceStampFilter() { } void mitk::LabelSetImageSurfaceStampFilter::GenerateData() { // GenerateOutputInformation(); this->SetNthOutput(0, this->GetInput(0)); mitk::Image::Pointer inputImage = this->GetInput(0); if (m_Surface.IsNull()) { MITK_ERROR << "Input surface is nullptr."; return; } mitk::SurfaceToImageFilter::Pointer surfaceToImageFilter = mitk::SurfaceToImageFilter::New(); surfaceToImageFilter->MakeOutputBinaryOn(); surfaceToImageFilter->SetInput(m_Surface); surfaceToImageFilter->SetImage(inputImage); surfaceToImageFilter->Update(); mitk::Image::Pointer resultImage = surfaceToImageFilter->GetOutput(); AccessByItk_1(inputImage, ItkImageProcessing, resultImage); inputImage->DisconnectPipeline(); } template void mitk::LabelSetImageSurfaceStampFilter::ItkImageProcessing(itk::Image *itkImage, mitk::Image::Pointer resultImage) { typedef itk::Image ImageType; mitk::LabelSetImage::Pointer LabelSetInputImage = dynamic_cast(GetInput()); try { typename ImageType::Pointer itkResultImage = ImageType::New(); mitk::CastToItkImage(resultImage, itkResultImage); typedef itk::ImageRegionConstIterator SourceIteratorType; typedef itk::ImageRegionIterator TargetIteratorType; SourceIteratorType sourceIter(itkResultImage, itkResultImage->GetLargestPossibleRegion()); sourceIter.GoToBegin(); TargetIteratorType targetIter(itkImage, itkImage->GetLargestPossibleRegion()); targetIter.GoToBegin(); int activeLabel = (LabelSetInputImage->GetActiveLabel(LabelSetInputImage->GetActiveLayer()))->GetValue(); while (!sourceIter.IsAtEnd()) { auto sourceValue = static_cast(sourceIter.Get()); auto targetValue = static_cast(targetIter.Get()); if ((sourceValue != 0) && (m_ForceOverwrite || !LabelSetInputImage->GetLabel(targetValue)->GetLocked())) // skip exterior and locked labels { targetIter.Set(activeLabel); } ++sourceIter; ++targetIter; } } catch (itk::ExceptionObject &e) { mitkThrow() << e.GetDescription(); } this->Modified(); } void mitk::LabelSetImageSurfaceStampFilter::GenerateOutputInformation() { mitk::Image::Pointer inputImage = (mitk::Image *)this->GetInput(); mitk::Image::Pointer output = this->GetOutput(); itkDebugMacro(<< "GenerateOutputInformation()"); if (inputImage.IsNull()) return; } diff --git a/Modules/Multilabel/mitkLabelSetImageVtkMapper2D.cpp b/Modules/Multilabel/mitkLabelSetImageVtkMapper2D.cpp index ecb7f9cf43..3f5fe77ab9 100644 --- a/Modules/Multilabel/mitkLabelSetImageVtkMapper2D.cpp +++ b/Modules/Multilabel/mitkLabelSetImageVtkMapper2D.cpp @@ -1,653 +1,654 @@ /*=================================================================== 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 "mitkLabelSetImageVtkMapper2D.h" // MITK #include #include #include #include #include #include #include #include #include #include #include #include #include #include // MITK Rendering #include "vtkMitkLevelWindowFilter.h" #include "vtkMitkThickSlicesFilter.h" #include "vtkNeverTranslucentTexture.h" // VTK #include #include #include #include #include #include #include #include #include #include #include #include //#include // ITK #include #include mitk::LabelSetImageVtkMapper2D::LabelSetImageVtkMapper2D() { } mitk::LabelSetImageVtkMapper2D::~LabelSetImageVtkMapper2D() { } vtkProp *mitk::LabelSetImageVtkMapper2D::GetVtkProp(mitk::BaseRenderer *renderer) { // return the actor corresponding to the renderer return m_LSH.GetLocalStorage(renderer)->m_Actors; } mitk::LabelSetImageVtkMapper2D::LocalStorage *mitk::LabelSetImageVtkMapper2D::GetLocalStorage( mitk::BaseRenderer *renderer) { return m_LSH.GetLocalStorage(renderer); } void mitk::LabelSetImageVtkMapper2D::GenerateDataForRenderer(mitk::BaseRenderer *renderer) { LocalStorage *localStorage = m_LSH.GetLocalStorage(renderer); mitk::DataNode *node = this->GetDataNode(); auto *image = dynamic_cast(node->GetData()); assert(image && image->IsInitialized()); // check if there is a valid worldGeometry const PlaneGeometry *worldGeometry = renderer->GetCurrentWorldPlaneGeometry(); if ((worldGeometry == nullptr) || (!worldGeometry->IsValid()) || (!worldGeometry->HasReferenceGeometry())) return; image->Update(); int numberOfLayers = image->GetNumberOfLayers(); int activeLayer = image->GetActiveLayer(); float opacity = 1.0f; node->GetOpacity(opacity, renderer, "opacity"); if (numberOfLayers != localStorage->m_NumberOfLayers) { localStorage->m_NumberOfLayers = numberOfLayers; localStorage->m_ReslicedImageVector.clear(); localStorage->m_ReslicerVector.clear(); localStorage->m_LayerTextureVector.clear(); localStorage->m_LevelWindowFilterVector.clear(); localStorage->m_LayerMapperVector.clear(); localStorage->m_LayerActorVector.clear(); localStorage->m_Actors = vtkSmartPointer::New(); for (int lidx = 0; lidx < numberOfLayers; ++lidx) { localStorage->m_ReslicedImageVector.push_back(vtkSmartPointer::New()); localStorage->m_ReslicerVector.push_back(mitk::ExtractSliceFilter::New()); localStorage->m_LayerTextureVector.push_back(vtkSmartPointer::New()); localStorage->m_LevelWindowFilterVector.push_back(vtkSmartPointer::New()); localStorage->m_LayerMapperVector.push_back(vtkSmartPointer::New()); localStorage->m_LayerActorVector.push_back(vtkSmartPointer::New()); // do not repeat the texture (the image) localStorage->m_LayerTextureVector[lidx]->RepeatOff(); // set corresponding mappers for the actors localStorage->m_LayerActorVector[lidx]->SetMapper(localStorage->m_LayerMapperVector[lidx]); localStorage->m_Actors->AddPart(localStorage->m_LayerActorVector[lidx]); } localStorage->m_Actors->AddPart(localStorage->m_OutlineShadowActor); localStorage->m_Actors->AddPart(localStorage->m_OutlineActor); } // 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 for (int lidx = 0; lidx < numberOfLayers; ++lidx) { localStorage->m_ReslicedImageVector[lidx] = nullptr; localStorage->m_LayerMapperVector[lidx]->SetInputData(localStorage->m_EmptyPolyData); localStorage->m_OutlineActor->SetVisibility(false); localStorage->m_OutlineShadowActor->SetVisibility(false); } return; } for (int lidx = 0; lidx < numberOfLayers; ++lidx) { mitk::Image *layerImage = nullptr; // set main input for ExtractSliceFilter if (lidx == activeLayer) layerImage = image; else layerImage = image->GetLayerImage(lidx); localStorage->m_ReslicerVector[lidx]->SetInput(layerImage); localStorage->m_ReslicerVector[lidx]->SetWorldGeometry(worldGeometry); localStorage->m_ReslicerVector[lidx]->SetTimeStep(this->GetTimestep()); // set the transformation of the image to adapt reslice axis localStorage->m_ReslicerVector[lidx]->SetResliceTransformByGeometry( layerImage->GetTimeGeometry()->GetGeometryForTimeStep(this->GetTimestep())); // is the geometry of the slice based on the image image or the worldgeometry? bool inPlaneResampleExtentByGeometry = false; node->GetBoolProperty("in plane resample extent by geometry", inPlaneResampleExtentByGeometry, renderer); localStorage->m_ReslicerVector[lidx]->SetInPlaneResampleExtentByGeometry(inPlaneResampleExtentByGeometry); localStorage->m_ReslicerVector[lidx]->SetInterpolationMode(ExtractSliceFilter::RESLICE_NEAREST); localStorage->m_ReslicerVector[lidx]->SetVtkOutputRequest(true); // this is needed when thick mode was enabled before. These variables have to be reset to default values localStorage->m_ReslicerVector[lidx]->SetOutputDimensionality(2); localStorage->m_ReslicerVector[lidx]->SetOutputSpacingZDirection(1.0); localStorage->m_ReslicerVector[lidx]->SetOutputExtentZDirection(0, 0); // Bounds information for reslicing (only required if reference geometry is present) // this used for generating a vtkPLaneSource with the right size double sliceBounds[6]; sliceBounds[0] = 0.0; sliceBounds[1] = 0.0; sliceBounds[2] = 0.0; sliceBounds[3] = 0.0; sliceBounds[4] = 0.0; sliceBounds[5] = 0.0; localStorage->m_ReslicerVector[lidx]->GetClippedPlaneBounds(sliceBounds); // setup the textured plane this->GeneratePlane(renderer, sliceBounds); // get the spacing of the slice localStorage->m_mmPerPixel = localStorage->m_ReslicerVector[lidx]->GetOutputSpacing(); localStorage->m_ReslicerVector[lidx]->Modified(); // start the pipeline with updating the largest possible, needed if the geometry of the image has changed localStorage->m_ReslicerVector[lidx]->UpdateLargestPossibleRegion(); localStorage->m_ReslicedImageVector[lidx] = localStorage->m_ReslicerVector[lidx]->GetVtkOutput(); const auto *planeGeometry = dynamic_cast(worldGeometry); 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(layerImage->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 imageLayer localStorage->m_LevelWindowFilterVector[lidx]->SetClippingBounds(textureClippingBounds); localStorage->m_LevelWindowFilterVector[lidx]->SetLookupTable( image->GetLabelSet(lidx)->GetLookupTable()->GetVtkLookupTable()); // do not use a VTK lookup table (we do that ourselves in m_LevelWindowFilter) localStorage->m_LayerTextureVector[lidx]->SetColorModeToDirectScalars(); // connect the imageLayer with the levelwindow filter localStorage->m_LevelWindowFilterVector[lidx]->SetInputData(localStorage->m_ReslicedImageVector[lidx]); // connect the texture with the output of the levelwindow filter // check for texture interpolation property bool textureInterpolation = false; node->GetBoolProperty("texture interpolation", textureInterpolation, renderer); // set the interpolation modus according to the property localStorage->m_LayerTextureVector[lidx]->SetInterpolate(textureInterpolation); localStorage->m_LayerTextureVector[lidx]->SetInputConnection( localStorage->m_LevelWindowFilterVector[lidx]->GetOutputPort()); this->TransformActor(renderer); // set the plane as input for the mapper localStorage->m_LayerMapperVector[lidx]->SetInputConnection(localStorage->m_Plane->GetOutputPort()); // set the texture for the actor localStorage->m_LayerActorVector[lidx]->SetTexture(localStorage->m_LayerTextureVector[lidx]); localStorage->m_LayerActorVector[lidx]->GetProperty()->SetOpacity(opacity); } mitk::Label* activeLabel = image->GetActiveLabel(activeLayer); if (nullptr != activeLabel) { bool contourActive = false; node->GetBoolProperty("labelset.contour.active", contourActive, renderer); if (contourActive && activeLabel->GetVisible()) //contour rendering { //generate contours/outlines localStorage->m_OutlinePolyData = this->CreateOutlinePolyData(renderer, localStorage->m_ReslicedImageVector[activeLayer], activeLabel->GetValue()); localStorage->m_OutlineActor->SetVisibility(true); localStorage->m_OutlineShadowActor->SetVisibility(true); const mitk::Color& color = activeLabel->GetColor(); localStorage->m_OutlineActor->GetProperty()->SetColor(color.GetRed(), color.GetGreen(), color.GetBlue()); localStorage->m_OutlineShadowActor->GetProperty()->SetColor(0, 0, 0); float contourWidth(2.0); node->GetFloatProperty("labelset.contour.width", contourWidth, renderer); localStorage->m_OutlineActor->GetProperty()->SetLineWidth(contourWidth); localStorage->m_OutlineShadowActor->GetProperty()->SetLineWidth(contourWidth * 1.5); localStorage->m_OutlineActor->GetProperty()->SetOpacity(opacity); localStorage->m_OutlineShadowActor->GetProperty()->SetOpacity(opacity); localStorage->m_OutlineMapper->SetInputData(localStorage->m_OutlinePolyData); return; } } localStorage->m_OutlineActor->SetVisibility(false); localStorage->m_OutlineShadowActor->SetVisibility(false); } bool mitk::LabelSetImageVtkMapper2D::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; } vtkSmartPointer mitk::LabelSetImageVtkMapper2D::CreateOutlinePolyData(mitk::BaseRenderer *renderer, vtkImageData *image, int pixelValue) { LocalStorage *localStorage = this->GetLocalStorage(renderer); // get the min and max index values of each direction int *extent = image->GetExtent(); int xMin = extent[0]; int xMax = extent[1]; int yMin = extent[2]; int yMax = extent[3]; int *dims = image->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 = this->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 auto *currentPixel = static_cast(image->GetScalarPointer()); while (y <= yMax) { // if the current pixel value is set to something if ((currentPixel) && (*currentPixel == pixelValue)) { // 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) != pixelValue) { // 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) != pixelValue) { // 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) != pixelValue) { // 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) != pixelValue) { // 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::LabelSetImageVtkMapper2D::ApplyColor(mitk::BaseRenderer *renderer, const mitk::Color &color) { LocalStorage *localStorage = this->GetLocalStorage(renderer); localStorage->m_OutlineActor->GetProperty()->SetColor(color.GetRed(), color.GetGreen(), color.GetBlue()); localStorage->m_OutlineShadowActor->GetProperty()->SetColor(0, 0, 0); } void mitk::LabelSetImageVtkMapper2D::ApplyOpacity(mitk::BaseRenderer *renderer, int layer) { LocalStorage *localStorage = this->GetLocalStorage(renderer); float opacity = 1.0f; this->GetDataNode()->GetOpacity(opacity, renderer, "opacity"); localStorage->m_LayerActorVector[layer]->GetProperty()->SetOpacity(opacity); localStorage->m_OutlineActor->GetProperty()->SetOpacity(opacity); localStorage->m_OutlineShadowActor->GetProperty()->SetOpacity(opacity); } void mitk::LabelSetImageVtkMapper2D::ApplyLookuptable(mitk::BaseRenderer *renderer, int layer) { LocalStorage *localStorage = m_LSH.GetLocalStorage(renderer); auto *input = dynamic_cast(this->GetDataNode()->GetData()); localStorage->m_LevelWindowFilterVector[layer]->SetLookupTable( input->GetLabelSet(layer)->GetLookupTable()->GetVtkLookupTable()); } void mitk::LabelSetImageVtkMapper2D::Update(mitk::BaseRenderer *renderer) { bool visible = true; const DataNode *node = this->GetDataNode(); node->GetVisibility(visible, renderer, "visible"); if (!visible) return; auto *image = dynamic_cast(node->GetData()); if (image == nullptr || image->IsInitialized() == false) return; // Calculate time step of the image data for the specified renderer (integer value) this->CalculateTimeStep(renderer); // Check if time step is valid const TimeGeometry *dataTimeGeometry = image->GetTimeGeometry(); if ((dataTimeGeometry == nullptr) || (dataTimeGeometry->CountTimeSteps() == 0) || (!dataTimeGeometry->IsValidTimeStep(this->GetTimestep()))) { return; } image->UpdateOutputInformation(); LocalStorage *localStorage = m_LSH.GetLocalStorage(renderer); // check if something important has changed and we need to re-render if ((localStorage->m_LastDataUpdateTime < image->GetMTime()) || (localStorage->m_LastDataUpdateTime < image->GetPipelineMTime()) || (localStorage->m_LastDataUpdateTime < renderer->GetCurrentWorldPlaneGeometryUpdateTime()) || (localStorage->m_LastDataUpdateTime < renderer->GetCurrentWorldPlaneGeometry()->GetMTime())) { this->GenerateDataForRenderer(renderer); localStorage->m_LastDataUpdateTime.Modified(); } else if ((localStorage->m_LastPropertyUpdateTime < node->GetPropertyList()->GetMTime()) || (localStorage->m_LastPropertyUpdateTime < node->GetPropertyList(renderer)->GetMTime()) || (localStorage->m_LastPropertyUpdateTime < image->GetPropertyList()->GetMTime())) { this->GenerateDataForRenderer(renderer); localStorage->m_LastPropertyUpdateTime.Modified(); } } // set the two points defining the textured plane according to the dimension and spacing void mitk::LabelSetImageVtkMapper2D::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::LabelSetImageVtkMapper2D::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; } void mitk::LabelSetImageVtkMapper2D::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_ReslicerVector[0]->GetResliceAxes(); // same for all layers trans->SetMatrix(matrix); for (int lidx = 0; lidx < localStorage->m_NumberOfLayers; ++lidx) { // transform the plane/contour (the actual actor) to the corresponding view (axial, coronal or saggital) localStorage->m_LayerActorVector[lidx]->SetUserTransform(trans); // transform the origin to center based coordinates, because MITK is center based. localStorage->m_LayerActorVector[lidx]->SetPosition( -0.5 * localStorage->m_mmPerPixel[0], -0.5 * localStorage->m_mmPerPixel[1], 0.0); } // same for outline actor localStorage->m_OutlineActor->SetUserTransform(trans); localStorage->m_OutlineActor->SetPosition( -0.5 * localStorage->m_mmPerPixel[0], -0.5 * localStorage->m_mmPerPixel[1], 0.0); // same for outline shadow actor localStorage->m_OutlineShadowActor->SetUserTransform(trans); localStorage->m_OutlineShadowActor->SetPosition( -0.5 * localStorage->m_mmPerPixel[0], -0.5 * localStorage->m_mmPerPixel[1], 0.0); } void mitk::LabelSetImageVtkMapper2D::SetDefaultProperties(mitk::DataNode *node, mitk::BaseRenderer *renderer, bool overwrite) { // add/replace the following properties node->SetProperty("opacity", FloatProperty::New(1.0f), renderer); node->SetProperty("binary", BoolProperty::New(false), renderer); mitk::RenderingModeProperty::Pointer renderingModeProperty = mitk::RenderingModeProperty::New(RenderingModeProperty::LOOKUPTABLE_LEVELWINDOW_COLOR); node->SetProperty("Image Rendering.Mode", renderingModeProperty, renderer); mitk::LevelWindow levelwindow(32767.5, 65535); mitk::LevelWindowProperty::Pointer levWinProp = mitk::LevelWindowProperty::New(levelwindow); levWinProp->SetLevelWindow(levelwindow); node->SetProperty("levelwindow", levWinProp, renderer); node->SetProperty("labelset.contour.active", BoolProperty::New(true), renderer); node->SetProperty("labelset.contour.width", FloatProperty::New(2.0), renderer); Superclass::SetDefaultProperties(node, renderer, overwrite); } mitk::LabelSetImageVtkMapper2D::LocalStorage::~LocalStorage() { } mitk::LabelSetImageVtkMapper2D::LocalStorage::LocalStorage() { // Do as much actions as possible in here to avoid double executions. m_Plane = vtkSmartPointer::New(); m_Actors = vtkSmartPointer::New(); m_OutlinePolyData = vtkSmartPointer::New(); m_EmptyPolyData = vtkSmartPointer::New(); m_OutlineActor = vtkSmartPointer::New(); m_OutlineMapper = vtkSmartPointer::New(); m_OutlineShadowActor = vtkSmartPointer::New(); m_NumberOfLayers = 0; + m_mmPerPixel = nullptr; m_OutlineActor->SetMapper(m_OutlineMapper); m_OutlineShadowActor->SetMapper(m_OutlineMapper); m_OutlineActor->SetVisibility(false); m_OutlineShadowActor->SetVisibility(false); } diff --git a/Modules/PlanarFigure/src/IO/mitkPlanarFigureReader.cpp b/Modules/PlanarFigure/src/IO/mitkPlanarFigureReader.cpp index a242d0b3f5..df4fbe17a5 100644 --- a/Modules/PlanarFigure/src/IO/mitkPlanarFigureReader.cpp +++ b/Modules/PlanarFigure/src/IO/mitkPlanarFigureReader.cpp @@ -1,447 +1,442 @@ /*=================================================================== 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 "mitkPlanarFigureReader.h" #include "mitkPlanarAngle.h" #include "mitkPlanarArrow.h" #include "mitkPlanarBezierCurve.h" #include "mitkPlanarCircle.h" #include "mitkPlanarCross.h" #include "mitkPlanarDoubleEllipse.h" #include "mitkPlanarEllipse.h" #include "mitkPlanarFourPointAngle.h" #include "mitkPlanarLine.h" #include "mitkPlanarPolygon.h" #include "mitkPlanarRectangle.h" #include "mitkPlanarSubdivisionPolygon.h" #include "mitkPlaneGeometry.h" #include "mitkBasePropertySerializer.h" #include #include #include mitk::PlanarFigureReader::PlanarFigureReader() : PlanarFigureSource(), FileReader(), m_FileName(""), m_FilePrefix(""), m_FilePattern(""), m_Success(false) { this->SetNumberOfRequiredOutputs(1); this->SetNumberOfIndexedOutputs(1); this->SetNthOutput(0, this->MakeOutput(0)); m_CanReadFromMemory = true; // this->Modified(); // this->GetOutput()->Modified(); // this->GetOutput()->ReleaseData(); } mitk::PlanarFigureReader::~PlanarFigureReader() { } void mitk::PlanarFigureReader::GenerateData() { mitk::LocaleSwitch localeSwitch("C"); m_Success = false; this->SetNumberOfIndexedOutputs(0); // reset all outputs, we add new ones depending on the file content TiXmlDocument document; if (m_ReadFromMemory) { if (m_MemoryBuffer == nullptr || m_MemorySize == 0) { // check itkWarningMacro(<< "Sorry, memory buffer has not been set!"); return; } if (m_MemoryBuffer[m_MemorySize - 1] == '\0') { document.Parse(m_MemoryBuffer); } else { auto tmpArray = new char[(int)m_MemorySize + 1]; tmpArray[m_MemorySize] = '\0'; memcpy(tmpArray, m_MemoryBuffer, m_MemorySize); document.Parse(m_MemoryBuffer); delete[] tmpArray; } } else { if (m_FileName.empty()) { itkWarningMacro(<< "Sorry, filename has not been set!"); return; } if (this->CanReadFile(m_FileName.c_str()) == false) { itkWarningMacro(<< "Sorry, can't read file " << m_FileName << "!"); return; } if (!document.LoadFile(m_FileName)) { MITK_ERROR << "Could not open/read/parse " << m_FileName << ". TinyXML reports: '" << document.ErrorDesc() << "'. " << "The error occurred in row " << document.ErrorRow() << ", column " << document.ErrorCol() << "."; return; } } int fileVersion = 1; TiXmlElement *versionObject = document.FirstChildElement("Version"); if (versionObject != nullptr) { if (versionObject->QueryIntAttribute("FileVersion", &fileVersion) != TIXML_SUCCESS) { MITK_WARN << m_FileName << " does not contain version information! Trying version 1 format." << std::endl; } } else { MITK_WARN << m_FileName << " does not contain version information! Trying version 1 format." << std::endl; } if (fileVersion != 1) // add file version selection and version specific file parsing here, if newer file versions are created { MITK_WARN << "File version > 1 is not supported by this reader."; return; } /* file version 1 reader code */ for (TiXmlElement *pfElement = document.FirstChildElement("PlanarFigure"); pfElement != nullptr; pfElement = pfElement->NextSiblingElement("PlanarFigure")) { - if (pfElement == nullptr) - continue; - std::string type = pfElement->Attribute("type"); mitk::PlanarFigure::Pointer planarFigure = nullptr; if (type == "PlanarAngle") { planarFigure = mitk::PlanarAngle::New(); } else if (type == "PlanarCircle") { planarFigure = mitk::PlanarCircle::New(); } else if (type == "PlanarEllipse") { planarFigure = mitk::PlanarEllipse::New(); } else if (type == "PlanarCross") { planarFigure = mitk::PlanarCross::New(); } else if (type == "PlanarFourPointAngle") { planarFigure = mitk::PlanarFourPointAngle::New(); } else if (type == "PlanarLine") { planarFigure = mitk::PlanarLine::New(); } else if (type == "PlanarPolygon") { planarFigure = mitk::PlanarPolygon::New(); } else if (type == "PlanarSubdivisionPolygon") { planarFigure = mitk::PlanarSubdivisionPolygon::New(); } else if (type == "PlanarRectangle") { planarFigure = mitk::PlanarRectangle::New(); } else if (type == "PlanarArrow") { planarFigure = mitk::PlanarArrow::New(); } else if (type == "PlanarDoubleEllipse") { planarFigure = mitk::PlanarDoubleEllipse::New(); } else if (type == "PlanarBezierCurve") { planarFigure = mitk::PlanarBezierCurve::New(); } else { // unknown type MITK_WARN << "encountered unknown planar figure type '" << type << "'. Skipping this element."; continue; } // Read properties of the planar figure for (TiXmlElement *propertyElement = pfElement->FirstChildElement("property"); propertyElement != nullptr; propertyElement = propertyElement->NextSiblingElement("property")) { const char *keya = propertyElement->Attribute("key"); const std::string key(keya ? keya : ""); const char *typea = propertyElement->Attribute("type"); const std::string type(typea ? typea : ""); // hand propertyElement to specific reader std::stringstream propertyDeserializerClassName; propertyDeserializerClassName << type << "Serializer"; const std::list readers = itk::ObjectFactoryBase::CreateAllInstance(propertyDeserializerClassName.str().c_str()); if (readers.size() < 1) { MITK_ERROR << "No property reader found for " << type; } if (readers.size() > 1) { MITK_WARN << "Multiple property readers found for " << type << ". Using arbitrary first one."; } for (auto iter = readers.cbegin(); iter != readers.cend(); ++iter) { if (auto *reader = dynamic_cast(iter->GetPointer())) { const BaseProperty::Pointer property = reader->Deserialize(propertyElement->FirstChildElement()); if (property.IsNotNull()) { planarFigure->GetPropertyList()->ReplaceProperty(key, property); } else { MITK_ERROR << "There were errors while loading property '" << key << "' of type " << type << ". Your data may be corrupted"; } break; } } } // If we load a planarFigure, it has definitely been placed correctly. // If we do not set this property here, we cannot load old planarFigures // without messing up the interaction (PF-Interactor needs this property. planarFigure->GetPropertyList()->SetBoolProperty("initiallyplaced", true); // Which features (length or circumference etc) a figure has is decided by whether it is closed or not // the function SetClosed has to be called in case of PlanarPolygons to ensure they hold the correct feature auto *planarPolygon = dynamic_cast(planarFigure.GetPointer()); if (planarPolygon != nullptr) { bool isClosed = false; planarFigure->GetPropertyList()->GetBoolProperty("closed", isClosed); planarPolygon->SetClosed(isClosed); } // Read geometry of containing plane TiXmlElement *geoElement = pfElement->FirstChildElement("Geometry"); if (geoElement != nullptr) { try { // Create plane geometry mitk::PlaneGeometry::Pointer planeGeo = mitk::PlaneGeometry::New(); // Extract and set plane transform parameters const DoubleList transformList = this->GetDoubleAttributeListFromXMLNode(geoElement->FirstChildElement("transformParam"), "param", 12); typedef mitk::BaseGeometry::TransformType TransformType; TransformType::ParametersType parameters; parameters.SetSize(12); unsigned int i; DoubleList::const_iterator it; for (it = transformList.cbegin(), i = 0; it != transformList.cend(); ++it, ++i) { parameters.SetElement(i, *it); } typedef mitk::BaseGeometry::TransformType TransformType; TransformType::Pointer affineGeometry = TransformType::New(); affineGeometry->SetParameters(parameters); planeGeo->SetIndexToWorldTransform(affineGeometry); // Extract and set plane bounds const DoubleList boundsList = this->GetDoubleAttributeListFromXMLNode(geoElement->FirstChildElement("boundsParam"), "bound", 6); typedef mitk::BaseGeometry::BoundsArrayType BoundsArrayType; BoundsArrayType bounds; for (it = boundsList.cbegin(), i = 0; it != boundsList.cend(); ++it, ++i) { bounds[i] = *it; } planeGeo->SetBounds(bounds); // Extract and set spacing and origin const Vector3D spacing = this->GetVectorFromXMLNode(geoElement->FirstChildElement("Spacing")); planeGeo->SetSpacing(spacing); const Point3D origin = this->GetPointFromXMLNode(geoElement->FirstChildElement("Origin")); planeGeo->SetOrigin(origin); planarFigure->SetPlaneGeometry(planeGeo); } catch (...) { } } TiXmlElement *cpElement = pfElement->FirstChildElement("ControlPoints"); bool first = true; if (cpElement != nullptr) for (TiXmlElement *vertElement = cpElement->FirstChildElement("Vertex"); vertElement != nullptr; vertElement = vertElement->NextSiblingElement("Vertex")) { - if (vertElement == nullptr) - continue; int id = 0; mitk::Point2D::ValueType x = 0.0; mitk::Point2D::ValueType y = 0.0; if (vertElement->QueryIntAttribute("id", &id) == TIXML_WRONG_TYPE) return; // TODO: can we do a better error handling? if (vertElement->QueryDoubleAttribute("x", &x) == TIXML_WRONG_TYPE) return; // TODO: can we do a better error handling? if (vertElement->QueryDoubleAttribute("y", &y) == TIXML_WRONG_TYPE) return; // TODO: can we do a better error handling? Point2D p; p.SetElement(0, x); p.SetElement(1, y); if (first == true) // needed to set m_FigurePlaced to true { planarFigure->PlaceFigure(p); first = false; } planarFigure->SetControlPoint(id, p, true); } // Calculate feature quantities of this PlanarFigure planarFigure->EvaluateFeatures(); // Make sure that no control point is currently selected planarFigure->DeselectControlPoint(); // \TODO: what about m_FigurePlaced and m_SelectedControlPoint ?? this->SetNthOutput(this->GetNumberOfOutputs(), planarFigure); // add planarFigure as new output of this filter } m_Success = true; } mitk::Point3D mitk::PlanarFigureReader::GetPointFromXMLNode(TiXmlElement *e) { if (e == nullptr) throw std::invalid_argument("node invalid"); // TODO: can we do a better error handling? mitk::Point3D point; mitk::ScalarType p(-1.0); if (e->QueryDoubleAttribute("x", &p) == TIXML_WRONG_TYPE) throw std::invalid_argument("node malformatted"); // TODO: can we do a better error handling? point.SetElement(0, p); if (e->QueryDoubleAttribute("y", &p) == TIXML_WRONG_TYPE) throw std::invalid_argument("node malformatted"); // TODO: can we do a better error handling? point.SetElement(1, p); if (e->QueryDoubleAttribute("z", &p) == TIXML_WRONG_TYPE) throw std::invalid_argument("node malformatted"); // TODO: can we do a better error handling? point.SetElement(2, p); return point; } mitk::Vector3D mitk::PlanarFigureReader::GetVectorFromXMLNode(TiXmlElement *e) { if (e == nullptr) throw std::invalid_argument("node invalid"); // TODO: can we do a better error handling? mitk::Vector3D vector; mitk::ScalarType p(-1.0); if (e->QueryDoubleAttribute("x", &p) == TIXML_WRONG_TYPE) throw std::invalid_argument("node malformatted"); // TODO: can we do a better error handling? vector.SetElement(0, p); if (e->QueryDoubleAttribute("y", &p) == TIXML_WRONG_TYPE) throw std::invalid_argument("node malformatted"); // TODO: can we do a better error handling? vector.SetElement(1, p); if (e->QueryDoubleAttribute("z", &p) == TIXML_WRONG_TYPE) throw std::invalid_argument("node malformatted"); // TODO: can we do a better error handling? vector.SetElement(2, p); return vector; } mitk::PlanarFigureReader::DoubleList mitk::PlanarFigureReader::GetDoubleAttributeListFromXMLNode( TiXmlElement *e, const char *attributeNameBase, unsigned int count) { DoubleList list; if (e == nullptr) throw std::invalid_argument("node invalid"); // TODO: can we do a better error handling? for (unsigned int i = 0; i < count; ++i) { mitk::ScalarType p(-1.0); std::stringstream attributeName; attributeName << attributeNameBase << i; if (e->QueryDoubleAttribute(attributeName.str().c_str(), &p) == TIXML_WRONG_TYPE) throw std::invalid_argument("node malformatted"); // TODO: can we do a better error handling? list.push_back(p); } return list; } void mitk::PlanarFigureReader::GenerateOutputInformation() { } int mitk::PlanarFigureReader::CanReadFile(const char *name) { if (std::string(name).empty()) return false; return (itksys::SystemTools::LowerCase(itksys::SystemTools::GetFilenameLastExtension(name)) == ".pf"); // assume, we can read all .pf files // TiXmlDocument document(name); // if (document.LoadFile() == false) // return false; // return (document.FirstChildElement("PlanarFigure") != nullptr); } bool mitk::PlanarFigureReader::CanReadFile(const std::string filename, const std::string, const std::string) { if (filename.empty()) return false; return (itksys::SystemTools::LowerCase(itksys::SystemTools::GetFilenameLastExtension(filename)) == ".pf"); // assume, we can read all .pf files // TiXmlDocument document(filename); // if (document.LoadFile() == false) // return false; // return (document.FirstChildElement("PlanarFigure") != nullptr); } void mitk::PlanarFigureReader::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()); } } diff --git a/Modules/Segmentation/Algorithms/itkAdaptiveThresholdIterator.h b/Modules/Segmentation/Algorithms/itkAdaptiveThresholdIterator.h index e8e6e80175..5cdd9e93e7 100644 --- a/Modules/Segmentation/Algorithms/itkAdaptiveThresholdIterator.h +++ b/Modules/Segmentation/Algorithms/itkAdaptiveThresholdIterator.h @@ -1,245 +1,254 @@ /*=================================================================== 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 __itkAdaptiveThresholdIterator_h #define __itkAdaptiveThresholdIterator_h #include "itkConditionalConstIterator.h" #include "itkImage.h" #include "itkIndex.h" #include "itkSize.h" #include #include #include #include namespace itk { /** * \class AdaptiveThresholdIterator * \brief Iterates over an image using a variable image function, * which threshold can be varied during the iteration process. * * \ingroup ImageIterators * */ template class ITK_EXPORT AdaptiveThresholdIterator : public ConditionalConstIterator { public: /** Standard class typedefs. */ typedef AdaptiveThresholdIterator Self; typedef ConditionalConstIterator Superclass; typedef TImage ImageType; // A temporary image used for storing info about all indices typedef TImage TTempImage; typename TTempImage::Pointer tempPtr; //[!] isn't really used?! /** Type of function */ typedef TFunction FunctionType; /** Type of vector used to store location info in the spatial function */ typedef typename TFunction::InputType FunctionInputType; /** Size typedef support. */ typedef typename TImage::SizeType SizeType; /** Region typedef support */ typedef typename TImage::RegionType RegionType; typedef typename TImage::IndexType IndexType; /** Internal Pixel Type */ typedef typename TImage::InternalPixelType InternalPixelType; /** External Pixel Type */ typedef typename TImage::PixelType PixelType; /** Queue containing indices representing a voxel position */ typedef std::queue IndexQueueType; /** Map used to generate the output result */ typedef std::map QueueMapType; /** Dimension of the image the iterator walks. This constant is needed so * that functions that are templated over image iterator type (as opposed to * being templated over pixel type and dimension) can have compile time * access to the dimension of the image that the iterator walks. */ itkStaticConstMacro(NDimensions, unsigned int, TImage::ImageDimension); /** Constructor establishes an iterator to walk a particular image and a * particular region of that image. This version of the constructor uses * an explicit seed pixel for the flood fill, the "startIndex" */ AdaptiveThresholdIterator(ImageType *imagePtr, FunctionType *fnPtr, IndexType startIndex); /** Constructor establishes an iterator to walk a particular image and a * particular region of that image. This version of the constructor uses * an explicit list of seed pixels for the flood fill, the "startIndex" */ AdaptiveThresholdIterator(ImageType *imagePtr, FunctionType *fnPtr, std::vector &startIndex); /** Constructor establishes an iterator to walk a particular image and a * particular region of that image. This version of the constructor * should be used when the seed pixel is unknown */ AdaptiveThresholdIterator(ImageType *imagePtr, FunctionType *fnPtr); /** Default Destructor. */ ~AdaptiveThresholdIterator() override{}; /** Initializes the iterator, called from constructor */ void InitializeIterator(); // makes the iterator go one step further void DoExtendedFloodStep(); // set-method for member-variable void SetExpansionDirection(bool upwards); // Init-method void InitRegionGrowingState(); void SetMinTH(int min); void SetMaxTH(int max); int GetSeedPointValue(void); /** switch between fine and raw leakage detection */ void SetFineDetectionMode(bool fine = false) { m_FineDetectionMode = fine; m_DetectionStop = false; } /** Get the index. This provides a read only reference to the index. * This causes the index to be calculated from pointer arithmetic and is * therefore an expensive operation. * \sa SetIndex */ const IndexType GetIndex() override { return (*m_QueueMap.find(m_RegionGrowingState)).second.front(); } // [!] is never called?! const PixelType Get(void) const override { return const_cast(this->m_Image.GetPointer()) ->GetPixel((*m_QueueMap.find(m_RegionGrowingState)).second.front()); } //[!] is never called?! void Set(const PixelType &value) { const_cast(this->m_Image.GetPointer()) ->GetPixel((*m_QueueMap.find(m_RegionGrowingState)).second.front()) = value; } void GoToBegin(); /** Is the iterator at the end of the region? */ bool IsAtEnd() override { return this->m_IsAtEnd; }; /** Walk forward one index */ void operator++() override { this->DoExtendedFloodStep(); } virtual SmartPointer GetFunction() const { return m_Function; } /** operator= is provided to make sure the handle to the image is properly * reference counted. */ Self &operator=(const Self &it) { this->m_Image = it.m_Image; // copy the smart pointer this->m_Region = it.m_Region; + this->m_InitializeValue = it.m_InitializeValue; + this->m_RegionGrowingState = it.m_RegionGrowingState; + this->m_MinTH = it.m_MinTH; + this->m_MaxTH = it.m_MaxTH; + this->m_SeedPointValue = it.m_SeedPointValue; + this->m_VoxelCounter = it.m_VoxelCounter; + this->m_LastVoxelNumber = it.m_LastVoxelNumber; + this->m_DetectedLeakagePoint = it.m_DetectedLeakagePoint; + this->m_CurrentLeakageRatio = it.m_CurrentLeakageRatio; return *this; } /** Compute whether the index of interest should be included in the flood */ bool IsPixelIncluded(const IndexType &index) const override; // Calculate the value the outputImage is initialized to static int CalculateInitializeValue(int lower, int upper) { return ((upper - lower) + 1) * (-1); }; int GetLeakagePoint(void) { return m_DetectedLeakagePoint; } protected: /* * @brief Pointer on the output image to which the result shall be written */ SmartPointer m_OutputImage; SmartPointer m_Function; /** A list of locations to start the recursive fill */ std::vector m_StartIndices; /** The origin of the source image */ typename ImageType::PointType m_ImageOrigin; /** The spacing of the source image */ typename ImageType::SpacingType m_ImageSpacing; /** Region of the source image */ RegionType m_ImageRegion; bool m_UpwardsExpansion; int m_InitializeValue; void ExpandThresholdUpwards(); void ExpandThresholdDownwards(); void IncrementRegionGrowingState(); // calculates how many steps the voxel is from the current step int EstimateDistance(IndexType); // calculates how many expansion steps will be taken unsigned int CalculateMaxRGS(); private: void InsertIndexTypeIntoQueueMap(unsigned int key, IndexType index); int m_RegionGrowingState; QueueMapType m_QueueMap; int m_MinTH; int m_MaxTH; int m_SeedPointValue; unsigned int m_VoxelCounter; unsigned int m_LastVoxelNumber; int m_DetectedLeakagePoint; float m_CurrentLeakageRatio; void CheckSeedPointValue(); /* flag for switching between raw leakage detection (bigger bronchial vessels) * and fine leakage detection (smaller bronchial vessels [starting from leaves]) */ bool m_FineDetectionMode; bool m_DetectionStop; }; } // end namespace itk #ifndef ITK_MANUAL_INSTANTIATION #include "itkAdaptiveThresholdIterator.txx" #endif #endif diff --git a/Modules/Segmentation/Algorithms/itkConnectedAdaptiveThresholdImageFilter.h b/Modules/Segmentation/Algorithms/itkConnectedAdaptiveThresholdImageFilter.h index 628206e080..1984ee9b46 100644 --- a/Modules/Segmentation/Algorithms/itkConnectedAdaptiveThresholdImageFilter.h +++ b/Modules/Segmentation/Algorithms/itkConnectedAdaptiveThresholdImageFilter.h @@ -1,127 +1,125 @@ /*=================================================================== 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 __itkConnectedAdaptiveThresholdImageFilter_h #define __itkConnectedAdaptiveThresholdImageFilter_h #include "itkConnectedThresholdImageFilter.h" #include "itkImage.h" namespace itk { /** /class ConnectedAdaptiveThreholdImageFilter * \brief ImageFilter used for processing an image with an adaptive * iterator (such as itkAdaptiveThresholdIterator) * * \ingroup RegionGrowingSegmentation */ template class ITK_EXPORT ConnectedAdaptiveThresholdImageFilter : public ConnectedThresholdImageFilter { public: /** Standard class typedefs. */ typedef ConnectedAdaptiveThresholdImageFilter Self; typedef ConnectedThresholdImageFilter Superclass; typedef SmartPointer Pointer; typedef SmartPointer ConstPointer; /** Method for creation through the object factory. */ itkFactorylessNewMacro(Self) itkCloneMacro(Self) /** Run-time type information (and related methods). */ itkTypeMacro(ConnectedAdaptiveThresholdImageFilter, ConnectedThresholdImageFilter); typedef TInputImage InputImageType; typedef TOutputImage OutputImageType; typedef typename OutputImageType::Pointer OutputImagePointer; typedef typename InputImageType::IndexType IndexType; typedef typename InputImageType::PixelType PixelType; void SetGrowingDirectionIsUpwards(bool upwards) { m_GrowingDirectionIsUpwards = upwards; } /* Switch between fine and raw leakage detection. */ void SetFineDetectionMode(bool fine) { m_FineDetectionMode = fine; m_DiscardLastPreview = false; } int GetSeedpointValue(void) { return m_SeedpointValue; } int GetLeakagePoint(void) { return m_DetectedLeakagePoint; } - bool m_SegmentationCancelled; /* * Correct the position of the seed point, only performed if seed point value is outside threshold range * @param sizeOfVolume edge length of the square volume in which the search for a "better" seed is performed */ IndexType CorrectSeedPointPosition(unsigned int sizeOfVolume, int lowerTh, int upperTh); /* Sets all voxels in a square volume with the size of @param croppingSize * and the center point equal to @param seedPoint to the value zero. */ void CropMask(unsigned int croppingSize); /* Modifies the iterator mask to keep all previous segmentation results in the same mask. * @returnParam largest value in the segmentation mask */ unsigned int AdjustIteratorMask(); /* Sets parameters needed for adjusting the iterator mask * @param iteratorMaskForFineSegmentation pointer to the image containing the complete segmentation result of one * leaf (inclusively leakage-segmentation) * @param adjLowerTh lower threshold value of the segmentation without leakage-segmentation * @param adjLowerTh upper threshold value of the segmentation without leakage-segmentation * @param discardLeafSegmentation flag if the last segmentation preview ended with a leakage already in the first * step */ void SetParameterForFineSegmentation(TOutputImage *iteratorMaskForFineSegmentation, unsigned int adjLowerTh, unsigned int adjUpperTh, itk::Index<3> seedPoint, bool discardLeafSegmentation); TOutputImage *GetResultImage(); protected: ConnectedAdaptiveThresholdImageFilter(); ~ConnectedAdaptiveThresholdImageFilter() override{}; void GenerateData() override; - TOutputImage *m_IteratorMaskForFineSegmentation; - private: OutputImagePointer m_OutoutImageMaskFineSegmentation; bool m_GrowingDirectionIsUpwards; PixelType m_SeedpointValue; PixelType m_DetectedLeakagePoint; PixelType m_InitValue; unsigned int m_AdjLowerTh; unsigned int m_AdjUpperTh; itk::Index<3> m_SeedPointIndex; /* Flag for switching between raw segmentation and fine segmentation (Bronchial tree segmentation) */ bool m_FineDetectionMode; bool m_DiscardLastPreview; + bool m_SegmentationCancelled; }; } // end namespace itk #ifndef ITK_MANUAL_INSTANTIATION #include "itkConnectedAdaptiveThresholdImageFilter.txx" #endif #endif diff --git a/Modules/Segmentation/Algorithms/itkConnectedAdaptiveThresholdImageFilter.txx b/Modules/Segmentation/Algorithms/itkConnectedAdaptiveThresholdImageFilter.txx index 6d740e27f0..cb2a46ef18 100644 --- a/Modules/Segmentation/Algorithms/itkConnectedAdaptiveThresholdImageFilter.txx +++ b/Modules/Segmentation/Algorithms/itkConnectedAdaptiveThresholdImageFilter.txx @@ -1,300 +1,309 @@ /*=================================================================== 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 _itkConnectedAdaptiveThresholdImageFilter_txx #define _itkConnectedAdaptiveThresholdImageFilter_txx #include "itkAdaptiveThresholdIterator.h" #include "itkBinaryThresholdImageFunction.h" #include "itkConnectedAdaptiveThresholdImageFilter.h" #include "itkMinimumMaximumImageFilter.h" #include "itkThresholdImageFilter.h" namespace itk { /** * Constructor */ template ConnectedAdaptiveThresholdImageFilter::ConnectedAdaptiveThresholdImageFilter() - : m_FineDetectionMode(false) + : m_OutoutImageMaskFineSegmentation(nullptr), + m_GrowingDirectionIsUpwards(true), + m_SeedpointValue(0), + m_DetectedLeakagePoint(0), + m_InitValue(0), + m_AdjLowerTh(0), + m_AdjUpperTh(0), + m_FineDetectionMode(false), + m_DiscardLastPreview(false), + m_SegmentationCancelled(false) { } template void ConnectedAdaptiveThresholdImageFilter::GenerateData() { typename ConnectedAdaptiveThresholdImageFilter::InputImageConstPointer inputImage = this->GetInput(); typename ConnectedAdaptiveThresholdImageFilter::OutputImagePointer outputImage = this->GetOutput(); typename Superclass::InputPixelObjectType::Pointer lowerThreshold = this->GetLowerInput(); typename Superclass::InputPixelObjectType::Pointer upperThreshold = this->GetUpperInput(); // kommt drauf, wie wir hier die Pipeline aufbauen this->SetLower(lowerThreshold->Get()); this->SetUpper(upperThreshold->Get()); typedef BinaryThresholdImageFunction FunctionType; typedef AdaptiveThresholdIterator IteratorType; int initValue = IteratorType::CalculateInitializeValue((int)(this->GetLower()), (int)(this->GetUpper())); // Initialize the output according to the segmentation (fine or raw) if (m_FineDetectionMode) { outputImage = this->m_OutoutImageMaskFineSegmentation; } typename ConnectedAdaptiveThresholdImageFilter::OutputImageRegionType region = outputImage->GetRequestedRegion(); outputImage->SetBufferedRegion(region); outputImage->Allocate(); if (!m_FineDetectionMode) { // only initalize the output image if we are using the raw segmentation mode outputImage->FillBuffer((typename ConnectedAdaptiveThresholdImageFilter::OutputImagePixelType)initValue); } typename FunctionType::Pointer function = FunctionType::New(); function->SetInputImage(inputImage); typename Superclass::SeedContainerType seeds; seeds = this->GetSeeds(); // pass parameters needed for region growing to iterator IteratorType it(outputImage, function, seeds); it.SetFineDetectionMode(m_FineDetectionMode); it.SetExpansionDirection(m_GrowingDirectionIsUpwards); it.SetMinTH((int)(this->GetLower())); it.SetMaxTH((int)(this->GetUpper())); it.GoToBegin(); this->m_SeedpointValue = it.GetSeedPointValue(); if ((this->GetLower()) > this->m_SeedpointValue || this->m_SeedpointValue > (this->GetUpper())) { // set m_SegmentationCancelled to true, so if it doesn't reach the point where it is set back to false // we can asssume that there was an error this->m_SegmentationCancelled = true; return; } // iterate through image until while (!it.IsAtEnd()) { // make iterator go one step further (calls method DoFloodStep()) ++it; } this->m_DetectedLeakagePoint = it.GetLeakagePoint(); this->m_SegmentationCancelled = false; } template TOutputImage *itk::ConnectedAdaptiveThresholdImageFilter::GetResultImage() { return m_OutoutImageMaskFineSegmentation; } template typename ConnectedAdaptiveThresholdImageFilter::IndexType itk::ConnectedAdaptiveThresholdImageFilter::CorrectSeedPointPosition( unsigned int sizeOfVolume, int lowerTh, int upperTh) { typename ConnectedAdaptiveThresholdImageFilter::InputImageConstPointer inputImage = this->GetInput(); typedef typename TInputImage::IndexType IndexType; IndexType itkIntelligentSeedIndex; int seedPixelValue = inputImage->GetPixel(m_SeedPointIndex); // set new seed index to the voxel with the darkest value and shortest distance to original seed if (seedPixelValue > upperTh || seedPixelValue < lowerTh) { // MITK_INFO << "seed pixel value [BEFORE] = " << seedPixelValue; // ToDo crop region itk::Index<3> workindex; for (int i = 0; i < 3; i++) { workindex[i] = m_SeedPointIndex[i] - sizeOfVolume / 2; if (workindex[i] < 0) workindex[i] = 0; } itk::Size<3> worksize; for (int i = 0; i < 3; i++) { worksize[i] = sizeOfVolume; } itk::ImageRegion<3> workregion(workindex, worksize); itk::ImageRegionIterator regionIt(const_cast(inputImage.GetPointer()), workregion); // int darkestGrayValue=seedPixelValue; int currentGrayValue; float distance = (float)(sizeOfVolume / 2); float relativeDistance = 1; // between 0 and 1 mitk::Vector3D seedVector, currentVector; mitk::FillVector3D(seedVector, m_SeedPointIndex[0], m_SeedPointIndex[1], m_SeedPointIndex[2]); currentVector = seedVector; float costValue = 0; // beware, Depending on seeking upper or lower value... for (regionIt.GoToBegin(); !regionIt.IsAtEnd(); ++regionIt) { // get current gray value currentGrayValue = regionIt.Value(); // get current seed index m_SeedPointIndex = regionIt.GetIndex(); // fill current vector mitk::FillVector3D(currentVector, m_SeedPointIndex[0], m_SeedPointIndex[1], m_SeedPointIndex[2]); // calculate distance from original seed to new seed mitk::Vector3D distVector = currentVector - seedVector; distance = fabs(distVector.GetSquaredNorm()); relativeDistance = distance / (sizeOfVolume / 2); // calculate "cost function" float currentCostValue = (1 - relativeDistance) * currentGrayValue; if (currentCostValue < costValue && currentGrayValue < upperTh) { itkIntelligentSeedIndex = regionIt.GetIndex(); costValue = currentCostValue; // MITK_INFO <<"cost value="<< costValue; // MITK_INFO <<"darkest and closest Voxel ="<< currentGrayValue; // MITK_INFO <<"m_UPPER="<< upperTh; } } // MITK_INFO<< "seed pixel value [AFTER] =" << inputImage->GetPixel(itkIntelligentSeedIndex) <<"\n"; } else { // no correction of the seed point is needed, just pass the original seed itkIntelligentSeedIndex = m_SeedPointIndex; } return itkIntelligentSeedIndex; } template void itk::ConnectedAdaptiveThresholdImageFilter::CropMask(unsigned int croppingSize) { // initialize center point of the working region itk::Index<3> workindex; for (int i = 0; i < 3; i++) { workindex[i] = m_SeedPointIndex[i] - croppingSize / 2; if (workindex[i] < 0) workindex[i] = 0; } // initialize working volume itk::Size<3> worksize; for (int i = 0; i < 3; i++) { worksize[i] = croppingSize; } // set working region itk::ImageRegion<3> workregion(workindex, worksize); // check if the entire region is inside the image if (!(m_OutoutImageMaskFineSegmentation->GetLargestPossibleRegion().IsInside(workregion))) { // if not then crop to the intersection of the image (gemeinsame Schnittmenge Bild und workingRegion) if (!(workregion.Crop(m_OutoutImageMaskFineSegmentation->GetLargestPossibleRegion()))) { MITK_ERROR << "Cropping working region failed!"; return; } } // initialize region iterator itk::ImageRegionIterator regionIt(m_OutoutImageMaskFineSegmentation, workregion); for (regionIt.GoToBegin(); !regionIt.IsAtEnd(); ++regionIt) { // and set all voxel inside the working region to zero regionIt.Set(0); } } template unsigned int itk::ConnectedAdaptiveThresholdImageFilter::AdjustIteratorMask() { typedef itk::ThresholdImageFilter ThresholdFilterType; typedef itk::MinimumMaximumImageFilter MaxFilterType; typename ThresholdFilterType::Pointer thresholdFilter = ThresholdFilterType::New(); typename MaxFilterType::Pointer maxFilter = MaxFilterType::New(); unsigned int maxValue; if (!m_DiscardLastPreview) { // get the biggest value of the image maxFilter->SetInput(m_OutoutImageMaskFineSegmentation); maxFilter->UpdateLargestPossibleRegion(); maxValue = maxFilter->GetMaximum(); } else { // use the last biggest value in the preview. This was set in SetParameterForFineSegmentation(...adjLowerTh...) [] maxValue = m_AdjLowerTh; } // set all values upper to zero (thresouldOutside uses < and > NOT <= and >=) thresholdFilter->SetInput(m_OutoutImageMaskFineSegmentation); thresholdFilter->SetOutsideValue(0); thresholdFilter->ThresholdOutside(m_AdjLowerTh, maxValue); thresholdFilter->UpdateLargestPossibleRegion(); // set all values in between lower and upper (>=lower && <=upper) to the highest value in the image thresholdFilter->SetInput(thresholdFilter->GetOutput()); thresholdFilter->SetOutsideValue(maxValue); thresholdFilter->ThresholdOutside(0, m_AdjLowerTh - 1); thresholdFilter->UpdateLargestPossibleRegion(); m_OutoutImageMaskFineSegmentation = thresholdFilter->GetOutput(); return maxValue; } template void itk::ConnectedAdaptiveThresholdImageFilter::SetParameterForFineSegmentation( TOutputImage *iteratorMaskForFineSegmentation, unsigned int adjLowerTh, unsigned int adjUpperTh, itk::Index<3> seedPoint, bool discardLeafSegmentation) { // just to make sure we´re in the right mode and the mask exsits if (m_FineDetectionMode && iteratorMaskForFineSegmentation) { m_OutoutImageMaskFineSegmentation = iteratorMaskForFineSegmentation; m_AdjLowerTh = adjLowerTh; m_AdjUpperTh = adjUpperTh; // still needed? m_SeedPointIndex = seedPoint; m_DiscardLastPreview = discardLeafSegmentation; } else { if (!m_FineDetectionMode) { MITK_ERROR << "Fine-detection-segmentation mode not set!"; } else { MITK_ERROR << "Iterator-mask-image not set!"; } } } } // end namespace itk #endif diff --git a/Modules/Segmentation/Algorithms/mitkCalculateSegmentationVolume.cpp b/Modules/Segmentation/Algorithms/mitkCalculateSegmentationVolume.cpp index 8f8305f90e..7fe3898104 100644 --- a/Modules/Segmentation/Algorithms/mitkCalculateSegmentationVolume.cpp +++ b/Modules/Segmentation/Algorithms/mitkCalculateSegmentationVolume.cpp @@ -1,122 +1,122 @@ /*=================================================================== 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 "itkImageRegionConstIteratorWithIndex.h" #include "mitkCalculateSegmentationVolume.h" #include namespace mitk { - CalculateSegmentationVolume::CalculateSegmentationVolume() {} + CalculateSegmentationVolume::CalculateSegmentationVolume() : m_Volume(0){} CalculateSegmentationVolume::~CalculateSegmentationVolume() {} template void CalculateSegmentationVolume::ItkImageProcessing(itk::Image *itkImage, TPixel *itkNotUsed(dummy)) { itk::ImageRegionConstIteratorWithIndex> iterBinaryImage( itkImage, itkImage->GetLargestPossibleRegion()); typename itk::ImageRegionConstIteratorWithIndex>::IndexType currentIndex; typename itk::ImageRegionConstIteratorWithIndex>::IndexType minIndex; for (unsigned int i = 0; i < VImageDimension; ++i) minIndex[i] = std::numeric_limits::max(); typename itk::ImageRegionConstIteratorWithIndex>::IndexType maxIndex; for (unsigned int i = 0; i < VImageDimension; ++i) maxIndex[i] = std::numeric_limits::min(); m_CenterOfMass.Fill(0.0); m_Volume = 0; while (!iterBinaryImage.IsAtEnd()) { if (iterBinaryImage.Get() > static_cast(0.0)) { // update center of mass currentIndex = iterBinaryImage.GetIndex(); itk::Vector currentPoint; for (unsigned int i = 0; i < VImageDimension; ++i) currentPoint[i] = currentIndex[i]; m_CenterOfMass = (m_CenterOfMass * (static_cast(m_Volume) / static_cast(m_Volume + 1))) // e.g. 3 points: old center * 2/3 + currentPoint * 1/3; + currentPoint / static_cast(m_Volume + 1); // update number of voxels ++m_Volume; // update bounding box for (unsigned int i = 0; i < VImageDimension; ++i) { if (currentIndex[i] < minIndex[i]) minIndex[i] = currentIndex[i]; if (currentIndex[i] > maxIndex[i]) maxIndex[i] = currentIndex[i]; } } ++iterBinaryImage; } m_MinIndexOfBoundingBox[2] = 0.0; m_MaxIndexOfBoundingBox[2] = 0.0; for (unsigned int i = 0; i < VImageDimension; ++i) { m_MinIndexOfBoundingBox[i] = minIndex[i]; m_MaxIndexOfBoundingBox[i] = maxIndex[i]; } } bool CalculateSegmentationVolume::ReadyToRun() { Image::Pointer image; GetPointerParameter("Input", image); return image.IsNotNull() && GetGroupNode(); } bool CalculateSegmentationVolume::ThreadedUpdateFunction() { // get image Image::Pointer image; GetPointerParameter("Input", image); AccessFixedDimensionByItk(image.GetPointer(), ItkImageProcessing, 3); // some magic to call the correctly templated function (we only do 3D images here!) // consider single voxel volume Vector3D spacing = image->GetSlicedGeometry()->GetSpacing(); // spacing in mm float volumeML = (ScalarType)m_Volume * spacing[0] * spacing[1] * spacing[2] / 1000.0; // convert to ml DataNode *groupNode = GetGroupNode(); if (groupNode) { groupNode->SetProperty("volume", FloatProperty::New(volumeML)); groupNode->SetProperty("centerOfMass", Vector3DProperty::New(m_CenterOfMass)); groupNode->SetProperty("boundingBoxMinimum", Vector3DProperty::New(m_MinIndexOfBoundingBox)); groupNode->SetProperty("boundingBoxMaximum", Vector3DProperty::New(m_MaxIndexOfBoundingBox)); groupNode->SetProperty("showVolume", BoolProperty::New(true)); } return true; } } // namespace diff --git a/Modules/Segmentation/Algorithms/mitkDiffImageApplier.cpp b/Modules/Segmentation/Algorithms/mitkDiffImageApplier.cpp index 5ee9584ac0..64a3787caf 100644 --- a/Modules/Segmentation/Algorithms/mitkDiffImageApplier.cpp +++ b/Modules/Segmentation/Algorithms/mitkDiffImageApplier.cpp @@ -1,355 +1,363 @@ /*=================================================================== 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 "mitkDiffImageApplier.h" #include "mitkApplyDiffImageOperation.h" #include "mitkImageAccessByItk.h" #include "mitkImageCast.h" #include "mitkImageTimeSelector.h" #include "mitkRenderingManager.h" #include "mitkSegmentationInterpolationController.h" #include #include #include mitk::DiffImageApplier::DiffImageApplier() + : m_Image(nullptr), + m_SliceDifferenceImage(nullptr), + m_SliceIndex(0), + m_SliceDimension(0), + m_TimeStep(0), + m_Dimension0(0), + m_Dimension1(0), + m_Factor(1.0) { } mitk::DiffImageApplier::~DiffImageApplier() { } void mitk::DiffImageApplier::ExecuteOperation(Operation *operation) { auto *imageOperation = dynamic_cast(operation); if (imageOperation // we actually have the kind of operation that we can handle && imageOperation->IsImageStillValid()) // AND the image is not yet deleted { m_Image = imageOperation->GetImage(); Image::Pointer image3D = m_Image; // will be changed later in case of 3D+t m_SliceDifferenceImage = imageOperation->GetDiffImage(); m_TimeStep = imageOperation->GetTimeStep(); m_Factor = imageOperation->GetFactor(); if (m_SliceDifferenceImage->GetDimension() == 2) { m_SliceIndex = imageOperation->GetSliceIndex(); m_SliceDimension = imageOperation->GetSliceDimension(); switch (m_SliceDimension) { default: case 2: m_Dimension0 = 0; m_Dimension1 = 1; break; case 1: m_Dimension0 = 0; m_Dimension1 = 2; break; case 0: m_Dimension0 = 1; m_Dimension1 = 2; break; } if (m_SliceDifferenceImage->GetDimension() != 2 || (m_Image->GetDimension() < 3 || m_Image->GetDimension() > 4) || m_SliceDifferenceImage->GetDimension(0) != m_Image->GetDimension(m_Dimension0) || m_SliceDifferenceImage->GetDimension(1) != m_Image->GetDimension(m_Dimension1) || m_SliceIndex >= m_Image->GetDimension(m_SliceDimension)) { itkExceptionMacro( "Slice and image dimensions differ or slice index is too large. Sorry, cannot work like this."); return; } if (m_Image->GetDimension() == 4) { ImageTimeSelector::Pointer timeSelector = ImageTimeSelector::New(); timeSelector->SetInput(m_Image); timeSelector->SetTimeNr(m_TimeStep); timeSelector->UpdateLargestPossibleRegion(); image3D = timeSelector->GetOutput(); } // this will do a long long if/else to find out both pixel types AccessFixedDimensionByItk(image3D, ItkImageSwitch2DDiff, 3); if (m_Factor == 1 || m_Factor == -1) { if (m_Factor == -1) { // multiply diff pixels by factor and then send this diff slice AccessFixedDimensionByItk(m_SliceDifferenceImage, ItkInvertPixelValues, 2); } // just send the diff to SegmentationInterpolationController SegmentationInterpolationController *interpolator = SegmentationInterpolationController::InterpolatorForImage(m_Image); if (interpolator) { interpolator->BlockModified(true); interpolator->SetChangedSlice(m_SliceDifferenceImage, m_SliceDimension, m_SliceIndex, m_TimeStep); } m_Image->Modified(); if (interpolator) { interpolator->BlockModified(false); } if (m_Factor == -1) // return to normal values { AccessFixedDimensionByItk(m_SliceDifferenceImage, ItkInvertPixelValues, 2); } } else // no trivial case, too lazy to do something else { m_Image->Modified(); // check if interpolation is called. prefer to send diff directly } RenderingManager::GetInstance()->RequestUpdateAll(); } else if (m_SliceDifferenceImage->GetDimension() == 3) { // ... if (m_SliceDifferenceImage->GetDimension(0) != m_Image->GetDimension(0) || m_SliceDifferenceImage->GetDimension(1) != m_Image->GetDimension(1) || m_SliceDifferenceImage->GetDimension(2) != m_Image->GetDimension(2) || m_TimeStep >= m_Image->GetDimension(3)) { itkExceptionMacro("Diff image size differs from original image size. Sorry, cannot work like this."); return; } if (m_Image->GetDimension() == 4) { ImageTimeSelector::Pointer timeSelector = ImageTimeSelector::New(); timeSelector->SetInput(m_Image); timeSelector->SetTimeNr(m_TimeStep); timeSelector->UpdateLargestPossibleRegion(); image3D = timeSelector->GetOutput(); } // this will do a long long if/else to find out both pixel types AccessFixedDimensionByItk(image3D, ItkImageSwitch3DDiff, 3); if (m_Factor == 1 || m_Factor == -1) { if (m_Factor == -1) { // multiply diff pixels by factor and then send this diff slice AccessFixedDimensionByItk(m_SliceDifferenceImage, ItkInvertPixelValues, 3); } // just send the diff to SegmentationInterpolationController SegmentationInterpolationController *interpolator = SegmentationInterpolationController::InterpolatorForImage(m_Image); if (interpolator) { interpolator->BlockModified(true); interpolator->SetChangedVolume(m_SliceDifferenceImage, m_TimeStep); } m_Image->Modified(); if (interpolator) { interpolator->BlockModified(false); } if (m_Factor == -1) // return to normal values { AccessFixedDimensionByItk(m_SliceDifferenceImage, ItkInvertPixelValues, 3); } } else // no trivial case, too lazy to do something else { m_Image->Modified(); // check if interpolation is called. prefer to send diff directly } RenderingManager::GetInstance()->RequestUpdateAll(); } else { itkExceptionMacro("Diff image must be 2D or 3D. Sorry, cannot work like this."); return; } } m_Image = nullptr; m_SliceDifferenceImage = nullptr; } mitk::DiffImageApplier *mitk::DiffImageApplier::GetInstanceForUndo() { static DiffImageApplier::Pointer s_Instance = DiffImageApplier::New(); return s_Instance; } // basically copied from mitk/Core/Algorithms/mitkImageAccessByItk.h #define myMITKDiffImageApplierFilterAccessByItk(mitkImage, itkImageTypeFunction, pixeltype, dimension, itkimage2) \ if (typeId == MapPixelComponentType::value) \ \ { \ typedef itk::Image ImageType; \ typedef mitk::ImageToItk ImageToItkType; \ itk::SmartPointer imagetoitk = ImageToItkType::New(); \ const mitk::Image *constImage = mitkImage; \ mitk::Image *nonConstImage = const_cast(constImage); \ nonConstImage->Update(); \ imagetoitk->SetInput(nonConstImage); \ imagetoitk->Update(); \ itkImageTypeFunction(imagetoitk->GetOutput(), itkimage2); \ \ } #define myMITKDiffImageApplierFilterAccessAllTypesByItk(mitkImage, itkImageTypeFunction, dimension, itkimage2) \ \ { \ myMITKDiffImageApplierFilterAccessByItk(mitkImage, itkImageTypeFunction, double, dimension, itkimage2) else myMITKDiffImageApplierFilterAccessByItk( \ mitkImage, \ itkImageTypeFunction, \ float, \ dimension, \ itkimage2) else myMITKDiffImageApplierFilterAccessByItk(mitkImage, itkImageTypeFunction, int, dimension, itkimage2) else myMITKDiffImageApplierFilterAccessByItk(mitkImage, \ itkImageTypeFunction, \ unsigned int, \ dimension, \ itkimage2) else myMITKDiffImageApplierFilterAccessByItk(mitkImage, itkImageTypeFunction, short, dimension, itkimage2) else myMITKDiffImageApplierFilterAccessByItk(mitkImage, itkImageTypeFunction, unsigned short, dimension, itkimage2) else myMITKDiffImageApplierFilterAccessByItk(mitkImage, \ itkImageTypeFunction, \ char, \ dimension, \ itkimage2) else myMITKDiffImageApplierFilterAccessByItk(mitkImage, \ itkImageTypeFunction, \ unsigned char, \ dimension, \ itkimage2) \ \ } template void mitk::DiffImageApplier::ItkImageSwitch2DDiff(itk::Image *itkImage) { const int typeId = m_SliceDifferenceImage->GetPixelType().GetComponentType(); myMITKDiffImageApplierFilterAccessAllTypesByItk(m_SliceDifferenceImage, ItkImageProcessing2DDiff, 2, itkImage); } template void mitk::DiffImageApplier::ItkImageSwitch3DDiff(itk::Image *itkImage) { const int typeId = m_SliceDifferenceImage->GetPixelType().GetComponentType(); myMITKDiffImageApplierFilterAccessAllTypesByItk(m_SliceDifferenceImage, ItkImageProcessing3DDiff, 3, itkImage); } template void mitk::DiffImageApplier::ItkImageProcessing2DDiff(itk::Image *diffImage, itk::Image *outputImage) { typedef itk::Image DiffImageType; typedef itk::Image VolumeImageType; typedef itk::ImageSliceIteratorWithIndex OutputSliceIteratorType; typedef itk::ImageRegionConstIterator DiffSliceIteratorType; typename VolumeImageType::RegionType sliceInVolumeRegion; sliceInVolumeRegion = outputImage->GetLargestPossibleRegion(); sliceInVolumeRegion.SetSize(m_SliceDimension, 1); // just one slice sliceInVolumeRegion.SetIndex(m_SliceDimension, m_SliceIndex); // exactly this slice, please OutputSliceIteratorType outputIterator(outputImage, sliceInVolumeRegion); outputIterator.SetFirstDirection(m_Dimension0); outputIterator.SetSecondDirection(m_Dimension1); DiffSliceIteratorType diffIterator(diffImage, diffImage->GetLargestPossibleRegion()); // iterate over output slice (and over input slice simultaneously) outputIterator.GoToBegin(); diffIterator.GoToBegin(); while (!outputIterator.IsAtEnd()) { while (!outputIterator.IsAtEndOfSlice()) { while (!outputIterator.IsAtEndOfLine()) { TPixel2 newValue = outputIterator.Get() + (TPixel2)((double)diffIterator.Get() * m_Factor); outputIterator.Set(newValue); ++outputIterator; ++diffIterator; } outputIterator.NextLine(); } outputIterator.NextSlice(); } } template void mitk::DiffImageApplier::ItkImageProcessing3DDiff(itk::Image *diffImage, itk::Image *outputImage) { typedef itk::Image DiffImageType; typedef itk::Image VolumeImageType; typedef itk::ImageRegionIterator OutputSliceIteratorType; typedef itk::ImageRegionConstIterator DiffSliceIteratorType; OutputSliceIteratorType outputIterator(outputImage, outputImage->GetLargestPossibleRegion()); DiffSliceIteratorType diffIterator(diffImage, diffImage->GetLargestPossibleRegion()); // iterate over output slice (and over input slice simultaneously) outputIterator.GoToBegin(); diffIterator.GoToBegin(); while (!outputIterator.IsAtEnd()) { TPixel2 newValue = outputIterator.Get() + (TPixel2)((double)diffIterator.Get() * m_Factor); outputIterator.Set(newValue); ++outputIterator; ++diffIterator; } } #ifdef _MSC_VER # pragma warning(push) # pragma warning(disable:4146) // unary minus operator applied to unsigned type, result still unsigned #endif template void mitk::DiffImageApplier::ItkInvertPixelValues(itk::Image *itkImage) { typedef itk::ImageRegionIterator> IteratorType; IteratorType iter(itkImage, itkImage->GetLargestPossibleRegion()); iter.GoToBegin(); while (!iter.IsAtEnd()) { iter.Set(-(iter.Get())); ++iter; } } #ifdef _MSC_VER # pragma warning(pop) #endif diff --git a/Modules/Segmentation/Algorithms/mitkDiffSliceOperation.cpp b/Modules/Segmentation/Algorithms/mitkDiffSliceOperation.cpp index 7099e138e6..2982be76c0 100644 --- a/Modules/Segmentation/Algorithms/mitkDiffSliceOperation.cpp +++ b/Modules/Segmentation/Algorithms/mitkDiffSliceOperation.cpp @@ -1,102 +1,104 @@ /*=================================================================== 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 "mitkDiffSliceOperation.h" #include #include mitk::DiffSliceOperation::DiffSliceOperation() : Operation(1) { m_TimeStep = 0; m_zlibSliceContainer = nullptr; m_Image = nullptr; m_WorldGeometry = nullptr; m_SliceGeometry = nullptr; m_ImageIsValid = false; + m_DeleteObserverTag = 0; } mitk::DiffSliceOperation::DiffSliceOperation(mitk::Image *imageVolume, Image *slice, SlicedGeometry3D *sliceGeometry, unsigned int timestep, BaseGeometry *currentWorldGeometry) : Operation(1) { m_WorldGeometry = currentWorldGeometry->Clone(); /* Quick fix for bug 12338. Guard object - fix this when clone method of PlaneGeometry is cloning the reference geometry (see bug 13392)*/ // xxxx m_GuardReferenceGeometry = mitk::BaseGeometry::New(); m_GuardReferenceGeometry = dynamic_cast(m_WorldGeometry.GetPointer())->GetReferenceGeometry(); /*---------------------------------------------------------------------------------------------------*/ m_SliceGeometry = sliceGeometry->Clone(); m_TimeStep = timestep; m_zlibSliceContainer = CompressedImageContainer::New(); m_zlibSliceContainer->SetImage(slice); m_Image = imageVolume; + m_DeleteObserverTag = 0; if (m_Image) { /*add an observer to listen to the delete event of the image, this is necessary because the operation is then * invalid*/ itk::SimpleMemberCommand::Pointer command = itk::SimpleMemberCommand::New(); command->SetCallbackFunction(this, &DiffSliceOperation::OnImageDeleted); // get the id of the observer, used to remove it later on m_DeleteObserverTag = imageVolume->AddObserver(itk::DeleteEvent(), command); m_ImageIsValid = true; } else m_ImageIsValid = false; } mitk::DiffSliceOperation::~DiffSliceOperation() { m_WorldGeometry = nullptr; m_zlibSliceContainer = nullptr; if (m_ImageIsValid) { // if the image is still there, we have to remove the observer from it m_Image->RemoveObserver(m_DeleteObserverTag); } m_Image = nullptr; } mitk::Image::Pointer mitk::DiffSliceOperation::GetSlice() { Image::Pointer image = m_zlibSliceContainer->GetImage(); return image; } bool mitk::DiffSliceOperation::IsValid() { return m_ImageIsValid && m_zlibSliceContainer.IsNotNull() && (m_WorldGeometry.IsNotNull()); // TODO improve } void mitk::DiffSliceOperation::OnImageDeleted() { // if our imageVolume is removed e.g. from the datastorage the operation is no lnger valid m_ImageIsValid = false; } diff --git a/Modules/Segmentation/Algorithms/mitkImageToContourFilter.cpp b/Modules/Segmentation/Algorithms/mitkImageToContourFilter.cpp index ef6fe0753f..f48870f246 100644 --- a/Modules/Segmentation/Algorithms/mitkImageToContourFilter.cpp +++ b/Modules/Segmentation/Algorithms/mitkImageToContourFilter.cpp @@ -1,157 +1,158 @@ /*=================================================================== 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 "mitkImageToContourFilter.h" #include "mitkImageAccessByItk.h" #include "mitkImageCast.h" #include "mitkVtkRepresentationProperty.h" #include "vtkLinearTransform.h" #include "vtkMatrix4x4.h" #include "vtkProperty.h" #include "vtkSmartPointer.h" #include mitk::ImageToContourFilter::ImageToContourFilter() { this->m_UseProgressBar = false; this->m_ProgressStepSize = 1; + this->m_SliceGeometry = nullptr; } mitk::ImageToContourFilter::~ImageToContourFilter() { } void mitk::ImageToContourFilter::GenerateData() { mitk::Image::ConstPointer sliceImage = ImageToSurfaceFilter::GetInput(); if (!sliceImage) { MITK_ERROR << "mitk::ImageToContourFilter: No input available. Please set the input!" << std::endl; itkExceptionMacro("mitk::ImageToContourFilter: No input available. Please set the input!"); return; } if (sliceImage->GetDimension() > 2 || sliceImage->GetDimension() < 2) { MITK_ERROR << "mitk::ImageToImageFilter::GenerateData() works only with 2D images. Please assure that your input " "image is 2D!" << std::endl; itkExceptionMacro( "mitk::ImageToImageFilter::GenerateData() works only with 2D images. Please assure that your input image is 2D!"); return; } m_SliceGeometry = sliceImage->GetGeometry(); AccessFixedDimensionByItk(sliceImage, Itk2DContourExtraction, 2); // Setting progressbar if (this->m_UseProgressBar) mitk::ProgressBar::GetInstance()->Progress(this->m_ProgressStepSize); } template void mitk::ImageToContourFilter::Itk2DContourExtraction(const itk::Image *sliceImage) { typedef itk::Image ImageType; typedef itk::ContourExtractor2DImageFilter ContourExtractor; typedef itk::ConstantPadImageFilter PadFilterType; typename PadFilterType::Pointer padFilter = PadFilterType::New(); typename ImageType::SizeType lowerExtendRegion; lowerExtendRegion[0] = 1; lowerExtendRegion[1] = 1; typename ImageType::SizeType upperExtendRegion; upperExtendRegion[0] = 1; upperExtendRegion[1] = 1; /* * We need to pad here, since the ITK contour extractor fails if the * segmentation touches more than one image edge. * By padding the image for one row at each edge we overcome this issue */ padFilter->SetInput(sliceImage); padFilter->SetConstant(0); padFilter->SetPadLowerBound(lowerExtendRegion); padFilter->SetPadUpperBound(upperExtendRegion); typename ContourExtractor::Pointer contourExtractor = ContourExtractor::New(); contourExtractor->SetInput(padFilter->GetOutput()); contourExtractor->SetContourValue(0.5); contourExtractor->Update(); unsigned int foundPaths = contourExtractor->GetNumberOfOutputs(); vtkSmartPointer contourSurface = vtkSmartPointer::New(); vtkSmartPointer points = vtkSmartPointer::New(); vtkSmartPointer polygons = vtkSmartPointer::New(); unsigned int pointId(0); for (unsigned int i = 0; i < foundPaths; i++) { const ContourPath *currentPath = contourExtractor->GetOutput(i)->GetVertexList(); vtkSmartPointer polygon = vtkSmartPointer::New(); polygon->GetPointIds()->SetNumberOfIds(currentPath->Size()); Point3D currentPoint; Point3D currentWorldPoint; for (unsigned int j = 0; j < currentPath->Size(); j++) { currentPoint[0] = currentPath->ElementAt(j)[0]; currentPoint[1] = currentPath->ElementAt(j)[1]; currentPoint[2] = 0; m_SliceGeometry->IndexToWorld(currentPoint, currentWorldPoint); points->InsertPoint(pointId, currentWorldPoint[0], currentWorldPoint[1], currentWorldPoint[2]); polygon->GetPointIds()->SetId(j, pointId); pointId++; } // for2 polygons->InsertNextCell(polygon); } // for1 contourSurface->SetPoints(points); contourSurface->SetPolys(polygons); contourSurface->BuildLinks(); Surface::Pointer finalSurface = this->GetOutput(); finalSurface->SetVtkPolyData(contourSurface); } void mitk::ImageToContourFilter::GenerateOutputInformation() { Superclass::GenerateOutputInformation(); } void mitk::ImageToContourFilter::SetUseProgressBar(bool status) { this->m_UseProgressBar = status; } void mitk::ImageToContourFilter::SetProgressStepSize(unsigned int stepSize) { this->m_ProgressStepSize = stepSize; } diff --git a/Modules/Segmentation/Controllers/mitkSegmentationInterpolationController.cpp b/Modules/Segmentation/Controllers/mitkSegmentationInterpolationController.cpp index 1031296c03..87040ca083 100644 --- a/Modules/Segmentation/Controllers/mitkSegmentationInterpolationController.cpp +++ b/Modules/Segmentation/Controllers/mitkSegmentationInterpolationController.cpp @@ -1,589 +1,590 @@ /*=================================================================== 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 "mitkSegmentationInterpolationController.h" #include "mitkImageCast.h" #include "mitkImageReadAccessor.h" #include "mitkImageTimeSelector.h" #include #include //#include #include "mitkShapeBasedInterpolationAlgorithm.h" #include #include #include mitk::SegmentationInterpolationController::InterpolatorMapType mitk::SegmentationInterpolationController::s_InterpolatorForImage; // static member initialization mitk::SegmentationInterpolationController *mitk::SegmentationInterpolationController::InterpolatorForImage( const Image *image) { auto iter = s_InterpolatorForImage.find(image); if (iter != s_InterpolatorForImage.end()) { return iter->second; } else { return nullptr; } } -mitk::SegmentationInterpolationController::SegmentationInterpolationController() : m_BlockModified(false) +mitk::SegmentationInterpolationController::SegmentationInterpolationController() + : m_BlockModified(false), m_2DInterpolationActivated(false) { } void mitk::SegmentationInterpolationController::Activate2DInterpolation(bool status) { m_2DInterpolationActivated = status; } mitk::SegmentationInterpolationController *mitk::SegmentationInterpolationController::GetInstance() { static mitk::SegmentationInterpolationController::Pointer m_Instance; if (m_Instance.IsNull()) { m_Instance = SegmentationInterpolationController::New(); } return m_Instance; } mitk::SegmentationInterpolationController::~SegmentationInterpolationController() { // remove this from the list of interpolators for (auto iter = s_InterpolatorForImage.begin(); iter != s_InterpolatorForImage.end(); ++iter) { if (iter->second == this) { s_InterpolatorForImage.erase(iter); break; } } } void mitk::SegmentationInterpolationController::OnImageModified(const itk::EventObject &) { if (!m_BlockModified && m_Segmentation.IsNotNull() && m_2DInterpolationActivated) { SetSegmentationVolume(m_Segmentation); } } void mitk::SegmentationInterpolationController::BlockModified(bool block) { m_BlockModified = block; } void mitk::SegmentationInterpolationController::SetSegmentationVolume(const Image *segmentation) { // clear old information (remove all time steps m_SegmentationCountInSlice.clear(); // delete this from the list of interpolators auto iter = s_InterpolatorForImage.find(segmentation); if (iter != s_InterpolatorForImage.end()) { s_InterpolatorForImage.erase(iter); } if (!segmentation) return; if (segmentation->GetDimension() > 4 || segmentation->GetDimension() < 3) { itkExceptionMacro("SegmentationInterpolationController needs a 3D-segmentation or 3D+t, not 2D."); } if (m_Segmentation != segmentation) { // observe Modified() event of image itk::ReceptorMemberCommand::Pointer command = itk::ReceptorMemberCommand::New(); command->SetCallbackFunction(this, &SegmentationInterpolationController::OnImageModified); segmentation->AddObserver(itk::ModifiedEvent(), command); } m_Segmentation = segmentation; m_SegmentationCountInSlice.resize(m_Segmentation->GetTimeSteps()); for (unsigned int timeStep = 0; timeStep < m_Segmentation->GetTimeSteps(); ++timeStep) { m_SegmentationCountInSlice[timeStep].resize(3); for (unsigned int dim = 0; dim < 3; ++dim) { m_SegmentationCountInSlice[timeStep][dim].clear(); m_SegmentationCountInSlice[timeStep][dim].resize(m_Segmentation->GetDimension(dim)); m_SegmentationCountInSlice[timeStep][dim].assign(m_Segmentation->GetDimension(dim), 0); } } s_InterpolatorForImage.insert(std::make_pair(m_Segmentation, this)); // for all timesteps // scan whole image for (unsigned int timeStep = 0; timeStep < m_Segmentation->GetTimeSteps(); ++timeStep) { ImageTimeSelector::Pointer timeSelector = ImageTimeSelector::New(); timeSelector->SetInput(m_Segmentation); timeSelector->SetTimeNr(timeStep); timeSelector->UpdateLargestPossibleRegion(); Image::Pointer segmentation3D = timeSelector->GetOutput(); AccessFixedDimensionByItk_2(segmentation3D, ScanWholeVolume, 3, m_Segmentation, timeStep); } // PrintStatus(); SetReferenceVolume(m_ReferenceImage); Modified(); } void mitk::SegmentationInterpolationController::SetReferenceVolume(const Image *referenceImage) { m_ReferenceImage = referenceImage; if (m_ReferenceImage.IsNull()) return; // no image set - ignore it then assert(m_Segmentation.IsNotNull()); // should never happen // ensure the reference image has the same dimensionality and extents as the segmentation image if (m_ReferenceImage.IsNull() || m_Segmentation.IsNull() || m_ReferenceImage->GetDimension() != m_Segmentation->GetDimension() || m_ReferenceImage->GetPixelType().GetNumberOfComponents() != 1 || m_Segmentation->GetPixelType().GetNumberOfComponents() != 1) { MITK_WARN << "Segmentation image has different image characteristics than reference image." << std::endl; m_ReferenceImage = nullptr; return; } for (unsigned int dim = 0; dim < m_Segmentation->GetDimension(); ++dim) if (m_ReferenceImage->GetDimension(dim) != m_Segmentation->GetDimension(dim)) { MITK_WARN << "original patient image does not match segmentation (different extent in dimension " << dim << "), ignoring patient image" << std::endl; m_ReferenceImage = nullptr; return; } } void mitk::SegmentationInterpolationController::SetChangedVolume(const Image *sliceDiff, unsigned int timeStep) { if (!sliceDiff) return; if (sliceDiff->GetDimension() != 3) return; AccessFixedDimensionByItk_1(sliceDiff, ScanChangedVolume, 3, timeStep); // PrintStatus(); Modified(); } void mitk::SegmentationInterpolationController::SetChangedSlice(const Image *sliceDiff, unsigned int sliceDimension, unsigned int sliceIndex, unsigned int timeStep) { if (!sliceDiff) return; if (sliceDimension > 2) return; if (timeStep >= m_SegmentationCountInSlice.size()) return; if (sliceIndex >= m_SegmentationCountInSlice[timeStep][sliceDimension].size()) return; unsigned int dim0(0); unsigned int dim1(1); // determine the other two dimensions switch (sliceDimension) { default: case 2: dim0 = 0; dim1 = 1; break; case 1: dim0 = 0; dim1 = 2; break; case 0: dim0 = 1; dim1 = 2; break; } mitk::ImageReadAccessor readAccess(sliceDiff); auto *rawSlice = (unsigned char *)readAccess.GetData(); if (!rawSlice) return; AccessFixedDimensionByItk_1( sliceDiff, ScanChangedSlice, 2, SetChangedSliceOptions(sliceDimension, sliceIndex, dim0, dim1, timeStep, rawSlice)); Modified(); } template void mitk::SegmentationInterpolationController::ScanChangedSlice(const itk::Image *, const SetChangedSliceOptions &options) { auto *pixelData((DATATYPE *)options.pixelData); unsigned int timeStep(options.timeStep); unsigned int sliceDimension(options.sliceDimension); unsigned int sliceIndex(options.sliceIndex); if (sliceDimension > 2) return; if (sliceIndex >= m_SegmentationCountInSlice[timeStep][sliceDimension].size()) return; unsigned int dim0(options.dim0); unsigned int dim1(options.dim1); int numberOfPixels(0); // number of pixels in this slice that are not 0 unsigned int dim0max = m_SegmentationCountInSlice[timeStep][dim0].size(); unsigned int dim1max = m_SegmentationCountInSlice[timeStep][dim1].size(); // scan the slice from two directions // and set the flags for the two dimensions of the slice for (unsigned int v = 0; v < dim1max; ++v) { for (unsigned int u = 0; u < dim0max; ++u) { DATATYPE value = *(pixelData + u + v * dim0max); assert((signed)m_SegmentationCountInSlice[timeStep][dim0][u] + (signed)value >= 0); // just for debugging. This must always be true, otherwise some counting is going wrong assert((signed)m_SegmentationCountInSlice[timeStep][dim1][v] + (signed)value >= 0); m_SegmentationCountInSlice[timeStep][dim0][u] = static_cast(m_SegmentationCountInSlice[timeStep][dim0][u] + value); m_SegmentationCountInSlice[timeStep][dim1][v] = static_cast(m_SegmentationCountInSlice[timeStep][dim1][v] + value); numberOfPixels += static_cast(value); } } // flag for the dimension of the slice itself assert((signed)m_SegmentationCountInSlice[timeStep][sliceDimension][sliceIndex] + numberOfPixels >= 0); m_SegmentationCountInSlice[timeStep][sliceDimension][sliceIndex] += numberOfPixels; // MITK_INFO << "scan t=" << timeStep << " from (0,0) to (" << dim0max << "," << dim1max << ") (" << pixelData << "-" // << pixelData+dim0max*dim1max-1 << ") in slice " << sliceIndex << " found " << numberOfPixels << " pixels" << // std::endl; } template void mitk::SegmentationInterpolationController::ScanChangedVolume(const itk::Image *diffImage, unsigned int timeStep) { typedef itk::ImageSliceConstIteratorWithIndex> IteratorType; IteratorType iter(diffImage, diffImage->GetLargestPossibleRegion()); iter.SetFirstDirection(0); iter.SetSecondDirection(1); int numberOfPixels(0); // number of pixels in this slice that are not 0 typename IteratorType::IndexType index; unsigned int x = 0; unsigned int y = 0; unsigned int z = 0; iter.GoToBegin(); while (!iter.IsAtEnd()) { while (!iter.IsAtEndOfSlice()) { while (!iter.IsAtEndOfLine()) { index = iter.GetIndex(); x = index[0]; y = index[1]; z = index[2]; TPixel value = iter.Get(); assert((signed)m_SegmentationCountInSlice[timeStep][0][x] + (signed)value >= 0); // just for debugging. This must always be true, otherwise some counting is going wrong assert((signed)m_SegmentationCountInSlice[timeStep][1][y] + (signed)value >= 0); m_SegmentationCountInSlice[timeStep][0][x] = static_cast(m_SegmentationCountInSlice[timeStep][0][x] + value); m_SegmentationCountInSlice[timeStep][1][y] = static_cast(m_SegmentationCountInSlice[timeStep][1][y] + value); numberOfPixels += static_cast(value); ++iter; } iter.NextLine(); } assert((signed)m_SegmentationCountInSlice[timeStep][2][z] + numberOfPixels >= 0); m_SegmentationCountInSlice[timeStep][2][z] += numberOfPixels; numberOfPixels = 0; iter.NextSlice(); } } template void mitk::SegmentationInterpolationController::ScanWholeVolume(const itk::Image *, const Image *volume, unsigned int timeStep) { if (!volume) return; if (timeStep >= m_SegmentationCountInSlice.size()) return; ImageReadAccessor readAccess(volume, volume->GetVolumeData(timeStep)); for (unsigned int slice = 0; slice < volume->GetDimension(2); ++slice) { const auto *rawVolume = static_cast(readAccess.GetData()); // we again promise not to change anything, we'll just count const DATATYPE *rawSlice = rawVolume + (volume->GetDimension(0) * volume->GetDimension(1) * slice); ScanChangedSlice(nullptr, SetChangedSliceOptions(2, slice, 0, 1, timeStep, rawSlice)); } } void mitk::SegmentationInterpolationController::PrintStatus() { unsigned int timeStep(0); // if needed, put a loop over time steps around everyting, but beware, output will be long MITK_INFO << "Interpolator status (timestep 0): dimensions " << m_SegmentationCountInSlice[timeStep][0].size() << " " << m_SegmentationCountInSlice[timeStep][1].size() << " " << m_SegmentationCountInSlice[timeStep][2].size() << std::endl; MITK_INFO << "Slice 0: " << m_SegmentationCountInSlice[timeStep][2][0] << std::endl; // row "x" for (unsigned int index = 0; index < m_SegmentationCountInSlice[timeStep][0].size(); ++index) { if (m_SegmentationCountInSlice[timeStep][0][index] > 0) MITK_INFO << "O"; else MITK_INFO << "."; } MITK_INFO << std::endl; // rows "y" and "z" (diagonal) for (unsigned int index = 1; index < m_SegmentationCountInSlice[timeStep][1].size(); ++index) { if (m_SegmentationCountInSlice[timeStep][1][index] > 0) MITK_INFO << "O"; else MITK_INFO << "."; if (m_SegmentationCountInSlice[timeStep][2].size() > index) // if we also have a z value here, then print it, too { for (unsigned int indent = 1; indent < index; ++indent) MITK_INFO << " "; if (m_SegmentationCountInSlice[timeStep][2][index] > 0) MITK_INFO << m_SegmentationCountInSlice[timeStep][2][index]; //"O"; else MITK_INFO << "."; } MITK_INFO << std::endl; } // z indices that are larger than the biggest y index for (unsigned int index = m_SegmentationCountInSlice[timeStep][1].size(); index < m_SegmentationCountInSlice[timeStep][2].size(); ++index) { for (unsigned int indent = 0; indent < index; ++indent) MITK_INFO << " "; if (m_SegmentationCountInSlice[timeStep][2][index] > 0) MITK_INFO << m_SegmentationCountInSlice[timeStep][2][index]; //"O"; else MITK_INFO << "."; MITK_INFO << std::endl; } } mitk::Image::Pointer mitk::SegmentationInterpolationController::Interpolate(unsigned int sliceDimension, unsigned int sliceIndex, const mitk::PlaneGeometry *currentPlane, unsigned int timeStep) { if (m_Segmentation.IsNull()) return nullptr; if (!currentPlane) { return nullptr; } if (timeStep >= m_SegmentationCountInSlice.size()) return nullptr; if (sliceDimension > 2) return nullptr; unsigned int upperLimit = m_SegmentationCountInSlice[timeStep][sliceDimension].size(); if (sliceIndex >= upperLimit - 1) return nullptr; // can't interpolate first and last slice if (sliceIndex < 1) return nullptr; if (m_SegmentationCountInSlice[timeStep][sliceDimension][sliceIndex] > 0) return nullptr; // slice contains a segmentation, won't interpolate anything then unsigned int lowerBound(0); unsigned int upperBound(0); bool bounds(false); for (lowerBound = sliceIndex - 1; /*lowerBound >= 0*/; --lowerBound) { if (m_SegmentationCountInSlice[timeStep][sliceDimension][lowerBound] > 0) { bounds = true; break; } if (lowerBound == 0) break; // otherwise overflow and start at something like 4294967295 } if (!bounds) return nullptr; bounds = false; for (upperBound = sliceIndex + 1; upperBound < upperLimit; ++upperBound) { if (m_SegmentationCountInSlice[timeStep][sliceDimension][upperBound] > 0) { bounds = true; break; } } if (!bounds) return nullptr; // ok, we have found two neighboring slices with segmentations (and we made sure that the current slice does NOT // contain anything // MITK_INFO << "Interpolate in timestep " << timeStep << ", dimension " << sliceDimension << ": estimate slice " << // sliceIndex << " from slices " << lowerBound << " and " << upperBound << std::endl; mitk::Image::Pointer lowerMITKSlice; mitk::Image::Pointer upperMITKSlice; mitk::Image::Pointer resultImage; try { // Setting up the ExtractSliceFilter mitk::ExtractSliceFilter::Pointer extractor = ExtractSliceFilter::New(); extractor->SetInput(m_Segmentation); extractor->SetTimeStep(timeStep); extractor->SetResliceTransformByGeometry(m_Segmentation->GetTimeGeometry()->GetGeometryForTimeStep(timeStep)); extractor->SetVtkOutputRequest(false); // Reslicing the current plane extractor->SetWorldGeometry(currentPlane); extractor->Modified(); extractor->Update(); resultImage = extractor->GetOutput(); resultImage->DisconnectPipeline(); // Creating PlaneGeometry for lower slice mitk::PlaneGeometry::Pointer reslicePlane = currentPlane->Clone(); // Transforming the current origin so that it matches the lower slice mitk::Point3D origin = currentPlane->GetOrigin(); m_Segmentation->GetSlicedGeometry(timeStep)->WorldToIndex(origin, origin); origin[sliceDimension] = lowerBound; m_Segmentation->GetSlicedGeometry(timeStep)->IndexToWorld(origin, origin); reslicePlane->SetOrigin(origin); // Extract the lower slice extractor = ExtractSliceFilter::New(); extractor->SetInput(m_Segmentation); extractor->SetTimeStep(timeStep); extractor->SetResliceTransformByGeometry(m_Segmentation->GetTimeGeometry()->GetGeometryForTimeStep(timeStep)); extractor->SetVtkOutputRequest(false); extractor->SetWorldGeometry(reslicePlane); extractor->Modified(); extractor->Update(); lowerMITKSlice = extractor->GetOutput(); lowerMITKSlice->DisconnectPipeline(); // Transforming the current origin so that it matches the upper slice m_Segmentation->GetSlicedGeometry(timeStep)->WorldToIndex(origin, origin); origin[sliceDimension] = upperBound; m_Segmentation->GetSlicedGeometry(timeStep)->IndexToWorld(origin, origin); reslicePlane->SetOrigin(origin); // Extract the upper slice extractor = ExtractSliceFilter::New(); extractor->SetInput(m_Segmentation); extractor->SetTimeStep(timeStep); extractor->SetResliceTransformByGeometry(m_Segmentation->GetTimeGeometry()->GetGeometryForTimeStep(timeStep)); extractor->SetVtkOutputRequest(false); extractor->SetWorldGeometry(reslicePlane); extractor->Modified(); extractor->Update(); upperMITKSlice = extractor->GetOutput(); upperMITKSlice->DisconnectPipeline(); if (lowerMITKSlice.IsNull() || upperMITKSlice.IsNull()) return nullptr; } catch (const std::exception &e) { MITK_ERROR << "Error in 2D interpolation: " << e.what(); return nullptr; } // interpolation algorithm gets some inputs // two segmentations (guaranteed to be of the same data type, but no special data type guaranteed) // orientation (sliceDimension) of the segmentations // position of the two slices (sliceIndices) // one volume image (original patient image) // // interpolation algorithm can use e.g. itk::ImageSliceConstIteratorWithIndex to // inspect the original patient image at appropriate positions mitk::SegmentationInterpolationAlgorithm::Pointer algorithm = mitk::ShapeBasedInterpolationAlgorithm::New().GetPointer(); return algorithm->Interpolate(lowerMITKSlice.GetPointer(), lowerBound, upperMITKSlice.GetPointer(), upperBound, sliceIndex, sliceDimension, resultImage, timeStep, m_ReferenceImage); } diff --git a/Modules/Segmentation/DataManagement/mitkExtrudedContour.cpp b/Modules/Segmentation/DataManagement/mitkExtrudedContour.cpp index edbcc4dd1a..565f3a275a 100644 --- a/Modules/Segmentation/DataManagement/mitkExtrudedContour.cpp +++ b/Modules/Segmentation/DataManagement/mitkExtrudedContour.cpp @@ -1,387 +1,387 @@ /*=================================================================== 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 "mitkExtrudedContour.h" #include "mitkBaseProcess.h" #include "mitkNumericTypes.h" #include "mitkProportionalTimeGeometry.h" #include #include #include #include #include #include #include #include #include #include // vtkButterflySubdivisionFilter * subdivs; #include #include #include #include #include mitk::ExtrudedContour::ExtrudedContour() - : m_Contour(nullptr), m_ClippingGeometry(nullptr), m_AutomaticVectorGeneration(false) + : m_Contour(nullptr), m_ClippingGeometry(nullptr), m_AutomaticVectorGeneration(false), m_Decimate(nullptr) { ProportionalTimeGeometry::Pointer timeGeometry = ProportionalTimeGeometry::New(); timeGeometry->Initialize(1); SetTimeGeometry(timeGeometry); FillVector3D(m_Vector, 0.0, 0.0, 1.0); m_RightVector.Fill(0.0); m_ExtrusionFilter = vtkLinearExtrusionFilter::New(); m_ExtrusionFilter->CappingOff(); m_ExtrusionFilter->SetExtrusionTypeToVectorExtrusion(); double vtkvector[3] = {0, 0, 1}; // set extrusion vector m_ExtrusionFilter->SetVector(vtkvector); m_TriangleFilter = vtkTriangleFilter::New(); m_TriangleFilter->SetInputConnection(m_ExtrusionFilter->GetOutputPort()); m_SubdivisionFilter = vtkLinearSubdivisionFilter::New(); m_SubdivisionFilter->SetInputConnection(m_TriangleFilter->GetOutputPort()); m_SubdivisionFilter->SetNumberOfSubdivisions(4); m_ClippingBox = vtkPlanes::New(); m_ClipPolyDataFilter = vtkClipPolyData::New(); m_ClipPolyDataFilter->SetInputConnection(m_SubdivisionFilter->GetOutputPort()); m_ClipPolyDataFilter->SetClipFunction(m_ClippingBox); m_ClipPolyDataFilter->InsideOutOn(); m_Polygon = vtkPolygon::New(); m_ProjectionPlane = mitk::PlaneGeometry::New(); } mitk::ExtrudedContour::~ExtrudedContour() { m_ClipPolyDataFilter->Delete(); m_ClippingBox->Delete(); m_SubdivisionFilter->Delete(); m_TriangleFilter->Delete(); m_ExtrusionFilter->Delete(); m_Polygon->Delete(); } bool mitk::ExtrudedContour::IsInside(const Point3D &worldPoint) const { static double polygonNormal[3] = {0.0, 0.0, 1.0}; // project point onto plane float xt[3]; itk2vtk(worldPoint, xt); xt[0] = worldPoint[0] - m_Origin[0]; xt[1] = worldPoint[1] - m_Origin[1]; xt[2] = worldPoint[2] - m_Origin[2]; float dist = xt[0] * m_Normal[0] + xt[1] * m_Normal[1] + xt[2] * m_Normal[2]; xt[0] -= dist * m_Normal[0]; xt[1] -= dist * m_Normal[1]; xt[2] -= dist * m_Normal[2]; double x[3]; x[0] = xt[0] * m_Right[0] + xt[1] * m_Right[1] + xt[2] * m_Right[2]; x[1] = xt[0] * m_Down[0] + xt[1] * m_Down[1] + xt[2] * m_Down[2]; x[2] = 0; // determine whether it's in the selection loop and then evaluate point // in polygon only if absolutely necessary. if (x[0] >= this->m_ProjectedContourBounds[0] && x[0] <= this->m_ProjectedContourBounds[1] && x[1] >= this->m_ProjectedContourBounds[2] && x[1] <= this->m_ProjectedContourBounds[3] && this->m_Polygon->PointInPolygon(x, m_Polygon->Points->GetNumberOfPoints(), ((vtkDoubleArray *)this->m_Polygon->Points->GetData())->GetPointer(0), (double *)this->m_ProjectedContourBounds, polygonNormal) == 1) return true; else return false; } mitk::ScalarType mitk::ExtrudedContour::GetVolume() { return -1.0; } void mitk::ExtrudedContour::UpdateOutputInformation() { if (this->GetSource()) { this->GetSource()->UpdateOutputInformation(); } if (GetMTime() > m_LastCalculateExtrusionTime) { BuildGeometry(); BuildSurface(); } // if ( ( m_CalculateBoundingBox ) && ( m_PolyDataSeries.size() > 0 ) ) // CalculateBoundingBox(); } void mitk::ExtrudedContour::BuildSurface() { if (m_Contour.IsNull()) { SetVtkPolyData(nullptr); return; } // set extrusion contour vtkPolyData *polyData = vtkPolyData::New(); vtkCellArray *polys = vtkCellArray::New(); polys->InsertNextCell(m_Polygon->GetPointIds()); polyData->SetPoints(m_Polygon->GetPoints()); // float vtkpoint[3]; // unsigned int i, numPts = m_Polygon->GetNumberOfPoints(); // for(i=0; im_Polygon->Points->GetPoint(i); // pointids[i]=loopPoints->InsertNextPoint(vtkpoint); //} // polys->InsertNextCell( i, pointids ); // delete [] pointids; // polyData->SetPoints( loopPoints ); polyData->SetPolys(polys); polys->Delete(); m_ExtrusionFilter->SetInputData(polyData); polyData->Delete(); // set extrusion scale factor m_ExtrusionFilter->SetScaleFactor(GetGeometry()->GetExtentInMM(2)); SetVtkPolyData(m_SubdivisionFilter->GetOutput()); // if(m_ClippingGeometry.IsNull()) //{ // SetVtkPolyData(m_SubdivisionFilter->GetOutput()); //} // else //{ // m_ClipPolyDataFilter->SetInput(m_SubdivisionFilter->GetOutput()); // mitk::BoundingBox::BoundsArrayType bounds=m_ClippingGeometry->GetBounds(); // m_ClippingBox->SetBounds(bounds[0], bounds[1], bounds[2], bounds[3], bounds[4], bounds[5]); // m_ClippingBox->SetTransform(GetGeometry()->GetVtkTransform()); // m_ClipPolyDataFilter->SetClipFunction(m_ClippingBox); // m_ClipPolyDataFilter->SetValue(0); // SetVtkPolyData(m_ClipPolyDataFilter->GetOutput()); //} m_LastCalculateExtrusionTime.Modified(); } void mitk::ExtrudedContour::BuildGeometry() { if (m_Contour.IsNull()) return; // Initialize(1); Vector3D nullvector; nullvector.Fill(0.0); float xProj[3]; unsigned int i; unsigned int numPts = 20; // m_Contour->GetNumberOfPoints(); mitk::Contour::PathPointer path = m_Contour->GetContourPath(); mitk::Contour::PathType::InputType cstart = path->StartOfInput(); mitk::Contour::PathType::InputType cend = path->EndOfInput(); mitk::Contour::PathType::InputType cstep = (cend - cstart) / numPts; mitk::Contour::PathType::InputType ccur; // Part I: guarantee/calculate legal vectors m_Vector.Normalize(); itk2vtk(m_Vector, m_Normal); // check m_Vector if (mitk::Equal(m_Vector, nullvector) || m_AutomaticVectorGeneration) { if (m_AutomaticVectorGeneration == false) itkWarningMacro("Extrusion vector is 0 (" << m_Vector << "); trying to use normal of polygon"); vtkPoints *loopPoints = vtkPoints::New(); // mitk::Contour::PointsContainerIterator pointsIt = m_Contour->GetPoints()->Begin(); double vtkpoint[3]; unsigned int i = 0; for (i = 0, ccur = cstart; i < numPts; ++i, ccur += cstep) { itk2vtk(path->Evaluate(ccur), vtkpoint); loopPoints->InsertNextPoint(vtkpoint); } // Make sure points define a loop with a m_Normal vtkPolygon::ComputeNormal(loopPoints, m_Normal); loopPoints->Delete(); vtk2itk(m_Normal, m_Vector); if (mitk::Equal(m_Vector, nullvector)) { itkExceptionMacro("Cannot calculate normal of polygon"); } } // check m_RightVector if ((mitk::Equal(m_RightVector, nullvector)) || (mitk::Equal(m_RightVector * m_Vector, 0.0) == false)) { if (mitk::Equal(m_RightVector, nullvector)) { itkDebugMacro("Right vector is 0. Calculating."); } else { itkWarningMacro("Right vector (" << m_RightVector << ") not perpendicular to extrusion vector " << m_Vector << ": " << m_RightVector * m_Vector); } // calculate a legal m_RightVector if (mitk::Equal(m_Vector[1], 0.0f) == false) { FillVector3D(m_RightVector, 1.0f, -m_Vector[0] / m_Vector[1], 0.0f); m_RightVector.Normalize(); } else { FillVector3D(m_RightVector, 0.0f, 1.0f, 0.0f); } } // calculate down-vector VnlVector rightDV = m_RightVector.GetVnlVector(); rightDV.normalize(); vnl2vtk(rightDV, m_Right); VnlVector downDV = vnl_cross_3d(m_Vector.GetVnlVector(), rightDV); downDV.normalize(); vnl2vtk(downDV, m_Down); // Part II: calculate plane as base for extrusion, project the contour // on this plane and store as polygon for IsInside test and BoundingBox calculation // initialize m_ProjectionPlane, yet with origin at 0 m_ProjectionPlane->InitializeStandardPlane(rightDV, downDV); // create vtkPolygon from contour and simultaneously determine 2D bounds of // contour projected on m_ProjectionPlane // mitk::Contour::PointsContainerIterator pointsIt = m_Contour->GetPoints()->Begin(); m_Polygon->Points->Reset(); m_Polygon->Points->SetNumberOfPoints(numPts); m_Polygon->PointIds->Reset(); m_Polygon->PointIds->SetNumberOfIds(numPts); mitk::Point2D pt2d; mitk::Point3D pt3d; mitk::Point2D min, max; min.Fill(itk::NumericTraits::max()); max.Fill(itk::NumericTraits::min()); xProj[2] = 0.0; for (i = 0, ccur = cstart; i < numPts; ++i, ccur += cstep) { pt3d.CastFrom(path->Evaluate(ccur)); m_ProjectionPlane->Map(pt3d, pt2d); xProj[0] = pt2d[0]; if (pt2d[0] < min[0]) min[0] = pt2d[0]; if (pt2d[0] > max[0]) max[0] = pt2d[0]; xProj[1] = pt2d[1]; if (pt2d[1] < min[1]) min[1] = pt2d[1]; if (pt2d[1] > max[1]) max[1] = pt2d[1]; m_Polygon->Points->SetPoint(i, xProj); m_Polygon->PointIds->SetId(i, i); } // shift parametric origin to (0,0) for (i = 0; i < numPts; ++i) { double *pt = this->m_Polygon->Points->GetPoint(i); pt[0] -= min[0]; pt[1] -= min[1]; itkDebugMacro(<< i << ": (" << pt[0] << "," << pt[1] << "," << pt[2] << ")"); } this->m_Polygon->GetBounds(m_ProjectedContourBounds); // m_ProjectedContourBounds[4]=-1.0; m_ProjectedContourBounds[5]=1.0; // calculate origin (except translation along the normal) and bounds // of m_ProjectionPlane: // origin is composed of the minimum x-/y-coordinates of the polygon, // bounds from the extent of the polygon, both after projecting on the plane mitk::Point3D origin; m_ProjectionPlane->Map(min, origin); ScalarType bounds[6] = {0, max[0] - min[0], 0, max[1] - min[1], 0, 1}; m_ProjectionPlane->SetBounds(bounds); m_ProjectionPlane->SetOrigin(origin); // Part III: initialize geometry if (m_ClippingGeometry.IsNotNull()) { ScalarType min_dist = itk::NumericTraits::max(), max_dist = itk::NumericTraits::min(), dist; unsigned char i; for (i = 0; i < 8; ++i) { dist = m_ProjectionPlane->SignedDistance(m_ClippingGeometry->GetCornerPoint(i)); if (dist < min_dist) min_dist = dist; if (dist > max_dist) max_dist = dist; } // incorporate translation along the normal into origin origin = origin + m_Vector * min_dist; m_ProjectionPlane->SetOrigin(origin); bounds[5] = max_dist - min_dist; } else bounds[5] = 20; itk2vtk(origin, m_Origin); mitk::BaseGeometry::Pointer g3d = GetGeometry(0); assert(g3d.IsNotNull()); g3d->SetBounds(bounds); g3d->SetIndexToWorldTransform(m_ProjectionPlane->GetIndexToWorldTransform()); ProportionalTimeGeometry::Pointer timeGeometry = ProportionalTimeGeometry::New(); timeGeometry->Initialize(g3d, 1); SetTimeGeometry(timeGeometry); } unsigned long mitk::ExtrudedContour::GetMTime() const { unsigned long latestTime = Superclass::GetMTime(); if (m_Contour.IsNotNull()) { unsigned long localTime; localTime = m_Contour->GetMTime(); if (localTime > latestTime) latestTime = localTime; } return latestTime; } diff --git a/Modules/Segmentation/Interactions/mitkFastMarchingTool3D.cpp b/Modules/Segmentation/Interactions/mitkFastMarchingTool3D.cpp index ea76fab6e4..a01ad6399a 100644 --- a/Modules/Segmentation/Interactions/mitkFastMarchingTool3D.cpp +++ b/Modules/Segmentation/Interactions/mitkFastMarchingTool3D.cpp @@ -1,462 +1,464 @@ /*=================================================================== 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 "mitkFastMarchingTool3D.h" #include "mitkToolManager.h" #include "mitkBaseRenderer.h" #include "mitkInteractionConst.h" #include "mitkRenderingManager.h" #include "itkOrImageFilter.h" #include "mitkImageCast.h" #include "mitkImageTimeSelector.h" // us #include #include #include #include namespace mitk { MITK_TOOL_MACRO(MITKSEGMENTATION_EXPORT, FastMarchingTool3D, "FastMarching3D tool"); } mitk::FastMarchingTool3D::FastMarchingTool3D() : /*FeedbackContourTool*/ AutoSegmentationTool(), m_NeedUpdate(true), m_CurrentTimeStep(0), m_LowerThreshold(0), m_UpperThreshold(200), m_StoppingValue(100), m_Sigma(1.0), m_Alpha(-0.5), - m_Beta(3.0) + m_Beta(3.0), + m_PointSetAddObserverTag(0), + m_PointSetRemoveObserverTag(0) { } mitk::FastMarchingTool3D::~FastMarchingTool3D() { } bool mitk::FastMarchingTool3D::CanHandle(BaseData *referenceData) const { if (referenceData == nullptr) return false; auto *image = dynamic_cast(referenceData); if (image == nullptr) return false; if (image->GetDimension() < 3) return false; return true; } const char **mitk::FastMarchingTool3D::GetXPM() const { return nullptr; // mitkFastMarchingTool3D_xpm; } us::ModuleResource mitk::FastMarchingTool3D::GetIconResource() const { us::Module *module = us::GetModuleContext()->GetModule(); us::ModuleResource resource = module->GetResource("FastMarching_48x48.png"); return resource; } const char *mitk::FastMarchingTool3D::GetName() const { return "Fast Marching 3D"; } void mitk::FastMarchingTool3D::SetUpperThreshold(double value) { m_UpperThreshold = value / 10.0; m_ThresholdFilter->SetUpperThreshold(m_UpperThreshold); m_NeedUpdate = true; } void mitk::FastMarchingTool3D::SetLowerThreshold(double value) { m_LowerThreshold = value / 10.0; m_ThresholdFilter->SetLowerThreshold(m_LowerThreshold); m_NeedUpdate = true; } void mitk::FastMarchingTool3D::SetBeta(double value) { if (m_Beta != value) { m_Beta = value; m_SigmoidFilter->SetBeta(m_Beta); m_NeedUpdate = true; } } void mitk::FastMarchingTool3D::SetSigma(double value) { if (m_Sigma != value) { if (value > 0.0) { m_Sigma = value; m_GradientMagnitudeFilter->SetSigma(m_Sigma); m_NeedUpdate = true; } } } void mitk::FastMarchingTool3D::SetAlpha(double value) { if (m_Alpha != value) { m_Alpha = value; m_SigmoidFilter->SetAlpha(m_Alpha); m_NeedUpdate = true; } } void mitk::FastMarchingTool3D::SetStoppingValue(double value) { if (m_StoppingValue != value) { m_StoppingValue = value; m_FastMarchingFilter->SetStoppingValue(m_StoppingValue); m_NeedUpdate = true; } } void mitk::FastMarchingTool3D::Activated() { Superclass::Activated(); m_ResultImageNode = mitk::DataNode::New(); m_ResultImageNode->SetName("FastMarching_Preview"); m_ResultImageNode->SetBoolProperty("helper object", true); m_ResultImageNode->SetColor(0.0, 1.0, 0.0); m_ResultImageNode->SetVisibility(true); m_ToolManager->GetDataStorage()->Add(this->m_ResultImageNode, m_ToolManager->GetReferenceData(0)); m_SeedsAsPointSet = mitk::PointSet::New(); m_SeedsAsPointSetNode = mitk::DataNode::New(); m_SeedsAsPointSetNode->SetData(m_SeedsAsPointSet); m_SeedsAsPointSetNode->SetName("3D_FastMarching_PointSet"); m_SeedsAsPointSetNode->SetBoolProperty("helper object", true); m_SeedsAsPointSetNode->SetColor(0.0, 1.0, 0.0); m_SeedsAsPointSetNode->SetVisibility(true); // Create PointSetData Interactor m_SeedPointInteractor = mitk::PointSetDataInteractor::New(); // Load the according state machine for regular point set interaction m_SeedPointInteractor->LoadStateMachine("PointSet.xml"); // Set the configuration file that defines the triggers for the transitions m_SeedPointInteractor->SetEventConfig("PointSetConfig.xml"); // set the DataNode (which already is added to the DataStorage m_SeedPointInteractor->SetDataNode(m_SeedsAsPointSetNode); m_ReferenceImageAsITK = InternalImageType::New(); m_ProgressCommand = mitk::ToolCommand::New(); m_ThresholdFilter = ThresholdingFilterType::New(); m_ThresholdFilter->SetLowerThreshold(m_LowerThreshold); m_ThresholdFilter->SetUpperThreshold(m_UpperThreshold); m_ThresholdFilter->SetOutsideValue(0); m_ThresholdFilter->SetInsideValue(1.0); m_SmoothFilter = SmoothingFilterType::New(); m_SmoothFilter->AddObserver(itk::ProgressEvent(), m_ProgressCommand); m_SmoothFilter->SetTimeStep(0.05); m_SmoothFilter->SetNumberOfIterations(2); m_SmoothFilter->SetConductanceParameter(9.0); m_GradientMagnitudeFilter = GradientFilterType::New(); m_GradientMagnitudeFilter->AddObserver(itk::ProgressEvent(), m_ProgressCommand); m_GradientMagnitudeFilter->SetSigma(m_Sigma); m_SigmoidFilter = SigmoidFilterType::New(); m_SigmoidFilter->AddObserver(itk::ProgressEvent(), m_ProgressCommand); m_SigmoidFilter->SetAlpha(m_Alpha); m_SigmoidFilter->SetBeta(m_Beta); m_SigmoidFilter->SetOutputMinimum(0.0); m_SigmoidFilter->SetOutputMaximum(1.0); m_FastMarchingFilter = FastMarchingFilterType::New(); m_FastMarchingFilter->AddObserver(itk::ProgressEvent(), m_ProgressCommand); m_FastMarchingFilter->SetStoppingValue(m_StoppingValue); m_SeedContainer = NodeContainer::New(); m_SeedContainer->Initialize(); m_FastMarchingFilter->SetTrialPoints(m_SeedContainer); // set up pipeline m_SmoothFilter->SetInput(m_ReferenceImageAsITK); m_GradientMagnitudeFilter->SetInput(m_SmoothFilter->GetOutput()); m_SigmoidFilter->SetInput(m_GradientMagnitudeFilter->GetOutput()); m_FastMarchingFilter->SetInput(m_SigmoidFilter->GetOutput()); m_ThresholdFilter->SetInput(m_FastMarchingFilter->GetOutput()); m_ToolManager->GetDataStorage()->Add(m_SeedsAsPointSetNode, m_ToolManager->GetWorkingData(0)); itk::SimpleMemberCommand::Pointer pointAddedCommand = itk::SimpleMemberCommand::New(); pointAddedCommand->SetCallbackFunction(this, &mitk::FastMarchingTool3D::OnAddPoint); m_PointSetAddObserverTag = m_SeedsAsPointSet->AddObserver(mitk::PointSetAddEvent(), pointAddedCommand); itk::SimpleMemberCommand::Pointer pointRemovedCommand = itk::SimpleMemberCommand::New(); pointRemovedCommand->SetCallbackFunction(this, &mitk::FastMarchingTool3D::OnDelete); m_PointSetRemoveObserverTag = m_SeedsAsPointSet->AddObserver(mitk::PointSetRemoveEvent(), pointRemovedCommand); this->Initialize(); } void mitk::FastMarchingTool3D::Deactivated() { m_ToolManager->GetDataStorage()->Remove(this->m_ResultImageNode); m_ToolManager->GetDataStorage()->Remove(this->m_SeedsAsPointSetNode); this->ClearSeeds(); this->m_SmoothFilter->RemoveAllObservers(); this->m_SigmoidFilter->RemoveAllObservers(); this->m_GradientMagnitudeFilter->RemoveAllObservers(); this->m_FastMarchingFilter->RemoveAllObservers(); m_ResultImageNode = nullptr; mitk::RenderingManager::GetInstance()->RequestUpdateAll(); unsigned int numberOfPoints = m_SeedsAsPointSet->GetSize(); for (unsigned int i = 0; i < numberOfPoints; ++i) { mitk::Point3D point = m_SeedsAsPointSet->GetPoint(i); auto *doOp = new mitk::PointOperation(mitk::OpREMOVE, point, 0); m_SeedsAsPointSet->ExecuteOperation(doOp); } // Deactivate Interaction m_SeedPointInteractor->SetDataNode(nullptr); m_ToolManager->GetDataStorage()->Remove(m_SeedsAsPointSetNode); m_SeedsAsPointSetNode = nullptr; m_SeedsAsPointSet->RemoveObserver(m_PointSetAddObserverTag); m_SeedsAsPointSet->RemoveObserver(m_PointSetRemoveObserverTag); Superclass::Deactivated(); } void mitk::FastMarchingTool3D::Initialize() { m_ReferenceImage = dynamic_cast(m_ToolManager->GetReferenceData(0)->GetData()); if (m_ReferenceImage->GetTimeGeometry()->CountTimeSteps() > 1) { mitk::ImageTimeSelector::Pointer timeSelector = ImageTimeSelector::New(); timeSelector->SetInput(m_ReferenceImage); timeSelector->SetTimeNr(m_CurrentTimeStep); timeSelector->UpdateLargestPossibleRegion(); m_ReferenceImage = timeSelector->GetOutput(); } CastToItkImage(m_ReferenceImage, m_ReferenceImageAsITK); m_SmoothFilter->SetInput(m_ReferenceImageAsITK); m_NeedUpdate = true; } void mitk::FastMarchingTool3D::ConfirmSegmentation() { // combine preview image with current working segmentation if (dynamic_cast(m_ResultImageNode->GetData())) { // logical or combination of preview and segmentation slice OutputImageType::Pointer segmentationImageInITK = OutputImageType::New(); mitk::Image::Pointer workingImage = dynamic_cast(GetTargetSegmentationNode()->GetData()); if (workingImage->GetTimeGeometry()->CountTimeSteps() > 1) { mitk::ImageTimeSelector::Pointer timeSelector = mitk::ImageTimeSelector::New(); timeSelector->SetInput(workingImage); timeSelector->SetTimeNr(m_CurrentTimeStep); timeSelector->UpdateLargestPossibleRegion(); CastToItkImage(timeSelector->GetOutput(), segmentationImageInITK); } else { CastToItkImage(workingImage, segmentationImageInITK); } typedef itk::OrImageFilter OrImageFilterType; OrImageFilterType::Pointer orFilter = OrImageFilterType::New(); orFilter->SetInput(0, m_ThresholdFilter->GetOutput()); orFilter->SetInput(1, segmentationImageInITK); orFilter->Update(); // set image volume in current time step from itk image workingImage->SetVolume((void *)(m_ThresholdFilter->GetOutput()->GetPixelContainer()->GetBufferPointer()), m_CurrentTimeStep); this->m_ResultImageNode->SetVisibility(false); this->ClearSeeds(); workingImage->Modified(); } mitk::RenderingManager::GetInstance()->RequestUpdateAll(); m_ToolManager->ActivateTool(-1); } void mitk::FastMarchingTool3D::OnAddPoint() { // Add a new seed point for FastMarching algorithm mitk::Point3D clickInIndex; m_ReferenceImage->GetGeometry()->WorldToIndex(m_SeedsAsPointSet->GetPoint(m_SeedsAsPointSet->GetSize() - 1), clickInIndex); itk::Index<3> seedPosition; seedPosition[0] = clickInIndex[0]; seedPosition[1] = clickInIndex[1]; seedPosition[2] = clickInIndex[2]; NodeType node; const double seedValue = 0.0; node.SetValue(seedValue); node.SetIndex(seedPosition); this->m_SeedContainer->InsertElement(this->m_SeedContainer->Size(), node); m_FastMarchingFilter->Modified(); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); m_NeedUpdate = true; this->Update(); m_ReadyMessage.Send(); } void mitk::FastMarchingTool3D::OnDelete() { // delete last seed point if (!(this->m_SeedContainer->empty())) { // delete last element of seeds container this->m_SeedContainer->pop_back(); m_FastMarchingFilter->Modified(); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); m_NeedUpdate = true; this->Update(); } } void mitk::FastMarchingTool3D::Update() { const unsigned int progress_steps = 200; if (m_NeedUpdate) { m_ProgressCommand->AddStepsToDo(progress_steps); // remove interaction with poinset while updating m_SeedPointInteractor->SetDataNode(nullptr); CurrentlyBusy.Send(true); try { m_ThresholdFilter->Update(); } catch (itk::ExceptionObject &excep) { MITK_ERROR << "Exception caught: " << excep.GetDescription(); m_ProgressCommand->SetProgress(progress_steps); CurrentlyBusy.Send(false); std::string msg = excep.GetDescription(); ErrorMessage.Send(msg); return; } m_ProgressCommand->SetProgress(progress_steps); CurrentlyBusy.Send(false); // make output visible mitk::Image::Pointer result = mitk::Image::New(); CastToMitkImage(m_ThresholdFilter->GetOutput(), result); result->GetGeometry()->SetOrigin(m_ReferenceImage->GetGeometry()->GetOrigin()); result->GetGeometry()->SetIndexToWorldTransform(m_ReferenceImage->GetGeometry()->GetIndexToWorldTransform()); m_ResultImageNode->SetData(result); m_ResultImageNode->SetVisibility(true); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); // add interaction with poinset again m_SeedPointInteractor->SetDataNode(m_SeedsAsPointSetNode); } } void mitk::FastMarchingTool3D::ClearSeeds() { // clear seeds for FastMarching as well as the PointSet for visualization if (this->m_SeedContainer.IsNotNull()) this->m_SeedContainer->Initialize(); if (this->m_SeedsAsPointSet.IsNotNull()) { // remove observers from current pointset m_SeedsAsPointSet->RemoveObserver(m_PointSetAddObserverTag); m_SeedsAsPointSet->RemoveObserver(m_PointSetRemoveObserverTag); // renew pointset this->m_SeedsAsPointSet = mitk::PointSet::New(); this->m_SeedsAsPointSetNode->SetData(this->m_SeedsAsPointSet); m_SeedsAsPointSetNode->SetName("Seeds_Preview"); m_SeedsAsPointSetNode->SetBoolProperty("helper object", true); m_SeedsAsPointSetNode->SetColor(0.0, 1.0, 0.0); m_SeedsAsPointSetNode->SetVisibility(true); // add callback function for adding and removing points itk::SimpleMemberCommand::Pointer pointAddedCommand = itk::SimpleMemberCommand::New(); pointAddedCommand->SetCallbackFunction(this, &mitk::FastMarchingTool3D::OnAddPoint); m_PointSetAddObserverTag = m_SeedsAsPointSet->AddObserver(mitk::PointSetAddEvent(), pointAddedCommand); itk::SimpleMemberCommand::Pointer pointRemovedCommand = itk::SimpleMemberCommand::New(); pointRemovedCommand->SetCallbackFunction(this, &mitk::FastMarchingTool3D::OnDelete); m_PointSetRemoveObserverTag = m_SeedsAsPointSet->AddObserver(mitk::PointSetRemoveEvent(), pointRemovedCommand); } if (this->m_FastMarchingFilter.IsNotNull()) m_FastMarchingFilter->Modified(); this->m_NeedUpdate = true; } void mitk::FastMarchingTool3D::Reset() { // clear all seeds and preview empty result this->ClearSeeds(); m_ResultImageNode->SetVisibility(false); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } void mitk::FastMarchingTool3D::SetCurrentTimeStep(int t) { if (m_CurrentTimeStep != t) { m_CurrentTimeStep = t; this->Initialize(); } } diff --git a/Modules/Segmentation/Interactions/mitkLiveWireTool2D.cpp b/Modules/Segmentation/Interactions/mitkLiveWireTool2D.cpp index b5965a9746..2a65f692fb 100644 --- a/Modules/Segmentation/Interactions/mitkLiveWireTool2D.cpp +++ b/Modules/Segmentation/Interactions/mitkLiveWireTool2D.cpp @@ -1,669 +1,670 @@ /*=================================================================== 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 #include "mitkLiveWireTool2D.h" #include "mitkLiveWireTool2D.xpm" namespace mitk { MITK_TOOL_MACRO(MITKSEGMENTATION_EXPORT, LiveWireTool2D, "LiveWire tool"); } class RemoveFromDataStorage { public: RemoveFromDataStorage(mitk::DataStorage::Pointer dataStorage) : m_DataStorage(dataStorage) {} void operator()(mitk::DataNode *dataNode) { m_DataStorage->Remove(dataNode); } void operator()(const std::pair &dataNode) { m_DataStorage->Remove(dataNode.first); } private: mitk::DataStorage::Pointer m_DataStorage; }; -mitk::LiveWireTool2D::LiveWireTool2D() : SegTool2D("LiveWireTool"), m_PlaneGeometry(nullptr) +mitk::LiveWireTool2D::LiveWireTool2D() + : SegTool2D("LiveWireTool"), m_CreateAndUseDynamicCosts(false), m_PlaneGeometry(nullptr) { } mitk::LiveWireTool2D::~LiveWireTool2D() { this->ClearSegmentation(); } void mitk::LiveWireTool2D::RemoveHelperObjects() { DataStorage *dataStorage = m_ToolManager->GetDataStorage(); if (!m_EditingContours.empty()) std::for_each(m_EditingContours.begin(), m_EditingContours.end(), RemoveFromDataStorage(dataStorage)); if (!m_WorkingContours.empty()) std::for_each(m_WorkingContours.begin(), m_WorkingContours.end(), RemoveFromDataStorage(dataStorage)); if (m_EditingContourNode.IsNotNull()) dataStorage->Remove(m_EditingContourNode); if (m_LiveWireContourNode.IsNotNull()) dataStorage->Remove(m_LiveWireContourNode); if (m_ContourModelNode.IsNotNull()) dataStorage->Remove(m_ContourModelNode); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } void mitk::LiveWireTool2D::ReleaseHelperObjects() { this->RemoveHelperObjects(); if (!m_EditingContours.empty()) m_EditingContours.clear(); if (!m_WorkingContours.empty()) m_WorkingContours.clear(); m_EditingContourNode = nullptr; m_EditingContour = nullptr; m_LiveWireContourNode = nullptr; m_LiveWireContour = nullptr; m_ContourModelNode = nullptr; m_Contour = nullptr; } void mitk::LiveWireTool2D::ReleaseInteractors() { this->EnableContourLiveWireInteraction(false); m_LiveWireNodes.clear(); } void mitk::LiveWireTool2D::ConnectActionsAndFunctions() { CONNECT_CONDITION("CheckContourClosed", OnCheckPoint); CONNECT_FUNCTION("InitObject", OnInitLiveWire); CONNECT_FUNCTION("AddPoint", OnAddPoint); CONNECT_FUNCTION("CtrlAddPoint", OnAddPoint); CONNECT_FUNCTION("MovePoint", OnMouseMoveNoDynamicCosts); CONNECT_FUNCTION("FinishContour", OnFinish); CONNECT_FUNCTION("DeletePoint", OnLastSegmentDelete); CONNECT_FUNCTION("CtrlMovePoint", OnMouseMoved); } const char **mitk::LiveWireTool2D::GetXPM() const { return mitkLiveWireTool2D_xpm; } us::ModuleResource mitk::LiveWireTool2D::GetIconResource() const { return us::GetModuleContext()->GetModule()->GetResource("LiveWire_48x48.png"); } us::ModuleResource mitk::LiveWireTool2D::GetCursorIconResource() const { return us::GetModuleContext()->GetModule()->GetResource("LiveWire_Cursor_32x32.png"); } const char *mitk::LiveWireTool2D::GetName() const { return "Live Wire"; } void mitk::LiveWireTool2D::Activated() { Superclass::Activated(); this->ResetToStartState(); this->EnableContourLiveWireInteraction(true); } void mitk::LiveWireTool2D::Deactivated() { this->ConfirmSegmentation(); Superclass::Deactivated(); } void mitk::LiveWireTool2D::EnableContourLiveWireInteraction(bool on) { for (auto interactor : m_LiveWireNodes) { if (on) interactor->EnableInteraction(true); else interactor->EnableInteraction(false); } } void mitk::LiveWireTool2D::ConfirmSegmentation() { DataNode *workingNode(m_ToolManager->GetWorkingData(0)); if (!workingNode) return; auto *workingImage = dynamic_cast(workingNode->GetData()); if (!workingImage) return; // for all contours in list (currently created by tool) auto itWorkingContours = this->m_WorkingContours.begin(); std::vector sliceList; sliceList.reserve(m_WorkingContours.size()); while (itWorkingContours != this->m_WorkingContours.end()) { // if node contains data if (itWorkingContours->first->GetData()) { // if this is a contourModel auto *contourModel = dynamic_cast(itWorkingContours->first->GetData()); if (contourModel) { // for each timestep of this contourModel for (TimeStepType currentTimestep = 0; currentTimestep < contourModel->GetTimeGeometry()->CountTimeSteps(); ++currentTimestep) { // get the segmentation image slice at current timestep mitk::Image::Pointer workingSlice = this->GetAffectedImageSliceAs2DImage(itWorkingContours->second, workingImage, currentTimestep); mitk::ContourModel::Pointer projectedContour = mitk::ContourModelUtils::ProjectContourTo2DSlice(workingSlice, contourModel, true, false); mitk::ContourModelUtils::FillContourInSlice(projectedContour, workingSlice, workingImage, 1.0); // write back to image volume SliceInformation sliceInfo(workingSlice, itWorkingContours->second, currentTimestep); sliceList.push_back(sliceInfo); this->WriteSliceToVolume(sliceInfo); } } } ++itWorkingContours; } this->WriteBackSegmentationResult(sliceList, false); this->ClearSegmentation(); } void mitk::LiveWireTool2D::ClearSegmentation() { this->ReleaseHelperObjects(); this->ReleaseInteractors(); this->ResetToStartState(); } bool mitk::LiveWireTool2D::IsPositionEventInsideImageRegion(mitk::InteractionPositionEvent *positionEvent, mitk::BaseData *data) { bool IsPositionEventInsideImageRegion = data != nullptr && data->GetGeometry()->IsInside(positionEvent->GetPositionInWorld()); if (!IsPositionEventInsideImageRegion) { MITK_WARN("LiveWireTool2D") << "PositionEvent is outside ImageRegion!"; return false; } return true; } void mitk::LiveWireTool2D::OnInitLiveWire(StateMachineAction *, InteractionEvent *interactionEvent) { auto *positionEvent = dynamic_cast(interactionEvent); if (!positionEvent) return; mitk::DataNode *workingDataNode = m_ToolManager->GetWorkingData(0); if (!IsPositionEventInsideImageRegion(positionEvent, workingDataNode->GetData())) { this->ResetToStartState(); return; } m_LastEventSender = positionEvent->GetSender(); m_LastEventSlice = m_LastEventSender->GetSlice(); int timestep = positionEvent->GetSender()->GetTimeStep(); m_Contour = mitk::ContourModel::New(); m_Contour->Expand(timestep + 1); m_ContourModelNode = mitk::DataNode::New(); m_ContourModelNode->SetData(m_Contour); m_ContourModelNode->SetName("working contour node"); m_ContourModelNode->SetProperty("layer", IntProperty::New(100)); m_ContourModelNode->AddProperty("fixedLayer", BoolProperty::New(true)); m_ContourModelNode->SetProperty("helper object", mitk::BoolProperty::New(true)); m_ContourModelNode->AddProperty("contour.color", ColorProperty::New(1, 1, 0), nullptr, true); m_ContourModelNode->AddProperty("contour.points.color", ColorProperty::New(1.0, 0.0, 0.1), nullptr, true); m_ContourModelNode->AddProperty("contour.controlpoints.show", BoolProperty::New(true), nullptr, true); m_LiveWireContour = mitk::ContourModel::New(); m_LiveWireContour->Expand(timestep + 1); m_LiveWireContourNode = mitk::DataNode::New(); m_LiveWireContourNode->SetData(m_LiveWireContour); m_LiveWireContourNode->SetName("active livewire node"); m_LiveWireContourNode->SetProperty("layer", IntProperty::New(101)); m_LiveWireContourNode->AddProperty("fixedLayer", BoolProperty::New(true)); m_LiveWireContourNode->SetProperty("helper object", mitk::BoolProperty::New(true)); m_LiveWireContourNode->AddProperty("contour.color", ColorProperty::New(0.1, 1.0, 0.1), nullptr, true); m_LiveWireContourNode->AddProperty("contour.width", mitk::FloatProperty::New(4.0), nullptr, true); m_EditingContour = mitk::ContourModel::New(); m_EditingContour->Expand(timestep + 1); m_EditingContourNode = mitk::DataNode::New(); m_EditingContourNode->SetData(m_EditingContour); m_EditingContourNode->SetName("editing node"); m_EditingContourNode->SetProperty("layer", IntProperty::New(102)); m_EditingContourNode->AddProperty("fixedLayer", BoolProperty::New(true)); m_EditingContourNode->SetProperty("helper object", mitk::BoolProperty::New(true)); m_EditingContourNode->AddProperty("contour.color", ColorProperty::New(0.1, 1.0, 0.1), nullptr, true); m_EditingContourNode->AddProperty("contour.points.color", ColorProperty::New(0.0, 0.0, 1.0), nullptr, true); m_EditingContourNode->AddProperty("contour.width", mitk::FloatProperty::New(4.0), nullptr, true); m_ToolManager->GetDataStorage()->Add(m_ContourModelNode, workingDataNode); m_ToolManager->GetDataStorage()->Add(m_LiveWireContourNode, workingDataNode); m_ToolManager->GetDataStorage()->Add(m_EditingContourNode, workingDataNode); // set current slice as input for ImageToLiveWireContourFilter m_WorkingSlice = this->GetAffectedReferenceSlice(positionEvent); mitk::Point3D newOrigin = m_WorkingSlice->GetSlicedGeometry()->GetOrigin(); m_WorkingSlice->GetSlicedGeometry()->WorldToIndex(newOrigin, newOrigin); m_WorkingSlice->GetSlicedGeometry()->IndexToWorld(newOrigin, newOrigin); m_WorkingSlice->GetSlicedGeometry()->SetOrigin(newOrigin); m_LiveWireFilter = mitk::ImageLiveWireContourModelFilter::New(); m_LiveWireFilter->SetInput(m_WorkingSlice); // map click to pixel coordinates mitk::Point3D click = positionEvent->GetPositionInWorld(); itk::Index<3> idx; m_WorkingSlice->GetGeometry()->WorldToIndex(click, idx); // get the pixel the gradient in region of 5x5 itk::Index<3> indexWithHighestGradient; AccessFixedDimensionByItk_2(m_WorkingSlice, FindHighestGradientMagnitudeByITK, 2, idx, indexWithHighestGradient); // itk::Index to mitk::Point3D click[0] = indexWithHighestGradient[0]; click[1] = indexWithHighestGradient[1]; click[2] = indexWithHighestGradient[2]; m_WorkingSlice->GetGeometry()->IndexToWorld(click, click); // set initial start point m_Contour->AddVertex(click, true, timestep); m_LiveWireFilter->SetStartPoint(click); // remember plane geometry to determine if events were triggered in same plane m_PlaneGeometry = interactionEvent->GetSender()->GetCurrentWorldPlaneGeometry(); m_CreateAndUseDynamicCosts = true; // render assert(positionEvent->GetSender()->GetRenderWindow()); mitk::RenderingManager::GetInstance()->RequestUpdate(positionEvent->GetSender()->GetRenderWindow()); } void mitk::LiveWireTool2D::OnAddPoint(StateMachineAction *, InteractionEvent *interactionEvent) { // complete LiveWire interaction for last segment // add current LiveWire contour to the finished contour and reset // to start new segment and computation auto *positionEvent = dynamic_cast(interactionEvent); if (!positionEvent) return; if (m_PlaneGeometry != nullptr) { // this checks that the point is in the correct slice if (m_PlaneGeometry->DistanceFromPlane(positionEvent->GetPositionInWorld()) > mitk::sqrteps) { return; } } int timestep = positionEvent->GetSender()->GetTimeStep(); // add repulsive points to avoid to get the same path again typedef mitk::ImageLiveWireContourModelFilter::InternalImageType::IndexType IndexType; mitk::ContourModel::ConstVertexIterator iter = m_LiveWireContour->IteratorBegin(timestep); for (; iter != m_LiveWireContour->IteratorEnd(timestep); iter++) { IndexType idx; this->m_WorkingSlice->GetGeometry()->WorldToIndex((*iter)->Coordinates, idx); this->m_LiveWireFilter->AddRepulsivePoint(idx); } // remove duplicate first vertex, it's already contained in m_Contour m_LiveWireContour->RemoveVertexAt(0, timestep); // set last added point as control point m_LiveWireContour->SetControlVertexAt(m_LiveWireContour->GetNumberOfVertices(timestep) - 1, timestep); // merge contours m_Contour->Concatenate(m_LiveWireContour, timestep); // clear the livewire contour and reset the corresponding datanode m_LiveWireContour->Clear(timestep); // set new start point m_LiveWireFilter->SetStartPoint(positionEvent->GetPositionInWorld()); if (m_CreateAndUseDynamicCosts) { // use dynamic cost map for next update m_LiveWireFilter->CreateDynamicCostMap(m_Contour); m_LiveWireFilter->SetUseDynamicCostMap(true); // m_CreateAndUseDynamicCosts = false; } // render assert(positionEvent->GetSender()->GetRenderWindow()); mitk::RenderingManager::GetInstance()->RequestUpdate(positionEvent->GetSender()->GetRenderWindow()); } void mitk::LiveWireTool2D::OnMouseMoved(StateMachineAction *, InteractionEvent *interactionEvent) { // compute LiveWire segment from last control point to current mouse position auto *positionEvent = dynamic_cast(interactionEvent); if (!positionEvent) return; // actual LiveWire computation int timestep = positionEvent->GetSender()->GetTimeStep(); m_LiveWireFilter->SetEndPoint(positionEvent->GetPositionInWorld()); m_LiveWireFilter->SetTimeStep(timestep); m_LiveWireFilter->Update(); m_LiveWireContour = this->m_LiveWireFilter->GetOutput(); m_LiveWireContourNode->SetData(this->m_LiveWireContour); // render assert(positionEvent->GetSender()->GetRenderWindow()); positionEvent->GetSender()->GetRenderingManager()->RequestUpdate(positionEvent->GetSender()->GetRenderWindow()); } void mitk::LiveWireTool2D::OnMouseMoveNoDynamicCosts(StateMachineAction *, InteractionEvent *interactionEvent) { // do not use dynamic cost map m_LiveWireFilter->SetUseDynamicCostMap(false); OnMouseMoved(nullptr, interactionEvent); m_LiveWireFilter->SetUseDynamicCostMap(true); } bool mitk::LiveWireTool2D::OnCheckPoint(const InteractionEvent *interactionEvent) { // check double click on first control point to finish the LiveWire tool // // Check distance to first point. // Transition YES if click close to first control point // const auto *positionEvent = dynamic_cast(interactionEvent); if (positionEvent) { int timestep = positionEvent->GetSender()->GetTimeStep(); mitk::Point3D click = positionEvent->GetPositionInWorld(); mitk::Point3D first = this->m_Contour->GetVertexAt(0, timestep)->Coordinates; if (first.EuclideanDistanceTo(click) < 4.5) { // allow to finish return true; } else { return false; } } return false; } void mitk::LiveWireTool2D::OnFinish(StateMachineAction *, InteractionEvent *interactionEvent) { // finish livewire tool interaction auto *positionEvent = dynamic_cast(interactionEvent); if (!positionEvent) return; // Have to do that here so that the m_LastEventSender is set correctly mitk::SegTool2D::AddContourmarker(); // actual timestep int timestep = positionEvent->GetSender()->GetTimeStep(); // remove last control point being added by double click m_Contour->RemoveVertexAt(m_Contour->GetNumberOfVertices(timestep) - 1, timestep); // save contour and corresponding plane geometry to list std::pair cp( m_ContourModelNode, (positionEvent->GetSender()->GetCurrentWorldPlaneGeometry()->Clone().GetPointer())); this->m_WorkingContours.push_back(cp); std::pair ecp( m_EditingContourNode, (positionEvent->GetSender()->GetCurrentWorldPlaneGeometry()->Clone().GetPointer())); this->m_EditingContours.push_back(ecp); m_LiveWireFilter->SetUseDynamicCostMap(false); this->FinishTool(); } void mitk::LiveWireTool2D::FinishTool() { auto numberOfTimesteps = static_cast(m_Contour->GetTimeGeometry()->CountTimeSteps()); // close contour in each timestep for (int i = 0; i <= numberOfTimesteps; i++) m_Contour->Close(i); m_ToolManager->GetDataStorage()->Remove(m_LiveWireContourNode); // clear live wire contour node m_LiveWireContourNode = nullptr; m_LiveWireContour = nullptr; // A new ContourModelLiveWireInteractor is created that will listen to new events // set the livewire interactor to edit control points m_ContourInteractor = mitk::ContourModelLiveWireInteractor::New(); m_ContourInteractor->SetDataNode(m_ContourModelNode); // TODO load statemachine and config m_ContourInteractor->LoadStateMachine("ContourModelModificationInteractor.xml", us::GetModuleContext()->GetModule()); // Set the configuration file that defines the triggers for the transitions m_ContourInteractor->SetEventConfig("ContourModelModificationConfig.xml", us::GetModuleContext()->GetModule()); m_ContourInteractor->SetWorkingImage(this->m_WorkingSlice); m_ContourInteractor->SetEditingContourModelNode(this->m_EditingContourNode); m_ContourModelNode->SetDataInteractor(m_ContourInteractor.GetPointer()); this->m_LiveWireNodes.push_back(m_ContourInteractor); } void mitk::LiveWireTool2D::OnLastSegmentDelete(StateMachineAction *, InteractionEvent *interactionEvent) { int timestep = interactionEvent->GetSender()->GetTimeStep(); // if last point of current contour will be removed go to start state and remove nodes if (m_Contour->GetNumberOfVertices(timestep) <= 1) { m_ToolManager->GetDataStorage()->Remove(m_LiveWireContourNode); m_ToolManager->GetDataStorage()->Remove(m_ContourModelNode); m_ToolManager->GetDataStorage()->Remove(m_EditingContourNode); m_LiveWireContour = mitk::ContourModel::New(); m_Contour = mitk::ContourModel::New(); m_ContourModelNode->SetData(m_Contour); m_LiveWireContourNode->SetData(m_LiveWireContour); this->ResetToStartState(); // go to start state } else // remove last segment from contour and reset livewire contour { m_LiveWireContour = mitk::ContourModel::New(); m_LiveWireContourNode->SetData(m_LiveWireContour); mitk::ContourModel::Pointer newContour = mitk::ContourModel::New(); newContour->Expand(m_Contour->GetTimeSteps()); auto begin = m_Contour->IteratorBegin(); // iterate from last point to next active point auto newLast = m_Contour->IteratorBegin() + (m_Contour->GetNumberOfVertices() - 1); // go at least one down if (newLast != begin) { newLast--; } // search next active control point while (newLast != begin && !((*newLast)->IsControlPoint)) { newLast--; } // set position of start point for livewire filter to coordinates of the new last point m_LiveWireFilter->SetStartPoint((*newLast)->Coordinates); auto it = m_Contour->IteratorBegin(); // fill new Contour while (it <= newLast) { newContour->AddVertex((*it)->Coordinates, (*it)->IsControlPoint, timestep); it++; } newContour->SetClosed(m_Contour->IsClosed()); // set new contour visible m_ContourModelNode->SetData(newContour); m_Contour = newContour; assert(interactionEvent->GetSender()->GetRenderWindow()); mitk::RenderingManager::GetInstance()->RequestUpdate(interactionEvent->GetSender()->GetRenderWindow()); } } template void mitk::LiveWireTool2D::FindHighestGradientMagnitudeByITK(itk::Image *inputImage, itk::Index<3> &index, itk::Index<3> &returnIndex) { typedef itk::Image InputImageType; typedef typename InputImageType::IndexType IndexType; unsigned long xMAX = inputImage->GetLargestPossibleRegion().GetSize()[0]; unsigned long yMAX = inputImage->GetLargestPossibleRegion().GetSize()[1]; returnIndex[0] = index[0]; returnIndex[1] = index[1]; returnIndex[2] = 0.0; double gradientMagnitude = 0.0; double maxGradientMagnitude = 0.0; /* the size and thus the region of 7x7 is only used to calculate the gradient magnitude in that region not for searching the maximum value */ // maximum value in each direction for size typename InputImageType::SizeType size; size[0] = 7; size[1] = 7; // minimum value in each direction for startRegion IndexType startRegion; startRegion[0] = index[0] - 3; startRegion[1] = index[1] - 3; if (startRegion[0] < 0) startRegion[0] = 0; if (startRegion[1] < 0) startRegion[1] = 0; if (xMAX - index[0] < 7) startRegion[0] = xMAX - 7; if (yMAX - index[1] < 7) startRegion[1] = yMAX - 7; index[0] = startRegion[0] + 3; index[1] = startRegion[1] + 3; typename InputImageType::RegionType region; region.SetSize(size); region.SetIndex(startRegion); typedef typename itk::GradientMagnitudeImageFilter GradientMagnitudeFilterType; typename GradientMagnitudeFilterType::Pointer gradientFilter = GradientMagnitudeFilterType::New(); gradientFilter->SetInput(inputImage); gradientFilter->GetOutput()->SetRequestedRegion(region); gradientFilter->Update(); typename InputImageType::Pointer gradientMagnImage; gradientMagnImage = gradientFilter->GetOutput(); IndexType currentIndex; currentIndex[0] = 0; currentIndex[1] = 0; // search max (approximate) gradient magnitude for (int x = -1; x <= 1; ++x) { currentIndex[0] = index[0] + x; for (int y = -1; y <= 1; ++y) { currentIndex[1] = index[1] + y; gradientMagnitude = gradientMagnImage->GetPixel(currentIndex); // check for new max if (maxGradientMagnitude < gradientMagnitude) { maxGradientMagnitude = gradientMagnitude; returnIndex[0] = currentIndex[0]; returnIndex[1] = currentIndex[1]; returnIndex[2] = 0.0; } // end if } // end for y currentIndex[1] = index[1]; } // end for x } diff --git a/Modules/Segmentation/Interactions/mitkRegionGrowingTool.cpp b/Modules/Segmentation/Interactions/mitkRegionGrowingTool.cpp index 82174afa8b..e768f656e8 100644 --- a/Modules/Segmentation/Interactions/mitkRegionGrowingTool.cpp +++ b/Modules/Segmentation/Interactions/mitkRegionGrowingTool.cpp @@ -1,654 +1,655 @@ /*=================================================================== 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 "mitkRegionGrowingTool.h" #include "mitkApplicationCursor.h" #include "mitkBaseRenderer.h" #include "mitkImageDataItem.h" #include "mitkImageToContourModelFilter.h" #include "mitkOverwriteSliceImageFilter.h" #include "mitkRegionGrowingTool.xpm" #include "mitkRenderingManager.h" #include "mitkToolManager.h" #include "mitkExtractDirectedPlaneImageFilterNew.h" #include "mitkLabelSetImage.h" #include "mitkOverwriteDirectedPlaneImageFilter.h" // us #include #include #include #include // ITK #include "mitkITKImageImport.h" #include "mitkImageAccessByItk.h" #include #include #include #include #include namespace mitk { MITK_TOOL_MACRO(MITKSEGMENTATION_EXPORT, RegionGrowingTool, "Region growing tool"); } #define ROUND(a) ((a) > 0 ? (int)((a) + 0.5) : -(int)(0.5 - (a))) mitk::RegionGrowingTool::RegionGrowingTool() : FeedbackContourTool("PressMoveRelease"), m_SeedValue(0), m_ScreenYDifference(0), m_ScreenXDifference(0), m_MouseDistanceScaleFactor(0.5), + m_PaintingPixelValue(0), m_FillFeedbackContour(true), m_ConnectedComponentValue(1) { } mitk::RegionGrowingTool::~RegionGrowingTool() { } void mitk::RegionGrowingTool::ConnectActionsAndFunctions() { CONNECT_FUNCTION("PrimaryButtonPressed", OnMousePressed); CONNECT_FUNCTION("Move", OnMouseMoved); CONNECT_FUNCTION("Release", OnMouseReleased); } const char **mitk::RegionGrowingTool::GetXPM() const { return mitkRegionGrowingTool_xpm; } us::ModuleResource mitk::RegionGrowingTool::GetIconResource() const { us::Module *module = us::GetModuleContext()->GetModule(); us::ModuleResource resource = module->GetResource("RegionGrowing_48x48.png"); return resource; } us::ModuleResource mitk::RegionGrowingTool::GetCursorIconResource() const { us::Module *module = us::GetModuleContext()->GetModule(); us::ModuleResource resource = module->GetResource("RegionGrowing_Cursor_32x32.png"); return resource; } const char *mitk::RegionGrowingTool::GetName() const { return "Region Growing"; } void mitk::RegionGrowingTool::Activated() { Superclass::Activated(); } void mitk::RegionGrowingTool::Deactivated() { Superclass::Deactivated(); } // Get the average pixel value of square/cube with radius=neighborhood around index template void mitk::RegionGrowingTool::GetNeighborhoodAverage(itk::Image *itkImage, itk::Index index, ScalarType *result, unsigned int neighborhood) { // maybe assert that image dimension is only 2 or 3? auto neighborhoodInt = (int)neighborhood; TPixel averageValue(0); unsigned int numberOfPixels = (2 * neighborhood + 1) * (2 * neighborhood + 1); if (imageDimension == 3) { numberOfPixels *= (2 * neighborhood + 1); } MITK_DEBUG << "Getting neighborhood of " << numberOfPixels << " pixels around " << index; itk::Index currentIndex; for (int i = (0 - neighborhoodInt); i <= neighborhoodInt; ++i) { currentIndex[0] = index[0] + i; for (int j = (0 - neighborhoodInt); j <= neighborhoodInt; ++j) { currentIndex[1] = index[1] + j; if (imageDimension == 3) { for (int k = (0 - neighborhoodInt); k <= neighborhoodInt; ++k) { currentIndex[2] = index[2] + k; if (itkImage->GetLargestPossibleRegion().IsInside(currentIndex)) { averageValue += itkImage->GetPixel(currentIndex); } else { numberOfPixels -= 1; } } } else { if (itkImage->GetLargestPossibleRegion().IsInside(currentIndex)) { averageValue += itkImage->GetPixel(currentIndex); } else { numberOfPixels -= 1; } } } } *result = (ScalarType)averageValue; *result /= numberOfPixels; } // Check whether index lies inside a segmentation template void mitk::RegionGrowingTool::IsInsideSegmentation(itk::Image *itkImage, itk::Index index, bool *result) { if (itkImage->GetPixel(index) > 0) { *result = true; } else { *result = false; } } // Do the region growing (i.e. call an ITK filter that does it) template void mitk::RegionGrowingTool::StartRegionGrowing(itk::Image *inputImage, itk::Index seedIndex, std::array thresholds, mitk::Image::Pointer &outputImage) { MITK_DEBUG << "Starting region growing at index " << seedIndex << " with lower threshold " << thresholds[0] << " and upper threshold " << thresholds[1]; typedef itk::Image InputImageType; typedef itk::Image OutputImageType; typedef itk::ConnectedThresholdImageFilter RegionGrowingFilterType; typename RegionGrowingFilterType::Pointer regionGrower = RegionGrowingFilterType::New(); // perform region growing in desired segmented region regionGrower->SetInput(inputImage); regionGrower->AddSeed(seedIndex); regionGrower->SetLower(thresholds[0]); regionGrower->SetUpper(thresholds[1]); try { regionGrower->Update(); } catch (...) { return; // Should we do something? } typename OutputImageType::Pointer resultImage = regionGrower->GetOutput(); // Smooth result: Every pixel is replaced by the majority of the neighborhood typedef itk::NeighborhoodIterator NeighborhoodIteratorType; typedef itk::ImageRegionIterator ImageIteratorType; typename NeighborhoodIteratorType::RadiusType radius; radius.Fill(2); // for now, maybe make this something the user can adjust in the preferences? typedef itk::ImageDuplicator< OutputImageType > DuplicatorType; typename DuplicatorType::Pointer duplicator = DuplicatorType::New(); duplicator->SetInputImage(resultImage); duplicator->Update(); typename OutputImageType::Pointer resultDup = duplicator->GetOutput(); NeighborhoodIteratorType neighborhoodIterator(radius, resultDup, resultDup->GetRequestedRegion()); ImageIteratorType imageIterator(resultImage, resultImage->GetRequestedRegion()); for (neighborhoodIterator.GoToBegin(), imageIterator.GoToBegin(); !neighborhoodIterator.IsAtEnd(); ++neighborhoodIterator, ++imageIterator) { DefaultSegmentationDataType voteYes(0); DefaultSegmentationDataType voteNo(0); for (unsigned int i = 0; i < neighborhoodIterator.Size(); ++i) { if (neighborhoodIterator.GetPixel(i) > 0) { voteYes += 1; } else { voteNo += 1; } } if (voteYes > voteNo) { imageIterator.Set(1); } else { imageIterator.Set(0); } } if (resultImage.IsNull()) { MITK_DEBUG << "Region growing result is empty."; } // Can potentially have multiple regions, use connected component image filter to label disjunct regions typedef itk::ConnectedComponentImageFilter ConnectedComponentImageFilterType; typename ConnectedComponentImageFilterType::Pointer connectedComponentFilter = ConnectedComponentImageFilterType::New(); connectedComponentFilter->SetInput(resultImage); connectedComponentFilter->Update(); typename OutputImageType::Pointer resultImageCC = connectedComponentFilter->GetOutput(); m_ConnectedComponentValue = resultImageCC->GetPixel(seedIndex); outputImage = mitk::GrabItkImageMemory(resultImageCC); } void mitk::RegionGrowingTool::OnMousePressed(StateMachineAction *, InteractionEvent *interactionEvent) { auto *positionEvent = dynamic_cast(interactionEvent); if (!positionEvent) return; MITK_DEBUG << "OnMousePressed"; m_LastEventSender = positionEvent->GetSender(); m_LastEventSlice = m_LastEventSender->GetSlice(); m_LastScreenPosition = positionEvent->GetPointerPositionOnScreen(); // ReferenceSlice is from the underlying image, WorkingSlice from the active segmentation (can be empty) m_ReferenceSlice = FeedbackContourTool::GetAffectedReferenceSlice(positionEvent); m_WorkingSlice = FeedbackContourTool::GetAffectedWorkingSlice(positionEvent); if (m_WorkingSlice.IsNotNull()) // can't do anything without a working slice (i.e. a possibly empty segmentation) { MITK_DEBUG << "OnMousePressed: got working slice"; // 2. Determine if the user clicked inside or outside of the segmentation/working slice (i.e. the whole volume) mitk::BaseGeometry::Pointer workingSliceGeometry; workingSliceGeometry = m_WorkingSlice->GetGeometry(); workingSliceGeometry->WorldToIndex(positionEvent->GetPositionInWorld(), m_SeedPoint); itk::Index<2> indexInWorkingSlice2D; indexInWorkingSlice2D[0] = m_SeedPoint[0]; indexInWorkingSlice2D[1] = m_SeedPoint[1]; if (workingSliceGeometry->IsIndexInside(m_SeedPoint)) { MITK_DEBUG << "OnMousePressed: point " << positionEvent->GetPositionInWorld() << " (index coordinates " << m_SeedPoint << ") is inside working slice"; // 3. determine the pixel value under the last click to determine what to do bool inside(true); AccessFixedDimensionByItk_2(m_WorkingSlice, IsInsideSegmentation, 2, indexInWorkingSlice2D, &inside); m_PaintingPixelValue = inside ? 0 : 1; if (inside) { MITK_DEBUG << "Clicked inside segmentation"; // For now, we're doing nothing when the user clicks inside the segmentation. Behaviour can be implemented via // OnMousePressedInside() // When you do, be sure to remove the m_PaintingPixelValue check in OnMouseMoved() and OnMouseReleased() return; } else { MITK_DEBUG << "Clicked outside of segmentation"; OnMousePressedOutside(nullptr, interactionEvent); } } } } // Use this to implement a behaviour for when the user clicks inside a segmentation (for example remove something) // Old IpPic code is kept as comment for reference void mitk::RegionGrowingTool::OnMousePressedInside() { // mitk::InteractionPositionEvent* positionEvent = dynamic_cast( interactionEvent // ); // //const PositionEvent* positionEvent = dynamic_cast(stateEvent->GetEvent()); // checked in // OnMousePressed // // 3.1.1. Create a skeletonization of the segmentation and try to find a nice cut // // apply the skeletonization-and-cut algorithm // // generate contour to remove // // set m_ReferenceSlice = nullptr so nothing will happen during mouse move // // remember to fill the contour with 0 in mouserelease // mitkIpPicDescriptor* segmentationHistory = ipMITKSegmentationCreateGrowerHistory( workingPicSlice, // m_LastWorkingSeed, nullptr ); // free again // if (segmentationHistory) // { // tCutResult cutContour = ipMITKSegmentationGetCutPoints( workingPicSlice, segmentationHistory, // initialWorkingOffset ); // tCutResult is a ipSegmentation type // mitkIpPicFree( segmentationHistory ); // if (cutContour.cutIt) // { // int timestep = positionEvent->GetSender()->GetTimeStep(); // // 3.1.2 copy point from float* to mitk::Contour // ContourModel::Pointer contourInImageIndexCoordinates = ContourModel::New(); // contourInImageIndexCoordinates->Expand(timestep + 1); // contourInImageIndexCoordinates->SetClosed(true, timestep); // Point3D newPoint; // for (int index = 0; index < cutContour.deleteSize; ++index) // { // newPoint[0] = cutContour.deleteCurve[ 2 * index + 0 ] - 0.5;//correction is needed because the // output of the algorithm is center based // newPoint[1] = cutContour.deleteCurve[ 2 * index + 1 ] - 0.5;//and we want our contour displayed // corner based. // newPoint[2] = 0.0; // contourInImageIndexCoordinates->AddVertex( newPoint, timestep ); // } // free(cutContour.traceline); // free(cutContour.deleteCurve); // perhaps visualize this for fun? // free(cutContour.onGradient); // ContourModel::Pointer contourInWorldCoordinates = FeedbackContourTool::BackProjectContourFrom2DSlice( // m_WorkingSlice->GetGeometry(), contourInImageIndexCoordinates, true ); // true: sub 0.5 for // ipSegmentation correction // FeedbackContourTool::SetFeedbackContour( contourInWorldCoordinates ); // FeedbackContourTool::SetFeedbackContourVisible(true); // mitk::RenderingManager::GetInstance()->RequestUpdate( positionEvent->GetSender()->GetRenderWindow() ); // m_FillFeedbackContour = true; // } // else // { // m_FillFeedbackContour = false; // } // } // else // { // m_FillFeedbackContour = false; // } // m_ReferenceSlice = nullptr; // return true; } void mitk::RegionGrowingTool::OnMousePressedOutside(StateMachineAction *, InteractionEvent *interactionEvent) { auto *positionEvent = dynamic_cast(interactionEvent); if (positionEvent) { // Get geometry and indices mitk::BaseGeometry::Pointer workingSliceGeometry; workingSliceGeometry = m_WorkingSlice->GetGeometry(); itk::Index<2> indexInWorkingSlice2D; indexInWorkingSlice2D[0] = m_SeedPoint[0]; indexInWorkingSlice2D[1] = m_SeedPoint[1]; mitk::BaseGeometry::Pointer referenceSliceGeometry; referenceSliceGeometry = m_ReferenceSlice->GetGeometry(); itk::Index<3> indexInReferenceSlice; itk::Index<2> indexInReferenceSlice2D; referenceSliceGeometry->WorldToIndex(positionEvent->GetPositionInWorld(), indexInReferenceSlice); indexInReferenceSlice2D[0] = indexInReferenceSlice[0]; indexInReferenceSlice2D[1] = indexInReferenceSlice[1]; // Get seed neighborhood ScalarType averageValue(0); AccessFixedDimensionByItk_3(m_ReferenceSlice, GetNeighborhoodAverage, 2, indexInReferenceSlice2D, &averageValue, 1); m_SeedValue = averageValue; MITK_DEBUG << "Seed value is " << m_SeedValue; // Get level window settings LevelWindow lw(0, 500); // default window 0 to 500, can we do something smarter here? m_ToolManager->GetReferenceData(0)->GetLevelWindow( lw); // will fill lw if levelwindow property is present, otherwise won't touch it. ScalarType currentVisibleWindow = lw.GetWindow(); MITK_DEBUG << "Level window width is " << currentVisibleWindow; m_InitialThresholds[0] = m_SeedValue - currentVisibleWindow / 20.0; // 20 is arbitrary (though works reasonably // well), is there a better alternative (maybe // option in preferences)? m_InitialThresholds[1] = m_SeedValue + currentVisibleWindow / 20.0; m_Thresholds[0] = m_InitialThresholds[0]; m_Thresholds[1] = m_InitialThresholds[1]; // Perform region growing mitk::Image::Pointer resultImage = mitk::Image::New(); AccessFixedDimensionByItk_3( m_ReferenceSlice, StartRegionGrowing, 2, indexInWorkingSlice2D, m_Thresholds, resultImage); resultImage->SetGeometry(workingSliceGeometry); // Extract contour if (resultImage.IsNotNull() && m_ConnectedComponentValue >= 1) { float isoOffset = 0.33; mitk::ImageToContourModelFilter::Pointer contourExtractor = mitk::ImageToContourModelFilter::New(); contourExtractor->SetInput(resultImage); contourExtractor->SetContourValue(m_ConnectedComponentValue - isoOffset); contourExtractor->Update(); ContourModel::Pointer resultContour = ContourModel::New(); resultContour = contourExtractor->GetOutput(); // Show contour if (resultContour.IsNotNull()) { ContourModel::Pointer resultContourWorld = FeedbackContourTool::BackProjectContourFrom2DSlice( workingSliceGeometry, FeedbackContourTool::ProjectContourTo2DSlice(m_WorkingSlice, resultContour)); // this is not a beautiful solution, just one that works, check T22412 for details int timestep = positionEvent->GetSender()->GetTimeStep(); if (0 != timestep) { int size = resultContourWorld->GetNumberOfVertices(0); auto resultContourTimeWorld = mitk::ContourModel::New(); resultContourTimeWorld->Expand(timestep + 1); for (int loop = 0; loop < size; ++loop) { resultContourTimeWorld->AddVertex(resultContourWorld->GetVertexAt(loop, 0), timestep); } FeedbackContourTool::SetFeedbackContour(resultContourTimeWorld); } else { FeedbackContourTool::SetFeedbackContour(resultContourWorld); } FeedbackContourTool::SetFeedbackContourVisible(true); mitk::RenderingManager::GetInstance()->RequestUpdate(m_LastEventSender->GetRenderWindow()); } } } } void mitk::RegionGrowingTool::OnMouseMoved(StateMachineAction *, InteractionEvent *interactionEvent) { // Until OnMousePressedInside() implements a behaviour, we're just returning here whenever m_PaintingPixelValue is 0, // i.e. when the user clicked inside the segmentation if (m_PaintingPixelValue == 0) { return; } auto *positionEvent = dynamic_cast(interactionEvent); if (m_ReferenceSlice.IsNotNull() && positionEvent) { // Get geometry and indices mitk::BaseGeometry::Pointer workingSliceGeometry; workingSliceGeometry = m_WorkingSlice->GetGeometry(); itk::Index<2> indexInWorkingSlice2D; indexInWorkingSlice2D[0] = m_SeedPoint[0]; indexInWorkingSlice2D[1] = m_SeedPoint[1]; m_ScreenYDifference += positionEvent->GetPointerPositionOnScreen()[1] - m_LastScreenPosition[1]; m_ScreenXDifference += positionEvent->GetPointerPositionOnScreen()[0] - m_LastScreenPosition[0]; m_LastScreenPosition = positionEvent->GetPointerPositionOnScreen(); // Moving the mouse up and down adjusts the width of the threshold window, moving it left and right shifts the // threshold window m_Thresholds[0] = std::min( m_SeedValue, m_InitialThresholds[0] - (m_ScreenYDifference - m_ScreenXDifference) * m_MouseDistanceScaleFactor); m_Thresholds[1] = std::max( m_SeedValue, m_InitialThresholds[1] + (m_ScreenYDifference + m_ScreenXDifference) * m_MouseDistanceScaleFactor); MITK_DEBUG << "Screen difference X: " << m_ScreenXDifference; // Perform region growing again and show the result mitk::Image::Pointer resultImage = mitk::Image::New(); AccessFixedDimensionByItk_3( m_ReferenceSlice, StartRegionGrowing, 2, indexInWorkingSlice2D, m_Thresholds, resultImage); resultImage->SetGeometry(workingSliceGeometry); // Update the contour if (resultImage.IsNotNull() && m_ConnectedComponentValue >= 1) { float isoOffset = 0.33; mitk::ImageToContourModelFilter::Pointer contourExtractor = mitk::ImageToContourModelFilter::New(); contourExtractor->SetInput(resultImage); contourExtractor->SetContourValue(m_ConnectedComponentValue - isoOffset); contourExtractor->Update(); ContourModel::Pointer resultContour = ContourModel::New(); resultContour = contourExtractor->GetOutput(); // Show contour if (resultContour.IsNotNull()) { ContourModel::Pointer resultContourWorld = FeedbackContourTool::BackProjectContourFrom2DSlice( workingSliceGeometry, FeedbackContourTool::ProjectContourTo2DSlice(m_WorkingSlice, resultContour)); // this is not a beautiful solution, just one that works, check T22412 for details int timestep = positionEvent->GetSender()->GetTimeStep(); if (0 != timestep) { int size = resultContourWorld->GetNumberOfVertices(0); auto resultContourTimeWorld = mitk::ContourModel::New(); resultContourTimeWorld->Expand(timestep + 1); for (int loop = 0; loop < size; ++loop) { resultContourTimeWorld->AddVertex(resultContourWorld->GetVertexAt(loop, 0), timestep); } FeedbackContourTool::SetFeedbackContour(resultContourTimeWorld); } else { FeedbackContourTool::SetFeedbackContour(resultContourWorld); } FeedbackContourTool::SetFeedbackContourVisible(true); mitk::RenderingManager::GetInstance()->ForceImmediateUpdate(positionEvent->GetSender()->GetRenderWindow()); } } } } void mitk::RegionGrowingTool::OnMouseReleased(StateMachineAction *, InteractionEvent *interactionEvent) { // Until OnMousePressedInside() implements a behaviour, we're just returning here whenever m_PaintingPixelValue is 0, // i.e. when the user clicked inside the segmentation if (m_PaintingPixelValue == 0) { return; } auto *positionEvent = dynamic_cast(interactionEvent); if (m_WorkingSlice.IsNotNull() && m_FillFeedbackContour && positionEvent) { // Project contour into working slice ContourModel *feedbackContour(FeedbackContourTool::GetFeedbackContour()); ContourModel::Pointer projectedContour; // this is not a beautiful solution, just one that works, check T22412 for details int timestep = positionEvent->GetSender()->GetTimeStep(); if (0 != timestep) { int size = feedbackContour->GetNumberOfVertices(timestep); auto feedbackContourTime = mitk::ContourModel::New(); feedbackContourTime->Expand(timestep + 1); for (int loop = 0; loop < size; ++loop) { feedbackContourTime->AddVertex(feedbackContour->GetVertexAt(loop, timestep), 0); } projectedContour = FeedbackContourTool::ProjectContourTo2DSlice(m_WorkingSlice, feedbackContourTime, false, false); } else { projectedContour = FeedbackContourTool::ProjectContourTo2DSlice(m_WorkingSlice, feedbackContour, false, false); } // If there is a projected contour, fill it if (projectedContour.IsNotNull()) { // Get working data to pass to following method so we don't overwrite locked labels in a LabelSetImage mitk::DataNode *workingNode(m_ToolManager->GetWorkingData(0)); mitk::LabelSetImage *labelImage = workingNode != nullptr ? dynamic_cast(workingNode->GetData()) : nullptr; MITK_DEBUG << "Filling Segmentation"; if (labelImage != nullptr) { // m_PaintingPixelValue only decides whether to paint or not // For LabelSetImages we want to paint with the active label value auto activeLabel = labelImage->GetActiveLabel(labelImage->GetActiveLayer())->GetValue(); mitk::ContourModelUtils::FillContourInSlice(projectedContour, 0, m_WorkingSlice, labelImage, m_PaintingPixelValue * activeLabel); } else { mitk::ContourModelUtils::FillContourInSlice(projectedContour, 0, m_WorkingSlice, m_WorkingSlice, m_PaintingPixelValue); } this->WriteBackSegmentationResult(positionEvent, m_WorkingSlice); FeedbackContourTool::SetFeedbackContourVisible(false); } m_ScreenYDifference = 0; m_ScreenXDifference = 0; } } diff --git a/Modules/SurfaceInterpolation/mitkCreateDistanceImageFromSurfaceFilter.cpp b/Modules/SurfaceInterpolation/mitkCreateDistanceImageFromSurfaceFilter.cpp index aebf5e8798..b7bb5d067c 100644 --- a/Modules/SurfaceInterpolation/mitkCreateDistanceImageFromSurfaceFilter.cpp +++ b/Modules/SurfaceInterpolation/mitkCreateDistanceImageFromSurfaceFilter.cpp @@ -1,620 +1,621 @@ /*=================================================================== 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 "mitkCreateDistanceImageFromSurfaceFilter.h" #include "mitkImageCast.h" #include "vtkCellArray.h" #include "vtkCellData.h" #include "vtkDoubleArray.h" #include "vtkPolyData.h" #include "vtkSmartPointer.h" #include "itkImageRegionIteratorWithIndex.h" #include "itkNeighborhoodIterator.h" #include void mitk::CreateDistanceImageFromSurfaceFilter::CreateEmptyDistanceImage() { // Determine the bounds of the input points in index- and world-coordinates DistanceImageType::PointType minPointInWorldCoordinates, maxPointInWorldCoordinates; DistanceImageType::IndexType minPointInIndexCoordinates, maxPointInIndexCoordinates; DetermineBounds( minPointInWorldCoordinates, maxPointInWorldCoordinates, minPointInIndexCoordinates, maxPointInIndexCoordinates); // Calculate the extent of the region that contains all given points in MM. // To do this, we take the difference between the maximal and minimal // index-coordinates (must not be less than 1) and multiply it with the // spacing of the reference-image. Vector3D extentMM; for (unsigned int dim = 0; dim < 3; ++dim) { extentMM[dim] = (std::abs(maxPointInIndexCoordinates[dim] - minPointInIndexCoordinates[dim])) * m_ReferenceImage->GetSpacing()[dim]; } /* * Now create an empty distance image. The created image will always have the same number of pixels, independent from * the original image (e.g. always consists of 500000 pixels) and will have an isotropic spacing. * The spacing is calculated like the following: * The image's volume = 500000 Pixels = extentX*spacing*extentY*spacing*extentZ*spacing * So the spacing is: spacing = ( extentX*extentY*extentZ / 500000 )^(1/3) */ double basis = (extentMM[0] * extentMM[1] * extentMM[2]) / m_DistanceImageVolume; double exponent = 1.0 / 3.0; m_DistanceImageSpacing = pow(basis, exponent); // calculate the number of pixels of the distance image for each direction unsigned int numberOfXPixel = extentMM[0] / m_DistanceImageSpacing; unsigned int numberOfYPixel = extentMM[1] / m_DistanceImageSpacing; unsigned int numberOfZPixel = extentMM[2] / m_DistanceImageSpacing; // We increase the sizeOfRegion by 4 as we decrease the origin by 2 later. // This expansion of the region is necessary to achieve a complete // interpolation. DistanceImageType::SizeType sizeOfRegion; sizeOfRegion[0] = numberOfXPixel + 8; sizeOfRegion[1] = numberOfYPixel + 8; sizeOfRegion[2] = numberOfZPixel + 8; // The region starts at index 0,0,0 DistanceImageType::IndexType initialOriginAsIndex; initialOriginAsIndex.Fill(0); DistanceImageType::PointType originAsWorld = minPointInWorldCoordinates; DistanceImageType::RegionType lpRegion; lpRegion.SetSize(sizeOfRegion); lpRegion.SetIndex(initialOriginAsIndex); // We initialize the itk::Image with // * origin and direction to have it correctly placed and rotated in the world // * the largest possible region to set the extent to be calculated // * the isotropic spacing that we have calculated above m_DistanceImageITK = DistanceImageType::New(); m_DistanceImageITK->SetOrigin(originAsWorld); m_DistanceImageITK->SetDirection(m_ReferenceImage->GetDirection()); m_DistanceImageITK->SetRegions(lpRegion); m_DistanceImageITK->SetSpacing(m_DistanceImageSpacing); m_DistanceImageITK->Allocate(); // First of all the image is initialized with the value 10*m_DistanceImageSpacing for each pixel m_DistanceImageDefaultBufferValue = 10 * m_DistanceImageSpacing; m_DistanceImageITK->FillBuffer(m_DistanceImageDefaultBufferValue); // Now we move the origin of the distanceImage 2 index-Coordinates // in all directions DistanceImageType::IndexType originAsIndex; m_DistanceImageITK->TransformPhysicalPointToIndex(originAsWorld, originAsIndex); originAsIndex[0] -= 2; originAsIndex[1] -= 2; originAsIndex[2] -= 2; m_DistanceImageITK->TransformIndexToPhysicalPoint(originAsIndex, originAsWorld); m_DistanceImageITK->SetOrigin(originAsWorld); } mitk::CreateDistanceImageFromSurfaceFilter::CreateDistanceImageFromSurfaceFilter() + : m_DistanceImageSpacing(0.0), m_DistanceImageDefaultBufferValue(0.0) { m_DistanceImageVolume = 50000; this->m_UseProgressBar = false; this->m_ProgressStepSize = 5; mitk::Image::Pointer output = mitk::Image::New(); this->SetNthOutput(0, output.GetPointer()); } mitk::CreateDistanceImageFromSurfaceFilter::~CreateDistanceImageFromSurfaceFilter() { } void mitk::CreateDistanceImageFromSurfaceFilter::GenerateData() { this->PreprocessContourPoints(); this->CreateEmptyDistanceImage(); // First of all we have to build the equation-system from the existing contour-edge-points this->CreateSolutionMatrixAndFunctionValues(); if (this->m_UseProgressBar) mitk::ProgressBar::GetInstance()->Progress(1); m_Weights = m_SolutionMatrix.partialPivLu().solve(m_FunctionValues); if (this->m_UseProgressBar) mitk::ProgressBar::GetInstance()->Progress(2); // The last step is to create the distance map with the interpolated distance function this->FillDistanceImage(); if (this->m_UseProgressBar) mitk::ProgressBar::GetInstance()->Progress(2); m_Centers.clear(); m_Normals.clear(); } void mitk::CreateDistanceImageFromSurfaceFilter::PreprocessContourPoints() { unsigned int numberOfInputs = this->GetNumberOfIndexedInputs(); if (numberOfInputs == 0) { MITK_ERROR << "mitk::CreateDistanceImageFromSurfaceFilter: No input available. Please set an input!" << std::endl; itkExceptionMacro("mitk::CreateDistanceImageFromSurfaceFilter: No input available. Please set an input!"); return; } // First of all we have to extract the nomals and the surface points. // Duplicated points can be eliminated vtkSmartPointer polyData; vtkSmartPointer currentCellNormals; vtkSmartPointer existingPolys; vtkSmartPointer existingPoints; double p[3]; PointType currentPoint; PointType normal; for (unsigned int i = 0; i < numberOfInputs; i++) { auto currentSurface = this->GetInput(i); polyData = currentSurface->GetVtkPolyData(); if (polyData->GetNumberOfPolys() == 0) { MITK_INFO << "mitk::CreateDistanceImageFromSurfaceFilter: No input-polygons available. Please be sure the input " "surface consists of polygons!" << std::endl; } currentCellNormals = vtkDoubleArray::SafeDownCast(polyData->GetCellData()->GetNormals()); existingPolys = polyData->GetPolys(); existingPoints = polyData->GetPoints(); existingPolys->InitTraversal(); vtkIdType *cell(nullptr); vtkIdType cellSize(0); for (existingPolys->InitTraversal(); existingPolys->GetNextCell(cellSize, cell);) { for (vtkIdType j = 0; j < cellSize; j++) { existingPoints->GetPoint(cell[j], p); currentPoint.copy_in(p); int count = std::count(m_Centers.begin(), m_Centers.end(), currentPoint); if (count == 0) { double currentNormal[3]; currentCellNormals->GetTuple(cell[j], currentNormal); normal.copy_in(currentNormal); m_Normals.push_back(normal); m_Centers.push_back(currentPoint); } } // end for all points } // end for all cells } // end for all outputs } void mitk::CreateDistanceImageFromSurfaceFilter::CreateSolutionMatrixAndFunctionValues() { // For we can now calculate the exact size of the centers we initialize the data structures unsigned int numberOfCenters = m_Centers.size(); m_Centers.reserve(numberOfCenters * 3); m_FunctionValues.resize(numberOfCenters * 3); m_FunctionValues.fill(0); PointType currentPoint; PointType normal; // Create inner points for (unsigned int i = 0; i < numberOfCenters; i++) { currentPoint = m_Centers.at(i); normal = m_Normals.at(i); currentPoint[0] = currentPoint[0] - normal[0] * m_DistanceImageSpacing; currentPoint[1] = currentPoint[1] - normal[1] * m_DistanceImageSpacing; currentPoint[2] = currentPoint[2] - normal[2] * m_DistanceImageSpacing; m_Centers.push_back(currentPoint); m_FunctionValues[numberOfCenters + i] = -m_DistanceImageSpacing; } // Create outer points for (unsigned int i = 0; i < numberOfCenters; i++) { currentPoint = m_Centers.at(i); normal = m_Normals.at(i); currentPoint[0] = currentPoint[0] + normal[0] * m_DistanceImageSpacing; currentPoint[1] = currentPoint[1] + normal[1] * m_DistanceImageSpacing; currentPoint[2] = currentPoint[2] + normal[2] * m_DistanceImageSpacing; m_Centers.push_back(currentPoint); m_FunctionValues[numberOfCenters * 2 + i] = m_DistanceImageSpacing; } // Now we have created all centers and all function values. Next step is to create the solution matrix numberOfCenters = m_Centers.size(); m_SolutionMatrix.resize(numberOfCenters, numberOfCenters); m_Weights.resize(numberOfCenters); PointType p1; PointType p2; double norm; for (unsigned int i = 0; i < numberOfCenters; i++) { for (unsigned int j = 0; j < numberOfCenters; j++) { // Calculate the RBF value. Currently using Phi(r) = r with r is the euclidian distance between two points p1 = m_Centers.at(i); p2 = m_Centers.at(j); p1 = p1 - p2; norm = p1.two_norm(); m_SolutionMatrix(i, j) = norm; } } } void mitk::CreateDistanceImageFromSurfaceFilter::FillDistanceImage() { /* * Now we must calculate the distance for each pixel. But instead of calculating the distance value * for all of the image's pixels we proceed similar to the region growing algorithm: * * 1. Take the first pixel from the narrowband_point_list and calculate the distance for each neighbor (6er) * 2. If the current index's distance value is below a certain threshold push it into the list * 3. Next iteration take the next index from the list and originAsIndex with 1. again * * This is done until the narrowband_point_list is empty. */ typedef itk::ImageRegionIteratorWithIndex ImageIterator; typedef itk::NeighborhoodIterator NeighborhoodImageIterator; std::queue narrowbandPoints; PointType currentPoint = m_Centers.at(0); double distance = this->CalculateDistanceValue(currentPoint); // create itk::Point from vnl_vector DistanceImageType::PointType currentPointAsPoint; currentPointAsPoint[0] = currentPoint[0]; currentPointAsPoint[1] = currentPoint[1]; currentPointAsPoint[2] = currentPoint[2]; // Transform the input point in world-coordinates to index-coordinates DistanceImageType::IndexType currentIndex; m_DistanceImageITK->TransformPhysicalPointToIndex(currentPointAsPoint, currentIndex); assert( m_DistanceImageITK->GetLargestPossibleRegion().IsInside(currentIndex)); // we are quite certain this should hold narrowbandPoints.push(currentIndex); m_DistanceImageITK->SetPixel(currentIndex, distance); NeighborhoodImageIterator::RadiusType radius; radius.Fill(1); NeighborhoodImageIterator nIt(radius, m_DistanceImageITK, m_DistanceImageITK->GetLargestPossibleRegion()); unsigned int relativeNbIdx[] = {4, 10, 12, 14, 16, 22}; bool isInBounds = false; while (!narrowbandPoints.empty()) { nIt.SetLocation(narrowbandPoints.front()); narrowbandPoints.pop(); unsigned int *relativeNb = &relativeNbIdx[0]; for (int i = 0; i < 6; i++) { nIt.GetPixel(*relativeNb, isInBounds); if (isInBounds && nIt.GetPixel(*relativeNb) == m_DistanceImageDefaultBufferValue) { currentIndex = nIt.GetIndex(*relativeNb); // Transform the currently checked point from index-coordinates to // world-coordinates m_DistanceImageITK->TransformIndexToPhysicalPoint(currentIndex, currentPointAsPoint); // create a vnl_vector currentPoint[0] = currentPointAsPoint[0]; currentPoint[1] = currentPointAsPoint[1]; currentPoint[2] = currentPointAsPoint[2]; // and check the distance distance = this->CalculateDistanceValue(currentPoint); if (std::fabs(distance) <= m_DistanceImageSpacing * 2) { nIt.SetPixel(*relativeNb, distance); narrowbandPoints.push(currentIndex); } } relativeNb++; } } ImageIterator imgRegionIterator(m_DistanceImageITK, m_DistanceImageITK->GetLargestPossibleRegion()); imgRegionIterator.GoToBegin(); double prevPixelVal = 1; DistanceImageType::IndexType _size; _size.Fill(-1); _size += m_DistanceImageITK->GetLargestPossibleRegion().GetSize(); // Set every pixel inside the surface to -m_DistanceImageDefaultBufferValue except the edge point (so that the // received surface is closed) while (!imgRegionIterator.IsAtEnd()) { if (imgRegionIterator.Get() == m_DistanceImageDefaultBufferValue && prevPixelVal < 0) { while (imgRegionIterator.Get() == m_DistanceImageDefaultBufferValue) { if (imgRegionIterator.GetIndex()[0] == _size[0] || imgRegionIterator.GetIndex()[1] == _size[1] || imgRegionIterator.GetIndex()[2] == _size[2] || imgRegionIterator.GetIndex()[0] == 0U || imgRegionIterator.GetIndex()[1] == 0U || imgRegionIterator.GetIndex()[2] == 0U) { imgRegionIterator.Set(m_DistanceImageDefaultBufferValue); prevPixelVal = m_DistanceImageDefaultBufferValue; ++imgRegionIterator; break; } else { imgRegionIterator.Set((-1) * m_DistanceImageDefaultBufferValue); ++imgRegionIterator; prevPixelVal = (-1) * m_DistanceImageDefaultBufferValue; } } } else if (imgRegionIterator.GetIndex()[0] == _size[0] || imgRegionIterator.GetIndex()[1] == _size[1] || imgRegionIterator.GetIndex()[2] == _size[2] || imgRegionIterator.GetIndex()[0] == 0U || imgRegionIterator.GetIndex()[1] == 0U || imgRegionIterator.GetIndex()[2] == 0U) { imgRegionIterator.Set(m_DistanceImageDefaultBufferValue); prevPixelVal = m_DistanceImageDefaultBufferValue; ++imgRegionIterator; } else { prevPixelVal = imgRegionIterator.Get(); ++imgRegionIterator; } } Image::Pointer resultImage = this->GetOutput(); // Cast the created distance-Image from itk::Image to the mitk::Image // that is our output. CastToMitkImage(m_DistanceImageITK, resultImage); } double mitk::CreateDistanceImageFromSurfaceFilter::CalculateDistanceValue(PointType p) { double distanceValue(0); PointType p1; PointType p2; double norm; CenterList::iterator centerIter; unsigned int count(0); for (centerIter = m_Centers.begin(); centerIter != m_Centers.end(); centerIter++) { p1 = *centerIter; p2 = p - p1; norm = p2.two_norm(); distanceValue = distanceValue + (norm * m_Weights[count]); ++count; } return distanceValue; } void mitk::CreateDistanceImageFromSurfaceFilter::GenerateOutputInformation() { } void mitk::CreateDistanceImageFromSurfaceFilter::PrintEquationSystem() { std::stringstream out; out << "Nummber of rows: " << m_SolutionMatrix.rows() << " ****** Number of columns: " << m_SolutionMatrix.cols() << endl; out << "[ "; for (int i = 0; i < m_SolutionMatrix.rows(); i++) { for (int j = 0; j < m_SolutionMatrix.cols(); j++) { out << m_SolutionMatrix(i, j) << " "; } out << ";" << endl; } out << " ]\n\n\n"; for (unsigned int i = 0; i < m_Centers.size(); i++) { out << m_Centers.at(i) << ";" << endl; } std::cout << "Equation system: \n\n\n" << out.str(); } void mitk::CreateDistanceImageFromSurfaceFilter::SetInput(const mitk::Surface *surface) { this->SetInput(0, surface); } void mitk::CreateDistanceImageFromSurfaceFilter::SetInput(unsigned int idx, const mitk::Surface *surface) { if (this->GetInput(idx) != surface) { this->SetNthInput(idx, const_cast(surface)); this->Modified(); } } const mitk::Surface *mitk::CreateDistanceImageFromSurfaceFilter::GetInput() { if (this->GetNumberOfIndexedInputs() < 1) return nullptr; return static_cast(this->ProcessObject::GetInput(0)); } const mitk::Surface *mitk::CreateDistanceImageFromSurfaceFilter::GetInput(unsigned int idx) { if (this->GetNumberOfIndexedInputs() < 1) return nullptr; return static_cast(this->ProcessObject::GetInput(idx)); } void mitk::CreateDistanceImageFromSurfaceFilter::RemoveInputs(mitk::Surface *input) { DataObjectPointerArraySizeType nb = this->GetNumberOfIndexedInputs(); for (DataObjectPointerArraySizeType i = 0; i < nb; i++) { if (this->GetInput(i) == input) { this->RemoveInput(i); return; } } } void mitk::CreateDistanceImageFromSurfaceFilter::Reset() { for (unsigned int i = 0; i < this->GetNumberOfIndexedInputs(); i++) { this->PopBackInput(); } this->SetNumberOfIndexedInputs(0); this->SetNumberOfIndexedOutputs(1); mitk::Image::Pointer output = mitk::Image::New(); this->SetNthOutput(0, output.GetPointer()); } void mitk::CreateDistanceImageFromSurfaceFilter::SetUseProgressBar(bool status) { this->m_UseProgressBar = status; } void mitk::CreateDistanceImageFromSurfaceFilter::SetProgressStepSize(unsigned int stepSize) { this->m_ProgressStepSize = stepSize; } void mitk::CreateDistanceImageFromSurfaceFilter::SetReferenceImage(itk::ImageBase<3>::Pointer referenceImage) { m_ReferenceImage = referenceImage; } void mitk::CreateDistanceImageFromSurfaceFilter::DetermineBounds( DistanceImageType::PointType &minPointInWorldCoordinates, DistanceImageType::PointType &maxPointInWorldCoordinates, DistanceImageType::IndexType &minPointInIndexCoordinates, DistanceImageType::IndexType &maxPointInIndexCoordinates) { PointType firstCenter = m_Centers.at(0); DistanceImageType::PointType tmpPoint; tmpPoint[0] = firstCenter[0]; tmpPoint[1] = firstCenter[1]; tmpPoint[2] = firstCenter[2]; // transform the first point from world-coordinates to index-coordinates itk::ContinuousIndex tmpIndex; m_ReferenceImage->TransformPhysicalPointToContinuousIndex(tmpPoint, tmpIndex); // initialize the variables with this first point DistanceImageType::IndexValueType xmin = tmpIndex[0]; DistanceImageType::IndexValueType ymin = tmpIndex[1]; DistanceImageType::IndexValueType zmin = tmpIndex[2]; DistanceImageType::IndexValueType xmax = tmpIndex[0]; DistanceImageType::IndexValueType ymax = tmpIndex[1]; DistanceImageType::IndexValueType zmax = tmpIndex[2]; // iterate over the rest of the points auto centerIter = m_Centers.begin(); for (++centerIter; centerIter != m_Centers.end(); centerIter++) { tmpPoint[0] = (*centerIter)[0]; tmpPoint[1] = (*centerIter)[1]; tmpPoint[2] = (*centerIter)[2]; // transform each point from world-coordinates to index-coordinates m_ReferenceImage->TransformPhysicalPointToContinuousIndex(tmpPoint, tmpIndex); // and set the variables accordingly to find the minimum // and maximum in all directions in index-coordinates if (xmin > tmpIndex[0]) { xmin = tmpIndex[0]; } if (ymin > tmpIndex[1]) { ymin = tmpIndex[1]; } if (zmin > tmpIndex[2]) { zmin = tmpIndex[2]; } if (xmax < tmpIndex[0]) { xmax = tmpIndex[0]; } if (ymax < tmpIndex[1]) { ymax = tmpIndex[1]; } if (zmax < tmpIndex[2]) { zmax = tmpIndex[2]; } } // put the found coordinates into Index-Points minPointInIndexCoordinates[0] = xmin; minPointInIndexCoordinates[1] = ymin; minPointInIndexCoordinates[2] = zmin; maxPointInIndexCoordinates[0] = xmax; maxPointInIndexCoordinates[1] = ymax; maxPointInIndexCoordinates[2] = zmax; // and transform them into world-coordinates m_ReferenceImage->TransformIndexToPhysicalPoint(minPointInIndexCoordinates, minPointInWorldCoordinates); m_ReferenceImage->TransformIndexToPhysicalPoint(maxPointInIndexCoordinates, maxPointInWorldCoordinates); } diff --git a/Modules/SurfaceInterpolation/mitkImageToPointCloudFilter.cpp b/Modules/SurfaceInterpolation/mitkImageToPointCloudFilter.cpp index 766e08fb81..8497ab49a5 100644 --- a/Modules/SurfaceInterpolation/mitkImageToPointCloudFilter.cpp +++ b/Modules/SurfaceInterpolation/mitkImageToPointCloudFilter.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 "mitkImageToPointCloudFilter.h" #include #include #include #include #include #include #include #include #include -mitk::ImageToPointCloudFilter::ImageToPointCloudFilter() +mitk::ImageToPointCloudFilter::ImageToPointCloudFilter() : m_Geometry(nullptr) { m_Method = DetectionMethod(0); this->SetNumberOfRequiredInputs(1); this->SetNumberOfIndexedOutputs(1); } mitk::ImageToPointCloudFilter::~ImageToPointCloudFilter() { } void mitk::ImageToPointCloudFilter::GenerateData() { mitk::Image::ConstPointer image = ImageToUnstructuredGridFilter::GetInput(); m_Geometry = image->GetGeometry(); if (image.IsNull()) { MITK_ERROR << "mitk::ImageToContourFilter: No input available. " "Please set the input!" << std::endl; return; } mitk::Image::Pointer notConstImage = const_cast(image.GetPointer()); switch (m_Method) { case 0: AccessByItk_1(notConstImage.GetPointer(), StdDeviations, 2) break; case 1: AccessByItk_1(notConstImage.GetPointer(), StdDeviations, 3) break; case 2: AccessByItk_1(notConstImage.GetPointer(), StdDeviations, 4) break; default: AccessByItk_1(notConstImage.GetPointer(), StdDeviations, 2) break; } } template void mitk::ImageToPointCloudFilter::StdDeviations(itk::Image *image, int amount) { typedef itk::Image InputImageType; typedef itk::CastImageFilter ImagePTypeToFloatPTypeCasterType; typedef itk::LaplacianImageFilter LaplacianFilterType; typename LaplacianFilterType::Pointer lapFilter = LaplacianFilterType::New(); typename ImagePTypeToFloatPTypeCasterType::Pointer caster = ImagePTypeToFloatPTypeCasterType::New(); caster->SetInput(image); caster->Update(); FloatImageType::Pointer fImage = caster->GetOutput(); lapFilter->SetInput(fImage); lapFilter->UpdateLargestPossibleRegion(); mitk::Image::Pointer edgeImage = mitk::ImportItkImage(lapFilter->GetOutput()); mitk::ImageStatisticsCalculator::Pointer statCalc = mitk::ImageStatisticsCalculator::New(); statCalc->SetInputImage(edgeImage); mitk::ImageStatisticsCalculator::StatisticsContainer::Pointer stats = statCalc->GetStatistics(); double mean = stats->GetMean(); double stdDev = stats->GetStd(); double upperThreshold = mean + stdDev * amount; double lowerThreshold = mean - stdDev * amount; typename itk::ImageRegionIterator it(lapFilter->GetOutput(), lapFilter->GetOutput()->GetRequestedRegion()); vtkSmartPointer points = vtkSmartPointer::New(); double greatX = 0, greatY = 0, greatZ = 0; it.GoToBegin(); while (!it.IsAtEnd()) { if (it.Get() > lowerThreshold && it.Get() < upperThreshold) { it.Set(0); } else { it.Set(1); mitk::Point3D imagePoint; mitk::Point3D worldPoint; imagePoint[0] = it.GetIndex()[0]; imagePoint[1] = it.GetIndex()[1]; imagePoint[2] = it.GetIndex()[2]; m_Geometry->IndexToWorld(imagePoint, worldPoint); if (worldPoint[0] > greatX) greatX = worldPoint[0]; if (worldPoint[1] > greatY) greatY = worldPoint[1]; if (worldPoint[2] > greatZ) greatZ = worldPoint[2]; points->InsertNextPoint(worldPoint[0], worldPoint[1], worldPoint[2]); m_NumberOfExtractedPoints++; } ++it; } /*need to build the UnstructuredGrid with at least one vertex otherwise its not visible*/ vtkSmartPointer verts = vtkSmartPointer::New(); verts->GetPointIds()->SetNumberOfIds(m_NumberOfExtractedPoints); for (int i = 0; i < m_NumberOfExtractedPoints; i++) { verts->GetPointIds()->SetId(i, i); } vtkSmartPointer uGrid = vtkSmartPointer::New(); uGrid->Allocate(1); uGrid->InsertNextCell(verts->GetCellType(), verts->GetPointIds()); uGrid->SetPoints(points); mitk::UnstructuredGrid::Pointer outputGrid = mitk::UnstructuredGrid::New(); outputGrid->SetVtkUnstructuredGrid(uGrid); this->SetNthOutput(0, outputGrid); } void mitk::ImageToPointCloudFilter::GenerateOutputInformation() { Superclass::GenerateOutputInformation(); }