From d7722743d3c51f348f8de86a0b51e6532f20ad4c Mon Sep 17 00:00:00 2001 From: Bradley J Chambers Date: Fri, 16 Jan 2015 14:49:19 -0500 Subject: [PATCH] Add `filters.ground` and `kernels.ground` `filters.ground` is a PDAL filter that calls the PCL PMF code directly (vice going through PCLBlock or the PCL pipeline) `kernels.ground` is a PDAL kernel that calls `filters.ground` The primary motivation for this is so that we can retain all input dimentions, extracting only ground indices if desired, and labeling as ground if a Classification dimension is present. --- plugins/pcl/CMakeLists.txt | 16 ++- plugins/pcl/filters/GroundFilter.cpp | 167 +++++++++++++++++++++++++++ plugins/pcl/filters/GroundFilter.hpp | 81 +++++++++++++ plugins/pcl/kernel/GroundKernel.cpp | 88 +++++--------- plugins/pcl/kernel/GroundKernel.hpp | 17 ++- 5 files changed, 304 insertions(+), 65 deletions(-) create mode 100644 plugins/pcl/filters/GroundFilter.cpp create mode 100644 plugins/pcl/filters/GroundFilter.hpp diff --git a/plugins/pcl/CMakeLists.txt b/plugins/pcl/CMakeLists.txt index 8f5d410217..4e0d7543ca 100644 --- a/plugins/pcl/CMakeLists.txt +++ b/plugins/pcl/CMakeLists.txt @@ -29,6 +29,7 @@ set_package_properties(PCL PROPERTIES DESCRIPTION "Point Cloud Library" include_directories(${PCL_INCLUDE_DIRS}) include_directories(${CMAKE_CURRENT_LIST_DIR}) +include_directories(${CMAKE_CURRENT_SOURCE_DIR}/filters) link_directories(${PCL_LIBRARY_DIRS}) add_definitions(${PCL_DEFINITIONS}) if (NOT WIN32) @@ -122,6 +123,13 @@ PDAL_ADD_PLUGIN(pclblock_libname filter pclblock FILES "${srcs}" "${incs}" LINK_WITH ${PCL_LIBRARIES}) +# +# Ground Filter +# +PDAL_ADD_PLUGIN(ground_filter_libname filter ground + FILES filters/GroundFilter.hpp filters/GroundFilter.cpp + LINK_WITH ${PCL_LIBRARIES}) + # # PCL Kernel # @@ -138,9 +146,9 @@ PDAL_ADD_PLUGIN(pcl_libname kernel pcl set(srcs kernel/GroundKernel.cpp) set(incs kernel/GroundKernel.hpp) -PDAL_ADD_PLUGIN(ground_libname kernel ground +PDAL_ADD_PLUGIN(ground_kernel_libname kernel ground FILES "${srcs}" "${incs}" - LINK_WITH ${PCL_LIBRARIES} ${pclblock_libname}) + LINK_WITH ${ground_filter_libname}) # # Smooth Kernel @@ -156,7 +164,7 @@ if (WITH_TESTS) PDAL_ADD_TEST(pcltest FILES test/PCLBlockFilterTest.cpp LINK_WITH ${pclvisualizer_libname} ${pcd_reader_libname} - ${pcd_writer_libname} ${pclblock_libname} - ${pcl_libname} ${ground_libname} ${smooth_libname} + ${pcd_writer_libname} ${pclblock_libname} ${ground_filter_libname} + ${pcl_libname} ${ground_kernel_libname} ${smooth_libname} ) endif() diff --git a/plugins/pcl/filters/GroundFilter.cpp b/plugins/pcl/filters/GroundFilter.cpp new file mode 100644 index 0000000000..90be5e7f6f --- /dev/null +++ b/plugins/pcl/filters/GroundFilter.cpp @@ -0,0 +1,167 @@ +/****************************************************************************** +* Copyright (c) 2015, Bradley J Chambers (brad.chambers@gmail.com) +* +* All rights reserved. +* +* Redistribution and use in source and binary forms, with or without +* modification, are permitted provided that the following +* conditions are met: +* +* * Redistributions of source code must retain the above copyright +* notice, this list of conditions and the following disclaimer. +* * Redistributions in binary form must reproduce the above copyright +* notice, this list of conditions and the following disclaimer in +* the documentation and/or other materials provided +* with the distribution. +* * Neither the name of Hobu, Inc. or Flaxen Geo Consulting nor the +* names of its contributors may be used to endorse or promote +* products derived from this software without specific prior +* written permission. +* +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS +* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED +* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT +* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY +* OF SUCH DAMAGE. +****************************************************************************/ + +#include "GroundFilter.hpp" + +#include "PCLConversions.hpp" + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +CREATE_FILTER_PLUGIN(ground, pdal::GroundFilter) + +namespace pdal +{ + +void GroundFilter::processOptions(const Options& options) +{ + m_maxWindowSize = options.getValueOrDefault("maxWindowSize", 33); + m_slope = options.getValueOrDefault("slope", 1); + m_maxDistance = options.getValueOrDefault("maxDistance", 2.5); + m_initialDistance = options.getValueOrDefault("initialDistance", 0.15); + m_cellSize = options.getValueOrDefault("cellSize", 1); + m_classify = options.getValueOrDefault("classify", true); + m_extract = options.getValueOrDefault("extract", false); +} + +void GroundFilter::addDimensions(PointContextRef ctx) +{ ctx.registerDim(Dimension::Id::Classification); } + +PointBufferSet GroundFilter::run(PointBufferPtr input) +{ + bool logOutput = log()->getLevel() > LogLevel::Debug1; + if (logOutput) + log()->floatPrecision(8); + log()->get(LogLevel::Debug2) << "Process GroundFilter...\n"; + + // convert PointBuffer to PointXYZ + typedef pcl::PointCloud Cloud; + Cloud::Ptr cloud(new Cloud); + BOX3D const& bounds = input->calculateBounds(); + pclsupport::PDALtoPCD(*input, *cloud, bounds); + + // PCL should provide console output at similar verbosity level as PDAL + int level = log()->getLevel(); + switch (level) + { + case 0: + pcl::console::setVerbosityLevel(pcl::console::L_ALWAYS); + break; + case 1: + pcl::console::setVerbosityLevel(pcl::console::L_ERROR); + break; + case 2: + pcl::console::setVerbosityLevel(pcl::console::L_WARN); + break; + case 3: + pcl::console::setVerbosityLevel(pcl::console::L_INFO); + break; + case 4: + pcl::console::setVerbosityLevel(pcl::console::L_DEBUG); + break; + default: + pcl::console::setVerbosityLevel(pcl::console::L_VERBOSE); + break; + } + + // setup the PMF filter + pcl::ProgressiveMorphologicalFilter pmf; + pmf.setInputCloud(cloud); + pmf.setMaxWindowSize(m_maxWindowSize); + pmf.setSlope(m_slope); + pmf.setMaxDistance(m_maxDistance); + pmf.setInitialDistance(m_initialDistance); + pmf.setCellSize(m_cellSize); + + // run the PMF filter, grabbing indices of ground returns + pcl::PointIndicesPtr idx(new pcl::PointIndices); + pmf.extract(idx->indices); + + if (!idx->indices.empty() && (m_classify || m_extract)) + { + PointBufferSet pbSet; + + if (m_classify) + { + log()->get(LogLevel::Debug2) << "Labeled " << idx->indices.size() << " ground returns!\n"; + + // set the classification label of ground returns as 2 + // (corresponding to ASPRS LAS specification) + for (const auto& i : idx->indices) + { input->setField(Dimension::Id::Classification, i, 2); } + + pbSet.insert(input); + } + + if (m_extract) + { + log()->get(LogLevel::Debug2) << "Extracted " << idx->indices.size() << " ground returns!\n"; + + // create new PointBuffer containing only ground returns + PointBufferPtr output = input->makeNew(); + for (const auto& i : idx->indices) + { output->appendPoint(*input, i); } + + pbSet.erase(input); + pbSet.insert(output); + } + + return pbSet; + } + else + { + if (idx->indices.empty()) + log()->get(LogLevel::Debug2) << "Filtered cloud has no ground returns!\n"; + + if (!(m_classify || m_extract)) + log()->get(LogLevel::Debug2) << "Must choose --classify or --extract\n"; + + // return the input buffer unchanged + PointBufferSet pbSet; + pbSet.insert(input); + return pbSet; + } +} + +} // namespace pdal + diff --git a/plugins/pcl/filters/GroundFilter.hpp b/plugins/pcl/filters/GroundFilter.hpp new file mode 100644 index 0000000000..1d81bbe3e2 --- /dev/null +++ b/plugins/pcl/filters/GroundFilter.hpp @@ -0,0 +1,81 @@ +/****************************************************************************** +* Copyright (c) 2015, Bradley J Chambers (brad.chambers@gmail.com) +* +* All rights reserved. +* +* Redistribution and use in source and binary forms, with or without +* modification, are permitted provided that the following +* conditions are met: +* +* * Redistributions of source code must retain the above copyright +* notice, this list of conditions and the following disclaimer. +* * Redistributions in binary form must reproduce the above copyright +* notice, this list of conditions and the following disclaimer in +* the documentation and/or other materials provided +* with the distribution. +* * Neither the name of Hobu, Inc. or Flaxen Geo Consulting nor the +* names of its contributors may be used to endorse or promote +* products derived from this software without specific prior +* written permission. +* +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS +* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED +* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT +* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY +* OF SUCH DAMAGE. +****************************************************************************/ + +#pragma once + +#include +#include + +#include + +namespace pdal +{ + +class Options; +class PointBuffer; +class PointContext; + +typedef std::shared_ptr PointBufferPtr; +typedef PointContext PointContextRef; + +class PDAL_DLL GroundFilter : public Filter +{ +public: + SET_STAGE_NAME("filters.ground", "Progressive Morphological Filter") + SET_STAGE_LINK("http://www.pdal.io/stages/filters.ground.html") + SET_PLUGIN_VERSION("1.0.0b1") + + GroundFilter() : Filter() + {} + +private: + double m_maxWindowSize; + double m_slope; + double m_maxDistance; + double m_initialDistance; + double m_cellSize; + bool m_classify; + bool m_extract; + + virtual void addDimensions(PointContextRef ctx); + virtual void processOptions(const Options& options); + virtual void ready(PointContext ctx) {}; + virtual PointBufferSet run(PointBufferPtr buf); + + GroundFilter& operator=(const GroundFilter&); // not implemented + GroundFilter(const GroundFilter&); // not implemented +}; + +} // namespace pdal + diff --git a/plugins/pcl/kernel/GroundKernel.cpp b/plugins/pcl/kernel/GroundKernel.cpp index 7c11142f35..736085eadb 100644 --- a/plugins/pcl/kernel/GroundKernel.cpp +++ b/plugins/pcl/kernel/GroundKernel.cpp @@ -1,6 +1,6 @@ /****************************************************************************** * Copyright (c) 2011, Michael P. Gerlek (mpg@flaxen.com) -* Copyright (c) 2014, Bradley J Chambers (brad.chambers@gmail.com) +* Copyright (c) 2014-2015, Bradley J Chambers (brad.chambers@gmail.com) * * All rights reserved. * @@ -34,10 +34,20 @@ ****************************************************************************/ #include "GroundKernel.hpp" -#include "../filters/PCLBlock.hpp" + +#include +#include +#include +#include +#include +#include + #include "KernelFactory.hpp" +#include "KernelSupport.hpp" -#include +#include +#include +#include CREATE_KERNEL_PLUGIN(ground, pdal::GroundKernel) @@ -53,12 +63,9 @@ GroundKernel::GroundKernel() , m_maxDistance(2.5) , m_initialDistance(0.15) , m_cellSize(1) - , m_base(2) - , m_exponential(true) -{ - return; -} - + , m_classify(true) + , m_extract(false) +{} void GroundKernel::validateSwitches() { @@ -71,11 +78,8 @@ void GroundKernel::validateSwitches() { throw app_usage_error("--output/-o required"); } - - return; } - void GroundKernel::addSwitches() { po::options_description* file_options = new po::options_description("file options"); @@ -83,14 +87,13 @@ void GroundKernel::addSwitches() file_options->add_options() ("input,i", po::value(&m_inputFile)->default_value(""), "input file name") ("output,o", po::value(&m_outputFile)->default_value(""), "output file name") -// ("compress,z", po::value(&m_bCompress)->zero_tokens()->implicit_value(true), "Compress output data (if supported by output format)") ("maxWindowSize", po::value(&m_maxWindowSize)->default_value(33), "max window size") ("slope", po::value(&m_slope)->default_value(1), "slope") ("maxDistance", po::value(&m_maxDistance)->default_value(2.5), "max distance") ("initialDistance", po::value(&m_initialDistance)->default_value(0.15, "0.15"), "initial distance") ("cellSize", po::value(&m_cellSize)->default_value(1), "cell size") - ("base", po::value(&m_base)->default_value(2), "base") - ("exponential", po::value(&m_exponential)->default_value(true), "exponential?") + ("classify", po::bool_switch(&m_classify), "apply classification labels?") + ("extract", po::bool_switch(&m_extract), "extract ground returns?") ; addSwitchSet(file_options); @@ -119,7 +122,6 @@ std::unique_ptr GroundKernel::makeReader(Options readerOptions) return reader_stage; } - int GroundKernel::execute() { PointContext ctx; @@ -131,48 +133,21 @@ int GroundKernel::execute() std::unique_ptr readerStage = makeReader(readerOptions); - // go ahead and prepare/execute on reader stage only to grab input - // PointBufferSet, this makes the input PointBuffer available to both the - // processing pipeline and the visualizer - readerStage->prepare(ctx); - PointBufferSet pbSetIn = readerStage->execute(ctx); - - // the input PointBufferSet will be used to populate a BufferReader that is - // consumed by the processing pipeline - PointBufferPtr input_buffer = *pbSetIn.begin(); - BufferReader bufferReader; - bufferReader.setOptions(readerOptions); - bufferReader.addBuffer(input_buffer); - Options groundOptions; - std::ostringstream ss; - ss << "{"; - ss << " \"pipeline\": {"; - ss << " \"filters\": [{"; - ss << " \"name\": \"ProgressiveMorphologicalFilter\","; - ss << " \"setMaxWindowSize\": " << m_maxWindowSize << ","; - ss << " \"setSlope\": " << m_slope << ","; - ss << " \"setMaxDistance\": " << m_maxDistance << ","; - ss << " \"setInitialDistance\": " << m_initialDistance << ","; - ss << " \"setCellSize\": " << m_cellSize << ","; - ss << " \"setBase\": " << m_base << ","; - ss << " \"setExponential\": " << m_exponential; - ss << " }]"; - ss << " }"; - ss << "}"; - std::string json = ss.str(); - groundOptions.add("json", json); - groundOptions.add("debug", isDebug()); - groundOptions.add("verbose", getVerboseLevel()); - - std::unique_ptr groundStage(new filters::PCLBlock()); - groundStage->setInput(&bufferReader); + groundOptions.add("maxWindowSize", m_maxWindowSize); + groundOptions.add("slope", m_slope); + groundOptions.add("maxDistance", m_maxDistance); + groundOptions.add("initialDistance", m_initialDistance); + groundOptions.add("cellSize", m_cellSize); + groundOptions.add("classify", m_classify); + groundOptions.add("extract", m_extract); + + StageFactory f; + std::unique_ptr groundStage(f.createFilter("filters.ground")); groundStage->setOptions(groundOptions); + groundStage->setInput(readerStage.get()); - // the PCLBlock groundStage consumes the BufferReader rather than the - // readerStage - groundStage->setInput(&bufferReader); - + // setup the Writer and write the results Options writerOptions; writerOptions.add("filename", m_outputFile); setCommonOptions(writerOptions); @@ -214,4 +189,5 @@ int GroundKernel::execute() return 0; } -} // pdal +} // namespace pdal + diff --git a/plugins/pcl/kernel/GroundKernel.hpp b/plugins/pcl/kernel/GroundKernel.hpp index f17a135372..140a2113b9 100644 --- a/plugins/pcl/kernel/GroundKernel.hpp +++ b/plugins/pcl/kernel/GroundKernel.hpp @@ -1,6 +1,6 @@ /****************************************************************************** * Copyright (c) 2013, Howard Butler (hobu.inc@gmail.com) -* Copyright (c) 2014, Brad Chambers (brad.chambers@gmail.com) +* Copyright (c) 2014-2015, Brad Chambers (brad.chambers@gmail.com) * * All rights reserved. * @@ -36,12 +36,19 @@ #pragma once #include +#include #include "Kernel.hpp" +#include +#include + namespace pdal { +class Options; +class Stage; + class PDAL_DLL GroundKernel : public Kernel { public: @@ -64,9 +71,9 @@ class PDAL_DLL GroundKernel : public Kernel double m_maxDistance; double m_initialDistance; double m_cellSize; - double m_base; - bool m_exponential; -// bool m_bCompress; // Currently unused + bool m_classify; + bool m_extract; }; -} // pdal +} // namespace pdal +