gc_pointcloud.cc
Go to the documentation of this file.
00001 /*
00002  * This file is part of the rc_genicam_api package.
00003  *
00004  * Copyright (c) 2017 Roboception GmbH
00005  * All rights reserved
00006  *
00007  * Author: Heiko Hirschmueller
00008  *
00009  * Redistribution and use in source and binary forms, with or without
00010  * modification, are permitted provided that the following conditions are met:
00011  *
00012  * 1. Redistributions of source code must retain the above copyright notice,
00013  * this list of conditions and the following disclaimer.
00014  *
00015  * 2. Redistributions in binary form must reproduce the above copyright notice,
00016  * this list of conditions and the following disclaimer in the documentation
00017  * and/or other materials provided with the distribution.
00018  *
00019  * 3. Neither the name of the copyright holder nor the names of its contributors
00020  * may be used to endorse or promote products derived from this software without
00021  * specific prior written permission.
00022  *
00023  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
00024  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
00025  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
00026  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
00027  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
00028  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
00029  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
00030  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
00031  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
00032  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
00033  * POSSIBILITY OF SUCH DAMAGE.
00034  */
00035 
00036 #include <rc_genicam_api/system.h>
00037 #include <rc_genicam_api/interface.h>
00038 #include <rc_genicam_api/device.h>
00039 #include <rc_genicam_api/stream.h>
00040 #include <rc_genicam_api/buffer.h>
00041 #include <rc_genicam_api/image.h>
00042 #include <rc_genicam_api/imagelist.h>
00043 #include <rc_genicam_api/config.h>
00044 
00045 #include <rc_genicam_api/pixel_formats.h>
00046 
00047 #include <iostream>
00048 #include <fstream>
00049 #include <iomanip>
00050 #include <cmath>
00051 
00052 namespace
00053 {
00054 
00055 /*
00056   Computes a point cloud from the given synchronized left and disparity image
00057   pair and stores it in ply ascii format.
00058 
00059   @param f     Focal length factor (to be multiplicated with image width).
00060   @param t     Baseline in m.
00061   @param scale Disparity scale factor.
00062   @param left  Left camera image. The image must have format Mono8 or
00063                YCbCr411_8.
00064   @param disp  Corresponding disparity image, possibly downscaled by an integer
00065                factor. The image must be in format Coord3D_C16.
00066   @param conf  Optional corresponding confidence image in the same size as
00067                disp. The image must be in format Confidence8.
00068   @param error Optional corresponding error image in the same size as disp. The
00069                image must be in format Error8.
00070 */
00071 
00072 void storePointCloud(double f, double t, double scale,
00073                      std::shared_ptr<const rcg::Image> left,
00074                      std::shared_ptr<const rcg::Image> disp,
00075                      std::shared_ptr<const rcg::Image> conf=0,
00076                      std::shared_ptr<const rcg::Image> error=0)
00077 {
00078   // get size and scale factor between left image and disparity image
00079 
00080   uint32_t width=disp->getWidth();
00081   uint32_t height=disp->getHeight();
00082   bool bigendian=disp->isBigEndian();
00083   int ds=(left->getWidth()+disp->getWidth()-1)/disp->getWidth();
00084 
00085   // convert focal length factor into focal length in (disparity) pixels
00086 
00087   f*=width;
00088 
00089   // get pointer to disparity data and size of row in bytes
00090 
00091   const uint8_t *dps=disp->getPixels();
00092   size_t dstep=disp->getWidth()*sizeof(uint16_t)+disp->getXPadding();
00093 
00094   // count number of valid disparities
00095 
00096   int n=0;
00097   for (uint32_t k=0; k<height; k++)
00098   {
00099     int j=0;
00100     for (uint32_t i=0; i<width; i++)
00101     {
00102       if ((dps[j]|dps[j+1]) != 0) n++;
00103       j+=2;
00104     }
00105 
00106     dps+=dstep;
00107   }
00108 
00109   dps=disp->getPixels();
00110 
00111   // get pointer to optional confidence and error data and size of row in bytes
00112 
00113   const uint8_t *cps=0, *eps=0;
00114   size_t cstep=0, estep=0;
00115 
00116   if (conf)
00117   {
00118     cps=conf->getPixels();
00119     cstep=conf->getWidth()*sizeof(uint8_t)+conf->getXPadding();
00120   }
00121 
00122   if (error)
00123   {
00124     eps=error->getPixels();
00125     estep=error->getWidth()*sizeof(uint8_t)+error->getXPadding();
00126   }
00127 
00128   // open output file and write ASCII PLY header
00129 
00130   std::ostringstream name;
00131   double timestamp=left->getTimestampNS()/1000000000.0;
00132   name << "rc_vizard_" << std::setprecision(16) << timestamp << ".ply";
00133 
00134   std::ofstream out(name.str());
00135 
00136   out << "ply" << std::endl;
00137   out << "format ascii 1.0" << std::endl;
00138   out << "comment Created with gc_pointcloud from Roboception GmbH" << std::endl;
00139   out << "comment Camera [1 0 0; 0 1 0; 0 0 1] [0 0 0]" << std::endl;
00140   out << "element vertex " << n << std::endl;
00141   out << "property float32 x" << std::endl;
00142   out << "property float32 y" << std::endl;
00143   out << "property float32 z" << std::endl;
00144   out << "property float32 scan_size" << std::endl; // i.e. size of 3D point
00145 
00146   if (cps != 0)
00147   {
00148     out << "property float32 scan_conf" << std::endl; // optional confidence
00149   }
00150 
00151   if (eps != 0)
00152   {
00153     out << "property float32 scan_error" << std::endl; // optional error in 3D along line of sight
00154   }
00155 
00156   out << "property uint8 diffuse_red" << std::endl;
00157   out << "property uint8 diffuse_green" << std::endl;
00158   out << "property uint8 diffuse_blue" << std::endl;
00159   out << "end_header" << std::endl;
00160 
00161   // create point cloud
00162 
00163   for (uint32_t k=0; k<height; k++)
00164   {
00165     for (uint32_t i=0; i<width; i++)
00166     {
00167       // convert disparity from fixed comma 16 bit integer into float value
00168 
00169       double d;
00170       if (bigendian)
00171       {
00172         uint32_t j=i<<1;
00173         d=scale*((dps[j]<<8)|dps[j+1]);
00174       }
00175       else
00176       {
00177         uint32_t j=i<<1;
00178         d=scale*((dps[j+1]<<8)|dps[j]);
00179       }
00180 
00181       // if disparity is valid and color can be obtained
00182 
00183       if (d > 0)
00184       {
00185         // reconstruct 3D point from disparity value
00186 
00187         double x=(i+0.5-0.5*width)*t/d;
00188         double y=(k+0.5-0.5*height)*t/d;
00189         double z=f*t/d;
00190 
00191         // compute size of reconstructed point
00192 
00193         double x2=(i-0.5*width)*t/d;
00194         double size=2*1.4*std::abs(x2-x);
00195 
00196         // get corresponding color value
00197 
00198         uint8_t rgb[3];
00199         rcg::getColor(rgb, left, ds, i, k);
00200 
00201         // store colored point, optionally with confidence and error
00202 
00203         out << x << " " << y << " " << z << " " << size << " ";
00204 
00205         if (cps != 0)
00206         {
00207           out << cps[i]/255.0 << " ";
00208         }
00209 
00210         if (eps != 0)
00211         {
00212           out << eps[i]*scale*f*t/(d*d) << " ";
00213         }
00214 
00215         out << static_cast<int>(rgb[0]) << " ";
00216         out << static_cast<int>(rgb[1]) << " ";
00217         out << static_cast<int>(rgb[2]) << std::endl;
00218       }
00219     }
00220 
00221     dps+=dstep;
00222     cps+=cstep;
00223     eps+=estep;
00224   }
00225 
00226   out.close();
00227 }
00228 
00229 }
00230 
00231 int main(int argc, char *argv[])
00232 {
00233   try
00234   {
00235     if (argc >= 2)
00236     {
00237       // find specific device accross all systems and interfaces and open it
00238 
00239       std::shared_ptr<rcg::Device> dev=rcg::getDevice(argv[1]);
00240 
00241       if (dev)
00242       {
00243         dev->open(rcg::Device::CONTROL);
00244         std::shared_ptr<GenApi::CNodeMapRef> nodemap=dev->getRemoteNodeMap();
00245 
00246         // get focal length, baseline and disparity scale factor
00247 
00248         double f=rcg::getFloat(nodemap, "FocalLengthFactor", 0, 0, false);
00249         double t=rcg::getFloat(nodemap, "Baseline", 0, 0, true);
00250         double scale=rcg::getFloat(nodemap, "Scan3dCoordinateScale", 0, 0, true);
00251 
00252         // sanity check of some parameters
00253 
00254         rcg::checkFeature(nodemap, "Scan3dOutputMode", "DisparityC");
00255         rcg::checkFeature(nodemap, "Scan3dCoordinateOffset", "0");
00256         rcg::checkFeature(nodemap, "Scan3dInvalidDataFlag", "1");
00257         rcg::checkFeature(nodemap, "Scan3dInvalidDataValue", "0");
00258 
00259         // set to color format if available
00260 
00261         rcg::setEnum(nodemap, "PixelFormat", "YCbCr411_8", false);
00262 
00263         // enable left image and disparity image and disable all other streams
00264 
00265         {
00266           std::vector<std::string> component;
00267 
00268           rcg::getEnum(nodemap, "ComponentSelector", component, true);
00269 
00270           for (size_t i=0; i<component.size(); i++)
00271           {
00272             rcg::setEnum(nodemap, "ComponentSelector", component[i].c_str(), true);
00273 
00274             bool enable=(component[i] == "Intensity" || component[i] == "Disparity" ||
00275                          component[i] == "Confidence" || component[i] == "Error");
00276             rcg::setBoolean(nodemap, "ComponentEnable", enable, true);
00277           }
00278         }
00279 
00280         // open image stream
00281 
00282         std::vector<std::shared_ptr<rcg::Stream> > stream=dev->getStreams();
00283 
00284         if (stream.size() > 0)
00285         {
00286           // opening first stream
00287 
00288           stream[0]->open();
00289           stream[0]->startStreaming();
00290 
00291           // prepare buffers for time synchronization of images (buffer at most
00292           // 25 images in each list)
00293 
00294           rcg::ImageList left_list(25);
00295           rcg::ImageList disp_list(25);
00296           rcg::ImageList conf_list(25);
00297           rcg::ImageList error_list(25);
00298 
00299           bool run=true;
00300           int async=0, maxasync=50; // maximum number of asynchroneous images before giving up
00301 
00302           while (run && async < maxasync)
00303           {
00304             async++;
00305 
00306             // grab next image with timeout of 0.5 seconds
00307 
00308             const rcg::Buffer *buffer=stream[0]->grab(500);
00309             if (buffer != 0)
00310             {
00311               // check for a complete image in the buffer
00312 
00313               if (!buffer->getIsIncomplete() && buffer->getImagePresent())
00314               {
00315                 // store image in the corresponding list
00316 
00317                 uint64_t pixelformat=buffer->getPixelFormat();
00318                 if (pixelformat == Mono8 || pixelformat == YCbCr411_8)
00319                 {
00320                   left_list.add(buffer);
00321                 }
00322                 else if (pixelformat == Coord3D_C16)
00323                 {
00324                   disp_list.add(buffer);
00325                 }
00326                 else if (pixelformat == Confidence8)
00327                 {
00328                   conf_list.add(buffer);
00329                 }
00330                 else if (pixelformat == Error8)
00331                 {
00332                   error_list.add(buffer);
00333                 }
00334 
00335                 // check if both lists contain an image with the current time
00336                 // stamp
00337 
00338                 uint64_t timestamp=buffer->getTimestampNS();
00339 
00340                 std::shared_ptr<const rcg::Image> left=left_list.find(timestamp);
00341                 std::shared_ptr<const rcg::Image> disp=disp_list.find(timestamp);
00342                 std::shared_ptr<const rcg::Image> conf=conf_list.find(timestamp);
00343                 std::shared_ptr<const rcg::Image> error=error_list.find(timestamp);
00344 
00345                 if (left && disp && conf && error)
00346                 {
00347                   // compute and store point cloud from synchronized image pair
00348 
00349                   storePointCloud(f, t, scale, left, disp, conf, error);
00350 
00351                   // remove all images from the buffer with the current or an
00352                   // older time stamp
00353 
00354                   async=0;
00355                   left_list.removeOld(timestamp);
00356                   disp_list.removeOld(timestamp);
00357                   conf_list.removeOld(timestamp);
00358                   error_list.removeOld(timestamp);
00359 
00360                   // in this example, we exit the grabbing loop after receiving
00361                   // the first synchronized image pair
00362 
00363                   run=false;
00364                 }
00365               }
00366             }
00367             else
00368             {
00369               std::cerr << "Cannot grab images" << std::endl;
00370               break;
00371             }
00372           }
00373 
00374           // report if synchronization failed
00375 
00376           if (async >= maxasync && run)
00377           {
00378             std::cerr << "Cannot grab synchronized left and disparity image" << std::endl;
00379           }
00380 
00381           // stopping and closing image stream
00382 
00383           stream[0]->stopStreaming();
00384           stream[0]->close();
00385         }
00386         else
00387         {
00388           std::cerr << "No streams available" << std::endl;
00389         }
00390 
00391         // closing the communication to the device
00392 
00393         dev->close();
00394       }
00395       else
00396       {
00397         std::cerr << "Device '" << argv[1] << "' not found!" << std::endl;
00398       }
00399     }
00400     else
00401     {
00402       // show help
00403 
00404       std::cout << argv[0] << " [interface-id>:]<device-id>" << std::endl;
00405       std::cout << std::endl;
00406       std::cout << "Gets the first synchronized image set consisting of left, disparity, confidence" << std::endl;
00407       std::cout << "and error image, creates a point cloud and stores it in ply ascii format" << std::endl;
00408       std::cout << "in the current directory." << std::endl;
00409       std::cout << std::endl;
00410       std::cout << "<device-id> Device from which images will taken" << std::endl;
00411     }
00412   }
00413   catch (const std::exception &ex)
00414   {
00415     std::cerr << ex.what() << std::endl;
00416   }
00417 
00418   rcg::System::clearSystems();
00419 
00420   return 0;
00421 }


rc_visard_driver
Author(s): Heiko Hirschmueller , Christian Emmerich , Felix Ruess
autogenerated on Thu Jun 6 2019 20:43:02