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 "vtkImageContinuousErode3D.h"
38 #include "vtkImageContinuousDilate3D.h"
39 #include "vtkRenderWindow.h"
41 //------------------------------------------------------------------------------
42 // Create the tool and automagically (I like this word) insert it in
43 // the main window menu.
44 ADD_TOOL(vvToolSegmentation);
45 //------------------------------------------------------------------------------
48 //------------------------------------------------------------------------------
49 void vvToolSegmentation::Initialize()
51 SetToolName("Segmentation");
52 SetToolMenuName("Interactive Segmentation");
53 SetToolIconFilename(":/common/icons/ducky.ico");
54 SetToolTip("Image interactive segmentation (trial).");
55 SetToolExperimental(true);
57 //------------------------------------------------------------------------------
60 //------------------------------------------------------------------------------
61 vvToolSegmentation::vvToolSegmentation(vvMainWindowBase * parent, Qt::WindowFlags f)
62 :vvToolWidgetBase(parent,f),
63 vvToolBase<vvToolSegmentation>(parent),
64 Ui::vvToolSegmentation()
67 Ui_vvToolSegmentation::setupUi(mToolWidget);
68 setAttribute(Qt::WA_DeleteOnClose);
70 // Set how many inputs are needed for this tool
71 AddInputSelector("Select one image");
74 mCurrentLabelUnderMousePointer = 0.0;
75 mCurrentMousePositionInMM.resize(3);
76 // mCurrentMousePositionInPixel.resize(3);
78 mCurrentState = State_Default;
79 mKernelValue = 3; // FIXME must be odd. If even -> not symmetrical
80 mDefaultLUTColor = vtkSmartPointer<vtkLookupTable>::New();
81 mDefaultLUTColor->SetNumberOfTableValues(256);
82 #include "vvDefaultLut.h"
85 //------------------------------------------------------------------------------
88 //------------------------------------------------------------------------------
89 vvToolSegmentation::~vvToolSegmentation()
93 //------------------------------------------------------------------------------
96 //------------------------------------------------------------------------------
97 bool vvToolSegmentation::close()
100 if (mRefMaskActor) mRefMaskActor->RemoveActors();
102 if (mCurrentMaskActor) mCurrentMaskActor->RemoveActors();
103 for(int i=0; i<mCurrentCCLActors.size(); i++) {
104 if (mCurrentCCLActors[i]) mCurrentCCLActors[i]->RemoveActors();
108 mCurrentSlicerManager->Render();
111 //------------------------------------------------------------------------------
114 //------------------------------------------------------------------------------
115 void vvToolSegmentation::InputIsSelected(vvSlicerManager * m)
117 DD("InputIsSelected");
118 mCurrentSlicerManager = m;
119 mCurrentImage = mCurrentSlicerManager->GetImage();
121 // Refuse if non 3D image
122 if (mCurrentImage->GetNumberOfDimensions() != 3) {
123 QMessageBox::information(this,tr("Sorry only 3D yet"), tr("Sorry only 3D yet"));
129 //mLabelInputInfo->setText(QString("%1").arg(m->GetFileName().c_str()));
135 if (mRefMaskActor == NULL) {
141 mToolInputSelectionWidget->hide();
143 // Connect mouse position
144 connect(mCurrentSlicerManager, SIGNAL(MousePositionUpdatedSignal(int)),
145 this, SLOT(MousePositionChanged(int)));
146 connect(mCurrentSlicerManager, SIGNAL(KeyPressedSignal(std::string)),
147 this, SLOT(KeyPressed(std::string)));
149 //------------------------------------------------------------------------------
152 //------------------------------------------------------------------------------
153 void vvToolSegmentation::apply()
157 //------------------------------------------------------------------------------
160 //------------------------------------------------------------------------------
161 void vvToolSegmentation::OpenBinaryImage()
163 DD("OpenBinaryImage");
165 // Load browser and select image
166 QString Extensions = "Images files ( *.mha *.mhd *.hdr *.his)";
167 Extensions += ";;All Files (*)";
169 QFileDialog::getOpenFileName(this,tr("Open binary image"),
170 mMainWindowBase->GetInputPathName(),Extensions);
171 DD(filename.toStdString());
172 if (filename.size() == 0) return;
175 QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
176 vvImageReader::Pointer reader = vvImageReader::New();
177 std::vector<std::string> filenames;
178 filenames.push_back(filename.toStdString());
179 reader->SetInputFilenames(filenames);
180 reader->Update(vvImageReader::IMAGE);
181 QApplication::restoreOverrideCursor();
183 if (reader->GetLastError().size() != 0) {
184 std::cerr << "Error while reading " << filename.toStdString() << std::endl;
185 QString error = "Cannot open file \n";
186 error += reader->GetLastError().c_str();
187 QMessageBox::information(this,tr("Reading problem"),error);
191 mRefMaskImage = reader->GetOutput();
192 int dim = mRefMaskImage->GetNumberOfDimensions();
194 QMessageBox::information(this,tr("Sorry only 3D yet"), tr("Sorry only 3D yet"));
199 reader = vvImageReader::New();
200 reader->SetInputFilenames(filenames);
201 reader->Update(vvImageReader::IMAGE);
202 mCurrentMaskImage = reader->GetOutput();
204 // Add a new roi actor for the current mask
205 mCurrentMaskActor = CreateMaskActor(mCurrentMaskImage, 1, 0, false);
206 mCurrentMaskActor->Update(); // default color is red
207 UpdateMaskSize(mCurrentMaskImage, mCurrentMaskSizeInPixels, mCurrentMaskSizeInCC);
209 // Add a mask actor for the reference
210 mRefMaskActor = CreateMaskActor(mRefMaskImage, 0, 1, true);
211 mRefMaskActor->SetContourVisible(true);
212 mRefMaskActor->SetVisible(false);
213 mRefMaskActor->SetContourColor(0,1,0); // green contour
214 mRefMaskActor->UpdateColor();
215 mRefMaskActor->Update();
216 UpdateMaskSize(mRefMaskImage, mRefMaskSizeInPixels, mRefMaskSizeInCC);
219 UpdateMaskSizeLabels();
221 //------------------------------------------------------------------------------
224 //------------------------------------------------------------------------------
225 void vvToolSegmentation::UpdateMaskSizeLabels()
227 QString s("%1 pix (%2 cm3)");
228 s = s.arg(mRefMaskSizeInPixels).arg(mRefMaskSizeInCC);
229 mRefMaskSizeLabel->setText(s);
230 QString s2("%1 pix (%2 cm3)");
231 s2 = s2.arg(mCurrentMaskSizeInPixels).arg(mCurrentMaskSizeInCC);
232 mCurrentMaskSizeLabel->setText(s2);
234 //------------------------------------------------------------------------------
237 //------------------------------------------------------------------------------
238 void vvToolSegmentation::UpdateMaskSize(vvImage::Pointer image, long & pix, double & cc)
240 pix = ComputeNumberOfPixels(image, GetForegroundValue());
241 double vol = image->GetSpacing()[0]*image->GetSpacing()[1]*image->GetSpacing()[2];
242 cc = pix * vol / (10*10*10);
244 //------------------------------------------------------------------------------
247 //------------------------------------------------------------------------------
248 long vvToolSegmentation::ComputeNumberOfPixels(vvImage::Pointer image, double value)
251 vtkImageData * im = image->GetFirstVTKImageData();
252 char * pPix = (char*)im->GetScalarPointer(); // FIXME char ?
253 for(uint i=0; i<im->GetNumberOfPoints(); i++) {
254 if (pPix[i] == value) n++;
259 //------------------------------------------------------------------------------
262 //------------------------------------------------------------------------------
263 void vvToolSegmentation::KeyPressed(std::string KeyPress)
265 if (KeyPress == "G") {
268 if (KeyPress == "e") {
271 if (KeyPress == "d") {
272 Dilate(); // FIXME -> extend image BB !!
274 if (KeyPress == "L") {
277 if (KeyPress == "m") {
279 UpdateAndRenderNewMask();
281 if (KeyPress == "s") { // Supress "Remove" one label
282 if (mCurrentState == State_CCL) RemoveLabel();
284 if (KeyPress == "t") { // display remove ref contour
285 mRefMaskActor->SetContourVisible(!mRefMaskActor->IsContourVisible());
286 mRefMaskActor->UpdateColor();
287 mCurrentSlicerManager->Render();
289 if (KeyPress == "w") {
290 vvImageWriter::Pointer writer = vvImageWriter::New();
291 writer->SetOutputFileName("a.mha");
292 writer->SetInput(mCurrentMaskImage);
296 //------------------------------------------------------------------------------
299 //------------------------------------------------------------------------------
300 void vvToolSegmentation::RegionGrowing()
303 QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
304 // Merge labels if needed
306 // Get mouse location
307 DD(mCurrentLabelUnderMousePointer);
308 DDV(mCurrentMousePositionInMM, 3);
309 // DDV(mCurrentMousePositionInPixel, 3);
311 // Build RG filter parameters
312 typedef args_info_clitkRegionGrowing ArgsInfoType;
314 cmdline_parser_clitkRegionGrowing_init(&a);
316 a.type_arg = 4; // FIXME set by gui
321 a.seed_arg = new int[3];
323 DDV(mCurrentMousePositionInMM, 3);
324 vtkImageData * image = mCurrentImage->GetFirstVTKImageData();
325 double x = (mCurrentMousePositionInMM[0] - image->GetOrigin()[0]) / image->GetSpacing()[0];
326 double y = (mCurrentMousePositionInMM[1] - image->GetOrigin()[1]) / image->GetSpacing()[1];
327 double z = (mCurrentMousePositionInMM[2] - image->GetOrigin()[2]) / image->GetSpacing()[2];
333 // Build RG filter parameters
334 typedef clitk::RegionGrowingGenericFilter<args_info_clitkRegionGrowing> FilterType;
335 FilterType::Pointer filter = FilterType::New();
336 filter->SetArgsInfo(a);
337 filter->SetInputVVImage(mCurrentImage);
338 filter->SetIOVerbose(true);
340 mCurrentMaskImage = filter->GetOutputVVImage();
343 mCurrentMaskActor->RemoveActors();
344 mCurrentMaskActor = CreateMaskActor(mCurrentMaskImage, 1, 0, false);
345 mCurrentMaskActor->Update(); // default color is red
346 UpdateAndRenderNewMask();
347 UpdateMaskSizeLabels();
351 QApplication::restoreOverrideCursor();
353 //------------------------------------------------------------------------------
356 //------------------------------------------------------------------------------
357 void vvToolSegmentation::Merge()
359 if (mCurrentState != State_CCL) return;
363 for(int i=0; i<mCurrentCCLActors.size(); i++) {
364 if (mCurrentCCLActors[i]) {
365 mCurrentCCLActors[i]->SetVisible(false);
366 mCurrentCCLActors[i]->RemoveActors();
369 mCurrentCCLActors.clear();
372 vtkImageData * ccl = mCurrentCCLImage->GetFirstVTKImageData();
373 vtkImageData * mask = mCurrentMaskImage->GetFirstVTKImageData();
374 int * pCCL = (int*)ccl->GetScalarPointer();
375 char * pPix = (char*)mask->GetScalarPointer();
376 for(uint i=0; i<ccl->GetNumberOfPoints(); i++) {
377 if (pCCL[i] == 0) pPix[i] = GetBackgroundValue(); // copy BG. In CCL BG is always 0
380 // Display new mask and remove ccl
381 mCurrentCCLImage->Reset();
382 mCurrentMaskActor->RemoveActors(); // kill it
383 mCurrentMaskActor = CreateMaskActor(mCurrentMaskImage, 1, 0, false); // renew
384 mCurrentMaskActor->Update();
385 mCurrentMaskActor->SetVisible(true);
386 // mCurrentSlicerManager->Render();
387 mCurrentState = State_Default;
389 //------------------------------------------------------------------------------
392 //------------------------------------------------------------------------------
393 void vvToolSegmentation::Erode()
395 QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
396 // Merge labels if needed
398 // Get image and start erosion
399 vtkImageContinuousErode3D* erode = vtkImageContinuousErode3D::New();
400 erode->SetKernelSize(mKernelValue,mKernelValue,mKernelValue);
401 vtkImageData* image = mCurrentMaskImage->GetVTKImages()[0];
402 erode->SetInput(image);
404 image->DeepCopy(erode->GetOutput());
406 UpdateAndRenderNewMask();
408 QApplication::restoreOverrideCursor();
410 //------------------------------------------------------------------------------
413 //------------------------------------------------------------------------------
414 void vvToolSegmentation::Dilate()
416 QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
417 // Merge labels if needed
419 // Get image and start dilatation
420 vtkImageContinuousDilate3D* dilate = vtkImageContinuousDilate3D::New();
421 dilate->SetKernelSize(mKernelValue,mKernelValue,mKernelValue);
422 vtkImageData* image = mCurrentMaskImage->GetVTKImages()[0];
423 dilate->SetInput(image);
425 image->DeepCopy(dilate->GetOutput());
427 UpdateAndRenderNewMask();
429 QApplication::restoreOverrideCursor();
431 //------------------------------------------------------------------------------
434 //------------------------------------------------------------------------------
435 void vvToolSegmentation::UpdateAndRenderNewMask()
437 bool visible = mCurrentMaskActor->IsVisible();
438 bool cvisible = mCurrentMaskActor->IsContourVisible();
439 mCurrentMaskActor->SetVisible(false);
440 mCurrentMaskActor->SetContourVisible(false);
441 mCurrentMaskActor->UpdateImage();
442 mCurrentMaskActor->SetVisible(visible);
443 mCurrentMaskActor->SetContourVisible(cvisible);
445 mCurrentSlicerManager->Render();
446 UpdateMaskSize(mCurrentMaskImage, mCurrentMaskSizeInPixels, mCurrentMaskSizeInCC);
447 UpdateMaskSizeLabels();
449 //------------------------------------------------------------------------------
451 //------------------------------------------------------------------------------
452 void vvToolSegmentation::Labelize()
454 if (mCurrentState == State_CCL) return; // Do nothing in this case
457 QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
458 mCurrentMaskActor->SetVisible(false);
461 vtkImageData* image = mCurrentMaskImage->GetVTKImages()[0];
462 typedef args_info_clitkConnectedComponentLabeling ArgsInfoType;
464 cmdline_parser_clitkConnectedComponentLabeling_init(&a);
465 a.inputBG_arg = GetBackgroundValue();
466 a.full_flag = false; // FIXME set by gui
467 a.minSize_arg = 100; // FIXME set by gui
468 typedef clitk::ConnectedComponentLabelingGenericFilter<ArgsInfoType> FilterType;
469 FilterType::Pointer filter = FilterType::New();
470 filter->SetArgsInfo(a);
471 filter->SetInputVVImage(mCurrentMaskImage); // FIXME Check type is ok ? convert float ?
472 filter->SetIOVerbose(true);
474 DD(filter->GetOriginalNumberOfObjects());
475 DD(filter->GetSizeOfObjectsInPixels().size());
476 mCurrentCCLImage = filter->GetOutputVVImage();
477 DDV(filter->GetSizeOfObjectsInPixels(), filter->GetSizeOfObjectsInPixels().size());
482 vvImageWriter::Pointer writer = vvImageWriter::New();
483 writer->SetInput(mCurrentCCLImage);
484 writer->SetOutputFileName("bidon-ccl.mha");
486 DD(mCurrentCCLImage->IsScalarTypeInteger());
490 int n = filter->GetSizeOfObjectsInPixels().size();
491 mCurrentCCLActors.clear();
492 for(int i=1; i<=std::min(n,10); i++) { // Start at 1 because 0 is BG. FIXME max by gui
493 QSharedPointer<vvROIActor> actor = CreateMaskActor(mCurrentCCLImage, i, i+1, false);
494 mCurrentCCLActors.push_back( actor );
497 // mCurrentMaskActor->Update();
498 mCurrentSlicerManager->Render();
500 // UpdateAndRender();
501 mCurrentState = State_CCL;
502 QApplication::restoreOverrideCursor();
504 //------------------------------------------------------------------------------
506 //------------------------------------------------------------------------------
507 QSharedPointer<vvROIActor> vvToolSegmentation::CreateMaskActor(vvImage::Pointer image, int i, int colorID, bool BGMode)
509 static int depth = 1;
511 QSharedPointer<vvROIActor> actor = QSharedPointer<vvROIActor>(new vvROIActor);
512 double * color = mDefaultLUTColor->GetTableValue(colorID % mDefaultLUTColor->GetNumberOfTableValues ());
513 std::vector<double> c;
514 c.push_back(color[0]);
515 c.push_back(color[1]);
516 c.push_back(color[2]);
517 clitk::DicomRT_ROI::Pointer roi = clitk::DicomRT_ROI::New();
518 roi->SetFromBinaryImage(image, i, std::string("toto"), c, std::string("titi"));
520 actor->SetBGMode(true);
523 roi->SetForegroundValueLabelImage(i); // FG mode
524 actor->SetBGMode(false); // FG mode
527 actor->SetSlicerManager(mCurrentSlicerManager);
528 actor->Initialize(depth+i, true); // +1 to start at 1 not 0
529 actor->SetContourVisible(false);
530 actor->SetVisible(true);
533 //------------------------------------------------------------------------------
536 //------------------------------------------------------------------------------
537 void vvToolSegmentation::MousePositionChanged(int slicer)
539 // DD("MousePositionChanged ");
541 double x = mCurrentSlicerManager->GetSlicer(slicer)->GetCurrentPosition()[0];
542 double y = mCurrentSlicerManager->GetSlicer(slicer)->GetCurrentPosition()[1];
543 double z = mCurrentSlicerManager->GetSlicer(slicer)->GetCurrentPosition()[2];
544 mCurrentMousePositionInMM[0] = x;
545 mCurrentMousePositionInMM[1] = y;
546 mCurrentMousePositionInMM[2] = z;
547 // DDV(mCurrentMousePositionInMM, 3);
549 // vtkImageData * image = mCurrentCCLImage->GetFirstVTKImageData();
550 vtkImageData * image = mCurrentMaskImage->GetFirstVTKImageData();
551 double Xover = (x - image->GetOrigin()[0]) / image->GetSpacing()[0];
552 double Yover = (y - image->GetOrigin()[1]) / image->GetSpacing()[1];
553 double Zover = (z - image->GetOrigin()[2]) / image->GetSpacing()[2];
556 // mCurrentMousePositionInPixel[0] = Xover;
557 // mCurrentMousePositionInPixel[1] = Yover;
558 // mCurrentMousePositionInPixel[2] = Zover;
559 // DDV(mCurrentMousePositionInPixel, 3);
561 if (Xover >= image->GetWholeExtent()[0] &&
562 Xover <= image->GetWholeExtent()[1] &&
563 Yover >= image->GetWholeExtent()[2] &&
564 Yover <= image->GetWholeExtent()[3] &&
565 Zover >= image->GetWholeExtent()[4] &&
566 Zover <= image->GetWholeExtent()[5]) {
567 if (mCurrentState == State_Default) { // inside the mask
568 mCurrentLabelUnderMousePointer = 1;
571 else { // inside the label image
572 vtkImageData * image = mCurrentCCLImage->GetFirstVTKImageData();
573 mCurrentLabelUnderMousePointer =
574 mCurrentSlicerManager->GetSlicer(0)->GetScalarComponentAsDouble(image, Xover, Yover, Zover, ix, iy, iz, 0);
579 // DD("out of mask");
580 mCurrentLabelUnderMousePointer = 0; // BG is always 0 in CCL
583 //------------------------------------------------------------------------------
586 //------------------------------------------------------------------------------
587 void vvToolSegmentation::RemoveLabel() {
589 if (mCurrentLabelUnderMousePointer == 0) return;
590 // First actor=0 and is label 1. Label 0 is not an actor, it is BG
591 int actorNumber = mCurrentLabelUnderMousePointer-1;
592 // Set actor invisible
593 mCurrentCCLActors[actorNumber]->SetVisible(false);
594 mCurrentSlicerManager->Render();
596 vtkImageData * image = mCurrentCCLImage->GetFirstVTKImageData();
597 int * pPix = (int*)image->GetScalarPointer();
599 for(uint i=0; i<image->GetNumberOfPoints(); i++) {
600 if (pPix[i] == mCurrentLabelUnderMousePointer) pPix[i] = 0;
601 if (pPix[i] != 0) n++; // count the number of pixels in the foreground
604 mCurrentMaskSizeInPixels = n;
605 mCurrentMaskSizeInCC = mCurrentMaskImage->GetSpacing()[0] * mCurrentMaskImage->GetSpacing()[1] * mCurrentMaskImage->GetSpacing()[2] * n / (10*10*10);
606 UpdateMaskSizeLabels();
608 //------------------------------------------------------------------------------