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