From 374d630ece47a5a8c2b970c7a2551cba32c1ae86 Mon Sep 17 00:00:00 2001 From: Bradley J Chambers Date: Thu, 16 Jan 2020 08:27:12 -0500 Subject: [PATCH] Add some initial support for handling synthetic points * 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 --- filters/SMRFilter.cpp | 47 ++++++++++++++++--------------- filters/private/Segmentation.cpp | 22 ++++++++++++--- filters/private/Segmentation.hpp | 7 +++-- test/data/las/synthetic_test.las | Bin 0 -> 261 bytes test/unit/io/LasReaderTest.cpp | 18 ++++++++++++ test/unit/io/LasWriterTest.cpp | 47 +++++++++++++++++++++++++++++++ 6 files changed, 113 insertions(+), 28 deletions(-) create mode 100644 test/data/las/synthetic_test.las diff --git a/filters/SMRFilter.cpp b/filters/SMRFilter.cpp index d1fd8d97d4..14409ea115 100644 --- a/filters/SMRFilter.cpp +++ b/filters/SMRFilter.cpp @@ -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 -T ceil(double d) +template T ceil(double d) { return static_cast(std::ceil(d)); } -} +} // namespace CREATE_STATIC_STAGE(SMRFilter, s_info) @@ -94,13 +90,12 @@ struct SMRArgs std::string m_dir; std::vector 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 { @@ -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) @@ -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(Id::NumberOfReturns, i); - uint8_t rn = - keptView->getFieldAs(Id::ReturnNumber, i); + uint8_t nr = realView->getFieldAs(Id::NumberOfReturns, i); + uint8_t rn = realView->getFieldAs(Id::ReturnNumber, i); if ((nr == 0) && !nrOneZero) nrOneZero = true; if ((rn == 0) && !rnOneZero) @@ -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); } @@ -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); diff --git a/filters/private/Segmentation.cpp b/filters/private/Segmentation.cpp index a9278f5a93..7ac0f555d3 100644 --- a/filters/private/Segmentation.cpp +++ b/filters/private/Segmentation.cpp @@ -127,7 +127,7 @@ void ignoreDimRange(DimRange dr, PointViewPtr input, PointViewPtr keep, } void ignoreDimRanges(std::vector& ranges, PointViewPtr input, - PointViewPtr keep, PointViewPtr ignore) + PointViewPtr keep, PointViewPtr ignore) { std::sort(ranges.begin(), ranges.end()); PointRef point(*input, 0); @@ -141,6 +141,20 @@ void ignoreDimRanges(std::vector& 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(Id::Classification, i); + if (c & (1 << 5)) + ignore->appendPoint(*input, i); + else + keep->appendPoint(*input, i); + } +} + void segmentLastReturns(PointViewPtr input, PointViewPtr last, PointViewPtr other) { @@ -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; @@ -190,7 +204,7 @@ void segmentReturns(PointViewPtr input, PointViewPtr first, { uint8_t rn = input->getFieldAs(Id::ReturnNumber, i); uint8_t nr = input->getFieldAs(Id::NumberOfReturns, i); - + if ((((rn == 1) && (nr > 1)) && returnFirst) || (((rn > 1) && (rn < nr)) && returnIntermediate) || (((rn == nr) && (nr > 1)) && returnLast) || diff --git a/filters/private/Segmentation.hpp b/filters/private/Segmentation.hpp index d069981998..9ecf2614b1 100644 --- a/filters/private/Segmentation.hpp +++ b/filters/private/Segmentation.hpp @@ -70,8 +70,11 @@ PDAL_DLL std::vector extractClusters(PointView& view, PDAL_DLL void ignoreDimRange(DimRange dr, PointViewPtr input, PointViewPtr keep, PointViewPtr ignore); -PDAL_DLL void ignoreDimRanges(std::vector& ranges, - PointViewPtr input, PointViewPtr keep, PointViewPtr ignore); +PDAL_DLL void ignoreDimRanges(std::vector& 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); diff --git a/test/data/las/synthetic_test.las b/test/data/las/synthetic_test.las new file mode 100644 index 0000000000000000000000000000000000000000..74f9e7421b0303df0054d0f8ad24084c4ef46e32 GIT binary patch literal 261 zcmeZq40dC{0vMSBTpWF{NWg_a@(MCZ=YZa5)fzpWzAnV}{32F=izO zMj#V}(LuGyI`@ZFE%xX%x;j+;2YUz~Ml(1-<)JhOoK68soB(1Su;rWzKqeXh06_g2 A7XSbN literal 0 HcmV?d00001 diff --git a/test/unit/io/LasReaderTest.cpp b/test/unit/io/LasReaderTest.cpp index 8fc47684da..3cc1847e8a 100644 --- a/test/unit/io/LasReaderTest.cpp +++ b/test/unit/io/LasReaderTest.cpp @@ -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(Id::Classification, 0)); +} diff --git a/test/unit/io/LasWriterTest.cpp b/test/unit/io/LasWriterTest.cpp index 070675cafb..57f11d008b 100644 --- a/test/unit/io/LasWriterTest.cpp +++ b/test/unit/io/LasWriterTest.cpp @@ -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(Id::Classification, 0)); + + FileUtils::deleteFile(FILENAME); +} + +