X-Git-Url: https://git.creatis.insa-lyon.fr/pubgit/?a=blobdiff_plain;f=vv%2FvvToolSegmentation.cxx;h=63ff9b150f6cac958826bb87e83b9473310b9c0a;hb=HEAD;hp=dea3513013aff27801512bdf4143ed40830bd990;hpb=957a4c21ae6aa291c6ad57a45e35c14f5c5d05bb;p=clitk.git diff --git a/vv/vvToolSegmentation.cxx b/vv/vvToolSegmentation.cxx index dea3513..63ff9b1 100644 --- a/vv/vvToolSegmentation.cxx +++ b/vv/vvToolSegmentation.cxx @@ -14,7 +14,7 @@ - BSD See included LICENSE.txt file - CeCILL-B http://www.cecill.info/licences/Licence_CeCILL-B_V1-en.html -===========================================================================**/ + ===========================================================================**/ // vv #include "vvToolSegmentation.h" @@ -23,14 +23,24 @@ #include "vvToolInputSelectorWidget.h" #include "vvImageWriter.h" +// clitk +#include "clitkConnectedComponentLabeling_ggo.h" +#include "clitkConnectedComponentLabelingGenericFilter.h" +#include "clitkRegionGrowing_ggo.h" +#include "clitkRegionGrowingGenericFilter.h" + // Qt #include #include // vtk +#include +#include +#include #include "vtkImageContinuousErode3D.h" #include "vtkImageContinuousDilate3D.h" - +#include "vtkRenderWindow.h" + //------------------------------------------------------------------------------ // Create the tool and automagically (I like this word) insert it in // the main window menu. @@ -64,7 +74,16 @@ vvToolSegmentation::vvToolSegmentation(vvMainWindowBase * parent, Qt::WindowFlag AddInputSelector("Select one image"); // Init + mCurrentLabelUnderMousePointer = 0.0; + mCurrentMousePositionInMM.resize(3); + // mCurrentMousePositionInPixel.resize(3); + mRefMaskImage = NULL; + mCurrentState = State_Default; mKernelValue = 3; // FIXME must be odd. If even -> not symmetrical + mDefaultLUTColor = vtkSmartPointer::New(); + mDefaultLUTColor->SetNumberOfTableValues(256); +#include "vvDefaultLut.h" + } //------------------------------------------------------------------------------ @@ -72,6 +91,7 @@ vvToolSegmentation::vvToolSegmentation(vvMainWindowBase * parent, Qt::WindowFlag //------------------------------------------------------------------------------ vvToolSegmentation::~vvToolSegmentation() { + DD("destructor"); } //------------------------------------------------------------------------------ @@ -79,7 +99,14 @@ vvToolSegmentation::~vvToolSegmentation() //------------------------------------------------------------------------------ bool vvToolSegmentation::close() { - mRefMaskActor->RemoveActors(); + DD("remo ref"); + if (mRefMaskActor) mRefMaskActor->RemoveActors(); + DD("remo mask"); + if (mCurrentMaskActor) mCurrentMaskActor->RemoveActors(); + for(int i=0; iRemoveActors(); + } + DD("wclose"); QWidget::close(); mCurrentSlicerManager->Render(); return true; @@ -106,6 +133,21 @@ void vvToolSegmentation::InputIsSelected(vvSlicerManager * m) // Open mask OpenBinaryImage(); + + // If cancel: stop + if (mRefMaskActor == NULL) { + close(); + return; + } + + // Update gui + mToolInputSelectionWidget->hide(); + + // Connect mouse position + connect(mCurrentSlicerManager, SIGNAL(MousePositionUpdatedSignal(int)), + this, SLOT(MousePositionChanged(int))); + connect(mCurrentSlicerManager, SIGNAL(KeyPressedSignal(std::string)), + this, SLOT(KeyPressed(std::string))); } //------------------------------------------------------------------------------ @@ -149,69 +191,230 @@ void vvToolSegmentation::OpenBinaryImage() return; } - mMaskImage = reader->GetOutput(); - int dim = mMaskImage->GetNumberOfDimensions(); + mRefMaskImage = reader->GetOutput(); + int dim = mRefMaskImage->GetNumberOfDimensions(); if (dim != 3 ) { QMessageBox::information(this,tr("Sorry only 3D yet"), tr("Sorry only 3D yet")); close(); return; } - // Add a new roi actor - mRefMaskActor = QSharedPointer(new vvROIActor); - std::vector color; - color.push_back(1); - color.push_back(0); - color.push_back(0); - clitk::DicomRT_ROI::Pointer roi = clitk::DicomRT_ROI::New(); - roi->SetFromBinaryImage(mMaskImage, 1, std::string("toto"), color, filename.toStdString()); - mRefMaskActor->SetBGMode(true); - mRefMaskActor->SetROI(roi); - mRefMaskActor->SetSlicerManager(mCurrentSlicerManager); - mRefMaskActor->Initialize(10, true); + reader = vvImageReader::New(); + reader->SetInputFilenames(filenames); + reader->Update(vvImageReader::IMAGE); + mCurrentMaskImage = reader->GetOutput(); + + // Add a new roi actor for the current mask + mCurrentMaskActor = CreateMaskActor(mCurrentMaskImage, 1, 0, false); + mCurrentMaskActor->Update(); // default color is red + UpdateMaskSize(mCurrentMaskImage, mCurrentMaskSizeInPixels, mCurrentMaskSizeInCC); + + // Add a mask actor for the reference + mRefMaskActor = CreateMaskActor(mRefMaskImage, 0, 1, true); + mRefMaskActor->SetContourVisible(true); + mRefMaskActor->SetVisible(false); + mRefMaskActor->SetContourColor(0,1,0); // green contour + mRefMaskActor->UpdateColor(); mRefMaskActor->Update(); + UpdateMaskSize(mRefMaskImage, mRefMaskSizeInPixels, mRefMaskSizeInCC); - // Prepare widget to get keyboard event - grabKeyboard(); - //connect(this, SIGNAL(keyPressEvent(QKeyEvent*)), this, SLOT(keyPressed(QKeyEvent*))); + // Update GUI + UpdateMaskSizeLabels(); } //------------------------------------------------------------------------------ //------------------------------------------------------------------------------ -void vvToolSegmentation::keyPressEvent(QKeyEvent * event) +void vvToolSegmentation::UpdateMaskSizeLabels() { - vvToolWidgetBase::keyPressEvent(event); - //DD("key"); - - if (event->text() == "e") { + QString s("%1 pix (%2 cm3)"); + s = s.arg(mRefMaskSizeInPixels).arg(mRefMaskSizeInCC); + mRefMaskSizeLabel->setText(s); + QString s2("%1 pix (%2 cm3)"); + s2 = s2.arg(mCurrentMaskSizeInPixels).arg(mCurrentMaskSizeInCC); + mCurrentMaskSizeLabel->setText(s2); +} +//------------------------------------------------------------------------------ + + +//------------------------------------------------------------------------------ +void vvToolSegmentation::UpdateMaskSize(vvImage::Pointer image, long & pix, double & cc) +{ + pix = ComputeNumberOfPixels(image, GetForegroundValue()); + double vol = image->GetSpacing()[0]*image->GetSpacing()[1]*image->GetSpacing()[2]; + cc = pix * vol / (10*10*10); +} +//------------------------------------------------------------------------------ + + +//------------------------------------------------------------------------------ +long vvToolSegmentation::ComputeNumberOfPixels(vvImage::Pointer image, double value) +{ + int n=0; + vtkImageData * im = image->GetFirstVTKImageData(); + char * pPix = (char*)im->GetScalarPointer(); // FIXME char ? + for(uint i=0; iGetNumberOfPoints(); i++) { + if (pPix[i] == value) n++; + } + DD(n); + return n; +} +//------------------------------------------------------------------------------ + + +//------------------------------------------------------------------------------ +void vvToolSegmentation::KeyPressed(std::string KeyPress) +{ + if (KeyPress == "G") { + RegionGrowing(); + } + if (KeyPress == "e") { Erode(); } - if (event->text() == "d") { + if (KeyPress == "d") { Dilate(); // FIXME -> extend image BB !! } - if (event->text() == "s") { + if (KeyPress == "L") { + Labelize(); + } + if (KeyPress == "m") { + Merge(); + UpdateAndRenderNewMask(); + } + if (KeyPress == "s") { // Supress "Remove" one label + if (mCurrentState == State_CCL) RemoveLabel(); + } + if (KeyPress == "t") { // display remove ref contour + mRefMaskActor->SetContourVisible(!mRefMaskActor->IsContourVisible()); + mRefMaskActor->UpdateColor(); + mCurrentSlicerManager->Render(); + } + if (KeyPress == "w") { vvImageWriter::Pointer writer = vvImageWriter::New(); writer->SetOutputFileName("a.mha"); - writer->SetInput(mMaskImage); + writer->SetInput(mCurrentMaskImage); writer->Update(); } } //------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ +void vvToolSegmentation::RegionGrowing() +{ + DD("RegionGrowing"); + QApplication::setOverrideCursor(QCursor(Qt::WaitCursor)); + // Merge labels if needed + Merge(); + // Get mouse location + DD(mCurrentLabelUnderMousePointer); + DDV(mCurrentMousePositionInMM, 3); + // DDV(mCurrentMousePositionInPixel, 3); + + // Build RG filter parameters + typedef args_info_clitkRegionGrowing ArgsInfoType; + ArgsInfoType a; + cmdline_parser_clitkRegionGrowing_init(&a); + // FIXME parameters + a.type_arg = 4; // FIXME set by gui + DD(a.lower_arg); + a.lower_arg = 200; + a.upper_arg = 3000; + a.seed_given = 3; + a.seed_arg = new int[3]; + + DDV(mCurrentMousePositionInMM, 3); + vtkImageData * image = mCurrentImage->GetFirstVTKImageData(); + double x = (mCurrentMousePositionInMM[0] - image->GetOrigin()[0]) / image->GetSpacing()[0]; + double y = (mCurrentMousePositionInMM[1] - image->GetOrigin()[1]) / image->GetSpacing()[1]; + double z = (mCurrentMousePositionInMM[2] - image->GetOrigin()[2]) / image->GetSpacing()[2]; + a.seed_arg[0] = x; + a.seed_arg[1] = y; + a.seed_arg[2] = z; + a.verbose_flag = 1; + + // Build RG filter parameters + typedef clitk::RegionGrowingGenericFilter FilterType; + FilterType::Pointer filter = FilterType::New(); + filter->SetArgsInfo(a); + filter->SetInputVVImage(mCurrentImage); + filter->SetIOVerbose(true); + filter->Update(); + mCurrentMaskImage = filter->GetOutputVVImage(); + DD("filter done"); + + mCurrentMaskActor->RemoveActors(); + mCurrentMaskActor = CreateMaskActor(mCurrentMaskImage, 1, 0, false); + mCurrentMaskActor->Update(); // default color is red + UpdateAndRenderNewMask(); + UpdateMaskSizeLabels(); + DD("end"); + + // mouse pointer + QApplication::restoreOverrideCursor(); +} +//------------------------------------------------------------------------------ + + +//------------------------------------------------------------------------------ +void vvToolSegmentation::Merge() +{ + if (mCurrentState != State_CCL) return; + + DD("Merge"); + // Remove actors + for(int i=0; iSetVisible(false); + mCurrentCCLActors[i]->RemoveActors(); + } + } + mCurrentCCLActors.clear(); + + // Compute new mask + vtkImageData * ccl = mCurrentCCLImage->GetFirstVTKImageData(); + vtkImageData * mask = mCurrentMaskImage->GetFirstVTKImageData(); + int * pCCL = (int*)ccl->GetScalarPointer(); + char * pPix = (char*)mask->GetScalarPointer(); + for(uint i=0; iGetNumberOfPoints(); i++) { + if (pCCL[i] == 0) pPix[i] = GetBackgroundValue(); // copy BG. In CCL BG is always 0 + } + + // Display new mask and remove ccl + mCurrentCCLImage->Reset(); + mCurrentMaskActor->RemoveActors(); // kill it + mCurrentMaskActor = CreateMaskActor(mCurrentMaskImage, 1, 0, false); // renew + mCurrentMaskActor->Update(); + mCurrentMaskActor->SetVisible(true); + // mCurrentSlicerManager->Render(); + mCurrentState = State_Default; +} +//------------------------------------------------------------------------------ + + //------------------------------------------------------------------------------ void vvToolSegmentation::Erode() { QApplication::setOverrideCursor(QCursor(Qt::WaitCursor)); + // Merge labels if needed + Merge(); + // Get image and start erosion vtkImageContinuousErode3D* erode = vtkImageContinuousErode3D::New(); erode->SetKernelSize(mKernelValue,mKernelValue,mKernelValue); - vtkImageData* image = mMaskImage->GetVTKImages()[0]; + vtkImageData* image = mCurrentMaskImage->GetVTKImages()[0]; +#if VTK_MAJOR_VERSION <= 5 erode->SetInput(image); +#else + erode->SetInputData(image); +#endif erode->Update(); image->DeepCopy(erode->GetOutput()); +#if VTK_MAJOR_VERSION <= 5 image->Update(); - UpdateAndRender(); +#else + //image->Update(); +#endif + UpdateAndRenderNewMask(); erode->Delete(); QApplication::restoreOverrideCursor(); } @@ -222,14 +425,25 @@ void vvToolSegmentation::Erode() void vvToolSegmentation::Dilate() { QApplication::setOverrideCursor(QCursor(Qt::WaitCursor)); + // Merge labels if needed + Merge(); + // Get image and start dilatation vtkImageContinuousDilate3D* dilate = vtkImageContinuousDilate3D::New(); dilate->SetKernelSize(mKernelValue,mKernelValue,mKernelValue); - vtkImageData* image = mMaskImage->GetVTKImages()[0]; + vtkImageData* image = mCurrentMaskImage->GetVTKImages()[0]; +#if VTK_MAJOR_VERSION <= 5 dilate->SetInput(image); +#else + dilate->SetInputData(image); +#endif dilate->Update(); image->DeepCopy(dilate->GetOutput()); +#if VTK_MAJOR_VERSION <= 5 image->Update(); - UpdateAndRender(); +#else + //image->Update(); +#endif + UpdateAndRenderNewMask(); dilate->Delete(); QApplication::restoreOverrideCursor(); } @@ -237,18 +451,200 @@ void vvToolSegmentation::Dilate() //------------------------------------------------------------------------------ -void vvToolSegmentation::UpdateAndRender() +void vvToolSegmentation::UpdateAndRenderNewMask() { - bool visible = mRefMaskActor->IsVisible(); - bool cvisible = mRefMaskActor->IsContourVisible(); - mRefMaskActor->SetVisible(false); - mRefMaskActor->SetContourVisible(false); + bool visible = mCurrentMaskActor->IsVisible(); + bool cvisible = mCurrentMaskActor->IsContourVisible(); + mCurrentMaskActor->SetVisible(false); + mCurrentMaskActor->SetContourVisible(false); + mCurrentMaskActor->UpdateImage(); + mCurrentMaskActor->SetVisible(visible); + mCurrentMaskActor->SetContourVisible(cvisible); + mCurrentSlicerManager->Render(); + UpdateMaskSize(mCurrentMaskImage, mCurrentMaskSizeInPixels, mCurrentMaskSizeInCC); + UpdateMaskSizeLabels(); +} +//------------------------------------------------------------------------------ + +//------------------------------------------------------------------------------ +void vvToolSegmentation::Labelize() +{ + if (mCurrentState == State_CCL) return; // Do nothing in this case + DD("Labelize"); + // Waiting cursos + QApplication::setOverrideCursor(QCursor(Qt::WaitCursor)); + mCurrentMaskActor->SetVisible(false); + + // Build CCL filter + vtkImageData* image = mCurrentMaskImage->GetVTKImages()[0]; + typedef args_info_clitkConnectedComponentLabeling ArgsInfoType; + ArgsInfoType a; + cmdline_parser_clitkConnectedComponentLabeling_init(&a); + a.inputBG_arg = GetBackgroundValue(); + a.full_flag = false; // FIXME set by gui + a.minSize_arg = 100; // FIXME set by gui + typedef clitk::ConnectedComponentLabelingGenericFilter FilterType; + FilterType::Pointer filter = FilterType::New(); + filter->SetArgsInfo(a); + filter->SetInputVVImage(mCurrentMaskImage); // FIXME Check type is ok ? convert float ? + filter->SetIOVerbose(true); + filter->Update(); + DD(filter->GetOriginalNumberOfObjects()); + DD(filter->GetSizeOfObjectsInPixels().size()); + mCurrentCCLImage = filter->GetOutputVVImage(); + DDV(filter->GetSizeOfObjectsInPixels(), filter->GetSizeOfObjectsInPixels().size()); + DD("filter done"); + + /* + // DEBUG + vvImageWriter::Pointer writer = vvImageWriter::New(); + writer->SetInput(mCurrentCCLImage); + writer->SetOutputFileName("bidon-ccl.mha"); + writer->Update(); + DD(mCurrentCCLImage->IsScalarTypeInteger()); + */ + + // Create actors + int n = filter->GetSizeOfObjectsInPixels().size(); + mCurrentCCLActors.clear(); + for(int i=1; i<=std::min(n,10); i++) { // Start at 1 because 0 is BG. FIXME max by gui + QSharedPointer actor = CreateMaskActor(mCurrentCCLImage, i, i+1, false); + mCurrentCCLActors.push_back( actor ); + actor->Update(); + } + // mCurrentMaskActor->Update(); + mCurrentSlicerManager->Render(); + + // UpdateAndRender(); + mCurrentState = State_CCL; + QApplication::restoreOverrideCursor(); +} +//------------------------------------------------------------------------------ + +//------------------------------------------------------------------------------ +QSharedPointer vvToolSegmentation::CreateMaskActor(vvImage::Pointer image, int i, int colorID, bool BGMode) +{ + static int depth = 1; + depth += 1; + QSharedPointer actor = QSharedPointer(new vvROIActor); + double * color = mDefaultLUTColor->GetTableValue(colorID % mDefaultLUTColor->GetNumberOfTableValues ()); + std::vector c; + c.push_back(color[0]); + c.push_back(color[1]); + c.push_back(color[2]); + clitk::DicomRT_ROI::Pointer roi = clitk::DicomRT_ROI::New(); + roi->SetFromBinaryImage(image, i, std::string("toto"), c, std::string("titi")); + if (BGMode) { + actor->SetBGMode(true); + } + else { + roi->SetForegroundValueLabelImage(i); // FG mode + actor->SetBGMode(false); // FG mode + } + actor->SetROI(roi); + actor->SetSlicerManager(mCurrentSlicerManager); + actor->Initialize(depth+i, true); // +1 to start at 1 not 0 + actor->SetContourVisible(false); + actor->SetVisible(true); + return actor; +} +//------------------------------------------------------------------------------ + + +//------------------------------------------------------------------------------ +void vvToolSegmentation::MousePositionChanged(int slicer) +{ + // DD("MousePositionChanged "); + // DD(slicer); + double x = mCurrentSlicerManager->GetSlicer(slicer)->GetCurrentPosition()[0]; + double y = mCurrentSlicerManager->GetSlicer(slicer)->GetCurrentPosition()[1]; + double z = mCurrentSlicerManager->GetSlicer(slicer)->GetCurrentPosition()[2]; + mCurrentMousePositionInMM[0] = x; + mCurrentMousePositionInMM[1] = y; + mCurrentMousePositionInMM[2] = z; + // DDV(mCurrentMousePositionInMM, 3); + + // vtkImageData * image = mCurrentCCLImage->GetFirstVTKImageData(); + vtkImageData * image = mCurrentMaskImage->GetFirstVTKImageData(); + double Xover = (x - image->GetOrigin()[0]) / image->GetSpacing()[0]; + double Yover = (y - image->GetOrigin()[1]) / image->GetSpacing()[1]; + double Zover = (z - image->GetOrigin()[2]) / image->GetSpacing()[2]; + int ix, iy, iz; + + // mCurrentMousePositionInPixel[0] = Xover; + // mCurrentMousePositionInPixel[1] = Yover; + // mCurrentMousePositionInPixel[2] = Zover; + // DDV(mCurrentMousePositionInPixel, 3); +#if VTK_MAJOR_VERSION <= 5 + if (Xover >= image->GetWholeExtent()[0] && + Xover <= image->GetWholeExtent()[1] && + Yover >= image->GetWholeExtent()[2] && + Yover <= image->GetWholeExtent()[3] && + Zover >= image->GetWholeExtent()[4] && + Zover <= image->GetWholeExtent()[5]) { + if (mCurrentState == State_Default) { // inside the mask + mCurrentLabelUnderMousePointer = 1; + return; + } + else { // inside the label image + vtkImageData * image = mCurrentCCLImage->GetFirstVTKImageData(); + mCurrentLabelUnderMousePointer = + mCurrentSlicerManager->GetSlicer(0)->GetScalarComponentAsDouble(image, Xover, Yover, Zover, ix, iy, iz, 0); + return; + } + } + else { + // DD("out of mask"); + mCurrentLabelUnderMousePointer = 0; // BG is always 0 in CCL + } +#else + if (Xover >= image->GetInformation()->Get(vtkDataObject::DATA_EXTENT())[0] && + Xover <= image->GetInformation()->Get(vtkDataObject::DATA_EXTENT())[1] && + Yover >= image->GetInformation()->Get(vtkDataObject::DATA_EXTENT())[2] && + Yover <= image->GetInformation()->Get(vtkDataObject::DATA_EXTENT())[3] && + Zover >= image->GetInformation()->Get(vtkDataObject::DATA_EXTENT())[4] && + Zover <= image->GetInformation()->Get(vtkDataObject::DATA_EXTENT())[5]) { + if (mCurrentState == State_Default) { // inside the mask + mCurrentLabelUnderMousePointer = 1; + return; + } + else { // inside the label image + vtkImageData * image = mCurrentCCLImage->GetFirstVTKImageData(); + mCurrentLabelUnderMousePointer = + mCurrentSlicerManager->GetSlicer(0)->GetScalarComponentAsDouble(image, Xover, Yover, Zover, ix, iy, iz, 0); + return; + } + } + else { + // DD("out of mask"); + mCurrentLabelUnderMousePointer = 0; // BG is always 0 in CCL + } +#endif +} +//------------------------------------------------------------------------------ - //mRefMaskActor->RemoveActors(); - mRefMaskActor->UpdateImage(); - mRefMaskActor->SetVisible(visible); - mRefMaskActor->SetContourVisible(cvisible); + +//------------------------------------------------------------------------------ +void vvToolSegmentation::RemoveLabel() { + DD("RemoveLabel"); + if (mCurrentLabelUnderMousePointer == 0) return; + // First actor=0 and is label 1. Label 0 is not an actor, it is BG + int actorNumber = mCurrentLabelUnderMousePointer-1; + // Set actor invisible + mCurrentCCLActors[actorNumber]->SetVisible(false); mCurrentSlicerManager->Render(); + // Set image label + vtkImageData * image = mCurrentCCLImage->GetFirstVTKImageData(); + int * pPix = (int*)image->GetScalarPointer(); + int n = 0; + for(uint i=0; iGetNumberOfPoints(); i++) { + if (pPix[i] == mCurrentLabelUnderMousePointer) pPix[i] = 0; + if (pPix[i] != 0) n++; // count the number of pixels in the foreground + } + // Update mask size + mCurrentMaskSizeInPixels = n; + mCurrentMaskSizeInCC = mCurrentMaskImage->GetSpacing()[0] * mCurrentMaskImage->GetSpacing()[1] * mCurrentMaskImage->GetSpacing()[2] * n / (10*10*10); + UpdateMaskSizeLabels(); } //------------------------------------------------------------------------------