Merge pull request #7417 from afabri/Polyhedron-nifti-GF

Polyhedron Demo: Add reading NIFTI files (*.nii)
This commit is contained in:
Laurent Rineau 2023-05-11 16:55:26 +02:00
commit e717b873c5
3 changed files with 138 additions and 76 deletions

View File

@ -150,6 +150,10 @@ public:
float ty() const { return image_ptr->ty; }
float tz() const { return image_ptr->tz; }
float& tx(){ return image_ptr->tx; }
float& ty(){ return image_ptr->ty; }
float& tz(){ return image_ptr->tz; }
float value(const std::size_t i,
const std::size_t j,
const std::size_t k) const

View File

@ -58,6 +58,7 @@
#include <vtkImageData.h>
#include <vtkDICOMImageReader.h>
#include <vtkBMPReader.h>
#include <vtkNIFTIImageReader.h>
#include <vtkImageReader.h>
#include <vtkImageGaussianSmooth.h>
#include <vtkDemandDrivenPipeline.h>
@ -1086,9 +1087,10 @@ private Q_SLOTS:
QString Io_image_plugin::nameFilters() const
{
return QString("Inrimage files (*.inr *.inr.gz) ;; "
"Analyze files (*.hdr *.img *img.gz) ;; "
"Analyze files (*.hdr *.img *.img.gz) ;; "
"Stanford Exploration Project files (*.H *.HH) ;; "
"NRRD image files (*.nrrd)");
"NRRD image files (*.nrrd) ;; "
"NIFTI image files (*.nii)");
}
bool Io_image_plugin::canLoad(QFileInfo) const
@ -1143,6 +1145,23 @@ Io_image_plugin::load(QFileInfo fileinfo, bool& ok, bool add_to_scene)
#endif
}
// read a NIFTI file
if(fileinfo.suffix() == "nii")
{
#ifdef CGAL_USE_VTK
vtkNew<vtkNIFTIImageReader> reader;
reader->SetFileName(fileinfo.filePath().toUtf8());
reader->Update();
auto vtk_image = reader->GetOutput();
vtk_image->Print(std::cerr);
*image = CGAL::IO::read_vtk_image_data(vtk_image); // copy the image data
#else
CGAL::Three::Three::warning("VTK is required to read NIfTI files");
delete image;
return QList<Scene_item*>();
#endif
}
// read a sep file
else if (fileinfo.suffix() == "H" || fileinfo.suffix() == "HH")
{

View File

@ -20,6 +20,7 @@ typedef Edge_container Ec;
typedef Viewer_interface Vi;
namespace internal {
template <typename Word_type>
class Image_accessor
{
public:
@ -33,47 +34,47 @@ public:
int dx() const { return dx_; }
int dy() const { return dy_; }
int dz() const { return dz_; }
std::size_t xdim() const { return im_.xdim(); }
std::size_t ydim() const { return im_.ydim(); }
std::size_t zdim() const { return im_.zdim(); }
double vx() const { return im_.vx(); }
double vy() const { return im_.vy(); }
double vz() const { return im_.vz(); }
std::size_t xdim() const { return im_->xdim(); }
std::size_t ydim() const { return im_->ydim(); }
std::size_t zdim() const { return im_->zdim(); }
double vx() const { return im_->vx(); }
double vy() const { return im_->vy(); }
double vz() const { return im_->vz(); }
double tx() const { return im_.image()->tx; }
double ty() const { return im_.image()->ty; }
double tz() const { return im_.image()->tz; }
double tx() const { return im_->image()->tx; }
double ty() const { return im_->image()->ty; }
double tz() const { return im_->image()->tz; }
private:
unsigned char non_null_neighbor_data(std::size_t i,
Word_type non_null_neighbor_data(std::size_t i,
std::size_t j,
std::size_t k) const;
unsigned char image_data(std::size_t i, std::size_t j, std::size_t k) const;
Word_type image_data(std::size_t i, std::size_t j, std::size_t k) const;
void add_to_normal(unsigned char v,
void add_to_normal(Word_type v,
float& x, float& y, float& z,
int dx, int dy, int dz) const;
private:
const Image& im_;
const Image* im_;
int dx_, dy_, dz_;
const QColor default_color_;
std::map<unsigned char, QColor> colors_;
QColor default_color_;
std::map<Word_type, QColor> colors_;
};
Image_accessor::Image_accessor(const Image& im, int dx, int dy, int dz)
: im_(im)
template <typename Word_type>
Image_accessor<Word_type>::Image_accessor(const Image& im, int dx, int dy, int dz)
: im_(&im)
, dx_(dx)
, dy_(dy)
, dz_(dz)
, default_color_()
, colors_()
{
const std::size_t xdim = im_.xdim();
const std::size_t ydim = im_.ydim();
const std::size_t zdim = im_.zdim();
const std::size_t xdim = im_->xdim();
const std::size_t ydim = im_->ydim();
const std::size_t zdim = im_->zdim();
for(std::size_t i=0 ; i<xdim ; i+=dx_)
{
@ -81,7 +82,7 @@ Image_accessor::Image_accessor(const Image& im, int dx, int dy, int dz)
{
for(std::size_t k=0 ; k<zdim ; k+=dz_)
{
unsigned char c = image_data(i,j,k);
Word_type c = image_data(i,j,k);
if ( 0 != c ) { colors_.insert(std::make_pair(c,QColor())); }
}
}
@ -89,7 +90,7 @@ Image_accessor::Image_accessor(const Image& im, int dx, int dy, int dz)
double i=0;
const double starting_hue = 45./360.; // magenta
for ( std::map<unsigned char, QColor>::iterator it = colors_.begin(),
for ( auto it = colors_.begin(),
end = colors_.end() ; it != end ; ++it, i += 1.)
{
double hue = starting_hue + 1./double(colors_.size()) * i;
@ -98,18 +99,19 @@ Image_accessor::Image_accessor(const Image& im, int dx, int dy, int dz)
}
}
template <typename Word_type>
bool
Image_accessor::
Image_accessor<Word_type>::
is_vertex_active(std::size_t i, std::size_t j, std::size_t k) const
{
unsigned char v1 = image_data(i-dx_, j-dy_, k-dz_);
unsigned char v2 = image_data(i-dx_, j-dy_, k );
unsigned char v3 = image_data(i-dx_, j , k-dz_);
unsigned char v4 = image_data(i-dx_, j , k );
unsigned char v5 = image_data(i , j-dy_, k-dz_);
unsigned char v6 = image_data(i , j-dy_, k );
unsigned char v7 = image_data(i , j , k-dz_);
unsigned char v8 = image_data(i , j , k );
Word_type v1 = image_data(i-dx_, j-dy_, k-dz_);
Word_type v2 = image_data(i-dx_, j-dy_, k );
Word_type v3 = image_data(i-dx_, j , k-dz_);
Word_type v4 = image_data(i-dx_, j , k );
Word_type v5 = image_data(i , j-dy_, k-dz_);
Word_type v6 = image_data(i , j-dy_, k );
Word_type v7 = image_data(i , j , k-dz_);
Word_type v8 = image_data(i , j , k );
// don't draw interior vertices
if ( v1 != 0 && v2 != 0 && v3 != 0 && v4 != 0 &&
@ -121,33 +123,35 @@ is_vertex_active(std::size_t i, std::size_t j, std::size_t k) const
return ( v1 != 0 || v2 != 0 || v3 != 0 || v4 != 0 ||
v5 != 0 || v6 != 0 || v7 != 0 || v8 != 0 );
}
template <typename Word_type>
const QColor&
Image_accessor::vertex_color(std::size_t i, std::size_t j, std::size_t k) const
Image_accessor<Word_type>::vertex_color(std::size_t i, std::size_t j, std::size_t k) const
{
unsigned char c = non_null_neighbor_data(i,j,k);
Word_type c = non_null_neighbor_data(i,j,k);
if ( 0 == c ) { return default_color_; }
std::map<unsigned char, QColor>::const_iterator color = colors_.find(c);
auto color = colors_.find(c);
if ( colors_.end() == color ) { return default_color_; }
return color->second;
}
unsigned char
Image_accessor::image_data(std::size_t i, std::size_t j, std::size_t k) const
template <typename Word_type>
Word_type
Image_accessor<Word_type>::image_data(std::size_t i, std::size_t j, std::size_t k) const
{
if ( i<im_.xdim() && j<im_.ydim() && k<im_.zdim() )
return CGAL::IMAGEIO::static_evaluate<unsigned char>(im_.image(),i,j,k);
if ( i<im_->xdim() && j<im_->ydim() && k<im_->zdim() )
return CGAL::IMAGEIO::static_evaluate<Word_type>(im_->image(),i,j,k);
else
return 0;
}
unsigned char
Image_accessor::
template <typename Word_type>
Word_type
Image_accessor<Word_type>::
non_null_neighbor_data(std::size_t i, std::size_t j, std::size_t k) const
{
unsigned char v = image_data(i-dx_, j-dy_, k-dz_);
Word_type v = image_data(i-dx_, j-dy_, k-dz_);
if ( v != 0 ) { return v; }
v = image_data(i-dx_, j-dy_, k );
@ -174,12 +178,13 @@ non_null_neighbor_data(std::size_t i, std::size_t j, std::size_t k) const
return 0;
}
template <typename Word_type>
void
Image_accessor::
Image_accessor<Word_type>::
normal(std::size_t i, std::size_t j, std::size_t k,
float& x, float& y, float& z) const
{
unsigned char v = image_data(i-dx_, j-dy_, k-dz_);
Word_type v = image_data(i-dx_, j-dy_, k-dz_);
add_to_normal(v,x,y,z, 1 , 1 , 1);
v = image_data( i-dx_, j-dy_, k);
@ -204,9 +209,10 @@ normal(std::size_t i, std::size_t j, std::size_t k,
add_to_normal(v,x,y,z, -1 , -1 , -1);
}
template <typename Word_type>
void
Image_accessor::
add_to_normal(unsigned char v,
Image_accessor<Word_type>::
add_to_normal(Word_type v,
float& x, float& y, float& z,
int dx, int dy, int dz) const
{
@ -218,14 +224,11 @@ add_to_normal(unsigned char v,
}
}
class Vertex_buffer_helper
{
public:
Vertex_buffer_helper(const Image_accessor& data, bool is_ogl_4_3);
void fill_buffer_data();
virtual void fill_buffer_data() = 0;
virtual ~Vertex_buffer_helper() {}
float* colors() { return colors_.data(); }
float* normals() { return normals_.data(); }
@ -236,6 +239,19 @@ public:
std::size_t normal_size() const { return normals_.size(); }
std::size_t vertex_size() const { return vertices_.size(); }
std::size_t quad_size() const { return quads_.size(); }
protected:
std::vector<float> colors_, normals_, vertices_;
std::vector<unsigned int> quads_;
};
template <typename Word_type>
class Vertex_buffer_helper_impl : public Vertex_buffer_helper
{
public:
Vertex_buffer_helper_impl(Image_accessor<Word_type>&& data, bool is_ogl_4_3);
~Vertex_buffer_helper_impl() override {}
void fill_buffer_data() override;
private:
void treat_vertex(std::size_t i, std::size_t j, std::size_t k);
@ -254,26 +270,28 @@ private:
int dz() const { return data_.dz(); }
private:
static int vertex_not_found_;
static constexpr int vertex_not_found_ = -1;
const Image_accessor& data_;
const Image_accessor<Word_type> data_;
typedef std::map<int, std::size_t> Indices;
Indices indices_;
std::vector<float> colors_, normals_, vertices_;
std::vector<unsigned int> quads_;
bool is_ogl_4_3;
};
int Vertex_buffer_helper::vertex_not_found_ = -1;
// template <typename Word_type>
// int Vertex_buffer_helper_impl<Word_type>::vertex_not_found_ = -1;
Vertex_buffer_helper::
Vertex_buffer_helper(const Image_accessor& data, bool b)
: data_(data), is_ogl_4_3(b)
template <typename Word_type>
Vertex_buffer_helper_impl<Word_type>::
Vertex_buffer_helper_impl(Image_accessor<Word_type>&& data, bool b)
: data_(std::move(data)), is_ogl_4_3(b)
{}
template <typename Word_type>
void
Vertex_buffer_helper::
Vertex_buffer_helper_impl<Word_type>::
fill_buffer_data()
{
std::size_t i,j,k;
@ -289,8 +307,9 @@ fill_buffer_data()
}
}
template <typename Word_type>
void
Vertex_buffer_helper::treat_vertex(std::size_t i, std::size_t j, std::size_t k)
Vertex_buffer_helper_impl<Word_type>::treat_vertex(std::size_t i, std::size_t j, std::size_t k)
{
if ( data_.is_vertex_active(i,j,k) )
{
@ -301,8 +320,9 @@ Vertex_buffer_helper::treat_vertex(std::size_t i, std::size_t j, std::size_t k)
}
}
template <typename Word_type>
void
Vertex_buffer_helper::push_color(std::size_t i, std::size_t j, std::size_t k)
Vertex_buffer_helper_impl<Word_type>::push_color(std::size_t i, std::size_t j, std::size_t k)
{
const QColor& color = data_.vertex_color(i,j,k);
if ( ! color.isValid() ) { return; }
@ -311,8 +331,9 @@ Vertex_buffer_helper::push_color(std::size_t i, std::size_t j, std::size_t k)
colors_.push_back(float(color.blue())/255.f);
}
template <typename Word_type>
void
Vertex_buffer_helper::push_normal(std::size_t i, std::size_t j, std::size_t k)
Vertex_buffer_helper_impl<Word_type>::push_normal(std::size_t i, std::size_t j, std::size_t k)
{
float x=0.f, y=0.f, z=0.f;
data_.normal(i,j,k,x,y,z);
@ -327,8 +348,9 @@ Vertex_buffer_helper::push_normal(std::size_t i, std::size_t j, std::size_t k)
normals_.push_back(z);
}
template <typename Word_type>
void
Vertex_buffer_helper::push_vertex(std::size_t i, std::size_t j, std::size_t k)
Vertex_buffer_helper_impl<Word_type>::push_vertex(std::size_t i, std::size_t j, std::size_t k)
{
indices_.insert(std::make_pair(compute_position(i,j,k),
vertices_.size()/3));
@ -352,8 +374,9 @@ Vertex_buffer_helper::push_vertex(std::size_t i, std::size_t j, std::size_t k)
vertices_.push_back( (dk - 0.5) * data_.vz() + data_.tz());
}
template <typename Word_type>
void
Vertex_buffer_helper::push_quads(std::size_t i, std::size_t j, std::size_t k)
Vertex_buffer_helper_impl<Word_type>::push_quads(std::size_t i, std::size_t j, std::size_t k)
{
int pos1 = vertex_index(i-dx(), j , k);
int pos2 = vertex_index(i-dx(), j-dy(), k);
@ -372,8 +395,9 @@ Vertex_buffer_helper::push_quads(std::size_t i, std::size_t j, std::size_t k)
push_quad(pos1, pos2, pos3, pos4);
}
template <typename Word_type>
void
Vertex_buffer_helper::push_quad(int pos1, int pos2, int pos3, int pos4)
Vertex_buffer_helper_impl<Word_type>::push_quad(int pos1, int pos2, int pos3, int pos4)
{
if ( pos1 != vertex_not_found_
&& pos2 != vertex_not_found_
@ -391,8 +415,9 @@ Vertex_buffer_helper::push_quad(int pos1, int pos2, int pos3, int pos4)
}
}
template <typename Word_type>
int
Vertex_buffer_helper::
Vertex_buffer_helper_impl<Word_type>::
compute_position(std::size_t i, std::size_t j, std::size_t k) const
{
return static_cast<int>(
@ -401,8 +426,9 @@ compute_position(std::size_t i, std::size_t j, std::size_t k) const
+ k/dz());
}
template <typename Word_type>
int
Vertex_buffer_helper::
Vertex_buffer_helper_impl<Word_type>::
vertex_index(std::size_t i, std::size_t j, std::size_t k) const
{
if ( i > data_.xdim() || j > data_.ydim() || k > data_.zdim() )
@ -751,17 +777,30 @@ void Scene_image_item::initializeBuffers(Viewer_interface *v) const
}
}
template <typename Word_type>
internal::Vertex_buffer_helper*
init_helper(const Image &im, int dx, int dy, int dz, bool is_ogl_4_3)
{
internal::Image_accessor<Word_type> image_data_accessor(im, dx, dy, dz);
return new internal::Vertex_buffer_helper_impl<Word_type>(std::move(image_data_accessor),
is_ogl_4_3);
}
void Scene_image_item::computeElements() const
{
QApplication::setOverrideCursor(Qt::WaitCursor);
d->draw_Bbox(bbox(), &d->v_box);
if(!d->is_hidden)
{
internal::Image_accessor image_data_accessor (*m_image,
d->m_voxel_scale,
d->m_voxel_scale,
d->m_voxel_scale);
d->helper = new internal::Vertex_buffer_helper(image_data_accessor, d->is_ogl_4_3);
// internal::Image_accessor image_data_accessor (*m_image,
// d->m_voxel_scale,
// d->m_voxel_scale,
// d->m_voxel_scale);
// d->helper = new internal::Vertex_buffer_helper(image_data_accessor, d->is_ogl_4_3);
CGAL_IMAGE_IO_CASE(m_image->image(),
d->helper = init_helper<Word>(*m_image,
d->m_voxel_scale, d->m_voxel_scale, d->m_voxel_scale,
d->is_ogl_4_3));
d->helper->fill_buffer_data();
getTriangleContainer(0)->allocate(
Tc::Smooth_vertices,