/* * Software License Agreement (BSD License) * * Point Cloud Library (PCL) - www.pointclouds.org * Copyright (c) 2010-2012, Willow Garage, Inc. * * 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 the copyright holder(s) 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 #include #include #include #include #ifdef HAVE_OPENNI #include #include #include #endif #include #include namespace pcl { /** \brief Base class for PCD file grabber. * \ingroup io */ class PCL_EXPORTS PCDGrabberBase : public Grabber { public: /** \brief Constructor taking just one PCD file or one TAR file containing multiple PCD files. * \param[in] pcd_file path to the PCD file * \param[in] frames_per_second frames per second. If 0, start() functions like a trigger, publishing the next PCD in the list. * \param[in] repeat whether to play PCD file in an endless loop or not. */ PCDGrabberBase (const std::string& pcd_file, float frames_per_second, bool repeat); /** \brief Constructor taking a list of paths to PCD files, that are played in the order they appear in the list. * \param[in] pcd_files vector of paths to PCD files. * \param[in] frames_per_second frames per second. If 0, start() functions like a trigger, publishing the next PCD in the list. * \param[in] repeat whether to play PCD file in an endless loop or not. */ PCDGrabberBase (const std::vector& pcd_files, float frames_per_second, bool repeat); /** \brief Virtual destructor. */ ~PCDGrabberBase () noexcept; /** \brief Starts playing the list of PCD files if frames_per_second is > 0. Otherwise it works as a trigger: publishes only the next PCD file in the list. */ void start () override; /** \brief Stops playing the list of PCD files if frames_per_second is > 0. Otherwise the method has no effect. */ void stop () override; /** \brief Triggers a callback with new data */ virtual void trigger (); /** \brief Indicates whether the grabber is streaming or not. * \return true if grabber is started and hasn't run out of PCD files. */ bool isRunning () const override; /** \return The name of the grabber */ std::string getName () const override; /** \brief Rewinds to the first PCD file in the list.*/ virtual void rewind (); /** \brief Returns the frames_per_second. 0 if grabber is trigger-based */ float getFramesPerSecond () const override; /** \brief Returns whether the repeat flag is on */ bool isRepeatOn () const; /** \brief Get cloud (in ROS form) at a particular location */ bool getCloudAt (std::size_t idx, pcl::PCLPointCloud2 &blob, Eigen::Vector4f &origin, Eigen::Quaternionf &orientation) const; /** \brief Returns the size */ std::size_t numFrames () const; private: virtual void publish (const pcl::PCLPointCloud2& blob, const Eigen::Vector4f& origin, const Eigen::Quaternionf& orientation, const std::string& file_name) const = 0; // to separate and hide the implementation from interface: PIMPL struct PCDGrabberImpl; PCDGrabberImpl* impl_; }; //////////////////////////////////////////////////////////////////////////////////////////////////////////////// template class PointCloud; template class PCDGrabber : public PCDGrabberBase, public FileGrabber { public: using Ptr = shared_ptr; using ConstPtr = shared_ptr; PCDGrabber (const std::string& pcd_path, float frames_per_second = 0, bool repeat = false); PCDGrabber (const std::vector& pcd_files, float frames_per_second = 0, bool repeat = false); /** \brief Virtual destructor. */ ~PCDGrabber () noexcept { stop (); } // Inherited from FileGrabber const typename pcl::PointCloud::ConstPtr operator[] (std::size_t idx) const override; // Inherited from FileGrabber std::size_t size () const override; protected: void publish (const pcl::PCLPointCloud2& blob, const Eigen::Vector4f& origin, const Eigen::Quaternionf& orientation, const std::string& file_name) const override; boost::signals2::signal::ConstPtr&)>* signal_; boost::signals2::signal* file_name_signal_; #ifdef HAVE_OPENNI boost::signals2::signal* depth_image_signal_; boost::signals2::signal* image_signal_; boost::signals2::signal* image_depth_image_signal_; #endif }; //////////////////////////////////////////////////////////////////////////////////////////////////////////////// template PCDGrabber::PCDGrabber (const std::string& pcd_path, float frames_per_second, bool repeat) : PCDGrabberBase (pcd_path, frames_per_second, repeat) { signal_ = createSignal::ConstPtr&)>(); file_name_signal_ = createSignal(); #ifdef HAVE_OPENNI depth_image_signal_ = createSignal (); image_signal_ = createSignal (); image_depth_image_signal_ = createSignal (); #endif } //////////////////////////////////////////////////////////////////////////////////////////////////////////////// template PCDGrabber::PCDGrabber (const std::vector& pcd_files, float frames_per_second, bool repeat) : PCDGrabberBase (pcd_files, frames_per_second, repeat), signal_ () { signal_ = createSignal::ConstPtr&)>(); file_name_signal_ = createSignal(); #ifdef HAVE_OPENNI depth_image_signal_ = createSignal (); image_signal_ = createSignal (); image_depth_image_signal_ = createSignal (); #endif } //////////////////////////////////////////////////////////////////////////////////////////////////////////////// template const typename pcl::PointCloud::ConstPtr PCDGrabber::operator[] (std::size_t idx) const { pcl::PCLPointCloud2 blob; Eigen::Vector4f origin; Eigen::Quaternionf orientation; getCloudAt (idx, blob, origin, orientation); typename pcl::PointCloud::Ptr cloud (new pcl::PointCloud ()); pcl::fromPCLPointCloud2 (blob, *cloud); cloud->sensor_origin_ = origin; cloud->sensor_orientation_ = orientation; return (cloud); } /////////////////////////////////////////////////////////////////////////////////////////////////////////////// template std::size_t PCDGrabber::size () const { return (numFrames ()); } //////////////////////////////////////////////////////////////////////////////////////////////////////////////// template void PCDGrabber::publish (const pcl::PCLPointCloud2& blob, const Eigen::Vector4f& origin, const Eigen::Quaternionf& orientation, const std::string& file_name) const { typename pcl::PointCloud::Ptr cloud (new pcl::PointCloud ()); pcl::fromPCLPointCloud2 (blob, *cloud); cloud->sensor_origin_ = origin; cloud->sensor_orientation_ = orientation; signal_->operator () (cloud); if (file_name_signal_->num_slots() > 0) file_name_signal_->operator()(file_name); #ifdef HAVE_OPENNI // If dataset is not organized, return if (!cloud->isOrganized ()) return; shared_ptr depth_meta_data (new xn::DepthMetaData); depth_meta_data->AllocateData (cloud->width, cloud->height); XnDepthPixel* depth_map = depth_meta_data->WritableData (); std::uint32_t k = 0; for (std::uint32_t i = 0; i < cloud->height; ++i) for (std::uint32_t j = 0; j < cloud->width; ++j) { depth_map[k] = static_cast ((*cloud)[k].z * 1000); ++k; } openni_wrapper::DepthImage::Ptr depth_image (new openni_wrapper::DepthImage (depth_meta_data, 0.075f, 525, 0, 0)); if (depth_image_signal_->num_slots() > 0) depth_image_signal_->operator()(depth_image); // ---[ RGB special case std::vector fields; int rgba_index = pcl::getFieldIndex ("rgb", fields); if (rgba_index == -1) rgba_index = pcl::getFieldIndex ("rgba", fields); if (rgba_index >= 0) { rgba_index = fields[rgba_index].offset; shared_ptr image_meta_data (new xn::ImageMetaData); image_meta_data->AllocateData (cloud->width, cloud->height, XN_PIXEL_FORMAT_RGB24); XnRGB24Pixel* image_map = image_meta_data->WritableRGB24Data (); k = 0; for (std::uint32_t i = 0; i < cloud->height; ++i) { for (std::uint32_t j = 0; j < cloud->width; ++j) { // Fill r/g/b data, assuming that the order is BGRA pcl::RGB rgb; memcpy (&rgb, reinterpret_cast (&(*cloud)[k]) + rgba_index, sizeof (RGB)); image_map[k].nRed = static_cast (rgb.r); image_map[k].nGreen = static_cast (rgb.g); image_map[k].nBlue = static_cast (rgb.b); ++k; } } openni_wrapper::Image::Ptr image (new openni_wrapper::ImageRGB24 (image_meta_data)); if (image_signal_->num_slots() > 0) image_signal_->operator()(image); if (image_depth_image_signal_->num_slots() > 0) image_depth_image_signal_->operator()(image, depth_image, 1.0f / 525.0f); } #endif } }