]> Creatis software - clitk.git/blob - vv/vvToolSegmentation.cxx
Update mask size when label is removed.
[clitk.git] / vv / vvToolSegmentation.cxx
1 /*=========================================================================
2   Program:   vv                     http://www.creatis.insa-lyon.fr/rio/vv
3
4   Authors belong to:
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
8
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.
12
13   It is distributed under dual licence
14
15   - BSD        See included LICENSE.txt file
16   - CeCILL-B   http://www.cecill.info/licences/Licence_CeCILL-B_V1-en.html
17   ===========================================================================**/
18
19 // vv
20 #include "vvToolSegmentation.h"
21 #include "vvSlicerManager.h"
22 #include "vvSlicer.h"
23 #include "vvToolInputSelectorWidget.h"
24 #include "vvImageWriter.h"
25
26 // clitk
27 #include "clitkConnectedComponentLabeling_ggo.h"
28 #include "clitkConnectedComponentLabelingGenericFilter.h"
29
30 // Qt
31 #include <QFileDialog>
32 #include <QMessageBox>
33
34 // vtk
35 #include "vtkImageContinuousErode3D.h"
36 #include "vtkImageContinuousDilate3D.h"
37 #include "vtkRenderWindow.h"
38
39 //------------------------------------------------------------------------------
40 // Create the tool and automagically (I like this word) insert it in
41 // the main window menu.
42 ADD_TOOL(vvToolSegmentation);
43 //------------------------------------------------------------------------------
44
45
46 //------------------------------------------------------------------------------
47 void vvToolSegmentation::Initialize()
48 {
49   SetToolName("Segmentation");
50   SetToolMenuName("Interactive Segmentation");
51   SetToolIconFilename(":/common/icons/ducky.ico");
52   SetToolTip("Image interactive segmentation (trial).");
53   SetToolExperimental(true);
54 }
55 //------------------------------------------------------------------------------
56
57
58 //------------------------------------------------------------------------------
59 vvToolSegmentation::vvToolSegmentation(vvMainWindowBase * parent, Qt::WindowFlags f)
60   :vvToolWidgetBase(parent,f),
61    vvToolBase<vvToolSegmentation>(parent),
62    Ui::vvToolSegmentation()
63 {
64   // GUI Initialization
65   Ui_vvToolSegmentation::setupUi(mToolWidget);
66   setAttribute(Qt::WA_DeleteOnClose);
67
68   // Set how many inputs are needed for this tool
69   AddInputSelector("Select one image");
70   
71   // Init
72   mRefMaskImage = NULL;
73   mCurrentState = State_Default;
74   mKernelValue = 3; // FIXME must be odd. If even -> not symmetrical
75   mDefaultLUTColor = vtkSmartPointer<vtkLookupTable>::New();
76   mDefaultLUTColor->SetNumberOfTableValues(256);
77 #include "vvDefaultLut.h"
78   
79 }
80 //------------------------------------------------------------------------------
81
82
83 //------------------------------------------------------------------------------
84 vvToolSegmentation::~vvToolSegmentation()
85 {
86   DD("destructor");
87 }
88 //------------------------------------------------------------------------------
89
90
91 //------------------------------------------------------------------------------
92 bool vvToolSegmentation::close()
93 {
94   DD("remo ref");
95   if (mRefMaskActor) mRefMaskActor->RemoveActors();
96   DD("remo mask");
97   if (mCurrentMaskActor) mCurrentMaskActor->RemoveActors();
98   for(int i=0; i<mCurrentCCLActors.size(); i++) {
99     if (mCurrentCCLActors[i]) mCurrentCCLActors[i]->RemoveActors();
100   }
101   DD("wclose");
102   QWidget::close();  
103   mCurrentSlicerManager->Render();
104   return true;
105 }
106 //------------------------------------------------------------------------------
107
108
109 //------------------------------------------------------------------------------
110 void vvToolSegmentation::InputIsSelected(vvSlicerManager * m)
111 {
112   DD("InputIsSelected");
113   mCurrentSlicerManager = m;
114   mCurrentImage = mCurrentSlicerManager->GetImage();
115
116   // Refuse if non 3D image
117   if (mCurrentImage->GetNumberOfDimensions() != 3) {
118     QMessageBox::information(this,tr("Sorry only 3D yet"), tr("Sorry only 3D yet"));
119     close();
120     return;
121   }
122
123   // Change gui
124   //mLabelInputInfo->setText(QString("%1").arg(m->GetFileName().c_str()));
125
126   // Open mask
127   OpenBinaryImage();
128   
129   // If cancel: stop
130   if (mRefMaskActor == NULL) {
131     close();
132     return;
133   }
134
135   // Update gui
136   mToolInputSelectionWidget->hide();
137
138   // Connect mouse position
139   connect(mCurrentSlicerManager, SIGNAL(MousePositionUpdatedSignal(int)),
140           this, SLOT(MousePositionChanged(int)));
141   connect(mCurrentSlicerManager, SIGNAL(KeyPressedSignal(std::string)),
142           this, SLOT(KeyPressed(std::string)));
143 }
144 //------------------------------------------------------------------------------
145
146
147 //------------------------------------------------------------------------------
148 void vvToolSegmentation::apply()
149 {
150   DD("apply");
151 }
152 //------------------------------------------------------------------------------
153
154
155 //------------------------------------------------------------------------------
156 void vvToolSegmentation::OpenBinaryImage()
157 {
158   DD("OpenBinaryImage");
159
160   // Load browser and select image
161   QString Extensions = "Images files ( *.mha *.mhd *.hdr *.his)";
162   Extensions += ";;All Files (*)";
163   QString filename =
164     QFileDialog::getOpenFileName(this,tr("Open binary image"),
165                                  mMainWindowBase->GetInputPathName(),Extensions);
166   DD(filename.toStdString());
167   if (filename.size() == 0) return;
168   
169   // Open Image
170   QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
171   vvImageReader::Pointer reader = vvImageReader::New();
172   std::vector<std::string> filenames;
173   filenames.push_back(filename.toStdString());
174   reader->SetInputFilenames(filenames);
175   reader->Update(vvImageReader::IMAGE);
176   QApplication::restoreOverrideCursor();
177   
178   if (reader->GetLastError().size() != 0) {
179     std::cerr << "Error while reading " << filename.toStdString() << std::endl;
180     QString error = "Cannot open file \n";
181     error += reader->GetLastError().c_str();
182     QMessageBox::information(this,tr("Reading problem"),error);
183     return;
184   }
185
186   mRefMaskImage = reader->GetOutput();
187   int dim = mRefMaskImage->GetNumberOfDimensions();
188   if (dim != 3 ) {
189     QMessageBox::information(this,tr("Sorry only 3D yet"), tr("Sorry only 3D yet"));
190     close();
191     return;
192   }
193
194   reader = vvImageReader::New();
195   reader->SetInputFilenames(filenames);
196   reader->Update(vvImageReader::IMAGE);
197   mCurrentMaskImage = reader->GetOutput();
198
199   // Add a new roi actor for the current mask
200   mCurrentMaskActor = CreateMaskActor(mCurrentMaskImage, 1, 0, false);
201   mCurrentMaskActor->Update(); // default color is red
202   UpdateMaskSize(mCurrentMaskImage, mCurrentMaskSizeInPixels, mCurrentMaskSizeInCC);  
203
204   // Add a mask actor for the reference
205   mRefMaskActor = CreateMaskActor(mRefMaskImage, 0, 1, true);
206   mRefMaskActor->SetContourVisible(true);
207   mRefMaskActor->SetVisible(false);
208   mRefMaskActor->SetContourColor(0,1,0); // green contour
209   mRefMaskActor->UpdateColor();
210   mRefMaskActor->Update();
211   UpdateMaskSize(mRefMaskImage, mRefMaskSizeInPixels, mRefMaskSizeInCC);  
212
213   // Update GUI
214   UpdateMaskSizeLabels();
215 }
216 //------------------------------------------------------------------------------
217
218
219 //------------------------------------------------------------------------------
220 void vvToolSegmentation::UpdateMaskSizeLabels()
221 {
222   QString s("%1 pix (%2 cm3)");
223   s = s.arg(mRefMaskSizeInPixels).arg(mRefMaskSizeInCC);
224   mRefMaskSizeLabel->setText(s);
225   QString s2("%1 pix (%2 cm3)");
226   s2 = s2.arg(mCurrentMaskSizeInPixels).arg(mCurrentMaskSizeInCC);
227   mCurrentMaskSizeLabel->setText(s2);
228 }
229 //------------------------------------------------------------------------------
230
231
232 //------------------------------------------------------------------------------
233 void vvToolSegmentation::UpdateMaskSize(vvImage::Pointer image, long & pix, double & cc)
234 {
235   pix = ComputeNumberOfPixels(image, GetForegroundValue());
236   double vol = image->GetSpacing()[0]*image->GetSpacing()[1]*image->GetSpacing()[2];
237   cc = pix * vol / (10*10*10);
238 }
239 //------------------------------------------------------------------------------
240
241
242 //------------------------------------------------------------------------------
243 long vvToolSegmentation::ComputeNumberOfPixels(vvImage::Pointer image, double value) 
244 {
245   int n=0;
246   vtkImageData * im = image->GetFirstVTKImageData();
247   char * pPix = (char*)im->GetScalarPointer(); // FIXME char ?
248   for(uint i=0; i<im->GetNumberOfPoints(); i++) {
249     if (pPix[i] == value) n++;
250   }
251   DD(n);
252   return n;
253 }
254 //------------------------------------------------------------------------------
255
256
257 //------------------------------------------------------------------------------
258 void vvToolSegmentation::KeyPressed(std::string KeyPress)
259
260   if (KeyPress == "e") {
261     Erode();
262   }
263   if (KeyPress == "d") {
264     Dilate(); // FIXME -> extend image BB !!
265   }
266   if (KeyPress == "L") {
267     Labelize(); 
268   }
269   if (KeyPress == "m") {
270     Merge(); 
271     UpdateAndRenderNewMask();
272   }
273   if (KeyPress == "s") { // Supress "Remove" one label
274     if (mCurrentState == State_CCL) RemoveLabel();
275   }
276   if (KeyPress == "t") { // display remove ref contour
277     mRefMaskActor->SetContourVisible(!mRefMaskActor->IsContourVisible());
278     mRefMaskActor->UpdateColor();
279     mCurrentSlicerManager->Render();
280   }
281   if (KeyPress == "w") {
282     vvImageWriter::Pointer writer = vvImageWriter::New();
283     writer->SetOutputFileName("a.mha");
284     writer->SetInput(mCurrentMaskImage);
285     writer->Update();
286   }
287 }
288 //------------------------------------------------------------------------------
289
290
291 //------------------------------------------------------------------------------
292 void vvToolSegmentation::Merge()
293 {
294   if (mCurrentState != State_CCL) return;
295   
296   DD("Merge");
297   // Remove actors
298   for(int i=0; i<mCurrentCCLActors.size(); i++) {
299     if (mCurrentCCLActors[i]) {
300       mCurrentCCLActors[i]->SetVisible(false);
301       mCurrentCCLActors[i]->RemoveActors();
302     }
303   }
304   mCurrentCCLActors.clear();
305
306   // Compute new mask
307   vtkImageData * ccl  = mCurrentCCLImage->GetFirstVTKImageData();
308   vtkImageData * mask = mCurrentMaskImage->GetFirstVTKImageData();
309   int * pCCL = (int*)ccl->GetScalarPointer();
310   char * pPix = (char*)mask->GetScalarPointer();
311   for(uint i=0; i<ccl->GetNumberOfPoints(); i++) {
312     if (pCCL[i] == 0) pPix[i] = GetBackgroundValue(); // copy BG. In CCL BG is always 0
313   }
314
315   // Display new mask and remove ccl
316   mCurrentCCLImage->Reset();
317   mCurrentMaskActor->RemoveActors(); // kill it
318   mCurrentMaskActor = CreateMaskActor(mCurrentMaskImage, 1, 0, false); // renew
319   mCurrentMaskActor->Update();
320   mCurrentMaskActor->SetVisible(true); 
321   // mCurrentSlicerManager->Render();
322   mCurrentState = State_Default;
323 }
324 //------------------------------------------------------------------------------
325
326
327 //------------------------------------------------------------------------------
328 void vvToolSegmentation::Erode()
329 {
330   QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
331   // Merge labels if needed
332   Merge();
333   // Get image and start erosion
334   vtkImageContinuousErode3D* erode = vtkImageContinuousErode3D::New();
335   erode->SetKernelSize(mKernelValue,mKernelValue,mKernelValue);
336   vtkImageData* image = mCurrentMaskImage->GetVTKImages()[0];
337   erode->SetInput(image);
338   erode->Update();
339   image->DeepCopy(erode->GetOutput());
340   image->Update();
341   UpdateAndRenderNewMask();
342   erode->Delete();
343   QApplication::restoreOverrideCursor();
344 }
345 //------------------------------------------------------------------------------
346
347
348 //------------------------------------------------------------------------------
349 void vvToolSegmentation::Dilate()
350 {
351   QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
352   // Merge labels if needed
353   Merge();
354   // Get image and start dilatation
355   vtkImageContinuousDilate3D* dilate = vtkImageContinuousDilate3D::New();
356   dilate->SetKernelSize(mKernelValue,mKernelValue,mKernelValue);
357   vtkImageData* image = mCurrentMaskImage->GetVTKImages()[0];
358   dilate->SetInput(image);
359   dilate->Update();
360   image->DeepCopy(dilate->GetOutput());
361   image->Update();
362   UpdateAndRenderNewMask();
363   dilate->Delete();
364   QApplication::restoreOverrideCursor();
365 }
366 //------------------------------------------------------------------------------
367
368
369 //------------------------------------------------------------------------------
370 void vvToolSegmentation::UpdateAndRenderNewMask()
371 {
372   bool visible = mCurrentMaskActor->IsVisible();
373   bool cvisible = mCurrentMaskActor->IsContourVisible();
374   mCurrentMaskActor->SetVisible(false);
375   mCurrentMaskActor->SetContourVisible(false);
376   mCurrentMaskActor->UpdateImage();
377   mCurrentMaskActor->SetVisible(visible);
378   mCurrentMaskActor->SetContourVisible(cvisible);
379
380   mCurrentSlicerManager->Render();
381   UpdateMaskSize(mCurrentMaskImage, mCurrentMaskSizeInPixels, mCurrentMaskSizeInCC);
382   UpdateMaskSizeLabels();
383 }
384 //------------------------------------------------------------------------------
385
386 //------------------------------------------------------------------------------
387 void vvToolSegmentation::Labelize()
388 {
389   if (mCurrentState == State_CCL) return; // Do nothing in this case
390   DD("Labelize");
391   // Waiting cursos
392   QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
393   mCurrentMaskActor->SetVisible(false);
394   
395   // Build CCL filter
396   vtkImageData* image = mCurrentMaskImage->GetVTKImages()[0];
397   typedef args_info_clitkConnectedComponentLabeling ArgsInfoType;
398   ArgsInfoType a;
399   cmdline_parser_clitkConnectedComponentLabeling_init(&a);
400   a.inputBG_arg = GetBackgroundValue();
401   a.full_flag = false;  // FIXME set by gui
402   a.minSize_arg = 100;  // FIXME set by gui 
403   typedef clitk::ConnectedComponentLabelingGenericFilter<ArgsInfoType> FilterType;
404   FilterType::Pointer filter = FilterType::New();
405   filter->SetArgsInfo(a);
406   filter->SetInputVVImage(mCurrentMaskImage); // FIXME Check type is ok ? convert float ?
407   filter->SetIOVerbose(true);  
408   filter->Update();
409   DD(filter->GetOriginalNumberOfObjects());
410   DD(filter->GetSizeOfObjectsInPixels().size());
411   mCurrentCCLImage = filter->GetOutputVVImage();
412   DDV(filter->GetSizeOfObjectsInPixels(), filter->GetSizeOfObjectsInPixels().size());
413   DD("filter done");
414
415   /*
416   // DEBUG
417   vvImageWriter::Pointer writer = vvImageWriter::New();
418   writer->SetInput(mCurrentCCLImage);
419   writer->SetOutputFileName("bidon-ccl.mha");
420   writer->Update(); 
421   DD(mCurrentCCLImage->IsScalarTypeInteger());
422   */
423   
424   // Create actors 
425   int n = filter->GetSizeOfObjectsInPixels().size();
426   mCurrentCCLActors.clear();
427   for(int i=1; i<=std::min(n,10); i++) { // Start at 1 because 0 is BG. FIXME max by gui
428     QSharedPointer<vvROIActor> actor = CreateMaskActor(mCurrentCCLImage, i, i+1, false); 
429     mCurrentCCLActors.push_back( actor );
430     actor->Update();    
431   }
432   //  mCurrentMaskActor->Update();
433   mCurrentSlicerManager->Render();
434   
435   // UpdateAndRender();
436   mCurrentState = State_CCL;
437   QApplication::restoreOverrideCursor();
438 }
439 //------------------------------------------------------------------------------
440
441 //------------------------------------------------------------------------------
442 QSharedPointer<vvROIActor> vvToolSegmentation::CreateMaskActor(vvImage::Pointer image, int i, int colorID, bool BGMode)
443 {
444   static int depth = 1;
445   depth += 1;
446   QSharedPointer<vvROIActor> actor = QSharedPointer<vvROIActor>(new vvROIActor);
447   double * color = mDefaultLUTColor->GetTableValue(colorID % mDefaultLUTColor->GetNumberOfTableValues ());
448   std::vector<double> c;
449   c.push_back(color[0]);
450   c.push_back(color[1]);
451   c.push_back(color[2]);
452   clitk::DicomRT_ROI::Pointer roi = clitk::DicomRT_ROI::New();
453   roi->SetFromBinaryImage(image, i, std::string("toto"), c, std::string("titi"));
454   if (BGMode) {
455     actor->SetBGMode(true);
456   }
457   else {
458     roi->SetForegroundValueLabelImage(i); // FG mode
459     actor->SetBGMode(false); // FG mode
460   }
461   actor->SetROI(roi);
462   actor->SetSlicerManager(mCurrentSlicerManager);
463   actor->Initialize(depth+i, true); // +1 to start at 1 not 0
464   actor->SetContourVisible(false);
465   actor->SetVisible(true);
466   return actor;
467 }
468 //------------------------------------------------------------------------------
469
470
471 //------------------------------------------------------------------------------
472 void vvToolSegmentation::MousePositionChanged(int slicer)
473 {
474   if (mCurrentState == State_Default) return; // Do nothing in this case
475
476   double x = mCurrentSlicerManager->GetSlicer(slicer)->GetCurrentPosition()[0];
477   double y = mCurrentSlicerManager->GetSlicer(slicer)->GetCurrentPosition()[1];
478   double z = mCurrentSlicerManager->GetSlicer(slicer)->GetCurrentPosition()[2];
479   vtkImageData * image = mCurrentCCLImage->GetFirstVTKImageData();
480   double Xover = (x - image->GetOrigin()[0]) / image->GetSpacing()[0];
481   double Yover = (y - image->GetOrigin()[1]) / image->GetSpacing()[1];
482   double Zover = (z - image->GetOrigin()[2]) / image->GetSpacing()[2];
483   int ix, iy, iz;
484   
485   if (Xover >= image->GetWholeExtent()[0] &&
486       Xover <= image->GetWholeExtent()[1] &&
487       Yover >= image->GetWholeExtent()[2] &&
488       Yover <= image->GetWholeExtent()[3] &&
489       Zover >= image->GetWholeExtent()[4] &&
490       Zover <= image->GetWholeExtent()[5]) {
491     mCurrentLabelUnderMousePointer = 
492       mCurrentSlicerManager->GetSlicer(0)->GetScalarComponentAsDouble(image, Xover, Yover, Zover, ix, iy, iz, 0);
493     // DD(Xover); DD(Yover); DD(Zover);
494     // DD(ix); DD(iy); DD(iz);
495     // DD(valueOver);
496   }
497   else {
498     // DD("out of mask");
499     mCurrentLabelUnderMousePointer = 0; // BG is always 0 in CCL
500   }
501   // DD(mCurrentLabelUnderMousePointer);
502 }
503 //------------------------------------------------------------------------------
504
505
506 //------------------------------------------------------------------------------
507 void vvToolSegmentation::RemoveLabel() {
508   DD("RemoveLabel");
509   if (mCurrentLabelUnderMousePointer == 0) return;
510   // First actor=0 and is label 1. Label 0 is not an actor, it is BG
511   int actorNumber = mCurrentLabelUnderMousePointer-1; 
512   // Set actor invisible
513   mCurrentCCLActors[actorNumber]->SetVisible(false);
514   mCurrentSlicerManager->Render();
515   // Set image label
516   vtkImageData * image = mCurrentCCLImage->GetFirstVTKImageData();
517   int * pPix = (int*)image->GetScalarPointer();
518   int n = 0;
519   for(uint i=0; i<image->GetNumberOfPoints(); i++) {
520     if (pPix[i] == mCurrentLabelUnderMousePointer) pPix[i] = 0;
521     if (pPix[i] != 0) n++; // count the number of pixels in the foreground
522   }
523   // Update mask size
524   mCurrentMaskSizeInPixels = n;
525   mCurrentMaskSizeInCC = mCurrentMaskImage->GetSpacing()[0] * mCurrentMaskImage->GetSpacing()[1] * mCurrentMaskImage->GetSpacing()[2] * n / (10*10*10);
526   UpdateMaskSizeLabels();
527 }
528 //------------------------------------------------------------------------------