Skip to content

Commit

Permalink
Add some initial support for handling synthetic points
Browse files Browse the repository at this point in the history
* Add tests for LasReader and LasWriter

* Add method to Segmentation.cpp to separate PointViews into synthetic
  and real points

* Add arguments to SMRF to allow/ignore synthetic points during
  processing
  • Loading branch information
chambbj committed Jan 17, 2020
1 parent 1872800 commit 374d630
Show file tree
Hide file tree
Showing 6 changed files with 113 additions and 28 deletions.
47 changes: 25 additions & 22 deletions filters/SMRFilter.cpp
Expand Up @@ -63,23 +63,19 @@ namespace pdal
using namespace Dimension;
using namespace Eigen;

static StaticPluginInfo const s_info
{
"filters.smrf",
"Simple Morphological Filter (Pingel et al., 2013)",
"http://pdal.io/stages/filters.smrf.html"
};
static StaticPluginInfo const s_info{
"filters.smrf", "Simple Morphological Filter (Pingel et al., 2013)",
"http://pdal.io/stages/filters.smrf.html"};

// Without the cast, MSVC complains, which is ridiculous when the output
// is, by definition, an int.
namespace
{
template<typename T>
T ceil(double d)
template <typename T> T ceil(double d)
{
return static_cast<T>(std::ceil(d));
}
}
} // namespace

CREATE_STATIC_STAGE(SMRFilter, s_info)

Expand All @@ -94,13 +90,12 @@ struct SMRArgs
std::string m_dir;
std::vector<DimRange> m_ignored;
StringList m_returns;
bool m_allowSynthetic;
};

SMRFilter::SMRFilter() : m_args(new SMRArgs)
{}
SMRFilter::SMRFilter() : m_args(new SMRArgs) {}

SMRFilter::~SMRFilter()
{}
SMRFilter::~SMRFilter() {}

std::string SMRFilter::getName() const
{
Expand All @@ -119,6 +114,8 @@ void SMRFilter::addArgs(ProgramArgs& args)
args.add("ignore", "Ignore values", m_args->m_ignored);
args.add("returns", "Include last returns?", m_args->m_returns,
{"last", "only"});
args.add("synthetic", "Allow synthetic returns?", m_args->m_allowSynthetic,
true);
}

void SMRFilter::addDimensions(PointLayoutPtr layout)
Expand Down Expand Up @@ -185,17 +182,22 @@ PointViewSet SMRFilter::run(PointViewPtr view)
Segmentation::ignoreDimRanges(m_args->m_ignored, view, keptView,
ignoredView);

PointViewPtr syntheticView = keptView->makeNew();
PointViewPtr realView = keptView->makeNew();
if (m_args->m_allowSynthetic)
realView->append(*keptView);
else
Segmentation::ignoreSynthetic(keptView, realView, syntheticView);

// Check for 0's in ReturnNumber and NumberOfReturns
bool nrOneZero(false);
bool rnOneZero(false);
bool nrAllZero(true);
bool rnAllZero(true);
for (PointId i = 0; i < keptView->size(); ++i)
for (PointId i = 0; i < realView->size(); ++i)
{
uint8_t nr =
keptView->getFieldAs<uint8_t>(Id::NumberOfReturns, i);
uint8_t rn =
keptView->getFieldAs<uint8_t>(Id::ReturnNumber, i);
uint8_t nr = realView->getFieldAs<uint8_t>(Id::NumberOfReturns, i);
uint8_t rn = realView->getFieldAs<uint8_t>(Id::ReturnNumber, i);
if ((nr == 0) && !nrOneZero)
nrOneZero = true;
if ((rn == 0) && !rnOneZero)
Expand All @@ -212,18 +214,18 @@ PointViewSet SMRFilter::run(PointViewPtr view)
"1.");

// Segment kept view into two views
PointViewPtr firstView = keptView->makeNew();
PointViewPtr secondView = keptView->makeNew();
PointViewPtr firstView = realView->makeNew();
PointViewPtr secondView = realView->makeNew();
if (nrAllZero && rnAllZero)
{
log()->get(LogLevel::Warning)
<< "Both NumberOfReturns and ReturnNumber are filled with 0's. "
"Proceeding without any further return filtering.\n";
firstView->append(*keptView);
firstView->append(*realView);
}
else
{
Segmentation::segmentReturns(keptView, firstView, secondView,
Segmentation::segmentReturns(realView, firstView, secondView,
m_args->m_returns);
}

Expand Down Expand Up @@ -271,6 +273,7 @@ PointViewSet SMRFilter::run(PointViewPtr view)
classifyGround(firstView, ZIpro);

PointViewPtr outView = view->makeNew();
outView->append(*syntheticView);
outView->append(*ignoredView);
outView->append(*secondView);
outView->append(*firstView);
Expand Down
22 changes: 18 additions & 4 deletions filters/private/Segmentation.cpp
Expand Up @@ -127,7 +127,7 @@ void ignoreDimRange(DimRange dr, PointViewPtr input, PointViewPtr keep,
}

void ignoreDimRanges(std::vector<DimRange>& ranges, PointViewPtr input,
PointViewPtr keep, PointViewPtr ignore)
PointViewPtr keep, PointViewPtr ignore)
{
std::sort(ranges.begin(), ranges.end());
PointRef point(*input, 0);
Expand All @@ -141,6 +141,20 @@ void ignoreDimRanges(std::vector<DimRange>& ranges, PointViewPtr input,
}
}

void ignoreSynthetic(PointViewPtr input, PointViewPtr keep, PointViewPtr ignore)
{
using namespace Dimension;

for (PointId i = 0; i < input->size(); ++i)
{
uint8_t c = input->getFieldAs<uint8_t>(Id::Classification, i);
if (c & (1 << 5))
ignore->appendPoint(*input, i);
else
keep->appendPoint(*input, i);
}
}

void segmentLastReturns(PointViewPtr input, PointViewPtr last,
PointViewPtr other)
{
Expand All @@ -157,8 +171,8 @@ void segmentLastReturns(PointViewPtr input, PointViewPtr last,
}
}

void segmentReturns(PointViewPtr input, PointViewPtr first,
PointViewPtr second, StringList returns)
void segmentReturns(PointViewPtr input, PointViewPtr first, PointViewPtr second,
StringList returns)
{
using namespace Dimension;

Expand Down Expand Up @@ -190,7 +204,7 @@ void segmentReturns(PointViewPtr input, PointViewPtr first,
{
uint8_t rn = input->getFieldAs<uint8_t>(Id::ReturnNumber, i);
uint8_t nr = input->getFieldAs<uint8_t>(Id::NumberOfReturns, i);

if ((((rn == 1) && (nr > 1)) && returnFirst) ||
(((rn > 1) && (rn < nr)) && returnIntermediate) ||
(((rn == nr) && (nr > 1)) && returnLast) ||
Expand Down
7 changes: 5 additions & 2 deletions filters/private/Segmentation.hpp
Expand Up @@ -70,8 +70,11 @@ PDAL_DLL std::vector<PointIdList> extractClusters(PointView& view,

PDAL_DLL void ignoreDimRange(DimRange dr, PointViewPtr input, PointViewPtr keep,
PointViewPtr ignore);
PDAL_DLL void ignoreDimRanges(std::vector<DimRange>& ranges,
PointViewPtr input, PointViewPtr keep, PointViewPtr ignore);
PDAL_DLL void ignoreDimRanges(std::vector<DimRange>& ranges, PointViewPtr input,
PointViewPtr keep, PointViewPtr ignore);

PDAL_DLL void ignoreSynthetic(PointViewPtr input, PointViewPtr keep,
PointViewPtr ignore);

PDAL_DLL void segmentLastReturns(PointViewPtr input, PointViewPtr last,
PointViewPtr other);
Expand Down
Binary file added test/data/las/synthetic_test.las
Binary file not shown.
18 changes: 18 additions & 0 deletions test/unit/io/LasReaderTest.cpp
Expand Up @@ -536,3 +536,21 @@ TEST(LasReaderTest, IgnoreVLRs)
EXPECT_FALSE(!m.empty()) << "No value for node " << i;
}
}

TEST(LasReaderTest, SyntheticPoints)
{
using namespace Dimension;

PointTable table;

Options readOps;
readOps.add("filename", Support::datapath("las/synthetic_test.las"));
LasReader reader;
reader.setOptions(readOps);

reader.prepare(table);
PointViewSet viewSet = reader.execute(table);
PointViewPtr outView = *viewSet.begin();

EXPECT_EQ(ClassLabel::CreatedNeverClassified | (1 << 5), outView->getFieldAs<uint8_t>(Id::Classification, 0));
}
47 changes: 47 additions & 0 deletions test/unit/io/LasWriterTest.cpp
Expand Up @@ -1425,3 +1425,50 @@ TEST(LasWriterTest, issue2320)
}
#endif

TEST(LasWriterTest, synthetic_points)
{
using namespace Dimension;

const std::string FILENAME(Support::temppath("synthetic_test.las"));
PointTable table;

table.layout()->registerDims({Id::X, Id::Y, Id::Z, Id::Classification});

BufferReader bufferReader;

PointViewPtr view(new PointView(table));
view->setField(Id::X, 0, 1.0);
view->setField(Id::Y, 0, 2.0);
view->setField(Id::Z, 0, 3.0);
view->setField(Id::Classification, 0, ClassLabel::Ground | (1 << 5));
bufferReader.addView(view);

Options writerOps;
writerOps.add("filename", FILENAME);

LasWriter writer;
writer.setOptions(writerOps);
writer.setInput(bufferReader);

writer.prepare(table);
writer.execute(table);

Options readerOps;
readerOps.add("filename", FILENAME);

PointTable readTable;

LasReader reader;
reader.setOptions(readerOps);

reader.prepare(readTable);
PointViewSet viewSet = reader.execute(readTable);
EXPECT_EQ(viewSet.size(), 1u);
view = *viewSet.begin();
EXPECT_EQ(view->size(), 1u);
EXPECT_EQ(ClassLabel::Ground | (1 << 5), view->getFieldAs<uint8_t>(Id::Classification, 0));

FileUtils::deleteFile(FILENAME);
}


0 comments on commit 374d630

Please sign in to comment.