diff --git a/Modules/Segmentation/Algorithms/mitkContourModelSubDivisionFilter.cpp b/Modules/Segmentation/Algorithms/mitkContourModelSubDivisionFilter.cpp index 2efb3fc92c..ab71af402d 100644 --- a/Modules/Segmentation/Algorithms/mitkContourModelSubDivisionFilter.cpp +++ b/Modules/Segmentation/Algorithms/mitkContourModelSubDivisionFilter.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 "mitkContourModelSubDivisionFilter.h" #include #include mitk::ContourModelSubDivisionFilter::ContourModelSubDivisionFilter() { OutputType::Pointer output = dynamic_cast ( this->MakeOutput( 0 ).GetPointer() ); this->SetNumberOfRequiredInputs(1); - this->SetNumberOfOutputs( 1 ); + this->SetNumberOfIndexedOutputs( 1 ); this->SetNthOutput(0, output.GetPointer()); this->m_InterpolationIterations = 4; } mitk::ContourModelSubDivisionFilter::~ContourModelSubDivisionFilter() { } void mitk::ContourModelSubDivisionFilter::SetInput ( const mitk::ContourModelSubDivisionFilter::InputType* input ) { this->SetInput( 0, input ); } void mitk::ContourModelSubDivisionFilter::SetInput ( unsigned int idx, const mitk::ContourModelSubDivisionFilter::InputType* input ) { if ( idx + 1 > this->GetNumberOfInputs() ) { this->SetNumberOfRequiredInputs(idx + 1); } if ( input != static_cast ( this->ProcessObject::GetInput ( idx ) ) ) { this->ProcessObject::SetNthInput ( idx, const_cast ( input ) ); this->Modified(); } } const mitk::ContourModelSubDivisionFilter::InputType* mitk::ContourModelSubDivisionFilter::GetInput( void ) { if (this->GetNumberOfInputs() < 1) return NULL; return static_cast(this->ProcessObject::GetInput(0)); } const mitk::ContourModelSubDivisionFilter::InputType* mitk::ContourModelSubDivisionFilter::GetInput( unsigned int idx ) { if (this->GetNumberOfInputs() < 1) return NULL; return static_cast(this->ProcessObject::GetInput(idx)); } void mitk::ContourModelSubDivisionFilter::GenerateData() { mitk::ContourModel::Pointer input = const_cast(this->GetInput(0)); mitk::ContourModelSubDivisionFilter::OutputType::Pointer outputContour = this->GetOutput(); mitk::ContourModel::Pointer contour(input); unsigned int timestep = input->GetTimeSteps(); for ( int currentTimestep = 0; currentTimestep < timestep; currentTimestep++) { if( input->GetNumberOfVertices(currentTimestep) >= 4) { for( int iterations = 0; iterations < this->m_InterpolationIterations; iterations++) { InputType::VertexIterator it = contour->IteratorBegin(); InputType::VertexIterator end = contour->IteratorEnd(); InputType::VertexIterator first = contour->IteratorBegin(); InputType::VertexIterator last = contour->IteratorEnd()-1; //tempory contour to store result of a subdivision iteration mitk::ContourModel::Pointer tempContour = mitk::ContourModel::New(); //insert subpoints while ( it != end ) { //add the current point to the temp contour tempContour->AddVertex((*it)->Coordinates, (*it)->IsControlPoint, currentTimestep); //control points for interpolation InputType::VertexIterator Ci = it; InputType::VertexIterator CiPlus1; InputType::VertexIterator CiPlus2; InputType::VertexIterator CiMinus1; //consider all possible cases if( it == first) { if( input->IsClosed(currentTimestep) ) { CiPlus1 = it + 1; CiPlus2 = it + 2; CiMinus1 = last; } else { CiPlus1 = it + 1; CiPlus2 = it + 2; CiMinus1 = it; } } else if( it == last ) { if( input->IsClosed(currentTimestep) ) { CiPlus1 = first; CiPlus2 = first + 1; CiMinus1 = it -1; } else { //don't add point after last break; } } else if( it == (last - 1) ) { if( input->IsClosed(currentTimestep) ) { CiPlus1 = it + 1; CiPlus2 = first; CiMinus1 = it -1; } else { CiPlus1 = it + 1; CiPlus2 = it + 1; CiMinus1 = it -1; } } else { CiPlus1 = it + 1; CiPlus2 = it + 2; CiMinus1 = it -1; } /* F2i = Ci * F2i+1 = -1/16Ci-1 + 9/16Ci + 9/16Ci+1 - 1/16Ci+2 */ mitk::Point3D subpoint; mitk::Point3D a; a[0]=(-1.0/16.0) * (*CiMinus1)->Coordinates[0]; a[1]=(-1.0/16.0) * (*CiMinus1)->Coordinates[1]; a[2]= (-1.0/16.0) * (*CiMinus1)->Coordinates[2]; mitk::Point3D b; b[0]=(9.0/16.0) * (*Ci)->Coordinates[0]; b[1]=(9.0/16.0) * (*Ci)->Coordinates[1]; b[2]= (9.0/16.0) * (*Ci)->Coordinates[2]; mitk::Point3D c; c[0]=(9.0/16.0) * (*CiPlus1)->Coordinates[0]; c[1]=(9.0/16.0) * (*CiPlus1)->Coordinates[1]; c[2]= (9.0/16.0) * (*CiPlus1)->Coordinates[2]; mitk::Point3D d; d[0]=(-1.0/16.0) * (*CiPlus2)->Coordinates[0]; d[1]=(-1.0/16.0) * (*CiPlus2)->Coordinates[1]; d[2]= (-1.0/16.0) * (*CiPlus2)->Coordinates[2]; subpoint[0] = a[0] + b[0] + c[0] + d[0]; subpoint[1] = a[1] + b[1] + c[1] + d[1]; subpoint[2] = a[2] + b[2] + c[2] + d[2]; InputType::VertexType subdivisionPoint(subpoint,false); //add the new subdivision point to our tempContour tempContour->AddVertex(subdivisionPoint.Coordinates, currentTimestep); it++; } //set the interpolated contour as the contour for the next iteration contour = tempContour; } } else { //filter not executeable - set input to output contour = input; } } //somehow the isClosed property is not set via copy constructor contour->SetIsClosed(input->IsClosed()); this->SetOutput(0, contour); } diff --git a/Modules/Segmentation/Algorithms/mitkContourModelToSurfaceFilter.h b/Modules/Segmentation/Algorithms/mitkContourModelToSurfaceFilter.h index 52ce1eef5b..0ffade6471 100644 --- a/Modules/Segmentation/Algorithms/mitkContourModelToSurfaceFilter.h +++ b/Modules/Segmentation/Algorithms/mitkContourModelToSurfaceFilter.h @@ -1,63 +1,63 @@ /*=================================================================== 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_CONTOURMODEL_TO_Surface_FILTER_H_ #define _MITK_CONTOURMODEL_TO_Surface_FILTER_H_ #include "mitkCommon.h" #include "SegmentationExports.h" #include "mitkContourModel.h" #include "mitkContourModelSource.h" #include namespace mitk { class Segmentation_EXPORT ContourModelToSurfaceFilter : public SurfaceSource { public: /** Standard class typedefs. */ - mitkClassMacro( ContourModelToSurfaceFilter,BaseProcess ); + mitkClassMacro( ContourModelToSurfaceFilter, SurfaceSource ); /** Method for creation through the object factory. */ itkNewMacro(Self); typedef mitk::Surface OutputType; typedef mitk::ContourModel InputType; /** Set/Get the image input of this process object. */ virtual void SetInput( const InputType *input); virtual void SetInput( unsigned int idx, const InputType * input); const InputType * GetInput(void); const InputType * GetInput(unsigned int idx); protected: ContourModelToSurfaceFilter(); ~ContourModelToSurfaceFilter(); virtual void GenerateData(); }; } #endif diff --git a/Modules/Segmentation/Algorithms/mitkImageLiveWireContourModelFilter.cpp b/Modules/Segmentation/Algorithms/mitkImageLiveWireContourModelFilter.cpp index 69094bfa56..85904f2b70 100644 --- a/Modules/Segmentation/Algorithms/mitkImageLiveWireContourModelFilter.cpp +++ b/Modules/Segmentation/Algorithms/mitkImageLiveWireContourModelFilter.cpp @@ -1,416 +1,428 @@ /*=================================================================== 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 "mitkImageLiveWireContourModelFilter.h" #include #include #include mitk::ImageLiveWireContourModelFilter::ImageLiveWireContourModelFilter() { OutputType::Pointer output = dynamic_cast ( this->MakeOutput( 0 ).GetPointer() ); this->SetNumberOfRequiredInputs(1); - this->SetNumberOfOutputs( 1 ); + this->SetNumberOfIndexedOutputs( 1 ); this->SetNthOutput(0, output.GetPointer()); m_CostFunction = ImageLiveWireContourModelFilter::CostFunctionType::New(); m_ShortestPathFilter = ShortestPathImageFilterType::New(); m_ShortestPathFilter->SetCostFunction(m_CostFunction); m_UseDynamicCostMap = false; m_ImageModified = false; m_Timestep = 0; } mitk::ImageLiveWireContourModelFilter::~ImageLiveWireContourModelFilter() { } mitk::ImageLiveWireContourModelFilter::OutputType* mitk::ImageLiveWireContourModelFilter::GetOutput() { return Superclass::GetOutput(); } void mitk::ImageLiveWireContourModelFilter::SetInput ( const mitk::ImageLiveWireContourModelFilter::InputType* input ) { this->SetInput( 0, input ); } void mitk::ImageLiveWireContourModelFilter::SetInput ( unsigned int idx, const mitk::ImageLiveWireContourModelFilter::InputType* input ) { if ( idx + 1 > this->GetNumberOfInputs() ) { this->SetNumberOfRequiredInputs(idx + 1); } if ( input != static_cast ( this->ProcessObject::GetInput ( idx ) ) ) { this->ProcessObject::SetNthInput ( idx, const_cast ( input ) ); this->Modified(); this->m_ImageModified = true; m_ShortestPathFilter = ShortestPathImageFilterType::New(); m_ShortestPathFilter->SetCostFunction(m_CostFunction); } } const mitk::ImageLiveWireContourModelFilter::InputType* mitk::ImageLiveWireContourModelFilter::GetInput( void ) { if (this->GetNumberOfInputs() < 1) return NULL; return static_cast(this->ProcessObject::GetInput(0)); } const mitk::ImageLiveWireContourModelFilter::InputType* mitk::ImageLiveWireContourModelFilter::GetInput( unsigned int idx ) { if (this->GetNumberOfInputs() < 1) return NULL; return static_cast(this->ProcessObject::GetInput(idx)); } void mitk::ImageLiveWireContourModelFilter::GenerateData() { mitk::Image::ConstPointer input = dynamic_cast(this->GetInput()); if(!input) { MITK_ERROR << "No input available."; itkExceptionMacro("mitk::ImageToLiveWireContourFilter: No input available. Please set the input!"); return; } if( input->GetDimension() != 2 ) { MITK_ERROR << "Filter is only working on 2D images."; itkExceptionMacro("mitk::ImageToLiveWireContourFilter: Filter is only working on 2D images.. Please make sure that the input is 2D!"); return; } + if( m_ImageModified ) + { + AccessFixedDimensionByItk(input, ItkPreProcessImage, 2); + m_ImageModified = false; + } input->GetGeometry()->WorldToIndex(m_StartPoint, m_StartPointInIndex); input->GetGeometry()->WorldToIndex(m_EndPoint, m_EndPointInIndex); //only start calculating if both indices are inside image geometry if( input->GetGeometry()->IsIndexInside(this->m_StartPointInIndex) && input->GetGeometry()->IsIndexInside(this->m_EndPointInIndex) ) { - AccessFixedDimensionByItk(input, ItkProcessImage, 2); - m_ImageModified = false; + try + { + this->UpdateLiveWire(); + } + catch( itk::ExceptionObject & e ) + { + MITK_INFO << "Exception caught during live wiring calculation: " << e; + m_ImageModified = true; + return; + } } } - template -void mitk::ImageLiveWireContourModelFilter::ItkProcessImage (itk::Image* inputImage) +void mitk::ImageLiveWireContourModelFilter::ItkPreProcessImage (itk::Image* inputImage) { - typedef itk::Image< TPixel, VImageDimension > InputImageType; - typedef typename InputImageType::IndexType IndexType; - + typedef itk::Image< TPixel, VImageDimension > InputImageType; + typedef itk::CastImageFilter< InputImageType, FloatImageType > CastFilterType; - /* compute the requested region for itk filters */ + typename CastFilterType::Pointer castFilter = CastFilterType::New(); + castFilter->SetInput(inputImage); + castFilter->Update(); + m_PreProcessedImage = castFilter->GetOutput(); + m_CostFunction->SetImage( m_PreProcessedImage ); + m_ShortestPathFilter->SetInput( m_PreProcessedImage ); +} - IndexType startPoint, endPoint; +void mitk::ImageLiveWireContourModelFilter::UpdateLiveWire() +{ +// compute the requested region for itk filters + FloatImageType::IndexType startPoint, endPoint; startPoint[0] = m_StartPointInIndex[0]; startPoint[1] = m_StartPointInIndex[1]; endPoint[0] = m_EndPointInIndex[0]; endPoint[1] = m_EndPointInIndex[1]; //minimum value in each direction for startRegion - IndexType startRegion; + FloatImageType::IndexType startRegion; startRegion[0] = startPoint[0] < endPoint[0] ? startPoint[0] : endPoint[0]; startRegion[1] = startPoint[1] < endPoint[1] ? startPoint[1] : endPoint[1]; //maximum value in each direction for size - typename InputImageType::SizeType size; + FloatImageType::SizeType size; size[0] = abs( startPoint[0] - endPoint[0] ); size[1] = abs( startPoint[1] - endPoint[1] ); + MITK_INFO << "start region: " << startRegion[0] << " " << startRegion[1]; + MITK_INFO << "size: " << size[0] << " " << size[1]; - typename CostFunctionType::RegionType region; + CostFunctionType::RegionType region; region.SetSize( size ); region.SetIndex( startRegion ); - /*---------------------------------------------*/ //inputImage->SetRequestedRegion(region); - typedef itk::CastImageFilter< InputImageType, FloatImageType > CastFilterType; - typename CastFilterType::Pointer castFilter = CastFilterType::New(); - castFilter->SetInput(inputImage); - castFilter->Update(); - /* extracts features from image and calculates costs */ - if( m_ImageModified ) - m_CostFunction->SetImage(castFilter->GetOutput()); + // extracts features from image and calculates costs + //m_CostFunction->SetImage(m_PreProcessedImage); m_CostFunction->SetStartIndex(startPoint); m_CostFunction->SetEndIndex(endPoint); m_CostFunction->SetRequestedRegion(region); m_CostFunction->SetUseCostMap(m_UseDynamicCostMap); - /*---------------------------------------------*/ - - /* calculate shortest path between start and end point */ + // calculate shortest path between start and end point m_ShortestPathFilter->SetFullNeighborsMode(true); - m_ShortestPathFilter->SetInput(castFilter->GetOutput()); + //m_ShortestPathFilter->SetInput( m_CostFunction->SetImage(m_PreProcessedImage) ); m_ShortestPathFilter->SetMakeOutputImage(false); //m_ShortestPathFilter->SetCalcAllDistances(true); m_ShortestPathFilter->SetStartIndex(startPoint); m_ShortestPathFilter->SetEndIndex(endPoint); - m_ShortestPathFilter->Update(); - /*---------------------------------------------*/ - - - /* construct contour from path image */ + // construct contour from path image //get the shortest path as vector - typename std::vector< ShortestPathImageFilterType::IndexType> shortestPath = m_ShortestPathFilter->GetVectorPath(); + ShortestPathType shortestPath = m_ShortestPathFilter->GetVectorPath(); - //fill the output contour with controll points from the path + //fill the output contour with control points from the path OutputType::Pointer output = dynamic_cast ( this->MakeOutput( 0 ).GetPointer() ); this->SetNthOutput(0, output.GetPointer()); +// OutputType::Pointer output = dynamic_cast ( this->GetOutput() ); output->Expand(m_Timestep+1); +// output->Clear(); + mitk::Image::ConstPointer input = dynamic_cast(this->GetInput()); - typename std::vector< ShortestPathImageFilterType::IndexType>::iterator pathIterator = shortestPath.begin(); + ShortestPathType::const_iterator pathIterator = shortestPath.begin(); while(pathIterator != shortestPath.end()) { mitk::Point3D currentPoint; currentPoint[0] = (*pathIterator)[0]; currentPoint[1] = (*pathIterator)[1]; currentPoint[2] = 0; - input->GetGeometry()->IndexToWorld(currentPoint, currentPoint); output->AddVertex(currentPoint, false, m_Timestep); pathIterator++; } - /*---------------------------------------------*/ } + bool mitk::ImageLiveWireContourModelFilter::CreateDynamicCostMap(mitk::ContourModel* path) { mitk::Image::ConstPointer input = dynamic_cast(this->GetInput()); if(input) { AccessFixedDimensionByItk_1(input,CreateDynamicCostMapByITK, 2, path); return true; } else { return false; } } template void mitk::ImageLiveWireContourModelFilter::CreateDynamicCostMapByITK( itk::Image* inputImage, mitk::ContourModel* path ) { /*++++++++++ create dynamic cost transfer map ++++++++++*/ /* Compute the costs of the gradient magnitude dynamically. * using a map of the histogram of gradient magnitude image. * Use the histogram gradient map to interpolate the costs * with gaussing function including next two bins right and left * to current position x. With the histogram gradient costs are interpolated * with a gaussing function summation of next two bins right and left * to current position x. */ std::vector< itk::Index > shortestPath; mitk::Image::ConstPointer input = dynamic_cast(this->GetInput()); if(path == NULL) { OutputType::Pointer output = this->GetOutput(); mitk::ContourModel::VertexIterator it = output->IteratorBegin(); while( it != output->IteratorEnd() ) { itk::Index cur; mitk::Point3D c = (*it)->Coordinates; input->GetGeometry()->WorldToIndex(c, c); cur[0] = c[0]; cur[1] = c[1]; shortestPath.push_back( cur); it++; } } else { mitk::ContourModel::VertexIterator it = path->IteratorBegin(); while( it != path->IteratorEnd() ) { itk::Index cur; mitk::Point3D c = (*it)->Coordinates; input->GetGeometry()->WorldToIndex(c, c); cur[0] = c[0]; cur[1] = c[1]; shortestPath.push_back( cur); it++; } } /*+++ filter image gradient magnitude +++*/ typedef itk::GradientMagnitudeImageFilter< itk::Image, itk::Image > GradientMagnitudeFilterType; typename GradientMagnitudeFilterType::Pointer gradientFilter = GradientMagnitudeFilterType::New(); gradientFilter->SetInput(inputImage); gradientFilter->Update(); typename itk::Image::Pointer gradientMagnImage = gradientFilter->GetOutput(); //get the path //iterator of path typename std::vector< itk::Index >::iterator pathIterator = shortestPath.begin(); std::map< int, int > histogram; //create histogram within path while(pathIterator != shortestPath.end()) { //count pixel values //use scale factor to avoid mapping gradients between 0.0 and 1.0 to same bin histogram[ static_cast( gradientMagnImage->GetPixel((*pathIterator)) * ImageLiveWireContourModelFilter::CostFunctionType::MAPSCALEFACTOR ) ] += 1; pathIterator++; } double max = 1.0; if( !histogram.empty() ) { std::map< int, int >::iterator itMAX; //get max of histogramm int currentMaxValue = 0; std::map< int, int >::iterator it = histogram.begin(); while( it != histogram.end()) { if((*it).second > currentMaxValue) { itMAX = it; currentMaxValue = (*it).second; } it++; } std::map< int, int >::key_type keyOfMax = itMAX->first; /*+++++++++++++++++++++++++ compute the to max of gaussian summation ++++++++++++++++++++++++*/ std::map< int, int >::iterator end = histogram.end(); std::map< int, int >::iterator last = --(histogram.end()); std::map< int, int >::iterator left2; std::map< int, int >::iterator left1; std::map< int, int >::iterator right1; std::map< int, int >::iterator right2; right1 = itMAX; if(right1 == end || right1 == last ) { right2 = end; } else//( right1 <= last ) { std::map< int, int >::iterator temp = right1; right2 = ++right1;//rght1 + 1 right1 = temp; } if( right1 == histogram.begin() ) { left1 = end; left2 = end; } else if( right1 == (++(histogram.begin())) ) { std::map< int, int >::iterator temp = right1; left1 = --right1;//rght1 - 1 right1 = temp; left2 = end; } else { std::map< int, int >::iterator 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 = ImageLiveWireContourModelFilter::CostFunctionType::Gaussian(keyOfMax, left2->first, left2->second); } if( left1 != end ) { partLeft1 = ImageLiveWireContourModelFilter::CostFunctionType::Gaussian(keyOfMax, left1->first, left1->second); } if( right1 != end ) { partRight1 = ImageLiveWireContourModelFilter::CostFunctionType::Gaussian(keyOfMax, right1->first, right1->second); } if( right2 != end ) { partRight2 = ImageLiveWireContourModelFilter::CostFunctionType::Gaussian(keyOfMax, right2->first, right2->second); } /*----------------------------------------------------------------------------*/ max = (partRight1 + partRight2 + partLeft1 + partLeft2); } this->m_CostFunction->SetDynamicCostMap(histogram); this->m_CostFunction->SetCostMapMaximum(max); } diff --git a/Modules/Segmentation/Algorithms/mitkImageLiveWireContourModelFilter.h b/Modules/Segmentation/Algorithms/mitkImageLiveWireContourModelFilter.h index 74ff4fa4e8..50be8bc02d 100644 --- a/Modules/Segmentation/Algorithms/mitkImageLiveWireContourModelFilter.h +++ b/Modules/Segmentation/Algorithms/mitkImageLiveWireContourModelFilter.h @@ -1,153 +1,159 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #ifndef _mitkImageLiveWireContourModelFilter_h__ #define _mitkImageLiveWireContourModelFilter_h__ #include "mitkCommon.h" #include "SegmentationExports.h" #include "mitkContourModel.h" #include "mitkContourModelSource.h" #include #include #include #include #include namespace mitk { /** \brief Calculates a LiveWire contour between two points in an image. For defining costs between two pixels specific features are extraced from the image and tranformed into a single cost value. \sa ShortestPathCostFunctionLiveWire The filter is able to create dynamic cost tranfer map and thus use on the fly training. \Note On the fly training will only be used for next update. The computation uses the last calculated segment to map cost according to features in the area of the segment. For time resolved purposes use ImageLiveWireContourModelFilter::SetTimestep( unsigned int ) to create the LiveWire contour at a specific timestep. \ingroup ContourModelFilters \ingroup Process */ class Segmentation_EXPORT ImageLiveWireContourModelFilter : public ContourModelSource { public: mitkClassMacro(ImageLiveWireContourModelFilter, ContourModelSource); itkNewMacro(Self); typedef ContourModel OutputType; typedef OutputType::Pointer OutputTypePointer; typedef mitk::Image InputType; typedef itk::Image< float, 2 > FloatImageType; typedef itk::ShortestPathImageFilter< FloatImageType, FloatImageType > ShortestPathImageFilterType; typedef itk::ShortestPathCostFunctionLiveWire< FloatImageType > CostFunctionType; + typedef std::vector< FloatImageType::IndexType > ShortestPathType; - /** \brief start point in worldcoordinates*/ + /** \brief start point in world coordinates*/ itkSetMacro(StartPoint, mitk::Point3D); itkGetMacro(StartPoint, mitk::Point3D); - /** \brief end point in woorldcoordinates*/ + /** \brief end point in woorld coordinates*/ itkSetMacro(EndPoint, mitk::Point3D); itkGetMacro(EndPoint, mitk::Point3D); /** \brief Create dynamic cost tranfer map - use on the fly training. \Note On the fly training will be used for next update only. The computation uses the last calculated segment to map cost according to features in the area of the segment. */ itkSetMacro(UseDynamicCostMap, bool); itkGetMacro(UseDynamicCostMap, bool); virtual void SetInput( const InputType *input); virtual void SetInput( unsigned int idx, const InputType * input); const InputType* GetInput(void); const InputType* GetInput(unsigned int idx); virtual OutputType* GetOutput(); /** \brief Create dynamic cost tranfer map - on the fly training*/ bool CreateDynamicCostMap(mitk::ContourModel* path=NULL); void SetTimestep( unsigned int timestep ) { m_Timestep = timestep; } unsigned int GetTimestep() { return m_Timestep; } protected: ImageLiveWireContourModelFilter(); virtual ~ImageLiveWireContourModelFilter(); void GenerateOutputInformation() {}; void GenerateData(); + void UpdateLiveWire(); + /** \brief start point in worldcoordinates*/ mitk::Point3D m_StartPoint; /** \brief end point in woorldcoordinates*/ mitk::Point3D m_EndPoint; /** \brief Start point in index*/ mitk::Point3D m_StartPointInIndex; /** \brief End point in index*/ mitk::Point3D m_EndPointInIndex; /** \brief The cost function to compute costs between two pixels*/ CostFunctionType::Pointer m_CostFunction; /** \brief Shortest path filter according to cost function m_CostFunction*/ ShortestPathImageFilterType::Pointer m_ShortestPathFilter; /** \brief Flag to use a dynmic cost map or not*/ bool m_UseDynamicCostMap; bool m_ImageModified; unsigned int m_Timestep; template - void ItkProcessImage (itk::Image* inputImage); + void ItkPreProcessImage (itk::Image* inputImage); template void CreateDynamicCostMapByITK(itk::Image* inputImage, mitk::ContourModel* path=NULL); + + FloatImageType::Pointer m_PreProcessedImage; + }; } #endif diff --git a/Modules/Segmentation/Algorithms/mitkImageToLiveWireContourFilter.cpp b/Modules/Segmentation/Algorithms/mitkImageToLiveWireContourFilter.cpp index a9b7a18f0d..9a85d4c4c9 100644 --- a/Modules/Segmentation/Algorithms/mitkImageToLiveWireContourFilter.cpp +++ b/Modules/Segmentation/Algorithms/mitkImageToLiveWireContourFilter.cpp @@ -1,194 +1,194 @@ /*=================================================================== 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 "mitkImageToLiveWireContourFilter.h" #include #include #include mitk::ImageToLiveWireContourFilter::ImageToLiveWireContourFilter() { OutputType::Pointer output = dynamic_cast ( this->MakeOutput( 0 ).GetPointer() ); this->SetNumberOfRequiredInputs(1); - this->SetNumberOfOutputs( 1 ); + this->SetNumberOfIndexedOutputs( 1 ); this->SetNthOutput(0, output.GetPointer()); } mitk::ImageToLiveWireContourFilter::~ImageToLiveWireContourFilter() { } void mitk::ImageToLiveWireContourFilter::SetInput ( const mitk::ImageToLiveWireContourFilter::InputType* input ) { this->SetInput( 0, input ); } void mitk::ImageToLiveWireContourFilter::SetInput ( unsigned int idx, const mitk::ImageToLiveWireContourFilter::InputType* input ) { if ( idx + 1 > this->GetNumberOfInputs() ) { this->SetNumberOfRequiredInputs(idx + 1); } if ( input != static_cast ( this->ProcessObject::GetInput ( idx ) ) ) { this->ProcessObject::SetNthInput ( idx, const_cast ( input ) ); this->Modified(); } } const mitk::ImageToLiveWireContourFilter::InputType* mitk::ImageToLiveWireContourFilter::GetInput( void ) { if (this->GetNumberOfInputs() < 1) return NULL; return static_cast(this->ProcessObject::GetInput(0)); } const mitk::ImageToLiveWireContourFilter::InputType* mitk::ImageToLiveWireContourFilter::GetInput( unsigned int idx ) { if (this->GetNumberOfInputs() < 1) return NULL; return static_cast(this->ProcessObject::GetInput(idx)); } void mitk::ImageToLiveWireContourFilter::GenerateData() { mitk::Image::ConstPointer input = dynamic_cast(this->GetInput()); if(!input) { MITK_ERROR << "No input available."; itkExceptionMacro("mitk::ImageToLiveWireContourFilter: No input available. Please set the input!"); return; } if( input->GetDimension() != 2 ) { MITK_ERROR << "Filter is only working on 2D images."; itkExceptionMacro("mitk::ImageToLiveWireContourFilter: Filter is only working on 2D images.. Please make sure that the input is 2D!"); return; } input->GetGeometry()->WorldToIndex(m_StartPoint, m_StartPointInIndex); input->GetGeometry()->WorldToIndex(m_EndPoint, m_EndPointInIndex); AccessFixedDimensionByItk(input, ItkProcessImage, 2); } template void mitk::ImageToLiveWireContourFilter::ItkProcessImage (itk::Image* inputImage) { //typedef itk::Image< TPixel, VImageDimension > InputImageType; //typedef itk::Image< float, 2 > FloatImageType; //typedef typename itk::ShortestPathImageFilter< InputImageType, InputImageType > ShortestPathImageFilterType; //typedef typename itk::ShortestPathCostFunctionLiveWire< InputImageType > CostFunctionType; //typedef InputImageType::IndexType IndexType; ///* compute the requested region for itk filters */ //typename IndexType startPoint, endPoint; // //startPoint[0] = m_StartPointInIndex[0]; //startPoint[1] = m_StartPointInIndex[1]; //endPoint[0] = m_EndPointInIndex[0]; //endPoint[1] = m_EndPointInIndex[1]; ////minimum value in each direction for startRegion //typename IndexType startRegion; //startRegion[0] = startPoint[0] < endPoint[0] ? startPoint[0] : endPoint[0]; //startRegion[1] = startPoint[1] < endPoint[1] ? startPoint[1] : endPoint[1]; ////maximum value in each direction for size //typename InputImageType::SizeType size; //size[0] = startPoint[0] > endPoint[0] ? startPoint[0] : endPoint[0]; //size[1] = startPoint[1] > endPoint[1] ? startPoint[1] : endPoint[1]; //typename InputImageType::RegionType region; //region.SetSize( size ); //region.SetIndex( startRegion ); ///*---------------------------------------------*/ ///* extracts features from image and calculates costs */ //typename CostFunctionType::Pointer costFunction = CostFunctionType::New(); //costFunction->SetImage(inputImage); //costFunction->SetStartIndex(startPoint); //costFunction->SetEndIndex(endPoint); //costFunction->SetRequestedRegion(region); ///*---------------------------------------------*/ ///* calculate shortest path between start and end point */ //ShortestPathImageFilterType::Pointer shortestPathFilter = ShortestPathImageFilterType::New(); //shortestPathFilter->SetFullNeighborsMode(true); //shortestPathFilter->SetInput(inputImage); //shortestPathFilter->SetMakeOutputImage(true); //shortestPathFilter->SetStoreVectorOrder(false); ////shortestPathFilter->SetActivateTimeOut(true); //shortestPathFilter->SetStartIndex(startPoint); //shortestPathFilter->SetEndIndex(endPoint); //shortestPathFilter->Update(); ///*---------------------------------------------*/ ///* construct contour from path image */ ////get the shortest path as vector //std::vector< itk::Index<3> > shortestPath = shortestPathFilter->GetVectorPath(); ////fill the output contour with controll points from the path //OutputType::Pointer outputContour = this->GetOutput(); //mitk::Image::ConstPointer input = dynamic_cast(this->GetInput()); //std::vector< itk::Index<3> >::iterator pathIterator = shortestPath.begin(); //while(pathIterator != shortestPath.end()) //{ // mitk::Point3D currentPoint; // currentPoint[0] = (*pathIterator)[0]; // currentPoint[1] = (*pathIterator)[1]; // input->GetGeometry(0)->IndexToWorld(currentPoint, currentPoint); // outputContour->AddVertex(currentPoint); // // pathIterator++; //} /*---------------------------------------------*/ } diff --git a/Modules/Segmentation/DataManagement/mitkContourElement.cpp b/Modules/Segmentation/DataManagement/mitkContourElement.cpp index ba11702f46..ed030095e2 100644 --- a/Modules/Segmentation/DataManagement/mitkContourElement.cpp +++ b/Modules/Segmentation/DataManagement/mitkContourElement.cpp @@ -1,386 +1,673 @@ /*=================================================================== 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 mitk::ContourElement::ContourElement() { this->m_Vertices = new VertexListType(); this->m_IsClosed = false; } mitk::ContourElement::ContourElement(const mitk::ContourElement &other) : m_Vertices(other.m_Vertices), m_IsClosed(other.m_IsClosed) { } mitk::ContourElement::~ContourElement() { delete this->m_Vertices; } void mitk::ContourElement::AddVertex(mitk::Point3D &vertex, bool isControlPoint) { this->m_Vertices->push_back(new VertexType(vertex, isControlPoint)); } void mitk::ContourElement::AddVertex(VertexType &vertex) { this->m_Vertices->push_back(&vertex); } void mitk::ContourElement::AddVertexAtFront(mitk::Point3D &vertex, bool isControlPoint) { this->m_Vertices->push_front(new VertexType(vertex, isControlPoint)); } void mitk::ContourElement::AddVertexAtFront(VertexType &vertex) { this->m_Vertices->push_front(&vertex); } void mitk::ContourElement::InsertVertexAtIndex(mitk::Point3D &vertex, bool isControlPoint, int index) { if(index > 0 && this->GetSize() > index) { VertexIterator _where = this->m_Vertices->begin(); _where += index; this->m_Vertices->insert(_where, new VertexType(vertex, isControlPoint)); } } mitk::ContourElement::VertexType* mitk::ContourElement::GetVertexAt(int index) { return this->m_Vertices->at(index); } mitk::ContourElement::VertexType* mitk::ContourElement::GetVertexAt(const mitk::Point3D &point, float eps) { /* current version iterates over the whole deque - should some kind of an octree with spatial query*/ if(eps > 0) { //currently no method with better performance is available return BruteForceGetVertexAt(point, eps); }//if eps < 0 return NULL; } mitk::ContourElement::VertexType* mitk::ContourElement::BruteForceGetVertexAt(const mitk::Point3D &point, float eps) { if(eps > 0) { std::deque< std::pair > nearestlist; ConstVertexIterator it = this->m_Vertices->begin(); ConstVertexIterator end = this->m_Vertices->end(); while(it != end) { mitk::Point3D currentPoint = (*it)->Coordinates; double distance = currentPoint.EuclideanDistanceTo(point); if(distance < eps) { //if list is emtpy, add point to list if(nearestlist.size() < 1) { nearestlist.push_front(std::pair( (*it)->Coordinates.EuclideanDistanceTo(point), (*it) )); } //found an approximate point - check if current is closer then first in nearestlist else if( distance < nearestlist.front().first ) { //found even closer vertex nearestlist.push_front(std::pair( (*it)->Coordinates.EuclideanDistanceTo(point), (*it) )); } }//if distance > eps it++; }//while if(nearestlist.size() > 0) { /*++++++++++++++++++++ return the nearest active point if one was found++++++++++++++++++*/ std::deque< std::pair >::iterator it = nearestlist.begin(); std::deque< std::pair >::iterator end = nearestlist.end(); while(it != end) { if( (*it).second->IsControlPoint ) { return (*it).second; } it++; } /*---------------------------------------------------------------------------------------*/ //return closest point return nearestlist.front().second; } } return NULL; } mitk::ContourElement::VertexType* mitk::ContourElement::OptimizedGetVertexAt(const mitk::Point3D &point, float eps) { if( (eps > 0) && (this->m_Vertices->size()>0) ) { int k = 1; int dim = 3; int nPoints = this->m_Vertices->size(); ANNpointArray pointsArray; ANNpoint queryPoint; ANNidxArray indexArray; ANNdistArray distanceArray; ANNkd_tree* kdTree; queryPoint = annAllocPt(dim); pointsArray = annAllocPts(nPoints, dim); indexArray = new ANNidx[k]; distanceArray = new ANNdist[k]; int i = 0; //fill points array with our control points for(VertexIterator it = this->m_Vertices->begin(); it != this->m_Vertices->end(); it++, i++) { mitk::Point3D cur = (*it)->Coordinates; pointsArray[i][0]= cur[0]; pointsArray[i][1]= cur[1]; pointsArray[i][2]= cur[2]; } //create the kd tree kdTree = new ANNkd_tree(pointsArray,nPoints, dim); //fill mitk::Point3D into ANN query point queryPoint[0] = point[0]; queryPoint[1] = point[1]; queryPoint[2] = point[2]; //k nearest neighbour search kdTree->annkSearch(queryPoint, k, indexArray, distanceArray, eps); VertexType* ret = NULL; try { ret = this->m_Vertices->at(indexArray[0]); } catch(std::out_of_range ex) { //ret stays NULL return ret; } //clean up ANN delete [] indexArray; delete [] distanceArray; delete kdTree; annClose(); return ret; } return NULL; } mitk::ContourElement::VertexListType* mitk::ContourElement::GetVertexList() { return this->m_Vertices; } bool mitk::ContourElement::IsClosed() { return this->m_IsClosed; } bool mitk::ContourElement::IsNearContour(const mitk::Point3D &point, float eps) { ConstVertexIterator it1 = this->m_Vertices->begin(); ConstVertexIterator it2 = this->m_Vertices->begin(); it2 ++; // it2 runs one position ahead ConstVertexIterator end = this->m_Vertices->end(); int counter = 0; for (; it1 != end; it1++, it2++, counter++) { if (it2 == end) it2 = this->m_Vertices->begin(); mitk::Point3D v1 = (*it1)->Coordinates; mitk::Point3D v2 = (*it2)->Coordinates; const float l2 = v1.SquaredEuclideanDistanceTo(v2); mitk::Vector3D p_v1 = point - v1; mitk::Vector3D v2_v1 = v2 - v1; double tc = (p_v1 * v2_v1) / l2; // take into account we have line segments and not (infinite) lines if (tc < 0.0) tc = 0.0; if (tc > 1.0) tc = 1.0; mitk::Point3D crossPoint = v1 + v2_v1 * tc; double distance = point.SquaredEuclideanDistanceTo(crossPoint); if (distance < eps) { return true; } } return false; } void mitk::ContourElement::Close() { this->m_IsClosed = true; } void mitk::ContourElement::Open() { this->m_IsClosed = false; } void mitk::ContourElement::SetIsClosed( bool isClosed) { isClosed ? this->Close() : this->Open(); } +mitk::ContourElement::VertexListType* +mitk::ContourElement::GetControlVertices() +{ + VertexListType* newVertices = new VertexListType(); + VertexIterator it = this->m_Vertices->begin(); + VertexIterator end = this->m_Vertices->end(); + + while(it != end) + { + if((*it)->IsControlPoint) + { + newVertices->push_back((*it)); + } + it++; + } + + return newVertices; +} void mitk::ContourElement::Concatenate(mitk::ContourElement* other) { if( other->GetSize() > 0) { ConstVertexIterator it = other->m_Vertices->begin(); ConstVertexIterator end = other->m_Vertices->end(); //add all vertices of other after last vertex while(it != end) { this->m_Vertices->push_back(*it); it++; } } } bool mitk::ContourElement::RemoveVertex(mitk::ContourElement::VertexType* vertex) { VertexIterator it = this->m_Vertices->begin(); VertexIterator end = this->m_Vertices->end(); //search for vertex and remove it if exists while(it != end) { if((*it) == vertex) { this->m_Vertices->erase(it); return true; } it++; } return false; } bool mitk::ContourElement::RemoveVertexAt(int index) { if( index >= 0 && index < this->m_Vertices->size() ) { this->m_Vertices->erase(this->m_Vertices->begin()+index); return true; } else { return false; } } bool mitk::ContourElement::RemoveVertexAt(mitk::Point3D &point, float eps) { /* current version iterates over the whole deque - should be some kind of an octree with spatial query*/ if(eps > 0){ VertexIterator it = this->m_Vertices->begin(); VertexIterator end = this->m_Vertices->end(); while(it != end) { mitk::Point3D currentPoint = (*it)->Coordinates; if(currentPoint.EuclideanDistanceTo(point) < eps) { //approximate point found //now erase it this->m_Vertices->erase(it); return true; } it++; } } return false; } void mitk::ContourElement::Clear() { this->m_Vertices->clear(); } +//---------------------------------------------------------------------- +void mitk::ContourElement::Interpolate() +{ + VertexListType* newVertices = new VertexListType(); + + int nverts = this->m_Vertices->size(); + for (int i=0; (i+1)DoBezierInterpolation(i, i+1, newVertices); + } + + if ( this->IsClosed() ) + { + this->DoBezierInterpolation(nverts-1, 0, newVertices); + } + + delete this->m_Vertices; + + this->m_Vertices = newVertices; +} +//---------------------------------------------------------------------- +void mitk::ContourElement::RedistributeControlVertices(const VertexType* selected, int period) +{ + int counter = 0; + VertexIterator _where = this->m_Vertices->begin(); + + if (selected != NULL) + { + while (_where != this->m_Vertices->end()) + { + if ((*_where) == selected) + { + break; + } + _where++; + } + } + + VertexIterator _iter = _where; + while (_iter != this->m_Vertices->end()) + { + div_t divresult; + divresult = div (counter,period); + (*_iter)->IsControlPoint = (divresult.rem == 0); + counter++; + _iter++; + } + + _iter = _where; + counter = 0; + while (_iter != this->m_Vertices->begin()) + { + div_t divresult; + divresult = div (counter,period); + (*_iter)->IsControlPoint = (divresult.rem == 0); + counter++; + _iter--; + } +} +//---------------------------------------------------------------------- +void mitk::ContourElement::DoBezierInterpolation( int idx1, int idx2, VertexListType* vertices ) +{ + mitk::Point3D mp1; + mp1 = this->m_Vertices->at(idx1)->Coordinates; + + mitk::Point3D mp2; + mp2 = this->m_Vertices->at(idx2)->Coordinates; + +// this->RemoveIntermediateVerticesAt(idx1); +// this->AddIntermediateVertex(mp1, idx1); + vertices->push_back(new VertexType(mp1, this->m_Vertices->at(idx1)->IsControlPoint)); + + int maxRecursion = 0; + int tmp = 3; + + const int MaximumCurveLineSegments = 100; + const double MaximumCurveError = 0.005; + + while ( 2*tmp < MaximumCurveLineSegments ) + { + tmp *= 2; + maxRecursion++; + } + + // There are four control points with 3 components each, plus one + // value for the recursion depth of this point + double *controlPointsStack = new double[(3*4+1)*(maxRecursion+1)]; + int stackCount = 0; + + double slope1[3]; + double slope2[3]; + + this->GetNthNodeSlope( idx1, slope1 ); + this->GetNthNodeSlope( idx2, slope2 ); + + controlPointsStack[0] = 0; + double *p1 = controlPointsStack+1; + double *p2 = controlPointsStack+4; + double *p3 = controlPointsStack+7; + double *p4 = controlPointsStack+10; + + const VertexType* vertex1 = this->m_Vertices->at(idx1); + + p1[0] = vertex1->Coordinates[0]; + p1[1] = vertex1->Coordinates[1]; + p1[2] = vertex1->Coordinates[2]; + + const VertexType* vertex2 = this->m_Vertices->at(idx2); + + p4[0] = vertex2->Coordinates[0]; + p4[1] = vertex2->Coordinates[1]; + p4[2] = vertex2->Coordinates[2]; + + double distance = sqrt( vtkMath::Distance2BetweenPoints( p1, p4 ) ); + + p2[0] = p1[0] + .333*distance*slope1[0]; + p2[1] = p1[1] + .333*distance*slope1[1]; + p2[2] = p1[2] + .333*distance*slope1[2]; + + p3[0] = p4[0] - .333*distance*slope2[0]; + p3[1] = p4[1] - .333*distance*slope2[1]; + p3[2] = p4[2] - .333*distance*slope2[2]; + + stackCount++; + + while ( stackCount ) + { + //process last point on stack + int recursionLevel = static_cast(controlPointsStack[13*(stackCount-1)]); + + p1 = controlPointsStack + 13*(stackCount-1)+1; + p2 = controlPointsStack + 13*(stackCount-1)+4; + p3 = controlPointsStack + 13*(stackCount-1)+7; + p4 = controlPointsStack + 13*(stackCount-1)+10; + + double totalDist = 0; + totalDist += sqrt(vtkMath::Distance2BetweenPoints(p1,p2)); + totalDist += sqrt(vtkMath::Distance2BetweenPoints(p2,p3)); + totalDist += sqrt(vtkMath::Distance2BetweenPoints(p3,p4)); + + distance = sqrt(vtkMath::Distance2BetweenPoints(p1,p4)); + + if ( recursionLevel >= maxRecursion || distance == 0 || + (totalDist - distance)/distance < MaximumCurveError ) + { + mitk::Point3D mp2; + mp2[0] = p2[0]; mp2[1] = p2[1]; mp2[2] = p2[2]; + //this->InsertVertexAtIndex(mp2, false, idx1); + vertices->push_back(new VertexType(mp2, false)); + + mitk::Point3D mp3; + mp3[0] = p3[0]; mp3[1] = p3[1]; mp3[2] = p3[2]; + //this->InsertVertexAtIndex(mp3, false, idx1); + vertices->push_back(new VertexType(mp3, false)); + + if ( stackCount > 1 ) + { + mitk::Point3D mp4; + mp4[0] = p4[0]; mp4[1] = p4[1]; mp4[2] = p4[2]; + //this->InsertVertexAtIndex(mp4, false, idx1); + vertices->push_back(new VertexType(mp4, false)); + } + stackCount--; + } + else + { + double p12[3], p23[3], p34[3], p123[3], p234[3], p1234[3]; + + this->ComputeMidpoint( p1, p2, p12 ); + this->ComputeMidpoint( p2, p3, p23 ); + this->ComputeMidpoint( p3, p4, p34 ); + this->ComputeMidpoint( p12, p23, p123 ); + this->ComputeMidpoint( p23, p34, p234 ); + this->ComputeMidpoint( p123, p234, p1234 ); + + // add these two points to the stack + controlPointsStack[13*(stackCount-1)] = recursionLevel+1; + controlPointsStack[13*(stackCount)] = recursionLevel+1; + + double *newp1 = controlPointsStack + 13*(stackCount)+1; + double *newp2 = controlPointsStack + 13*(stackCount)+4; + double *newp3 = controlPointsStack + 13*(stackCount)+7; + double *newp4 = controlPointsStack + 13*(stackCount)+10; + + newp1[0] = p1[0]; + newp1[1] = p1[1]; + newp1[2] = p1[2]; + + newp2[0] = p12[0]; + newp2[1] = p12[1]; + newp2[2] = p12[2]; + + newp3[0] = p123[0]; + newp3[1] = p123[1]; + newp3[2] = p123[2]; + + newp4[0] = p1234[0]; + newp4[1] = p1234[1]; + newp4[2] = p1234[2]; + + p1[0] = p1234[0]; + p1[1] = p1234[1]; + p1[2] = p1234[2]; + + p2[0] = p234[0]; + p2[1] = p234[1]; + p2[2] = p234[2]; + + p3[0] = p34[0]; + p3[1] = p34[1]; + p3[2] = p34[2]; + + stackCount++; + } + } + + delete [] controlPointsStack; +} + +//---------------------------------------------------------------------- +int mitk::ContourElement::GetNthNodeSlope( int n, double slope[3]) +{ + if ( n < 0 || + static_cast(n) >= this->m_Vertices->size() ) + { + return 0; + } + + int idx1, idx2; + + if ( n == 0 && !this->IsClosed() ) + { + idx1 = 0; + idx2 = 1; + } + else if ( n == this->m_Vertices->size()-1 && !this->IsClosed() ) + { + idx1 = this->m_Vertices->size()-2; + idx2 = idx1+1; + } + else + { + idx1 = n - 1; + idx2 = n + 1; + + if ( idx1 < 0 ) + { + idx1 += this->m_Vertices->size(); + } + if ( idx2 >= this->m_Vertices->size() ) + { + idx2 -= this->m_Vertices->size(); + } + } + + slope[0] = + this->m_Vertices->at(idx2)->Coordinates[0] - + this->m_Vertices->at(idx1)->Coordinates[0]; + slope[1] = + this->m_Vertices->at(idx2)->Coordinates[1] - + this->m_Vertices->at(idx1)->Coordinates[1]; + slope[2] = + this->m_Vertices->at(idx2)->Coordinates[2] - + this->m_Vertices->at(idx1)->Coordinates[2]; + + vtkMath::Normalize( slope ); + return 1; +} diff --git a/Modules/Segmentation/DataManagement/mitkContourElement.h b/Modules/Segmentation/DataManagement/mitkContourElement.h index a0bb2e9631..50de0fc4fa 100644 --- a/Modules/Segmentation/DataManagement/mitkContourElement.h +++ b/Modules/Segmentation/DataManagement/mitkContourElement.h @@ -1,233 +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 _MITK_ContourElement_H_ #define _MITK_ContourElement_H_ #include "mitkCommon.h" #include "SegmentationExports.h" #include #include #include namespace mitk { /** \brief Represents a contour in 3D space. A ContourElement is consisting of linked vertices implicitely defining the contour. They are stored in a double ended queue making it possible to add vertices at front and end of the contour and to iterate in both directions. To mark a vertex as a special one it can be set as a control point. \Note It is highly not recommend to use this class directly as no secure mechanism is used here. Use mitk::ContourModel instead providing some additional features. */ class Segmentation_EXPORT ContourElement : public itk::LightObject { public: mitkClassMacro(ContourElement, itk::LightObject); itkNewMacro(Self); mitkCloneMacro(Self); /*+++++++++++++++++++++ Data container representing vertices +++++++++++++++++*/ /** \brief Represents a single vertex of contour. */ struct ContourModelVertex { ContourModelVertex(mitk::Point3D &point, bool active=false) : Coordinates(point), IsControlPoint(active) { }; /** \brief Treat point special. */ bool IsControlPoint; /** \brief Coordinates in 3D space. */ mitk::Point3D Coordinates; }; /*+++++++++++++++++++++ END Data container representing vertices ++++++++++++++*/ /*+++++++++++++++ typedefs +++++++++++++++++++++++++++++++*/ typedef ContourModelVertex VertexType; typedef std::deque VertexListType; typedef VertexListType::iterator VertexIterator; typedef VertexListType::const_iterator ConstVertexIterator; /*+++++++++++++++ END typedefs ++++++++++++++++++++++++++++*/ /*++++++++++++++++ inline methods +++++++++++++++++++++++*/ /** \brief Return a const iterator a the front. */ virtual ConstVertexIterator ConstIteratorBegin() { return this->m_Vertices->begin(); } /** \brief Return a const iterator a the end. */ virtual ConstVertexIterator ConstIteratorEnd() { return this->m_Vertices->end(); } /** \brief Return an iterator a the front. */ virtual VertexIterator IteratorBegin() { return this->m_Vertices->begin(); } /** \brief Return an iterator a the end. */ virtual VertexIterator IteratorEnd() { return this->m_Vertices->end(); } /** \brief Returns the number of contained vertices. */ virtual int GetSize() { return this->m_Vertices->size(); } /*++++++++++++++++ END inline methods +++++++++++++++++++++++*/ /** \brief Add a vertex at the end of the contour \param vertex - coordinates in 3D space. \param isControlPoint - is the vertex a special control point.*/ virtual void AddVertex(mitk::Point3D &vertex, bool isControlPoint); /** \brief Add a vertex at the end of the contour \param vertex - a ContourModelVertex. */ virtual void AddVertex(VertexType &vertex); /** \brief Add a vertex at the front of the contour \param vertex - coordinates in 3D space. \param isControlPoint - is the vertex a special control point.*/ virtual void AddVertexAtFront(mitk::Point3D &vertex, bool isControlPoint); /** \brief Add a vertex at the front of the contour \param vertex - a ContourModelVertex. */ virtual void AddVertexAtFront(VertexType &vertex); /** \brief Add a vertex at a given index of the contour \param vertex - coordinates in 3D space. \param isControlPoint - is the vertex a special control point. \param index - the index to be inserted at. */ virtual void InsertVertexAtIndex(mitk::Point3D &vertex, bool isControlPoint, int index); /** \brief Returns the vertex a given index \param index */ virtual VertexType* GetVertexAt(int index); /** \brief Returns the approximate nearest vertex a given posoition in 3D space \param point - query position in 3D space. \param eps - the error bound for search algorithm. */ virtual VertexType* GetVertexAt(const mitk::Point3D &point, float eps); /** \brief Returns the container of the vertices. */ VertexListType* GetVertexList(); /** \brief Returns if the conour is closed or not. */ virtual bool IsClosed(); /** \brief Returns whether a given point is near a contour, according to eps. */ virtual bool IsNearContour(const mitk::Point3D &point, float eps); /** \brief Close the contour. Connect first with last element. */ virtual void Close(); /** \brief Open the contour. Disconnect first and last element. */ virtual void Open(); /** \brief Set the contours IsClosed property. \param isClosed - true = closed; false = open; */ virtual void SetIsClosed(bool isClosed); /** \brief Concatenate the contuor with a another contour. All vertices of the other contour will be add after last vertex. */ void Concatenate(mitk::ContourElement* other); /** \brief Remove the given vertex from the container if exists. \param vertex - the vertex to be removed. */ virtual bool RemoveVertex(VertexType* vertex); /** \brief Remove a vertex at given index within the container if exists. \param index - the index to be removed at. */ virtual bool RemoveVertexAt(int index); /** \brief Remove the approximate nearest vertex at given position in 3D space if one exists. \param point - query point in 3D space. \param eps - error bound for search algorithm. */ virtual bool RemoveVertexAt(mitk::Point3D &point, float eps); /** \brief Clear the storage container. */ virtual void Clear(); /** \brief Returns the approximate nearest vertex a given posoition in 3D space \param point - query position in 3D space. \param eps - the error bound for search algorithm. */ VertexType* BruteForceGetVertexAt(const mitk::Point3D &point, float eps); /** \brief Returns the approximate nearest vertex a given posoition in 3D space \param point - query position in 3D space. \param eps - the error bound for search algorithm. */ VertexType* OptimizedGetVertexAt(const mitk::Point3D &point, float eps); + void Interpolate(); + + VertexListType* GetControlVertices(); + + void RedistributeControlVertices(const VertexType* selected, int period); protected: ContourElement(); ContourElement(const mitk::ContourElement &other); virtual ~ContourElement(); + /** \brief Finds the 4th order bezier curve between given indexes. + Adapted from vtkBezierContourLineInterpolator + \param idx1 - first index + \param idx2 - second index + */ + void DoBezierInterpolation( int idx1, int idx2, VertexListType* vertices ); + + int GetNthNodeSlope( int n, double slope[3]); + + void ComputeMidpoint( double p1[3], double p2[3], double mid[3] ) + { + mid[0] = (p1[0] + p2[0])/2; + mid[1] = (p1[1] + p2[1])/2; + mid[2] = (p1[2] + p2[2])/2; + } + VertexListType* m_Vertices; //double ended queue with vertices bool m_IsClosed; }; } #endif diff --git a/Modules/Segmentation/DataManagement/mitkContourModel.cpp b/Modules/Segmentation/DataManagement/mitkContourModel.cpp index 5d3d7c36f4..15ad60e5df 100644 --- a/Modules/Segmentation/DataManagement/mitkContourModel.cpp +++ b/Modules/Segmentation/DataManagement/mitkContourModel.cpp @@ -1,560 +1,596 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include #include mitk::ContourModel::ContourModel() { //set to initial state this->InitializeEmpty(); } mitk::ContourModel::ContourModel(const mitk::ContourModel &other) : m_ContourSeries(other.m_ContourSeries), m_lineInterpolation(other.m_lineInterpolation) { m_SelectedVertex = NULL; } mitk::ContourModel::~ContourModel() { m_SelectedVertex = NULL; this->m_ContourSeries.clear();//TODO check destruction } void mitk::ContourModel::AddVertex(mitk::Point3D &vertex, int timestep) { if(!this->IsEmptyTimeStep(timestep) ) { this->AddVertex(vertex, false, timestep); } } void mitk::ContourModel::AddVertex(mitk::Point3D &vertex, bool isControlPoint, int timestep) { if(!this->IsEmptyTimeStep(timestep)) { this->m_ContourSeries[timestep]->AddVertex(vertex, isControlPoint); this->InvokeEvent( ContourModelSizeChangeEvent() ); this->Modified(); } } void mitk::ContourModel::AddVertex(VertexType &vertex, int timestep) { if(!this->IsEmptyTimeStep(timestep)) { this->m_ContourSeries[timestep]->AddVertex(vertex); this->InvokeEvent( ContourModelSizeChangeEvent() ); this->Modified(); } } void mitk::ContourModel::AddVertexAtFront(mitk::Point3D &vertex, int timestep) { if(!this->IsEmptyTimeStep(timestep) ) { this->AddVertexAtFront(vertex, false, timestep); } } void mitk::ContourModel::AddVertexAtFront(mitk::Point3D &vertex, bool isControlPoint, int timestep) { if(!this->IsEmptyTimeStep(timestep)) { this->m_ContourSeries[timestep]->AddVertexAtFront(vertex, isControlPoint); this->InvokeEvent( ContourModelSizeChangeEvent() ); this->Modified(); } } void mitk::ContourModel::AddVertexAtFront(VertexType &vertex, int timestep) { if(!this->IsEmptyTimeStep(timestep)) { this->m_ContourSeries[timestep]->AddVertexAtFront(vertex); this->InvokeEvent( ContourModelSizeChangeEvent() ); this->Modified(); } } void mitk::ContourModel::InsertVertexAtIndex(mitk::Point3D &vertex, int index, bool isControlPoint, int timestep) { if(!this->IsEmptyTimeStep(timestep)) { if(index > 0 && this->m_ContourSeries[timestep]->GetSize() > index) { this->m_ContourSeries[timestep]->InsertVertexAtIndex(vertex, isControlPoint, index); this->InvokeEvent( ContourModelSizeChangeEvent() ); this->Modified(); } } } int mitk::ContourModel::GetNumberOfVertices( int timestep) { if(!this->IsEmptyTimeStep(timestep)) { return this->m_ContourSeries[timestep]->GetSize(); } return -1; } const mitk::ContourModel::VertexType* mitk::ContourModel::GetVertexAt(int index, int timestep) const { if(!this->IsEmptyTimeStep(timestep)) { return this->m_ContourSeries[timestep]->GetVertexAt(index); } return NULL; } void mitk::ContourModel::Close( int timestep) { if(!this->IsEmptyTimeStep(timestep)) { this->m_ContourSeries[timestep]->Close(); this->InvokeEvent( ContourModelClosedEvent() ); this->Modified(); } } void mitk::ContourModel::Open( int timestep) { if(!this->IsEmptyTimeStep(timestep)) { this->m_ContourSeries[timestep]->Open(); this->InvokeEvent( ContourModelClosedEvent() ); this->Modified(); } } void mitk::ContourModel::SetIsClosed(bool isClosed, int timestep) { if(!this->IsEmptyTimeStep(timestep)) { this->m_ContourSeries[timestep]->SetIsClosed(isClosed); this->InvokeEvent( ContourModelClosedEvent() ); this->Modified(); } } bool mitk::ContourModel::IsEmptyTimeStep( int t) const { return (t < 0) || (this->m_ContourSeries.size() <= t); } bool mitk::ContourModel::IsNearContour(mitk::Point3D &point, float eps, int timestep) { if(!this->IsEmptyTimeStep(timestep)) { return this->m_ContourSeries[timestep]->IsNearContour(point, eps); } return false; } void mitk::ContourModel::Concatenate(mitk::ContourModel* other, int timestep) { if(!this->IsEmptyTimeStep(timestep)) { if( !this->m_ContourSeries[timestep]->IsClosed() ) { this->m_ContourSeries[timestep]->Concatenate(other->m_ContourSeries[timestep]); this->InvokeEvent( ContourModelSizeChangeEvent() ); this->Modified(); } } } mitk::ContourModel::VertexIterator mitk::ContourModel::IteratorBegin( int timestep) { if(!this->IsEmptyTimeStep(timestep)) { return this->m_ContourSeries[timestep]->IteratorBegin(); } else { mitkThrow() << "No iterator at invalid timestep " << timestep << ". There are only " << this->GetTimeSteps() << " timesteps available."; } } mitk::ContourModel::VertexIterator mitk::ContourModel::IteratorEnd( int timestep) { if(!this->IsEmptyTimeStep(timestep)) { return this->m_ContourSeries[timestep]->IteratorEnd(); } else { mitkThrow() << "No iterator at invalid timestep " << timestep << ". There are only " << this->GetTimeSteps() << " timesteps available."; } } bool mitk::ContourModel::IsClosed( int timestep) { if(!this->IsEmptyTimeStep(timestep)) { return this->m_ContourSeries[timestep]->IsClosed(); } return false; } +bool mitk::ContourModel::SelectVertexAt(mitk::Point3D &point, float eps, int timestep) +{ + if(!this->IsEmptyTimeStep(timestep)) + { + this->m_SelectedVertex = this->m_ContourSeries[timestep]->GetVertexAt(point, eps); + } + return this->m_SelectedVertex != NULL; +} bool mitk::ContourModel::SelectVertexAt(int index, int timestep) { if(!this->IsEmptyTimeStep(timestep)) { return (this->m_SelectedVertex = this->m_ContourSeries[timestep]->GetVertexAt(index)); } return false; } - - - -bool mitk::ContourModel::SelectVertexAt(mitk::Point3D &point, float eps, int timestep) +/* +bool mitk::ContourModel::SetControlVertexAt(mitk::Point3D &point, float eps, int timestep) { if(!this->IsEmptyTimeStep(timestep)) { - this->m_SelectedVertex = this->m_ContourSeries[timestep]->GetVertexAt(point, eps); + VertexType* vertex = this->m_ContourSeries[timestep]->GetVertexAt(point, eps); + if (ve } - return this->m_SelectedVertex != NULL; + return false; } - +bool mitk::ContourModel::SetControlVertexAt(int index, int timestep) +{ + if(!this->IsEmptyTimeStep(timestep)) + { + this->m_ContourSeries[timestep]->GetVertexAt(index)->IsControlPoint = true; + return true; + } + return false; +} +*/ bool mitk::ContourModel::RemoveVertex(VertexType* vertex, int timestep) { if(!this->IsEmptyTimeStep(timestep)) { if(this->m_ContourSeries[timestep]->RemoveVertex(vertex)) { this->Modified(); this->InvokeEvent( ContourModelSizeChangeEvent() ); return true; } } return false; } bool mitk::ContourModel::RemoveVertexAt(int index, int timestep) { if(!this->IsEmptyTimeStep(timestep)) { if(this->m_ContourSeries[timestep]->RemoveVertexAt(index)) { this->Modified(); this->InvokeEvent( ContourModelSizeChangeEvent() ); return true; } } return false; } bool mitk::ContourModel::RemoveVertexAt(mitk::Point3D &point, float eps, int timestep) { if(!this->IsEmptyTimeStep(timestep)) { if(this->m_ContourSeries[timestep]->RemoveVertexAt(point, eps)) { this->Modified(); this->InvokeEvent( ContourModelSizeChangeEvent() ); return true; } } return false; } void mitk::ContourModel::ShiftSelectedVertex(mitk::Vector3D &translate) { if(this->m_SelectedVertex) { this->ShiftVertex(this->m_SelectedVertex,translate); this->Modified(); } } void mitk::ContourModel::ShiftContour(mitk::Vector3D &translate, int timestep) { if(!this->IsEmptyTimeStep(timestep)) { VertexListType* vList = this->m_ContourSeries[timestep]->GetVertexList(); VertexIterator it = vList->begin(); VertexIterator end = vList->end(); //shift all vertices while(it != end) { this->ShiftVertex((*it),translate); it++; } this->Modified(); this->InvokeEvent( ContourModelShiftEvent() ); } } void mitk::ContourModel::ShiftVertex(VertexType* vertex, mitk::Vector3D &vector) { vertex->Coordinates[0] += vector[0]; vertex->Coordinates[1] += vector[1]; vertex->Coordinates[2] += vector[2]; } void mitk::ContourModel::Clear(int timestep) { if(!this->IsEmptyTimeStep(timestep)) { //clear data at timestep this->m_ContourSeries[timestep]->Clear(); this->InitializeEmpty(); this->Modified(); } } void mitk::ContourModel::Expand( int timeSteps ) { int oldSize = this->m_ContourSeries.size(); if( timeSteps > 0 && timeSteps > oldSize ) { Superclass::Expand(timeSteps); //insert contours for each new timestep for( int i = oldSize; i < timeSteps; i++) { m_ContourSeries.push_back(mitk::ContourElement::New()); } this->InvokeEvent( ContourModelExpandTimeBoundsEvent() ); } } void mitk::ContourModel::SetRequestedRegionToLargestPossibleRegion () { //no support for regions } bool mitk::ContourModel::RequestedRegionIsOutsideOfTheBufferedRegion () { //no support for regions return false; } bool mitk::ContourModel::VerifyRequestedRegion () { //no support for regions return true; } const mitk::Geometry3D * mitk::ContourModel::GetUpdatedGeometry (int t) { return Superclass::GetUpdatedGeometry(t); } mitk::Geometry3D* mitk::ContourModel::GetGeometry (int t)const { return Superclass::GetGeometry(t); } void mitk::ContourModel::SetRequestedRegion( const itk::DataObject *data) { //no support for regions } void mitk::ContourModel::Clear() { //clear data and set to initial state again this->ClearData(); this->InitializeEmpty(); this->Modified(); } +void mitk::ContourModel::Interpolate(int timestep) +{ + if(!this->IsEmptyTimeStep(timestep)) + { + this->m_ContourSeries[timestep]->Interpolate(); + this->InvokeEvent( ContourModelClosedEvent() ); + this->Modified(); + } +} + +void mitk::ContourModel::RedistributeControlVertices(int period, int timestep) +{ + if(!this->IsEmptyTimeStep(timestep)) + { + this->m_ContourSeries[timestep]->RedistributeControlVertices(this->GetSelectedVertex(), period); + this->InvokeEvent( ContourModelClosedEvent() ); + this->Modified(); + } +} + void mitk::ContourModel::ClearData() { //call the superclass, this releases the data of BaseData Superclass::ClearData(); //clear out the time resolved contours this->m_ContourSeries.clear(); } void mitk::ContourModel::InitializeEmpty() { //clear data at timesteps this->m_ContourSeries.resize(0); this->m_ContourSeries.push_back(mitk::ContourElement::New()); //set number of timesteps to one this->InitializeTimeSlicedGeometry(1); m_SelectedVertex = NULL; this->m_lineInterpolation = ContourModel::LINEAR; } void mitk::ContourModel::UpdateOutputInformation() { if ( this->GetSource() ) { this->GetSource()->UpdateOutputInformation(); } //update the bounds of the geometry according to the stored vertices float mitkBounds[6]; //calculate the boundingbox at each timestep typedef itk::BoundingBox BoundingBoxType; typedef BoundingBoxType::PointsContainer PointsContainer; int timesteps = this->GetTimeSteps(); //iterate over the timesteps for(int currenTimeStep = 0; currenTimeStep < timesteps; currenTimeStep++) { if( dynamic_cast< mitk::PlaneGeometry* >(this->GetGeometry(currenTimeStep)) ) { //do not update bounds for 2D geometries, as they are unfortunately defined with min bounds 0! return; } else {//we have a 3D geometry -> let's update bounds //only update bounds if the contour was modified if (this->GetMTime() > this->GetGeometry(currenTimeStep)->GetBoundingBox()->GetMTime()) { mitkBounds[0] = 0.0; mitkBounds[1] = 0.0; mitkBounds[2] = 0.0; mitkBounds[3] = 0.0; mitkBounds[4] = 0.0; mitkBounds[5] = 0.0; BoundingBoxType::Pointer boundingBox = BoundingBoxType::New(); PointsContainer::Pointer points = PointsContainer::New(); VertexIterator it = this->IteratorBegin(currenTimeStep); VertexIterator end = this->IteratorEnd(currenTimeStep); //fill the boundingbox with the points while(it != end) { Point3D currentP = (*it)->Coordinates; BoundingBoxType::PointType p; p.CastFrom(currentP); points->InsertElement(points->Size(), p); it++; } //construct the new boundingBox boundingBox->SetPoints(points); boundingBox->ComputeBoundingBox(); BoundingBoxType::BoundsArrayType tmp = boundingBox->GetBounds(); mitkBounds[0] = tmp[0]; mitkBounds[1] = tmp[1]; mitkBounds[2] = tmp[2]; mitkBounds[3] = tmp[3]; mitkBounds[4] = tmp[4]; mitkBounds[5] = tmp[5]; //set boundingBox at current timestep Geometry3D* geometry3d = this->GetGeometry(currenTimeStep); geometry3d->SetBounds(mitkBounds); } } } GetTimeSlicedGeometry()->UpdateInformation(); } void mitk::ContourModel::ExecuteOperation(mitk::Operation* operation) { //not supported yet } diff --git a/Modules/Segmentation/DataManagement/mitkContourModel.h b/Modules/Segmentation/DataManagement/mitkContourModel.h index 5dd5baf3cc..9a94d55df9 100644 --- a/Modules/Segmentation/DataManagement/mitkContourModel.h +++ b/Modules/Segmentation/DataManagement/mitkContourModel.h @@ -1,401 +1,419 @@ /*=================================================================== 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_CONTOURMODEL_H_ #define _MITK_CONTOURMODEL_H_ #include "mitkCommon.h" #include "SegmentationExports.h" #include "mitkBaseData.h" #include namespace mitk { /** \brief ContourModel is a structure of linked vertices defining a contour in 3D space. The vertices are stored in a mitk::ContourElement is stored for each timestep. The contour line segments are implicitly defined by the given linked vertices. By default two control points are are linked by a straight line.It is possible to add vertices at front and end of the contour and to iterate in both directions. Points are specified containing coordinates and additional (data) information, see mitk::ContourElement. For accessing a specific vertex either an index or a position in 3D Space can be used. The vertices are best accessed by using a VertexIterator. Interaction with the contour is thus available without any mitk interactor class using the api of ContourModel. It is possible to shift single vertices also as shifting the whole contour. A contour can be either open like a single curved line segment or closed. A closed contour can for example represent a jordan curve. \section mitkPointSetDisplayOptions The default mappers for this data structure are mitk::ContourModelGLMapper2D and mitk::ContourModelMapper3D. See these classes for display options which can can be set via properties. */ class Segmentation_EXPORT ContourModel : public BaseData { public: mitkClassMacro(ContourModel, BaseData); itkNewMacro(Self); mitkCloneMacro(Self); /*+++++++++++++++ typedefs +++++++++++++++++++++++++++++++*/ typedef mitk::ContourElement::VertexType VertexType; typedef mitk::ContourElement::VertexListType VertexListType; typedef mitk::ContourElement::VertexIterator VertexIterator; typedef std::vector< mitk::ContourElement::Pointer > ContourModelSeries; /*+++++++++++++++ END typedefs ++++++++++++++++++++++++++++*/ /** \brief Possible interpolation of the line segments between control points */ enum LineSegmentInterpolation{ LINEAR, B_SPLINE }; /*++++++++++++++++ inline methods +++++++++++++++++++++++*/ /** \brief Get the current selected vertex. */ VertexType* GetSelectedVertex() { return this->m_SelectedVertex; } /** \brief Deselect vertex. */ void Deselect() { this->m_SelectedVertex = NULL; } - /** \brief Deselect vertex. + /** \brief Set selected vertex as control point */ void SetSelectedVertexAsControlPoint(bool isControlPoint=true) { - if(this->m_SelectedVertex && (this->m_SelectedVertex->IsControlPoint != isControlPoint) ) + if (this->m_SelectedVertex) { m_SelectedVertex->IsControlPoint = isControlPoint; this->Modified(); } } /** \brief Set the interpolation of the line segments between control points. */ void SetLineSegmentInterpolation(LineSegmentInterpolation interpolation) { this->m_lineInterpolation = interpolation; this->Modified(); } /** \brief Get the interpolation of the line segments between control points. */ LineSegmentInterpolation GetLineSegmentInterpolation() { return this->m_lineInterpolation; } /*++++++++++++++++ END inline methods +++++++++++++++++++++++*/ /** \brief Add a vertex to the contour at given timestep. The vertex is added at the end of contour. \pararm vertex - coordinate representation of a control point \pararm timestep - the timestep at which the vertex will be add ( default 0) @Note Adding a vertex to a timestep which exceeds the timebounds of the contour will not be added, the TimeSlicedGeometry will not be expanded. */ void AddVertex(mitk::Point3D &vertex, int timestep=0); /** \brief Add a vertex to the contour at given timestep. The vertex is added at the end of contour. \param vertex - coordinate representation of a control point \param timestep - the timestep at which the vertex will be add ( default 0) @Note Adding a vertex to a timestep which exceeds the timebounds of the contour will not be added, the TimeSlicedGeometry will not be expanded. */ void AddVertex(VertexType &vertex, int timestep=0); /** \brief Add a vertex to the contour. \pararm vertex - coordinate representation of a control point \pararm timestep - the timestep at which the vertex will be add ( default 0) \pararm isControlPoint - specifies the vertex to be handled in a special way (e.g. control points will be rendered). @Note Adding a vertex to a timestep which exceeds the timebounds of the contour will not be added, the TimeSlicedGeometry will not be expanded. */ void AddVertex(mitk::Point3D &vertex, bool isControlPoint, int timestep=0); /** \brief Add a vertex to the contour at given timestep AT THE FRONT of the contour. The vertex is added at the FRONT of contour. \pararm vertex - coordinate representation of a control point \pararm timestep - the timestep at which the vertex will be add ( default 0) @Note Adding a vertex to a timestep which exceeds the timebounds of the contour will not be added, the TimeSlicedGeometry will not be expanded. */ void AddVertexAtFront(mitk::Point3D &vertex, int timestep=0); /** \brief Add a vertex to the contour at given timestep AT THE FRONT of the contour. The vertex is added at the FRONT of contour. \pararm vertex - coordinate representation of a control point \pararm timestep - the timestep at which the vertex will be add ( default 0) @Note Adding a vertex to a timestep which exceeds the timebounds of the contour will not be added, the TimeSlicedGeometry will not be expanded. */ void AddVertexAtFront(VertexType &vertex, int timestep=0); /** \brief Add a vertex to the contour at given timestep AT THE FRONT of the contour. \pararm vertex - coordinate representation of a control point \pararm timestep - the timestep at which the vertex will be add ( default 0) \pararm isControlPoint - specifies the vertex to be handled in a special way (e.g. control points will be rendered). @Note Adding a vertex to a timestep which exceeds the timebounds of the contour will not be added, the TimeSlicedGeometry will not be expanded. */ void AddVertexAtFront(mitk::Point3D &vertex, bool isControlPoint, int timestep=0); /** \brief Insert a vertex at given index. */ void InsertVertexAtIndex(mitk::Point3D &vertex, int index, bool isControlPoint=false, int timestep=0); /** \brief Return if the contour is closed or not. */ bool IsClosed( int timestep=0); /** \brief Concatenate two contours. The starting control point of the other will be added at the end of the contour. */ void Concatenate(mitk::ContourModel* other, int timestep=0); /** \brief Returns a const VertexIterator at the start element of the contour. @throw mitk::Exception if the timestep is invalid. */ VertexIterator IteratorBegin( int timestep=0); /** \brief Close the contour. The last control point will be linked with the first point. */ virtual void Close( int timestep=0); /** \brief Set isClosed to false contour. The link between the last control point the first point will be removed. */ virtual void Open( int timestep=0); /** \brief Set isClosed to given boolean. false - The link between the last control point the first point will be removed. true - The last control point will be linked with the first point. */ virtual void SetIsClosed(bool isClosed, int timestep=0); /** \brief Returns a const VertexIterator at the end element of the contour. @throw mitk::Exception if the timestep is invalid. */ VertexIterator IteratorEnd( int timestep=0); /** \brief Returns the number of vertices at a given timestep. \pararm timestep - default = 0 */ int GetNumberOfVertices( int timestep=0); /** \brief Returns the vertex at the index position within the container. */ virtual const VertexType* GetVertexAt(int index, int timestep=0) const; /** \brief Check if there isn't something at this timestep. */ virtual bool IsEmptyTimeStep( int t) const; /** \brief Check if mouse cursor is near the contour. */ virtual bool IsNearContour(mitk::Point3D &point, float eps, int timestep); /** \brief Mark a vertex at an index in the container as selected. */ bool SelectVertexAt(int index, int timestep=0); + /** \brief Mark a vertex at an index in the container as control point. + */ +// bool SetControlVertexAt(int index, int timestep=0); + /** \brief Mark a vertex at a given position in 3D space. \pararm point - query point in 3D space \pararm eps - radius for nearest neighbour search (error bound). \pararm timestep - search at this timestep @return true = vertex found; false = no vertex found */ bool SelectVertexAt(mitk::Point3D &point, float eps, int timestep=0); +/* + \pararm point - query point in 3D space + \pararm eps - radius for nearest neighbour search (error bound). + \pararm timestep - search at this timestep + + @return true = vertex found; false = no vertex found + */ +// bool SetControlVertexAt(mitk::Point3D &point, float eps, int timestep=0); /** \brief Remove a vertex at given index within the container. @return true = the vertex was successfuly removed; false = wrong index. */ bool RemoveVertexAt(int index, int timestep=0); /** \brief Remove a vertex at given timestep within the container. @return true = the vertex was successfuly removed. */ bool RemoveVertex(VertexType* vertex, int timestep=0); /** \brief Remove a vertex at a query position in 3D space. The vertex to be removed will be search by nearest neighbour search. Note that possibly no vertex at this position and eps is stored inside the contour. @return true = the vertex was successfuly removed; false = no vertex found. */ bool RemoveVertexAt(mitk::Point3D &point, float eps, int timestep=0); /** \brief Shift the currently selected vertex by a translation vector. \pararm translate - the translation vector. */ void ShiftSelectedVertex(mitk::Vector3D &translate); /** \brief Shift the whole contour by a translation vector at given timestep. \pararm translate - the translation vector. \pararm timestep - at this timestep the contour will be shifted. */ void ShiftContour(mitk::Vector3D &translate, int timestep=0); /** \brief Clear the storage container at given timestep. All control points are removed at timestep. */ virtual void Clear(int timestep); /*++++++++++++++++++ method inherit from base data +++++++++++++++++++++++++++*/ /** \brief Inherit from base data - no region support available for contourModel objects. */ virtual void SetRequestedRegionToLargestPossibleRegion (); /** \brief Inherit from base data - no region support available for contourModel objects. */ virtual bool RequestedRegionIsOutsideOfTheBufferedRegion (); /** \brief Inherit from base data - no region support available for contourModel objects. */ virtual bool VerifyRequestedRegion (); /** \brief Get the updated geometry with recomputed bounds. */ virtual const mitk::Geometry3D* GetUpdatedGeometry (int t=0); /** \brief Get the Geometry3D for timestep t. */ virtual mitk::Geometry3D* GetGeometry (int t=0) const; /** \brief Inherit from base data - no region support available for contourModel objects. */ virtual void SetRequestedRegion( const itk::DataObject *data); /** \brief Expand the timebounds of the TimeSlicedGeometry to given number of timesteps. */ virtual void Expand( int timeSteps ); /** \brief Update the OutputInformation of a ContourModel object The BoundingBox of the contour will be updated, if necessary. */ virtual void UpdateOutputInformation(); /** \brief Clear the storage container. The object is set to initial state. All control points are removed and the number of timesteps are set to 1. */ virtual void Clear(); /** \brief overwrite if the Data can be called by an Interactor (StateMachine). */ void ExecuteOperation(Operation* operation); + /** \brief Rebuilds all line segments for timestep according to the active type of interpolation method + \param timestep - at this timestep all lines will be rebuilt. + */ + virtual void Interpolate(int timestep); + + virtual void RedistributeControlVertices(int period, int timestep); protected: ContourModel(); ContourModel(const mitk::ContourModel &other); virtual ~ContourModel(); //inherit from BaseData. called by Clear() virtual void ClearData(); //inherit from BaseData. Initial state of a contour with no vertices and a single timestep. virtual void InitializeEmpty(); //Shift a vertex void ShiftVertex(VertexType* vertex, mitk::Vector3D &vector); //Storage with time resolved support. ContourModelSeries m_ContourSeries; //The currently selected vertex. VertexType* m_SelectedVertex; //The interpolation of the line segment between control points. LineSegmentInterpolation m_lineInterpolation; }; itkEventMacro( ContourModelEvent, itk::AnyEvent ); itkEventMacro( ContourModelShiftEvent, ContourModelEvent ); itkEventMacro( ContourModelSizeChangeEvent, ContourModelEvent ); itkEventMacro( ContourModelAddEvent, ContourModelSizeChangeEvent ); itkEventMacro( ContourModelRemoveEvent, ContourModelSizeChangeEvent ); itkEventMacro( ContourModelExpandTimeBoundsEvent, ContourModelEvent ); itkEventMacro( ContourModelClosedEvent, ContourModelEvent ); } #endif diff --git a/Modules/Segmentation/IO/mitkContourModelReader.cpp b/Modules/Segmentation/IO/mitkContourModelReader.cpp index 057084ce96..de15baaac0 100644 --- a/Modules/Segmentation/IO/mitkContourModelReader.cpp +++ b/Modules/Segmentation/IO/mitkContourModelReader.cpp @@ -1,215 +1,215 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkContourModelReader.h" #include #include #include mitk::ContourModelReader::ContourModelReader() { m_Success = false; } mitk::ContourModelReader::~ContourModelReader() {} void mitk::ContourModelReader::GenerateData() { std::locale::global(std::locale("C")); m_Success = false; if ( m_FileName == "" ) { itkWarningMacro( << "Sorry, filename has not been set!" ); return ; } if ( ! this->CanReadFile( m_FileName.c_str() ) ) { itkWarningMacro( << "Sorry, can't read file " << m_FileName << "!" ); return ; } try{ TiXmlDocument doc(m_FileName.c_str()); bool loadOkay = doc.LoadFile(); if (loadOkay) { TiXmlHandle docHandle( &doc ); //handle geometry information TiXmlElement* currentContourElement = docHandle.FirstChildElement("contourModel").FirstChildElement("head").FirstChildElement("geometryInformation").ToElement(); /*++++ handle n contourModels within data tags ++++*/ unsigned int contourCounter(0); for( TiXmlElement* currentContourElement = docHandle.FirstChildElement("contourModel").FirstChildElement("data").ToElement(); currentContourElement != NULL; currentContourElement = currentContourElement->NextSiblingElement()) { mitk::ContourModel::Pointer newContourModel = mitk::ContourModel::New(); if(currentContourElement->FirstChildElement("timestep") != NULL) { /*++++ handle n timesteps within timestep tags ++++*/ for( TiXmlElement* currentTimeSeries = currentContourElement->FirstChildElement("timestep")->ToElement(); currentTimeSeries != NULL; currentTimeSeries = currentTimeSeries->NextSiblingElement()) { unsigned int currentTimeStep(0); currentTimeStep = atoi(currentTimeSeries->Attribute("n")); this->ReadPoints(newContourModel, currentTimeSeries, currentTimeStep); int isClosed; currentTimeSeries->QueryIntAttribute("isClosed", &isClosed); if( isClosed ) { newContourModel->Close(currentTimeStep); } } /*++++ END handle n timesteps within timestep tags ++++*/ } else { //this should not happen MITK_WARN << "wrong file format!"; //newContourModel = this->ReadPoint(newContourModel, currentContourElement, 0); } this->SetNthOutput( contourCounter, newContourModel ); contourCounter++; } /*++++ END handle n contourModels within data tags ++++*/ } else { MITK_WARN << "XML parser error!"; } }catch(...) { MITK_ERROR << "Cannot read contourModel."; m_Success = false; } m_Success = true; } void mitk::ContourModelReader::ReadPoints(mitk::ContourModel::Pointer newContourModel, TiXmlElement* currentTimeSeries, unsigned int currentTimeStep) { //check if the timesteps in contourModel have to be expanded if(currentTimeStep != newContourModel->GetTimeSteps()) { newContourModel->Expand(currentTimeStep+1); } //read all points within controlPoints tag if(currentTimeSeries->FirstChildElement("controlPoints")->FirstChildElement("point") != NULL) { for( TiXmlElement* currentPoint = currentTimeSeries->FirstChildElement("controlPoints")->FirstChildElement("point")->ToElement(); currentPoint != NULL; currentPoint = currentPoint->NextSiblingElement()) { mitk::PointSpecificationType spec((mitk::PointSpecificationType) 0); double x(0.0); double y(0.0); double z(0.0); x = atof(currentPoint->FirstChildElement("x")->GetText()); y = atof(currentPoint->FirstChildElement("y")->GetText()); z = atof(currentPoint->FirstChildElement("z")->GetText()); int isActivePoint; currentPoint->QueryIntAttribute("isActive", &isActivePoint); mitk::Point3D point; mitk::FillVector3D(point, x, y, z); newContourModel->AddVertex(point, isActivePoint,currentTimeStep); } } else { //nothing to read } } void mitk::ContourModelReader::GenerateOutputInformation() { } int mitk::ContourModelReader::CanReadFile ( const char *name ) { std::ifstream in( name ); bool isGood = in.good(); in.close(); return isGood; } bool mitk::ContourModelReader::CanReadFile(const std::string filename, const std::string filePrefix, const std::string filePattern) { // First check the extension if( filename == "" ) { //MITK_INFO<<"No filename specified."<GetNumberOfOutputs(); - this->SetNumberOfOutputs( num ); + this->SetNumberOfIndexedOutputs( num ); for ( unsigned int i = prevNum; i < num; ++i ) { this->SetNthOutput( i, this->MakeOutput( i ).GetPointer() ); } } bool mitk::ContourModelReader::GetSuccess() const { return m_Success; } diff --git a/Modules/Segmentation/Interactions/mitkContourModelInteractor.cpp b/Modules/Segmentation/Interactions/mitkContourModelInteractor.cpp index e95b9bdf25..bdb7e1a963 100644 --- a/Modules/Segmentation/Interactions/mitkContourModelInteractor.cpp +++ b/Modules/Segmentation/Interactions/mitkContourModelInteractor.cpp @@ -1,283 +1,281 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkContourModelInteractor.h" #include "mitkToolManager.h" #include "mitkBaseRenderer.h" #include "mitkRenderingManager.h" #include #include #include mitk::ContourModelInteractor::ContourModelInteractor(DataNode* dataNode) :Interactor("ContourModelInteractor2", dataNode) { CONNECT_ACTION( AcCHECKPOINT, OnCheckPointClick ); CONNECT_ACTION( AcCHECKOBJECT, OnCheckContourClick ); CONNECT_ACTION( AcDELETEPOINT, OnDeletePoint ); CONNECT_ACTION( AcMOVEPOINT, OnMovePoint ); // CONNECT_ACTION( AcMOVE, OnMoveContour ); CONNECT_ACTION( AcMOVE, OnMove ); CONNECT_ACTION( AcFINISH, OnFinishEditing ); } mitk::ContourModelInteractor::~ContourModelInteractor() { } float mitk::ContourModelInteractor::CanHandleEvent(StateEvent const* stateEvent) const { float returnValue = 0.0; //if it is a key event that can be handled in the current state, then return 0.5 mitk::PositionEvent const *positionEvent = dynamic_cast (stateEvent->GetEvent()); //Key event handling: if (positionEvent == NULL) { //check for delete and escape event if(stateEvent->GetId() == 12 || stateEvent->GetId() == 14) { return 1.0; } //check, if the current state has a transition waiting for that key event. else if (this->GetCurrentState()->GetTransition(stateEvent->GetId())!=NULL) { return 0.5; } else { return 0; } } int timestep = stateEvent->GetEvent()->GetSender()->GetTimeStep(); mitk::ContourModel *contour = dynamic_cast( m_DataNode->GetData() ); if ( contour != NULL ) { mitk::Point3D worldPoint3D = positionEvent->GetWorldPosition(); mitk::Geometry3D *contourGeometry = dynamic_cast< Geometry3D * >( contour->GetGeometry( timestep ) ); if ( contourGeometry ) { //if click is inside bounds the interactor can handle the event best if( contourGeometry->IsInside(worldPoint3D) ) { return 1.0; } return 0.9; } } return returnValue; } void mitk::ContourModelInteractor::DataChanged() { //go to initial state const mitk::Event* nullEvent = new mitk::Event(NULL, Type_User, BS_NoButton, BS_NoButton, Key_none); mitk::StateEvent* newStateEvent = new mitk::StateEvent(AcFINISH, nullEvent); this->HandleEvent( newStateEvent ); delete newStateEvent; delete nullEvent; return; } bool mitk::ContourModelInteractor::OnCheckPointClick( Action* action, const StateEvent* stateEvent) { const PositionEvent* positionEvent = dynamic_cast(stateEvent->GetEvent()); if (!positionEvent) return false; mitk::StateEvent* newStateEvent = NULL; int timestep = stateEvent->GetEvent()->GetSender()->GetTimeStep(); mitk::ContourModel *contour = dynamic_cast( m_DataNode->GetData() ); contour->Deselect(); /* * Check distance to any vertex. * Transition YES if click close to a vertex */ mitk::Point3D click = positionEvent->GetWorldPosition(); if (contour->SelectVertexAt(click, 1.5, timestep) ) { contour->SetSelectedVertexAsControlPoint(); assert( stateEvent->GetEvent()->GetSender()->GetRenderWindow() ); mitk::RenderingManager::GetInstance()->RequestUpdate( stateEvent->GetEvent()->GetSender()->GetRenderWindow() ); newStateEvent = new mitk::StateEvent(EIDYES, stateEvent->GetEvent()); m_lastMousePosition = click; } else { newStateEvent = new mitk::StateEvent(EIDNO, stateEvent->GetEvent()); } this->HandleEvent( newStateEvent ); return true; } bool mitk::ContourModelInteractor::OnCheckContourClick( Action* action, const StateEvent* stateEvent) { const PositionEvent* positionEvent = dynamic_cast(stateEvent->GetEvent()); if (!positionEvent) return false; int timestep = stateEvent->GetEvent()->GetSender()->GetTimeStep(); mitk::Point3D click = positionEvent->GetWorldPosition(); mitk::StateEvent* newStateEvent = NULL; mitk::ContourModel *contour = dynamic_cast( m_DataNode->GetData() ); mitk::Geometry3D *contourGeometry = dynamic_cast< Geometry3D * >( contour->GetGeometry( timestep ) ); if ( contourGeometry->IsInside(click) ) { m_lastMousePosition = click; newStateEvent = new mitk::StateEvent(EIDYES, stateEvent->GetEvent()); } else { newStateEvent = new mitk::StateEvent(EIDNO, stateEvent->GetEvent()); } this->HandleEvent( newStateEvent ); return true; } bool mitk::ContourModelInteractor::OnDeletePoint( Action* action, const StateEvent* stateEvent) { int timestep = stateEvent->GetEvent()->GetSender()->GetTimeStep(); mitk::ContourModel *contour = dynamic_cast( m_DataNode->GetData() ); contour->RemoveVertex(contour->GetSelectedVertex()); return true; } bool mitk::ContourModelInteractor::OnMove( Action* action, const StateEvent* stateEvent) { const PositionEvent* positionEvent = dynamic_cast(stateEvent->GetEvent()); if (!positionEvent) return false; int timestep = positionEvent->GetSender()->GetTimeStep(); mitk::ContourModel *contour = dynamic_cast( m_DataNode->GetData() ); mitk::Point3D currentPosition = positionEvent->GetWorldPosition(); m_DataNode->SetBoolProperty("contour.hovering", contour->IsNearContour(currentPosition, 1.5, timestep) ); assert( positionEvent->GetSender()->GetRenderWindow() ); mitk::RenderingManager::GetInstance()->RequestUpdate( positionEvent->GetSender()->GetRenderWindow() ); return true; } bool mitk::ContourModelInteractor::OnMovePoint( Action* action, const StateEvent* stateEvent) { const PositionEvent* positionEvent = dynamic_cast(stateEvent->GetEvent()); if (!positionEvent) return false; mitk::ContourModel *contour = dynamic_cast( m_DataNode->GetData() ); mitk::Vector3D translation; mitk::Point3D currentPosition = positionEvent->GetWorldPosition(); translation[0] = currentPosition[0] - this->m_lastMousePosition[0]; translation[1] = currentPosition[1] - this->m_lastMousePosition[1]; translation[2] = currentPosition[2] - this->m_lastMousePosition[2]; contour->ShiftSelectedVertex(translation); this->m_lastMousePosition = positionEvent->GetWorldPosition(); assert( positionEvent->GetSender()->GetRenderWindow() ); mitk::RenderingManager::GetInstance()->RequestUpdate( positionEvent->GetSender()->GetRenderWindow() ); return true; } bool mitk::ContourModelInteractor::OnMoveContour( Action* action, const StateEvent* stateEvent) { const PositionEvent* positionEvent = dynamic_cast(stateEvent->GetEvent()); if (!positionEvent) return false; int timestep = positionEvent->GetSender()->GetTimeStep(); mitk::ContourModel *contour = dynamic_cast( m_DataNode->GetData() ); mitk::Vector3D translation; mitk::Point3D currentPosition = positionEvent->GetWorldPosition(); translation[0] = currentPosition[0] - this->m_lastMousePosition[0]; translation[1] = currentPosition[1] - this->m_lastMousePosition[1]; translation[2] = currentPosition[2] - this->m_lastMousePosition[2]; contour->ShiftContour(translation, timestep); this->m_lastMousePosition = positionEvent->GetWorldPosition(); assert( positionEvent->GetSender()->GetRenderWindow() ); mitk::RenderingManager::GetInstance()->RequestUpdate( positionEvent->GetSender()->GetRenderWindow() ); return true; } bool mitk::ContourModelInteractor::OnFinishEditing( Action* action, const StateEvent* stateEvent) { - m_DataNode->SetBoolProperty( "contour.editing", false ); - - // mitk::ContourModel *contour = dynamic_cast( m_DataNode->GetData() ); - // contour->Deselect(); + mitk::ContourModel *contour = dynamic_cast( m_DataNode->GetData() ); + contour->Deselect(); assert( stateEvent->GetEvent()->GetSender()->GetRenderWindow() ); mitk::RenderingManager::GetInstance()->RequestUpdate( stateEvent->GetEvent()->GetSender()->GetRenderWindow() ); return true; } diff --git a/Modules/Segmentation/Interactions/mitkContourModelLiveWireInteractor.cpp b/Modules/Segmentation/Interactions/mitkContourModelLiveWireInteractor.cpp index ec86f43d9e..b1681ce82a 100644 --- a/Modules/Segmentation/Interactions/mitkContourModelLiveWireInteractor.cpp +++ b/Modules/Segmentation/Interactions/mitkContourModelLiveWireInteractor.cpp @@ -1,354 +1,435 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkContourModelLiveWireInteractor.h" #include "mitkToolManager.h" #include "mitkBaseRenderer.h" #include "mitkRenderingManager.h" #include #include #include mitk::ContourModelLiveWireInteractor::ContourModelLiveWireInteractor(DataNode* dataNode) :ContourModelInteractor(dataNode) { m_LiveWireFilter = mitk::ImageLiveWireContourModelFilter::New(); - mitk::ContourModel::Pointer m_ContourLeft = mitk::ContourModel::New(); - mitk::ContourModel::Pointer m_ContourRight = mitk::ContourModel::New(); + //mitk::ContourModel::Pointer m_ContourLeft = mitk::ContourModel::New(); + //mitk::ContourModel::Pointer m_ContourRight = mitk::ContourModel::New(); m_NextActiveVertexDown.Fill(0); m_NextActiveVertexUp.Fill(0); } mitk::ContourModelLiveWireInteractor::~ContourModelLiveWireInteractor() { } bool mitk::ContourModelLiveWireInteractor::OnCheckPointClick( Action* action, const StateEvent* stateEvent) { const PositionEvent* positionEvent = dynamic_cast(stateEvent->GetEvent()); if (!positionEvent) { this->HandleEvent( new mitk::StateEvent(EIDNO, stateEvent->GetEvent()) ); return false; } mitk::StateEvent* newStateEvent = NULL; int timestep = stateEvent->GetEvent()->GetSender()->GetTimeStep(); mitk::ContourModel *contour = dynamic_cast( m_DataNode->GetData() ); - contour->Deselect(); /* * Check distance to any vertex. * Transition YES if click close to a vertex */ mitk::Point3D click = positionEvent->GetWorldPosition(); - if (contour->SelectVertexAt(click, 1.5, timestep) ) { +// contour->RedistributeControlVertices(10,timestep); + contour->SetSelectedVertexAsControlPoint(); m_DataNode->SetBoolProperty( "contour.editing", true ); + this->m_LeftLiveWireContourNode->SetVisibility(true); + this->m_RightLiveWireContourNode->SetVisibility(true); - assert( stateEvent->GetEvent()->GetSender()->GetRenderWindow() ); - mitk::RenderingManager::GetInstance()->RequestUpdate( stateEvent->GetEvent()->GetSender()->GetRenderWindow() ); newStateEvent = new mitk::StateEvent(EIDYES, stateEvent->GetEvent()); m_lastMousePosition = click; - - mitk::ContourModel *contour = dynamic_cast( m_DataNode->GetData() ); - - // m_ContourLeft = mitk::ContourModel::New(); //get coordinates of next active vertex downwards from selected vertex int downIndex = this->SplitContourFromSelectedVertex( contour, m_ContourLeft, false, timestep); mitk::ContourModel::VertexIterator itDown = contour->IteratorBegin() + downIndex; m_NextActiveVertexDown = (*itDown)->Coordinates; - - // m_ContourRight = mitk::ContourModel::New(); //get coordinates of next active vertex upwards from selected vertex int upIndex = this->SplitContourFromSelectedVertex( contour, m_ContourRight, true, timestep); mitk::ContourModel::VertexIterator itUp = contour->IteratorBegin() + upIndex; m_NextActiveVertexUp = (*itUp)->Coordinates; - } else { m_DataNode->SetBoolProperty( "contour.editing", false ); + this->m_LeftLiveWireContourNode->SetVisibility(false); + this->m_RightLiveWireContourNode->SetVisibility(false); newStateEvent = new mitk::StateEvent(EIDNO, stateEvent->GetEvent()); } this->HandleEvent( newStateEvent ); assert( positionEvent->GetSender()->GetRenderWindow() ); mitk::RenderingManager::GetInstance()->RequestUpdate( positionEvent->GetSender()->GetRenderWindow() ); return true; } - - bool mitk::ContourModelLiveWireInteractor::OnDeletePoint( Action* action, const StateEvent* stateEvent) { int timestep = stateEvent->GetEvent()->GetSender()->GetTimeStep(); mitk::ContourModel *contour = dynamic_cast( m_DataNode->GetData() ); if(contour->GetSelectedVertex()) { mitk::ContourModel::Pointer newContour = mitk::ContourModel::New(); newContour->Expand(contour->GetTimeSteps()); - newContour->Concatenate( m_ContourLeft, timestep ); + newContour->Concatenate( m_ContourLeft, timestep ); //recompute contour between neighbored two active control points this->m_LiveWireFilter->SetStartPoint( m_NextActiveVertexDown ); this->m_LiveWireFilter->SetEndPoint( m_NextActiveVertexUp ); this->m_LiveWireFilter->Update(); - mitk::ContourModel::Pointer liveWireContour = mitk::ContourModel::New(); - liveWireContour = this->m_LiveWireFilter->GetOutput(); + mitk::ContourModel *liveWireContour = this->m_LiveWireFilter->GetOutput(); + + if (!liveWireContour) + { + MITK_WARN << "could not retrieve liveWireContour"; + return false; + } + + if (liveWireContour->GetNumberOfVertices() < 3) + { + MITK_WARN << "liveWireContour has less than 3 points"; + return false; + } + liveWireContour->RemoveVertexAt( 0, timestep); + liveWireContour->RemoveVertexAt( liveWireContour->GetNumberOfVertices(timestep) - 1, timestep); //insert new liveWire computed points newContour->Concatenate( liveWireContour, timestep ); newContour->Concatenate( m_ContourRight, timestep ); newContour->SetIsClosed(contour->IsClosed(timestep), timestep); m_DataNode->SetData(newContour); - assert( stateEvent->GetEvent()->GetSender()->GetRenderWindow() ); mitk::RenderingManager::GetInstance()->RequestUpdate( stateEvent->GetEvent()->GetSender()->GetRenderWindow() ); } return true; } - - - bool mitk::ContourModelLiveWireInteractor::OnMovePoint( Action* action, const StateEvent* stateEvent) { const PositionEvent* positionEvent = dynamic_cast(stateEvent->GetEvent()); if (!positionEvent) return false; int timestep = stateEvent->GetEvent()->GetSender()->GetTimeStep(); + mitk::Point3D currentPosition = positionEvent->GetWorldPosition(); mitk::ContourModel *contour = dynamic_cast( m_DataNode->GetData() ); +/* + if (m_ContourLeft->GetNumberOfVertices() < 3) + { + MITK_WARN << "contour left has less than 3 points"; + return false; + } + + if (m_ContourRight->GetNumberOfVertices() < 3) + { + MITK_WARN << "contour right has less than 3 points"; + return false; + } +*/ mitk::ContourModel::Pointer newContour = mitk::ContourModel::New(); newContour->Expand(contour->GetTimeSteps()); - /*++++++++++++++ concatenate LEFT ++++++++++++++++++++++++++++++*/ - newContour->Concatenate( m_ContourLeft, timestep ); + // concatenate left original contour + newContour->Concatenate( this->m_ContourLeft, timestep ); - - mitk::Point3D currentPosition = positionEvent->GetWorldPosition(); - - - /*+++++++++++++++ start computation ++++++++++++++++++++*/ //recompute contour between previous active vertex and selected vertex this->m_LiveWireFilter->SetStartPoint( m_NextActiveVertexDown ); this->m_LiveWireFilter->SetEndPoint( currentPosition ); this->m_LiveWireFilter->Update(); - mitk::ContourModel::Pointer liveWireContour = mitk::ContourModel::New(); - liveWireContour = this->m_LiveWireFilter->GetOutput(); + mitk::ContourModel::Pointer leftLiveWire = this->m_LiveWireFilter->GetOutput(); + if (!leftLiveWire) + { + MITK_WARN << "could not retrieve leftLiveWire"; + return false; + } + + if (leftLiveWire->GetNumberOfVertices() < 3) + { + MITK_WARN << "leftLiveWire has less than 3 points"; + leftLiveWire->Clear(); + return false; + } //remove point at current position because it is included in next livewire segment too. - liveWireContour->RemoveVertexAt( liveWireContour->GetNumberOfVertices(timestep) - 1, timestep); + //leftLiveWire->RemoveVertexAt( leftLiveWire->GetNumberOfVertices(timestep) - 1, timestep); + leftLiveWire->RemoveVertexAt( 0, timestep); - /*++++++++++++++ concatenate LIVEWIRE ++++++++++++++++++++++++++++++*/ - //insert new liveWire computed points - newContour->Concatenate( liveWireContour, timestep ); + newContour->Deselect(); + leftLiveWire->SelectVertexAt(leftLiveWire->GetNumberOfVertices(timestep) - 1, timestep); + leftLiveWire->SetSelectedVertexAsControlPoint(true); + + m_LeftLiveWireContourNode->SetData(leftLiveWire); + + //m_LiveWireContourLeft->Interpolate(timestep); + + // concatenate left live wire + newContour->Concatenate( leftLiveWire, timestep ); //recompute contour between selected vertex and next active vertex this->m_LiveWireFilter->SetStartPoint( currentPosition ); this->m_LiveWireFilter->SetEndPoint( m_NextActiveVertexUp ); this->m_LiveWireFilter->Update(); - liveWireContour = this->m_LiveWireFilter->GetOutput(); + mitk::ContourModel::Pointer rightLiveWire = this->m_LiveWireFilter->GetOutput(); + if (!rightLiveWire) + { + MITK_WARN << "could not retrieve leftLiveWire"; + return false; + } + + if (rightLiveWire->GetNumberOfVertices() < 3) + { + MITK_WARN << "rightLiveWire has less than 3 points"; + rightLiveWire->Clear(); + return false; + } - //make point at mouse position active again, so it is drawn - const_cast( liveWireContour->GetVertexAt(0, timestep) )->IsControlPoint = true; + rightLiveWire->RemoveVertexAt( 0, timestep); + //rightLiveWire->RemoveVertexAt( rightLiveWire->GetNumberOfVertices(timestep) - 1, timestep); - /*++++++++++++++ concatenate RIGHT ++++++++++++++++++++++++++++++*/ - //insert new liveWire computed points - newContour->Concatenate( liveWireContour, timestep ); - /*------------------------------------------------*/ + m_RightLiveWireContourNode->SetData(rightLiveWire); + //m_LiveWireContourRight->Interpolate(timestep); + // concatenate right live wire + newContour->Concatenate( rightLiveWire, timestep ); + + //make point at mouse position active again, so it is drawn + const_cast( newContour->GetVertexAt(0, timestep) )->IsControlPoint = true; + // concatenate right original contour +// mitk::ContourModel *contourRight = dynamic_cast( this->m_ContourRight->GetData() ); newContour->Concatenate( m_ContourRight, timestep ); newContour->SetIsClosed(contour->IsClosed(timestep), timestep); - newContour->SelectVertexAt(currentPosition,1.5,timestep); - //newContour->Deselect();//just to make sure +// newContour->SelectVertexAt(currentPosition,1.5,timestep); + + //newContour->RedistributeControlVertices( 10, timestep ); + + //newContour->Interpolate(timestep); + m_DataNode->SetData(newContour); this->m_lastMousePosition = positionEvent->GetWorldPosition(); assert( positionEvent->GetSender()->GetRenderWindow() ); mitk::RenderingManager::GetInstance()->RequestUpdate( positionEvent->GetSender()->GetRenderWindow() ); return true; } -int mitk::ContourModelLiveWireInteractor::SplitContourFromSelectedVertex(mitk::ContourModel* sourceContour, mitk::ContourModel* destinationContour, bool fromSelectedUpwards, int timestep) +int mitk::ContourModelLiveWireInteractor::SplitContourFromSelectedVertex(mitk::ContourModel* sourceContour, + mitk::ContourModel* destinationContour, + bool fromSelectedUpwards, int timestep) { mitk::ContourModel::VertexIterator end =sourceContour->IteratorEnd(); mitk::ContourModel::VertexIterator begin =sourceContour->IteratorBegin(); //search next active control point to left and rigth and set as start and end point for filter mitk::ContourModel::VertexIterator itSelected = sourceContour->IteratorBegin(); //move iterator to position while((*itSelected) != sourceContour->GetSelectedVertex()) { itSelected++; } //CASE search upwards for next control point if(fromSelectedUpwards) { mitk::ContourModel::VertexIterator itUp = itSelected; if(itUp != end) { - itUp++;//step once up otherwise the the loop breaks immediately + itUp++;//step once up otherwise the loop breaks immediately } while( itUp != end && !((*itUp)->IsControlPoint)){ itUp++; } mitk::ContourModel::VertexIterator it = itUp; if(itSelected != begin) { //copy the rest of the original contour while(it != end) { destinationContour->AddVertex( (*it)->Coordinates, (*it)->IsControlPoint, timestep); it++; } } //else do not copy the contour //return the offset of iterator at one before next-vertex-upwards if(itUp != begin) { return std::distance( begin, itUp) - 1; } else { return std::distance( begin, itUp); } } else //CASE search downwards for next control point { mitk::ContourModel::VertexIterator itDown = itSelected; mitk::ContourModel::VertexIterator it = sourceContour->IteratorBegin(); if( itSelected != begin ) { if(itDown != begin) { itDown--;//step once down otherwise the the loop breaks immediately } while( itDown != begin && !((*itDown)->IsControlPoint)){ itDown--; } if(it != end)//if not empty { //always add the first vertex destinationContour->AddVertex( (*it)->Coordinates, (*it)->IsControlPoint, timestep); it++; } //copy from begin to itDown while(it <= itDown) { destinationContour->AddVertex( (*it)->Coordinates, (*it)->IsControlPoint, timestep); it++; } } else { //if selected vertex is the first element search from end of contour downwards itDown = end; itDown--; while(!((*itDown)->IsControlPoint)){itDown--;} //move one forward as we don't want the first control point it++; //move iterator to second control point while( (it!=end) && !((*it)->IsControlPoint) ){it++;} //copy from begin to itDown while(it <= itDown) { //copy the contour from second control point to itDown destinationContour->AddVertex( (*it)->Coordinates, (*it)->IsControlPoint, timestep); it++; } } //add vertex at itDown - it's not considered during while loop if( it != begin && it != end) { //destinationContour->AddVertex( (*it)->Coordinates, (*it)->IsControlPoint, timestep); } //return the offset of iterator at one after next-vertex-downwards if( itDown != end) { return std::distance( begin, itDown);// + 1;//index of next vertex } else { return std::distance( begin, itDown) - 1; } } } + +void mitk::ContourModelLiveWireInteractor::CorrectIntersections(mitk::ContourModel* cprev, mitk::ContourModel* cnext) +{ + +} + +bool mitk::ContourModelLiveWireInteractor::OnFinishEditing( Action* action, const StateEvent* stateEvent) +{ + m_DataNode->SetBoolProperty( "contour.editing", false ); + + int timestep = stateEvent->GetEvent()->GetSender()->GetTimeStep(); + + mitk::ContourModel *leftLiveWire = dynamic_cast( this->m_LeftLiveWireContourNode->GetData() ); + leftLiveWire->Clear(timestep); + + mitk::ContourModel *rightLiveWire = dynamic_cast( this->m_RightLiveWireContourNode->GetData() ); + rightLiveWire->Clear(timestep); + + this->m_LeftLiveWireContourNode->SetVisibility(false); + this->m_RightLiveWireContourNode->SetVisibility(false); + // mitk::ContourModel *contour = dynamic_cast( m_DataNode->GetData() ); + // contour->Deselect(); + + assert( stateEvent->GetEvent()->GetSender()->GetRenderWindow() ); + mitk::RenderingManager::GetInstance()->RequestUpdate( stateEvent->GetEvent()->GetSender()->GetRenderWindow() ); + + return true; +} diff --git a/Modules/Segmentation/Interactions/mitkContourModelLiveWireInteractor.h b/Modules/Segmentation/Interactions/mitkContourModelLiveWireInteractor.h index ae44899f95..087a4f2fa6 100644 --- a/Modules/Segmentation/Interactions/mitkContourModelLiveWireInteractor.h +++ b/Modules/Segmentation/Interactions/mitkContourModelLiveWireInteractor.h @@ -1,89 +1,103 @@ /*=================================================================== 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 mitkContourModelLiveWireInteractor_h_Included #define mitkContourModelLiveWireInteractor_h_Included #include "mitkCommon.h" #include "SegmentationExports.h" #include "mitkContourModelInteractor.h" -#include -#include #include namespace mitk { /** \brief \sa Interactor \sa ContourModelInteractor \ingroup Interaction \warning Make sure the working image is properly set, otherwise the algorithm for computing livewire contour segments will not work! */ class Segmentation_EXPORT ContourModelLiveWireInteractor : public ContourModelInteractor { public: mitkClassMacro(ContourModelLiveWireInteractor, ContourModelInteractor); mitkNewMacro1Param(Self, DataNode*); + virtual void SetLeftLiveWireContourModelNode (mitk::DataNode* _arg) + { + this->m_LeftLiveWireContourNode = _arg; + this->Modified(); + } + + virtual void SetRightLiveWireContourModelNode (mitk::DataNode* _arg) + { + this->m_RightLiveWireContourNode = _arg; + this->Modified(); + } virtual void SetWorkingImage (mitk::Image* _arg) { if (this->m_WorkingImage != _arg) { this->m_WorkingImage = _arg; this->m_LiveWireFilter->SetInput(this->m_WorkingImage); this->Modified(); } } protected: ContourModelLiveWireInteractor(DataNode* dataNode); virtual ~ContourModelLiveWireInteractor(); virtual bool OnDeletePoint(Action*, const StateEvent*); virtual bool OnMovePoint(Action*, const StateEvent*); virtual bool OnCheckPointClick( Action* action, const StateEvent* stateEvent); + virtual bool OnFinishEditing( Action* action, const StateEvent* stateEvent); int SplitContourFromSelectedVertex(mitk::ContourModel* sourceContour, mitk::ContourModel* destinationContour, bool fromSelectedUpwards, int timestep); + void CorrectIntersections(mitk::ContourModel* cprev, mitk::ContourModel* cnext); + mitk::ImageLiveWireContourModelFilter::Pointer m_LiveWireFilter; mitk::Image::Pointer m_WorkingImage; mitk::Point3D m_NextActiveVertexDown; mitk::Point3D m_NextActiveVertexUp; + mitk::DataNode::Pointer m_LeftLiveWireContourNode; + mitk::DataNode::Pointer m_RightLiveWireContourNode; mitk::ContourModel::Pointer m_ContourLeft; mitk::ContourModel::Pointer m_ContourRight; }; } // namespace #endif diff --git a/Modules/Segmentation/Interactions/mitkLiveWireTool2D.cpp b/Modules/Segmentation/Interactions/mitkLiveWireTool2D.cpp index 20082f9f8e..7fa3017e35 100644 --- a/Modules/Segmentation/Interactions/mitkLiveWireTool2D.cpp +++ b/Modules/Segmentation/Interactions/mitkLiveWireTool2D.cpp @@ -1,653 +1,685 @@ /*=================================================================== 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 "mitkLiveWireTool2D.h" #include "mitkToolManager.h" #include "mitkBaseRenderer.h" #include "mitkRenderingManager.h" #include "mitkLiveWireTool2D.xpm" #include #include #include #include "mitkContourUtils.h" #include "mitkContour.h" #include namespace mitk { MITK_TOOL_MACRO(Segmentation_EXPORT, LiveWireTool2D, "LiveWire tool"); } mitk::LiveWireTool2D::LiveWireTool2D() :SegTool2D("LiveWireTool") { + /* m_Contour = mitk::ContourModel::New(); m_ContourModelNode = mitk::DataNode::New(); m_ContourModelNode->SetData( m_Contour ); m_ContourModelNode->SetProperty("name", StringProperty::New("working contour node")); m_ContourModelNode->SetProperty("visible", BoolProperty::New(true)); m_ContourModelNode->AddProperty( "contour.color", ColorProperty::New(0.9, 1.0, 0.1), NULL, true ); m_ContourModelNode->AddProperty( "selectedcolor", ColorProperty::New(1.0, 0.0, 0.1), NULL, true ); m_LiveWireContour = mitk::ContourModel::New(); m_LiveWireContourNode = mitk::DataNode::New(); //m_LiveWireContourNode->SetData( m_LiveWireContour ); m_LiveWireContourNode->SetProperty("name", StringProperty::New("active livewire node")); m_LiveWireContourNode->SetProperty("visible", BoolProperty::New(true)); m_LiveWireContourNode->AddProperty( "contour.color", ColorProperty::New(0.1, 1.0, 0.1), NULL, true ); m_LiveWireContourNode->AddProperty( "selectedcolor", ColorProperty::New(0.5, 0.5, 0.1), NULL, true ); - +*/ m_LiveWireFilter = mitk::ImageLiveWireContourModelFilter::New(); // great magic numbers CONNECT_ACTION( AcINITNEWOBJECT, OnInitLiveWire ); CONNECT_ACTION( AcADDPOINT, OnAddPoint ); CONNECT_ACTION( AcMOVE, OnMouseMoveNoDynamicCosts ); CONNECT_ACTION( AcCHECKPOINT, OnCheckPoint ); CONNECT_ACTION( AcFINISH, OnFinish ); CONNECT_ACTION( AcDELETEPOINT, OnLastSegmentDelete ); CONNECT_ACTION( AcADDLINE, OnMouseMoved ); } mitk::LiveWireTool2D::~LiveWireTool2D() { m_Contours.clear(); } float mitk::LiveWireTool2D::CanHandleEvent( StateEvent const *stateEvent) const { mitk::PositionEvent const *positionEvent = dynamic_cast (stateEvent->GetEvent()); //Key event handling: if (positionEvent == NULL) { //check for delete and escape event if(stateEvent->GetId() == 12 || stateEvent->GetId() == 14) { return 1.0; } //check, if the current state has a transition waiting for that key event. else if (this->GetCurrentState()->GetTransition(stateEvent->GetId())!=NULL) { return 0.5; } else { return 0.0; } } else { if ( positionEvent->GetSender()->GetMapperID() != BaseRenderer::Standard2D ) return 0.0; // we don't want anything but 2D return 1.0; } } const char** mitk::LiveWireTool2D::GetXPM() const { return mitkLiveWireTool2D_xpm; } const char* mitk::LiveWireTool2D::GetName() const { return "LiveWire"; } void mitk::LiveWireTool2D::Activated() { Superclass::Activated(); } void mitk::LiveWireTool2D::Deactivated() { this->FinishTool(); DataNode* workingNode( m_ToolManager->GetWorkingData(0) ); if ( !workingNode ) return; Image* workingImage = dynamic_cast(workingNode->GetData()); if ( !workingImage ) return; ContourUtils::Pointer contourUtils = mitk::ContourUtils::New(); /*+++++++++++++++++++++++ for all contours in list (currently created by tool) ++++++++++++++++++++++++++++++++++++*/ std::vector< std::pair >::iterator it = m_Contours.begin(); while(it != m_Contours.end() ) { //++++++++++if node contains data if( it->first->GetData() ) { //+++++++++++++++if this is a contourModel mitk::ContourModel* contourModel = dynamic_cast(it->first->GetData()); if( contourModel ) { //++++++++++++++++++++++ for each timestep of this contourModel for( int currentTimestep = 0; currentTimestep < contourModel->GetTimeSlicedGeometry()->GetTimeSteps(); currentTimestep++) { //get the segmentation image slice at current timestep mitk::Image::Pointer workingSlice = this->GetAffectedImageSliceAs2DImage(it->second, workingImage, currentTimestep); /*++++++++++++++++++++++ transfer to plain old contour to use contour util functionality +++++++++++++++++++++++*/ mitk::Contour::Pointer plainOldContour = mitk::Contour::New(); mitk::ContourModel::VertexIterator iter = contourModel->IteratorBegin(currentTimestep); while(iter != contourModel->IteratorEnd(currentTimestep) ) { plainOldContour->AddVertex( (*iter)->Coordinates ); iter++; } /*-------------------------------------------------------------------------------*/ mitk::Contour::Pointer projectedContour = contourUtils->ProjectContourTo2DSlice(workingSlice, plainOldContour, true, false); contourUtils->FillContourInSlice(projectedContour, workingSlice, 1.0); //write back to image volume this->WriteBackSegmentationResult(it->second, workingSlice, currentTimestep); } //remove contour node from datastorage m_ToolManager->GetDataStorage()->Remove( it->first ); } } ++it; } m_Contours.clear(); + m_ToolManager->GetDataStorage()->Remove( m_LeftLiveWireContourNode ); + m_ToolManager->GetDataStorage()->Remove( m_RightLiveWireContourNode ); + + m_LeftLiveWireContourNode = NULL; + m_LeftLiveWireContour = NULL; + + m_RightLiveWireContourNode = NULL; + m_RightLiveWireContour = NULL; + Superclass::Deactivated(); } bool mitk::LiveWireTool2D::OnInitLiveWire (Action* action, const StateEvent* stateEvent) { + MITK_INFO << "started"; + const PositionEvent* positionEvent = dynamic_cast(stateEvent->GetEvent()); if (!positionEvent) return false; m_LastEventSender = positionEvent->GetSender(); m_LastEventSlice = m_LastEventSender->GetSlice(); if ( Superclass::CanHandleEvent(stateEvent) < 1.0 ) return false; - 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->AddProperty( "contour.color", ColorProperty::New(0.9, 1.0, 0.1), NULL, true ); + m_ContourModelNode->AddProperty( "contour.points.color", ColorProperty::New(1.0, 0.0, 0.1), NULL, true ); + m_ContourModelNode->AddProperty( "contour.points.show", BoolProperty::New(false), NULL, true ); + m_ContourModelNode->AddProperty( "contour.controlpoints.show", BoolProperty::New(true), NULL, true ); - m_ToolManager->GetDataStorage()->Add( m_ContourModelNode ); + 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->AddProperty( "contour.color", ColorProperty::New(0.1, 1.0, 0.1), NULL, true ); + m_LiveWireContourNode->AddProperty( "contour.points.color", ColorProperty::New(0.5, 0.5, 0.1), NULL, true ); + + m_LeftLiveWireContour = mitk::ContourModel::New(); + m_LeftLiveWireContour->Expand(timestep+1); + m_LeftLiveWireContourNode = mitk::DataNode::New(); + m_LeftLiveWireContourNode->SetData( m_LeftLiveWireContour ); + m_LeftLiveWireContourNode->SetName("left node"); + m_LeftLiveWireContourNode->AddProperty( "contour.color", ColorProperty::New(0.0, 0.0, 1.0), NULL, true ); + m_LeftLiveWireContourNode->AddProperty( "contour.points.color", ColorProperty::New(0.0, 0.0, 1.0), NULL, true ); + m_LeftLiveWireContourNode->AddProperty( "contour.points.show", BoolProperty::New(true), NULL, true ); + m_LeftLiveWireContourNode->AddProperty( "contour.controlpoints.show", BoolProperty::New(true), NULL, true ); + + m_RightLiveWireContour = mitk::ContourModel::New(); + m_RightLiveWireContour->Expand(timestep+1); + m_RightLiveWireContourNode = mitk::DataNode::New(); + m_RightLiveWireContourNode->SetData( m_RightLiveWireContour ); + m_RightLiveWireContourNode->SetName("right node"); + m_RightLiveWireContourNode->AddProperty( "contour.color", ColorProperty::New(1.0, 0.0, 0.0), NULL, true ); + m_RightLiveWireContourNode->AddProperty( "contour.points.color", ColorProperty::New(1.0, 0.0, 0.0), NULL, true ); + m_RightLiveWireContourNode->AddProperty( "contour.points.show", BoolProperty::New(true), NULL, true ); + m_RightLiveWireContourNode->AddProperty( "contour.controlpoints.show", BoolProperty::New(true), NULL, true ); + m_ToolManager->GetDataStorage()->Add( m_ContourModelNode ); m_ToolManager->GetDataStorage()->Add( m_LiveWireContourNode ); + m_ToolManager->GetDataStorage()->Add( m_RightLiveWireContourNode ); + m_ToolManager->GetDataStorage()->Add( m_LeftLiveWireContourNode ); //set current slice as input for ImageToLiveWireContourFilter m_WorkingSlice = this->GetAffectedReferenceSlice(positionEvent); m_LiveWireFilter->SetInput(m_WorkingSlice); //map click to pixel coordinates mitk::Point3D click = const_cast(positionEvent->GetWorldPosition()); 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); m_CreateAndUseDynamicCosts = true; //render assert( positionEvent->GetSender()->GetRenderWindow() ); mitk::RenderingManager::GetInstance()->RequestUpdate( positionEvent->GetSender()->GetRenderWindow() ); + MITK_INFO << "3"; + return true; } bool mitk::LiveWireTool2D::OnAddPoint (Action* action, const StateEvent* stateEvent) { //complete LiveWire interaction for last segment //add current LiveWire contour to the finished contour and reset //to start new segment and computation /* check if event can be handled */ const PositionEvent* positionEvent = dynamic_cast(stateEvent->GetEvent()); if (!positionEvent) return false; if ( Superclass::CanHandleEvent(stateEvent) < 1.0 ) return false; /* END check if event can be handled */ int timestep = positionEvent->GetSender()->GetTimeStep(); //remove duplicate first vertex, it's already contained in m_Contour m_LiveWireContour->RemoveVertexAt(0, timestep); /* TODO fix this hack*/ //set last to active added point if( m_LiveWireContour->GetNumberOfVertices(timestep) > 0) { const_cast( m_LiveWireContour->GetVertexAt(m_LiveWireContour->GetNumberOfVertices(timestep)-1, timestep) )->IsControlPoint = true; } //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(const_cast(positionEvent->GetWorldPosition())); 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() ); return true; } bool mitk::LiveWireTool2D::OnMouseMoved( Action* action, const StateEvent* stateEvent) { //compute LiveWire segment from last control point to current mouse position /* check if event can be handled */ if ( Superclass::CanHandleEvent(stateEvent) < 1.0 ) return false; const PositionEvent* positionEvent = dynamic_cast(stateEvent->GetEvent()); if (!positionEvent) return false; /* END check if event can be handled */ /* actual LiveWire computation */ int timestep = positionEvent->GetSender()->GetTimeStep(); m_LiveWireFilter->SetEndPoint(const_cast(positionEvent->GetWorldPosition())); m_LiveWireFilter->SetTimestep(timestep); m_LiveWireFilter->Update(); //ContourModel::VertexType* currentVertex = const_cast(m_LiveWireContour->GetVertexAt(0)); this->m_LiveWireContour = this->m_LiveWireFilter->GetOutput(); this->m_LiveWireContourNode->SetData(this->m_LiveWireFilter->GetOutput()); /* END actual LiveWire computation */ //render assert( positionEvent->GetSender()->GetRenderWindow() ); mitk::RenderingManager::GetInstance()->RequestUpdate( positionEvent->GetSender()->GetRenderWindow() ); return true; } bool mitk::LiveWireTool2D::OnMouseMoveNoDynamicCosts(Action* action, const StateEvent* stateEvent) { //do not use dynamic cost map m_LiveWireFilter->SetUseDynamicCostMap(false); OnMouseMoved(action, stateEvent); m_LiveWireFilter->SetUseDynamicCostMap(true); return true; } bool mitk::LiveWireTool2D::OnCheckPoint( Action* action, const StateEvent* stateEvent) { //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 // mitk::StateEvent* newStateEvent = NULL; const PositionEvent* positionEvent = dynamic_cast(stateEvent->GetEvent()); if (!positionEvent) { //stay in current state newStateEvent = new mitk::StateEvent(EIDNO, stateEvent->GetEvent()); } else { int timestep = positionEvent->GetSender()->GetTimeStep(); mitk::Point3D click = positionEvent->GetWorldPosition(); mitk::Point3D first = this->m_Contour->GetVertexAt(0, timestep)->Coordinates; - if (first.EuclideanDistanceTo(click) < 1.5) + if (first.EuclideanDistanceTo(click) < 4.5) { //finish newStateEvent = new mitk::StateEvent(EIDYES, stateEvent->GetEvent()); }else { //stay active newStateEvent = new mitk::StateEvent(EIDNO, stateEvent->GetEvent()); } } this->HandleEvent( newStateEvent ); return true; } bool mitk::LiveWireTool2D::OnFinish( Action* action, const StateEvent* stateEvent) { // finish livewire tool interaction /* check if event can be handled */ if ( Superclass::CanHandleEvent(stateEvent) < 1.0 ) return false; const PositionEvent* positionEvent = dynamic_cast(stateEvent->GetEvent()); if (!positionEvent) return false; /* END check if event can be handled */ //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 contourPair(m_ContourModelNode.GetPointer(), dynamic_cast(positionEvent->GetSender()->GetCurrentWorldGeometry2D()->Clone().GetPointer()) ); m_Contours.push_back(contourPair); m_LiveWireFilter->SetUseDynamicCostMap(false); this->FinishTool(); return true; } void mitk::LiveWireTool2D::FinishTool() { unsigned int numberOfTimesteps = m_Contour->GetTimeSlicedGeometry()->GetTimeSteps(); //close contour in each timestep for( int i = 0; i <= numberOfTimesteps; i++) { m_Contour->Close(i); } - //clear LiveWire node m_ToolManager->GetDataStorage()->Remove( m_LiveWireContourNode ); + + // clear live wire contour node m_LiveWireContourNode = NULL; m_LiveWireContour = NULL; - //TODO visual feedback for completing livewire tool m_ContourModelNode->AddProperty( "contour.color", ColorProperty::New(1.0, 1.0, 0.1), NULL, true ); - m_ContourModelNode->SetProperty("name", StringProperty::New("contour node")); + m_ContourModelNode->SetName("contour node"); //set the livewire interactor to edit control points mitk::ContourModelLiveWireInteractor::Pointer interactor = mitk::ContourModelLiveWireInteractor::New(m_ContourModelNode); interactor->SetWorkingImage(this->m_WorkingSlice); + interactor->SetRightLiveWireContourModelNode(this->m_RightLiveWireContourNode); + interactor->SetLeftLiveWireContourModelNode(this->m_LeftLiveWireContourNode); m_ContourModelNode->SetInteractor(interactor); //add interactor to globalInteraction instance mitk::GlobalInteraction::GetInstance()->AddInteractor(interactor); - /* END complete livewire tool interaction */ - - - /* reset contours and datanodes */ - m_Contour = mitk::ContourModel::New(); - m_ContourModelNode = mitk::DataNode::New(); - m_ContourModelNode->SetData( m_Contour ); - m_ContourModelNode->SetProperty("name", StringProperty::New("working contour node")); - m_ContourModelNode->SetProperty("visible", BoolProperty::New(true)); - m_ContourModelNode->AddProperty( "contour.color", ColorProperty::New(0.9, 1.0, 0.1), NULL, true ); - m_ContourModelNode->AddProperty( "points.color", ColorProperty::New(1.0, 0.0, 0.1), NULL, true ); - - m_LiveWireContour = mitk::ContourModel::New(); - m_LiveWireContourNode = mitk::DataNode::New(); - //m_LiveWireContourNode->SetData( m_LiveWireContour ); - m_LiveWireContourNode->SetProperty("name", StringProperty::New("active livewire node")); - m_LiveWireContourNode->SetProperty("visible", BoolProperty::New(true)); - m_LiveWireContourNode->AddProperty( "contour.color", ColorProperty::New(0.1, 1.0, 0.1), NULL, true ); - m_LiveWireContourNode->AddProperty( "points.color", ColorProperty::New(0.5, 0.5, 0.1), NULL, true ); - /* END reset contours and datanodes */ } bool mitk::LiveWireTool2D::OnLastSegmentDelete( Action* action, const StateEvent* stateEvent) { int timestep = stateEvent->GetEvent()->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_LiveWireContour = mitk::ContourModel::New(); m_Contour = mitk::ContourModel::New(); m_ContourModelNode->SetData( m_Contour ); m_LiveWireContourNode->SetData( m_LiveWireContour ); Superclass::Deactivated(); //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()); mitk::ContourModel::VertexIterator begin = m_Contour->IteratorBegin(); //iterate from last point to next active point mitk::ContourModel::VertexIterator 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); mitk::ContourModel::VertexIterator it = m_Contour->IteratorBegin(); //fill new Contour while(it <= newLast) { newContour->AddVertex((*it)->Coordinates, (*it)->IsControlPoint, timestep); it++; } newContour->SetIsClosed(m_Contour->IsClosed()); //set new contour visible m_ContourModelNode->SetData(newContour); m_Contour = newContour; assert( stateEvent->GetEvent()->GetSender()->GetRenderWindow() ); mitk::RenderingManager::GetInstance()->RequestUpdate( stateEvent->GetEvent()->GetSender()->GetRenderWindow() ); } return true; } 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< InputImageType, InputImageType> 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/mitkLiveWireTool2D.h b/Modules/Segmentation/Interactions/mitkLiveWireTool2D.h index 99366a5b80..d9ca1d88f0 100644 --- a/Modules/Segmentation/Interactions/mitkLiveWireTool2D.h +++ b/Modules/Segmentation/Interactions/mitkLiveWireTool2D.h @@ -1,121 +1,127 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #ifndef mitkCorrectorTool2D_h_Included #define mitkCorrectorTool2D_h_Included #include "mitkCommon.h" #include "SegmentationExports.h" #include "mitkSegTool2D.h" #include #include #include namespace mitk { /** \brief A 2D segmentation tool based on LiveWire approach. The contour between the last user added point and the current mouse position is computed by searching the shortest path according to specific features of the image. The contour thus snappest to the boundary of objects. \sa SegTool2D \sa ImageLiveWireContourModelFilter \ingroup Interaction \ingroup ToolManagerEtAl \warning Only to be instantiated by mitk::ToolManager. */ class Segmentation_EXPORT LiveWireTool2D : public SegTool2D { public: mitkClassMacro(LiveWireTool2D, SegTool2D); itkNewMacro(LiveWireTool2D); virtual const char** GetXPM() const; virtual const char* GetName() const; protected: LiveWireTool2D(); virtual ~LiveWireTool2D(); /** * \brief Calculates how good the data, this statemachine handles, is hit by the event. * */ virtual float CanHandleEvent( StateEvent const *stateEvent) const; virtual void Activated(); virtual void Deactivated(); /// \brief Initialize tool virtual bool OnInitLiveWire (Action*, const StateEvent*); /// \brief Add a control point and finish current segment virtual bool OnAddPoint (Action*, const StateEvent*); /// \breif Actual LiveWire computation virtual bool OnMouseMoved(Action*, const StateEvent*); /// \brief Check double click on first control point to finish the LiveWire tool virtual bool OnCheckPoint(Action*, const StateEvent*); /// \brief Finish LiveWire tool virtual bool OnFinish(Action*, const StateEvent*); /// \brief Close the contour virtual bool OnLastSegmentDelete(Action*, const StateEvent*); /// \brief Don't use dynamic cost map for LiveWire calculation virtual bool OnMouseMoveNoDynamicCosts(Action*, const StateEvent*); /// \brief Finish contour interaction. void FinishTool(); //the contour already set by the user mitk::ContourModel::Pointer m_Contour; //the corresponding datanode mitk::DataNode::Pointer m_ContourModelNode; //the current LiveWire computed contour mitk::ContourModel::Pointer m_LiveWireContour; + mitk::ContourModel::Pointer m_RightLiveWireContour; + mitk::ContourModel::Pointer m_LeftLiveWireContour; + //the corresponding datanode mitk::DataNode::Pointer m_LiveWireContourNode; + mitk::DataNode::Pointer m_LeftLiveWireContourNode; + mitk::DataNode::Pointer m_RightLiveWireContourNode; + //the current reference image mitk::Image::Pointer m_WorkingSlice; mitk::ImageLiveWireContourModelFilter::Pointer m_LiveWireFilter; bool m_CreateAndUseDynamicCosts; std::vector< std::pair > m_Contours; template void FindHighestGradientMagnitudeByITK(itk::Image* inputImage, itk::Index<3> &index, itk::Index<3> &returnIndex); }; } // namespace #endif diff --git a/Modules/Segmentation/Rendering/mitkContourModelGLMapper2D.cpp b/Modules/Segmentation/Rendering/mitkContourModelGLMapper2D.cpp index 94d00e7b41..5053820699 100644 --- a/Modules/Segmentation/Rendering/mitkContourModelGLMapper2D.cpp +++ b/Modules/Segmentation/Rendering/mitkContourModelGLMapper2D.cpp @@ -1,305 +1,359 @@ /*=================================================================== 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 "mitkContourModelGLMapper2D.h" #include "mitkBaseRenderer.h" #include "mitkPlaneGeometry.h" #include "mitkColorProperty.h" #include "mitkProperties.h" #include "mitkContourModel.h" #include "mitkContourModelSubDivisionFilter.h" #include #include "mitkGL.h" mitk::ContourModelGLMapper2D::ContourModelGLMapper2D() { } mitk::ContourModelGLMapper2D::~ContourModelGLMapper2D() { } void mitk::ContourModelGLMapper2D::Paint(mitk::BaseRenderer * renderer) { BaseLocalStorage *ls = m_LSH.GetLocalStorage(renderer); mitk::DataNode* dataNode = this->GetDataNode(); bool visible = true; dataNode->GetVisibility(visible, renderer, "visible"); if ( !visible ) return; bool updateNeccesary=true; int timestep = renderer->GetTimeStep(); mitk::ContourModel::Pointer input = const_cast(this->GetInput()); mitk::ContourModel::Pointer renderingContour = input; bool subdivision = false; dataNode->GetBoolProperty( "subdivision curve", subdivision, renderer ); if (subdivision) { mitk::ContourModel::Pointer subdivContour = mitk::ContourModel::New(); mitk::ContourModelSubDivisionFilter::Pointer subdivFilter = mitk::ContourModelSubDivisionFilter::New(); subdivFilter->SetInput(input); subdivFilter->Update(); subdivContour = subdivFilter->GetOutput(); if(subdivContour->GetNumberOfVertices() == 0 ) { subdivContour = input; } renderingContour = subdivContour; } renderingContour->UpdateOutputInformation(); if( renderingContour->GetMTime() < ls->GetLastGenerateDataTime() ) updateNeccesary = false; if(renderingContour->GetNumberOfVertices(timestep) < 1) updateNeccesary = false; if (updateNeccesary) { // ok, das ist aus GenerateData kopiert mitk::DisplayGeometry::Pointer displayGeometry = renderer->GetDisplayGeometry(); assert(displayGeometry.IsNotNull()); //apply color and opacity read from the PropertyList ApplyProperties(renderer); bool isEditing = false; dataNode->GetBoolProperty("contour.editing", isEditing); mitk::ColorProperty::Pointer colorprop; if (isEditing) colorprop = dynamic_cast(dataNode->GetProperty("contour.editing.color", renderer)); else colorprop = dynamic_cast(dataNode->GetProperty("contour.color", renderer)); if(colorprop) { //set the color of the contour double red = colorprop->GetColor().GetRed(); double green = colorprop->GetColor().GetGreen(); double blue = colorprop->GetColor().GetBlue(); glColor4f(red,green,blue,0.5); } - mitk::ColorProperty::Pointer selectedcolor = dynamic_cast(dataNode->GetProperty("points.color", renderer)); + mitk::ColorProperty::Pointer selectedcolor = dynamic_cast(dataNode->GetProperty("contour.points.color", renderer)); if(!selectedcolor) { selectedcolor = mitk::ColorProperty::New(1.0,0.0,0.1); } vtkLinearTransform* transform = dataNode->GetVtkTransform(); // ContourModel::OutputType point; mitk::Point3D point; mitk::Point3D p, projected_p; float vtkp[3]; float lineWidth = 3.0; bool isHovering = false; dataNode->GetBoolProperty("contour.hovering", isHovering); if (isHovering) dataNode->GetFloatProperty("contour.hovering.width", lineWidth); else dataNode->GetFloatProperty("contour.width", lineWidth); bool drawit=false; mitk::ContourModel::VertexIterator pointsIt = renderingContour->IteratorBegin(timestep); Point2D pt2d; // projected_p in display coordinates Point2D lastPt2d; + int index = 0; + while ( pointsIt != renderingContour->IteratorEnd(timestep) ) { lastPt2d = pt2d; point = (*pointsIt)->Coordinates; itk2vtk(point, vtkp); transform->TransformPoint(vtkp, vtkp); vtk2itk(vtkp,p); displayGeometry->Project(p, projected_p); displayGeometry->Map(projected_p, pt2d); displayGeometry->WorldToDisplay(pt2d, pt2d); Vector3D diff=p-projected_p; ScalarType scalardiff = diff.GetNorm(); //draw lines bool projectmode=false; dataNode->GetVisibility(projectmode, renderer, "contour.project-onto-plane"); if(projectmode) { drawit=true; } else if(scalardiff<0.25) { drawit=true; } if(drawit) { //lastPt2d is not valid in first step if( !(pointsIt == renderingContour->IteratorBegin(timestep)) ) { glLineWidth(lineWidth); glBegin (GL_LINES); glVertex2f(pt2d[0], pt2d[1]); glVertex2f(lastPt2d[0], lastPt2d[1]); glEnd(); glLineWidth(1); } + bool showControlPoints = false; + dataNode->GetBoolProperty("contour.controlpoints.show", showControlPoints); + + if (showControlPoints) + { + //draw active points + if ((*pointsIt)->IsControlPoint) + { + float pointsize = 4; + Point2D tmp; + + Vector2D horz,vert; + horz[1]=0; + vert[0]=0; + horz[0]=pointsize; + vert[1]=pointsize; + glColor3f(selectedcolor->GetColor().GetRed(), selectedcolor->GetColor().GetBlue(), selectedcolor->GetColor().GetGreen()); + glLineWidth(1); + //a rectangle around the point with the selected color + glBegin (GL_LINE_LOOP); + tmp=pt2d-horz; glVertex2fv(&tmp[0]); + tmp=pt2d+vert; glVertex2fv(&tmp[0]); + tmp=pt2d+horz; glVertex2fv(&tmp[0]); + tmp=pt2d-vert; glVertex2fv(&tmp[0]); + glEnd(); + glLineWidth(1); + //the actual point in the specified color to see the usual color of the point + glColor3f(colorprop->GetColor().GetRed(),colorprop->GetColor().GetGreen(),colorprop->GetColor().GetBlue()); + glPointSize(1); + glBegin (GL_POINTS); + tmp=pt2d; glVertex2fv(&tmp[0]); + glEnd (); + } + } + + bool showPoints = false; + dataNode->GetBoolProperty("contour.points.show", showPoints); - //draw active points - if ((*pointsIt)->IsControlPoint) + if (showPoints) { - float pointsize = 4; + float pointsize = 3; Point2D tmp; Vector2D horz,vert; horz[1]=0; vert[0]=0; horz[0]=pointsize; vert[1]=pointsize; - glColor3f(selectedcolor->GetColor().GetRed(), selectedcolor->GetColor().GetBlue(), selectedcolor->GetColor().GetGreen()); + glColor3f(0.0, 0.0, 0.0); glLineWidth(1); //a rectangle around the point with the selected color glBegin (GL_LINE_LOOP); tmp=pt2d-horz; glVertex2fv(&tmp[0]); tmp=pt2d+vert; glVertex2fv(&tmp[0]); tmp=pt2d+horz; glVertex2fv(&tmp[0]); tmp=pt2d-vert; glVertex2fv(&tmp[0]); glEnd(); glLineWidth(1); //the actual point in the specified color to see the usual color of the point glColor3f(colorprop->GetColor().GetRed(),colorprop->GetColor().GetGreen(),colorprop->GetColor().GetBlue()); glPointSize(1); glBegin (GL_POINTS); tmp=pt2d; glVertex2fv(&tmp[0]); glEnd (); } + +/* + std::string l; + std::stringstream ss; + ss << index; + l.append(ss.str()); + + mitk::VtkPropRenderer* OpenGLrenderer = dynamic_cast( renderer ); + float rgb[3]; + rgb[0] = 0.0; rgb[1] = 0.0; rgb[2] = 0.0; + OpenGLrenderer->WriteSimpleText(l, pt2d[0] + 2, pt2d[1] + 2,rgb[0], rgb[1],rgb[2]); + + index++; +*/ } pointsIt++; }//end while iterate over controlpoints //close contour if necessary if(renderingContour->IsClosed(timestep) && drawit) { lastPt2d = pt2d; point = renderingContour->GetVertexAt(0,timestep)->Coordinates; itk2vtk(point, vtkp); transform->TransformPoint(vtkp, vtkp); vtk2itk(vtkp,p); displayGeometry->Project(p, projected_p); displayGeometry->Map(projected_p, pt2d); displayGeometry->WorldToDisplay(pt2d, pt2d); glLineWidth(lineWidth); glBegin (GL_LINES); glVertex2f(lastPt2d[0], lastPt2d[1]); glVertex2f( pt2d[0], pt2d[1] ); glEnd(); glLineWidth(1); } //draw selected vertex if exists if(renderingContour->GetSelectedVertex()) { //transform selected vertex point = renderingContour->GetSelectedVertex()->Coordinates; itk2vtk(point, vtkp); transform->TransformPoint(vtkp, vtkp); vtk2itk(vtkp,p); displayGeometry->Project(p, projected_p); displayGeometry->Map(projected_p, pt2d); displayGeometry->WorldToDisplay(pt2d, pt2d); Vector3D diff=p-projected_p; ScalarType scalardiff = diff.GetNorm(); //---------------------------------- //draw point if close to plane if(scalardiff<0.25) { float pointsize = 5; Point2D tmp; glColor3f(0.0, 1.0, 0.0); glLineWidth(1); //a diamond around the point glBegin (GL_LINE_LOOP); //begin from upper left corner and paint clockwise tmp[0]=pt2d[0]-pointsize; tmp[1]=pt2d[1]+pointsize; glVertex2fv(&tmp[0]); tmp[0]=pt2d[0]+pointsize; tmp[1]=pt2d[1]+pointsize; glVertex2fv(&tmp[0]); tmp[0]=pt2d[0]+pointsize; tmp[1]=pt2d[1]-pointsize; glVertex2fv(&tmp[0]); tmp[0]=pt2d[0]-pointsize; tmp[1]=pt2d[1]-pointsize; glVertex2fv(&tmp[0]); glEnd (); } //------------------------------------ } } } const mitk::ContourModel* mitk::ContourModelGLMapper2D::GetInput(void) { return static_cast ( GetDataNode()->GetData() ); } void mitk::ContourModelGLMapper2D::SetDefaultProperties(mitk::DataNode* node, mitk::BaseRenderer* renderer, bool overwrite) { node->AddProperty( "contour.color", ColorProperty::New(0.9, 1.0, 0.1), renderer, overwrite ); node->AddProperty( "contour.editing", mitk::BoolProperty::New( false ), renderer, overwrite ); node->AddProperty( "contour.editing.color", ColorProperty::New(0.1, 0.9, 0.1), renderer, overwrite ); - node->AddProperty( "points.color", ColorProperty::New(1.0, 0.0, 0.1), renderer, overwrite ); + node->AddProperty( "contour.points.color", ColorProperty::New(1.0, 0.0, 0.1), renderer, overwrite ); + node->AddProperty( "contour.points.show", mitk::BoolProperty::New( false ), renderer, overwrite ); + node->AddProperty( "contour.controlpoints.show", mitk::BoolProperty::New( false ), renderer, overwrite ); node->AddProperty( "contour.width", mitk::FloatProperty::New( 1.0 ), renderer, overwrite ); node->AddProperty( "contour.hovering.width", mitk::FloatProperty::New( 3.0 ), renderer, overwrite ); node->AddProperty( "contour.hovering", mitk::BoolProperty::New( false ), renderer, overwrite ); node->AddProperty( "subdivision curve", mitk::BoolProperty::New( false ), renderer, overwrite ); node->AddProperty( "contour.project-onto-plane", mitk::BoolProperty::New( false ), renderer, overwrite ); Superclass::SetDefaultProperties(node, renderer, overwrite); }