From ebe6bcb1b152ae727ee6bdfb25c6e2dd28e3c836 Mon Sep 17 00:00:00 2001 From: Bradley J Chambers Date: Mon, 31 Mar 2014 10:15:45 -0400 Subject: [PATCH] add random kernel * uniform distribution (user-defined bounds) * normal distribution (user-defined mean and standard deviation) --- apps/pdal.cpp | 7 + include/pdal/Utils.hpp | 2 + include/pdal/drivers/faux/Reader.hpp | 15 ++- include/pdal/kernel/Kernel.hpp | 1 + include/pdal/kernel/Random.hpp | 82 ++++++++++++ src/CMakeLists.txt | 2 + src/Utils.cpp | 19 +++ src/drivers/faux/Reader.cpp | 30 ++++- src/kernel/Random.cpp | 184 +++++++++++++++++++++++++++ 9 files changed, 339 insertions(+), 3 deletions(-) create mode 100644 include/pdal/kernel/Random.hpp create mode 100644 src/kernel/Random.cpp diff --git a/apps/pdal.cpp b/apps/pdal.cpp index f0c777bcad..73e5a535e4 100644 --- a/apps/pdal.cpp +++ b/apps/pdal.cpp @@ -54,6 +54,7 @@ void outputVersion() std::cout << " - pcl" << std::endl; #endif std::cout << " - pipeline" << std::endl; + std::cout << " - random" << std::endl; std::cout << " - translate" << std::endl; std::cout << std::endl; std::cout << "See http://pdal.io/apps.html for more detail"; @@ -150,6 +151,12 @@ int main(int argc, char* argv[]) pdal::kernel::Diff app(count, args); return app.run(); } + + if (boost::iequals(action, "random")) + { + pdal::kernel::Random app(count, args); + return app.run(); + } std::cerr << "Action '" << action <<"' not recognized" << std::endl << std::endl; outputVersion(); diff --git a/include/pdal/Utils.hpp b/include/pdal/Utils.hpp index b06033aad2..e20b90649a 100644 --- a/include/pdal/Utils.hpp +++ b/include/pdal/Utils.hpp @@ -58,6 +58,8 @@ class PDAL_DLL Utils public: static void random_seed(unsigned int seed); static double random(double minimum, double maximum); + static double uniform(const double& minimum=0.0f, const double& maximum=1.0f, boost::uint32_t seed=0); + static double normal(const double& mean=0.0f, const double& sigma=1.0f, boost::uint32_t seed=0); // compares two values to within the datatype's epsilon template diff --git a/include/pdal/drivers/faux/Reader.hpp b/include/pdal/drivers/faux/Reader.hpp index 90c665b48f..076f806cdd 100644 --- a/include/pdal/drivers/faux/Reader.hpp +++ b/include/pdal/drivers/faux/Reader.hpp @@ -47,7 +47,9 @@ enum Mode { Constant, Random, - Ramp + Ramp, + Uniform, + Normal }; @@ -68,6 +70,10 @@ enum Mode // bounding box // - "ramp" generates its points as a linear ramp from the minimum of the // bbox to the maximum +// - "uniform" generates points that are uniformly distributed within the +// given bounding box +// - "normal" generates points that are normally distributed with a given +// mean and standard deviation in each of the XYZ dimensions // In all these modes, however, the Time field is always set to the point // number. // @@ -77,7 +83,6 @@ enum Mode // class PDAL_DLL Reader : public pdal::Reader { - public: SET_STAGE_NAME("drivers.faux.reader", "Faux Reader") SET_STAGE_ENABLED(true) @@ -94,6 +99,12 @@ class PDAL_DLL Reader : public pdal::Reader double m_maxY; double m_minZ; double m_maxZ; + double m_mean_x; + double m_mean_y; + double m_mean_z; + double m_stdev_x; + double m_stdev_y; + double m_stdev_z; uint64_t m_time; int m_numReturns; int m_returnNum; diff --git a/include/pdal/kernel/Kernel.hpp b/include/pdal/kernel/Kernel.hpp index c8cef1c853..e366b78f80 100644 --- a/include/pdal/kernel/Kernel.hpp +++ b/include/pdal/kernel/Kernel.hpp @@ -45,6 +45,7 @@ #endif #include "Pipeline.hpp" #include "Delta.hpp" +#include "Random.hpp" #include "Translate.hpp" #include "Diff.hpp" diff --git a/include/pdal/kernel/Random.hpp b/include/pdal/kernel/Random.hpp new file mode 100644 index 0000000000..b7bcf06adf --- /dev/null +++ b/include/pdal/kernel/Random.hpp @@ -0,0 +1,82 @@ +/****************************************************************************** +* Copyright (c) 2014, Brad 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. +****************************************************************************/ + +#ifndef INCLUDED_PDAL_KERNEL_RANDOM_HPP +#define INCLUDED_PDAL_KERNEL_RANDOM_HPP + +#include + +#include +#include + +#include + +#include "Application.hpp" + +#define SEPARATORS ",| " + +#include +typedef boost::tokenizer > tokenizer; + +namespace pdal +{ +namespace kernel +{ + +class PDAL_DLL Random : public Application +{ +public: + Random(int argc, const char* argv[]); + int execute(); + +private: + void addSwitches(); + void validateSwitches(); + + Stage* makeReader(Options readerOptions); + + std::string m_outputFile; + bool m_bCompress; + boost::uint64_t m_numPointsToWrite; + pdal::Bounds m_bounds; + std::string m_distribution; + std::string m_means; + std::string m_stdevs; +}; + +} // kernel +} // pdal + +#endif + diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 5d9634e8cf..1efc00be32 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -690,6 +690,7 @@ set(PDAL_KERNEL_HPP ${PDAL_KERNEL_HEADERS}/Kernel.hpp ${PDAL_KERNEL_HEADERS}/Pipeline.hpp ${PDAL_KERNEL_HEADERS}/Delta.hpp + ${PDAL_KERNEL_HEADERS}/Random.hpp ${PDAL_KERNEL_HEADERS}/Translate.hpp ) @@ -700,6 +701,7 @@ set(PDAL_KERNEL_CPP ${PDAL_KERNEL_SRC}/Info.cpp ${PDAL_KERNEL_SRC}/Pipeline.cpp ${PDAL_KERNEL_SRC}/Delta.cpp + ${PDAL_KERNEL_SRC}/Random.cpp ${PDAL_KERNEL_SRC}/Translate.cpp ) diff --git a/src/Utils.cpp b/src/Utils.cpp index 7004fb34eb..852db497dd 100644 --- a/src/Utils.cpp +++ b/src/Utils.cpp @@ -34,6 +34,9 @@ #include #include +#include +#include +#include #include @@ -84,6 +87,22 @@ double Utils::random(double minimum, double maximum) return t; } +double Utils::uniform(const double& minimum, const double& maximum, boost::uint32_t seed) +{ + boost::random::mt19937 gen(seed); + boost::random::uniform_real_distribution dist(minimum, maximum); + + return dist(gen); +} + +double Utils::normal(const double& mean, const double& sigma, boost::uint32_t seed) +{ + boost::random::mt19937 gen(seed); + boost::random::normal_distribution dist(mean, sigma); + + return dist(gen); +} + void* Utils::registerPlugin(void* stageFactoryPtr, string const& filename, string const& registerMethod, string const& versionMethod) { diff --git a/src/drivers/faux/Reader.cpp b/src/drivers/faux/Reader.cpp index 8d4a86d3a6..104699ee8c 100644 --- a/src/drivers/faux/Reader.cpp +++ b/src/drivers/faux/Reader.cpp @@ -34,6 +34,8 @@ #include +#include + #include #include @@ -50,6 +52,8 @@ static Mode string2mode(const std::string& str) if (boost::iequals(str, "constant")) return Constant; if (boost::iequals(str, "random")) return Random; if (boost::iequals(str, "ramp")) return Ramp; + if (boost::iequals(str, "uniform")) return Uniform; + if (boost::iequals(str, "normal")) return Normal; throw pdal_error("invalid Mode option: " + str); } @@ -63,7 +67,7 @@ Reader::Reader(const Options& options) void Reader::processOptions(const Options& options) { - Bounds bounds = options.getValueOrThrow>("bounds"); + Bounds bounds = options.getValueOrDefault>("bounds",Bounds(0,0,0,1,1,1)); const std::vector>& ranges = bounds.dimensions(); m_minX = ranges[0].getMinimum(); m_maxX = ranges[0].getMaximum(); @@ -75,6 +79,13 @@ void Reader::processOptions(const Options& options) // For backward compatibility. if (m_count == 0) m_count = options.getValueOrThrow("num_points"); + + m_mean_x = options.getValueOrDefault("mean_x",0.0); + m_mean_y = options.getValueOrDefault("mean_y",0.0); + m_mean_z = options.getValueOrDefault("mean_z",0.0); + m_stdev_x = options.getValueOrDefault("stdev_x",1.0); + m_stdev_y = options.getValueOrDefault("stdev_y",1.0); + m_stdev_z = options.getValueOrDefault("stdev_z",1.0); m_mode = string2mode(options.getValueOrThrow("mode")); m_numReturns = options.getValueOrDefault("number_of_returns", 0); if (m_numReturns > 10) @@ -115,6 +126,10 @@ point_count_t Reader::read(PointBuffer& buf, point_count_t count) log()->get(LogLevel::Debug5) << "Reading a point buffer of " << count << " points." << std::endl; + boost::uint64_t time = count; + + boost::uint32_t seed = static_cast(std::time(NULL)); + for (PointId idx = 0; idx < count; ++idx) { double x; @@ -137,6 +152,19 @@ point_count_t Reader::read(PointBuffer& buf, point_count_t count) y = m_minY + delY * idx; z = m_minZ + delZ * idx; break; + case Uniform: + x = Utils::uniform(m_minX, m_maxX, seed++); + y = Utils::uniform(m_minY, m_maxY, seed++); + z = Utils::uniform(m_minZ, m_maxZ, seed++); + break; + case Normal: + x = Utils::normal(m_mean_x, m_stdev_x, seed++); + y = Utils::normal(m_mean_y, m_stdev_y, seed++); + z = Utils::normal(m_mean_z, m_stdev_z, seed++); + break; + default: + throw pdal_error("invalid mode in FauxReader"); + break; } buf.setField(Dimension::Id::X, idx, x); diff --git a/src/kernel/Random.cpp b/src/kernel/Random.cpp new file mode 100644 index 0000000000..50829e77da --- /dev/null +++ b/src/kernel/Random.cpp @@ -0,0 +1,184 @@ +/****************************************************************************** +* Copyright (c) 2014, Brad 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 + +namespace pdal +{ +namespace kernel +{ + +Random::Random(int argc, const char* argv[]) + : Application(argc, argv, "random") + , m_outputFile("") + , m_bCompress(false) + , m_numPointsToWrite(0) + , m_distribution("uniform") + , m_means("") + , m_stdevs("") +{ +} + + +void Random::validateSwitches() +{ + if (m_outputFile == "") + throw app_usage_error("--output/-o required"); +} + + +void Random::addSwitches() +{ + po::options_description* file_options = new po::options_description("file options"); + + file_options->add_options() + ("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)") + ("count", po::value(&m_numPointsToWrite)->default_value(0), "How many points should we write?") + ("bounds", po::value >(&m_bounds), "Extent (in XYZ to clip output to)") + ("mean", po::value< std::string >(&m_means), "A comma-separated or quoted, space-separated list of means (normal mode): \n--mean 0.0,0.0,0.0\n--mean \"0.0 0.0 0.0\"") + ("stdev", po::value< std::string >(&m_stdevs), "A comma-separated or quoted, space-separated list of standard deviations (normal mode): \n--stdev 0.0,0.0,0.0\n--stdev \"0.0 0.0 0.0\"") + ("distribution", po::value(&m_distribution)->default_value("uniform"), "Distribution (uniform / normal)") + ; + + addSwitchSet(file_options); + + addPositionalSwitch("input", 1); + addPositionalSwitch("output", 1); +} + +Stage* Random::makeReader(Options readerOptions) +{ + if (isDebug()) + { + readerOptions.add("debug", true); + boost::uint32_t verbosity(getVerboseLevel()); + if (!verbosity) + verbosity = 1; + + readerOptions.add("verbose", verbosity); + readerOptions.add("log", "STDERR"); + } + + //std::unique_ptr reader_stage(AppSupport::makeReader(readerOptions)); + StageFactory factory; + Stage* reader_stage = factory.createReader("drivers.faux.reader", readerOptions); + + return reader_stage; +} + +int Random::execute() +{ + Options readerOptions; + { + boost::char_separator sep(SEPARATORS); + std::vector means; + tokenizer mean_tokens(m_means, sep); + for (tokenizer::iterator t = mean_tokens.begin(); t != mean_tokens.end(); ++t) + { + means.push_back(boost::lexical_cast(*t)); + } + + if (means.size()) + { + readerOptions.add("mean_x", means[0]); + readerOptions.add("mean_y", means[1]); + readerOptions.add("mean_z", means[2]); + } + + std::vector stdevs; + tokenizer stdev_tokens(m_stdevs, sep); + for (tokenizer::iterator t = stdev_tokens.begin(); t != stdev_tokens.end(); ++t) + { + stdevs.push_back(boost::lexical_cast(*t)); + } + + if (stdevs.size()) + { + readerOptions.add("stdev_x", stdevs[0]); + readerOptions.add("stdev_y", stdevs[1]); + readerOptions.add("stdev_z", stdevs[2]); + } + + if (m_bounds.size()) + readerOptions.add >("bounds", m_bounds); + + if (boost::iequals(m_distribution, "uniform")) + readerOptions.add("mode", "uniform"); + else if (boost::iequals(m_distribution, "normal")) + readerOptions.add("mode", "normal"); + else if (boost::iequals(m_distribution, "random")) + readerOptions.add("mode", "random"); + else + throw pdal_error("invalid distribution: " + m_distribution); + readerOptions.add("num_points", m_numPointsToWrite); + readerOptions.add("debug", isDebug()); + readerOptions.add("verbose", getVerboseLevel()); + } + + Options writerOptions; + { + writerOptions.add("filename", m_outputFile); + writerOptions.add("debug", isDebug()); + writerOptions.add("verbose", getVerboseLevel()); + + if (m_bCompress) + { + writerOptions.add("compression", true); + } + } + + Stage* final_stage = makeReader(readerOptions); + + Writer* writer = AppSupport::makeWriter(writerOptions, final_stage); + PointContext ctx; + + UserCallback* callback; + if (!getProgressShellCommand().size()) + callback = static_cast(new PercentageCallback); + else + callback = static_cast(new ShellScriptCallback(getProgressShellCommand())); + writer->setUserCallback(callback); + writer->prepare(ctx); + writer->execute(ctx); + + delete writer; + delete final_stage; + + return 0; +} + +} // kernel +} // pdal +