Skip to content

Commit

Permalink
Fix voxel downsize filter/tests.
Browse files Browse the repository at this point in the history
  • Loading branch information
abellgithub committed Feb 21, 2020
1 parent afbe6f7 commit 60b9a00
Show file tree
Hide file tree
Showing 4 changed files with 213 additions and 151 deletions.
25 changes: 14 additions & 11 deletions doc/stages/filters.voxeldownsize.rst
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,11 @@ filters.voxeldownsize
===============================================================================

The **voxeldownsize filter** is a voxel-based sampling filter.
The input point
cloud is divided into 3D voxels at the given cell size. For each populated
voxel, either first point entering in the voxel or center of a voxel (depending on mode argument) is accepted and voxel is marked as populated.
All other points entering in the same voxel are ignored/skipped.
The input point cloud is divided into 3D voxels at the given cell size.
For each populated voxel, either first point entering in the voxel or
center of a voxel (depending on mode argument) is accepted and voxel is
marked as populated. All other points entering in the same voxel are
filtered out.

Example
-------
Expand All @@ -27,9 +28,9 @@ Example
.. seealso::

:ref:`filters.voxelcenternearestneighbor` offers a similar solution,
using
the coordinates of the voxel center as the query point in a 3D nearest neighbor search.
The nearest neighbor is then added to the output point cloud, along with any existing dimensions.
using the coordinates of the voxel center as the query point in a 3D
nearest neighbor search. The nearest neighbor is then added to the
output point cloud, along with any existing dimensions.

Options
-------------------------------------------------------------------------------
Expand All @@ -39,9 +40,11 @@ cell

mode
Mode for voxel based filtering. [Default: voxelcenter]
**voxelcenter**: Retain center of a populated voxel. Coordinates of a first point detected will be modified and set to the center of a populated voxel.
**firstinvoxel**: Retain first point detected in each voxel.
**voxelcenter**: Coordinates of the first point found in each voxel will
be modified to those at the center of the voxel.
**firstinvoxel**: Only the first point found in each voxel is retained.

.. warning::
If you choose **voxelcenter** mode, you are loosing your dimensional data/point coordinates.

If you choose **voxelcenter** mode, you are overwriting the X, Y and Z
values of retained points.

106 changes: 60 additions & 46 deletions filters/VoxelDownsizeFilter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,37 @@ static StaticPluginInfo const s_info

CREATE_STATIC_STAGE(VoxelDownsizeFilter, s_info)

std::istream& operator>>(std::istream& in, VoxelDownsizeFilter::Mode& mode)
{
std::string s;
in >> s;

s = Utils::tolower(s);
if (s == "center")
mode = VoxelDownsizeFilter::Mode::Center;
else if (s == "first")
mode = VoxelDownsizeFilter::Mode::First;
else
throw pdal_error("filters.voxeldownsize: Invalid 'mode' option '" +
s + "'. " "Valid options are 'center' and 'first'");
return in;
}


std::ostream& operator<<(std::ostream& out,
const VoxelDownsizeFilter::Mode& mode)
{
switch (mode)
{
case VoxelDownsizeFilter::Mode::Center:
out << "center";
case VoxelDownsizeFilter::Mode::First:
out << "first";
}
return out;
}


VoxelDownsizeFilter::VoxelDownsizeFilter()
{}

Expand All @@ -60,38 +91,24 @@ std::string VoxelDownsizeFilter::getName() const
void VoxelDownsizeFilter::addArgs(ProgramArgs& args)
{
args.add("cell", "Cell size", m_cell, 0.001);
args.add("mode", "Method for downsizing : voxelcenter / firstinvoxel",
m_mode, "voxelcenter");
args.add("mode", "Method for downsizing : center / first",
m_mode, Mode::Center);
}


void VoxelDownsizeFilter::ready(PointTableRef)
{
m_pivotVoxelInitialized = false;
}


void VoxelDownsizeFilter::prepared(PointTableRef)
{
m_mode = Utils::toupper(m_mode);
if (m_mode == "VOXELCENTER")
m_isFirstInVoxelMode = false;
else if (m_mode == "FIRSTINVOXEL")
m_isFirstInVoxelMode = true;
else
throwError("Invalid mode specified.");
}
{ m_populatedVoxels.clear(); }


PointViewSet VoxelDownsizeFilter::run(PointViewPtr view)
{
PointViewPtr output = view->makeNew();
PointRef point(*view);
for (PointId id = 0; id < view->size(); ++id)
{
if (voxelize(view->point(id)))
{
point.setPointId(id);
if (voxelize(point))
output->appendPoint(*view, id);
}
}

PointViewSet viewSet;
Expand All @@ -100,42 +117,39 @@ PointViewSet VoxelDownsizeFilter::run(PointViewPtr view)
}


bool VoxelDownsizeFilter::voxelize(PointRef point)
bool VoxelDownsizeFilter::voxelize(PointRef& point)
{
/*
* Calculate the voxel coordinates for the incoming point.
* gx, gy, gz will be the global coordinates from (0, 0, 0).
*/
int gx = std::floor(point.getFieldAs<double>(Dimension::Id::X) / m_cell);
int gy = std::floor(point.getFieldAs<double>(Dimension::Id::Y) / m_cell);
int gz = std::floor(point.getFieldAs<double>(Dimension::Id::Z) / m_cell);

if (!m_pivotVoxelInitialized)
double x = point.getFieldAs<double>(Dimension::Id::X);
double y = point.getFieldAs<double>(Dimension::Id::Y);
double z = point.getFieldAs<double>(Dimension::Id::Z);
if (m_populatedVoxels.empty())
{
/*
* Save global coordinates of first incoming point's voxel.
* This will act as a Pivot for calculation of local coordinates of the
* voxels.
*/
m_pivotVoxel[0] = gx; // X Coordinate of an Pivot voxel
m_pivotVoxel[1] = gy; // Y Coordinate of an Pivot voxel
m_pivotVoxel[2] = gz; // Z Coordinate of an Pivot voxel
m_pivotVoxelInitialized = true;
m_originX = x - (m_cell / 2);
m_originY = y - (m_cell / 2);
m_originZ = z - (m_cell / 2);
}

/*
* Calculate the local voxel coordinates for incoming point, Using the
* Pivot voxel.
*/
auto t = std::make_tuple(gx - m_pivotVoxel[0], gy - m_pivotVoxel[1],
gz - m_pivotVoxel[2]);
// Offset by origin.
x -= m_originX;
y -= m_originY;
z -= m_originZ;

Voxel v = std::make_tuple((int)(std::floor(x / m_cell)),
(int)(std::floor(y / m_cell)), (int)(std::floor(z / m_cell)));

auto inserted = m_populatedVoxels.insert(t).second;
if (!m_isFirstInVoxelMode && inserted)
auto inserted = m_populatedVoxels.insert(v).second;
if ((m_mode == Mode::Center) && inserted)
{
point.setField<double>(Dimension::Id::X, (gx + 0.5) * m_cell);
point.setField<double>(Dimension::Id::Y, (gy + 0.5) * m_cell);
point.setField<double>(Dimension::Id::Z, (gz + 0.5) * m_cell);
point.setField(Dimension::Id::X,
(std::get<0>(v) + 0.5) * m_cell + m_originX);
point.setField(Dimension::Id::Y,
(std::get<1>(v) + 0.5) * m_cell + m_originY);
point.setField(Dimension::Id::Z,
(std::get<2>(v) + 0.5) * m_cell + m_originZ);
}
return inserted;
}
Expand Down
24 changes: 17 additions & 7 deletions filters/VoxelDownsizeFilter.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,12 @@ class PointView;

class PDAL_DLL VoxelDownsizeFilter : public Filter, public Streamable
{
using Voxel = std::tuple<int, int, int>;
enum class Mode
{
First,
Center
};
public:
VoxelDownsizeFilter();
VoxelDownsizeFilter& operator=(const VoxelDownsizeFilter&) = delete;
Expand All @@ -59,16 +65,20 @@ class PDAL_DLL VoxelDownsizeFilter : public Filter, public Streamable
virtual PointViewSet run(PointViewPtr view) override;
virtual void ready(PointTableRef) override;
virtual bool processOne(PointRef& point) override;
virtual void prepared(PointTableRef) override;

bool voxelize(PointRef point);
bool voxelize(PointRef& point);

double m_cell;
std::set<std::tuple<int, int, int>> m_populatedVoxels;
int m_pivotVoxel[3]; // [0/1/2]: X/Y/Z dimension
bool m_pivotVoxelInitialized;
std::string m_mode;
bool m_isFirstInVoxelMode; // True: firstinvoxel, False: voxelcenter mode
double m_originX;
double m_originY;
double m_originZ;
std::set<Voxel> m_populatedVoxels;
Mode m_mode;

friend std::istream& operator>>(std::istream& in,
VoxelDownsizeFilter::Mode&);
friend std::ostream& operator<<(std::ostream& out,
const VoxelDownsizeFilter::Mode&);
};

} // namespace pdal

0 comments on commit 60b9a00

Please sign in to comment.