diff --git a/Core/Code/Resources/Interactions/Legacy/StateMachine.xml b/Core/Code/Resources/Interactions/Legacy/StateMachine.xml index 9e12e74455..009f3b95b2 100644 --- a/Core/Code/Resources/Interactions/Legacy/StateMachine.xml +++ b/Core/Code/Resources/Interactions/Legacy/StateMachine.xml @@ -1,4086 +1,4107 @@ - + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Modules/GraphAlgorithms/itkShortestPathCostFunctionLiveWire.txx b/Modules/GraphAlgorithms/itkShortestPathCostFunctionLiveWire.txx index 7cdb6d05d9..6f26dfeb9e 100644 --- a/Modules/GraphAlgorithms/itkShortestPathCostFunctionLiveWire.txx +++ b/Modules/GraphAlgorithms/itkShortestPathCostFunctionLiveWire.txx @@ -1,440 +1,440 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #ifndef __itkShortestPathCostFunctionLiveWire_txx #define __itkShortestPathCostFunctionLiveWire_txx #include "itkShortestPathCostFunctionLiveWire.h" #include #include #include #include #include #include #include #include namespace itk { // Constructor template ShortestPathCostFunctionLiveWire ::ShortestPathCostFunctionLiveWire() { m_UseRepulsivePoints = false; m_GradientMax = 0.0; m_Initialized = false; m_UseCostMap = false; m_MaxMapCosts = -1.0; } template void ShortestPathCostFunctionLiveWire ::AddRepulsivePoint( const IndexType& index ) { - m_MaskImage->SetPixel(index, 255); + this->m_MaskImage->SetPixel(index, 255); m_UseRepulsivePoints = true; } template void ShortestPathCostFunctionLiveWire ::RemoveRepulsivePoint( const IndexType& index ) { this->m_MaskImage->SetPixel(index, 0); } template void ShortestPathCostFunctionLiveWire ::SetImage(const TInputImageType* _arg) { if (this->m_Image != _arg) { this->m_Image = _arg; // initialize mask image - m_MaskImage = UnsignedCharImageType::New(); - m_MaskImage->SetRegions(this->m_Image->GetLargestPossibleRegion()); - m_MaskImage->SetOrigin( this->m_Image->GetOrigin() ); - m_MaskImage->SetSpacing( this->m_Image->GetSpacing() ); - m_MaskImage->SetDirection( this->m_Image->GetDirection() ); - m_MaskImage->Allocate (); - m_MaskImage->FillBuffer(0); + this->m_MaskImage = UnsignedCharImageType::New(); + this->m_MaskImage->SetRegions( this->m_Image->GetLargestPossibleRegion()); + this->m_MaskImage->SetOrigin( this->m_Image->GetOrigin() ); + this->m_MaskImage->SetSpacing( this->m_Image->GetSpacing() ); + this->m_MaskImage->SetDirection( this->m_Image->GetDirection() ); + this->m_MaskImage->Allocate (); + this->m_MaskImage->FillBuffer(0); this->Modified(); this->m_Initialized = false; } } template void ShortestPathCostFunctionLiveWire ::ClearRepulsivePoints() { m_UseRepulsivePoints = false; - m_MaskImage->FillBuffer(0); + this->m_MaskImage->FillBuffer(0); } template double ShortestPathCostFunctionLiveWire ::GetCost(IndexType p1 ,IndexType p2) { // local component costs // weights double w1; double w2; double w3; double costs = 0.0; // if we are on the mask, return asap if (m_UseRepulsivePoints) { if ( (this->m_MaskImage->GetPixel(p1) != 0) || (this->m_MaskImage->GetPixel(p2) != 0) ) return 1000; } unsigned long xMAX = this->m_Image->GetLargestPossibleRegion().GetSize()[0]; unsigned long yMAX = this->m_Image->GetLargestPossibleRegion().GetSize()[1]; double gradientX, gradientY; gradientX = gradientY = 0.0; double gradientCost; double gradientMagnitude; // Gradient Magnitude costs gradientMagnitude = this->m_GradientMagnitudeImage->GetPixel(p2); gradientX = m_GradientImage->GetPixel(p2)[0]; gradientY = m_GradientImage->GetPixel(p2)[1]; if(m_UseCostMap && !m_CostMap.empty()) { std::map< int, int >::iterator end = m_CostMap.end(); std::map< int, int >::iterator last = --(m_CostMap.end()); //current position std::map< int, int >::iterator x; //std::map< int, int >::key_type keyOfX = static_cast::key_type>(gradientMagnitude * 1000); int keyOfX = static_cast(gradientMagnitude /* ShortestPathCostFunctionLiveWire::MAPSCALEFACTOR*/); x = m_CostMap.find( keyOfX ); std::map< int, int >::iterator left2; std::map< int, int >::iterator left1; std::map< int, int >::iterator right1; std::map< int, int >::iterator right2; if( x == end ) {//x can also be == end if the key is not in the map but between two other keys //search next key within map from x upwards right1 = m_CostMap.lower_bound( keyOfX ); } else { right1 = x; } if(right1 == end || right1 == last ) { right2 = end; } else//( right1 != (end-1) ) { std::map< int, int >::iterator temp = right1; right2 = ++right1;//rght1 + 1 right1 = temp; } if( right1 == m_CostMap.begin() ) { left1 = end; left2 = end; } else if( right1 == (++(m_CostMap.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 = ShortestPathCostFunctionLiveWire::Gaussian(keyOfX, left2->first, left2->second); } if( left1 != end ) { partLeft1 = ShortestPathCostFunctionLiveWire::Gaussian(keyOfX, left1->first, left1->second); } if( right1 != end ) { partRight1 = ShortestPathCostFunctionLiveWire::Gaussian(keyOfX, right1->first, right1->second); } if( right2 != end ) { partRight2 = ShortestPathCostFunctionLiveWire::Gaussian(keyOfX, right2->first, right2->second); } if( m_MaxMapCosts > 0.0 ) { gradientCost = 1.0 - ( (partRight1 + partRight2 + partLeft1 + partLeft2) / m_MaxMapCosts ); } else {//use linear mapping gradientCost = 1.0 - (gradientMagnitude / m_GradientMax); } } else {//use linear mapping //value between 0 (good) and 1 (bad) gradientCost = 1.0 - (gradientMagnitude / m_GradientMax); } // Laplacian zero crossing costs // f(p) = 0; if I(p)=0 // or 1; if I(p)!=0 double laplacianCost; typename Superclass::PixelType laplaceImageValue; laplaceImageValue = m_EdgeImage->GetPixel(p2); if(laplaceImageValue < 0 || laplaceImageValue > 0) { laplacianCost = 1.0; } else { laplacianCost = 0.0; } // Gradient direction costs //vector q-p i.e. p2-p1 double vQP[2]; vQP[0] = p2[0] - p1[0]; vQP[1] = p2[1] - p1[1]; //------- //vector p-q i.e. p1-p2 double vPQ[2]; vPQ[0] = p1[0] - p2[0]; vPQ[1] = p1[1] - p2[1]; //------- // gradient vector at p1 double nGradientAtP1[2]; nGradientAtP1[0] = gradientX;//previously computed for gradient magnitude nGradientAtP1[1] = gradientY; //gradient direction unit vector of p1 nGradientAtP1[0] /= gradientMagnitude; nGradientAtP1[1] /= gradientMagnitude; //------- // gradient vector at p1 double nGradientAtP2[2]; nGradientAtP2[0] = m_GradientImage->GetPixel(p2)[0]; nGradientAtP2[1] = m_GradientImage->GetPixel(p2)[1]; nGradientAtP2[0] /= m_GradientMagnitudeImage->GetPixel(p2); nGradientAtP2[1] /= m_GradientMagnitudeImage->GetPixel(p2); double scalarProduct = (nGradientAtP1[0] * nGradientAtP2[0]) + (nGradientAtP1[1] * nGradientAtP2[1]); if( abs(scalarProduct) >= 1.0) { //this should probably not happen; make sure the input for acos is valid scalarProduct = 0.999999999; } double gradientDirectionCost = acos( scalarProduct ) / 3.14159265; if (this->m_UseCostMap) { w1 = 0.43; w2= 0.43; w3 = 0.14; }else{ w1 = 0.10; w2= 0.85; w3 = 0.05; } costs = w1 * laplacianCost + w2 * gradientCost + w3 * gradientDirectionCost; //scale by euclidian distance double costScale; if( p1[0] == p2[0] || p1[1] == p2[1]) { //horizontal or vertical neighbor costScale = 1.0; } else { //diagonal neighbor costScale = sqrt(2.0); } costs *= costScale; return costs; } template double ShortestPathCostFunctionLiveWire ::GetMinCost() { return minCosts; } template void ShortestPathCostFunctionLiveWire ::Initialize() { if(!m_Initialized) { typedef itk::CastImageFilter< TInputImageType, FloatImageType > CastFilterType; typename CastFilterType::Pointer castFilter = CastFilterType::New(); castFilter->SetInput(this->m_Image); // init gradient magnitude image typedef itk::GradientMagnitudeImageFilter< FloatImageType, FloatImageType> GradientMagnitudeFilterType; typename GradientMagnitudeFilterType::Pointer gradientFilter = GradientMagnitudeFilterType::New(); gradientFilter->SetInput(castFilter->GetOutput()); //gradientFilter->SetNumberOfThreads(4); //gradientFilter->GetOutput()->SetRequestedRegion(m_RequestedRegion); gradientFilter->Update(); this->m_GradientMagnitudeImage = gradientFilter->GetOutput(); typedef itk::StatisticsImageFilter StatisticsImageFilterType; typename StatisticsImageFilterType::Pointer statisticsImageFilter = StatisticsImageFilterType::New(); statisticsImageFilter->SetInput(this->m_GradientMagnitudeImage); statisticsImageFilter->Update(); m_GradientMax = statisticsImageFilter->GetMaximum(); typedef itk::GradientImageFilter< FloatImageType > GradientFilterType; typename GradientFilterType::Pointer filter = GradientFilterType::New(); //sigma is specified in millimeters //filter->SetSigma( 1.5 ); filter->SetInput(castFilter->GetOutput()); filter->Update(); m_GradientImage = filter->GetOutput(); // init zero crossings //typedef itk::ZeroCrossingImageFilter< TInputImageType, UnsignedCharImageType > ZeroCrossingImageFilterType; //ZeroCrossingImageFilterType::Pointer zeroCrossingImageFilter = ZeroCrossingImageFilterType::New(); //zeroCrossingImageFilter->SetInput(this->m_Image); //zeroCrossingImageFilter->SetBackgroundValue(1); //zeroCrossingImageFilter->SetForegroundValue(0); //zeroCrossingImageFilter->SetNumberOfThreads(4); //zeroCrossingImageFilter->Update(); //m_EdgeImage = zeroCrossingImageFilter->GetOutput(); //cast image to float to apply canny edge dection filter /*typedef itk::CastImageFilter< TInputImageType, FloatImageType > CastFilterType; CastFilterType::Pointer castFilter = CastFilterType::New(); castFilter->SetInput(this->m_Image);*/ //typedef itk::LaplacianImageFilter filterType; //filterType::Pointer laplacianFilter = filterType::New(); //laplacianFilter->SetInput( castFilter->GetOutput() ); // NOTE: input image type must be double or float //laplacianFilter->Update(); //m_EdgeImage = laplacianFilter->GetOutput(); //init canny edge detection typedef itk::CannyEdgeDetectionImageFilter CannyEdgeDetectionImageFilterType; typename CannyEdgeDetectionImageFilterType::Pointer cannyEdgeDetectionfilter = CannyEdgeDetectionImageFilterType::New(); cannyEdgeDetectionfilter->SetInput(castFilter->GetOutput()); cannyEdgeDetectionfilter->SetUpperThreshold(30); cannyEdgeDetectionfilter->SetLowerThreshold(15); cannyEdgeDetectionfilter->SetVariance(4); cannyEdgeDetectionfilter->SetMaximumError(.01f); cannyEdgeDetectionfilter->Update(); m_EdgeImage = cannyEdgeDetectionfilter->GetOutput(); // set minCosts minCosts = 0.0; // The lower, the more thouroughly! 0 = dijkstra. If estimate costs are lower than actual costs everything is fine. If estimation is higher than actual costs, you might not get the shortest but a different path. m_Initialized = true; } // check start/end point value startValue= this->m_Image->GetPixel(this->m_StartIndex); endValue= this->m_Image->GetPixel(this->m_EndIndex); } template double ShortestPathCostFunctionLiveWire::SigmoidFunction(double I, double max, double min, double alpha, double beta) { // Using the SIgmoid formula from ITK Software Guide 6.3.2 Non Linear Mappings double Exponent = -1 * ((I - beta) / alpha); double Factor = 1 / (1 + exp(Exponent)); double newI = (max - min) * Factor + min; return newI; } template double ShortestPathCostFunctionLiveWire::Gaussian(double x, double xOfGaussian, double yOfGaussian) { return yOfGaussian * exp( -0.5 * pow( (x - xOfGaussian), 2) ); } } // end namespace itk #endif // __itkShortestPathCostFunctionLiveWire_txx diff --git a/Modules/Segmentation/Interactions/mitkContourModelLiveWireInteractor.cpp b/Modules/Segmentation/Interactions/mitkContourModelLiveWireInteractor.cpp index 1cbf067bf6..93407c25fc 100644 --- a/Modules/Segmentation/Interactions/mitkContourModelLiveWireInteractor.cpp +++ b/Modules/Segmentation/Interactions/mitkContourModelLiveWireInteractor.cpp @@ -1,475 +1,473 @@ /*=================================================================== 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 #include "mitkIOUtil.h" 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()) ); return false; } mitk::StateEvent* newStateEvent = NULL; int timestep = stateEvent->GetEvent()->GetSender()->GetTimeStep(); mitk::ContourModel *contour = dynamic_cast( m_DataNode->GetData() ); assert ( contour ); 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(false); //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); m_NextActiveVertexDownIter = contour->IteratorBegin() + downIndex; m_NextActiveVertexDown = (*m_NextActiveVertexDownIter)->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); m_NextActiveVertexUpIter = contour->IteratorBegin() + upIndex; m_NextActiveVertexUp = (*m_NextActiveVertexUpIter)->Coordinates; // clear previous void positions this->m_LiveWireFilter->ClearRepulsivePoints(); // set the current contour as void positions in the cost map // start with down side mitk::ContourModel::VertexIterator iter = contour->IteratorBegin(timestep); for (;iter != m_NextActiveVertexDownIter; iter++) { itk::Index<2> idx; this->m_WorkingSlice->GetGeometry()->WorldToIndex((*iter)->Coordinates, idx); this->m_LiveWireFilter->AddRepulsivePoint( idx ); } // continue with upper side iter = m_NextActiveVertexUpIter + 1; for (;iter != contour->IteratorEnd(timestep); iter++) { itk::Index<2> idx; this->m_WorkingSlice->GetGeometry()->WorldToIndex((*iter)->Coordinates, idx); this->m_LiveWireFilter->AddRepulsivePoint( idx ); } // clear container with void points between neighboring control points m_ContourBeingModified.clear(); // let us have the selected point as a control point contour->SetSelectedVertexAsControlPoint(true); // finally, allow to leave current state newStateEvent = new mitk::StateEvent(EIDYES, stateEvent->GetEvent()); } else { // do not allow to leave current state newStateEvent = new mitk::StateEvent(EIDNO, stateEvent->GetEvent()); } this->HandleEvent( newStateEvent ); assert( positionEvent->GetSender()->GetRenderWindow() ); mitk::RenderingManager::GetInstance()->RequestUpdate( positionEvent->GetSender()->GetRenderWindow() ); return true; } void mitk::ContourModelLiveWireInteractor::SetEditingContourModelNode (mitk::DataNode* _arg) { if (this->m_EditingContourNode != _arg) { this->m_EditingContourNode = _arg; this->Modified(); } } void mitk::ContourModelLiveWireInteractor::SetWorkingImage (mitk::Image* _arg) { if (this->m_WorkingSlice != _arg) { this->m_WorkingSlice = _arg; this->m_LiveWireFilter->SetInput(this->m_WorkingSlice); this->Modified(); } } bool mitk::ContourModelLiveWireInteractor::OnDeletePoint( Action* action, const StateEvent* stateEvent) { int timestep = stateEvent->GetEvent()->GetSender()->GetTimeStep(); mitk::ContourModel *contour = dynamic_cast( m_DataNode->GetData() ); assert ( contour ); 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( this->m_NextActiveVertexDown ); this->m_LiveWireFilter->SetEndPoint( this->m_NextActiveVertexUp ); // this->m_LiveWireFilter->ClearRepulsivePoints(); this->m_LiveWireFilter->Update(); mitk::ContourModel *liveWireContour = this->m_LiveWireFilter->GetOutput(); assert ( liveWireContour ); if ( liveWireContour->IsEmpty(timestep) ) 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( this->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 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(); mitk::ContourModel *contour = dynamic_cast( m_DataNode->GetData() ); assert ( contour ); + mitk::ContourModel::Pointer editingContour = mitk::ContourModel::New(); + editingContour->Expand(contour->GetTimeSteps()); + // recompute left live wire, i.e. the contour between previous active vertex and selected vertex this->m_LiveWireFilter->SetStartPoint( this->m_NextActiveVertexDown ); this->m_LiveWireFilter->SetEndPoint( currentPosition ); // remove void positions between previous active vertex and next active vertex. if (!m_ContourBeingModified.empty()) { std::vector< itk::Index< 2 > >::const_iterator iter = m_ContourBeingModified.begin(); for (;iter != m_ContourBeingModified.end(); iter++) { this->m_LiveWireFilter->RemoveRepulsivePoint( (*iter) ); } } // update to get the left livewire. Remember that the points in the rest of the contour are already // set as void positions in the filter this->m_LiveWireFilter->Update(); mitk::ContourModel::Pointer leftLiveWire = this->m_LiveWireFilter->GetOutput(); assert ( leftLiveWire ); if ( !leftLiveWire->IsEmpty(timestep) ) leftLiveWire->RemoveVertexAt(0, timestep); + editingContour->Concatenate( leftLiveWire, timestep ); + + //the new index of the selected vertex + unsigned int selectedVertexIndex = this->m_ContourLeft->GetNumberOfVertices(timestep) + leftLiveWire->GetNumberOfVertices(timestep) -1; + // at this point the container has to be empty m_ContourBeingModified.clear(); // add points from left live wire contour mitk::ContourModel::VertexIterator iter = leftLiveWire->IteratorBegin(timestep); for (;iter != leftLiveWire->IteratorEnd(timestep); iter++) { itk::Index<2> idx; this->m_WorkingSlice->GetGeometry()->WorldToIndex((*iter)->Coordinates, idx); this->m_LiveWireFilter->AddRepulsivePoint( idx ); // add indices m_ContourBeingModified.push_back(idx); } // recompute right live wire, i.e. the contour between selected vertex and next active vertex this->m_LiveWireFilter->SetStartPoint( currentPosition ); this->m_LiveWireFilter->SetEndPoint( m_NextActiveVertexUp ); // update filter with all contour points set as void but the right live wire portion to be calculated now this->m_LiveWireFilter->Update(); mitk::ContourModel::Pointer rightLiveWire = this->m_LiveWireFilter->GetOutput(); assert ( rightLiveWire ); // reject strange paths if ( abs (rightLiveWire->GetNumberOfVertices(timestep) - leftLiveWire->GetNumberOfVertices(timestep)) > 50 ) { return false; } if ( !leftLiveWire->IsEmpty(timestep) ) leftLiveWire->SetControlVertexAt(leftLiveWire->GetNumberOfVertices()-1, timestep); if ( !rightLiveWire->IsEmpty(timestep) ) rightLiveWire->RemoveVertexAt(0, timestep); + editingContour->Concatenate( rightLiveWire, timestep ); + // not really needed /* // add points from right live wire contour iter = rightLiveWire->IteratorBegin(timestep); for (;iter != rightLiveWire->IteratorEnd(timestep); iter++) { itk::Index<2> idx; this->m_WorkingSlice->GetGeometry()->WorldToIndex((*iter)->Coordinates, idx); this->m_LiveWireFilter->AddRepulsivePoint( idx ); // add indices m_ContourBeingModified.push_back(idx); } */ - mitk::ContourModel::Pointer editingContour = mitk::ContourModel::New(); - editingContour->Expand(contour->GetTimeSteps()); - - editingContour->Concatenate( leftLiveWire, timestep ); - editingContour->Concatenate( rightLiveWire, timestep ); m_EditingContourNode->SetData(editingContour); 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 but only if we have more than one vertex - if (!leftLiveWire->IsEmpty()) - { - newContour->Concatenate( leftLiveWire, timestep, true); - } + newContour->Concatenate( editingContour, timestep, true); // set last inserted vertex as selected - newContour->SelectVertexAt(newContour->GetNumberOfVertices()-1, timestep); + newContour->SelectVertexAt(selectedVertexIndex, timestep); + + //set as control point + newContour->SetSelectedVertexAsControlPoint(true); - // concatenate right live wire but only if we have more than one vertex - if (!rightLiveWire->IsEmpty()) - { - newContour->Concatenate( rightLiveWire, timestep, true ); - } // 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* srcContour, mitk::ContourModel* destContour, bool fromSelectedUpwards, int timestep) { mitk::ContourModel::VertexIterator end = srcContour->IteratorEnd(); mitk::ContourModel::VertexIterator begin = srcContour->IteratorBegin(); //search next active control point to left and rigth and set as start and end point for filter mitk::ContourModel::VertexIterator itSelected = begin; // move iterator to position while ((*itSelected) != srcContour->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) { destContour->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 = srcContour->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 destContour->AddVertex( (*it)->Coordinates, (*it)->IsControlPoint, timestep); it++; } //copy from begin to itDown while(it <= itDown) { destContour->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 destContour->AddVertex( (*it)->Coordinates, (*it)->IsControlPoint, timestep); it++; } } /* //add vertex at itDown - it's not considered during while loop if( it != begin && it != end) { //destContour->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) { int timestep = stateEvent->GetEvent()->GetSender()->GetTimeStep(); mitk::ContourModel *editingContour = dynamic_cast( this->m_EditingContourNode->GetData() ); assert ( editingContour ); editingContour->Clear(timestep); /* mitk::ContourModel *rightLiveWire = dynamic_cast( this->m_RightLiveWireContourNode->GetData() ); assert ( rightLiveWire ); rightLiveWire->Clear(timestep); */ assert( stateEvent->GetEvent()->GetSender()->GetRenderWindow() ); mitk::RenderingManager::GetInstance()->RequestUpdate( stateEvent->GetEvent()->GetSender()->GetRenderWindow() ); return true; } diff --git a/Modules/Segmentation/Interactions/mitkLiveWireTool2D.cpp b/Modules/Segmentation/Interactions/mitkLiveWireTool2D.cpp index 07f6030edf..962e3de251 100644 --- a/Modules/Segmentation/Interactions/mitkLiveWireTool2D.cpp +++ b/Modules/Segmentation/Interactions/mitkLiveWireTool2D.cpp @@ -1,669 +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 "mitkLiveWireTool2D.h" #include "mitkToolManager.h" #include "mitkBaseRenderer.h" #include "mitkRenderingManager.h" #include "mitkLiveWireTool2D.xpm" #include #include #include "mitkContourUtils.h" #include "mitkContour.h" #include // us #include "mitkModule.h" #include "mitkModuleResource.h" #include namespace mitk { MITK_TOOL_MACRO(Segmentation_EXPORT, LiveWireTool2D, "LiveWire tool"); } mitk::LiveWireTool2D::LiveWireTool2D() :SegTool2D("LiveWireTool") { // 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() { this->m_WorkingContours.clear(); this->m_EditingContours.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; } mitk::ModuleResource mitk::LiveWireTool2D::GetIconResource() const { Module* module = GetModuleContext()->GetModule(); ModuleResource resource = module->GetResource("LiveWire_48x48.png"); return resource; } mitk::ModuleResource mitk::LiveWireTool2D::GetCursorIconResource() const { Module* module = GetModuleContext()->GetModule(); ModuleResource resource = module->GetResource("LiveWire_Cursor_32x32.png"); return resource; } const char* mitk::LiveWireTool2D::GetName() const { return "Live Wire"; } void mitk::LiveWireTool2D::Activated() { Superclass::Activated(); } void mitk::LiveWireTool2D::Deactivated() { this->ClearContours(); Superclass::Deactivated(); } void mitk::LiveWireTool2D::ClearContours() { // for all contours in list (currently created by tool) std::vector< std::pair >::iterator iter = this->m_WorkingContours.begin(); while(iter != this->m_WorkingContours.end() ) { //remove contour node from datastorage m_ToolManager->GetDataStorage()->Remove( iter->first ); ++iter; } this->m_WorkingContours.clear(); // for all contours in list (currently created by tool) std::vector< std::pair >::iterator itEditingContours = this->m_EditingContours.begin(); while(itEditingContours != this->m_EditingContours.end() ) { //remove contour node from datastorage m_ToolManager->GetDataStorage()->Remove( itEditingContours->first ); ++itEditingContours; } this->m_EditingContours.clear(); std::vector< mitk::ContourModelLiveWireInteractor::Pointer >::iterator itLiveWireInteractors = this->m_LiveWireInteractors.begin(); while(itLiveWireInteractors != this->m_LiveWireInteractors.end() ) { // remove interactors from globalInteraction instance mitk::GlobalInteraction::GetInstance()->RemoveInteractor( *itLiveWireInteractors ); ++itLiveWireInteractors; } this->m_LiveWireInteractors.clear(); } void mitk::LiveWireTool2D::ConfirmSegmentation() { DataNode* workingNode( m_ToolManager->GetWorkingData(0) ); assert ( workingNode ); Image* workingImage = dynamic_cast(workingNode->GetData()); assert ( workingImage ); ContourUtils::Pointer contourUtils = mitk::ContourUtils::New(); // for all contours in list (currently created by tool) std::vector< std::pair >::iterator itWorkingContours = this->m_WorkingContours.begin(); while(itWorkingContours != this->m_WorkingContours.end() ) { // if node contains data if( itWorkingContours->first->GetData() ) { // if this is a contourModel mitk::ContourModel* contourModel = dynamic_cast(itWorkingContours->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(itWorkingContours->second, workingImage, currentTimestep); mitk::ContourModel::Pointer projectedContour = contourUtils->ProjectContourTo2DSlice(workingSlice, contourModel, true, false); contourUtils->FillContourInSlice(projectedContour, workingSlice, 1.0); //write back to image volume this->WriteBackSegmentationResult(itWorkingContours->second, workingSlice, currentTimestep); } //remove contour node from datastorage // m_ToolManager->GetDataStorage()->Remove( itWorkingContours->first ); } } ++itWorkingContours; } /* this->m_WorkingContours.clear(); // for all contours in list (currently created by tool) std::vector< std::pair >::iterator itEditingContours = this->m_EditingContours.begin(); while(itEditingContours != this->m_EditingContours.end() ) { //remove contour node from datastorage m_ToolManager->GetDataStorage()->Remove( itEditingContours->first ); ++itEditingContours; } this->m_EditingContours.clear(); std::vector< mitk::ContourModelLiveWireInteractor::Pointer >::iterator itLiveWireInteractors = this->m_LiveWireInteractors.begin(); while(itLiveWireInteractors != this->m_LiveWireInteractors.end() ) { // remove interactors from globalInteraction instance mitk::GlobalInteraction::GetInstance()->RemoveInteractor( *itLiveWireInteractors ); ++itLiveWireInteractors; } this->m_LiveWireInteractors.clear(); */ this->ClearContours(); } bool mitk::LiveWireTool2D::OnInitLiveWire (Action* action, const StateEvent* stateEvent) { 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(1, 1, 0), NULL, true ); m_ContourModelNode->AddProperty( "contour.points.color", ColorProperty::New(1.0, 0.0, 0.1), NULL, true ); m_ContourModelNode->AddProperty( "contour.controlpoints.show", BoolProperty::New(true), NULL, true ); m_LiveWireContour = mitk::ContourModel::New(); m_LiveWireContour->Expand(timestep+1); m_LiveWireContourNode = mitk::DataNode::New(); m_LiveWireContourNode->SetData( m_LiveWireContour ); m_LiveWireContourNode->SetName("active livewire node"); + m_LiveWireContourNode->SetProperty( "layer", IntProperty::New(100)); + m_LiveWireContourNode->SetProperty( "helper object", mitk::BoolProperty::New(true)); m_LiveWireContourNode->AddProperty( "contour.color", ColorProperty::New(0.1, 1.0, 0.1), NULL, true ); m_LiveWireContourNode->AddProperty( "contour.width", mitk::FloatProperty::New( 4.0 ), NULL, true ); m_EditingContour = mitk::ContourModel::New(); m_EditingContour->Expand(timestep+1); m_EditingContourNode = mitk::DataNode::New(); m_EditingContourNode->SetData( m_EditingContour ); m_EditingContourNode->SetName("editing node"); + m_EditingContourNode->SetProperty( "layer", IntProperty::New(100)); + m_EditingContourNode->SetProperty( "helper object", mitk::BoolProperty::New(true)); m_EditingContourNode->AddProperty( "contour.color", ColorProperty::New(0.1, 1.0, 0.1), NULL, true ); m_EditingContourNode->AddProperty( "contour.points.color", ColorProperty::New(0.0, 0.0, 1.0), NULL, true ); m_EditingContourNode->AddProperty( "contour.width", mitk::FloatProperty::New( 4.0 ), NULL, true ); m_ToolManager->GetDataStorage()->Add( m_ContourModelNode ); m_ToolManager->GetDataStorage()->Add( m_LiveWireContourNode ); m_ToolManager->GetDataStorage()->Add( m_EditingContourNode ); //set current slice as input for ImageToLiveWireContourFilter m_WorkingSlice = this->GetAffectedReferenceSlice(positionEvent); m_LiveWireFilter = mitk::ImageLiveWireContourModelFilter::New(); 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() ); 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(); //add repulsive points to avoid to get the same path again typedef mitk::ImageLiveWireContourModelFilter::InternalImageType::IndexType IndexType; mitk::ContourModel::ConstVertexIterator iter = m_LiveWireContour->IteratorBegin(timestep); for (;iter != m_LiveWireContour->IteratorEnd(timestep); iter++) { IndexType idx; this->m_WorkingSlice->GetGeometry()->WorldToIndex((*iter)->Coordinates, idx); this->m_LiveWireFilter->AddRepulsivePoint( idx ); } //remove duplicate first vertex, it's already contained in m_Contour m_LiveWireContour->RemoveVertexAt(0, timestep); // set last added point as control point m_LiveWireContour->SetControlVertexAt(m_LiveWireContour->GetNumberOfVertices(timestep)-1, timestep); //merge contours m_Contour->Concatenate(m_LiveWireContour, timestep); //clear the livewire contour and reset the corresponding datanode m_LiveWireContour->Clear(timestep); //set new start point m_LiveWireFilter->SetStartPoint(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; // actual LiveWire computation int timestep = positionEvent->GetSender()->GetTimeStep(); m_LiveWireFilter->SetEndPoint(const_cast(positionEvent->GetWorldPosition())); m_LiveWireFilter->SetTimeStep(m_TimeStep); m_LiveWireFilter->Update(); m_LiveWireContour = this->m_LiveWireFilter->GetOutput(); m_LiveWireContourNode->SetData( this->m_LiveWireContour ); //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) < 4.5) { // allow to 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; // actual timestep int timestep = positionEvent->GetSender()->GetTimeStep(); // remove last control point being added by double click m_Contour->RemoveVertexAt(m_Contour->GetNumberOfVertices(timestep) - 1, timestep); // save contour and corresponding plane geometry to list std::pair cp(m_ContourModelNode, dynamic_cast(positionEvent->GetSender()->GetCurrentWorldGeometry2D()->Clone().GetPointer()) ); this->m_WorkingContours.push_back(cp); std::pair ecp(m_EditingContourNode, dynamic_cast(positionEvent->GetSender()->GetCurrentWorldGeometry2D()->Clone().GetPointer()) ); this->m_EditingContours.push_back(ecp); 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); } m_ToolManager->GetDataStorage()->Remove( m_LiveWireContourNode ); // clear live wire contour node m_LiveWireContourNode = NULL; m_LiveWireContour = NULL; //change color as visual feedback of completed livewire //m_ContourModelNode->AddProperty( "contour.color", ColorProperty::New(1.0, 1.0, 0.1), NULL, true ); //m_ContourModelNode->SetName("contour node"); //set the livewire interactor to edit control points m_ContourInteractor = mitk::ContourModelLiveWireInteractor::New(m_ContourModelNode); m_ContourInteractor->SetWorkingImage(this->m_WorkingSlice); m_ContourInteractor->SetEditingContourModelNode(this->m_EditingContourNode); m_ContourModelNode->SetInteractor(m_ContourInteractor); this->m_LiveWireInteractors.push_back( m_ContourInteractor ); //add interactor to globalInteraction instance mitk::GlobalInteraction::GetInstance()->AddInteractor(m_ContourInteractor); } 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_ToolManager->GetDataStorage()->Remove( m_EditingContourNode ); m_LiveWireContour = mitk::ContourModel::New(); m_Contour = mitk::ContourModel::New(); m_ContourModelNode->SetData( m_Contour ); m_LiveWireContourNode->SetData( m_LiveWireContour ); 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 }