1 /*
2  * Software License Agreement (BSD License)
3  *
4  *  Copyright (c) 2011, Willow Garage, Inc.
5  *  All rights reserved.
6  *
7  *  Redistribution and use in source and binary forms, with or without
8  *  modification, are permitted provided that the following conditions
9  *  are met:
10  *
11  *   * Redistributions of source code must retain the above copyright
12  *     notice, this list of conditions and the following disclaimer.
13  *   * Redistributions in binary form must reproduce the above
14  *     copyright notice, this list of conditions and the following
15  *     disclaimer in the documentation and/or other materials provided
16  *     with the distribution.
17  *   * Neither the name of Willow Garage, Inc. nor the names of its
18  *     contributors may be used to endorse or promote products derived
19  *     from this software without specific prior written permission.
20  *
21  *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22  *  "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23  *  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
24  *  FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
25  *  COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
26  *  INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
27  *  BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
28  *  LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
29  *  CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30  *  LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
31  *  ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
32  *  POSSIBILITY OF SUCH DAMAGE.
33  *
34  * Author: Nico Blodow (blodow@cs.tum.edu)
35  *         Christian Potthast (potthast@usc.edu)
36  */
37 
38 #include <pcl/common/time.h>
39 #include <pcl/console/parse.h>
40 #include <pcl/console/print.h>
41 #include <pcl/io/openni_grabber.h>
42 #include <pcl/io/pcd_io.h>
43 #include <pcl/visualization/common/float_image_utils.h>
44 #include <pcl/visualization/image_viewer.h>
45 #include <pcl/visualization/vtk.h>
46 #include <pcl/point_types.h>
47 
48 #include <boost/filesystem.hpp>
49 
50 #include <mutex>
51 
52 using namespace pcl::console;
53 using namespace boost::filesystem;
54 
55 class OpenNIGrabFrame {
56 public:
OpenNIGrabFrame(pcl::OpenNIGrabber & grabber,bool paused)57   OpenNIGrabFrame(pcl::OpenNIGrabber& grabber, bool paused)
58   : grabber_(grabber)
59   , image_viewer_("RGB Image")
60   , depth_image_viewer_("Depth Image")
61   , quit_(false)
62   , continuous_(!paused)
63   , trigger_(false)
64   , importer_(vtkSmartPointer<vtkImageImport>::New())
65   , depth_importer_(vtkSmartPointer<vtkImageImport>::New())
66   , writer_(vtkSmartPointer<vtkTIFFWriter>::New())
67   , flipper_(vtkSmartPointer<vtkImageFlip>::New())
68   {
69     importer_->SetNumberOfScalarComponents(3);
70     importer_->SetDataScalarTypeToUnsignedChar();
71     depth_importer_->SetNumberOfScalarComponents(1);
72     depth_importer_->SetDataScalarTypeToUnsignedShort();
73     writer_->SetCompressionToPackBits();
74     flipper_->SetFilteredAxes(1);
75   }
76 
77   void
image_callback(const openni_wrapper::Image::Ptr & image,const openni_wrapper::DepthImage::Ptr & depth_image,float)78   image_callback(const openni_wrapper::Image::Ptr& image,
79                  const openni_wrapper::DepthImage::Ptr& depth_image,
80                  float)
81   {
82     std::lock_guard<std::mutex> lock(image_mutex_);
83     image_ = image;
84     depth_image_ = depth_image;
85     lock.unlock();
86   }
87 
88   void
keyboard_callback(const pcl::visualization::KeyboardEvent & event,void *)89   keyboard_callback(const pcl::visualization::KeyboardEvent& event, void*)
90   {
91     if (event.keyUp()) {
92       switch (event.getKeyCode()) {
93       case 27:
94       case 'Q':
95       case 'q':
96         quit_ = true;
97         break;
98       case ' ':
99         continuous_ = !continuous_;
100         break;
101       case 'G':
102       case 'g':
103         trigger_ = true;
104         break;
105       }
106     }
107   }
108 
109   void
mouse_callback(const pcl::visualization::MouseEvent & mouse_event,void *)110   mouse_callback(const pcl::visualization::MouseEvent& mouse_event, void*)
111   {
112     if (mouse_event.getType() == pcl::visualization::MouseEvent::MouseButtonPress &&
113         mouse_event.getButton() == pcl::visualization::MouseEvent::LeftButton) {
114       trigger_ = true;
115     }
116   }
117 
118   void
saveImages()119   saveImages()
120   {
121     std::string time = boost::posix_time::to_iso_string(
122         boost::posix_time::microsec_clock::local_time());
123     openni_wrapper::Image::Ptr image;
124     openni_wrapper::DepthImage::Ptr depth_image;
125 
126     image_mutex_.lock();
127     image.swap(image_);
128     depth_image.swap(depth_image_);
129     image_mutex_.unlock();
130 
131     if (image) {
132       const void* data;
133       if (image->getEncoding() != openni_wrapper::Image::RGB) {
134         static unsigned char* rgb_data = 0;
135         static unsigned rgb_data_size = 0;
136 
137         if (rgb_data_size < image->getWidth() * image->getHeight()) {
138           delete[] rgb_data;
139           rgb_data_size = image->getWidth() * image->getHeight();
140           rgb_data = new unsigned char[rgb_data_size * 3];
141         }
142         image->fillRGB(image->getWidth(), image->getHeight(), rgb_data);
143         data = reinterpret_cast<const void*>(rgb_data);
144       }
145       else
146         data = reinterpret_cast<const void*>(image->getMetaData().Data());
147 
148       importer_->SetWholeExtent(
149           0, image->getWidth() - 1, 0, image->getHeight() - 1, 0, 0);
150       importer_->SetDataExtentToWholeExtent();
151 
152       const std::string rgb_frame_filename = "frame_" + time + "_rgb.tiff";
153       importer_->SetImportVoidPointer(const_cast<void*>(data), 1);
154       importer_->Update();
155       flipper_->SetInputConnection(importer_->GetOutputPort());
156       flipper_->Update();
157       writer_->SetFileName(rgb_frame_filename.c_str());
158       writer_->SetInputConnection(flipper_->GetOutputPort());
159       writer_->Write();
160     }
161     if (depth_image) {
162       const std::string depth_frame_filename = "frame_" + time + "_depth.tiff";
163 
164       depth_importer_->SetWholeExtent(
165           0, depth_image->getWidth() - 1, 0, depth_image->getHeight() - 1, 0, 0);
166       depth_importer_->SetDataExtentToWholeExtent();
167       depth_importer_->SetImportVoidPointer(
168           const_cast<void*>(
169               reinterpret_cast<const void*>(depth_image->getDepthMetaData().Data())),
170           1);
171       depth_importer_->Update();
172       flipper_->SetInputConnection(depth_importer_->GetOutputPort());
173       flipper_->Update();
174       writer_->SetFileName(depth_frame_filename.c_str());
175       writer_->SetInputConnection(flipper_->GetOutputPort());
176       writer_->Write();
177     }
178 
179     trigger_ = false;
180   }
181 
182   int
run()183   run()
184   {
185     // register the keyboard and mouse callback for the visualizer
186     image_viewer_.registerMouseCallback(&OpenNIGrabFrame::mouse_callback, *this);
187     image_viewer_.registerKeyboardCallback(&OpenNIGrabFrame::keyboard_callback, *this);
188     depth_image_viewer_.registerMouseCallback(&OpenNIGrabFrame::mouse_callback, *this);
189     depth_image_viewer_.registerKeyboardCallback(&OpenNIGrabFrame::keyboard_callback,
190                                                  *this);
191 
192     std::function<void(const openni_wrapper::Image::Ptr&,
193                        const openni_wrapper::DepthImage::Ptr&,
194                        float)>
195         image_cb = [this](const openni_wrapper::Image::Ptr& img,
196                           const openni_wrapper::DepthImage::Ptr& depth,
197                           float f) { image_callback(img, depth, f); };
198     boost::signals2::connection image_connection = grabber_.registerCallback(image_cb);
199 
200     // start receiving point clouds
201     grabber_.start();
202     unsigned char* rgb_data = 0;
203     unsigned rgb_data_size = 0;
204     unsigned char* depth_data = 0;
205 
206     // wait until user quits program with Ctrl-C, but no busy-waiting -> sleep (1);
207     while (!image_viewer_.wasStopped() && !quit_) {
208       std::string time = boost::posix_time::to_iso_string(
209           boost::posix_time::microsec_clock::local_time());
210       openni_wrapper::Image::Ptr image;
211       openni_wrapper::DepthImage::Ptr depth_image;
212 
213       if (image_mutex_.try_lock()) {
214         image.swap(image_);
215         depth_image.swap(depth_image_);
216         image_mutex_.unlock();
217       }
218 
219       if (image) {
220         const void* data;
221         if (image->getEncoding() != openni_wrapper::Image::RGB) {
222           if (rgb_data_size < image->getWidth() * image->getHeight()) {
223             delete[] rgb_data;
224             rgb_data_size = image->getWidth() * image->getHeight();
225             rgb_data = new unsigned char[rgb_data_size * 3];
226           }
227           image->fillRGB(image->getWidth(), image->getHeight(), rgb_data);
228           data = reinterpret_cast<const void*>(rgb_data);
229         }
230         else
231           data = reinterpret_cast<const void*>(image->getMetaData().Data());
232         image_viewer_.addRGBImage(
233             (const unsigned char*)data, image->getWidth(), image->getHeight());
234 
235         if (continuous_ || trigger_) {
236           importer_->SetWholeExtent(
237               0, image->getWidth() - 1, 0, image->getHeight() - 1, 0, 0);
238           importer_->SetDataExtentToWholeExtent();
239 
240           const std::string rgb_frame_filename = "frame_" + time + "_rgb.tiff";
241           importer_->SetImportVoidPointer(const_cast<void*>(data), 1);
242           importer_->Update();
243           flipper_->SetInputConnection(importer_->GetOutputPort());
244           flipper_->Update();
245           writer_->SetFileName(rgb_frame_filename.c_str());
246           writer_->SetInputConnection(flipper_->GetOutputPort());
247           writer_->Write();
248           std::cout << "writing rgb frame: " << rgb_frame_filename << std::endl;
249         }
250       }
251 
252       if (depth_image) {
253         delete[] depth_data;
254         depth_data = pcl::visualization::FloatImageUtils::getVisualImage(
255             reinterpret_cast<const unsigned short*>(
256                 depth_image->getDepthMetaData().Data()),
257             depth_image->getWidth(),
258             depth_image->getHeight(),
259             std::numeric_limits<unsigned short>::min(),
260             // Scale so that the colors look brigher on screen
261             std::numeric_limits<unsigned short>::max() / 10,
262             true);
263 
264         depth_image_viewer_.addRGBImage(
265             depth_data, depth_image->getWidth(), depth_image->getHeight());
266         if (continuous_ || trigger_) {
267           const std::string depth_frame_filename = "frame_" + time + "_depth.tiff";
268 
269           depth_importer_->SetWholeExtent(
270               0, depth_image->getWidth() - 1, 0, depth_image->getHeight() - 1, 0, 0);
271           depth_importer_->SetDataExtentToWholeExtent();
272           depth_importer_->SetImportVoidPointer(
273               const_cast<void*>(reinterpret_cast<const void*>(
274                   depth_image->getDepthMetaData().Data())),
275               1);
276           depth_importer_->Update();
277           flipper_->SetInputConnection(depth_importer_->GetOutputPort());
278           flipper_->Update();
279           writer_->SetFileName(depth_frame_filename.c_str());
280           writer_->SetInputConnection(flipper_->GetOutputPort());
281           writer_->Write();
282           std::cout << "writing depth frame: " << depth_frame_filename << std::endl;
283         }
284       }
285       trigger_ = false;
286       image_viewer_.spinOnce();
287       depth_image_viewer_.spinOnce();
288     }
289 
290     image_mutex_.lock();
291     // stop the grabber
292     grabber_.stop();
293     image_mutex_.unlock();
294     return 0;
295   }
296 
297   pcl::OpenNIGrabber& grabber_;
298   pcl::visualization::ImageViewer image_viewer_;
299   pcl::visualization::ImageViewer depth_image_viewer_;
300   bool quit_;
301   bool continuous_;
302   bool trigger_;
303   mutable std::mutex image_mutex_;
304   openni_wrapper::Image::Ptr image_;
305   openni_wrapper::DepthImage::Ptr depth_image_;
306   vtkSmartPointer<vtkImageImport> importer_, depth_importer_;
307   vtkSmartPointer<vtkTIFFWriter> writer_;
308   vtkSmartPointer<vtkImageFlip> flipper_;
309 };
310 
311 void
usage(char ** argv)312 usage(char** argv)
313 {
314   std::cout << "usage: " << argv[0]
315             << " [((<device_id> | <path-to-oni-file>) [-imagemode <mode>] "
316                "[-depthformat <format>] [-paused] | -l [<device_id>]| -h | --help)]"
317             << std::endl;
318   std::cout << argv[0] << " -h | --help : shows this help" << std::endl;
319   std::cout << argv[0] << " -l : list all available devices" << std::endl;
320   std::cout << argv[0]
321             << " -l <device-id> : list all available modes for specified device"
322             << std::endl;
323 
324   std::cout << "                 device_id may be #1, #2, ... for the first, second "
325                "etc device in the list"
326 #ifndef _WIN32
327             << " or" << std::endl
328             << "                 bus@address for the device connected to a specific "
329                "usb-bus / address combination or"
330             << std::endl
331             << "                 <serial-number>"
332 #endif
333             << std::endl;
334   std::cout << "                 -paused : start grabber in paused mode. Toggle pause "
335                "by pressing the space bar\n";
336   std::cout << "                           or grab single frames by just pressing the "
337                "left mouse button.\n";
338   std::cout << std::endl;
339   std::cout << "examples:" << std::endl;
340   std::cout << argv[0] << " \"#1\"" << std::endl;
341   std::cout << "    uses the first device." << std::endl;
342   std::cout << argv[0] << " \"./temp/test.oni\"" << std::endl;
343   std::cout << "    uses the oni-player device to play back oni file given by path."
344             << std::endl;
345   std::cout << argv[0] << " -l" << std::endl;
346   std::cout << "    lists all available devices." << std::endl;
347   std::cout << argv[0] << " -l \"#2\"" << std::endl;
348   std::cout << "    lists all available modes for the second device" << std::endl;
349 #ifndef _WIN32
350   std::cout << argv[0] << " A00361800903049A" << std::endl;
351   std::cout << "    uses the device with the serial number \'A00361800903049A\'."
352             << std::endl;
353   std::cout << argv[0] << " 1@16" << std::endl;
354   std::cout << "    uses the device on address 16 at USB bus 1." << std::endl;
355 #endif
356 }
357 
358 int
main(int argc,char ** argv)359 main(int argc, char** argv)
360 {
361   std::string device_id("");
362   pcl::OpenNIGrabber::Mode image_mode = pcl::OpenNIGrabber::OpenNI_Default_Mode;
363 
364   if (argc >= 2) {
365     device_id = argv[1];
366     if (device_id == "--help" || device_id == "-h") {
367       usage(argv);
368       return 0;
369     }
370     else if (device_id == "-l") {
371       if (argc >= 3) {
372         pcl::OpenNIGrabber grabber(argv[2]);
373         auto device = grabber.getDevice();
374         std::vector<std::pair<int, XnMapOutputMode>> modes;
375 
376         if (device->hasImageStream()) {
377           std::cout << std::endl
378                     << "Supported image modes for device: " << device->getVendorName()
379                     << " , " << device->getProductName() << std::endl;
380           modes = grabber.getAvailableImageModes();
381           for (std::vector<std::pair<int, XnMapOutputMode>>::const_iterator it =
382                    modes.begin();
383                it != modes.end();
384                ++it) {
385             std::cout << it->first << " = " << it->second.nXRes << " x "
386                       << it->second.nYRes << " @ " << it->second.nFPS << std::endl;
387           }
388         }
389       }
390       else {
391         openni_wrapper::OpenNIDriver& driver =
392             openni_wrapper::OpenNIDriver::getInstance();
393         if (driver.getNumberDevices() > 0) {
394           for (unsigned deviceIdx = 0; deviceIdx < driver.getNumberDevices();
395                ++deviceIdx) {
396             std::cout << "Device: " << deviceIdx + 1
397                       << ", vendor: " << driver.getVendorName(deviceIdx)
398                       << ", product: " << driver.getProductName(deviceIdx)
399                       << ", connected: " << driver.getBus(deviceIdx) << " @ "
400                       << driver.getAddress(deviceIdx) << ", serial number: \'"
401                       << driver.getSerialNumber(deviceIdx) << "\'" << std::endl;
402           }
403         }
404         else
405           std::cout << "No devices connected." << std::endl;
406 
407         std::cout << "Virtual Devices available: ONI player" << std::endl;
408       }
409       return 0;
410     }
411   }
412   else {
413     openni_wrapper::OpenNIDriver& driver = openni_wrapper::OpenNIDriver::getInstance();
414     if (driver.getNumberDevices() > 0)
415       std::cout << "Device Id not set, using first device." << std::endl;
416   }
417 
418   unsigned mode;
419   if (pcl::console::parse(argc, argv, "-imagemode", mode) != -1)
420     image_mode = static_cast<pcl::OpenNIGrabber::Mode>(mode);
421 
422   int depthformat = openni_wrapper::OpenNIDevice::OpenNI_12_bit_depth;
423   pcl::console::parse_argument(argc, argv, "-depthformat", depthformat);
424 
425   pcl::OpenNIGrabber grabber(
426       device_id, pcl::OpenNIGrabber::OpenNI_Default_Mode, image_mode);
427   // Set the depth output format
428   grabber.getDevice()->setDepthOutputFormat(
429       static_cast<openni_wrapper::OpenNIDevice::DepthMode>(depthformat));
430 
431   bool paused = find_switch(argc, argv, "-paused");
432 
433   OpenNIGrabFrame grabFrame(grabber, paused);
434   return grabFrame.run();
435 }
436