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"
31 #include <QFileDialog>
32 #include <QMessageBox>
35 #include "vtkImageContinuousErode3D.h"
36 #include "vtkImageContinuousDilate3D.h"
37 #include "vtkRenderWindow.h"
39 //------------------------------------------------------------------------------
40 // Create the tool and automagically (I like this word) insert it in
41 // the main window menu.
42 ADD_TOOL(vvToolSegmentation);
43 //------------------------------------------------------------------------------
46 //------------------------------------------------------------------------------
47 void vvToolSegmentation::Initialize()
49 SetToolName("Segmentation");
50 SetToolMenuName("Interactive Segmentation");
51 SetToolIconFilename(":/common/icons/ducky.ico");
52 SetToolTip("Image interactive segmentation (trial).");
53 SetToolExperimental(true);
55 //------------------------------------------------------------------------------
58 //------------------------------------------------------------------------------
59 vvToolSegmentation::vvToolSegmentation(vvMainWindowBase * parent, Qt::WindowFlags f)
60 :vvToolWidgetBase(parent,f),
61 vvToolBase<vvToolSegmentation>(parent),
62 Ui::vvToolSegmentation()
65 Ui_vvToolSegmentation::setupUi(mToolWidget);
66 setAttribute(Qt::WA_DeleteOnClose);
68 // Set how many inputs are needed for this tool
69 AddInputSelector("Select one image");
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"
80 //------------------------------------------------------------------------------
83 //------------------------------------------------------------------------------
84 vvToolSegmentation::~vvToolSegmentation()
88 //------------------------------------------------------------------------------
91 //------------------------------------------------------------------------------
92 bool vvToolSegmentation::close()
95 if (mRefMaskActor) mRefMaskActor->RemoveActors();
97 if (mCurrentMaskActor) mCurrentMaskActor->RemoveActors();
98 for(int i=0; i<mCurrentCCLActors.size(); i++) {
99 if (mCurrentCCLActors[i]) mCurrentCCLActors[i]->RemoveActors();
103 mCurrentSlicerManager->Render();
106 //------------------------------------------------------------------------------
109 //------------------------------------------------------------------------------
110 void vvToolSegmentation::InputIsSelected(vvSlicerManager * m)
112 DD("InputIsSelected");
113 mCurrentSlicerManager = m;
114 mCurrentImage = mCurrentSlicerManager->GetImage();
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"));
124 //mLabelInputInfo->setText(QString("%1").arg(m->GetFileName().c_str()));
130 if (mRefMaskActor == NULL) {
136 mToolInputSelectionWidget->hide();
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)));
144 //------------------------------------------------------------------------------
147 //------------------------------------------------------------------------------
148 void vvToolSegmentation::apply()
152 //------------------------------------------------------------------------------
155 //------------------------------------------------------------------------------
156 void vvToolSegmentation::OpenBinaryImage()
158 DD("OpenBinaryImage");
160 // Load browser and select image
161 QString Extensions = "Images files ( *.mha *.mhd *.hdr *.his)";
162 Extensions += ";;All Files (*)";
164 QFileDialog::getOpenFileName(this,tr("Open binary image"),
165 mMainWindowBase->GetInputPathName(),Extensions);
166 DD(filename.toStdString());
167 if (filename.size() == 0) return;
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();
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);
186 mRefMaskImage = reader->GetOutput();
187 int dim = mRefMaskImage->GetNumberOfDimensions();
189 QMessageBox::information(this,tr("Sorry only 3D yet"), tr("Sorry only 3D yet"));
194 reader = vvImageReader::New();
195 reader->SetInputFilenames(filenames);
196 reader->Update(vvImageReader::IMAGE);
197 mCurrentMaskImage = reader->GetOutput();
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);
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);
214 UpdateMaskSizeLabels();
216 //------------------------------------------------------------------------------
219 //------------------------------------------------------------------------------
220 void vvToolSegmentation::UpdateMaskSizeLabels()
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);
229 //------------------------------------------------------------------------------
232 //------------------------------------------------------------------------------
233 void vvToolSegmentation::UpdateMaskSize(vvImage::Pointer image, long & pix, double & cc)
235 pix = ComputeNumberOfPixels(image, GetForegroundValue());
236 double vol = image->GetSpacing()[0]*image->GetSpacing()[1]*image->GetSpacing()[2];
237 cc = pix * vol / (10*10*10);
239 //------------------------------------------------------------------------------
242 //------------------------------------------------------------------------------
243 long vvToolSegmentation::ComputeNumberOfPixels(vvImage::Pointer image, double value)
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++;
254 //------------------------------------------------------------------------------
257 //------------------------------------------------------------------------------
258 void vvToolSegmentation::KeyPressed(std::string KeyPress)
260 if (KeyPress == "e") {
263 if (KeyPress == "d") {
264 Dilate(); // FIXME -> extend image BB !!
266 if (KeyPress == "L") {
269 if (KeyPress == "m") {
271 UpdateAndRenderNewMask();
273 if (KeyPress == "s") { // Supress "Remove" one label
274 if (mCurrentState == State_CCL) RemoveLabel();
276 if (KeyPress == "t") { // display remove ref contour
277 mRefMaskActor->SetContourVisible(!mRefMaskActor->IsContourVisible());
278 mRefMaskActor->UpdateColor();
279 mCurrentSlicerManager->Render();
281 if (KeyPress == "w") {
282 vvImageWriter::Pointer writer = vvImageWriter::New();
283 writer->SetOutputFileName("a.mha");
284 writer->SetInput(mCurrentMaskImage);
288 //------------------------------------------------------------------------------
291 //------------------------------------------------------------------------------
292 void vvToolSegmentation::Merge()
294 if (mCurrentState != State_CCL) return;
298 for(int i=0; i<mCurrentCCLActors.size(); i++) {
299 if (mCurrentCCLActors[i]) {
300 mCurrentCCLActors[i]->SetVisible(false);
301 mCurrentCCLActors[i]->RemoveActors();
304 mCurrentCCLActors.clear();
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
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;
324 //------------------------------------------------------------------------------
327 //------------------------------------------------------------------------------
328 void vvToolSegmentation::Erode()
330 QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
331 // Merge labels if needed
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);
339 image->DeepCopy(erode->GetOutput());
341 UpdateAndRenderNewMask();
343 QApplication::restoreOverrideCursor();
345 //------------------------------------------------------------------------------
348 //------------------------------------------------------------------------------
349 void vvToolSegmentation::Dilate()
351 QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
352 // Merge labels if needed
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);
360 image->DeepCopy(dilate->GetOutput());
362 UpdateAndRenderNewMask();
364 QApplication::restoreOverrideCursor();
366 //------------------------------------------------------------------------------
369 //------------------------------------------------------------------------------
370 void vvToolSegmentation::UpdateAndRenderNewMask()
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);
380 mCurrentSlicerManager->Render();
381 UpdateMaskSize(mCurrentMaskImage, mCurrentMaskSizeInPixels, mCurrentMaskSizeInCC);
382 UpdateMaskSizeLabels();
384 //------------------------------------------------------------------------------
386 //------------------------------------------------------------------------------
387 void vvToolSegmentation::Labelize()
389 if (mCurrentState == State_CCL) return; // Do nothing in this case
392 QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
393 mCurrentMaskActor->SetVisible(false);
396 vtkImageData* image = mCurrentMaskImage->GetVTKImages()[0];
397 typedef args_info_clitkConnectedComponentLabeling ArgsInfoType;
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);
409 DD(filter->GetOriginalNumberOfObjects());
410 DD(filter->GetSizeOfObjectsInPixels().size());
411 mCurrentCCLImage = filter->GetOutputVVImage();
412 DDV(filter->GetSizeOfObjectsInPixels(), filter->GetSizeOfObjectsInPixels().size());
417 vvImageWriter::Pointer writer = vvImageWriter::New();
418 writer->SetInput(mCurrentCCLImage);
419 writer->SetOutputFileName("bidon-ccl.mha");
421 DD(mCurrentCCLImage->IsScalarTypeInteger());
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 );
432 // mCurrentMaskActor->Update();
433 mCurrentSlicerManager->Render();
435 // UpdateAndRender();
436 mCurrentState = State_CCL;
437 QApplication::restoreOverrideCursor();
439 //------------------------------------------------------------------------------
441 //------------------------------------------------------------------------------
442 QSharedPointer<vvROIActor> vvToolSegmentation::CreateMaskActor(vvImage::Pointer image, int i, int colorID, bool BGMode)
444 static int 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"));
455 actor->SetBGMode(true);
458 roi->SetForegroundValueLabelImage(i); // FG mode
459 actor->SetBGMode(false); // FG mode
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);
468 //------------------------------------------------------------------------------
471 //------------------------------------------------------------------------------
472 void vvToolSegmentation::MousePositionChanged(int slicer)
474 if (mCurrentState == State_Default) return; // Do nothing in this case
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];
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);
498 // DD("out of mask");
499 mCurrentLabelUnderMousePointer = 0; // BG is always 0 in CCL
501 // DD(mCurrentLabelUnderMousePointer);
503 //------------------------------------------------------------------------------
506 //------------------------------------------------------------------------------
507 void vvToolSegmentation::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();
516 vtkImageData * image = mCurrentCCLImage->GetFirstVTKImageData();
517 int * pPix = (int*)image->GetScalarPointer();
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
524 mCurrentMaskSizeInPixels = n;
525 mCurrentMaskSizeInCC = mCurrentMaskImage->GetSpacing()[0] * mCurrentMaskImage->GetSpacing()[1] * mCurrentMaskImage->GetSpacing()[2] * n / (10*10*10);
526 UpdateMaskSizeLabels();
528 //------------------------------------------------------------------------------