1 /*=========================================================================
2 Program: vv http://www.creatis.insa-lyon.fr/rio/vv
5 - University of LYON http://www.universite-lyon.fr/
6 - Léon Bérard cancer center http://www.centreleonberard.fr
7 - CREATIS CNRS laboratory http://www.creatis.insa-lyon.fr
9 This software is distributed WITHOUT ANY WARRANTY; without even
10 the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
11 PURPOSE. See the copyright notices for more information.
13 It is distributed under dual licence
15 - BSD See included LICENSE.txt file
16 - CeCILL-B http://www.cecill.info/licences/Licence_CeCILL-B_V1-en.html
17 ===========================================================================**/
20 #include "vvToolSegmentation.h"
21 #include "vvSlicerManager.h"
23 #include "vvToolInputSelectorWidget.h"
24 #include "vvImageWriter.h"
27 #include "clitkConnectedComponentLabeling_ggo.h"
28 #include "clitkConnectedComponentLabelingGenericFilter.h"
29 #include "clitkRegionGrowing_ggo.h"
30 #include "clitkRegionGrowingGenericFilter.h"
33 #include <QFileDialog>
34 #include <QMessageBox>
37 #include <vtkVersion.h>
38 #include <vtkStreamingDemandDrivenPipeline.h>
39 #include <vtkInformation.h>
40 #include "vtkImageContinuousErode3D.h"
41 #include "vtkImageContinuousDilate3D.h"
42 #include "vtkRenderWindow.h"
44 //------------------------------------------------------------------------------
45 // Create the tool and automagically (I like this word) insert it in
46 // the main window menu.
47 ADD_TOOL(vvToolSegmentation);
48 //------------------------------------------------------------------------------
51 //------------------------------------------------------------------------------
52 void vvToolSegmentation::Initialize()
54 SetToolName("Segmentation");
55 SetToolMenuName("Interactive Segmentation");
56 SetToolIconFilename(":/common/icons/ducky.ico");
57 SetToolTip("Image interactive segmentation (trial).");
58 SetToolExperimental(true);
60 //------------------------------------------------------------------------------
63 //------------------------------------------------------------------------------
64 vvToolSegmentation::vvToolSegmentation(vvMainWindowBase * parent, Qt::WindowFlags f)
65 :vvToolWidgetBase(parent,f),
66 vvToolBase<vvToolSegmentation>(parent),
67 Ui::vvToolSegmentation()
70 Ui_vvToolSegmentation::setupUi(mToolWidget);
71 setAttribute(Qt::WA_DeleteOnClose);
73 // Set how many inputs are needed for this tool
74 AddInputSelector("Select one image");
77 mCurrentLabelUnderMousePointer = 0.0;
78 mCurrentMousePositionInMM.resize(3);
79 // mCurrentMousePositionInPixel.resize(3);
81 mCurrentState = State_Default;
82 mKernelValue = 3; // FIXME must be odd. If even -> not symmetrical
83 mDefaultLUTColor = vtkSmartPointer<vtkLookupTable>::New();
84 mDefaultLUTColor->SetNumberOfTableValues(256);
85 #include "vvDefaultLut.h"
88 //------------------------------------------------------------------------------
91 //------------------------------------------------------------------------------
92 vvToolSegmentation::~vvToolSegmentation()
96 //------------------------------------------------------------------------------
99 //------------------------------------------------------------------------------
100 bool vvToolSegmentation::close()
103 if (mRefMaskActor) mRefMaskActor->RemoveActors();
105 if (mCurrentMaskActor) mCurrentMaskActor->RemoveActors();
106 for(int i=0; i<mCurrentCCLActors.size(); i++) {
107 if (mCurrentCCLActors[i]) mCurrentCCLActors[i]->RemoveActors();
111 mCurrentSlicerManager->Render();
114 //------------------------------------------------------------------------------
117 //------------------------------------------------------------------------------
118 void vvToolSegmentation::InputIsSelected(vvSlicerManager * m)
120 DD("InputIsSelected");
121 mCurrentSlicerManager = m;
122 mCurrentImage = mCurrentSlicerManager->GetImage();
124 // Refuse if non 3D image
125 if (mCurrentImage->GetNumberOfDimensions() != 3) {
126 QMessageBox::information(this,tr("Sorry only 3D yet"), tr("Sorry only 3D yet"));
132 //mLabelInputInfo->setText(QString("%1").arg(m->GetFileName().c_str()));
138 if (mRefMaskActor == NULL) {
144 mToolInputSelectionWidget->hide();
146 // Connect mouse position
147 connect(mCurrentSlicerManager, SIGNAL(MousePositionUpdatedSignal(int)),
148 this, SLOT(MousePositionChanged(int)));
149 connect(mCurrentSlicerManager, SIGNAL(KeyPressedSignal(std::string)),
150 this, SLOT(KeyPressed(std::string)));
152 //------------------------------------------------------------------------------
155 //------------------------------------------------------------------------------
156 void vvToolSegmentation::apply()
160 //------------------------------------------------------------------------------
163 //------------------------------------------------------------------------------
164 void vvToolSegmentation::OpenBinaryImage()
166 DD("OpenBinaryImage");
168 // Load browser and select image
169 QString Extensions = "Images files ( *.mha *.mhd *.hdr *.his)";
170 Extensions += ";;All Files (*)";
172 QFileDialog::getOpenFileName(this,tr("Open binary image"),
173 mMainWindowBase->GetInputPathName(),Extensions);
174 DD(filename.toStdString());
175 if (filename.size() == 0) return;
178 QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
179 vvImageReader::Pointer reader = vvImageReader::New();
180 std::vector<std::string> filenames;
181 filenames.push_back(filename.toStdString());
182 reader->SetInputFilenames(filenames);
183 reader->Update(vvImageReader::IMAGE);
184 QApplication::restoreOverrideCursor();
186 if (reader->GetLastError().size() != 0) {
187 std::cerr << "Error while reading " << filename.toStdString() << std::endl;
188 QString error = "Cannot open file \n";
189 error += reader->GetLastError().c_str();
190 QMessageBox::information(this,tr("Reading problem"),error);
194 mRefMaskImage = reader->GetOutput();
195 int dim = mRefMaskImage->GetNumberOfDimensions();
197 QMessageBox::information(this,tr("Sorry only 3D yet"), tr("Sorry only 3D yet"));
202 reader = vvImageReader::New();
203 reader->SetInputFilenames(filenames);
204 reader->Update(vvImageReader::IMAGE);
205 mCurrentMaskImage = reader->GetOutput();
207 // Add a new roi actor for the current mask
208 mCurrentMaskActor = CreateMaskActor(mCurrentMaskImage, 1, 0, false);
209 mCurrentMaskActor->Update(); // default color is red
210 UpdateMaskSize(mCurrentMaskImage, mCurrentMaskSizeInPixels, mCurrentMaskSizeInCC);
212 // Add a mask actor for the reference
213 mRefMaskActor = CreateMaskActor(mRefMaskImage, 0, 1, true);
214 mRefMaskActor->SetContourVisible(true);
215 mRefMaskActor->SetVisible(false);
216 mRefMaskActor->SetContourColor(0,1,0); // green contour
217 mRefMaskActor->UpdateColor();
218 mRefMaskActor->Update();
219 UpdateMaskSize(mRefMaskImage, mRefMaskSizeInPixels, mRefMaskSizeInCC);
222 UpdateMaskSizeLabels();
224 //------------------------------------------------------------------------------
227 //------------------------------------------------------------------------------
228 void vvToolSegmentation::UpdateMaskSizeLabels()
230 QString s("%1 pix (%2 cm3)");
231 s = s.arg(mRefMaskSizeInPixels).arg(mRefMaskSizeInCC);
232 mRefMaskSizeLabel->setText(s);
233 QString s2("%1 pix (%2 cm3)");
234 s2 = s2.arg(mCurrentMaskSizeInPixels).arg(mCurrentMaskSizeInCC);
235 mCurrentMaskSizeLabel->setText(s2);
237 //------------------------------------------------------------------------------
240 //------------------------------------------------------------------------------
241 void vvToolSegmentation::UpdateMaskSize(vvImage::Pointer image, long & pix, double & cc)
243 pix = ComputeNumberOfPixels(image, GetForegroundValue());
244 double vol = image->GetSpacing()[0]*image->GetSpacing()[1]*image->GetSpacing()[2];
245 cc = pix * vol / (10*10*10);
247 //------------------------------------------------------------------------------
250 //------------------------------------------------------------------------------
251 long vvToolSegmentation::ComputeNumberOfPixels(vvImage::Pointer image, double value)
254 vtkImageData * im = image->GetFirstVTKImageData();
255 char * pPix = (char*)im->GetScalarPointer(); // FIXME char ?
256 for(uint i=0; i<im->GetNumberOfPoints(); i++) {
257 if (pPix[i] == value) n++;
262 //------------------------------------------------------------------------------
265 //------------------------------------------------------------------------------
266 void vvToolSegmentation::KeyPressed(std::string KeyPress)
268 if (KeyPress == "G") {
271 if (KeyPress == "e") {
274 if (KeyPress == "d") {
275 Dilate(); // FIXME -> extend image BB !!
277 if (KeyPress == "L") {
280 if (KeyPress == "m") {
282 UpdateAndRenderNewMask();
284 if (KeyPress == "s") { // Supress "Remove" one label
285 if (mCurrentState == State_CCL) RemoveLabel();
287 if (KeyPress == "t") { // display remove ref contour
288 mRefMaskActor->SetContourVisible(!mRefMaskActor->IsContourVisible());
289 mRefMaskActor->UpdateColor();
290 mCurrentSlicerManager->Render();
292 if (KeyPress == "w") {
293 vvImageWriter::Pointer writer = vvImageWriter::New();
294 writer->SetOutputFileName("a.mha");
295 writer->SetInput(mCurrentMaskImage);
299 //------------------------------------------------------------------------------
302 //------------------------------------------------------------------------------
303 void vvToolSegmentation::RegionGrowing()
306 QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
307 // Merge labels if needed
309 // Get mouse location
310 DD(mCurrentLabelUnderMousePointer);
311 DDV(mCurrentMousePositionInMM, 3);
312 // DDV(mCurrentMousePositionInPixel, 3);
314 // Build RG filter parameters
315 typedef args_info_clitkRegionGrowing ArgsInfoType;
317 cmdline_parser_clitkRegionGrowing_init(&a);
319 a.type_arg = 4; // FIXME set by gui
324 a.seed_arg = new int[3];
326 DDV(mCurrentMousePositionInMM, 3);
327 vtkImageData * image = mCurrentImage->GetFirstVTKImageData();
328 double x = (mCurrentMousePositionInMM[0] - image->GetOrigin()[0]) / image->GetSpacing()[0];
329 double y = (mCurrentMousePositionInMM[1] - image->GetOrigin()[1]) / image->GetSpacing()[1];
330 double z = (mCurrentMousePositionInMM[2] - image->GetOrigin()[2]) / image->GetSpacing()[2];
336 // Build RG filter parameters
337 typedef clitk::RegionGrowingGenericFilter<args_info_clitkRegionGrowing> FilterType;
338 FilterType::Pointer filter = FilterType::New();
339 filter->SetArgsInfo(a);
340 filter->SetInputVVImage(mCurrentImage);
341 filter->SetIOVerbose(true);
343 mCurrentMaskImage = filter->GetOutputVVImage();
346 mCurrentMaskActor->RemoveActors();
347 mCurrentMaskActor = CreateMaskActor(mCurrentMaskImage, 1, 0, false);
348 mCurrentMaskActor->Update(); // default color is red
349 UpdateAndRenderNewMask();
350 UpdateMaskSizeLabels();
354 QApplication::restoreOverrideCursor();
356 //------------------------------------------------------------------------------
359 //------------------------------------------------------------------------------
360 void vvToolSegmentation::Merge()
362 if (mCurrentState != State_CCL) return;
366 for(int i=0; i<mCurrentCCLActors.size(); i++) {
367 if (mCurrentCCLActors[i]) {
368 mCurrentCCLActors[i]->SetVisible(false);
369 mCurrentCCLActors[i]->RemoveActors();
372 mCurrentCCLActors.clear();
375 vtkImageData * ccl = mCurrentCCLImage->GetFirstVTKImageData();
376 vtkImageData * mask = mCurrentMaskImage->GetFirstVTKImageData();
377 int * pCCL = (int*)ccl->GetScalarPointer();
378 char * pPix = (char*)mask->GetScalarPointer();
379 for(uint i=0; i<ccl->GetNumberOfPoints(); i++) {
380 if (pCCL[i] == 0) pPix[i] = GetBackgroundValue(); // copy BG. In CCL BG is always 0
383 // Display new mask and remove ccl
384 mCurrentCCLImage->Reset();
385 mCurrentMaskActor->RemoveActors(); // kill it
386 mCurrentMaskActor = CreateMaskActor(mCurrentMaskImage, 1, 0, false); // renew
387 mCurrentMaskActor->Update();
388 mCurrentMaskActor->SetVisible(true);
389 // mCurrentSlicerManager->Render();
390 mCurrentState = State_Default;
392 //------------------------------------------------------------------------------
395 //------------------------------------------------------------------------------
396 void vvToolSegmentation::Erode()
398 QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
399 // Merge labels if needed
401 // Get image and start erosion
402 vtkImageContinuousErode3D* erode = vtkImageContinuousErode3D::New();
403 erode->SetKernelSize(mKernelValue,mKernelValue,mKernelValue);
404 vtkImageData* image = mCurrentMaskImage->GetVTKImages()[0];
405 #if VTK_MAJOR_VERSION <= 5
406 erode->SetInput(image);
408 erode->SetInputData(image);
411 image->DeepCopy(erode->GetOutput());
412 #if VTK_MAJOR_VERSION <= 5
417 UpdateAndRenderNewMask();
419 QApplication::restoreOverrideCursor();
421 //------------------------------------------------------------------------------
424 //------------------------------------------------------------------------------
425 void vvToolSegmentation::Dilate()
427 QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
428 // Merge labels if needed
430 // Get image and start dilatation
431 vtkImageContinuousDilate3D* dilate = vtkImageContinuousDilate3D::New();
432 dilate->SetKernelSize(mKernelValue,mKernelValue,mKernelValue);
433 vtkImageData* image = mCurrentMaskImage->GetVTKImages()[0];
434 #if VTK_MAJOR_VERSION <= 5
435 dilate->SetInput(image);
437 dilate->SetInputData(image);
440 image->DeepCopy(dilate->GetOutput());
441 #if VTK_MAJOR_VERSION <= 5
446 UpdateAndRenderNewMask();
448 QApplication::restoreOverrideCursor();
450 //------------------------------------------------------------------------------
453 //------------------------------------------------------------------------------
454 void vvToolSegmentation::UpdateAndRenderNewMask()
456 bool visible = mCurrentMaskActor->IsVisible();
457 bool cvisible = mCurrentMaskActor->IsContourVisible();
458 mCurrentMaskActor->SetVisible(false);
459 mCurrentMaskActor->SetContourVisible(false);
460 mCurrentMaskActor->UpdateImage();
461 mCurrentMaskActor->SetVisible(visible);
462 mCurrentMaskActor->SetContourVisible(cvisible);
464 mCurrentSlicerManager->Render();
465 UpdateMaskSize(mCurrentMaskImage, mCurrentMaskSizeInPixels, mCurrentMaskSizeInCC);
466 UpdateMaskSizeLabels();
468 //------------------------------------------------------------------------------
470 //------------------------------------------------------------------------------
471 void vvToolSegmentation::Labelize()
473 if (mCurrentState == State_CCL) return; // Do nothing in this case
476 QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
477 mCurrentMaskActor->SetVisible(false);
480 vtkImageData* image = mCurrentMaskImage->GetVTKImages()[0];
481 typedef args_info_clitkConnectedComponentLabeling ArgsInfoType;
483 cmdline_parser_clitkConnectedComponentLabeling_init(&a);
484 a.inputBG_arg = GetBackgroundValue();
485 a.full_flag = false; // FIXME set by gui
486 a.minSize_arg = 100; // FIXME set by gui
487 typedef clitk::ConnectedComponentLabelingGenericFilter<ArgsInfoType> FilterType;
488 FilterType::Pointer filter = FilterType::New();
489 filter->SetArgsInfo(a);
490 filter->SetInputVVImage(mCurrentMaskImage); // FIXME Check type is ok ? convert float ?
491 filter->SetIOVerbose(true);
493 DD(filter->GetOriginalNumberOfObjects());
494 DD(filter->GetSizeOfObjectsInPixels().size());
495 mCurrentCCLImage = filter->GetOutputVVImage();
496 DDV(filter->GetSizeOfObjectsInPixels(), filter->GetSizeOfObjectsInPixels().size());
501 vvImageWriter::Pointer writer = vvImageWriter::New();
502 writer->SetInput(mCurrentCCLImage);
503 writer->SetOutputFileName("bidon-ccl.mha");
505 DD(mCurrentCCLImage->IsScalarTypeInteger());
509 int n = filter->GetSizeOfObjectsInPixels().size();
510 mCurrentCCLActors.clear();
511 for(int i=1; i<=std::min(n,10); i++) { // Start at 1 because 0 is BG. FIXME max by gui
512 QSharedPointer<vvROIActor> actor = CreateMaskActor(mCurrentCCLImage, i, i+1, false);
513 mCurrentCCLActors.push_back( actor );
516 // mCurrentMaskActor->Update();
517 mCurrentSlicerManager->Render();
519 // UpdateAndRender();
520 mCurrentState = State_CCL;
521 QApplication::restoreOverrideCursor();
523 //------------------------------------------------------------------------------
525 //------------------------------------------------------------------------------
526 QSharedPointer<vvROIActor> vvToolSegmentation::CreateMaskActor(vvImage::Pointer image, int i, int colorID, bool BGMode)
528 static int depth = 1;
530 QSharedPointer<vvROIActor> actor = QSharedPointer<vvROIActor>(new vvROIActor);
531 double * color = mDefaultLUTColor->GetTableValue(colorID % mDefaultLUTColor->GetNumberOfTableValues ());
532 std::vector<double> c;
533 c.push_back(color[0]);
534 c.push_back(color[1]);
535 c.push_back(color[2]);
536 clitk::DicomRT_ROI::Pointer roi = clitk::DicomRT_ROI::New();
537 roi->SetFromBinaryImage(image, i, std::string("toto"), c, std::string("titi"));
539 actor->SetBGMode(true);
542 roi->SetForegroundValueLabelImage(i); // FG mode
543 actor->SetBGMode(false); // FG mode
546 actor->SetSlicerManager(mCurrentSlicerManager);
547 actor->Initialize(depth+i, true); // +1 to start at 1 not 0
548 actor->SetContourVisible(false);
549 actor->SetVisible(true);
552 //------------------------------------------------------------------------------
555 //------------------------------------------------------------------------------
556 void vvToolSegmentation::MousePositionChanged(int slicer)
558 // DD("MousePositionChanged ");
560 double x = mCurrentSlicerManager->GetSlicer(slicer)->GetCurrentPosition()[0];
561 double y = mCurrentSlicerManager->GetSlicer(slicer)->GetCurrentPosition()[1];
562 double z = mCurrentSlicerManager->GetSlicer(slicer)->GetCurrentPosition()[2];
563 mCurrentMousePositionInMM[0] = x;
564 mCurrentMousePositionInMM[1] = y;
565 mCurrentMousePositionInMM[2] = z;
566 // DDV(mCurrentMousePositionInMM, 3);
568 // vtkImageData * image = mCurrentCCLImage->GetFirstVTKImageData();
569 vtkImageData * image = mCurrentMaskImage->GetFirstVTKImageData();
570 double Xover = (x - image->GetOrigin()[0]) / image->GetSpacing()[0];
571 double Yover = (y - image->GetOrigin()[1]) / image->GetSpacing()[1];
572 double Zover = (z - image->GetOrigin()[2]) / image->GetSpacing()[2];
575 // mCurrentMousePositionInPixel[0] = Xover;
576 // mCurrentMousePositionInPixel[1] = Yover;
577 // mCurrentMousePositionInPixel[2] = Zover;
578 // DDV(mCurrentMousePositionInPixel, 3);
579 #if VTK_MAJOR_VERSION <= 5
580 if (Xover >= image->GetWholeExtent()[0] &&
581 Xover <= image->GetWholeExtent()[1] &&
582 Yover >= image->GetWholeExtent()[2] &&
583 Yover <= image->GetWholeExtent()[3] &&
584 Zover >= image->GetWholeExtent()[4] &&
585 Zover <= image->GetWholeExtent()[5]) {
586 if (mCurrentState == State_Default) { // inside the mask
587 mCurrentLabelUnderMousePointer = 1;
590 else { // inside the label image
591 vtkImageData * image = mCurrentCCLImage->GetFirstVTKImageData();
592 mCurrentLabelUnderMousePointer =
593 mCurrentSlicerManager->GetSlicer(0)->GetScalarComponentAsDouble(image, Xover, Yover, Zover, ix, iy, iz, 0);
598 // DD("out of mask");
599 mCurrentLabelUnderMousePointer = 0; // BG is always 0 in CCL
602 if (Xover >= image->GetInformation()->Get(vtkDataObject::DATA_EXTENT())[0] &&
603 Xover <= image->GetInformation()->Get(vtkDataObject::DATA_EXTENT())[1] &&
604 Yover >= image->GetInformation()->Get(vtkDataObject::DATA_EXTENT())[2] &&
605 Yover <= image->GetInformation()->Get(vtkDataObject::DATA_EXTENT())[3] &&
606 Zover >= image->GetInformation()->Get(vtkDataObject::DATA_EXTENT())[4] &&
607 Zover <= image->GetInformation()->Get(vtkDataObject::DATA_EXTENT())[5]) {
608 if (mCurrentState == State_Default) { // inside the mask
609 mCurrentLabelUnderMousePointer = 1;
612 else { // inside the label image
613 vtkImageData * image = mCurrentCCLImage->GetFirstVTKImageData();
614 mCurrentLabelUnderMousePointer =
615 mCurrentSlicerManager->GetSlicer(0)->GetScalarComponentAsDouble(image, Xover, Yover, Zover, ix, iy, iz, 0);
620 // DD("out of mask");
621 mCurrentLabelUnderMousePointer = 0; // BG is always 0 in CCL
625 //------------------------------------------------------------------------------
628 //------------------------------------------------------------------------------
629 void vvToolSegmentation::RemoveLabel() {
631 if (mCurrentLabelUnderMousePointer == 0) return;
632 // First actor=0 and is label 1. Label 0 is not an actor, it is BG
633 int actorNumber = mCurrentLabelUnderMousePointer-1;
634 // Set actor invisible
635 mCurrentCCLActors[actorNumber]->SetVisible(false);
636 mCurrentSlicerManager->Render();
638 vtkImageData * image = mCurrentCCLImage->GetFirstVTKImageData();
639 int * pPix = (int*)image->GetScalarPointer();
641 for(uint i=0; i<image->GetNumberOfPoints(); i++) {
642 if (pPix[i] == mCurrentLabelUnderMousePointer) pPix[i] = 0;
643 if (pPix[i] != 0) n++; // count the number of pixels in the foreground
646 mCurrentMaskSizeInPixels = n;
647 mCurrentMaskSizeInCC = mCurrentMaskImage->GetSpacing()[0] * mCurrentMaskImage->GetSpacing()[1] * mCurrentMaskImage->GetSpacing()[2] * n / (10*10*10);
648 UpdateMaskSizeLabels();
650 //------------------------------------------------------------------------------