mitk::ContourModel::Pointer mitk::ContourUtils::BackProjectContourFrom2DSlice(const Geometry3D* sliceGeometry, ContourModel* contourIn2D, bool itkNotUsed( correctionForIpSegmentation ) ) { if ( !sliceGeometry || !contourIn2D ) return NULL; ContourModel::Pointer worldContour = ContourModel::New(); worldContour->Initialize(*contourIn2D); int numberOfTimesteps = contourIn2D->GetTimeGeometry()->CountTimeSteps(); for(int currentTimestep = 0; currentTimestep < numberOfTimesteps; currentTimestep++) { ContourModel::VertexIterator iter = contourIn2D->Begin(currentTimestep); ContourModel::VertexIterator end = contourIn2D->End(currentTimestep); while( iter != end) { Point3D currentPointIn2D = (*iter)->Coordinates; Point3D worldPointIn3D; worldPointIn3D.Fill(0.0); sliceGeometry->IndexToWorld( currentPointIn2D, worldPointIn3D ); //MITK_INFO << "index " << currentPointIn2D << " world " << worldPointIn3D << std::endl; worldContour->AddVertex( worldPointIn3D, currentTimestep ); iter++; } } return worldContour; }
mitk::ContourModel::Pointer mitk::ContourUtils::ProjectContourTo2DSlice(Image* slice, ContourModel* contourIn3D, bool itkNotUsed( correctionForIpSegmentation ), bool constrainToInside) { if ( !slice || !contourIn3D ) return NULL; ContourModel::Pointer projectedContour = ContourModel::New(); projectedContour->Initialize(*contourIn3D); const Geometry3D* sliceGeometry = slice->GetGeometry(); int numberOfTimesteps = contourIn3D->GetTimeGeometry()->CountTimeSteps(); for(int currentTimestep = 0; currentTimestep < numberOfTimesteps; currentTimestep++) { ContourModel::VertexIterator iter = contourIn3D->Begin(currentTimestep); ContourModel::VertexIterator end = contourIn3D->End(currentTimestep); while( iter != end) { Point3D currentPointIn3D = (*iter)->Coordinates; Point3D projectedPointIn2D; projectedPointIn2D.Fill(0.0); sliceGeometry->WorldToIndex( currentPointIn3D, projectedPointIn2D ); // MITK_INFO << "world point " << currentPointIn3D << " in index is " << projectedPointIn2D; if ( !sliceGeometry->IsIndexInside( projectedPointIn2D ) && constrainToInside ) { MITK_INFO << "**" << currentPointIn3D << " is " << projectedPointIn2D << " --> correct it (TODO)" << std::endl; } projectedContour->AddVertex( projectedPointIn2D, currentTimestep ); iter++; } } return projectedContour; }
/** Uses ipSegmentation algorithms to do the actual region growing. The result (binary image) is first smoothed by a 5x5 circle mask, then its contour is extracted and converted to MITK coordinates. */ mitkIpPicDescriptor* mitk::RegionGrowingTool::PerformRegionGrowingAndUpdateContour(int timestep) { // 1. m_OriginalPicSlice and m_SeedPointMemoryOffset are set to sensitive values, as well as m_LowerThreshold and m_UpperThreshold assert (m_OriginalPicSlice); if (m_OriginalPicSlice->n[0] != 256 || m_OriginalPicSlice->n[1] != 256) // ??? assert( (m_SeedPointMemoryOffset < static_cast<int>( m_OriginalPicSlice->n[0] * m_OriginalPicSlice->n[1] )) && (m_SeedPointMemoryOffset >= 0) ); // inside the image // 2. ipSegmentation is used to perform region growing float ignored; int oneContourOffset( 0 ); mitkIpPicDescriptor* regionGrowerResult = ipMITKSegmentationGrowRegion4N( m_OriginalPicSlice, m_SeedPointMemoryOffset, // seed point true, // grayvalue interval relative to seed point gray value? m_LowerThreshold, m_UpperThreshold, 0, // continue until done (maxIterations == 0) NULL, // allocate new memory (only this time, on mouse move we'll reuse the old buffer) oneContourOffset, // a pixel that is near the resulting contour ignored // ignored by us ); if (!regionGrowerResult || oneContourOffset == -1) { ContourModel::Pointer dummyContour = ContourModel::New(); dummyContour->Initialize(); FeedbackContourTool::SetFeedbackContour( *dummyContour ); if (regionGrowerResult) ipMITKSegmentationFree(regionGrowerResult); return NULL; } // 3. We smooth the result a little to reduce contour complexity bool smoothResult( true ); // currently fixed, perhaps remove else block mitkIpPicDescriptor* smoothedRegionGrowerResult; if (smoothResult) { // Smooth the result (otherwise very detailed contour) smoothedRegionGrowerResult = SmoothIPPicBinaryImage( regionGrowerResult, oneContourOffset ); ipMITKSegmentationFree( regionGrowerResult ); } else { smoothedRegionGrowerResult = regionGrowerResult; } // 4. convert the result of region growing into a mitk::Contour // At this point oneContourOffset could be useless, if smoothing destroyed a thin bridge. In these // cases, we have two or more unconnected segmentation regions, and we don't know, which one is touched by oneContourOffset. // In the bad case, the contour is not the one around our seedpoint, so the result looks very strange to the user. // -> we remove the point where the contour started so far. Then we look from the bottom of the image for the first segmentation pixel // and start another contour extraction from there. This is done, until the seedpoint is inside the contour int numberOfContourPoints( 0 ); int newBufferSize( 0 ); float* contourPoints = ipMITKSegmentationGetContour8N( smoothedRegionGrowerResult, oneContourOffset, numberOfContourPoints, newBufferSize ); // memory allocated with malloc if (contourPoints) { while ( !ipMITKSegmentationIsInsideContour( contourPoints, // contour numberOfContourPoints, // points in contour m_SeedPointMemoryOffset % smoothedRegionGrowerResult->n[0], // test point x m_SeedPointMemoryOffset / smoothedRegionGrowerResult->n[0] // test point y ) ) { // we decide that this cannot be part of the segmentation because the seedpoint is not contained in the contour (fill the 4-neighborhood with 0) ipMITKSegmentationReplaceRegion4N( smoothedRegionGrowerResult, oneContourOffset, 0 ); // move the contour offset to the last row (x position of the seed point) int rowLength = smoothedRegionGrowerResult->n[0]; // number of pixels in a row oneContourOffset = m_SeedPointMemoryOffset % smoothedRegionGrowerResult->n[0] // x of seed point + rowLength*(smoothedRegionGrowerResult->n[1]-1); // y of last row while ( oneContourOffset >=0 && (*(static_cast<ipMITKSegmentationTYPE*>(smoothedRegionGrowerResult->data) + oneContourOffset) == 0) ) { oneContourOffset -= rowLength; // if pixel at data+oneContourOffset is 0, then move up one row } if ( oneContourOffset < 0 ) { break; // just use the last contour we found } free(contourPoints); // release contour memory contourPoints = ipMITKSegmentationGetContour8N( smoothedRegionGrowerResult, oneContourOffset, numberOfContourPoints, newBufferSize ); // memory allocated with malloc } // copy point from float* to mitk::Contour ContourModel::Pointer contourInImageIndexCoordinates = ContourModel::New(); contourInImageIndexCoordinates->Expand(timestep + 1); contourInImageIndexCoordinates->SetClosed(true, timestep); Point3D newPoint; for (int index = 0; index < numberOfContourPoints; ++index) { newPoint[0] = contourPoints[ 2 * index + 0 ] - 0.5;//correction is needed because the output of the algorithm is center based newPoint[1] = contourPoints[ 2 * index + 1 ] - 0.5;//and we want our contour displayed corner based. newPoint[2] = 0; contourInImageIndexCoordinates->AddVertex( newPoint, timestep ); } free(contourPoints); ContourModel::Pointer contourInWorldCoordinates = FeedbackContourTool::BackProjectContourFrom2DSlice( m_ReferenceSlice->GetGeometry(), contourInImageIndexCoordinates, true ); // true: sub 0.5 for ipSegmentation correctio FeedbackContourTool::SetFeedbackContour( *contourInWorldCoordinates ); } // 5. Result HAS TO BE freed by caller, contains the binary region growing result return smoothedRegionGrowerResult; }
/** Insert the point to the feedback contour,finish to build the contour and at the same time the painting function */ bool mitk::PaintbrushTool::MouseMoved(mitk::InteractionEvent* interactionEvent, bool leftMouseButtonPressed) { if ( SegTool2D::CanHandleEvent(interactionEvent) < 1.0 ) return false; mitk::InteractionPositionEvent* positionEvent = dynamic_cast<mitk::InteractionPositionEvent*>( interactionEvent ); //const PositionEvent* positionEvent = dynamic_cast<const PositionEvent*>(stateEvent->GetEvent()); CheckIfCurrentSliceHasChanged( positionEvent ); if ( m_LastContourSize != m_Size ) { UpdateContour( positionEvent ); m_LastContourSize = m_Size; } // stateEvent->GetId() == 530 // || stateEvent->GetId() == 534 // || stateEvent->GetId() == 1 // || stateEvent->GetId() == 5 // ); Point3D worldCoordinates = positionEvent->GetPositionInWorld(); Point3D indexCoordinates; m_WorkingSlice->GetGeometry()->WorldToIndex( worldCoordinates, indexCoordinates ); MITK_DEBUG << "Mouse at W " << worldCoordinates << std::endl; MITK_DEBUG << "Mouse at I " << indexCoordinates << std::endl; // round to nearest voxel center (abort if this hasn't changed) if ( m_Size % 2 == 0 ) // even { indexCoordinates[0] = ROUND( indexCoordinates[0]);// /*+ 0.5*/) + 0.5; indexCoordinates[1] = ROUND( indexCoordinates[1]);// /*+ 0.5*/ ) + 0.5; } else // odd { indexCoordinates[0] = ROUND( indexCoordinates[0] ) ; indexCoordinates[1] = ROUND( indexCoordinates[1] ) ; } static Point3D lastPos; // uninitialized: if somebody finds out how this can be initialized in a one-liner, tell me if ( fabs(indexCoordinates[0] - lastPos[0]) > mitk::eps || fabs(indexCoordinates[1] - lastPos[1]) > mitk::eps || fabs(indexCoordinates[2] - lastPos[2]) > mitk::eps || leftMouseButtonPressed ) { lastPos = indexCoordinates; } else { MITK_DEBUG << "." << std::flush; return false; } MITK_DEBUG << "Mouse at C " << indexCoordinates; int timestep = positionEvent->GetSender()->GetTimeStep(); ContourModel::Pointer contour = ContourModel::New(); contour->Expand(timestep + 1); contour->SetClosed(true, timestep); ContourModel::VertexIterator it = m_MasterContour->Begin(); ContourModel::VertexIterator end = m_MasterContour->End(); while(it != end) { Point3D point = (*it)->Coordinates; point[0] += indexCoordinates[ 0 ]; point[1] += indexCoordinates[ 1 ]; contour->AddVertex( point, timestep ); it++; } if (leftMouseButtonPressed) { FeedbackContourTool::FillContourInSlice( contour, timestep, m_WorkingSlice, m_PaintingPixelValue ); m_WorkingNode->SetData(m_WorkingSlice); m_WorkingNode->Modified(); } // visualize contour ContourModel::Pointer displayContour = ContourModel::New(); displayContour->Initialize(); //for (unsigned int index = 0; index < contour->GetNumberOfPoints(); ++index) //{ // Point3D point = contour->GetPoints()->ElementAt(index); // if ( m_Size % 2 == 0 ) // even // { // point[0] += 0.5; // point[1] += 0.5; // } // displayContour->AddVertex( point ); //} displayContour = FeedbackContourTool::BackProjectContourFrom2DSlice( m_WorkingSlice->GetGeometry(), /*displayContour*/contour ); SetFeedbackContour( *displayContour ); assert( positionEvent->GetSender()->GetRenderWindow() ); RenderingManager::GetInstance()->RequestUpdate( positionEvent->GetSender()->GetRenderWindow() ); return true; }