diff --git a/Modules/ContourModel/Algorithms/mitkContourModelUtils.cpp b/Modules/ContourModel/Algorithms/mitkContourModelUtils.cpp index 7d78bf8b01..21ea240370 100755 --- a/Modules/ContourModel/Algorithms/mitkContourModelUtils.cpp +++ b/Modules/ContourModel/Algorithms/mitkContourModelUtils.cpp @@ -1,212 +1,288 @@ /*=================================================================== 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 "mitkContourModelUtils.h" #include "mitkImageCast.h" #include "mitkImageAccessByItk.h" #include "mitkContourModel.h" #include "itkCastImageFilter.h" #include "mitkImage.h" #include "mitkSurface.h" #include "vtkPolyData.h" #include "mitkContourModelToSurfaceFilter.h" #include "vtkPolyDataToImageStencil.h" #include "vtkImageStencil.h" #include "mitkImageVtkAccessor.h" #include "vtkSmartPointer.h" #include "vtkImageData.h" #include "vtkImageLogic.h" #include "vtkPointData.h" - +#include "mitkLabelSetImage.h" mitk::ContourModelUtils::ContourModelUtils() { } mitk::ContourModelUtils::~ContourModelUtils() { } mitk::ContourModel::Pointer mitk::ContourModelUtils::ProjectContourTo2DSlice(Image* slice, ContourModel* contourIn3D, bool itkNotUsed( correctionForIpSegmentation ), bool constrainToInside) { if ( !slice || !contourIn3D ) return nullptr; ContourModel::Pointer projectedContour = ContourModel::New(); projectedContour->Initialize(*contourIn3D); const BaseGeometry* sliceGeometry = slice->GetGeometry(); int numberOfTimesteps = contourIn3D->GetTimeGeometry()->CountTimeSteps(); for(int currentTimestep = 0; currentTimestep < numberOfTimesteps; currentTimestep++) { auto iter = contourIn3D->Begin(currentTimestep); auto 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_DEBUG << "**" << currentPointIn3D << " is " << projectedPointIn2D << " --> correct it (TODO)" << std::endl; } projectedContour->AddVertex( projectedPointIn2D, currentTimestep ); iter++; } } return projectedContour; } mitk::ContourModel::Pointer mitk::ContourModelUtils::BackProjectContourFrom2DSlice(const BaseGeometry* sliceGeometry, ContourModel* contourIn2D, bool itkNotUsed( correctionForIpSegmentation )) { if ( !sliceGeometry || !contourIn2D ) return nullptr; ContourModel::Pointer worldContour = ContourModel::New(); worldContour->Initialize(*contourIn2D); int numberOfTimesteps = contourIn2D->GetTimeGeometry()->CountTimeSteps(); for(int currentTimestep = 0; currentTimestep < numberOfTimesteps; currentTimestep++) { auto iter = contourIn2D->Begin(currentTimestep); auto 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; } -void mitk::ContourModelUtils::FillContourInSlice( ContourModel* projectedContour, Image* sliceImage, int paintingPixelValue ) +void mitk::ContourModelUtils::FillContourInSlice( ContourModel* projectedContour, Image* sliceImage, mitk::Image::Pointer workingImage, int paintingPixelValue ) { - mitk::ContourModelUtils::FillContourInSlice(projectedContour, 0, sliceImage, paintingPixelValue); + mitk::ContourModelUtils::FillContourInSlice(projectedContour, 0, sliceImage,workingImage, paintingPixelValue); } -void mitk::ContourModelUtils::FillContourInSlice( ContourModel* projectedContour, unsigned int timeStep, Image* sliceImage, int paintingPixelValue ) +void mitk::ContourModelUtils::FillContourInSlice( ContourModel* projectedContour, unsigned int timeStep, Image* sliceImage, mitk::Image::Pointer workingImage , int eraseMode) { //create a surface of the input ContourModel mitk::Surface::Pointer surface = mitk::Surface::New(); mitk::ContourModelToSurfaceFilter::Pointer contourModelFilter = mitk::ContourModelToSurfaceFilter::New(); contourModelFilter->SetInput(projectedContour); contourModelFilter->Update(); surface = contourModelFilter->GetOutput(); // that's our vtkPolyData-Surface vtkSmartPointer surface2D = vtkSmartPointer::New(); if (surface->GetVtkPolyData(timeStep) == nullptr) { MITK_WARN << "No surface has been created from contour model. Add more points to fill contour in slice."; return; } surface2D->SetPoints(surface->GetVtkPolyData(timeStep)->GetPoints()); surface2D->SetLines(surface->GetVtkPolyData(timeStep)->GetLines()); surface2D->Modified(); //surface2D->Update(); - // prepare the binary image's voxel grid vtkSmartPointer whiteImage = vtkSmartPointer::New(); whiteImage->DeepCopy(sliceImage->GetVtkImageData()); // fill the image with foreground voxels: unsigned char inval = 255; unsigned char outval = 0; vtkIdType count = whiteImage->GetNumberOfPoints(); for (vtkIdType i = 0; i < count; ++i) { whiteImage->GetPointData()->GetScalars()->SetTuple1(i, inval); } - // polygonal data --> image stencil: vtkSmartPointer pol2stenc = vtkSmartPointer::New(); //Set a minimal tolerance, so that clipped pixels will be added to contour as well. pol2stenc->SetTolerance(mitk::eps); pol2stenc->SetInputData(surface2D); pol2stenc->Update(); // cut the corresponding white image and set the background: vtkSmartPointer imgstenc = vtkSmartPointer::New(); imgstenc->SetInputData(whiteImage); imgstenc->SetStencilConnection(pol2stenc->GetOutputPort()); imgstenc->ReverseStencilOff(); imgstenc->SetBackgroundValue(outval); imgstenc->Update(); + mitk::LabelSetImage* labelImage; // Todo: Get the working Image + int activePixelValue = eraseMode; + labelImage = dynamic_cast(workingImage.GetPointer()); + if (labelImage) + { + activePixelValue = labelImage->GetActiveLabel()->GetValue(); + } + // Fill according to the Color Team + vtkSmartPointer filledImage = imgstenc->GetOutput(); + vtkSmartPointer resultImage = sliceImage->GetVtkImageData(); + FillSliceInSlice(filledImage, resultImage, workingImage, eraseMode); + /* + count = filledImage->GetNumberOfPoints(); + if (activePixelValue == 0) + { + for (vtkIdType i = 0; i < count; ++i) + { + if (filledImage->GetPointData()->GetScalars()->GetTuple1(i) > 1) + { + resultImage->GetPointData()->GetScalars()->SetTuple1(i, eraseMode); + } + } + } + else if (eraseMode != 0) // We are not erasing... + { + for (vtkIdType i = 0; i < count; ++i) + { + if (filledImage->GetPointData()->GetScalars()->GetTuple1(i) > 1) + { + int targetValue = resultImage->GetPointData()->GetScalars()->GetTuple1(i); + if (labelImage) + { + if (!labelImage->GetLabel(targetValue)->GetLocked()) + { + resultImage->GetPointData()->GetScalars()->SetTuple1(i, eraseMode); + } + } else + { + resultImage->GetPointData()->GetScalars()->SetTuple1(i, eraseMode); + } + } + } + } + else + { + for (vtkIdType i = 0; i < count; ++i) + { + if ((resultImage->GetPointData()->GetScalars()->GetTuple1(i) == activePixelValue) & (filledImage->GetPointData()->GetScalars()->GetTuple1(i) > 1)) + { + resultImage->GetPointData()->GetScalars()->SetTuple1(i, eraseMode); + } + } + }*/ + sliceImage->SetVolume(resultImage->GetScalarPointer()); +} - //Fill according to painting value - vtkSmartPointer booleanOperation = vtkSmartPointer::New(); - - booleanOperation->SetInput2Data(sliceImage->GetVtkImageData()); - booleanOperation->SetOperationToOr(); - booleanOperation->SetOutputTrueValue(1.0); +void mitk::ContourModelUtils::FillSliceInSlice(vtkSmartPointer filledImage, vtkSmartPointer resultImage, mitk::Image::Pointer image, int eraseMode) +{ + mitk::LabelSetImage* labelImage; // Todo: Get the working Image + int activePixelValue = eraseMode; + labelImage = dynamic_cast(image.GetPointer()); + if (labelImage) + { + activePixelValue = labelImage->GetActiveLabel()->GetValue(); + } - if(paintingPixelValue == 1) + int count = filledImage->GetNumberOfPoints(); + if (activePixelValue == 0) + { + for (vtkIdType i = 0; i < count; ++i) + { + if (filledImage->GetPointData()->GetScalars()->GetTuple1(i) > 1) { - //COMBINE - //slice or stencil - booleanOperation->SetInputConnection(imgstenc->GetOutputPort()); - booleanOperation->SetOperationToOr(); - } else + resultImage->GetPointData()->GetScalars()->SetTuple1(i, eraseMode); + } + } + } + else if (eraseMode != 0) // We are not erasing... + { + for (vtkIdType i = 0; i < count; ++i) + { + if (filledImage->GetPointData()->GetScalars()->GetTuple1(i) > 1) { - //CUT - //slice and not(stencil) - vtkSmartPointer booleanOperationNOT = vtkSmartPointer::New(); - booleanOperationNOT->SetInputConnection(imgstenc->GetOutputPort()); - booleanOperationNOT->SetOperationToNot(); - booleanOperationNOT->Update(); - booleanOperation->SetInputConnection(booleanOperationNOT->GetOutputPort()); - booleanOperation->SetOperationToAnd(); + int targetValue = resultImage->GetPointData()->GetScalars()->GetTuple1(i); + if (labelImage) + { + if (!labelImage->GetLabel(targetValue)->GetLocked()) + { + resultImage->GetPointData()->GetScalars()->SetTuple1(i, eraseMode); + } + } else + { + resultImage->GetPointData()->GetScalars()->SetTuple1(i, eraseMode); + } } - booleanOperation->Update(); - - //copy scalars to output image slice - sliceImage->SetVolume(booleanOperation->GetOutput()->GetScalarPointer()); + } + } + else + { + for (vtkIdType i = 0; i < count; ++i) + { + if ((resultImage->GetPointData()->GetScalars()->GetTuple1(i) == activePixelValue) & (filledImage->GetPointData()->GetScalars()->GetTuple1(i) > 1)) + { + resultImage->GetPointData()->GetScalars()->SetTuple1(i, eraseMode); + } + } + } } diff --git a/Modules/ContourModel/Algorithms/mitkContourModelUtils.h b/Modules/ContourModel/Algorithms/mitkContourModelUtils.h index e2ad466393..5c0d88f98d 100644 --- a/Modules/ContourModel/Algorithms/mitkContourModelUtils.h +++ b/Modules/ContourModel/Algorithms/mitkContourModelUtils.h @@ -1,77 +1,81 @@ /*=================================================================== 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 mitkContourModelUtilshIncludett #define mitkContourModelUtilshIncludett #include "mitkImage.h" #include #include "mitkContourModel.h" #include +#include namespace mitk { /** * \brief Helpful methods for working with contours and images * * */ class MITKCONTOURMODEL_EXPORT ContourModelUtils : public itk::Object { public: mitkClassMacroItkParent(ContourModelUtils, itk::Object); /** \brief Projects a contour onto an image point by point. Converts from world to index coordinates. \param correctionForIpSegmentation adds 0.5 to x and y index coordinates (difference between ipSegmentation and MITK contours) */ static ContourModel::Pointer ProjectContourTo2DSlice(Image* slice, ContourModel* contourIn3D, bool correctionForIpSegmentation, bool constrainToInside); /** \brief Projects a slice index coordinates of a contour back into world coordinates. \param correctionForIpSegmentation subtracts 0.5 to x and y index coordinates (difference between ipSegmentation and MITK contours) */ static ContourModel::Pointer BackProjectContourFrom2DSlice(const BaseGeometry* sliceGeometry, ContourModel* contourIn2D, bool correctionForIpSegmentation = false); /** \brief Fill a contour in a 2D slice with a specified pixel value at time step 0. */ - static void FillContourInSlice( ContourModel* projectedContour, Image* sliceImage, int paintingPixelValue = 1 ); + static void FillContourInSlice( ContourModel* projectedContour, Image* sliceImage, mitk::Image::Pointer workingImage, int paintingPixelValue = 1 ); /** \brief Fill a contour in a 2D slice with a specified pixel value at a given time step. */ - static void FillContourInSlice( ContourModel* projectedContour, unsigned int timeStep, Image* sliceImage, int paintingPixelValue = 1 ); - + static void FillContourInSlice( ContourModel* projectedContour, unsigned int timeStep, Image* sliceImage, mitk::Image::Pointer workingImage, int paintingPixelValue = 1 ); + /** + \brief Fills a image (filledImage) into another image (resultImage) by incorporating the rules of LabelSet-Images + */ + static void FillSliceInSlice(vtkSmartPointer filledImage, vtkSmartPointer resultImage, mitk::Image::Pointer image, int eraseMode); protected: ContourModelUtils(); virtual ~ContourModelUtils(); }; } #endif diff --git a/Modules/ContourModel/CMakeLists.txt b/Modules/ContourModel/CMakeLists.txt index 9866a3d387..82cdb04801 100644 --- a/Modules/ContourModel/CMakeLists.txt +++ b/Modules/ContourModel/CMakeLists.txt @@ -1,9 +1,9 @@ MITK_CREATE_MODULE( INCLUDE_DIRS Algorithms DataManagement IO Rendering - DEPENDS MitkCore MitkSceneSerializationBase MitkLegacyGL MitkOverlays + DEPENDS MitkCore MitkSceneSerializationBase MitkLegacyGL MitkOverlays MitkMultilabel PACKAGE_DEPENDS ITK|ITKReview # AUTOLOAD_WITH MitkCore TODO: Create IO Submodule and autoload that one instead. WARNINGS_AS_ERRORS ) add_subdirectory(Testing) diff --git a/Modules/Multilabel/Testing/files.cmake b/Modules/Multilabel/Testing/files.cmake index a00fe0dce3..fefbaf56fa 100644 --- a/Modules/Multilabel/Testing/files.cmake +++ b/Modules/Multilabel/Testing/files.cmake @@ -1,7 +1,8 @@ set(MODULE_TESTS mitkLabelTest.cpp mitkLabelSetTest.cpp mitkLabelSetImageTest.cpp #mitkLabelSetImageIOTest.cpp # Deactivated. Not supported yet - requires low level writer access. + mitkLabelSetImageSurfaceStampFilterTest.cpp ) diff --git a/Modules/Multilabel/Testing/mitkLabelSetImageSurfaceStampFilterTest.cpp b/Modules/Multilabel/Testing/mitkLabelSetImageSurfaceStampFilterTest.cpp new file mode 100644 index 0000000000..7a7a4ee23b --- /dev/null +++ b/Modules/Multilabel/Testing/mitkLabelSetImageSurfaceStampFilterTest.cpp @@ -0,0 +1,86 @@ +/*=================================================================== + +The Medical Imaging Interaction Toolkit (MITK) + +Copyright (c) German Cancer Research Center, +Division of Medical and Biological Informatics. +All rights reserved. + +This software is distributed WITHOUT ANY WARRANTY; without +even the implied warranty of MERCHANTABILITY or FITNESS FOR +A PARTICULAR PURPOSE. + +See LICENSE.txt or http://www.mitk.org for details. + +===================================================================*/ + +#include +#include +#include +#include +#include + +#include +#include +#include + +class mitkLabelSetImageSurfaceStampFilterTestSuite : public mitk::TestFixture +{ + CPPUNIT_TEST_SUITE(mitkLabelSetImageSurfaceStampFilterTestSuite); + + MITK_TEST(Result_Match_Expectation); + + CPPUNIT_TEST_SUITE_END(); + +private: + mitk::LabelSetImage::Pointer m_LabelSetImage; + mitk::Surface::Pointer m_Surface; + +public: + + void setUp() override + { + mitk::Image::Pointer regularImage = mitk::ImageGenerator::GenerateRandomImage(50,50,50,1,1,1,1,0.3,0.2); + m_LabelSetImage = dynamic_cast(mitk::IOUtil::LoadBaseData(GetTestDataFilePath("Multilabel/EmptyMultiLabelSegmentation.nrrd")).GetPointer()); + mitk::Label::Pointer label1 = mitk::Label::New(); + label1->SetName("Label1"); + mitk::Label::PixelType value1 = 1; + label1->SetValue(value1); + + mitk::Label::Pointer label2 = mitk::Label::New(); + label2->SetName("Label2"); + mitk::Label::PixelType value2 = 2; + label2->SetValue(value2); + + m_Surface = mitk::IOUtil::LoadSurface(GetTestDataFilePath("BallBinary30x30x30Reference.vtp")); + } + + void tearDown() override + { + // Delete LabelSetImage + m_LabelSetImage = 0; + } + + void Result_Match_Expectation() + { + mitk::LabelSetImageSurfaceStampFilter::Pointer filter = mitk::LabelSetImageSurfaceStampFilter::New(); + filter->SetSurface(m_Surface); + filter->SetForceOverwrite(true); + filter->SetInput(m_LabelSetImage); + filter->Update(); + mitk::LabelSetImage::Pointer result =dynamic_cast(m_LabelSetImage.GetPointer());//dynamic_cast(filter->GetOutput()); + //result->DisconnectPipeline(); + //mitk::LabelSetImage::Pointer result =dynamic_cast(m_LabelSetImage->Clone().GetPointer());//dynamic_cast(filter->GetOutput()); + + mitk::LabelSetImage::Pointer expectedResult = dynamic_cast(mitk::IOUtil::LoadBaseData(GetTestDataFilePath("Multilabel/StampResultBasedOnEmptyML.nrrd")).GetPointer()); + + MITK_ASSERT_EQUAL(result, expectedResult, "Result after stamping should be equal to the saved version"); + } + + // Reduce contours with nth point + void TestGetActiveLabel() + { + } +}; + +MITK_TEST_SUITE_REGISTRATION(mitkLabelSetImageSurfaceStampFilter) \ No newline at end of file diff --git a/Modules/Multilabel/Testing/mitkLabelSetImageTest.cpp b/Modules/Multilabel/Testing/mitkLabelSetImageTest.cpp index 597efef47f..5a47d56c2c 100644 --- a/Modules/Multilabel/Testing/mitkLabelSetImageTest.cpp +++ b/Modules/Multilabel/Testing/mitkLabelSetImageTest.cpp @@ -1,414 +1,414 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include #include #include #include #include class mitkLabelSetImageTestSuite : public mitk::TestFixture { CPPUNIT_TEST_SUITE(mitkLabelSetImageTestSuite); MITK_TEST(TestInitialize); MITK_TEST(TestAddLayer); MITK_TEST(TestGetActiveLabelSet); MITK_TEST(TestGetActiveLabel); MITK_TEST(TestInitializeByLabeledImage); MITK_TEST(TestGetLabelSet); MITK_TEST(TestGetLabel); MITK_TEST(TestSetExteriorLabel); MITK_TEST(TestGetTotalNumberOfLabels); MITK_TEST(TestExistsLabel); MITK_TEST(TestExistsLabelSet); MITK_TEST(TestSetActiveLayer); MITK_TEST(TestRemoveLayer); MITK_TEST(TestRemoveLabels); MITK_TEST(TestMergeLabel); // TODO check it these functionalities can be moved into a process object -// MITK_TEST(TestMergeLabels); -// MITK_TEST(TestConcatenate); -// MITK_TEST(TestClearBuffer); -// MITK_TEST(TestUpdateCenterOfMass); -// MITK_TEST(TestGetVectorImage); -// MITK_TEST(TestSetVectorImage); -// MITK_TEST(TestGetLayerImage); + // MITK_TEST(TestMergeLabels); + // MITK_TEST(TestConcatenate); + // MITK_TEST(TestClearBuffer); + // MITK_TEST(TestUpdateCenterOfMass); + // MITK_TEST(TestGetVectorImage); + // MITK_TEST(TestSetVectorImage); + // MITK_TEST(TestGetLayerImage); CPPUNIT_TEST_SUITE_END(); private: mitk::LabelSetImage::Pointer m_LabelSetImage; public: void setUp() override { // Create a new labelset image m_LabelSetImage = mitk::LabelSetImage::New(); mitk::Image::Pointer regularImage = mitk::Image::New(); unsigned int dimensions[3] = {256,256,312}; regularImage->Initialize(mitk::MakeScalarPixelType(), 3, dimensions); m_LabelSetImage->Initialize(regularImage); } void tearDown() override { // Delete LabelSetImage m_LabelSetImage = 0; } // Reduce contours with nth point void TestInitialize() { // LabelSet image should always has the pixel type mitk::Label::PixelType CPPUNIT_ASSERT_MESSAGE("LabelSetImage has wrong pixel type", - m_LabelSetImage->GetPixelType() == mitk::MakeScalarPixelType()); + m_LabelSetImage->GetPixelType() == mitk::MakeScalarPixelType()); mitk::Image::Pointer regularImage = mitk::Image::New(); unsigned int dimensions[3] = {256,256,312}; regularImage->Initialize(mitk::MakeScalarPixelType(), 3, dimensions); mitk::BaseGeometry::Pointer regularImageGeo = regularImage->GetGeometry(); mitk::BaseGeometry::Pointer labelImageGeo = m_LabelSetImage->GetGeometry(); MITK_ASSERT_EQUAL(labelImageGeo, regularImageGeo, "LabelSetImage has wrong geometry"); // By default one layer containing the exterior label should be added CPPUNIT_ASSERT_MESSAGE("Image was not correctly initialized - number of layers is not one",m_LabelSetImage->GetNumberOfLayers() == 1); CPPUNIT_ASSERT_MESSAGE("Image was not correctly initialized - active layer has wrong ID",m_LabelSetImage->GetActiveLayer() == 0); CPPUNIT_ASSERT_MESSAGE("Image was not correctly initialized - active label is not the exterior label",m_LabelSetImage->GetActiveLabel()->GetValue() == 0); } void TestAddLayer() { CPPUNIT_ASSERT_MESSAGE("Number of layers is not zero",m_LabelSetImage->GetNumberOfLayers() == 1); m_LabelSetImage->AddLayer(); CPPUNIT_ASSERT_MESSAGE("Layer was not added correctly to image - number of layers is not one",m_LabelSetImage->GetNumberOfLayers() == 2); CPPUNIT_ASSERT_MESSAGE("Layer was not added correctly to image - active layer has wrong ID", m_LabelSetImage->GetActiveLayer() == 1); CPPUNIT_ASSERT_MESSAGE("Layer was not added correctly to image - active label is not the exterior label",m_LabelSetImage->GetActiveLabel()->GetValue() == 0); mitk::LabelSet::Pointer newlayer = mitk::LabelSet::New(); mitk::Label::Pointer label1 = mitk::Label::New(); label1->SetName("Label1"); label1->SetValue(1); mitk::Label::Pointer label2 = mitk::Label::New(); label2->SetName("Label2"); label2->SetValue(200); newlayer->AddLabel(label1); newlayer->AddLabel(label2); newlayer->SetActiveLabel(200); unsigned int layerID = m_LabelSetImage->AddLayer(newlayer); CPPUNIT_ASSERT_MESSAGE("Layer was not added correctly to image - number of layers is not two",m_LabelSetImage->GetNumberOfLayers() == 3); CPPUNIT_ASSERT_MESSAGE("Layer was not added correctly to image - active layer has wrong ID",m_LabelSetImage->GetActiveLayer() == layerID); CPPUNIT_ASSERT_MESSAGE("Layer was not added correctly to image - active label is wrong",m_LabelSetImage->GetActiveLabel(layerID)->GetValue() == 200); } void TestGetActiveLabelSet() { mitk::LabelSet::Pointer newlayer = mitk::LabelSet::New(); mitk::Label::Pointer label1 = mitk::Label::New(); label1->SetName("Label1"); label1->SetValue(1); mitk::Label::Pointer label2 = mitk::Label::New(); label2->SetName("Label2"); label2->SetValue(200); newlayer->AddLabel(label1); newlayer->AddLabel(label2); newlayer->SetActiveLabel(200); unsigned int layerID = m_LabelSetImage->AddLayer(newlayer); mitk::LabelSet::Pointer activeLayer = m_LabelSetImage->GetActiveLabelSet(); CPPUNIT_ASSERT_MESSAGE("Wrong layer ID was returned", layerID == 1); CPPUNIT_ASSERT_MESSAGE("Wrong active labelset returned", mitk::Equal(*newlayer, *activeLayer, 0.00001, true)); } void TestGetActiveLabel() { mitk::Label::Pointer label1 = mitk::Label::New(); label1->SetName("Label1"); mitk::Label::PixelType value1 = 1; label1->SetValue(value1); mitk::Label::Pointer label2 = mitk::Label::New(); label2->SetName("Label2"); mitk::Label::PixelType value2 = 200; label2->SetValue(value2); m_LabelSetImage->GetActiveLabelSet()->AddLabel(label1); m_LabelSetImage->GetActiveLabelSet()->AddLabel(label2); m_LabelSetImage->GetActiveLabelSet()->SetActiveLabel(1); CPPUNIT_ASSERT_MESSAGE("Layer was not added correctly to image - active label is wrong",m_LabelSetImage->GetActiveLabel()->GetValue() == value1); m_LabelSetImage->GetActiveLabelSet()->SetActiveLabel(value2); CPPUNIT_ASSERT_MESSAGE("Layer was not added correctly to image - active label is wrong",m_LabelSetImage->GetActiveLabel()->GetValue() == value2); } void TestInitializeByLabeledImage() { mitk::Image::Pointer image = mitk::IOUtil::LoadImage(GetTestDataFilePath("Multilabel/LabelSetTestInitializeImage.nrrd")); m_LabelSetImage->InitializeByLabeledImage(image); CPPUNIT_ASSERT_MESSAGE("Image - number of labels is not 6",m_LabelSetImage->GetNumberOfLabels() == 6); } void TestGetLabelSet() { // Test get non existing lset mitk::LabelSet::ConstPointer lset = m_LabelSetImage->GetLabelSet(10000); CPPUNIT_ASSERT_MESSAGE("Non existing labelset is not NULL", lset.IsNull()); lset = m_LabelSetImage->GetLabelSet(0); CPPUNIT_ASSERT_MESSAGE("Existing labelset is NULL", lset.IsNotNull()); } void TestGetLabel() { mitk::Label::Pointer label1 = mitk::Label::New(); label1->SetName("Label1"); mitk::Label::PixelType value1 = 1; label1->SetValue(value1); mitk::Label::Pointer label2 = mitk::Label::New(); label2->SetName("Label2"); mitk::Label::PixelType value2 = 200; label2->SetValue(value2); m_LabelSetImage->GetActiveLabelSet()->AddLabel(label1); m_LabelSetImage->AddLayer(); m_LabelSetImage->GetLabelSet(1)->AddLabel(label2); CPPUNIT_ASSERT_MESSAGE("Wrong label retrieved for active layer", mitk::Equal(*m_LabelSetImage->GetLabel(1), *label1, 0.0001, true)); CPPUNIT_ASSERT_MESSAGE("Wrong label retrieved for layer 1", mitk::Equal(*m_LabelSetImage->GetLabel(200, 1), *label2, 0.0001, true)); // Try to get a non existing label mitk::Label* label3 = m_LabelSetImage->GetLabel(1000); CPPUNIT_ASSERT_MESSAGE("Non existing label should be NULL", label3 == NULL); // Try to get a label from a non existing layer label3 = m_LabelSetImage->GetLabel(200, 1000); CPPUNIT_ASSERT_MESSAGE("Label from non existing layer should be NULL", label3 == NULL); } void TestSetExteriorLabel() { mitk::Label::Pointer exteriorLabel = mitk::Label::New(); exteriorLabel->SetName("MyExteriorSpecialLabel"); mitk::Label::PixelType value1 = 10000; exteriorLabel->SetValue(value1); m_LabelSetImage->SetExteriorLabel(exteriorLabel); CPPUNIT_ASSERT_MESSAGE("Wrong label retrieved for layer 1", mitk::Equal(*m_LabelSetImage->GetExteriorLabel(), *exteriorLabel, 0.0001, true)); // Exterior label should be set automatically for each new layer m_LabelSetImage->AddLayer(); CPPUNIT_ASSERT_MESSAGE("Wrong label retrieved for layer 1", mitk::Equal(*m_LabelSetImage->GetLabel(10000, 1), *exteriorLabel, 0.0001, true)); } void TestGetTotalNumberOfLabels() { mitk::Label::Pointer label1 = mitk::Label::New(); label1->SetName("Label1"); mitk::Label::PixelType value1 = 1; label1->SetValue(value1); mitk::Label::Pointer label2 = mitk::Label::New(); label2->SetName("Label2"); mitk::Label::PixelType value2 = 200; label2->SetValue(value2); m_LabelSetImage->GetActiveLabelSet()->AddLabel(label1); m_LabelSetImage->AddLayer(); m_LabelSetImage->GetLabelSet(1)->AddLabel(label2); CPPUNIT_ASSERT_MESSAGE("Wrong total number of labels", m_LabelSetImage->GetTotalNumberOfLabels() == 4); // added 2 labels + 2 exterior default labels } void TestExistsLabel() { mitk::Label::Pointer label = mitk::Label::New(); label->SetName("Label2"); mitk::Label::PixelType value = 200; label->SetValue(value); m_LabelSetImage->AddLayer(); m_LabelSetImage->GetLabelSet(1)->AddLabel(label); m_LabelSetImage->SetActiveLayer(0); CPPUNIT_ASSERT_MESSAGE("Existing label was not found", m_LabelSetImage->ExistLabel(value) == true); CPPUNIT_ASSERT_MESSAGE("Non existing label was found", m_LabelSetImage->ExistLabel(10000) == false); } void TestExistsLabelSet() { // Cache active layer mitk::LabelSet::ConstPointer activeLayer = m_LabelSetImage->GetActiveLabelSet(); // Add new layer mitk::LabelSet::Pointer newlayer = mitk::LabelSet::New(); mitk::Label::Pointer label1 = mitk::Label::New(); label1->SetName("Label1"); label1->SetValue(1); mitk::Label::Pointer label2 = mitk::Label::New(); label2->SetName("Label2"); label2->SetValue(200); newlayer->AddLabel(label1); newlayer->AddLabel(label2); newlayer->SetActiveLabel(200); m_LabelSetImage->AddLayer(newlayer); CPPUNIT_ASSERT_MESSAGE("Check for existing layer failed",m_LabelSetImage->ExistLabelSet(0) == true); CPPUNIT_ASSERT_MESSAGE("Check for existing layer failed",m_LabelSetImage->ExistLabelSet(1) == true); CPPUNIT_ASSERT_MESSAGE("Check for existing layer failed",m_LabelSetImage->ExistLabelSet(20) == false); } void TestSetActiveLayer() { // Cache active layer mitk::LabelSet::ConstPointer activeLayer = m_LabelSetImage->GetActiveLabelSet(); // Add new layer mitk::LabelSet::Pointer newlayer = mitk::LabelSet::New(); mitk::Label::Pointer label1 = mitk::Label::New(); label1->SetName("Label1"); label1->SetValue(1); mitk::Label::Pointer label2 = mitk::Label::New(); label2->SetName("Label2"); label2->SetValue(200); newlayer->AddLabel(label1); newlayer->AddLabel(label2); newlayer->SetActiveLabel(200); unsigned int layerID = m_LabelSetImage->AddLayer(newlayer); // Set initial layer as active layer m_LabelSetImage->SetActiveLayer(0); CPPUNIT_ASSERT_MESSAGE("Wrong active labelset returned", mitk::Equal(*activeLayer, *m_LabelSetImage->GetActiveLabelSet(), 0.00001, true)); // Set previously added layer as active layer m_LabelSetImage->SetActiveLayer(layerID); CPPUNIT_ASSERT_MESSAGE("Wrong active labelset returned", mitk::Equal(*newlayer, *m_LabelSetImage->GetActiveLabelSet(), 0.00001, true)); // Set a non existing layer as active layer - nothing should change m_LabelSetImage->SetActiveLayer(10000); CPPUNIT_ASSERT_MESSAGE("Wrong active labelset returned", mitk::Equal(*newlayer, *m_LabelSetImage->GetActiveLabelSet(), 0.00001, true)); } void TestRemoveLayer() { // Cache active layer mitk::LabelSet::ConstPointer activeLayer = m_LabelSetImage->GetActiveLabelSet(); // Add new layers m_LabelSetImage->AddLayer(); mitk::LabelSet::Pointer newlayer = mitk::LabelSet::New(); mitk::Label::Pointer label1 = mitk::Label::New(); label1->SetName("Label1"); label1->SetValue(1); mitk::Label::Pointer label2 = mitk::Label::New(); label2->SetName("Label2"); label2->SetValue(200); newlayer->AddLabel(label1); newlayer->AddLabel(label2); newlayer->SetActiveLabel(200); m_LabelSetImage->AddLayer(newlayer); CPPUNIT_ASSERT_MESSAGE("Wrong active labelset returned", mitk::Equal(*newlayer, *m_LabelSetImage->GetActiveLabelSet(), 0.00001, true)); m_LabelSetImage->RemoveLayer(); CPPUNIT_ASSERT_MESSAGE("Wrong number of layers, after a layer was removed", m_LabelSetImage->GetNumberOfLayers() == 2); CPPUNIT_ASSERT_MESSAGE("Check for existing layer failed",m_LabelSetImage->ExistLabelSet(2) == false); CPPUNIT_ASSERT_MESSAGE("Check for existing layer failed",m_LabelSetImage->ExistLabelSet(1) == true); CPPUNIT_ASSERT_MESSAGE("Check for existing layer failed",m_LabelSetImage->ExistLabelSet(0) == true); m_LabelSetImage->RemoveLayer(); CPPUNIT_ASSERT_MESSAGE("Wrong number of layers, after a layer was removed", m_LabelSetImage->GetNumberOfLayers() == 1); CPPUNIT_ASSERT_MESSAGE("Check for existing layer failed",m_LabelSetImage->ExistLabelSet(1) == false); CPPUNIT_ASSERT_MESSAGE("Check for existing layer failed",m_LabelSetImage->ExistLabelSet(0) == true); CPPUNIT_ASSERT_MESSAGE("Wrong active layer", mitk::Equal(*activeLayer, *m_LabelSetImage->GetActiveLabelSet(), 0.00001, true)); m_LabelSetImage->RemoveLayer(); CPPUNIT_ASSERT_MESSAGE("Wrong number of layers, after a layer was removed", m_LabelSetImage->GetNumberOfLayers() == 0); CPPUNIT_ASSERT_MESSAGE("Check for existing layer failed",m_LabelSetImage->ExistLabelSet(0) == false); CPPUNIT_ASSERT_MESSAGE("Active layers is not NULL although all layer have been removed", m_LabelSetImage->GetActiveLabelSet() == 0); } void TestRemoveLabels() { mitk::Image::Pointer image = mitk::IOUtil::LoadImage(GetTestDataFilePath("Multilabel/LabelSetTestInitializeImage.nrrd")); m_LabelSetImage->InitializeByLabeledImage(image); CPPUNIT_ASSERT_MESSAGE("Image - number of labels is not 6",m_LabelSetImage->GetNumberOfLabels() == 6); // 2ndMin because of the exterior label = 0 CPPUNIT_ASSERT_MESSAGE("Labels with value 1 and 3 was not remove from the image", m_LabelSetImage->GetStatistics()->GetScalarValue2ndMin() == 1); CPPUNIT_ASSERT_MESSAGE("Label with value 7 was not remove from the image", m_LabelSetImage->GetStatistics()->GetScalarValueMax() == 7); CPPUNIT_ASSERT_MESSAGE("Label with ID 3 does not exists after initialization",m_LabelSetImage->ExistLabel(3) == true); CPPUNIT_ASSERT_MESSAGE("Label with ID 7 does not exists after initialization",m_LabelSetImage->ExistLabel(7) == true); std::vector labelsToBeRemoved; labelsToBeRemoved.push_back(1); labelsToBeRemoved.push_back(3); labelsToBeRemoved.push_back(7); m_LabelSetImage->RemoveLabels(labelsToBeRemoved); CPPUNIT_ASSERT_MESSAGE("Wrong number of labels after some have been removed", m_LabelSetImage->GetNumberOfLabels() == 3); // Values within the image are 0, 1, 3, 5, 6, 7 - New Min/Max value should be 5 / 6 // 2ndMin because of the exterior label = 0 CPPUNIT_ASSERT_MESSAGE("Labels with value 1 and 3 was not remove from the image", m_LabelSetImage->GetStatistics()->GetScalarValue2ndMin() == 5); CPPUNIT_ASSERT_MESSAGE("Label with value 7 was not remove from the image", m_LabelSetImage->GetStatistics()->GetScalarValueMax() == 6); } void TestMergeLabel() { mitk::Image::Pointer image = mitk::IOUtil::LoadImage(GetTestDataFilePath("Multilabel/LabelSetTestInitializeImage.nrrd")); m_LabelSetImage = 0; m_LabelSetImage = mitk::LabelSetImage::New(); m_LabelSetImage->InitializeByLabeledImage(image); CPPUNIT_ASSERT_MESSAGE("Image - number of labels is not 6",m_LabelSetImage->GetNumberOfLabels() == 6); // 2ndMin because of the exterior label = 0 CPPUNIT_ASSERT_MESSAGE("Wrong MIN value", m_LabelSetImage->GetStatistics()->GetScalarValueMin() == 0); CPPUNIT_ASSERT_MESSAGE("Wrong MAX value", m_LabelSetImage->GetStatistics()->GetScalarValueMax() == 7); m_LabelSetImage->GetActiveLabelSet()->SetActiveLabel(6); // Merge label 7 with label 0. Result should be that label 7 is not present any more m_LabelSetImage->MergeLabel(7); CPPUNIT_ASSERT_MESSAGE("Label with value 7 was not remove from the image", m_LabelSetImage->GetStatistics()->GetScalarValueMax() == 6); m_LabelSetImage->GetStatistics()->GetScalarValue2ndMax(); // Count all pixels with value 7 = 823 // Count all pixels with value 6 = 507 // Check if merge label has 507 + 823 = 1330 pixels CPPUNIT_ASSERT_MESSAGE("Label with value 7 was not remove from the image", m_LabelSetImage->GetStatistics()->GetCountOfMaxValuedVoxels() == 1330); } }; -MITK_TEST_SUITE_REGISTRATION(mitkLabelSetImage) +MITK_TEST_SUITE_REGISTRATION(mitkLabelSetImage) \ No newline at end of file diff --git a/Modules/Multilabel/autoload/IO/mitkLabelSetImageIO.cpp b/Modules/Multilabel/autoload/IO/mitkLabelSetImageIO.cpp index 85686e3368..c96c4cd409 100644 --- a/Modules/Multilabel/autoload/IO/mitkLabelSetImageIO.cpp +++ b/Modules/Multilabel/autoload/IO/mitkLabelSetImageIO.cpp @@ -1,651 +1,456 @@ /*=================================================================== 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 __mitkLabelSetImageWriter__cpp #define __mitkLabelSetImageWriter__cpp #include "mitkBasePropertySerializer.h" #include "mitkImageAccessByItk.h" #include "mitkIOMimeTypes.h" #include "mitkLabelSetImageIO.h" #include "mitkLabelSetImageConverter.h" #include +#include "mitkLabelSetIOHelper.h" // itk #include "itkMetaDataDictionary.h" #include "itkMetaDataObject.h" #include "itkNrrdImageIO.h" #include "itkImageFileWriter.h" #include "itkImageFileReader.h" -#include "tinyxml.h" - - namespace mitk { LabelSetImageIO::LabelSetImageIO() : AbstractFileIO(LabelSetImage::GetStaticNameOfClass(), IOMimeTypes::NRRD_MIMETYPE(), "MITK Multilabel Image") { AbstractFileWriter::SetRanking(10); AbstractFileReader::SetRanking(10); this->RegisterService(); } IFileIO::ConfidenceLevel LabelSetImageIO::GetWriterConfidenceLevel() const { if (AbstractFileIO::GetWriterConfidenceLevel() == Unsupported) return Unsupported; const LabelSetImage* input = static_cast(this->GetInput()); if (input) return Supported; else return Unsupported; } void LabelSetImageIO::Write() { ValidateOutputLocation(); auto input = dynamic_cast(this->GetInput()); mitk::LocaleSwitch localeSwitch("C"); mitk::Image::Pointer inputVector = mitk::ConvertLabelSetImageToImage(input); // image write if ( inputVector.IsNull() ) { mitkThrow() << "Cannot write non-image data"; } itk::NrrdImageIO::Pointer nrrdImageIo = itk::NrrdImageIO::New(); // Clone the image geometry, because we might have to change it // for writing purposes BaseGeometry::Pointer geometry = inputVector->GetGeometry()->Clone(); // Check if geometry information will be lost if (inputVector->GetDimension() == 2 && !geometry->Is2DConvertable()) { MITK_WARN << "Saving a 2D image with 3D geometry information. Geometry information will be lost! You might consider using Convert2Dto3DImageFilter before saving."; // set matrix to identity mitk::AffineTransform3D::Pointer affTrans = mitk::AffineTransform3D::New(); affTrans->SetIdentity(); mitk::Vector3D spacing = geometry->GetSpacing(); mitk::Point3D origin = geometry->GetOrigin(); geometry->SetIndexToWorldTransform(affTrans); geometry->SetSpacing(spacing); geometry->SetOrigin(origin); } LocalFile localFile(this); const std::string path = localFile.GetFileName(); MITK_INFO << "Writing image: " << path << std::endl; try { // Implementation of writer using itkImageIO directly. This skips the use // of templated itkImageFileWriter, which saves the multiplexing on MITK side. const unsigned int dimension = inputVector->GetDimension(); const unsigned int* const dimensions = inputVector->GetDimensions(); const mitk::PixelType pixelType = inputVector->GetPixelType(); const mitk::Vector3D mitkSpacing = geometry->GetSpacing(); const mitk::Point3D mitkOrigin = geometry->GetOrigin(); // Due to templating in itk, we are forced to save a 4D spacing and 4D Origin, // though they are not supported in MITK itk::Vector spacing4D; spacing4D[0] = mitkSpacing[0]; spacing4D[1] = mitkSpacing[1]; spacing4D[2] = mitkSpacing[2]; spacing4D[3] = 1; // There is no support for a 4D spacing. However, we should have a valid value here itk::Vector origin4D; origin4D[0] = mitkOrigin[0]; origin4D[1] = mitkOrigin[1]; origin4D[2] = mitkOrigin[2]; origin4D[3] = 0; // There is no support for a 4D origin. However, we should have a valid value here // Set the necessary information for imageIO nrrdImageIo->SetNumberOfDimensions(dimension); nrrdImageIo->SetPixelType(pixelType.GetPixelType()); nrrdImageIo->SetComponentType(pixelType.GetComponentType() < PixelComponentUserType ? static_cast(pixelType.GetComponentType()) : itk::ImageIOBase::UNKNOWNCOMPONENTTYPE); nrrdImageIo->SetNumberOfComponents(pixelType.GetNumberOfComponents()); itk::ImageIORegion ioRegion(dimension); for (unsigned int i = 0; i < dimension; i++) { nrrdImageIo->SetDimensions(i, dimensions[i]); nrrdImageIo->SetSpacing(i, spacing4D[i]); nrrdImageIo->SetOrigin(i, origin4D[i]); mitk::Vector3D mitkDirection; mitkDirection.SetVnlVector(geometry->GetIndexToWorldTransform()->GetMatrix().GetVnlMatrix().get_column(i)); itk::Vector direction4D; direction4D[0] = mitkDirection[0]; direction4D[1] = mitkDirection[1]; direction4D[2] = mitkDirection[2]; // MITK only supports a 3x3 direction matrix. Due to templating in itk, however, we must // save a 4x4 matrix for 4D images. in this case, add an homogneous component to the matrix. if (i == 3) { direction4D[3] = 1; // homogenous component } else { direction4D[3] = 0; } vnl_vector axisDirection(dimension); for (unsigned int j = 0; j < dimension; j++) { axisDirection[j] = direction4D[j] / spacing4D[i]; } nrrdImageIo->SetDirection(i, axisDirection); ioRegion.SetSize(i, inputVector->GetLargestPossibleRegion().GetSize(i)); ioRegion.SetIndex(i, inputVector->GetLargestPossibleRegion().GetIndex(i)); } //use compression if available nrrdImageIo->UseCompressionOn(); nrrdImageIo->SetIORegion(ioRegion); nrrdImageIo->SetFileName(path); // label set specific meta data char keybuffer[512]; char valbuffer[512]; sprintf(keybuffer, "modality"); sprintf(valbuffer, "org.mitk.image.multilabel"); itk::EncapsulateMetaData(nrrdImageIo->GetMetaDataDictionary(), std::string(keybuffer), std::string(valbuffer)); sprintf(keybuffer, "layers"); sprintf(valbuffer, "%1d", input->GetNumberOfLayers()); itk::EncapsulateMetaData(nrrdImageIo->GetMetaDataDictionary(), std::string(keybuffer), std::string(valbuffer)); for (unsigned int layerIdx = 0; layerIdxGetNumberOfLayers(); layerIdx++) { sprintf(keybuffer, "layer_%03d", layerIdx); // layer idx sprintf(valbuffer, "%1d", input->GetNumberOfLabels(layerIdx)); // number of labels for the layer itk::EncapsulateMetaData(nrrdImageIo->GetMetaDataDictionary(), std::string(keybuffer), std::string(valbuffer)); mitk::LabelSet::LabelContainerConstIteratorType iter = input->GetLabelSet(layerIdx)->IteratorConstBegin(); unsigned int count(0); while (iter != input->GetLabelSet(layerIdx)->IteratorConstEnd()) { std::unique_ptr document; document.reset(new TiXmlDocument()); TiXmlDeclaration* decl = new TiXmlDeclaration("1.0", "", ""); // TODO what to write here? encoding? etc.... document->LinkEndChild(decl); - TiXmlElement * labelElem = GetLabelAsTiXmlElement(iter->second); + TiXmlElement * labelElem = mitk::LabelSetIOHelper::GetLabelAsTiXmlElement(iter->second); document->LinkEndChild(labelElem); TiXmlPrinter printer; printer.SetIndent(""); printer.SetLineBreak(""); document->Accept(&printer); sprintf(keybuffer, "org.mitk.label_%03u_%05u", layerIdx, count); itk::EncapsulateMetaData(nrrdImageIo->GetMetaDataDictionary(), std::string(keybuffer), printer.Str()); ++iter; ++count; } } // end label set specific meta data ImageReadAccessor imageAccess(inputVector); nrrdImageIo->Write(imageAccess.GetData()); } catch (const std::exception& e) { mitkThrow() << e.what(); } // end image write } IFileIO::ConfidenceLevel LabelSetImageIO::GetReaderConfidenceLevel() const { if (AbstractFileIO::GetReaderConfidenceLevel() == Unsupported) return Unsupported; const std::string fileName = this->GetLocalFileName(); itk::NrrdImageIO::Pointer io = itk::NrrdImageIO::New(); io->SetFileName(fileName); io->ReadImageInformation(); itk::MetaDataDictionary imgMetaDataDictionary = io->GetMetaDataDictionary(); std::string value (""); itk::ExposeMetaData (imgMetaDataDictionary, "modality", value); if (value.compare("org.mitk.image.multilabel") == 0) { return Supported; } else return Unsupported; } std::vector LabelSetImageIO::Read() { mitk::LocaleSwitch localeSwitch("C"); // begin regular image loading, adapted from mitkItkImageIO itk::NrrdImageIO::Pointer nrrdImageIO = itk::NrrdImageIO::New(); Image::Pointer image = Image::New(); const unsigned int MINDIM = 2; const unsigned int MAXDIM = 4; const std::string path = this->GetLocalFileName(); MITK_INFO << "loading " << path << " via itk::ImageIOFactory... " << std::endl; // Check to see if we can read the file given the name or prefix if (path.empty()) { mitkThrow() << "Empty filename in mitk::ItkImageIO "; } // Got to allocate space for the image. Determine the characteristics of // the image. nrrdImageIO->SetFileName(path); nrrdImageIO->ReadImageInformation(); unsigned int ndim = nrrdImageIO->GetNumberOfDimensions(); if (ndim < MINDIM || ndim > MAXDIM) { MITK_WARN << "Sorry, only dimensions 2, 3 and 4 are supported. The given file has " << ndim << " dimensions! Reading as 4D."; ndim = MAXDIM; } itk::ImageIORegion ioRegion(ndim); itk::ImageIORegion::SizeType ioSize = ioRegion.GetSize(); itk::ImageIORegion::IndexType ioStart = ioRegion.GetIndex(); unsigned int dimensions[MAXDIM]; dimensions[0] = 0; dimensions[1] = 0; dimensions[2] = 0; dimensions[3] = 0; ScalarType spacing[MAXDIM]; spacing[0] = 1.0f; spacing[1] = 1.0f; spacing[2] = 1.0f; spacing[3] = 1.0f; Point3D origin; origin.Fill(0); unsigned int i; for (i = 0; i < ndim; ++i) { ioStart[i] = 0; ioSize[i] = nrrdImageIO->GetDimensions(i); if (iGetDimensions(i); spacing[i] = nrrdImageIO->GetSpacing(i); if (spacing[i] <= 0) spacing[i] = 1.0f; } if (i<3) { origin[i] = nrrdImageIO->GetOrigin(i); } } ioRegion.SetSize(ioSize); ioRegion.SetIndex(ioStart); MITK_INFO << "ioRegion: " << ioRegion << std::endl; nrrdImageIO->SetIORegion(ioRegion); void* buffer = new unsigned char[nrrdImageIO->GetImageSizeInBytes()]; nrrdImageIO->Read(buffer); image->Initialize(MakePixelType(nrrdImageIO), ndim, dimensions); image->SetImportChannel(buffer, 0, Image::ManageMemory); // access direction of itk::Image and include spacing mitk::Matrix3D matrix; matrix.SetIdentity(); unsigned int j, itkDimMax3 = (ndim >= 3 ? 3 : ndim); for (i = 0; i < itkDimMax3; ++i) for (j = 0; j < itkDimMax3; ++j) matrix[i][j] = nrrdImageIO->GetDirection(j)[i]; // re-initialize PlaneGeometry with origin and direction PlaneGeometry* planeGeometry = image->GetSlicedGeometry(0)->GetPlaneGeometry(0); planeGeometry->SetOrigin(origin); planeGeometry->GetIndexToWorldTransform()->SetMatrix(matrix); // re-initialize SlicedGeometry3D SlicedGeometry3D* slicedGeometry = image->GetSlicedGeometry(0); slicedGeometry->InitializeEvenlySpaced(planeGeometry, image->GetDimension(2)); slicedGeometry->SetSpacing(spacing); MITK_INFO << slicedGeometry->GetCornerPoint(false, false, false); MITK_INFO << slicedGeometry->GetCornerPoint(true, true, true); // re-initialize TimeGeometry ProportionalTimeGeometry::Pointer timeGeometry = ProportionalTimeGeometry::New(); timeGeometry->Initialize(slicedGeometry, image->GetDimension(3)); image->SetTimeGeometry(timeGeometry); buffer = NULL; MITK_INFO << "number of image components: " << image->GetPixelType().GetNumberOfComponents() << std::endl; const itk::MetaDataDictionary& dictionary = nrrdImageIO->GetMetaDataDictionary(); for (itk::MetaDataDictionary::ConstIterator iter = dictionary.Begin(), iterEnd = dictionary.End(); iter != iterEnd; ++iter) { std::string key = std::string("meta.") + iter->first; if (iter->second->GetMetaDataObjectTypeInfo() == typeid(std::string)) { std::string value = dynamic_cast*>(iter->second.GetPointer())->GetMetaDataObjectValue(); image->SetProperty(key.c_str(), mitk::StringProperty::New(value)); } } // end regular image loading LabelSetImage::Pointer output = ConvertImageToLabelSetImage(image); // get labels and add them as properties to the image char keybuffer[256]; unsigned int numberOfLayers = GetIntByKey(dictionary, "layers"); std::string _xmlStr; mitk::Label::Pointer label; for (unsigned int layerIdx = 0; layerIdx < numberOfLayers; layerIdx++) { sprintf(keybuffer, "layer_%03d", layerIdx); int numberOfLabels = GetIntByKey(dictionary, keybuffer); mitk::LabelSet::Pointer labelSet = mitk::LabelSet::New(); for (int labelIdx = 0; labelIdx < numberOfLabels; labelIdx++) { TiXmlDocument doc; sprintf(keybuffer, "label_%03d_%05d", layerIdx, labelIdx); _xmlStr = GetStringByKey(dictionary, keybuffer); doc.Parse(_xmlStr.c_str()); TiXmlElement * labelElem = doc.FirstChildElement("Label"); if (labelElem == 0) mitkThrow() << "Error parsing NRRD header for mitk::LabelSetImage IO"; - label = LoadLabelFromTiXmlDocument(labelElem); + label = mitk::LabelSetIOHelper::LoadLabelFromTiXmlDocument(labelElem); if (label->GetValue() == 0) // set exterior label is needed to hold exterior information output->SetExteriorLabel(label); labelSet->AddLabel(label); labelSet->SetLayer(layerIdx); } output->AddLabelSetToLayer(layerIdx, labelSet); } MITK_INFO << "...finished!" << std::endl; std::vector result; result.push_back(output.GetPointer()); return result; } int LabelSetImageIO::GetIntByKey(const itk::MetaDataDictionary & dic,const std::string & str) { std::vector imgMetaKeys = dic.GetKeys(); std::vector::const_iterator itKey = imgMetaKeys.begin(); std::string metaString(""); for (; itKey != imgMetaKeys.end(); itKey ++) { itk::ExposeMetaData (dic, *itKey, metaString); if (itKey->find(str.c_str()) != std::string::npos) { return atoi(metaString.c_str()); } } return 0; } std::string LabelSetImageIO::GetStringByKey(const itk::MetaDataDictionary & dic,const std::string & str) { std::vector imgMetaKeys = dic.GetKeys(); std::vector::const_iterator itKey = imgMetaKeys.begin(); std::string metaString(""); for (; itKey != imgMetaKeys.end(); itKey ++) { itk::ExposeMetaData (dic, *itKey, metaString); if (itKey->find(str.c_str()) != std::string::npos) { return metaString; } } return metaString; } -Label::Pointer LabelSetImageIO::LoadLabelFromTiXmlDocument(TiXmlElement * labelElem) -{ - // reread - TiXmlElement * propElem = labelElem->FirstChildElement("property"); - - std::string name; - mitk::BaseProperty::Pointer prop; - - mitk::Label::Pointer label = mitk::Label::New(); - while(propElem) - { - LabelSetImageIO::PropertyFromXmlElem( name, prop, propElem ); - label->SetProperty( name, prop ); - propElem = propElem->NextSiblingElement( "property" ); - } - - return label.GetPointer(); -} - -bool LabelSetImageIO::PropertyFromXmlElem(std::string& key, mitk::BaseProperty::Pointer& prop, TiXmlElement* elem) -{ - std::string type; - elem->QueryStringAttribute("type", &type); - elem->QueryStringAttribute("key", &key); - - // construct name of serializer class - std::string serializername(type); - serializername += "Serializer"; - - std::list allSerializers = itk::ObjectFactoryBase::CreateAllInstance(serializername.c_str()); - if (allSerializers.size() < 1) - MITK_ERROR << "No serializer found for " << type << ". Skipping object"; - - if (allSerializers.size() > 1) - MITK_WARN << "Multiple deserializers found for " << type << "Using arbitrarily the first one."; - - for ( std::list::iterator iter = allSerializers.begin(); - iter != allSerializers.end(); - ++iter ) - { - if (BasePropertySerializer* serializer = dynamic_cast( iter->GetPointer() ) ) - { - try - { - prop = serializer->Deserialize(elem->FirstChildElement()); - } - catch (std::exception& e) - { - MITK_ERROR << "Deserializer " << serializer->GetNameOfClass() << " failed: " << e.what(); - return false; - } - break; - } - } - if(prop.IsNull()) - return false; - return true; -} - -void LabelSetImageIO::LoadLabelSetImagePreset(const std::string & presetFilename, mitk::LabelSetImage::Pointer& inputImage ) -{ - std::unique_ptr presetXmlDoc(new TiXmlDocument()); - - bool ok = presetXmlDoc->LoadFile(presetFilename); - if ( !ok ) - return; - - TiXmlElement * presetElem = presetXmlDoc->FirstChildElement("LabelSetImagePreset"); - if(!presetElem) - { - MITK_INFO << "No valid preset XML"; - return; - } - - int numberOfLayers; - presetElem->QueryIntAttribute("layers", &numberOfLayers); - - for(int i = 0 ; i < numberOfLayers; i++) - { - TiXmlElement * layerElem = presetElem->FirstChildElement("Layer"); - int numberOfLabels; - layerElem->QueryIntAttribute("labels", &numberOfLabels); - - if(inputImage->GetLabelSet(i) == NULL) inputImage->AddLayer(); - - TiXmlElement * labelElement = layerElem->FirstChildElement("Label"); - if(labelElement == NULL) break; - for(int j = 0 ; j < numberOfLabels; j++) - { - - mitk::Label::Pointer label = mitk::LabelSetImageIO::LoadLabelFromTiXmlDocument(labelElement); - inputImage->GetLabelSet()->AddLabel(label); - - labelElement = labelElement->NextSiblingElement("Label"); - if(labelElement == NULL) break; - } - } -} - LabelSetImageIO* LabelSetImageIO::IOClone() const { return new LabelSetImageIO(*this); } -TiXmlElement * LabelSetImageIO::GetLabelAsTiXmlElement(Label * label) -{ - - TiXmlElement* labelElem = new TiXmlElement("Label"); - - // add XML contents - const PropertyList::PropertyMap* propmap = label->GetMap(); - for ( PropertyList::PropertyMap::const_iterator iter = propmap->begin(); - iter != propmap->end(); - ++iter ) - { - std::string key = iter->first; - const BaseProperty* property = iter->second; - TiXmlElement* element = PropertyToXmlElem( key, property ); - if (element) - labelElem->LinkEndChild( element ); - - } - return labelElem; -} - -TiXmlElement* LabelSetImageIO::PropertyToXmlElem( const std::string& key, const BaseProperty* property ) -{ - TiXmlElement* keyelement = new TiXmlElement("property"); - keyelement->SetAttribute("key", key); - keyelement->SetAttribute("type", property->GetNameOfClass()); - - // construct name of serializer class - std::string serializername(property->GetNameOfClass()); - serializername += "Serializer"; - - std::list allSerializers = itk::ObjectFactoryBase::CreateAllInstance(serializername.c_str()); - if (allSerializers.size() < 1) - MITK_ERROR << "No serializer found for " << property->GetNameOfClass() << ". Skipping object"; - - if (allSerializers.size() > 1) - MITK_WARN << "Multiple serializers found for " << property->GetNameOfClass() << "Using arbitrarily the first one."; - - for ( std::list::iterator iter = allSerializers.begin(); - iter != allSerializers.end(); - ++iter ) - { - if (BasePropertySerializer* serializer = dynamic_cast( iter->GetPointer() ) ) - { - serializer->SetProperty(property); - try - { - TiXmlElement* valueelement = serializer->Serialize(); - if (valueelement) - keyelement->LinkEndChild( valueelement ); - } - catch (std::exception& e) - { - MITK_ERROR << "Serializer " << serializer->GetNameOfClass() << " failed: " << e.what(); - } - break; - } - } - return keyelement; -} - -bool LabelSetImageIO::SaveLabelSetImagePreset(const std::string & presetFilename, LabelSetImage::Pointer & inputImage) -{ - TiXmlDocument * presetXmlDoc = new TiXmlDocument(); - - TiXmlDeclaration * decl = new TiXmlDeclaration( "1.0", "", "" ); - presetXmlDoc->LinkEndChild( decl ); - - TiXmlElement * presetElement = new TiXmlElement("LabelSetImagePreset"); - presetElement->SetAttribute("layers", inputImage->GetNumberOfLayers()); - - presetXmlDoc->LinkEndChild(presetElement); - - for (unsigned int layerIdx=0; layerIdxGetNumberOfLayers(); layerIdx++) - { - TiXmlElement * layerElement = new TiXmlElement("Layer"); - layerElement->SetAttribute("index", layerIdx); - layerElement->SetAttribute("labels", inputImage->GetNumberOfLabels(layerIdx)); - - presetElement->LinkEndChild(layerElement); - - for (unsigned int labelIdx=0; labelIdxGetNumberOfLabels(layerIdx); labelIdx++) - { - TiXmlElement * labelAsXml = LabelSetImageIO::GetLabelAsTiXmlElement(inputImage->GetLabel(labelIdx,layerIdx)); - layerElement->LinkEndChild(labelAsXml); - } - } - - bool wasSaved = presetXmlDoc->SaveFile(presetFilename); - delete presetXmlDoc; - - return wasSaved; -} - } //namespace #endif //__mitkLabelSetImageWriter__cpp diff --git a/Modules/Multilabel/autoload/IO/mitkLabelSetImageIO.h b/Modules/Multilabel/autoload/IO/mitkLabelSetImageIO.h index 23bd4a15cc..807b51024f 100644 --- a/Modules/Multilabel/autoload/IO/mitkLabelSetImageIO.h +++ b/Modules/Multilabel/autoload/IO/mitkLabelSetImageIO.h @@ -1,75 +1,66 @@ /*=================================================================== 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 __mitkLabelSetImageWriter_h -#define __mitkLabelSetImageWriter_h +#ifndef __mitkLabelSetImageIO_h +#define __mitkLabelSetImageIO_h -#include "MitkMultilabelIOExports.h" #include #include namespace mitk { /** * Writes a LabelSetImage to a file * @ingroup Process */ // The export macro should be removed. Currently, the unit // tests directly instantiate this class. - class MITKMULTILABELIO_EXPORT LabelSetImageIO : public mitk::AbstractFileIO + class LabelSetImageIO : public mitk::AbstractFileIO { public: typedef mitk::LabelSetImage InputType; LabelSetImageIO(); // -------------- AbstractFileReader ------------- using AbstractFileReader::Read; /** * @brief Reads a number of mitk::LabelSetImages from the file system * @return a vector of mitk::LabelSetImages * @throws throws an mitk::Exception if an error ocurrs during parsing the nrrd header */ virtual std::vector Read() override; virtual ConfidenceLevel GetReaderConfidenceLevel() const override; // -------------- AbstractFileWriter ------------- virtual void Write() override; virtual ConfidenceLevel GetWriterConfidenceLevel() const override; // -------------- LabelSetImageIO specific functions ------------- - static bool SaveLabelSetImagePreset(const std::string & presetFilename, mitk::LabelSetImage::Pointer & inputImage); int GetIntByKey(const itk::MetaDataDictionary &dic, const std::string &str); std::string GetStringByKey(const itk::MetaDataDictionary &dic, const std::string &str); - static Label::Pointer LoadLabelFromTiXmlDocument(TiXmlElement *labelElem); - static void LoadLabelSetImagePreset(const std::string &presetFilename, mitk::LabelSetImage::Pointer &inputImage); private: LabelSetImageIO* IOClone() const override; - - static TiXmlElement *GetLabelAsTiXmlElement(Label *label); - - static TiXmlElement * PropertyToXmlElem( const std::string& key, const BaseProperty* property ); - static bool PropertyFromXmlElem(std::string& key, mitk::BaseProperty::Pointer& prop, TiXmlElement* elem); }; } // end of namespace mitk -#endif // __mitkLabelSetImageWriter_h +#endif // __mitkLabelSetImageIO_h diff --git a/Modules/Multilabel/files.cmake b/Modules/Multilabel/files.cmake index 7dcb657a8b..d068dd46b6 100644 --- a/Modules/Multilabel/files.cmake +++ b/Modules/Multilabel/files.cmake @@ -1,15 +1,17 @@ set(CPP_FILES mitkLabel.cpp mitkLabelSet.cpp mitkLabelSetImage.cpp mitkLabelSetImageConverter.cpp mitkLabelSetImageSource.cpp + mitkLabelSetImageSurfaceStampFilter.cpp mitkLabelSetImageToSurfaceFilter.cpp mitkLabelSetImageToSurfaceThreadedFilter.cpp mitkLabelSetImageVtkMapper2D.cpp mitkMultilabelObjectFactory.cpp + mitkLabelSetIOHelper.cpp ) set(RESOURCE_FILES ) diff --git a/Modules/Multilabel/mitkLabelSetIOHelper.cpp b/Modules/Multilabel/mitkLabelSetIOHelper.cpp new file mode 100644 index 0000000000..b631192825 --- /dev/null +++ b/Modules/Multilabel/mitkLabelSetIOHelper.cpp @@ -0,0 +1,228 @@ +/*=================================================================== + +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 "mitkLabelSetIOHelper.h" + +#include "mitkLabelSetImage.h" +#include +#include + + +bool mitk::LabelSetIOHelper::SaveLabelSetImagePreset(std::string &presetFilename, mitk::LabelSetImage::Pointer &inputImage) +{ + if (presetFilename.find(".lsetp") == std::string::npos) + { + presetFilename.append(".lsetp"); + } + + TiXmlDocument * presetXmlDoc = new TiXmlDocument(); + + TiXmlDeclaration * decl = new TiXmlDeclaration( "1.0", "", "" ); + presetXmlDoc->LinkEndChild( decl ); + + TiXmlElement * presetElement = new TiXmlElement("LabelSetImagePreset"); + presetElement->SetAttribute("layers", inputImage->GetNumberOfLayers()); + + presetXmlDoc->LinkEndChild(presetElement); + + for (unsigned int layerIdx=0; layerIdxGetNumberOfLayers(); layerIdx++) + { + TiXmlElement * layerElement = new TiXmlElement("Layer"); + layerElement->SetAttribute("index", layerIdx); + layerElement->SetAttribute("labels", inputImage->GetNumberOfLabels(layerIdx)); + + presetElement->LinkEndChild(layerElement); + + for (unsigned int labelIdx=0; labelIdxGetNumberOfLabels(layerIdx); labelIdx++) + { + TiXmlElement * labelAsXml = LabelSetIOHelper::GetLabelAsTiXmlElement(inputImage->GetLabel(labelIdx,layerIdx)); + layerElement->LinkEndChild(labelAsXml); + } + } + + bool wasSaved = presetXmlDoc->SaveFile(presetFilename); + delete presetXmlDoc; + + return wasSaved; +} + +void mitk::LabelSetIOHelper::LoadLabelSetImagePreset(std::string &presetFilename, mitk::LabelSetImage::Pointer &inputImage) +{ + if (presetFilename.find(".lsetp") == std::string::npos) + { + presetFilename.append(".lsetp"); + } + + std::unique_ptr presetXmlDoc(new TiXmlDocument()); + + bool ok = presetXmlDoc->LoadFile(presetFilename); + if ( !ok ) + return; + + TiXmlElement * presetElem = presetXmlDoc->FirstChildElement("LabelSetImagePreset"); + if(!presetElem) + { + MITK_INFO << "No valid preset XML"; + return; + } + + int numberOfLayers; + presetElem->QueryIntAttribute("layers", &numberOfLayers); + + for(int i = 0 ; i < numberOfLayers; i++) + { + TiXmlElement * layerElem = presetElem->FirstChildElement("Layer"); + int numberOfLabels; + layerElem->QueryIntAttribute("labels", &numberOfLabels); + + if(inputImage->GetLabelSet(i) == NULL) inputImage->AddLayer(); + + TiXmlElement * labelElement = layerElem->FirstChildElement("Label"); + if(labelElement == NULL) break; + for(int j = 0 ; j < numberOfLabels; j++) + { + + mitk::Label::Pointer label = mitk::LabelSetIOHelper::LoadLabelFromTiXmlDocument(labelElement); + + if(label->GetValue() == 0) + inputImage->SetExteriorLabel(label); + else + inputImage->GetLabelSet()->AddLabel(label); + + labelElement = labelElement->NextSiblingElement("Label"); + if(labelElement == NULL) break; + } + } +} + +TiXmlElement* mitk::LabelSetIOHelper::GetLabelAsTiXmlElement(Label *label) +{ + TiXmlElement* labelElem = new TiXmlElement("Label"); + + // add XML contents + const PropertyList::PropertyMap* propmap = label->GetMap(); + for ( PropertyList::PropertyMap::const_iterator iter = propmap->begin(); + iter != propmap->end(); + ++iter ) + { + std::string key = iter->first; + const BaseProperty* property = iter->second; + TiXmlElement* element = PropertyToXmlElem( key, property ); + if (element) + labelElem->LinkEndChild( element ); + + } + return labelElem; +} + +mitk::Label::Pointer mitk::LabelSetIOHelper::LoadLabelFromTiXmlDocument(TiXmlElement *labelElem) +{ + // reread + TiXmlElement * propElem = labelElem->FirstChildElement("property"); + + std::string name; + mitk::BaseProperty::Pointer prop; + + mitk::Label::Pointer label = mitk::Label::New(); + while(propElem) + { + LabelSetIOHelper::PropertyFromXmlElem( name, prop, propElem ); + label->SetProperty( name, prop ); + propElem = propElem->NextSiblingElement( "property" ); + } + + return label.GetPointer(); +} + +TiXmlElement* mitk::LabelSetIOHelper::PropertyToXmlElem( const std::string& key, const BaseProperty* property ) +{ + TiXmlElement* keyelement = new TiXmlElement("property"); + keyelement->SetAttribute("key", key); + keyelement->SetAttribute("type", property->GetNameOfClass()); + + // construct name of serializer class + std::string serializername(property->GetNameOfClass()); + serializername += "Serializer"; + + std::list allSerializers = itk::ObjectFactoryBase::CreateAllInstance(serializername.c_str()); + if (allSerializers.size() < 1) + MITK_ERROR << "No serializer found for " << property->GetNameOfClass() << ". Skipping object"; + + if (allSerializers.size() > 1) + MITK_WARN << "Multiple serializers found for " << property->GetNameOfClass() << "Using arbitrarily the first one."; + + for ( std::list::iterator iter = allSerializers.begin(); + iter != allSerializers.end(); + ++iter ) + { + if (BasePropertySerializer* serializer = dynamic_cast( iter->GetPointer() ) ) + { + serializer->SetProperty(property); + try + { + TiXmlElement* valueelement = serializer->Serialize(); + if (valueelement) + keyelement->LinkEndChild( valueelement ); + } + catch (std::exception& e) + { + MITK_ERROR << "Serializer " << serializer->GetNameOfClass() << " failed: " << e.what(); + } + break; + } + } + return keyelement; +} + +bool mitk::LabelSetIOHelper::PropertyFromXmlElem(std::string& key, mitk::BaseProperty::Pointer& prop, TiXmlElement* elem) +{ + std::string type; + elem->QueryStringAttribute("type", &type); + elem->QueryStringAttribute("key", &key); + + // construct name of serializer class + std::string serializername(type); + serializername += "Serializer"; + + std::list allSerializers = itk::ObjectFactoryBase::CreateAllInstance(serializername.c_str()); + if (allSerializers.size() < 1) + MITK_ERROR << "No serializer found for " << type << ". Skipping object"; + + if (allSerializers.size() > 1) + MITK_WARN << "Multiple deserializers found for " << type << "Using arbitrarily the first one."; + + for ( std::list::iterator iter = allSerializers.begin(); + iter != allSerializers.end(); + ++iter ) + { + if (BasePropertySerializer* serializer = dynamic_cast( iter->GetPointer() ) ) + { + try + { + prop = serializer->Deserialize(elem->FirstChildElement()); + } + catch (std::exception& e) + { + MITK_ERROR << "Deserializer " << serializer->GetNameOfClass() << " failed: " << e.what(); + return false; + } + break; + } + } + if(prop.IsNull()) + return false; + return true; +} diff --git a/Modules/Multilabel/mitkLabelSetIOHelper.h b/Modules/Multilabel/mitkLabelSetIOHelper.h new file mode 100644 index 0000000000..a2e6a749c1 --- /dev/null +++ b/Modules/Multilabel/mitkLabelSetIOHelper.h @@ -0,0 +1,96 @@ +/*=================================================================== + +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 __mitkLabelSetIOHelper_h +#define __mitkLabelSetIOHelper_h + +#include +#include + +class TiXmlElement; + +namespace mitk { + +class BaseProperty; +class LabelSetImage; +class Label; + +/** + * @brief The LabelSetIOHelper is a static helper class that supports serialization of mitk::LabelSetImage + * + * This class provides static functions for converting mitk::Label into XML and also allows the serialization + * of mitk::LabelSet as presets + */ +class MITKMULTILABEL_EXPORT LabelSetIOHelper +{ + +public: + + /** + * @brief Saves the mitk::LabelSet configuration of inputImage to presetFilename. + * The preset is stored as "*.lsetp" + * @param presetFilename the filename including the filesystem path + * @param inputImage the input image from which the preset should be generated + * @return true if the serialization was successful and false otherwise + */ + static bool SaveLabelSetImagePreset(std::string & presetFilename, itk::SmartPointer & inputImage); + + /** + * @brief Loads an existing preset for a mitk::LabelSetImage from presetFilename and applies it to inputImage + * @param presetFilename the filename of the preset including the filesystem path + * @param inputImage the image to which the loaded preset will be applied + */ + static void LoadLabelSetImagePreset(std::string &presetFilename, itk::SmartPointer &inputImage); + + /** + * @brief Creates a mitk::Label from a TiXmlElement + * @param labelElem the xml element from which a mitk::Label will be created + * @return the created mitk::Label + */ + static itk::SmartPointer LoadLabelFromTiXmlDocument(TiXmlElement *labelElem); + + /** + * @brief Creates a TiXmlElement from a mitk::Label + * @param label the mitk::Label from which the xml element will be created + * @return the created TiXmlElement + */ + static TiXmlElement *GetLabelAsTiXmlElement(Label *label); + + /** + * @brief Since a mitk::Label is basically a mitk::PropertyList this function coverts the label's properties into XML + * @param key the property's key which will be used in the XML element + * @param property the mitk::BaseProperty that should be converted + * @return the created TiXmlElement + */ + static TiXmlElement * PropertyToXmlElem( const std::string& key, const BaseProperty* property ); + + /** + * @brief Since a mitk::Label is basically a mitk::PropertyList this function coverts a XML element into a property + * @param key the property's key + * @param prop the mitk::BaseProperty that will be created + * @param elem the XML elem from which the property will be created + * @return true if the conversion was successful and false otherwise + */ + static bool PropertyFromXmlElem(std::string& key, itk::SmartPointer& prop, TiXmlElement* elem); + +private: + LabelSetIOHelper(); + +}; + +} + +#endif // __mitkLabelSetIOHelper_h diff --git a/Modules/Multilabel/mitkLabelSetImageSurfaceStampFilter.cpp b/Modules/Multilabel/mitkLabelSetImageSurfaceStampFilter.cpp new file mode 100644 index 0000000000..dc94cfa62f --- /dev/null +++ b/Modules/Multilabel/mitkLabelSetImageSurfaceStampFilter.cpp @@ -0,0 +1,108 @@ +/*=================================================================== + +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 "mitkLabelSetImageSurfaceStampFilter.h" + +#include "mitkImageAccessByItk.h" +#include "mitkImageCast.h" + +#include +#include +#include +#include + +mitk::LabelSetImageSurfaceStampFilter::LabelSetImageSurfaceStampFilter() +{ + this->SetNumberOfIndexedInputs(1); + this->SetNumberOfRequiredInputs(1); +} + +mitk::LabelSetImageSurfaceStampFilter::~LabelSetImageSurfaceStampFilter() +{ +} + +void mitk::LabelSetImageSurfaceStampFilter::GenerateData() +{ + //GenerateOutputInformation(); + this->SetNthOutput(0,this->GetInput(0)); + + mitk::Image::Pointer inputImage = this->GetInput(0); + + if (m_Surface.IsNull()) + { + MITK_ERROR << "Input surface is NULL."; + return; + } + + mitk::SurfaceToImageFilter::Pointer surfaceToImageFilter = mitk::SurfaceToImageFilter::New(); + surfaceToImageFilter->MakeOutputBinaryOn(); + surfaceToImageFilter->SetInput(m_Surface); + surfaceToImageFilter->SetImage(inputImage); + surfaceToImageFilter->Update(); + mitk::Image::Pointer resultImage = surfaceToImageFilter->GetOutput(); + + AccessByItk_1(inputImage, ItkImageProcessing, resultImage); + inputImage->DisconnectPipeline(); +} + +template +void mitk::LabelSetImageSurfaceStampFilter::ItkImageProcessing( itk::Image* itkImage, mitk::Image::Pointer resultImage ) +{ + typedef itk::Image ImageType; + mitk::LabelSetImage::Pointer LabelSetInputImage = dynamic_cast(GetInput()); + try + { + typename ImageType::Pointer itkResultImage = ImageType::New(); + mitk::CastToItkImage(resultImage, itkResultImage); + + typedef itk::ImageRegionConstIterator< ImageType > SourceIteratorType; + typedef itk::ImageRegionIterator< ImageType > TargetIteratorType; + + SourceIteratorType sourceIter(itkResultImage, itkResultImage->GetLargestPossibleRegion()); + sourceIter.GoToBegin(); + + TargetIteratorType targetIter(itkImage, itkImage->GetLargestPossibleRegion()); + targetIter.GoToBegin(); + + int activeLabel = (LabelSetInputImage->GetActiveLabel(LabelSetInputImage->GetActiveLayer()))->GetValue(); + + while (!sourceIter.IsAtEnd()) + { + int sourceValue = static_cast(sourceIter.Get()); + int targetValue = static_cast(targetIter.Get()); + + if ((sourceValue != 0) && (m_ForceOverwrite || !LabelSetInputImage->GetLabel(targetValue)->GetLocked())) // skip exterior and locked labels + { + targetIter.Set(activeLabel); + } + ++sourceIter; + ++targetIter; + } + } + catch (itk::ExceptionObject& e) + { + mitkThrow() << e.GetDescription(); + } + this->Modified(); +} + +void mitk::LabelSetImageSurfaceStampFilter::GenerateOutputInformation() +{ + mitk::Image::Pointer inputImage = (mitk::Image*) this->GetInput(); + mitk::Image::Pointer output = this->GetOutput(); + itkDebugMacro(<<"GenerateOutputInformation()"); + if(inputImage.IsNull()) return; +} diff --git a/Modules/Multilabel/mitkLabelSetImageSurfaceStampFilter.h b/Modules/Multilabel/mitkLabelSetImageSurfaceStampFilter.h new file mode 100644 index 0000000000..4056af7cf1 --- /dev/null +++ b/Modules/Multilabel/mitkLabelSetImageSurfaceStampFilter.h @@ -0,0 +1,71 @@ +/*=================================================================== + +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 __mitkLabelSetImageSurfaceStampFilter_h +#define __mitkLabelSetImageSurfaceStampFilter_h + +#include "MitkMultilabelExports.h" + +//MITK +#include +#include "mitkImageToImageFilter.h" +#include +#include + +namespace mitk +{ + class MITKMULTILABEL_EXPORT LabelSetImageSurfaceStampFilter : public ImageToImageFilter + { + public: + + mitkClassMacro( LabelSetImageSurfaceStampFilter , ImageToImageFilter ); + itkFactorylessNewMacro(Self); + itkCloneMacro(Self); + + itkGetConstMacro(Surface,Surface::Pointer); + itkSetMacro(Surface,Surface::Pointer); + itkGetConstMacro(ForceOverwrite,bool); + itkSetMacro(ForceOverwrite,bool); + private: + /*! + \brief standard constructor + */ + LabelSetImageSurfaceStampFilter(); + /*! + \brief standard destructor + */ + ~LabelSetImageSurfaceStampFilter(); + /*! + \brief Method generating the output information of this filter (e.g. image dimension, image type, etc.). + The interface ImageToImageFilter requires this implementation. Everything is taken from the input image. + */ + virtual void GenerateOutputInformation() override; + /*! + \brief Method generating the output of this filter. Called in the updated process of the pipeline. + This method generates the smoothed output image. + */ + virtual void GenerateData() override; + + /*! + \brief Internal templated method calling the ITK bilteral filter. Here the actual filtering is performed. + */ + template + void ItkImageProcessing( itk::Image* itkImage, mitk::Image::Pointer resultImage ); + + Surface::Pointer m_Surface; + bool m_ForceOverwrite; + }; +} //END mitk namespace +#endif diff --git a/Modules/Segmentation/Algorithms/mitkContourModelSetToImageFilter.cpp b/Modules/Segmentation/Algorithms/mitkContourModelSetToImageFilter.cpp index da18224413..a4b608bd89 100644 --- a/Modules/Segmentation/Algorithms/mitkContourModelSetToImageFilter.cpp +++ b/Modules/Segmentation/Algorithms/mitkContourModelSetToImageFilter.cpp @@ -1,280 +1,280 @@ /*=================================================================== 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 "mitkContourModelSetToImageFilter.h" #include #include #include #include #include #include #include mitk::ContourModelSetToImageFilter::ContourModelSetToImageFilter() : m_MakeOutputBinary( true ), m_TimeStep( 0 ), m_ReferenceImage( nullptr ) { // Create the output. itk::DataObject::Pointer output = this->MakeOutput(0); Superclass::SetNumberOfRequiredInputs(1); Superclass::SetNumberOfRequiredOutputs(1); Superclass::SetNthOutput(0, output); } mitk::ContourModelSetToImageFilter::~ContourModelSetToImageFilter() { } void mitk::ContourModelSetToImageFilter::GenerateInputRequestedRegion() { mitk::Image* output = this->GetOutput(); if((output->IsInitialized()==false) ) return; GenerateTimeInInputRegion(output, const_cast< mitk::Image * > ( m_ReferenceImage )); } void mitk::ContourModelSetToImageFilter::GenerateOutputInformation() { mitk::Image::Pointer output = this->GetOutput(); itkDebugMacro(<<"GenerateOutputInformation()"); if((m_ReferenceImage == nullptr) || (m_ReferenceImage->IsInitialized() == false) || (m_ReferenceImage->GetTimeGeometry() == nullptr)) return; if (m_MakeOutputBinary) { output->Initialize(mitk::MakeScalarPixelType() , *m_ReferenceImage->GetTimeGeometry(),1,1); } else { output->Initialize(m_ReferenceImage->GetPixelType(), *m_ReferenceImage->GetTimeGeometry()); } output->SetPropertyList(m_ReferenceImage->GetPropertyList()->Clone()); } itk::DataObject::Pointer mitk::ContourModelSetToImageFilter::MakeOutput ( DataObjectPointerArraySizeType /*idx*/ ) { return OutputType::New().GetPointer(); } itk::DataObject::Pointer mitk::ContourModelSetToImageFilter::MakeOutput( const DataObjectIdentifierType & name ) { itkDebugMacro("MakeOutput(" << name << ")"); if( this->IsIndexedOutputName(name) ) { return this->MakeOutput( this->MakeIndexFromOutputName(name) ); } return OutputType::New().GetPointer(); } const mitk::ContourModelSet* mitk::ContourModelSetToImageFilter::GetInput(void) { if (this->GetNumberOfInputs() < 1) { return nullptr; } return static_cast< const mitk::ContourModelSet* > ( this->ProcessObject::GetInput(0) ); } void mitk::ContourModelSetToImageFilter::SetInput(const ContourModelSet *input) { // Process object is not const-correct so the const_cast is required here this->ProcessObject::SetNthInput(0, const_cast< mitk::ContourModelSet* >( input ) ); } void mitk::ContourModelSetToImageFilter::SetImage(const mitk::Image *refImage) { m_ReferenceImage = refImage; } const mitk::Image* mitk::ContourModelSetToImageFilter::GetImage(void) { return m_ReferenceImage; } void mitk::ContourModelSetToImageFilter::GenerateData() { mitk::ContourModelSet* contourSet = const_cast(this->GetInput()); // Initializing progressbar unsigned int num_contours = contourSet->GetContourModelList()->size(); mitk::ProgressBar::GetInstance()->AddStepsToDo(num_contours); // Assure that the volume data of the output is set (fill volume with zeros) this->InitializeOutputEmpty(); mitk::Image::Pointer outputImage = const_cast(this->GetOutput()); if(outputImage.IsNull() || outputImage->IsInitialized() == false || !outputImage->IsVolumeSet(m_TimeStep)) { MITK_ERROR<<"Error creating output for specified image!"; return; } if (!contourSet || contourSet->GetContourModelList()->size() == 0) { MITK_ERROR<<"No contours specified!"; return; } mitk::BaseGeometry* outputImageGeo = outputImage->GetGeometry(m_TimeStep); // Create mitkVtkImageOverwrite which is needed to write the slice back into the volume vtkSmartPointer reslice = vtkSmartPointer::New(); // Create ExtractSliceFilter for extracting the corresponding slices from the volume mitk::ExtractSliceFilter::Pointer extractor = mitk::ExtractSliceFilter::New(reslice); extractor->SetInput( outputImage ); extractor->SetTimeStep( m_TimeStep ); extractor->SetResliceTransformByGeometry( outputImageGeo ); // Fill each contour of the contourmodelset into the image auto it = contourSet->Begin(); auto end = contourSet->End(); while (it != end) { mitk::ContourModel* contour = it->GetPointer(); // 1. Create slice geometry using the contour points mitk::PlaneGeometry::Pointer plane = mitk::PlaneGeometry::New(); mitk::Point3D point3D, tempPoint; mitk::Vector3D normal; mitk::Image::Pointer slice; int sliceIndex; bool isFrontside = true; bool isRotated = false; // Determine plane orientation point3D = contour->GetVertexAt(0)->Coordinates; tempPoint = contour->GetVertexAt(contour->GetNumberOfVertices()*0.25)->Coordinates; mitk::Vector3D vec = point3D - tempPoint; vec.Normalize(); outputImageGeo->WorldToIndex(point3D, point3D); mitk::PlaneGeometry::PlaneOrientation orientation; if (mitk::Equal(vec[0], 0)) { orientation = mitk::PlaneGeometry::Sagittal; sliceIndex = point3D[0]; } else if (mitk::Equal(vec[1], 0)) { orientation = mitk::PlaneGeometry::Frontal; sliceIndex = point3D[1]; } else if (mitk::Equal(vec[2], 0)) { orientation = mitk::PlaneGeometry::Axial; sliceIndex = point3D[2]; } else { // TODO Maybe rotate geometry to extract slice? MITK_ERROR<<"Cannot detect correct slice number! Only axial, sagittal and frontal oriented contours are supported!"; return; } // Initialize plane using the detected orientation plane->InitializeStandardPlane(outputImageGeo, orientation, sliceIndex, isFrontside, isRotated); point3D = plane->GetOrigin(); normal = plane->GetNormal(); normal.Normalize(); point3D += normal * 0.5;//pixelspacing is 1, so half the spacing is 0.5 plane->SetOrigin(point3D); // 2. Extract slice at the given position extractor->SetWorldGeometry( plane ); extractor->SetVtkOutputRequest(false); reslice->SetOverwriteMode(false); extractor->Modified(); extractor->Update(); slice = extractor->GetOutput(); slice->DisconnectPipeline(); // 3. Fill contour into slice mitk::ContourModel::Pointer projectedContour = mitk::ContourModelUtils::ProjectContourTo2DSlice(slice, contour, true, false); - mitk::ContourModelUtils::FillContourInSlice(projectedContour, slice); + mitk::ContourModelUtils::FillContourInSlice(projectedContour, slice, outputImage); // 4. Write slice back into image volume reslice->SetInputSlice(slice->GetVtkImageData()); //set overwrite mode to true to write back to the image volume reslice->SetOverwriteMode(true); reslice->Modified(); extractor->Modified(); extractor->Update(); reslice->SetInputSlice(nullptr); // Progress mitk::ProgressBar::GetInstance()->Progress(); ++it; } outputImage->Modified(); outputImage->GetVtkImageData()->Modified(); } void mitk::ContourModelSetToImageFilter::InitializeOutputEmpty() { // Initialize the output's volume with zeros mitk::Image* output = this->GetOutput(); unsigned int byteSize = output->GetPixelType().GetSize(); if(output->GetDimension() < 4) { for (unsigned int dim = 0; dim < output->GetDimension(); ++dim) { byteSize *= output->GetDimension(dim); } mitk::ImageWriteAccessor writeAccess(output, output->GetVolumeData(0)); memset( writeAccess.GetData(), 0, byteSize ); } else { //if we have a time-resolved image we need to set memory to 0 for each time step for (unsigned int dim = 0; dim < 3; ++dim) { byteSize *= output->GetDimension(dim); } for( unsigned int volumeNumber = 0; volumeNumber < output->GetDimension(3); volumeNumber++) { mitk::ImageWriteAccessor writeAccess(output, output->GetVolumeData(volumeNumber)); memset( writeAccess.GetData(), 0, byteSize ); } } } diff --git a/Modules/Segmentation/Algorithms/mitkContourUtils.cpp b/Modules/Segmentation/Algorithms/mitkContourUtils.cpp index 80b9831cc1..8e06ff8047 100755 --- a/Modules/Segmentation/Algorithms/mitkContourUtils.cpp +++ b/Modules/Segmentation/Algorithms/mitkContourUtils.cpp @@ -1,79 +1,79 @@ /*=================================================================== 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 "mitkContourUtils.h" #include "mitkContourModelUtils.h" mitk::ContourUtils::ContourUtils() { } mitk::ContourUtils::~ContourUtils() { } mitk::ContourModel::Pointer mitk::ContourUtils::ProjectContourTo2DSlice(Image* slice, Contour* contourIn3D, bool itkNotUsed( correctionForIpSegmentation ), bool constrainToInside) { mitk::Contour::PointsContainerIterator it = contourIn3D->GetPoints()->Begin(); mitk::Contour::PointsContainerIterator end = contourIn3D->GetPoints()->End(); mitk::ContourModel::Pointer contour = mitk::ContourModel::New(); while(it!=end) { contour->AddVertex(it.Value()); it++; } return mitk::ContourModelUtils::ProjectContourTo2DSlice(slice, contour, false/*not used*/, constrainToInside ); } mitk::ContourModel::Pointer mitk::ContourUtils::BackProjectContourFrom2DSlice(const BaseGeometry* sliceGeometry, Contour* contourIn2D, bool itkNotUsed( correctionForIpSegmentation ) ) { mitk::Contour::PointsContainerIterator it = contourIn2D->GetPoints()->Begin(); mitk::Contour::PointsContainerIterator end = contourIn2D->GetPoints()->End(); mitk::ContourModel::Pointer contour = mitk::ContourModel::New(); while(it!=end) { contour->AddVertex(it.Value()); it++; } return mitk::ContourModelUtils::BackProjectContourFrom2DSlice(sliceGeometry, contour, false/*not used*/); } void mitk::ContourUtils::FillContourInSlice( Contour* projectedContour, Image* sliceImage, int paintingPixelValue ) { mitk::Contour::PointsContainerIterator it = projectedContour->GetPoints()->Begin(); mitk::Contour::PointsContainerIterator end = projectedContour->GetPoints()->End(); mitk::ContourModel::Pointer contour = mitk::ContourModel::New(); while(it!=end) { contour->AddVertex(it.Value()); it++; } - mitk::ContourModelUtils::FillContourInSlice(contour, sliceImage, paintingPixelValue); + mitk::ContourModelUtils::FillContourInSlice(contour, sliceImage, sliceImage, paintingPixelValue); } diff --git a/Modules/Segmentation/Algorithms/mitkCorrectorAlgorithm.cpp b/Modules/Segmentation/Algorithms/mitkCorrectorAlgorithm.cpp index 6f471ee5dd..966902a12d 100644 --- a/Modules/Segmentation/Algorithms/mitkCorrectorAlgorithm.cpp +++ b/Modules/Segmentation/Algorithms/mitkCorrectorAlgorithm.cpp @@ -1,498 +1,501 @@ /*=================================================================== 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 "mitkCorrectorAlgorithm.h" #include "mitkImageCast.h" #include "mitkImageAccessByItk.h" #include "mitkITKImageImport.h" #include "mitkImageDataItem.h" #include "mitkContourUtils.h" #include "mitkLegacyAdaptors.h" #include #include "itkImageDuplicator.h" #include "itkImageRegionIterator.h" #include "itkCastImageFilter.h" + mitk::CorrectorAlgorithm::CorrectorAlgorithm() :ImageToImageFilter() , m_FillColor(1) , m_EraseColor(0) { } mitk::CorrectorAlgorithm::~CorrectorAlgorithm() { } template void ConvertBackToCorrectPixelType(itk::Image< TPixel, VDimensions> * reference, mitk::Image::Pointer target, itk::Image< mitk::CorrectorAlgorithm::DefaultSegmentationDataType, 2 >::Pointer segmentationPixelTypeImage) { typedef itk::Image< mitk::CorrectorAlgorithm::DefaultSegmentationDataType, 2 > InputImageType; typedef itk::Image< TPixel, 2 > OutputImageType; typedef itk::CastImageFilter< InputImageType, OutputImageType > CastImageFilterType; typename CastImageFilterType::Pointer castImageFilter = CastImageFilterType::New(); castImageFilter->SetInput(segmentationPixelTypeImage); castImageFilter->Update(); typename OutputImageType::Pointer tempItkImage = castImageFilter->GetOutput(); tempItkImage->DisconnectPipeline(); mitk::CastToMitkImage(tempItkImage, target); } void mitk::CorrectorAlgorithm::GenerateData() { Image::Pointer inputImage = const_cast(ImageToImageFilter::GetInput(0)); if (inputImage.IsNull() || inputImage->GetDimension() != 2) { itkExceptionMacro("CorrectorAlgorithm needs a 2D image as input."); } if (m_Contour.IsNull()) { itkExceptionMacro("CorrectorAlgorithm needs a Contour object as input."); } // copy the input (since m_WorkingImage will be changed later) m_WorkingImage = inputImage; TimeGeometry::Pointer originalGeometry = NULL; if (inputImage->GetTimeGeometry()) { originalGeometry = inputImage->GetTimeGeometry()->Clone(); m_WorkingImage->SetTimeGeometry(originalGeometry); } else { itkExceptionMacro("Original image does not have a 'Time sliced geometry'! Cannot copy."); } Image::Pointer temporarySlice; + // Convert to DefaultSegmentationDataType (because TobiasHeimannCorrectionAlgorithm relys on that data type) { itk::Image< DefaultSegmentationDataType, 2 >::Pointer correctPixelTypeImage; CastToItkImage(m_WorkingImage, correctPixelTypeImage); assert(correctPixelTypeImage.IsNotNull()); // possible bug in CastToItkImage ? // direction maxtrix is wrong/broken/not working after CastToItkImage, leading to a failed assertion in // mitk/Core/DataStructures/mitkSlicedGeometry3D.cpp, 479: // virtual void mitk::SlicedGeometry3D::SetSpacing(const mitk::Vector3D&): Assertion `aSpacing[0]>0 && aSpacing[1]>0 && aSpacing[2]>0' failed // solution here: we overwrite it with an unity matrix itk::Image< DefaultSegmentationDataType, 2 >::DirectionType imageDirection; + imageDirection.SetIdentity(); //correctPixelTypeImage->SetDirection(imageDirection); temporarySlice = this->GetOutput(); // temporarySlice = ImportItkImage( correctPixelTypeImage ); + //m_FillColor = 1; + m_EraseColor = 0; + ImprovedHeimannCorrectionAlgorithm(correctPixelTypeImage); //this is suboptimal, needs to be kept synchronous to DefaultSegmentationDataType if (inputImage->GetChannelDescriptor().GetPixelType().GetComponentType() == itk::ImageIOBase::USHORT) { //the cast at the beginning did not copy the data CastToMitkImage(correctPixelTypeImage, temporarySlice); } else { //it did copy the data and cast the pixel type AccessByItk_n(m_WorkingImage, ConvertBackToCorrectPixelType, (temporarySlice, correctPixelTypeImage)); } } temporarySlice->SetTimeGeometry(originalGeometry); } template itk::Index<2> mitk::CorrectorAlgorithm::ensureIndexInImage(ScalarType i0, ScalarType i1) { itk::Index<2> toReturn; itk::Size<5> size = m_WorkingImage->GetLargestPossibleRegion().GetSize(); toReturn[0] = std::min((ScalarType)(size[0] - 1), std::max((ScalarType)0.0, i0)); toReturn[1] = std::min((ScalarType)(size[1] - 1), std::max((ScalarType)0.0, i1)); return toReturn; } bool mitk::CorrectorAlgorithm::ImprovedHeimannCorrectionAlgorithm(itk::Image< DefaultSegmentationDataType, 2 >::Pointer pic) { /*! Some documentation (not by the original author) TobiasHeimannCorrectionAlgorithm will be called, when the user has finished drawing a freehand line. There should be different results, depending on the line's properties: 1. Without any prior segmentation, the start point and the end point of the drawn line will be connected to a contour and the area enclosed by the contour will be marked as segmentation. 2. When the whole line is inside a segmentation, start and end point will be connected to a contour and the area of this contour will be subtracted from the segmentation. 3. When the line starts inside a segmentation and ends outside with only a single transition from segmentation to no-segmentation, nothing will happen. 4. When there are multiple transitions between inside-segmentation and outside-segmentation, the line will be divided in so called segments. Each segment is either fully inside or fully outside a segmentation. When it is inside a segmentation, its enclosed area will be subtracted from the segmentation. When the segment is outside a segmentation, its enclosed area it will be added to the segmentation. The algorithm is described in full length in Tobias Heimann's diploma thesis (MBI Technical Report 145, p. 37 - 40). */ ContourModel::Pointer projectedContour = mitk::ContourModelUtils::ProjectContourTo2DSlice(m_WorkingImage, m_Contour, true, false); bool firstPointIsFillingColor = false; if (projectedContour.IsNull() || projectedContour->GetNumberOfVertices() < 2) { return false; } // Read the first point of the contour ContourModel::VertexIterator contourIter = projectedContour->Begin(); if (contourIter == projectedContour->End()) return false; itk::Index<2> previousIndex; previousIndex = ensureIndexInImage((*contourIter)->Coordinates[0], (*contourIter)->Coordinates[1]); ++contourIter; int currentColor = (pic->GetPixel(previousIndex) == m_FillColor); firstPointIsFillingColor = currentColor; TSegData currentSegment; int countOfSegments = 1; bool firstSegment = true; ContourModel::VertexIterator contourEnd = projectedContour->End(); for (; contourIter != contourEnd; ++contourIter) { // Get current point itk::Index<2> currentIndex; currentIndex = ensureIndexInImage((*contourIter)->Coordinates[0] + 0.5, (*contourIter)->Coordinates[1] + 0.5); // Calculate length and slope double slopeX = currentIndex[0] - previousIndex[0]; double slopeY = currentIndex[1] - previousIndex[1]; double length = std::sqrt(slopeX * slopeX + slopeY * slopeY); double deltaX = slopeX / length; double deltaY = slopeY / length; for (double i = 0; i <= length && length > 0; i += 1) { itk::Index<2> temporaryIndex; temporaryIndex = ensureIndexInImage(previousIndex[0] + deltaX * i, previousIndex[1] + deltaY * i); if (!pic->GetLargestPossibleRegion().IsInside(temporaryIndex)) continue; if ((pic->GetPixel(temporaryIndex) == m_FillColor) != currentColor) { currentSegment.points.push_back(temporaryIndex); if (!firstSegment) { ModifySegment(currentSegment, pic); } else { firstSegment = false; } currentSegment = TSegData(); ++countOfSegments; currentColor = (pic->GetPixel(temporaryIndex) == m_FillColor); } currentSegment.points.push_back(temporaryIndex); } previousIndex = currentIndex; } // Check if only on Segment if (firstSegment && currentSegment.points.size() > 0) { - ContourModel::Pointer projectedContour = mitk::ContourModelUtils::ProjectContourTo2DSlice(m_WorkingImage, m_Contour, true, false); - projectedContour->Close(); - if (firstPointIsFillingColor) - { - ContourModelUtils::FillContourInSlice(projectedContour, 0, m_WorkingImage, m_EraseColor); - } - else - { - ContourModelUtils::FillContourInSlice(projectedContour, 0, m_WorkingImage, m_FillColor); - } + ContourModel::Pointer projectedContour = mitk::ContourModelUtils::ProjectContourTo2DSlice( m_WorkingImage, m_Contour, true, false ); + projectedContour->Close(); + if (firstPointIsFillingColor) + { + ContourModelUtils::FillContourInSlice(projectedContour, 0, m_WorkingImage, m_WorkingImage, m_EraseColor); + } else + { + ContourModelUtils::FillContourInSlice(projectedContour, 0, m_WorkingImage, m_WorkingImage, m_FillColor); + } } return true; } void mitk::CorrectorAlgorithm::ColorSegment(const mitk::CorrectorAlgorithm::TSegData &segment, itk::Image< mitk::CorrectorAlgorithm::DefaultSegmentationDataType, 2 >::Pointer pic) { int colorMode = (pic->GetPixel(segment.points[0]) == m_FillColor); int color = 0; if (colorMode) color = m_EraseColor; else color = m_FillColor; std::vector< itk::Index<2> >::const_iterator indexIterator; std::vector< itk::Index<2> >::const_iterator indexEnd; indexIterator = segment.points.begin(); indexEnd = segment.points.end(); for (; indexIterator != indexEnd; ++indexIterator) { pic->SetPixel(*indexIterator, color); } } -itk::Image< mitk::CorrectorAlgorithm::DefaultSegmentationDataType, 2 >::Pointer mitk::CorrectorAlgorithm::CloneImage(itk::Image< mitk::CorrectorAlgorithm::DefaultSegmentationDataType, 2 >::Pointer pic) +static itk::Image< mitk::CorrectorAlgorithm::DefaultSegmentationDataType, 2 >::Pointer CloneImage(itk::Image< mitk::CorrectorAlgorithm::DefaultSegmentationDataType, 2 >::Pointer pic) { typedef itk::Image< mitk::CorrectorAlgorithm::DefaultSegmentationDataType, 2 > ItkImageType; - typedef itk::ImageDuplicator< ItkImageType > DuplicatorType; DuplicatorType::Pointer duplicator = DuplicatorType::New(); duplicator->SetInputImage(pic); duplicator->Update(); return duplicator->GetOutput(); } -itk::Index<2> mitk::CorrectorAlgorithm::GetFirstPoint(const mitk::CorrectorAlgorithm::TSegData &segment, itk::Image< mitk::CorrectorAlgorithm::DefaultSegmentationDataType, 2 >::Pointer pic) +static itk::Index<2> GetFirstPoint(const mitk::CorrectorAlgorithm::TSegData &segment, itk::Image< mitk::CorrectorAlgorithm::DefaultSegmentationDataType, 2 >::Pointer pic, int fillColor) { int colorMode = (pic->GetPixel(segment.points[0]) == m_FillColor); std::vector< itk::Index<2> >::const_iterator indexIterator; std::vector< itk::Index<2> >::const_iterator indexEnd; indexIterator = segment.points.begin(); indexEnd = segment.points.end(); itk::Index<2> index; for (; indexIterator != indexEnd; ++indexIterator) { for (int xOffset = -1; xOffset < 2; ++xOffset) { for (int yOffset = -1; yOffset < 2; ++yOffset) { index = ensureIndexInImage((*indexIterator)[0] - xOffset, (*indexIterator)[1] - yOffset); if ((pic->GetPixel(index) == m_FillColor) != colorMode) { return index; } } } } mitkThrow() << "No Starting point is found next to the curve."; } std::vector > mitk::CorrectorAlgorithm::FindSeedPoints(const mitk::CorrectorAlgorithm::TSegData &segment, itk::Image< mitk::CorrectorAlgorithm::DefaultSegmentationDataType, 2 >::Pointer pic) { typedef itk::Image< mitk::CorrectorAlgorithm::DefaultSegmentationDataType, 2 > ItkImageType; typedef itk::Image< mitk::CorrectorAlgorithm::DefaultSegmentationDataType, 2 >::Pointer ItkImagePointerType; std::vector > seedPoints; try { itk::Index<2> firstPoint = GetFirstPoint(segment, pic); seedPoints.push_back(firstPoint); } catch (mitk::Exception e) { return seedPoints; } if (segment.points.size() < 4) return seedPoints; std::vector< itk::Index<2> >::const_iterator indexIterator; std::vector< itk::Index<2> >::const_iterator indexEnd; indexIterator = segment.points.begin(); indexEnd = segment.points.end(); ItkImagePointerType listOfPoints = CloneImage(pic); listOfPoints->FillBuffer(0); listOfPoints->SetPixel(seedPoints[0], 1); for (; indexIterator != indexEnd; ++indexIterator) { listOfPoints->SetPixel(*indexIterator, 2); } indexIterator = segment.points.begin(); indexIterator++; indexIterator++; indexEnd--; indexEnd--; for (; indexIterator != indexEnd; ++indexIterator) { bool pointFound = true; while (pointFound) { pointFound = false; itk::Index<2> index; itk::Index<2> index2; for (int xOffset = -1; xOffset < 2; ++xOffset) { for (int yOffset = -1; yOffset < 2; ++yOffset) { index = ensureIndexInImage((*indexIterator)[0] - xOffset, (*indexIterator)[1] - yOffset); index2 = index; if (listOfPoints->GetPixel(index2) > 0) continue; index[0] = index[0] - 1; index = ensureIndexInImage(index[0], index[1]); if (listOfPoints->GetPixel(index) == 1) { pointFound = true; seedPoints.push_back(index2); listOfPoints->SetPixel(index2, 1); continue; } index[0] = index[0] + 2; index = ensureIndexInImage(index[0], index[1]); if (listOfPoints->GetPixel(index) == 1) { pointFound = true; seedPoints.push_back(index2); listOfPoints->SetPixel(index2, 1); continue; } index[0] = index[0] - 1; index[1] = index[1] - 1; index = ensureIndexInImage(index[0], index[1]); if (listOfPoints->GetPixel(index) == 1) { pointFound = true; seedPoints.push_back(index2); listOfPoints->SetPixel(index2, 1); continue; } index[1] = index[1] + 2; index = ensureIndexInImage(index[0], index[1]); if (listOfPoints->GetPixel(index) == 1) { pointFound = true; seedPoints.push_back(index2); listOfPoints->SetPixel(index2, 1); continue; } } } } } return seedPoints; } int mitk::CorrectorAlgorithm::FillRegion(const std::vector > &seedPoints, itk::Image< mitk::CorrectorAlgorithm::DefaultSegmentationDataType, 2 >::Pointer pic) { int numberOfPixel = 0; int mode = (pic->GetPixel(seedPoints[0]) == m_FillColor); int drawColor = m_FillColor; if (mode) { drawColor = m_EraseColor; } std::vector > workPoints; workPoints = seedPoints; // workPoints.push_back(seedPoints[0]); while (workPoints.size() > 0) { itk::Index<2> currentIndex = workPoints.back(); workPoints.pop_back(); if ((pic->GetPixel(currentIndex) == m_FillColor) == mode) ++numberOfPixel; pic->SetPixel(currentIndex, drawColor); currentIndex = ensureIndexInImage(currentIndex[0] - 1, currentIndex[1]); if (pic->GetLargestPossibleRegion().IsInside(currentIndex) && (pic->GetPixel(currentIndex) == m_FillColor) == mode) workPoints.push_back(currentIndex); currentIndex = ensureIndexInImage(currentIndex[0] + 2, currentIndex[1]); if (pic->GetLargestPossibleRegion().IsInside(currentIndex) && (pic->GetPixel(currentIndex) == m_FillColor) == mode) workPoints.push_back(currentIndex); currentIndex = ensureIndexInImage(currentIndex[0] - 1, currentIndex[1] - 1); if (pic->GetLargestPossibleRegion().IsInside(currentIndex) && (pic->GetPixel(currentIndex) == m_FillColor) == mode) workPoints.push_back(currentIndex); currentIndex = ensureIndexInImage(currentIndex[0], currentIndex[1] + 2); if (pic->GetLargestPossibleRegion().IsInside(currentIndex) && (pic->GetPixel(currentIndex) == m_FillColor) == mode) workPoints.push_back(currentIndex); } return numberOfPixel; } - -void mitk::CorrectorAlgorithm::OverwriteImage(itk::Image< mitk::CorrectorAlgorithm::DefaultSegmentationDataType, 2 >::Pointer source, itk::Image< mitk::CorrectorAlgorithm::DefaultSegmentationDataType, 2 >::Pointer target) +static void OverwriteImage(itk::Image< mitk::CorrectorAlgorithm::DefaultSegmentationDataType, 2 >::Pointer source, itk::Image< mitk::CorrectorAlgorithm::DefaultSegmentationDataType, 2 >::Pointer target) { typedef itk::Image< mitk::CorrectorAlgorithm::DefaultSegmentationDataType, 2 > ItkImageType; typedef itk::ImageRegionIterator ImageIteratorType; ImageIteratorType sourceIter(source, source->GetLargestPossibleRegion()); ImageIteratorType targetIter(target, target->GetLargestPossibleRegion()); while (!sourceIter.IsAtEnd()) { targetIter.Set(sourceIter.Get()); ++sourceIter; ++targetIter; } } bool mitk::CorrectorAlgorithm::ModifySegment(const TSegData &segment, itk::Image< DefaultSegmentationDataType, 2 >::Pointer pic) { typedef itk::Image< DefaultSegmentationDataType, 2 >::Pointer ItkImagePointerType; ItkImagePointerType firstSideImage = CloneImage(pic); ColorSegment(segment, firstSideImage); ItkImagePointerType secondSideImage = CloneImage(firstSideImage); std::vector > seedPoints = FindSeedPoints(segment, firstSideImage); if (seedPoints.size() < 1) return false; int firstSidePixel = FillRegion(seedPoints, firstSideImage); std::vector > secondSeedPoints = FindSeedPoints(segment, firstSideImage); if (secondSeedPoints.size() < 1) return false; int secondSidePixel = FillRegion(secondSeedPoints, secondSideImage); if (firstSidePixel < secondSidePixel) { OverwriteImage(firstSideImage, pic); } else { OverwriteImage(secondSideImage, pic); } return true; -} \ No newline at end of file +} diff --git a/Modules/Segmentation/Algorithms/mitkCorrectorAlgorithm.h b/Modules/Segmentation/Algorithms/mitkCorrectorAlgorithm.h index 0d08405e47..cc51ce9cea 100644 --- a/Modules/Segmentation/Algorithms/mitkCorrectorAlgorithm.h +++ b/Modules/Segmentation/Algorithms/mitkCorrectorAlgorithm.h @@ -1,110 +1,110 @@ /*=================================================================== 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 mitkCorrectorAlgorithmhIncluded #define mitkCorrectorAlgorithmhIncluded #include "mitkImageToImageFilter.h" #include #include "mitkContourModel.h" #include "ipSegmentation.h" #include #include +#define multilabelSegmentationType unsigned short namespace mitk { /** * This class encapsulates an algorithm, which takes a 2D binary image and a contour. * The algorithm tests if the line begins and ends inside or outside a segmentation * and whether areas should be added to or subtracted from the segmentation shape. * * This class has two outputs: * \li the modified input image from GetOutput() * * The output image is a combination of the original input with the generated difference image. * * \sa CorrectorTool2D */ class MITKSEGMENTATION_EXPORT CorrectorAlgorithm : public ImageToImageFilter { public: mitkClassMacro(CorrectorAlgorithm, ImageToImageFilter); itkFactorylessNewMacro(Self) itkCloneMacro(Self) typedef mitk::Label::PixelType DefaultSegmentationDataType; /** * \brief User drawn contour */ void SetContour(ContourModel* contour){ this->m_Contour = contour; } + itkSetMacro(FillColor, int); + itkGetConstMacro(FillColor, int); + + itkSetMacro(EraseColor, int); + itkGetConstMacro(EraseColor, int); /** * \brief Calculated difference image. */ //itkGetObjectMacro(DifferenceImage, Image); - itkSetMacro(FillColor, int); - itkSetMacro(EraseColor, int); - - itkGetMacro(FillColor, int); - itkGetMacro(EraseColor, int); - // used by TobiasHeimannCorrectionAlgorithm typedef struct { int lineStart; int lineEnd; bool modified; std::vector< itk::Index<2> > points; } TSegData; protected: CorrectorAlgorithm(); virtual ~CorrectorAlgorithm(); // does the actual processing virtual void GenerateData() override; bool ImprovedHeimannCorrectionAlgorithm(itk::Image< DefaultSegmentationDataType, 2 >::Pointer pic); bool ModifySegment(const TSegData &segment, itk::Image< DefaultSegmentationDataType, 2 >::Pointer pic); Image::Pointer m_WorkingImage; ContourModel::Pointer m_Contour; Image::Pointer m_DifferenceImage; int m_FillColor; int m_EraseColor; private: template itk::Index<2> ensureIndexInImage(ScalarType i0, ScalarType i1); void ColorSegment(const mitk::CorrectorAlgorithm::TSegData &segment, itk::Image< mitk::CorrectorAlgorithm::DefaultSegmentationDataType, 2 >::Pointer pic); itk::Image< mitk::CorrectorAlgorithm::DefaultSegmentationDataType, 2 >::Pointer CloneImage(itk::Image< mitk::CorrectorAlgorithm::DefaultSegmentationDataType, 2 >::Pointer pic); itk::Index<2> GetFirstPoint(const mitk::CorrectorAlgorithm::TSegData &segment, itk::Image< mitk::CorrectorAlgorithm::DefaultSegmentationDataType, 2 >::Pointer pic); std::vector > FindSeedPoints(const mitk::CorrectorAlgorithm::TSegData &segment, itk::Image< mitk::CorrectorAlgorithm::DefaultSegmentationDataType, 2 >::Pointer pic); int FillRegion(const std::vector > &seedPoints, itk::Image< mitk::CorrectorAlgorithm::DefaultSegmentationDataType, 2 >::Pointer pic); void OverwriteImage(itk::Image< mitk::CorrectorAlgorithm::DefaultSegmentationDataType, 2 >::Pointer source, itk::Image< mitk::CorrectorAlgorithm::DefaultSegmentationDataType, 2 >::Pointer target); }; } -#endif \ No newline at end of file +#endif diff --git a/Modules/Segmentation/Algorithms/mitkSurfaceStampImageFilter.cpp b/Modules/Segmentation/Algorithms/mitkSurfaceStampImageFilter.cpp new file mode 100644 index 0000000000..63028d82f8 --- /dev/null +++ b/Modules/Segmentation/Algorithms/mitkSurfaceStampImageFilter.cpp @@ -0,0 +1,299 @@ +/*=================================================================== + +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 "mitkSurfaceStampImageFilter.h" +#include "mitkTimeHelper.h" +#include "mitkImageWriteAccessor.h" +#include "mitkImageAccessByItk.h" +#include + +#include +#include +#include +#include +#include +#include + +#include + +mitk::SurfaceStampImageFilter::SurfaceStampImageFilter() : +m_MakeOutputBinary( false ), +m_OverwriteBackground( false ), +m_ForegroundValue( 1.0 ), +m_BackgroundValue( 0.0 ) +{ +} + +mitk::SurfaceStampImageFilter::~SurfaceStampImageFilter() +{ +} + +void mitk::SurfaceStampImageFilter::GenerateInputRequestedRegion() +{ + mitk::Image* outputImage = this->GetOutput(); + if((outputImage->IsInitialized()==false) ) + return; + + GenerateTimeInInputRegion(outputImage, const_cast< mitk::Image * > ( this->GetInput() )); +} + +void mitk::SurfaceStampImageFilter::GenerateOutputInformation() +{ + mitk::Image::ConstPointer inputImage = this->GetInput(); + mitk::Image::Pointer outputImage = this->GetOutput(); + + itkDebugMacro(<<"GenerateOutputInformation()"); + + if( inputImage.IsNull() || + (inputImage->IsInitialized() == false) || + (inputImage->GetTimeGeometry() == NULL)) return; + + if (m_MakeOutputBinary) + outputImage->Initialize(mitk::MakeScalarPixelType() , *inputImage->GetTimeGeometry()); + else + outputImage->Initialize(inputImage->GetPixelType(), *inputImage->GetTimeGeometry()); + + outputImage->SetPropertyList(inputImage->GetPropertyList()->Clone()); +} + +void mitk::SurfaceStampImageFilter::SetSurface(mitk::Surface *surface) +{ + m_Surface = surface; +} + +void mitk::SurfaceStampImageFilter::GenerateData() +{ + mitk::Image::ConstPointer inputImage = this->GetInput(); + + if (inputImage.IsNull()) + return; + + mitk::Image::Pointer outputImage = this->GetOutput(); + if(outputImage->IsInitialized()==false ) + return; + + if (m_Surface.IsNull()) + return; + + mitk::Image::RegionType outputRegion = outputImage->GetRequestedRegion(); + + int tstart=outputRegion.GetIndex(3); + int tmax=tstart+outputRegion.GetSize(3); + + if ( tmax > 0) + { + int t; + for(t=tstart;tSurfaceStamp( t ); + } + } + else + { + this->SurfaceStamp( 0 ); + } +} + +void mitk::SurfaceStampImageFilter::SurfaceStamp(int time) +{ + mitk::Image::Pointer inputImage = this->GetInput(); + + const mitk::TimeGeometry *surfaceTimeGeometry = GetInput()->GetTimeGeometry(); + const mitk::TimeGeometry *imageTimeGeometry = inputImage->GetTimeGeometry(); + + // Convert time step from image time-frame to surface time-frame + mitk::TimePointType matchingTimePoint = imageTimeGeometry->TimeStepToTimePoint(time); + mitk::TimeStepType surfaceTimeStep = surfaceTimeGeometry->TimePointToTimeStep(matchingTimePoint); + + vtkPolyData * polydata = m_Surface->GetVtkPolyData( surfaceTimeStep ); + if (!polydata) + mitkThrow() << "Polydata is null."; + + vtkSmartPointer transformFilter = vtkSmartPointer::New(); + transformFilter->SetInputData(polydata); +// transformFilter->ReleaseDataFlagOn(); + + vtkSmartPointer transform = vtkSmartPointer::New(); + BaseGeometry::Pointer geometry = surfaceTimeGeometry->GetGeometryForTimeStep( surfaceTimeStep ); + + transform->PostMultiply(); + transform->Concatenate(geometry->GetVtkTransform()->GetMatrix()); + // take image geometry into account. vtk-Image information will be changed to unit spacing and zero origin below. + BaseGeometry::Pointer imageGeometry = imageTimeGeometry->GetGeometryForTimeStep(time); + + transform->Concatenate(imageGeometry->GetVtkTransform()->GetLinearInverse()); + transformFilter->SetTransform(transform); + transformFilter->Update(); + + polydata = transformFilter->GetOutput(); + + if ( !polydata || !polydata->GetNumberOfPoints() ) + mitkThrow() << "Polydata retrieved from transformation is null or has no points."; + + MeshType::Pointer mesh = MeshType::New(); + mesh->SetCellsAllocationMethod( MeshType::CellsAllocatedDynamicallyCellByCell ); + unsigned int numberOfPoints = polydata->GetNumberOfPoints(); + mesh->GetPoints()->Reserve( numberOfPoints ); + + vtkPoints* points = polydata->GetPoints(); + + MeshType::PointType point; + for( int i=0; i < numberOfPoints; i++ ) + { + double* aux = points->GetPoint(i); + point[0] = aux[0]; + point[1] = aux[1]; + point[2] = aux[2]; + mesh->SetPoint( i, point ); + } + + // Load the polygons into the itk::Mesh + typedef MeshType::CellAutoPointer CellAutoPointerType; + typedef MeshType::CellType CellType; + typedef itk::TriangleCell< CellType > TriangleCellType; + typedef MeshType::PointIdentifier PointIdentifierType; + typedef MeshType::CellIdentifier CellIdentifierType; + + // Read the number of polygons + CellIdentifierType numberOfPolygons = 0; + numberOfPolygons = polydata->GetNumberOfPolys(); + vtkCellArray* polys = polydata->GetPolys(); + + PointIdentifierType numberOfCellPoints = 3; + CellIdentifierType i = 0; + + for (i=0; iGetCell(i); + cellIds = vcell->GetPointIds(); + + CellAutoPointerType cell; + TriangleCellType * triangleCell = new TriangleCellType; + PointIdentifierType k; + for( k = 0; k < numberOfCellPoints; k++ ) + { + triangleCell->SetPointId( k, cellIds->GetId(k) ); + } + + cell.TakeOwnership( triangleCell ); + mesh->SetCell( i, cell ); + } + + if ( !mesh->GetNumberOfPoints() ) + mitkThrow() << "Generated itk mesh is empty."; + + if (m_MakeOutputBinary) + { + this->SurfaceStampBinaryOutputProcessing( mesh ); + } + else + { + AccessFixedDimensionByItk_1(inputImage, SurfaceStampProcessing, 3, mesh); + } +} + +void mitk::SurfaceStampImageFilter::SurfaceStampBinaryOutputProcessing( MeshType* mesh ) +{ + mitk::Image* inputImage = const_cast< mitk::Image* >( this->GetInput() ); + + mitk::Image::Pointer outputImage = this->GetOutput(); + + typedef itk::Image< unsigned char, 3 > BinaryImageType; + BinaryImageType::Pointer itkInputImage; + mitk::CastToItkImage(inputImage, itkInputImage); + + typedef itk::TriangleMeshToBinaryImageFilter FilterType; + + FilterType::Pointer filter = FilterType::New(); + filter->SetInput(mesh); + filter->SetInfoImage(itkInputImage); + filter->SetInsideValue(1); + filter->SetOutsideValue(0); + filter->Update(); + + BinaryImageType::Pointer resultImage = filter->GetOutput(); + resultImage->DisconnectPipeline(); + + mitk::CastToMitkImage(resultImage, outputImage); +} + +template < typename TPixel > +void mitk::SurfaceStampImageFilter::SurfaceStampProcessing(itk::Image< TPixel, 3 >* input, MeshType* mesh) +{ + typedef itk::Image< TPixel, 3 > ImageType; + typedef itk::Image< unsigned char, 3 > BinaryImageType; + + typedef itk::TriangleMeshToBinaryImageFilter FilterType; + + BinaryImageType::Pointer binaryInput = BinaryImageType::New(); + binaryInput->SetSpacing( input->GetSpacing() ); + binaryInput->SetOrigin( input->GetOrigin() ); + binaryInput->SetDirection( input->GetDirection() ); + binaryInput->SetRegions( input->GetLargestPossibleRegion() ); + binaryInput->Allocate(); + binaryInput->FillBuffer(0); + + FilterType::Pointer filter = FilterType::New(); + filter->SetInput(mesh); + filter->SetInfoImage(binaryInput); + filter->SetInsideValue(1); + filter->SetOutsideValue(0); + filter->Update(); + + BinaryImageType::Pointer resultImage = filter->GetOutput(); + resultImage->DisconnectPipeline(); + + mitk::Image::Pointer outputImage = this->GetOutput(); + typename ImageType::Pointer itkOutputImage; + mitk::CastToItkImage(outputImage, itkOutputImage); + + typedef itk::ImageRegionConstIterator< BinaryImageType > BinaryIteratorType; + typedef itk::ImageRegionConstIterator< ImageType > InputIteratorType; + typedef itk::ImageRegionIterator< ImageType > OutputIteratorType; + + BinaryIteratorType sourceIter( resultImage, resultImage->GetLargestPossibleRegion() ); + sourceIter.GoToBegin(); + + InputIteratorType inputIter( input, input->GetLargestPossibleRegion() ); + inputIter.GoToBegin(); + + OutputIteratorType outputIter( itkOutputImage, itkOutputImage->GetLargestPossibleRegion() ); + outputIter.GoToBegin(); + + typename ImageType::PixelType inputValue; + unsigned char sourceValue; + + typename ImageType::PixelType fgValue = static_cast< typename ImageType::PixelType >(m_ForegroundValue); + typename ImageType::PixelType bgValue = static_cast< typename ImageType::PixelType >(m_BackgroundValue); + + while ( !sourceIter.IsAtEnd() ) + { + sourceValue = static_cast< unsigned char >(sourceIter.Get()); + inputValue = static_cast< typename ImageType::PixelType >(inputIter.Get()); + + if (sourceValue != 0) + outputIter.Set( fgValue ); + else if (m_OverwriteBackground) + outputIter.Set( bgValue ); + else + outputIter.Set( inputValue ); + + ++sourceIter; + ++inputIter; + ++outputIter; + } +} \ No newline at end of file diff --git a/Modules/Segmentation/Algorithms/mitkSurfaceStampImageFilter.h b/Modules/Segmentation/Algorithms/mitkSurfaceStampImageFilter.h new file mode 100644 index 0000000000..c0ed7d6f46 --- /dev/null +++ b/Modules/Segmentation/Algorithms/mitkSurfaceStampImageFilter.h @@ -0,0 +1,104 @@ +/*=================================================================== + +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 _mitkSurfaceStampImageFilter_h__ +#define _mitkSurfaceStampImageFilter_h__ + +#include "mitkCommon.h" +#include "MitkSegmentationExports.h" +#include "mitkImageToImageFilter.h" +#include "mitkSurface.h" + +#include + +class vtkPolyData; + +namespace mitk { +/** + * + * @brief Converts surface data to pixel data. Requires a surface and an + * image, which header information defines the output image. + * + * The resulting image has the same dimension, size, and Geometry3D + * as the input image. The image is cut using a vtkStencil. + * The user can decide if he wants to keep the original values or create a + * binary image by setting MakeBinaryOutputOn (default is \a false). If + * set to \a true all voxels inside the surface are set to one and all + * outside voxel are set to zero. + * + * NOTE: Since the reference input image is passed to the vtkStencil in + * any case, the image needs to be initialized with pixel values greater than + * the numerical minimum of the used pixel type (e.g. at least -127 for + * unsigned char images, etc.) to produce a correct binary image + * representation of the surface in MakeOutputBinary mode. + * + * @ingroup SurfaceFilters + * @ingroup Process + */ +class MITKSEGMENTATION_EXPORT SurfaceStampImageFilter : public ImageToImageFilter +{ +public: + mitkClassMacro(SurfaceStampImageFilter, ImageToImageFilter); + itkNewMacro(Self); + + itkSetMacro(MakeOutputBinary, bool); + itkGetMacro(MakeOutputBinary, bool); + itkBooleanMacro(MakeOutputBinary); + + itkSetMacro(OverwriteBackground, bool); + itkGetMacro(OverwriteBackground, bool); + itkBooleanMacro(OverwriteBackground); + + itkGetConstMacro(BackgroundValue, float); + itkSetMacro(BackgroundValue, float); + + itkGetConstMacro(ForegroundValue, float); + itkSetMacro(ForegroundValue, float); + + virtual void GenerateInputRequestedRegion(); + + virtual void GenerateOutputInformation(); + + virtual void GenerateData(); + + void SetSurface(mitk::Surface *surface); + + typedef itk::QuadEdgeMesh< double, 3 > MeshType; + +protected: + + SurfaceStampImageFilter(); + + virtual ~SurfaceStampImageFilter(); + + void SurfaceStamp(int time = 0); + + + template < typename TPixel > + void SurfaceStampProcessing( itk::Image< TPixel, 3 >* input, MeshType* mesh); + + void SurfaceStampBinaryOutputProcessing( MeshType* mesh ); + + bool m_MakeOutputBinary; + bool m_OverwriteBackground; + float m_BackgroundValue; + float m_ForegroundValue; + + mitk::Surface::Pointer m_Surface; +}; +} // namespace mitk + +#endif /* MITKCOONSPATCHFILTER_H_HEADER_INCLUDED_C10B22CD */ diff --git a/Modules/Segmentation/Controllers/mitkSliceBasedInterpolationController.cpp b/Modules/Segmentation/Controllers/mitkSliceBasedInterpolationController.cpp new file mode 100644 index 0000000000..7a96345395 --- /dev/null +++ b/Modules/Segmentation/Controllers/mitkSliceBasedInterpolationController.cpp @@ -0,0 +1,420 @@ +/*=================================================================== + +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 "mitkSliceBasedInterpolationController.h" + +#include "mitkImageCast.h" +#include "mitkImageAccessByItk.h" +#include "mitkImageTimeSelector.h" +#include "mitkExtractSliceFilter.h" +#include "mitkImageReadAccessor.h" + +#include "mitkShapeBasedInterpolationAlgorithm.h" + +#include +#include +#include + +mitk::SliceBasedInterpolationController::InterpolatorMapType mitk::SliceBasedInterpolationController::s_InterpolatorForImage; // static member initialization + +mitk::SliceBasedInterpolationController* mitk::SliceBasedInterpolationController::InterpolatorForImage(const Image* image) +{ + InterpolatorMapType::iterator iter = s_InterpolatorForImage.find( image ); + if ( iter != s_InterpolatorForImage.end() ) + { + return iter->second; + } + else + { + return NULL; + } +} + +mitk::SliceBasedInterpolationController::SliceBasedInterpolationController() : +m_WorkingImage(NULL), +m_ReferenceImage(NULL) +{ +} + +mitk::SliceBasedInterpolationController::~SliceBasedInterpolationController() +{ + // remove this from the list of interpolators + for ( InterpolatorMapType::iterator iter = s_InterpolatorForImage.begin(); + iter != s_InterpolatorForImage.end(); + ++iter ) + { + if (iter->second == this) + { + s_InterpolatorForImage.erase( iter ); + break; + } + } +} + +void mitk::SliceBasedInterpolationController::ResetLabelCount() +{ + m_LabelCountInSlice.clear(); + int numberOfLabels = m_WorkingImage->GetNumberOfLabels(); + m_LabelCountInSlice.resize( m_WorkingImage->GetTimeSteps() ); + + for (unsigned int timeStep = 0; timeStep < m_WorkingImage->GetTimeSteps(); ++timeStep) + { + m_LabelCountInSlice[timeStep].resize(3); + for (unsigned int dim = 0; dim < 3; ++dim) + { + m_LabelCountInSlice[timeStep][dim].clear(); + m_LabelCountInSlice[timeStep][dim].resize( m_WorkingImage->GetDimension(dim) ); + for (unsigned int slice = 0; slice < m_WorkingImage->GetDimension(dim); ++slice) + { + m_LabelCountInSlice[timeStep][dim][slice].clear(); + m_LabelCountInSlice[timeStep][dim][slice].resize( numberOfLabels ); + m_LabelCountInSlice[timeStep][dim][slice].assign( numberOfLabels, 0 ); + } + } + } +} + +void mitk::SliceBasedInterpolationController::SetWorkingImage( LabelSetImage* newImage ) +{ + if (m_WorkingImage != newImage) + { + // delete the current working image from the list of interpolators + InterpolatorMapType::iterator iter = s_InterpolatorForImage.find( m_WorkingImage); + if ( iter != s_InterpolatorForImage.end() ) + { + s_InterpolatorForImage.erase( iter ); + } + + m_WorkingImage = newImage; + + s_InterpolatorForImage.insert( std::make_pair( m_WorkingImage, this ) ); + } + + this->ResetLabelCount(); + + AccessFixedDimensionByItk_1( m_WorkingImage, ScanImageITKProcessing, 3, 0 ); + + // for all timesteps, scan whole image: TODO: enable this again for 3D+time +/* + for (unsigned int timeStep = 0; timeStep < m_WorkingImage->GetTimeSteps(); ++timeStep) + { + ImageTimeSelector::Pointer timeSelector = ImageTimeSelector::New(); + timeSelector->SetInput( m_WorkingImage ); + timeSelector->SetTimeNr( timeStep ); + timeSelector->UpdateLargestPossibleRegion(); + Image::Pointer segmentation3D = timeSelector->GetOutput(); + this->SetChangedVolume( segmentation3D.GetPointer(), timeStep ); + } +*/ +// this->Modified(); +} + +void mitk::SliceBasedInterpolationController::SetReferenceImage( Image* newImage ) +{ + if (!newImage) return; + + m_ReferenceImage = newImage; + + // ensure the reference image has the same dimensionality and extents as the segmentation image + if ( m_WorkingImage.IsNull() + || m_ReferenceImage->GetDimension() != m_WorkingImage->GetDimension() + || m_ReferenceImage->GetPixelType().GetNumberOfComponents() != 1 + || m_WorkingImage->GetPixelType().GetNumberOfComponents() != 1 + ) + { + MITK_WARN << "Segmentation image has different image characteristics than reference image." << std::endl; + m_ReferenceImage = NULL; + return; + } + + for (unsigned int dim = 0; dim < m_WorkingImage->GetDimension(); ++dim) + { + if ( m_ReferenceImage->GetDimension(dim) != m_WorkingImage->GetDimension(dim) ) + { + MITK_WARN << "original patient image does not match segmentation (different extent in dimension " << dim + << "), ignoring patient image" << std::endl; + m_ReferenceImage = NULL; + return; + } + } +} + +void mitk::SliceBasedInterpolationController::SetChangedSlice( const Image* slice, unsigned int sliceDimension, unsigned int sliceIndex, unsigned int timeStep ) +{ + if ( !slice ) return; + if ( slice->GetDimension() != 2 ) return; + if ( sliceDimension > 2 ) return; + if ( m_WorkingImage.IsNull() ) return; + + // check if the number of labels has changed + int numberOfLabels = m_WorkingImage->GetNumberOfLabels(); + if (m_LabelCountInSlice[0][0][0].size() != numberOfLabels) + return; + + unsigned int dim0(0); + unsigned int dim1(1); + + // determine the other two dimensions + switch (sliceDimension) + { + default: + case 2: + dim0 = 0; dim1 = 1; break; + case 1: + dim0 = 0; dim1 = 2; break; + case 0: + dim0 = 1; dim1 = 2; break; + } + + AccessFixedDimensionByItk_1( slice, ScanSliceITKProcessing, 2, SetChangedSliceOptions(sliceDimension, sliceIndex, dim0, dim1, timeStep) ); + +// this->Modified(); +} + +template < typename PixelType > +void mitk::SliceBasedInterpolationController::ScanSliceITKProcessing( const itk::Image* input, const SetChangedSliceOptions& options ) +{ + unsigned int timeStep = options.timeStep; + unsigned int sliceDimension = options.sliceDimension; + unsigned int sliceIndex = options.sliceIndex; + + if ( sliceDimension > 2 ) return; + if ( sliceIndex >= m_LabelCountInSlice[timeStep][sliceDimension].size() ) return; + + unsigned int dim0(options.dim0); + unsigned int dim1(options.dim1); + + std::vector numberOfPixels; // number of pixels in the current slice that are equal to the active label + int numberOfLabels = m_WorkingImage->GetNumberOfLabels(); + numberOfPixels.resize( numberOfLabels ); + + typedef itk::Image ImageType; + typedef itk::ImageRegionConstIteratorWithIndex< ImageType > IteratorType; + + IteratorType iter( input, input->GetLargestPossibleRegion() ); + iter.GoToBegin(); + + typename IteratorType::IndexType index; + + while ( !iter.IsAtEnd() ) + { + index = iter.GetIndex(); + int value = static_cast< int >(iter.Get()); + ++ m_LabelCountInSlice[timeStep][dim0][index[0]][value]; + ++ m_LabelCountInSlice[timeStep][dim1][index[1]][value]; + ++ numberOfPixels[value]; + ++iter; + } + + for (unsigned int label=0; label +void mitk::SliceBasedInterpolationController::ScanImageITKProcessing( itk::Image* input, unsigned int timeStep ) +{ + typedef itk::ImageSliceConstIteratorWithIndex< itk::Image > IteratorType; + + IteratorType iter( input, input->GetLargestPossibleRegion() ); + iter.SetFirstDirection(0); + iter.SetSecondDirection(1); + + typename IteratorType::IndexType index; + unsigned int x = 0; + unsigned int y = 0; + unsigned int z = 0; + + std::vector numberOfPixels; // number of pixels per slice that are equal to the active label + int numberOfLabels = m_WorkingImage->GetNumberOfLabels(); + numberOfPixels.resize( numberOfLabels ); + + iter.GoToBegin(); + while ( !iter.IsAtEnd() ) + { + while ( !iter.IsAtEndOfSlice() ) + { + while ( !iter.IsAtEndOfLine() ) + { + index = iter.GetIndex(); + x = index[0]; + y = index[1]; + z = index[2]; + + int value = static_cast( iter.Get() ); + ++m_LabelCountInSlice[timeStep][0][x][value]; + ++m_LabelCountInSlice[timeStep][1][y][value]; + ++numberOfPixels[value]; + + ++iter; + } + iter.NextLine(); + } + + for (unsigned int label = 0; label < numberOfLabels; ++label) + m_LabelCountInSlice[timeStep][2][z][label] += numberOfPixels[label]; + + // clear label counter + numberOfPixels.assign(numberOfLabels, 0); + iter.NextSlice(); + } +} + +mitk::Image::Pointer mitk::SliceBasedInterpolationController::Interpolate(unsigned int sliceDimension, + unsigned int sliceIndex, + const mitk::PlaneGeometry* currentPlane, + unsigned int timeStep ) +{ + if (m_WorkingImage.IsNull()) return NULL; + if (!currentPlane) return NULL; + if ( timeStep >= m_LabelCountInSlice.size() ) return NULL; + if ( sliceDimension > 2 ) return NULL; + int upperLimit = m_LabelCountInSlice[timeStep][sliceDimension].size(); + if ( sliceIndex >= upperLimit - 1 ) return NULL; // can't interpolate first and last slice + if ( sliceIndex < 1 ) return NULL; + + int pixelValue = m_WorkingImage->GetActiveLabel()->GetValue(); + + // slice contains a segmentation, won't interpolate anything then + if ( m_LabelCountInSlice[timeStep][sliceDimension][sliceIndex][pixelValue] > 0 ) return NULL; + + int lowerBound(0); + int upperBound(0); + bool bounds(false); + + for (lowerBound = sliceIndex - 1; /*lowerBound >= 0*/; --lowerBound) + { + if ( m_LabelCountInSlice[timeStep][sliceDimension][lowerBound][pixelValue] > 0 ) + { + bounds = true; + break; + } + + if (lowerBound == 0) break; + } + + if (!bounds) return NULL; + + bounds = false; + for (upperBound = sliceIndex + 1 ; upperBound < upperLimit; ++upperBound) + { + if ( m_LabelCountInSlice[timeStep][sliceDimension][upperBound][pixelValue] > 0 ) + { + bounds = true; + break; + } + } + + if (!bounds) return NULL; + + // ok, we have found two neighboring slices with the active label + // (and we made sure that the current slice does NOT contain the active label + //Setting up the ExtractSliceFilter + mitk::ExtractSliceFilter::Pointer extractor = ExtractSliceFilter::New(); + extractor->SetInput(m_WorkingImage); + extractor->SetTimeStep(timeStep); + extractor->SetResliceTransformByGeometry( m_WorkingImage->GetTimeGeometry()->GetGeometryForTimeStep( timeStep ) ); + extractor->SetVtkOutputRequest(false); + + //Reslicing the current plane + extractor->SetWorldGeometry(currentPlane); + extractor->Modified(); + + try + { + extractor->Update(); + } + catch(const std::exception &e) + { + MITK_ERROR<<"Error in 2D interpolation: " << e.what(); + return NULL; + } + + mitk::Image::Pointer resultImage = extractor->GetOutput(); + resultImage->DisconnectPipeline(); + + //Creating PlaneGeometry for lower slice + mitk::PlaneGeometry::Pointer reslicePlane = currentPlane->Clone(); + + //Transforming the current origin so that it matches the lower slice + mitk::Point3D origin = currentPlane->GetOrigin(); + m_WorkingImage->GetSlicedGeometry()->WorldToIndex(origin, origin); + origin[sliceDimension] = lowerBound; + m_WorkingImage->GetSlicedGeometry()->IndexToWorld(origin, origin); + reslicePlane->SetOrigin(origin); + + //Extract the lower slice + extractor->SetWorldGeometry(reslicePlane); + extractor->Modified(); + + try + { + extractor->Update(); + } + catch(const std::exception &e) + { + MITK_ERROR<<"Error in 2D interpolation: " << e.what(); + return NULL; + } + + mitk::Image::Pointer lowerMITKSlice = extractor->GetOutput(); + lowerMITKSlice->DisconnectPipeline(); + + //Transforming the current origin so that it matches the upper slice + m_WorkingImage->GetSlicedGeometry()->WorldToIndex(origin, origin); + origin[sliceDimension] = upperBound; + m_WorkingImage->GetSlicedGeometry()->IndexToWorld(origin, origin); + reslicePlane->SetOrigin(origin); + + //Extract the upper slice + extractor->SetWorldGeometry(reslicePlane); + extractor->Modified(); + + try + { + extractor->Update(); + } + catch(const std::exception &e) + { + MITK_ERROR<<"Error in 2D interpolation: " << e.what(); + return NULL; + } + + mitk::Image::Pointer upperMITKSlice = extractor->GetOutput(); + upperMITKSlice->DisconnectPipeline(); + + if ( lowerMITKSlice.IsNull() || upperMITKSlice.IsNull() ) return NULL; + + // interpolation algorithm gets some inputs + // two segmentations (guaranteed to be of the same data type, but no special data type guaranteed) + // orientation (sliceDimension) of the segmentations + // position of the two slices (sliceIndices) + // one volume image (original patient image) + // + // interpolation algorithm can use e.g. itk::ImageSliceConstIteratorWithIndex to + // inspect the original patient image at appropriate positions + + mitk::SegmentationInterpolationAlgorithm::Pointer algorithm = mitk::ShapeBasedInterpolationAlgorithm::New().GetPointer(); + + algorithm->Interpolate( lowerMITKSlice.GetPointer(), lowerBound, + upperMITKSlice.GetPointer(), upperBound, + sliceIndex, + 0, + resultImage); + + return resultImage; +} \ No newline at end of file diff --git a/Modules/Segmentation/Controllers/mitkSliceBasedInterpolationController.h b/Modules/Segmentation/Controllers/mitkSliceBasedInterpolationController.h new file mode 100644 index 0000000000..7c61820512 --- /dev/null +++ b/Modules/Segmentation/Controllers/mitkSliceBasedInterpolationController.h @@ -0,0 +1,193 @@ +/*=================================================================== + +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 mitkSliceBasedInterpolationController_h_Included +#define mitkSliceBasedInterpolationController_h_Included + +#include +#include "mitkLabelSetImage.h" + +#include +#include + +#include +#include + +namespace mitk +{ +class Image; + +/** + \brief Generates interpolations of 2D slices. + + \sa QmitkSlicesInterpolator + \sa QmitkInteractiveSegmentation + + \ingroup ToolManagerEtAl + + There is a separate page describing the general design of QmitkInteractiveSegmentation: \ref QmitkInteractiveSegmentationTechnicalPage + + This class keeps track of the contents of a 3D segmentation image. + \attention mitk::SliceBasedInterpolationController assumes that the image contains pixel values of 0 and 1. + + After you set the segmentation image using SetSegmentationVolume(), the whole image is scanned for pixels other than 0. + SliceBasedInterpolationController registers as an observer to the segmentation image, and repeats the scan whenvever the + image is modified. + + You can prevent this (time consuming) scan if you do the changes slice-wise and send difference images to SliceBasedInterpolationController. + For this purpose SetChangedSlice() should be used. mitk::OverwriteImageFilter already does this every time it changes a + slice of an image. There is a static method InterpolatorForImage(), which can be used to find out if there already is an interpolator + instance for a specified image. OverwriteImageFilter uses this to get to know its interpolator. + + SliceBasedInterpolationController needs to maintain some information about the image slices (in every dimension). + This information is stored internally in m_SegmentationCountInSlice, which is basically three std::vectors (one for each dimension). + Each item describes one image dimension, each vector item holds the count of pixels in "its" slice. This is perhaps better to understand + from the following picture (where red items just mean to symbolize "there is some segmentation" - in reality there is an integer count). + + \image html slice_based_segmentation_interpolator.png + + $Author$ +*/ +class MITKSEGMENTATION_EXPORT SliceBasedInterpolationController : public itk::Object +{ + public: + + mitkClassMacroItkParent(SliceBasedInterpolationController, itk::Object) + itkFactorylessNewMacro(Self) + itkCloneMacro(Self) + + /** + \brief Find interpolator for a given image. + \return NULL if there is no interpolator yet. + + This method is useful if several "clients" modify the same image and want to access the interpolations. + Then they can share the same object. + */ + static SliceBasedInterpolationController* InterpolatorForImage(const Image*); + + /** + \brief Initialize with a whole volume. + + Will scan the volume for segmentation pixels (values other than 0) and fill some internal data structures. + You don't have to call this method every time something changes, but only + when several slices at once change. + + When you change a single slice, call SetChangedSlice() instead. + */ + void SetWorkingImage( LabelSetImage* image ); + + /** + \brief Set a reference image (original patient image) - optional. + + If this image is set (must exactly match the dimensions of the segmentation), + the interpolation algorithm may consider image content to improve the interpolated + (estimated) segmentation. + */ + void SetReferenceImage( Image* image ); + + /** + \brief Update after changing a single slice in the working image. + + \param image is a 2D image with the difference image of the slice determined by sliceDimension and sliceIndex. + The difference is (pixel value in the new slice minus pixel value in the old slice). + + \param sliceDimension Number of the dimension which is constant for all pixels of the meant slice. + + \param sliceIndex Which slice to take, in the direction specified by sliceDimension. Count starts from 0. + + \param timeStep Which time step is changed + */ + void SetChangedSlice( const Image* image, unsigned int sliceDimension, unsigned int sliceIndex, unsigned int timeStep ); + + /** + \brief Update after changing the whole working image. + + \param image is a 3D image with the difference image of the slice determined by sliceDimension and sliceIndex. + The difference is (pixel value in the new slice minus pixel value in the old slice). + + \param timeStep Which time step is changed + */ +// void SetChangedImage( const Image* image, unsigned int timeStep ); + + /** + \brief Generates an interpolated image for the given slice. + + \param sliceDimension Number of the dimension which is constant for all pixels of the meant slice. + + \param sliceIndex Which slice to take, in the direction specified by sliceDimension. Count starts from 0. + + \param timeStep Which time step to use + */ + Image::Pointer Interpolate( unsigned int sliceDimension, unsigned int sliceIndex, const mitk::PlaneGeometry* currentPlane, unsigned int timeStep ); + + /** + \brief Initializes the internal container with the number of voxels per label. + */ + void ResetLabelCount(); + + protected: + + /** + \brief Protected class of mitk::SliceBasedInterpolationController. Don't use (you shouldn't be able to do so)! + */ + class MITKSEGMENTATION_EXPORT SetChangedSliceOptions + { + public: + SetChangedSliceOptions( unsigned int sd, unsigned int si, unsigned int d0, unsigned int d1, unsigned int t) + : sliceDimension(sd), sliceIndex(si), dim0(d0), dim1(d1), timeStep(t) + { + } + + unsigned int sliceDimension; + unsigned int sliceIndex; + unsigned int dim0; + unsigned int dim1; + unsigned int timeStep; +// void* pixelData; + }; + + typedef std::vector LabelCounterVectorType; + typedef std::vector< LabelCounterVectorType > LabelCounterSliceVectorType; + typedef std::vector< std::vector< LabelCounterSliceVectorType > > LabelCounterSliceTimeVectorType; + typedef std::map< const Image*, SliceBasedInterpolationController* > InterpolatorMapType; + + SliceBasedInterpolationController();// purposely hidden + virtual ~SliceBasedInterpolationController(); + + /// internal scan of a single slice + template < typename PixelType > + void ScanSliceITKProcessing( const itk::Image*, const SetChangedSliceOptions& options ); + + /// internal scan of the whole image + template < typename TPixel, unsigned int VImageDimension > + void ScanImageITKProcessing( itk::Image*, unsigned int timeStep ); + + /** + An array that of flags. One for each dimension of the image. A flag is set, when a slice in a certain dimension + has at least one pixel that is not 0 (which would mean that it has to be considered by the interpolation algorithm). + E.g. flags for axial slices are stored in m_SegmentationCountInSlice[0][index]. + Enhanced with time steps it is now m_SegmentationCountInSlice[timeStep][0][index] + */ + LabelCounterSliceTimeVectorType m_LabelCountInSlice; + + static InterpolatorMapType s_InterpolatorForImage; + + LabelSetImage::Pointer m_WorkingImage; + Image::Pointer m_ReferenceImage; +}; +} // namespace + +#endif diff --git a/Modules/Segmentation/Interactions/mitkContourTool.cpp b/Modules/Segmentation/Interactions/mitkContourTool.cpp index da1f7060bb..41e7c7e7d7 100644 --- a/Modules/Segmentation/Interactions/mitkContourTool.cpp +++ b/Modules/Segmentation/Interactions/mitkContourTool.cpp @@ -1,173 +1,189 @@ /*=================================================================== 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 "mitkContourTool.h" #include "mitkToolManager.h" #include "mitkOverwriteSliceImageFilter.h" #include "mitkOverwriteDirectedPlaneImageFilter.h" #include "mitkAbstractTransformGeometry.h" #include "mitkBaseRenderer.h" #include "mitkRenderingManager.h" //#include "mitkProperties.h" //#include "mitkPlanarCircle.h" +#include "mitkLabelSetImage.h" #include "mitkStateMachineAction.h" #include "mitkInteractionEvent.h" mitk::ContourTool::ContourTool(int paintingPixelValue) :FeedbackContourTool("PressMoveReleaseWithCTRLInversion"), - m_PaintingPixelValue(paintingPixelValue) + m_PaintingPixelValue(paintingPixelValue), + m_CurrentLabelID(1) { } mitk::ContourTool::~ContourTool() { } void mitk::ContourTool::ConnectActionsAndFunctions() { CONNECT_FUNCTION( "PrimaryButtonPressed", OnMousePressed); CONNECT_FUNCTION( "Move", OnMouseMoved); CONNECT_FUNCTION( "Release", OnMouseReleased); CONNECT_FUNCTION( "InvertLogic", OnInvertLogic); } void mitk::ContourTool::Activated() { Superclass::Activated(); } void mitk::ContourTool::Deactivated() { Superclass::Deactivated(); } /** Just show the contour, insert the first point. */ void mitk::ContourTool::OnMousePressed( StateMachineAction*, InteractionEvent* interactionEvent ) { mitk::InteractionPositionEvent* positionEvent = dynamic_cast( interactionEvent ); if (!positionEvent) return; m_LastEventSender = positionEvent->GetSender(); m_LastEventSlice = m_LastEventSender->GetSlice(); int timestep = positionEvent->GetSender()->GetTimeStep(); ContourModel* contour = FeedbackContourTool::GetFeedbackContour(); //Clear feedback contour contour->Initialize(); //expand time bounds because our contour was initialized contour->Expand( timestep + 1 ); //draw as a closed contour contour->SetClosed(true,timestep); //add first mouse position mitk::Point3D point = positionEvent->GetPositionInWorld(); contour->AddVertex( point, timestep ); FeedbackContourTool::SetFeedbackContourVisible(true); assert( positionEvent->GetSender()->GetRenderWindow() ); mitk::RenderingManager::GetInstance()->RequestUpdate( positionEvent->GetSender()->GetRenderWindow() ); } /** Insert the point to the feedback contour. */ void mitk::ContourTool::OnMouseMoved( StateMachineAction*, InteractionEvent* interactionEvent ) { mitk::InteractionPositionEvent* positionEvent = dynamic_cast( interactionEvent ); if (!positionEvent) return; int timestep = positionEvent->GetSender()->GetTimeStep(); ContourModel* contour = FeedbackContourTool::GetFeedbackContour(); mitk::Point3D point = positionEvent->GetPositionInWorld(); contour->AddVertex( point, timestep ); assert( positionEvent->GetSender()->GetRenderWindow() ); mitk::RenderingManager::GetInstance()->RequestUpdate( positionEvent->GetSender()->GetRenderWindow() ); } /** Close the contour, project it to the image slice and fill it in 2D. */ void mitk::ContourTool::OnMouseReleased( StateMachineAction*, InteractionEvent* interactionEvent ) { // 1. Hide the feedback contour, find out which slice the user clicked, find out which slice of the toolmanager's working image corresponds to that FeedbackContourTool::SetFeedbackContourVisible(false); mitk::InteractionPositionEvent* positionEvent = dynamic_cast( interactionEvent ); //const PositionEvent* positionEvent = dynamic_cast(stateEvent->GetEvent()); if (!positionEvent) return; assert( positionEvent->GetSender()->GetRenderWindow() ); mitk::RenderingManager::GetInstance()->RequestUpdate( positionEvent->GetSender()->GetRenderWindow() ); DataNode* workingNode( m_ToolManager->GetWorkingData(0) ); if (!workingNode) return; Image* image = dynamic_cast(workingNode->GetData()); const PlaneGeometry* planeGeometry( (positionEvent->GetSender()->GetCurrentWorldPlaneGeometry() ) ); if ( !image || !planeGeometry ) return; + // Check if it is a multilabel-image + // If yes, get the new drawing color from it. + // Otherwise nothing happens. + mitk::LabelSetImage* labelSetImage = dynamic_cast(image); + if (labelSetImage) + { + mitk::Label* label = labelSetImage->GetActiveLabel(labelSetImage->GetActiveLayer()); + m_CurrentLabelID = label->GetValue(); + } else + { + m_CurrentLabelID = 1; + } + const AbstractTransformGeometry* abstractTransformGeometry( dynamic_cast (positionEvent->GetSender()->GetCurrentWorldPlaneGeometry() ) ); if ( !image || abstractTransformGeometry ) return; // 2. Slice is known, now we try to get it as a 2D image and project the contour into index coordinates of this slice Image::Pointer slice = SegTool2D::GetAffectedImageSliceAs2DImage( positionEvent, image ); if ( slice.IsNull() ) { MITK_ERROR << "Unable to extract slice." << std::endl; return; } ContourModel* feedbackContour = FeedbackContourTool::GetFeedbackContour(); ContourModel::Pointer projectedContour = FeedbackContourTool::ProjectContourTo2DSlice( slice, feedbackContour, true, false ); // true: actually no idea why this is neccessary, but it works :-( if (projectedContour.IsNull()) return; int timestep = positionEvent->GetSender()->GetTimeStep(); - FeedbackContourTool::FillContourInSlice( projectedContour, timestep, slice, m_PaintingPixelValue ); + mitk::ContourModelUtils::FillContourInSlice(projectedContour, timestep, slice, image, (m_PaintingPixelValue*m_CurrentLabelID)); - this->WriteBackSegmentationResult(positionEvent, slice); + //this->WriteBackSegmentationResult(positionEvent, slice); + SegTool2D::WriteBackSegmentationResult(positionEvent,slice); // 4. Make sure the result is drawn again --> is visible then. assert( positionEvent->GetSender()->GetRenderWindow() ); } /** Called when the CTRL key is pressed. Will change the painting pixel value from 0 to 1 or from 1 to 0. */ void mitk::ContourTool::OnInvertLogic( StateMachineAction*, InteractionEvent* interactionEvent ) { // Inversion only for 0 and 1 as painting values if (m_PaintingPixelValue == 1) { m_PaintingPixelValue = 0; FeedbackContourTool::SetFeedbackContourColor( 1.0, 0.0, 0.0 ); } else if (m_PaintingPixelValue == 0) { m_PaintingPixelValue = 1; FeedbackContourTool::SetFeedbackContourColorDefault(); } } diff --git a/Modules/Segmentation/Interactions/mitkContourTool.h b/Modules/Segmentation/Interactions/mitkContourTool.h index 6af8e5526b..546e84d2fe 100644 --- a/Modules/Segmentation/Interactions/mitkContourTool.h +++ b/Modules/Segmentation/Interactions/mitkContourTool.h @@ -1,80 +1,81 @@ /*=================================================================== 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 mitkContourTool_h_Included #define mitkContourTool_h_Included #include "mitkCommon.h" #include #include "mitkFeedbackContourTool.h" namespace mitk { class Image; class StateMachineAction; class InteractionEvent; /** \brief Simple contour filling tool. \sa FeedbackContourTool \sa ExtractImageFilter \sa OverwriteSliceImageFilter \ingroup Interaction \ingroup ToolManagerEtAl Fills a visible contour (from FeedbackContourTool) during mouse dragging. When the mouse button is released, ContourTool tries to extract a slice from the working image and fill in the (filled) contour as a binary image. The painting "color" is defined by m_PaintingPixelValue, which is set in the constructor (by sub-classes) or during some event (e.g. in OnInvertLogic - when CTRL is pressed). \warning Only to be instantiated by mitk::ToolManager. $Author$ */ class MITKSEGMENTATION_EXPORT ContourTool : public FeedbackContourTool { public: mitkClassMacro(ContourTool, FeedbackContourTool); protected: ContourTool(int paintingPixelValue = 1); // purposely hidden virtual ~ContourTool(); virtual void Activated() override; virtual void Deactivated() override; virtual void OnMousePressed( StateMachineAction*, InteractionEvent* interactionEvent ); virtual void OnMouseMoved( StateMachineAction*, InteractionEvent* interactionEvent ); virtual void OnMouseReleased( StateMachineAction*, InteractionEvent* interactionEvent ); virtual void OnInvertLogic( StateMachineAction*, InteractionEvent* interactionEvent ); void ConnectActionsAndFunctions() override; int m_PaintingPixelValue; + int m_CurrentLabelID; }; } // namespace #endif diff --git a/Modules/Segmentation/Interactions/mitkCorrectorTool2D.cpp b/Modules/Segmentation/Interactions/mitkCorrectorTool2D.cpp index 38911535ba..39e27b2bad 100644 --- a/Modules/Segmentation/Interactions/mitkCorrectorTool2D.cpp +++ b/Modules/Segmentation/Interactions/mitkCorrectorTool2D.cpp @@ -1,191 +1,195 @@ /*=================================================================== 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 "mitkCorrectorTool2D.h" #include "mitkCorrectorAlgorithm.h" #include "mitkToolManager.h" #include "mitkBaseRenderer.h" #include "mitkRenderingManager.h" #include "mitkImageReadAccessor.h" #include "mitkAbstractTransformGeometry.h" #include "mitkLabelSetImage.h" #include "mitkCorrectorTool2D.xpm" +#include "mitkLabelSetImage.h" // us #include #include #include #include namespace mitk { MITK_TOOL_MACRO(MITKSEGMENTATION_EXPORT, CorrectorTool2D, "Correction tool"); } mitk::CorrectorTool2D::CorrectorTool2D(int paintingPixelValue) :FeedbackContourTool("PressMoveRelease"), m_PaintingPixelValue(paintingPixelValue) { GetFeedbackContour()->SetClosed( false ); // don't close the contour to a polygon } mitk::CorrectorTool2D::~CorrectorTool2D() { } void mitk::CorrectorTool2D::ConnectActionsAndFunctions() { CONNECT_FUNCTION( "PrimaryButtonPressed", OnMousePressed); CONNECT_FUNCTION( "Move", OnMouseMoved); CONNECT_FUNCTION( "Release", OnMouseReleased); } const char** mitk::CorrectorTool2D::GetXPM() const { return mitkCorrectorTool2D_xpm; } us::ModuleResource mitk::CorrectorTool2D::GetIconResource() const { us::Module* module = us::GetModuleContext()->GetModule(); us::ModuleResource resource = module->GetResource("Correction_48x48.png"); return resource; } us::ModuleResource mitk::CorrectorTool2D::GetCursorIconResource() const { us::Module* module = us::GetModuleContext()->GetModule(); us::ModuleResource resource = module->GetResource("Correction_Cursor_32x32.png"); return resource; } const char* mitk::CorrectorTool2D::GetName() const { return "Correction"; } void mitk::CorrectorTool2D::Activated() { Superclass::Activated(); } void mitk::CorrectorTool2D::Deactivated() { Superclass::Deactivated(); } void mitk::CorrectorTool2D::OnMousePressed ( StateMachineAction*, InteractionEvent* interactionEvent ) { mitk::InteractionPositionEvent* positionEvent = dynamic_cast( interactionEvent ); if (!positionEvent) return; m_LastEventSender = positionEvent->GetSender(); m_LastEventSlice = m_LastEventSender->GetSlice(); int timestep = positionEvent->GetSender()->GetTimeStep(); ContourModel* contour = FeedbackContourTool::GetFeedbackContour(); contour->Initialize(); contour->Expand(timestep + 1); contour->SetClosed(false, timestep); mitk::Point3D point = positionEvent->GetPositionInWorld(); contour->AddVertex( point, timestep ); FeedbackContourTool::SetFeedbackContourVisible(true); } void mitk::CorrectorTool2D::OnMouseMoved( StateMachineAction*, InteractionEvent* interactionEvent ) { mitk::InteractionPositionEvent* positionEvent = dynamic_cast( interactionEvent ); if (!positionEvent) return; int timestep = positionEvent->GetSender()->GetTimeStep(); ContourModel* contour = FeedbackContourTool::GetFeedbackContour(); mitk::Point3D point = positionEvent->GetPositionInWorld(); contour->AddVertex( point, timestep ); assert( positionEvent->GetSender()->GetRenderWindow() ); mitk::RenderingManager::GetInstance()->RequestUpdate( positionEvent->GetSender()->GetRenderWindow() ); } void mitk::CorrectorTool2D::OnMouseReleased( StateMachineAction*, InteractionEvent* interactionEvent ) { // 1. Hide the feedback contour, find out which slice the user clicked, find out which slice of the toolmanager's working image corresponds to that FeedbackContourTool::SetFeedbackContourVisible(false); mitk::InteractionPositionEvent* positionEvent = dynamic_cast( interactionEvent ); //const PositionEvent* positionEvent = dynamic_cast(stateEvent->GetEvent()); if (!positionEvent) return; assert( positionEvent->GetSender()->GetRenderWindow() ); mitk::RenderingManager::GetInstance()->RequestUpdate( positionEvent->GetSender()->GetRenderWindow() ); DataNode* workingNode( m_ToolManager->GetWorkingData(0) ); if (!workingNode) return; Image* image = dynamic_cast(workingNode->GetData()); const PlaneGeometry* planeGeometry( (positionEvent->GetSender()->GetCurrentWorldPlaneGeometry() ) ); if ( !image || !planeGeometry ) return; const AbstractTransformGeometry* abstractTransformGeometry( dynamic_cast (positionEvent->GetSender()->GetCurrentWorldPlaneGeometry() ) ); if ( !image || abstractTransformGeometry ) return; // 2. Slice is known, now we try to get it as a 2D image and project the contour into index coordinates of this slice m_WorkingSlice = FeedbackContourTool::GetAffectedImageSliceAs2DImage( positionEvent, image ); if ( m_WorkingSlice.IsNull() ) { MITK_ERROR << "Unable to extract slice." << std::endl; return; } int timestep = positionEvent->GetSender()->GetTimeStep(); mitk::ContourModel::Pointer singleTimestepContour = mitk::ContourModel::New(); mitk::ContourModel::VertexIterator it = FeedbackContourTool::GetFeedbackContour()->Begin(timestep); mitk::ContourModel::VertexIterator end = FeedbackContourTool::GetFeedbackContour()->End(timestep); while(it!=end) { singleTimestepContour->AddVertex((*it)->Coordinates); it++; } CorrectorAlgorithm::Pointer algorithm = CorrectorAlgorithm::New(); algorithm->SetInput( m_WorkingSlice ); algorithm->SetContour( singleTimestepContour ); mitk::LabelSetImage::Pointer labelSetImage = dynamic_cast(workingNode->GetData()); + int workingColorId (1); if (labelSetImage.IsNotNull()) { - algorithm->SetFillColor(labelSetImage->GetActiveLabel()->GetValue()); + workingColorId = labelSetImage->GetActiveLabel()->GetValue(); + algorithm->SetFillColor(workingColorId); } try { algorithm->UpdateLargestPossibleRegion(); } catch ( std::exception& e ) { MITK_ERROR << "Caught exception '" << e.what() << "'" << std::endl; } mitk::Image::Pointer resultSlice = mitk::Image::New(); resultSlice->Initialize(algorithm->GetOutput()); - mitk::ImageReadAccessor imAccess(algorithm->GetOutput()); - resultSlice->SetVolume(imAccess.GetData()); - this->WriteBackSegmentationResult(positionEvent, resultSlice); + mitk::Image::Pointer erg1 = FeedbackContourTool::GetAffectedImageSliceAs2DImage( positionEvent, image ); + SegTool2D::WritePreviewOnWorkingImage(erg1, algorithm->GetOutput(), image, workingColorId,0); + SegTool2D::WriteBackSegmentationResult(positionEvent,erg1); + //this->WriteBackSegmentationResult(positionEvent, resultSlice); } diff --git a/Modules/Segmentation/Interactions/mitkFeedbackContourTool.cpp b/Modules/Segmentation/Interactions/mitkFeedbackContourTool.cpp index 176bacf19b..f3bb726331 100644 --- a/Modules/Segmentation/Interactions/mitkFeedbackContourTool.cpp +++ b/Modules/Segmentation/Interactions/mitkFeedbackContourTool.cpp @@ -1,129 +1,129 @@ /*=================================================================== 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 "mitkFeedbackContourTool.h" #include "mitkToolManager.h" #include "mitkProperties.h" #include "mitkStringProperty.h" #include "mitkColorProperty.h" #include "mitkDataStorage.h" #include "mitkBaseRenderer.h" #include "mitkRenderingManager.h" mitk::FeedbackContourTool::FeedbackContourTool(const char* type) :SegTool2D(type), m_FeedbackContourVisible(false) { m_FeedbackContour = ContourModel::New(); m_FeedbackContour->SetClosed(true); m_FeedbackContourNode = DataNode::New(); m_FeedbackContourNode->SetData( m_FeedbackContour ); m_FeedbackContourNode->SetProperty("name", StringProperty::New("One of FeedbackContourTool's feedback nodes")); m_FeedbackContourNode->SetProperty("visible", BoolProperty::New(true)); m_FeedbackContourNode->SetProperty("helper object", BoolProperty::New(true)); m_FeedbackContourNode->SetProperty("layer", IntProperty::New(1000)); m_FeedbackContourNode->SetProperty("contour.project-onto-plane", BoolProperty::New(false)); m_FeedbackContourNode->SetProperty("contour.width", FloatProperty::New(1.0)); SetFeedbackContourColorDefault(); } mitk::FeedbackContourTool::~FeedbackContourTool() { } void mitk::FeedbackContourTool::SetFeedbackContourColor( float r, float g, float b ) { m_FeedbackContourNode->SetProperty("contour.color", ColorProperty::New(r, g, b)); } void mitk::FeedbackContourTool::SetFeedbackContourColorDefault() { m_FeedbackContourNode->SetProperty("contour.color", ColorProperty::New(0.0/255.0, 255.0/255.0, 0.0/255.0)); } void mitk::FeedbackContourTool::Deactivated() { Superclass::Deactivated(); DataStorage* storage = m_ToolManager->GetDataStorage(); if ( storage && m_FeedbackContourNode.IsNotNull()) { storage->Remove( m_FeedbackContourNode ); m_FeedbackContour->Clear(); SetFeedbackContourVisible(false); } } void mitk::FeedbackContourTool::Activated() { Superclass::Activated(); SetFeedbackContourVisible(true); } mitk::ContourModel* mitk::FeedbackContourTool::GetFeedbackContour() { return m_FeedbackContour; } void mitk::FeedbackContourTool::SetFeedbackContour(ContourModel::Pointer contour) { m_FeedbackContour = contour; m_FeedbackContourNode->SetData(m_FeedbackContour); } void mitk::FeedbackContourTool::SetFeedbackContourVisible(bool visible) { if ( m_FeedbackContourVisible == visible ) return; // nothing changed if ( DataStorage* storage = m_ToolManager->GetDataStorage() ) { if (visible) { storage->Add( m_FeedbackContourNode ); } else { storage->Remove( m_FeedbackContourNode ); } } m_FeedbackContourVisible = visible; } mitk::ContourModel::Pointer mitk::FeedbackContourTool::ProjectContourTo2DSlice(Image* slice, ContourModel* contourIn3D, bool correctionForIpSegmentation, bool constrainToInside) { return mitk::ContourModelUtils::ProjectContourTo2DSlice(slice, contourIn3D, correctionForIpSegmentation, constrainToInside); } mitk::ContourModel::Pointer mitk::FeedbackContourTool::BackProjectContourFrom2DSlice(const BaseGeometry* sliceGeometry, ContourModel* contourIn2D, bool correctionForIpSegmentation) { return mitk::ContourModelUtils::BackProjectContourFrom2DSlice(sliceGeometry, contourIn2D, correctionForIpSegmentation); } void mitk::FeedbackContourTool::FillContourInSlice( ContourModel* projectedContour, Image* sliceImage, int paintingPixelValue ) { this->FillContourInSlice(projectedContour, 0, sliceImage, paintingPixelValue); } void mitk::FeedbackContourTool::FillContourInSlice( ContourModel* projectedContour, unsigned int timeStep, Image* sliceImage, int paintingPixelValue ) { - mitk::ContourModelUtils::FillContourInSlice(projectedContour, timeStep, sliceImage, paintingPixelValue); + mitk::ContourModelUtils::FillContourInSlice(projectedContour, timeStep, sliceImage, sliceImage, paintingPixelValue); } diff --git a/Modules/Segmentation/Interactions/mitkLiveWireTool2D.cpp b/Modules/Segmentation/Interactions/mitkLiveWireTool2D.cpp index 4e4949d58b..bdddb5f97c 100644 --- a/Modules/Segmentation/Interactions/mitkLiveWireTool2D.cpp +++ b/Modules/Segmentation/Interactions/mitkLiveWireTool2D.cpp @@ -1,682 +1,682 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include #include #include #include #include #include #include #include #include #include #include #include #include "mitkLiveWireTool2D.h" #include "mitkLiveWireTool2D.xpm" namespace mitk { MITK_TOOL_MACRO(MITKSEGMENTATION_EXPORT, LiveWireTool2D, "LiveWire tool"); } class RemoveFromDataStorage { public: RemoveFromDataStorage(mitk::DataStorage::Pointer dataStorage) : m_DataStorage(dataStorage) { } void operator()(mitk::DataNode* dataNode) { m_DataStorage->Remove(dataNode); } void operator()(const std::pair& dataNode) { m_DataStorage->Remove(dataNode.first); } private: mitk::DataStorage::Pointer m_DataStorage; }; mitk::LiveWireTool2D::LiveWireTool2D() : SegTool2D("LiveWireTool"), m_PlaneGeometry(NULL) { } mitk::LiveWireTool2D::~LiveWireTool2D() { this->ClearSegmentation(); } void mitk::LiveWireTool2D::RemoveHelperObjects() { DataStorage* dataStorage = m_ToolManager->GetDataStorage(); if (!m_EditingContours.empty()) std::for_each(m_EditingContours.begin(), m_EditingContours.end(), RemoveFromDataStorage(dataStorage)); if (!m_WorkingContours.empty()) std::for_each(m_WorkingContours.begin(), m_WorkingContours.end(), RemoveFromDataStorage(dataStorage)); if (m_EditingContourNode.IsNotNull()) dataStorage->Remove(m_EditingContourNode); if (m_LiveWireContourNode.IsNotNull()) dataStorage->Remove(m_LiveWireContourNode); if (m_ContourModelNode.IsNotNull()) dataStorage->Remove(m_ContourModelNode); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } void mitk::LiveWireTool2D::ReleaseHelperObjects() { this->RemoveHelperObjects(); if (!m_EditingContours.empty()) m_EditingContours.clear(); if (!m_WorkingContours.empty()) m_WorkingContours.clear(); m_EditingContourNode = NULL; m_EditingContour = NULL; m_LiveWireContourNode = NULL; m_LiveWireContour = NULL; m_ContourModelNode = NULL; m_Contour = NULL; } void mitk::LiveWireTool2D::ReleaseInteractors() { this->EnableContourLiveWireInteraction(false); m_LiveWireNodes.clear(); } void mitk::LiveWireTool2D::ConnectActionsAndFunctions() { CONNECT_CONDITION("CheckContourClosed", OnCheckPoint); CONNECT_FUNCTION("InitObject", OnInitLiveWire); CONNECT_FUNCTION("AddPoint", OnAddPoint); CONNECT_FUNCTION("CtrlAddPoint", OnAddPoint); CONNECT_FUNCTION("MovePoint", OnMouseMoveNoDynamicCosts); CONNECT_FUNCTION("FinishContour", OnFinish); CONNECT_FUNCTION("DeletePoint", OnLastSegmentDelete); CONNECT_FUNCTION("CtrlMovePoint", OnMouseMoved); } const char** mitk::LiveWireTool2D::GetXPM() const { return mitkLiveWireTool2D_xpm; } us::ModuleResource mitk::LiveWireTool2D::GetIconResource() const { return us::GetModuleContext()->GetModule()->GetResource("LiveWire_48x48.png"); } us::ModuleResource mitk::LiveWireTool2D::GetCursorIconResource() const { return us::GetModuleContext()->GetModule()->GetResource("LiveWire_Cursor_32x32.png"); } const char* mitk::LiveWireTool2D::GetName() const { return "Live Wire"; } void mitk::LiveWireTool2D::Activated() { Superclass::Activated(); this->ResetToStartState(); this->EnableContourLiveWireInteraction(true); } void mitk::LiveWireTool2D::Deactivated() { this->ConfirmSegmentation(); Superclass::Deactivated(); } void mitk::LiveWireTool2D::EnableContourLiveWireInteraction(bool on) { for (auto interactor : m_LiveWireNodes) { if(on) interactor->EnableInteraction(true); else interactor->EnableInteraction(false); } } void mitk::LiveWireTool2D::ConfirmSegmentation() { DataNode* workingNode( m_ToolManager->GetWorkingData(0) ); if (!workingNode) return; Image* workingImage = dynamic_cast(workingNode->GetData()); if (!workingImage) return; // for all contours in list (currently created by tool) std::vector< std::pair >::iterator itWorkingContours = this->m_WorkingContours.begin(); std::vector sliceList; sliceList.reserve(m_WorkingContours.size()); 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( TimeStepType currentTimestep = 0; currentTimestep < contourModel->GetTimeGeometry()->CountTimeSteps(); ++currentTimestep) { //get the segmentation image slice at current timestep mitk::Image::Pointer workingSlice = this->GetAffectedImageSliceAs2DImage(itWorkingContours->second, workingImage, currentTimestep); mitk::ContourModel::Pointer projectedContour = mitk::ContourModelUtils::ProjectContourTo2DSlice(workingSlice, contourModel, true, false); - mitk::ContourModelUtils::FillContourInSlice(projectedContour, workingSlice, 1.0); + mitk::ContourModelUtils::FillContourInSlice(projectedContour, workingSlice, workingImage, 1.0); //write back to image volume SliceInformation sliceInfo (workingSlice, itWorkingContours->second, currentTimestep); sliceList.push_back(sliceInfo); this->WriteSliceToVolume(sliceInfo); } } } ++itWorkingContours; } this->WriteBackSegmentationResult(sliceList, false); this->ClearSegmentation(); } void mitk::LiveWireTool2D::ClearSegmentation() { this->ReleaseHelperObjects(); this->ReleaseInteractors(); this->ResetToStartState(); } bool mitk::LiveWireTool2D::IsPositionEventInsideImageRegion(mitk::InteractionPositionEvent* positionEvent, mitk::BaseData *data) { bool IsPositionEventInsideImageRegion = data != nullptr && data->GetGeometry()->IsInside(positionEvent->GetPositionInWorld()); if(!IsPositionEventInsideImageRegion) { MITK_WARN("LiveWireTool2D") << "PositionEvent is outside ImageRegion!"; return false; } return true; } void mitk::LiveWireTool2D::OnInitLiveWire ( StateMachineAction*, InteractionEvent* interactionEvent ) { mitk::InteractionPositionEvent* positionEvent = dynamic_cast( interactionEvent ); if (!positionEvent) return; mitk::DataNode* workingDataNode = m_ToolManager->GetWorkingData(0); if(!IsPositionEventInsideImageRegion(positionEvent, workingDataNode->GetData())) { this->ResetToStartState(); return; } m_LastEventSender = positionEvent->GetSender(); m_LastEventSlice = m_LastEventSender->GetSlice(); 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->SetProperty( "layer", IntProperty::New(100)); m_ContourModelNode->AddProperty( "fixedLayer", BoolProperty::New(true)); m_ContourModelNode->SetProperty( "helper object", mitk::BoolProperty::New(true)); 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(101)); m_LiveWireContourNode->AddProperty( "fixedLayer", BoolProperty::New(true)); 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(102)); m_EditingContourNode->AddProperty( "fixedLayer", BoolProperty::New(true)); 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, workingDataNode); m_ToolManager->GetDataStorage()->Add(m_LiveWireContourNode, workingDataNode); m_ToolManager->GetDataStorage()->Add(m_EditingContourNode, workingDataNode); //set current slice as input for ImageToLiveWireContourFilter m_WorkingSlice = this->GetAffectedReferenceSlice(positionEvent); mitk::Point3D newOrigin = m_WorkingSlice->GetSlicedGeometry()->GetOrigin(); m_WorkingSlice->GetSlicedGeometry()->WorldToIndex(newOrigin, newOrigin); m_WorkingSlice->GetSlicedGeometry()->IndexToWorld(newOrigin, newOrigin); m_WorkingSlice->GetSlicedGeometry()->SetOrigin(newOrigin); m_LiveWireFilter = mitk::ImageLiveWireContourModelFilter::New(); m_LiveWireFilter->SetInput(m_WorkingSlice); //map click to pixel coordinates mitk::Point3D click = positionEvent->GetPositionInWorld(); 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); // remember plane geometry to determine if events were triggered in same plane m_PlaneGeometry = interactionEvent->GetSender()->GetCurrentWorldPlaneGeometry(); m_CreateAndUseDynamicCosts = true; //render assert( positionEvent->GetSender()->GetRenderWindow() ); mitk::RenderingManager::GetInstance()->RequestUpdate( positionEvent->GetSender()->GetRenderWindow() ); } void mitk::LiveWireTool2D::OnAddPoint ( StateMachineAction*, InteractionEvent* interactionEvent ) { //complete LiveWire interaction for last segment //add current LiveWire contour to the finished contour and reset //to start new segment and computation mitk::InteractionPositionEvent* positionEvent = dynamic_cast( interactionEvent ); if (!positionEvent) return; if (m_PlaneGeometry != NULL) { // this checks that the point is in the correct slice if (m_PlaneGeometry->DistanceFromPlane(positionEvent->GetPositionInWorld()) > mitk::sqrteps) { return; } } 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(positionEvent->GetPositionInWorld()); 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() ); } void mitk::LiveWireTool2D::OnMouseMoved( StateMachineAction*, InteractionEvent* interactionEvent ) { //compute LiveWire segment from last control point to current mouse position mitk::InteractionPositionEvent* positionEvent = dynamic_cast( interactionEvent ); if (!positionEvent) return; // actual LiveWire computation int timestep = positionEvent->GetSender()->GetTimeStep(); m_LiveWireFilter->SetEndPoint(positionEvent->GetPositionInWorld()); m_LiveWireFilter->SetTimeStep( timestep ); m_LiveWireFilter->Update(); m_LiveWireContour = this->m_LiveWireFilter->GetOutput(); m_LiveWireContourNode->SetData( this->m_LiveWireContour ); //render assert( positionEvent->GetSender()->GetRenderWindow() ); positionEvent->GetSender()->GetRenderingManager()->RequestUpdate( positionEvent->GetSender()->GetRenderWindow() ); } void mitk::LiveWireTool2D::OnMouseMoveNoDynamicCosts( StateMachineAction*, InteractionEvent* interactionEvent ) { //do not use dynamic cost map m_LiveWireFilter->SetUseDynamicCostMap(false); OnMouseMoved( NULL, interactionEvent); m_LiveWireFilter->SetUseDynamicCostMap(true); } bool mitk::LiveWireTool2D::OnCheckPoint( const InteractionEvent* interactionEvent) { //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 // const mitk::InteractionPositionEvent* positionEvent = dynamic_cast( interactionEvent ); if (positionEvent) { int timestep = positionEvent->GetSender()->GetTimeStep(); mitk::Point3D click = positionEvent->GetPositionInWorld(); mitk::Point3D first = this->m_Contour->GetVertexAt(0, timestep)->Coordinates; if (first.EuclideanDistanceTo(click) < 4.5) { // allow to finish return true; } else { return false; } } return false; } void mitk::LiveWireTool2D::OnFinish( StateMachineAction*, InteractionEvent* interactionEvent ) { // finish livewire tool interaction mitk::InteractionPositionEvent* positionEvent = dynamic_cast( interactionEvent ); if (!positionEvent) return; // Have to do that here so that the m_LastEventSender is set correctly mitk::SegTool2D::AddContourmarker(); // 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, (positionEvent->GetSender()->GetCurrentWorldPlaneGeometry()->Clone().GetPointer()) ); this->m_WorkingContours.push_back(cp); std::pair ecp(m_EditingContourNode, (positionEvent->GetSender()->GetCurrentWorldPlaneGeometry()->Clone().GetPointer()) ); this->m_EditingContours.push_back(ecp); m_LiveWireFilter->SetUseDynamicCostMap(false); this->FinishTool(); } void mitk::LiveWireTool2D::FinishTool() { int numberOfTimesteps = static_cast(m_Contour->GetTimeGeometry()->CountTimeSteps()); //close contour in each timestep for( unsigned 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; // A new ContourModelLiveWireInteractor is created that will listen to new events //set the livewire interactor to edit control points m_ContourInteractor = mitk::ContourModelLiveWireInteractor::New(); m_ContourInteractor->SetDataNode(m_ContourModelNode); // TODO load statemachine and config m_ContourInteractor->LoadStateMachine("ContourModelModificationInteractor.xml",us::GetModuleContext()->GetModule()); // Set the configuration file that defines the triggers for the transitions m_ContourInteractor->SetEventConfig("ContourModelModificationConfig.xml",us::GetModuleContext()->GetModule()); m_ContourInteractor->SetWorkingImage(this->m_WorkingSlice); m_ContourInteractor->SetEditingContourModelNode(this->m_EditingContourNode); m_ContourModelNode->SetDataInteractor(m_ContourInteractor.GetPointer()); this->m_LiveWireNodes.push_back( m_ContourInteractor ); } void mitk::LiveWireTool2D::OnLastSegmentDelete( StateMachineAction*, InteractionEvent* interactionEvent ) { int timestep = interactionEvent->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 ); this->ResetToStartState(); //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->SetClosed(m_Contour->IsClosed()); //set new contour visible m_ContourModelNode->SetData(newContour); m_Contour = newContour; assert( interactionEvent->GetSender()->GetRenderWindow() ); mitk::RenderingManager::GetInstance()->RequestUpdate( interactionEvent->GetSender()->GetRenderWindow() ); } } template void mitk::LiveWireTool2D::FindHighestGradientMagnitudeByITK(itk::Image* inputImage, itk::Index<3> &index, itk::Index<3> &returnIndex) { typedef itk::Image InputImageType; typedef typename InputImageType::IndexType IndexType; unsigned long xMAX = inputImage->GetLargestPossibleRegion().GetSize()[0]; unsigned long yMAX = inputImage->GetLargestPossibleRegion().GetSize()[1]; returnIndex[0] = index[0]; returnIndex[1] = index[1]; returnIndex[2] = 0.0; double gradientMagnitude = 0.0; double maxGradientMagnitude = 0.0; /* the size and thus the region of 7x7 is only used to calculate the gradient magnitude in that region not for searching the maximum value */ //maximum value in each direction for size typename InputImageType::SizeType size; size[0] = 7; size[1] = 7; //minimum value in each direction for startRegion IndexType startRegion; startRegion[0] = index[0] - 3; startRegion[1] = index[1] - 3; if(startRegion[0] < 0) startRegion[0] = 0; if(startRegion[1] < 0) startRegion[1] = 0; if(xMAX - index[0] < 7) startRegion[0] = xMAX - 7; if(yMAX - index[1] < 7) startRegion[1] = yMAX - 7; index[0] = startRegion[0] + 3; index[1] = startRegion[1] + 3; typename InputImageType::RegionType region; region.SetSize( size ); region.SetIndex( startRegion ); typedef typename itk::GradientMagnitudeImageFilter< InputImageType, InputImageType> GradientMagnitudeFilterType; typename GradientMagnitudeFilterType::Pointer gradientFilter = GradientMagnitudeFilterType::New(); gradientFilter->SetInput(inputImage); gradientFilter->GetOutput()->SetRequestedRegion(region); gradientFilter->Update(); typename InputImageType::Pointer gradientMagnImage; gradientMagnImage = gradientFilter->GetOutput(); IndexType currentIndex; currentIndex[0] = 0; currentIndex[1] = 0; // search max (approximate) gradient magnitude for( int x = -1; x <= 1; ++x) { currentIndex[0] = index[0] + x; for( int y = -1; y <= 1; ++y) { currentIndex[1] = index[1] + y; gradientMagnitude = gradientMagnImage->GetPixel(currentIndex); //check for new max if(maxGradientMagnitude < gradientMagnitude) { maxGradientMagnitude = gradientMagnitude; returnIndex[0] = currentIndex[0]; returnIndex[1] = currentIndex[1]; returnIndex[2] = 0.0; }//end if }//end for y currentIndex[1] = index[1]; }//end for x } diff --git a/Modules/Segmentation/Interactions/mitkPaintbrushTool.cpp b/Modules/Segmentation/Interactions/mitkPaintbrushTool.cpp index 9b659e8b54..2d83fa47bb 100644 --- a/Modules/Segmentation/Interactions/mitkPaintbrushTool.cpp +++ b/Modules/Segmentation/Interactions/mitkPaintbrushTool.cpp @@ -1,510 +1,521 @@ /*=================================================================== 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 "mitkPaintbrushTool.h" #include "mitkToolManager.h" #include "mitkOverwriteSliceImageFilter.h" #include "mitkBaseRenderer.h" #include "mitkImageDataItem.h" #include "ipSegmentation.h" #include "mitkAbstractTransformGeometry.h" #include "mitkLevelWindowProperty.h" +#include "mitkLabelSetImage.h" +#include "mitkContourModelUtils.h" #define ROUND(a) ((a)>0 ? (int)((a)+0.5) : -(int)(0.5-(a))) int mitk::PaintbrushTool::m_Size = 1; mitk::PaintbrushTool::PaintbrushTool(int paintingPixelValue) :FeedbackContourTool("PressMoveReleaseWithCTRLInversionAllMouseMoves"), m_PaintingPixelValue(paintingPixelValue), m_LastContourSize(0) // other than initial mitk::PaintbrushTool::m_Size (around l. 28) { m_MasterContour = ContourModel::New(); m_MasterContour->Initialize(); m_CurrentPlane = nullptr; m_WorkingNode = DataNode::New(); m_WorkingNode->SetProperty( "levelwindow", mitk::LevelWindowProperty::New( mitk::LevelWindow(0, 1) ) ); m_WorkingNode->SetProperty( "binary", mitk::BoolProperty::New(true) ); } mitk::PaintbrushTool::~PaintbrushTool() { } void mitk::PaintbrushTool::ConnectActionsAndFunctions() { CONNECT_FUNCTION( "PrimaryButtonPressed", OnMousePressed); CONNECT_FUNCTION( "Move", OnPrimaryButtonPressedMoved); CONNECT_FUNCTION( "MouseMove", OnMouseMoved); CONNECT_FUNCTION( "Release", OnMouseReleased); CONNECT_FUNCTION( "InvertLogic", OnInvertLogic); } void mitk::PaintbrushTool::Activated() { Superclass::Activated(); FeedbackContourTool::SetFeedbackContourVisible(true); SizeChanged.Send(m_Size); m_ToolManager->WorkingDataChanged += mitk::MessageDelegate( this, &mitk::PaintbrushTool::OnToolManagerWorkingDataModified ); } void mitk::PaintbrushTool::Deactivated() { FeedbackContourTool::SetFeedbackContourVisible(false); if (m_ToolManager->GetDataStorage()->Exists(m_WorkingNode)) m_ToolManager->GetDataStorage()->Remove(m_WorkingNode); m_WorkingSlice = nullptr; m_CurrentPlane = nullptr; m_ToolManager->WorkingDataChanged -= mitk::MessageDelegate( this, &mitk::PaintbrushTool::OnToolManagerWorkingDataModified ); Superclass::Deactivated(); } void mitk::PaintbrushTool::SetSize(int value) { m_Size = value; } mitk::Point2D mitk::PaintbrushTool::upperLeft(mitk::Point2D p) { p[0] -= 0.5; p[1] += 0.5; return p; } void mitk::PaintbrushTool::UpdateContour(const InteractionPositionEvent* positionEvent) { //MITK_INFO<<"Update..."; // examine stateEvent and create a contour that matches the pixel mask that we are going to draw //mitk::InteractionPositionEvent* positionEvent = dynamic_cast( interactionEvent ); //const PositionEvent* positionEvent = dynamic_cast(stateEvent->GetEvent()); if (!positionEvent) return; // Get Spacing of current Slice //mitk::Vector3D vSpacing = m_WorkingSlice->GetSlicedGeometry()->GetPlaneGeometry(0)->GetSpacing(); // // Draw a contour in Square according to selected brush size // int radius = (m_Size)/2; float fradius = static_cast(m_Size) / 2.0f; ContourModel::Pointer contourInImageIndexCoordinates = ContourModel::New(); // estimate center point of the brush ( relative to the pixel the mouse points on ) // -- left upper corner for even sizes, // -- midpoint for uneven sizes mitk::Point2D centerCorrection; centerCorrection.Fill(0); // even --> correction of [+0.5, +0.5] bool evenSize = ((m_Size % 2) == 0); if( evenSize ) { centerCorrection[0] += 0.5; centerCorrection[1] += 0.5; } // we will compute the control points for the upper left quarter part of a circle contour std::vector< mitk::Point2D > quarterCycleUpperRight; std::vector< mitk::Point2D > quarterCycleLowerRight; std::vector< mitk::Point2D > quarterCycleLowerLeft; std::vector< mitk::Point2D > quarterCycleUpperLeft; mitk::Point2D curPoint; bool curPointIsInside = true; curPoint[0] = 0; curPoint[1] = radius; quarterCycleUpperRight.push_back( upperLeft(curPoint) ); // to estimate if a pixel is inside the circle, we need to compare against the 'outer radius' // i.e. the distance from the midpoint [0,0] to the border of the pixel [0,radius] //const float outer_radius = static_cast(radius) + 0.5; while (curPoint[1] > 0) { // Move right until pixel is outside circle float curPointX_squared = 0.0f; float curPointY_squared = (curPoint[1] - centerCorrection[1] ) * (curPoint[1] - centerCorrection[1] ); while( curPointIsInside ) { // increment posX and chec curPoint[0]++; curPointX_squared = (curPoint[0] - centerCorrection[0] ) * (curPoint[0] - centerCorrection[0] ); const float len = sqrt( curPointX_squared + curPointY_squared); if ( len > fradius ) { // found first Pixel in this horizontal line, that is outside the circle curPointIsInside = false; } } quarterCycleUpperRight.push_back( upperLeft(curPoint) ); // Move down until pixel is inside circle while( !curPointIsInside ) { // increment posX and chec curPoint[1]--; curPointY_squared = (curPoint[1] - centerCorrection[1] ) * (curPoint[1] - centerCorrection[1] ); const float len = sqrt( curPointX_squared + curPointY_squared); if ( len <= fradius ) { // found first Pixel in this horizontal line, that is outside the circle curPointIsInside = true; quarterCycleUpperRight.push_back( upperLeft(curPoint) ); } // Quarter cycle is full, when curPoint y position is 0 if (curPoint[1] <= 0) break; } } // QuarterCycle is full! Now copy quarter cycle to other quarters. if( !evenSize ) { std::vector< mitk::Point2D >::const_iterator it = quarterCycleUpperRight.begin(); while( it != quarterCycleUpperRight.end() ) { mitk::Point2D p; p = *it; // the contour points in the lower right corner have same position but with negative y values p[1] *= -1; quarterCycleLowerRight.push_back(p); // the contour points in the lower left corner have same position // but with both x,y negative p[0] *= -1; quarterCycleLowerLeft.push_back(p); // the contour points in the upper left corner have same position // but with x negative p[1] *= -1; quarterCycleUpperLeft.push_back(p); it++; } } else { std::vector< mitk::Point2D >::const_iterator it = quarterCycleUpperRight.begin(); while( it != quarterCycleUpperRight.end() ) { mitk::Point2D p,q; p = *it; q = p; // the contour points in the lower right corner have same position but with negative y values q[1] *= -1; // correct for moved offset if size even = the midpoint is not the midpoint of the current pixel // but its upper rigt corner q[1] += 1; quarterCycleLowerRight.push_back(q); q = p; // the contour points in the lower left corner have same position // but with both x,y negative q[1] = -1.0f * q[1] + 1; q[0] = -1.0f * q[0] + 1; quarterCycleLowerLeft.push_back(q); // the contour points in the upper left corner have same position // but with x negative q = p; q[0] *= -1; q[0] += 1; quarterCycleUpperLeft.push_back(q); it++; } } // fill contour with poins in right ordering, starting with the upperRight block mitk::Point3D tempPoint; for (unsigned int i=0; iAddVertex( tempPoint ); } // the lower right has to be parsed in reverse order for (int i=quarterCycleLowerRight.size()-1; i>=0; i--) { tempPoint[0] = quarterCycleLowerRight[i][0]; tempPoint[1] = quarterCycleLowerRight[i][1]; tempPoint[2] = 0; contourInImageIndexCoordinates->AddVertex( tempPoint ); } for (unsigned int i=0; iAddVertex( tempPoint ); } // the upper left also has to be parsed in reverse order for (int i=quarterCycleUpperLeft.size()-1; i>=0; i--) { tempPoint[0] = quarterCycleUpperLeft[i][0]; tempPoint[1] = quarterCycleUpperLeft[i][1]; tempPoint[2] = 0; contourInImageIndexCoordinates->AddVertex( tempPoint ); } m_MasterContour = contourInImageIndexCoordinates; } /** Just show the contour, get one point as the central point and add surrounding points to the contour. */ void mitk::PaintbrushTool::OnMousePressed ( StateMachineAction*, InteractionEvent* interactionEvent ) { mitk::InteractionPositionEvent* positionEvent = dynamic_cast( interactionEvent ); if (!positionEvent) return; m_LastEventSender = positionEvent->GetSender(); m_LastEventSlice = m_LastEventSender->GetSlice(); m_MasterContour->SetClosed(true); this->MouseMoved(interactionEvent, true); } void mitk::PaintbrushTool::OnMouseMoved( StateMachineAction*, InteractionEvent* interactionEvent ) { MouseMoved(interactionEvent, false); } void mitk::PaintbrushTool::OnPrimaryButtonPressedMoved( StateMachineAction*, InteractionEvent* interactionEvent ) { MouseMoved(interactionEvent, true); } /** Insert the point to the feedback contour,finish to build the contour and at the same time the painting function */ void mitk::PaintbrushTool::MouseMoved(mitk::InteractionEvent* interactionEvent, bool leftMouseButtonPressed) { mitk::InteractionPositionEvent* positionEvent = dynamic_cast( interactionEvent ); CheckIfCurrentSliceHasChanged( positionEvent ); if ( m_LastContourSize != m_Size ) { UpdateContour( positionEvent ); m_LastContourSize = m_Size; } 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; } 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 ); + DataNode* workingNode( m_ToolManager->GetWorkingData(0) ); + Image::Pointer image = dynamic_cast(workingNode->GetData()); + LabelSetImage* labelImage = dynamic_cast(image.GetPointer()); + int activeColor = 1; + if (labelImage != 0) + { + activeColor = labelImage->GetActiveLabel()->GetValue(); + } + + mitk::ContourModelUtils::FillContourInSlice(contour, timestep, m_WorkingSlice, image, m_PaintingPixelValue*activeColor); m_WorkingNode->SetData(m_WorkingSlice); m_WorkingNode->Modified(); } // visualize contour ContourModel::Pointer displayContour = this->GetFeedbackContour(); displayContour->Clear(); ContourModel::Pointer tmp = FeedbackContourTool::BackProjectContourFrom2DSlice( m_WorkingSlice->GetGeometry(), /*displayContour*/contour ); // copy transformed contour into display contour it = tmp->Begin(); end = tmp->End(); while(it != end) { Point3D point = (*it)->Coordinates; displayContour->AddVertex( point, timestep ); it++; } m_FeedbackContourNode->GetData()->Modified(); assert( positionEvent->GetSender()->GetRenderWindow() ); RenderingManager::GetInstance()->RequestUpdate( positionEvent->GetSender()->GetRenderWindow() ); } void mitk::PaintbrushTool::OnMouseReleased( StateMachineAction*, InteractionEvent* interactionEvent ) { //When mouse is released write segmentationresult back into image mitk::InteractionPositionEvent* positionEvent = dynamic_cast( interactionEvent ); if (!positionEvent) return; CheckIfCurrentSliceHasChanged(positionEvent); this->WriteBackSegmentationResult(positionEvent, m_WorkingSlice->Clone()); } /** Called when the CTRL key is pressed. Will change the painting pixel value from 0 to 1 or from 1 to 0. */ void mitk::PaintbrushTool::OnInvertLogic( StateMachineAction*, InteractionEvent* interactionEvent ) { // Inversion only for 0 and 1 as painting values if (m_PaintingPixelValue == 1) { m_PaintingPixelValue = 0; FeedbackContourTool::SetFeedbackContourColor( 1.0, 0.0, 0.0 ); } else if (m_PaintingPixelValue == 0) { m_PaintingPixelValue = 1; FeedbackContourTool::SetFeedbackContourColorDefault(); } mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } void mitk::PaintbrushTool::CheckIfCurrentSliceHasChanged(const InteractionPositionEvent *event) { const PlaneGeometry* planeGeometry( (event->GetSender()->GetCurrentWorldPlaneGeometry() ) ); const AbstractTransformGeometry* abstractTransformGeometry( dynamic_cast (event->GetSender()->GetCurrentWorldPlaneGeometry() ) ); DataNode* workingNode( m_ToolManager->GetWorkingData(0) ); if (!workingNode) return; Image::Pointer image = dynamic_cast(workingNode->GetData()); if ( !image || !planeGeometry || abstractTransformGeometry ) return; if(m_CurrentPlane.IsNull() || m_WorkingSlice.IsNull()) { m_CurrentPlane = const_cast(planeGeometry); m_WorkingSlice = SegTool2D::GetAffectedImageSliceAs2DImage(event, image)->Clone(); m_WorkingNode->ReplaceProperty( "color", workingNode->GetProperty("color") ); m_WorkingNode->SetData(m_WorkingSlice); } else { bool isSameSlice (false); isSameSlice = mitk::MatrixEqualElementWise(planeGeometry->GetIndexToWorldTransform()->GetMatrix(),m_CurrentPlane->GetIndexToWorldTransform()->GetMatrix()); isSameSlice = mitk::Equal(planeGeometry->GetIndexToWorldTransform()->GetOffset(),m_CurrentPlane->GetIndexToWorldTransform()->GetOffset()); if (!isSameSlice) { m_ToolManager->GetDataStorage()->Remove(m_WorkingNode); m_CurrentPlane = nullptr; m_WorkingSlice = nullptr; m_WorkingNode = nullptr; m_CurrentPlane = const_cast(planeGeometry); m_WorkingSlice = SegTool2D::GetAffectedImageSliceAs2DImage(event, image)->Clone(); m_WorkingNode = mitk::DataNode::New(); m_WorkingNode->SetProperty( "levelwindow", mitk::LevelWindowProperty::New( mitk::LevelWindow(0, 1) ) ); m_WorkingNode->SetProperty( "binary", mitk::BoolProperty::New(true) ); m_WorkingNode->SetData(m_WorkingSlice); //So that the paintbrush contour vanished in the previous render window RenderingManager::GetInstance()->RequestUpdateAll(); } } if(!m_ToolManager->GetDataStorage()->Exists(m_WorkingNode)) { m_WorkingNode->SetProperty( "outline binary", mitk::BoolProperty::New(true) ); m_WorkingNode->SetProperty( "color", workingNode->GetProperty("color") ); m_WorkingNode->SetProperty( "name", mitk::StringProperty::New("Paintbrush_Node") ); m_WorkingNode->SetProperty( "helper object", mitk::BoolProperty::New(true) ); m_WorkingNode->SetProperty( "opacity", mitk::FloatProperty::New(0.8) ); m_WorkingNode->SetProperty( "includeInBoundingBox", mitk::BoolProperty::New(false)); m_WorkingNode->SetVisibility(false, mitk::BaseRenderer::GetInstance( mitk::BaseRenderer::GetRenderWindowByName("stdmulti.widget4"))); m_ToolManager->GetDataStorage()->Add(m_WorkingNode); } } void mitk::PaintbrushTool::OnToolManagerWorkingDataModified() { //Here we simply set the current working slice to null. The next time the mouse is moved //within a renderwindow a new slice will be extracted from the new working data m_WorkingSlice = nullptr; } diff --git a/Modules/Segmentation/Interactions/mitkRegionGrowingTool.cpp b/Modules/Segmentation/Interactions/mitkRegionGrowingTool.cpp index 3e5084794d..0fee9f6177 100644 --- a/Modules/Segmentation/Interactions/mitkRegionGrowingTool.cpp +++ b/Modules/Segmentation/Interactions/mitkRegionGrowingTool.cpp @@ -1,517 +1,536 @@ /*=================================================================== 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 "mitkRegionGrowingTool.h" #include "mitkToolManager.h" #include "mitkOverwriteSliceImageFilter.h" #include "mitkImageDataItem.h" #include "mitkBaseRenderer.h" #include "mitkRenderingManager.h" #include "mitkApplicationCursor.h" #include "mitkImageToContourModelFilter.h" #include "mitkRegionGrowingTool.xpm" +#include "mitkOverwriteDirectedPlaneImageFilter.h" +#include "mitkExtractDirectedPlaneImageFilterNew.h" +#include "mitkLabelSetImage.h" // us #include #include #include #include // ITK #include "mitkImageAccessByItk.h" #include #include #include #include #include "mitkITKImageImport.h" namespace mitk { MITK_TOOL_MACRO(MITKSEGMENTATION_EXPORT, RegionGrowingTool, "Region growing tool"); } #define ROUND(a) ((a)>0 ? (int)((a)+0.5) : -(int)(0.5-(a))) mitk::RegionGrowingTool::RegionGrowingTool() :FeedbackContourTool("PressMoveRelease"), m_SeedValue(0), m_ScreenYDifference(0), m_ScreenXDifference(0), m_VisibleWindow(0), m_MouseDistanceScaleFactor(0.5), m_FillFeedbackContour(true), m_ConnectedComponentValue(1) { } mitk::RegionGrowingTool::~RegionGrowingTool() { } void mitk::RegionGrowingTool::ConnectActionsAndFunctions() { CONNECT_FUNCTION( "PrimaryButtonPressed", OnMousePressed); CONNECT_FUNCTION( "Move", OnMouseMoved); CONNECT_FUNCTION( "Release", OnMouseReleased); } const char** mitk::RegionGrowingTool::GetXPM() const { return mitkRegionGrowingTool_xpm; } us::ModuleResource mitk::RegionGrowingTool::GetIconResource() const { us::Module* module = us::GetModuleContext()->GetModule(); us::ModuleResource resource = module->GetResource("RegionGrowing_48x48.png"); return resource; } us::ModuleResource mitk::RegionGrowingTool::GetCursorIconResource() const { us::Module* module = us::GetModuleContext()->GetModule(); us::ModuleResource resource = module->GetResource("RegionGrowing_Cursor_32x32.png"); return resource; } const char* mitk::RegionGrowingTool::GetName() const { return "Region Growing"; } void mitk::RegionGrowingTool::Activated() { Superclass::Activated(); } void mitk::RegionGrowingTool::Deactivated() { Superclass::Deactivated(); } // Get the average pixel value of square/cube with radius=neighborhood around index template void mitk::RegionGrowingTool::GetNeighborhoodAverage(itk::Image* itkImage, itk::Index index, ScalarType* result, unsigned int neighborhood) { // maybe assert that image dimension is only 2 or 3? int neighborhoodInt = (int) neighborhood; TPixel averageValue(0); unsigned int numberOfPixels = (2*neighborhood + 1) * (2*neighborhood + 1); if (imageDimension == 3) { numberOfPixels *= (2*neighborhood + 1); } MITK_DEBUG << "Getting neighborhood of " << numberOfPixels << " pixels around " << index; itk::Index currentIndex; for (int i = (0 - neighborhoodInt); i <= neighborhoodInt; ++i) { currentIndex[0] = index[0] + i; for (int j = (0 - neighborhoodInt); j <= neighborhoodInt; ++j) { currentIndex[1] = index[1] + j; if (imageDimension == 3) { for (int k = (0 - neighborhoodInt); k <= neighborhoodInt; ++k) { currentIndex[2] = index[2] + k; if (itkImage->GetLargestPossibleRegion().IsInside(currentIndex)) { averageValue += itkImage->GetPixel(currentIndex); } else { numberOfPixels -= 1; } } } else { if (itkImage->GetLargestPossibleRegion().IsInside(currentIndex)) { averageValue += itkImage->GetPixel(currentIndex); } else { numberOfPixels -= 1; } } } } *result = (ScalarType) averageValue; *result /= numberOfPixels; } // Check whether index lies inside a segmentation template void mitk::RegionGrowingTool::IsInsideSegmentation(itk::Image* itkImage, itk::Index index, bool* result) { if (itkImage->GetPixel(index) > 0) { *result = true; } else { *result = false; } } // Do the region growing (i.e. call an ITK filter that does it) template void mitk::RegionGrowingTool::StartRegionGrowing(itk::Image* inputImage, itk::Index seedIndex, std::array thresholds, mitk::Image::Pointer& outputImage) { MITK_DEBUG << "Starting region growing at index " << seedIndex << " with lower threshold " << thresholds[0] << " and upper threshold " << thresholds[1]; typedef itk::Image InputImageType; typedef itk::Image OutputImageType; typedef itk::ConnectedThresholdImageFilter RegionGrowingFilterType; typename RegionGrowingFilterType::Pointer regionGrower = RegionGrowingFilterType::New(); // perform region growing in desired segmented region regionGrower->SetInput(inputImage); regionGrower->AddSeed(seedIndex); regionGrower->SetLower(thresholds[0]); regionGrower->SetUpper(thresholds[1]); try { regionGrower->Update(); } catch(...) { return; // Should we do something? } typename OutputImageType::Pointer resultImage = regionGrower->GetOutput(); // Smooth result: Every pixel is replaced by the majority of the neighborhood typedef itk::NeighborhoodIterator NeighborhoodIteratorType; typedef itk::ImageRegionIterator ImageIteratorType; typename NeighborhoodIteratorType::RadiusType radius; radius.Fill(2); // for now, maybe make this something the user can adjust in the preferences? NeighborhoodIteratorType neighborhoodIterator(radius, resultImage, resultImage->GetRequestedRegion()); ImageIteratorType imageIterator(resultImage, resultImage->GetRequestedRegion()); for (neighborhoodIterator.GoToBegin(), imageIterator.GoToBegin(); !neighborhoodIterator.IsAtEnd(); ++neighborhoodIterator, ++imageIterator) { DefaultSegmentationDataType voteYes(0); DefaultSegmentationDataType voteNo(0); for (unsigned int i = 0; i < neighborhoodIterator.Size(); ++i) { if (neighborhoodIterator.GetPixel(i) > 0) { voteYes += 1; } else { voteNo += 1; } } if (voteYes > voteNo) { imageIterator.Set(1); } else { imageIterator.Set(0); } } if (resultImage.IsNull()) { MITK_DEBUG << "Region growing result is empty."; } // Can potentially have multiple regions, use connected component image filter to label disjunct regions typedef itk::ConnectedComponentImageFilter ConnectedComponentImageFilterType; typename ConnectedComponentImageFilterType::Pointer connectedComponentFilter = ConnectedComponentImageFilterType::New(); connectedComponentFilter->SetInput(resultImage); connectedComponentFilter->Update(); typename OutputImageType::Pointer resultImageCC = connectedComponentFilter->GetOutput(); m_ConnectedComponentValue = resultImageCC->GetPixel(seedIndex); outputImage = mitk::GrabItkImageMemory(resultImageCC); } void mitk::RegionGrowingTool::OnMousePressed ( StateMachineAction*, InteractionEvent* interactionEvent ) { mitk::InteractionPositionEvent* positionEvent = dynamic_cast( interactionEvent ); if (!positionEvent) return; MITK_DEBUG << "OnMousePressed"; m_LastEventSender = positionEvent->GetSender(); m_LastEventSlice = m_LastEventSender->GetSlice(); m_LastScreenPosition = positionEvent->GetPointerPositionOnScreen(); // ReferenceSlice is from the underlying image, WorkingSlice from the active segmentation (can be empty) m_ReferenceSlice = FeedbackContourTool::GetAffectedReferenceSlice( positionEvent ); m_WorkingSlice = FeedbackContourTool::GetAffectedWorkingSlice( positionEvent ); if (m_WorkingSlice.IsNotNull()) // can't do anything without a working slice (i.e. a possibly empty segmentation) { MITK_DEBUG << "OnMousePressed: got working slice"; // 2. Determine if the user clicked inside or outside of the segmentation/working slice (i.e. the whole volume) mitk::BaseGeometry::Pointer workingSliceGeometry; workingSliceGeometry = m_WorkingSlice->GetTimeGeometry()->GetGeometryForTimeStep(m_LastEventSender->GetTimeStep()); workingSliceGeometry->WorldToIndex(positionEvent->GetPositionInWorld(), m_SeedPoint); itk::Index<2> indexInWorkingSlice2D; indexInWorkingSlice2D[0] = m_SeedPoint[0]; indexInWorkingSlice2D[1] = m_SeedPoint[1]; if (workingSliceGeometry->IsIndexInside(m_SeedPoint)) { - MITK_DEBUG << "OnMousePressed: point " << positionEvent->GetPositionInWorld() << " (index coordinates " << m_SeedPoint << ") is inside working slice"; + MITK_DEBUG << "OnMousePressed: point " << positionEvent->GetPositionInWorld() << " (index coordinates " << m_SeedPoint << ") is inside working slice"; + + ContourModel::Pointer projectedContour = FeedbackContourTool::ProjectContourTo2DSlice( m_WorkingSlice, feedbackContour, false, false ); // false: don't add any 0.5 + // false: don't constrain the contour to the image's inside + if (projectedContour.IsNotNull()) + { + //FeedbackContourTool::FillContourInSlice( projectedContour, timestep, m_WorkingSlice, m_PaintingPixelValue ); + DataNode* workingNode( m_ToolManager->GetWorkingData(0) ); + Image::Pointer image = dynamic_cast(workingNode->GetData()); + LabelSetImage* labelImage = dynamic_cast(image.GetPointer()); + int activeColor = 1; + if (labelImage != 0) + { + activeColor = labelImage->GetActiveLabel()->GetValue(); + } + + mitk::ContourModelUtils::FillContourInSlice(projectedContour, timestep, m_WorkingSlice, image, m_PaintingPixelValue*activeColor); // 3. determine the pixel value under the last click to determine what to do bool inside(true); AccessFixedDimensionByItk_2(m_WorkingSlice, IsInsideSegmentation, 2, indexInWorkingSlice2D, &inside); m_PaintingPixelValue = inside ? 0 : 1; if (inside) { MITK_DEBUG << "Clicked inside segmentation"; // For now, we're doing nothing when the user clicks inside the segmentation. Behaviour can be implemented via OnMousePressedInside() // When you do, be sure to remove the m_PaintingPixelValue check in OnMouseMoved() and OnMouseReleased() return; } else { MITK_DEBUG << "Clicked outside of segmentation"; OnMousePressedOutside(nullptr, interactionEvent); } } } } // Use this to implement a behaviour for when the user clicks inside a segmentation (for example remove something) // Old IpPic code is kept as comment for reference void mitk::RegionGrowingTool::OnMousePressedInside() { // mitk::InteractionPositionEvent* positionEvent = dynamic_cast( interactionEvent ); // //const PositionEvent* positionEvent = dynamic_cast(stateEvent->GetEvent()); // checked in OnMousePressed // // 3.1.1. Create a skeletonization of the segmentation and try to find a nice cut // // apply the skeletonization-and-cut algorithm // // generate contour to remove // // set m_ReferenceSlice = NULL so nothing will happen during mouse move // // remember to fill the contour with 0 in mouserelease // mitkIpPicDescriptor* segmentationHistory = ipMITKSegmentationCreateGrowerHistory( workingPicSlice, m_LastWorkingSeed, NULL ); // free again // if (segmentationHistory) // { // tCutResult cutContour = ipMITKSegmentationGetCutPoints( workingPicSlice, segmentationHistory, initialWorkingOffset ); // tCutResult is a ipSegmentation type // mitkIpPicFree( segmentationHistory ); // if (cutContour.cutIt) // { // int timestep = positionEvent->GetSender()->GetTimeStep(); // // 3.1.2 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 < cutContour.deleteSize; ++index) // { // newPoint[0] = cutContour.deleteCurve[ 2 * index + 0 ] - 0.5;//correction is needed because the output of the algorithm is center based // newPoint[1] = cutContour.deleteCurve[ 2 * index + 1 ] - 0.5;//and we want our contour displayed corner based. // newPoint[2] = 0.0; // contourInImageIndexCoordinates->AddVertex( newPoint, timestep ); // } // free(cutContour.traceline); // free(cutContour.deleteCurve); // perhaps visualize this for fun? // free(cutContour.onGradient); // ContourModel::Pointer contourInWorldCoordinates = FeedbackContourTool::BackProjectContourFrom2DSlice( m_WorkingSlice->GetGeometry(), contourInImageIndexCoordinates, true ); // true: sub 0.5 for ipSegmentation correction // FeedbackContourTool::SetFeedbackContour( contourInWorldCoordinates ); // FeedbackContourTool::SetFeedbackContourVisible(true); // mitk::RenderingManager::GetInstance()->RequestUpdate( positionEvent->GetSender()->GetRenderWindow() ); // m_FillFeedbackContour = true; // } // else // { // m_FillFeedbackContour = false; // } // } // else // { // m_FillFeedbackContour = false; // } // m_ReferenceSlice = NULL; // return true; } void mitk::RegionGrowingTool::OnMousePressedOutside(StateMachineAction*, InteractionEvent* interactionEvent) { mitk::InteractionPositionEvent* positionEvent = dynamic_cast(interactionEvent); if (positionEvent) { // Get geometry and indices mitk::BaseGeometry::Pointer workingSliceGeometry; workingSliceGeometry = m_WorkingSlice->GetTimeGeometry()->GetGeometryForTimeStep(m_LastEventSender->GetTimeStep()); itk::Index<2> indexInWorkingSlice2D; indexInWorkingSlice2D[0] = m_SeedPoint[0]; indexInWorkingSlice2D[1] = m_SeedPoint[1]; mitk::BaseGeometry::Pointer referenceSliceGeometry; referenceSliceGeometry = m_ReferenceSlice->GetTimeGeometry()->GetGeometryForTimeStep(m_LastEventSender->GetTimeStep()); itk::Index<3> indexInReferenceSlice; itk::Index<2> indexInReferenceSlice2D; referenceSliceGeometry->WorldToIndex(positionEvent->GetPositionInWorld(), indexInReferenceSlice); indexInReferenceSlice2D[0] = indexInReferenceSlice[0]; indexInReferenceSlice2D[1] = indexInReferenceSlice[1]; // Get seed neighborhood ScalarType averageValue(0); AccessFixedDimensionByItk_3(m_ReferenceSlice, GetNeighborhoodAverage, 2, indexInReferenceSlice2D, &averageValue, 1); m_SeedValue = averageValue; MITK_DEBUG << "Seed value is " << m_SeedValue; // Get level window settings LevelWindow lw(0, 500); // default window 0 to 500, can we do something smarter here? m_ToolManager->GetReferenceData(0)->GetLevelWindow(lw); // will fill lw if levelwindow property is present, otherwise won't touch it. ScalarType currentVisibleWindow = lw.GetWindow(); MITK_DEBUG << "Level window width is " << currentVisibleWindow; m_InitialThresholds[0] = m_SeedValue - currentVisibleWindow / 20.0; // 20 is arbitrary (though works reasonably well), is there a better alternative (maybe option in preferences)? m_InitialThresholds[1] = m_SeedValue + currentVisibleWindow / 20.0; m_Thresholds[0] = m_InitialThresholds[0]; m_Thresholds[1] = m_InitialThresholds[1]; // Perform region growing mitk::Image::Pointer resultImage = mitk::Image::New(); AccessFixedDimensionByItk_3(m_ReferenceSlice, StartRegionGrowing, 2, indexInWorkingSlice2D, m_Thresholds, resultImage); resultImage->SetGeometry(workingSliceGeometry); // Extract contour if (resultImage.IsNotNull() && m_ConnectedComponentValue >= 1) { mitk::ImageToContourModelFilter::Pointer contourExtractor = mitk::ImageToContourModelFilter::New(); contourExtractor->SetInput(resultImage); contourExtractor->SetContourValue(m_ConnectedComponentValue - 0.5); contourExtractor->Update(); ContourModel::Pointer resultContour = ContourModel::New(); resultContour = contourExtractor->GetOutput(); // Show contour if (resultContour.IsNotNull()) { ContourModel::Pointer resultContourWorld = FeedbackContourTool::BackProjectContourFrom2DSlice(workingSliceGeometry, FeedbackContourTool::ProjectContourTo2DSlice(m_WorkingSlice, resultContour)); FeedbackContourTool::SetFeedbackContour(resultContourWorld); FeedbackContourTool::SetFeedbackContourVisible(true); mitk::RenderingManager::GetInstance()->RequestUpdate(m_LastEventSender->GetRenderWindow()); } } } } void mitk::RegionGrowingTool::OnMouseMoved(StateMachineAction*, InteractionEvent* interactionEvent ) { // Until OnMousePressedInside() implements a behaviour, we're just returning here whenever m_PaintingPixelValue is 0, i.e. when the user clicked inside the segmentation if (m_PaintingPixelValue == 0) { return; } mitk::InteractionPositionEvent* positionEvent = dynamic_cast(interactionEvent); if ( m_ReferenceSlice.IsNotNull() && positionEvent) { // Get geometry and indices mitk::BaseGeometry::Pointer workingSliceGeometry; workingSliceGeometry = m_WorkingSlice->GetTimeGeometry()->GetGeometryForTimeStep(m_LastEventSender->GetTimeStep()); itk::Index<2> indexInWorkingSlice2D; indexInWorkingSlice2D[0] = m_SeedPoint[0]; indexInWorkingSlice2D[1] = m_SeedPoint[1]; m_ScreenYDifference += positionEvent->GetPointerPositionOnScreen()[1] - m_LastScreenPosition[1]; m_ScreenXDifference += positionEvent->GetPointerPositionOnScreen()[0] - m_LastScreenPosition[0]; m_LastScreenPosition = positionEvent->GetPointerPositionOnScreen(); // Moving the mouse up and down adjusts the width of the threshold window, moving it left and right shifts the threshold window m_Thresholds[0] = std::min(m_SeedValue, m_InitialThresholds[0] - (m_ScreenYDifference - m_ScreenXDifference) * m_MouseDistanceScaleFactor); m_Thresholds[1] = std::max(m_SeedValue, m_InitialThresholds[1] + (m_ScreenYDifference + m_ScreenXDifference) * m_MouseDistanceScaleFactor); MITK_DEBUG << "Screen difference X: " << m_ScreenXDifference; // Perform region growing again and show the result mitk::Image::Pointer resultImage = mitk::Image::New(); AccessFixedDimensionByItk_3(m_ReferenceSlice, StartRegionGrowing, 2, indexInWorkingSlice2D, m_Thresholds, resultImage); resultImage->SetGeometry(workingSliceGeometry); // Update the contour if (resultImage.IsNotNull() && m_ConnectedComponentValue >= 1) { mitk::ImageToContourModelFilter::Pointer contourExtractor = mitk::ImageToContourModelFilter::New(); contourExtractor->SetInput(resultImage); contourExtractor->SetContourValue(m_ConnectedComponentValue - 0.5); contourExtractor->Update(); ContourModel::Pointer resultContour = ContourModel::New(); resultContour = contourExtractor->GetOutput(); // Show contour if (resultContour.IsNotNull()) { ContourModel::Pointer resultContourWorld = FeedbackContourTool::BackProjectContourFrom2DSlice(workingSliceGeometry, FeedbackContourTool::ProjectContourTo2DSlice(m_WorkingSlice, resultContour)); FeedbackContourTool::SetFeedbackContour(resultContourWorld); FeedbackContourTool::SetFeedbackContourVisible(true); mitk::RenderingManager::GetInstance()->ForceImmediateUpdate(positionEvent->GetSender()->GetRenderWindow()); } } } } void mitk::RegionGrowingTool::OnMouseReleased(StateMachineAction*, InteractionEvent* interactionEvent) { // Until OnMousePressedInside() implements a behaviour, we're just returning here whenever m_PaintingPixelValue is 0, i.e. when the user clicked inside the segmentation if (m_PaintingPixelValue == 0) { return; } mitk::InteractionPositionEvent* positionEvent = dynamic_cast(interactionEvent); if (m_WorkingSlice.IsNotNull() && m_FillFeedbackContour && positionEvent) { // Project contour into working slice ContourModel* feedbackContour(FeedbackContourTool::GetFeedbackContour()); ContourModel::Pointer projectedContour = FeedbackContourTool::ProjectContourTo2DSlice(m_WorkingSlice, feedbackContour, false, false); // If there is a projected contour, fill it if (projectedContour.IsNotNull()) { MITK_DEBUG << "Filling Segmentation"; FeedbackContourTool::FillContourInSlice(projectedContour, positionEvent->GetSender()->GetTimeStep(), m_WorkingSlice, m_PaintingPixelValue); this->WriteBackSegmentationResult(positionEvent, m_WorkingSlice); FeedbackContourTool::SetFeedbackContourVisible(false); } } } diff --git a/Modules/Segmentation/Interactions/mitkSegTool2D.cpp b/Modules/Segmentation/Interactions/mitkSegTool2D.cpp index 0c69281a0b..0abd114b74 100644 --- a/Modules/Segmentation/Interactions/mitkSegTool2D.cpp +++ b/Modules/Segmentation/Interactions/mitkSegTool2D.cpp @@ -1,491 +1,576 @@ /*=================================================================== 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 "mitkSegTool2D.h" #include "mitkToolManager.h" #include "mitkDataStorage.h" #include "mitkBaseRenderer.h" #include "mitkPlaneGeometry.h" #include "mitkExtractImageFilter.h" #include "mitkExtractDirectedPlaneImageFilter.h" //Include of the new ImageExtractor #include "mitkExtractDirectedPlaneImageFilterNew.h" #include "mitkPlanarCircle.h" #include "mitkOverwriteSliceImageFilter.h" #include "mitkOverwriteDirectedPlaneImageFilter.h" #include "mitkMorphologicalOperations.h" #include "usGetModuleContext.h" //Includes for 3DSurfaceInterpolation #include "mitkImageToContourFilter.h" #include "mitkSurfaceInterpolationController.h" #include "mitkImageTimeSelector.h" //includes for resling and overwriting #include #include #include #include #include #include "mitkOperationEvent.h" #include "mitkUndoController.h" #include "mitkAbstractTransformGeometry.h" +#include "mitkImageAccessByItk.h" +#include "mitkImageToItk.h" +#include "mitkImageCast.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) :Tool(type), m_LastEventSender(NULL), 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 InteractionPositionEvent* 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) { if (!positionEvent) return NULL; 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 this->GetAffectedImageSliceAs2DImage(positionEvent->GetSender()->GetCurrentWorldPlaneGeometry(), image, timeStep); } mitk::Image::Pointer mitk::SegTool2D::GetAffectedImageSliceAs2DImage(const PlaneGeometry* planeGeometry, const Image* image, unsigned int timeStep) { if ( !image || !planeGeometry ) return NULL; //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 ) ); 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 NULL; Image* workingImage = dynamic_cast(workingNode->GetData()); if ( !workingImage ) return NULL; return GetAffectedImageSliceAs2DImage( positionEvent, workingImage ); } mitk::Image::Pointer mitk::SegTool2D::GetAffectedReferenceSlice(const InteractionPositionEvent* positionEvent) { DataNode* referenceNode( m_ToolManager->GetReferenceData(0) ); if ( !referenceNode ) return NULL; Image* referenceImage = dynamic_cast(referenceNode->GetData()); if ( !referenceImage ) return NULL; return GetAffectedImageSliceAs2DImage( positionEvent, referenceImage ); } void mitk::SegTool2D::WriteBackSegmentationResult (const InteractionPositionEvent* positionEvent, Image* slice) { if(!positionEvent) return; const PlaneGeometry* planeGeometry( (positionEvent->GetSender()->GetCurrentWorldPlaneGeometry() ) ); const AbstractTransformGeometry* abstractTransformGeometry( dynamic_cast (positionEvent->GetSender()->GetCurrentWorldPlaneGeometry() ) ); if( planeGeometry && slice && !abstractTransformGeometry) { DataNode* workingNode( m_ToolManager->GetWorkingData(0) ); Image* 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) ); Image* image = dynamic_cast(workingNode->GetData()); this->UpdateSurfaceInterpolation(slice, image, planeGeometry, false); if (m_SurfaceInterpolationEnabled) this->AddContourmarker(); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } void mitk::SegTool2D::WriteBackSegmentationResult(std::vector sliceList, bool writeSliceToVolume) { std::vector contourList; contourList.reserve(sliceList.size()); ImageToContourFilter::Pointer contourExtractor = ImageToContourFilter::New(); DataNode* workingNode( m_ToolManager->GetWorkingData(0) ); Image* 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(mitk::SegTool2D::SliceInformation sliceInfo) { DataNode* workingNode( m_ToolManager->GetWorkingData(0) ); Image* 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); DiffSliceOperation* undoOperation = new DiffSliceOperation(const_cast(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(); /*============= BEGIN undo/redo feature block ========================*/ //specify the undo operation with the edited slice DiffSliceOperation* 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 = NULL; doOperation = NULL; /*============= 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 == NULL) 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 mitk::PlaneGeometry* plane = dynamic_cast (dynamic_cast< const mitk::SlicedGeometry3D*>( 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 ( mitk::DataStorage::SetOfObjects::const_iterator 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 << " http://bugs.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< SliceType > OutputIteratorType; + typedef itk::ImageRegionConstIterator< SliceType > InputIteratorType; + + InputIteratorType inputIterator( sourceSliceITK, sourceSliceITK->GetLargestPossibleRegion() ); + OutputIteratorType outputIterator( targetSlice, targetSlice->GetLargestPossibleRegion() ); + + outputIterator.GoToBegin(); + inputIterator.GoToBegin(); + + mitk::LabelSetImage* 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() ) + { + int 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 timestep ) +{ + 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 fea22c9eac..aeb334d40a 100644 --- a/Modules/Segmentation/Interactions/mitkSegTool2D.h +++ b/Modules/Segmentation/Interactions/mitkSegTool2D.h @@ -1,179 +1,181 @@ /*=================================================================== 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 mitkSegTool2D_h_Included #define mitkSegTool2D_h_Included #include "mitkCommon.h" #include #include "mitkTool.h" #include "mitkImage.h" #include "mitkInteractionPositionEvent.h" #include "mitkPlanePositionManager.h" #include "mitkRestorePlanePositionOperation.h" #include "mitkInteractionConst.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 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); 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*); // purposely hidden virtual ~SegTool2D(); struct SliceInformation { mitk::Image::Pointer slice; mitk::PlaneGeometry* plane; unsigned int timestep; SliceInformation () {} SliceInformation (mitk::Image* slice, mitk::PlaneGeometry* plane, unsigned int 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 */ virtual bool FilterEvents(InteractionEvent *interactionEvent, DataNode *dataNode) override; /** \brief Extract the slice of an image that the user just scribbles on. \return NULL 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*, const Image* image); /** \brief Extract the slice of an image cut by given plane. \return NULL 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); /** \brief Extract the slice of the currently selected working image that the user just scribbles on. \return NULL 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 NULL 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 (std::vector sliceList, bool writeSliceToVolume = true); + void WritePreviewOnWorkingImage( Image* targetSlice, Image* sourceSlice, Image* workingImage, int paintingPixelValue, int timestep ); + void WriteSliceToVolume (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 diff --git a/Modules/Segmentation/Interactions/mitkSegmentationInteractor.cpp b/Modules/Segmentation/Interactions/mitkSegmentationInteractor.cpp new file mode 100644 index 0000000000..a21e9d2dd2 --- /dev/null +++ b/Modules/Segmentation/Interactions/mitkSegmentationInteractor.cpp @@ -0,0 +1,63 @@ +/*=================================================================== + + The Medical Imaging Interaction Toolkit (MITK) + + Copyright (c) German Cancer Research Center, + Division of Medical and Biological Informatics. + All rights reserved. + + This software is distributed WITHOUT ANY WARRANTY; without + even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. + + See LICENSE.txt or http://www.mitk.org for details. + + ===================================================================*/ + +#include "mitkSegmentationInteractor.h" +#include "mitkLabelSetImage.h" +#include "mitkInteractionPositionEvent.h" +#include "mitkToolManager.h" +#include "mitkToolManagerProvider.h" + +#include + +void mitk::SegmentationInteractor::ConnectActionsAndFunctions() +{ + Superclass::ConnectActionsAndFunctions(); + + //CONNECT_FUNCTION("change_active_label", ChangeActiveLabel); +} + +bool mitk::SegmentationInteractor::ChangeActiveLabel(StateMachineAction*, InteractionEvent* interactionEvent) +{ + BaseRenderer::Pointer sender = interactionEvent->GetSender(); + InteractionPositionEvent* positionEvent = static_cast(interactionEvent); + + //MLI TODO + //m_LastDisplayCoordinate = m_CurrentDisplayCoordinate; + //m_CurrentDisplayCoordinate = positionEvent->GetPointerPositionOnScreen(); + + mitk::ToolManager* toolManager = mitk::ToolManagerProvider::GetInstance()->GetToolManager(); + assert(toolManager); + + DataNode* workingNode( toolManager->GetWorkingData(0) ); + if (workingNode) + { + mitk::LabelSetImage* workingImage = dynamic_cast(workingNode->GetData()); + assert(workingImage); + + int timestep = positionEvent->GetSender()->GetTimeStep(); + int pixelValue = workingImage->GetPixelValueByWorldCoordinate( positionEvent->GetPositionInWorld(), timestep ); + workingImage->GetActiveLabelSet()->SetActiveLabel(pixelValue); // can be the background + + // Call Events + //workingImage->ActiveLabelEvent.Send(pixelValue); + + //MLI TODO + //toolManager->WorkingDataModified.Send(); + } + + sender->GetRenderingManager()->RequestUpdateAll(); + return true; +} diff --git a/Modules/Segmentation/Interactions/mitkSegmentationInteractor.h b/Modules/Segmentation/Interactions/mitkSegmentationInteractor.h new file mode 100644 index 0000000000..ca1a0e0bb9 --- /dev/null +++ b/Modules/Segmentation/Interactions/mitkSegmentationInteractor.h @@ -0,0 +1,56 @@ +/*=================================================================== + + 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 mitkSegmentationInteractor_h +#define mitkSegmentationInteractor_h + +#include "MitkSegmentationExports.h" +#include "mitkDisplayInteractor.h" + +namespace mitk +{ + /** + *\class SegmentationInteractor + *@brief Observer that adds interaction with a segmentation session to the default display interaction. + * + * At the moment, this includes changing the active label. + * + * @ingroup Interaction + **/ + + class MITKSEGMENTATION_EXPORT SegmentationInteractor: public DisplayInteractor + { + public: + mitkClassMacro(SegmentationInteractor, DisplayInteractor) + itkNewMacro(Self) + + protected: + SegmentationInteractor() {}; + virtual ~SegmentationInteractor() {}; + /** + * Derived function. + * Connects the action names used in the state machine pattern with functions implemented within + * this InteractionEventObserver. This is only necessary here because the events are processed by the state machine. + */ + virtual void ConnectActionsAndFunctions(); + + /** + * Changes the active label. + */ + bool ChangeActiveLabel(StateMachineAction*, InteractionEvent*); + }; +} +#endif diff --git a/Modules/Segmentation/Interactions/mitkSetRegionTool.cpp b/Modules/Segmentation/Interactions/mitkSetRegionTool.cpp index af573eab86..781d402694 100644 --- a/Modules/Segmentation/Interactions/mitkSetRegionTool.cpp +++ b/Modules/Segmentation/Interactions/mitkSetRegionTool.cpp @@ -1,160 +1,237 @@ /*=================================================================== 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 "mitkSetRegionTool.h" #include "mitkToolManager.h" #include "mitkBaseRenderer.h" #include "mitkImageDataItem.h" +#include "mitkLegacyAdaptors.h" +#include "mitkLabelSetImage.h" #include #include #include #include #include mitk::SetRegionTool::SetRegionTool(int paintingPixelValue) :FeedbackContourTool("PressMoveRelease"), m_PaintingPixelValue(paintingPixelValue) { } mitk::SetRegionTool::~SetRegionTool() { } void mitk::SetRegionTool::ConnectActionsAndFunctions() { CONNECT_FUNCTION( "PrimaryButtonPressed", OnMousePressed); CONNECT_FUNCTION( "Release", OnMouseReleased); CONNECT_FUNCTION( "Move",OnMouseMoved); } void mitk::SetRegionTool::Activated() { Superclass::Activated(); } void mitk::SetRegionTool::Deactivated() { Superclass::Deactivated(); } void mitk::SetRegionTool::OnMousePressed ( StateMachineAction*, InteractionEvent* interactionEvent ) { mitk::InteractionPositionEvent* positionEvent = dynamic_cast( interactionEvent ); if (!positionEvent) return; m_LastEventSender = positionEvent->GetSender(); m_LastEventSlice = m_LastEventSender->GetSlice(); // 1. Get the working image Image::Pointer workingSlice = FeedbackContourTool::GetAffectedWorkingSlice( positionEvent ); if ( workingSlice.IsNull() ) return; // can't do anything without the segmentation // if click was outside the image, don't continue const BaseGeometry* sliceGeometry = workingSlice->GetGeometry(); itk::Index<3> projectedPointIn2D; sliceGeometry->WorldToIndex( positionEvent->GetPositionInWorld(), projectedPointIn2D ); if ( !sliceGeometry->IsIndexInside( projectedPointIn2D ) ) { MITK_ERROR << "point apparently not inside segmentation slice" << std::endl; return; // can't use that as a seed point } typedef itk::Image InputImageType; typedef InputImageType::IndexType IndexType; typedef itk::ConnectedThresholdImageFilter RegionGrowingFilterType; RegionGrowingFilterType::Pointer regionGrower = RegionGrowingFilterType::New(); // convert world coordinates to image indices IndexType seedIndex; sliceGeometry->WorldToIndex( positionEvent->GetPositionInWorld(), seedIndex); //perform region growing in desired segmented region InputImageType::Pointer itkImage = InputImageType::New(); CastToItkImage(workingSlice, itkImage); regionGrower->SetInput( itkImage ); regionGrower->AddSeed( seedIndex ); InputImageType::PixelType bound = itkImage->GetPixel(seedIndex); regionGrower->SetLower( bound ); regionGrower->SetUpper( bound ); regionGrower->SetReplaceValue(1); itk::BinaryFillholeImageFilter::Pointer fillHolesFilter = itk::BinaryFillholeImageFilter::New(); fillHolesFilter->SetInput(regionGrower->GetOutput()); fillHolesFilter->SetForegroundValue(1); //Store result and preview mitk::Image::Pointer resultImage = mitk::GrabItkImageMemory(fillHolesFilter->GetOutput()); resultImage->SetGeometry(workingSlice->GetGeometry()); +// // Get the current working color +// DataNode* workingNode( m_ToolManager->GetWorkingData(0) ); +// if (!workingNode) return; + +// Image* image = dynamic_cast(workingNode->GetData()); +// LabelSetImage* labelImage = dynamic_cast(image); +// int activeColor = 1; +// if (labelImage != 0) +// { +// activeColor = labelImage->GetActiveLabel()->GetValue(); +// } + + +// // 2. Determine the contour that surronds the selected "piece of the image" + +// // find a contour seed point +// unsigned int oneContourOffset = static_cast( m_SeedPointMemoryOffset ); // safe because of earlier check if m_SeedPointMemoryOffset < 0 + +// /** +// * The logic of finding a starting point for the contour is the following: +// * +// * - If the initial seed point is 0, we are either inside a hole or outside of every segmentation. +// * We move to the right until we hit a 1, which must be part of a contour. +// * +// * - If the initial seed point is 1, then ... +// * we now do the same (running to the right) until we hit a 1 +// * +// * In both cases the found contour point is used to extract a contour and +// * then a test is applied to find out if the initial seed point is contained +// * in the contour. If this is the case, filling should be applied, otherwise +// * nothing is done. +// */ +// unsigned int size = originalPicSlice->n[0] * originalPicSlice->n[1]; +///* +// unsigned int rowSize = originalPicSlice->n[0]; +//*/ +// ipMITKSegmentationTYPE* data = static_cast(originalPicSlice->data); + +// if ( data[oneContourOffset] == 0 ) // initial seed 0 +// { +// for ( ; oneContourOffset < size; ++oneContourOffset ) +// { +// if ( data[oneContourOffset] > 0 ) break; +// } +// } +// else if ( data[oneContourOffset] == activeColor ) // initial seed 1 +// { +// unsigned int lastValidPixel = size-1; // initialization, will be changed lateron +// bool inSeg = true; // inside segmentation? +// for ( ; oneContourOffset < size; ++oneContourOffset ) +// { +// if ( ( data[oneContourOffset] != activeColor ) && inSeg ) // pixel 0 and inside-flag set: this happens at the first pixel outside a filled region +// { +// inSeg = false; +// lastValidPixel = oneContourOffset - 1; // store the last pixel position inside a filled region +// break; +// } +// else // pixel 1, inside-flag doesn't matter: this happens while we are inside a filled region +// { +// inSeg = true; // first iteration lands here +// } + +// } +// oneContourOffset = lastValidPixel; +// } + mitk::ImageToContourModelFilter::Pointer contourextractor = mitk::ImageToContourModelFilter::New(); contourextractor->SetInput(resultImage); contourextractor->Update(); mitk::ContourModel::Pointer awesomeContour = contourextractor->GetOutput(); FeedbackContourTool::SetFeedbackContour( awesomeContour ); FeedbackContourTool::SetFeedbackContourVisible(true); mitk::RenderingManager::GetInstance()->RequestUpdate(positionEvent->GetSender()->GetRenderWindow()); } void mitk::SetRegionTool::OnMouseReleased( StateMachineAction*, InteractionEvent* interactionEvent ) { mitk::InteractionPositionEvent* positionEvent = dynamic_cast( interactionEvent ); if (!positionEvent) return; assert( positionEvent->GetSender()->GetRenderWindow() ); // 1. Hide the feedback contour, find out which slice the user clicked, find out which slice of the toolmanager's working image corresponds to that FeedbackContourTool::SetFeedbackContourVisible(false); mitk::RenderingManager::GetInstance()->RequestUpdate(positionEvent->GetSender()->GetRenderWindow()); int timeStep = positionEvent->GetSender()->GetTimeStep(); DataNode* workingNode( m_ToolManager->GetWorkingData(0) ); if (!workingNode) return; Image* image = dynamic_cast(workingNode->GetData()); const PlaneGeometry* planeGeometry( (positionEvent->GetSender()->GetCurrentWorldPlaneGeometry() ) ); if ( !image || !planeGeometry ) return; Image::Pointer slice = FeedbackContourTool::GetAffectedImageSliceAs2DImage( positionEvent, image ); if ( slice.IsNull() ) { MITK_ERROR << "Unable to extract slice." << std::endl; return; } ContourModel* feedbackContour( FeedbackContourTool::GetFeedbackContour() ); ContourModel::Pointer projectedContour = FeedbackContourTool::ProjectContourTo2DSlice( slice, feedbackContour, false, false ); // false: don't add 0.5 (done by FillContourInSlice) // false: don't constrain the contour to the image's inside if (projectedContour.IsNull()) return; - FeedbackContourTool::FillContourInSlice( projectedContour, timeStep, slice, m_PaintingPixelValue ); + LabelSetImage* labelImage = dynamic_cast(image); + int activeColor = 1; + if (labelImage != 0) + { + activeColor = labelImage->GetActiveLabel()->GetValue(); + } + + mitk::ContourModelUtils::FillContourInSlice(projectedContour, timeStep, slice, image, m_PaintingPixelValue*activeColor); + + //FeedbackContourTool::FillContourInSlice( projectedContour, timeStep, slice, m_PaintingPixelValue ); this->WriteBackSegmentationResult(positionEvent, slice); } void mitk::SetRegionTool::OnMouseMoved(mitk::StateMachineAction *, mitk::InteractionEvent *) { } diff --git a/Modules/Segmentation/files.cmake b/Modules/Segmentation/files.cmake index 47956f1937..404535ebed 100644 --- a/Modules/Segmentation/files.cmake +++ b/Modules/Segmentation/files.cmake @@ -1,119 +1,123 @@ set(CPP_FILES Algorithms/mitkCalculateSegmentationVolume.cpp Algorithms/mitkContourModelSetToImageFilter.cpp Algorithms/mitkContourSetToPointSetFilter.cpp Algorithms/mitkContourUtils.cpp Algorithms/mitkCorrectorAlgorithm.cpp Algorithms/mitkDiffImageApplier.cpp Algorithms/mitkDiffSliceOperation.cpp Algorithms/mitkDiffSliceOperationApplier.cpp Algorithms/mitkFeatureBasedEdgeDetectionFilter.cpp Algorithms/mitkImageLiveWireContourModelFilter.cpp Algorithms/mitkImageToContourFilter.cpp #Algorithms/mitkImageToContourModelFilter.cpp Algorithms/mitkImageToLiveWireContourFilter.cpp Algorithms/mitkManualSegmentationToSurfaceFilter.cpp Algorithms/mitkOtsuSegmentationFilter.cpp Algorithms/mitkOverwriteDirectedPlaneImageFilter.cpp Algorithms/mitkOverwriteSliceImageFilter.cpp Algorithms/mitkSegmentationObjectFactory.cpp Algorithms/mitkShapeBasedInterpolationAlgorithm.cpp Algorithms/mitkShowSegmentationAsSmoothedSurface.cpp Algorithms/mitkShowSegmentationAsSurface.cpp Algorithms/mitkVtkImageOverwrite.cpp Controllers/mitkSegmentationInterpolationController.cpp Controllers/mitkToolManager.cpp Controllers/mitkSegmentationModuleActivator.cpp Controllers/mitkToolManagerProvider.cpp DataManagement/mitkContour.cpp #DataManagement/mitkContourElement.cpp #DataManagement/mitkContourModel.cpp DataManagement/mitkContourSet.cpp DataManagement/mitkExtrudedContour.cpp Interactions/mitkAdaptiveRegionGrowingTool.cpp Interactions/mitkAddContourTool.cpp Interactions/mitkAutoCropTool.cpp Interactions/mitkAutoSegmentationTool.cpp Interactions/mitkBinaryThresholdTool.cpp Interactions/mitkBinaryThresholdULTool.cpp Interactions/mitkCalculateGrayValueStatisticsTool.cpp Interactions/mitkCalculateVolumetryTool.cpp Interactions/mitkContourModelInteractor.cpp Interactions/mitkContourModelLiveWireInteractor.cpp Interactions/mitkLiveWireTool2D.cpp Interactions/mitkContourTool.cpp Interactions/mitkCorrectorTool2D.cpp Interactions/mitkCreateSurfaceTool.cpp Interactions/mitkDrawPaintbrushTool.cpp Interactions/mitkErasePaintbrushTool.cpp Interactions/mitkEraseRegionTool.cpp Interactions/mitkFastMarchingTool.cpp Interactions/mitkFastMarchingTool3D.cpp Interactions/mitkFeedbackContourTool.cpp Interactions/mitkFillRegionTool.cpp Interactions/mitkOtsuTool3D.cpp Interactions/mitkPaintbrushTool.cpp Interactions/mitkPixelManipulationTool.cpp Interactions/mitkRegionGrowingTool.cpp Interactions/mitkSegmentationsProcessingTool.cpp Interactions/mitkSetRegionTool.cpp Interactions/mitkSegTool2D.cpp Interactions/mitkSubtractContourTool.cpp Interactions/mitkTool.cpp Interactions/mitkToolCommand.cpp Interactions/mitkWatershedTool.cpp Interactions/mitkPickingTool.cpp + Interactions/mitkSegmentationInteractor.cpp #SO #IO/mitkContourModelIOFactory.cpp #IO/mitkContourModelReader.cpp #IO/mitkContourModelWriter.cpp #IO/mitkContourModelWriterFactory.cpp Rendering/mitkContourMapper2D.cpp #Rendering/mitkContourModelGLMapper2D.cpp #Rendering/mitkContourModelMapper2D.cpp #Rendering/mitkContourModelMapper3D.cpp Rendering/mitkContourSetMapper2D.cpp Rendering/mitkContourSetVtkMapper3D.cpp Rendering/mitkContourVtkMapper3D.cpp SegmentationUtilities/BooleanOperations/mitkBooleanOperation.cpp SegmentationUtilities/MorphologicalOperations/mitkMorphologicalOperations.cpp +#Added from ML + Controllers/mitkSliceBasedInterpolationController.cpp + Algorithms/mitkSurfaceStampImageFilter.cpp ) set(RESOURCE_FILES Add_48x48.png Add_Cursor_32x32.png Correction_48x48.png Correction_Cursor_32x32.png Erase_48x48.png Erase_Cursor_32x32.png FastMarching_48x48.png FastMarching_Cursor_32x32.png Fill_48x48.png Fill_Cursor_32x32.png LiveWire_48x48.png LiveWire_Cursor_32x32.png Otsu_48x48.png Paint_48x48.png Paint_Cursor_32x32.png Pick_48x48.png RegionGrowing_48x48.png RegionGrowing_Cursor_32x32.png Subtract_48x48.png Subtract_Cursor_32x32.png Threshold_48x48.png TwoThresholds_48x48.png Watershed_48x48.png Watershed_Cursor_32x32.png Wipe_48x48.png Wipe_Cursor_32x32.png Interactions/dummy.xml Interactions/LiveWireTool.xml Interactions/PressMoveRelease.xml Interactions/PressMoveReleaseAndPointSetting.xml Interactions/PressMoveReleaseWithCTRLInversion.xml Interactions/PressMoveReleaseWithCTRLInversionAllMouseMoves.xml Interactions/SegmentationToolsConfig.xml Interactions/ContourModelModificationConfig.xml Interactions/ContourModelModificationInteractor.xml ) diff --git a/Modules/SegmentationUI/Qmitk/QmitkLabelSetWidget.cpp b/Modules/SegmentationUI/Qmitk/QmitkLabelSetWidget.cpp new file mode 100644 index 0000000000..dc0f02cdb3 --- /dev/null +++ b/Modules/SegmentationUI/Qmitk/QmitkLabelSetWidget.cpp @@ -0,0 +1,1248 @@ +/*=================================================================== + +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 "QmitkLabelSetWidget.h" + +// mitk +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// Qmitk +#include +#include + +#include + +// Qt +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// itk +#include + +// todo: +// berry +//#include + +QmitkLabelSetWidget::QmitkLabelSetWidget(QWidget* parent) +: QWidget(parent) +, m_ToolManager(NULL) +, m_DataStorage(NULL) +, m_Completer(NULL) +{ + m_Controls.setupUi(this); + + m_ColorSequenceRainbow.GoToBegin(); + + m_ToolManager = mitk::ToolManagerProvider::GetInstance()->GetToolManager(); + assert(m_ToolManager); + + m_Controls.m_LabelSearchBox->setAlwaysShowClearIcon(true); + m_Controls.m_LabelSearchBox->setShowSearchIcon(true); + + QStringList completionList; + completionList << ""; + m_Completer = new QCompleter(completionList, this); + m_Completer->setCaseSensitivity(Qt::CaseInsensitive); + m_Controls.m_LabelSearchBox->setCompleter(m_Completer); + + connect( m_Controls.m_LabelSearchBox, SIGNAL(returnPressed()), this, SLOT(OnSearchLabel()) ); + //connect( m_Controls.m_LabelSetTableWidget, SIGNAL(labelListModified(const QStringList&)), this, SLOT( OnLabelListModified(const QStringList&)) ); + //connect( m_Controls.m_LabelSetTableWidget, SIGNAL(mergeLabel(int)), this, SLOT( OnMergeLabel(int)) ); + + QStringListModel* completeModel = static_cast (m_Completer->model()); + completeModel->setStringList(GetLabelStringList()); + + m_Controls.m_LabelSearchBox->setEnabled(false); + + + m_Controls.m_lblCaption->setText(""); + + InitializeTableWidget(); +} + +QmitkLabelSetWidget::~QmitkLabelSetWidget() +{ +} + + +void QmitkLabelSetWidget::OnTableViewContextMenuRequested(const QPoint& pos) +{ + QTableWidgetItem *itemAt = m_Controls.m_LabelSetTableWidget->itemAt(pos); + + //OnItemClicked(itemAt); + + if (!itemAt) return; + int pixelValue = itemAt->data(Qt::UserRole).toInt(); + QMenu* menu = new QMenu(m_Controls.m_LabelSetTableWidget); + + if (m_Controls.m_LabelSetTableWidget->selectedItems().size()>1) + { + QAction* mergeAction = new QAction(QIcon(":/Qmitk/MergeLabels.png"), "Merge selection on current label", this ); + mergeAction->setEnabled(true); + QObject::connect( mergeAction, SIGNAL( triggered(bool) ), this, SLOT( OnMergeLabels(bool) ) ); + menu->addAction(mergeAction); + + QAction* removeLabelsAction = new QAction(QIcon(":/Qmitk/RemoveLabel.png"), "Remove selected labels", this ); + removeLabelsAction->setEnabled(true); + QObject::connect( removeLabelsAction, SIGNAL( triggered(bool) ), this, SLOT( OnRemoveLabels(bool) ) ); + menu->addAction(removeLabelsAction); + + QAction* eraseLabelsAction = new QAction(QIcon(":/Qmitk/EraseLabel.png"), "Erase selected labels", this ); + eraseLabelsAction->setEnabled(true); + QObject::connect( eraseLabelsAction, SIGNAL( triggered(bool) ), this, SLOT( OnEraseLabels(bool) ) ); + menu->addAction(eraseLabelsAction); + + QAction* combineAndCreateSurfaceAction = new QAction(QIcon(":/Qmitk/CreateSurface.png"), "Combine and create a surface", this ); + combineAndCreateSurfaceAction->setEnabled(true); + QObject::connect( combineAndCreateSurfaceAction, SIGNAL( triggered(bool) ), this, SLOT( OnCombineAndCreateSurface(bool) ) ); + menu->addAction(combineAndCreateSurfaceAction); + + QAction* createMasksAction = new QAction(QIcon(":/Qmitk/CreateMask.png"), "Create a mask for each selected label", this ); + createMasksAction->setEnabled(true); + QObject::connect( createMasksAction, SIGNAL( triggered(bool) ), this, SLOT( OnCreateMasks(bool) ) ); + menu->addAction(createMasksAction); + + QAction* combineAndCreateMaskAction = new QAction(QIcon(":/Qmitk/CreateMask.png"), "Combine and create a mask", this ); + combineAndCreateMaskAction->setEnabled(true); + QObject::connect( combineAndCreateMaskAction, SIGNAL( triggered(bool) ), this, SLOT( OnCombineAndCreateMask(bool) ) ); + menu->addAction(combineAndCreateMaskAction); + } + else + { + QAction* renameAction = new QAction(QIcon(":/Qmitk/RenameLabel.png"), "Rename...", this ); + renameAction->setEnabled(true); + QObject::connect( renameAction, SIGNAL( triggered(bool) ), this, SLOT( OnRenameLabel(bool) ) ); + menu->addAction(renameAction); + + QAction* removeAction = new QAction(QIcon(":/Qmitk/RemoveLabel.png"), "Remove...", this ); + removeAction->setEnabled(true); + QObject::connect( removeAction, SIGNAL( triggered(bool) ), this, SLOT( OnRemoveLabel(bool) ) ); + menu->addAction(removeAction); + + QAction* eraseAction = new QAction(QIcon(":/Qmitk/EraseLabel.png"), "Erase...", this ); + eraseAction->setEnabled(true); + QObject::connect( eraseAction, SIGNAL( triggered(bool) ), this, SLOT( OnEraseLabel(bool) ) ); + menu->addAction(eraseAction); + + QAction* mergeAction = new QAction(QIcon(":/Qmitk/MergeLabels.png"), "Merge...", this ); + mergeAction->setEnabled(true); + QObject::connect( mergeAction, SIGNAL( triggered(bool) ), this, SLOT( OnMergeLabel(bool) ) ); + menu->addAction(mergeAction); + + QAction* randomColorAction = new QAction(QIcon(":/Qmitk/RandomColor.png"), "Random color", this ); + randomColorAction->setEnabled(true); + QObject::connect( randomColorAction, SIGNAL( triggered(bool) ), this, SLOT( OnRandomColor(bool) ) ); + menu->addAction(randomColorAction); + + QAction* viewOnlyAction = new QAction(QIcon(":/Qmitk/visible.png"), "View only", this ); + viewOnlyAction->setEnabled(true); + QObject::connect( viewOnlyAction, SIGNAL( triggered(bool) ), this, SLOT( OnSetOnlyActiveLabelVisible(bool) ) ); + menu->addAction(viewOnlyAction); + + QAction* viewAllAction = new QAction(QIcon(":/Qmitk/visible.png"), "View all", this ); + viewAllAction->setEnabled(true); + QObject::connect( viewAllAction, SIGNAL( triggered(bool) ), this, SLOT( OnSetAllLabelsVisible(bool) ) ); + menu->addAction(viewAllAction); + + QAction* hideAllAction = new QAction(QIcon(":/Qmitk/invisible.png"), "Hide all", this ); + hideAllAction->setEnabled(true); + QObject::connect( hideAllAction, SIGNAL( triggered(bool) ), this, SLOT( OnSetAllLabelsInvisible(bool) ) ); + menu->addAction(hideAllAction); + + QAction* lockAllAction = new QAction(QIcon(":/Qmitk/lock.png"), "Lock all", this ); + lockAllAction->setEnabled(true); + QObject::connect( lockAllAction, SIGNAL( triggered(bool) ), this, SLOT( OnLockAllLabels(bool) ) ); + menu->addAction(lockAllAction); + + QAction* unlockAllAction = new QAction(QIcon(":/Qmitk/unlock.png"), "Unlock all", this ); + unlockAllAction->setEnabled(true); + QObject::connect( unlockAllAction, SIGNAL( triggered(bool) ), this, SLOT( OnUnlockAllLabels(bool) ) ); + menu->addAction(unlockAllAction); + + QAction* createSurfaceAction = new QAction(QIcon(":/Qmitk/CreateSurface.png"), "Create surface", this ); + createSurfaceAction->setEnabled(true); + createSurfaceAction->setMenu(new QMenu()); + + QAction* tmp1 = createSurfaceAction->menu()->addAction(QString("Detailed")); + QAction* tmp2 = createSurfaceAction->menu()->addAction(QString("Smoothed")); + + QObject::connect( tmp1, SIGNAL( triggered(bool) ), this, SLOT( OnCreateDetailedSurface(bool) ) ); + QObject::connect( tmp2, SIGNAL( triggered(bool) ), this, SLOT( OnCreateSmoothedSurface(bool) ) ); + + menu->addAction(createSurfaceAction); + + QAction* createMaskAction = new QAction(QIcon(":/Qmitk/CreateMask.png"), "Create mask", this ); + createMaskAction->setEnabled(true); + QObject::connect( createMaskAction, SIGNAL( triggered(bool) ), this, SLOT( OnCreateMask(bool) ) ); + + menu->addAction(createMaskAction); + + QAction* createCroppedMaskAction = new QAction(QIcon(":/Qmitk/CreateMask.png"), "Create cropped mask", this ); + createCroppedMaskAction->setEnabled(true); + QObject::connect( createCroppedMaskAction, SIGNAL( triggered(bool) ), this, SLOT( OnCreateCroppedMask(bool) ) ); + +// QAction* importAction = new QAction(QIcon(":/Qmitk/RenameLabel.png"), "Import...", this ); +// importAction->setEnabled(true); +// QObject::connect( importAction, SIGNAL( triggered(bool) ), this, SLOT( OnImportSegmentationSession(bool) ) ); +// menu->addAction(importAction); + + menu->addAction(createCroppedMaskAction); + + QSlider * opacitySlider = new QSlider; + opacitySlider->setMinimum(0); + opacitySlider->setMaximum(100); + opacitySlider->setOrientation(Qt::Horizontal); + QObject::connect( opacitySlider, SIGNAL( valueChanged(int) ), this, SLOT( OnOpacityChanged(int) ) ); + + QLabel* _OpacityLabel = new QLabel("Opacity: "); + QVBoxLayout* _OpacityWidgetLayout = new QVBoxLayout; + _OpacityWidgetLayout->setContentsMargins(4,4,4,4); + _OpacityWidgetLayout->addWidget(_OpacityLabel); + _OpacityWidgetLayout->addWidget(opacitySlider); + QWidget* _OpacityWidget = new QWidget; + _OpacityWidget->setLayout(_OpacityWidgetLayout); + + QWidgetAction * OpacityAction = new QWidgetAction(this); + OpacityAction->setDefaultWidget(_OpacityWidget); + // QObject::connect( m_OpacityAction, SIGNAL( changed() ), this, SLOT( OpacityActionChanged() ) ); + opacitySlider->setValue(static_cast(GetWorkingImage()->GetLabel(pixelValue)->GetOpacity()*100)); + + menu->addAction(OpacityAction); + } + menu->popup(QCursor::pos()); +} + +void QmitkLabelSetWidget::OnUnlockAllLabels(bool /*value*/) +{ + GetWorkingImage()->GetLabelSet()->SetAllLabelsLocked(false); + mitk::RenderingManager::GetInstance()->RequestUpdateAll(); +} + +void QmitkLabelSetWidget::OnLockAllLabels(bool /*value*/) +{ + GetWorkingImage()->GetLabelSet()->SetAllLabelsLocked(true); + mitk::RenderingManager::GetInstance()->RequestUpdateAll(); +} + +void QmitkLabelSetWidget::OnSetAllLabelsVisible(bool /*value*/) +{ + GetWorkingImage()->GetLabelSet()->SetAllLabelsVisible(true); + mitk::RenderingManager::GetInstance()->RequestUpdateAll(); + UpdateAllTableWidgetItems(); +} + +void QmitkLabelSetWidget::OnSetAllLabelsInvisible(bool /*value*/) +{ + GetWorkingImage()->GetLabelSet()->SetAllLabelsVisible(false); + mitk::RenderingManager::GetInstance()->RequestUpdateAll(); + UpdateAllTableWidgetItems(); +} + +void QmitkLabelSetWidget::OnSetOnlyActiveLabelVisible(bool /*value*/) +{ + mitk::LabelSetImage * workingImage = GetWorkingImage(); + int pixelValue = GetPixelValueOfSelectedItem(); + + workingImage->GetActiveLabelSet()->SetAllLabelsVisible(false); + workingImage->GetLabel(pixelValue)->SetVisible(true); + + GetWorkingImage()->GetActiveLabelSet()->UpdateLookupTable(pixelValue); + + this->WaitCursorOn(); + const mitk::Point3D& pos = workingImage->GetLabel(pixelValue)->GetCenterOfMassCoordinates(); + this->WaitCursorOff(); + if (pos.GetVnlVector().max_value() > 0.0) + emit goToLabel(pos); + + UpdateAllTableWidgetItems(); +} + + +void QmitkLabelSetWidget::OnMergeLabel(bool /*value*/) +{ + QmitkSearchLabelDialog dialog(this); + dialog.setWindowTitle("Select a second label.."); + dialog.SetLabelSuggestionList(GetLabelStringList()); + int dialogReturnValue = dialog.exec(); + if ( dialogReturnValue == QDialog::Rejected ) return; + + int pixelValue = -1; + for(int i = 0 ; i < m_Controls.m_LabelSetTableWidget->columnCount();i++) + { + if( dialog.GetLabelSetWidgetTableCompleteWord() == QString( m_Controls.m_LabelSetTableWidget->item( i ,0)->text() ) ) + pixelValue = m_Controls.m_LabelSetTableWidget->item(i ,0)->data(Qt::UserRole).toInt(); + } + + if(pixelValue == -1 ) + { + MITK_INFO << "unknown label";; + return; + } + + GetWorkingImage()->MergeLabel(pixelValue); + + mitk::RenderingManager::GetInstance()->RequestUpdateAll(); +} + +void QmitkLabelSetWidget::OnEraseLabel(bool /*value*/) +{ + int pixelValue = GetPixelValueOfSelectedItem(); + QString question = "Do you really want to erase the contents of label \""; + question.append(QString::fromStdString(GetWorkingImage()->GetLabel(pixelValue)->GetName())); + question.append("\"?"); + + QMessageBox::StandardButton answerButton = QMessageBox::question( this, "Erase label", + question, QMessageBox::Yes | QMessageBox::Cancel, QMessageBox::Yes); + + if (answerButton == QMessageBox::Yes) + { + this->WaitCursorOn(); + GetWorkingImage()->EraseLabel(pixelValue); + this->WaitCursorOff(); + mitk::RenderingManager::GetInstance()->RequestUpdateAll(); + } +} + +void QmitkLabelSetWidget::OnRemoveLabel(bool /*value*/) +{ + int pixelValue = GetPixelValueOfSelectedItem(); + QString question = "Do you really want to remove label \""; + question.append(QString::fromStdString(GetWorkingImage()->GetLabel(pixelValue)->GetName())); + question.append("\"?"); + + QMessageBox::StandardButton answerButton = QMessageBox::question( this, "Remove label", + question, QMessageBox::Yes | QMessageBox::Cancel, QMessageBox::Yes); + + if (answerButton == QMessageBox::Yes) + { + this->WaitCursorOn(); + GetWorkingImage()->GetActiveLabelSet()->RemoveLabel(pixelValue); + GetWorkingImage()->EraseLabel(pixelValue); + this->WaitCursorOff(); + } + mitk::RenderingManager::GetInstance()->RequestUpdateAll(); +} + +void QmitkLabelSetWidget::OnRenameLabel(bool /*value*/) +{ + QmitkNewSegmentationDialog dialog(this); + dialog.setWindowTitle("Rename Label"); + dialog.SetSuggestionList( m_OrganColors ); + //MLI TODO + //dialog.SetColor(GetWorkingImage()->GetActiveLabel()->GetColor()); + //dialog.SetSegmentationName(GetWorkingImage()->GetActiveLabel()->GetName()); + + if ( dialog.exec() == QDialog::Rejected ) return; + int pixelValue = GetWorkingImage()->GetActiveLabel()->GetValue(); + + GetWorkingImage()->GetLabel(pixelValue)->SetColor(dialog.GetColor()); + GetWorkingImage()->GetLabel(pixelValue)->SetName(dialog.GetSegmentationName().toStdString()); + GetWorkingImage()->GetActiveLabelSet()->UpdateLookupTable(pixelValue); +} + +void QmitkLabelSetWidget::OnCombineAndCreateMask( bool /*value*/ ) +{ + m_Controls.m_LabelSetTableWidget->selectedRanges(); + // ...to do... // +} + +void QmitkLabelSetWidget::OnCreateMasks(bool /*value*/) +{ + m_Controls.m_LabelSetTableWidget->selectedRanges(); + // ..to do.. // +} + +void QmitkLabelSetWidget::OnCombineAndCreateSurface(bool /*value*/) +{ + m_Controls.m_LabelSetTableWidget->selectedRanges(); + // ..to do.. // +} + +void QmitkLabelSetWidget::OnEraseLabels(bool /*value*/) +{ + QString question = "Do you really want to erase the selected labels?"; + + QMessageBox::StandardButton answerButton = QMessageBox::question( this, "Erase selected labels", + question, QMessageBox::Yes | QMessageBox::Cancel, QMessageBox::Yes); + + if (answerButton == QMessageBox::Yes) + { + QList ranges = m_Controls.m_LabelSetTableWidget->selectedRanges(); + if ( ranges.isEmpty() ) + return; + + std::vector VectorOfLablePixelValues; + foreach (QTableWidgetSelectionRange a, ranges) + for(int i = a.topRow(); i <= a.bottomRow(); i++) + VectorOfLablePixelValues.push_back(m_Controls.m_LabelSetTableWidget->item(i,0)->data(Qt::UserRole).toInt()); + + this->WaitCursorOn(); + GetWorkingImage()->EraseLabels(VectorOfLablePixelValues); + this->WaitCursorOff(); + mitk::RenderingManager::GetInstance()->RequestUpdateAll(); + } +} + +void QmitkLabelSetWidget::OnRemoveLabels(bool /*value*/) +{ + QString question = "Do you really want to remove selected labels?"; + QMessageBox::StandardButton answerButton = QMessageBox::question( this, "Remove selected labels", + question, QMessageBox::Yes | QMessageBox::Cancel, QMessageBox::Yes); + + if (answerButton == QMessageBox::Yes) + { + QList ranges = m_Controls.m_LabelSetTableWidget->selectedRanges(); + if ( ranges.isEmpty() ) + return; + + std::vector VectorOfLablePixelValues; + foreach (QTableWidgetSelectionRange a, ranges) + for(int i = a.topRow(); i <= a.bottomRow(); i++) + VectorOfLablePixelValues.push_back(m_Controls.m_LabelSetTableWidget->item(i,0)->data(Qt::UserRole).toInt()); + + this->WaitCursorOn(); + GetWorkingImage()->RemoveLabels(VectorOfLablePixelValues); + this->WaitCursorOff(); + } + + mitk::RenderingManager::GetInstance()->RequestUpdateAll(); +} + +void QmitkLabelSetWidget::OnMergeLabels(bool /*value*/) +{ + int pixelValue = GetPixelValueOfSelectedItem(); + QString question = "Do you really want to merge selected labels into \""; + question.append(QString::fromStdString(GetWorkingImage()->GetLabel(pixelValue)->GetName())); + question.append("\"?"); + + QMessageBox::StandardButton answerButton = QMessageBox::question( this, "Merge selected label", + question, QMessageBox::Yes | QMessageBox::Cancel, QMessageBox::Yes); + + if (answerButton == QMessageBox::Yes) + { + QList ranges = m_Controls.m_LabelSetTableWidget->selectedRanges(); + if ( ranges.isEmpty() ) + return; + + std::vector VectorOfLablePixelValues; + foreach (QTableWidgetSelectionRange a, ranges) + for(int i = a.topRow(); i <= a.bottomRow(); i++) + VectorOfLablePixelValues.push_back(m_Controls.m_LabelSetTableWidget->item(i,0)->data(Qt::UserRole).toInt()); + + this->WaitCursorOn(); + int pixelValue = m_Controls.m_LabelSetTableWidget->item(m_Controls.m_LabelSetTableWidget->currentRow(),0)->data(Qt::UserRole).toInt(); + + GetWorkingImage()->MergeLabels(VectorOfLablePixelValues,pixelValue); + this->WaitCursorOff(); + + mitk::RenderingManager::GetInstance()->RequestUpdateAll(); + } +} + +void QmitkLabelSetWidget::OnLockedButtonClicked() +{ + int row; + for(int i=0; irowCount(); i++) + { + if (sender() == m_Controls.m_LabelSetTableWidget->cellWidget(i,LOCKED_COL)) + { + row = i; + } + } + if (row >= 0 && row < m_Controls.m_LabelSetTableWidget->rowCount()) + { + int pixelValue = m_Controls.m_LabelSetTableWidget->item(row,0)->data(Qt::UserRole).toInt(); + GetWorkingImage()->GetLabel(pixelValue)->SetLocked(!GetWorkingImage()->GetLabel(pixelValue)->GetLocked() ); + } +} + +void QmitkLabelSetWidget::OnVisibleButtonClicked() +{ + int row; + for(int i=0; irowCount(); i++) + { + if (sender() == m_Controls.m_LabelSetTableWidget->cellWidget(i,VISIBLE_COL)) + { + row = i; + break; + } + } + + if (row >= 0 && row < m_Controls.m_LabelSetTableWidget->rowCount()) + { + QTableWidgetItem * item = m_Controls.m_LabelSetTableWidget->item(row,0); + OnItemClicked(item); + int pixelValue = item->data(Qt::UserRole).toInt(); + GetWorkingImage()->GetLabel(pixelValue)->SetVisible(!GetWorkingImage()->GetLabel(pixelValue)->GetVisible() ); + GetWorkingImage()->GetActiveLabelSet()->UpdateLookupTable(pixelValue); + mitk::RenderingManager::GetInstance()->RequestUpdateAll(); + } +} + +void QmitkLabelSetWidget::OnColorButtonClicked() +{ + int row; + for(int i=0; irowCount(); i++) + { + if (sender() == m_Controls.m_LabelSetTableWidget->cellWidget(i,COLOR_COL)) + { + row = i; + } + } + + if (row >= 0 && row < m_Controls.m_LabelSetTableWidget->rowCount()) + { + int pixelValue = m_Controls.m_LabelSetTableWidget->item(row,0)->data(Qt::UserRole).toInt(); + const mitk::Color& color = GetWorkingImage()->GetLabel(pixelValue)->GetColor(); + QColor initial(color.GetRed()*255,color.GetGreen()*255,color.GetBlue()*255); + QColor qcolor = QColorDialog::getColor(initial,0,QString("Change color")); + if (!qcolor.isValid()) + return; + + QPushButton* button = (QPushButton*) m_Controls.m_LabelSetTableWidget->cellWidget(row,COLOR_COL); + if (!button) return; + + button->setAutoFillBackground(true); + + QString styleSheet = "background-color:rgb("; + styleSheet.append(QString::number(qcolor.red())); + styleSheet.append(","); + styleSheet.append(QString::number(qcolor.green())); + styleSheet.append(","); + styleSheet.append(QString::number(qcolor.blue())); + styleSheet.append(")"); + button->setStyleSheet(styleSheet); + + mitk::Color newColor; + newColor.SetRed(qcolor.red()/255.0); + newColor.SetGreen(qcolor.green()/255.0); + newColor.SetBlue(qcolor.blue()/255.0); + + GetWorkingImage()->GetLabel(pixelValue)->SetColor(newColor); + + GetWorkingImage()->GetActiveLabelSet()->UpdateLookupTable(pixelValue); + } +} + +void QmitkLabelSetWidget::OnRandomColor(bool /*value*/) +{ + int pixelValue = GetPixelValueOfSelectedItem(); + GetWorkingImage()->GetLabel(pixelValue)->SetColor(m_ColorSequenceRainbow.GetNextColor() ); + GetWorkingImage()->GetActiveLabelSet()->UpdateLookupTable(pixelValue); +} + +void QmitkLabelSetWidget::SetOrganColors(const QStringList& organColors) +{ + m_OrganColors = organColors; +} + +void QmitkLabelSetWidget::OnActiveLabelChanged(int pixelValue) +{ + mitk::LabelSetImage* workingImage = GetWorkingImage(); + assert(workingImage); + workingImage->GetActiveLabelSet()->SetActiveLabel(pixelValue); + //MITK_INFO << "Active Label set to << " << pixelValue; + + mitk::SurfaceBasedInterpolationController* interpolator = mitk::SurfaceBasedInterpolationController::GetInstance(); + if (interpolator) + interpolator->SetActiveLabel(pixelValue); +} + +void QmitkLabelSetWidget::OnItemClicked(QTableWidgetItem *item) +{ + if (!item) return; + + int pixelValue = item->data(Qt::UserRole).toInt(); + + QList ranges = m_Controls.m_LabelSetTableWidget->selectedRanges(); + if(!ranges.empty() && ranges.back().rowCount() == 1) + { + SelectLabelByPixelValue(pixelValue); + OnActiveLabelChanged(pixelValue); + mitk::RenderingManager::GetInstance()->RequestUpdateAll(); + } +} + +void QmitkLabelSetWidget::OnItemDoubleClicked(QTableWidgetItem *item) +{ + if (!item) return; + int pixelValue = item->data(Qt::UserRole).toInt(); + //OnItemClicked(item); <<-- Double click first call OnItemClicked + WaitCursorOn(); + mitk::LabelSetImage * workingImage = GetWorkingImage(); + workingImage->UpdateCenterOfMass(pixelValue); + const mitk::Point3D& pos = workingImage->GetLabel(pixelValue)->GetCenterOfMassCoordinates(); + WaitCursorOff(); + if (pos.GetVnlVector().max_value() > 0.0) emit goToLabel(pos); +} + +void QmitkLabelSetWidget::SelectLabelByPixelValue(mitk::Label::PixelType pixelValue) +{ + //MITK_INFO << "QmitkLabelSetWidget::SelectLabelByPixelValue " << pixelValue; + + if(!GetWorkingImage()->ExistLabel(pixelValue)) return; + for(int row = 0 ; row < m_Controls.m_LabelSetTableWidget->rowCount(); row++) + { + if(m_Controls.m_LabelSetTableWidget->item(row,0)->data(Qt::UserRole).toInt() == pixelValue) + { + m_Controls.m_LabelSetTableWidget->clearSelection(); + m_Controls.m_LabelSetTableWidget->setSelectionMode(QAbstractItemView::SingleSelection); + m_Controls.m_LabelSetTableWidget->selectRow(row); + m_Controls.m_LabelSetTableWidget->scrollToItem(m_Controls.m_LabelSetTableWidget->item(row,0)); + m_Controls.m_LabelSetTableWidget->setSelectionMode(QAbstractItemView::ExtendedSelection); + //SelectTableWidgetItem(m_Controls.m_LabelSetTableWidget->item(i,0)); + //emit resetView(); + //GetWorkingImage()->Modified(); + return; + } + } +} + +void QmitkLabelSetWidget::InsertTableWidgetItem(mitk::Label * label) +{ + const mitk::Color& color = label->GetColor(); + + QTableWidget * tableWidget = m_Controls.m_LabelSetTableWidget; + + QString styleSheet = "background-color:rgb("; + styleSheet.append(QString::number(color[0]*255)); + styleSheet.append(","); + styleSheet.append(QString::number(color[1]*255)); + styleSheet.append(","); + styleSheet.append(QString::number(color[2]*255)); + styleSheet.append(")"); + + int colWidth = (tableWidget->columnWidth(NAME_COL) < 180) ? 180 : tableWidget->columnWidth(NAME_COL)-2; + QString text = fontMetrics().elidedText(label->GetName().c_str(), Qt::ElideMiddle, colWidth); + QTableWidgetItem *nameItem = new QTableWidgetItem(text); + nameItem->setTextAlignment(Qt::AlignCenter | Qt::AlignLeft); + // ---!--- + // IMPORTANT: ADD PIXELVALUE TO TABLEWIDGETITEM.DATA + nameItem->setData(Qt::UserRole,QVariant(label->GetValue())); + // ---!--- + + QPushButton * pbColor = new QPushButton(tableWidget); + pbColor->setFixedSize(24,24); + pbColor->setCheckable(false); + pbColor->setAutoFillBackground(true); + pbColor->setToolTip("Change label color"); + pbColor->setStyleSheet(styleSheet); + + connect( pbColor, SIGNAL(clicked()), this, SLOT(OnColorButtonClicked()) ); + + QPushButton * pbLocked = new QPushButton(tableWidget); + pbLocked->setFixedSize(24,24); + QIcon * iconLocked = new QIcon(); + iconLocked->addFile(QString::fromUtf8(":/Qmitk/lock.png"), QSize(), QIcon::Normal, QIcon::Off); + iconLocked->addFile(QString::fromUtf8(":/Qmitk/unlock.png"), QSize(), QIcon::Normal, QIcon::On); + pbLocked->setIcon(*iconLocked); + pbLocked->setIconSize(QSize(24,24)); + pbLocked->setCheckable(true); + pbLocked->setToolTip("Lock/unlock label"); + pbLocked->setChecked(!label->GetLocked()); + connect( pbLocked, SIGNAL(clicked()), this, SLOT(OnLockedButtonClicked()) ); + + QPushButton * pbVisible = new QPushButton(tableWidget); + pbVisible->setFixedSize(24,24); + pbVisible->setAutoRepeat(false); + QIcon * iconVisible = new QIcon(); + iconVisible->addFile(QString::fromUtf8(":/Qmitk/visible.png"), QSize(), QIcon::Normal, QIcon::Off); + iconVisible->addFile(QString::fromUtf8(":/Qmitk/invisible.png"), QSize(), QIcon::Normal, QIcon::On); + pbVisible->setIcon(*iconVisible); + pbVisible->setIconSize(QSize(24,24)); + pbVisible->setCheckable(true); + pbVisible->setToolTip("Show/hide label"); + pbVisible->setChecked(!label->GetVisible()); + + connect( pbVisible, SIGNAL(clicked()), this, SLOT(OnVisibleButtonClicked()) ); + + int row = tableWidget->rowCount(); + tableWidget->insertRow(row); + tableWidget->setRowHeight(row,24); + tableWidget->setItem(row, 0, nameItem ); + tableWidget->setCellWidget(row, 1, pbLocked); + tableWidget->setCellWidget(row, 2, pbColor); + tableWidget->setCellWidget(row, 3, pbVisible); + tableWidget->selectRow(row); + + //m_LabelSetImage->SetActiveLabel(label->GetPixelValue()); + //m_ToolManager->WorkingDataModified.Send(); + //emit activeLabelChanged(label->GetPixelValue()); + + if (row == 0) + tableWidget->hideRow(row); // hide exterior label + + mitk::LabelSetImage * workingImage; + if((workingImage = GetWorkingImage()) == NULL) return; +} + +void QmitkLabelSetWidget::UpdateAllTableWidgetItems() +{ + QTableWidget * tableWidget = m_Controls.m_LabelSetTableWidget; + + mitk::LabelSetImage * workingImage = GetWorkingImage(); + if(!workingImage) return; + + // add all labels + m_LabelStringList.clear(); + for(int i = 0 ; i < tableWidget->rowCount(); i++) + { + UpdateTableWidgetItem(tableWidget->item(i,0)); + m_LabelStringList.append( tableWidget->item(i,0)->text() ); + } + + OnLabelListModified(m_LabelStringList); + + mitk::RenderingManager::GetInstance()->RequestUpdateAll(); +} + +void QmitkLabelSetWidget::UpdateTableWidgetItem(QTableWidgetItem *item) +{ + mitk::LabelSetImage * workingImage = GetWorkingImage(); + mitk::Label * label = workingImage->GetLabel(item->data(Qt::UserRole).toInt()); + + const mitk::Color& color = label->GetColor(); + + QTableWidget * tableWidget = m_Controls.m_LabelSetTableWidget; + + QString styleSheet = "background-color:rgb("; + styleSheet.append(QString::number(color[0]*255)); + styleSheet.append(","); + styleSheet.append(QString::number(color[1]*255)); + styleSheet.append(","); + styleSheet.append(QString::number(color[2]*255)); + styleSheet.append(")"); + + // Update text Label tableWdget->item(row,0) + int colWidth = (tableWidget->columnWidth(NAME_COL) < 180) ? 180 : tableWidget->columnWidth(NAME_COL)-2; + QString text = fontMetrics().elidedText(label->GetName().c_str(), Qt::ElideMiddle, colWidth); + item->setText(text); + + QPushButton * pbLocked = dynamic_cast(tableWidget->cellWidget(item->row(),1)); + pbLocked->setChecked(!label->GetLocked()); + + QPushButton * pbColor = dynamic_cast(tableWidget->cellWidget(item->row(),2)); + pbColor->setStyleSheet(styleSheet); + + QPushButton * pbVisible = dynamic_cast(tableWidget->cellWidget(item->row(),3)); + pbVisible->setChecked(!label->GetVisible()); + + if (item->row() == 0)tableWidget->hideRow(item->row()); // hide exterior label +} + + +void QmitkLabelSetWidget::ResetAllTableWidgetItems() +{ + QTableWidget * tableWidget = m_Controls.m_LabelSetTableWidget; + // remove all rows + while (tableWidget->rowCount()) + tableWidget->removeRow( 0 ); + + mitk::LabelSetImage * workingImage = GetWorkingImage(); + if(!workingImage) return; + + // add all labels + m_LabelStringList.clear(); + + mitk::LabelSet::LabelContainerConstIteratorType it = workingImage->GetActiveLabelSet()->IteratorConstBegin(); + mitk::LabelSet::LabelContainerConstIteratorType end = workingImage->GetActiveLabelSet()->IteratorConstEnd(); + + int pixelValue =-1; + while (it != end) + { + InsertTableWidgetItem(it->second); + if(GetWorkingImage()->GetActiveLabel() == it->second) // get active + pixelValue = it->first; + m_LabelStringList.append( QString(it->second->GetName().c_str()) ); + it++; + } + + SelectLabelByPixelValue(pixelValue); + + OnLabelListModified(m_LabelStringList); + + std::stringstream captionText; + captionText << "Number of labels: " << workingImage->GetNumberOfLabels() - 1; + m_Controls.m_lblCaption->setText(captionText.str().c_str()); + + mitk::RenderingManager::GetInstance()->RequestUpdateAll(); +} + +int QmitkLabelSetWidget::GetPixelValueOfSelectedItem() +{ + if(m_Controls.m_LabelSetTableWidget->currentItem()) + return m_Controls.m_LabelSetTableWidget->currentItem()->data(Qt::UserRole).toInt(); + return -1; +} + +QStringList & QmitkLabelSetWidget::GetLabelStringList() +{ + return m_LabelStringList; +} + +void QmitkLabelSetWidget::InitializeTableWidget() +{ + QTableWidget * tableWidged = m_Controls.m_LabelSetTableWidget; + + tableWidged->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Maximum); + tableWidged->setTabKeyNavigation(false); + tableWidged->setAlternatingRowColors(false); + tableWidged->setFocusPolicy(Qt::NoFocus); + tableWidged->setColumnCount(4); + tableWidged->resizeColumnToContents(NAME_COL); + tableWidged->setColumnWidth(LOCKED_COL,25); + tableWidged->setColumnWidth(COLOR_COL,25); + tableWidged->setColumnWidth(VISIBLE_COL,25); +#if QT_VERSION >= QT_VERSION_CHECK(5,0,0) + tableWidged->horizontalHeader()->setSectionResizeMode(0, QHeaderView::Stretch); +#else + tableWidged->horizontalHeader()->setResizeMode(0, QHeaderView::Stretch); +#endif + tableWidged->setContextMenuPolicy(Qt::CustomContextMenu); + tableWidged->horizontalHeader()->hide(); + tableWidged->setSortingEnabled ( false ); + tableWidged->verticalHeader()->hide(); + tableWidged->setEditTriggers(QAbstractItemView::NoEditTriggers); + tableWidged->setSelectionMode(QAbstractItemView::ExtendedSelection); + tableWidged->setSelectionBehavior(QAbstractItemView::SelectRows); + + connect(tableWidged, SIGNAL(itemClicked(QTableWidgetItem *)), this, SLOT(OnItemClicked(QTableWidgetItem *))); + connect(tableWidged, SIGNAL(itemDoubleClicked(QTableWidgetItem *)), this, SLOT(OnItemDoubleClicked(QTableWidgetItem *))); + connect(tableWidged, SIGNAL(customContextMenuRequested(const QPoint&)), this, SLOT(OnTableViewContextMenuRequested(const QPoint&)) ); + + //connect( m_Controls.m_LabelSetTableWidget, SIGNAL(activeLabelChanged(int)), this, SLOT(OnActiveLabelChanged(int)) ); + //connect( m_Controls.m_LabelSetTableWidget, SIGNAL(importSegmentation()), this, SLOT( OnImportSegmentation()) ); + //connect( m_Controls.m_LabelSetTableWidget, SIGNAL(importLabeledImage()), this, SLOT( OnImportLabeledImage()) ); + //connect( m_Controls.m_LabelSetTableWidget, SIGNAL(renameLabel(int, const mitk::Color&, const std::string&)), this, SLOT(OnRenameLabel(int, const mitk::Color&, const std::string&)) ); + //connect( m_Controls.m_LabelSetTableWidget, SIGNAL(createSurface(int, bool)), this, SLOT(OnCreateSurface(int, bool)) ); + //connect( m_Controls.m_LabelSetTableWidget, SIGNAL(toggleOutline(bool)), this, SLOT(OnToggleOutline(bool)) ); + //connect( m_Controls.m_LabelSetTableWidget, SIGNAL(goToLabel(const mitk::Point3D&)), this, SIGNAL(goToLabel(const mitk::Point3D&)) ); + //connect( m_Controls.m_LabelSetTableWidget, SIGNAL(combineAndCreateSurface( const QList& )), + // this, SLOT(OnCombineAndCreateSurface( const QList&)) ); + + //connect( m_Controls.m_LabelSetTableWidget, SIGNAL(createMask(int)), this, SLOT(OnCreateMask(int)) ); + //connect( m_Controls.m_LabelSetTableWidget, SIGNAL(createCroppedMask(int)), this, SLOT(OnCreateCroppedMask(int)) ); + //connect( m_Controls.m_LabelSetTableWidget, SIGNAL(combineAndCreateMask( const QList& )), + // this, SLOT(OnCombineAndCreateMask( const QList&)) ); +} + +void QmitkLabelSetWidget::OnOpacityChanged(int value) +{ + int pixelValue = m_Controls.m_LabelSetTableWidget->currentItem()->data(Qt::UserRole).toInt(); + float opacity = static_cast(value)/100.0f; + GetWorkingImage()->GetLabel(pixelValue)->SetOpacity(opacity); + GetWorkingImage()->GetActiveLabelSet()->UpdateLookupTable(pixelValue); +} + +void QmitkLabelSetWidget::setEnabled(bool enabled) +{ + QWidget::setEnabled(enabled); + UpdateControls(); +} + +void QmitkLabelSetWidget::SetDataStorage( mitk::DataStorage* storage ) +{ + m_DataStorage = storage; +} + +void QmitkLabelSetWidget::OnSearchLabel() +{ + std::string text = m_Controls.m_LabelSearchBox->text().toStdString(); + int pixelValue = -1; + int row = -1; + for(int i = 0; i < m_Controls.m_LabelSetTableWidget->rowCount(); i++){ + if( m_Controls.m_LabelSetTableWidget->item(i,0)->text().toStdString().compare(text) == 0) + { + pixelValue = m_Controls.m_LabelSetTableWidget->item(i,0)->data(Qt::UserRole).toInt(); + row = i; + break; + } + } + if(pixelValue == -1) return; + GetWorkingImage()->GetActiveLabelSet()->SetActiveLabel(pixelValue); + + QTableWidgetItem* nameItem = m_Controls.m_LabelSetTableWidget->item(row,NAME_COL); + if (!nameItem) return; + + m_Controls.m_LabelSetTableWidget->clearSelection(); + m_Controls.m_LabelSetTableWidget->setSelectionMode(QAbstractItemView::SingleSelection); + m_Controls.m_LabelSetTableWidget->selectRow(row); + m_Controls.m_LabelSetTableWidget->scrollToItem(nameItem); + m_Controls.m_LabelSetTableWidget->setSelectionMode(QAbstractItemView::ExtendedSelection); + + this->WaitCursorOn(); + mitk::Point3D pos = GetWorkingImage()->GetLabel(pixelValue)->GetCenterOfMassCoordinates(); + + m_ToolManager->WorkingDataChanged(); + + if (pos.GetVnlVector().max_value() > 0.0) + emit goToLabel(pos); + else + { + GetWorkingImage()->UpdateCenterOfMass(pixelValue); + mitk::Point3D pos = GetWorkingImage()->GetLabel(pixelValue)->GetCenterOfMassCoordinates(); + emit goToLabel(pos); + } + + this->WaitCursorOff(); +} + +void QmitkLabelSetWidget::OnLabelListModified(const QStringList& list) +{ + QStringListModel* completeModel = static_cast (m_Completer->model()); + completeModel->setStringList(list); +} + +mitk::LabelSetImage * QmitkLabelSetWidget::GetWorkingImage() +{ + mitk::DataNode* workingNode = GetWorkingNode(); + mitk::LabelSetImage* workingImage = dynamic_cast(workingNode->GetData()); + assert(workingImage); + return workingImage; +} + +mitk::DataNode * QmitkLabelSetWidget::GetWorkingNode() +{ + mitk::DataNode* workingNode = m_ToolManager->GetWorkingData(0); + assert(workingNode); + return workingNode; +} + + +void QmitkLabelSetWidget::UpdateControls() +{ + mitk::DataNode* workingNode = m_ToolManager->GetWorkingData(0); + bool hasWorkingData = (workingNode != NULL); + + m_Controls.m_LabelSetTableWidget->setEnabled(hasWorkingData); + m_Controls.m_LabelSearchBox->setEnabled(hasWorkingData); + + if (!hasWorkingData) return; + + QStringListModel* completeModel = static_cast (m_Completer->model()); + completeModel->setStringList(GetLabelStringList()); +} + + + +void QmitkLabelSetWidget::OnCreateCroppedMask(bool) +{ + m_ToolManager->ActivateTool(-1); + + mitk::DataNode* workingNode = GetWorkingNode(); + mitk::LabelSetImage* workingImage = GetWorkingImage(); + + mitk::Image::Pointer maskImage; + + int pixelValue = GetPixelValueOfSelectedItem(); + try + { + this->WaitCursorOn(); + + mitk::AutoCropImageFilter::Pointer cropFilter = mitk::AutoCropImageFilter::New(); + cropFilter->SetInput( workingImage->CreateLabelMask(pixelValue) ); + cropFilter->SetBackgroundValue( 0 ); + cropFilter->SetMarginFactor(1.15); + cropFilter->Update(); + + maskImage = cropFilter->GetOutput(); + + this->WaitCursorOff(); + } + catch ( mitk::Exception& e ) + { + this->WaitCursorOff(); + MITK_ERROR << "Exception caught: " << e.GetDescription(); + QMessageBox::information(this, "Create Mask", "Could not create a mask out of the selected label.\n"); + return; + } + + if (maskImage.IsNull()) + { + QMessageBox::information(this, "Create Mask", "Could not create a mask out of the selected label.\n"); + return; + } + + mitk::DataNode::Pointer maskNode = mitk::DataNode::New(); + std::string name = workingImage->GetLabel(pixelValue, workingImage->GetActiveLayer())->GetName(); + name += "-mask"; + maskNode->SetName(name); + maskNode->SetData(maskImage); + maskNode->SetBoolProperty("binary", true); + maskNode->SetBoolProperty("outline binary", true); + maskNode->SetBoolProperty("outline binary shadow", true); + maskNode->SetFloatProperty("outline width", 2.0); + maskNode->SetColor(workingImage->GetLabel(pixelValue)->GetColor()); + maskNode->SetOpacity(1.0); + + m_DataStorage->Add(maskNode, workingNode); +} + +void QmitkLabelSetWidget::OnCreateMask(bool /*triggered*/) +{ + m_ToolManager->ActivateTool(-1); + + mitk::DataNode* workingNode = GetWorkingNode(); + mitk::LabelSetImage* workingImage = GetWorkingImage(); + + mitk::Image::Pointer maskImage; + + int pixelValue = GetPixelValueOfSelectedItem(); + + try + { + this->WaitCursorOn(); + maskImage = workingImage->CreateLabelMask(pixelValue); + this->WaitCursorOff(); + } + catch ( mitk::Exception& e ) + { + this->WaitCursorOff(); + MITK_ERROR << "Exception caught: " << e.GetDescription(); + QMessageBox::information(this, "Create Mask", "Could not create a mask out of the selected label.\n"); + return; + } + + if (maskImage.IsNull()) + { + QMessageBox::information(this, "Create Mask", "Could not create a mask out of the selected label.\n"); + return; + } + + mitk::DataNode::Pointer maskNode = mitk::DataNode::New(); + std::string name = workingImage->GetLabel(pixelValue, workingImage->GetActiveLayer())->GetName(); + name += "-mask"; + maskNode->SetName(name); + maskNode->SetData(maskImage); + maskNode->SetBoolProperty("binary", true); + maskNode->SetBoolProperty("outline binary", true); + maskNode->SetBoolProperty("outline binary shadow", true); + maskNode->SetFloatProperty("outline width", 2.0); + maskNode->SetColor(workingImage->GetLabel(pixelValue)->GetColor()); + maskNode->SetOpacity(1.0); + + m_DataStorage->Add(maskNode, workingNode); +} + +void QmitkLabelSetWidget::OnToggleOutline(bool value) +{ + mitk::DataNode* workingNode = m_ToolManager->GetWorkingData(0); + assert(workingNode); + + workingNode->SetBoolProperty( "labelset.contour.all", value); + workingNode->GetData()->Modified(); // fixme: workaround to force data-type rendering (and not only property-type) + mitk::RenderingManager::GetInstance()->RequestUpdateAll(); +} + +void QmitkLabelSetWidget::OnCreateSmoothedSurface(bool /*triggered*/) +{ + m_ToolManager->ActivateTool(-1); + + mitk::DataNode::Pointer workingNode = GetWorkingNode(); + mitk::LabelSetImage* workingImage = GetWorkingImage(); + int pixelValue = GetPixelValueOfSelectedItem(); + + mitk::LabelSetImageToSurfaceThreadedFilter::Pointer surfaceFilter = mitk::LabelSetImageToSurfaceThreadedFilter::New(); + + itk::SimpleMemberCommand::Pointer successCommand = itk::SimpleMemberCommand::New(); + successCommand->SetCallbackFunction(this, &QmitkLabelSetWidget::OnThreadedCalculationDone); + surfaceFilter->AddObserver(mitk::ResultAvailable(), successCommand); + + itk::SimpleMemberCommand::Pointer errorCommand = itk::SimpleMemberCommand::New(); + errorCommand->SetCallbackFunction(this, &QmitkLabelSetWidget::OnThreadedCalculationDone); + surfaceFilter->AddObserver(mitk::ProcessingError(), errorCommand); + + mitk::DataNode::Pointer groupNode = workingNode; + surfaceFilter->SetPointerParameter("Group node", groupNode); + surfaceFilter->SetPointerParameter("Input", workingImage); + surfaceFilter->SetParameter("RequestedLabel", pixelValue); + surfaceFilter->SetParameter("Smooth", true); + surfaceFilter->SetDataStorage( *m_DataStorage ); + + mitk::StatusBar::GetInstance()->DisplayText("Surface creation is running in background..."); + + try + { + surfaceFilter->StartAlgorithm(); + } + catch ( mitk::Exception & e ) + { + MITK_ERROR << "Exception caught: " << e.GetDescription(); + QMessageBox::information(this, "Create Surface", "Could not create a surface mesh out of the selected label. See error log for details.\n"); + } +} + +void QmitkLabelSetWidget::OnCreateDetailedSurface(bool /*triggered*/) +{ + m_ToolManager->ActivateTool(-1); + + mitk::DataNode::Pointer workingNode = GetWorkingNode(); + mitk::LabelSetImage* workingImage = GetWorkingImage(); + int pixelValue = GetPixelValueOfSelectedItem(); + + mitk::LabelSetImageToSurfaceThreadedFilter::Pointer surfaceFilter = mitk::LabelSetImageToSurfaceThreadedFilter::New(); + + itk::SimpleMemberCommand::Pointer successCommand = itk::SimpleMemberCommand::New(); + successCommand->SetCallbackFunction(this, &QmitkLabelSetWidget::OnThreadedCalculationDone); + surfaceFilter->AddObserver(mitk::ResultAvailable(), successCommand); + + itk::SimpleMemberCommand::Pointer errorCommand = itk::SimpleMemberCommand::New(); + errorCommand->SetCallbackFunction(this, &QmitkLabelSetWidget::OnThreadedCalculationDone); + surfaceFilter->AddObserver(mitk::ProcessingError(), errorCommand); + + mitk::DataNode::Pointer groupNode = workingNode; + surfaceFilter->SetPointerParameter("Group node", groupNode); + surfaceFilter->SetPointerParameter("Input", workingImage); + surfaceFilter->SetParameter("RequestedLabel", pixelValue); + surfaceFilter->SetParameter("Smooth", false); + surfaceFilter->SetDataStorage( *m_DataStorage ); + + mitk::StatusBar::GetInstance()->DisplayText("Surface creation is running in background..."); + + try + { + surfaceFilter->StartAlgorithm(); + } + catch ( mitk::Exception & e ) + { + MITK_ERROR << "Exception caught: " << e.GetDescription(); + QMessageBox::information(this, "Create Surface", "Could not create a surface mesh out of the selected label. See error log for details.\n"); + } +} + +void QmitkLabelSetWidget::OnImportLabeledImage() +{ +/* + m_ToolManager->ActivateTool(-1); + + mitk::DataNode* referenceNode = m_ToolManager->GetReferenceData(0); + assert(referenceNode); + + // Ask the user for a list of files to open + QStringList fileNames = QFileDialog::getOpenFileNames( this, "Open Image", m_LastFileOpenPath, + mitk::CoreObjectFactory::GetInstance()->GetFileExtensions()); + + if (fileNames.empty()) + return; + + try + { + this->WaitCursorOn(); + mitk::Image::Pointer image = mitk::IOUtil::LoadImage( fileNames.front().toStdString() ); + if (image.IsNull()) + { + this->WaitCursorOff(); + QMessageBox::information(this, "Import Labeled Image", "Could not load the selected segmentation.\n"); + return; + } + + mitk::LabelSetImage::Pointer newImage = mitk::LabelSetImage::New(); + newImage->InitializeByLabeledImage(image); + this->WaitCursorOff(); + + mitk::DataNode::Pointer newNode = mitk::DataNode::New(); + std::string newName = referenceNode->GetName(); + newName += "-labels"; + newNode->SetName(newName); + newNode->SetData(newImage); + m_DataStorage->Add(newNode, referenceNode); + } + catch (mitk::Exception & e) + { + this->WaitCursorOff(); + MITK_ERROR << "Exception caught: " << e.GetDescription(); + QMessageBox::information(this, "Import Labeled Image", "Could not load the selected segmentation. See error log for details.\n"); + return; + } + + this->UpdateControls(); + + mitk::RenderingManager::GetInstance()->RequestUpdateAll(); + */ +} + +void QmitkLabelSetWidget::OnImportSegmentation() +{ +/* + m_ToolManager->ActivateTool(-1); + + mitk::DataNode* workingNode = m_ToolManager->GetWorkingData(0); + assert(workingNode); + + mitk::LabelSetImage* workingImage = dynamic_cast( workingNode->GetData() ); + assert(workingImage); + + std::string fileExtensions("Segmentation files (*.lset);;"); + QString qfileName = QFileDialog::getOpenFileName(this, "Import Segmentation", m_LastFileOpenPath, fileExtensions.c_str() ); + if (qfileName.isEmpty() ) return; + + mitk::NrrdLabelSetImageReader::Pointer reader = mitk::NrrdLabelSetImageReader::New(); + reader->SetFileName(qfileName.toLatin1()); + + try + { + this->WaitCursorOn(); + reader->Update(); + mitk::LabelSetImage::Pointer newImage = reader->GetOutput(); + workingImage->Concatenate(newImage); + this->WaitCursorOff(); + } + catch ( mitk::Exception& e ) + { + this->WaitCursorOff(); + MITK_ERROR << "Exception caught: " << e.GetDescription(); + QMessageBox::information(this, "Import Segmentation", "Could not import the selected segmentation session.\n See error log for details.\n"); + } +*/ + mitk::RenderingManager::GetInstance()->RequestUpdateAll(); +} + +void QmitkLabelSetWidget::WaitCursorOn() +{ + QApplication::setOverrideCursor( QCursor(Qt::WaitCursor) ); +} + +void QmitkLabelSetWidget::WaitCursorOff() +{ + this->RestoreOverrideCursor(); +} + +void QmitkLabelSetWidget::RestoreOverrideCursor() +{ + QApplication::restoreOverrideCursor(); +} + +void QmitkLabelSetWidget::OnThreadedCalculationDone() +{ + mitk::StatusBar::GetInstance()->Clear(); +} diff --git a/Modules/SegmentationUI/Qmitk/QmitkLabelSetWidget.h b/Modules/SegmentationUI/Qmitk/QmitkLabelSetWidget.h new file mode 100644 index 0000000000..56798630be --- /dev/null +++ b/Modules/SegmentationUI/Qmitk/QmitkLabelSetWidget.h @@ -0,0 +1,158 @@ +/*=================================================================== + +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 QmitkLabelSetWidget_h +#define QmitkLabelSetWidget_h + +#include "MitkSegmentationUIExports.h" + +#include +#include "mitkColorSequenceRainbow.h" +#include "mitkNumericTypes.h" +#include "mitkLabel.h" + + +class QmitkDataStorageComboBox; +class QCompleter; + +namespace mitk { + class LabelSetImage; + class LabelSet; + class Label; + class DataStorage; + class ToolManager; + class DataNode; +} + +class MITKSEGMENTATIONUI_EXPORT QmitkLabelSetWidget : public QWidget +{ + Q_OBJECT + +public: + + explicit QmitkLabelSetWidget(QWidget* parent = NULL); + ~QmitkLabelSetWidget(); + + void SetDataStorage( mitk::DataStorage* storage ); + + void SetOrganColors(const QStringList& organColors); + + void UpdateControls(); + + virtual void setEnabled(bool enabled); + + QStringList &GetLabelStringList(); + + +signals: + + /// \brief Send a signal when it was requested to go to a label. + void goToLabel(const mitk::Point3D&); + void resetView(); + +public slots: + + void ResetAllTableWidgetItems(); + void UpdateAllTableWidgetItems(); + void SelectLabelByPixelValue(mitk::Label::PixelType pixelValue); + +private slots: + + // Label Set Dependend + void OnOpacityChanged(int); + void OnUnlockAllLabels(bool); + void OnLockAllLabels(bool); + void OnSetAllLabelsVisible(bool); + void OnSetAllLabelsInvisible(bool); + void OnSetOnlyActiveLabelVisible(bool); + void OnRandomColor(bool); + void OnRemoveLabel(bool); + void OnRemoveLabels(bool); + void OnRenameLabel(bool); + void OnLockedButtonClicked(); + void OnVisibleButtonClicked(); + void OnColorButtonClicked(); + void OnItemClicked(QTableWidgetItem *item); + void OnItemDoubleClicked(QTableWidgetItem *item); + void OnTableViewContextMenuRequested(const QPoint&); + void InsertTableWidgetItem(mitk::Label * label); + void UpdateTableWidgetItem(QTableWidgetItem *item); + // reaction to "returnPressed" signal from ... + void OnSearchLabel(); + // reaction to the button "Change Label" + void OnActiveLabelChanged(int pixelValue); + + //LabelSetImage Dependet + void OnCreateDetailedSurface(bool); + void OnCreateSmoothedSurface(bool); + // reaction to the signal "createMask" from QmitkLabelSetTableWidget + void OnCreateMask(bool); + void OnCreateMasks(bool); + // reaction to the signal "createCroppedMask" from QmitkLabelSetTableWidget + void OnCreateCroppedMask(bool); + void OnCombineAndCreateMask(bool); + void OnCombineAndCreateSurface(bool); + void OnEraseLabel(bool); + void OnEraseLabels(bool); + // reaction to signal "mergeLabel" from QmitkLabelSetTableWidget + void OnMergeLabel(bool); + void OnMergeLabels(bool); + // reaction to the button "Import Segmentation" + void OnImportSegmentation(); + // reaction to the button "Import Labeled Image" + void OnImportLabeledImage(); + + // reaction to signal "labelListModified" from QmitkLabelSetTableWidget + void OnLabelListModified(const QStringList& list); + // reaction to the signal "toggleOutline" from QmitkLabelSetTableWidget + void OnToggleOutline(bool); + +private: + + enum TableColumns { NAME_COL=0, LOCKED_COL, COLOR_COL, VISIBLE_COL }; + + void WaitCursorOn(); + + void WaitCursorOff(); + + void RestoreOverrideCursor(); + + void OnThreadedCalculationDone(); + + void InitializeTableWidget(); + + int GetPixelValueOfSelectedItem(); + + mitk::LabelSetImage * GetWorkingImage(); + + mitk::DataNode * GetWorkingNode(); + + Ui::QmitkLabelSetWidgetControls m_Controls; + + mitk::ColorSequenceRainbow m_ColorSequenceRainbow; + + QCompleter* m_Completer; + + mitk::DataStorage* m_DataStorage; + + mitk::ToolManager* m_ToolManager; + + QStringList m_OrganColors; + + QStringList m_LabelStringList; +}; + +#endif diff --git a/Modules/SegmentationUI/Qmitk/QmitkLabelSetWidgetControls.ui b/Modules/SegmentationUI/Qmitk/QmitkLabelSetWidgetControls.ui new file mode 100644 index 0000000000..a9b4c069a3 --- /dev/null +++ b/Modules/SegmentationUI/Qmitk/QmitkLabelSetWidgetControls.ui @@ -0,0 +1,66 @@ + + + QmitkLabelSetWidgetControls + + + + 0 + 0 + 312 + 366 + + + + + 0 + 0 + + + + + + + + 6 + + + 6 + + + 6 + + + 6 + + + + + Caption + + + + + + + + + + + 0 + 0 + + + + + + + + + ctkSearchBox + QLineEdit +
ctkSearchBox.h
+
+
+ + +
diff --git a/Modules/SegmentationUI/Qmitk/QmitkMaskStampWidget.cpp b/Modules/SegmentationUI/Qmitk/QmitkMaskStampWidget.cpp new file mode 100644 index 0000000000..76a37c3786 --- /dev/null +++ b/Modules/SegmentationUI/Qmitk/QmitkMaskStampWidget.cpp @@ -0,0 +1,121 @@ +/*=================================================================== + +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 "QmitkMaskStampWidget.h" + +#include +#include +#include + +#include + +QmitkMaskStampWidget::QmitkMaskStampWidget(QWidget* parent, const char* /*name*/) : +QWidget(parent), +m_ToolManager(NULL), +m_DataStorage(NULL) +{ + m_Controls.setupUi(this); + m_Controls.m_InformationWidget->hide(); + + m_ToolManager = mitk::ToolManagerProvider::GetInstance()->GetToolManager(); + assert(m_ToolManager); + m_ToolManager->ActivateTool(-1); + + mitk::TNodePredicateDataType::Pointer isImage = mitk::TNodePredicateDataType::New(); + mitk::NodePredicateProperty::Pointer isBinary = mitk::NodePredicateProperty::New("binary", mitk::BoolProperty::New(true)); + mitk::NodePredicateAnd::Pointer isMask = mitk::NodePredicateAnd::New(isBinary, isImage); + + mitk::NodePredicateAnd::Pointer maskPredicate = mitk::NodePredicateAnd::New(); + maskPredicate->AddPredicate(isMask); + maskPredicate->AddPredicate(mitk::NodePredicateNot::New(mitk::NodePredicateProperty::New("helper object"))); + + m_Controls.m_cbMaskNodeSelector->SetPredicate( maskPredicate ); + + connect(m_Controls.m_pbStamp, SIGNAL(clicked()), this, SLOT(OnStamp())); + connect( m_Controls.m_cbShowInformation, SIGNAL(toggled(bool)), this, SLOT(OnShowInformation(bool)) ); + m_Controls.m_InformationWidget->hide(); +} + +QmitkMaskStampWidget::~QmitkMaskStampWidget() +{ +} + +void QmitkMaskStampWidget::SetDataStorage( mitk::DataStorage* storage ) +{ + m_DataStorage = storage; + m_Controls.m_cbMaskNodeSelector->SetDataStorage(m_DataStorage); +} + +void QmitkMaskStampWidget::OnStamp() +{ + mitk::DataNode* maskNode = m_Controls.m_cbMaskNodeSelector->GetSelectedNode(); + + if (!maskNode) + { + QMessageBox::information( this, "Mask Stamp", "Please load and select a mask before starting some action."); + return; + } + + mitk::Image* mask = dynamic_cast(maskNode->GetData() ); + if (!mask) + { + QMessageBox::information( this, "Mask Stamp", "Please load and select a mask before starting some action."); + return; + } + + mitk::DataNode* workingNode = m_ToolManager->GetWorkingData(0); + + if (!workingNode) + { + QMessageBox::information( this, "Mask Stamp", "Please load and select a segmentation before starting some action."); + return; + } + + mitk::LabelSetImage* workingImage = dynamic_cast( workingNode->GetData() ); + + if (!workingImage) + { + QMessageBox::information( this, "Mask Stamp", "Please load and select a segmentation before starting some action."); + return; + } + + QApplication::setOverrideCursor( QCursor(Qt::BusyCursor) ); + try + { + workingImage->MaskStamp( mask, m_Controls.m_chkOverwrite->isChecked() ); + } + catch ( mitk::Exception & e ) + { + QApplication::restoreOverrideCursor(); + MITK_ERROR << "Exception caught: " << e.GetDescription(); + QMessageBox::information( this, "Mask Stamp", "Could not stamp the selected mask.\n See error log for details.\n"); + return; + } + + QApplication::restoreOverrideCursor(); + + maskNode->SetVisibility(false); + + mitk::RenderingManager::GetInstance()->RequestUpdateAll(); +} + +void QmitkMaskStampWidget::OnShowInformation(bool on) +{ + if (on) + m_Controls.m_InformationWidget->show(); + else + m_Controls.m_InformationWidget->hide(); +} diff --git a/Modules/SegmentationUI/Qmitk/QmitkMaskStampWidget.h b/Modules/SegmentationUI/Qmitk/QmitkMaskStampWidget.h new file mode 100644 index 0000000000..67105bd751 --- /dev/null +++ b/Modules/SegmentationUI/Qmitk/QmitkMaskStampWidget.h @@ -0,0 +1,65 @@ +/*=================================================================== + +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 QmitkMaskStampWidget_h_Included +#define QmitkMaskStampWidget_h_Included + +#include "MitkSegmentationUIExports.h" +#include "mitkDataNode.h" + +#include + +#include "ui_QmitkMaskStampWidgetGUIControls.h" + +namespace mitk { + class ToolManager; +} + +/** + \brief GUI for mask stamp functionality + + \ingroup ToolManagerEtAl + \ingroup Widgets +*/ + +class MITKSEGMENTATIONUI_EXPORT QmitkMaskStampWidget : public QWidget +{ + Q_OBJECT + + public: + + QmitkMaskStampWidget(QWidget* parent = 0, const char* name = 0); + virtual ~QmitkMaskStampWidget(); + + void SetDataStorage( mitk::DataStorage* storage ); + + protected slots: + + void OnShowInformation(bool); + + void OnStamp(); + + private: + + mitk::ToolManager* m_ToolManager; + + mitk::DataStorage* m_DataStorage; + + Ui::QmitkMaskStampWidgetGUIControls m_Controls; + +}; + +#endif diff --git a/Modules/SegmentationUI/Qmitk/QmitkMaskStampWidgetGUIControls.ui b/Modules/SegmentationUI/Qmitk/QmitkMaskStampWidgetGUIControls.ui new file mode 100644 index 0000000000..77ed1255c6 --- /dev/null +++ b/Modules/SegmentationUI/Qmitk/QmitkMaskStampWidgetGUIControls.ui @@ -0,0 +1,139 @@ + + + QmitkMaskStampWidgetGUIControls + + + + 0 + 0 + 267 + 276 + + + + + 0 + 0 + + + + + 0 + 0 + + + + GUI Controls + + + + + + + 2 + + + 2 + + + + + + + force overwrite + + + + + + + Apply + + + + + + + + + + 0 + 0 + + + + + + + + + 0 + 0 + + + + usage information + + + + + + + + 0 + 0 + + + + + + + <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> +<html><head><meta name="qrichtext" content="1" /><style type="text/css"> +p, li { white-space: pre-wrap; } +</style></head><body style=" font-family:'MS Shell Dlg 2'; font-size:8pt; font-weight:400; font-style:normal;"> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Sans'; font-size:10pt;">This tool fills the interior of the selected mask with the active label. Other labels within the same region will be overwritten if they are not locked.</span> </p> +<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-family:'Sans'; font-size:10pt;"><br /></p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Sans'; font-size:10pt;">Select a surface from the combo box above.</span></p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Sans'; font-size:10pt;">Check &quot;Force Overwrite&quot; to force the filling of regions occupied with other labels.</span></p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Sans'; font-size:10pt;">Press &quot;Apply&quot; to run the tool.</span></p></body></html> + + + Qt::AlignVCenter + + + true + + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + QmitkDataStorageComboBox + QComboBox +
QmitkDataStorageComboBox.h
+
+
+ + + + +
diff --git a/Modules/SegmentationUI/Qmitk/QmitkSearchLabelDialog.cpp b/Modules/SegmentationUI/Qmitk/QmitkSearchLabelDialog.cpp new file mode 100644 index 0000000000..24fecb01ef --- /dev/null +++ b/Modules/SegmentationUI/Qmitk/QmitkSearchLabelDialog.cpp @@ -0,0 +1,82 @@ +/*=================================================================== + +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 "QmitkSearchLabelDialog.h" + + +#include "mitkOrganTypeProperty.h" + +#include + +#include +#include +#include +#include +#include +#include +#include + +QmitkSearchLabelDialog::QmitkSearchLabelDialog(QWidget* parent, Qt::WindowFlags f) +:QDialog(parent, f) +{ + m_Controls = new Ui::QmitkSearchLabelDialogGUI(); + m_Controls->setupUi(this); + + m_LabelIndex = -1; + + QStringList completionList; + completionList << ""; + m_Completer = new QCompleter(completionList); + m_Completer->setCaseSensitivity(Qt::CaseInsensitive); + m_Controls->m_LabelName->setCompleter(m_Completer); + + connect( m_Completer, SIGNAL(activated(const QString&)), this, SLOT(OnLabelCompleterChanged(const QString&)) ); + connect( m_Controls->m_pbCancel, SIGNAL(clicked()), this, SLOT(reject()) ); + connect( m_Controls->m_pbAccept, SIGNAL(clicked()), this, SLOT(accept()) ); + + m_Controls->m_LabelName->setFocus(); +} + +QmitkSearchLabelDialog::~QmitkSearchLabelDialog() +{ +} + +int QmitkSearchLabelDialog::GetLabelSetWidgetTableIndex() +{ + return m_LabelIndex; +} + +QString QmitkSearchLabelDialog::GetLabelSetWidgetTableCompleteWord() +{ + return m_CompleteWord; +} + +void QmitkSearchLabelDialog::SetLabelSuggestionList(QStringList stringList) +{ + m_LabelList = stringList; + QStringListModel* completeModel = static_cast (m_Completer->model()); + completeModel->setStringList(m_LabelList); +} + +void QmitkSearchLabelDialog::OnLabelCompleterChanged(const QString& completedWord) +{ + if (m_LabelList.contains(completedWord)) + { + m_LabelIndex = m_LabelList.indexOf(completedWord); + m_CompleteWord = completedWord; +// emit(goToLabel(m_LabelIndex)); + } +} diff --git a/Modules/SegmentationUI/Qmitk/QmitkSearchLabelDialog.h b/Modules/SegmentationUI/Qmitk/QmitkSearchLabelDialog.h new file mode 100644 index 0000000000..683cee0c0c --- /dev/null +++ b/Modules/SegmentationUI/Qmitk/QmitkSearchLabelDialog.h @@ -0,0 +1,66 @@ +/*=================================================================== + +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 QmitkSearchLabelDialog_h_Included +#define QmitkSearchLabelDialog_h_Included + +#include "MitkSegmentationUIExports.h" + +#include + +#include +#include + +class MITKSEGMENTATIONUI_EXPORT QmitkSearchLabelDialog : public QDialog +{ + Q_OBJECT + + public: + + QmitkSearchLabelDialog(QWidget* parent = 0, Qt::WindowFlags f = 0); + virtual ~QmitkSearchLabelDialog(); + + int GetLabelSetWidgetTableIndex(); + + QString GetLabelSetWidgetTableCompleteWord(); + + void SetLabelSuggestionList(QStringList stringList); + +signals: + + void goToLabel(int); + + public slots: + + protected slots: + + void OnLabelCompleterChanged(const QString& completedWord); + + protected: + + Ui::QmitkSearchLabelDialogGUI *m_Controls; + + QCompleter* m_Completer; + + QStringList m_LabelList; + + int m_LabelIndex; + + QString m_CompleteWord; + +}; + +#endif diff --git a/Modules/SegmentationUI/Qmitk/QmitkSearchLabelDialogGUI.ui b/Modules/SegmentationUI/Qmitk/QmitkSearchLabelDialogGUI.ui new file mode 100644 index 0000000000..f665e53128 --- /dev/null +++ b/Modules/SegmentationUI/Qmitk/QmitkSearchLabelDialogGUI.ui @@ -0,0 +1,70 @@ + + + QmitkSearchLabelDialogGUI + + + + 0 + 0 + 259 + 71 + + + + + 0 + 0 + + + + Search Label + + + false + + + false + + + true + + + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Accept + + + + + + + Cancel + + + + + + + + + + diff --git a/Modules/SegmentationUI/Qmitk/QmitkSliceBasedInterpolatorWidget.cpp b/Modules/SegmentationUI/Qmitk/QmitkSliceBasedInterpolatorWidget.cpp new file mode 100644 index 0000000000..19dec75bab --- /dev/null +++ b/Modules/SegmentationUI/Qmitk/QmitkSliceBasedInterpolatorWidget.cpp @@ -0,0 +1,646 @@ +/*=================================================================== + +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 "QmitkSliceBasedInterpolatorWidget.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "QmitkStdMultiWidget.h" + +#include + +#include +#include +#include +#include + +QmitkSliceBasedInterpolatorWidget::QmitkSliceBasedInterpolatorWidget(QWidget* parent, const char* /*name*/) : QWidget(parent), +m_SliceInterpolatorController( mitk::SliceBasedInterpolationController::New() ), +m_ToolManager(NULL), +m_Activated(false), +m_DataStorage(NULL), +m_LastSNC(0), +m_LastSliceIndex(0) +{ + m_Controls.setupUi(this); + + m_ToolManager = mitk::ToolManagerProvider::GetInstance()->GetToolManager(); + Q_ASSERT(m_ToolManager); + + m_ToolManager->WorkingDataChanged += mitk::MessageDelegate( this, &QmitkSliceBasedInterpolatorWidget::OnToolManagerWorkingDataModified ); + + connect(m_Controls.m_btStart, SIGNAL(toggled(bool)), this, SLOT(OnToggleWidgetActivation(bool))); + connect(m_Controls.m_btApplyForCurrentSlice, SIGNAL(clicked()), this, SLOT(OnAcceptInterpolationClicked())); + connect(m_Controls.m_btApplyForAllSlices, SIGNAL(clicked()), this, SLOT(OnAcceptAllInterpolationsClicked())); + + itk::ReceptorMemberCommand::Pointer command = itk::ReceptorMemberCommand::New(); + command->SetCallbackFunction( this, &QmitkSliceBasedInterpolatorWidget::OnSliceInterpolationInfoChanged ); + m_InterpolationInfoChangedObserverTag = m_SliceInterpolatorController->AddObserver( itk::ModifiedEvent(), command ); + + // feedback node and its visualization properties + m_PreviewNode = mitk::DataNode::New(); + m_PreviewNode->SetName("3D tool preview"); + + m_PreviewNode->SetProperty("texture interpolation", mitk::BoolProperty::New(false) ); + m_PreviewNode->SetProperty("layer", mitk::IntProperty::New(100) ); + m_PreviewNode->SetProperty("binary", mitk::BoolProperty::New(true) ); + m_PreviewNode->SetProperty("outline binary", mitk::BoolProperty::New(true) ); + m_PreviewNode->SetProperty("outline binary shadow", mitk::BoolProperty::New(true) ); + m_PreviewNode->SetProperty("helper object", mitk::BoolProperty::New(true) ); + m_PreviewNode->SetOpacity(1.0); + m_PreviewNode->SetColor(0.0, 1.0, 0.0); + + m_Controls.m_btApplyForCurrentSlice->setEnabled(false); + m_Controls.m_btApplyForAllSlices->setEnabled(false); + + this->setEnabled(false); +} + +QmitkSliceBasedInterpolatorWidget::~QmitkSliceBasedInterpolatorWidget() +{ + m_ToolManager->WorkingDataChanged -= mitk::MessageDelegate(this, &QmitkSliceBasedInterpolatorWidget::OnToolManagerWorkingDataModified); + + foreach(mitk::SliceNavigationController* slicer, m_ControllerToSliceObserverTag.keys()) + { + slicer->RemoveObserver(m_ControllerToDeleteObserverTag.take(slicer)); + slicer->RemoveObserver(m_ControllerToTimeObserverTag.take(slicer)); + slicer->RemoveObserver(m_ControllerToSliceObserverTag.take(slicer)); + } + + m_ActionToSliceDimensionMap.clear(); + + // remove observer + m_SliceInterpolatorController->RemoveObserver( m_InterpolationInfoChangedObserverTag ); +} + +const QmitkSliceBasedInterpolatorWidget::ActionToSliceDimensionMapType QmitkSliceBasedInterpolatorWidget::CreateActionToSliceDimension() +{ + ActionToSliceDimensionMapType actionToSliceDimension; + foreach(mitk::SliceNavigationController* slicer, m_ControllerToDeleteObserverTag.keys()) + { + std::string name = slicer->GetRenderer()->GetName(); + if (name == "stdmulti.widget1") + name = "Axial (red window)"; + else if (name == "stdmulti.widget2") + name = "Sagittal (green window)"; + else if (name == "stdmulti.widget3") + name = "Coronal (blue window)"; + actionToSliceDimension[new QAction(QString::fromStdString(name),0)] = slicer; + } + + return actionToSliceDimension; +} + +void QmitkSliceBasedInterpolatorWidget::SetDataStorage(mitk::DataStorage& storage) +{ + m_DataStorage = &storage; +} + +void QmitkSliceBasedInterpolatorWidget::SetSliceNavigationControllers(const QList &controllers) +{ + Q_ASSERT(!controllers.empty()); + + // connect to the slice navigation controller. after each change, call the interpolator + foreach(mitk::SliceNavigationController* slicer, controllers) + { + //Has to be initialized + m_LastSNC = slicer; + + m_TimeStep.insert(slicer, slicer->GetTime()->GetPos()); + + itk::MemberCommand::Pointer deleteCommand = itk::MemberCommand::New(); + deleteCommand->SetCallbackFunction( this, &QmitkSliceBasedInterpolatorWidget::OnSliceNavigationControllerDeleted); + m_ControllerToDeleteObserverTag.insert(slicer, slicer->AddObserver(itk::DeleteEvent(), deleteCommand)); + + itk::MemberCommand::Pointer timeChangedCommand = itk::MemberCommand::New(); + timeChangedCommand->SetCallbackFunction( this, &QmitkSliceBasedInterpolatorWidget::OnTimeChanged); + m_ControllerToTimeObserverTag.insert(slicer, slicer->AddObserver(mitk::SliceNavigationController::TimeSlicedGeometryEvent(NULL,0), timeChangedCommand)); + + itk::MemberCommand::Pointer sliceChangedCommand = itk::MemberCommand::New(); + sliceChangedCommand->SetCallbackFunction( this, &QmitkSliceBasedInterpolatorWidget::OnSliceChanged); + m_ControllerToSliceObserverTag.insert(slicer, slicer->AddObserver(mitk::SliceNavigationController::GeometrySliceEvent(NULL,0), sliceChangedCommand)); + } + + m_ActionToSliceDimensionMap = this->CreateActionToSliceDimension(); +} + +void QmitkSliceBasedInterpolatorWidget::OnToolManagerWorkingDataModified() +{ + mitk::DataNode* workingNode = this->m_ToolManager->GetWorkingData(0); + if (!workingNode) + { + this->setEnabled(false); + return; + } + + mitk::LabelSetImage* workingImage = dynamic_cast< mitk::LabelSetImage* >( workingNode->GetData() ); + // TODO adapt tool manager so that this check is done there, e.g. convenience function +// Q_ASSERT(workingImage); + if (!workingImage) + { + this->setEnabled(false); + return; + } + + if (workingImage->GetDimension() > 4 || workingImage->GetDimension() < 3) + { + this->setEnabled(false); + return; + } + + m_WorkingImage = workingImage; + + this->setEnabled(true); +} + +void QmitkSliceBasedInterpolatorWidget::OnTimeChanged(itk::Object* sender, const itk::EventObject& e) +{ + //Check if we really have a GeometryTimeEvent + if (!dynamic_cast(&e)) + return; + + mitk::SliceNavigationController* slicer = dynamic_cast(sender); + Q_ASSERT(slicer); + + m_TimeStep[slicer]/* = event.GetPos()*/; + + //TODO Macht das hier wirklich Sinn???? + if (m_LastSNC == slicer) + { + slicer->SendSlice();//will trigger a new interpolation + } +} + +void QmitkSliceBasedInterpolatorWidget::OnSliceChanged(itk::Object *sender, const itk::EventObject &e) +{ + if (m_Activated && m_WorkingImage.IsNotNull()) + { + //Check whether we really have a GeometrySliceEvent + if (!dynamic_cast(&e)) + return; + + mitk::SliceNavigationController* slicer = dynamic_cast(sender); + if (slicer) + { + this->TranslateAndInterpolateChangedSlice(e, slicer); + mitk::RenderingManager::GetInstance()->RequestUpdateAll(); + // slicer->GetRenderer()->RequestUpdate(); + } + } +} + +void QmitkSliceBasedInterpolatorWidget::TranslateAndInterpolateChangedSlice(const itk::EventObject& e, mitk::SliceNavigationController* slicer) +{ + if (m_Activated && m_WorkingImage.IsNotNull()) + { + const mitk::SliceNavigationController::GeometrySliceEvent& geometrySliceEvent = dynamic_cast(e); + mitk::TimeGeometry* timeGeometry = geometrySliceEvent.GetTimeGeometry(); + if (timeGeometry && m_TimeStep.contains(slicer)) + { + mitk::SlicedGeometry3D* slicedGeometry = dynamic_cast(timeGeometry->GetGeometryForTimeStep(m_TimeStep[slicer]).GetPointer()); + if (slicedGeometry) + { + mitk::PlaneGeometry* plane = slicedGeometry->GetPlaneGeometry(geometrySliceEvent.GetPos()); + if (plane) + { + m_LastSNC = slicer; + this->Interpolate(plane, m_TimeStep[slicer], slicer); + } + } + } + } +} + +void QmitkSliceBasedInterpolatorWidget::Interpolate( mitk::PlaneGeometry* plane, unsigned int timeStep, mitk::SliceNavigationController* slicer ) +{ + int clickedSliceDimension(-1); + int clickedSliceIndex(-1); + + // calculate real slice position, i.e. slice of the image and not slice of the TimeSlicedGeometry + // see if timestep is needed here + mitk::SegTool2D::DetermineAffectedImageSlice( m_WorkingImage, plane, clickedSliceDimension, clickedSliceIndex ); + + mitk::Image::Pointer interpolation = m_SliceInterpolatorController->Interpolate( clickedSliceDimension, clickedSliceIndex, plane, timeStep ); + + m_PreviewNode->SetData( interpolation ); + + const mitk::Color& color = m_WorkingImage->GetActiveLabel()->GetColor(); + m_PreviewNode->SetColor(color); + + m_LastSNC = slicer; + m_LastSliceIndex = clickedSliceIndex; +} + +mitk::Image::Pointer QmitkSliceBasedInterpolatorWidget::GetWorkingSlice(const mitk::PlaneGeometry* planeGeometry) +{ + unsigned int timeStep = m_LastSNC->GetTime()->GetPos(); + + //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( m_WorkingImage ); + extractor->SetTimeStep( timeStep ); + extractor->SetWorldGeometry( planeGeometry ); + extractor->SetVtkOutputRequest(false); + extractor->SetResliceTransformByGeometry( m_WorkingImage->GetTimeGeometry()->GetGeometryForTimeStep(timeStep) ); + + extractor->Modified(); + + try + { + extractor->Update(); + } + catch ( itk::ExceptionObject & excep ) + { + MITK_ERROR << "Exception caught: " << excep.GetDescription(); + return NULL; + } + + mitk::Image::Pointer slice = extractor->GetOutput(); + + //specify the undo operation with the non edited slice + //MLI TODO added code starts here + mitk::SlicedGeometry3D* sliceGeometry = dynamic_cast(slice->GetGeometry()); + //m_undoOperation = new mitk::DiffSliceOperation(m_WorkingImage, extractor->GetVtkOutput(), slice->GetGeometry(), timeStep, const_cast(planeGeometry)); + // added code ends here + m_undoOperation = new mitk::DiffSliceOperation(m_WorkingImage, extractor->GetOutput(), sliceGeometry, timeStep, const_cast(planeGeometry)); + + slice->DisconnectPipeline(); + + return slice; +} + +void QmitkSliceBasedInterpolatorWidget::OnToggleWidgetActivation(bool enabled) +{ + Q_ASSERT(m_ToolManager); + + mitk::DataNode* workingNode = m_ToolManager->GetWorkingData(0); + if (!workingNode) return; + + m_Controls.m_btApplyForCurrentSlice->setEnabled(enabled); + m_Controls.m_btApplyForAllSlices->setEnabled(enabled); + + if (enabled) + m_Controls.m_btStart->setText("Stop"); + else + m_Controls.m_btStart->setText("Start"); + + unsigned int numberOfExistingTools = m_ToolManager->GetTools().size(); + for (unsigned int i = 0; i < numberOfExistingTools; i++) + { + //mitk::SegTool2D* tool = dynamic_cast(m_ToolManager->GetToolById(i)); + //MLI TODO + //if (tool) tool->SetEnable2DInterpolation( enabled ); + } + + if (enabled) + { + if (!m_DataStorage->Exists(m_PreviewNode)) + { + m_DataStorage->Add( m_PreviewNode ); + } + + m_SliceInterpolatorController->SetWorkingImage( m_WorkingImage ); + this->UpdateVisibleSuggestion(); + } + else + { + if (m_DataStorage->Exists(m_PreviewNode)) + { + m_DataStorage->Remove( m_PreviewNode ); + } + + mitk::UndoController::GetCurrentUndoModel()->Clear(); + } + + m_Activated = enabled; + + mitk::RenderingManager::GetInstance()->RequestUpdateAll(); +} + +template +void QmitkSliceBasedInterpolatorWidget::WritePreviewOnWorkingImage( itk::Image* targetSlice, const mitk::Image* sourceSlice, int overwritevalue ) +{ + typedef itk::Image ImageType; + + typename ImageType::Pointer sourceSliceITK; + mitk::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< ImageType > OutputIteratorType; + typedef itk::ImageRegionConstIterator< ImageType > InputIteratorType; + + InputIteratorType inputIterator( sourceSliceITK, sourceSliceITK->GetLargestPossibleRegion() ); + OutputIteratorType outputIterator( targetSlice, targetSlice->GetLargestPossibleRegion() ); + + outputIterator.GoToBegin(); + inputIterator.GoToBegin(); + + int activePixelValue = m_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() ) + { + int targetValue = static_cast(outputIterator.Get()); + if ( inputIterator.Get() != 0 ) + { + if (!m_WorkingImage->GetLabel(targetValue)->GetLocked()) + outputIterator.Set( overwritevalue ); + } + + ++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 QmitkSliceBasedInterpolatorWidget::OnAcceptInterpolationClicked() +{ + if (m_WorkingImage.IsNotNull() && m_PreviewNode->GetData()) + { + const mitk::PlaneGeometry* planeGeometry = m_LastSNC->GetCurrentPlaneGeometry(); + if (!planeGeometry) return; + + mitk::Image::Pointer sliceImage = this->GetWorkingSlice(planeGeometry); + if (sliceImage.IsNull()) return; + + mitk::Image::Pointer previewSlice = dynamic_cast( m_PreviewNode->GetData() ); + + AccessFixedDimensionByItk_2( sliceImage, WritePreviewOnWorkingImage, 2, previewSlice, m_WorkingImage->GetActiveLabel()->GetValue() ); + + //Make sure that for reslicing and overwriting the same alogrithm is used. We can specify the mode of the vtk reslicer + vtkSmartPointer overwrite = vtkSmartPointer::New(); + overwrite->SetInputSlice(sliceImage->GetVtkImageData()); + //set overwrite mode to true to write back to the image volume + overwrite->SetOverwriteMode(true); + overwrite->Modified(); + + unsigned int timeStep = m_LastSNC->GetTime()->GetPos(); + + mitk::ExtractSliceFilter::Pointer extractor = mitk::ExtractSliceFilter::New(overwrite); + extractor->SetInput( m_WorkingImage ); + extractor->SetTimeStep( timeStep ); + extractor->SetWorldGeometry( planeGeometry ); + extractor->SetVtkOutputRequest(false); + extractor->SetResliceTransformByGeometry( m_WorkingImage->GetTimeGeometry()->GetGeometryForTimeStep(timeStep) ); + + extractor->Modified(); + + try + { + extractor->Update(); + } + catch ( itk::ExceptionObject & excep ) + { + MITK_ERROR << "Exception caught: " << excep.GetDescription(); + return; + } + + //the image was modified within the pipeline, but not marked so + m_WorkingImage->Modified(); + + int clickedSliceDimension(-1); + int clickedSliceIndex(-1); + + mitk::SegTool2D::DetermineAffectedImageSlice( m_WorkingImage, planeGeometry, clickedSliceDimension, clickedSliceIndex ); + + m_SliceInterpolatorController->SetChangedSlice( sliceImage, clickedSliceDimension, clickedSliceIndex, timeStep ); + + //specify the undo operation with the edited slice + //MLI TODO added code starts here + mitk::SlicedGeometry3D* sliceGeometry = dynamic_cast(sliceImage->GetGeometry()); + //m_undoOperation = new mitk::DiffSliceOperation(m_WorkingImage, extractor->GetVtkOutput(), slice->GetGeometry(), timeStep, const_cast(planeGeometry)); + // added code ends here + m_doOperation = new mitk::DiffSliceOperation( + m_WorkingImage, extractor->GetOutput(), sliceGeometry, timeStep, const_cast(planeGeometry)); + + //create an operation event for the undo stack + mitk::OperationEvent* undoStackItem = new mitk::OperationEvent( + mitk::DiffSliceOperationApplier::GetInstance(), m_doOperation, m_undoOperation, "Slice Interpolation" ); + + //add it to the undo controller + mitk::UndoController::GetCurrentUndoModel()->SetOperationEvent( undoStackItem ); + + //clear the pointers as the operation are stored in the undo controller and also deleted from there + m_undoOperation = NULL; + m_doOperation = NULL; + + m_PreviewNode->SetData(NULL); + + mitk::RenderingManager::GetInstance()->RequestUpdateAll(); + } +} + +void QmitkSliceBasedInterpolatorWidget::AcceptAllInterpolations(mitk::SliceNavigationController* slicer) +{ + // Since we need to shift the plane it must be clone so that the original plane isn't altered + mitk::PlaneGeometry::Pointer reslicePlane = slicer->GetCurrentPlaneGeometry()->Clone(); + unsigned int timeStep = slicer->GetTime()->GetPos(); + + int sliceDimension(-1); + int sliceIndex(-1); + + mitk::SegTool2D::DetermineAffectedImageSlice( m_WorkingImage, reslicePlane, sliceDimension, sliceIndex ); + + unsigned int zslices = m_WorkingImage->GetDimension( sliceDimension ); + + mitk::ProgressBar::GetInstance()->Reset(); + mitk::ProgressBar::GetInstance()->AddStepsToDo(zslices); + + mitk::Point3D origin = reslicePlane->GetOrigin(); + + for (unsigned int idx = 0; idx < zslices; ++idx) + { + // Transforming the current origin of the reslice plane + // so that it matches the one of the next slice + m_WorkingImage->GetSlicedGeometry()->WorldToIndex(origin, origin); + origin[sliceDimension] = idx; + m_WorkingImage->GetSlicedGeometry()->IndexToWorld(origin, origin); + reslicePlane->SetOrigin(origin); + + mitk::Image::Pointer interpolation = m_SliceInterpolatorController->Interpolate( sliceDimension, idx, reslicePlane, timeStep ); + + if (interpolation.IsNotNull()) + { + m_PreviewNode->SetData(interpolation); + + mitk::Image::Pointer sliceImage = this->GetWorkingSlice(reslicePlane); + if (sliceImage.IsNull()) return; + + AccessFixedDimensionByItk_2( sliceImage, WritePreviewOnWorkingImage, 2, interpolation, m_WorkingImage->GetActiveLabel()->GetValue() ); + + //Make sure that for reslicing and overwriting the same alogrithm is used. We can specify the mode of the vtk reslicer + vtkSmartPointer overwrite = vtkSmartPointer::New(); + overwrite->SetInputSlice(sliceImage->GetVtkImageData()); + //set overwrite mode to true to write back to the image volume + overwrite->SetOverwriteMode(true); + overwrite->Modified(); + + mitk::ExtractSliceFilter::Pointer extractor = mitk::ExtractSliceFilter::New(overwrite); + extractor->SetInput( m_WorkingImage ); + extractor->SetTimeStep( timeStep ); + extractor->SetWorldGeometry( reslicePlane ); + extractor->SetVtkOutputRequest(true); + extractor->SetResliceTransformByGeometry( m_WorkingImage->GetTimeGeometry()->GetGeometryForTimeStep(timeStep) ); + + extractor->Modified(); + + try + { + extractor->Update(); + } + catch ( itk::ExceptionObject & excep ) + { + MITK_ERROR << "Exception caught: " << excep.GetDescription(); + return; + } + + m_WorkingImage->Modified(); + + mitk::RenderingManager::GetInstance()->RequestUpdateAll(mitk::RenderingManager::REQUEST_UPDATE_2DWINDOWS); + } + + mitk::ProgressBar::GetInstance()->Progress(); + } + + m_SliceInterpolatorController->SetWorkingImage(m_WorkingImage); + + mitk::RenderingManager::GetInstance()->RequestUpdateAll(); +} + +void QmitkSliceBasedInterpolatorWidget::OnAcceptAllInterpolationsClicked() +{ + QMenu orientationPopup(this); + std::map::const_iterator it; + for(it = m_ActionToSliceDimensionMap.begin(); it != m_ActionToSliceDimensionMap.end(); it++) + orientationPopup.addAction(it->first); + + connect( &orientationPopup, SIGNAL(triggered(QAction*)), this, SLOT(OnAcceptAllPopupActivated(QAction*)) ); + + orientationPopup.exec( QCursor::pos() ); +} + +void QmitkSliceBasedInterpolatorWidget::OnAcceptAllPopupActivated(QAction* action) +{ + ActionToSliceDimensionMapType::const_iterator iter = m_ActionToSliceDimensionMap.find( action ); + if (iter != m_ActionToSliceDimensionMap.end()) + { + mitk::SliceNavigationController* slicer = iter->second; + this->AcceptAllInterpolations( slicer ); + } +} + +void QmitkSliceBasedInterpolatorWidget::UpdateVisibleSuggestion() +{ + if (m_Activated && m_LastSNC) + { + // determine which one is the current view, try to do an initial interpolation + mitk::BaseRenderer* renderer = m_LastSNC->GetRenderer(); + if (renderer && renderer->GetMapperID() == mitk::BaseRenderer::Standard2D) + { + const mitk::TimeGeometry* timeGeometry = dynamic_cast( renderer->GetWorldTimeGeometry() ); + if (timeGeometry) + { + mitk::SliceNavigationController::GeometrySliceEvent event( const_cast(timeGeometry), renderer->GetSlice() ); + this->TranslateAndInterpolateChangedSlice(event, m_LastSNC); + mitk::RenderingManager::GetInstance()->RequestUpdateAll(); + } + } + } +} + +void QmitkSliceBasedInterpolatorWidget::OnSliceInterpolationInfoChanged(const itk::EventObject& /*e*/) +{ + // something (e.g. undo) changed the interpolation info, we should refresh our display + this->UpdateVisibleSuggestion(); +} + +void QmitkSliceBasedInterpolatorWidget::OnSliceNavigationControllerDeleted(const itk::Object *sender, const itk::EventObject& /*e*/) +{ + //Don't know how to avoid const_cast here?! + mitk::SliceNavigationController* slicer = dynamic_cast(const_cast(sender)); + if (slicer) + { + m_ControllerToTimeObserverTag.remove(slicer); + m_ControllerToSliceObserverTag.remove(slicer); + m_ControllerToDeleteObserverTag.remove(slicer); + } +} + +void QmitkSliceBasedInterpolatorWidget::WaitCursorOn() +{ + QApplication::setOverrideCursor( QCursor(Qt::WaitCursor) ); +} + +void QmitkSliceBasedInterpolatorWidget::WaitCursorOff() +{ + this->RestoreOverrideCursor(); +} + +void QmitkSliceBasedInterpolatorWidget::RestoreOverrideCursor() +{ + QApplication::restoreOverrideCursor(); +} diff --git a/Modules/SegmentationUI/Qmitk/QmitkSliceBasedInterpolatorWidget.h b/Modules/SegmentationUI/Qmitk/QmitkSliceBasedInterpolatorWidget.h new file mode 100644 index 0000000000..8b037414c0 --- /dev/null +++ b/Modules/SegmentationUI/Qmitk/QmitkSliceBasedInterpolatorWidget.h @@ -0,0 +1,199 @@ +/*=================================================================== + +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 QmitkSliceBasedInterpolatorWidget_h_Included +#define QmitkSliceBasedInterpolatorWidget_h_Included + +#include "MitkSegmentationUIExports.h" +#include "mitkDataStorage.h" +#include "mitkSliceBasedInterpolationController.h" + +#include + +#include + +#include "ui_QmitkSliceBasedInterpolatorWidgetGUIControls.h" + +namespace mitk +{ + class PlaneGeometry; + class SliceNavigationController; + class LabelSetImage; + class ToolManager; + class DiffSliceOperation; +} + +/** + \brief GUI for slices interpolation. + + \ingroup ToolManagerEtAl + \ingroup Widgets + + \sa QmitkInteractiveSegmentation + \sa mitk::SegmentationInterpolation + + There is a separate page describing the general design of QmitkInteractiveSegmentation: \ref QmitkInteractiveSegmentationTechnicalPage + + While mitk::SegmentationInterpolation does the bookkeeping of interpolation + (keeping track of which slices contain how much segmentation) and the algorithmic work, + QmitkSliceBasedInterpolatorWidget is responsible to watch the GUI, to notice, which slice is currently + visible. It triggers generation of interpolation suggestions and also triggers acception of + suggestions. + + \todo show/hide feedback on demand + + Last contributor: $Author: maleike $ +*/ + +class MITKSEGMENTATIONUI_EXPORT QmitkSliceBasedInterpolatorWidget : public QWidget +{ + Q_OBJECT + + public: + + QmitkSliceBasedInterpolatorWidget(QWidget* parent = 0, const char* name = 0); + virtual ~QmitkSliceBasedInterpolatorWidget(); + + void SetDataStorage(mitk::DataStorage& storage); + + /** + Sets the slice navigation controllers for getting slice changed events from the views. + */ + void SetSliceNavigationControllers(const QList &controllers); + + void OnToolManagerWorkingDataModified(); + + void OnTimeChanged(itk::Object* sender, const itk::EventObject&); + + void OnSliceChanged(itk::Object* sender, const itk::EventObject&); + + void OnSliceNavigationControllerDeleted(const itk::Object *sender, const itk::EventObject& ); + + /** + Just public because it is called by itk::Commands. You should not need to call this. + */ + void OnSliceInterpolationInfoChanged(const itk::EventObject&); + + Ui::QmitkSliceBasedInterpolatorWidgetGUIControls m_Controls; + + signals: + + void signalSliceBasedInterpolationEnabled(bool); + + public slots: + + /** + \brief Reaction to "Start/Stop" button click + */ + void OnToggleWidgetActivation(bool); + + protected slots: + + /** + \brief Reaction to "Accept Current Slice" button click. + */ + void OnAcceptInterpolationClicked(); + + /* + \brief Reaction to "Accept All Slices" button click. + Opens popup to ask about which orientation should be interpolated + */ + void OnAcceptAllInterpolationsClicked(); + + /* + \brief Called from popup menu of OnAcceptAllInterpolationsClicked() + Will trigger interpolation for all slices in given orientation + */ + void OnAcceptAllPopupActivated(QAction* action); + + protected: + + typedef std::map ActionToSliceDimensionMapType; + + const ActionToSliceDimensionMapType CreateActionToSliceDimension(); + + ActionToSliceDimensionMapType m_ActionToSliceDimensionMap; + + void AcceptAllInterpolations(mitk::SliceNavigationController* slicer); + + void WaitCursorOn(); + + void WaitCursorOff(); + + void RestoreOverrideCursor(); + + /** + Gets the working slice based on the given plane geometry and last saved interaction + + \param planeGeometry a plane geometry + */ + mitk::Image::Pointer GetWorkingSlice(const mitk::PlaneGeometry* planeGeometry); + + /** + Retrieves the currently selected PlaneGeometry from a SlicedGeometry3D that is generated by a SliceNavigationController + and calls Interpolate to further process this PlaneGeometry into an interpolation. + + \param e is a actually a mitk::SliceNavigationController::GeometrySliceEvent, sent by a SliceNavigationController + \param slice the SliceNavigationController + */ + void TranslateAndInterpolateChangedSlice(const itk::EventObject& e, mitk::SliceNavigationController* slicer); + + /** + Given a PlaneGeometry, this method figures out which slice of the first working image (of the associated ToolManager) + should be interpolated. The actual work is then done by our SegmentationInterpolation object. + */ + void Interpolate( mitk::PlaneGeometry* plane, unsigned int timeStep, mitk::SliceNavigationController *slicer ); + + /** + Called internally to update the interpolation suggestion. Finds out about the focused render window and requests an interpolation. + */ + void UpdateVisibleSuggestion(); + +private: + + mitk::SliceBasedInterpolationController::Pointer m_SliceInterpolatorController; + + mitk::ToolManager* m_ToolManager; + + bool m_Activated; + + template + void WritePreviewOnWorkingImage( itk::Image* target, const mitk::Image* source, int overwritevalue ); + + QHash m_ControllerToTimeObserverTag; + QHash m_ControllerToSliceObserverTag; + QHash m_ControllerToDeleteObserverTag; + + unsigned int m_InterpolationInfoChangedObserverTag; + + mitk::DiffSliceOperation* m_doOperation; + mitk::DiffSliceOperation* m_undoOperation; + + mitk::DataNode::Pointer m_PreviewNode; + mitk::Image::Pointer m_PreviewImage; + + mitk::LabelSetImage::Pointer m_WorkingImage; + + mitk::SliceNavigationController* m_LastSNC; + + unsigned int m_LastSliceIndex; + + QHash m_TimeStep; + + mitk::DataStorage::Pointer m_DataStorage; +}; + +#endif diff --git a/Modules/SegmentationUI/Qmitk/QmitkSliceBasedInterpolatorWidgetGUIControls.ui b/Modules/SegmentationUI/Qmitk/QmitkSliceBasedInterpolatorWidgetGUIControls.ui new file mode 100644 index 0000000000..d8e8eeeca3 --- /dev/null +++ b/Modules/SegmentationUI/Qmitk/QmitkSliceBasedInterpolatorWidgetGUIControls.ui @@ -0,0 +1,159 @@ + + + QmitkSliceBasedInterpolatorWidgetGUIControls + + + + 0 + 0 + 265 + 94 + + + + + 0 + 0 + + + + + 0 + 0 + + + + Slice-Based Interpolation + + + + + + + QLayout::SetDefaultConstraint + + + 6 + + + + + QLayout::SetDefaultConstraint + + + + + true + + + + 0 + 0 + + + + Start/Stop the 3D interpolation utility + + + Start + + + + :/Qmitk/Start.png + :/Qmitk/Stop.png:/Qmitk/Start.png + + + + 32 + 32 + + + + true + + + false + + + Qt::ToolButtonTextUnderIcon + + + Qt::NoArrow + + + + + + + + 0 + 0 + + + + Accept Single + + + + :/Qmitk/AcceptCurrentInterpolation.png:/Qmitk/AcceptCurrentInterpolation.png + + + + 32 + 32 + + + + Qt::ToolButtonTextUnderIcon + + + + + + + + 0 + 0 + + + + Accept All + + + + :/Qmitk/AcceptAllInterpolations.png:/Qmitk/AcceptAllInterpolations.png + + + + 32 + 32 + + + + Qt::ToolButtonTextUnderIcon + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + + + diff --git a/Modules/SegmentationUI/Qmitk/QmitkSurfaceBasedInterpolatorWidget.cpp b/Modules/SegmentationUI/Qmitk/QmitkSurfaceBasedInterpolatorWidget.cpp new file mode 100644 index 0000000000..7e5c88d18d --- /dev/null +++ b/Modules/SegmentationUI/Qmitk/QmitkSurfaceBasedInterpolatorWidget.cpp @@ -0,0 +1,346 @@ +/*=================================================================== + +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 "QmitkSurfaceBasedInterpolatorWidget.h" + +#include "mitkColorProperty.h" +#include "mitkProperties.h" +#include "mitkRenderingManager.h" +#include "mitkProgressBar.h" +#include "mitkOperationEvent.h" +#include "mitkInteractionConst.h" +#include "mitkVtkRepresentationProperty.h" +#include "mitkUndoController.h" +#include "mitkSegTool2D.h" +#include "mitkSurfaceToImageFilter.h" +#include "mitkSliceNavigationController.h" +#include + +#include "QmitkStdMultiWidget.h" + +#include +#include + +#include + + +QmitkSurfaceBasedInterpolatorWidget::QmitkSurfaceBasedInterpolatorWidget(QWidget* parent, const char* /*name*/) : QWidget(parent), +m_SurfaceBasedInterpolatorController(mitk::SurfaceBasedInterpolationController::GetInstance()), +m_ToolManager(NULL), +m_DataStorage(NULL), +m_Activated(false) +{ + m_Controls.setupUi(this); + + m_ToolManager = mitk::ToolManagerProvider::GetInstance()->GetToolManager(); + Q_ASSERT(m_ToolManager); + + m_ToolManager->WorkingDataChanged += mitk::MessageDelegate( this, &QmitkSurfaceBasedInterpolatorWidget::OnToolManagerWorkingDataModified ); + + connect(m_Controls.m_btStart, SIGNAL(toggled(bool)), this, SLOT(OnToggleWidgetActivation(bool))); + connect(m_Controls.m_btAccept, SIGNAL(clicked()), this, SLOT(OnAcceptInterpolationClicked())); + connect(m_Controls.m_cbShowPositionNodes, SIGNAL(toggled(bool)), this, SLOT(OnShowMarkers(bool))); + + itk::ReceptorMemberCommand::Pointer command = itk::ReceptorMemberCommand::New(); + command->SetCallbackFunction( this, &QmitkSurfaceBasedInterpolatorWidget::OnSurfaceInterpolationInfoChanged ); + m_SurfaceInterpolationInfoChangedObserverTag = m_SurfaceBasedInterpolatorController->AddObserver( itk::ModifiedEvent(), command ); + + m_InterpolatedSurfaceNode = mitk::DataNode::New(); + m_InterpolatedSurfaceNode->SetName( "Surface Interpolation feedback" ); + m_InterpolatedSurfaceNode->SetProperty( "color", mitk::ColorProperty::New(255.0,255.0,0.0) ); + m_InterpolatedSurfaceNode->SetProperty( "opacity", mitk::FloatProperty::New(0.5) ); + m_InterpolatedSurfaceNode->SetProperty( "includeInBoundingBox", mitk::BoolProperty::New(false)); + m_InterpolatedSurfaceNode->SetProperty( "helper object", mitk::BoolProperty::New(true) ); + m_InterpolatedSurfaceNode->SetVisibility(false); + + m_3DContourNode = mitk::DataNode::New(); + m_3DContourNode->SetName( "Drawn Contours" ); + m_3DContourNode->SetProperty( "color", mitk::ColorProperty::New(0.0, 0.0, 0.0) ); + m_3DContourNode->SetProperty( "helper object", mitk::BoolProperty::New(true)); + m_3DContourNode->SetProperty( "material.representation", mitk::VtkRepresentationProperty::New(VTK_WIREFRAME)); + m_3DContourNode->SetProperty( "material.wireframeLineWidth", mitk::FloatProperty::New(2.0f)); + m_3DContourNode->SetProperty( "includeInBoundingBox", mitk::BoolProperty::New(false)); + m_3DContourNode->SetVisibility(false, mitk::BaseRenderer::GetInstance( mitk::BaseRenderer::GetRenderWindowByName("stdmulti.widget1"))); + m_3DContourNode->SetVisibility(false, mitk::BaseRenderer::GetInstance( mitk::BaseRenderer::GetRenderWindowByName("stdmulti.widget2"))); + m_3DContourNode->SetVisibility(false, mitk::BaseRenderer::GetInstance( mitk::BaseRenderer::GetRenderWindowByName("stdmulti.widget3"))); + m_3DContourNode->SetVisibility(false, mitk::BaseRenderer::GetInstance( mitk::BaseRenderer::GetRenderWindowByName("stdmulti.widget4"))); + + connect(&m_Watcher, SIGNAL(started()), this, SLOT(StartUpdateInterpolationTimer())); + connect(&m_Watcher, SIGNAL(finished()), this, SLOT(OnSurfaceInterpolationFinished())); + connect(&m_Watcher, SIGNAL(finished()), this, SLOT(StopUpdateInterpolationTimer())); + + m_Timer = new QTimer(this); + connect(m_Timer, SIGNAL(timeout()), this, SLOT(ChangeSurfaceColor())); + + m_Controls.m_btAccept->setEnabled(false); + m_Controls.m_cbShowPositionNodes->setEnabled(false); + + this->setEnabled(false); +} + +void QmitkSurfaceBasedInterpolatorWidget::SetDataStorage(mitk::DataStorage& storage) +{ + m_DataStorage = &storage; +} + +QmitkSurfaceBasedInterpolatorWidget::~QmitkSurfaceBasedInterpolatorWidget() +{ + m_ToolManager->WorkingDataChanged -= mitk::MessageDelegate(this, &QmitkSurfaceBasedInterpolatorWidget::OnToolManagerWorkingDataModified); + + if(m_DataStorage->Exists(m_3DContourNode)) + m_DataStorage->Remove(m_3DContourNode); + + if(m_DataStorage->Exists(m_InterpolatedSurfaceNode)) + m_DataStorage->Remove(m_InterpolatedSurfaceNode); + + // remove observer + m_SurfaceBasedInterpolatorController->RemoveObserver( m_SurfaceInterpolationInfoChangedObserverTag ); + + delete m_Timer; +} + +void QmitkSurfaceBasedInterpolatorWidget::ShowInterpolationResult(bool status) +{ + if (m_InterpolatedSurfaceNode.IsNotNull()) + m_InterpolatedSurfaceNode->SetVisibility(status); + + if (m_3DContourNode.IsNotNull()) + m_3DContourNode->SetVisibility(status, mitk::BaseRenderer::GetInstance( mitk::BaseRenderer::GetRenderWindowByName("stdmulti.widget4"))); + + mitk::RenderingManager::GetInstance()->RequestUpdateAll(); +} + + void QmitkSurfaceBasedInterpolatorWidget::OnSurfaceInterpolationFinished() + { + mitk::Surface::Pointer interpolatedSurface = m_SurfaceBasedInterpolatorController->GetInterpolationResult(); + + if ( interpolatedSurface.IsNotNull() ) + { + m_InterpolatedSurfaceNode->SetData(interpolatedSurface); + m_3DContourNode->SetData(m_SurfaceBasedInterpolatorController->GetContoursAsSurface()); + this->ShowInterpolationResult(true); + } + else + { + m_InterpolatedSurfaceNode->SetData(NULL); + m_3DContourNode->SetData(NULL); + this->ShowInterpolationResult(false); + } + } + +void QmitkSurfaceBasedInterpolatorWidget::OnShowMarkers(bool state) +{ + mitk::DataStorage::SetOfObjects::ConstPointer allContourMarkers = m_DataStorage->GetSubset( + mitk::NodePredicateProperty::New("isContourMarker", mitk::BoolProperty::New(true))); + + for (mitk::DataStorage::SetOfObjects::ConstIterator it = allContourMarkers->Begin(); it != allContourMarkers->End(); ++it) + { + it->Value()->SetProperty("helper object", mitk::BoolProperty::New(!state)); + } + + mitk::SegTool2D::Pointer manualSegmentationTool; + + unsigned int numberOfExistingTools = m_ToolManager->GetTools().size(); + + for(unsigned int i = 0; i < numberOfExistingTools; i++) + { + manualSegmentationTool = dynamic_cast(m_ToolManager->GetToolById(i)); + + if (manualSegmentationTool) + { + manualSegmentationTool->SetShowMarkerNodes( state ); + } + } +} + +void QmitkSurfaceBasedInterpolatorWidget::StartUpdateInterpolationTimer() +{ + m_Timer->start(500); +} + +void QmitkSurfaceBasedInterpolatorWidget::StopUpdateInterpolationTimer() +{ + m_Timer->stop(); + m_InterpolatedSurfaceNode->SetProperty("color", mitk::ColorProperty::New(255.0,255.0,0.0)); + mitk::RenderingManager::GetInstance()->RequestUpdate(mitk::BaseRenderer::GetInstance( mitk::BaseRenderer::GetRenderWindowByName("stdmulti.widget4"))->GetRenderWindow() ); +} + +void QmitkSurfaceBasedInterpolatorWidget::ChangeSurfaceColor() +{ + float currentColor[3]; + m_InterpolatedSurfaceNode->GetColor(currentColor); + + float yellow[3] = {255.0,255.0,0.0}; + + if( currentColor[2] == yellow[2]) + { + m_InterpolatedSurfaceNode->SetProperty("color", mitk::ColorProperty::New(255.0,255.0,255.0)); + } + else + { + m_InterpolatedSurfaceNode->SetProperty("color", mitk::ColorProperty::New(yellow)); + } + m_InterpolatedSurfaceNode->Update(); + mitk::RenderingManager::GetInstance()->RequestUpdate(mitk::BaseRenderer::GetInstance( mitk::BaseRenderer::GetRenderWindowByName("stdmulti.widget4"))->GetRenderWindow()); +} + +void QmitkSurfaceBasedInterpolatorWidget::OnToolManagerWorkingDataModified() +{ + mitk::DataNode* workingNode = this->m_ToolManager->GetWorkingData(0); + if (!workingNode) + { + this->setEnabled(false); + return; + } + + mitk::LabelSetImage* workingImage = dynamic_cast< mitk::LabelSetImage* >( workingNode->GetData() ); + // TODO adapt tool manager so that this check is done there, e.g. convenience function +// Q_ASSERT(workingImage); + if (!workingImage) + { + this->setEnabled(false); + return; + } + + if (workingImage->GetDimension() > 4 || workingImage->GetDimension() < 3) + { + this->setEnabled(false); + return; + } + + m_WorkingImage = workingImage; + + this->setEnabled(true); +} + +void QmitkSurfaceBasedInterpolatorWidget::OnRunInterpolation() +{ + m_SurfaceBasedInterpolatorController->Interpolate(); +} + +void QmitkSurfaceBasedInterpolatorWidget::OnToggleWidgetActivation(bool enabled) +{ + Q_ASSERT(m_ToolManager); + + mitk::DataNode* workingNode = m_ToolManager->GetWorkingData(0); + if (!workingNode) return; + + m_Controls.m_btAccept->setEnabled(enabled); + m_Controls.m_cbShowPositionNodes->setEnabled(enabled); + + if (enabled) + m_Controls.m_btStart->setText("Stop"); + else + m_Controls.m_btStart->setText("Start"); + + for (unsigned int i = 0; i < m_ToolManager->GetTools().size(); i++) + { + mitk::SegTool2D* tool = dynamic_cast(m_ToolManager->GetToolById(i)); + if (tool) tool->SetEnable3DInterpolation( enabled ); + } + + if (enabled) + { + if (!m_DataStorage->Exists(m_InterpolatedSurfaceNode)) + { + m_DataStorage->Add( m_InterpolatedSurfaceNode ); + } + + if (!m_DataStorage->Exists(m_3DContourNode)) + { + m_DataStorage->Add( m_3DContourNode ); + } + + mitk::Vector3D spacing = m_WorkingImage->GetGeometry(0)->GetSpacing(); + double minSpacing (100); + double maxSpacing (0); + for (int i =0; i < 3; i++) + { + if (spacing[i] < minSpacing) + { + minSpacing = spacing[i]; + } + else if (spacing[i] > maxSpacing) + { + maxSpacing = spacing[i]; + } + } + + m_SurfaceBasedInterpolatorController->SetWorkingImage(m_WorkingImage); + m_SurfaceBasedInterpolatorController->SetActiveLabel(m_WorkingImage->GetActiveLabel()->GetValue()); + m_SurfaceBasedInterpolatorController->SetMaxSpacing(maxSpacing); + m_SurfaceBasedInterpolatorController->SetMinSpacing(minSpacing); + m_SurfaceBasedInterpolatorController->SetDistanceImageVolume(50000); + + int ret = QMessageBox::Yes; + + if (m_SurfaceBasedInterpolatorController->EstimatePortionOfNeededMemory() > 0.5) + { + QMessageBox msgBox; + msgBox.setText("Due to short handed system memory the 3D interpolation may be very slow!"); + msgBox.setInformativeText("Are you sure you want to activate the 3D interpolation?"); + msgBox.setStandardButtons(QMessageBox::No | QMessageBox::Yes); + ret = msgBox.exec(); + } + + if (m_Watcher.isRunning()) + m_Watcher.waitForFinished(); + + if (ret == QMessageBox::Yes) + { + m_Future = QtConcurrent::run(this, &QmitkSurfaceBasedInterpolatorWidget::OnRunInterpolation); + m_Watcher.setFuture(m_Future); + } + } + else + { + if (m_DataStorage->Exists(m_InterpolatedSurfaceNode)) + { + m_DataStorage->Remove( m_InterpolatedSurfaceNode ); + } + if (m_DataStorage->Exists(m_3DContourNode)) + { + m_DataStorage->Remove( m_3DContourNode ); + } + + mitk::UndoController::GetCurrentUndoModel()->Clear(); + } + + m_Activated = enabled; + + mitk::RenderingManager::GetInstance()->RequestUpdateAll(); +} + +void QmitkSurfaceBasedInterpolatorWidget::OnAcceptInterpolationClicked() +{ + if (m_InterpolatedSurfaceNode.IsNotNull() && m_InterpolatedSurfaceNode->GetData()) + { +// m_WorkingImage->SurfaceStamp(dynamic_cast(m_InterpolatedSurfaceNode->GetData()), false); + this->ShowInterpolationResult(false); + } +} + +void QmitkSurfaceBasedInterpolatorWidget::OnSurfaceInterpolationInfoChanged(const itk::EventObject& /*e*/) +{ + if (m_Activated) + { + if (m_Watcher.isRunning()) + m_Watcher.waitForFinished(); + + m_Future = QtConcurrent::run(this, &QmitkSurfaceBasedInterpolatorWidget::OnRunInterpolation); + m_Watcher.setFuture(m_Future); + } +} diff --git a/Modules/SegmentationUI/Qmitk/QmitkSurfaceBasedInterpolatorWidget.h b/Modules/SegmentationUI/Qmitk/QmitkSurfaceBasedInterpolatorWidget.h new file mode 100644 index 0000000000..7170a8231f --- /dev/null +++ b/Modules/SegmentationUI/Qmitk/QmitkSurfaceBasedInterpolatorWidget.h @@ -0,0 +1,126 @@ +/*=================================================================== + +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 QmitkSurfaceBasedInterpolatorWidgetWidget_h_Included +#define QmitkSurfaceBasedInterpolatorWidgetWidget_h_Included + +#include "MitkSegmentationUIExports.h" +#include "mitkDataNode.h" +#include "mitkDataStorage.h" +#include "mitkSurfaceBasedInterpolationController.h" +#include "mitkLabelSetImage.h" + +#include + +#include + +//For running 3D interpolation in background +#include +#include +#include +#include + +#include "ui_QmitkSurfaceBasedInterpolatorWidgetGUIControls.h" + +namespace mitk { + class ToolManager; +} + +/** + \brief GUI for surface-based interpolation. + + \ingroup ToolManagerEtAl + \ingroup Widgets + + \sa QmitkInteractiveSegmentation + \sa mitk::SurfaceBasedInterpolationController + + There is a separate page describing the general design of QmitkInteractiveSegmentation: \ref QmitkInteractiveSegmentationTechnicalPage + + QmitkSurfaceBasedInterpolatorWidgetController is responsible to watch the GUI, to notice, which slice is currently + visible. It triggers generation of interpolation suggestions and also triggers acception of suggestions. +*/ +class MITKSEGMENTATIONUI_EXPORT QmitkSurfaceBasedInterpolatorWidget : public QWidget +{ + Q_OBJECT + + public: + + QmitkSurfaceBasedInterpolatorWidget(QWidget* parent = 0, const char* name = 0); + virtual ~QmitkSurfaceBasedInterpolatorWidget(); + + void SetDataStorage(mitk::DataStorage& storage); + + void OnToolManagerWorkingDataModified(); + + /** + Just public because it is called by itk::Commands. You should not need to call this. + */ + void OnSurfaceInterpolationInfoChanged(const itk::EventObject&); + + /** + * @brief Set the visibility of the interpolation + */ + void ShowInterpolationResult(bool); + + Ui::QmitkSurfaceBasedInterpolatorWidgetGUIControls m_Controls; + + public slots: + + /** + \brief Reaction to "Start/Stop" button click + */ + void OnToggleWidgetActivation(bool); + + protected slots: + + void OnAcceptInterpolationClicked(); + + void OnSurfaceInterpolationFinished(); + + void OnRunInterpolation(); + + void OnShowMarkers(bool); + + void StartUpdateInterpolationTimer(); + + void StopUpdateInterpolationTimer(); + + void ChangeSurfaceColor(); + +private: + + mitk::SurfaceBasedInterpolationController::Pointer m_SurfaceBasedInterpolatorController; + + mitk::ToolManager* m_ToolManager; + + bool m_Activated; + + unsigned int m_SurfaceInterpolationInfoChangedObserverTag; + + mitk::DataNode::Pointer m_InterpolatedSurfaceNode; + mitk::DataNode::Pointer m_3DContourNode; + + mitk::DataStorage::Pointer m_DataStorage; + + mitk::LabelSetImage::Pointer m_WorkingImage; + + QFuture m_Future; + QFutureWatcher m_Watcher; + QTimer* m_Timer; +}; + +#endif diff --git a/Modules/SegmentationUI/Qmitk/QmitkSurfaceBasedInterpolatorWidgetGUIControls.ui b/Modules/SegmentationUI/Qmitk/QmitkSurfaceBasedInterpolatorWidgetGUIControls.ui new file mode 100644 index 0000000000..7f639be5fb --- /dev/null +++ b/Modules/SegmentationUI/Qmitk/QmitkSurfaceBasedInterpolatorWidgetGUIControls.ui @@ -0,0 +1,136 @@ + + + QmitkSurfaceBasedInterpolatorWidgetGUIControls + + + + 0 + 0 + 248 + 94 + + + + + 0 + 0 + + + + + 0 + 0 + + + + Surface-Based Interpolation + + + + + + + 2 + + + QLayout::SetMinimumSize + + + 6 + + + + + 6 + + + + + true + + + + 0 + 0 + + + + Start/Stop the 3D interpolation utility + + + Start + + + + :/Qmitk/Start.png + :/Qmitk/Stop.png:/Qmitk/Start.png + + + + 32 + 32 + + + + true + + + false + + + Qt::ToolButtonTextUnderIcon + + + Qt::NoArrow + + + + + + + true + + + + 0 + 0 + + + + Accept + + + + :/Qmitk/AcceptSurfaceInterpolation.png:/Qmitk/AcceptSurfaceInterpolation.png + + + + 32 + 32 + + + + Qt::ToolButtonTextUnderIcon + + + + + + + + + true + + + show position nodes + + + + + + + + + + + diff --git a/Modules/SegmentationUI/Qmitk/QmitkSurfaceStampWidget.cpp b/Modules/SegmentationUI/Qmitk/QmitkSurfaceStampWidget.cpp new file mode 100644 index 0000000000..daf931c1b5 --- /dev/null +++ b/Modules/SegmentationUI/Qmitk/QmitkSurfaceStampWidget.cpp @@ -0,0 +1,121 @@ +/*=================================================================== + +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 "QmitkSurfaceStampWidget.h" + +#include +#include +#include + +#include + + +QmitkSurfaceStampWidget::QmitkSurfaceStampWidget(QWidget* parent, const char* /*name*/) +: QWidget(parent), +m_ToolManager(NULL), +m_DataStorage(NULL) +{ + m_Controls.setupUi(this); + m_Controls.m_InformationWidget->hide(); + + m_ToolManager = mitk::ToolManagerProvider::GetInstance()->GetToolManager(); + assert(m_ToolManager); + m_ToolManager->ActivateTool(-1); + + mitk::NodePredicateAnd::Pointer m_SurfacePredicate = mitk::NodePredicateAnd::New(); + m_SurfacePredicate->AddPredicate(mitk::NodePredicateDataType::New("Surface")); + m_SurfacePredicate->AddPredicate(mitk::NodePredicateNot::New(mitk::NodePredicateProperty::New("helper object"))); + + m_Controls.m_cbSurfaceNodeSelector->SetPredicate( m_SurfacePredicate ); + + connect(m_Controls.m_pbStamp, SIGNAL(clicked()), this, SLOT(OnStamp())); + connect( m_Controls.m_cbShowInformation, SIGNAL(toggled(bool)), this, SLOT(OnShowInformation(bool)) ); + m_Controls.m_InformationWidget->hide(); +} + +QmitkSurfaceStampWidget::~QmitkSurfaceStampWidget() +{ +} + +void QmitkSurfaceStampWidget::SetDataStorage( mitk::DataStorage* storage ) +{ + m_DataStorage = storage; + m_Controls.m_cbSurfaceNodeSelector->SetDataStorage(m_DataStorage); +} + +void QmitkSurfaceStampWidget::OnStamp() +{ + mitk::DataNode* surfaceNode = m_Controls.m_cbSurfaceNodeSelector->GetSelectedNode(); + + if (!surfaceNode) + { + QMessageBox::information(this, "Surface Stamp", "Please load and select a surface before starting some action."); + return; + } + + m_ToolManager = mitk::ToolManagerProvider::GetInstance()->GetToolManager(); + assert(m_ToolManager); + m_ToolManager->ActivateTool(-1); + + mitk::Surface* surface = dynamic_cast(surfaceNode->GetData() ); + if ( !surface ) + { + QMessageBox::information(this, "Surface Stamp", "Please load and select a surface before starting some action."); + return; + } + + mitk::DataNode* workingNode = m_ToolManager->GetWorkingData(0); + + if (!workingNode) + { + QMessageBox::information( this, "Surface Stamp", "Please load and select a segmentation before starting some action."); + return; + } + + mitk::LabelSetImage* workingImage = dynamic_cast( workingNode->GetData() ); + + if (!workingImage) + { + QMessageBox::information( this, "Surface Stamp", "Please load and select a segmentation before starting some action."); + return; + } + + QApplication::setOverrideCursor( QCursor(Qt::BusyCursor) ); + + try + { +// workingImage->SurfaceStamp( surface, m_Controls.m_chkOverwrite->isChecked() ); + } + catch ( mitk::Exception & e ) + { + QApplication::restoreOverrideCursor(); + MITK_ERROR << "Exception caught: " << e.GetDescription(); + QMessageBox::information( this, "Surface Stamp", "Could not stamp the selected surface.\n See error log for details.\n"); + return; + } + + QApplication::restoreOverrideCursor(); + + mitk::RenderingManager::GetInstance()->RequestUpdateAll(); +} + +void QmitkSurfaceStampWidget::OnShowInformation(bool on) +{ + if (on) + m_Controls.m_InformationWidget->show(); + else + m_Controls.m_InformationWidget->hide(); +} diff --git a/Modules/SegmentationUI/Qmitk/QmitkSurfaceStampWidget.h b/Modules/SegmentationUI/Qmitk/QmitkSurfaceStampWidget.h new file mode 100644 index 0000000000..bc4df565f4 --- /dev/null +++ b/Modules/SegmentationUI/Qmitk/QmitkSurfaceStampWidget.h @@ -0,0 +1,64 @@ +/*=================================================================== + +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 QmitkSurfaceStampWidget_h_Included +#define QmitkSurfaceStampWidget_h_Included + +#include "MitkSegmentationUIExports.h" + +#include + +#include "ui_QmitkSurfaceStampWidgetGUIControls.h" + +namespace mitk { + class ToolManager; +} + +/** + \brief GUI for surface-based interpolation. + + \ingroup ToolManagerEtAl + \ingroup Widgets +*/ + +class MITKSEGMENTATIONUI_EXPORT QmitkSurfaceStampWidget : public QWidget +{ + Q_OBJECT + + public: + + QmitkSurfaceStampWidget(QWidget* parent = 0, const char* name = 0); + virtual ~QmitkSurfaceStampWidget(); + + + void SetDataStorage( mitk::DataStorage* storage ); + + protected slots: + + void OnShowInformation(bool); + + void OnStamp(); + +private: + + mitk::ToolManager* m_ToolManager; + + mitk::DataStorage* m_DataStorage; + + Ui::QmitkSurfaceStampWidgetGUIControls m_Controls; +}; + +#endif diff --git a/Modules/SegmentationUI/Qmitk/QmitkSurfaceStampWidgetGUIControls.ui b/Modules/SegmentationUI/Qmitk/QmitkSurfaceStampWidgetGUIControls.ui new file mode 100644 index 0000000000..2f7ff23dba --- /dev/null +++ b/Modules/SegmentationUI/Qmitk/QmitkSurfaceStampWidgetGUIControls.ui @@ -0,0 +1,139 @@ + + + QmitkSurfaceStampWidgetGUIControls + + + + 0 + 0 + 228 + 308 + + + + + 0 + 0 + + + + + 0 + 0 + + + + GUI Controls + + + + + + + 2 + + + 2 + + + + + + + force overwrite + + + + + + + Apply + + + + + + + + + + 0 + 0 + + + + + + + + + 0 + 0 + + + + usage information + + + + + + + + 0 + 0 + + + + + + + <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> +<html><head><meta name="qrichtext" content="1" /><style type="text/css"> +p, li { white-space: pre-wrap; } +</style></head><body style=" font-family:'MS Shell Dlg 2'; font-size:8pt; font-weight:400; font-style:normal;"> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Sans'; font-size:10pt;">This functionality fills the interior of the selected surface with the active label. Other labels within the same region will be overwritten if they are not locked.</span></p> +<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-family:'Sans'; font-size:10pt;"><br /></p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Sans'; font-size:10pt;">Select a surface from the combo box above.</span></p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Sans'; font-size:10pt;">Check &quot;Force Overwrite&quot; to force the filling of regions occupied with other labels.</span></p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Sans'; font-size:10pt;">Press &quot;Apply&quot; to run the tool.</span></p></body></html> + + + Qt::AlignVCenter + + + true + + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + QmitkDataStorageComboBox + QComboBox +
QmitkDataStorageComboBox.h
+
+
+ + + + +
diff --git a/Modules/SegmentationUI/files.cmake b/Modules/SegmentationUI/files.cmake index 29ee29119b..bb15743fe6 100644 --- a/Modules/SegmentationUI/files.cmake +++ b/Modules/SegmentationUI/files.cmake @@ -1,62 +1,87 @@ set( CPP_FILES Qmitk/QmitkAdaptiveRegionGrowingToolGUI.cpp Qmitk/QmitkBinaryThresholdToolGUI.cpp Qmitk/QmitkBinaryThresholdULToolGUI.cpp Qmitk/QmitkCalculateGrayValueStatisticsToolGUI.cpp Qmitk/QmitkConfirmSegmentationDialog.cpp Qmitk/QmitkCopyToClipBoardDialog.cpp Qmitk/QmitkDrawPaintbrushToolGUI.cpp Qmitk/QmitkErasePaintbrushToolGUI.cpp Qmitk/QmitkFastMarchingTool3DGUI.cpp Qmitk/QmitkFastMarchingToolGUI.cpp Qmitk/QmitkLiveWireTool2DGUI.cpp Qmitk/QmitkNewSegmentationDialog.cpp Qmitk/QmitkOtsuTool3DGUI.cpp Qmitk/QmitkPaintbrushToolGUI.cpp Qmitk/QmitkPickingToolGUI.cpp Qmitk/QmitkPixelManipulationToolGUI.cpp Qmitk/QmitkSlicesInterpolator.cpp Qmitk/QmitkToolGUI.cpp Qmitk/QmitkToolGUIArea.cpp Qmitk/QmitkToolReferenceDataSelectionBox.cpp Qmitk/QmitkToolRoiDataSelectionBox.cpp Qmitk/QmitkToolSelectionBox.cpp Qmitk/QmitkToolWorkingDataSelectionBox.cpp Qmitk/QmitkWatershedToolGUI.cpp +#Added from ML +Qmitk/QmitkLabelSetWidget.cpp +Qmitk/QmitkSurfaceStampWidget.cpp +Qmitk/QmitkMaskStampWidget.cpp +Qmitk/QmitkSliceBasedInterpolatorWidget.cpp +Qmitk/QmitkSurfaceBasedInterpolatorWidget.cpp +Qmitk/QmitkSearchLabelDialog.cpp ) set(MOC_H_FILES Qmitk/QmitkAdaptiveRegionGrowingToolGUI.h Qmitk/QmitkBinaryThresholdToolGUI.h Qmitk/QmitkBinaryThresholdULToolGUI.h Qmitk/QmitkCalculateGrayValueStatisticsToolGUI.h Qmitk/QmitkConfirmSegmentationDialog.h Qmitk/QmitkCopyToClipBoardDialog.h Qmitk/QmitkDrawPaintbrushToolGUI.h Qmitk/QmitkErasePaintbrushToolGUI.h Qmitk/QmitkFastMarchingTool3DGUI.h Qmitk/QmitkFastMarchingToolGUI.h Qmitk/QmitkLiveWireTool2DGUI.h Qmitk/QmitkNewSegmentationDialog.h Qmitk/QmitkOtsuTool3DGUI.h Qmitk/QmitkPaintbrushToolGUI.h Qmitk/QmitkPickingToolGUI.h Qmitk/QmitkPixelManipulationToolGUI.h Qmitk/QmitkSlicesInterpolator.h Qmitk/QmitkToolGUI.h Qmitk/QmitkToolGUIArea.h Qmitk/QmitkToolReferenceDataSelectionBox.h Qmitk/QmitkToolRoiDataSelectionBox.h Qmitk/QmitkToolSelectionBox.h Qmitk/QmitkToolWorkingDataSelectionBox.h Qmitk/QmitkWatershedToolGUI.h +#Added from ML +Qmitk/QmitkLabelSetWidget.h +Qmitk/QmitkSurfaceStampWidget.h +Qmitk/QmitkMaskStampWidget.h +Qmitk/QmitkSliceBasedInterpolatorWidget.h +Qmitk/QmitkSurfaceBasedInterpolatorWidget.h +Qmitk/QmitkSearchLabelDialog.h ) set(UI_FILES Qmitk/QmitkAdaptiveRegionGrowingToolGUIControls.ui Qmitk/QmitkConfirmSegmentationDialog.ui Qmitk/QmitkOtsuToolWidgetControls.ui Qmitk/QmitkPickingToolGUIControls.ui Qmitk/QmitkLiveWireTool2DGUIControls.ui +#Added from ML +Qmitk/QmitkLabelSetWidgetControls.ui +Qmitk/QmitkSurfaceStampWidgetGUIControls.ui +Qmitk/QmitkMaskStampWidgetGUIControls.ui +Qmitk/QmitkSliceBasedInterpolatorWidgetGUIControls.ui +Qmitk/QmitkSurfaceBasedInterpolatorWidgetGUIControls.ui +Qmitk/QmitkSearchLabelDialogGUI.ui ) + +set(QRC_FILES + resources/SegmentationUI.qrc +) \ No newline at end of file diff --git a/Modules/SegmentationUI/resources/AcceptAllInterpolations.png b/Modules/SegmentationUI/resources/AcceptAllInterpolations.png new file mode 100644 index 0000000000..a7dec4a81b Binary files /dev/null and b/Modules/SegmentationUI/resources/AcceptAllInterpolations.png differ diff --git a/Modules/SegmentationUI/resources/AcceptCurrentInterpolation.png b/Modules/SegmentationUI/resources/AcceptCurrentInterpolation.png new file mode 100644 index 0000000000..0b8a181f90 Binary files /dev/null and b/Modules/SegmentationUI/resources/AcceptCurrentInterpolation.png differ diff --git a/Modules/SegmentationUI/resources/AcceptSurfaceInterpolation.png b/Modules/SegmentationUI/resources/AcceptSurfaceInterpolation.png new file mode 100644 index 0000000000..a3041cfbd1 Binary files /dev/null and b/Modules/SegmentationUI/resources/AcceptSurfaceInterpolation.png differ diff --git a/Modules/SegmentationUI/resources/Accept_48x48.png b/Modules/SegmentationUI/resources/Accept_48x48.png new file mode 100644 index 0000000000..4bd302dddd Binary files /dev/null and b/Modules/SegmentationUI/resources/Accept_48x48.png differ diff --git a/Modules/SegmentationUI/resources/AddLayer_48x48.png b/Modules/SegmentationUI/resources/AddLayer_48x48.png new file mode 100644 index 0000000000..516de90c37 Binary files /dev/null and b/Modules/SegmentationUI/resources/AddLayer_48x48.png differ diff --git a/Modules/SegmentationUI/resources/AdvancedTools.png b/Modules/SegmentationUI/resources/AdvancedTools.png new file mode 100644 index 0000000000..880e0165bf Binary files /dev/null and b/Modules/SegmentationUI/resources/AdvancedTools.png differ diff --git a/Modules/SegmentationUI/resources/BooleanDifference_48x48.png b/Modules/SegmentationUI/resources/BooleanDifference_48x48.png new file mode 100644 index 0000000000..424faa7fe5 Binary files /dev/null and b/Modules/SegmentationUI/resources/BooleanDifference_48x48.png differ diff --git a/Modules/SegmentationUI/resources/BooleanIntersection_48x48.png b/Modules/SegmentationUI/resources/BooleanIntersection_48x48.png new file mode 100644 index 0000000000..9050fe4586 Binary files /dev/null and b/Modules/SegmentationUI/resources/BooleanIntersection_48x48.png differ diff --git a/Modules/SegmentationUI/resources/BooleanUnion_48x48.png b/Modules/SegmentationUI/resources/BooleanUnion_48x48.png new file mode 100644 index 0000000000..0cc1c491f4 Binary files /dev/null and b/Modules/SegmentationUI/resources/BooleanUnion_48x48.png differ diff --git a/Modules/SegmentationUI/resources/Cancel_48x48.png b/Modules/SegmentationUI/resources/Cancel_48x48.png new file mode 100644 index 0000000000..ea60501185 Binary files /dev/null and b/Modules/SegmentationUI/resources/Cancel_48x48.png differ diff --git a/Modules/SegmentationUI/resources/ClearSeeds_48x48.png b/Modules/SegmentationUI/resources/ClearSeeds_48x48.png new file mode 100644 index 0000000000..04c799b1e8 Binary files /dev/null and b/Modules/SegmentationUI/resources/ClearSeeds_48x48.png differ diff --git a/Modules/SegmentationUI/resources/CreateMask.png b/Modules/SegmentationUI/resources/CreateMask.png new file mode 100644 index 0000000000..590e47bdf0 Binary files /dev/null and b/Modules/SegmentationUI/resources/CreateMask.png differ diff --git a/Modules/SegmentationUI/resources/CreateSurface.png b/Modules/SegmentationUI/resources/CreateSurface.png new file mode 100644 index 0000000000..913db1e786 Binary files /dev/null and b/Modules/SegmentationUI/resources/CreateSurface.png differ diff --git a/Modules/SegmentationUI/resources/DeleteLayer_48x48.png b/Modules/SegmentationUI/resources/DeleteLayer_48x48.png new file mode 100644 index 0000000000..219c1ac093 Binary files /dev/null and b/Modules/SegmentationUI/resources/DeleteLayer_48x48.png differ diff --git a/Modules/SegmentationUI/resources/EraseLabel.png b/Modules/SegmentationUI/resources/EraseLabel.png new file mode 100644 index 0000000000..4b62db240f Binary files /dev/null and b/Modules/SegmentationUI/resources/EraseLabel.png differ diff --git a/Modules/SegmentationUI/resources/Help_48x48.png b/Modules/SegmentationUI/resources/Help_48x48.png new file mode 100644 index 0000000000..a5bae86cae Binary files /dev/null and b/Modules/SegmentationUI/resources/Help_48x48.png differ diff --git a/Modules/SegmentationUI/resources/ImportLabelSet_48x48.png b/Modules/SegmentationUI/resources/ImportLabelSet_48x48.png new file mode 100644 index 0000000000..88cfb84854 Binary files /dev/null and b/Modules/SegmentationUI/resources/ImportLabelSet_48x48.png differ diff --git a/Modules/SegmentationUI/resources/ImportLabeledImage_48x48.png b/Modules/SegmentationUI/resources/ImportLabeledImage_48x48.png new file mode 100644 index 0000000000..03cf5a3db2 Binary files /dev/null and b/Modules/SegmentationUI/resources/ImportLabeledImage_48x48.png differ diff --git a/Modules/SegmentationUI/resources/LockExterior_48x48.png b/Modules/SegmentationUI/resources/LockExterior_48x48.png new file mode 100644 index 0000000000..f89e0b306b Binary files /dev/null and b/Modules/SegmentationUI/resources/LockExterior_48x48.png differ diff --git a/Modules/SegmentationUI/resources/MergeLabels.png b/Modules/SegmentationUI/resources/MergeLabels.png new file mode 100644 index 0000000000..676cd269e7 Binary files /dev/null and b/Modules/SegmentationUI/resources/MergeLabels.png differ diff --git a/Modules/SegmentationUI/resources/NewLabel_48x48.png b/Modules/SegmentationUI/resources/NewLabel_48x48.png new file mode 100644 index 0000000000..c1e9549eaf Binary files /dev/null and b/Modules/SegmentationUI/resources/NewLabel_48x48.png differ diff --git a/Modules/SegmentationUI/resources/NextLayer_48x48.png b/Modules/SegmentationUI/resources/NextLayer_48x48.png new file mode 100644 index 0000000000..a633f21d0f Binary files /dev/null and b/Modules/SegmentationUI/resources/NextLayer_48x48.png differ diff --git a/Modules/SegmentationUI/resources/PreviousLayer_48x48.png b/Modules/SegmentationUI/resources/PreviousLayer_48x48.png new file mode 100644 index 0000000000..9a06cc7a5b Binary files /dev/null and b/Modules/SegmentationUI/resources/PreviousLayer_48x48.png differ diff --git a/Modules/SegmentationUI/resources/RandomColor.png b/Modules/SegmentationUI/resources/RandomColor.png new file mode 100644 index 0000000000..91036d5213 Binary files /dev/null and b/Modules/SegmentationUI/resources/RandomColor.png differ diff --git a/Modules/SegmentationUI/resources/RemoveLabel.png b/Modules/SegmentationUI/resources/RemoveLabel.png new file mode 100644 index 0000000000..7b98895fc8 Binary files /dev/null and b/Modules/SegmentationUI/resources/RemoveLabel.png differ diff --git a/Modules/SegmentationUI/resources/RenameLabel.png b/Modules/SegmentationUI/resources/RenameLabel.png new file mode 100644 index 0000000000..479b32b5e2 Binary files /dev/null and b/Modules/SegmentationUI/resources/RenameLabel.png differ diff --git a/Modules/SegmentationUI/resources/Run_48x48.png b/Modules/SegmentationUI/resources/Run_48x48.png new file mode 100644 index 0000000000..49451510fa Binary files /dev/null and b/Modules/SegmentationUI/resources/Run_48x48.png differ diff --git a/Modules/SegmentationUI/resources/SegmentationInteractor_48x48.png b/Modules/SegmentationUI/resources/SegmentationInteractor_48x48.png new file mode 100644 index 0000000000..a98edc0ba3 Binary files /dev/null and b/Modules/SegmentationUI/resources/SegmentationInteractor_48x48.png differ diff --git a/Modules/SegmentationUI/resources/SegmentationUI.qrc b/Modules/SegmentationUI/resources/SegmentationUI.qrc new file mode 100644 index 0000000000..6e151cbcfe --- /dev/null +++ b/Modules/SegmentationUI/resources/SegmentationUI.qrc @@ -0,0 +1,39 @@ + + + AcceptCurrentInterpolation.png + AcceptAllInterpolations.png + AcceptSurfaceInterpolation.png + BooleanDifference_48x48.png + BooleanIntersection_48x48.png + BooleanUnion_48x48.png + Accept_48x48.png + Cancel_48x48.png + Run_48x48.png + ImportLabelSet_48x48.png + ImportLabeledImage_48x48.png + DeleteLayer_48x48.png + PreviousLayer_48x48.png + NextLayer_48x48.png + AddLayer_48x48.png + SegmentationInteractor_48x48.png + LockExterior_48x48.png + UnlockExterior_48x48.png + NewLabel_48x48.png + ClearSeeds_48x48.png + Start.png + Stop.png + Help_48x48.png + AdvancedTools.png + visible.png + invisible.png + lock.png + unlock.png + MergeLabels.png + RemoveLabel.png + EraseLabel.png + CreateSurface.png + CreateMask.png + RandomColor.png + RenameLabel.png + + diff --git a/Modules/SegmentationUI/resources/Start.png b/Modules/SegmentationUI/resources/Start.png new file mode 100644 index 0000000000..409e25942f Binary files /dev/null and b/Modules/SegmentationUI/resources/Start.png differ diff --git a/Modules/SegmentationUI/resources/Stop.png b/Modules/SegmentationUI/resources/Stop.png new file mode 100644 index 0000000000..0b44a81fff Binary files /dev/null and b/Modules/SegmentationUI/resources/Stop.png differ diff --git a/Modules/SegmentationUI/resources/UnlockExterior_48x48.png b/Modules/SegmentationUI/resources/UnlockExterior_48x48.png new file mode 100644 index 0000000000..69cb4beac0 Binary files /dev/null and b/Modules/SegmentationUI/resources/UnlockExterior_48x48.png differ diff --git a/Modules/SegmentationUI/resources/invisible.png b/Modules/SegmentationUI/resources/invisible.png new file mode 100644 index 0000000000..bf6cf840af Binary files /dev/null and b/Modules/SegmentationUI/resources/invisible.png differ diff --git a/Modules/SegmentationUI/resources/lock.png b/Modules/SegmentationUI/resources/lock.png new file mode 100644 index 0000000000..230fcac038 Binary files /dev/null and b/Modules/SegmentationUI/resources/lock.png differ diff --git a/Modules/SegmentationUI/resources/unlock.png b/Modules/SegmentationUI/resources/unlock.png new file mode 100644 index 0000000000..b683d6a2ba Binary files /dev/null and b/Modules/SegmentationUI/resources/unlock.png differ diff --git a/Modules/SegmentationUI/resources/visible.png b/Modules/SegmentationUI/resources/visible.png new file mode 100644 index 0000000000..ad943ad41c Binary files /dev/null and b/Modules/SegmentationUI/resources/visible.png differ diff --git a/Modules/SurfaceInterpolation/CMakeLists.txt b/Modules/SurfaceInterpolation/CMakeLists.txt index cab2c46a95..ffec5b6605 100644 --- a/Modules/SurfaceInterpolation/CMakeLists.txt +++ b/Modules/SurfaceInterpolation/CMakeLists.txt @@ -1,7 +1,8 @@ MITK_CREATE_MODULE( - DEPENDS MitkImageExtraction MitkImageStatistics + DEPENDS MitkImageExtraction MitkContourModel MitkAlgorithmsExt MitkImageStatistics PACKAGE_DEPENDS PUBLIC Eigen + #DEPENDS MitkAlgorithmsExt MitkContourModel WARNINGS_AS_ERRORS ) add_subdirectory(Testing) diff --git a/Modules/SurfaceInterpolation/files.cmake b/Modules/SurfaceInterpolation/files.cmake index dcd61f705e..7d2db9fb04 100644 --- a/Modules/SurfaceInterpolation/files.cmake +++ b/Modules/SurfaceInterpolation/files.cmake @@ -1,9 +1,10 @@ set(CPP_FILES mitkComputeContourSetNormalsFilter.cpp mitkCreateDistanceImageFromSurfaceFilter.cpp mitkImageToPointCloudFilter.cpp mitkPlaneProposer.cpp mitkPointCloudScoringFilter.cpp mitkReduceContourSetFilter.cpp mitkSurfaceInterpolationController.cpp + mitkSurfaceBasedInterpolationController.cpp ) diff --git a/Modules/SurfaceInterpolation/mitkSurfaceBasedInterpolationController.cpp b/Modules/SurfaceInterpolation/mitkSurfaceBasedInterpolationController.cpp new file mode 100644 index 0000000000..8594bdf5ec --- /dev/null +++ b/Modules/SurfaceInterpolation/mitkSurfaceBasedInterpolationController.cpp @@ -0,0 +1,300 @@ +/*=================================================================== + +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 "mitkSurfaceBasedInterpolationController.h" + +#include "mitkMemoryUtilities.h" +#include "mitkImageAccessByItk.h" +#include "mitkImageCast.h" +#include "mitkImageToSurfaceFilter.h" +#include "mitkRestorePlanePositionOperation.h" +#include "mitkContourModelToSurfaceFilter.h" +#include "mitkInteractionConst.h" +#include "mitkColorProperty.h" +#include "mitkProperties.h" +#include "mitkIOUtil.h" +#include "mitkComputeContourSetNormalsFilter.h" + +#include "vtkPolyData.h" +#include "vtkSmartPointer.h" +#include "vtkAppendPolyData.h" +#include "mitkVtkRepresentationProperty.h" +#include "vtkProperty.h" + +#include + +mitk::SurfaceBasedInterpolationController::SurfaceBasedInterpolationController() : +m_MinSpacing(1.0), +m_MaxSpacing(1.0), +m_WorkingImage(NULL), +m_ActiveLabel(0) +{ + this->Initialize(); +} + +mitk::SurfaceBasedInterpolationController::~SurfaceBasedInterpolationController() +{ + ContourListMap::iterator it = m_MapOfContourLists.begin(); + for (; it != m_MapOfContourLists.end(); it++) + { + for (unsigned int j = 0; j < m_MapOfContourLists[(*it).first].size(); ++j) + { + delete(m_MapOfContourLists[(*it).first].at(j).second); + } + m_MapOfContourLists.erase(it); + } +} + +void mitk::SurfaceBasedInterpolationController::Initialize() +{ + m_InterpolateSurfaceFilter = CreateDistanceImageFromSurfaceFilter::New(); + m_InterpolateSurfaceFilter->SetUseProgressBar(false); + + m_Contours = Surface::New(); + + m_InterpolationResult = NULL; +} + +mitk::SurfaceBasedInterpolationController* mitk::SurfaceBasedInterpolationController::GetInstance() +{ + static mitk::SurfaceBasedInterpolationController* m_Instance; + + if ( m_Instance == 0) + { + m_Instance = new SurfaceBasedInterpolationController(); + } + + return m_Instance; +} + +void mitk::SurfaceBasedInterpolationController::AddNewContour(mitk::ContourModel::Pointer newContour, RestorePlanePositionOperation* op) +{ + if (m_ActiveLabel == 0) + return; + + AffineTransform3D::Pointer transform = AffineTransform3D::New(); + transform = op->GetTransform(); + + mitk::Vector3D direction = op->GetDirectionVector(); + int pos(-1); + + for (unsigned int i=0; i < m_MapOfContourLists[m_ActiveLabel].size(); i++) + { + itk::Matrix diffM = transform->GetMatrix()-m_MapOfContourLists[m_ActiveLabel].at(i).second->GetTransform()->GetMatrix(); + bool isSameMatrix(true); + for (unsigned int j = 0; j < 3; j++) + { + if (fabs(diffM[j][0]) > 0.0001 && fabs(diffM[j][1]) > 0.0001 && fabs(diffM[j][2]) > 0.0001) + { + isSameMatrix = false; + break; + } + } + itk::Vector diffV = m_MapOfContourLists[m_ActiveLabel].at(i).second->GetTransform()->GetOffset()-transform->GetOffset(); + if ( isSameMatrix && m_MapOfContourLists[m_ActiveLabel].at(i).second->GetPos() == op->GetPos() && (fabs(diffV[0]) < 0.0001 && fabs(diffV[1]) < 0.0001 && fabs(diffV[2]) < 0.0001) ) + { + pos = i; + break; + } + } + + if (pos == -1 && newContour->GetNumberOfVertices() > 0) // add a new contour + { + mitk::RestorePlanePositionOperation* newOp = new mitk::RestorePlanePositionOperation (OpRESTOREPLANEPOSITION, op->GetWidth(), + op->GetHeight(), op->GetSpacing(), op->GetPos(), direction, transform); + ContourPositionPair newData = std::make_pair(newContour,newOp); + m_MapOfContourLists[m_ActiveLabel].push_back(newData); + } + else if (pos != -1) // replace existing contour + { + m_MapOfContourLists[m_ActiveLabel].at(pos).first = newContour; + } + + this->Modified(); +} + +void mitk::SurfaceBasedInterpolationController::Interpolate() +{ + if (m_MapOfContourLists[m_ActiveLabel].size() < 2) + { + //If no interpolation is possible reset the interpolation result + m_InterpolationResult = NULL; + return; + } + + m_InterpolateSurfaceFilter->Reset(); + + //MLI TODO + //m_InterpolateSurfaceFilter->SetReferenceImage( m_WorkingImage ); + + //CreateDistanceImageFromSurfaceFilter::Pointer interpolateSurfaceFilter = CreateDistanceImageFromSurfaceFilter::New(); + vtkSmartPointer polyDataAppender = vtkSmartPointer::New(); + + for (unsigned int i=0; i < m_MapOfContourLists[m_ActiveLabel].size(); i++) + { + mitk::ContourModelToSurfaceFilter::Pointer converter = mitk::ContourModelToSurfaceFilter::New(); + converter->SetInput(m_MapOfContourLists[m_ActiveLabel].at(i).first); + converter->Update(); + + mitk::Surface::Pointer surface = converter->GetOutput(); + surface->DisconnectPipeline(); + + ReduceContourSetFilter::Pointer reduceFilter = ReduceContourSetFilter::New(); + reduceFilter->SetUseProgressBar(false); + reduceFilter->SetInput(surface); + reduceFilter->SetMinSpacing(m_MinSpacing); + reduceFilter->SetMaxSpacing(m_MaxSpacing); + reduceFilter->Update(); + + ComputeContourSetNormalsFilter::Pointer normalsFilter = ComputeContourSetNormalsFilter::New(); + normalsFilter->SetUseProgressBar(false); + normalsFilter->SetInput(reduceFilter->GetOutput()); + normalsFilter->SetMaxSpacing(m_MaxSpacing); + //MLI TODO + //normalsFilter->SetSegmentationBinaryImage(m_WorkingImage, m_ActiveLabel); + //normalsFilter->Update(); + + m_InterpolateSurfaceFilter->SetInput(i, normalsFilter->GetOutput()); + + polyDataAppender->AddInputData(reduceFilter->GetOutput()->GetVtkPolyData()); + } + + polyDataAppender->Update(); + m_Contours->SetVtkPolyData(polyDataAppender->GetOutput()); + + // update the filter and get the resulting distance-image + m_InterpolateSurfaceFilter->Update(); + Image::Pointer distanceImage = m_InterpolateSurfaceFilter->GetOutput(); + if (distanceImage.IsNull()) + { + //If no interpolation is possible reset the interpolation result + m_InterpolationResult = NULL; + return; + } + + // create a surface from the distance-image + mitk::ImageToSurfaceFilter::Pointer imageToSurfaceFilter = mitk::ImageToSurfaceFilter::New(); + imageToSurfaceFilter->SetInput( distanceImage ); + imageToSurfaceFilter->SetThreshold( 0 ); + imageToSurfaceFilter->SetSmooth(true); + imageToSurfaceFilter->SetSmoothIteration(20); + imageToSurfaceFilter->Update(); + m_InterpolationResult = imageToSurfaceFilter->GetOutput(); + + m_InterpolationResult->DisconnectPipeline(); +} + +mitk::Surface::Pointer mitk::SurfaceBasedInterpolationController::GetInterpolationResult() +{ + return m_InterpolationResult; +} + +mitk::Surface* mitk::SurfaceBasedInterpolationController::GetContoursAsSurface() +{ + return m_Contours; +} + +void mitk::SurfaceBasedInterpolationController::SetMinSpacing(double minSpacing) +{ + m_MinSpacing = minSpacing; +} + +void mitk::SurfaceBasedInterpolationController::SetMaxSpacing(double maxSpacing) +{ + m_MaxSpacing = maxSpacing; +} + +void mitk::SurfaceBasedInterpolationController::SetDistanceImageVolume(unsigned int value) +{ + m_DistanceImageVolume = value; +} + +void mitk::SurfaceBasedInterpolationController::SetWorkingImage(Image* workingImage) +{ + m_WorkingImage = workingImage; +} + +mitk::Image* mitk::SurfaceBasedInterpolationController::GetImage() +{ + return m_InterpolateSurfaceFilter->GetOutput(); +} + +double mitk::SurfaceBasedInterpolationController::EstimatePortionOfNeededMemory() +{ + double numberOfPoints = 0.0; + for (unsigned int i = 0; i < m_MapOfContourLists[m_ActiveLabel].size(); i++) + { + numberOfPoints += m_MapOfContourLists[m_ActiveLabel].at(i).first->GetNumberOfVertices()*3; + } + + double sizeOfPoints = pow(numberOfPoints,2)*sizeof(double); + double totalMem = mitk::MemoryUtilities::GetTotalSizeOfPhysicalRam(); + double percentage = sizeOfPoints/totalMem; + return percentage; +} + +void mitk::SurfaceBasedInterpolationController::SetActiveLabel(int activeLabel) +{ + if (m_ActiveLabel == activeLabel) + return; + + if (activeLabel == 0) + return; + + m_ActiveLabel = activeLabel; + + ContourListMap::iterator it = m_MapOfContourLists.find(m_ActiveLabel); + + if (it == m_MapOfContourLists.end()) + { + ContourPositionPairList newList; + m_MapOfContourLists.insert(std::pair(m_ActiveLabel, newList)); + m_InterpolationResult = NULL; + } + + this->Modified(); +} + +/* +void mitk::SurfaceBasedInterpolationController::RemoveSegmentationFromContourList(mitk::Image *segmentation) +{ + if (segmentation != 0) + { + m_MapOfContourLists.erase(segmentation); + if (m_SelectedSegmentation == segmentation) + { + SetSegmentationImage(NULL); + m_SelectedSegmentation = 0; + } + } +} +*/ + +/* +void mitk::SurfaceBasedInterpolationController::OnSegmentationDeleted(const itk::Object *caller, const itk::EventObject &event) +{ + mitk::Image* tempImage = dynamic_cast(const_cast(caller)); + if (tempImage) + { + RemoveSegmentationFromContourList(tempImage); + if (tempImage == m_SelectedSegmentation) + { + SetSegmentationImage(NULL); + m_SelectedSegmentation = 0; + } + } +} +*/ \ No newline at end of file diff --git a/Modules/SurfaceInterpolation/mitkSurfaceBasedInterpolationController.h b/Modules/SurfaceInterpolation/mitkSurfaceBasedInterpolationController.h new file mode 100644 index 0000000000..46ed1d8b67 --- /dev/null +++ b/Modules/SurfaceInterpolation/mitkSurfaceBasedInterpolationController.h @@ -0,0 +1,150 @@ +/*=================================================================== + +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 mitkSurfaceBasedInterpolationController_h_Included +#define mitkSurfaceBasedInterpolationController_h_Included + +//#include "mitkCommon.h" +#include +#include "mitkRestorePlanePositionOperation.h" +#include "mitkSurface.h" +#include "mitkContourModel.h" + +#include "mitkCreateDistanceImageFromSurfaceFilter.h" +#include "mitkReduceContourSetFilter.h" + +#include "mitkProgressBar.h" + +namespace mitk +{ + class RestorePlanePositionOperation; + + class MITKSURFACEINTERPOLATION_EXPORT SurfaceBasedInterpolationController : public itk::Object + { + public: + + mitkClassMacroItkParent(SurfaceBasedInterpolationController, itk::Object) + itkFactorylessNewMacro(Self) + itkCloneMacro(Self) + + static SurfaceBasedInterpolationController* GetInstance(); + + /** + * Adds a new extracted contour to the list + */ + void AddNewContour(ContourModel::Pointer newContour, RestorePlanePositionOperation *op); + + /** + * Launches the interpolation method. A surface mesh is generated out of the given extracted contours. + */ + void Interpolate(); + + /** + * Retrieves a surface mesh resulting from the interpolation of the given extracted contours. + */ + mitk::Surface::Pointer GetInterpolationResult(); + + /** + * Sets the minimum spacing of the current selected segmentation + * This is needed since the contour points we reduced before, are used to interpolate the surface + */ + void SetMinSpacing(double minSpacing); + + /** + * Sets the minimum spacing of the current selected segmentation + * This is needed since the contour points we reduced before, are used to interpolate the surface + */ + void SetMaxSpacing(double maxSpacing); + + /** + * Sets the volume i.e. the number of pixels that the distance image should have + * By evaluation we found out that 50.000 pixel delivers a good result + */ + void SetDistanceImageVolume(unsigned int value); + + /** + * Sets the working image used by the interpolation method. + * This is needed because the calculation of the normals needs to now wheather a normal points toward the inside of a segmentation or not + */ + void SetWorkingImage(Image* workingImage); + + /** + * Retrieves the input contours as a mitk::Surface + */ + Surface* GetContoursAsSurface(); + + /** + * Sets the current list of contour points which is used for the surface interpolation + * @param activeLabel The active label in the current working image + */ + void SetActiveLabel(int activeLabel); + + mitk::Image* GetImage(); + + /** + * Estimates the memory that is needed to build up the equation system for the interpolation. + * \returns The percentage of the real memory which will be used by the interpolation calculation + */ + double EstimatePortionOfNeededMemory(); + + protected: + + SurfaceBasedInterpolationController(); + + ~SurfaceBasedInterpolationController(); + + void Initialize(); + + private: + +// void OnSegmentationDeleted(const itk::Object *caller, const itk::EventObject &event); + /* + struct ContourPositionPair { + ContourModel::Pointer contour; + RestorePlanePositionOperation* position; + }; + */ + typedef std::pair< ContourModel::Pointer, RestorePlanePositionOperation* > ContourPositionPair; + typedef std::vector< ContourPositionPair > ContourPositionPairList; + typedef std::map ContourListMap; + + //ReduceContourSetFilter::Pointer m_ReduceFilter; + //ComputeContourSetNormalsFilter::Pointer m_NormalsFilter; + CreateDistanceImageFromSurfaceFilter::Pointer m_InterpolateSurfaceFilter; + + double m_MinSpacing; + double m_MaxSpacing; + + unsigned int m_DistanceImageVolume; + + Image* m_WorkingImage; + + Surface::Pointer m_Contours; + +// vtkSmartPointer m_PolyData; + + ContourListMap m_MapOfContourLists; + + mitk::Surface::Pointer m_InterpolationResult; + +// unsigned int m_CurrentNumberOfReducedContours; + + int m_ActiveLabel; + +// std::map m_SegmentationObserverTags; + }; +} +#endif diff --git a/Plugins/PluginList.cmake b/Plugins/PluginList.cmake index 2f3421d6c5..095e181ca9 100644 --- a/Plugins/PluginList.cmake +++ b/Plugins/PluginList.cmake @@ -1,72 +1,73 @@ # Plug-ins must be ordered according to their dependencies set(MITK_PLUGINS org.blueberry.core.runtime:ON org.blueberry.core.expressions:OFF org.blueberry.core.commands:OFF org.blueberry.core.jobs:OFF org.blueberry.ui.qt:OFF org.blueberry.ui.qt.help:OFF org.blueberry.ui.qt.log:ON org.blueberry.ui.qt.objectinspector:OFF #org.blueberry.test:ON #org.blueberry.uitest:ON #Testing/org.blueberry.core.runtime.tests:ON #Testing/org.blueberry.osgi.tests:ON org.mitk.core.services:ON org.mitk.gui.common:ON org.mitk.planarfigure:ON org.mitk.core.ext:OFF org.mitk.core.jobs:OFF org.mitk.diffusionimaging:OFF org.mitk.simulation:OFF org.mitk.gui.qt.application:ON org.mitk.gui.qt.coreapplication:OFF org.mitk.gui.qt.ext:OFF org.mitk.gui.qt.extapplication:OFF org.mitk.gui.qt.common:ON org.mitk.gui.qt.stdmultiwidgeteditor:ON org.mitk.gui.qt.common.legacy:OFF org.mitk.gui.qt.cmdlinemodules:OFF org.mitk.gui.qt.diffusionimagingapp:OFF org.mitk.gui.qt.datamanager:ON org.mitk.gui.qt.datamanagerlight:OFF org.mitk.gui.qt.properties:ON org.mitk.gui.qt.basicimageprocessing:OFF org.mitk.gui.qt.dicom:OFF org.mitk.gui.qt.diffusionimaging:OFF org.mitk.gui.qt.dosevisualization:OFF org.mitk.gui.qt.geometrytools:OFF org.mitk.gui.qt.igtexamples:OFF org.mitk.gui.qt.igttracking:OFF org.mitk.gui.qt.openigtlink:OFF org.mitk.gui.qt.imagecropper:OFF org.mitk.gui.qt.imagenavigator:ON org.mitk.gui.qt.viewnavigator:OFF org.mitk.gui.qt.materialeditor:OFF org.mitk.gui.qt.measurementtoolbox:OFF org.mitk.gui.qt.moviemaker:OFF org.mitk.gui.qt.pointsetinteraction:OFF org.mitk.gui.qt.pointsetinteractionmultispectrum:OFF org.mitk.gui.qt.python:OFF org.mitk.gui.qt.registration:OFF org.mitk.gui.qt.remeshing:OFF org.mitk.gui.qt.segmentation:OFF org.mitk.gui.qt.simulation:OFF org.mitk.gui.qt.aicpregistration:OFF org.mitk.gui.qt.toftutorial:OFF org.mitk.gui.qt.tofutil:OFF org.mitk.gui.qt.tubegraph:OFF org.mitk.gui.qt.ugvisualization:OFF org.mitk.gui.qt.ultrasound:OFF org.mitk.gui.qt.volumevisualization:OFF org.mitk.gui.qt.eventrecorder:OFF org.mitk.gui.qt.xnat:OFF org.mitk.gui.qt.spectrocamrecorder:OFF org.mitk.gui.qt.classificationsegmentation:OFF + org.mitk.gui.qt.multilabelsegmentation:ON ) diff --git a/Plugins/org.mitk.gui.qt.multilabelsegmentation/CMakeLists.txt b/Plugins/org.mitk.gui.qt.multilabelsegmentation/CMakeLists.txt new file mode 100644 index 0000000000..235d87da4b --- /dev/null +++ b/Plugins/org.mitk.gui.qt.multilabelsegmentation/CMakeLists.txt @@ -0,0 +1,9 @@ +project(org_mitk_gui_qt_multilabelsegmentation) + +include_directories(${CTK_INCLUDE_DIRS}) + +mitk_create_plugin( + EXPORT_DIRECTIVE MITK_QT_SEGMENTATION + EXPORTED_INCLUDE_SUFFIXES src + MODULE_DEPENDS MitkSegmentation MitkSegmentationUI +) diff --git a/Plugins/org.mitk.gui.qt.multilabelsegmentation/documentation/UserManual/QmitkSegmentationTechnical.dox b/Plugins/org.mitk.gui.qt.multilabelsegmentation/documentation/UserManual/QmitkSegmentationTechnical.dox new file mode 100644 index 0000000000..b8992e8a55 --- /dev/null +++ b/Plugins/org.mitk.gui.qt.multilabelsegmentation/documentation/UserManual/QmitkSegmentationTechnical.dox @@ -0,0 +1,101 @@ +/** + +\page QmitkSegmentationTechnicalPage Technical design of QmitkSegmentation + +\li \ref QmitkSegmentationTechnicalPage2 +\li \ref QmitkSegmentationTechnicalPage3 +\li \ref QmitkSegmentationTechnicalPage4 + +\section QmitkSegmentationTechnicalPage2 Introduction + +QmitkSegmentation was designed for the liver resection planning +project "ReLiver". +The goal was a stable, well-documented, extensible, and testable +re-implementation of a functionality called "ERIS", which was used for manual +segmentation in 2D slices of 3D or 3D+t images. +Re-implementation was chosen because it seemed to be easier to write +documentation and tests for newly developed code. In addition, the old code had +some design weaknesses (e.g. a monolithic class), which would be hard to +maintain in the future. + +By now Segmentation is a well tested and easily extensible vehicle for all kinds of interactive +segmentation applications. A separate page describes how you can extend Segmentation with new +tools in a shared object (DLL): \ref toolextensions. + +\section QmitkSegmentationTechnicalPage3 Overview of tasks + +We identified the following major tasks: + +
    +
  1. Management of images: what is the original patient image, what +images are the active segmentations? +
  2. Management of drawing tools: there is a set of drawing tools, one at +a time is active, that is, someone has to decide which tool will receive mouse +(and other) events. +
  3. Drawing tools: each tool can modify a segmentation in reaction to +user interaction. To do so, the tools have to know about the relevant images. +
  4. Slice manipulation: drawing tools need to have means to extract a +single slice from an image volume and to write a single slice back into an image +volume. +
  5. Interpolation of unsegmented slices: some class has to keep track of +all the segmentations in a volume and generate suggestions for missing slices. +This should be possible in all three orthogonal slice direction. +
  6. Undo: Slice manipulations should be undoable, no matter whether a +tool or the interpolation mechanism changed something. +
  7. GUI: Integration of everything. +
+ + +\section QmitkSegmentationTechnicalPage4 Classes involved + +The above blocks correspond to a number of classes. Here is an overview of all +related classes with their responsibilities and relations: + +\imageMacro{"interactive_segmentation_classes.png","html",12} + +
    +
  1. Management of images: mitk::ToolManager has a set of reference +data (original images) and a second set of working data (segmentations). +mitk::Tool objects know a ToolManager and can ask the manager for the currently +relevant images. There are two GUI elements that enable +the user to modify the set of reference and working images (QmitkToolReferenceDataSelectionBox and QmitkToolWorkingDataSelectionBox). GUI and non-GUI +classes are coupled by itk::Events (non-GUI to GUI) and direct method calls (GUI +to non-GUI). +
  2. Management of drawing tools: As a second task, ToolManager manages all available tools and makes sure that one at a time is able to receive MITK events. +The GUI for selecting tools is implemented in QmitkToolSelectionBox. +
  3. Drawing tools: Drawing tools all inherit from mitk::Tool, which is a +mitk::StateMachine. There is a number of derivations from Tool, each offering +some helper methods for specific sub-classes, like manipulation of 2D slices. +Tools are instantiated through the itk::ObjectFactory, which means that there is +also one factory for each tool (e.g. mitk::AddContourToolFactory). For the GUI representation, each tool has an +identification, consisting of a name and an icon (XPM). The actual drawing +methods are mainly implemented in mitk::SegTool2D (helper methods) and its +sub-classes for region growing, freehand drawing, etc. +
  4. Slice manipulation: There are two filters for manipulation of slices +inside a 3D image volume. mitk::ExtractImageFilter retrieves a single 2D slice +from a 3D volume. mitk::OverwriteSliceImageFilter replaces a slice inside a 3D +volume with a second slice which is a parameter to the filter. These classes are +used extensively by most of the tools to fulfill their task. +mitk::OverwriteSliceImageFilter cooperates with the interpolation classes to +inform them of single slice modifications. +
  5. Interpolation of unsegmented slices: There are two classes involved +in interpolation: mitk::SegmentationInterpolationController knows a mitk::Image (the +segmentation) and scans its contents for slices with non-zero pixels. It keeps +track of changes in the image and is always able to tell, which neighbors of a +slice (in the three orthogonal slice directions) contain segmentations. The +class also performs this interpolation for single slices on demand. +Again, we have a second class responsible for the GUI: +QmitkSlicesInterpolator enables/disables interpolation and offers to +accept interpolations for one or all slices. +
  6. Undo: Undo functionality is implemented in mitk::OverwriteSliceImageFilter, +since this is the central place where all image modifications are made. The filter stores a binary difference image +to the undo stack as a mitk::ApplyDiffImageOperation. When the user requests undo, this ApplyDiffImageOperation +will be executed by a singleton class DiffImageApplier. The operation itself observes the image, which it refers to, +for itk::DeleteEvent, so no undo operation will be executed on/for images that have already been destroyed. +
  7. GUI: The top-level GUI is the functionality +QmitkSegmentation, which is very thin in comparison to ERIS. There are +separate widgets for image and tool selection, for interpolation. Additionaly, +there are some methods to create, delete, crop, load and save segmentations. +
+ +**/ diff --git a/Plugins/org.mitk.gui.qt.multilabelsegmentation/documentation/UserManual/ToolExtensionsGeneralOverview.dox b/Plugins/org.mitk.gui.qt.multilabelsegmentation/documentation/UserManual/ToolExtensionsGeneralOverview.dox new file mode 100644 index 0000000000..6df1762137 --- /dev/null +++ b/Plugins/org.mitk.gui.qt.multilabelsegmentation/documentation/UserManual/ToolExtensionsGeneralOverview.dox @@ -0,0 +1,181 @@ +/** + +\page toolextensions How to extend the Segmentation view with external tools + +
    +
  • \ref ToolExtensionsGeneralOverview2
  • +
  • \ref ToolExtensionsGeneralOverview3
  • +
      +
    • \ref ToolExtensionsGeneralOverview31
    • +
    • \ref ToolExtensionsGeneralOverview32
    • +
    • \ref ToolExtensionsGeneralOverview33
    • +
    +
  • \ref ToolExtensionsGeneralOverview4
  • +
  • \ref ToolExtensionsGeneralOverview5
  • +
  • \ref ToolExtensionsGeneralOverview6
  • +
+ +\section ToolExtensionsGeneralOverview2 Introduction + +The application for manual segmentation in MITK (Segmentation view) comes +with a tool class framework that is extensible with new tools (description at +\ref QmitkSegmentationTechnicalPage). The usual way +to create new tools (since it is mostly used inside DKFZ) is to just add new +files to the MITK source code tree. However, this requires to be familiar with +the MITK build system and turnaround time during development might be long +(recompiling parts of MITK again and again). + +For external users who just want to use +MITK as a library and application, there is a way to create new segmentation +tools in an MITK external project, which will compile the new tools into a +shared object (DLL). Such shared objects can be loaded via the ITK object factory +and its autoload feature on application startup. This document describes +how to build such external extensions. + +Example files can be found in the MITK source code in the directory +${MITK_SOURCE_DIR}/QApplications/ToolExtensionsExample/. + +\section ToolExtensionsGeneralOverview3 What might be part of an extension + +The extension concept assumes that you want to create one or several new +interactive segmentation tools for Segmentation or another MITK +functionality that uses the tools infrastructure. In the result you +will create a shared object (DLL), which contains several tools and their GUI +counterparts, plus optional code that your extension requires. The following +sections shortly describe each of these parts. + +\subsection ToolExtensionsGeneralOverview31 Tool classes + +A tool is basically any subclass of mitk::Tool. Tools are created at runtime +through the ITK object factory (so they inherit from itk::Object). Tools +should handle the interaction part of a segmentation method, i.e. create +seed points, draw contours, etc., in order to parameterize segmentation algorithms. +Simple algorithms can even be part of a tool. A tools is identified by icon (XPM format), +name (short string) and optionally a group name (e.g. the group name for Segmentation +is "default"). + +There is a naming convention: you should put a tool called \c mitk::ExternalTool into +files called \c mitkExternalTool.h and \c mitkExternalTool.cpp. This is \e required if +you use the convenience macros described below, because there need to be ITK factories, +which names are directly derived from the file names of the tools. For the example of mitk::ExternalTool +there would be a factory called \c mitk::ExternalToolFactory in a file named \c mitkExternalToolFactory.cpp. + +\subsection ToolExtensionsGeneralOverview32 GUI classes for tools + +Tools are non-graphical classes that only implement interactions in renderwindows. However, +some tools will need a means to allow the user to set some parameters -- a graphical user interface, GUI. +In the Qt3 case, tool GUIs inherit from QmitkToolGUI, which is a mixture of QWidget and itk::Object. +Tool GUIs are also created through the ITK object factory. + +Tools inform their GUIs about state changes by messages. Tool GUIs communicate with their associated tools +via direct method calls (they know their tools). See mitk::BinaryThresholdTool for examples. + +Again a naming convention: if the convenience macros for tool extension shared objects are used, +you have to put a tool GUI called \c QmitkExternalToolGUI into a files named \c QmitkExternalToolGUI.cpp +and \c QmitkExternalToolGUI.h. The convenience macro will create a factory called \c QmitkExternalToolGUIFactory +into a file named \c QmitkExternalToolGUIFactory.cpp. + +\subsection ToolExtensionsGeneralOverview33 Additional files + +If you are writing tools MITK externally, these tools might depend on additional files, e.g. +segmentation algorithms. These can also be compiled into a tool extension shared object. + +\section ToolExtensionsGeneralOverview4 Writing a CMake file for a tool extension + +Summing up the last section, an example tool extension could comprise the following files: +\verbatim +mitkExternalTool.h \ +mitkExternalTool.xpm >--- implementing mitk::ExternalTool (header, icon, implementation) +mitkExternalTool.cpp / + +QmitkExternalToolGUI.h ,-- implementing a GUI for mitk::ExternalTool +QmitkExternalToolGUI.cpp / + +externalalgorithm.h \ +externalalgorithm.cpp \ +externalalgorithmsolver.h >-- a couple of files (not related to MITK tools) +externalalgorithmsolver.cpp / +\endverbatim + +This should all be compiled into one shared object. Just like ITK, VTK and MITK we +will use CMake for this purpose (I assume you either know or are willing to learn about +www.cmake.org) +A CMake file for the above example would look like this: + +\code +project( ExternalTool ) + +find_package(ITK) +find_package(MITK) +find_package(Qt3) + +add_definitions(${QT_DEFINITIONS}) + +set( TOOL_QT3GUI_FILES + QmitkExternalToolGUI.cpp + ) + +set( TOOL_FILES + mitkExternalTool.cpp + ) + +set( TOOL_ADDITIONAL_CPPS + externalalgorithm.cpp + externalalgorithmsolver.cpp + ) + +set( TOOL_ADDITIONAL_MOC_H +) + +MITK_GENERATE_TOOLS_LIBRARY(mitkExternalTools) +\endcode + +Basically, you only have to change the definitions of \c TOOL_FILES and, optionally, +\c TOOL_QT3GUI_FILES, \c TOOL_ADDITIONAL_CPPS and \c TOOL_ADDITIONAL_MOC_H. +For all .cpp files in \c TOOL_FILES and \c TOOL_QT3GUI_FILES there will be factories +created assuming the naming conventions described in the sections above. +Files listed in \c TOOL_ADDITIONAL_CPPS will just be compiled. Files listed in +\c TOOL_ADDITIONAL_MOC_H will be run through Qts meta object compiler \c moc -- +this is neccessary for all objects that have the Q_OBJECT macro in their declaration. +\c moc will create new files that will also be compiled into the library. + +\section ToolExtensionsGeneralOverview5 Compiling the extension + +For compiling a tool extension, you will need a compiled version of MITK. We will +assume MITK was compiled into /home/user/mitk/debug. You need to build MITK with +BUILD_SHARED_CORE turned on! + +You build the tool extension just like any other CMake based project: +\li know where your source code is (e.g. /home/user/mitk/tool-extension-src) +\li change into the directory, where you want to compile the shared object (e.g. /home/user/mitk/tool-extension-debug) +\li invoke cmake: ccmake /home/user/mitk/tool-extension-src +\li configure (press c or the "configure" button) +\li set the ITK_DIR variable to the directory, where you compiled ITK +\li set the MITK_DIR variable to the directory, where you compiled MITK: /home/user/mitk/debug +\li configure (press "c" or the "configure" button) +\li generate (press "g" or the "generate" button) + +This should do it and leave you with a or project file or Makefile that you can compile (using make or VisualStudio). + +\section ToolExtensionsGeneralOverview6 Configuring ITK autoload + +If the compile succeeds, you will get a library mitkExternalTools.dll or libmitkExternalTools.so. +This library exports a symbol \c itkLoad which is expected by the ITK object factory. + +On application startup the ITK object factory will search a list of directories from +the environment variable \c ITK_AUTOLOAD_PATH. Set this environment variable to your +binary directory (/home/user/mitk/tool-extension-debug). + +The ITK object factory will load all shared objects that it finds in the specified directories +and will test if they contain a symbol (function pointer) \c itkLoad, which is expected +to return a pointer to a itk::ObjectFactoryBase instance. If such a symbol is found, the +returned factory will be registered with the ITK object factory. + +If you successfully followed all the steps above, MITK will find your mitk::ExternalTool on +application startup, when the ITK object factory is asked to create all known instances of +mitk::Tool. Furthermore, if your mitk::ExternalTool claims to be part of the "default" group, +there will be a new icon in Segmentation, which activates your tool. + +Have fun! (And Windows users: welcome to the world of DLLs) + +**/ diff --git a/Plugins/org.mitk.gui.qt.multilabelsegmentation/documentation/UserManual/interactive_segmentation_classes.png b/Plugins/org.mitk.gui.qt.multilabelsegmentation/documentation/UserManual/interactive_segmentation_classes.png new file mode 100644 index 0000000000..9a9a4382f7 Binary files /dev/null and b/Plugins/org.mitk.gui.qt.multilabelsegmentation/documentation/UserManual/interactive_segmentation_classes.png differ diff --git a/Plugins/org.mitk.gui.qt.multilabelsegmentation/documentation/UserManual/interactive_segmentation_classes.xcf b/Plugins/org.mitk.gui.qt.multilabelsegmentation/documentation/UserManual/interactive_segmentation_classes.xcf new file mode 100644 index 0000000000..6e37987eb4 Binary files /dev/null and b/Plugins/org.mitk.gui.qt.multilabelsegmentation/documentation/UserManual/interactive_segmentation_classes.xcf differ diff --git a/Plugins/org.mitk.gui.qt.multilabelsegmentation/documentation/UserManual/multilabelsegmentation.png b/Plugins/org.mitk.gui.qt.multilabelsegmentation/documentation/UserManual/multilabelsegmentation.png new file mode 100644 index 0000000000..d13c72b5b6 Binary files /dev/null and b/Plugins/org.mitk.gui.qt.multilabelsegmentation/documentation/UserManual/multilabelsegmentation.png differ diff --git a/Plugins/org.mitk.gui.qt.multilabelsegmentation/documentation/UserManual/org_mitk_gui_qt_multilabelsegmentation.dox b/Plugins/org.mitk.gui.qt.multilabelsegmentation/documentation/UserManual/org_mitk_gui_qt_multilabelsegmentation.dox new file mode 100644 index 0000000000..590a1d9e9d --- /dev/null +++ b/Plugins/org.mitk.gui.qt.multilabelsegmentation/documentation/UserManual/org_mitk_gui_qt_multilabelsegmentation.dox @@ -0,0 +1,395 @@ +/** +\page org_mitk_views_multilabelsegmentation User Manual + +\imageMacro{"multilabelsegmentation.png", "html", 12} + +Some of the features described below are closed source additions to the open source toolkit MITK and are not +available in every application. + +Available sections: + + - \ref org_mitk_gui_qt_segmentationUserManualOverview + - \ref org_mitk_gui_qt_segmentationUserManualTechnical + - \ref org_mitk_gui_qt_segmentationUserManualCreateOpenSaveImportAdd + - \ref org_mitk_gui_qt_segmentationUserManualLabelTable + - \ref org_mitk_gui_qt_segmentationUserManualLabelCreation + - \ref org_mitk_gui_qt_segmentationUserManualLayerCreation + - \ref org_mitk_gui_qt_segmentationUserManualLabelSearch + - \ref org_mitk_gui_qt_segmentationUserManualLabelEditing + - \ref org_mitk_gui_qt_segmentationUserManualContouring + - \ref org_mitk_gui_qt_segmentationUserManualInterpolation + - \ref org_mitk_gui_qt_segmentationUserManualOrganSegmentation + - \ref org_mitk_gui_qt_segmentationUserManualOrganSegmentation1 + - \ref org_mitk_gui_qt_segmentationUserManualOrganSegmentation2 + - \ref org_mitk_gui_qt_segmentationUserManualOrganSegmentation99 + - \ref org_mitk_gui_qt_segmentationUserManualLesionSegmentation + - \ref org_mitk_gui_qt_segmentationUserManualOperationsOnLabels + - \ref org_mitk_gui_qt_segmentationUserManualOperationsOnSingleSelection + - \ref org_mitk_gui_qt_segmentationUserManualOperationsOnMultipleSelection + - \ref org_mitk_gui_qt_segmentationUserManualSegmentationUtilities + - \ref org_mitk_gui_qt_segmentationUserManualTechnicalDetail + +\section org_mitk_gui_qt_segmentationUserManualOverview Overview + +The Segmentation Perspective allows to create segmentations of structures of interest in biomedical and biological +images. Examples of such structures are an organ, a vessel or a lesion in a CT or MR image, or a cell or organelle in microscopy +images. The perspective groups a number of tools which can be used for: +
    +
  • fully-automatic segmentation +
  • semi-automatic segmentation +
  • manual segmentation +
+ +\imageMacro{"org_mitk_gui_qt_segmentationIMGapplication.png", "Segmentation perspective consisting of the Data Manager view, the +Segmentation view and the Segmentation Utilities view.", 12} + +If you wonder what segmentations are good for, we shortly revisit the concept of a segmentation here. +A CT or MR image is made up of volume of physical measurements (volume elements are called voxels). +In CT images, for example, the gray value of each voxel corresponds to the mass absorption coefficient +for X-rays in this voxel, which is similar in many %parts of the human body. +The gray value does not contain any further information, so the computer does not know whether a given +voxel is part of the body or the background, nor can it tell a brain from a liver. +However, the distinction between voxels belonging to an object and a background structure is required when: +
    +
  • you want to know the volume of a given organ (the computer needs to know which %parts of the image belong to this organ) +
  • you want to create 3D polygon visualizations (the computer needs to know the surfaces of structures that should be drawn) +
  • as a necessary preprocessing step for therapy planning, therapy support, and therapy monitoring +
+ +This distinction between voxels inside and object and background is called segmentation. +Since many objects might be segmented on an image, each segmentation is assigned a different label. A label +has a name, a color, and a number of properties that we will describe later. +The current implementation of the Segmentation perspective uses a voxel-based approach for segmentations, i.e. each voxel +of an image must be completely assigned to either a label or background. In a future version of the toolkit we will also enable +the generation of segmentations based on contours. In this way, the user will be able to better define the borders of the +segmented objects by taking advantage of his/her medical knowledge. Segmentations based on contours are not restricted to +the voxel size of the medical image and can be arbitrarily positioned. + +The remainder of this document will summarize the features of the Segmentation perspective and how they are used. + +\subsection org_mitk_gui_qt_segmentationUserManualTechnical Technical Issues + +The Segmentation Perspective makes a number of assumptions. To know what this view can be used for, it will help you to know that: +
    +
  • Images must be 2D, 3D, or 3D+t. +
  • Images must be single-values, i.e. CT, MRI or "typical" ultrasound. Images from color doppler, microscopy or +photographs (RGB) are not yet supported. +
  • Segmentations are handled as multi-labeled images of the same extent as the original medical image. +
+ +\section org_mitk_gui_qt_segmentationUserManualCreateOpenSaveImportAdd Start Segmenting + +To start using the Segmentation Perspective you will have to either create a new segmentation session or +load an existing one from disk. The Segmentation toolbar collects buttons for the these actions: + +\imageMacro{"org_mitk_gui_qt_segmentationIMGtoolbar.png", "Segmentation toolbar", 12} + +
    +
  • Create segmentation session
  • a new segmentation session is created. +
  • Load segmentation session
  • a segmentation session can be loaded from disk (.lset file extensions). +
  • Save segmentation session
  • the current segmentation session can be saved to disk. +
  • Import segmentation session
  • a segmentation session can be incorporated into the current one. All new labels will be appended + at the end of the table. +
  • Add label
  • a new label is appended to the current segmentation session, at the end of the table. +
+ +\section org_mitk_gui_qt_segmentationUserManualLabelTable The Label Table + +The following label properties are readily available to modify: + +
    +
  • Name
  • the name of the label. Can be a predefined one or any other. +
  • Color
  • the color of the label. +
  • Visible
  • whether the label is currently visible or hiden. +
  • Locked
  • whether the label is locked or editable. A locked label cannot be overwritten by another. +
+ +The Label Table is shown below: + +\imageMacro{org_mitk_gui_qt_segmentationIMGlabeltable.png", "The Label Table showing all the labels in the current segmentation session", 12} + +\section org_mitk_gui_qt_segmentationUserManualLabelCreation Creating a New Label + +Click the "New Label" button to add a new label. A dialog will show-up to enter the name and color. Preset organ names and +corresponding colors are offered while you type in, but you can set any name. The new name if not known will be automatically +remembered and made available the next time you create a new label. In the current implementation of the plugin, +the maximum number of labels is restricted to 255. If you need more, you will have to create a new segmentation session. + +\section org_mitk_gui_qt_segmentationUserManualLayerCreation Creating a New Layer + +A layer is a set of labels that occupy a non-overlapping anatomical space. The best way to describe them is by a real use +case. Imagine you are working on a radiotherpay planning application. In the first layer of your segmentation +session you would like to trace the contours of the liver and neighboring organs. You can accomodate all these segmentations +in separate labels because they all occupy different anamical regions and do not overlap. Now say you would like to segment +the arteries and veins inside the liver. If you don´t trace them in a different layer, you will overwrite the previous ones. +You may also need a third layer for segmenting the different irrigation territories in the liver and a fourth layer to contain +the lession you would like to treat. + +The next figure illustrates the Layer Manager . The buttons in it contained serve for adding a new layer, selecting the +previous and the next one. The active layer is shown together with the buttons. + +\imageMacro{"org_mitk_gui_qt_segmentationIMGlayerManager.png", "Correction Tool",12} + +\section org_mitk_gui_qt_segmentationUserManualLabelSearch Searching a Label + +It may happen that many labels (e.g. > 200) are present in a segmentation session and therefore manual searching is time consuming. +The Label Search edit box allows for quickly finding the label you want. Just start writing its name and and you will get +assitance for completing its name. If the label you were searching is found, press enter and it will became the active one. + +\imageMacro{"org_mitk_gui_qt_segmentationIMGsearchlabel.png", "Label search", 12} + +\section org_mitk_gui_qt_segmentationUserManualLabelEditing Label Editing + +First of all, you have to select the active label by clicking on the corresponding row in the Label Table. Only one label +can be active at the time. Then you can select an editing tool in the toolbox. +The selected editing tool will be active and its corresponding button will stay pressed until you click the same button again. +Selecting a different tool also deactivates the previous one. If you are familiar with the MITK Workbench, you know that +clicking and moving the mouse in any of the 2D windows will move around the crosshair that defines the position in +the image you are standing on. Depending on the tool that you activate, this behavior may be disabled during segmenting +-- otherwise you might have a hard time concentrating on the contour you are drawing --. + +If you have to delineate a lot of images, you should try using shortcuts to switch tools. Just hit the first letter of each +tool to activate it (e.g. "A" for Add, "S" for Subtract, etc.). If you hover the cursor on a tool button you will get a tip +message reminding you this. + +\subsection org_mitk_gui_qt_segmentationUserManualManualEditingTools Manual Editing Tools + +All of the editing tools work by the same principle: you use the mouse (left button) to click anywhere in a 2D +window (any of the orientations axial, sagittal, or frontal), move the mouse while holding the mouse button and +release to finish the editing action. + +At any time you can undo or redo your past actions with corresponding buttons in the application\'s toolbar. + +\subsection org_mitk_gui_qt_segmentationUserManualContouring Manual Contouring + +The "Add" and "Subtract" tools allow for freely tracing a contour around an organ or structure in an image. +You might also use manual contouring to further enhance or correct a segmentation resulting from sub-optimal automatic methods. +The drawback of manual contouring is that you might need to define contours on many 2D slices to segment the whole structure. +However, this is moderated by the interpolation tools, which are described later. + +\imageMacro{"org_mitk_gui_qt_segmentationIMGiconAddSubtract.png", "Add and Subtract Tools",12} + +Use the left mouse button to draw a closed contour. When releasing the mouse button, the contour will be added (Add tool) to or +removed from (Subtract tool) the current segmentation. +Hold down the CTRL / CMD key to invert the operation (this will switch tools temporarily to allow for quick corrections). + +\imageMacro{"org_mitk_gui_qt_segmentationIMGiconPaintWipe.png", "Paint and Wipe Tools", 12cm} + +Use the slider below the toolbox to change the radius of the paintbrush tool. Move the mouse +in any 2D window and press the left button to draw or erase the active label. +As the Add/Subtract tools, holding CTRL/CMD while drawing will invert the current tool's behavior. + +\imageMacro{"org_mitk_gui_qt_segmentationIMGiconRegionGrowing.png", "Region Growing Tool", 12} + +Click with the left button at one point in a 2D window and move up or down while keeping it pressed. This action +will make your segmentation grow (up) or shrink (down). When working on an image with a high range of grey values, +the selection range can be influenced more strongly by moving the cursor at higher velocity. The region growing algorithm +tool selects all pixels around the mouse cursor that have a similar gray value as the pixel below the first clicked position. +This enables you to quickly create segmentations of structures that have a good contrast to surrounding tissue, e.g. the lungs. + +A common issue with region growing is the so called "leakage" which happens when the structure of interest is connected to +other pixels, of similar gray values, through a narrow "bridge" at the border of the structure. +The Region Growing tool comes with a "leakage detection/removal" feature. If leakage happens, you can left-click into the +leakage region and the tool will try to automatically remove this region (see illustration below). + +\imageMacro{"org_mitk_gui_qt_segmentationIMGleakage.png", "Leakage correction feature of the Region Growing tool", 12} +
+\imageMacro{"org_mitk_gui_qt_segmentationIMGiconCorrection.png", "Correction Tool", 12} + +The corrector tool is used to perform small corrective changes. The following figure illustrates its use: +
    +
  • if the user draws a line which %starts and ends outside the active label, the +endpoints of the line are connected and the region inside the resulting contour is incorporated to the active label. +
  • if the user draws a line which %starts and ends outside the active label, the smaller region on the side is cut off (left image) +
  • if the line is drawn fully inside the active label, the marked region is added to the segmentation (right image) +
+ +\imageMacro{"org_mitk_gui_qt_segmentationIMGcorrectionActions.png", "Actions of the Correction tool illustrated.", 12} +
+\imageMacro{"org_mitk_gui_qt_segmentationIMGiconFillErase.png", "Fill and Erase Tools", 12} + +The Fill Tool works by filling a hole surrounded by the active label, at the clicked position. If you click outside the +hole (but yet inside the label) all holes found surrounded by the active label will be filled. + +The Erase Tool removes a connected region of the active label around the clicked position. + +\imageMacro{"org_mitk_gui_qt_segmentationIMGiconLiveWire.png", "LiveWire Tool", 12} + +The LiveWire tool acts as a magnetic lasso with a contour snapping to edges of objects. + +\imageMacro{"org_mitk_gui_qt_segmentationIMGLiveWireUsage.png", "Steps for using LiveWire Tool", 12} + +
    +
  • (1) To start the Tool you have to double click near the edge of the object you want to segment. The initial anchor point will +snap to the edge within a 3x3 region. +
  • (2) Move the mouse. You don't have trace the edge of the object. The contour will automatically snap to it. +
  • (3) To fix a segment you can set anchor points by single left mouse button click. +
  • (4) Go on with moving the mouse and setting anchor points. +
  • (5) To close the contour double click on the initial anchor point. +
  • (6) After closing the contour, it can further be edited by dragging, inserting and deleting anchor points. +
+ +The contour will be transferred to the active label when the tool is deactivated. + +\imageMacro{"org_mitk_gui_qt_segmentationIMGiconFastMarching2D.png", "FastMarching Tool", 12} + +The FastMarching tool ... + +\subsection org_mitk_gui_qt_segmentationUserManualInterpolation Interpolation + +Segmenting modern CT and MR volumes is very time-consuming, because structures of interest can easily +cover a range of 50 or more slices. The Segmentation plugin offers two helpful features for these cases: + +
    +
  • 3D Interpolation +
  • 2D Interpolation +
+
+ +The 3D interpolation is activated by default when using the manual segmentation tools. That means if you start +contouring, from the second contour onwards, the surface of the segmented area will be interpolated based on the available contours. +The interpolation works with all available manual tools. Please note that this is currently a pure geometrical interpolation, +i.e. image intensity information is not taken into account. With each new contour that the user traces, the interpolation result +will improve, but the more contours you provide the longer the recalculation will take. To optimize the interpolation result you +should try to provide sparse contours in arbitrary oriented planes. Generating parallel contours along a given direction is not +a good approach. During interpolation recalculation, the generated surface blinks in yellow/white. When the interpolation +has finished the surface is shown yellow with a small opacity. Additional to the surface, black contours are shown in the 3D render +window. They mark the positions of all the drawn contours which were used for the interpolation. You can navigate between the drawn +contours by clicking on the „Position“ - Nodes in the Data Manager which are located below the active segmentation. If you don't +want to see these nodes just unckeck the „Show Position Nodes“ Checkbox and these nodes will +be hidden. If you want to delete a drawn contour we recommend to use the Erase-Tool since Redo/Undo is not yet working for 3D +interpolation. + +\imageMacro{"org_mitk_gui_qt_segmentation3DInterpolationWrongRight.png", "3D Interpolation HowTo", 12} + +Finally, you can accept the interpolation result by clicking the "Accept" - button below the toolbox. The region inside the surface is +assigned to the active label. + +
+The 2D Interpolation creates suggestions for extending the active label into neighboring slices provided +
    +
  • a segmentation of the same active label is available a couple of slices away AND +
  • is completely clear of the active label in between -- i.e. there will be no interpolation suggestion if there is even only +a single pixel of the active label between the aforementioned slices. +
+ +Interpolated suggestions are displayed as contours with the same color as the active label. To accept an interpolation suggestion +in a single slice, click the "Accept" button. You can also accept all interpolated suggestions at once in a +given window, by pressing the "Accept All" button. + +\imageMacro{"org_mitk_gui_qt_segmentation2DInterpolation.png", "2D Interpolation", 12} + +\section org_mitk_gui_qt_segmentationUserManualOrganSegmentation Organ Segmentation + +\note This feature is only available in our 3M3 Demo Application (http://www.mint-medical.de/productssolutions/mitk3m3/mitk3m3/#downloads) but not in the open source part of MITK + +The manual contouring described above is a fallback option that will work for any kind of images and structures of interest. +However, manual contouring is very time-consuming and tedious. +This is why a major part of image analysis research is working towards automatic segmentation methods. +The Segmentation View comprises a number of easy-to-use tools for segmentation of CT images (Liver) and MR image (left ventricle and wall, left and right lung). + +\subsection org_mitk_gui_qt_segmentationUserManualOrganSegmentation1 Liver on CT Images + +On CT image volumes, preferably with a contrast agent in the portal venous phase, the Liver tool will fully automatically analyze and segment the image. +All you have to do is to load and select the image, then click the "Liver" button. +During the process, which takes a minute or two, you will get visual progress feedback by means of a contour that moves closer and closer to the real liver boundaries. + +\subsection org_mitk_gui_qt_segmentationUserManualOrganSegmentation2 Heart, Lung, and Hippocampus on MRI + +While liver segmentation is performed fully automatic, the following tools for segmentation of the heart, the lungs, and the hippocampus need a minimum amount of guidance. +Click one of the buttons on the "Organ segmentation" page to add an average %model of the respective organ to the image. +This %model can be dragged to the right position by using the left mouse button while holding down the CTRL key. +You can also use CTRL + middle mouse button to rotate or CTRL + right mouse button to scale the %model. + +Before starting the automatic segmentation process by clicking the "Start segmentation" button, try placing the %model closely to the organ in the MR image +(in most cases, you do not need to rotate or scale the %model). +During the segmentation process, a green contour that moves closer and closer to the real liver boundaries will provide you with visual feedback of the segmentation progress. + +The algorithms used for segmentation of the heart and lung are method which need training by a number of example images. +They will not work well with other kind of images, so here is a list of the image types that were used for training: +
    +
  • Hippocampus segmentation: T1-weighted MR images, 1.5 Tesla scanner (Magnetom Vision, Siemens Medical Solutions), 1.0 mm isotropic resolution +
  • Heart: Left ventricle inner segmentation (LV Model): MRI; velocity encoded cine (VEC-cine) MRI sequence; trained on systole and diastole +
  • Heart: Left ventricular wall segmentation (LV Inner Wall, LV Outer Wall): 4D MRI; short axis 12 slice spin lock sequence(SA_12_sl); trained on whole heart cycle +
  • Lung segmentation: 3D and 4D MRI; works best on FLASH3D and TWIST4D sequences +
+ +\subsection org_mitk_gui_qt_segmentationUserManualOrganSegmentation99 Other Organs + +As mentioned in the Heart/Lung section, most of the underlying methods are based on "training". +The basic algorithm is versatile and can be applied on all kinds of segmentation problems where the structure of interest is topologically like a sphere (and not like a torus etc.). +If you are interested in other organs than those offered by the current version of the Segmentation view, +please contact our research team. + +\subsection org_mitk_gui_qt_segmentationUserManualLesionSegmentation Lesion Segmentation + +\note This feature is only available in our 3M3 Demo Application (http://www.mint-medical.de/productssolutions/mitk3m3/mitk3m3/#downloads) but not in the open source part of MITK + +Lesion segmentation is a little different from organ segmentation. Since lesions are not part of the healthy body, they sometimes have a diffused border, +and are often found in varying places all over the body. +The tools in this section offer efficient ways to create 3D segmentations of such lesions. + +The Segmentation View currently offers support for enlarged lymph nodes. + +To segment an enlarged lymph node, find a more or less central slice of it, activate the "Lymph Node" tool and draw a rough contour on the inside of the lymph node. +When releasing the mouse button, a segmentation algorithm is started in a background task. The result will become visible after a couple of seconds, but you do not have to wait for it. +If you need to segment several lymph nodes, you can continue to inspect the image right after closing the drawn contour. + +If the lymph node segmentation is not to your content, you can select the "Lymph Node Correction" tool and drag %parts of the lymph node surface towards the right position (works in 3D, not slice-by-slice). +This kind of correction helps in many cases. +If nothing else helps, you can still use the pure manual tools as a fallback. + +\section org_mitk_gui_qt_segmentationUserManualOperationsOnLabels Operations on Labels + +Depending on your selection in the Label Table , several actions are offered: + +\subsection org_mitk_gui_qt_segmentationUserManualOperationsOnSingleSelection Single Label Selection + +If you right click on any label in the table, a menu will pop-up offering the following actions to be performed on the selected label: + +
    +
  • Rename...
  • : change the name and/or color of the selected label. +
  • Remove label
  • : delete the selected label. +
  • Erase label
  • : only clear the contents of the selected label. +
  • Random color
  • : generate a surface mesh out of the selected label. +
  • View only
  • : generate a mask out of the selected label. A mask is a binary image with "1" inside and "0" outside. +
  • View/Hide all
  • : generate a mask out of the selected label. A mask is a binary image with "1" inside and "0" outside. +
  • Lock/Unlock all
  • : generate a mask out of the selected label. A mask is a binary image with "1" inside and "0" outside. +
  • Create surface
  • : generate a surface out of the selected label. +
  • Create mask
  • : generate a mask out of the selected label. A mask is a binary image with "1" inside and "0" outside. +
+ +\imageMacro{"org_mitk_gui_qt_segmentationIMGLabelTableSingleSelectionContextMenu.png", "Context menu for single label selection", 12} + +\subsection org_mitk_gui_qt_segmentationUserManualOperationsOnMultipleSelection Multiple Label Selection + +If more than one label is selected, a different menu will show up: + +\imageMacro{"org_mitk_gui_qt_segmentationIMGLabelTableMultipleSelectionContextMenu.png", "Context menu for multiple label selection", 12} + +
    +
  • Merge selection on current label
  • : transfer the contents of the selected labels in the Label Table into the current one. +
  • Remove selected labels
  • : delete the selected labels. +
  • Erase selected labels
  • : only clear the contents of the selected labels. +
  • Create a surface for each selected label
  • : generate a surface mesh out of each selected label. +
  • Combine and create a surface
  • : generate a surface out of the combination of the selected labels. +
  • Create a mask for each selected label
  • : generate a mask out of each selected label. A mask is a binary image with "1" inside and "0" outside. +
  • Combine and create a mask
  • : generate a mask out of the combination of the selected labels. +
+ +\section org_mitk_gui_qt_segmentationUserManualSegmentationUtilities Segmentation Utilities + +The Segmentation Utilities view ... + +One is the surface masking utility. It is used for create a new label from a surface. The surface is used as a "mask": the region inside +the surface is assigned a new label. + +\imageMacro{"org_mitk_gui_qt_segmentationIMGsurfacemask.png", "Surface masking utility", 12} + +Select the image and the surface in the corresponding drop-down boxes (both are selected automatically if there is just one image and one surface) + +\section org_mitk_gui_qt_segmentationUserManualTechnicalDetail Technical Information for Developers + +For technical specifications see : \subpage QmitkSegmentationTechnicalPage and for information on the extensions of the tools system : \subpage toolextensions. + +*/ diff --git a/Plugins/org.mitk.gui.qt.multilabelsegmentation/documentation/UserManual/org_mitk_gui_qt_segmentation2DInterpolation.png b/Plugins/org.mitk.gui.qt.multilabelsegmentation/documentation/UserManual/org_mitk_gui_qt_segmentation2DInterpolation.png new file mode 100644 index 0000000000..09ed1e4f17 Binary files /dev/null and b/Plugins/org.mitk.gui.qt.multilabelsegmentation/documentation/UserManual/org_mitk_gui_qt_segmentation2DInterpolation.png differ diff --git a/Plugins/org.mitk.gui.qt.multilabelsegmentation/documentation/UserManual/org_mitk_gui_qt_segmentation3DInterpolationWrongRight.png b/Plugins/org.mitk.gui.qt.multilabelsegmentation/documentation/UserManual/org_mitk_gui_qt_segmentation3DInterpolationWrongRight.png new file mode 100644 index 0000000000..d2e65a8600 Binary files /dev/null and b/Plugins/org.mitk.gui.qt.multilabelsegmentation/documentation/UserManual/org_mitk_gui_qt_segmentation3DInterpolationWrongRight.png differ diff --git a/Plugins/org.mitk.gui.qt.multilabelsegmentation/documentation/UserManual/org_mitk_gui_qt_segmentationIMGLabelTableMultipleSelectionContextMenu.png b/Plugins/org.mitk.gui.qt.multilabelsegmentation/documentation/UserManual/org_mitk_gui_qt_segmentationIMGLabelTableMultipleSelectionContextMenu.png new file mode 100644 index 0000000000..ac71514fe9 Binary files /dev/null and b/Plugins/org.mitk.gui.qt.multilabelsegmentation/documentation/UserManual/org_mitk_gui_qt_segmentationIMGLabelTableMultipleSelectionContextMenu.png differ diff --git a/Plugins/org.mitk.gui.qt.multilabelsegmentation/documentation/UserManual/org_mitk_gui_qt_segmentationIMGLabelTableSingleSelectionContextMenu.png b/Plugins/org.mitk.gui.qt.multilabelsegmentation/documentation/UserManual/org_mitk_gui_qt_segmentationIMGLabelTableSingleSelectionContextMenu.png new file mode 100644 index 0000000000..13f648df2a Binary files /dev/null and b/Plugins/org.mitk.gui.qt.multilabelsegmentation/documentation/UserManual/org_mitk_gui_qt_segmentationIMGLabelTableSingleSelectionContextMenu.png differ diff --git a/Plugins/org.mitk.gui.qt.multilabelsegmentation/documentation/UserManual/org_mitk_gui_qt_segmentationIMGLiveWireUsage.PNG b/Plugins/org.mitk.gui.qt.multilabelsegmentation/documentation/UserManual/org_mitk_gui_qt_segmentationIMGLiveWireUsage.PNG new file mode 100644 index 0000000000..3e963ce632 Binary files /dev/null and b/Plugins/org.mitk.gui.qt.multilabelsegmentation/documentation/UserManual/org_mitk_gui_qt_segmentationIMGLiveWireUsage.PNG differ diff --git a/Plugins/org.mitk.gui.qt.multilabelsegmentation/documentation/UserManual/org_mitk_gui_qt_segmentationIMGapplication.png b/Plugins/org.mitk.gui.qt.multilabelsegmentation/documentation/UserManual/org_mitk_gui_qt_segmentationIMGapplication.png new file mode 100644 index 0000000000..7d01cb32bc Binary files /dev/null and b/Plugins/org.mitk.gui.qt.multilabelsegmentation/documentation/UserManual/org_mitk_gui_qt_segmentationIMGapplication.png differ diff --git a/Plugins/org.mitk.gui.qt.multilabelsegmentation/documentation/UserManual/org_mitk_gui_qt_segmentationIMGcorrectionActions.png b/Plugins/org.mitk.gui.qt.multilabelsegmentation/documentation/UserManual/org_mitk_gui_qt_segmentationIMGcorrectionActions.png new file mode 100644 index 0000000000..dc8dc8ce03 Binary files /dev/null and b/Plugins/org.mitk.gui.qt.multilabelsegmentation/documentation/UserManual/org_mitk_gui_qt_segmentationIMGcorrectionActions.png differ diff --git a/Plugins/org.mitk.gui.qt.multilabelsegmentation/documentation/UserManual/org_mitk_gui_qt_segmentationIMGdatamanagerview.png b/Plugins/org.mitk.gui.qt.multilabelsegmentation/documentation/UserManual/org_mitk_gui_qt_segmentationIMGdatamanagerview.png new file mode 100644 index 0000000000..abf6e7f638 Binary files /dev/null and b/Plugins/org.mitk.gui.qt.multilabelsegmentation/documentation/UserManual/org_mitk_gui_qt_segmentationIMGdatamanagerview.png differ diff --git a/Plugins/org.mitk.gui.qt.multilabelsegmentation/documentation/UserManual/org_mitk_gui_qt_segmentationIMGiconAddSubtract.png b/Plugins/org.mitk.gui.qt.multilabelsegmentation/documentation/UserManual/org_mitk_gui_qt_segmentationIMGiconAddSubtract.png new file mode 100644 index 0000000000..53927a9ec2 Binary files /dev/null and b/Plugins/org.mitk.gui.qt.multilabelsegmentation/documentation/UserManual/org_mitk_gui_qt_segmentationIMGiconAddSubtract.png differ diff --git a/Plugins/org.mitk.gui.qt.multilabelsegmentation/documentation/UserManual/org_mitk_gui_qt_segmentationIMGiconCorrection.png b/Plugins/org.mitk.gui.qt.multilabelsegmentation/documentation/UserManual/org_mitk_gui_qt_segmentationIMGiconCorrection.png new file mode 100644 index 0000000000..41f21628e6 Binary files /dev/null and b/Plugins/org.mitk.gui.qt.multilabelsegmentation/documentation/UserManual/org_mitk_gui_qt_segmentationIMGiconCorrection.png differ diff --git a/Plugins/org.mitk.gui.qt.multilabelsegmentation/documentation/UserManual/org_mitk_gui_qt_segmentationIMGiconFastMarching2D.png b/Plugins/org.mitk.gui.qt.multilabelsegmentation/documentation/UserManual/org_mitk_gui_qt_segmentationIMGiconFastMarching2D.png new file mode 100644 index 0000000000..ac6c4eddc8 Binary files /dev/null and b/Plugins/org.mitk.gui.qt.multilabelsegmentation/documentation/UserManual/org_mitk_gui_qt_segmentationIMGiconFastMarching2D.png differ diff --git a/Plugins/org.mitk.gui.qt.multilabelsegmentation/documentation/UserManual/org_mitk_gui_qt_segmentationIMGiconFill.png b/Plugins/org.mitk.gui.qt.multilabelsegmentation/documentation/UserManual/org_mitk_gui_qt_segmentationIMGiconFill.png new file mode 100644 index 0000000000..33b9498598 Binary files /dev/null and b/Plugins/org.mitk.gui.qt.multilabelsegmentation/documentation/UserManual/org_mitk_gui_qt_segmentationIMGiconFill.png differ diff --git a/Plugins/org.mitk.gui.qt.multilabelsegmentation/documentation/UserManual/org_mitk_gui_qt_segmentationIMGiconFillErase.png b/Plugins/org.mitk.gui.qt.multilabelsegmentation/documentation/UserManual/org_mitk_gui_qt_segmentationIMGiconFillErase.png new file mode 100644 index 0000000000..6acdb3acdc Binary files /dev/null and b/Plugins/org.mitk.gui.qt.multilabelsegmentation/documentation/UserManual/org_mitk_gui_qt_segmentationIMGiconFillErase.png differ diff --git a/Plugins/org.mitk.gui.qt.multilabelsegmentation/documentation/UserManual/org_mitk_gui_qt_segmentationIMGiconLiveWire.png b/Plugins/org.mitk.gui.qt.multilabelsegmentation/documentation/UserManual/org_mitk_gui_qt_segmentationIMGiconLiveWire.png new file mode 100644 index 0000000000..8c28ce7bf6 Binary files /dev/null and b/Plugins/org.mitk.gui.qt.multilabelsegmentation/documentation/UserManual/org_mitk_gui_qt_segmentationIMGiconLiveWire.png differ diff --git a/Plugins/org.mitk.gui.qt.multilabelsegmentation/documentation/UserManual/org_mitk_gui_qt_segmentationIMGiconPaintWipe.png b/Plugins/org.mitk.gui.qt.multilabelsegmentation/documentation/UserManual/org_mitk_gui_qt_segmentationIMGiconPaintWipe.png new file mode 100644 index 0000000000..c034048640 Binary files /dev/null and b/Plugins/org.mitk.gui.qt.multilabelsegmentation/documentation/UserManual/org_mitk_gui_qt_segmentationIMGiconPaintWipe.png differ diff --git a/Plugins/org.mitk.gui.qt.multilabelsegmentation/documentation/UserManual/org_mitk_gui_qt_segmentationIMGiconRegionGrowing.png b/Plugins/org.mitk.gui.qt.multilabelsegmentation/documentation/UserManual/org_mitk_gui_qt_segmentationIMGiconRegionGrowing.png new file mode 100644 index 0000000000..4479918187 Binary files /dev/null and b/Plugins/org.mitk.gui.qt.multilabelsegmentation/documentation/UserManual/org_mitk_gui_qt_segmentationIMGiconRegionGrowing.png differ diff --git a/Plugins/org.mitk.gui.qt.multilabelsegmentation/documentation/UserManual/org_mitk_gui_qt_segmentationIMGlabeltable.png b/Plugins/org.mitk.gui.qt.multilabelsegmentation/documentation/UserManual/org_mitk_gui_qt_segmentationIMGlabeltable.png new file mode 100644 index 0000000000..351020c950 Binary files /dev/null and b/Plugins/org.mitk.gui.qt.multilabelsegmentation/documentation/UserManual/org_mitk_gui_qt_segmentationIMGlabeltable.png differ diff --git a/Plugins/org.mitk.gui.qt.multilabelsegmentation/documentation/UserManual/org_mitk_gui_qt_segmentationIMGlayerManager.png b/Plugins/org.mitk.gui.qt.multilabelsegmentation/documentation/UserManual/org_mitk_gui_qt_segmentationIMGlayerManager.png new file mode 100644 index 0000000000..8bfd980219 Binary files /dev/null and b/Plugins/org.mitk.gui.qt.multilabelsegmentation/documentation/UserManual/org_mitk_gui_qt_segmentationIMGlayerManager.png differ diff --git a/Plugins/org.mitk.gui.qt.multilabelsegmentation/documentation/UserManual/org_mitk_gui_qt_segmentationIMGleakage.png b/Plugins/org.mitk.gui.qt.multilabelsegmentation/documentation/UserManual/org_mitk_gui_qt_segmentationIMGleakage.png new file mode 100644 index 0000000000..a2e27f3df1 Binary files /dev/null and b/Plugins/org.mitk.gui.qt.multilabelsegmentation/documentation/UserManual/org_mitk_gui_qt_segmentationIMGleakage.png differ diff --git a/Plugins/org.mitk.gui.qt.multilabelsegmentation/documentation/UserManual/org_mitk_gui_qt_segmentationIMGsearchlabel.png b/Plugins/org.mitk.gui.qt.multilabelsegmentation/documentation/UserManual/org_mitk_gui_qt_segmentationIMGsearchlabel.png new file mode 100644 index 0000000000..5beb87e00e Binary files /dev/null and b/Plugins/org.mitk.gui.qt.multilabelsegmentation/documentation/UserManual/org_mitk_gui_qt_segmentationIMGsearchlabel.png differ diff --git a/Plugins/org.mitk.gui.qt.multilabelsegmentation/documentation/UserManual/org_mitk_gui_qt_segmentationIMGselection.png b/Plugins/org.mitk.gui.qt.multilabelsegmentation/documentation/UserManual/org_mitk_gui_qt_segmentationIMGselection.png new file mode 100644 index 0000000000..25db6192cb Binary files /dev/null and b/Plugins/org.mitk.gui.qt.multilabelsegmentation/documentation/UserManual/org_mitk_gui_qt_segmentationIMGselection.png differ diff --git a/Plugins/org.mitk.gui.qt.multilabelsegmentation/documentation/UserManual/org_mitk_gui_qt_segmentationIMGsurfacemask.png b/Plugins/org.mitk.gui.qt.multilabelsegmentation/documentation/UserManual/org_mitk_gui_qt_segmentationIMGsurfacemask.png new file mode 100644 index 0000000000..cb81d900a8 Binary files /dev/null and b/Plugins/org.mitk.gui.qt.multilabelsegmentation/documentation/UserManual/org_mitk_gui_qt_segmentationIMGsurfacemask.png differ diff --git a/Plugins/org.mitk.gui.qt.multilabelsegmentation/documentation/UserManual/org_mitk_gui_qt_segmentationIMGtoolbar.png b/Plugins/org.mitk.gui.qt.multilabelsegmentation/documentation/UserManual/org_mitk_gui_qt_segmentationIMGtoolbar.png new file mode 100644 index 0000000000..ddb08b0449 Binary files /dev/null and b/Plugins/org.mitk.gui.qt.multilabelsegmentation/documentation/UserManual/org_mitk_gui_qt_segmentationIMGtoolbar.png differ diff --git a/Plugins/org.mitk.gui.qt.multilabelsegmentation/documentation/UserManual/segmentationFromSurfaceBefore.png b/Plugins/org.mitk.gui.qt.multilabelsegmentation/documentation/UserManual/segmentationFromSurfaceBefore.png new file mode 100644 index 0000000000..d12d44fb9f Binary files /dev/null and b/Plugins/org.mitk.gui.qt.multilabelsegmentation/documentation/UserManual/segmentationFromSurfaceBefore.png differ diff --git a/Plugins/org.mitk.gui.qt.multilabelsegmentation/documentation/doxygen/MultiLabelSegmentationProperties.dox b/Plugins/org.mitk.gui.qt.multilabelsegmentation/documentation/doxygen/MultiLabelSegmentationProperties.dox new file mode 100644 index 0000000000..e4d8347623 --- /dev/null +++ b/Plugins/org.mitk.gui.qt.multilabelsegmentation/documentation/doxygen/MultiLabelSegmentationProperties.dox @@ -0,0 +1,10 @@ +/** +\page MultiLabelSegmentationPropertiesPage The Segmentation properties + +These properties control how a segmentation is rendered: + +
    +
  • property #1 +
  • property #2 +
+*/ \ No newline at end of file diff --git a/Plugins/org.mitk.gui.qt.multilabelsegmentation/files.cmake b/Plugins/org.mitk.gui.qt.multilabelsegmentation/files.cmake new file mode 100644 index 0000000000..335addf56d --- /dev/null +++ b/Plugins/org.mitk.gui.qt.multilabelsegmentation/files.cmake @@ -0,0 +1,104 @@ +set(SRC_CPP_FILES + QmitkMultiLabelSegmentationPreferencePage.cpp +) + +set(INTERNAL_CPP_FILES + mitkPluginActivator.cpp + QmitkMultiLabelSegmentationView.cpp + QmitkThresholdAction.cpp + QmitkCreatePolygonModelAction.cpp + QmitkAutocropAction.cpp + QmitkConvertSurfaceToLabelAction.cpp + QmitkConvertMaskToLabelAction.cpp + QmitkConvertToMultiLabelSegmentationAction.cpp + QmitkCreateMultiLabelSegmentationAction.cpp + QmitkLoadMultiLabelPresetAction.cpp + QmitkCreateMultiLabelPresetAction.cpp + Common/QmitkDataSelectionWidget.cpp + SegmentationUtilities/QmitkMultiLabelSegmentationUtilitiesView.cpp + SegmentationUtilities/QmitkSegmentationUtilityWidget.cpp + SegmentationUtilities/BooleanOperations/QmitkBooleanOperationsWidget.cpp + SegmentationUtilities/MorphologicalOperations/QmitkMorphologicalOperationsWidget.cpp + SegmentationUtilities/SurfaceToImage/QmitkSurfaceToImageWidget.cpp + SegmentationUtilities/ImageMasking/QmitkImageMaskingWidget.cpp +) + +set(UI_FILES + src/internal/QmitkMultiLabelSegmentationControls.ui + src/internal/Common/QmitkDataSelectionWidgetControls.ui + src/internal/SegmentationUtilities/QmitkMultiLabelSegmentationUtilitiesViewControls.ui + src/internal/SegmentationUtilities/BooleanOperations/QmitkBooleanOperationsWidgetControls.ui + src/internal/SegmentationUtilities/MorphologicalOperations/QmitkMorphologicalOperationsWidgetControls.ui + src/internal/SegmentationUtilities/SurfaceToImage/QmitkSurfaceToImageWidgetControls.ui + src/internal/SegmentationUtilities/ImageMasking/QmitkImageMaskingWidgetControls.ui +) + +set(MOC_H_FILES + src/QmitkMultiLabelSegmentationPreferencePage.h + src/internal/mitkPluginActivator.h + src/internal/QmitkMultiLabelSegmentationView.h + src/internal/QmitkThresholdAction.h + src/internal/QmitkCreatePolygonModelAction.h + src/internal/QmitkAutocropAction.h + src/internal/QmitkConvertSurfaceToLabelAction.h + src/internal/QmitkLoadMultiLabelPresetAction.h + src/internal/QmitkCreateMultiLabelPresetAction.h + src/internal/QmitkConvertMaskToLabelAction.h + src/internal/QmitkConvertToMultiLabelSegmentationAction.h + src/internal/QmitkCreateMultiLabelSegmentationAction.h + src/internal/Common/QmitkDataSelectionWidget.h + src/internal/SegmentationUtilities/QmitkMultiLabelSegmentationUtilitiesView.h + src/internal/SegmentationUtilities/QmitkSegmentationUtilityWidget.h + src/internal/SegmentationUtilities/BooleanOperations/QmitkBooleanOperationsWidget.h + src/internal/SegmentationUtilities/MorphologicalOperations/QmitkMorphologicalOperationsWidget.h + src/internal/SegmentationUtilities/SurfaceToImage/QmitkSurfaceToImageWidget.h + src/internal/SegmentationUtilities/ImageMasking/QmitkImageMaskingWidget.h +) + +set(CACHED_RESOURCE_FILES + resources/BooleanDifference_48x48.png + resources/BooleanIntersection_48x48.png + resources/BooleanOperations_48x48.png + resources/BooleanUnion_48x48.png + resources/Closing_48x48.png + resources/CTKWidgets_48x48.png + resources/deformablePlane.png + resources/Dilate_48x48.png + resources/Erode_48x48.png + resources/FillHoles_48x48.png + resources/Icons.svg + resources/ImageMasking_48x48.png + resources/MorphologicalOperations_48x48.png + resources/multilabelsegmentation.png + resources/MultiLabelSegmentationUtilities_48x48.png + resources/NewLabel_48x48.png + resources/NewSegmentationSession_48x48.png + resources/Opening_48x48.png + resources/SurfaceToImage_48x48.png + plugin.xml +) + +set(QRC_FILES + resources/multilabelsegmentation.qrc + resources/MultiLabelSegmentationUtilities.qrc + resources/MorphologicalOperationsWidget.qrc + resources/BooleanOperationsWidget.qrc +) + +set(CPP_FILES) + +foreach(file ${SRC_CPP_FILES}) + set(CPP_FILES ${CPP_FILES} src/${file}) +endforeach(file ${SRC_CPP_FILES}) + +#usFunctionEmbedResources( +#CPP_FILES +# LIBRARY_NAME "liborg_mitk_gui_qt_multilabelsegmentation" +#ROOT_DIR resources +#FILES Interactions/SegmentationInteraction.xml +# Interactions/ConfigSegmentation.xml +#) + +foreach(file ${INTERNAL_CPP_FILES}) + set(CPP_FILES ${CPP_FILES} src/internal/${file}) +endforeach(file ${INTERNAL_CPP_FILES}) diff --git a/Plugins/org.mitk.gui.qt.multilabelsegmentation/manifest_headers.cmake b/Plugins/org.mitk.gui.qt.multilabelsegmentation/manifest_headers.cmake new file mode 100644 index 0000000000..ca346536b9 --- /dev/null +++ b/Plugins/org.mitk.gui.qt.multilabelsegmentation/manifest_headers.cmake @@ -0,0 +1,5 @@ +set(Plugin-Name "MITK MultiLabelSegmentation") +set(Plugin-Version "1.0.0") +set(Plugin-Vendor "DKFZ, Medical and Biological Informatics") +set(Plugin-ContactAddress "http://www.mitk.org") +set(Require-Plugin org.mitk.gui.qt.common org.mitk.gui.qt.datamanager) diff --git a/Plugins/org.mitk.gui.qt.multilabelsegmentation/plugin.xml b/Plugins/org.mitk.gui.qt.multilabelsegmentation/plugin.xml new file mode 100644 index 0000000000..312edde71f --- /dev/null +++ b/Plugins/org.mitk.gui.qt.multilabelsegmentation/plugin.xml @@ -0,0 +1,42 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Plugins/org.mitk.gui.qt.multilabelsegmentation/resources/BooleanDifference_48x48.png b/Plugins/org.mitk.gui.qt.multilabelsegmentation/resources/BooleanDifference_48x48.png new file mode 100644 index 0000000000..424faa7fe5 Binary files /dev/null and b/Plugins/org.mitk.gui.qt.multilabelsegmentation/resources/BooleanDifference_48x48.png differ diff --git a/Plugins/org.mitk.gui.qt.multilabelsegmentation/resources/BooleanIntersection_48x48.png b/Plugins/org.mitk.gui.qt.multilabelsegmentation/resources/BooleanIntersection_48x48.png new file mode 100644 index 0000000000..2db4c0dc89 Binary files /dev/null and b/Plugins/org.mitk.gui.qt.multilabelsegmentation/resources/BooleanIntersection_48x48.png differ diff --git a/Plugins/org.mitk.gui.qt.multilabelsegmentation/resources/BooleanOperationsWidget.qrc b/Plugins/org.mitk.gui.qt.multilabelsegmentation/resources/BooleanOperationsWidget.qrc new file mode 100644 index 0000000000..ef0eab4b45 --- /dev/null +++ b/Plugins/org.mitk.gui.qt.multilabelsegmentation/resources/BooleanOperationsWidget.qrc @@ -0,0 +1,7 @@ + + + BooleanDifference_48x48.png + BooleanIntersection_48x48.png + BooleanUnion_48x48.png + + diff --git a/Plugins/org.mitk.gui.qt.multilabelsegmentation/resources/BooleanOperations_48x48.png b/Plugins/org.mitk.gui.qt.multilabelsegmentation/resources/BooleanOperations_48x48.png new file mode 100644 index 0000000000..27d28f9926 Binary files /dev/null and b/Plugins/org.mitk.gui.qt.multilabelsegmentation/resources/BooleanOperations_48x48.png differ diff --git a/Plugins/org.mitk.gui.qt.multilabelsegmentation/resources/BooleanUnion_48x48.png b/Plugins/org.mitk.gui.qt.multilabelsegmentation/resources/BooleanUnion_48x48.png new file mode 100644 index 0000000000..891740c7c1 Binary files /dev/null and b/Plugins/org.mitk.gui.qt.multilabelsegmentation/resources/BooleanUnion_48x48.png differ diff --git a/Plugins/org.mitk.gui.qt.multilabelsegmentation/resources/CTKWidgets_48x48.png b/Plugins/org.mitk.gui.qt.multilabelsegmentation/resources/CTKWidgets_48x48.png new file mode 100644 index 0000000000..ed7caf5cad Binary files /dev/null and b/Plugins/org.mitk.gui.qt.multilabelsegmentation/resources/CTKWidgets_48x48.png differ diff --git a/Plugins/org.mitk.gui.qt.multilabelsegmentation/resources/Closing_48x48.png b/Plugins/org.mitk.gui.qt.multilabelsegmentation/resources/Closing_48x48.png new file mode 100644 index 0000000000..2d998a55cf Binary files /dev/null and b/Plugins/org.mitk.gui.qt.multilabelsegmentation/resources/Closing_48x48.png differ diff --git a/Plugins/org.mitk.gui.qt.multilabelsegmentation/resources/Dilate_48x48.png b/Plugins/org.mitk.gui.qt.multilabelsegmentation/resources/Dilate_48x48.png new file mode 100644 index 0000000000..669f95d156 Binary files /dev/null and b/Plugins/org.mitk.gui.qt.multilabelsegmentation/resources/Dilate_48x48.png differ diff --git a/Plugins/org.mitk.gui.qt.multilabelsegmentation/resources/Erode_48x48.png b/Plugins/org.mitk.gui.qt.multilabelsegmentation/resources/Erode_48x48.png new file mode 100644 index 0000000000..eaa6f4884e Binary files /dev/null and b/Plugins/org.mitk.gui.qt.multilabelsegmentation/resources/Erode_48x48.png differ diff --git a/Plugins/org.mitk.gui.qt.multilabelsegmentation/resources/FillHoles_48x48.png b/Plugins/org.mitk.gui.qt.multilabelsegmentation/resources/FillHoles_48x48.png new file mode 100644 index 0000000000..30ea751927 Binary files /dev/null and b/Plugins/org.mitk.gui.qt.multilabelsegmentation/resources/FillHoles_48x48.png differ diff --git a/Plugins/org.mitk.gui.qt.multilabelsegmentation/resources/Icons.svg b/Plugins/org.mitk.gui.qt.multilabelsegmentation/resources/Icons.svg new file mode 100644 index 0000000000..2fd004fb64 --- /dev/null +++ b/Plugins/org.mitk.gui.qt.multilabelsegmentation/resources/Icons.svg @@ -0,0 +1,3069 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + AND + OR + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Plugins/org.mitk.gui.qt.multilabelsegmentation/resources/ImageMasking_48x48.png b/Plugins/org.mitk.gui.qt.multilabelsegmentation/resources/ImageMasking_48x48.png new file mode 100644 index 0000000000..9c05e9fb20 Binary files /dev/null and b/Plugins/org.mitk.gui.qt.multilabelsegmentation/resources/ImageMasking_48x48.png differ diff --git a/Plugins/org.mitk.gui.qt.multilabelsegmentation/resources/Interactions/ConfigSegmentation.xml b/Plugins/org.mitk.gui.qt.multilabelsegmentation/resources/Interactions/ConfigSegmentation.xml new file mode 100644 index 0000000000..11d05617e4 --- /dev/null +++ b/Plugins/org.mitk.gui.qt.multilabelsegmentation/resources/Interactions/ConfigSegmentation.xml @@ -0,0 +1,54 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Plugins/org.mitk.gui.qt.multilabelsegmentation/resources/Interactions/SegmentationInteraction.xml b/Plugins/org.mitk.gui.qt.multilabelsegmentation/resources/Interactions/SegmentationInteraction.xml new file mode 100644 index 0000000000..3f55840cd2 --- /dev/null +++ b/Plugins/org.mitk.gui.qt.multilabelsegmentation/resources/Interactions/SegmentationInteraction.xml @@ -0,0 +1,12 @@ + + + + + + + + diff --git a/Plugins/org.mitk.gui.qt.multilabelsegmentation/resources/MorphologicalOperationsWidget.qrc b/Plugins/org.mitk.gui.qt.multilabelsegmentation/resources/MorphologicalOperationsWidget.qrc new file mode 100644 index 0000000000..3fe0afc1f8 --- /dev/null +++ b/Plugins/org.mitk.gui.qt.multilabelsegmentation/resources/MorphologicalOperationsWidget.qrc @@ -0,0 +1,9 @@ + + + Dilate_48x48.png + Erode_48x48.png + Closing_48x48.png + Opening_48x48.png + FillHoles_48x48.png + + diff --git a/Plugins/org.mitk.gui.qt.multilabelsegmentation/resources/MorphologicalOperations_48x48.png b/Plugins/org.mitk.gui.qt.multilabelsegmentation/resources/MorphologicalOperations_48x48.png new file mode 100644 index 0000000000..f19c6714e3 Binary files /dev/null and b/Plugins/org.mitk.gui.qt.multilabelsegmentation/resources/MorphologicalOperations_48x48.png differ diff --git a/Plugins/org.mitk.gui.qt.multilabelsegmentation/resources/MultiLabelSegmentationUtilities.qrc b/Plugins/org.mitk.gui.qt.multilabelsegmentation/resources/MultiLabelSegmentationUtilities.qrc new file mode 100644 index 0000000000..0ba8817c3d --- /dev/null +++ b/Plugins/org.mitk.gui.qt.multilabelsegmentation/resources/MultiLabelSegmentationUtilities.qrc @@ -0,0 +1,10 @@ + + + BooleanOperations_48x48.png + ImageMasking_48x48.png + MorphologicalOperations_48x48.png + SurfaceToImage_48x48.png + MultiLabelSegmentationUtilities_48x48.png + CTKWidgets_48x48.png + + diff --git a/Plugins/org.mitk.gui.qt.multilabelsegmentation/resources/MultiLabelSegmentationUtilities_48x48.png b/Plugins/org.mitk.gui.qt.multilabelsegmentation/resources/MultiLabelSegmentationUtilities_48x48.png new file mode 100644 index 0000000000..d441187899 Binary files /dev/null and b/Plugins/org.mitk.gui.qt.multilabelsegmentation/resources/MultiLabelSegmentationUtilities_48x48.png differ diff --git a/Plugins/org.mitk.gui.qt.multilabelsegmentation/resources/NewLabel_48x48.png b/Plugins/org.mitk.gui.qt.multilabelsegmentation/resources/NewLabel_48x48.png new file mode 100644 index 0000000000..c98380621e Binary files /dev/null and b/Plugins/org.mitk.gui.qt.multilabelsegmentation/resources/NewLabel_48x48.png differ diff --git a/Plugins/org.mitk.gui.qt.multilabelsegmentation/resources/NewSegmentationSession_48x48.png b/Plugins/org.mitk.gui.qt.multilabelsegmentation/resources/NewSegmentationSession_48x48.png new file mode 100644 index 0000000000..6818f12b4a Binary files /dev/null and b/Plugins/org.mitk.gui.qt.multilabelsegmentation/resources/NewSegmentationSession_48x48.png differ diff --git a/Plugins/org.mitk.gui.qt.multilabelsegmentation/resources/Opening_48x48.png b/Plugins/org.mitk.gui.qt.multilabelsegmentation/resources/Opening_48x48.png new file mode 100644 index 0000000000..14095dfdd2 Binary files /dev/null and b/Plugins/org.mitk.gui.qt.multilabelsegmentation/resources/Opening_48x48.png differ diff --git a/Plugins/org.mitk.gui.qt.multilabelsegmentation/resources/SurfaceToImage_48x48.png b/Plugins/org.mitk.gui.qt.multilabelsegmentation/resources/SurfaceToImage_48x48.png new file mode 100644 index 0000000000..6ad6070caa Binary files /dev/null and b/Plugins/org.mitk.gui.qt.multilabelsegmentation/resources/SurfaceToImage_48x48.png differ diff --git a/Plugins/org.mitk.gui.qt.multilabelsegmentation/resources/deformablePlane.png b/Plugins/org.mitk.gui.qt.multilabelsegmentation/resources/deformablePlane.png new file mode 100644 index 0000000000..28c5fb54e8 Binary files /dev/null and b/Plugins/org.mitk.gui.qt.multilabelsegmentation/resources/deformablePlane.png differ diff --git a/Plugins/org.mitk.gui.qt.multilabelsegmentation/resources/multilabelsegmentation.png b/Plugins/org.mitk.gui.qt.multilabelsegmentation/resources/multilabelsegmentation.png new file mode 100644 index 0000000000..d13c72b5b6 Binary files /dev/null and b/Plugins/org.mitk.gui.qt.multilabelsegmentation/resources/multilabelsegmentation.png differ diff --git a/Plugins/org.mitk.gui.qt.multilabelsegmentation/resources/multilabelsegmentation.qrc b/Plugins/org.mitk.gui.qt.multilabelsegmentation/resources/multilabelsegmentation.qrc new file mode 100644 index 0000000000..5a5003c555 --- /dev/null +++ b/Plugins/org.mitk.gui.qt.multilabelsegmentation/resources/multilabelsegmentation.qrc @@ -0,0 +1,7 @@ + + + multilabelsegmentation.png + NewLabel_48x48.png + NewSegmentationSession_48x48.png + + diff --git a/Plugins/org.mitk.gui.qt.multilabelsegmentation/src/QmitkMultiLabelSegmentationPreferencePage.cpp b/Plugins/org.mitk.gui.qt.multilabelsegmentation/src/QmitkMultiLabelSegmentationPreferencePage.cpp new file mode 100644 index 0000000000..2d4e21e132 --- /dev/null +++ b/Plugins/org.mitk.gui.qt.multilabelsegmentation/src/QmitkMultiLabelSegmentationPreferencePage.cpp @@ -0,0 +1,170 @@ +/*=================================================================== + +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 "QmitkMultiLabelSegmentationPreferencePage.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +QmitkMultiLabelSegmentationPreferencePage::QmitkMultiLabelSegmentationPreferencePage() +: m_MainControl(0) +, m_Initializing(false) +{ + +} + +QmitkMultiLabelSegmentationPreferencePage::~QmitkMultiLabelSegmentationPreferencePage() +{ + +} + +void QmitkMultiLabelSegmentationPreferencePage::Init(berry::IWorkbench::Pointer ) +{ + +} + +void QmitkMultiLabelSegmentationPreferencePage::CreateQtControl(QWidget* parent) +{ + m_Initializing = true; + berry::IPreferencesService* prefService + = berry::Platform::GetPreferencesService(); + + m_SegmentationPreferencesNode = prefService->GetSystemPreferences()->Node("/org.mitk.views.segmentation"); + + m_MainControl = new QWidget(parent); + + QVBoxLayout* displayOptionsLayout = new QVBoxLayout; + m_RadioOutline = new QRadioButton( "Draw as outline", m_MainControl); + displayOptionsLayout->addWidget( m_RadioOutline ); + m_RadioOverlay = new QRadioButton( "Draw as transparent overlay", m_MainControl); + displayOptionsLayout->addWidget( m_RadioOverlay ); + + QFormLayout *formLayout = new QFormLayout; + formLayout->setHorizontalSpacing(8); + formLayout->setVerticalSpacing(24); + formLayout->addRow( "2D display", displayOptionsLayout ); + + m_VolumeRenderingCheckBox = new QCheckBox( "Show as volume rendering", m_MainControl ); + formLayout->addRow( "3D display", m_VolumeRenderingCheckBox ); + connect( m_VolumeRenderingCheckBox, SIGNAL(stateChanged(int)), this, SLOT(OnVolumeRenderingCheckboxChecked(int)) ); + + QFormLayout* surfaceLayout = new QFormLayout; + surfaceLayout->setSpacing(8); + + m_SmoothingSpinBox = new QDoubleSpinBox(m_MainControl); + m_SmoothingSpinBox->setMinimum(0.0); + m_SmoothingSpinBox->setSingleStep(0.5); + m_SmoothingSpinBox->setValue(0.1); + m_SmoothingSpinBox->setToolTip("The Smoothing value is used as Sigma for a gaussian blur."); + surfaceLayout->addRow("Smoothing value (mm)", m_SmoothingSpinBox); + + m_DecimationSpinBox = new QDoubleSpinBox(m_MainControl); + m_DecimationSpinBox->setMinimum(0.0); + m_DecimationSpinBox->setMaximum(0.99); + m_DecimationSpinBox->setSingleStep(0.1); + m_DecimationSpinBox->setValue(0.5); + m_DecimationSpinBox->setToolTip("Valid range is [0, 1). High values increase decimation, especially when very close to 1. A value of 0 disables decimation."); + surfaceLayout->addRow("Decimation rate", m_DecimationSpinBox); + + m_SelectionModeCheckBox = new QCheckBox("Enable auto-selection mode", m_MainControl); + m_SelectionModeCheckBox->setToolTip("If checked the segmentation plugin ensures that only one segmentation and the according greyvalue image are visible at one time."); + formLayout->addRow("Data node selection mode",m_SelectionModeCheckBox); + + formLayout->addRow("Smoothed surface creation", surfaceLayout); + + m_MainControl->setLayout(formLayout); + this->Update(); + m_Initializing = false; +} + +QWidget* QmitkMultiLabelSegmentationPreferencePage::GetQtControl() const +{ + return m_MainControl; +} + +bool QmitkMultiLabelSegmentationPreferencePage::PerformOk() +{ + m_SegmentationPreferencesNode->PutBool("draw outline", m_RadioOutline->isChecked()); + m_SegmentationPreferencesNode->PutBool("volume rendering", m_VolumeRenderingCheckBox->isChecked()); + m_SegmentationPreferencesNode->PutDouble("smoothing value", m_SmoothingSpinBox->value()); + m_SegmentationPreferencesNode->PutDouble("decimation rate", m_DecimationSpinBox->value()); + m_SegmentationPreferencesNode->PutBool("auto selection", m_SelectionModeCheckBox->isChecked()); + return true; +} + +void QmitkMultiLabelSegmentationPreferencePage::PerformCancel() +{ + +} + +void QmitkMultiLabelSegmentationPreferencePage::Update() +{ + //m_EnableSingleEditing->setChecked(m_SegmentationPreferencesNode->GetBool("Single click property editing", true)); + if (m_SegmentationPreferencesNode->GetBool("draw outline", true) ) + { + m_RadioOutline->setChecked( true ); + } + else + { + m_RadioOverlay->setChecked( true ); + } + + m_VolumeRenderingCheckBox->setChecked( m_SegmentationPreferencesNode->GetBool("volume rendering", false) ); + + if (m_SegmentationPreferencesNode->GetBool("smoothing hint", true)) + { + m_SmoothingSpinBox->setDisabled(true); + } + else + { + m_SmoothingSpinBox->setEnabled(true); + } + + m_SelectionModeCheckBox->setChecked( m_SegmentationPreferencesNode->GetBool("auto selection", true) ); + + m_SmoothingSpinBox->setValue(m_SegmentationPreferencesNode->GetDouble("smoothing value", 0.1)); + m_DecimationSpinBox->setValue(m_SegmentationPreferencesNode->GetDouble("decimation rate", 0.5)); +} + +void QmitkMultiLabelSegmentationPreferencePage::OnVolumeRenderingCheckboxChecked(int state) +{ + if (m_Initializing) return; + + if ( state != Qt::Unchecked ) + { + QMessageBox::information(NULL, + "Memory warning", + "Turning on volume rendering of segmentations will make the application more memory intensive (and potentially prone to crashes).\n\n" + "If you encounter out-of-memory problems, try turning off volume rendering again."); + } +} + +void QmitkMultiLabelSegmentationPreferencePage::OnSmoothingCheckboxChecked(int state) +{ + if (state != Qt::Unchecked) + m_SmoothingSpinBox->setDisabled(true); + else + m_SmoothingSpinBox->setEnabled(true); +} diff --git a/Plugins/org.mitk.gui.qt.multilabelsegmentation/src/QmitkMultiLabelSegmentationPreferencePage.h b/Plugins/org.mitk.gui.qt.multilabelsegmentation/src/QmitkMultiLabelSegmentationPreferencePage.h new file mode 100644 index 0000000000..95cb8c74c4 --- /dev/null +++ b/Plugins/org.mitk.gui.qt.multilabelsegmentation/src/QmitkMultiLabelSegmentationPreferencePage.h @@ -0,0 +1,81 @@ +/*=================================================================== + +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 QmitkMultiLabelSegmentationPreferencePage_h_included +#define QmitkMultiLabelSegmentationPreferencePage_h_included + +#include "berryIQtPreferencePage.h" +#include "org_mitk_gui_qt_multilabelsegmentation_Export.h" +#include + +class QWidget; +class QCheckBox; +class QRadioButton; +class QDoubleSpinBox; + +class MITK_QT_SEGMENTATION QmitkMultiLabelSegmentationPreferencePage : public QObject, public berry::IQtPreferencePage +{ + Q_OBJECT + Q_INTERFACES(berry::IPreferencePage) + +public: + + QmitkMultiLabelSegmentationPreferencePage(); + ~QmitkMultiLabelSegmentationPreferencePage(); + + void Init(berry::IWorkbench::Pointer workbench); + + void CreateQtControl(QWidget* widget); + + QWidget* GetQtControl() const; + + /// + /// \see IPreferencePage::PerformOk() + /// + virtual bool PerformOk(); + + /// + /// \see IPreferencePage::PerformCancel() + /// + virtual void PerformCancel(); + + /// + /// \see IPreferencePage::Update() + /// + virtual void Update(); + +protected slots: + + void OnVolumeRenderingCheckboxChecked(int); + void OnSmoothingCheckboxChecked(int); + +protected: + + QWidget* m_MainControl; + QRadioButton* m_RadioOutline; + QRadioButton* m_RadioOverlay; + QCheckBox* m_VolumeRenderingCheckBox; + QDoubleSpinBox* m_SmoothingSpinBox; + QDoubleSpinBox* m_DecimationSpinBox; + QCheckBox* m_SelectionModeCheckBox; + + bool m_Initializing; + + berry::IPreferences::Pointer m_SegmentationPreferencesNode; +}; + +#endif /* QMITKDATAMANAGERPREFERENCEPAGE_H_ */ diff --git a/Plugins/org.mitk.gui.qt.multilabelsegmentation/src/internal/Common/QmitkDataSelectionWidget.cpp b/Plugins/org.mitk.gui.qt.multilabelsegmentation/src/internal/Common/QmitkDataSelectionWidget.cpp new file mode 100644 index 0000000000..34eca8427a --- /dev/null +++ b/Plugins/org.mitk.gui.qt.multilabelsegmentation/src/internal/Common/QmitkDataSelectionWidget.cpp @@ -0,0 +1,187 @@ +/*=================================================================== + +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 "QmitkDataSelectionWidget.h" +#include "internal/mitkPluginActivator.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +static mitk::NodePredicateBase::Pointer CreatePredicate(QmitkDataSelectionWidget::PredicateType predicateType) +{ + + mitk::NodePredicateAnd::Pointer segmentationPredicate = mitk::NodePredicateAnd::New(); + segmentationPredicate->AddPredicate(mitk::TNodePredicateDataType::New()); + segmentationPredicate->AddPredicate(mitk::NodePredicateNot::New(mitk::NodePredicateProperty::New("helper object"))); + + mitk::NodePredicateAnd::Pointer maskPredicate = mitk::NodePredicateAnd::New(); + maskPredicate->AddPredicate(mitk::NodePredicateProperty::New("binary", mitk::BoolProperty::New(true))); + maskPredicate->AddPredicate(mitk::NodePredicateNot::New(mitk::NodePredicateProperty::New("helper object", mitk::BoolProperty::New(true)))); + + mitk::NodePredicateDataType::Pointer isDwi = mitk::NodePredicateDataType::New("DiffusionImage"); + mitk::NodePredicateDataType::Pointer isDti = mitk::NodePredicateDataType::New("TensorImage"); + mitk::NodePredicateDataType::Pointer isQbi = mitk::NodePredicateDataType::New("QBallImage"); + mitk::TNodePredicateDataType::Pointer isImage = mitk::TNodePredicateDataType::New(); + + mitk::NodePredicateOr::Pointer validImages = mitk::NodePredicateOr::New(); + validImages->AddPredicate(isImage); + validImages->AddPredicate(isDwi); + validImages->AddPredicate(isDti); + validImages->AddPredicate(isQbi); + + mitk::NodePredicateAnd::Pointer imagePredicate = mitk::NodePredicateAnd::New(); + imagePredicate->AddPredicate(validImages); + imagePredicate->AddPredicate(mitk::NodePredicateNot::New(segmentationPredicate)); + imagePredicate->AddPredicate(mitk::NodePredicateNot::New(mitk::NodePredicateProperty::New("binary", mitk::BoolProperty::New(true)))); + imagePredicate->AddPredicate(mitk::NodePredicateNot::New(mitk::NodePredicateProperty::New("helper object", mitk::BoolProperty::New(true)))); + + mitk::NodePredicateAnd::Pointer surfacePredicate = mitk::NodePredicateAnd::New(); + surfacePredicate->AddPredicate(mitk::TNodePredicateDataType::New()); + surfacePredicate->AddPredicate(mitk::NodePredicateNot::New(mitk::NodePredicateProperty::New("helper object", mitk::BoolProperty::New(true)))); + + switch(predicateType) + { + case QmitkDataSelectionWidget::ImagePredicate: + return imagePredicate.GetPointer(); + + case QmitkDataSelectionWidget::MaskPredicate: + return maskPredicate.GetPointer(); + + case QmitkDataSelectionWidget::SegmentationPredicate: + return segmentationPredicate.GetPointer(); + + case QmitkDataSelectionWidget::SurfacePredicate: + return surfacePredicate.GetPointer(); + + default: + assert(false && "Unknown predefined predicate!"); + return NULL; + } +} + +QmitkDataSelectionWidget::QmitkDataSelectionWidget(QWidget* parent) + : QWidget(parent) +{ + m_Controls.setupUi(this); + m_Controls.helpLabel->hide(); +} + +QmitkDataSelectionWidget::~QmitkDataSelectionWidget() +{ +} + +unsigned int QmitkDataSelectionWidget::AddDataStorageComboBox(QmitkDataSelectionWidget::PredicateType predicate) +{ + return this->AddDataStorageComboBox("", predicate); +} + +unsigned int QmitkDataSelectionWidget::AddDataStorageComboBox(mitk::NodePredicateBase* predicate) +{ + return this->AddDataStorageComboBox("", predicate); +} + +unsigned int QmitkDataSelectionWidget::AddDataStorageComboBox(const QString &labelText, QmitkDataSelectionWidget::PredicateType predicate) +{ + return this->AddDataStorageComboBox(labelText, CreatePredicate(predicate)); +} + +unsigned int QmitkDataSelectionWidget::AddDataStorageComboBox(const QString &labelText, mitk::NodePredicateBase* predicate) +{ + int row = m_Controls.gridLayout->rowCount(); + + if (!labelText.isEmpty()) + { + QLabel* label = new QLabel(labelText, m_Controls.dataSelectionWidget); + label->setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Preferred); + m_Controls.gridLayout->addWidget(label, row, 0); + } + + QmitkDataStorageComboBox* comboBox = new QmitkDataStorageComboBox(this->GetDataStorage(), predicate, m_Controls.dataSelectionWidget); + connect(comboBox, SIGNAL(OnSelectionChanged(const mitk::DataNode *)), this, SLOT(OnSelectionChanged(const mitk::DataNode *))); + comboBox->SetAutoSelectNewItems(true); + m_Controls.gridLayout->addWidget(comboBox, row, 1); + + m_DataStorageComboBoxes.push_back(comboBox); + return static_cast(m_DataStorageComboBoxes.size() - 1); +} + +mitk::DataStorage::Pointer QmitkDataSelectionWidget::GetDataStorage() const +{ + ctkServiceReference ref = mitk::PluginActivator::getContext()->getServiceReference(); + assert(ref == true); + + mitk::IDataStorageService* service = mitk::PluginActivator::getContext()->getService(ref); + + assert(service); + + return service->GetDefaultDataStorage()->GetDataStorage(); +} + +mitk::DataNode::Pointer QmitkDataSelectionWidget::GetSelection(unsigned int index) +{ + assert(index < m_DataStorageComboBoxes.size()); + return m_DataStorageComboBoxes[index]->GetSelectedNode(); +} + +void QmitkDataSelectionWidget::SetPredicate(unsigned int index, PredicateType predicate) +{ + this->SetPredicate(index, CreatePredicate(predicate)); +} + +void QmitkDataSelectionWidget::SetPredicate(unsigned int index, mitk::NodePredicateBase* predicate) +{ + assert(index < m_DataStorageComboBoxes.size()); + m_DataStorageComboBoxes[index]->SetPredicate(predicate); +} + +void QmitkDataSelectionWidget::SetHelpText(const QString& text) +{ + if (!text.isEmpty()) + { + m_Controls.helpLabel->setText(text); + + if (!m_Controls.helpLabel->isVisible()) + m_Controls.helpLabel->show(); + } + else + { + m_Controls.helpLabel->hide(); + } +} + +void QmitkDataSelectionWidget::OnSelectionChanged(const mitk::DataNode* selection) +{ + std::vector::iterator it = std::find(m_DataStorageComboBoxes.begin(), m_DataStorageComboBoxes.end(), sender()); + assert(it != m_DataStorageComboBoxes.end()); + + emit SelectionChanged(std::distance(m_DataStorageComboBoxes.begin(), it), selection); +} diff --git a/Plugins/org.mitk.gui.qt.multilabelsegmentation/src/internal/Common/QmitkDataSelectionWidget.h b/Plugins/org.mitk.gui.qt.multilabelsegmentation/src/internal/Common/QmitkDataSelectionWidget.h new file mode 100644 index 0000000000..3ccd830c98 --- /dev/null +++ b/Plugins/org.mitk.gui.qt.multilabelsegmentation/src/internal/Common/QmitkDataSelectionWidget.h @@ -0,0 +1,70 @@ +/*=================================================================== + +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 QmitkDataSelectionWidget_h +#define QmitkDataSelectionWidget_h + +#include +#include +#include +#include + +namespace mitk +{ + class NodePredicateBase; +} + +class QmitkDataStorageComboBox; + +class QmitkDataSelectionWidget : public QWidget +{ + Q_OBJECT + +public: + enum PredicateType + { + ImagePredicate, + MaskPredicate, + SegmentationPredicate, + SurfacePredicate + }; + + explicit QmitkDataSelectionWidget(QWidget* parent = NULL); + ~QmitkDataSelectionWidget(); + + unsigned int AddDataStorageComboBox(PredicateType predicate); + unsigned int AddDataStorageComboBox(mitk::NodePredicateBase* predicate = NULL); + unsigned int AddDataStorageComboBox(const QString &labelText, PredicateType predicate); + unsigned int AddDataStorageComboBox(const QString &labelText, mitk::NodePredicateBase* predicate = NULL); + + mitk::DataStorage::Pointer GetDataStorage() const; + mitk::DataNode::Pointer GetSelection(unsigned int index); + void SetPredicate(unsigned int index, PredicateType predicate); + void SetPredicate(unsigned int index, mitk::NodePredicateBase* predicate); + void SetHelpText(const QString& text); + +signals: + void SelectionChanged(unsigned int index, const mitk::DataNode* selection); + +private slots: + void OnSelectionChanged(const mitk::DataNode* selection); + +private: + Ui::QmitkDataSelectionWidgetControls m_Controls; + std::vector m_DataStorageComboBoxes; +}; + +#endif diff --git a/Plugins/org.mitk.gui.qt.multilabelsegmentation/src/internal/Common/QmitkDataSelectionWidgetControls.ui b/Plugins/org.mitk.gui.qt.multilabelsegmentation/src/internal/Common/QmitkDataSelectionWidgetControls.ui new file mode 100644 index 0000000000..1849a496d9 --- /dev/null +++ b/Plugins/org.mitk.gui.qt.multilabelsegmentation/src/internal/Common/QmitkDataSelectionWidgetControls.ui @@ -0,0 +1,64 @@ + + + QmitkDataSelectionWidgetControls + + + + 0 + 0 + 333 + 191 + + + + + + + + 0 + + + + + + + + Data Selection + + + + + + + 0 + + + + + + + + + 0 + 0 + + + + color: red + + + + + + true + + + + + + + + + + + diff --git a/Plugins/org.mitk.gui.qt.multilabelsegmentation/src/internal/QmitkAutocropAction.cpp b/Plugins/org.mitk.gui.qt.multilabelsegmentation/src/internal/QmitkAutocropAction.cpp new file mode 100644 index 0000000000..c15ae4f6e0 --- /dev/null +++ b/Plugins/org.mitk.gui.qt.multilabelsegmentation/src/internal/QmitkAutocropAction.cpp @@ -0,0 +1,197 @@ +/*=================================================================== + +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 "QmitkAutocropAction.h" + +#include "mitkAutoCropImageFilter.h" +#include "mitkImageCast.h" +#include "mitkRenderingManager.h" +#include "mitkProgressBar.h" + +#include + +//needed for qApp +#include + +QmitkAutocropAction::QmitkAutocropAction() +{ +} + +QmitkAutocropAction::~QmitkAutocropAction() +{ +} + +void QmitkAutocropAction::Run( const QList &selectedNodes ) +{ + foreach ( mitk::DataNode::Pointer node, selectedNodes ) + { + if (node) + { + mitk::Image::Pointer image = dynamic_cast( node->GetData() ); + if (image.IsNull()) return; + + mitk::ProgressBar::GetInstance()->AddStepsToDo(10); + mitk::ProgressBar::GetInstance()->Progress(2); + + qApp->processEvents(); + + mitk::AutoCropImageFilter::Pointer cropFilter = mitk::AutoCropImageFilter::New(); + cropFilter->SetInput( image ); + cropFilter->SetBackgroundValue( 0 ); + cropFilter->SetMarginFactor(1.5); + try + { + cropFilter->Update(); + + image = cropFilter->GetOutput(); + + if (image.IsNotNull()) + { + + if (image->GetDimension() == 4) + { + MITK_INFO << "4D AUTOCROP DOES NOT WORK AT THE MOMENT"; + throw "4D AUTOCROP DOES NOT WORK AT THE MOMENT"; + + unsigned int timesteps = image->GetDimension(3); + for (unsigned int i = 0; i < timesteps; i++) + { + mitk::ImageTimeSelector::Pointer imageTimeSelector = mitk::ImageTimeSelector::New(); + imageTimeSelector->SetInput(image); + imageTimeSelector->SetTimeNr(i); + imageTimeSelector->UpdateLargestPossibleRegion(); + + // We split a long nested code line into separate calls for debugging: + mitk::ImageSource::OutputImageType *_3dSlice = imageTimeSelector->GetOutput(); + mitk::Image::Pointer _cropped3dSlice = this->IncreaseCroppedImageSize(_3dSlice); + + // +++ BUG +++ BUG +++ BUG +++ BUG +++ BUG +++ BUG +++ BUG +++ + void *_data = _cropped3dSlice->GetData(); + + // + // We write some stripes into the image + if ((i & 1) == 0) + { + int depth = _cropped3dSlice->GetDimension(2); + int height = _cropped3dSlice->GetDimension(1); + int width = _cropped3dSlice->GetDimension(0); + + for (int z = 0; z < depth; ++z) + for (int y = 0; y < height; ++y) + for (int x = 0; x < width; ++x) + reinterpret_cast(_data)[(width * height * z) + (width * y) + x] = x & 1; + // + } + + image->SetVolume(_data, i); + } + node->SetData( image ); // bug fix 3145 + } + else + { + node->SetData( this->IncreaseCroppedImageSize(image) ); // bug fix 3145 + } + // Reinit node + mitk::RenderingManager::GetInstance()->InitializeViews( + node->GetData()->GetTimeSlicedGeometry(), mitk::RenderingManager::REQUEST_UPDATE_ALL, true ); + mitk::RenderingManager::GetInstance()->RequestUpdateAll(); + + } + } + catch(...) + { + MITK_ERROR << "Cropping image failed..."; + } + mitk::ProgressBar::GetInstance()->Progress(8); + } + else + { + MITK_INFO << " a NULL node selected"; + } + } +} + +mitk::Image::Pointer QmitkAutocropAction::IncreaseCroppedImageSize( mitk::Image::Pointer image ) +{ + typedef itk::Image< short, 3 > ImageType; + typedef itk::Image< unsigned char, 3 > PADOutputImageType; + + ImageType::Pointer itkTransformImage = ImageType::New(); + mitk::CastToItkImage( image, itkTransformImage ); + + typedef itk::ConstantPadImageFilter< ImageType, PADOutputImageType > PadFilterType; + PadFilterType::Pointer padFilter = PadFilterType::New(); + + unsigned long upperPad[3]; + unsigned long lowerPad[3]; + int borderLiner = 3; + + mitk::Point3D mitkOriginPoint; + double origin[3]; + origin[0]=0; + origin[1]=0; + origin[2]=0; + itkTransformImage->SetOrigin(origin); + + lowerPad[0]=borderLiner; + lowerPad[1]=borderLiner; + lowerPad[2]=borderLiner; + + upperPad[0]=borderLiner; + upperPad[1]=borderLiner; + upperPad[2]=borderLiner; + + padFilter->SetInput(itkTransformImage); + padFilter->SetConstant(0); + padFilter->SetPadUpperBound(upperPad); + padFilter->SetPadLowerBound(lowerPad); + padFilter->UpdateLargestPossibleRegion(); + + + mitk::Image::Pointer paddedImage = mitk::Image::New(); + paddedImage->InitializeByItk(padFilter->GetOutput()); + mitk::CastToMitkImage(padFilter->GetOutput(), paddedImage); + + //calculate translation according to padding to get the new origin + mitk::Point3D paddedOrigin = image->GetGeometry()->GetOrigin(); + mitk::Vector3D spacing = image->GetGeometry()->GetSpacing(); + paddedOrigin[0] -= (borderLiner)*spacing[0]; + paddedOrigin[1] -= (borderLiner)*spacing[1]; + paddedOrigin[2] -= (borderLiner)*spacing[2]; + + paddedImage->GetGeometry()->SetOrigin( paddedOrigin ); + + return paddedImage; +} + +void QmitkAutocropAction::SetSmoothed(bool /*smoothed*/) +{ + //not needed +} + +void QmitkAutocropAction::SetDecimated(bool /*decimated*/) +{ + //not needed +} + +void QmitkAutocropAction::SetDataStorage(mitk::DataStorage* /*dataStorage*/) +{ + //not needed +} + +void QmitkAutocropAction::SetFunctionality(berry::QtViewPart* /*functionality*/) +{ + //not needed +} diff --git a/Plugins/org.mitk.gui.qt.multilabelsegmentation/src/internal/QmitkAutocropAction.h b/Plugins/org.mitk.gui.qt.multilabelsegmentation/src/internal/QmitkAutocropAction.h new file mode 100644 index 0000000000..acb9ae57d9 --- /dev/null +++ b/Plugins/org.mitk.gui.qt.multilabelsegmentation/src/internal/QmitkAutocropAction.h @@ -0,0 +1,56 @@ +/*=================================================================== + +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 QMITK_AUTOCROPACTION_H +#define QMITK_AUTOCROPACTION_H + +#include "mitkIContextMenuAction.h" + +#include "org_mitk_gui_qt_multilabelsegmentation_Export.h" + +#include "vector" +#include "mitkDataNode.h" +#include "mitkImage.h" + +class QmitkStdMultiWidget; + +class MITK_QT_SEGMENTATION QmitkAutocropAction : public QObject, public mitk::IContextMenuAction +{ + Q_OBJECT + Q_INTERFACES(mitk::IContextMenuAction) + +public: + + QmitkAutocropAction(); + virtual ~QmitkAutocropAction(); + + //interface methods + void Run( const QList& selectedNodes ); + void SetDataStorage(mitk::DataStorage* dataStorage); + void SetSmoothed(bool smoothed); + void SetDecimated(bool decimated); + void SetFunctionality(berry::QtViewPart* functionality); + +protected: + + mitk::Image::Pointer IncreaseCroppedImageSize( mitk::Image::Pointer image ); + +private: + + typedef QList NodeList; + +}; + +#endif // QMITK_AUTOCROPACTION_H diff --git a/Plugins/org.mitk.gui.qt.multilabelsegmentation/src/internal/QmitkConvertMaskToLabelAction.cpp b/Plugins/org.mitk.gui.qt.multilabelsegmentation/src/internal/QmitkConvertMaskToLabelAction.cpp new file mode 100644 index 0000000000..4d4dfd16b5 --- /dev/null +++ b/Plugins/org.mitk.gui.qt.multilabelsegmentation/src/internal/QmitkConvertMaskToLabelAction.cpp @@ -0,0 +1,102 @@ +/*=================================================================== + +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 "QmitkConvertMaskToLabelAction.h" + +#include "mitkRenderingManager.h" +#include "mitkLabelSetImage.h" +#include "mitkToolManagerProvider.h" + +//needed for qApp +#include + +QmitkConvertMaskToLabelAction::QmitkConvertMaskToLabelAction() +{ +} + +QmitkConvertMaskToLabelAction::~QmitkConvertMaskToLabelAction() +{ +} + +void QmitkConvertMaskToLabelAction::Run( const QList &selectedNodes ) +{ + mitk::ToolManager::Pointer toolManager = mitk::ToolManagerProvider::GetInstance()->GetToolManager(); + assert(toolManager); + + mitk::DataNode* workingNode = toolManager->GetWorkingData(0); + if (!workingNode) + { + MITK_INFO << "There is no available segmentation. Please load or create one before using this tool."; + return; + } + + mitk::LabelSetImage* workingImage = dynamic_cast( workingNode->GetData() ); + assert(workingImage); + + foreach ( mitk::DataNode::Pointer maskNode, selectedNodes ) + { + if (maskNode) + { + mitk::Image* mask = dynamic_cast(maskNode->GetData() ); + if (!mask) continue; + + std::string name = maskNode->GetName(); + mitk::Color color; + mitk::ColorProperty::Pointer colorProp; + maskNode->GetProperty(colorProp,"color"); + if (colorProp.IsNull()) continue; + color = colorProp->GetValue(); + workingImage->GetLabelSet()->AddLabel(name,color); + //workingImage->AddLabelEvent.Send(); + + try + { + workingImage->MaskStamp( mask, false ); + } + catch ( mitk::Exception& e ) + { + MITK_ERROR << "Exception caught: " << e.GetDescription(); + return; + } + + maskNode->SetVisibility(false); + mitk::RenderingManager::GetInstance()->RequestUpdateAll(); + } + else + { + MITK_INFO << " a NULL node was selected"; + } + } +} + +void QmitkConvertMaskToLabelAction::SetSmoothed(bool /*smoothed*/) +{ + //not needed +} + +void QmitkConvertMaskToLabelAction::SetDecimated(bool /*decimated*/) +{ + //not needed +} + +void QmitkConvertMaskToLabelAction::SetDataStorage(mitk::DataStorage* /*dataStorage*/) +{ + //not needed +} + +void QmitkConvertMaskToLabelAction::SetFunctionality(berry::QtViewPart* /*functionality*/) +{ + //not needed +} diff --git a/Plugins/org.mitk.gui.qt.multilabelsegmentation/src/internal/QmitkConvertMaskToLabelAction.h b/Plugins/org.mitk.gui.qt.multilabelsegmentation/src/internal/QmitkConvertMaskToLabelAction.h new file mode 100644 index 0000000000..cc89d03990 --- /dev/null +++ b/Plugins/org.mitk.gui.qt.multilabelsegmentation/src/internal/QmitkConvertMaskToLabelAction.h @@ -0,0 +1,54 @@ +/*=================================================================== + +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 __QmitkConvertMaskToLabelAction_H_ +#define __QmitkConvertMaskToLabelAction_H_ + +#include "mitkIContextMenuAction.h" + +#include "org_mitk_gui_qt_multilabelsegmentation_Export.h" + +#include "vector" +#include "mitkDataNode.h" +#include "mitkImage.h" + +class QmitkStdMultiWidget; + +class MITK_QT_SEGMENTATION QmitkConvertMaskToLabelAction : public QObject, public mitk::IContextMenuAction +{ + Q_OBJECT + Q_INTERFACES(mitk::IContextMenuAction) + +public: + + QmitkConvertMaskToLabelAction(); + virtual ~QmitkConvertMaskToLabelAction(); + + //interface methods + void Run( const QList& selectedNodes ); + void SetDataStorage(mitk::DataStorage* dataStorage); + void SetSmoothed(bool smoothed); + void SetDecimated(bool decimated); + void SetFunctionality(berry::QtViewPart* functionality); + +protected: + +private: + + typedef QList NodeList; + +}; + +#endif // __QmitkConvertMaskToLabelAction_H_ diff --git a/Plugins/org.mitk.gui.qt.multilabelsegmentation/src/internal/QmitkConvertSurfaceToLabelAction.cpp b/Plugins/org.mitk.gui.qt.multilabelsegmentation/src/internal/QmitkConvertSurfaceToLabelAction.cpp new file mode 100644 index 0000000000..f888833118 --- /dev/null +++ b/Plugins/org.mitk.gui.qt.multilabelsegmentation/src/internal/QmitkConvertSurfaceToLabelAction.cpp @@ -0,0 +1,106 @@ +/*=================================================================== + +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 "QmitkConvertSurfaceToLabelAction.h" + +#include "mitkRenderingManager.h" +#include "mitkLabelSetImage.h" +#include "mitkToolManagerProvider.h" + +//needed for qApp +#include +#include + +QmitkConvertSurfaceToLabelAction::QmitkConvertSurfaceToLabelAction() +{ +} + +QmitkConvertSurfaceToLabelAction::~QmitkConvertSurfaceToLabelAction() +{ +} + +void QmitkConvertSurfaceToLabelAction::Run( const QList &selectedNodes ) +{ + mitk::ToolManager::Pointer toolManager = mitk::ToolManagerProvider::GetInstance()->GetToolManager(); + assert(toolManager); + + mitk::DataNode* workingNode = toolManager->GetWorkingData(0); + if (!workingNode) + { + MITK_INFO << "There is no available segmentation. Please load or create one before using this tool."; + return; + } + + mitk::LabelSetImage* workingImage = dynamic_cast( workingNode->GetData() ); + assert(workingImage); + + foreach ( mitk::DataNode::Pointer surfaceNode, selectedNodes ) + { + if (surfaceNode) + { + mitk::Surface* surface = dynamic_cast(surfaceNode->GetData() ); + if (!surface) continue; + + std::string name = surfaceNode->GetName(); + mitk::Color color; + mitk::ColorProperty::Pointer colorProp; + surfaceNode->GetProperty(colorProp,"color"); + if (colorProp.IsNull()) continue; + color = colorProp->GetValue(); + workingImage->GetLabelSet()->AddLabel(name,color); + //workingImage->AddLabelEvent.Send(); + + try + { + QApplication::setOverrideCursor( QCursor(Qt::WaitCursor) ); +// workingImage->SurfaceStamp( surface, false ); + QApplication::restoreOverrideCursor(); + } + catch ( mitk::Exception& e ) + { + QApplication::restoreOverrideCursor(); + MITK_ERROR << "Exception caught: " << e.GetDescription(); + return; + } + + surfaceNode->SetVisibility(false); + mitk::RenderingManager::GetInstance()->RequestUpdateAll(); + } + else + { + MITK_INFO << " a NULL node was selected"; + } + } +} + +void QmitkConvertSurfaceToLabelAction::SetSmoothed(bool /*smoothed*/) +{ + //not needed +} + +void QmitkConvertSurfaceToLabelAction::SetDecimated(bool /*decimated*/) +{ + //not needed +} + +void QmitkConvertSurfaceToLabelAction::SetDataStorage(mitk::DataStorage* /*dataStorage*/) +{ + //not needed +} + +void QmitkConvertSurfaceToLabelAction::SetFunctionality(berry::QtViewPart* /*functionality*/) +{ + //not needed +} diff --git a/Plugins/org.mitk.gui.qt.multilabelsegmentation/src/internal/QmitkConvertSurfaceToLabelAction.h b/Plugins/org.mitk.gui.qt.multilabelsegmentation/src/internal/QmitkConvertSurfaceToLabelAction.h new file mode 100644 index 0000000000..5cbde8ed99 --- /dev/null +++ b/Plugins/org.mitk.gui.qt.multilabelsegmentation/src/internal/QmitkConvertSurfaceToLabelAction.h @@ -0,0 +1,54 @@ +/*=================================================================== + +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 __QmitkConvertSurfaceToLabelAction_H_ +#define __QmitkConvertSurfaceToLabelAction_H_ + +#include "mitkIContextMenuAction.h" + +#include "org_mitk_gui_qt_multilabelsegmentation_Export.h" + +#include "vector" +#include "mitkDataNode.h" +#include "mitkImage.h" + +class QmitkStdMultiWidget; + +class MITK_QT_SEGMENTATION QmitkConvertSurfaceToLabelAction : public QObject, public mitk::IContextMenuAction +{ + Q_OBJECT + Q_INTERFACES(mitk::IContextMenuAction) + +public: + + QmitkConvertSurfaceToLabelAction(); + virtual ~QmitkConvertSurfaceToLabelAction(); + + //interface methods + void Run( const QList& selectedNodes ); + void SetDataStorage(mitk::DataStorage* dataStorage); + void SetSmoothed(bool smoothed); + void SetDecimated(bool decimated); + void SetFunctionality(berry::QtViewPart* functionality); + +protected: + +private: + + typedef QList NodeList; + +}; + +#endif // __QmitkConvertSurfaceToLabelAction_H_ diff --git a/Plugins/org.mitk.gui.qt.multilabelsegmentation/src/internal/QmitkConvertToMultiLabelSegmentationAction.cpp b/Plugins/org.mitk.gui.qt.multilabelsegmentation/src/internal/QmitkConvertToMultiLabelSegmentationAction.cpp new file mode 100644 index 0000000000..d256668004 --- /dev/null +++ b/Plugins/org.mitk.gui.qt.multilabelsegmentation/src/internal/QmitkConvertToMultiLabelSegmentationAction.cpp @@ -0,0 +1,83 @@ +/*=================================================================== + +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 "QmitkConvertToMultiLabelSegmentationAction.h" + +#include "mitkLabelSetImage.h" +#include "mitkRenderingManager.h" + +//needed for qApp +#include + +QmitkConvertToMultiLabelSegmentationAction::QmitkConvertToMultiLabelSegmentationAction() +{ +} + +QmitkConvertToMultiLabelSegmentationAction::~QmitkConvertToMultiLabelSegmentationAction() +{ +} + +void QmitkConvertToMultiLabelSegmentationAction::Run( const QList &selectedNodes ) +{ + foreach ( mitk::DataNode::Pointer referenceNode, selectedNodes ) + { + if (referenceNode.IsNotNull()) + { + mitk::Image::Pointer referenceImage = dynamic_cast( referenceNode->GetData() ); + if (referenceImage.IsNull()) return; + + mitk::LabelSetImage::Pointer lsImage = mitk::LabelSetImage::New(); + try + { + lsImage->InitializeByLabeledImage(referenceImage); + } + catch (mitk::Exception &e) + { + MITK_ERROR << "Exception caught: " << e.GetDescription(); + return; + } + if (m_DataStorage.IsNotNull()) + { + mitk::DataNode::Pointer newNode = mitk::DataNode::New(); + std::string newName = referenceNode->GetName(); + newName += "-labels"; + newNode->SetName(newName); + newNode->SetData(lsImage); + m_DataStorage->Add(newNode,referenceNode); + } + lsImage->Modified(); + } + } +} + +void QmitkConvertToMultiLabelSegmentationAction::SetDataStorage(mitk::DataStorage* dataStorage) +{ + m_DataStorage = dataStorage; +} + +void QmitkConvertToMultiLabelSegmentationAction::SetFunctionality(berry::QtViewPart* /*functionality*/) +{ + //not needed +} + +void QmitkConvertToMultiLabelSegmentationAction::SetSmoothed(bool smoothed) +{ + //not needed +} + +void QmitkConvertToMultiLabelSegmentationAction::SetDecimated(bool decimated) +{ + //not needed +} diff --git a/Plugins/org.mitk.gui.qt.multilabelsegmentation/src/internal/QmitkConvertToMultiLabelSegmentationAction.h b/Plugins/org.mitk.gui.qt.multilabelsegmentation/src/internal/QmitkConvertToMultiLabelSegmentationAction.h new file mode 100644 index 0000000000..72fc44ca80 --- /dev/null +++ b/Plugins/org.mitk.gui.qt.multilabelsegmentation/src/internal/QmitkConvertToMultiLabelSegmentationAction.h @@ -0,0 +1,51 @@ +/*=================================================================== + +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 QMITK_ConvertToMultiLabelSegmentation_H +#define QMITK_ConvertToMultiLabelSegmentation_H + +#include "mitkIContextMenuAction.h" + +#include "org_mitk_gui_qt_multilabelsegmentation_Export.h" + +#include "vector" +#include "mitkDataNode.h" +//#include "mitkImage.h" + +class MITK_QT_SEGMENTATION QmitkConvertToMultiLabelSegmentationAction : public QObject, public mitk::IContextMenuAction +{ + Q_OBJECT + Q_INTERFACES(mitk::IContextMenuAction) + +public: + + QmitkConvertToMultiLabelSegmentationAction(); + virtual ~QmitkConvertToMultiLabelSegmentationAction(); + + //interface methods + virtual void Run( const QList& selectedNodes ); + virtual void SetDataStorage(mitk::DataStorage* dataStorage); + virtual void SetFunctionality(berry::QtViewPart* functionality); + virtual void SetSmoothed(bool smoothed); + virtual void SetDecimated(bool decimated); + +private: + + typedef QList NodeList; + + mitk::DataStorage::Pointer m_DataStorage; +}; + +#endif // QMITK_ConvertToMultiLabelSegmentation_H diff --git a/Plugins/org.mitk.gui.qt.multilabelsegmentation/src/internal/QmitkCreateMultiLabelPresetAction.cpp b/Plugins/org.mitk.gui.qt.multilabelsegmentation/src/internal/QmitkCreateMultiLabelPresetAction.cpp new file mode 100644 index 0000000000..a423485f95 --- /dev/null +++ b/Plugins/org.mitk.gui.qt.multilabelsegmentation/src/internal/QmitkCreateMultiLabelPresetAction.cpp @@ -0,0 +1,87 @@ +/*=================================================================== + +The Medical Imaging Interaction Toolkit (MITK) + +Copyright (c) German Cancer Research Center, +Division of Medical and Biological Informatics. +All rights reserved. + +This software is distributed WITHOUT ANY WARRANTY; without +even the implied warranty of MERCHANTABILITY or FITNESS FOR +A PARTICULAR PURPOSE. + +See LICENSE.txt or http://www.mitk.org for details. + +===================================================================*/ + +#include +#include "QFileDialog" +#include "QMessageBox" + +#include "tinyxml.h" + +#include "mitkLabelSetImage.h" +#include "mitkLabelSetIOHelper.h" +#include "QmitkCreateMultiLabelPresetAction.h" + +QmitkCreateMultiLabelPresetAction::QmitkCreateMultiLabelPresetAction() +{ +} + +QmitkCreateMultiLabelPresetAction::~QmitkCreateMultiLabelPresetAction() +{ +} + +void QmitkCreateMultiLabelPresetAction::Run( const QList &selectedNodes ) +{ + foreach ( mitk::DataNode::Pointer referenceNode, selectedNodes ) + { + if (referenceNode.IsNotNull()) + { + mitk::LabelSetImage::Pointer referenceImage = dynamic_cast( referenceNode->GetData() ); + assert(referenceImage); + + if(referenceImage->GetNumberOfLabels() <= 1) + { + QMessageBox::information(NULL, "Create LabelSetImage Preset", "Could not create a LabelSetImage preset.\nNo Labels defined!\n");\ + return; + } + + std::string sName = referenceNode->GetName(); + QString qName; + qName.sprintf("%s.lsetp",sName.c_str()); + QString filename = QFileDialog::getSaveFileName( NULL,"save file dialog",QString(),"LabelSet Preset(*.lsetp)"); + if ( filename.isEmpty() ) + return; + + std::string fileName = filename.toStdString(); + bool wasSaved = mitk::LabelSetIOHelper::SaveLabelSetImagePreset(fileName,referenceImage); + + if(!wasSaved) + { + QMessageBox::information(NULL, "Create LabelSetImage Preset", "Could not save a LabelSetImage preset as Xml.\n");\ + return; + } + } + } +} + +void QmitkCreateMultiLabelPresetAction::SetDataStorage(mitk::DataStorage* dataStorage) +{ + m_DataStorage = dataStorage; +} + +void QmitkCreateMultiLabelPresetAction::SetFunctionality(berry::QtViewPart* /*functionality*/) +{ + //not needed +} + +void QmitkCreateMultiLabelPresetAction::SetSmoothed(bool smoothed) +{ + //not needed +} + +void QmitkCreateMultiLabelPresetAction::SetDecimated(bool decimated) +{ + //not needed +} diff --git a/Plugins/org.mitk.gui.qt.multilabelsegmentation/src/internal/QmitkCreateMultiLabelPresetAction.h b/Plugins/org.mitk.gui.qt.multilabelsegmentation/src/internal/QmitkCreateMultiLabelPresetAction.h new file mode 100644 index 0000000000..a9d9849c51 --- /dev/null +++ b/Plugins/org.mitk.gui.qt.multilabelsegmentation/src/internal/QmitkCreateMultiLabelPresetAction.h @@ -0,0 +1,50 @@ +/*=================================================================== + +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 QMITK_QmitkCreateMultiLabelPresetAction_H +#define QMITK_QmitkCreateMultiLabelPresetAction_H + +#include "mitkIContextMenuAction.h" + +#include "org_mitk_gui_qt_multilabelsegmentation_Export.h" + +#include "vector" +#include "mitkDataNode.h" + +class MITK_QT_SEGMENTATION QmitkCreateMultiLabelPresetAction : public QObject, public mitk::IContextMenuAction +{ + Q_OBJECT + Q_INTERFACES(mitk::IContextMenuAction) + +public: + + QmitkCreateMultiLabelPresetAction(); + virtual ~QmitkCreateMultiLabelPresetAction(); + + //interface methods + virtual void Run( const QList& selectedNodes ); + virtual void SetDataStorage(mitk::DataStorage* dataStorage); + virtual void SetFunctionality(berry::QtViewPart* functionality); + virtual void SetSmoothed(bool smoothed); + virtual void SetDecimated(bool decimated); + +private: + + typedef QList NodeList; + + mitk::DataStorage::Pointer m_DataStorage; +}; + +#endif // QMITK_CreateMultiLabelSegmentation_H diff --git a/Plugins/org.mitk.gui.qt.multilabelsegmentation/src/internal/QmitkCreateMultiLabelSegmentationAction.cpp b/Plugins/org.mitk.gui.qt.multilabelsegmentation/src/internal/QmitkCreateMultiLabelSegmentationAction.cpp new file mode 100644 index 0000000000..578c9699cb --- /dev/null +++ b/Plugins/org.mitk.gui.qt.multilabelsegmentation/src/internal/QmitkCreateMultiLabelSegmentationAction.cpp @@ -0,0 +1,113 @@ +/*=================================================================== + +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 "QmitkCreateMultiLabelSegmentationAction.h" + +#include "mitkLabelSetImage.h" +#include "mitkRenderingManager.h" + +#include "QInputDialog" +#include "QMessageBox" + +#include "QmitkNewSegmentationDialog.h" +#include "QmitkMultiLabelSegmentationView.h" +#include "QmitkMultiLabelSegmentationOrganNamesHandling.cpp" +//needed for qApp +#include + +QmitkCreateMultiLabelSegmentationAction::QmitkCreateMultiLabelSegmentationAction() +{ +} + +QmitkCreateMultiLabelSegmentationAction::~QmitkCreateMultiLabelSegmentationAction() +{ +} + +void QmitkCreateMultiLabelSegmentationAction::Run( const QList &selectedNodes ) +{ + foreach ( mitk::DataNode::Pointer referenceNode, selectedNodes ) + { + if (referenceNode.IsNotNull()) + { + + mitk::Image* referenceImage = dynamic_cast( referenceNode->GetData() ); + assert(referenceImage); + + QString newName = QString::fromStdString(referenceNode->GetName()); + newName.append("-labels"); + + bool ok = false; + newName = QInputDialog::getText(NULL, "New Segmentation Session", "New name:", QLineEdit::Normal, newName, &ok); + if(!ok) return; + + mitk::LabelSetImage::Pointer workingImage = mitk::LabelSetImage::New(); + + try + { + workingImage->Initialize(referenceImage); + } + catch ( mitk::Exception& e ) + { + MITK_ERROR << "Exception caught: " << e.GetDescription(); + QMessageBox::information(NULL, "New Segmentation Session", "Could not create a new segmentation session.\n"); + return; + } + + mitk::DataNode::Pointer workingNode = mitk::DataNode::New(); + workingNode->SetData(workingImage); + workingNode->SetName(newName.toStdString()); + + // set additional image information + workingImage->GetExteriorLabel()->SetProperty("name.parent",mitk::StringProperty::New(referenceNode->GetName().c_str())); + workingImage->GetExteriorLabel()->SetProperty("name.image",mitk::StringProperty::New(newName.toStdString().c_str())); + + if (!m_DataStorage->Exists(workingNode)) + m_DataStorage->Add(workingNode, referenceNode); + + QmitkNewSegmentationDialog* dialog = new QmitkNewSegmentationDialog( ); + dialog->SetSuggestionList( mitk::OrganNamesHandling::GetDefaultOrganColorString()); + dialog->setWindowTitle("New Label"); + + int dialogReturnValue = dialog->exec(); + + if ( dialogReturnValue == QDialog::Rejected ) return; + + QString segName = dialog->GetSegmentationName(); + if(segName.isEmpty()) segName = "Unnamed"; + workingImage->GetActiveLabelSet()->AddLabel(segName.toStdString(), dialog->GetColor()); + + } + } +} + +void QmitkCreateMultiLabelSegmentationAction::SetDataStorage(mitk::DataStorage* dataStorage) +{ + m_DataStorage = dataStorage; +} + +void QmitkCreateMultiLabelSegmentationAction::SetFunctionality(berry::QtViewPart* /*functionality*/) +{ + //not needed +} + +void QmitkCreateMultiLabelSegmentationAction::SetSmoothed(bool smoothed) +{ + //not needed +} + +void QmitkCreateMultiLabelSegmentationAction::SetDecimated(bool decimated) +{ + //not needed +} diff --git a/Plugins/org.mitk.gui.qt.multilabelsegmentation/src/internal/QmitkCreateMultiLabelSegmentationAction.h b/Plugins/org.mitk.gui.qt.multilabelsegmentation/src/internal/QmitkCreateMultiLabelSegmentationAction.h new file mode 100644 index 0000000000..7cc8b4e1d6 --- /dev/null +++ b/Plugins/org.mitk.gui.qt.multilabelsegmentation/src/internal/QmitkCreateMultiLabelSegmentationAction.h @@ -0,0 +1,50 @@ +/*=================================================================== + +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 QMITK_CreateMultiLabelSegmentation_H +#define QMITK_CreateMultiLabelSegmentation_H + +#include "mitkIContextMenuAction.h" + +#include "org_mitk_gui_qt_multilabelsegmentation_Export.h" + +#include "vector" +#include "mitkDataNode.h" + +class MITK_QT_SEGMENTATION QmitkCreateMultiLabelSegmentationAction : public QObject, public mitk::IContextMenuAction +{ + Q_OBJECT + Q_INTERFACES(mitk::IContextMenuAction) + +public: + + QmitkCreateMultiLabelSegmentationAction(); + virtual ~QmitkCreateMultiLabelSegmentationAction(); + + //interface methods + virtual void Run( const QList& selectedNodes ); + virtual void SetDataStorage(mitk::DataStorage* dataStorage); + virtual void SetFunctionality(berry::QtViewPart* functionality); + virtual void SetSmoothed(bool smoothed); + virtual void SetDecimated(bool decimated); + +private: + + typedef QList NodeList; + + mitk::DataStorage::Pointer m_DataStorage; +}; + +#endif // QMITK_CreateMultiLabelSegmentation_H diff --git a/Plugins/org.mitk.gui.qt.multilabelsegmentation/src/internal/QmitkCreatePolygonModelAction.cpp b/Plugins/org.mitk.gui.qt.multilabelsegmentation/src/internal/QmitkCreatePolygonModelAction.cpp new file mode 100644 index 0000000000..93f72d0b6c --- /dev/null +++ b/Plugins/org.mitk.gui.qt.multilabelsegmentation/src/internal/QmitkCreatePolygonModelAction.cpp @@ -0,0 +1,173 @@ +/*=================================================================== + +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 "QmitkCreatePolygonModelAction.h" + +// MITK +#include +#include +#include +#include +#include + +#include +#include + +// Blueberry +#include +#include +#include +#include +#include + +using namespace berry; +using namespace mitk; +using namespace std; + +QmitkCreatePolygonModelAction::QmitkCreatePolygonModelAction() +{ +} + +QmitkCreatePolygonModelAction::~QmitkCreatePolygonModelAction() +{ +} + +void QmitkCreatePolygonModelAction::Run(const QList &selectedNodes) +{ + DataNode::Pointer selectedNode = selectedNodes[0]; + Image::Pointer image = dynamic_cast(selectedNode->GetData()); + + if (image.IsNull()) + return; + + try + { + if (!m_IsSmoothed) + { + ShowSegmentationAsSurface::Pointer surfaceFilter = ShowSegmentationAsSurface::New(); + + itk::SimpleMemberCommand::Pointer successCommand = itk::SimpleMemberCommand::New(); + successCommand->SetCallbackFunction(this, &QmitkCreatePolygonModelAction::OnSurfaceCalculationDone); + surfaceFilter->AddObserver(ResultAvailable(), successCommand); + + itk::SimpleMemberCommand::Pointer errorCommand = itk::SimpleMemberCommand::New(); + errorCommand->SetCallbackFunction(this, &QmitkCreatePolygonModelAction::OnSurfaceCalculationDone); + surfaceFilter->AddObserver(ProcessingError(), errorCommand); + + surfaceFilter->SetDataStorage(*m_DataStorage); + surfaceFilter->SetPointerParameter("Input", image); + surfaceFilter->SetPointerParameter("Group node", selectedNode); + surfaceFilter->SetParameter("Show result", true); + surfaceFilter->SetParameter("Sync visibility", false); + surfaceFilter->SetParameter("Smooth", false); + surfaceFilter->SetParameter("Apply median", false); + surfaceFilter->SetParameter("Median kernel size", 3u); + surfaceFilter->SetParameter("Gaussian SD", 1.5f); + surfaceFilter->SetParameter("Decimate mesh", m_IsDecimated); + surfaceFilter->SetParameter("Decimation rate", 0.8f); + + StatusBar::GetInstance()->DisplayText("Surface creation started in background..."); + + surfaceFilter->StartAlgorithm(); + } + else + { + ShowSegmentationAsSmoothedSurface::Pointer surfaceFilter = ShowSegmentationAsSmoothedSurface::New(); + + itk::SimpleMemberCommand::Pointer successCommand = itk::SimpleMemberCommand::New(); + successCommand->SetCallbackFunction(this, &QmitkCreatePolygonModelAction::OnSurfaceCalculationDone); + surfaceFilter->AddObserver(mitk::ResultAvailable(), successCommand); + + itk::SimpleMemberCommand::Pointer errorCommand = itk::SimpleMemberCommand::New(); + errorCommand->SetCallbackFunction(this, &QmitkCreatePolygonModelAction::OnSurfaceCalculationDone); + surfaceFilter->AddObserver(mitk::ProcessingError(), errorCommand); + + surfaceFilter->SetDataStorage(*m_DataStorage); + surfaceFilter->SetPointerParameter("Input", image); + surfaceFilter->SetPointerParameter("Group node", selectedNode); + + berry::IWorkbenchPart::Pointer activePart = + berry::PlatformUI::GetWorkbench()->GetActiveWorkbenchWindow()->GetActivePage()->GetActivePart(); + mitk::IRenderWindowPart* renderPart = dynamic_cast(activePart.GetPointer()); + mitk::SliceNavigationController* timeNavController = 0; + if (renderPart != 0) + { + timeNavController = renderPart->GetRenderingManager()->GetTimeNavigationController(); + } + + int timeNr = timeNavController != 0 ? timeNavController->GetTime()->GetPos() : 0; + surfaceFilter->SetParameter("TimeNr", timeNr); + + IPreferencesService* prefService = berry::Platform::GetPreferencesService(); + IPreferences::Pointer segPref = prefService->GetSystemPreferences()->Node("/org.mitk.views.segmentation"); + + bool smoothingHint = segPref->GetBool("smoothing hint", true); + ScalarType smoothing = segPref->GetDouble("smoothing value", 1.0); + ScalarType decimation = segPref->GetDouble("decimation rate", 0.5); + ScalarType closing = segPref->GetDouble("closing ratio", 0.0); + + if (smoothingHint) + { + smoothing = 0.0; + Vector3D spacing = image->GetGeometry()->GetSpacing(); + + for (Vector3D::Iterator iter = spacing.Begin(); iter != spacing.End(); ++iter) + smoothing = max(smoothing, *iter); + } + + surfaceFilter->SetParameter("Smoothing", smoothing); + surfaceFilter->SetParameter("Decimation", decimation); + surfaceFilter->SetParameter("Closing", closing); + + ProgressBar::GetInstance()->AddStepsToDo(8); + StatusBar::GetInstance()->DisplayText("Smoothed surface creation started in background..."); + + try { + surfaceFilter->StartAlgorithm(); + } catch (...) + { + MITK_ERROR<<"Error creating smoothed polygon model: Not enough memory!"; + } + } + } + catch(...) + { + MITK_ERROR << "Surface creation failed!"; + } +} + +void QmitkCreatePolygonModelAction::OnSurfaceCalculationDone() +{ + StatusBar::GetInstance()->Clear(); +} + +void QmitkCreatePolygonModelAction::SetDataStorage(DataStorage *dataStorage) +{ + m_DataStorage = dataStorage; +} + +void QmitkCreatePolygonModelAction::SetSmoothed(bool smoothed) +{ + m_IsSmoothed = smoothed; +} + +void QmitkCreatePolygonModelAction::SetDecimated(bool decimated) +{ + m_IsDecimated = decimated; +} + +void QmitkCreatePolygonModelAction::SetFunctionality(QtViewPart *) +{ +} diff --git a/Plugins/org.mitk.gui.qt.multilabelsegmentation/src/internal/QmitkCreatePolygonModelAction.h b/Plugins/org.mitk.gui.qt.multilabelsegmentation/src/internal/QmitkCreatePolygonModelAction.h new file mode 100644 index 0000000000..b7adfaaed8 --- /dev/null +++ b/Plugins/org.mitk.gui.qt.multilabelsegmentation/src/internal/QmitkCreatePolygonModelAction.h @@ -0,0 +1,57 @@ +/*=================================================================== + +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 QMITKCREATEPOLYGONMODELACTION_H +#define QMITKCREATEPOLYGONMODELACTION_H + +#include + +// Parent classes +#include +#include + +// Data members +#include + +class QmitkStdMultiWidget; + +class MITK_QT_SEGMENTATION QmitkCreatePolygonModelAction : public QObject, public mitk::IContextMenuAction +{ + Q_OBJECT + Q_INTERFACES(mitk::IContextMenuAction) + +public: + QmitkCreatePolygonModelAction(); + ~QmitkCreatePolygonModelAction(); + + // IContextMenuAction + void Run(const QList &selectedNodes); + void SetDataStorage(mitk::DataStorage *dataStorage); + void SetSmoothed(bool smoothed); + void SetDecimated(bool decimated); + void SetFunctionality(berry::QtViewPart *functionality); + + void OnSurfaceCalculationDone(); + +private: + QmitkCreatePolygonModelAction(const QmitkCreatePolygonModelAction &); + QmitkCreatePolygonModelAction & operator=(const QmitkCreatePolygonModelAction &); + + mitk::DataStorage::Pointer m_DataStorage; + bool m_IsSmoothed; + bool m_IsDecimated; +}; + +#endif diff --git a/Plugins/org.mitk.gui.qt.multilabelsegmentation/src/internal/QmitkLoadMultiLabelPresetAction.cpp b/Plugins/org.mitk.gui.qt.multilabelsegmentation/src/internal/QmitkLoadMultiLabelPresetAction.cpp new file mode 100644 index 0000000000..2a2ee7e338 --- /dev/null +++ b/Plugins/org.mitk.gui.qt.multilabelsegmentation/src/internal/QmitkLoadMultiLabelPresetAction.cpp @@ -0,0 +1,76 @@ +/*=================================================================== + +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 "QmitkLoadMultiLabelPresetAction.h" + +#include "mitkLabelSetImage.h" +#include "mitkLabelSetIOHelper.h" +#include "mitkRenderingManager.h" + +#include "QFileDialog" +#include "QInputDialog" +#include "QMessageBox" + +#include "tinyxml.h" + +QmitkLoadMultiLabelPresetAction::QmitkLoadMultiLabelPresetAction() +{ +} + +QmitkLoadMultiLabelPresetAction::~QmitkLoadMultiLabelPresetAction() +{ +} + +void QmitkLoadMultiLabelPresetAction::Run( const QList &selectedNodes ) +{ + foreach ( mitk::DataNode::Pointer referenceNode, selectedNodes ) + { + + if (referenceNode.IsNull()) return; + + mitk::LabelSetImage::Pointer referenceImage = dynamic_cast( referenceNode->GetData() ); + assert(referenceImage); + + std::string sName = referenceNode->GetName(); + QString qName; + qName.sprintf("%s.lsetp",sName.c_str()); + QString filename = QFileDialog::getOpenFileName(NULL,"Load file",QString(),"LabelSet Preset(*.lsetp)"); + if ( filename.isEmpty() ) + return; + + std::string fileName = filename.toStdString(); + mitk::LabelSetIOHelper::LoadLabelSetImagePreset(fileName, referenceImage); + } +} + +void QmitkLoadMultiLabelPresetAction::SetDataStorage(mitk::DataStorage* dataStorage) +{ + m_DataStorage = dataStorage; +} + +void QmitkLoadMultiLabelPresetAction::SetFunctionality(berry::QtViewPart* /*functionality*/) +{ + //not needed +} + +void QmitkLoadMultiLabelPresetAction::SetSmoothed(bool smoothed) +{ + //not needed +} + +void QmitkLoadMultiLabelPresetAction::SetDecimated(bool decimated) +{ + //not needed +} diff --git a/Plugins/org.mitk.gui.qt.multilabelsegmentation/src/internal/QmitkLoadMultiLabelPresetAction.h b/Plugins/org.mitk.gui.qt.multilabelsegmentation/src/internal/QmitkLoadMultiLabelPresetAction.h new file mode 100644 index 0000000000..eb888d6a6d --- /dev/null +++ b/Plugins/org.mitk.gui.qt.multilabelsegmentation/src/internal/QmitkLoadMultiLabelPresetAction.h @@ -0,0 +1,50 @@ +/*=================================================================== + +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 QMITK_QmitkLoadMultiLabelPresetAction_H +#define QMITK_QmitkLoadMultiLabelPresetAction_H + +#include "mitkIContextMenuAction.h" + +#include "org_mitk_gui_qt_multilabelsegmentation_Export.h" + +#include "vector" +#include "mitkDataNode.h" + +class MITK_QT_SEGMENTATION QmitkLoadMultiLabelPresetAction : public QObject, public mitk::IContextMenuAction +{ + Q_OBJECT + Q_INTERFACES(mitk::IContextMenuAction) + +public: + + QmitkLoadMultiLabelPresetAction(); + virtual ~QmitkLoadMultiLabelPresetAction(); + + //interface methods + virtual void Run( const QList& selectedNodes ); + virtual void SetDataStorage(mitk::DataStorage* dataStorage); + virtual void SetFunctionality(berry::QtViewPart* functionality); + virtual void SetSmoothed(bool smoothed); + virtual void SetDecimated(bool decimated); + +private: + + typedef QList NodeList; + + mitk::DataStorage::Pointer m_DataStorage; +}; + +#endif // QMITK_CreateMultiLabelSegmentation_H diff --git a/Plugins/org.mitk.gui.qt.multilabelsegmentation/src/internal/QmitkMultiLabelSegmentationControls.ui b/Plugins/org.mitk.gui.qt.multilabelsegmentation/src/internal/QmitkMultiLabelSegmentationControls.ui new file mode 100644 index 0000000000..138b48b36a --- /dev/null +++ b/Plugins/org.mitk.gui.qt.multilabelsegmentation/src/internal/QmitkMultiLabelSegmentationControls.ui @@ -0,0 +1,810 @@ + + + QmitkMultiLabelSegmentationControls + + + + 0 + 0 + 191 + 454 + + + + + 0 + 0 + + + + + 0 + 0 + + + + + MS Shell Dlg 2 + 8 + 50 + false + false + false + false + + + + QmitkSegmentation + + + + + + + 0 + 0 + + + + Patient Image + + + + 2 + + + 4 + + + 2 + + + 4 + + + 2 + + + + + + 0 + 0 + + + + + + + + + + + + 0 + 0 + + + + Segmentation + + + + 2 + + + 4 + + + 2 + + + 4 + + + 2 + + + + + + 0 + 0 + + + + + + + + + + + 3 + + + 4 + + + + + Create a new segmentation session + + + ... + + + + :/multilabelsegmentation/NewSegmentationSession_48x48.png:/multilabelsegmentation/NewSegmentationSession_48x48.png + + + + 28 + 28 + + + + N + + + true + + + + + + + Add a new label to the current segmentation session + + + ... + + + + :/multilabelsegmentation/NewLabel_48x48.png:/multilabelsegmentation/NewLabel_48x48.png + + + + 28 + 28 + + + + N + + + true + + + + + + + Qt::Horizontal + + + + 5 + 20 + + + + + + + + + 0 + 34 + + + + Show a table with all labels in the current segmentation session + + + >> + + + + 28 + 28 + + + + true + + + false + + + Qt::NoArrow + + + + + + + + + + 0 + 0 + + + + + 0 + 20 + + + + + + + + 2 + + + QLayout::SetDefaultConstraint + + + + + Disable the active tool + + + ... + + + + :/Qmitk/SegmentationInteractor_48x48.png:/Qmitk/SegmentationInteractor_48x48.png + + + + 28 + 28 + + + + false + + + true + + + + + + + Lock/Unlock exterior + + + ... + + + + :/Qmitk/UnlockExterior_48x48.png + :/Qmitk/LockExterior_48x48.png:/Qmitk/UnlockExterior_48x48.png + + + + 28 + 28 + + + + true + + + true + + + + + + + Add a layer to the current segmentation session + + + ... + + + + :/Qmitk/AddLayer_48x48.png:/Qmitk/AddLayer_48x48.png + + + + 28 + 28 + + + + true + + + + + + + Delete the active layer + + + ... + + + + :/Qmitk/DeleteLayer_48x48.png:/Qmitk/DeleteLayer_48x48.png + + + + 28 + 28 + + + + true + + + + + + + Change to the previous available layer + + + ... + + + + :/Qmitk/PreviousLayer_48x48.png:/Qmitk/PreviousLayer_48x48.png + + + + 28 + 28 + + + + true + + + + + + + + 0 + 0 + + + + + 50 + 30 + + + + + 40 + 30 + + + + + 12 + + + + Switch to a layer + + + + 0 + + + + + + + + Change to the next available layer + + + ... + + + + :/Qmitk/NextLayer_48x48.png:/Qmitk/NextLayer_48x48.png + + + + 28 + 28 + + + + true + + + + + + + Qt::Horizontal + + + + 0 + 20 + + + + + + + + + + + 0 + 0 + + + + QTabWidget::tab-bar { alignment: middle; } + + + 0 + + + true + + + false + + + + 2D Tools + + + + + + + 0 + 0 + + + + + 50 + false + + + + + + + + + 0 + 0 + + + + + 50 + false + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + 3D Tools + + + + + + + 0 + 0 + + + + + 50 + false + + + + + + + + + 0 + 0 + + + + + 50 + false + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + + + + 0 + 0 + + + + Interpolation + + + + 2 + + + QLayout::SetMinimumSize + + + 2 + + + 2 + + + 2 + + + 2 + + + + + + 0 + 0 + + + + + Disabled + + + + + 2D Interpolation + + + + + 3D Interpolation + + + + + + + + + 0 + 0 + + + + 1 + + + + + 0 + 0 + + + + + QLayout::SetMinimumSize + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + 0 + 0 + + + + + 0 + 0 + + + + + 50 + false + + + + + + + + + + 0 + 0 + + + + + QLayout::SetMinimumSize + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + 0 + 0 + + + + + 0 + 50 + + + + + 50 + false + + + + + + + + + + 0 + 0 + + + + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + groupBox_4 + m_LabelSetWidget + groupBox + m_tw2DTools + m_gbInterpolation + verticalSpacer + + + + + QmitkDataStorageComboBox + QComboBox +
QmitkDataStorageComboBox.h
+
+ + QmitkToolSelectionBox + QWidget +
QmitkToolSelectionBox.h
+
+ + QmitkToolGUIArea + QWidget +
QmitkToolGUIArea.h
+
+ + QmitkLabelSetWidget + QWidget +
Qmitk/QmitkLabelSetWidget.h
+ 1 +
+ + QmitkSliceBasedInterpolatorWidget + QWidget +
QmitkSliceBasedInterpolatorWidget.h
+ 1 +
+ + QmitkSurfaceBasedInterpolatorWidget + QWidget +
QmitkSurfaceBasedInterpolatorWidget.h
+ 1 +
+
+ + QmitkToolReferenceDataSelectionBox.h + QmitkToolGUIArea.h + QmitkToolSelectionBox.h + + + + + +
diff --git a/Plugins/org.mitk.gui.qt.multilabelsegmentation/src/internal/QmitkMultiLabelSegmentationOrganNamesHandling.cpp b/Plugins/org.mitk.gui.qt.multilabelsegmentation/src/internal/QmitkMultiLabelSegmentationOrganNamesHandling.cpp new file mode 100644 index 0000000000..574320c919 --- /dev/null +++ b/Plugins/org.mitk.gui.qt.multilabelsegmentation/src/internal/QmitkMultiLabelSegmentationOrganNamesHandling.cpp @@ -0,0 +1,105 @@ +/*=================================================================== + +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 + +namespace mitk +{ +struct OrganNamesHandling +{ + static QStringList GetDefaultOrganColorString() + { + QStringList organColors; + + AppendToOrganList(organColors, "Ankle", 255, 255, 153); + AppendToOrganList(organColors, "Appendix", 161, 107, 54); + AppendToOrganList(organColors, "Blood vessels", 255, 49, 49); + AppendToOrganList(organColors, "Bone", 255, 255, 153); + AppendToOrganList(organColors, "Brain", 255, 156, 202); + AppendToOrganList(organColors, "Bronchial tree", 0, 160, 209); + AppendToOrganList(organColors, "Coccyx", 255, 255, 153); + AppendToOrganList(organColors, "Colon", 161, 107, 54); + AppendToOrganList(organColors, "Cyst", 150, 189, 228); + AppendToOrganList(organColors, "Elbow", 255, 255, 153); + AppendToOrganList(organColors, "Eye", 18, 7, 161); + AppendToOrganList(organColors, "Fallopian tube", 161, 19, 39); + AppendToOrganList(organColors, "Fat", 237, 255, 41); + AppendToOrganList(organColors, "Gall Bladder", 86, 127, 24); + AppendToOrganList(organColors, "Hand", 255, 222, 199); + AppendToOrganList(organColors, "Heart", 153, 0, 0); + AppendToOrganList(organColors, "Hip", 255, 255, 153); + AppendToOrganList(organColors, "Kidney", 250, 89, 0); + AppendToOrganList(organColors, "Knee", 255, 255, 153); + AppendToOrganList(organColors, "Larynx", 102, 0, 0); + AppendToOrganList(organColors, "Liver", 194, 142, 0); + AppendToOrganList(organColors, "Lung", 107, 220, 255); + AppendToOrganList(organColors, "Lymph node", 10, 250, 56); + AppendToOrganList(organColors, "Muscle", 102, 0, 0); + AppendToOrganList(organColors, "Nerve", 255, 234, 79); + AppendToOrganList(organColors, "Nose", 255, 222, 199); + AppendToOrganList(organColors, "Oesophagus", 102, 0, 0); + AppendToOrganList(organColors, "Ovaries", 234, 0, 117); + AppendToOrganList(organColors, "Pancreas", 249, 171, 61); + AppendToOrganList(organColors, "Pelvis", 255, 255, 153); + AppendToOrganList(organColors, "Penis", 255, 222, 199); + AppendToOrganList(organColors, "Pharynx", 102, 0, 0); + AppendToOrganList(organColors, "Prostate", 209, 163, 117); + AppendToOrganList(organColors, "Rectum", 161, 107, 54); + AppendToOrganList(organColors, "Sacrum", 255, 255, 153); + AppendToOrganList(organColors, "Seminal vesicle", 199, 232, 255); + AppendToOrganList(organColors, "Shoulder", 255, 255, 153); + AppendToOrganList(organColors, "Spinal cord", 255, 234, 79); + AppendToOrganList(organColors, "Spleen", 249, 108, 61); + AppendToOrganList(organColors, "Stomach", 161, 107, 54); + AppendToOrganList(organColors, "Teeth", 255, 252, 216); + AppendToOrganList(organColors, "Testicles", 199, 232, 255); + AppendToOrganList(organColors, "Thyroid", 255, 179, 184); + AppendToOrganList(organColors, "Tongue", 102, 0, 0); + AppendToOrganList(organColors, "Tumor", 147, 112, 17); + AppendToOrganList(organColors, "Urethra", 197, 204, 0); + AppendToOrganList(organColors, "Urinary bladder", 197, 204, 0); + AppendToOrganList(organColors, "Uterus", 161, 19, 39); + AppendToOrganList(organColors, "Vagina", 161, 19, 39); + AppendToOrganList(organColors, "Vertebra", 255, 255, 153); + AppendToOrganList(organColors, "Wrist", 255, 255, 153); + return organColors; + } + + static void UpdateOrganList(QStringList& organColors, const QString& organname, mitk::Color color) + { + QString listElement( organname + QColor(color.GetRed() * 255 , color.GetGreen() * 255 , color.GetBlue() * 255).name() ); + + // remove previous definition if necessary + int oldIndex = organColors.indexOf( QRegExp(QRegExp::escape(organname) + "#......", Qt::CaseInsensitive)); + if (oldIndex < 0 || organColors.at(oldIndex) != listElement ) + { + if (oldIndex >= 0) + { + organColors.removeAt( oldIndex ); + } + + // add colored organ name AND sort list + organColors.append( listElement ); + organColors.sort(); + } + } + + static void AppendToOrganList(QStringList& organColors, const QString& organname, int r, int g, int b) + { + organColors.append( organname + QColor(r, g, b).name() ); + } +}; +} diff --git a/Plugins/org.mitk.gui.qt.multilabelsegmentation/src/internal/QmitkMultiLabelSegmentationView.cpp b/Plugins/org.mitk.gui.qt.multilabelsegmentation/src/internal/QmitkMultiLabelSegmentationView.cpp new file mode 100644 index 0000000000..8b2a1df5b8 --- /dev/null +++ b/Plugins/org.mitk.gui.qt.multilabelsegmentation/src/internal/QmitkMultiLabelSegmentationView.cpp @@ -0,0 +1,951 @@ +/*=================================================================== + +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 "QmitkMultiLabelSegmentationView.h" + +// blueberry +#include +#include + +// mitk +#include "mitkLabelSetImage.h" +#include "mitkStatusBar.h" +#include "mitkApplicationCursor.h" +#include "mitkToolManagerProvider.h" +//#include "mitkSegmentationObjectFactory.h" +#include "mitkSegTool2D.h" +#include "mitkPlanePositionManager.h" +#include "mitkPluginActivator.h" +#include "mitkInteractionEventObserver.h" + +// Qmitk +#include "QmitkMultiLabelSegmentationOrganNamesHandling.cpp" +#include "QmitkRenderWindow.h" +#include "QmitkNewSegmentationDialog.h" + +// us +#include +#include +#include +#include +#include + +// Qt +#include +#include +#include +#include + +#include "tinyxml.h" + +#include + +const std::string QmitkMultiLabelSegmentationView::VIEW_ID = "org.mitk.views.multilabelsegmentation"; + +QmitkMultiLabelSegmentationView::QmitkMultiLabelSegmentationView() : + m_Parent(NULL), + m_IRenderWindowPart(NULL), + m_ReferenceNode(NULL), + m_ToolManager(NULL), + m_WorkingNode(NULL), + m_MouseCursorSet(false) +{ + m_SegmentationPredicate = mitk::NodePredicateAnd::New(); + m_SegmentationPredicate->AddPredicate(mitk::TNodePredicateDataType::New()); + m_SegmentationPredicate->AddPredicate(mitk::NodePredicateNot::New(mitk::NodePredicateProperty::New("helper object"))); + m_SegmentationPredicate->AddPredicate(mitk::NodePredicateProperty::New("visible", mitk::BoolProperty::New(true))); + + mitk::TNodePredicateDataType::Pointer isImage = mitk::TNodePredicateDataType::New(); + mitk::NodePredicateProperty::Pointer isBinary = mitk::NodePredicateProperty::New("binary", mitk::BoolProperty::New(true)); + mitk::NodePredicateAnd::Pointer isMask = mitk::NodePredicateAnd::New(isBinary, isImage); + + mitk::NodePredicateDataType::Pointer isDwi = mitk::NodePredicateDataType::New("DiffusionImage"); + mitk::NodePredicateDataType::Pointer isDti = mitk::NodePredicateDataType::New("TensorImage"); + mitk::NodePredicateDataType::Pointer isQbi = mitk::NodePredicateDataType::New("QBallImage"); + + mitk::NodePredicateOr::Pointer validImages = mitk::NodePredicateOr::New(); + validImages->AddPredicate(isImage); + validImages->AddPredicate(isDwi); + validImages->AddPredicate(isDti); + validImages->AddPredicate(isQbi); + + m_ReferencePredicate = mitk::NodePredicateAnd::New(); + m_ReferencePredicate->AddPredicate(validImages); + m_ReferencePredicate->AddPredicate(mitk::NodePredicateNot::New(m_SegmentationPredicate)); + m_ReferencePredicate->AddPredicate(mitk::NodePredicateNot::New(isMask)); + m_ReferencePredicate->AddPredicate(mitk::NodePredicateNot::New(mitk::NodePredicateProperty::New("helper object"))); +} + +QmitkMultiLabelSegmentationView::~QmitkMultiLabelSegmentationView() +{ + //m_ToolManager->ActivateTool(-1); + /* + todo: check this + m_Controls.m_SliceBasedInterpolatorWidget->EnableInterpolation(false); + ctkPluginContext* context = mitk::PluginActivator::getContext(); + ctkServiceReference ppmRef = context->getServiceReference(); + mitk::PlanePositionManagerService* service = context->getService(ppmRef); + service->RemoveAllPlanePositions(); + context->ungetService(ppmRef); +*/ + //m_ToolManager->SetReferenceData(NULL); + //m_ToolManager->SetWorkingData(NULL); + + //m_ServiceRegistration.Unregister(); +} + +void QmitkMultiLabelSegmentationView::CreateQtPartControl(QWidget* parent) +{ + // setup the basic GUI of this view +// m_Parent = parent; + m_Controls.setupUi(parent); + + + // *------------------------ + // * DATA SLECTION WIDGETS + // *------------------------ + + m_Controls.m_cbReferenceNodeSelector->SetAutoSelectNewItems(true); + m_Controls.m_cbReferenceNodeSelector->SetPredicate(m_ReferencePredicate); + m_Controls.m_cbReferenceNodeSelector->SetDataStorage(this->GetDataStorage()); + + m_Controls.m_cbWorkingNodeSelector->SetAutoSelectNewItems(true); + m_Controls.m_cbWorkingNodeSelector->SetPredicate(m_SegmentationPredicate); + m_Controls.m_cbWorkingNodeSelector->SetDataStorage(this->GetDataStorage()); + + connect( m_Controls.m_cbReferenceNodeSelector, SIGNAL( OnSelectionChanged( const mitk::DataNode* ) ), + this, SLOT( OnReferenceSelectionChanged( const mitk::DataNode* ) ) ); + + connect( m_Controls.m_cbWorkingNodeSelector, SIGNAL( OnSelectionChanged( const mitk::DataNode* ) ), + this, SLOT( OnSegmentationSelectionChanged( const mitk::DataNode* ) ) ); + + // this->OnReferenceSelectionChanged( m_Controls.m_cbReferenceNodeSelector->GetSelectedNode() ); + + // *------------------------ + // * ToolManager + // *------------------------ + + m_ToolManager = mitk::ToolManagerProvider::GetInstance()->GetToolManager(); + assert(m_ToolManager); + m_ToolManager->SetDataStorage( *(this->GetDataStorage()) ); + m_ToolManager->InitializeTools(); + //use the same ToolManager instance for our 3D Tools + m_Controls.m_ManualToolSelectionBox3D->SetToolManager(*m_ToolManager); + + // *------------------------ + // * LabelSetWidget + // *------------------------ + + m_Controls.m_LabelSetWidget->SetDataStorage(this->GetDataStorage()); + m_Controls.m_LabelSetWidget->SetOrganColors(mitk::OrganNamesHandling::GetDefaultOrganColorString()); + m_Controls.m_LabelSetWidget->hide(); + + // *------------------------ + // * Interpolation + // *------------------------ + + m_Controls.m_SurfaceBasedInterpolatorWidget->SetDataStorage( *(this->GetDataStorage()) ); + m_Controls.m_SliceBasedInterpolatorWidget->SetDataStorage( *(this->GetDataStorage()) ); + connect( m_Controls.m_cbInterpolation, SIGNAL( activated (int) ), this, SLOT( OnInterpolationSelectionChanged(int) ) ); + + m_Controls.m_cbInterpolation->setCurrentIndex(0); + m_Controls.m_swInterpolation->hide(); + + // *------------------------ + // * ToolSelection 2D + // *------------------------ + + m_Controls.m_ManualToolSelectionBox2D->SetGenerateAccelerators(true); + m_Controls.m_ManualToolSelectionBox2D->SetToolGUIArea( m_Controls.m_ManualToolGUIContainer2D ); + m_Controls.m_ManualToolSelectionBox2D->SetDisplayedToolGroups("Add Subtract Fill Erase Paint Wipe 'Region Growing' FastMarching2D Correction 'Live Wire'");// todo: "Correction 'Live Wire'" + m_Controls.m_ManualToolSelectionBox2D->SetEnabledMode( QmitkToolSelectionBox::EnabledWithReferenceAndWorkingDataVisible ); + connect( m_Controls.m_ManualToolSelectionBox2D, SIGNAL(ToolSelected(int)), this, SLOT(OnManualTool2DSelected(int)) ); + + + // *------------------------ + // * ToolSelection 3D + // *------------------------ + + m_Controls.m_ManualToolSelectionBox3D->SetGenerateAccelerators(true); + m_Controls.m_ManualToolSelectionBox3D->SetToolGUIArea( m_Controls.m_ManualToolGUIContainer3D ); + m_Controls.m_ManualToolSelectionBox3D->SetDisplayedToolGroups("Threshold 'Two Thresholds' 'Auto Threshold' 'Multiple Otsu'"); // todo add : FastMarching3D RegionGrowing Watershed + m_Controls.m_ManualToolSelectionBox3D->SetLayoutColumns(2); + m_Controls.m_ManualToolSelectionBox3D->SetEnabledMode( QmitkToolSelectionBox::EnabledWithReferenceAndWorkingDataVisible ); + + // *------------------------* + // * Connect PushButtons (pb) + // *------------------------* + + connect( m_Controls.m_pbNewLabel, SIGNAL(clicked()), this, SLOT( OnNewLabel()) ); + connect( m_Controls.m_pbNewSegmentationSession, SIGNAL(clicked()), this, SLOT( OnNewSegmentationSession()) ); + connect( m_Controls.m_pbShowLabelTable, SIGNAL(toggled(bool)), this, SLOT( OnShowLabelTable(bool)) ); + + // *------------------------* + // * Connect LabelSetWidget + // *------------------------* + + connect(m_Controls.m_LabelSetWidget, SIGNAL(goToLabel(const mitk::Point3D&)), this, SLOT(OnGoToLabel(const mitk::Point3D&)) ); + connect(m_Controls.m_LabelSetWidget, SIGNAL(resetView()), this, SLOT(OnResetView()) ); + + + // *------------------------* + // * DATA SLECTION WIDGET + // *------------------------* + m_IRenderWindowPart = this->GetRenderWindowPart(); + if (m_IRenderWindowPart) + { + QList controllers; + controllers.push_back(m_IRenderWindowPart->GetQmitkRenderWindow("axial")->GetSliceNavigationController()); + controllers.push_back(m_IRenderWindowPart->GetQmitkRenderWindow("sagittal")->GetSliceNavigationController()); + controllers.push_back(m_IRenderWindowPart->GetQmitkRenderWindow("coronal")->GetSliceNavigationController()); + m_Controls.m_SliceBasedInterpolatorWidget->SetSliceNavigationControllers(controllers); + // m_Controls.m_LabelSetWidget->SetRenderWindowPart(this->m_IRenderWindowPart); + } + +// this->InitializeListeners(); + + connect( m_Controls.m_btAddLayer, SIGNAL(clicked()), this, SLOT( OnAddLayer()) ); + connect( m_Controls.m_btDeleteLayer, SIGNAL(clicked()), this, SLOT( OnDeleteLayer()) ); + connect( m_Controls.m_btPreviousLayer, SIGNAL(clicked()), this, SLOT( OnPreviousLayer()) ); + connect( m_Controls.m_btNextLayer, SIGNAL(clicked()), this, SLOT( OnNextLayer()) ); + connect( m_Controls.m_btLockExterior, SIGNAL(toggled(bool)), this, SLOT( OnLockExteriorToggled(bool)) ); + connect( m_Controls.m_btDeactivateTool, SIGNAL(clicked()), this, SLOT( OnDeactivateActiveTool()) ); + connect( m_Controls.m_cbActiveLayer, SIGNAL(currentIndexChanged(int)), this, SLOT( OnChangeLayer(int)) ); + + m_Controls.m_btAddLayer->hide(); + m_Controls.m_btDeactivateTool->hide(); + m_Controls.m_btDeleteLayer->hide(); + m_Controls.m_btLockExterior->hide(); + m_Controls.m_btNextLayer->hide(); + m_Controls.m_btPreviousLayer->hide(); + m_Controls.m_cbActiveLayer->hide(); +} + +void QmitkMultiLabelSegmentationView::Activated() +{ + m_ToolManager->SetReferenceData(m_Controls.m_cbReferenceNodeSelector->GetSelectedNode()); + m_ToolManager->SetWorkingData(m_Controls.m_cbWorkingNodeSelector->GetSelectedNode()); +} + +void QmitkMultiLabelSegmentationView::Deactivated() +{ + // Not yet implemented +} + +void QmitkMultiLabelSegmentationView::Visible() +{ + // Not yet implemented +} + +void QmitkMultiLabelSegmentationView::Hidden() +{ + // Not yet implemented +} +void QmitkMultiLabelSegmentationView::InitializeListeners() +{ + if (m_Interactor.IsNull()) + { + us::Module* module = us::GetModuleContext()->GetModule(); + std::vector resources = module->FindResources("/", "*", true); + for (std::vector::iterator iter = resources.begin(); iter != resources.end(); ++iter) + { + MITK_INFO << iter->GetResourcePath(); + } + + m_Interactor = mitk::SegmentationInteractor::New(); + if (!m_Interactor->LoadStateMachine("SegmentationInteraction.xml", module)) + { + MITK_WARN << "Error loading state machine"; + } + + if (!m_Interactor->SetEventConfig ("ConfigSegmentation.xml", module)) + { + MITK_WARN << "Error loading state machine configuration"; + } + + // Register as listener via micro services + us::ServiceProperties props; + props["name"] = std::string("SegmentationInteraction"); + m_ServiceRegistration = us::GetModuleContext()->RegisterService(m_Interactor.GetPointer(), props); + } +} + +void QmitkMultiLabelSegmentationView::SetFocus () +{ +} + +bool QmitkMultiLabelSegmentationView::CheckForSameGeometry(const mitk::Image *image1, const mitk::Image *image2) const +{ + bool isSameGeometry(true); + + if (image1 && image2) + { + mitk::BaseGeometry::Pointer geo1 = image1->GetGeometry(); + mitk::BaseGeometry::Pointer geo2 = image2->GetGeometry(); + + isSameGeometry = isSameGeometry && mitk::Equal(geo1->GetOrigin(), geo2->GetOrigin()); + isSameGeometry = isSameGeometry && mitk::Equal(geo1->GetExtent(0), geo2->GetExtent(0)); + isSameGeometry = isSameGeometry && mitk::Equal(geo1->GetExtent(1), geo2->GetExtent(1)); + isSameGeometry = isSameGeometry && mitk::Equal(geo1->GetExtent(2), geo2->GetExtent(2)); + isSameGeometry = isSameGeometry && mitk::Equal(geo1->GetSpacing(), geo2->GetSpacing()); + isSameGeometry = isSameGeometry && mitk::MatrixEqualElementWise(geo1->GetIndexToWorldTransform()->GetMatrix(), geo2->GetIndexToWorldTransform()->GetMatrix()); + + return isSameGeometry; + } + else + { + return false; + } +} + +void QmitkMultiLabelSegmentationView::RenderWindowPartActivated(mitk::IRenderWindowPart* renderWindowPart) +{ + if (m_IRenderWindowPart != renderWindowPart) + { + m_IRenderWindowPart = renderWindowPart; + m_Parent->setEnabled(true); + + QList controllers; + controllers.push_back(renderWindowPart->GetQmitkRenderWindow("axial")->GetSliceNavigationController()); + controllers.push_back(renderWindowPart->GetQmitkRenderWindow("sagittal")->GetSliceNavigationController()); + controllers.push_back(renderWindowPart->GetQmitkRenderWindow("coronal")->GetSliceNavigationController()); + m_Controls.m_SliceBasedInterpolatorWidget->SetSliceNavigationControllers(controllers); + } +} + +void QmitkMultiLabelSegmentationView::RenderWindowPartDeactivated(mitk::IRenderWindowPart* /*renderWindowPart*/) +{ + m_ToolManager->ActivateTool(-1); + m_IRenderWindowPart = 0; + m_Parent->setEnabled(false); +} + +int QmitkMultiLabelSegmentationView::GetSizeFlags(bool width) +{ + if(!width) + { + return berry::Constants::MIN | berry::Constants::MAX | berry::Constants::FILL; + } + else + { + return 0; + } +} + +int QmitkMultiLabelSegmentationView::ComputePreferredSize(bool width, int /*availableParallel*/, int /*availablePerpendicular*/, int preferredResult) +{ + if(width==false) + { + return 100; + } + else + { + return preferredResult; + } +} + +void QmitkMultiLabelSegmentationView::UpdateControls() +{ + mitk::DataNode* referenceNode = m_ToolManager->GetReferenceData(0); + bool hasReferenceNode = referenceNode != NULL; + + mitk::DataNode* workingNode = m_ToolManager->GetWorkingData(0); + bool hasValidWorkingNode = workingNode != NULL; + + m_Controls.m_pbNewLabel->setEnabled(false); + m_Controls.m_gbInterpolation->setEnabled(false); + m_Controls.m_SliceBasedInterpolatorWidget->setEnabled(false); + m_Controls.m_SurfaceBasedInterpolatorWidget->setEnabled(false); + m_Controls.m_btDeactivateTool->setEnabled(false); + m_Controls.m_LabelSetWidget->setEnabled(false); + m_Controls.m_btAddLayer->setEnabled(false); + m_Controls.m_btDeleteLayer->setEnabled(false); + m_Controls.m_cbActiveLayer->setEnabled(false); + m_Controls.m_btPreviousLayer->setEnabled(false); + m_Controls.m_btNextLayer->setEnabled(false); + m_Controls.m_btLockExterior->setChecked(false); + m_Controls.m_pbShowLabelTable->setChecked(false); + + m_Controls.m_ManualToolSelectionBox3D->SetEnabledMode(QmitkToolSelectionBox::EnabledWithReferenceAndWorkingDataVisible); + m_Controls.m_ManualToolSelectionBox2D->SetEnabledMode(QmitkToolSelectionBox::EnabledWithReferenceAndWorkingDataVisible); + + if(hasValidWorkingNode) + { + // TODO adapt tool manager so that this check is done there, e.g. convenience function + mitk::LabelSetImage* workingImage = dynamic_cast(workingNode->GetData()); + hasValidWorkingNode = workingImage != nullptr; + if (hasValidWorkingNode) + { + m_Controls.m_pbNewLabel->setEnabled(true); + m_Controls.m_gbInterpolation->setEnabled(true); + m_Controls.m_SliceBasedInterpolatorWidget->setEnabled(true); + m_Controls.m_SurfaceBasedInterpolatorWidget->setEnabled(true); + m_Controls.m_btDeactivateTool->setEnabled(true); + m_Controls.m_LabelSetWidget->setEnabled(true); + m_Controls.m_btAddLayer->setEnabled(true); + + + int activeLayer = workingImage->GetActiveLayer(); + int numberOfLayers = workingImage->GetNumberOfLayers(); + + m_Controls.m_cbActiveLayer->blockSignals(true); + m_Controls.m_cbActiveLayer->clear(); + for (unsigned int lidx=0; lidxGetNumberOfLayers(); ++lidx) + m_Controls.m_cbActiveLayer->addItem(QString::number(lidx)); + m_Controls.m_cbActiveLayer->setCurrentIndex(activeLayer); + m_Controls.m_cbActiveLayer->blockSignals(false); + + m_Controls.m_btDeleteLayer->setEnabled(numberOfLayers>1); + m_Controls.m_cbActiveLayer->setEnabled(numberOfLayers>1); + m_Controls.m_btPreviousLayer->setEnabled(activeLayer>0); + m_Controls.m_btNextLayer->setEnabled(activeLayer!=numberOfLayers-1); + m_Controls.m_btLockExterior->setChecked(workingImage->GetLabel(0)->GetLocked()); + + m_Controls.m_pbShowLabelTable->setChecked(workingImage->GetNumberOfLabels() > 1 /*1st is exterior*/); + + //MLI TODO + //m_Controls.m_ManualToolSelectionBox2D->SetEnabledMode(QmitkToolSelectionBox::EnabledWithWorkingDataVisible); + } + } + + if(hasValidWorkingNode && hasReferenceNode) + { + int layer = -1; + referenceNode->GetIntProperty("layer", layer); + workingNode->SetIntProperty("layer", layer+1); + } + + this->RequestRenderWindowUpdate(mitk::RenderingManager::REQUEST_UPDATE_ALL); +} + +void QmitkMultiLabelSegmentationView::OnNewSegmentationSession() +{ + mitk::DataNode* referenceNode = m_Controls.m_cbReferenceNodeSelector->GetSelectedNode(); + + if (!referenceNode) + { + QMessageBox::information( m_Parent, "New Segmentation Session", "Please load and select a patient image before starting some action."); + return; + } + + m_ToolManager->ActivateTool(-1); + + mitk::Image* referenceImage = dynamic_cast( referenceNode->GetData() ); + assert(referenceImage); + + QString newName = QString::fromStdString(referenceNode->GetName()); + newName.append("-labels"); + + bool ok = false; + newName = QInputDialog::getText(m_Parent, "New Segmentation Session", "New name:", QLineEdit::Normal, newName, &ok); + + if(!ok) return; + + this->WaitCursorOn(); + + mitk::LabelSetImage::Pointer workingImage = mitk::LabelSetImage::New(); + + try + { + workingImage->Initialize(referenceImage); + } + catch ( mitk::Exception& e ) + { + this->WaitCursorOff(); + MITK_ERROR << "Exception caught: " << e.GetDescription(); + QMessageBox::information(m_Parent, "New Segmentation Session", "Could not create a new segmentation session.\n"); + return; + } + + this->WaitCursorOff(); + + mitk::DataNode::Pointer workingNode = mitk::DataNode::New(); + workingNode->SetData(workingImage); + workingNode->SetName(newName.toStdString()); + workingImage->GetExteriorLabel()->SetProperty("name.parent",mitk::StringProperty::New(referenceNode->GetName().c_str())); + workingImage->GetExteriorLabel()->SetProperty("name.image",mitk::StringProperty::New(newName.toStdString().c_str())); + + if (!this->GetDataStorage()->Exists(workingNode)) + this->GetDataStorage()->Add(workingNode, referenceNode); + + m_Controls.m_LabelSetWidget->ResetAllTableWidgetItems(); + + OnNewLabel(); + + m_Controls.m_LabelSetWidget->ResetAllTableWidgetItems(); +} + +void QmitkMultiLabelSegmentationView::OnNewLabel() +{ + m_ToolManager->ActivateTool(-1); + + mitk::DataNode* workingNode = m_ToolManager->GetWorkingData(0); + if (!workingNode) + { + QMessageBox::information(m_Parent, "New Segmentation Session", "Please load and select a patient image before starting some action."); + return; + } + + mitk::LabelSetImage* workingImage = dynamic_cast(workingNode->GetData()); + if (!workingImage) + { + QMessageBox::information(m_Parent, "New Segmentation Session", "Please load and select a patient image before starting some action."); + return; + } + + QmitkNewSegmentationDialog* dialog = new QmitkNewSegmentationDialog( m_Parent ); + dialog->SetSuggestionList( mitk::OrganNamesHandling::GetDefaultOrganColorString() ); + dialog->setWindowTitle("New Label"); + + int dialogReturnValue = dialog->exec(); + + if ( dialogReturnValue == QDialog::Rejected ) return; + + QString segName = dialog->GetSegmentationName(); + if(segName.isEmpty()) segName = "Unnamed"; + workingImage->GetActiveLabelSet()->AddLabel(segName.toStdString(), dialog->GetColor()); + + UpdateControls(); +} + +void QmitkMultiLabelSegmentationView::OnShowLabelTable(bool value) +{ + if (value) + { + m_Controls.m_LabelSetWidget->show(); + m_Controls.m_btAddLayer->show(); + m_Controls.m_btDeactivateTool->show(); + m_Controls.m_btDeleteLayer->show(); + m_Controls.m_btLockExterior->show(); + m_Controls.m_btNextLayer->show(); + m_Controls.m_btPreviousLayer->show(); + m_Controls.m_cbActiveLayer->show(); + } + else + { + m_Controls.m_LabelSetWidget->hide(); + m_Controls.m_btAddLayer->hide(); + m_Controls.m_btDeactivateTool->hide(); + m_Controls.m_btDeleteLayer->hide(); + m_Controls.m_btLockExterior->hide(); + m_Controls.m_btNextLayer->hide(); + m_Controls.m_btPreviousLayer->hide(); + m_Controls.m_cbActiveLayer->hide(); + } +} + +void QmitkMultiLabelSegmentationView::OnNextLayer() +{ + m_ToolManager->ActivateTool(-1); + + mitk::DataNode* workingNode = m_ToolManager->GetWorkingData(0); + assert(workingNode); + mitk::LabelSetImage* workingImage = dynamic_cast(workingNode->GetData()); + assert(workingImage); + + OnChangeLayer(workingImage->GetActiveLayer() + 1 ); +} + +void QmitkMultiLabelSegmentationView::OnPreviousLayer() +{ + m_ToolManager->ActivateTool(-1); + + mitk::DataNode* workingNode = m_ToolManager->GetWorkingData(0); + assert(workingNode); + mitk::LabelSetImage* workingImage = dynamic_cast(workingNode->GetData()); + assert(workingImage); + + OnChangeLayer(workingImage->GetActiveLayer() - 1 ); +} + + +void QmitkMultiLabelSegmentationView::OnChangeLayer(int layer) +{ + m_ToolManager->ActivateTool(-1); + + mitk::DataNode* workingNode = m_ToolManager->GetWorkingData(0); + assert(workingNode); + + mitk::LabelSetImage* workingImage = dynamic_cast(workingNode->GetData()); + assert(workingImage); + + this->WaitCursorOn(); + workingImage->SetActiveLayer( layer ); + this->WaitCursorOff(); + + UpdateControls(); + m_Controls.m_LabelSetWidget->ResetAllTableWidgetItems(); +} + +void QmitkMultiLabelSegmentationView::OnDeleteLayer() +{ + m_ToolManager->ActivateTool(-1); + + mitk::DataNode* workingNode = m_ToolManager->GetWorkingData(0); + assert(workingNode); + + mitk::LabelSetImage* workingImage = dynamic_cast(workingNode->GetData()); + assert(workingImage); + + if (workingImage->GetNumberOfLayers() < 2) + return; + + QString question = "Do you really want to delete the current layer?"; + + QMessageBox::StandardButton answerButton = QMessageBox::question( m_Controls.m_LabelSetWidget, "Delete layer", + question, QMessageBox::Yes | QMessageBox::Cancel, QMessageBox::Yes); + + if (answerButton != QMessageBox::Yes) return; + + try + { + this->WaitCursorOn(); + workingImage->RemoveLayer(); + this->WaitCursorOff(); + } + catch ( mitk::Exception& e ) + { + this->WaitCursorOff(); + MITK_ERROR << "Exception caught: " << e.GetDescription(); + QMessageBox::information(m_Controls.m_LabelSetWidget, "Delete Layer", "Could not delete the currently active layer. See error log for details.\n"); + return; + } + + UpdateControls(); + m_Controls.m_LabelSetWidget->ResetAllTableWidgetItems(); +} + +void QmitkMultiLabelSegmentationView::OnAddLayer() +{ + m_ToolManager->ActivateTool(-1); + + mitk::DataNode* workingNode = m_ToolManager->GetWorkingData(0); + assert(workingNode); + + mitk::LabelSetImage* workingImage = dynamic_cast(workingNode->GetData()); + assert(workingImage); + + QString question = "Do you really want to add a layer to the current segmentation session?"; + QMessageBox::StandardButton answerButton = QMessageBox::question( m_Controls.m_LabelSetWidget, "Add layer", + question, QMessageBox::Yes | QMessageBox::Cancel, QMessageBox::Yes); + + if (answerButton != QMessageBox::Yes) return; + + int newLabelSetId = -1; + try + { + WaitCursorOn(); + newLabelSetId = workingImage->AddLayer(); + WaitCursorOff(); + } + catch ( mitk::Exception& e ) + { + WaitCursorOff(); + MITK_ERROR << "Exception caught: " << e.GetDescription(); + QMessageBox::information(m_Controls.m_LabelSetWidget, "Add Layer", "Could not add a new layer. See error log for details.\n"); + return; + } + + // Update controls and label set list for direct response + m_Controls.m_LabelSetWidget->ResetAllTableWidgetItems(); + OnNewLabel(); + UpdateControls(); +} + +void QmitkMultiLabelSegmentationView::OnDeactivateActiveTool() +{ + m_ToolManager->ActivateTool(-1); +} + +void QmitkMultiLabelSegmentationView::OnLockExteriorToggled(bool checked) +{ + mitk::DataNode* workingNode = m_ToolManager->GetWorkingData(0); + assert(workingNode); + + mitk::LabelSetImage* workingImage = dynamic_cast(workingNode->GetData()); + assert(workingImage); + + workingImage->GetLabel(0)->SetLocked(checked); +} + +void QmitkMultiLabelSegmentationView::NodeAdded(const mitk::DataNode*) +{ + /* + bool isHelperObject(false); + node->GetBoolProperty("helper object", isHelperObject); + if (isHelperObject) return; + + if (m_ReferenceNode.IsNotNull() && dynamic_cast(node->GetData())) + { + mitk::LabelSetImage* workingImage = dynamic_cast(node->GetData()); + + if (workingImage->GetNumberOfLabels() > 2) + m_Controls.m_LabelSetWidget->show(); + else + m_Controls.m_LabelSetWidget->hide(); + } + */ +} + +void QmitkMultiLabelSegmentationView::NodeRemoved(const mitk::DataNode* node) +{ + bool isHelperObject(false); + node->GetBoolProperty("helper object", isHelperObject); + if (isHelperObject) return; + + if (m_ReferenceNode.IsNotNull() && dynamic_cast(node->GetData())) + { + // remove all possible contour markers of the segmentation + mitk::DataStorage::SetOfObjects::ConstPointer allContourMarkers = + this->GetDataStorage()->GetDerivations(node, mitk::NodePredicateProperty::New("isContourMarker", mitk::BoolProperty::New(true))); + + ctkPluginContext* context = mitk::PluginActivator::getContext(); + ctkServiceReference ppmRef = context->getServiceReference(); + mitk::PlanePositionManagerService* service = context->getService(ppmRef); + + for (mitk::DataStorage::SetOfObjects::ConstIterator it = allContourMarkers->Begin(); it != allContourMarkers->End(); ++it) + { + std::string nodeName = node->GetName(); + unsigned int t = nodeName.find_last_of(" "); + unsigned int id = atof(nodeName.substr(t+1).c_str())-1; + + service->RemovePlanePosition(id); + + this->GetDataStorage()->Remove(it->Value()); + } + + context->ungetService(ppmRef); + service = NULL; + } +} + +void QmitkMultiLabelSegmentationView::OnInterpolationSelectionChanged(int index) +{ + if (index == 1) + { + m_Controls.m_SurfaceBasedInterpolatorWidget->m_Controls.m_btStart->setChecked(false);//OnToggleWidgetActivation(false); + m_Controls.m_swInterpolation->setCurrentIndex(0); + m_Controls.m_swInterpolation->show(); + } + else if (index == 2) + { + m_Controls.m_SliceBasedInterpolatorWidget->m_Controls.m_btStart->setChecked(false); + m_Controls.m_swInterpolation->setCurrentIndex(1); + m_Controls.m_swInterpolation->show(); + } + else + { + m_Controls.m_SurfaceBasedInterpolatorWidget->m_Controls.m_btStart->setChecked(false); + m_Controls.m_SliceBasedInterpolatorWidget->m_Controls.m_btStart->setChecked(false); + m_Controls.m_swInterpolation->setCurrentIndex(2); + m_Controls.m_swInterpolation->hide(); + } +} + +void QmitkMultiLabelSegmentationView::OnReferenceSelectionChanged( const mitk::DataNode* node ) +{ + m_ToolManager->ActivateTool(-1); + + m_ReferenceNode = const_cast(node); + + m_ToolManager->SetReferenceData(m_ReferenceNode); + + //check match of segmentation and reference image geometries + if (node && m_WorkingNode.IsNotNull()) + { + mitk::Image* workingImage = dynamic_cast(m_WorkingNode->GetData()); + assert(workingImage); + + mitk::Image* refImage = dynamic_cast(node->GetData()); + assert(refImage); + + if (!this->CheckForSameGeometry(refImage, workingImage)) + return; + } + + this->UpdateControls(); + //m_Controls.m_LabelSetWidget->ResetAllTableWidgetItems(); +} + +void QmitkMultiLabelSegmentationView::OnEstablishLabelSetConnection() +{ + MITK_INFO << "Connection Established"; + mitk::LabelSetImage* workingImage = dynamic_cast(m_WorkingNode->GetData()); + assert(workingImage); + + workingImage->GetActiveLabelSet()->AddLabelEvent + += mitk::MessageDelegate(m_Controls.m_LabelSetWidget,&QmitkLabelSetWidget::ResetAllTableWidgetItems); + workingImage->GetActiveLabelSet()->RemoveLabelEvent + += mitk::MessageDelegate(m_Controls.m_LabelSetWidget,&QmitkLabelSetWidget::ResetAllTableWidgetItems); + workingImage->GetActiveLabelSet()->ModifyLabelEvent + += mitk::MessageDelegate(m_Controls.m_LabelSetWidget,&QmitkLabelSetWidget::UpdateAllTableWidgetItems); + workingImage->GetActiveLabelSet()->AllLabelsModifiedEvent + += mitk::MessageDelegate(m_Controls.m_LabelSetWidget,&QmitkLabelSetWidget::UpdateAllTableWidgetItems); + workingImage->GetActiveLabelSet()->ActiveLabelEvent + += mitk::MessageDelegate1(m_Controls.m_LabelSetWidget,&QmitkLabelSetWidget::SelectLabelByPixelValue); +} + + +void QmitkMultiLabelSegmentationView::OnLooseLabelSetConnection() +{ + MITK_INFO << "Connection Lost"; + mitk::LabelSetImage* workingImage = dynamic_cast(m_WorkingNode->GetData()); + assert(workingImage); + + // Reset LabelSetWidget Events + workingImage->GetActiveLabelSet()->AddLabelEvent + -= mitk::MessageDelegate(m_Controls.m_LabelSetWidget,&QmitkLabelSetWidget::ResetAllTableWidgetItems); + workingImage->GetActiveLabelSet()->RemoveLabelEvent + -= mitk::MessageDelegate(m_Controls.m_LabelSetWidget,&QmitkLabelSetWidget::ResetAllTableWidgetItems); + workingImage->GetActiveLabelSet()->ModifyLabelEvent + -= mitk::MessageDelegate(m_Controls.m_LabelSetWidget,&QmitkLabelSetWidget::UpdateAllTableWidgetItems); + workingImage->GetActiveLabelSet()->AllLabelsModifiedEvent + -= mitk::MessageDelegate(m_Controls.m_LabelSetWidget,&QmitkLabelSetWidget::UpdateAllTableWidgetItems); + workingImage->GetActiveLabelSet()->ActiveLabelEvent + -= mitk::MessageDelegate1(m_Controls.m_LabelSetWidget,&QmitkLabelSetWidget::SelectLabelByPixelValue); +} + +void QmitkMultiLabelSegmentationView::OnSegmentationSelectionChanged(const mitk::DataNode *node) +{ + m_ToolManager->ActivateTool(-1); + + if(m_WorkingNode.IsNotNull()) + { + mitk::LabelSetImage* workingImage = dynamic_cast(m_WorkingNode->GetData()); + assert(workingImage); + + //Loose LabelSetConnections + OnLooseLabelSetConnection(); + + workingImage->BeforeChangeLayerEvent + -= mitk::MessageDelegate(this,&QmitkMultiLabelSegmentationView::OnLooseLabelSetConnection); + workingImage->AfterchangeLayerEvent + -= mitk::MessageDelegate(this,&QmitkMultiLabelSegmentationView::OnEstablishLabelSetConnection); + } + + m_WorkingNode = const_cast(node); + + if(m_WorkingNode.IsNotNull()) + { + mitk::LabelSetImage* workingImage = dynamic_cast(m_WorkingNode->GetData()); + assert(workingImage); + + //Establish LabelSetConnection + OnEstablishLabelSetConnection(); + + workingImage->BeforeChangeLayerEvent + += mitk::MessageDelegate(this,&QmitkMultiLabelSegmentationView::OnLooseLabelSetConnection); + workingImage->AfterchangeLayerEvent + += mitk::MessageDelegate(this,&QmitkMultiLabelSegmentationView::OnEstablishLabelSetConnection); + } + + m_ToolManager->SetWorkingData(m_WorkingNode); + + //check match of segmentation and reference image geometries + if (node && m_ReferenceNode.IsNotNull()) + { + mitk::Image* refImage = dynamic_cast(m_ReferenceNode->GetData()); + assert(refImage); + + mitk::Image* workingImage = dynamic_cast(node->GetData()); + assert(workingImage); + + if (!this->CheckForSameGeometry(refImage, workingImage)) + return; + } + + if (m_WorkingNode.IsNotNull()) + { + mitk::DataStorage::SetOfObjects::ConstPointer segNodes = this->GetDataStorage()->GetSubset(m_SegmentationPredicate); + for(mitk::DataStorage::SetOfObjects::const_iterator iter = segNodes->begin(); iter != segNodes->end(); ++iter) + { + mitk::DataNode* _segNode = *iter; + _segNode->SetVisibility(false); + } + + m_WorkingNode->SetVisibility(true); + } + + this->UpdateControls(); + + if (m_WorkingNode.IsNotNull()) + { + m_Controls.m_LabelSetWidget->ResetAllTableWidgetItems(); + } +} + +void QmitkMultiLabelSegmentationView::OnManualTool2DSelected(int id) +{ + this->ResetMouseCursor(); + mitk::StatusBar::GetInstance()->DisplayText(""); + + if (id >= 0) + { + std::string text = "Active Tool: \""; + text += m_ToolManager->GetToolById(id)->GetName(); + text += "\""; + mitk::StatusBar::GetInstance()->DisplayText(text.c_str()); + + us::ModuleResource resource = m_ToolManager->GetToolById(id)->GetCursorIconResource(); + if (resource.IsValid()) + this->SetMouseCursor(resource, 0, 0); + } +} + +void QmitkMultiLabelSegmentationView::ResetMouseCursor() +{ + if ( m_MouseCursorSet ) + { + mitk::ApplicationCursor::GetInstance()->PopCursor(); + m_MouseCursorSet = false; + } +} + +void QmitkMultiLabelSegmentationView::SetMouseCursor( const us::ModuleResource resource, int hotspotX, int hotspotY ) +{ + // Remove previously set mouse cursor + if ( m_MouseCursorSet ) + { + mitk::ApplicationCursor::GetInstance()->PopCursor(); + } + + us::ModuleResourceStream cursor(resource, std::ios::binary); + mitk::ApplicationCursor::GetInstance()->PushCursor( cursor, hotspotX, hotspotY ); + m_MouseCursorSet = true; +} + +void QmitkMultiLabelSegmentationView::OnGoToLabel(const mitk::Point3D& pos) +{ + if (m_IRenderWindowPart) + m_IRenderWindowPart->SetSelectedPosition(pos); +} + +void QmitkMultiLabelSegmentationView::OnResetView() +{ + if (m_IRenderWindowPart) + m_IRenderWindowPart->ForceImmediateUpdate(); +} + +QString QmitkMultiLabelSegmentationView::GetLastFileOpenPath() +{ + return this->GetPreferences()->Get("LastFileOpenPath", ""); +} + +void QmitkMultiLabelSegmentationView::SetLastFileOpenPath(const QString& path) +{ + this->GetPreferences()->Put("LastFileOpenPath", path); + this->GetPreferences()->Flush(); +} diff --git a/Plugins/org.mitk.gui.qt.multilabelsegmentation/src/internal/QmitkMultiLabelSegmentationView.h b/Plugins/org.mitk.gui.qt.multilabelsegmentation/src/internal/QmitkMultiLabelSegmentationView.h new file mode 100644 index 0000000000..7c653b61d5 --- /dev/null +++ b/Plugins/org.mitk.gui.qt.multilabelsegmentation/src/internal/QmitkMultiLabelSegmentationView.h @@ -0,0 +1,174 @@ +/*=================================================================== + +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 QmitkMultiLabelSegmentationView_h +#define QmitkMultiLabelSegmentationView_h + +#include + +#include +#include "mitkSegmentationInteractor.h" + +#include "ui_QmitkMultiLabelSegmentationControls.h" + + +class QmitkRenderWindow; + +/** + * \ingroup ToolManagerEtAl + * \ingroup org_mitk_gui_qt_multilabelsegmentation_internal + */ +class QmitkMultiLabelSegmentationView : public QmitkAbstractView, public mitk::ILifecycleAwarePart +{ + Q_OBJECT + +public: + + static const std::string VIEW_ID; + + QmitkMultiLabelSegmentationView(); + virtual ~QmitkMultiLabelSegmentationView(); + + typedef std::map NodeTagMapType; + + // GUI setup + void CreateQtPartControl(QWidget* parent); + + // ILifecycleAwarePart interface +public: + void Activated(); + void Deactivated(); + void Visible(); + void Hidden(); + + virtual int GetSizeFlags(bool width); + virtual int ComputePreferredSize(bool width, int /*availableParallel*/, int /*availablePerpendicular*/, int preferredResult); + +protected slots: + + /// \brief reaction to the selection of a new patient (reference) image in the DataStorage combobox + void OnReferenceSelectionChanged(const mitk::DataNode* node); + + /// \brief reaction to the selection of a new Segmentation (working) image in the DataStorage combobox + void OnSegmentationSelectionChanged(const mitk::DataNode *node); + + /// \brief reaction to ... + void OnInterpolationSelectionChanged(int); + + /// \brief reaction to the selection of any 2D segmentation tool + void OnManualTool2DSelected(int id); + + /// \brief reaction to button "New Label" + void OnNewLabel(); + + /// \brief reaction to button "Show Label Table" + void OnShowLabelTable(bool value); + + /// \brief reaction to button "New Segmentation Session" + void OnNewSegmentationSession(); + + /// \brief reaction to signal "goToLabel" from labelset widget + void OnGoToLabel(const mitk::Point3D& pos); + + void OnResetView(); + + // reaction to the button "Add Layer" + void OnAddLayer(); + + // reaction to the button "Delete Layer" + void OnDeleteLayer(); + + // reaction to the button "Previous Layer" + void OnPreviousLayer(); + + // reaction to the button "Next Layer" + void OnNextLayer(); + + // reaction to the combobox change "Change Layer" + void OnChangeLayer(int); + + // reaction to the button "Deactive Active Tool" + void OnDeactivateActiveTool(); + + // reaction to the button "Lock exterior" + void OnLockExteriorToggled(bool); + +protected: + + void OnEstablishLabelSetConnection(); + void OnLooseLabelSetConnection(); + + void SetFocus(); + + void UpdateControls(); + + void RenderWindowPartActivated(mitk::IRenderWindowPart *renderWindowPart); + + void RenderWindowPartDeactivated(mitk::IRenderWindowPart *renderWindowPart); + + void ResetMouseCursor(); + + void SetMouseCursor(const us::ModuleResource, int hotspotX, int hotspotY ); + + void InitializeListeners(); + + /// \brief Checks if two images have the same size and geometry + bool CheckForSameGeometry(const mitk::Image *image1, const mitk::Image *image2) const; + + /// \brief Reimplemented from QmitkAbstractView + virtual void NodeAdded(const mitk::DataNode* node); + + /// \brief Reimplemented from QmitkAbstractView + virtual void NodeRemoved(const mitk::DataNode* node); + + QString GetLastFileOpenPath(); + + void SetLastFileOpenPath(const QString& path); + + // handling of a list of known (organ name, organ color) combination + // ATTENTION these methods are defined in QmitkSegmentationOrganNamesHandling.cpp +// QStringList GetDefaultOrganColorString(); +// void UpdateOrganList(QStringList& organColors, const QString& organname, mitk::Color colorname); +// void AppendToOrganList(QStringList& organColors, const QString& organname, int r, int g, int b); + + /// \brief the Qt parent of our GUI (NOT of this object) + QWidget* m_Parent; + + /// \brief Qt GUI file + Ui::QmitkMultiLabelSegmentationControls m_Controls; + + mitk::IRenderWindowPart* m_IRenderWindowPart; + + mitk::ToolManager* m_ToolManager; + + mitk::DataNode::Pointer m_ReferenceNode; + mitk::DataNode::Pointer m_WorkingNode; + + mitk::NodePredicateAnd::Pointer m_ReferencePredicate; + mitk::NodePredicateAnd::Pointer m_SegmentationPredicate; + + bool m_MouseCursorSet; + + mitk::SegmentationInteractor::Pointer m_Interactor; + + /** + * Reference to the service registration of the observer, + * it is needed to unregister the observer on unload. + */ + us::ServiceRegistration m_ServiceRegistration; +}; + +#endif // QmitkMultiLabelSegmentationView_h diff --git a/Plugins/org.mitk.gui.qt.multilabelsegmentation/src/internal/QmitkThresholdAction.cpp b/Plugins/org.mitk.gui.qt.multilabelsegmentation/src/internal/QmitkThresholdAction.cpp new file mode 100644 index 0000000000..7b0547a5e1 --- /dev/null +++ b/Plugins/org.mitk.gui.qt.multilabelsegmentation/src/internal/QmitkThresholdAction.cpp @@ -0,0 +1,117 @@ +/*=================================================================== + +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 "QmitkThresholdAction.h" + +// MITK +#include +#include +#include + +// Qt +#include +#include +#include +#include + +using namespace berry; +using namespace mitk; +using namespace std; + +QmitkThresholdAction::QmitkThresholdAction() +{ +} + +QmitkThresholdAction::~QmitkThresholdAction() +{ +} + +void QmitkThresholdAction::Run(const QList &selectedNodes) +{ + m_ThresholdingToolManager = ToolManager::New(m_DataStorage); + + m_ThresholdingToolManager->RegisterClient(); + + Tool *binaryThresholdTool = m_ThresholdingToolManager->GetToolById(m_ThresholdingToolManager->GetToolIdByToolType()); + if (binaryThresholdTool != NULL) + { + QmitkBinaryThresholdToolGUI *gui = dynamic_cast(binaryThresholdTool->GetGUI("Qmitk", "GUI").GetPointer()); + if (gui != NULL) + { + QDialog thresholdingDialog(QApplication::activeWindow(), Qt::Window | Qt::WindowStaysOnTopHint); + + thresholdingDialog.setWindowFlags(thresholdingDialog.windowFlags() & ~Qt::WindowMinimizeButtonHint); + + QDialogButtonBox* buttonBox = new QDialogButtonBox(QDialogButtonBox::Cancel); + connect(buttonBox, SIGNAL(rejected()), &thresholdingDialog, SLOT(reject())); + connect(gui, SIGNAL(thresholdAccepted()), &thresholdingDialog, SLOT(reject())); + + QVBoxLayout *layout = new QVBoxLayout; + layout->setContentsMargins(3, 3, 3, 3); + + gui->SetTool(binaryThresholdTool); + gui->setParent(&thresholdingDialog); + + layout->addWidget(gui); + layout->addWidget(buttonBox); + + thresholdingDialog.setLayout(layout); + thresholdingDialog.setMinimumWidth(350); + + m_SelectedNode = selectedNodes[0]; + m_ThresholdingToolManager->SetReferenceData(selectedNodes[0]); + m_ThresholdingToolManager->ActivateTool(m_ThresholdingToolManager->GetToolIdByToolType()); + + m_ThresholdingToolManager->ActiveToolChanged += mitk::MessageDelegate(this, &QmitkThresholdAction::OnThresholdingToolManagerToolModified); + thresholdingDialog.exec(); + m_ThresholdingToolManager->ActiveToolChanged -= mitk::MessageDelegate(this, &QmitkThresholdAction::OnThresholdingToolManagerToolModified); + + m_ThresholdingToolManager->SetReferenceData(NULL); + m_ThresholdingToolManager->ActivateTool(-1); + m_SelectedNode = 0; + } + } + + m_ThresholdingToolManager->UnregisterClient(); +} + +void QmitkThresholdAction::OnThresholdingToolManagerToolModified() +{ + if (m_ThresholdingToolManager.IsNotNull()) + { + if (m_ThresholdingToolManager->GetActiveToolID() < 0) + { + m_ThresholdingToolManager->SetReferenceData(m_SelectedNode); + m_ThresholdingToolManager->ActivateTool(m_ThresholdingToolManager->GetToolIdByToolType()); + } + } +} + +void QmitkThresholdAction::SetDataStorage(DataStorage *dataStorage) +{ + m_DataStorage = dataStorage; +} + +void QmitkThresholdAction::SetSmoothed(bool) +{ +} + +void QmitkThresholdAction::SetDecimated(bool) +{ +} + +void QmitkThresholdAction::SetFunctionality(QtViewPart* /*functionality*/) +{ +} diff --git a/Plugins/org.mitk.gui.qt.multilabelsegmentation/src/internal/QmitkThresholdAction.h b/Plugins/org.mitk.gui.qt.multilabelsegmentation/src/internal/QmitkThresholdAction.h new file mode 100644 index 0000000000..53f3169acd --- /dev/null +++ b/Plugins/org.mitk.gui.qt.multilabelsegmentation/src/internal/QmitkThresholdAction.h @@ -0,0 +1,57 @@ +/*=================================================================== + +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 QMITKTHRESHOLDACTION_H +#define QMITKTHRESHOLDACTION_H + +#include + +// Parent classes +#include +#include + +// Data members +#include +#include + + +class MITK_QT_SEGMENTATION QmitkThresholdAction : public QObject, public mitk::IContextMenuAction +{ + Q_OBJECT + Q_INTERFACES(mitk::IContextMenuAction) + +public: + QmitkThresholdAction(); + ~QmitkThresholdAction(); + + // IContextMenuAction + void Run(const QList &selectedNodes); + void SetDataStorage(mitk::DataStorage *dataStorage); + void SetSmoothed(bool smoothed); + void SetDecimated(bool decimated); + void SetFunctionality(berry::QtViewPart *functionality); + + void OnThresholdingToolManagerToolModified(); + +private: + QmitkThresholdAction(const QmitkThresholdAction &); + QmitkThresholdAction & operator=(const QmitkThresholdAction &); + + mitk::DataNode::Pointer m_SelectedNode; + mitk::DataStorage::Pointer m_DataStorage; + mitk::ToolManager::Pointer m_ThresholdingToolManager; +}; + +#endif diff --git a/Plugins/org.mitk.gui.qt.multilabelsegmentation/src/internal/SegmentationUtilities/BooleanOperations/QmitkBooleanOperationsWidget.cpp b/Plugins/org.mitk.gui.qt.multilabelsegmentation/src/internal/SegmentationUtilities/BooleanOperations/QmitkBooleanOperationsWidget.cpp new file mode 100644 index 0000000000..ce40d43b5c --- /dev/null +++ b/Plugins/org.mitk.gui.qt.multilabelsegmentation/src/internal/SegmentationUtilities/BooleanOperations/QmitkBooleanOperationsWidget.cpp @@ -0,0 +1,144 @@ +/*=================================================================== + +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 "QmitkBooleanOperationsWidget.h" +#include "../../Common/QmitkDataSelectionWidget.h" +#include +#include +#include + +static const char* const HelpText = "Select two different masks above"; + +std::string GetPrefix(mitk::BooleanOperation::Type type) +{ + switch (type) + { + case mitk::BooleanOperation::Difference: + return "DifferenceFrom_"; + + case mitk::BooleanOperation::Intersection: + return "IntersectionWith_"; + + case mitk::BooleanOperation::Union: + return "UnionWith_"; + + default: + assert(false && "Unknown boolean operation type"); + return "UNKNOWN_BOOLEAN_OPERATION_WITH_"; + } +} + +void AddToDataStorage(mitk::DataStorage::Pointer dataStorage, mitk::Image::Pointer segmentation, const std::string& name, mitk::DataNode::Pointer parent = NULL) +{ + mitk::DataNode::Pointer dataNode = mitk::DataNode::New(); + + dataNode->SetBoolProperty("binary", true); + dataNode->SetName(name); + dataNode->SetData(segmentation); + + dataStorage->Add(dataNode, parent); +} + +QmitkBooleanOperationsWidget::QmitkBooleanOperationsWidget(mitk::SliceNavigationController* timeNavigationController, QWidget* parent) + : QmitkSegmentationUtilityWidget(timeNavigationController, parent) +{ + m_Controls.setupUi(this); + + m_Controls.dataSelectionWidget->AddDataStorageComboBox(QmitkDataSelectionWidget::MaskPredicate); + m_Controls.dataSelectionWidget->AddDataStorageComboBox(QmitkDataSelectionWidget::MaskPredicate); + + m_Controls.dataSelectionWidget->SetHelpText(HelpText); + + connect(m_Controls.dataSelectionWidget, SIGNAL(SelectionChanged(unsigned int, const mitk::DataNode*)), this, SLOT(OnSelectionChanged(unsigned int, const mitk::DataNode*))); + connect(m_Controls.differenceButton, SIGNAL(clicked()), this, SLOT(OnDifferenceButtonClicked())); + connect(m_Controls.intersectionButton, SIGNAL(clicked()), this, SLOT(OnIntersectionButtonClicked())); + connect(m_Controls.unionButton, SIGNAL(clicked()), this, SLOT(OnUnionButtonClicked())); +} + +QmitkBooleanOperationsWidget::~QmitkBooleanOperationsWidget() +{ +} + +void QmitkBooleanOperationsWidget::OnSelectionChanged(unsigned int index, const mitk::DataNode* selection) +{ + QmitkDataSelectionWidget* dataSelectionWidget = m_Controls.dataSelectionWidget; + + mitk::DataNode::Pointer node0 = dataSelectionWidget->GetSelection(0); + mitk::DataNode::Pointer node1 = dataSelectionWidget->GetSelection(1); + + if (node0.IsNotNull() && node1.IsNotNull() && node0 != node1) + { + dataSelectionWidget->SetHelpText(""); + this->EnableButtons(); + } + else + { + dataSelectionWidget->SetHelpText(HelpText); + this->EnableButtons(false); + } +} + +void QmitkBooleanOperationsWidget::EnableButtons(bool enable) +{ + m_Controls.differenceButton->setEnabled(enable); + m_Controls.intersectionButton->setEnabled(enable); + m_Controls.unionButton->setEnabled(enable); +} + +void QmitkBooleanOperationsWidget::OnDifferenceButtonClicked() +{ + this->DoBooleanOperation(mitk::BooleanOperation::Difference); +} + +void QmitkBooleanOperationsWidget::OnIntersectionButtonClicked() +{ + this->DoBooleanOperation(mitk::BooleanOperation::Intersection); +} + +void QmitkBooleanOperationsWidget::OnUnionButtonClicked() +{ + this->DoBooleanOperation(mitk::BooleanOperation::Union); +} + +void QmitkBooleanOperationsWidget::DoBooleanOperation(mitk::BooleanOperation::Type type) +{ + mitk::SliceNavigationController* timeNavigationController = this->GetTimeNavigationController(); + assert(timeNavigationController != NULL); + + mitk::Image::Pointer segmentation0 = static_cast(m_Controls.dataSelectionWidget->GetSelection(0)->GetData()); + mitk::Image::Pointer segmentation1 = static_cast(m_Controls.dataSelectionWidget->GetSelection(1)->GetData()); + mitk::Image::Pointer result; + + try + { + mitk::BooleanOperation booleanOperation(type, segmentation0, segmentation1, timeNavigationController->GetTime()->GetPos()); + result = booleanOperation.GetResult(); + } + catch (const mitk::Exception &exception) + { + MITK_ERROR << "Boolean operation failed: " << exception.GetDescription(); + } + + assert(result.IsNotNull()); + + QmitkDataSelectionWidget* dataSelectionWidget = m_Controls.dataSelectionWidget; + + AddToDataStorage( + dataSelectionWidget->GetDataStorage(), + result, + GetPrefix(type) + dataSelectionWidget->GetSelection(1)->GetName(), + dataSelectionWidget->GetSelection(0)); +} diff --git a/Plugins/org.mitk.gui.qt.multilabelsegmentation/src/internal/SegmentationUtilities/BooleanOperations/QmitkBooleanOperationsWidget.h b/Plugins/org.mitk.gui.qt.multilabelsegmentation/src/internal/SegmentationUtilities/BooleanOperations/QmitkBooleanOperationsWidget.h new file mode 100644 index 0000000000..321a6b28cf --- /dev/null +++ b/Plugins/org.mitk.gui.qt.multilabelsegmentation/src/internal/SegmentationUtilities/BooleanOperations/QmitkBooleanOperationsWidget.h @@ -0,0 +1,45 @@ +/*=================================================================== + +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 QmitkBooleanOperationsWidget_h +#define QmitkBooleanOperationsWidget_h + +#include "../QmitkSegmentationUtilityWidget.h" +#include +#include + +class QmitkBooleanOperationsWidget : public QmitkSegmentationUtilityWidget +{ + Q_OBJECT + +public: + explicit QmitkBooleanOperationsWidget(mitk::SliceNavigationController* timeNavigationController, QWidget* parent = NULL); + ~QmitkBooleanOperationsWidget(); + +private slots: + void OnSelectionChanged(unsigned int index, const mitk::DataNode* selection); + void OnDifferenceButtonClicked(); + void OnIntersectionButtonClicked(); + void OnUnionButtonClicked(); + +private: + void EnableButtons(bool enable = true); + void DoBooleanOperation(mitk::BooleanOperation::Type type); + + Ui::QmitkBooleanOperationsWidgetControls m_Controls; +}; + +#endif diff --git a/Plugins/org.mitk.gui.qt.multilabelsegmentation/src/internal/SegmentationUtilities/BooleanOperations/QmitkBooleanOperationsWidgetControls.ui b/Plugins/org.mitk.gui.qt.multilabelsegmentation/src/internal/SegmentationUtilities/BooleanOperations/QmitkBooleanOperationsWidgetControls.ui new file mode 100644 index 0000000000..bbd900d8b9 --- /dev/null +++ b/Plugins/org.mitk.gui.qt.multilabelsegmentation/src/internal/SegmentationUtilities/BooleanOperations/QmitkBooleanOperationsWidgetControls.ui @@ -0,0 +1,161 @@ + + + QmitkBooleanOperationsWidgetControls + + + + 0 + 0 + 251 + 300 + + + + + + + + + + + + false + + + + 0 + 0 + + + + Difference + + + + :/MultiLabelSegmentationUtilities/BooleanOperations/BooleanDifference_48x48.png:/MultiLabelSegmentationUtilities/BooleanOperations/BooleanDifference_48x48.png + + + + 24 + 24 + + + + false + + + false + + + Qt::ToolButtonTextUnderIcon + + + Qt::NoArrow + + + + + + + false + + + + 0 + 0 + + + + Intersection + + + + :/MultiLabelSegmentationUtilities/BooleanOperations/BooleanIntersection_48x48.png:/MultiLabelSegmentationUtilities/BooleanOperations/BooleanIntersection_48x48.png + + + + 24 + 24 + + + + false + + + false + + + Qt::ToolButtonTextUnderIcon + + + Qt::NoArrow + + + + + + + false + + + + 0 + 0 + + + + Union + + + + :/MultiLabelSegmentationUtilities/BooleanOperations/BooleanUnion_48x48.png:/MultiLabelSegmentationUtilities/BooleanOperations/BooleanUnion_48x48.png + + + + 24 + 24 + + + + false + + + false + + + Qt::ToolButtonTextUnderIcon + + + Qt::NoArrow + + + + + + + + + Qt::Vertical + + + + 20 + 273 + + + + + + + + + QmitkDataSelectionWidget + QWidget +
internal/Common/QmitkDataSelectionWidget.h
+
+
+ + + + +
diff --git a/Plugins/org.mitk.gui.qt.multilabelsegmentation/src/internal/SegmentationUtilities/ImageMasking/QmitkImageMaskingWidget.cpp b/Plugins/org.mitk.gui.qt.multilabelsegmentation/src/internal/SegmentationUtilities/ImageMasking/QmitkImageMaskingWidget.cpp new file mode 100644 index 0000000000..6c0d655fc9 --- /dev/null +++ b/Plugins/org.mitk.gui.qt.multilabelsegmentation/src/internal/SegmentationUtilities/ImageMasking/QmitkImageMaskingWidget.cpp @@ -0,0 +1,342 @@ +/*=================================================================== + +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 "QmitkImageMaskingWidget.h" +#include "../../Common/QmitkDataSelectionWidget.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +static const char* const HelpText = "Select a patient image and a mask"; + +QmitkImageMaskingWidget::QmitkImageMaskingWidget(mitk::SliceNavigationController* timeNavigationController, QWidget* parent) + : QmitkSegmentationUtilityWidget(timeNavigationController, parent) +{ + m_Controls.setupUi(this); + + m_Controls.dataSelectionWidget->AddDataStorageComboBox(QmitkDataSelectionWidget::ImagePredicate); + m_Controls.dataSelectionWidget->AddDataStorageComboBox(QmitkDataSelectionWidget::MaskPredicate); + m_Controls.dataSelectionWidget->SetHelpText(HelpText); + +// mitk::IDataStorageService::Pointer service = +// berry::Platform::GetServiceRegistry().GetServiceById(mitk::IDataStorageService::ID); + +// assert(service.IsNotNull()); + + mitk::DataStorage::Pointer ds = m_Controls.dataSelectionWidget->GetDataStorage(); + m_Controls.m_LabelSetWidget->SetDataStorage(ds); + m_Controls.m_LabelSetWidget->setEnabled(true); + + m_Controls.m_MaskStampWidget->SetDataStorage(ds); + + this->EnableButtons(false); + + connect (m_Controls.rbMaskImage, SIGNAL(toggled(bool)), this, SLOT(OnImageMaskingToggled(bool))); + connect (m_Controls.rbMaskSurface, SIGNAL(toggled(bool)), this, SLOT(OnSurfaceMaskingToggled(bool))); + connect (m_Controls.btnMaskImage, SIGNAL(pressed()), this, SLOT(OnMaskImagePressed())); + + connect(m_Controls.dataSelectionWidget, SIGNAL(SelectionChanged(unsigned int, const mitk::DataNode*)), + this, SLOT(OnSelectionChanged(unsigned int, const mitk::DataNode*))); + + if( m_Controls.dataSelectionWidget->GetSelection(0).IsNotNull() && + m_Controls.dataSelectionWidget->GetSelection(1).IsNotNull() ) + { + this->OnSelectionChanged( 0, m_Controls.dataSelectionWidget->GetSelection(0)); + } +} + +QmitkImageMaskingWidget::~QmitkImageMaskingWidget() +{ +} + +void QmitkImageMaskingWidget::OnSelectionChanged(unsigned int index, const mitk::DataNode* selection) +{ + QmitkDataSelectionWidget* dataSelectionWidget = m_Controls.dataSelectionWidget; + mitk::DataNode::Pointer node0 = dataSelectionWidget->GetSelection(0); + mitk::DataNode::Pointer node1 = dataSelectionWidget->GetSelection(1); + + if (node0.IsNull() || node1.IsNull() ) + { + if( m_Controls.rbMaskImage->isChecked() ) + { + dataSelectionWidget->SetHelpText(HelpText); + } + else + { + dataSelectionWidget->SetHelpText("Select a patient image and a surface"); + } + this->EnableButtons(false); + } + else + { + this->SelectionControl(index, selection); + } +} + +void QmitkImageMaskingWidget::SelectionControl(unsigned int index, const mitk::DataNode* selection) +{ + QmitkDataSelectionWidget* dataSelectionWidget = m_Controls.dataSelectionWidget; + mitk::DataNode::Pointer node = dataSelectionWidget->GetSelection(index); + + //if Image-Masking is enabled, check if image-dimension of reference and binary image is identical + if( m_Controls.rbMaskImage->isChecked() ) + { + if( dataSelectionWidget->GetSelection(0) == dataSelectionWidget->GetSelection(1) ) + { + dataSelectionWidget->SetHelpText("Select two different images above"); + this->EnableButtons(false); + return; + } + /* + else if( node.IsNotNull() && selection ) + { + mitk::Image::Pointer referenceImage = dynamic_cast ( dataSelectionWidget->GetSelection(0)->GetData() ); + mitk::Image::Pointer maskImage = dynamic_cast ( dataSelectionWidget->GetSelection(1)->GetData() ); + + if( referenceImage->GetLargestPossibleRegion().GetSize() != maskImage->GetLargestPossibleRegion().GetSize() ) + { + dataSelectionWidget->SetHelpText("Different image sizes cannot be masked"); + this->EnableButtons(false); + return; + } + } + + else + { + dataSelectionWidget->SetHelpText(HelpText); + return; + } + */ + } + + dataSelectionWidget->SetHelpText(""); + this->EnableButtons(); +} + +void QmitkImageMaskingWidget::EnableButtons(bool enable) +{ + m_Controls.btnMaskImage->setEnabled(enable); +} + +void QmitkImageMaskingWidget::OnImageMaskingToggled(bool status) +{ + if (status) + { + m_Controls.dataSelectionWidget->SetHelpText("Select a patient image and a mask"); + m_Controls.dataSelectionWidget->SetPredicate(1, QmitkDataSelectionWidget::MaskPredicate); + } +} + +void QmitkImageMaskingWidget::OnSurfaceMaskingToggled(bool status) +{ + if (status) + { + m_Controls.dataSelectionWidget->SetHelpText("Select a patient image and a surface"); + m_Controls.dataSelectionWidget->SetPredicate(1, QmitkDataSelectionWidget::SurfacePredicate); + } +} + + +void QmitkImageMaskingWidget::OnMaskImagePressed() +{ + //Disable Buttons during calculation and initialize Progressbar + this->EnableButtons(false); + mitk::ProgressBar::GetInstance()->AddStepsToDo(4); + mitk::ProgressBar::GetInstance()->Progress(); + + QmitkDataSelectionWidget* dataSelectionWidget = m_Controls.dataSelectionWidget; + + //create result image, get mask node and reference image + mitk::Image::Pointer resultImage(0); + mitk::DataNode::Pointer maskingNode = dataSelectionWidget->GetSelection(1); + mitk::Image::Pointer referenceImage = static_cast(dataSelectionWidget->GetSelection(0)->GetData()); + + if(referenceImage.IsNull() || maskingNode.IsNull() ) + { + MITK_ERROR << "Selection does not contain an image"; + QMessageBox::information( this, "Image and Surface Masking", "Selection does not contain an image", QMessageBox::Ok ); + m_Controls.btnMaskImage->setEnabled(true); + return; + } + + //Do Image-Masking + if (m_Controls.rbMaskImage->isChecked()) + { + mitk::ProgressBar::GetInstance()->Progress(); + + mitk::Image::Pointer maskImage = dynamic_cast ( maskingNode->GetData() ); + + if( referenceImage->GetLargestPossibleRegion().GetSize() != maskImage->GetLargestPossibleRegion().GetSize() ) + { + mitk::PadImageFilter::Pointer padImageFilter = mitk::PadImageFilter::New(); + padImageFilter->SetInput(0, maskImage); + padImageFilter->SetInput(1, referenceImage); + padImageFilter->SetPadConstant(0); + padImageFilter->SetBinaryFilter(false); + padImageFilter->SetLowerThreshold(0); + padImageFilter->SetUpperThreshold(1); + + MITK_INFO << "Padding mask ..."; + + padImageFilter->Update(); + + maskImage = padImageFilter->GetOutput(); + } + + if(maskImage.IsNull() ) + { + MITK_ERROR << "Selection does not contain a binary image"; + QMessageBox::information( this, "Image and Surface Masking", "Selection does not contain a binary image", QMessageBox::Ok ); + this->EnableButtons(); + return; + } + + resultImage = this->MaskImage( referenceImage, maskImage ); + } + + //Do Surface-Masking + else + { + mitk::ProgressBar::GetInstance()->Progress(); + + //1. convert surface to image + mitk::Surface::Pointer surface = dynamic_cast ( maskingNode->GetData() ); + + //TODO Get 3D Surface of current time step + + if(surface.IsNull()) + { + MITK_ERROR << "Selection does not contain a surface"; + QMessageBox::information( this, "Image and Surface Masking", "Selection does not contain a surface", QMessageBox::Ok ); + this->EnableButtons(); + return; + } + + mitk::Image::Pointer maskImage = this->ConvertSurfaceToImage( referenceImage, surface ); + + //2. mask reference image with mask image + if(maskImage.IsNotNull() && + referenceImage->GetLargestPossibleRegion().GetSize() == maskImage->GetLargestPossibleRegion().GetSize() ) + { + resultImage = this->MaskImage( referenceImage, maskImage ); + } + } + + mitk::ProgressBar::GetInstance()->Progress(); + + if( resultImage.IsNull() ) + { + MITK_ERROR << "Masking failed"; + QMessageBox::information( this, "Image and Surface Masking", "Masking failed. For more information please see logging window.", QMessageBox::Ok ); + this->EnableButtons(); + mitk::ProgressBar::GetInstance()->Progress(4); + return; + } + + //Add result to data storage + this->AddToDataStorage( + dataSelectionWidget->GetDataStorage(), + resultImage, + dataSelectionWidget->GetSelection(0)->GetName() + "_" + dataSelectionWidget->GetSelection(1)->GetName(), + dataSelectionWidget->GetSelection(0)); + + this->EnableButtons(); + + mitk::ProgressBar::GetInstance()->Progress(); +} + +mitk::Image::Pointer QmitkImageMaskingWidget::MaskImage(mitk::Image::Pointer referenceImage, mitk::Image::Pointer maskImage ) +{ + mitk::Image::Pointer resultImage(0); + + mitk::MaskImageFilter::Pointer maskFilter = mitk::MaskImageFilter::New(); + maskFilter->SetInput( referenceImage ); + maskFilter->SetMask( maskImage ); + //MLI TODO + //if ( m_Controls.m_chkMakeOutputBinary->isChecked() ) + //{ + // maskFilter->SetInsideValue(1.0); + // maskFilter->SetOutsideValue(0.0); + // maskFilter->SetOverrideOutsideValue(true); + // maskFilter->SetOverrideInsideValue(true); + //} + + //maskFilter->SetOverrideOutsideValue( m_Controls.m_chkOverwriteBackground->isChecked() ); + //maskFilter->SetOverrideInsideValue( m_Controls.m_chkOverwriteForeground->isChecked() ); + //maskFilter->SetInsideValue( m_Controls.m_leForegroundValue->text().toFloat() );//referenceImage->GetStatistics()->GetScalarValueMin() ); + //maskFilter->SetOutsideValue( m_Controls.m_leBackgroundValue->text().toFloat() );//referenceImage->GetStatistics()->GetScalarValueMin() ); + + try + { + maskFilter->Update(); + } + catch(itk::ExceptionObject& excpt) + { + MITK_ERROR << excpt.GetDescription(); + return 0; + } + + resultImage = maskFilter->GetOutput(); + + return resultImage; +} + +mitk::Image::Pointer QmitkImageMaskingWidget::ConvertSurfaceToImage( mitk::Image::Pointer image, mitk::Surface::Pointer surface ) +{ + mitk::ProgressBar::GetInstance()->AddStepsToDo(2); + mitk::ProgressBar::GetInstance()->Progress(); + + mitk::SurfaceToImageFilter::Pointer surfaceToImageFilter = mitk::SurfaceToImageFilter::New(); + surfaceToImageFilter->MakeOutputBinaryOn(); + surfaceToImageFilter->SetInput(surface); + surfaceToImageFilter->SetImage(image); + try + { + surfaceToImageFilter->Update(); + } + catch(itk::ExceptionObject& excpt) + { + MITK_ERROR << excpt.GetDescription(); + return 0; + } + + mitk::ProgressBar::GetInstance()->Progress(); + mitk::Image::Pointer resultImage = mitk::Image::New(); + resultImage = surfaceToImageFilter->GetOutput(); + + return resultImage; +} + +void QmitkImageMaskingWidget::AddToDataStorage(mitk::DataStorage::Pointer dataStorage, mitk::Image::Pointer segmentation, const std::string& name, mitk::DataNode::Pointer parent ) +{ + mitk::DataNode::Pointer dataNode = mitk::DataNode::New(); + + dataNode->SetName(name); + dataNode->SetData(segmentation); + + dataStorage->Add(dataNode, parent); +} diff --git a/Plugins/org.mitk.gui.qt.multilabelsegmentation/src/internal/SegmentationUtilities/ImageMasking/QmitkImageMaskingWidget.h b/Plugins/org.mitk.gui.qt.multilabelsegmentation/src/internal/SegmentationUtilities/ImageMasking/QmitkImageMaskingWidget.h new file mode 100644 index 0000000000..9dde336331 --- /dev/null +++ b/Plugins/org.mitk.gui.qt.multilabelsegmentation/src/internal/SegmentationUtilities/ImageMasking/QmitkImageMaskingWidget.h @@ -0,0 +1,81 @@ +/*=================================================================== + +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 QmitkImageMaskingWidget_h +#define QmitkImageMaskingWidget_h + +#include "../QmitkSegmentationUtilityWidget.h" +#include + +#include +#include + +/*! + \brief QmitkImageMaskingWidget + + Tool masks an image with a binary image or a surface. The Method requires + an image and a binary image mask or a surface. The input image and the binary + image mask must be of the same size. Masking with a surface creates first a + binary image of the surface and then use this for the masking of the input image. +*/ +class QmitkImageMaskingWidget : public QmitkSegmentationUtilityWidget +{ + Q_OBJECT + +public: + + /** @brief Default constructor, including creation of GUI elements and signals/slots connections. */ + explicit QmitkImageMaskingWidget(mitk::SliceNavigationController* timeNavigationController, QWidget* parent = NULL); + + /** @brief Defaul destructor. */ + ~QmitkImageMaskingWidget(); + +private slots: + + /** @brief This slot is called if the selection in the workbench is changed. */ + void OnSelectionChanged(unsigned int index, const mitk::DataNode* selection); + + /** @brief This slot is called if user activates the radio button for masking an image with a binary image mask. */ + void OnImageMaskingToggled(bool); + + /** @brief This slot is called if user activates the radio button for masking an image with a surface. */ + void OnSurfaceMaskingToggled(bool); + + /** @brief This slot is called if user activates the button to mask an image. */ + void OnMaskImagePressed(); + +private: + + /** @brief Check if selections is valid. */ + void SelectionControl( unsigned int index, const mitk::DataNode* selection); + + /** @brief Enable buttons if data selction is valid. */ + void EnableButtons(bool enable = true); + + /** @brief Mask an image with a given binary mask. Note that the input image and the mask image must be of the same size. */ + mitk::Image::Pointer MaskImage(mitk::Image::Pointer referenceImage, mitk::Image::Pointer maskImage ); + + /** @brief Convert a surface into an binary image. */ + mitk::Image::Pointer ConvertSurfaceToImage( mitk::Image::Pointer image, mitk::Surface::Pointer surface ); + + /** @brief Adds a new data object to the DataStorage.*/ + void AddToDataStorage(mitk::DataStorage::Pointer dataStorage, mitk::Image::Pointer segmentation, + const std::string& name, mitk::DataNode::Pointer parent = NULL); + + Ui::QmitkImageMaskingWidgetControls m_Controls; +}; + +#endif diff --git a/Plugins/org.mitk.gui.qt.multilabelsegmentation/src/internal/SegmentationUtilities/ImageMasking/QmitkImageMaskingWidgetControls.ui b/Plugins/org.mitk.gui.qt.multilabelsegmentation/src/internal/SegmentationUtilities/ImageMasking/QmitkImageMaskingWidgetControls.ui new file mode 100644 index 0000000000..f2efb8aaf6 --- /dev/null +++ b/Plugins/org.mitk.gui.qt.multilabelsegmentation/src/internal/SegmentationUtilities/ImageMasking/QmitkImageMaskingWidgetControls.ui @@ -0,0 +1,277 @@ + + + QmitkImageMaskingWidgetControls + + + + 0 + 0 + 197 + 420 + + + + + 2 + + + 4 + + + + + 1 + + + + Masks + + + + 3 + + + 4 + + + + + + 0 + 10 + + + + + + + + Masking Mode + + + + + + Image Masking + + + true + + + + + + + Surface Masking + + + + + + + + + + Mask + + + + + + + make output binary + + + + + + + overwrite foreground + + + + + + + overwrite background + + + + + + + + + Background Value: + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + 50 + 0 + + + + + 50 + 16777215 + + + + 0.0 + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + + + + Foreground Value: + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + 50 + 0 + + + + + 50 + 16777215 + + + + 1.0 + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + + Qt::Vertical + + + + 20 + 8 + + + + + + + + + Labels + + + + 3 + + + 4 + + + + + + 0 + 0 + + + + + 0 + 50 + + + + + + + + + + + Qt::Vertical + + + + 20 + 113 + + + + + + + + + + + + + QmitkDataSelectionWidget + QWidget +
internal/Common/QmitkDataSelectionWidget.h
+
+ + QmitkLabelSetWidget + QWidget +
Qmitk/QmitkLabelSetWidget.h
+ 1 +
+ + QmitkMaskStampWidget + QWidget +
QmitkMaskStampWidget.h
+ 1 +
+
+ + +
diff --git a/Plugins/org.mitk.gui.qt.multilabelsegmentation/src/internal/SegmentationUtilities/MorphologicalOperations/QmitkMorphologicalOperationsWidget.cpp b/Plugins/org.mitk.gui.qt.multilabelsegmentation/src/internal/SegmentationUtilities/MorphologicalOperations/QmitkMorphologicalOperationsWidget.cpp new file mode 100644 index 0000000000..6647dc61f3 --- /dev/null +++ b/Plugins/org.mitk.gui.qt.multilabelsegmentation/src/internal/SegmentationUtilities/MorphologicalOperations/QmitkMorphologicalOperationsWidget.cpp @@ -0,0 +1,254 @@ +/*=================================================================== + +The Medical Imaging Interaction Toolkit (MITK) + +Copyright (c) German Cancer Research Center, +Division of Medical and Biological Informatics. +All rights reserved. + +This software is distributed WITHOUT ANY WARRANTY; without +even the implied warranty of MERCHANTABILITY or FITNESS FOR +A PARTICULAR PURPOSE. + +See LICENSE.txt or http://www.mitk.org for details. + +===================================================================*/ + +#include "QmitkMorphologicalOperationsWidget.h" +#include +#include +#include +#include +#include + +static const char* const HelpText = "Select a mask above"; + +QmitkMorphologicalOperationsWidget::QmitkMorphologicalOperationsWidget(mitk::SliceNavigationController* timeNavigationController, QWidget* parent) + : QmitkSegmentationUtilityWidget(timeNavigationController, parent) +{ + m_Controls.setupUi(this); + + m_Controls.m_DataSelectionWidget->AddDataStorageComboBox(QmitkDataSelectionWidget::MaskPredicate); + m_Controls.m_DataSelectionWidget->SetHelpText(HelpText); + +// mitk::IDataStorageService::Pointer service = +// berry::Platform::GetServiceRegistry().GetServiceById(mitk::IDataStorageService::ID); + +// assert(service.IsNotNull()); + + mitk::DataStorage::Pointer ds = m_Controls.m_DataSelectionWidget->GetDataStorage(); + m_Controls.m_LabelSetWidget->SetDataStorage(ds); + m_Controls.m_LabelSetWidget->setEnabled(true); + + + m_Controls.m_ToolSelectionBox->SetGenerateAccelerators(true); + m_Controls.m_ToolSelectionBox->SetToolGUIArea( m_Controls.m_ToolGUIContainer ); + m_Controls.m_ToolSelectionBox->SetEnabledMode( QmitkToolSelectionBox::EnabledWithReferenceAndWorkingDataVisible ); + m_Controls.m_ToolSelectionBox->SetDisplayedToolGroups("Median Dilate Erode Open Close 'Fill Holes' 'Keep N Largest' 'Split' 'Region Selector'"); +// m_Controls.m_LabelSetWidget->SetDataStorage( *(this->GetDataStorage()) ); + + connect(m_Controls.btnClosing, SIGNAL(clicked()), this, SLOT(OnClosingButtonClicked())); + connect(m_Controls.btnOpening, SIGNAL(clicked()), this, SLOT(OnOpeningButtonClicked())); + connect(m_Controls.btnDilatation, SIGNAL(clicked()), this, SLOT(OnDilatationButtonClicked())); + connect(m_Controls.btnErosion, SIGNAL(clicked()), this, SLOT(OnErosionButtonClicked())); + connect(m_Controls.btnFillHoles, SIGNAL(clicked()), this, SLOT(OnFillHolesButtonClicked())); + connect(m_Controls.radioButtonMorphoCross, SIGNAL(clicked()), this, SLOT(OnRadioButtonsClicked())); + connect(m_Controls.radioButtonMorphoBall, SIGNAL(clicked()), this, SLOT(OnRadioButtonsClicked())); + connect(m_Controls.m_DataSelectionWidget, SIGNAL(SelectionChanged(unsigned int, const mitk::DataNode*)), this, SLOT(OnSelectionChanged(unsigned int, const mitk::DataNode*))); + + if (m_Controls.m_DataSelectionWidget->GetSelection(0).IsNotNull()) + this->OnSelectionChanged(0, m_Controls.m_DataSelectionWidget->GetSelection(0)); +} + +QmitkMorphologicalOperationsWidget::~QmitkMorphologicalOperationsWidget() +{ +} + +void QmitkMorphologicalOperationsWidget::OnSelectionChanged(unsigned int index, const mitk::DataNode* selection) +{ + QmitkDataSelectionWidget* m_DataSelectionWidget = m_Controls.m_DataSelectionWidget; + mitk::DataNode::Pointer node = m_DataSelectionWidget->GetSelection(0); + + if (node.IsNotNull()) + { + m_Controls.m_DataSelectionWidget->SetHelpText(""); + this->EnableButtons(true); + } + else + { + m_Controls.m_DataSelectionWidget->SetHelpText(HelpText); + this->EnableButtons(false); + } +} + +void QmitkMorphologicalOperationsWidget::EnableButtons(bool enable) +{ + m_Controls.btnClosing->setEnabled(enable); + m_Controls.btnDilatation->setEnabled(enable); + m_Controls.btnErosion->setEnabled(enable); + m_Controls.btnFillHoles->setEnabled(enable); + m_Controls.btnOpening->setEnabled(enable); +} + +void QmitkMorphologicalOperationsWidget::OnRadioButtonsClicked() +{ + bool enable = m_Controls.radioButtonMorphoBall->isChecked(); + + m_Controls.sliderMorphFactor->setEnabled(enable); + m_Controls.spinBoxMorphFactor->setEnabled(enable); +} + +void QmitkMorphologicalOperationsWidget::OnClosingButtonClicked() +{ + QApplication::setOverrideCursor(QCursor(Qt::BusyCursor)); + mitk::RenderingManager::GetInstance()->RequestUpdateAll(); + + QmitkDataSelectionWidget* m_DataSelectionWidget = m_Controls.m_DataSelectionWidget; + mitk::DataNode::Pointer node = m_DataSelectionWidget->GetSelection(0); + mitk::Image::Pointer image = static_cast(node->GetData()); + bool ball = m_Controls.radioButtonMorphoBall->isChecked(); + + mitk::MorphologicalOperations::StructuralElementType structuralElement = ball + ? mitk::MorphologicalOperations::Ball + : mitk::MorphologicalOperations::Cross; + + try + { + mitk::MorphologicalOperations::Closing(image, m_Controls.spinBoxMorphFactor->value(), structuralElement); + } + catch (const itk::ExceptionObject& exception) + { + MITK_WARN << "Exception caught: " << exception.GetDescription(); + + QApplication::restoreOverrideCursor(); + return; + } + + node->SetData(image); + + mitk::RenderingManager::GetInstance()->RequestUpdateAll(); + QApplication::restoreOverrideCursor(); +} + +void QmitkMorphologicalOperationsWidget::OnOpeningButtonClicked() +{ + QApplication::setOverrideCursor(QCursor(Qt::BusyCursor)); + mitk::RenderingManager::GetInstance()->RequestUpdateAll(); + + QmitkDataSelectionWidget* m_DataSelectionWidget = m_Controls.m_DataSelectionWidget; + mitk::DataNode::Pointer node = m_DataSelectionWidget->GetSelection(0); + mitk::Image::Pointer image = static_cast(node->GetData()); + bool ball = m_Controls.radioButtonMorphoBall->isChecked(); + + mitk::MorphologicalOperations::StructuralElementType structuralElement = ball + ? mitk::MorphologicalOperations::Ball + : mitk::MorphologicalOperations::Cross; + + try + { + mitk::MorphologicalOperations::Opening(image, m_Controls.spinBoxMorphFactor->value(), structuralElement); + } + catch (const itk::ExceptionObject& exception) + { + MITK_WARN << "Exception caught: " << exception.GetDescription(); + + QApplication::restoreOverrideCursor(); + return; + } + + node->SetData(image); + + mitk::RenderingManager::GetInstance()->RequestUpdateAll(); + QApplication::restoreOverrideCursor(); +} + +void QmitkMorphologicalOperationsWidget::OnDilatationButtonClicked() +{ + QApplication::setOverrideCursor(QCursor(Qt::BusyCursor)); + mitk::RenderingManager::GetInstance()->RequestUpdateAll(); + + QmitkDataSelectionWidget* m_DataSelectionWidget = m_Controls.m_DataSelectionWidget; + mitk::DataNode::Pointer node = m_DataSelectionWidget->GetSelection(0); + mitk::Image::Pointer image = static_cast(node->GetData()); + bool ball = m_Controls.radioButtonMorphoBall->isChecked(); + + mitk::MorphologicalOperations::StructuralElementType structuralElement = ball + ? mitk::MorphologicalOperations::Ball + : mitk::MorphologicalOperations::Cross; + + try + { + mitk::MorphologicalOperations::Dilate(image, m_Controls.spinBoxMorphFactor->value(), structuralElement); + } + catch (const itk::ExceptionObject& exception) + { + MITK_WARN << "Exception caught: " << exception.GetDescription(); + + QApplication::restoreOverrideCursor(); + return; + } + + node->SetData(image); + + mitk::RenderingManager::GetInstance()->RequestUpdateAll(); + QApplication::restoreOverrideCursor(); +} + +void QmitkMorphologicalOperationsWidget::OnErosionButtonClicked() +{ + QApplication::setOverrideCursor(QCursor(Qt::BusyCursor)); + mitk::RenderingManager::GetInstance()->RequestUpdateAll(); + + QmitkDataSelectionWidget* m_DataSelectionWidget = m_Controls.m_DataSelectionWidget; + mitk::DataNode::Pointer node = m_DataSelectionWidget->GetSelection(0); + mitk::Image::Pointer image = static_cast(node->GetData()); + bool ball = m_Controls.radioButtonMorphoBall->isChecked(); + + mitk::MorphologicalOperations::StructuralElementType structuralElement = ball + ? mitk::MorphologicalOperations::Ball + : mitk::MorphologicalOperations::Cross; + + try + { + mitk::MorphologicalOperations::Erode(image, m_Controls.spinBoxMorphFactor->value(), structuralElement); + } + catch (const itk::ExceptionObject& exception) + { + MITK_WARN << "Exception caught: " << exception.GetDescription(); + + QApplication::restoreOverrideCursor(); + return; + } + + node->SetData(image); + + mitk::RenderingManager::GetInstance()->RequestUpdateAll(); + QApplication::restoreOverrideCursor(); +} + +void QmitkMorphologicalOperationsWidget::OnFillHolesButtonClicked() +{ + QApplication::setOverrideCursor(QCursor(Qt::BusyCursor)); + mitk::RenderingManager::GetInstance()->RequestUpdateAll(); + + QmitkDataSelectionWidget* m_DataSelectionWidget = m_Controls.m_DataSelectionWidget; + mitk::DataNode::Pointer node = m_DataSelectionWidget->GetSelection(0); + mitk::Image::Pointer image = static_cast(node->GetData()); + + try + { + mitk::MorphologicalOperations::FillHoles(image); + } + catch (const itk::ExceptionObject& exception) + { + MITK_WARN << "Exception caught: " << exception.GetDescription(); + + QApplication::restoreOverrideCursor(); + return; + } + + node->SetData(image); + + mitk::RenderingManager::GetInstance()->RequestUpdateAll(); + QApplication::restoreOverrideCursor(); +} diff --git a/Plugins/org.mitk.gui.qt.multilabelsegmentation/src/internal/SegmentationUtilities/MorphologicalOperations/QmitkMorphologicalOperationsWidget.h b/Plugins/org.mitk.gui.qt.multilabelsegmentation/src/internal/SegmentationUtilities/MorphologicalOperations/QmitkMorphologicalOperationsWidget.h new file mode 100644 index 0000000000..01fe747748 --- /dev/null +++ b/Plugins/org.mitk.gui.qt.multilabelsegmentation/src/internal/SegmentationUtilities/MorphologicalOperations/QmitkMorphologicalOperationsWidget.h @@ -0,0 +1,49 @@ +/*=================================================================== + +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 QmitkMorphologicalOperationsWidget_h +#define QmitkMorphologicalOperationsWidget_h + +#include "../QmitkSegmentationUtilityWidget.h" +#include + +/** \brief GUI class for morphological segmentation tools. + */ +class QmitkMorphologicalOperationsWidget : public QmitkSegmentationUtilityWidget +{ + Q_OBJECT + +public: + explicit QmitkMorphologicalOperationsWidget(mitk::SliceNavigationController* timeNavigationController, QWidget* parent = NULL); + ~QmitkMorphologicalOperationsWidget(); + +public slots: + void OnClosingButtonClicked(); + void OnOpeningButtonClicked(); + void OnDilatationButtonClicked(); + void OnErosionButtonClicked(); + void OnFillHolesButtonClicked(); + void OnSelectionChanged(unsigned int index, const mitk::DataNode* selection); + void OnRadioButtonsClicked(); + +protected: + void EnableButtons(bool enable); + +private: + Ui::QmitkMorphologicalOperationsWidgetControls m_Controls; +}; + +#endif diff --git a/Plugins/org.mitk.gui.qt.multilabelsegmentation/src/internal/SegmentationUtilities/MorphologicalOperations/QmitkMorphologicalOperationsWidgetControls.ui b/Plugins/org.mitk.gui.qt.multilabelsegmentation/src/internal/SegmentationUtilities/MorphologicalOperations/QmitkMorphologicalOperationsWidgetControls.ui new file mode 100644 index 0000000000..7f33ec3a47 --- /dev/null +++ b/Plugins/org.mitk.gui.qt.multilabelsegmentation/src/internal/SegmentationUtilities/MorphologicalOperations/QmitkMorphologicalOperationsWidgetControls.ui @@ -0,0 +1,429 @@ + + + QmitkMorphologicalOperationsWidgetControls + + + + 0 + 0 + 236 + 507 + + + + + 0 + 0 + + + + Form + + + + + + + 0 + 0 + + + + 0 + + + + Masks + + + + 3 + + + 4 + + + + + + + + Structuring Element + + + + + + Ball + + + true + + + + + + + Cross + + + + + + + + + + + + Radius + + + + + + + 1 + + + 20 + + + 1 + + + Qt::Horizontal + + + + + + + 1 + + + 20 + + + + + + + + + + + false + + + + 0 + 0 + + + + Dilatation + + + + :/SegmentationUtilities/MorphologicalOperations/Dilate_48x48.png:/SegmentationUtilities/MorphologicalOperations/Dilate_48x48.png + + + + 32 + 32 + + + + Qt::ToolButtonTextUnderIcon + + + + + + + false + + + + 0 + 0 + + + + Globally fills holes in segmentation (radius not required) + + + Fill Holes + + + + :/SegmentationUtilities/MorphologicalOperations/FillHoles_48x48.png:/SegmentationUtilities/MorphologicalOperations/FillHoles_48x48.png + + + + 32 + 32 + + + + Qt::ToolButtonTextUnderIcon + + + + + + + false + + + + 0 + 0 + + + + Erosion + + + + :/SegmentationUtilities/MorphologicalOperations/Erode_48x48.png:/SegmentationUtilities/MorphologicalOperations/Erode_48x48.png + + + + 32 + 32 + + + + Qt::ToolButtonTextUnderIcon + + + + + + + false + + + + 0 + 0 + + + + Closing + + + + :/SegmentationUtilities/MorphologicalOperations/Closing_48x48.png:/SegmentationUtilities/MorphologicalOperations/Closing_48x48.png + + + + 32 + 32 + + + + Qt::ToolButtonTextUnderIcon + + + + + + + false + + + + 0 + 0 + + + + Opening + + + + :/SegmentationUtilities/MorphologicalOperations/Opening_48x48.png:/SegmentationUtilities/MorphologicalOperations/Opening_48x48.png + + + + 32 + 32 + + + + Qt::ToolButtonTextUnderIcon + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + Labels + + + + 3 + + + 4 + + + + + + 0 + 0 + + + + + 0 + 50 + + + + + + + + + 0 + 0 + + + + + 15 + 0 + + + + + 50 + false + + + + + + + + + 0 + 0 + + + + + 0 + 15 + + + + + 50 + false + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + + + + QmitkDataSelectionWidget + QWidget +
internal/Common/QmitkDataSelectionWidget.h
+ 1 +
+ + QmitkToolSelectionBox + QWidget +
QmitkToolSelectionBox.h
+
+ + QmitkToolGUIArea + QWidget +
QmitkToolGUIArea.h
+
+ + QmitkLabelSetWidget + QWidget +
Qmitk/QmitkLabelSetWidget.h
+ 1 +
+
+ + + + + + sliderMorphFactor + valueChanged(int) + spinBoxMorphFactor + setValue(int) + + + 240 + 27 + + + 766 + 36 + + + + + spinBoxMorphFactor + valueChanged(int) + sliderMorphFactor + setValue(int) + + + 784 + 38 + + + 657 + 38 + + + + +
diff --git a/Plugins/org.mitk.gui.qt.multilabelsegmentation/src/internal/SegmentationUtilities/QmitkMultiLabelSegmentationUtilitiesView.cpp b/Plugins/org.mitk.gui.qt.multilabelsegmentation/src/internal/SegmentationUtilities/QmitkMultiLabelSegmentationUtilitiesView.cpp new file mode 100644 index 0000000000..ff3a1374f2 --- /dev/null +++ b/Plugins/org.mitk.gui.qt.multilabelsegmentation/src/internal/SegmentationUtilities/QmitkMultiLabelSegmentationUtilitiesView.cpp @@ -0,0 +1,90 @@ +/*=================================================================== + +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. + +===================================================================*/ + +// #define ENABLE_CTK_WIDGETS_WIDGET + +#include "QmitkMultiLabelSegmentationUtilitiesView.h" +#include "BooleanOperations/QmitkBooleanOperationsWidget.h" +#include "MorphologicalOperations/QmitkMorphologicalOperationsWidget.h" +#include "SurfaceToImage/QmitkSurfaceToImageWidget.h" +#include "ImageMasking/QmitkImageMaskingWidget.h" + +QmitkMultiLabelSegmentationUtilitiesView::QmitkMultiLabelSegmentationUtilitiesView() : +m_BooleanOperationsWidget(NULL), +m_MorphologicalOperationsWidget(NULL), +m_SurfaceToImageWidget(NULL), +m_ImageMaskingWidget(NULL) +{ +} + +QmitkMultiLabelSegmentationUtilitiesView::~QmitkMultiLabelSegmentationUtilitiesView() +{ +} + +void QmitkMultiLabelSegmentationUtilitiesView::CreateQtPartControl(QWidget* parent) +{ + m_Controls.setupUi(parent); + + mitk::IRenderWindowPart* renderWindowPart = this->GetRenderWindowPart(); + + mitk::SliceNavigationController* timeNavigationController = renderWindowPart != NULL + ? renderWindowPart->GetTimeNavigationController() + : NULL; + + m_BooleanOperationsWidget = new QmitkBooleanOperationsWidget(timeNavigationController, parent); + + m_MorphologicalOperationsWidget = new QmitkMorphologicalOperationsWidget(timeNavigationController, parent); + + m_SurfaceToImageWidget = new QmitkSurfaceToImageWidget(timeNavigationController, parent); + + m_ImageMaskingWidget = new QmitkImageMaskingWidget(timeNavigationController, parent); + + this->AddUtilityWidget(m_BooleanOperationsWidget, QIcon(":/MultiLabelSegmentationUtilities/BooleanOperations_48x48.png"), "Boolean Operations"); + + this->AddUtilityWidget(m_MorphologicalOperationsWidget, QIcon(":/MultiLabelSegmentationUtilities/MorphologicalOperations_48x48.png"), "Morphological Operations"); + + this->AddUtilityWidget(m_SurfaceToImageWidget, QIcon(":/MultiLabelSegmentationUtilities/SurfaceToImage_48x48.png"), "Surface To Image"); + + this->AddUtilityWidget(m_ImageMaskingWidget, QIcon(":/MultiLabelSegmentationUtilities/ImageMasking_48x48.png"), "Image Masking"); +} + +void QmitkMultiLabelSegmentationUtilitiesView::AddUtilityWidget(QWidget* widget, const QIcon& icon, const QString& text) +{ + m_Controls.toolBox->addItem(widget, icon, text); +} + +void QmitkMultiLabelSegmentationUtilitiesView::SetFocus() +{ + m_Controls.toolBox->setFocus(); +} + +void QmitkMultiLabelSegmentationUtilitiesView::RenderWindowPartActivated(mitk::IRenderWindowPart* renderWindowPart) +{ + mitk::SliceNavigationController* timeNavigationController = renderWindowPart->GetTimeNavigationController(); + + m_BooleanOperationsWidget->SetTimeNavigationController(timeNavigationController); + m_MorphologicalOperationsWidget->SetTimeNavigationController(timeNavigationController); + m_SurfaceToImageWidget->SetTimeNavigationController(timeNavigationController); + m_ImageMaskingWidget->SetTimeNavigationController(timeNavigationController); +} + +void QmitkMultiLabelSegmentationUtilitiesView::RenderWindowPartDeactivated(mitk::IRenderWindowPart*) +{ + m_BooleanOperationsWidget->SetTimeNavigationController(NULL); + m_MorphologicalOperationsWidget->SetTimeNavigationController(NULL); + m_SurfaceToImageWidget->SetTimeNavigationController(NULL); + m_ImageMaskingWidget->SetTimeNavigationController(NULL); +} diff --git a/Plugins/org.mitk.gui.qt.multilabelsegmentation/src/internal/SegmentationUtilities/QmitkMultiLabelSegmentationUtilitiesView.h b/Plugins/org.mitk.gui.qt.multilabelsegmentation/src/internal/SegmentationUtilities/QmitkMultiLabelSegmentationUtilitiesView.h new file mode 100644 index 0000000000..865257ebe3 --- /dev/null +++ b/Plugins/org.mitk.gui.qt.multilabelsegmentation/src/internal/SegmentationUtilities/QmitkMultiLabelSegmentationUtilitiesView.h @@ -0,0 +1,57 @@ +/*=================================================================== + +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 QmitkMultiLabelSegmentationUtilitiesView_h +#define QmitkMultiLabelSegmentationUtilitiesView_h + +#include +#include +#include + +class QmitkBooleanOperationsWidget; +class QmitkSurfaceToImageWidget; +class QmitkImageMaskingWidget; +class QmitkMorphologicalOperationsWidget; + +class QmitkMultiLabelSegmentationUtilitiesView : public QmitkAbstractView, public mitk::IRenderWindowPartListener +{ + Q_OBJECT + +public: + QmitkMultiLabelSegmentationUtilitiesView(); + ~QmitkMultiLabelSegmentationUtilitiesView(); + + void CreateQtPartControl(QWidget* parent); + void SetFocus(); + + void RenderWindowPartActivated(mitk::IRenderWindowPart* renderWindowPart); + void RenderWindowPartDeactivated(mitk::IRenderWindowPart* renderWindowPart); + +private: + void AddUtilityWidget(QWidget* widget, const QIcon& icon, const QString& text); + + QmitkBooleanOperationsWidget* m_BooleanOperationsWidget; + + QmitkMorphologicalOperationsWidget* m_MorphologicalOperationsWidget; + + QmitkSurfaceToImageWidget* m_SurfaceToImageWidget; + + QmitkImageMaskingWidget* m_ImageMaskingWidget; + + Ui::QmitkMultiLabelSegmentationUtilitiesViewControls m_Controls; +}; + +#endif diff --git a/Plugins/org.mitk.gui.qt.multilabelsegmentation/src/internal/SegmentationUtilities/QmitkMultiLabelSegmentationUtilitiesViewControls.ui b/Plugins/org.mitk.gui.qt.multilabelsegmentation/src/internal/SegmentationUtilities/QmitkMultiLabelSegmentationUtilitiesViewControls.ui new file mode 100644 index 0000000000..5853d2f7a1 --- /dev/null +++ b/Plugins/org.mitk.gui.qt.multilabelsegmentation/src/internal/SegmentationUtilities/QmitkMultiLabelSegmentationUtilitiesViewControls.ui @@ -0,0 +1,61 @@ + + + QmitkMultiLabelSegmentationUtilitiesViewControls + + + + 0 + 0 + 289 + 418 + + + + + 0 + 0 + + + + QmitkTemplate + + + icon-size: 32px 32px + + + + 0 + + + + + + + + ::tab +{ + background: qlineargradient(spread:pad, x1:0, y1:0, x2:0, y2:1, stop:0 palette(light), stop:1 palette(midlight)); +} + +::tab:selected +{ + background: palette(highlight); + color: palette(highlighted-text); +} + + + -1 + + + 0 + + + + + + + + + + + diff --git a/Plugins/org.mitk.gui.qt.multilabelsegmentation/src/internal/SegmentationUtilities/QmitkSegmentationUtilityWidget.cpp b/Plugins/org.mitk.gui.qt.multilabelsegmentation/src/internal/SegmentationUtilities/QmitkSegmentationUtilityWidget.cpp new file mode 100644 index 0000000000..3616272fbd --- /dev/null +++ b/Plugins/org.mitk.gui.qt.multilabelsegmentation/src/internal/SegmentationUtilities/QmitkSegmentationUtilityWidget.cpp @@ -0,0 +1,38 @@ +/*=================================================================== + +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 "QmitkSegmentationUtilityWidget.h" + +QmitkSegmentationUtilityWidget::QmitkSegmentationUtilityWidget(mitk::SliceNavigationController* timeNavigationController, QWidget* parent) + : QWidget(parent) +{ + this->SetTimeNavigationController(timeNavigationController); +} + +QmitkSegmentationUtilityWidget::~QmitkSegmentationUtilityWidget() +{ +} + +mitk::SliceNavigationController* QmitkSegmentationUtilityWidget::GetTimeNavigationController() const +{ + return m_TimeNavigationController; +} + +void QmitkSegmentationUtilityWidget::SetTimeNavigationController(mitk::SliceNavigationController* timeNavigationController) +{ + m_TimeNavigationController = timeNavigationController; + this->setEnabled(timeNavigationController != NULL); +} diff --git a/Plugins/org.mitk.gui.qt.multilabelsegmentation/src/internal/SegmentationUtilities/QmitkSegmentationUtilityWidget.h b/Plugins/org.mitk.gui.qt.multilabelsegmentation/src/internal/SegmentationUtilities/QmitkSegmentationUtilityWidget.h new file mode 100644 index 0000000000..a588a993b4 --- /dev/null +++ b/Plugins/org.mitk.gui.qt.multilabelsegmentation/src/internal/SegmentationUtilities/QmitkSegmentationUtilityWidget.h @@ -0,0 +1,55 @@ +/*=================================================================== + +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 QmitkSegmentationUtilityWidget_h +#define QmitkSegmentationUtilityWidget_h + +#include + +namespace mitk +{ + class SliceNavigationController; +} + +/** \brief Base class for segmentation utility widgets that need access to the time navigation controller. + * + * Call GetTimeNavigationController() in your derived class to gain access to the time navigation controller. + * The time navigation controller is not not available at all times and hence this method can return NULL. + */ +class QmitkSegmentationUtilityWidget : public QWidget +{ + Q_OBJECT + +public: + explicit QmitkSegmentationUtilityWidget(mitk::SliceNavigationController* timeNavigationController, QWidget* parent = NULL); + virtual ~QmitkSegmentationUtilityWidget(); + + /** \brief Usually called only from QmitkSegmentationUtilitiesView::RenderWindowPartActivated() and QmitkSegmentationUtilitiesView::RenderWindowPartDeactivated(). + */ + void SetTimeNavigationController(mitk::SliceNavigationController* timeNavigationController); + +protected: + /** \brief Call this method to access the time navigation controller. + * + * \return Pointer to the time navigation controller or NULL, if it is not available. + */ + mitk::SliceNavigationController* GetTimeNavigationController() const; + +private: + mitk::SliceNavigationController* m_TimeNavigationController; +}; + +#endif diff --git a/Plugins/org.mitk.gui.qt.multilabelsegmentation/src/internal/SegmentationUtilities/SurfaceToImage/QmitkSurfaceToImageWidget.cpp b/Plugins/org.mitk.gui.qt.multilabelsegmentation/src/internal/SegmentationUtilities/SurfaceToImage/QmitkSurfaceToImageWidget.cpp new file mode 100644 index 0000000000..b6764eb904 --- /dev/null +++ b/Plugins/org.mitk.gui.qt.multilabelsegmentation/src/internal/SegmentationUtilities/SurfaceToImage/QmitkSurfaceToImageWidget.cpp @@ -0,0 +1,179 @@ +/*=================================================================== + +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 "QmitkSurfaceToImageWidget.h" + +#include +#include +#include +#include +#include +#include + +#include + +#include + +static const char* const HelpText = "Select a patient image and a surface"; + +QmitkSurfaceToImageWidget::QmitkSurfaceToImageWidget(mitk::SliceNavigationController* timeNavigationController, QWidget* parent) + : QmitkSegmentationUtilityWidget(timeNavigationController, parent) +{ + m_Controls.setupUi(this); + + m_Controls.m_DataSelectionWidget->AddDataStorageComboBox(QmitkDataSelectionWidget::ImagePredicate); + m_Controls.m_DataSelectionWidget->AddDataStorageComboBox(QmitkDataSelectionWidget::SurfacePredicate); + m_Controls.m_DataSelectionWidget->SetHelpText(HelpText); + +// mitk::IDataStorageService* service = +// berry::Platform::GetServiceRegistry().GetServiceById(mitk::IDataStorageService::ID); + +// assert(service.IsNotNull()); + + mitk::DataStorage::Pointer ds = m_Controls.m_DataSelectionWidget->GetDataStorage(); + m_Controls.m_LabelSetWidget->SetDataStorage(ds); + m_Controls.m_LabelSetWidget->setEnabled(true); + + m_Controls.m_SurfaceStampWidget->SetDataStorage(ds); + + this->EnableButtons(false); + + m_Controls.m_chkMakeOutputBinary->setChecked(true); + + connect (m_Controls.btnSurface2Image, SIGNAL(pressed()), this, SLOT(OnSurface2ImagePressed())); + connect(m_Controls.m_DataSelectionWidget, SIGNAL(SelectionChanged(unsigned int, const mitk::DataNode*)), + this, SLOT(OnSelectionChanged(unsigned int, const mitk::DataNode*))); + connect (m_Controls.m_chkMakeOutputBinary, SIGNAL(toggled(bool)), this, SLOT(OnMakeOutputBinaryChanged(bool))); + + if( m_Controls.m_DataSelectionWidget->GetSelection(0).IsNotNull() && + m_Controls.m_DataSelectionWidget->GetSelection(1).IsNotNull() ) + { + this->OnSelectionChanged( 0, m_Controls.m_DataSelectionWidget->GetSelection(0)); + } +} + +QmitkSurfaceToImageWidget::~QmitkSurfaceToImageWidget() +{ +} + +void QmitkSurfaceToImageWidget::EnableButtons(bool enable) +{ + m_Controls.btnSurface2Image->setEnabled(enable); + m_Controls.m_chkMakeOutputBinary->setEnabled(enable); + m_Controls.m_chkOverwriteBackground->setEnabled(enable && !m_Controls.m_chkMakeOutputBinary->isChecked()); + m_Controls.m_leForegroundValue->setEnabled(enable && !m_Controls.m_chkMakeOutputBinary->isChecked()); + m_Controls.m_leBackgroundValue->setEnabled(enable && !m_Controls.m_chkMakeOutputBinary->isChecked()); +} + +void QmitkSurfaceToImageWidget::OnSelectionChanged(unsigned int index, const mitk::DataNode* selection) +{ + mitk::DataNode::Pointer imageNode = m_Controls.m_DataSelectionWidget->GetSelection(0); + mitk::DataNode::Pointer surfaceNode = m_Controls.m_DataSelectionWidget->GetSelection(1); + + if (imageNode.IsNull() || surfaceNode.IsNull() ) + { + m_Controls.m_DataSelectionWidget->SetHelpText(HelpText); + this->EnableButtons(false); + } + else + { + mitk::Image::Pointer image = dynamic_cast( m_Controls.m_DataSelectionWidget->GetSelection(0)->GetData() ); + mitk::Surface::Pointer surface = dynamic_cast( m_Controls.m_DataSelectionWidget->GetSelection(1)->GetData() ); + if( image->GetTimeSteps() != surface->GetTimeSteps() ) + { + m_Controls.m_DataSelectionWidget->SetHelpText("Image and surface are of different size"); + this->EnableButtons(false); + } + else + { + m_Controls.m_DataSelectionWidget->SetHelpText(""); + this->EnableButtons(); + } + } +} + +void QmitkSurfaceToImageWidget::OnSurface2ImagePressed() +{ + this->EnableButtons(false); + + mitk::Image::Pointer image = dynamic_cast( m_Controls.m_DataSelectionWidget->GetSelection(0)->GetData() ); + mitk::Surface::Pointer surface = dynamic_cast( m_Controls.m_DataSelectionWidget->GetSelection(1)->GetData() ); + + if( image.IsNull() || surface.IsNull()) + { + MITK_ERROR << "Selection does not contain an image and/or a surface"; + QMessageBox::information( this, "Surface To Image", "Selection does not contain an image and/or a surface", QMessageBox::Ok ); + this->EnableButtons(); + return; + } + + mitk::Image::Pointer resultImage(0); + resultImage = this->ConvertSurfaceToImage( image, surface ); + + if( resultImage.IsNull() ) + { + QMessageBox::information( this, "Surface to Image", "Could not stamp surface.\n See error log for details.", QMessageBox::Ok ); + this->EnableButtons(); + return; + } + + //create name for result node + std::string nameOfResultImage = m_Controls.m_DataSelectionWidget->GetSelection(0)->GetName(); + nameOfResultImage.append("_"); + nameOfResultImage.append(m_Controls.m_DataSelectionWidget->GetSelection(1)->GetName()); + + //create data node and add to data storage + mitk::DataNode::Pointer resultNode = mitk::DataNode::New(); + resultNode->SetData( resultImage ); + resultNode->SetProperty("name", mitk::StringProperty::New(nameOfResultImage) ); + resultNode->SetProperty("binary", mitk::BoolProperty::New(m_Controls.m_chkMakeOutputBinary->isChecked()) ); + + m_Controls.m_DataSelectionWidget->GetDataStorage()->Add(resultNode, m_Controls.m_DataSelectionWidget->GetSelection(0)); + + this->EnableButtons(); +} + +mitk::Image::Pointer QmitkSurfaceToImageWidget::ConvertSurfaceToImage( mitk::Image::Pointer image, mitk::Surface::Pointer surface ) +{ + mitk::SurfaceStampImageFilter::Pointer filter = mitk::SurfaceStampImageFilter::New(); + filter->SetInput(image); + filter->SetSurface(surface); + filter->SetMakeOutputBinary(m_Controls.m_chkMakeOutputBinary->isChecked()); + filter->SetOverwriteBackground(m_Controls.m_chkOverwriteBackground->isChecked()); + filter->SetForegroundValue(m_Controls.m_leForegroundValue->text().toFloat()); + filter->SetBackgroundValue(m_Controls.m_leBackgroundValue->text().toFloat()); + + try + { + filter->Update(); + } + catch(mitk::Exception& e) + { + MITK_ERROR << "exception caught: " << e.GetDescription(); + return 0; + } + + mitk::Image::Pointer resultImage = filter->GetOutput(); + + return resultImage; +} + +void QmitkSurfaceToImageWidget::OnMakeOutputBinaryChanged(bool value) +{ + m_Controls.m_chkOverwriteBackground->setEnabled(!value); + m_Controls.m_leForegroundValue->setEnabled(!value); + m_Controls.m_leBackgroundValue->setEnabled(!value); +} diff --git a/Plugins/org.mitk.gui.qt.multilabelsegmentation/src/internal/SegmentationUtilities/SurfaceToImage/QmitkSurfaceToImageWidget.h b/Plugins/org.mitk.gui.qt.multilabelsegmentation/src/internal/SegmentationUtilities/SurfaceToImage/QmitkSurfaceToImageWidget.h new file mode 100644 index 0000000000..471b06d90c --- /dev/null +++ b/Plugins/org.mitk.gui.qt.multilabelsegmentation/src/internal/SegmentationUtilities/SurfaceToImage/QmitkSurfaceToImageWidget.h @@ -0,0 +1,67 @@ +/*=================================================================== + +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 QmitkSurfaceToImageWidget_h +#define QmitkSurfaceToImageWidget_h + +#include "../QmitkSegmentationUtilityWidget.h" +#include + +#include +#include + +/*! + \brief QmitkSurfaceToImageWidget + + The Tool converts a surface to a binary image. The Method requires + a surface and an image, which header information defines the output + image. The resulting binary image has the same dimension, size, and + Geometry3D as the input image. +*/ +class QmitkSurfaceToImageWidget : public QmitkSegmentationUtilityWidget +{ + Q_OBJECT + +public: + + /** @brief Default constructor, including creation of GUI elements and signals/slots connections. */ + explicit QmitkSurfaceToImageWidget(mitk::SliceNavigationController* timeNavigationController, QWidget* parent = NULL); + + /** @brief Defaul destructor. */ + ~QmitkSurfaceToImageWidget(); + +private slots: + + /** @brief This slot is called if the selection in the workbench is changed. */ + void OnSelectionChanged(unsigned int index, const mitk::DataNode* selection); + + /** @brief This slot is called if user activates the button to convert a surface into a binary image. */ + void OnSurface2ImagePressed(); + + void OnMakeOutputBinaryChanged(bool value); + +private: + + /** @brief Enable buttons if data selction is valid. */ + void EnableButtons(bool enable = true); + + /** @brief Convert a surface into an binary image. */ + mitk::Image::Pointer ConvertSurfaceToImage( mitk::Image::Pointer image, mitk::Surface::Pointer surface ); + + Ui::QmitkSurfaceToImageWidgetControls m_Controls; +}; + +#endif diff --git a/Plugins/org.mitk.gui.qt.multilabelsegmentation/src/internal/SegmentationUtilities/SurfaceToImage/QmitkSurfaceToImageWidgetControls.ui b/Plugins/org.mitk.gui.qt.multilabelsegmentation/src/internal/SegmentationUtilities/SurfaceToImage/QmitkSurfaceToImageWidgetControls.ui new file mode 100644 index 0000000000..6548e7060e --- /dev/null +++ b/Plugins/org.mitk.gui.qt.multilabelsegmentation/src/internal/SegmentationUtilities/SurfaceToImage/QmitkSurfaceToImageWidgetControls.ui @@ -0,0 +1,245 @@ + + + QmitkSurfaceToImageWidgetControls + + + + 0 + 0 + 198 + 482 + + + + + 3 + + + 4 + + + + + 0 + + + + Masks + + + + 3 + + + 4 + + + + + + + + make output binary + + + + + + + overwrite background + + + + + + + + + Background Value: + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + 50 + 0 + + + + + 50 + 16777215 + + + + 0.0 + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + + + + Foreground Value: + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + 50 + 0 + + + + + 50 + 16777215 + + + + 1.0 + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + + Convert + + + + + + + Qt::Vertical + + + + 20 + 308 + + + + + + + + + Labels + + + + 2 + + + 2 + + + + + + 0 + 0 + + + + + 0 + 50 + + + + + + + + + 0 + 20 + + + + + + + + Qt::Vertical + + + + 20 + 202 + + + + + + + + + + + + + QmitkDataSelectionWidget + QWidget +
internal/Common/QmitkDataSelectionWidget.h
+ 1 +
+ + QmitkLabelSetWidget + QWidget +
Qmitk/QmitkLabelSetWidget.h
+ 1 +
+ + QmitkSurfaceStampWidget + QWidget +
QmitkSurfaceStampWidget.h
+ 1 +
+
+ + +
diff --git a/Plugins/org.mitk.gui.qt.multilabelsegmentation/src/internal/mitkPluginActivator.cpp b/Plugins/org.mitk.gui.qt.multilabelsegmentation/src/internal/mitkPluginActivator.cpp new file mode 100644 index 0000000000..bc3da79167 --- /dev/null +++ b/Plugins/org.mitk.gui.qt.multilabelsegmentation/src/internal/mitkPluginActivator.cpp @@ -0,0 +1,69 @@ +/*=================================================================== + +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 "mitkPluginActivator.h" + +#include "QmitkMultiLabelSegmentationView.h" +#include "QmitkThresholdAction.h" +#include "QmitkCreatePolygonModelAction.h" +#include "QmitkAutocropAction.h" +#include "QmitkConvertSurfaceToLabelAction.h" +#include "QmitkConvertMaskToLabelAction.h" +#include "QmitkConvertToMultiLabelSegmentationAction.h" +#include "QmitkCreateMultiLabelSegmentationAction.h" +#include "QmitkMultiLabelSegmentationPreferencePage.h" +#include "QmitkLoadMultiLabelPresetAction.h" +#include "QmitkCreateMultiLabelPresetAction.h" +#include "SegmentationUtilities/QmitkMultiLabelSegmentationUtilitiesView.h" + +#include + +ctkPluginContext* mitk::PluginActivator::m_Context = NULL; + +//MLI TODO +US_INITIALIZE_MODULE //("MultiLabelSegmentation", "liborg_mitk_gui_qt_multilabelsegmentation") + +void mitk::PluginActivator::start(ctkPluginContext *context) +{ + BERRY_REGISTER_EXTENSION_CLASS(QmitkMultiLabelSegmentationView, context) + BERRY_REGISTER_EXTENSION_CLASS(QmitkThresholdAction, context) + BERRY_REGISTER_EXTENSION_CLASS(QmitkCreatePolygonModelAction, context) + BERRY_REGISTER_EXTENSION_CLASS(QmitkAutocropAction, context) + BERRY_REGISTER_EXTENSION_CLASS(QmitkConvertSurfaceToLabelAction, context) + BERRY_REGISTER_EXTENSION_CLASS(QmitkConvertMaskToLabelAction, context) + BERRY_REGISTER_EXTENSION_CLASS(QmitkConvertToMultiLabelSegmentationAction, context) + BERRY_REGISTER_EXTENSION_CLASS(QmitkCreateMultiLabelSegmentationAction, context) + BERRY_REGISTER_EXTENSION_CLASS(QmitkCreateMultiLabelPresetAction, context) + BERRY_REGISTER_EXTENSION_CLASS(QmitkLoadMultiLabelPresetAction, context) + BERRY_REGISTER_EXTENSION_CLASS(QmitkMultiLabelSegmentationPreferencePage, context) + BERRY_REGISTER_EXTENSION_CLASS(QmitkMultiLabelSegmentationUtilitiesView, context) + + m_Context = context; +} + +void mitk::PluginActivator::stop(ctkPluginContext* context) +{ + Q_UNUSED(context) + m_Context = NULL; +} + +ctkPluginContext* mitk::PluginActivator::getContext() +{ + return m_Context; +} + +#if QT_VERSION < QT_VERSION_CHECK(5, 0, 0) +Q_EXPORT_PLUGIN2(org_mitk_gui_qt_multilabelsegmentation, mitk::PluginActivator) +#endif diff --git a/Plugins/org.mitk.gui.qt.multilabelsegmentation/src/internal/mitkPluginActivator.h b/Plugins/org.mitk.gui.qt.multilabelsegmentation/src/internal/mitkPluginActivator.h new file mode 100644 index 0000000000..b2fe5addce --- /dev/null +++ b/Plugins/org.mitk.gui.qt.multilabelsegmentation/src/internal/mitkPluginActivator.h @@ -0,0 +1,44 @@ +/*=================================================================== + +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 MITKPLUGINACTIVATOR_H +#define MITKPLUGINACTIVATOR_H + +// Parent classes +#include +#include + +namespace mitk +{ + class PluginActivator : public QObject, public ctkPluginActivator + { + Q_OBJECT +#if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0) + Q_PLUGIN_METADATA(IID "org_mitk_gui_qt_multilabelsegmentation") +#endif + Q_INTERFACES(ctkPluginActivator) + + public: + void start(ctkPluginContext *context); + void stop(ctkPluginContext *context); + + static ctkPluginContext* getContext(); + + private: + static ctkPluginContext* m_Context; + }; +} + +#endif diff --git a/Plugins/org.mitk.gui.qt.multilabelsegmentation/target_libraries.cmake b/Plugins/org.mitk.gui.qt.multilabelsegmentation/target_libraries.cmake new file mode 100644 index 0000000000..6c71bbbf08 --- /dev/null +++ b/Plugins/org.mitk.gui.qt.multilabelsegmentation/target_libraries.cmake @@ -0,0 +1,3 @@ +set(target_libraries + CTKWidgets +)