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