From b2f3eedb2f9dbc65a887e5eae6a392838b4077c6 Mon Sep 17 00:00:00 2001 From: Andreas Fabri Date: Thu, 24 Nov 2022 16:14:11 +0000 Subject: [PATCH 01/13] 3d Demo: Try to read bmp files --- .../include/CGAL/IO/read_vtk_image_data.h | 6 +- .../Plugins/Mesh_3/Io_image_plugin.cpp | 76 +++++++++++++++---- 2 files changed, 64 insertions(+), 18 deletions(-) diff --git a/CGAL_ImageIO/include/CGAL/IO/read_vtk_image_data.h b/CGAL_ImageIO/include/CGAL/IO/read_vtk_image_data.h index ccce66f3ca6..3b7efa6c4df 100644 --- a/CGAL_ImageIO/include/CGAL/IO/read_vtk_image_data.h +++ b/CGAL_ImageIO/include/CGAL/IO/read_vtk_image_data.h @@ -85,9 +85,9 @@ read_vtk_image_data(vtkImageData* vtk_image, Image_3::Own owning = Image_3::OWN_ CGAL_assertion(vtk_image->GetPointData()->GetScalars()->GetNumberOfTuples() == dims[0]*dims[1]*dims[2]); if(owning == Image_3::OWN_THE_DATA) { image->data = ::ImageIO_alloc(dims[0]*dims[1]*dims[2]*image->wdim); - // std::cerr << "GetNumberOfTuples()=" << vtk_image->GetPointData()->GetScalars()->GetNumberOfTuples() - // << "\nimage->size()=" << dims[0]*dims[1]*dims[2] - // << "\nwdim=" << image->wdim << '\n'; + std::cerr << "GetNumberOfTuples()=" << vtk_image->GetPointData()->GetScalars()->GetNumberOfTuples() + << "\nimage->size()=" << dims[0]*dims[1]*dims[2] + << "\nwdim=" << image->wdim << '\n'; vtk_image->GetPointData()->GetScalars()->ExportToVoidPointer(image->data); } else { image->data = vtk_image->GetPointData()->GetScalars()->GetVoidPointer(0); diff --git a/Polyhedron/demo/Polyhedron/Plugins/Mesh_3/Io_image_plugin.cpp b/Polyhedron/demo/Polyhedron/Plugins/Mesh_3/Io_image_plugin.cpp index f74562e6e58..671850655d6 100644 --- a/Polyhedron/demo/Polyhedron/Plugins/Mesh_3/Io_image_plugin.cpp +++ b/Polyhedron/demo/Polyhedron/Plugins/Mesh_3/Io_image_plugin.cpp @@ -49,6 +49,7 @@ #include #include +#include #include #include @@ -60,8 +61,10 @@ #include #include +#include #include #include +#include #include #include #include @@ -1314,24 +1317,67 @@ Image* Io_image_plugin::createDCMImage(QString dirname) { Image* image = nullptr; #ifdef CGAL_USE_VTK - vtkNew dicom_reader; - dicom_reader->SetDirectoryName(dirname.toUtf8()); - auto executive = - vtkDemandDrivenPipeline::SafeDownCast(dicom_reader->GetExecutive()); - if (executive) - { - executive->SetReleaseDataFlag(0, 0); // where 0 is the port index + bool is_dcm = false; + bool is_bmp = false; + + vtkStringArray* files = vtkStringArray::New(); + boost::filesystem::path p(dirname.toUtf8().data()); + for(boost::filesystem::directory_entry& x : boost::filesystem::directory_iterator(p)){ + std::string s(x.path().extension().string()); + if(s == std::string(".dcm") || (s == std::string(".DCM"))){ is_dcm = true;} + if(s == std::string(".bmp") || (s == std::string(".BMP"))){ is_bmp = true;} + std::cout << x.path().string() << std::endl; + files->InsertNextValue(x.path().string()); + } + + if(is_dcm){ + vtkNew dicom_reader; + dicom_reader->SetDirectoryName(dirname.toUtf8()); + + auto executive = + vtkDemandDrivenPipeline::SafeDownCast(dicom_reader->GetExecutive()); + if (executive) + { + executive->SetReleaseDataFlag(0, 0); // where 0 is the port index + } + + vtkNew smoother; + smoother->SetStandardDeviations(1., 1., 1.); + smoother->SetInputConnection(dicom_reader->GetOutputPort()); + smoother->Update(); + auto vtk_image = smoother->GetOutput(); + vtk_image->Print(std::cerr); + image = new Image; + *image = CGAL::IO::read_vtk_image_data(vtk_image); // copy the + // image data + } + + if(is_bmp){ + vtkNew bmp_reader; + bmp_reader->SetFileNames(files); + + auto executive = + vtkDemandDrivenPipeline::SafeDownCast(bmp_reader->GetExecutive()); + if (executive) + { + executive->SetReleaseDataFlag(0, 0); // where 0 is the port index + } + vtkNew smoother; + smoother->SetStandardDeviations(1., 1., 1.); + smoother->SetInputConnection(bmp_reader->GetOutputPort()); + smoother->Update(); + auto vtk_image = smoother->GetOutput(); + vtk_image->Print(std::cerr); + image = new Image; + + std::cout << "A" << std::endl; + *image = CGAL::IO::read_vtk_image_data(vtk_image); // copy the + // image data + + std::cout << "B" << std::endl; } - vtkNew smoother; - smoother->SetStandardDeviations(1., 1., 1.); - smoother->SetInputConnection(dicom_reader->GetOutputPort()); - smoother->Update(); - auto vtk_image = smoother->GetOutput(); - vtk_image->Print(std::cerr); - image = new Image; - *image = CGAL::IO::read_vtk_image_data(vtk_image); // copy the image data #else CGAL::Three::Three::warning("You need VTK to read a DCM file"); CGAL_USE(dirname); From 0f0bd3ff6d5ac4d6d1cef09510978d44b4cc8a3c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mael=20Rouxel-Labb=C3=A9?= Date: Fri, 25 Nov 2022 01:04:45 +0100 Subject: [PATCH 02/13] WIP bmp reading --- .../include/CGAL/IO/read_vtk_image_data.h | 47 +++++++++++++++---- CGAL_ImageIO/include/CGAL/Image_3.h | 2 +- Mesh_3/doc/Mesh_3/CGAL/Image_3.h | 2 +- .../Plugins/Mesh_3/Io_image_plugin.cpp | 24 ++++++---- 4 files changed, 55 insertions(+), 20 deletions(-) diff --git a/CGAL_ImageIO/include/CGAL/IO/read_vtk_image_data.h b/CGAL_ImageIO/include/CGAL/IO/read_vtk_image_data.h index 3b7efa6c4df..f230bc32bc3 100644 --- a/CGAL_ImageIO/include/CGAL/IO/read_vtk_image_data.h +++ b/CGAL_ImageIO/include/CGAL/IO/read_vtk_image_data.h @@ -69,26 +69,55 @@ read_vtk_image_data(vtkImageData* vtk_image, Image_3::Own owning = Image_3::OWN_ image->ty = static_cast(offset[1]); image->tz = static_cast(offset[2]); image->endianness = ::_getEndianness(); + int vtk_type = vtk_image->GetScalarType(); if(vtk_type == VTK_SIGNED_CHAR) vtk_type = VTK_CHAR; - if(vtk_type < 0 || vtk_type > VTK_DOUBLE) - vtk_type = VTK_DOUBLE; - const VTK_to_ImageIO_type_mapper& imageio_type = - VTK_to_ImageIO_type[vtk_type]; + if(vtk_type < 0 || vtk_type > VTK_DOUBLE) vtk_type = VTK_DOUBLE; + const VTK_to_ImageIO_type_mapper& imageio_type = VTK_to_ImageIO_type[vtk_type]; image->wdim = imageio_type.wdim; image->wordKind = imageio_type.wordKind; image->sign = imageio_type.sign; + + const int cn = vtk_image->GetNumberOfScalarComponents(); + if (!vtk_image->GetPointData() || !vtk_image->GetPointData()->GetScalars()) { ::_freeImage(image); return Image_3(); } + + // If there is more than a scalar per point, vtk_image->data is not immediately + // interpretable in Image_3->data + CGAL_assertion(owning == Image_3::OWN_THE_DATA || cn == 1); + CGAL_assertion(vtk_image->GetPointData()->GetScalars()->GetNumberOfTuples() == dims[0]*dims[1]*dims[2]); + if(owning == Image_3::OWN_THE_DATA) { - image->data = ::ImageIO_alloc(dims[0]*dims[1]*dims[2]*image->wdim); - std::cerr << "GetNumberOfTuples()=" << vtk_image->GetPointData()->GetScalars()->GetNumberOfTuples() - << "\nimage->size()=" << dims[0]*dims[1]*dims[2] - << "\nwdim=" << image->wdim << '\n'; - vtk_image->GetPointData()->GetScalars()->ExportToVoidPointer(image->data); + int dims_n = dims[0]*dims[1]*dims[2]; + image->data = ::ImageIO_alloc(dims_n * image->wdim); + std::cerr << "GetNumberOfTuples() = " << vtk_image->GetPointData()->GetScalars()->GetNumberOfTuples() << "\n" + << "components = " << cn << "\n" + << "wdim = " << image->wdim << "\n" + << "image->size() = " << dims_n << std::endl; + + if(cn == 1) { + vtk_image->GetPointData()->GetScalars()->ExportToVoidPointer(image->data); + } else { + std::cerr << "Warning: input has " << cn << " components; only the value of the first component will be used." << std::endl; + + // cast the data void pointers to make it possible to do pointer arithmetic + char* src = static_cast(vtk_image->GetPointData()->GetScalars()->GetVoidPointer(0)); + char* dest = static_cast(image->data); + + for(int i=0; iwdim because we casted to char* and not the actual data type + memcpy(dest + image->wdim*i, src + cn*image->wdim*i, image->wdim * sizeof(char)); + + // Check that we are not discarding useful data + CGAL_assertion(*(src + cn*image->wdim*i) == *(src + cn*image->wdim*i + 1)); + CGAL_assertion(*(src + cn*image->wdim*i) == *(src + cn*image->wdim*i + 2)); + } + } } else { image->data = vtk_image->GetPointData()->GetScalars()->GetVoidPointer(0); } diff --git a/CGAL_ImageIO/include/CGAL/Image_3.h b/CGAL_ImageIO/include/CGAL/Image_3.h index f6b186ef36c..e653e9de83b 100644 --- a/CGAL_ImageIO/include/CGAL/Image_3.h +++ b/CGAL_ImageIO/include/CGAL/Image_3.h @@ -87,7 +87,7 @@ public: protected: Image_shared_ptr image_ptr; - // implementation in src/CGAL_ImageIO/Image_3.cpp + // implementation in Image_3_impl.h bool private_read(_image* im, Own own_the_data = OWN_THE_DATA); public: diff --git a/Mesh_3/doc/Mesh_3/CGAL/Image_3.h b/Mesh_3/doc/Mesh_3/CGAL/Image_3.h index 415638914a4..3f5bce1ddcb 100644 --- a/Mesh_3/doc/Mesh_3/CGAL/Image_3.h +++ b/Mesh_3/doc/Mesh_3/CGAL/Image_3.h @@ -14,7 +14,7 @@ public: /// The default-constructor. The object is invalid until a call to `read()`. Image_3(); - /// Open an 3D image file. + /// Open a 3D image file. /// /// Returns `true` if the file was sucessfully loaded. bool read(const char* file); diff --git a/Polyhedron/demo/Polyhedron/Plugins/Mesh_3/Io_image_plugin.cpp b/Polyhedron/demo/Polyhedron/Plugins/Mesh_3/Io_image_plugin.cpp index 671850655d6..d13f4f8080f 100644 --- a/Polyhedron/demo/Polyhedron/Plugins/Mesh_3/Io_image_plugin.cpp +++ b/Polyhedron/demo/Polyhedron/Plugins/Mesh_3/Io_image_plugin.cpp @@ -1321,14 +1321,23 @@ Image* Io_image_plugin::createDCMImage(QString dirname) bool is_dcm = false; bool is_bmp = false; + std::vector paths; vtkStringArray* files = vtkStringArray::New(); boost::filesystem::path p(dirname.toUtf8().data()); for(boost::filesystem::directory_entry& x : boost::filesystem::directory_iterator(p)){ std::string s(x.path().extension().string()); - if(s == std::string(".dcm") || (s == std::string(".DCM"))){ is_dcm = true;} - if(s == std::string(".bmp") || (s == std::string(".BMP"))){ is_bmp = true;} - std::cout << x.path().string() << std::endl; - files->InsertNextValue(x.path().string()); + if(s == std::string(".dcm") || (s == std::string(".DCM"))){ is_dcm = true; CGAL_assertion(!is_bmp); } + if(s == std::string(".bmp") || (s == std::string(".BMP"))){ is_bmp = true; CGAL_assertion(!is_dcm); } + paths.push_back(x.path()); + } + + // directory_iterator does not guarantee a sorted order + std::sort(std::begin(paths), std::end(paths)); + + for(const boost::filesystem::path& p : paths) + { + std::cout << p.string() << std::endl; + files->InsertNextValue(p.string()); } if(is_dcm){ @@ -1369,17 +1378,14 @@ Image* Io_image_plugin::createDCMImage(QString dirname) smoother->Update(); auto vtk_image = smoother->GetOutput(); vtk_image->Print(std::cerr); - image = new Image; - std::cout << "A" << std::endl; + image = new Image; *image = CGAL::IO::read_vtk_image_data(vtk_image); // copy the // image data - - std::cout << "B" << std::endl; } #else - CGAL::Three::Three::warning("You need VTK to read a DCM file"); + CGAL::Three::Three::warning("You need VTK to read DCM/BMP files"); CGAL_USE(dirname); #endif return image; From 76c695dfc12106d43a28b6ce341789f5f7ceb245 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mael=20Rouxel-Labb=C3=A9?= Date: Mon, 28 Nov 2022 12:01:12 +0100 Subject: [PATCH 03/13] Fix assertion that G & B colors are the same as R (only gray is supported) --- CGAL_ImageIO/include/CGAL/IO/read_vtk_image_data.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/CGAL_ImageIO/include/CGAL/IO/read_vtk_image_data.h b/CGAL_ImageIO/include/CGAL/IO/read_vtk_image_data.h index f230bc32bc3..0587f7a8c58 100644 --- a/CGAL_ImageIO/include/CGAL/IO/read_vtk_image_data.h +++ b/CGAL_ImageIO/include/CGAL/IO/read_vtk_image_data.h @@ -111,11 +111,11 @@ read_vtk_image_data(vtkImageData* vtk_image, Image_3::Own owning = Image_3::OWN_ for(int i=0; iwdim because we casted to char* and not the actual data type - memcpy(dest + image->wdim*i, src + cn*image->wdim*i, image->wdim * sizeof(char)); + memcpy(dest + image->wdim*i, src + cn*image->wdim*i, image->wdim); - // Check that we are not discarding useful data - CGAL_assertion(*(src + cn*image->wdim*i) == *(src + cn*image->wdim*i + 1)); - CGAL_assertion(*(src + cn*image->wdim*i) == *(src + cn*image->wdim*i + 2)); + // Check that we are not discarding useful data (i.e., green & blue are identical to red) + CGAL_assertion(memcmp(src + cn*image->wdim*i, src + cn*image->wdim*i + image->wdim, image->wdim) == 0); + CGAL_assertion(memcmp(src + cn*image->wdim*i, src + cn*image->wdim*i + 2*image->wdim, image->wdim) == 0); } } } else { From 45c0ecfe42b41af63ff943162689f608c6b8b605 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mael=20Rouxel-Labb=C3=A9?= Date: Mon, 28 Nov 2022 12:05:59 +0100 Subject: [PATCH 04/13] Add an extra assertion --- CGAL_ImageIO/include/CGAL/IO/read_vtk_image_data.h | 1 + 1 file changed, 1 insertion(+) diff --git a/CGAL_ImageIO/include/CGAL/IO/read_vtk_image_data.h b/CGAL_ImageIO/include/CGAL/IO/read_vtk_image_data.h index 0587f7a8c58..22b19dee6ed 100644 --- a/CGAL_ImageIO/include/CGAL/IO/read_vtk_image_data.h +++ b/CGAL_ImageIO/include/CGAL/IO/read_vtk_image_data.h @@ -103,6 +103,7 @@ read_vtk_image_data(vtkImageData* vtk_image, Image_3::Own owning = Image_3::OWN_ vtk_image->GetPointData()->GetScalars()->ExportToVoidPointer(image->data); } else { std::cerr << "Warning: input has " << cn << " components; only the value of the first component will be used." << std::endl; + CGAL_assertion(cn >= 3); // if it's more than 1, it needs to be more than 3 // cast the data void pointers to make it possible to do pointer arithmetic char* src = static_cast(vtk_image->GetPointData()->GetScalars()->GetVoidPointer(0)); From 9a607498beb8d7b47767b97b23f743f455173271 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mael=20Rouxel-Labb=C3=A9?= Date: Tue, 6 Dec 2022 22:43:05 +0100 Subject: [PATCH 05/13] Avoid some code duplication --- .../Plugins/Mesh_3/Io_image_plugin.cpp | 76 ++++++++----------- 1 file changed, 33 insertions(+), 43 deletions(-) diff --git a/Polyhedron/demo/Polyhedron/Plugins/Mesh_3/Io_image_plugin.cpp b/Polyhedron/demo/Polyhedron/Plugins/Mesh_3/Io_image_plugin.cpp index d13f4f8080f..1f93878bec9 100644 --- a/Polyhedron/demo/Polyhedron/Plugins/Mesh_3/Io_image_plugin.cpp +++ b/Polyhedron/demo/Polyhedron/Plugins/Mesh_3/Io_image_plugin.cpp @@ -236,6 +236,7 @@ public: connect(CGAL::Three::Three::connectableScene(),SIGNAL(itemIndexSelected(int)), this, SLOT(connect_controls(int))); } + Viewer_interface* v = CGAL::Three::Three::mainViewer(); CGAL_assertion(v != nullptr); pxr_.setViewer(v); @@ -527,6 +528,7 @@ private: CGAL::Three::Scene_group_item* group; std::vector threads; + struct Controls{ CGAL::Three::Scene_item* group; CGAL::Three::Scene_item* x_item; @@ -536,11 +538,14 @@ private: int y_value; int z_value; }; + Controls *current_control; QMap group_map; unsigned int intersection_id; + bool loadDCM(QString filename); Image* createDCMImage(QString dirname); + QLayout* createOrGetDockLayout() { QLayout* layout = nullptr; QDockWidget* controlDockWidget = mw->findChild("volumePlanesControl");; @@ -1228,7 +1233,6 @@ bool Io_image_plugin::loadDCM(QString dirname) connect(ui.imageType, SIGNAL(currentIndexChanged(int)), this, SLOT(on_imageType_changed(int))); - // Add precision values to the dialog for ( int i=1 ; i<9 ; ++i ) { @@ -1240,7 +1244,6 @@ bool Io_image_plugin::loadDCM(QString dirname) ui.imageType->addItem(QString("Segmented image")); ui.imageType->addItem(QString("Gray-level image")); - // Open window QApplication::restoreOverrideCursor(); int return_code = dialog.exec(); @@ -1248,64 +1251,51 @@ bool Io_image_plugin::loadDCM(QString dirname) { return false; } + QApplication::setOverrideCursor(Qt::WaitCursor); QApplication::processEvents(); - // Get selected precision - int voxel_scale = ui.precisionList->currentIndex() + 1; - - //Get the image type - QString type = ui.imageType->currentText(); - Scene_image_item* image_item; - if(type == "Gray-level image") + Image *image = createDCMImage(dirname); + if(image->image() == nullptr) { - - Image *image = createDCMImage(dirname); - if(image->image() == nullptr) - { - QMessageBox::warning(mw, mw->windowTitle(), - tr("Error with file %1/:\nunknown file format!").arg(dirname)); - CGAL::Three::Three::warning(tr("Opening of file %1/ failed!").arg(dirname)); - result = false; - } - else - { - CGAL::Three::Three::information(tr("File %1/ successfully opened.").arg(dirname)); - } - if(result) - { - //Create planes - image_item = new Scene_image_item(image,125, true); - msgBox.setText("Planes created : 0/3"); - msgBox.setStandardButtons(QMessageBox::NoButton); - msgBox.show(); - createPlanes(image_item); - image_item->setName(fileinfo.baseName()); - scene->addItem(image_item); - } + QMessageBox::warning(mw, mw->windowTitle(), + tr("Error with file %1/:\nunknown file format!").arg(dirname)); + CGAL::Three::Three::warning(tr("Opening of file %1/ failed!").arg(dirname)); + result = false; } else { - Image *image = createDCMImage(dirname); - if(image->image() == nullptr) + CGAL::Three::Three::information(tr("File %1/ successfully opened.").arg(dirname)); + } + + if(result) + { + // Get the image type + QString type = ui.imageType->currentText(); + Scene_image_item* image_item; + if(type == "Gray-level image") { - QMessageBox::warning(mw, mw->windowTitle(), - tr("Error with file %1/:\nunknown file format!").arg(dirname)); - CGAL::Three::Three::warning(tr("Opening of file %1/ failed!").arg(dirname)); - result = false; + // Create planes + image_item = new Scene_image_item(image,125, true); + msgBox.setText("Planes created : 0/3"); + msgBox.setStandardButtons(QMessageBox::NoButton); + msgBox.show(); + createPlanes(image_item); + image_item->setName(fileinfo.baseName()); + scene->addItem(image_item); } else { - CGAL::Three::Three::information(tr("File %1/ successfully opened.").arg(dirname)); - } - if(result) - { + // Get selected precision + int voxel_scale = ui.precisionList->currentIndex() + 1; + image_item = new Scene_image_item(image,voxel_scale, false); image_item->setName(fileinfo.baseName()); scene->addItem(image_item); } } } + return result; #else CGAL::Three::Three::warning("You need VTK to read a DCM file"); From d90d47fb79849b87a9ec615162cb59b437fc4cd6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mael=20Rouxel-Labb=C3=A9?= Date: Tue, 6 Dec 2022 22:57:42 +0100 Subject: [PATCH 06/13] Fix indentation (no real changes) --- .../Plugins/Mesh_3/Io_image_plugin.cpp | 565 ++++++++++-------- 1 file changed, 332 insertions(+), 233 deletions(-) diff --git a/Polyhedron/demo/Polyhedron/Plugins/Mesh_3/Io_image_plugin.cpp b/Polyhedron/demo/Polyhedron/Plugins/Mesh_3/Io_image_plugin.cpp index 1f93878bec9..4e13744ef2b 100644 --- a/Polyhedron/demo/Polyhedron/Plugins/Mesh_3/Io_image_plugin.cpp +++ b/Polyhedron/demo/Polyhedron/Plugins/Mesh_3/Io_image_plugin.cpp @@ -44,19 +44,11 @@ #include #include -#include -#include - -#include -#include -#include - #include #include #include "Raw_image_dialog.h" #include -#include -#include + #ifdef CGAL_USE_VTK #include @@ -73,44 +65,57 @@ #include +#include +#include +#include + +#include +#include +#include +#include + // Covariant return types don't work for scalar types and we cannot // have templates here, hence this unfortunate hack. // The input float value we are reading is always in // 0..1 and min_max is the range it came from. -struct IntConverter { +struct IntConverter +{ std::pair min_max; - int operator()(float f) { + int operator()(float f) + { float s = f * float((min_max.second - min_max.first)); //approximate instead of just floor. - if (s - floor(s) >= 0.5){ + if (s - floor(s) >= 0.5) return int(s)+1 + min_max.first; - } - else{ + else return s + float(min_max.first); - } } }; -struct DoubleConverter { +struct DoubleConverter +{ std::pair min_max; - float operator()(float f) { + float operator()(float f) + { float s = f * (min_max.second - min_max.first); return s + min_max.first; } }; -class PixelReader : public QObject +class PixelReader + : public QObject { Q_OBJECT + public Q_SLOTS: - void update(const QMouseEvent *e) { - getPixel(e->pos()); - } + void update(const QMouseEvent *e) { getPixel(e->pos()); } + Q_SIGNALS: void x(QString); + public: void setIC(const IntConverter& x) { ic = x; fc = boost::optional(); } void setFC(const DoubleConverter& x) { fc = x; ic = boost::optional(); } @@ -120,7 +125,8 @@ private: boost::optional ic; boost::optional fc; Viewer_interface* viewer; - void getPixel(const QPoint& e) { + void getPixel(const QPoint& e) + { const auto data = read_pixel_as_float_rgb(e, viewer, viewer->camera()); if(fc) { Q_EMIT x(QString::number((*fc)(data[0]), 'f', 6 )); @@ -130,48 +136,59 @@ private: } }; - -class Plane_slider : public QSlider +class Plane_slider + : public QSlider { Q_OBJECT + public: - Plane_slider(const CGAL::qglviewer::Vec& v, int id, Scene_interface* scene, - CGAL::qglviewer::ManipulatedFrame* frame, Qt::Orientation ori, QWidget* widget) - : QSlider(ori, widget), v(v), id(id), scene(scene), frame(frame) { + Plane_slider(const CGAL::qglviewer::Vec& v, + int id, + Scene_interface* scene, + CGAL::qglviewer::ManipulatedFrame* frame, + Qt::Orientation ori, + QWidget* widget) + : QSlider(ori, widget), v(v), id(id), scene(scene), frame(frame) + { this->setTracking(true); - connect(frame, SIGNAL(manipulated()), this, SLOT(updateCutPlane())); + connect(frame, SIGNAL(manipulated()), this, SLOT(updateCutPlane())); } public Q_SLOTS: void updateCutPlane() { - ready_to_cut = true; - QTimer::singleShot(0,this,SLOT(updateValue())); + ready_to_cut = true; + QTimer::singleShot(0,this,SLOT(updateValue())); } void setFramePosition() { if(!ready_to_move) return; + const CGAL::qglviewer::Vec offset = Three::mainViewer()->offset(); CGAL::qglviewer::Vec v2 = v * (this->value() / scale); - v2+=offset; + v2 += offset; frame->setTranslationWithConstraint(v2); scene->itemChanged(id); Q_EMIT realChange(this->value() / scale); ready_to_move = false; } - void updateValue() { + + void updateValue() + { if(!ready_to_cut) return; + typedef qreal qglviewer_real; qglviewer_real a, b, c; + frame->getPosition(a, b, c); const CGAL::qglviewer::Vec offset = Three::mainViewer()->offset(); - a-=offset.x; - b-=offset.y; - c-=offset.z; + a -= offset.x; + b -= offset.y; + c -= offset.z; float sum1 = float(a + b + c); float sum2 = float(v.x + v.y + v.z); sum1 /= sum2; @@ -184,7 +201,9 @@ public Q_SLOTS: ready_to_move = true; QTimer::singleShot(0,this,SLOT(setFramePosition())); } + unsigned int getScale() const { return scale; } + Q_SIGNALS: void realChange(int); @@ -194,6 +213,7 @@ private: bool ready_to_move; CGAL::qglviewer::Vec v; int id; + Scene_interface* scene; CGAL::qglviewer::ManipulatedFrame* frame; }; @@ -211,25 +231,33 @@ class Io_image_plugin : Q_PLUGIN_METADATA(IID "com.geometryfactory.PolyhedronDemo.IOPluginInterface/1.90" FILE "io_image_plugin.json") public: - - bool applicable(QAction*) const override{ + bool applicable(QAction*) const override + { return qobject_cast(scene->item(scene->mainSelectionIndex())); } using Polyhedron_demo_io_plugin_interface::init; - void init(QMainWindow* mainWindow, CGAL::Three::Scene_interface* scene_interface, Messages_interface *mi) override { + void init(QMainWindow* mainWindow, + CGAL::Three::Scene_interface* scene_interface, + Messages_interface *mi) override + { this->message_interface = mi; this->scene = scene_interface; this->mw = mainWindow; this->is_gray = false; + x_control = nullptr; y_control = nullptr; z_control = nullptr; current_control = nullptr; + planeSwitch = new QAction("Add Volume Planes", mw); - QAction *actionLoadDCM = new QAction("Open Directory (for DCM files)", mw); + + QAction *actionLoadDCM = new QAction("Open Directory (DCM files)", mw); connect(actionLoadDCM, SIGNAL(triggered()), this, SLOT(on_actionLoadDCM_triggered())); - if(planeSwitch) { + + if(planeSwitch) + { planeSwitch->setProperty("subMenuName", "3D Mesh Generation"); connect(planeSwitch, SIGNAL(triggered()), this, SLOT(selectPlanes())); @@ -239,6 +267,7 @@ public: Viewer_interface* v = CGAL::Three::Three::mainViewer(); CGAL_assertion(v != nullptr); + pxr_.setViewer(v); connect(v, SIGNAL(pointSelected(const QMouseEvent *)), &pxr_, SLOT(update(const QMouseEvent *))); createOrGetDockLayout(); @@ -246,19 +275,19 @@ public: this, SLOT(connectNewViewer(QObject*))); QMenu* menuFile = mw->findChild("menuFile"); - if ( nullptr != menuFile ) + if(nullptr != menuFile ) { QList menuFileActions = menuFile->actions(); // Look for action just after "Load..." action QAction* actionAfterLoad = nullptr; - for ( QList::iterator it_action = menuFileActions.begin(), - end = menuFileActions.end() ; it_action != end ; ++ it_action ) //Q_FOREACH( QAction* action, menuFileActions) + for(QList::iterator it_action = menuFileActions.begin(), + end = menuFileActions.end() ; it_action != end ; ++ it_action ) //Q_FOREACH( QAction* action, menuFileActions) { - if ( NULL != *it_action && (*it_action)->text().contains("Load Plugin") ) + if(NULL != *it_action && (*it_action)->text().contains("Load Plugin")) { ++it_action; - if ( it_action != end && NULL != *it_action ) + if(it_action != end && NULL != *it_action) { actionAfterLoad = *it_action; } @@ -266,29 +295,34 @@ public: } // Insert "Load implicit function" action - if ( nullptr != actionAfterLoad ) + if(nullptr != actionAfterLoad) { menuFile->insertAction(actionAfterLoad,actionLoadDCM); } } } - QList actions() const override{ + + QList actions() const override + { return QList() << planeSwitch; } + virtual void closure() override { - QDockWidget* controlDockWidget = mw->findChild("volumePlanesControl"); - if(controlDockWidget) - controlDockWidget->hide(); + QDockWidget* controlDockWidget = mw->findChild("volumePlanesControl"); + if(controlDockWidget) + controlDockWidget->hide(); } - Io_image_plugin() : planeSwitch(nullptr) {} + + Io_image_plugin() : planeSwitch(nullptr) { } QString nameFilters() const override; bool canLoad(QFileInfo) const override; - QList load(QFileInfo fileinfo, bool& ok, bool add_to_scene=true) override; + QList load(QFileInfo fileinfo, bool& ok, bool add_to_scene = true) override; bool canSave(const CGAL::Three::Scene_item*) override; - bool save(QFileInfo fileinfo, QList& items ) override{ + bool save(QFileInfo fileinfo, QList& items ) override + { Scene_item* item = items.front(); const Scene_image_item* im_item = qobject_cast(item); @@ -298,16 +332,17 @@ public: items.pop_front(); return ok; } - bool isDefaultLoader(const Scene_item* item) const override{ + + bool isDefaultLoader(const Scene_item* item) const override + { if(qobject_cast(item)) return true; return false; } + QString name() const override{ return "segmented images"; } - public Q_SLOTS: - void setXNum(int i) { x_cubeLabel->setText(QString("%1").arg(i)); @@ -338,7 +373,6 @@ public Q_SLOTS: int i = s.toInt(); z_slider->setValue(i*qobject_cast(z_slider)->getScale()); z_slider->sliderMoved(i); - } void on_imageType_changed(int index) @@ -348,38 +382,50 @@ public Q_SLOTS: else ui.groupBox->setVisible(false); } - void selectPlanes() { + void selectPlanes() + { std::vector< Scene_image_item* > seg_items; Scene_image_item* seg_img; seg_img = nullptr; - for(int i = 0; i < scene->numberOfEntries(); ++i) { + for(int i = 0; i < scene->numberOfEntries(); ++i) + { Scene_image_item* tmp = qobject_cast(scene->item(i)); - if(tmp != nullptr){ + if(tmp != nullptr) seg_items.push_back(tmp); - } } - if(seg_items.empty()) { + + if(seg_items.empty()) + { QMessageBox::warning(mw, tr("No suitable item found"), tr("Load an inrimage or hdr file to enable Volume Planes.")); return; - } else { + } + else + { QList items; for(std::vector< Scene_image_item* >::const_iterator it = seg_items.begin(); - it != seg_items.end(); ++it) { + it != seg_items.end(); ++it) + { items << (*it)->name(); } + bool ok; QString selected = QInputDialog::getItem(mw, tr("Select a dataset:"), tr("Items"), items, 0, false, &ok); if(!ok || selected.isEmpty()) return; + for(std::vector< Scene_image_item*>::const_iterator it = seg_items.begin(); - it != seg_items.end(); ++it) { + it != seg_items.end(); ++it) + { if(selected == (*it)->name()) seg_img = *it; } } + if(group_map.keys().contains(seg_img)) + { CGAL::Three::Three::warning("This item already has volume planes."); + } else { // Opens a modal Dialog to prevent the user from manipulating things that could mess with the planes creation and cause a segfault. @@ -390,15 +436,18 @@ public Q_SLOTS: } } - void addVP(Volume_plane_thread* thread) { + void addVP(Volume_plane_thread* thread) + { Volume_plane_interface* plane = thread->getItem(); plane->init(Three::mainViewer()); + // add the interface for this Volume_plane int id = scene->addItem(plane); scene->changeGroup(plane, group); group->lockChild(plane); //connect(plane->manipulatedFrame(), &CGAL::qglviewer::ManipulatedFrame::manipulated, // plane, &Volume_plane_interface::redraw); + switch(thread->type()) { case 'x': @@ -449,20 +498,22 @@ public Q_SLOTS: default: break; } + std::vector::iterator it = std::find(threads.begin(), threads.end(), thread); - // this slot has been connected to a thread that hasn't been - // registered here. + // this slot has been connected to a thread that hasn't been registered here. assert(it != threads.end()); delete *it; threads.erase(it); update_msgBox(); Volume_plane_intersection* intersection = dynamic_cast(scene->item(intersection_id)); - if(!intersection) { + if(!intersection) + { // the intersection is gone before it was initialized return; } + // FIXME downcasting mode // FIXME this will bug if two volume planes are generated simultaneously by the plugin if(Volume_plane* p = dynamic_cast< Volume_plane* >(plane)) { @@ -472,6 +523,7 @@ public Q_SLOTS: } else if(Volume_plane* p = dynamic_cast< Volume_plane* >(plane)) { intersection->setZ(p); } + connect(plane, SIGNAL(planeDestructionIncoming(Volume_plane_interface*)), intersection, SLOT(planeRemoved(Volume_plane_interface*))); @@ -480,28 +532,22 @@ public Q_SLOTS: void on_actionLoadDCM_triggered() { QSettings settings; - QString start_dir = settings.value("Open directory", - QDir::current().dirName()).toString(); - QString dir = - QFileDialog::getExistingDirectory(mw, - tr("Open directory"), - start_dir, - QFileDialog::ShowDirsOnly - | QFileDialog::DontResolveSymlinks); + QString start_dir = settings.value("Open directory", QDir::current().dirName()).toString(); + QString dir = QFileDialog::getExistingDirectory(mw, tr("Open directory"), + start_dir, QFileDialog::ShowDirsOnly | QFileDialog::DontResolveSymlinks); - if (!dir.isEmpty()) { + if(!dir.isEmpty()) + { QFileInfo fileinfo(dir); - if (fileinfo.isDir() && fileinfo.isReadable()) + if(fileinfo.isDir() && fileinfo.isReadable()) { - settings.setValue("Open directory", - fileinfo.absoluteDir().absolutePath()); + settings.setValue("Open directory", fileinfo.absoluteDir().absolutePath()); QApplication::setOverrideCursor(Qt::WaitCursor); QApplication::processEvents(); loadDCM(dir); QApplication::restoreOverrideCursor(); } } - } void connectNewViewer(QObject* o) @@ -513,11 +559,14 @@ public Q_SLOTS: o->installEventFilter(c.z_item); } } + private: CGAL::qglviewer::Vec first_offset; bool is_gray; + Messages_interface* message_interface; QMessageBox msgBox; + QAction* planeSwitch; QWidget *x_control, *y_control, *z_control; QSlider *x_slider, *y_slider, *z_slider; @@ -529,7 +578,8 @@ private: CGAL::Three::Scene_group_item* group; std::vector threads; - struct Controls{ + struct Controls + { CGAL::Three::Scene_item* group; CGAL::Three::Scene_item* x_item; CGAL::Three::Scene_item* y_item; @@ -539,18 +589,20 @@ private: int z_value; }; - Controls *current_control; + Controls* current_control; QMap group_map; unsigned int intersection_id; bool loadDCM(QString filename); Image* createDCMImage(QString dirname); - QLayout* createOrGetDockLayout() { + QLayout* createOrGetDockLayout() + { QLayout* layout = nullptr; - QDockWidget* controlDockWidget = mw->findChild("volumePlanesControl");; + QDockWidget* controlDockWidget = mw->findChild("volumePlanesControl"); - if(!controlDockWidget) { + if(!controlDockWidget) + { controlDockWidget = new QDockWidget(mw); controlDockWidget->setObjectName("volumePlanesControl"); QWidget* content = new QWidget(controlDockWidget); @@ -576,7 +628,9 @@ private: controlDockWidget->setWidget(content); controlDockWidget->hide(); - } else { + } + else + { layout = controlDockWidget->findChild("vpSliderLayout"); controlDockWidget->show(); controlDockWidget->raise(); @@ -585,7 +639,8 @@ private: return layout; } - void createPlanes(Scene_image_item* seg_img) { + void createPlanes(Scene_image_item* seg_img) + { QApplication::setOverrideCursor(Qt::WaitCursor); //Control widgets creation QLayout* layout = createOrGetDockLayout(); @@ -678,11 +733,13 @@ private: z_box->addWidget(z_cubeLabel); show_sliders &= seg_img->image()->zdim() > 1; } + x_control->setEnabled(show_sliders); y_control->setEnabled(show_sliders); z_control->setEnabled(show_sliders); - if(!(seg_img == nullptr)) { + if(!(seg_img == nullptr)) + { const CGAL::Image_3* img = seg_img->image(); CGAL_IMAGE_IO_CASE(img->image(), this->launchAdders(seg_img, seg_img->name())) @@ -696,15 +753,17 @@ private: this->intersection_id = scene->addItem(i); scene->changeGroup(i, group); group->lockChild(i); - } else { + } + else + { QMessageBox::warning(mw, tr("Something went wrong"), tr("Selected a suitable Object but couldn't get an image pointer.")); return; } } - template - void launchAdders(Scene_image_item* seg_img, const QString& name) { + void launchAdders(Scene_image_item* seg_img, const QString& name) + { const CGAL::Image_3* img = seg_img->image(); const Word* begin = (const Word*)img->data(); const Word* end = (const Word*)img->data() + img->size(); @@ -719,9 +778,9 @@ private: Volume_plane *y_item = new Volume_plane(img->image()->tx,img->image()->ty, img->image()->tz); Volume_plane *z_item = new Volume_plane(img->image()->tx,img->image()->ty, img->image()->tz); - x_item->setProperty("img",QVariant::fromValue((void*)seg_img)); - y_item->setProperty("img",QVariant::fromValue((void*)seg_img)); - z_item->setProperty("img",QVariant::fromValue((void*)seg_img)); + x_item->setProperty("img", QVariant::fromValue((void*)seg_img)); + y_item->setProperty("img", QVariant::fromValue((void*)seg_img)); + z_item->setProperty("img", QVariant::fromValue((void*)seg_img)); x_item->setColor(QColor("red")); y_item->setColor(QColor("green")); @@ -741,6 +800,7 @@ private: connect(group, SIGNAL(aboutToBeDestroyed()), this, SLOT(erase_group())); scene->addItem(group); + Controls c; c.group = group; c.x_item = x_item; @@ -769,19 +829,23 @@ private: first_offset = Three::mainViewer()->offset(); } + template - void switchReaderConverter(std::pair minmax) { + void switchReaderConverter(std::pair minmax) + { switchReaderConverter(minmax, typename boost::is_integral::type()); } template - void switchReaderConverter(std::pair minmax, boost::true_type) { + void switchReaderConverter(std::pair minmax, boost::true_type) + { // IntConverter IntConverter x = { minmax }; pxr_.setIC(x); } template - void switchReaderConverter(std::pair minmax, boost::false_type) { + void switchReaderConverter(std::pair minmax, boost::false_type) + { // IntConverter DoubleConverter x = { minmax }; pxr_.setFC(x); } @@ -797,7 +861,7 @@ private Q_SLOTS: void update_msgBox() { static int nbPlanes =0; - nbPlanes ++; + ++nbPlanes; msgBox.setText(QString("Planes created : %1/3").arg(nbPlanes)); if(nbPlanes == 3) { @@ -809,6 +873,7 @@ private Q_SLOTS: scene->item(i)->invalidateOpenGLBuffers(); } } + msgBox.hide(); nbPlanes = 0; QApplication::restoreOverrideCursor(); @@ -817,11 +882,9 @@ private Q_SLOTS: // Avoids the segfault after the deletion of an item void erase_group() { - CGAL::Three::Scene_group_item* group_item = qobject_cast(sender()); if(group_item) { - Q_FOREACH(CGAL::Three::Scene_item* key, group_map.keys()) { if(group_map[key].group == group_item) @@ -834,38 +897,7 @@ private Q_SLOTS: } } } - //try to re-connect to another group - if(!group_map.isEmpty()) - { - int id = scene->item_id(group_map.keys().first()); - connect_controls(id); - } - } - //destroy planes on image deletion - void on_img_detroyed() - { - Scene_image_item* img_item = qobject_cast(sender()); - if(img_item) - { - Scene_group_item* group = qobject_cast(group_map[img_item].group); - if(!group) - return; - group_map[img_item].x_item = nullptr; - group_map[img_item].y_item = nullptr; - group_map[img_item].z_item = nullptr; - disconnect(group_map[img_item].group, SIGNAL(aboutToBeDestroyed()), - this, SLOT(erase_group())); - group_map.remove(img_item); - QList deletion; - Q_FOREACH(Scene_interface::Item_id id, group->getChildren()) - { - Scene_item* child = group->getChild(id); - group->unlockChild(child); - deletion.append(scene->item_id(child)); - } - deletion.append(scene->item_id(group)); - scene->erase(deletion); - } + //try to re-connect to another group if(!group_map.isEmpty()) { @@ -873,11 +905,49 @@ private Q_SLOTS: connect_controls(id); } } + + // destroy planes on image deletion + void on_img_detroyed() + { + Scene_image_item* img_item = qobject_cast(sender()); + if(img_item) + { + Scene_group_item* group = qobject_cast(group_map[img_item].group); + if(!group) + return; + + group_map[img_item].x_item = nullptr; + group_map[img_item].y_item = nullptr; + group_map[img_item].z_item = nullptr; + disconnect(group_map[img_item].group, SIGNAL(aboutToBeDestroyed()), + this, SLOT(erase_group())); + group_map.remove(img_item); + + QList deletion; + Q_FOREACH(Scene_interface::Item_id id, group->getChildren()) + { + Scene_item* child = group->getChild(id); + group->unlockChild(child); + deletion.append(scene->item_id(child)); + } + deletion.append(scene->item_id(group)); + scene->erase(deletion); + } + + //try to re-connect to another group + if(!group_map.isEmpty()) + { + int id = scene->item_id(group_map.keys().first()); + connect_controls(id); + } + } + void connect_controls(int id) { CGAL::Three::Scene_item* sel_itm = scene->item(id); if(!sel_itm) return; + if(!group_map.contains(sel_itm)) //the planes are not yet created or the selected item is not a segmented_image { Scene_image_item* img = (Scene_image_item*)sel_itm->property("img").value(); @@ -886,15 +956,18 @@ private Q_SLOTS: else return; } + Controls c = group_map[sel_itm]; current_control = &group_map[sel_itm]; bool show_sliders = true; + // x line if(c.x_item != nullptr) { Volume_plane_interface* x_plane = qobject_cast(c.x_item); if(x_slider) delete x_slider; + x_slider = new Plane_slider(x_plane->translationVector(), scene->item_id(x_plane), scene, x_plane->manipulatedFrame(), Qt::Horizontal, x_control); x_slider->setRange(0, (x_plane->cDim() - 1) * 100); @@ -910,12 +983,14 @@ private Q_SLOTS: x_box->addWidget(x_cubeLabel); show_sliders &= qobject_cast(sel_itm)->image()->xdim() > 1; } + //y line if(c.y_item != nullptr) { Volume_plane_interface* y_plane = qobject_cast(c.y_item); if(y_slider) delete y_slider; + y_slider = new Plane_slider(y_plane->translationVector(), scene->item_id(y_plane), scene, y_plane->manipulatedFrame(), Qt::Horizontal, z_control); y_slider->setRange(0, (y_plane->cDim() - 1) * 100); @@ -930,12 +1005,14 @@ private Q_SLOTS: y_box->addWidget(y_cubeLabel); show_sliders &= qobject_cast(sel_itm)->image()->ydim() > 1; } + // z line if(c.z_item != nullptr) { Volume_plane_interface* z_plane = qobject_cast(c.z_item); if(z_slider) delete z_slider; + z_slider = new Plane_slider(z_plane->translationVector(), scene->item_id(z_plane), scene, z_plane->manipulatedFrame(), Qt::Horizontal, z_control); z_slider->setRange(0, (z_plane->cDim() - 1) * 100); @@ -955,7 +1032,8 @@ private Q_SLOTS: y_control->setEnabled(show_sliders); z_control->setEnabled(show_sliders); } -//Keeps the position of the planes for the next time + + // Keeps the position of the planes for the next time void set_value() { current_control->x_value = x_slider->value(); @@ -981,19 +1059,18 @@ private Q_SLOTS: if(group_map.isEmpty()) z_control->hide(); } - }; - -QString Io_image_plugin::nameFilters() const { +QString Io_image_plugin::nameFilters() const +{ return QString("Inrimage files (*.inr *.inr.gz) ;; " "Analyze files (*.hdr *.img *img.gz) ;; " "Stanford Exploration Project files (*.H *.HH) ;; " "NRRD image files (*.nrrd)"); } - -bool Io_image_plugin::canLoad(QFileInfo) const { +bool Io_image_plugin::canLoad(QFileInfo) const +{ return true; } @@ -1002,14 +1079,18 @@ void convert(Image* image) { float *f_data = (float*)ImageIO_alloc(image->xdim()*image->ydim()*image->zdim()*sizeof(float)); Word* d_data = (Word*)(image->data()); - //convert image from double to float - for(std::size_t x = 0; xxdim(); ++x) - for(std::size_t y = 0; yydim(); ++y) + + // convert image from double to float + for(std::size_t x = 0; xxdim(); ++x) { + for(std::size_t y = 0; yydim(); ++y) { for(std::size_t z = 0; zzdim(); ++z) { std::size_t i =(z * image->ydim() + y) * image->xdim() + x; f_data[i] =(float)d_data[i]; } + } + } + ImageIO_free(d_data); image->image()->data = (void*)f_data; image->image()->wdim = 4; @@ -1023,8 +1104,8 @@ Io_image_plugin::load(QFileInfo fileinfo, bool& ok, bool add_to_scene) QApplication::restoreOverrideCursor(); Image* image = new Image; - //read a nrrd file - if (fileinfo.suffix() == "nrrd") + // read a nrrd file + if(fileinfo.suffix() == "nrrd") { #ifdef CGAL_USE_VTK vtkNew reader; @@ -1040,7 +1121,7 @@ Io_image_plugin::load(QFileInfo fileinfo, bool& ok, bool add_to_scene) #endif } - //read a sep file + // read a sep file else if (fileinfo.suffix() == "H" || fileinfo.suffix() == "HH") { CGAL::SEP_to_ImageIO reader(fileinfo.filePath().toUtf8().data()); @@ -1049,94 +1130,104 @@ Io_image_plugin::load(QFileInfo fileinfo, bool& ok, bool add_to_scene) } else if(fileinfo.suffix() != "H" && fileinfo.suffix() != "HH" && - !image->read(fileinfo.filePath().toUtf8())) + !image->read(fileinfo.filePath().toUtf8())) + { + QMessageBox qmb(QMessageBox::NoIcon, + "Raw Dialog", + tr("Error with file %1:\n" + "unknown file format!\n" + "\n" + "Open it as a raw image?").arg(fileinfo.fileName()), + QMessageBox::Yes|QMessageBox::No); + + bool success = true; + if(qmb.exec() == QMessageBox::Yes) { - QMessageBox qmb(QMessageBox::NoIcon, - "Raw Dialog", - tr("Error with file %1:\n" - "unknown file format!\n" - "\n" - "Open it as a raw image?").arg(fileinfo.fileName()), - QMessageBox::Yes|QMessageBox::No); + Raw_image_dialog raw_dialog; + raw_dialog.label_file_size->setText(QString("%1 B").arg(fileinfo.size())); + raw_dialog.buttonBox->button(QDialogButtonBox::Open)->setEnabled(false); + if(raw_dialog.exec()) + { + QApplication::setOverrideCursor(Qt::WaitCursor); + QApplication::processEvents(); - bool success = true; - if(qmb.exec() == QMessageBox::Yes) { - Raw_image_dialog raw_dialog; - raw_dialog.label_file_size->setText(QString("%1 B").arg(fileinfo.size())); - raw_dialog.buttonBox->button(QDialogButtonBox::Open)->setEnabled(false); - if( raw_dialog.exec() ){ - - QApplication::setOverrideCursor(Qt::WaitCursor); - QApplication::processEvents(); - - if(image->read_raw(fileinfo.filePath().toUtf8(), - raw_dialog.dim_x->value(), - raw_dialog.dim_y->value(), - raw_dialog.dim_z->value(), - raw_dialog.spacing_x->value(), - raw_dialog.spacing_y->value(), - raw_dialog.spacing_z->value(), - raw_dialog.offset->value(), - raw_dialog.image_word_size(), - raw_dialog.image_word_kind(), - raw_dialog.image_sign()) - ){ - switch(raw_dialog.image_word_kind()) + if(image->read_raw(fileinfo.filePath().toUtf8(), + raw_dialog.dim_x->value(), + raw_dialog.dim_y->value(), + raw_dialog.dim_z->value(), + raw_dialog.spacing_x->value(), + raw_dialog.spacing_y->value(), + raw_dialog.spacing_z->value(), + raw_dialog.offset->value(), + raw_dialog.image_word_size(), + raw_dialog.image_word_kind(), + raw_dialog.image_sign())) + { + switch(raw_dialog.image_word_kind()) + { + case WK_FLOAT: + is_gray = true; + convert(image); + break; + case WK_FIXED: + { + switch(raw_dialog.image_word_size()) { - case WK_FLOAT: + case 2: is_gray = true; - convert(image); + convert(image); break; - case WK_FIXED: - { - switch(raw_dialog.image_word_size()) - { - case 2: - is_gray = true; - convert(image); - break; - case 4: - is_gray = true; - convert(image); - break; - default: - is_gray = false; - break; - } + case 4: + is_gray = true; + convert(image); break; - } default: + is_gray = false; break; } - QSettings settings; - settings.beginGroup(QUrl::toPercentEncoding(fileinfo.absoluteFilePath())); - settings.setValue("is_raw", true); - settings.setValue("dim_x", raw_dialog.dim_x->value()); - settings.setValue("dim_y", raw_dialog.dim_y->value()); - settings.setValue("dim_z", raw_dialog.dim_z->value()); - settings.setValue("spacing_x", raw_dialog.spacing_x->value()); - settings.setValue("spacing_y", raw_dialog.spacing_y->value()); - settings.setValue("spacing_z", raw_dialog.spacing_z->value()); - settings.setValue("offset", raw_dialog.offset->value()); - settings.setValue("wdim", QVariant::fromValue(raw_dialog.image_word_size())); - settings.setValue("wk", raw_dialog.image_word_kind()); - settings.setValue("sign", raw_dialog.image_sign()); - settings.endGroup(); - }else { - success = false; + break; } - }else { + default: + break; + } + + QSettings settings; + settings.beginGroup(QUrl::toPercentEncoding(fileinfo.absoluteFilePath())); + settings.setValue("is_raw", true); + settings.setValue("dim_x", raw_dialog.dim_x->value()); + settings.setValue("dim_y", raw_dialog.dim_y->value()); + settings.setValue("dim_z", raw_dialog.dim_z->value()); + settings.setValue("spacing_x", raw_dialog.spacing_x->value()); + settings.setValue("spacing_y", raw_dialog.spacing_y->value()); + settings.setValue("spacing_z", raw_dialog.spacing_z->value()); + settings.setValue("offset", raw_dialog.offset->value()); + settings.setValue("wdim", QVariant::fromValue(raw_dialog.image_word_size())); + settings.setValue("wk", raw_dialog.image_word_kind()); + settings.setValue("sign", raw_dialog.image_sign()); + settings.endGroup(); + } + else + { success = false; } - }else { + } + else + { success = false; } - if(!success){ - ok = false; - delete image; - return QList(); - } } + else + { + success = false; + } + + if(!success) + { + ok = false; + delete image; + return QList(); + } + } // Get display precision QDialog dialog; @@ -1149,18 +1240,19 @@ Io_image_plugin::load(QFileInfo fileinfo, bool& ok, bool add_to_scene) dialog.setWindowFlags(Qt::Dialog|Qt::CustomizeWindowHint|Qt::WindowCloseButtonHint); // Add precision values to the dialog - for ( int i=1 ; i<9 ; ++i ) + for(int i=1 ; i<9; ++i) { QString s = tr("1:%1").arg(i*i*i); ui.precisionList->addItem(s); } - //Adds Image type + // Adds Image type ui.imageType->addItem(QString("Segmented image")); ui.imageType->addItem(QString("Gray-level image")); QString type; int voxel_scale = 0; + // Open window QApplication::restoreOverrideCursor(); if(!is_gray) @@ -1180,9 +1272,13 @@ Io_image_plugin::load(QFileInfo fileinfo, bool& ok, bool add_to_scene) type = ui.imageType->currentText(); } else + { type = "Gray-level image"; + } + QApplication::setOverrideCursor(Qt::WaitCursor); QApplication::processEvents(); + Scene_image_item* image_item; if(type == "Gray-level image") { @@ -1196,10 +1292,14 @@ Io_image_plugin::load(QFileInfo fileinfo, bool& ok, bool add_to_scene) createPlanes(image_item); } else + { image_item = new Scene_image_item(image,voxel_scale, false); + } image_item->setName(fileinfo.baseName()); + if(add_to_scene) CGAL::Three::Three::scene()->addItem(image_item); + return QList() << image_item; } @@ -1303,6 +1403,7 @@ bool Io_image_plugin::loadDCM(QString dirname) return false; #endif } + Image* Io_image_plugin::createDCMImage(QString dirname) { Image* image = nullptr; @@ -1334,8 +1435,7 @@ Image* Io_image_plugin::createDCMImage(QString dirname) vtkNew dicom_reader; dicom_reader->SetDirectoryName(dirname.toUtf8()); - auto executive = - vtkDemandDrivenPipeline::SafeDownCast(dicom_reader->GetExecutive()); + auto executive = vtkDemandDrivenPipeline::SafeDownCast(dicom_reader->GetExecutive()); if (executive) { executive->SetReleaseDataFlag(0, 0); // where 0 is the port index @@ -1352,34 +1452,33 @@ Image* Io_image_plugin::createDCMImage(QString dirname) // image data } - if(is_bmp){ + if(is_bmp) + { vtkNew bmp_reader; bmp_reader->SetFileNames(files); - auto executive = - vtkDemandDrivenPipeline::SafeDownCast(bmp_reader->GetExecutive()); - if (executive) - { - executive->SetReleaseDataFlag(0, 0); // where 0 is the port index - } + auto executive = vtkDemandDrivenPipeline::SafeDownCast(bmp_reader->GetExecutive()); + if(executive) + executive->SetReleaseDataFlag(0, 0); // where 0 is the port index + vtkNew smoother; smoother->SetStandardDeviations(1., 1., 1.); smoother->SetInputConnection(bmp_reader->GetOutputPort()); smoother->Update(); + auto vtk_image = smoother->GetOutput(); vtk_image->Print(std::cerr); image = new Image; - *image = CGAL::IO::read_vtk_image_data(vtk_image); // copy the - // image data + *image = CGAL::IO::read_vtk_image_data(vtk_image); // copy the image data } #else CGAL::Three::Three::warning("You need VTK to read DCM/BMP files"); CGAL_USE(dirname); #endif - return image; + return image; } #include "Io_image_plugin.moc" From c36c54c1399e11f22fad0a53b449ce8e65c5964c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mael=20Rouxel-Labb=C3=A9?= Date: Wed, 7 Dec 2022 11:42:30 +0100 Subject: [PATCH 07/13] Fix "Planes for unnamed" --- Polyhedron/demo/Polyhedron/Plugins/Mesh_3/Io_image_plugin.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Polyhedron/demo/Polyhedron/Plugins/Mesh_3/Io_image_plugin.cpp b/Polyhedron/demo/Polyhedron/Plugins/Mesh_3/Io_image_plugin.cpp index 4e13744ef2b..8cb9f4853c0 100644 --- a/Polyhedron/demo/Polyhedron/Plugins/Mesh_3/Io_image_plugin.cpp +++ b/Polyhedron/demo/Polyhedron/Plugins/Mesh_3/Io_image_plugin.cpp @@ -1377,11 +1377,11 @@ bool Io_image_plugin::loadDCM(QString dirname) { // Create planes image_item = new Scene_image_item(image,125, true); + image_item->setName(fileinfo.baseName()); msgBox.setText("Planes created : 0/3"); msgBox.setStandardButtons(QMessageBox::NoButton); msgBox.show(); createPlanes(image_item); - image_item->setName(fileinfo.baseName()); scene->addItem(image_item); } else From 1c04eea7034b63139bb5cb0c9a868f555c460aef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mael=20Rouxel-Labb=C3=A9?= Date: Wed, 7 Dec 2022 12:19:24 +0100 Subject: [PATCH 08/13] Refactor DCM / BMP to avoid code duplication + add smoothing selection to GUI --- .../Plugins/Mesh_3/Image_res_dialog.ui | 116 ++++++++--- .../Plugins/Mesh_3/Io_image_plugin.cpp | 182 ++++++++++-------- 2 files changed, 189 insertions(+), 109 deletions(-) diff --git a/Polyhedron/demo/Polyhedron/Plugins/Mesh_3/Image_res_dialog.ui b/Polyhedron/demo/Polyhedron/Plugins/Mesh_3/Image_res_dialog.ui index 066201cbed7..41834b0133e 100644 --- a/Polyhedron/demo/Polyhedron/Plugins/Mesh_3/Image_res_dialog.ui +++ b/Polyhedron/demo/Polyhedron/Plugins/Mesh_3/Image_res_dialog.ui @@ -6,8 +6,8 @@ 0 0 - 421 - 370 + 529 + 347 @@ -19,24 +19,31 @@ Load a 3D image - - - + + + + + Image &type + + + imageType + + + + + Qt::Vertical 20 - 0 + 40 - - - - + Qt::Horizontal @@ -46,33 +53,48 @@ - - + + - Please choose the image &type - - - imageType + Smooth image data - + + + + Qt::Horizontal + + + + + + + - Drawing settings for a segment image + + + 0 + + + 0 + + + 0 + - - - - - false - - - - - + + + 0 + + + 6 + + + 11 @@ -80,20 +102,20 @@ - 1:x means that x voxels of the original image are represented by 1 cube in the drawn image + <html><head/><body><p><span style=" font-size:10pt;">1:x means that x voxels of the original image are represented by 1 cube in the drawn image</span></p></body></html> true - - + + 1 - Please choose the image drawing &precision + Segmented image drawing &precision true @@ -103,11 +125,41 @@ + + + + false + + + + + + + + + + true + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + diff --git a/Polyhedron/demo/Polyhedron/Plugins/Mesh_3/Io_image_plugin.cpp b/Polyhedron/demo/Polyhedron/Plugins/Mesh_3/Io_image_plugin.cpp index 8cb9f4853c0..04badd6299d 100644 --- a/Polyhedron/demo/Polyhedron/Plugins/Mesh_3/Io_image_plugin.cpp +++ b/Polyhedron/demo/Polyhedron/Plugins/Mesh_3/Io_image_plugin.cpp @@ -73,6 +73,7 @@ #include #include #include +#include // Covariant return types don't work for scalar types and we cannot // have templates here, hence this unfortunate hack. @@ -220,6 +221,12 @@ private: const unsigned int Plane_slider::scale = 100; +enum class Directory_extension_type +{ + DCM = 0, + BMP +}; + class Io_image_plugin : public QObject, public CGAL::Three::Polyhedron_demo_plugin_helper, @@ -256,6 +263,9 @@ public: QAction *actionLoadDCM = new QAction("Open Directory (DCM files)", mw); connect(actionLoadDCM, SIGNAL(triggered()), this, SLOT(on_actionLoadDCM_triggered())); + QAction *actionLoadBMP = new QAction("Open Directory (BMP files)", mw); + connect(actionLoadBMP, SIGNAL(triggered()), this, SLOT(on_actionLoadBMP_triggered())); + if(planeSwitch) { planeSwitch->setProperty("subMenuName", "3D Mesh Generation"); @@ -297,7 +307,8 @@ public: // Insert "Load implicit function" action if(nullptr != actionAfterLoad) { - menuFile->insertAction(actionAfterLoad,actionLoadDCM); + menuFile->insertAction(actionAfterLoad, actionLoadDCM); + menuFile->insertAction(actionAfterLoad, actionLoadBMP); } } } @@ -529,7 +540,7 @@ public Q_SLOTS: } - void on_actionLoadDCM_triggered() + void loadDirectory(const Directory_extension_type ext) { QSettings settings; QString start_dir = settings.value("Open directory", QDir::current().dirName()).toString(); @@ -544,12 +555,22 @@ public Q_SLOTS: settings.setValue("Open directory", fileinfo.absoluteDir().absolutePath()); QApplication::setOverrideCursor(Qt::WaitCursor); QApplication::processEvents(); - loadDCM(dir); - QApplication::restoreOverrideCursor(); + + loadDirectory(dir, ext); } } } + void on_actionLoadDCM_triggered() + { + return loadDirectory(Directory_extension_type::DCM); + } + + void on_actionLoadBMP_triggered() + { + return loadDirectory(Directory_extension_type::BMP); + } + void connectNewViewer(QObject* o) { Q_FOREACH(Controls c, group_map.values()) @@ -593,8 +614,8 @@ private: QMap group_map; unsigned int intersection_id; - bool loadDCM(QString filename); - Image* createDCMImage(QString dirname); + bool loadDirectory(const QString& filename, const Directory_extension_type ext); + Image* createDirectoryImage(const QString& dirname, const Directory_extension_type ext, const bool smooth); QLayout* createOrGetDockLayout() { @@ -1308,10 +1329,15 @@ bool Io_image_plugin::canSave(const CGAL::Three::Scene_item* item) return qobject_cast(item); } -bool Io_image_plugin::loadDCM(QString dirname) +bool Io_image_plugin::loadDirectory(const QString& dirname, + const Directory_extension_type ext) { +#ifndef CGAL_USE_VTK QApplication::restoreOverrideCursor(); -#ifdef CGAL_USE_VTK + CGAL::Three::Three::warning("VTK is required to read DCM and BMP files"); + CGAL_USE(dirname); + return false; +#else QFileInfo fileinfo; fileinfo.setFile(dirname); bool result = true; @@ -1355,17 +1381,18 @@ bool Io_image_plugin::loadDCM(QString dirname) QApplication::setOverrideCursor(Qt::WaitCursor); QApplication::processEvents(); - Image *image = createDCMImage(dirname); + bool smooth = ui.smoothImage->isChecked(); + + Image *image = createDirectoryImage(dirname, ext, smooth); if(image->image() == nullptr) { - QMessageBox::warning(mw, mw->windowTitle(), - tr("Error with file %1/:\nunknown file format!").arg(dirname)); - CGAL::Three::Three::warning(tr("Opening of file %1/ failed!").arg(dirname)); + QMessageBox::warning(mw, mw->windowTitle(), tr("Error opening directory %1/!").arg(dirname)); + CGAL::Three::Three::warning(tr("Opening of directory %1/ failed!").arg(dirname)); result = false; } else { - CGAL::Three::Three::information(tr("File %1/ successfully opened.").arg(dirname)); + CGAL::Three::Three::information(tr("Directory %1/ successfully opened.").arg(dirname)); } if(result) @@ -1396,86 +1423,87 @@ bool Io_image_plugin::loadDCM(QString dirname) } } + QApplication::restoreOverrideCursor(); return result; -#else - CGAL::Three::Three::warning("You need VTK to read a DCM file"); - CGAL_USE(dirname); - return false; #endif } -Image* Io_image_plugin::createDCMImage(QString dirname) +Image* Io_image_plugin::createDirectoryImage(const QString& dirname, + const Directory_extension_type ext, + const bool smooth) { Image* image = nullptr; -#ifdef CGAL_USE_VTK - bool is_dcm = false; - bool is_bmp = false; - - std::vector paths; - vtkStringArray* files = vtkStringArray::New(); - boost::filesystem::path p(dirname.toUtf8().data()); - for(boost::filesystem::directory_entry& x : boost::filesystem::directory_iterator(p)){ - std::string s(x.path().extension().string()); - if(s == std::string(".dcm") || (s == std::string(".DCM"))){ is_dcm = true; CGAL_assertion(!is_bmp); } - if(s == std::string(".bmp") || (s == std::string(".BMP"))){ is_bmp = true; CGAL_assertion(!is_dcm); } - paths.push_back(x.path()); - } - - // directory_iterator does not guarantee a sorted order - std::sort(std::begin(paths), std::end(paths)); - - for(const boost::filesystem::path& p : paths) +#ifndef CGAL_USE_VTK + CGAL::Three::Three::warning("VTK is required to read DCM and BMP files"); + CGAL_USE(dirname); + CGAL_USE(ext); +#else + auto create_image = [&](auto&& reader) -> void { - std::cout << p.string() << std::endl; - files->InsertNextValue(p.string()); - } - - if(is_dcm){ - vtkNew dicom_reader; - dicom_reader->SetDirectoryName(dirname.toUtf8()); - - auto executive = vtkDemandDrivenPipeline::SafeDownCast(dicom_reader->GetExecutive()); - if (executive) - { - executive->SetReleaseDataFlag(0, 0); // where 0 is the port index - } - - vtkNew smoother; - smoother->SetStandardDeviations(1., 1., 1.); - smoother->SetInputConnection(dicom_reader->GetOutputPort()); - smoother->Update(); - auto vtk_image = smoother->GetOutput(); - vtk_image->Print(std::cerr); - image = new Image; - *image = CGAL::IO::read_vtk_image_data(vtk_image); // copy the - // image data - } - - if(is_bmp) - { - vtkNew bmp_reader; - bmp_reader->SetFileNames(files); - - auto executive = vtkDemandDrivenPipeline::SafeDownCast(bmp_reader->GetExecutive()); + auto executive = vtkDemandDrivenPipeline::SafeDownCast(reader->GetExecutive()); if(executive) executive->SetReleaseDataFlag(0, 0); // where 0 is the port index - vtkNew smoother; - smoother->SetStandardDeviations(1., 1., 1.); - smoother->SetInputConnection(bmp_reader->GetOutputPort()); - smoother->Update(); + vtkImageData* vtk_image = nullptr; + vtkNew smoother; // must be here because it will own the vtk image + + if(smooth) + { + smoother->SetStandardDeviations(1., 1., 1.); + smoother->SetInputConnection(reader->GetOutputPort()); + smoother->Update(); + vtk_image = smoother->GetOutput(); + } + else + { + reader->Update(); + vtk_image = reader->GetOutput(); + } - auto vtk_image = smoother->GetOutput(); vtk_image->Print(std::cerr); - image = new Image; *image = CGAL::IO::read_vtk_image_data(vtk_image); // copy the image data - } + }; -#else - CGAL::Three::Three::warning("You need VTK to read DCM/BMP files"); - CGAL_USE(dirname); + image = new Image; + if(ext == Directory_extension_type::DCM) + { + vtkNew dicom_reader; + dicom_reader->SetDirectoryName(dirname.toUtf8()); + create_image(dicom_reader); + } + else + { + CGAL_assertion(ext == Directory_extension_type::BMP); + + // vtkBMPReader does not provide SetDirectoryName()... + std::vector paths; + vtkStringArray* files = vtkStringArray::New(); + boost::filesystem::path p(dirname.toUtf8().data()); + for(boost::filesystem::directory_entry& x : boost::filesystem::directory_iterator(p)) + { + std::string s = x.path().extension().string(); + std::transform(s.begin(), s.end(), s.begin(), tolower); + if(s != ".bmp") + continue; + + paths.push_back(x.path()); + } + + // boost::filesystem::directory_iterator does not guarantee a sorted order + std::sort(std::begin(paths), std::end(paths)); + + for(const boost::filesystem::path& p : paths) + files->InsertNextValue(p.string()); + + if(files->GetSize() == 0) + return image; + + vtkNew bmp_reader; + bmp_reader->SetFileNames(files); + create_image(bmp_reader); + } #endif return image; From f7925fdd1a8050497506cfdff4e07e3ee8acc7f5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mael=20Rouxel-Labb=C3=A9?= Date: Wed, 7 Dec 2022 12:19:50 +0100 Subject: [PATCH 09/13] Reposition DCM / BMP underneath "Load..." (instead of the bottom of "Files") --- Polyhedron/demo/Polyhedron/Plugins/Mesh_3/Io_image_plugin.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Polyhedron/demo/Polyhedron/Plugins/Mesh_3/Io_image_plugin.cpp b/Polyhedron/demo/Polyhedron/Plugins/Mesh_3/Io_image_plugin.cpp index 04badd6299d..4b1d4cf60b6 100644 --- a/Polyhedron/demo/Polyhedron/Plugins/Mesh_3/Io_image_plugin.cpp +++ b/Polyhedron/demo/Polyhedron/Plugins/Mesh_3/Io_image_plugin.cpp @@ -294,7 +294,7 @@ public: for(QList::iterator it_action = menuFileActions.begin(), end = menuFileActions.end() ; it_action != end ; ++ it_action ) //Q_FOREACH( QAction* action, menuFileActions) { - if(NULL != *it_action && (*it_action)->text().contains("Load Plugin")) + if(NULL != *it_action && (*it_action)->text().contains("Load...")) { ++it_action; if(it_action != end && NULL != *it_action) From f3ec4653e98982fe5e4c5bebf93977ad672adfa9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mael=20Rouxel-Labb=C3=A9?= Date: Wed, 7 Dec 2022 12:20:22 +0100 Subject: [PATCH 10/13] List the main item above its volume plane in item list --- Polyhedron/demo/Polyhedron/Plugins/Mesh_3/Io_image_plugin.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Polyhedron/demo/Polyhedron/Plugins/Mesh_3/Io_image_plugin.cpp b/Polyhedron/demo/Polyhedron/Plugins/Mesh_3/Io_image_plugin.cpp index 4b1d4cf60b6..a9c10ed9175 100644 --- a/Polyhedron/demo/Polyhedron/Plugins/Mesh_3/Io_image_plugin.cpp +++ b/Polyhedron/demo/Polyhedron/Plugins/Mesh_3/Io_image_plugin.cpp @@ -1408,8 +1408,9 @@ bool Io_image_plugin::loadDirectory(const QString& dirname, msgBox.setText("Planes created : 0/3"); msgBox.setStandardButtons(QMessageBox::NoButton); msgBox.show(); - createPlanes(image_item); + scene->addItem(image_item); + createPlanes(image_item); } else { From 282166307c5739e777ccc6a96e0e3f3a7c357d1d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mael=20Rouxel-Labb=C3=A9?= Date: Wed, 7 Dec 2022 12:20:39 +0100 Subject: [PATCH 11/13] Misc minor changes --- CGAL_ImageIO/include/CGAL/IO/read_vtk_image_data.h | 11 ++++++----- .../Polyhedron/Plugins/Mesh_3/Io_image_plugin.cpp | 4 ++-- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/CGAL_ImageIO/include/CGAL/IO/read_vtk_image_data.h b/CGAL_ImageIO/include/CGAL/IO/read_vtk_image_data.h index 22b19dee6ed..0492e2acc6a 100644 --- a/CGAL_ImageIO/include/CGAL/IO/read_vtk_image_data.h +++ b/CGAL_ImageIO/include/CGAL/IO/read_vtk_image_data.h @@ -94,16 +94,17 @@ read_vtk_image_data(vtkImageData* vtk_image, Image_3::Own owning = Image_3::OWN_ if(owning == Image_3::OWN_THE_DATA) { int dims_n = dims[0]*dims[1]*dims[2]; image->data = ::ImageIO_alloc(dims_n * image->wdim); - std::cerr << "GetNumberOfTuples() = " << vtk_image->GetPointData()->GetScalars()->GetNumberOfTuples() << "\n" - << "components = " << cn << "\n" - << "wdim = " << image->wdim << "\n" - << "image->size() = " << dims_n << std::endl; + + // std::cerr << "GetNumberOfTuples() = " << vtk_image->GetPointData()->GetScalars()->GetNumberOfTuples() << "\n" + // << "components = " << cn << "\n" + // << "wdim = " << image->wdim << "\n" + // << "image->size() = " << dims_n << std::endl; if(cn == 1) { vtk_image->GetPointData()->GetScalars()->ExportToVoidPointer(image->data); } else { std::cerr << "Warning: input has " << cn << " components; only the value of the first component will be used." << std::endl; - CGAL_assertion(cn >= 3); // if it's more than 1, it needs to be more than 3 + CGAL_assertion(cn >= 3); // if it's more than 1, it needs to be at least 3 // cast the data void pointers to make it possible to do pointer arithmetic char* src = static_cast(vtk_image->GetPointData()->GetScalars()->GetVoidPointer(0)); diff --git a/Polyhedron/demo/Polyhedron/Plugins/Mesh_3/Io_image_plugin.cpp b/Polyhedron/demo/Polyhedron/Plugins/Mesh_3/Io_image_plugin.cpp index a9c10ed9175..fa020b7a45e 100644 --- a/Polyhedron/demo/Polyhedron/Plugins/Mesh_3/Io_image_plugin.cpp +++ b/Polyhedron/demo/Polyhedron/Plugins/Mesh_3/Io_image_plugin.cpp @@ -1136,7 +1136,7 @@ Io_image_plugin::load(QFileInfo fileinfo, bool& ok, bool add_to_scene) vtk_image->Print(std::cerr); *image = CGAL::IO::read_vtk_image_data(vtk_image); // copy the image data #else - CGAL::Three::Three::warning("You need VTK to read a NRRD file"); + CGAL::Three::Three::warning("VTK is required to read NRRD files"); delete image; return QList(); #endif @@ -1366,7 +1366,7 @@ bool Io_image_plugin::loadDirectory(const QString& dirname, ui.precisionList->addItem(s); } - //Adds Image type + // Adds Image type ui.imageType->addItem(QString("Segmented image")); ui.imageType->addItem(QString("Gray-level image")); From 2cdaa261255bc6c35b1866f2c0e55f63c7c1c061 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mael=20Rouxel-Labb=C3=A9?= Date: Wed, 7 Dec 2022 12:37:22 +0100 Subject: [PATCH 12/13] Minor UI update --- .../Plugins/Mesh_3/Image_res_dialog.ui | 121 +++++++++--------- 1 file changed, 62 insertions(+), 59 deletions(-) diff --git a/Polyhedron/demo/Polyhedron/Plugins/Mesh_3/Image_res_dialog.ui b/Polyhedron/demo/Polyhedron/Plugins/Mesh_3/Image_res_dialog.ui index 41834b0133e..8e8c915e77f 100644 --- a/Polyhedron/demo/Polyhedron/Plugins/Mesh_3/Image_res_dialog.ui +++ b/Polyhedron/demo/Polyhedron/Plugins/Mesh_3/Image_res_dialog.ui @@ -6,7 +6,7 @@ 0 0 - 529 + 561 347 @@ -19,31 +19,8 @@ Load a 3D image - - - - - Image &type - - - imageType - - - - - - - Qt::Vertical - - - - 20 - 40 - - - - - + + Qt::Horizontal @@ -53,23 +30,6 @@ - - - - Smooth image data - - - - - - - Qt::Horizontal - - - - - - @@ -93,7 +53,7 @@ 6 - + @@ -102,20 +62,23 @@ - <html><head/><body><p><span style=" font-size:10pt;">1:x means that x voxels of the original image are represented by 1 cube in the drawn image</span></p></body></html> + <html><head/><body><p align="center"><span style=" font-size:10pt;">1:x means that x voxels of the original image are represented by 1 cube in the drawn image</span></p></body></html> true - + + + true + 1 - Segmented image drawing &precision + <html><head/><body><p align="right">Segmented image drawing &amp;precision </p></body></html> true @@ -125,7 +88,7 @@ - + false @@ -137,17 +100,7 @@ - - - - - - - true - - - - + Qt::Vertical @@ -160,6 +113,56 @@ + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + <html><head/><body><p align="right">Image &amp;type</p></body></html> + + + imageType + + + + + + + Qt::Horizontal + + + + + + + Smooth image data + + + + + + + + + + true + + + + + + From b219436ba1a240d969f286d4657297f7401eba76 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mael=20Rouxel-Labb=C3=A9?= Date: Wed, 7 Dec 2022 14:19:48 +0100 Subject: [PATCH 13/13] use Stream_support's get_file_extension() --- .../demo/Polyhedron/Plugins/Mesh_3/Io_image_plugin.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Polyhedron/demo/Polyhedron/Plugins/Mesh_3/Io_image_plugin.cpp b/Polyhedron/demo/Polyhedron/Plugins/Mesh_3/Io_image_plugin.cpp index fa020b7a45e..0819581709e 100644 --- a/Polyhedron/demo/Polyhedron/Plugins/Mesh_3/Io_image_plugin.cpp +++ b/Polyhedron/demo/Polyhedron/Plugins/Mesh_3/Io_image_plugin.cpp @@ -26,6 +26,7 @@ #include #include #include +#include #include #include #include @@ -1484,9 +1485,8 @@ Image* Io_image_plugin::createDirectoryImage(const QString& dirname, boost::filesystem::path p(dirname.toUtf8().data()); for(boost::filesystem::directory_entry& x : boost::filesystem::directory_iterator(p)) { - std::string s = x.path().extension().string(); - std::transform(s.begin(), s.end(), s.begin(), tolower); - if(s != ".bmp") + std::string s = x.path().string(); + if(CGAL::IO::internal::get_file_extension(s) != "bmp") continue; paths.push_back(x.path());