diff --git a/Modules/Segmentation/Interactions/mitkSegTool2D.cpp b/Modules/Segmentation/Interactions/mitkSegTool2D.cpp index cd83b0f699..306e056beb 100644 --- a/Modules/Segmentation/Interactions/mitkSegTool2D.cpp +++ b/Modules/Segmentation/Interactions/mitkSegTool2D.cpp @@ -1,629 +1,643 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #include "mitkSegTool2D.h" #include "mitkToolManager.h" #include "mitkBaseRenderer.h" #include "mitkDataStorage.h" #include "mitkPlaneGeometry.h" #include "mitkExtractDirectedPlaneImageFilter.h" #include "mitkExtractImageFilter.h" // Include of the new ImageExtractor #include "mitkExtractDirectedPlaneImageFilterNew.h" #include "mitkMorphologicalOperations.h" #include "mitkOverwriteDirectedPlaneImageFilter.h" #include "mitkOverwriteSliceImageFilter.h" #include "mitkPlanarCircle.h" #include "usGetModuleContext.h" // Includes for 3DSurfaceInterpolation #include "mitkImageTimeSelector.h" #include "mitkImageToContourFilter.h" #include "mitkSurfaceInterpolationController.h" // includes for resling and overwriting #include #include #include #include #include "mitkOperationEvent.h" #include "mitkUndoController.h" #include #include "mitkAbstractTransformGeometry.h" #include "mitkImageAccessByItk.h" #include "mitkImageCast.h" #include "mitkImageToItk.h" #include "mitkLabelSetImage.h" #define ROUND(a) ((a) > 0 ? (int)((a) + 0.5) : -(int)(0.5 - (a))) bool mitk::SegTool2D::m_SurfaceInterpolationEnabled = true; mitk::SegTool2D::SegTool2D(const char *type, const us::Module *interactorModule) : Tool(type, interactorModule), m_LastEventSender(nullptr), m_LastEventSlice(0), m_Contourmarkername("Position"), m_ShowMarkerNodes(false) { Tool::m_EventConfig = "DisplayConfigMITKNoCrosshair.xml"; } mitk::SegTool2D::~SegTool2D() { } bool mitk::SegTool2D::FilterEvents(InteractionEvent *interactionEvent, DataNode *) { const auto *positionEvent = dynamic_cast(interactionEvent); bool isValidEvent = (positionEvent && // Only events of type mitk::InteractionPositionEvent interactionEvent->GetSender()->GetMapperID() == BaseRenderer::Standard2D // Only events from the 2D renderwindows ); return isValidEvent; } bool mitk::SegTool2D::DetermineAffectedImageSlice(const Image *image, const PlaneGeometry *plane, int &affectedDimension, int &affectedSlice) { assert(image); assert(plane); // compare normal of plane to the three axis vectors of the image Vector3D normal = plane->GetNormal(); Vector3D imageNormal0 = image->GetSlicedGeometry()->GetAxisVector(0); Vector3D imageNormal1 = image->GetSlicedGeometry()->GetAxisVector(1); Vector3D imageNormal2 = image->GetSlicedGeometry()->GetAxisVector(2); normal.Normalize(); imageNormal0.Normalize(); imageNormal1.Normalize(); imageNormal2.Normalize(); imageNormal0.SetVnlVector(vnl_cross_3d(normal.GetVnlVector(), imageNormal0.GetVnlVector())); imageNormal1.SetVnlVector(vnl_cross_3d(normal.GetVnlVector(), imageNormal1.GetVnlVector())); imageNormal2.SetVnlVector(vnl_cross_3d(normal.GetVnlVector(), imageNormal2.GetVnlVector())); double eps(0.00001); // axial if (imageNormal2.GetNorm() <= eps) { affectedDimension = 2; } // sagittal else if (imageNormal1.GetNorm() <= eps) { affectedDimension = 1; } // frontal else if (imageNormal0.GetNorm() <= eps) { affectedDimension = 0; } else { affectedDimension = -1; // no idea return false; } // determine slice number in image BaseGeometry *imageGeometry = image->GetGeometry(0); Point3D testPoint = imageGeometry->GetCenter(); Point3D projectedPoint; plane->Project(testPoint, projectedPoint); Point3D indexPoint; imageGeometry->WorldToIndex(projectedPoint, indexPoint); affectedSlice = ROUND(indexPoint[affectedDimension]); MITK_DEBUG << "indexPoint " << indexPoint << " affectedDimension " << affectedDimension << " affectedSlice " << affectedSlice; // check if this index is still within the image if (affectedSlice < 0 || affectedSlice >= static_cast(image->GetDimension(affectedDimension))) return false; return true; } void mitk::SegTool2D::UpdateSurfaceInterpolation(const Image *slice, const Image *workingImage, const PlaneGeometry *plane, bool detectIntersection) { if (!m_SurfaceInterpolationEnabled) return; ImageToContourFilter::Pointer contourExtractor = ImageToContourFilter::New(); mitk::Surface::Pointer contour; if (detectIntersection) { // Test whether there is something to extract or whether the slice just contains intersections of others mitk::Image::Pointer slice2 = slice->Clone(); mitk::MorphologicalOperations::Erode(slice2, 2, mitk::MorphologicalOperations::Ball); contourExtractor->SetInput(slice2); contourExtractor->Update(); contour = contourExtractor->GetOutput(); if (contour->GetVtkPolyData()->GetNumberOfPoints() == 0) { // Remove contour! mitk::SurfaceInterpolationController::ContourPositionInformation contourInfo; contourInfo.contourNormal = plane->GetNormal(); contourInfo.contourPoint = plane->GetOrigin(); mitk::SurfaceInterpolationController::GetInstance()->RemoveContour(contourInfo); return; } } contourExtractor->SetInput(slice); contourExtractor->Update(); contour = contourExtractor->GetOutput(); mitk::ImageTimeSelector::Pointer timeSelector = mitk::ImageTimeSelector::New(); timeSelector->SetInput(workingImage); timeSelector->SetTimeNr(0); timeSelector->SetChannelNr(0); timeSelector->Update(); Image::Pointer dimRefImg = timeSelector->GetOutput(); if (contour->GetVtkPolyData()->GetNumberOfPoints() != 0 && dimRefImg->GetDimension() == 3) { mitk::SurfaceInterpolationController::GetInstance()->AddNewContour(contour); contour->DisconnectPipeline(); } else { // Remove contour! mitk::SurfaceInterpolationController::ContourPositionInformation contourInfo; contourInfo.contourNormal = plane->GetNormal(); contourInfo.contourPoint = plane->GetOrigin(); mitk::SurfaceInterpolationController::GetInstance()->RemoveContour(contourInfo); } } mitk::Image::Pointer mitk::SegTool2D::GetAffectedImageSliceAs2DImage(const InteractionPositionEvent *positionEvent, const Image *image, unsigned int component /*= 0*/) { if (!positionEvent) { return nullptr; } assert(positionEvent->GetSender()); // sure, right? unsigned int timeStep = positionEvent->GetSender()->GetTimeStep(image); // get the timestep of the visible part (time-wise) of the image return GetAffectedImageSliceAs2DImage(positionEvent->GetSender()->GetCurrentWorldPlaneGeometry(), image, timeStep, component); } -mitk::Image::Pointer mitk::SegTool2D::GetAffectedImageSliceAs2DImage(const PlaneGeometry *planeGeometry, const Image *image, unsigned int timeStep, unsigned int component /*= 0*/) +mitk::Image::Pointer mitk::SegTool2D::GetAffectedImageSliceAs2DImageByTimePoint(const PlaneGeometry* planeGeometry, const Image* image, TimePointType timePoint, unsigned int component /*= 0*/) +{ + if (!image || !planeGeometry) + { + return nullptr; + } + + if (!image->GetTimeGeometry()->IsValidTimePoint(timePoint)) + return nullptr; + + return SegTool2D::GetAffectedImageSliceAs2DImage(planeGeometry, image, image->GetTimeGeometry()->TimePointToTimeStep(timePoint), component); +} + + +mitk::Image::Pointer mitk::SegTool2D::GetAffectedImageSliceAs2DImage(const PlaneGeometry *planeGeometry, const Image *image, TimeStepType timeStep, unsigned int component /*= 0*/) { if (!image || !planeGeometry) { return nullptr; } // Make sure that for reslicing and overwriting the same alogrithm is used. We can specify the mode of the vtk reslicer vtkSmartPointer reslice = vtkSmartPointer::New(); // set to false to extract a slice reslice->SetOverwriteMode(false); reslice->Modified(); // use ExtractSliceFilter with our specific vtkImageReslice for overwriting and extracting mitk::ExtractSliceFilter::Pointer extractor = mitk::ExtractSliceFilter::New(reslice); extractor->SetInput(image); extractor->SetTimeStep(timeStep); extractor->SetWorldGeometry(planeGeometry); extractor->SetVtkOutputRequest(false); extractor->SetResliceTransformByGeometry(image->GetTimeGeometry()->GetGeometryForTimeStep(timeStep)); // additionally extract the given component // default is 0; the extractor checks for multi-component images extractor->SetComponent(component); extractor->Modified(); extractor->Update(); Image::Pointer slice = extractor->GetOutput(); return slice; } mitk::Image::Pointer mitk::SegTool2D::GetAffectedWorkingSlice(const InteractionPositionEvent *positionEvent) { DataNode *workingNode(m_ToolManager->GetWorkingData(0)); if (!workingNode) { return nullptr; } auto *workingImage = dynamic_cast(workingNode->GetData()); if (!workingImage) { return nullptr; } return GetAffectedImageSliceAs2DImage(positionEvent, workingImage); } mitk::Image::Pointer mitk::SegTool2D::GetAffectedReferenceSlice(const InteractionPositionEvent *positionEvent) { DataNode *referenceNode(m_ToolManager->GetReferenceData(0)); if (!referenceNode) { return nullptr; } auto *referenceImage = dynamic_cast(referenceNode->GetData()); if (!referenceImage) { return nullptr; } int displayedComponent = 0; if (referenceNode->GetIntProperty("Image.Displayed Component", displayedComponent)) { // found the displayed component return GetAffectedImageSliceAs2DImage(positionEvent, referenceImage, displayedComponent); } else { return GetAffectedImageSliceAs2DImage(positionEvent, referenceImage); } } void mitk::SegTool2D::WriteBackSegmentationResult(const InteractionPositionEvent *positionEvent, Image *slice) { if (!positionEvent) return; const PlaneGeometry *planeGeometry((positionEvent->GetSender()->GetCurrentWorldPlaneGeometry())); const auto *abstractTransformGeometry( dynamic_cast(positionEvent->GetSender()->GetCurrentWorldPlaneGeometry())); if (planeGeometry && slice && !abstractTransformGeometry) { DataNode *workingNode(m_ToolManager->GetWorkingData(0)); auto *image = dynamic_cast(workingNode->GetData()); unsigned int timeStep = positionEvent->GetSender()->GetTimeStep(image); this->WriteBackSegmentationResult(planeGeometry, slice, timeStep); } } void mitk::SegTool2D::WriteBackSegmentationResult(const PlaneGeometry *planeGeometry, Image *slice, unsigned int timeStep) { if (!planeGeometry || !slice) return; SliceInformation sliceInfo(slice, const_cast(planeGeometry), timeStep); this->WriteSliceToVolume(sliceInfo); DataNode *workingNode(m_ToolManager->GetWorkingData(0)); auto *image = dynamic_cast(workingNode->GetData()); this->UpdateSurfaceInterpolation(slice, image, planeGeometry, false); if (m_SurfaceInterpolationEnabled) this->AddContourmarker(); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } void mitk::SegTool2D::WriteBackSegmentationResult(const std::vector &sliceList, bool writeSliceToVolume) { std::vector contourList; contourList.reserve(sliceList.size()); ImageToContourFilter::Pointer contourExtractor = ImageToContourFilter::New(); DataNode *workingNode(m_ToolManager->GetWorkingData(0)); auto *image = dynamic_cast(workingNode->GetData()); mitk::ImageTimeSelector::Pointer timeSelector = mitk::ImageTimeSelector::New(); timeSelector->SetInput(image); timeSelector->SetTimeNr(0); timeSelector->SetChannelNr(0); timeSelector->Update(); Image::Pointer dimRefImg = timeSelector->GetOutput(); for (unsigned int i = 0; i < sliceList.size(); ++i) { SliceInformation currentSliceInfo = sliceList.at(i); if (writeSliceToVolume) this->WriteSliceToVolume(currentSliceInfo); if (m_SurfaceInterpolationEnabled && dimRefImg->GetDimension() == 3) { currentSliceInfo.slice->DisconnectPipeline(); contourExtractor->SetInput(currentSliceInfo.slice); contourExtractor->Update(); mitk::Surface::Pointer contour = contourExtractor->GetOutput(); contour->DisconnectPipeline(); contourList.push_back(contour); } } mitk::SurfaceInterpolationController::GetInstance()->AddNewContours(contourList); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } void mitk::SegTool2D::WriteSliceToVolume(const mitk::SegTool2D::SliceInformation &sliceInfo) { DataNode *workingNode(m_ToolManager->GetWorkingData(0)); auto *image = dynamic_cast(workingNode->GetData()); /*============= BEGIN undo/redo feature block ========================*/ // Create undo operation by caching the not yet modified slices mitk::Image::Pointer originalSlice = GetAffectedImageSliceAs2DImage(sliceInfo.plane, image, sliceInfo.timestep); auto *undoOperation = new DiffSliceOperation(image, originalSlice, dynamic_cast(originalSlice->GetGeometry()), sliceInfo.timestep, sliceInfo.plane); /*============= END undo/redo feature block ========================*/ // Make sure that for reslicing and overwriting the same alogrithm is used. We can specify the mode of the vtk // reslicer vtkSmartPointer reslice = vtkSmartPointer::New(); // Set the slice as 'input' reslice->SetInputSlice(sliceInfo.slice->GetVtkImageData()); // set overwrite mode to true to write back to the image volume reslice->SetOverwriteMode(true); reslice->Modified(); mitk::ExtractSliceFilter::Pointer extractor = mitk::ExtractSliceFilter::New(reslice); extractor->SetInput(image); extractor->SetTimeStep(sliceInfo.timestep); extractor->SetWorldGeometry(sliceInfo.plane); extractor->SetVtkOutputRequest(false); extractor->SetResliceTransformByGeometry(image->GetGeometry(sliceInfo.timestep)); extractor->Modified(); extractor->Update(); // the image was modified within the pipeline, but not marked so image->Modified(); image->GetVtkImageData()->Modified(); // also mark its node as modified (T27308). Can be removed if T27307 // is properly solved if (workingNode != nullptr) workingNode->Modified(); /*============= BEGIN undo/redo feature block ========================*/ // specify the undo operation with the edited slice auto *doOperation = new DiffSliceOperation(image, extractor->GetOutput(), dynamic_cast(sliceInfo.slice->GetGeometry()), sliceInfo.timestep, sliceInfo.plane); // create an operation event for the undo stack OperationEvent *undoStackItem = new OperationEvent(DiffSliceOperationApplier::GetInstance(), doOperation, undoOperation, "Segmentation"); // add it to the undo controller UndoStackItem::IncCurrObjectEventId(); UndoStackItem::IncCurrGroupEventId(); UndoController::GetCurrentUndoModel()->SetOperationEvent(undoStackItem); // clear the pointers as the operation are stored in the undocontroller and also deleted from there undoOperation = nullptr; doOperation = nullptr; /*============= END undo/redo feature block ========================*/ } void mitk::SegTool2D::SetShowMarkerNodes(bool status) { m_ShowMarkerNodes = status; } void mitk::SegTool2D::SetEnable3DInterpolation(bool enabled) { m_SurfaceInterpolationEnabled = enabled; } int mitk::SegTool2D::AddContourmarker() { if (m_LastEventSender == nullptr) return -1; us::ServiceReference serviceRef = us::GetModuleContext()->GetServiceReference(); PlanePositionManagerService *service = us::GetModuleContext()->GetService(serviceRef); unsigned int slicePosition = m_LastEventSender->GetSliceNavigationController()->GetSlice()->GetPos(); // the first geometry is needed otherwise restoring the position is not working const auto *plane = dynamic_cast(dynamic_cast( m_LastEventSender->GetSliceNavigationController()->GetCurrentGeometry3D()) ->GetPlaneGeometry(0)); unsigned int size = service->GetNumberOfPlanePositions(); unsigned int id = service->AddNewPlanePosition(plane, slicePosition); mitk::PlanarCircle::Pointer contourMarker = mitk::PlanarCircle::New(); mitk::Point2D p1; plane->Map(plane->GetCenter(), p1); mitk::Point2D p2 = p1; p2[0] -= plane->GetSpacing()[0]; p2[1] -= plane->GetSpacing()[1]; contourMarker->PlaceFigure(p1); contourMarker->SetCurrentControlPoint(p1); contourMarker->SetPlaneGeometry(const_cast(plane)); std::stringstream markerStream; mitk::DataNode *workingNode(m_ToolManager->GetWorkingData(0)); markerStream << m_Contourmarkername; markerStream << " "; markerStream << id + 1; DataNode::Pointer rotatedContourNode = DataNode::New(); rotatedContourNode->SetData(contourMarker); rotatedContourNode->SetProperty("name", StringProperty::New(markerStream.str())); rotatedContourNode->SetProperty("isContourMarker", BoolProperty::New(true)); rotatedContourNode->SetBoolProperty("PlanarFigureInitializedWindow", true, m_LastEventSender); rotatedContourNode->SetProperty("includeInBoundingBox", BoolProperty::New(false)); rotatedContourNode->SetProperty("helper object", mitk::BoolProperty::New(!m_ShowMarkerNodes)); rotatedContourNode->SetProperty("planarfigure.drawcontrolpoints", BoolProperty::New(false)); rotatedContourNode->SetProperty("planarfigure.drawname", BoolProperty::New(false)); rotatedContourNode->SetProperty("planarfigure.drawoutline", BoolProperty::New(false)); rotatedContourNode->SetProperty("planarfigure.drawshadow", BoolProperty::New(false)); if (plane) { if (id == size) { m_ToolManager->GetDataStorage()->Add(rotatedContourNode, workingNode); } else { mitk::NodePredicateProperty::Pointer isMarker = mitk::NodePredicateProperty::New("isContourMarker", mitk::BoolProperty::New(true)); mitk::DataStorage::SetOfObjects::ConstPointer markers = m_ToolManager->GetDataStorage()->GetDerivations(workingNode, isMarker); for (auto iter = markers->begin(); iter != markers->end(); ++iter) { std::string nodeName = (*iter)->GetName(); unsigned int t = nodeName.find_last_of(" "); unsigned int markerId = atof(nodeName.substr(t + 1).c_str()) - 1; if (id == markerId) { return id; } } m_ToolManager->GetDataStorage()->Add(rotatedContourNode, workingNode); } } return id; } void mitk::SegTool2D::InteractiveSegmentationBugMessage(const std::string &message) { MITK_ERROR << "********************************************************************************" << std::endl << " " << message << std::endl << "********************************************************************************" << std::endl << " " << std::endl << " If your image is rotated or the 2D views don't really contain the patient image, try to press the " "button next to the image selection. " << std::endl << " " << std::endl << " Please file a BUG REPORT: " << std::endl << " https://phabricator.mitk.org/" << std::endl << " Contain the following information:" << std::endl << " - What image were you working on?" << std::endl << " - Which region of the image?" << std::endl << " - Which tool did you use?" << std::endl << " - What did you do?" << std::endl << " - What happened (not)? What did you expect?" << std::endl; } template void InternalWritePreviewOnWorkingImage(itk::Image *targetSlice, const mitk::Image *sourceSlice, mitk::Image *originalImage, int overwritevalue) { typedef itk::Image SliceType; typename SliceType::Pointer sourceSliceITK; CastToItkImage(sourceSlice, sourceSliceITK); // now the original slice and the ipSegmentation-painted slice are in the same format, and we can just copy all pixels // that are non-zero typedef itk::ImageRegionIterator OutputIteratorType; typedef itk::ImageRegionConstIterator InputIteratorType; InputIteratorType inputIterator(sourceSliceITK, sourceSliceITK->GetLargestPossibleRegion()); OutputIteratorType outputIterator(targetSlice, targetSlice->GetLargestPossibleRegion()); outputIterator.GoToBegin(); inputIterator.GoToBegin(); auto *workingImage = dynamic_cast(originalImage); assert(workingImage); int activePixelValue = workingImage->GetActiveLabel()->GetValue(); if (activePixelValue == 0) // if exterior is the active label { while (!outputIterator.IsAtEnd()) { if (inputIterator.Get() != 0) { outputIterator.Set(overwritevalue); } ++outputIterator; ++inputIterator; } } else if (overwritevalue != 0) // if we are not erasing { while (!outputIterator.IsAtEnd()) { auto targetValue = static_cast(outputIterator.Get()); if (inputIterator.Get() != 0) { if (!workingImage->GetLabel(targetValue)->GetLocked()) { outputIterator.Set(overwritevalue); } } if (targetValue == overwritevalue) { outputIterator.Set(inputIterator.Get()); } ++outputIterator; ++inputIterator; } } else // if we are erasing { while (!outputIterator.IsAtEnd()) { const int targetValue = outputIterator.Get(); if (inputIterator.Get() != 0) { if (targetValue == activePixelValue) outputIterator.Set(overwritevalue); } ++outputIterator; ++inputIterator; } } } void mitk::SegTool2D::WritePreviewOnWorkingImage( Image *targetSlice, Image *sourceSlice, mitk::Image *workingImage, int paintingPixelValue, int) { if ((!targetSlice) || (!sourceSlice)) return; AccessFixedDimensionByItk_3( targetSlice, InternalWritePreviewOnWorkingImage, 2, sourceSlice, workingImage, paintingPixelValue); } diff --git a/Modules/Segmentation/Interactions/mitkSegTool2D.h b/Modules/Segmentation/Interactions/mitkSegTool2D.h index 474b90fae7..19f28b89b3 100644 --- a/Modules/Segmentation/Interactions/mitkSegTool2D.h +++ b/Modules/Segmentation/Interactions/mitkSegTool2D.h @@ -1,199 +1,205 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #ifndef mitkSegTool2D_h_Included #define mitkSegTool2D_h_Included #include "mitkCommon.h" #include "mitkImage.h" #include "mitkTool.h" #include #include "mitkInteractionPositionEvent.h" #include "mitkInteractionConst.h" #include "mitkPlanePositionManager.h" #include "mitkRestorePlanePositionOperation.h" #include namespace mitk { class BaseRenderer; /** \brief Abstract base class for segmentation tools. \sa Tool \ingroup Interaction \ingroup ToolManagerEtAl Implements 2D segmentation specific helper methods, that might be of use to all kind of 2D segmentation tools. At the moment these are: - Determination of the slice where the user paints upon (DetermineAffectedImageSlice) - Projection of a 3D contour onto a 2D plane/slice SegTool2D tries to structure the interaction a bit. If you pass "PressMoveRelease" as the interaction type of your derived tool, you might implement the methods OnMousePressed, OnMouseMoved, and OnMouseReleased. Yes, your guess about when they are called is correct. \warning Only to be instantiated by mitk::ToolManager. $Author$ */ class MITKSEGMENTATION_EXPORT SegTool2D : public Tool { public: mitkClassMacro(SegTool2D, Tool); /** \brief Calculates for a given Image and PlaneGeometry, which slice of the image (in index corrdinates) is meant by the plane. \return false, if no slice direction seems right (e.g. rotated planes) \param image \param plane \param affectedDimension The image dimension, which is constant for all points in the plane, e.g. Axial --> 2 \param affectedSlice The index of the image slice */ static bool DetermineAffectedImageSlice(const Image *image, const PlaneGeometry *plane, int &affectedDimension, int &affectedSlice); /** * @brief Updates the surface interpolation by extracting the contour form the given slice. * @param slice the slice from which the contour should be extracted * @param workingImage the segmentation image * @param plane the plane in which the slice lies * @param detectIntersection if true the slice is eroded before contour extraction. If the slice is empty after the * erosion it is most * likely an intersecting contour an will not be added to the SurfaceInterpolationController */ static void UpdateSurfaceInterpolation(const Image *slice, const Image *workingImage, const PlaneGeometry *plane, bool detectIntersection); + /** + * \brief Extract the slice of an image that the user just scribbles on. The given component denotes the vector component of a dwi-image. + * + * \param positionEvent Event that specifies the plane that should be used to slice + * \param image Image that should be sliced + * \param timeStep TimeStep of the image that shold be sliced + * \param component The component to be extracted of a given multi-component image. -1 is the default parameter to denote an invalid component. + * + * \return 'nullptr' if SegTool2D is either unable to determine which slice was affected, or if there was some problem + * getting the image data at that position. + */ + static Image::Pointer GetAffectedImageSliceAs2DImage(const InteractionPositionEvent* positionEvent, const Image* image, unsigned int component = 0); + + /** + * \brief Extract the slice of an image cut by given plane. The given component denotes the vector component of a dwi-image. + * + * \param planeGeometry Geometry defining the slice that should be cut out. + * \param image Image that should be sliced + * \param timeStep TimeStep of the image that shold be sliced + * \param component The component to be extracted of a given multi-component image. -1 is the default parameter to denote an invalid component. + * + * \return 'nullptr' if SegTool2D is either unable to determine which slice was affected, or if there was some problem + * getting the image data at that position. + */ + static Image::Pointer GetAffectedImageSliceAs2DImage(const PlaneGeometry* planeGeometry, + const Image* image, + TimeStepType timeStep, + unsigned int component = 0); + static Image::Pointer GetAffectedImageSliceAs2DImageByTimePoint(const PlaneGeometry* planeGeometry, + const Image* image, + TimePointType timePoint, + unsigned int component = 0); + + void SetShowMarkerNodes(bool); /** * \brief Enables or disables the 3D interpolation after writing back the 2D segmentation result, and defaults to * true. */ void SetEnable3DInterpolation(bool); protected: SegTool2D(); // purposely hidden SegTool2D(const char *, const us::Module *interactorModule = nullptr); // purposely hidden ~SegTool2D() override; struct SliceInformation { mitk::Image::Pointer slice; mitk::PlaneGeometry *plane; - unsigned int timestep; + mitk::TimeStepType timestep; SliceInformation() {} - SliceInformation(mitk::Image *slice, mitk::PlaneGeometry *plane, unsigned int timestep) + SliceInformation(mitk::Image *slice, mitk::PlaneGeometry *plane, mitk::TimeStepType timestep) { this->slice = slice; this->plane = plane; this->timestep = timestep; } }; /** * \brief Filters events that cannot be handle by 2D segmentation tools * * Current an event is discarded if it was not sent by a 2D renderwindow and if it is * not of type InteractionPositionEvent */ bool FilterEvents(InteractionEvent *interactionEvent, DataNode *dataNode) override; - /** - * \brief Extract the slice of an image that the user just scribbles on. The given component denotes the vector component of a dwi-image. - * - * \param positionEvent - * \param image - * \param component The component to be extracted of a given multi-component image. -1 is the default parameter to denote an invalid component. - * - * \return 'nullptr' if SegTool2D is either unable to determine which slice was affected, or if there was some problem - * getting the image data at that position. - */ - Image::Pointer GetAffectedImageSliceAs2DImage(const InteractionPositionEvent *positionEvent, const Image *image, unsigned int component = 0); - - /** - * \brief Extract the slice of an image cut by given plane. The given component denotes the vector component of a dwi-image. - * - * \param planeGeometry - * \param image - * \param timeStep - * \param component The component to be extracted of a given multi-component image. -1 is the default parameter to denote an invalid component. - * - * \return 'nullptr' if SegTool2D is either unable to determine which slice was affected, or if there was some problem - * getting the image data at that position. - */ - Image::Pointer GetAffectedImageSliceAs2DImage(const PlaneGeometry *planeGeometry, - const Image *image, - unsigned int timeStep, - unsigned int component = 0); - /** \brief Extract the slice of the currently selected working image that the user just scribbles on. \return nullptr if SegTool2D is either unable to determine which slice was affected, or if there was some problem getting the image data at that position, or just no working image is selected. */ Image::Pointer GetAffectedWorkingSlice(const InteractionPositionEvent *); /** \brief Extract the slice of the currently selected reference image that the user just scribbles on. \return nullptr if SegTool2D is either unable to determine which slice was affected, or if there was some problem getting the image data at that position, or just no reference image is selected. */ Image::Pointer GetAffectedReferenceSlice(const InteractionPositionEvent *); void WriteBackSegmentationResult(const InteractionPositionEvent *, Image *); void WriteBackSegmentationResult(const PlaneGeometry *planeGeometry, Image *, unsigned int timeStep); void WriteBackSegmentationResult(const std::vector &sliceList, bool writeSliceToVolume = true); void WritePreviewOnWorkingImage( Image *targetSlice, Image *sourceSlice, Image *workingImage, int paintingPixelValue, int timestep); void WriteSliceToVolume(const SliceInformation &sliceInfo); /** \brief Adds a new node called Contourmarker to the datastorage which holds a mitk::PlanarFigure. By selecting this node the slicestack will be reoriented according to the PlanarFigure's Geometry */ int AddContourmarker(); void InteractiveSegmentationBugMessage(const std::string &message); BaseRenderer *m_LastEventSender; unsigned int m_LastEventSlice; private: // The prefix of the contourmarkername. Suffix is a consecutive number const std::string m_Contourmarkername; bool m_ShowMarkerNodes; static bool m_SurfaceInterpolationEnabled; }; } // namespace #endif