diff --git a/Modules/Segmentation/Algorithms/mitkImageLiveWireContourModelFilter.cpp b/Modules/Segmentation/Algorithms/mitkImageLiveWireContourModelFilter.cpp index 77c7d3024d..1f0a0c4b49 100644 --- a/Modules/Segmentation/Algorithms/mitkImageLiveWireContourModelFilter.cpp +++ b/Modules/Segmentation/Algorithms/mitkImageLiveWireContourModelFilter.cpp @@ -1,447 +1,448 @@ /*=================================================================== 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->SetNumberOfIndexedOutputs( 1 ); this->SetNthOutput(0, output.GetPointer()); m_CostFunction = CostFunctionType::New(); m_ShortestPathFilter = ShortestPathImageFilterType::New(); m_ShortestPathFilter->SetCostFunction(m_CostFunction); m_UseDynamicCostMap = false; m_ImageModified = false; - m_Timestep = 0; + 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) ) { try { this->UpdateLiveWire(); } catch( itk::ExceptionObject & e ) { MITK_INFO << "Exception caught during live wiring calculation: " << e; m_ImageModified = true; return; } } } template void mitk::ImageLiveWireContourModelFilter::ItkPreProcessImage (itk::Image* inputImage) { typedef itk::Image< TPixel, VImageDimension > InputImageType; typedef itk::CastImageFilter< InputImageType, InternalImageType > CastFilterType; typename CastFilterType::Pointer castFilter = CastFilterType::New(); castFilter->SetInput(inputImage); castFilter->Update(); m_InternalImage = castFilter->GetOutput(); m_CostFunction->SetImage( m_InternalImage ); m_ShortestPathFilter->SetInput( m_InternalImage ); } void mitk::ImageLiveWireContourModelFilter::ClearRepulsivePoints() { m_CostFunction->ClearRepulsivePoints(); } void mitk::ImageLiveWireContourModelFilter::AddRepulsivePoint( const InternalImageType::IndexType& idx ) { m_CostFunction->AddRepulsivePoint(idx); } void mitk::ImageLiveWireContourModelFilter::SetRepulsivePoints(const ShortestPathType& points) { m_CostFunction->ClearRepulsivePoints(); ShortestPathType::const_iterator iter = points.begin(); for (;iter != points.end(); iter++) { m_CostFunction->AddRepulsivePoint( (*iter) ); } } void mitk::ImageLiveWireContourModelFilter::UpdateLiveWire() { // compute the requested region for itk filters InternalImageType::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 InternalImageType::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 InternalImageType::SizeType size; size[0] = abs( startPoint[0] - endPoint[0] ) + 1; size[1] = abs( startPoint[1] - endPoint[1] ) + 1; CostFunctionType::RegionType region; region.SetSize( size ); region.SetIndex( startRegion ); //inputImage->SetRequestedRegion(region); // extracts features from image and calculates costs //m_CostFunction->SetImage(m_InternalImage); 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 m_ShortestPathFilter->SetFullNeighborsMode(true); //m_ShortestPathFilter->SetInput( m_CostFunction->SetImage(m_InternalImage) ); m_ShortestPathFilter->SetMakeOutputImage(false); //m_ShortestPathFilter->SetCalcAllDistances(true); m_ShortestPathFilter->SetStartIndex(startPoint); m_ShortestPathFilter->SetEndIndex(endPoint); m_ShortestPathFilter->Update(); // construct contour from path image //get the shortest path as vector ShortestPathType shortestPath = m_ShortestPathFilter->GetVectorPath(); //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->Expand(m_TimeStep+1); // output->Clear(); mitk::Image::ConstPointer input = dynamic_cast(this->GetInput()); ShortestPathType::const_iterator pathIterator = shortestPath.begin(); while(pathIterator != shortestPath.end()) { mitk::Point3D currentPoint; currentPoint[0] = static_cast( (*pathIterator)[0] ); currentPoint[1] = static_cast( (*pathIterator)[1] ); currentPoint[2] = 0.0; input->GetGeometry()->IndexToWorld(currentPoint, currentPoint); - output->AddVertex(currentPoint, false, m_Timestep); + output->AddVertex(currentPoint, false, m_TimeStep); pathIterator++; } } bool mitk::ImageLiveWireContourModelFilter::CreateDynamicCostMap(mitk::ContourModel* path) { mitk::Image::ConstPointer input = dynamic_cast(this->GetInput()); - if(input) + if(!input) return false; + + try { AccessFixedDimensionByItk_1(input,CreateDynamicCostMapByITK, 2, path); - return true; } - else + catch( itk::ExceptionObject & e ) { + MITK_INFO << "Exception caught during dynamic cost map alculation: " << e; return false; } -} - + return true; +} 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 ad14230bfd..c8a7049c76 100644 --- a/Modules/Segmentation/Algorithms/mitkImageLiveWireContourModelFilter.h +++ b/Modules/Segmentation/Algorithms/mitkImageLiveWireContourModelFilter.h @@ -1,163 +1,164 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #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 > InternalImageType; + typedef itk::Image< float, 2 > InternalImageType; typedef itk::ShortestPathImageFilter< InternalImageType, InternalImageType > ShortestPathImageFilterType; - typedef itk::ShortestPathCostFunctionLiveWire< InternalImageType > CostFunctionType; - typedef std::vector< InternalImageType::IndexType > ShortestPathType; + typedef itk::ShortestPathCostFunctionLiveWire< InternalImageType > CostFunctionType; + typedef std::vector< InternalImageType::IndexType > ShortestPathType; /** \brief start point in world coordinates*/ itkSetMacro(StartPoint, mitk::Point3D); itkGetMacro(StartPoint, mitk::Point3D); /** \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); + /** \brief Actual time step + */ + itkSetMacro(TimeStep, unsigned int); + itkGetMacro(TimeStep, unsigned int); + + /** \brief Clear all repulsive points used in the cost function + */ void ClearRepulsivePoints(); + /** \brief Set a vector with repulsive points to use in the cost function + */ void SetRepulsivePoints(const ShortestPathType& points); + /** \brief Add a single repulsive point + */ void AddRepulsivePoint( const InternalImageType::IndexType& idx ); 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; + /** \brief Flag to decide whether to run image pre-processing*/ bool m_ImageModified; - unsigned int m_Timestep; + unsigned int m_TimeStep; template void ItkPreProcessImage (itk::Image* inputImage); template void CreateDynamicCostMapByITK(itk::Image* inputImage, mitk::ContourModel* path=NULL); InternalImageType::Pointer m_InternalImage; }; } #endif diff --git a/Modules/Segmentation/Interactions/mitkContourModelLiveWireInteractor.cpp b/Modules/Segmentation/Interactions/mitkContourModelLiveWireInteractor.cpp index 4431d1d274..3f6dfcbe66 100644 --- a/Modules/Segmentation/Interactions/mitkContourModelLiveWireInteractor.cpp +++ b/Modules/Segmentation/Interactions/mitkContourModelLiveWireInteractor.cpp @@ -1,409 +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 "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(); 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()) ); + MITK_WARN << "Could not get position event"; return false; } mitk::StateEvent* newStateEvent = NULL; int timestep = stateEvent->GetEvent()->GetSender()->GetTimeStep(); - mitk::ContourModel *contour = dynamic_cast( - m_DataNode->GetData() ); + mitk::ContourModel *contour = dynamic_cast( m_DataNode->GetData() ); + if (!contour) + { + this->HandleEvent( new mitk::StateEvent(EIDNO, stateEvent->GetEvent()) ); + MITK_WARN << "Could not get working contour"; + return false; + } contour->Deselect(); - /* - * Check distance to any vertex. - * Transition YES if click close to a vertex - */ + // 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 ); - - newStateEvent = new mitk::StateEvent(EIDYES, stateEvent->GetEvent()); m_lastMousePosition = click; 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_INFO << "contour left points: " << m_ContourLeft->GetNumberOfVertices(); + + m_LeftLiveWireContourNode->SetData(m_ContourLeft); + 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_INFO << "contour right points: " << m_ContourRight->GetNumberOfVertices(); + + m_RightLiveWireContourNode->SetData(m_ContourRight); + mitk::ContourModel::VertexIterator itUp = contour->IteratorBegin() + upIndex; m_NextActiveVertexUp = (*itUp)->Coordinates; + + if ( (m_ContourLeft->GetNumberOfVertices() < 3) || (m_ContourRight->GetNumberOfVertices() < 3) ) + { + newStateEvent = new mitk::StateEvent(EIDNO, stateEvent->GetEvent()); + m_ContourRight->Clear(); + m_ContourLeft->Clear(); + MITK_WARN << "contour left or right with less than 3 points"; + } + else + newStateEvent = new mitk::StateEvent(EIDYES, stateEvent->GetEvent()); } else { - m_DataNode->SetBoolProperty( "contour.editing", false ); + MITK_WARN << "Could not select a vertex"; 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) + { + MITK_WARN << "Could not get working contour"; + return false; + } + + if ( (m_ContourLeft->GetNumberOfVertices() < 3) || (m_ContourRight->GetNumberOfVertices() < 3) ) + { + m_ContourRight->Clear(); + m_ContourLeft->Clear(); + MITK_WARN << "contour left or right with less than 3 points"; + } if(contour->GetSelectedVertex()) { mitk::ContourModel::Pointer newContour = mitk::ContourModel::New(); newContour->Expand(contour->GetTimeSteps()); 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 *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 live wire computed points newContour->Concatenate( liveWireContour, timestep ); // insert right side of original contour 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; } - return true; + return false; } 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(); //recompute contour between previous active vertex and selected vertex this->m_LiveWireFilter->SetStartPoint( m_NextActiveVertexDown ); this->m_LiveWireFilter->SetEndPoint( currentPosition ); + this->m_LiveWireFilter->ClearRepulsivePoints(); this->m_LiveWireFilter->Update(); mitk::ContourModel::Pointer leftLiveWire = this->m_LiveWireFilter->GetOutput(); if (!leftLiveWire) return false; if (leftLiveWire->GetNumberOfVertices() < 3) { leftLiveWire->Clear(timestep); return false; } leftLiveWire->RemoveVertexAt(0, timestep); //recompute contour between selected vertex and next active vertex this->m_LiveWireFilter->SetStartPoint( currentPosition ); this->m_LiveWireFilter->SetEndPoint( m_NextActiveVertexUp ); this->m_LiveWireFilter->ClearRepulsivePoints(); + // TODO: try using all contour points typedef mitk::ImageLiveWireContourModelFilter::InternalImageType::IndexType IndexType; mitk::ContourModel::ConstVertexIterator iter = leftLiveWire->IteratorBegin(timestep); for (;iter != leftLiveWire->IteratorEnd(timestep); iter++) { IndexType idx; this->m_WorkingImage->GetGeometry()->WorldToIndex((*iter)->Coordinates, idx); this->m_LiveWireFilter->AddRepulsivePoint( idx ); } - // this->m_LiveWireFilter-SetLastShortestPathAsRepulsivePoints(); this->m_LiveWireFilter->Update(); mitk::ContourModel::Pointer rightLiveWire = this->m_LiveWireFilter->GetOutput(); if (!rightLiveWire) return false; if (rightLiveWire->GetNumberOfVertices() < 3) { rightLiveWire->Clear(); return false; } rightLiveWire->RemoveVertexAt(0, timestep); - // seba continue here leftLiveWire->SelectVertexAt(leftLiveWire->GetNumberOfVertices()-1, timestep); leftLiveWire->SetSelectedVertexAsControlPoint(true); // set corrected left live wire to its node m_LeftLiveWireContourNode->SetData(leftLiveWire); // set corrected right live wire to its node m_RightLiveWireContourNode->SetData(rightLiveWire); -/* - leftLiveWire->RemoveIntersections(rightLiveWire, timestep); - - if (leftLiveWire->GetNumberOfVertices() < 1) - return false; - - if (rightLiveWire->GetNumberOfVertices() < 1) - return false; -*/ mitk::ContourModel *contour = dynamic_cast( m_DataNode->GetData() ); mitk::ContourModel::Pointer newContour = mitk::ContourModel::New(); newContour->Expand(contour->GetTimeSteps()); // concatenate left original contour newContour->Concatenate( this->m_ContourLeft, timestep ); newContour->Deselect(); // concatenate left live wire newContour->Concatenate( leftLiveWire, timestep ); // concatenate right live wire newContour->Concatenate( rightLiveWire, timestep ); // concatenate right original contour newContour->Concatenate( this->m_ContourRight, timestep ); newContour->SetIsClosed(contour->IsClosed(timestep), 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) { 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 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; } } } 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() ); if (leftLiveWire) leftLiveWire->Clear(timestep); mitk::ContourModel *rightLiveWire = dynamic_cast( this->m_RightLiveWireContourNode->GetData() ); if (rightLiveWire) rightLiveWire->Clear(timestep); assert( stateEvent->GetEvent()->GetSender()->GetRenderWindow() ); mitk::RenderingManager::GetInstance()->RequestUpdate( stateEvent->GetEvent()->GetSender()->GetRenderWindow() ); return true; }