1 #ifdef _MSC_VER
2 #  pragma warning(disable:4244) // conversion with loss of data
3 #  pragma warning(disable:4996) // boost_1_65_1\boost/iostreams/positioning.hpp(96):
4                                 // warning C4996: 'std::fpos<_Mbstatet>::seekpos': warning STL4019:
5                                 // The member std::fpos::seekpos() is non-Standard
6 #endif
7 
8 #include "Volume_plane.h"
9 #include "Volume_plane_thread.h"
10 #include "Volume_plane_intersection.h"
11 #include "Messages_interface.h"
12 #include "config.h"
13 #include "Scene_image_item.h"
14 #include "Image_type.h"
15 #include "ui_Image_res_dialog.h"
16 
17 #include <CGAL/Image_3.h>
18 #include <CGAL/ImageIO.h>
19 #include <CGAL/SEP_to_ImageIO.h>
20 #include <CGAL/Three/Polyhedron_demo_plugin_helper.h>
21 #include <CGAL/Three/Polyhedron_demo_plugin_interface.h>
22 #include <CGAL/Three/Scene_interface.h>
23 #include <CGAL/Three/Scene_item.h>
24 #include <CGAL/Three/Scene_group_item.h>
25 #include <CGAL/Three/Three.h>
26 #include <CGAL/Three/Viewer_interface.h>
27 #include <CGAL/config.h>
28 #include <CGAL/use.h>
29 #include <QAction>
30 #include <QMenu>
31 #include <QMouseEvent>
32 #include <QList>
33 #include <QInputDialog>
34 #include <QSlider>
35 #include <QLabel>
36 #include <QLineEdit>
37 #include <QHBoxLayout>
38 #include <QVBoxLayout>
39 #include <QDockWidget>
40 #include <QMainWindow>
41 #include <QMessageBox>
42 #include <QString>
43 #include <QFontMetrics>
44 #include <QFileDialog>
45 #include <QPushButton>
46 
47 #include <cassert>
48 #include <iostream>
49 
50 #include <boost/type_traits.hpp>
51 #include <boost/optional.hpp>
52 
53 #include <QSettings>
54 #include <QUrl>
55 #include "Raw_image_dialog.h"
56 #include <CGAL/Three/Polyhedron_demo_io_plugin_interface.h>
57 #include <fstream>
58 #include <cstdlib>
59 #ifdef CGAL_USE_VTK
60 #include <CGAL/IO/read_vtk_image_data.h>
61 
62 #include <vtkNew.h>
63 #include <vtkImageData.h>
64 #include <vtkDICOMImageReader.h>
65 #include <vtkImageReader.h>
66 #include <vtkImageGaussianSmooth.h>
67 #include <vtkDemandDrivenPipeline.h>
68 #endif
69 #include <CGAL/Three/Three.h>
70 
71 // Covariant return types don't work for scalar types and we cannot
72 // have templates here, hence this unfortunate hack.
73 
74 // The input float value we are reading is always in
75 // 0..1 and min_max is the range it came from.
76 struct IntConverter {
77   std::pair<int, int> min_max;
78 
operator ()IntConverter79   int operator()(float f) {
80     float s = f * float((min_max.second - min_max.first));
81     //approximate instead of just floor.
82     if (s - floor(s) >= 0.5){
83       return int(s)+1 + min_max.first;
84     }
85     else{
86       return s + float(min_max.first);
87     }
88   }
89 };
90 
91 struct DoubleConverter {
92   std::pair<float, float> min_max;
93 
operator ()DoubleConverter94   float operator()(float f) {
95     float s = f * (min_max.second - min_max.first);
96     return s + min_max.first;
97   }
98 };
99 
100 class PixelReader : public QObject
101 {
102 Q_OBJECT
103 public Q_SLOTS:
update(const QMouseEvent * e)104   void update(const QMouseEvent *e) {
105     getPixel(e->pos());
106   }
107 Q_SIGNALS:
108   void x(QString);
109 public:
setIC(const IntConverter & x)110   void setIC(const IntConverter& x) { ic = x; fc = boost::optional<DoubleConverter>(); }
setFC(const DoubleConverter & x)111   void setFC(const DoubleConverter& x) { fc = x; ic = boost::optional<IntConverter>(); }
setViewer(Viewer_interface * viewer)112   void setViewer(Viewer_interface* viewer) { this->viewer = viewer; }
113 
114 private:
115   boost::optional<IntConverter> ic;
116   boost::optional<DoubleConverter> fc;
117   Viewer_interface* viewer;
getPixel(const QPoint & e)118   void getPixel(const QPoint& e) {
119     float data[3];
120     int vp[4];
121     viewer->glGetIntegerv(GL_VIEWPORT, vp);
122     viewer->glReadPixels(e.x(), vp[3] - e.y(), 1, 1, GL_RGB, GL_FLOAT, data);
123 
124     if(fc) {
125       Q_EMIT x(QString::number((*fc)(data[0]), 'f', 6 ));
126     } else if(ic) {
127       Q_EMIT x( QString::number((*ic)(data[0]) ));
128     }
129   }
130 };
131 
132 
133 class Plane_slider : public QSlider
134 {
135   Q_OBJECT
136 public:
Plane_slider(const CGAL::qglviewer::Vec & v,int id,Scene_interface * scene,CGAL::qglviewer::ManipulatedFrame * frame,Qt::Orientation ori,QWidget * widget)137   Plane_slider(const CGAL::qglviewer::Vec& v, int id, Scene_interface* scene,
138                CGAL::qglviewer::ManipulatedFrame* frame, Qt::Orientation ori, QWidget* widget)
139     : QSlider(ori, widget), v(v), id(id), scene(scene), frame(frame) {
140     this->setTracking(true);
141     connect(frame,  SIGNAL(manipulated()), this, SLOT(updateCutPlane()));
142   }
143 
144 public Q_SLOTS:
updateCutPlane()145   void updateCutPlane()
146   {
147      ready_to_cut = true;
148      QTimer::singleShot(0,this,SLOT(updateValue()));
149   }
150 
setFramePosition()151   void setFramePosition()
152   {
153     if(!ready_to_move)
154       return;
155     const CGAL::qglviewer::Vec offset = Three::mainViewer()->offset();
156     CGAL::qglviewer::Vec v2 = v * (this->value() / scale);
157     v2+=offset;
158     frame->setTranslationWithConstraint(v2);
159     scene->itemChanged(id);
160 
161     Q_EMIT realChange(this->value() / scale);
162     ready_to_move = false;
163   }
updateValue()164   void updateValue() {
165     if(!ready_to_cut)
166       return;
167     typedef qreal qglviewer_real;
168     qglviewer_real a, b, c;
169     frame->getPosition(a, b, c);
170     const CGAL::qglviewer::Vec offset = Three::mainViewer()->offset();
171     a-=offset.x;
172     b-=offset.y;
173     c-=offset.z;
174     float sum1 = float(a + b + c);
175     float sum2 = float(v.x + v.y + v.z);
176     sum1 /= sum2;
177     setValue(sum1 * float(scale));
178     ready_to_cut = false;
179   }
180 
updateFramePosition()181   void updateFramePosition()
182   {
183     ready_to_move = true;
184     QTimer::singleShot(0,this,SLOT(setFramePosition()));
185   }
getScale() const186   unsigned int getScale() const { return scale; }
187 Q_SIGNALS:
188   void realChange(int);
189 
190 private:
191   static const unsigned int scale;
192   bool ready_to_cut;
193   bool ready_to_move;
194   CGAL::qglviewer::Vec v;
195   int id;
196   Scene_interface* scene;
197   CGAL::qglviewer::ManipulatedFrame* frame;
198 };
199 
200 const unsigned int Plane_slider::scale = 100;
201 
202 class Io_image_plugin :
203   public QObject,
204   public CGAL::Three::Polyhedron_demo_plugin_helper,
205   public CGAL::Three::Polyhedron_demo_io_plugin_interface
206 {
207   Q_OBJECT
208   Q_INTERFACES(CGAL::Three::Polyhedron_demo_io_plugin_interface)
209   Q_INTERFACES(CGAL::Three::Polyhedron_demo_plugin_interface)
210   Q_PLUGIN_METADATA(IID "com.geometryfactory.PolyhedronDemo.IOPluginInterface/1.90" FILE "io_image_plugin.json")
211 
212 public:
213 
applicable(QAction *) const214   bool applicable(QAction*) const override{
215     return qobject_cast<Scene_image_item*>(scene->item(scene->mainSelectionIndex()));
216   }
217 
218   using Polyhedron_demo_io_plugin_interface::init;
init(QMainWindow * mainWindow,CGAL::Three::Scene_interface * scene_interface,Messages_interface * mi)219   void init(QMainWindow* mainWindow, CGAL::Three::Scene_interface* scene_interface, Messages_interface *mi) override {
220     this->message_interface = mi;
221     this->scene = scene_interface;
222     this->mw = mainWindow;
223     this->is_gray = false;
224     x_control = nullptr;
225     y_control = nullptr;
226     z_control = nullptr;
227     current_control = nullptr;
228     planeSwitch = new QAction("Add Volume Planes", mw);
229     QAction *actionLoadDCM = new QAction("Open Directory (for DCM files)", mw);
230     connect(actionLoadDCM, SIGNAL(triggered()), this, SLOT(on_actionLoadDCM_triggered()));
231     if(planeSwitch) {
232       planeSwitch->setProperty("subMenuName", "3D Mesh Generation");
233       connect(planeSwitch, SIGNAL(triggered()),
234               this, SLOT(selectPlanes()));
235       connect(CGAL::Three::Three::connectableScene(),SIGNAL(itemIndexSelected(int)),
236               this, SLOT(connect_controls(int)));
237     }
238     Viewer_interface* v = CGAL::Three::Three::mainViewer();
239     CGAL_assertion(v != nullptr);
240     pxr_.setViewer(v);
241     connect(v, SIGNAL(pointSelected(const QMouseEvent *)), &pxr_, SLOT(update(const QMouseEvent *)));
242     createOrGetDockLayout();
243     connect(mw, SIGNAL(newViewerCreated(QObject*)),
244             this, SLOT(connectNewViewer(QObject*)));
245 
246     QMenu* menuFile = mw->findChild<QMenu*>("menuFile");
247     if ( nullptr != menuFile )
248     {
249       QList<QAction*> menuFileActions = menuFile->actions();
250 
251       // Look for action just after "Load..." action
252       QAction* actionAfterLoad = nullptr;
253       for ( QList<QAction*>::iterator it_action = menuFileActions.begin(),
254            end = menuFileActions.end() ; it_action != end ; ++ it_action ) //Q_FOREACH( QAction* action, menuFileActions)
255       {
256         if ( NULL != *it_action && (*it_action)->text().contains("Load Plugin") )
257         {
258           ++it_action;
259           if ( it_action != end && NULL != *it_action )
260           {
261             actionAfterLoad = *it_action;
262           }
263         }
264       }
265 
266       // Insert "Load implicit function" action
267       if ( nullptr != actionAfterLoad )
268       {
269         menuFile->insertAction(actionAfterLoad,actionLoadDCM);
270       }
271     }
272   }
actions() const273   QList<QAction*> actions() const override{
274     return QList<QAction*>() << planeSwitch;
275   }
closure()276   virtual void closure() override
277   {
278       QDockWidget* controlDockWidget = mw->findChild<QDockWidget*>("volumePlanesControl");
279       if(controlDockWidget)
280           controlDockWidget->hide();
281   }
Io_image_plugin()282   Io_image_plugin() : planeSwitch(nullptr) {}
283 
284   QString nameFilters() const override;
285   bool canLoad(QFileInfo) const override;
286   QList<Scene_item*> load(QFileInfo fileinfo, bool& ok, bool add_to_scene=true) override;
287 
288   bool canSave(const CGAL::Three::Scene_item*) override;
save(QFileInfo fileinfo,QList<CGAL::Three::Scene_item * > & items)289   bool save(QFileInfo fileinfo, QList<CGAL::Three::Scene_item*>& items ) override{
290     Scene_item* item = items.front();
291     const Scene_image_item* im_item = qobject_cast<const Scene_image_item*>(item);
292 
293     point_image p_im = *im_item->image()->image();
294     bool ok = _writeImage(&p_im, fileinfo.filePath().toUtf8()) == 0;
295     if(ok)
296       items.pop_front();
297     return ok;
298   }
isDefaultLoader(const Scene_item * item) const299   bool isDefaultLoader(const Scene_item* item) const override{
300     if(qobject_cast<const Scene_image_item*>(item))
301       return true;
302     return false;
303   }
name() const304   QString name() const override{ return "segmented images"; }
305 
306 
307 public Q_SLOTS:
308 
setXNum(int i)309   void setXNum(int i)
310   {
311    x_cubeLabel->setText(QString("%1").arg(i));
312   }
setYNum(int i)313   void setYNum(int i)
314   {
315    y_cubeLabel->setText(QString("%1").arg(i));
316   }
setZNum(int i)317   void setZNum(int i)
318   {
319    z_cubeLabel->setText(QString("%1").arg(i));
320   }
321 
XTextEdited(QString s)322   void XTextEdited(QString s)
323   {
324     int i = s.toInt();
325     x_slider->setValue(i*qobject_cast<Plane_slider*>(x_slider)->getScale());
326     x_slider->sliderMoved(i);
327   }
YTextEdited(QString s)328   void YTextEdited(QString s)
329   {
330     int i = s.toInt();
331     y_slider->setValue(i*qobject_cast<Plane_slider*>(y_slider)->getScale());
332     y_slider->sliderMoved(i);
333   }
ZTextEdited(QString s)334   void ZTextEdited(QString s)
335   {
336     int i = s.toInt();
337     z_slider->setValue(i*qobject_cast<Plane_slider*>(z_slider)->getScale());
338     z_slider->sliderMoved(i);
339 
340   }
341 
on_imageType_changed(int index)342   void on_imageType_changed(int index)
343   {
344     if(index == 0)
345       ui.groupBox->setVisible(true);
346     else
347       ui.groupBox->setVisible(false);
348   }
selectPlanes()349   void selectPlanes() {
350 
351     std::vector< Scene_image_item* > seg_items;
352     Scene_image_item* seg_img;
353     seg_img = nullptr;
354     for(int i = 0; i < scene->numberOfEntries(); ++i) {
355       Scene_image_item* tmp = qobject_cast<Scene_image_item*>(scene->item(i));
356       if(tmp != nullptr){
357         seg_items.push_back(tmp);
358       }
359     }
360     if(seg_items.empty()) {
361       QMessageBox::warning(mw, tr("No suitable item found"), tr("Load an inrimage or hdr file to enable Volume Planes."));
362       return;
363     } else {
364       QList<QString> items;
365       for(std::vector< Scene_image_item* >::const_iterator it = seg_items.begin();
366           it != seg_items.end(); ++it) {
367         items << (*it)->name();
368       }
369       bool ok;
370       QString selected = QInputDialog::getItem(mw, tr("Select a dataset:"), tr("Items"), items, 0, false, &ok);
371       if(!ok || selected.isEmpty())
372         return;
373       for(std::vector< Scene_image_item*>::const_iterator it = seg_items.begin();
374           it != seg_items.end(); ++it) {
375         if(selected == (*it)->name())
376           seg_img = *it;
377       }
378     }
379     if(group_map.keys().contains(seg_img))
380       CGAL::Three::Three::warning("This item already has volume planes.");
381     else
382     {
383       // Opens a modal Dialog to prevent the user from manipulating things that could mess with the planes creation and cause a segfault.
384       msgBox.setText("Planes created : 0/3");
385       msgBox.setStandardButtons(QMessageBox::NoButton);
386       msgBox.show();
387       createPlanes(seg_img);
388     }
389   }
390 
addVP(Volume_plane_thread * thread)391   void addVP(Volume_plane_thread* thread) {
392     Volume_plane_interface* plane = thread->getItem();
393     plane->init(Three::mainViewer());
394     // add the interface for this Volume_plane
395     int id = scene->addItem(plane);
396     scene->changeGroup(plane, group);
397     group->lockChild(plane);
398     //connect(plane->manipulatedFrame(), &CGAL::qglviewer::ManipulatedFrame::manipulated,
399     //        plane, &Volume_plane_interface::redraw);
400     switch(thread->type())
401     {
402     case 'x':
403       delete x_slider;
404       x_slider = new Plane_slider(plane->translationVector(), id, scene, plane->manipulatedFrame(),
405                                          Qt::Horizontal, x_control);
406       x_slider->setRange(0, (plane->cDim() - 1) * 100);
407       connect(x_slider, SIGNAL(realChange(int)), this, SLOT(setXNum(int)));
408       connect(x_cubeLabel, SIGNAL(textEdited(QString)), this, SLOT(XTextEdited(QString)));
409       connect(x_slider, SIGNAL(realChange(int)), this, SLOT(set_value()));
410       connect(x_slider, SIGNAL(sliderMoved(int)), x_slider, SLOT(updateFramePosition()));
411       connect(plane, SIGNAL(manipulated(int)), this, SLOT(setXNum(int)));
412       connect(plane, SIGNAL(aboutToBeDestroyed()), this, SLOT(destroy_x_item()));
413       x_box->addWidget(x_slider);
414       x_box->addWidget(x_cubeLabel);
415       x_control->setVisible(true);
416       break;
417     case 'y':
418       delete y_slider;
419       y_slider = new Plane_slider(plane->translationVector(), id, scene, plane->manipulatedFrame(),
420                                          Qt::Horizontal, y_control);
421       y_slider->setRange(0, (plane->cDim() - 1) * 100);
422       connect(y_slider, SIGNAL(realChange(int)), this, SLOT(setYNum(int)));
423       connect(y_cubeLabel, SIGNAL(textEdited(QString)), this, SLOT(YTextEdited(QString)));
424       connect(y_slider, SIGNAL(realChange(int)), this, SLOT(set_value()));
425       connect(y_slider, SIGNAL(sliderMoved(int)), y_slider, SLOT(updateFramePosition()));
426       connect(plane, SIGNAL(manipulated(int)), this, SLOT(setYNum(int)));
427       connect(plane, SIGNAL(aboutToBeDestroyed()), this, SLOT(destroy_y_item()));
428       y_box->addWidget(y_slider);
429       y_box->addWidget(y_cubeLabel);
430       y_control->setVisible(true);
431       break;
432     case 'z':
433       delete z_slider;
434       z_slider = new Plane_slider(plane->translationVector(), id, scene, plane->manipulatedFrame(),
435                                          Qt::Horizontal, z_control);
436       z_slider->setRange(0, (plane->cDim() - 1) * 100);
437       connect(z_slider, SIGNAL(realChange(int)), this, SLOT(setZNum(int)));
438       connect(z_cubeLabel, SIGNAL(textEdited(QString)), this, SLOT(ZTextEdited(QString)));
439       connect(z_slider, SIGNAL(realChange(int)), this, SLOT(set_value()));
440       connect(z_slider, SIGNAL(sliderMoved(int)), z_slider, SLOT(updateFramePosition()));
441       connect(plane, SIGNAL(manipulated(int)), this, SLOT(setZNum(int)));
442       connect(plane, SIGNAL(aboutToBeDestroyed()), this, SLOT(destroy_z_item()));
443       z_box->addWidget(z_slider);
444       z_box->addWidget(z_cubeLabel);
445       z_control->setVisible(true);
446       break;
447     default:
448       break;
449     }
450     std::vector<Volume_plane_thread*>::iterator it = std::find(threads.begin(), threads.end(), thread);
451 
452     // this slot has been connected to a thread that hasn't been
453     // registered here.
454     assert(it != threads.end());
455     delete *it;
456     threads.erase(it);
457 
458     update_msgBox();
459     Volume_plane_intersection* intersection = dynamic_cast<Volume_plane_intersection*>(scene->item(intersection_id));
460     if(!intersection) {
461       // the intersection is gone before it was initialized
462       return;
463     }
464     // FIXME downcasting mode
465     // FIXME this will bug if two volume planes are generated simultaneously by the plugin
466     if(Volume_plane<x_tag>* p = dynamic_cast< Volume_plane<x_tag>* >(plane)) {
467       intersection->setX(p);
468     } else if(Volume_plane<y_tag>* p = dynamic_cast< Volume_plane<y_tag>* >(plane)) {
469       intersection->setY(p);
470     } else if(Volume_plane<z_tag>* p = dynamic_cast< Volume_plane<z_tag>* >(plane)) {
471       intersection->setZ(p);
472     }
473     connect(plane, SIGNAL(planeDestructionIncoming(Volume_plane_interface*)),
474             intersection, SLOT(planeRemoved(Volume_plane_interface*)));
475 
476   }
477 
on_actionLoadDCM_triggered()478   void on_actionLoadDCM_triggered()
479   {
480     QSettings settings;
481     QString start_dir = settings.value("Open directory",
482                                        QDir::current().dirName()).toString();
483     QString dir =
484       QFileDialog::getExistingDirectory(mw,
485                                         tr("Open directory"),
486                                         start_dir,
487                                         QFileDialog::ShowDirsOnly
488                                         | QFileDialog::DontResolveSymlinks);
489 
490     if (!dir.isEmpty()) {
491       QFileInfo fileinfo(dir);
492       if (fileinfo.isDir() && fileinfo.isReadable())
493       {
494         settings.setValue("Open directory",
495           fileinfo.absoluteDir().absolutePath());
496         QApplication::setOverrideCursor(Qt::WaitCursor);
497         QApplication::processEvents();
498         loadDCM(dir);
499         QApplication::restoreOverrideCursor();
500       }
501     }
502 
503   }
504 
connectNewViewer(QObject * o)505   void connectNewViewer(QObject* o)
506   {
507     Q_FOREACH(Controls c, group_map.values())
508     {
509       o->installEventFilter(c.x_item);
510       o->installEventFilter(c.y_item);
511       o->installEventFilter(c.z_item);
512     }
513   }
514 private:
515   CGAL::qglviewer::Vec first_offset;
516   bool is_gray;
517   Messages_interface* message_interface;
518   QMessageBox msgBox;
519   QAction* planeSwitch;
520   QWidget *x_control, *y_control, *z_control;
521   QSlider *x_slider, *y_slider, *z_slider;
522   QLineEdit*x_cubeLabel, *y_cubeLabel, *z_cubeLabel;
523   QHBoxLayout *x_box, *y_box, *z_box;
524   PixelReader pxr_;
525   Ui::ImagePrecisionDialog ui;
526 
527   CGAL::Three::Scene_group_item* group;
528   std::vector<Volume_plane_thread*> threads;
529   struct Controls{
530     CGAL::Three::Scene_item* group;
531     CGAL::Three::Scene_item* x_item;
532     CGAL::Three::Scene_item* y_item;
533     CGAL::Three::Scene_item* z_item;
534     int x_value;
535     int y_value;
536     int z_value;
537   };
538   Controls *current_control;
539   QMap<CGAL::Three::Scene_item*, Controls> group_map;
540   unsigned int intersection_id;
541   bool loadDCM(QString filename);
542   Image* createDCMImage(QString dirname);
createOrGetDockLayout()543   QLayout* createOrGetDockLayout() {
544     QLayout* layout = nullptr;
545     QDockWidget* controlDockWidget = mw->findChild<QDockWidget*>("volumePlanesControl");;
546 
547     if(!controlDockWidget) {
548       controlDockWidget = new QDockWidget(mw);
549       controlDockWidget->setObjectName("volumePlanesControl");
550       QWidget* content = new QWidget(controlDockWidget);
551       layout = new QVBoxLayout(content);
552       layout->setObjectName("vpSliderLayout");
553       controlDockWidget->setWindowTitle("Volume Planes Control");
554       mw->addDockWidget(Qt::LeftDockWidgetArea, controlDockWidget);
555 
556       QWidget* vlabels = new QWidget(content);
557       layout->addWidget(vlabels);
558       layout->setAlignment(Qt::AlignLeft);
559       QHBoxLayout* vbox = new QHBoxLayout(vlabels);
560       vbox->setAlignment(Qt::AlignLeft);
561       QLabel* text = new QLabel(vlabels);
562       text->setText("Value of that pixel:");
563       QLabel* help = new QLabel(vlabels);
564       help->setText("Cut planes for the selected image:");
565       QLabel* x = new QLabel(vlabels);
566 
567       connect(&pxr_, SIGNAL(x(QString)), x, SLOT(setText(QString)));
568 
569       layout->addWidget(help); vbox->addWidget(text); vbox->addWidget(x);
570       controlDockWidget->setWidget(content);
571       controlDockWidget->hide();
572 
573     } else {
574       layout = controlDockWidget->findChild<QLayout*>("vpSliderLayout");
575       controlDockWidget->show();
576       controlDockWidget->raise();
577     }
578 
579     return layout;
580   }
581 
createPlanes(Scene_image_item * seg_img)582   void createPlanes(Scene_image_item* seg_img) {
583     QApplication::setOverrideCursor(Qt::WaitCursor);
584     //Control widgets creation
585     QLayout* layout = createOrGetDockLayout();
586     QRegExpValidator* validator = new QRegExpValidator(QRegExp("\\d*"), this);
587     bool show_sliders = true;
588     if(x_control == nullptr)
589     {
590       x_control = new QWidget;
591       x_box = new QHBoxLayout(x_control);
592       layout->addWidget(x_control);
593 
594       QLabel* label = new QLabel(x_control);
595       label->setStyleSheet("QLabel { color : red; }");
596       label->setText("X Slice");
597 
598       x_cubeLabel = new QLineEdit(x_control);
599 
600       // Find the right width for the label to accommodate at least 9999
601       QFontMetrics metric = x_cubeLabel->fontMetrics();
602 #if QT_VERSION >= QT_VERSION_CHECK(5, 11, 0)
603       x_cubeLabel->setFixedWidth(metric.horizontalAdvance(QString(".9999.")));
604 #else
605       x_cubeLabel->setFixedWidth(metric.width(QString(".9999.")));
606 #endif
607       x_cubeLabel->setText("0");
608       x_cubeLabel->setValidator(validator);
609 
610       x_slider = new QSlider(mw);
611 
612       x_box->addWidget(label);
613       x_box->addWidget(x_slider);
614       x_box->addWidget(x_cubeLabel);
615       show_sliders &= seg_img->image()->xdim() > 1;
616     }
617 
618     if(y_control == nullptr)
619     {
620       y_control = new QWidget;
621       y_box = new QHBoxLayout(y_control);
622       layout->addWidget(y_control);
623 
624       QLabel* label = new QLabel(y_control);
625       label->setStyleSheet("QLabel { color : green; }");
626       label->setText("Y Slice");
627 
628       y_cubeLabel = new QLineEdit(y_control);
629 
630       // Find the right width for the label to accommodate at least 9999
631       QFontMetrics metric = y_cubeLabel->fontMetrics();
632 #if QT_VERSION >= QT_VERSION_CHECK(5, 11, 0)
633       y_cubeLabel->setFixedWidth(metric.horizontalAdvance(QString(".9999.")));
634 #else
635       y_cubeLabel->setFixedWidth(metric.width(QString(".9999.")));
636 #endif
637       y_cubeLabel->setText("0");
638       y_cubeLabel->setValidator(validator);
639       y_slider = new QSlider(mw);
640 
641       y_box->addWidget(label);
642       y_box->addWidget(y_slider);
643       y_box->addWidget(y_cubeLabel);
644       show_sliders &= seg_img->image()->ydim() > 1;
645     }
646 
647     if(z_control == nullptr)
648     {
649       z_control = new QWidget;
650       z_box = new QHBoxLayout(z_control);
651       layout->addWidget(z_control);
652 
653       QLabel* label = new QLabel(z_control);
654       label->setStyleSheet("QLabel { color : blue; }");
655       label->setText("Z Slice");
656 
657       z_cubeLabel = new QLineEdit(z_control);
658 
659       // Find the right width for the label to accommodate at least 9999
660       QFontMetrics metric = z_cubeLabel->fontMetrics();
661 #if QT_VERSION >= QT_VERSION_CHECK(5, 11, 0)
662       z_cubeLabel->setFixedWidth(metric.horizontalAdvance(QString(".9999.")));
663 #else
664       z_cubeLabel->setFixedWidth(metric.width(QString(".9999.")));
665 #endif
666       z_cubeLabel->setText("0");
667       z_cubeLabel->setValidator(validator);
668       z_slider = new QSlider(mw);
669 
670       z_box->addWidget(label);
671       z_box->addWidget(z_slider);
672       z_box->addWidget(z_cubeLabel);
673       show_sliders &= seg_img->image()->zdim() > 1;
674     }
675     x_control->setEnabled(show_sliders);
676     y_control->setEnabled(show_sliders);
677     z_control->setEnabled(show_sliders);
678 
679     if(!(seg_img == nullptr)) {
680       const CGAL::Image_3* img = seg_img->image();
681       CGAL_IMAGE_IO_CASE(img->image(), this->launchAdders<Word>(seg_img, seg_img->name()))
682 
683           Volume_plane_intersection* i
684           = new Volume_plane_intersection(img->xdim() * img->vx()-1,
685                                           img->ydim() * img->vy()-1,
686                                           img->zdim() * img->vz()-1,
687                                           img->image()->tx,
688                                           img->image()->ty,
689                                           img->image()->tz);
690       this->intersection_id = scene->addItem(i);
691       scene->changeGroup(i, group);
692       group->lockChild(i);
693     } else {
694       QMessageBox::warning(mw, tr("Something went wrong"), tr("Selected a suitable Object but couldn't get an image pointer."));
695       return;
696     }
697   }
698 
699 
700   template<typename Word>
launchAdders(Scene_image_item * seg_img,const QString & name)701   void launchAdders(Scene_image_item* seg_img, const QString& name) {
702     const CGAL::Image_3* img = seg_img->image();
703     const Word* begin = (const Word*)img->data();
704     const Word* end = (const Word*)img->data() + img->size();
705 
706     std::pair<const Word, const Word> minmax(*std::min_element(begin, end), *std::max_element(begin, end));
707 
708     Clamp_to_one_zero_range clamper = { minmax };
709 
710     switchReaderConverter< Word >(minmax);
711 
712     Volume_plane<x_tag> *x_item = new Volume_plane<x_tag>(img->image()->tx,img->image()->ty, img->image()->tz);
713     Volume_plane<y_tag> *y_item = new Volume_plane<y_tag>(img->image()->tx,img->image()->ty, img->image()->tz);
714     Volume_plane<z_tag> *z_item = new Volume_plane<z_tag>(img->image()->tx,img->image()->ty, img->image()->tz);
715 
716     x_item->setProperty("img",QVariant::fromValue((void*)seg_img));
717     y_item->setProperty("img",QVariant::fromValue((void*)seg_img));
718     z_item->setProperty("img",QVariant::fromValue((void*)seg_img));
719 
720     x_item->setColor(QColor("red"));
721     y_item->setColor(QColor("green"));
722     z_item->setColor(QColor("blue"));
723     Q_FOREACH(CGAL::QGLViewer* viewer, CGAL::QGLViewer::QGLViewerPool())
724     {
725       viewer->installEventFilter(x_item);
726       viewer->installEventFilter(y_item);
727       viewer->installEventFilter(z_item);
728     }
729 
730     connect(x_item, SIGNAL(selected(CGAL::Three::Scene_item*)), this, SLOT(select_plane(CGAL::Three::Scene_item*)));
731     connect(y_item, SIGNAL(selected(CGAL::Three::Scene_item*)), this, SLOT(select_plane(CGAL::Three::Scene_item*)));
732     connect(z_item, SIGNAL(selected(CGAL::Three::Scene_item*)), this, SLOT(select_plane(CGAL::Three::Scene_item*)));
733     scene->setSelectedItem(-1);
734     group = new Scene_group_item(QString("Planes for %1").arg(seg_img->name()));
735     connect(group, SIGNAL(aboutToBeDestroyed()),
736             this, SLOT(erase_group()));
737     scene->addItem(group);
738     Controls c;
739     c.group = group;
740     c.x_item = x_item;
741     c.y_item = y_item;
742     c.z_item = z_item;
743     c.x_value = 0;
744     c.y_value = 0;
745     c.z_value = 0;
746     group_map[seg_img] = c;
747     current_control = &group_map[seg_img];
748     connect(seg_img, SIGNAL(aboutToBeDestroyed()),
749             this, SLOT(on_img_detroyed()));
750 
751     threads.push_back(new X_plane_thread<Word>(x_item, img, clamper, name));
752 
753     connect(threads.back(), SIGNAL(finished(Volume_plane_thread*)), this, SLOT(addVP(Volume_plane_thread*)));
754     threads.back()->start();
755     threads.push_back(new Y_plane_thread<Word>(y_item,img, clamper, name));
756     connect(threads.back(), SIGNAL(finished(Volume_plane_thread*)), this, SLOT(addVP(Volume_plane_thread*)));
757     threads.back()->start();
758 
759     threads.push_back(new Z_plane_thread<Word>(z_item,img, clamper, name));
760     connect(threads.back(), SIGNAL(finished(Volume_plane_thread*)), this, SLOT(addVP(Volume_plane_thread*)));
761     threads.back()->start();
762 
763     first_offset = Three::mainViewer()->offset();
764 
765   }
766   template<typename T>
switchReaderConverter(std::pair<T,T> minmax)767   void switchReaderConverter(std::pair<T, T> minmax) {
768     switchReaderConverter(minmax, typename boost::is_integral<T>::type());
769   }
770 
771   template<typename T>
switchReaderConverter(std::pair<T,T> minmax,boost::true_type)772   void switchReaderConverter(std::pair<T, T> minmax, boost::true_type) {
773     // IntConverter
774     IntConverter x = { minmax }; pxr_.setIC(x);
775   }
776 
777   template<typename T>
switchReaderConverter(std::pair<T,T> minmax,boost::false_type)778   void switchReaderConverter(std::pair<T, T> minmax, boost::false_type) {
779     // IntConverter
780     DoubleConverter x = { minmax }; pxr_.setFC(x);
781   }
782 
783 private Q_SLOTS:
select_plane(CGAL::Three::Scene_item * item)784   void select_plane(CGAL::Three::Scene_item* item)
785   {
786     Scene_image_item* img = (Scene_image_item*)item->property("img").value<void*>();
787     if(img)
788       scene->setSelectedItem(scene->item_id(img));
789   }
790   //updates the msgBox
update_msgBox()791   void update_msgBox()
792   {
793     static int nbPlanes =0;
794     nbPlanes ++;
795     msgBox.setText(QString("Planes created : %1/3").arg(nbPlanes));
796     if(nbPlanes == 3)
797     {
798       const CGAL::qglviewer::Vec offset = Three::mainViewer()->offset();
799       if(offset != first_offset)
800       {
801         for(int i=0; i<scene->numberOfEntries(); ++i)
802         {
803           scene->item(i)->invalidateOpenGLBuffers();
804         }
805       }
806       msgBox.hide();
807       nbPlanes = 0;
808       QApplication::restoreOverrideCursor();
809     }
810   }
811   // Avoids the segfault after the deletion of an item
erase_group()812   void erase_group()
813   {
814 
815     CGAL::Three::Scene_group_item* group_item = qobject_cast<CGAL::Three::Scene_group_item*>(sender());
816     if(group_item)
817     {
818 
819       Q_FOREACH(CGAL::Three::Scene_item* key, group_map.keys())
820       {
821         if(group_map[key].group == group_item)
822         {
823           group_map[key].x_item = nullptr;
824           group_map[key].y_item = nullptr;
825           group_map[key].z_item = nullptr;
826           group_map.remove(key);
827           break;
828         }
829       }
830     }
831       //try to re-connect to another group
832       if(!group_map.isEmpty())
833       {
834         int id = scene->item_id(group_map.keys().first());
835         connect_controls(id);
836       }
837   }
838     //destroy planes on image deletion
on_img_detroyed()839     void on_img_detroyed()
840     {
841       Scene_image_item* img_item = qobject_cast<Scene_image_item*>(sender());
842       if(img_item)
843       {
844         Scene_group_item* group = qobject_cast<Scene_group_item*>(group_map[img_item].group);
845         if(!group)
846           return;
847         group_map[img_item].x_item = nullptr;
848         group_map[img_item].y_item = nullptr;
849         group_map[img_item].z_item = nullptr;
850         disconnect(group_map[img_item].group, SIGNAL(aboutToBeDestroyed()),
851                            this, SLOT(erase_group()));
852         group_map.remove(img_item);
853         QList<int> deletion;
854         Q_FOREACH(Scene_interface::Item_id id, group->getChildren())
855         {
856           Scene_item* child = group->getChild(id);
857           group->unlockChild(child);
858           deletion.append(scene->item_id(child));
859         }
860         deletion.append(scene->item_id(group));
861         scene->erase(deletion);
862       }
863     //try to re-connect to another group
864     if(!group_map.isEmpty())
865     {
866       int id = scene->item_id(group_map.keys().first());
867       connect_controls(id);
868     }
869   }
connect_controls(int id)870   void connect_controls(int id)
871   {
872     CGAL::Three::Scene_item* sel_itm = scene->item(id);
873     if(!sel_itm)
874       return;
875     if(!group_map.contains(sel_itm)) //the planes are not yet created or the selected item is not a segmented_image
876     {
877       Scene_image_item* img = (Scene_image_item*)sel_itm->property("img").value<void*>();
878       if(img)
879         sel_itm = img;
880       else
881         return;
882     }
883     Controls c = group_map[sel_itm];
884     current_control = &group_map[sel_itm];
885     bool show_sliders = true;
886     // x line
887     if(c.x_item != nullptr)
888     {
889       Volume_plane_interface* x_plane = qobject_cast<Volume_plane_interface*>(c.x_item);
890       if(x_slider)
891         delete x_slider;
892       x_slider = new Plane_slider(x_plane->translationVector(), scene->item_id(x_plane), scene, x_plane->manipulatedFrame(),
893                                   Qt::Horizontal, x_control);
894       x_slider->setRange(0, (x_plane->cDim() - 1) * 100);
895       connect(x_slider, SIGNAL(realChange(int)), this, SLOT(setXNum(int)));
896       connect(x_plane, SIGNAL(manipulated(int)), this, SLOT(setXNum(int)));
897       connect(x_cubeLabel, SIGNAL(textEdited(QString)), this, SLOT(XTextEdited(QString)));
898       connect(x_plane, SIGNAL(aboutToBeDestroyed()), this, SLOT(destroy_x_item()));
899       connect(x_slider, SIGNAL(realChange(int)), this, SLOT(set_value()));
900       connect(x_slider, SIGNAL(sliderMoved(int)), x_slider, SLOT(updateFramePosition()));
901       x_slider->setValue(c.x_value);
902 
903       x_box->addWidget(x_slider);
904       x_box->addWidget(x_cubeLabel);
905       show_sliders &= qobject_cast<Scene_image_item*>(sel_itm)->image()->xdim() > 1;
906     }
907     //y line
908     if(c.y_item != nullptr)
909     {
910       Volume_plane_interface* y_plane = qobject_cast<Volume_plane_interface*>(c.y_item);
911       if(y_slider)
912         delete y_slider;
913       y_slider = new Plane_slider(y_plane->translationVector(), scene->item_id(y_plane), scene, y_plane->manipulatedFrame(),
914                                   Qt::Horizontal, z_control);
915       y_slider->setRange(0, (y_plane->cDim() - 1) * 100);
916       connect(y_slider, SIGNAL(realChange(int)), this, SLOT(setYNum(int)));
917       connect(y_plane, SIGNAL(manipulated(int)), this, SLOT(setYNum(int)));
918       connect(y_cubeLabel, SIGNAL(textEdited(QString)), this, SLOT(YTextEdited(QString)));
919       connect(y_plane, SIGNAL(aboutToBeDestroyed()), this, SLOT(destroy_y_item()));
920       connect(y_slider, SIGNAL(realChange(int)), this, SLOT(set_value()));
921       connect(y_slider, SIGNAL(sliderMoved(int)), y_slider, SLOT(updateFramePosition()));
922       y_slider->setValue(c.y_value);
923       y_box->addWidget(y_slider);
924       y_box->addWidget(y_cubeLabel);
925       show_sliders &= qobject_cast<Scene_image_item*>(sel_itm)->image()->ydim() > 1;
926     }
927     // z line
928     if(c.z_item != nullptr)
929     {
930       Volume_plane_interface* z_plane = qobject_cast<Volume_plane_interface*>(c.z_item);
931       if(z_slider)
932         delete z_slider;
933       z_slider = new Plane_slider(z_plane->translationVector(), scene->item_id(z_plane), scene, z_plane->manipulatedFrame(),
934                                   Qt::Horizontal, z_control);
935       z_slider->setRange(0, (z_plane->cDim() - 1) * 100);
936       connect(z_slider, SIGNAL(realChange(int)), this, SLOT(setZNum(int)));
937       connect(z_plane, SIGNAL(manipulated(int)), this, SLOT(setZNum(int)));
938       connect(z_cubeLabel, SIGNAL(textEdited(QString)), this, SLOT(ZTextEdited(QString)));
939       connect(z_plane, SIGNAL(aboutToBeDestroyed()), this, SLOT(destroy_z_item()));
940       connect(z_slider, SIGNAL(sliderMoved(int)), z_slider, SLOT(updateFramePosition()));
941       connect(z_slider, SIGNAL(realChange(int)), this, SLOT(set_value()));
942       z_slider->setValue(c.z_value);
943       z_box->addWidget(z_slider);
944       z_box->addWidget(z_cubeLabel);
945       show_sliders &= qobject_cast<Scene_image_item*>(sel_itm)->image()->zdim() > 1;
946     }
947 
948       x_control->setEnabled(show_sliders);
949       y_control->setEnabled(show_sliders);
950       z_control->setEnabled(show_sliders);
951   }
952 //Keeps the position of the planes for the next time
set_value()953   void set_value()
954   {
955     current_control->x_value = x_slider->value();
956     current_control->y_value = y_slider->value();
957     current_control->z_value = z_slider->value();
958   }
959 
destroy_x_item()960   void destroy_x_item()
961   {
962     current_control->x_item = nullptr;
963     if(group_map.isEmpty())
964       x_control->hide();
965   }
destroy_y_item()966   void destroy_y_item()
967   {
968     current_control->y_item = nullptr;
969     if(group_map.isEmpty())
970       y_control->hide();
971   }
destroy_z_item()972   void destroy_z_item()
973   {
974     current_control->z_item = nullptr;
975     if(group_map.isEmpty())
976       z_control->hide();
977   }
978 
979 };
980 
981 
nameFilters() const982 QString Io_image_plugin::nameFilters() const {
983   return QString("Inrimage files (*.inr *.inr.gz) ;; "
984                  "Analyze files (*.hdr *.img *img.gz) ;; "
985                  "Stanford Exploration Project files (*.H *.HH)");
986 }
987 
988 
canLoad(QFileInfo) const989 bool Io_image_plugin::canLoad(QFileInfo) const {
990   return true;
991 }
992 
993 template<typename Word>
convert(Image * image)994 void convert(Image* image)
995 {
996   float *f_data = (float*)ImageIO_alloc(image->xdim()*image->ydim()*image->zdim()*sizeof(float));
997   Word* d_data = (Word*)(image->data());
998   //convert image from double to float
999   for(std::size_t x = 0; x<image->xdim(); ++x)
1000     for(std::size_t y = 0; y<image->ydim(); ++y)
1001       for(std::size_t z = 0; z<image->zdim(); ++z)
1002       {
1003         std::size_t i =(z * image->ydim() + y) * image->xdim() + x;
1004         f_data[i] =(float)d_data[i];
1005       }
1006   ImageIO_free(d_data);
1007   image->image()->data = (void*)f_data;
1008   image->image()->wdim = 4;
1009   image->image()->wordKind = WK_FLOAT;
1010 }
1011 
1012 QList<Scene_item*>
load(QFileInfo fileinfo,bool & ok,bool add_to_scene)1013 Io_image_plugin::load(QFileInfo fileinfo, bool& ok, bool add_to_scene)
1014 {
1015   ok = true;
1016   QApplication::restoreOverrideCursor();
1017   Image* image = new Image;
1018   if(fileinfo.suffix() != "H" && fileinfo.suffix() != "HH" &&
1019      !image->read(fileinfo.filePath().toUtf8()))
1020     {
1021       QMessageBox qmb(QMessageBox::NoIcon,
1022                       "Raw Dialog",
1023                       tr("Error with file <tt>%1</tt>:\n"
1024                          "unknown file format!\n"
1025                          "\n"
1026                          "Open it as a raw image?").arg(fileinfo.fileName()),
1027                       QMessageBox::Yes|QMessageBox::No);
1028 
1029       bool success = true;
1030       if(qmb.exec() == QMessageBox::Yes) {
1031         Raw_image_dialog raw_dialog;
1032         raw_dialog.label_file_size->setText(QString("%1 B").arg(fileinfo.size()));
1033         raw_dialog.buttonBox->button(QDialogButtonBox::Open)->setEnabled(false);
1034         if( raw_dialog.exec() ){
1035 
1036           QApplication::setOverrideCursor(Qt::WaitCursor);
1037           QApplication::processEvents();
1038 
1039           if(image->read_raw(fileinfo.filePath().toUtf8(),
1040                              raw_dialog.dim_x->value(),
1041                              raw_dialog.dim_y->value(),
1042                              raw_dialog.dim_z->value(),
1043                              raw_dialog.spacing_x->value(),
1044                              raw_dialog.spacing_y->value(),
1045                              raw_dialog.spacing_z->value(),
1046                              raw_dialog.offset->value(),
1047                              raw_dialog.image_word_size(),
1048                              raw_dialog.image_word_kind(),
1049                              raw_dialog.image_sign())
1050              ){
1051             switch(raw_dialog.image_word_kind())
1052             {
1053             case WK_FLOAT:
1054               is_gray = true;
1055               convert<double>(image);
1056               break;
1057             case WK_FIXED:
1058             {
1059               switch(raw_dialog.image_word_size())
1060               {
1061               case 2:
1062                 is_gray = true;
1063                 convert<short>(image);
1064                 break;
1065               case 4:
1066                 is_gray = true;
1067                 convert<int>(image);
1068                 break;
1069               default:
1070                 is_gray = false;
1071                 break;
1072               }
1073               break;
1074             }
1075             default:
1076               break;
1077             }
1078             QSettings settings;
1079             settings.beginGroup(QUrl::toPercentEncoding(fileinfo.absoluteFilePath()));
1080             settings.setValue("is_raw", true);
1081             settings.setValue("dim_x", raw_dialog.dim_x->value());
1082             settings.setValue("dim_y", raw_dialog.dim_y->value());
1083             settings.setValue("dim_z", raw_dialog.dim_z->value());
1084             settings.setValue("spacing_x", raw_dialog.spacing_x->value());
1085             settings.setValue("spacing_y", raw_dialog.spacing_y->value());
1086             settings.setValue("spacing_z", raw_dialog.spacing_z->value());
1087             settings.setValue("offset", raw_dialog.offset->value());
1088             settings.setValue("wdim", QVariant::fromValue(raw_dialog.image_word_size()));
1089             settings.setValue("wk", raw_dialog.image_word_kind());
1090             settings.setValue("sign", raw_dialog.image_sign());
1091             settings.endGroup();
1092           }else {
1093             success = false;
1094           }
1095         }else {
1096           success = false;
1097         }
1098       }else {
1099         success = false;
1100       }
1101       if(!success){
1102         ok = false;
1103         delete image;
1104         return QList<Scene_item*>();
1105       }
1106     }
1107   //read a sep file
1108   else if(fileinfo.suffix() == "H" || fileinfo.suffix() == "HH")
1109   {
1110     CGAL::SEP_to_ImageIO<float> reader(fileinfo.filePath().toUtf8().data());
1111     *image = *reader.cgal_image();
1112     is_gray = true;
1113   }
1114   // Get display precision
1115   QDialog dialog;
1116   ui.setupUi(&dialog);
1117 
1118   connect(ui.buttonBox, SIGNAL(accepted()), &dialog, SLOT(accept()));
1119   connect(ui.buttonBox, SIGNAL(rejected()), &dialog, SLOT(reject()));
1120   connect(ui.imageType, SIGNAL(currentIndexChanged(int)),
1121           this, SLOT(on_imageType_changed(int)));
1122   dialog.setWindowFlags(Qt::Dialog|Qt::CustomizeWindowHint|Qt::WindowCloseButtonHint);
1123 
1124   // Add precision values to the dialog
1125   for ( int i=1 ; i<9 ; ++i )
1126   {
1127     QString s = tr("1:%1").arg(i*i*i);
1128     ui.precisionList->addItem(s);
1129   }
1130 
1131   //Adds Image type
1132   ui.imageType->addItem(QString("Segmented image"));
1133   ui.imageType->addItem(QString("Gray-level image"));
1134 
1135   QString type;
1136   int voxel_scale = 0;
1137   // Open window
1138   QApplication::restoreOverrideCursor();
1139   if(!is_gray)
1140   {
1141     int return_code = dialog.exec();
1142     if(return_code != QDialog::Accepted)
1143     {
1144       delete image;
1145       ok = false;
1146       return QList<Scene_item*>();
1147     }
1148 
1149     // Get selected precision
1150     voxel_scale = ui.precisionList->currentIndex() + 1;
1151 
1152     //Get the image type
1153     type = ui.imageType->currentText();
1154   }
1155   else
1156     type = "Gray-level image";
1157   QApplication::setOverrideCursor(Qt::WaitCursor);
1158   QApplication::processEvents();
1159   Scene_image_item* image_item;
1160   if(type == "Gray-level image")
1161   {
1162     //Create planes
1163     image_item = new Scene_image_item(image,0, true);
1164     image_item->setName(fileinfo.baseName());
1165     msgBox.setText("Planes created : 0/3");
1166     msgBox.setStandardButtons(QMessageBox::NoButton);
1167     msgBox.show();
1168 
1169     createPlanes(image_item);
1170   }
1171   else
1172     image_item = new Scene_image_item(image,voxel_scale, false);
1173   image_item->setName(fileinfo.baseName());
1174   if(add_to_scene)
1175     CGAL::Three::Three::scene()->addItem(image_item);
1176   return QList<Scene_item*>() << image_item;
1177 }
1178 
canSave(const CGAL::Three::Scene_item * item)1179 bool Io_image_plugin::canSave(const CGAL::Three::Scene_item* item)
1180 {
1181   return qobject_cast<const Scene_image_item*>(item);
1182 }
1183 
loadDCM(QString dirname)1184 bool Io_image_plugin::loadDCM(QString dirname)
1185 {
1186   QApplication::restoreOverrideCursor();
1187 #ifdef CGAL_USE_VTK
1188   QFileInfo fileinfo;
1189   fileinfo.setFile(dirname);
1190   bool result = true;
1191   if(!fileinfo.isReadable())
1192   {
1193     QMessageBox::warning(mw, mw->windowTitle(),
1194                          tr("Cannot read directory <tt>%1</tt>!").arg(dirname));
1195     CGAL::Three::Three::warning(tr("Opening of directory %1 failed!").arg(dirname));
1196     result = false;
1197   }
1198   else
1199   {
1200     // Get display precision
1201     QDialog dialog;
1202     ui.setupUi(&dialog);
1203 
1204     connect(ui.buttonBox, SIGNAL(accepted()), &dialog, SLOT(accept()));
1205     connect(ui.buttonBox, SIGNAL(rejected()), &dialog, SLOT(reject()));
1206     connect(ui.imageType, SIGNAL(currentIndexChanged(int)),
1207             this, SLOT(on_imageType_changed(int)));
1208 
1209 
1210     // Add precision values to the dialog
1211     for ( int i=1 ; i<9 ; ++i )
1212     {
1213       QString s = tr("1:%1").arg(i*i*i);
1214       ui.precisionList->addItem(s);
1215     }
1216 
1217     //Adds Image type
1218     ui.imageType->addItem(QString("Segmented image"));
1219     ui.imageType->addItem(QString("Gray-level image"));
1220 
1221 
1222     // Open window
1223     QApplication::restoreOverrideCursor();
1224     int return_code = dialog.exec();
1225     if(return_code != QDialog::Accepted)
1226     {
1227       return false;
1228     }
1229     QApplication::setOverrideCursor(Qt::WaitCursor);
1230     QApplication::processEvents();
1231 
1232     // Get selected precision
1233     int voxel_scale = ui.precisionList->currentIndex() + 1;
1234 
1235     //Get the image type
1236     QString type = ui.imageType->currentText();
1237     Scene_image_item* image_item;
1238     if(type == "Gray-level image")
1239     {
1240 
1241       Image *image = createDCMImage(dirname);
1242       if(image->image() == nullptr)
1243       {
1244         QMessageBox::warning(mw, mw->windowTitle(),
1245                              tr("Error with file <tt>%1/</tt>:\nunknown file format!").arg(dirname));
1246         CGAL::Three::Three::warning(tr("Opening of file %1/ failed!").arg(dirname));
1247         result = false;
1248       }
1249       else
1250       {
1251         CGAL::Three::Three::information(tr("File %1/ successfully opened.").arg(dirname));
1252       }
1253       if(result)
1254       {
1255       //Create planes
1256       image_item = new Scene_image_item(image,125, true);
1257       msgBox.setText("Planes created : 0/3");
1258       msgBox.setStandardButtons(QMessageBox::NoButton);
1259       msgBox.show();
1260       createPlanes(image_item);
1261       image_item->setName(fileinfo.baseName());
1262       scene->addItem(image_item);
1263       }
1264     }
1265     else
1266     {
1267       Image *image = createDCMImage(dirname);
1268       if(image->image() == nullptr)
1269       {
1270         QMessageBox::warning(mw, mw->windowTitle(),
1271                              tr("Error with file <tt>%1/</tt>:\nunknown file format!").arg(dirname));
1272         CGAL::Three::Three::warning(tr("Opening of file %1/ failed!").arg(dirname));
1273         result = false;
1274       }
1275       else
1276       {
1277         CGAL::Three::Three::information(tr("File %1/ successfully opened.").arg(dirname));
1278       }
1279       if(result)
1280       {
1281         image_item = new Scene_image_item(image,voxel_scale, false);
1282         image_item->setName(fileinfo.baseName());
1283         scene->addItem(image_item);
1284       }
1285     }
1286   }
1287   return result;
1288 #else
1289   CGAL::Three::Three::warning("You need VTK to read a DCM file");
1290   CGAL_USE(dirname);
1291   return false;
1292 #endif
1293 }
createDCMImage(QString dirname)1294 Image* Io_image_plugin::createDCMImage(QString dirname)
1295 {
1296   Image* image = nullptr;
1297 #ifdef CGAL_USE_VTK
1298   vtkNew<vtkDICOMImageReader> dicom_reader;
1299   dicom_reader->SetDirectoryName(dirname.toUtf8());
1300 
1301   auto executive =
1302     vtkDemandDrivenPipeline::SafeDownCast(dicom_reader->GetExecutive());
1303   if (executive)
1304   {
1305     executive->SetReleaseDataFlag(0, 0); // where 0 is the port index
1306   }
1307 
1308   vtkNew<vtkImageGaussianSmooth> smoother;
1309   smoother->SetStandardDeviations(1., 1., 1.);
1310   smoother->SetInputConnection(dicom_reader->GetOutputPort());
1311   smoother->Update();
1312   auto vtk_image = smoother->GetOutput();
1313   vtk_image->Print(std::cerr);
1314   image = new Image;
1315   *image = CGAL::IO::read_vtk_image_data(vtk_image); // copy the image data
1316 #else
1317   CGAL::Three::Three::warning("You need VTK to read a DCM file");
1318   CGAL_USE(dirname);
1319 #endif
1320   return image;
1321 
1322 }
1323 
1324 #include "Io_image_plugin.moc"
1325