Visual Servoing Platform version 3.7.0
Loading...
Searching...
No Matches
vpDisplayPCL.cpp
1/*
2 * ViSP, open source Visual Servoing Platform software.
3 * Copyright (C) 2005 - 2025 by Inria. All rights reserved.
4 *
5 * This software is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 * See the file LICENSE.txt at the root directory of this source
10 * distribution for additional information about the GNU GPL.
11 *
12 * For using ViSP with software that can not be combined with the GNU
13 * GPL, please contact Inria about acquiring a ViSP Professional
14 * Edition License.
15 *
16 * See https://visp.inria.fr for more information.
17 *
18 * This software was developed at:
19 * Inria Rennes - Bretagne Atlantique
20 * Campus Universitaire de Beaulieu
21 * 35042 Rennes Cedex
22 * France
23 *
24 * If you have questions regarding the use of this file, please contact
25 * Inria at visp@inria.fr
26 *
27 * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
28 * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
29 *
30 * Description:
31 * Display a point cloud using PCL library.
32 */
33
34#include <visp3/core/vpConfig.h>
35
36#if defined(VISP_HAVE_PCL) && defined(VISP_HAVE_PCL_VISUALIZATION) && defined(VISP_HAVE_THREADS)
37
38#include <visp3/gui/vpDisplayPCL.h>
39
41
42#ifndef DOXYGEN_SHOULD_SKIP_THIS
43unsigned int vpDisplayPCL::PointCloudHandling::s_nb = 0;
44#endif
45
54vpDisplayPCL::vpDisplayPCL(int posx, int posy, const std::string &window_name)
55 : m_stop(false), m_thread_running(false), m_verbose(false), m_width(640), m_height(480), m_posx(posx), m_posy(posy),
56 m_window_name(window_name), m_viewer(nullptr)
57{ }
58
67vpDisplayPCL::vpDisplayPCL(unsigned int width, unsigned int height, int posx, int posy, const std::string &window_name)
68 : m_stop(false), m_thread_running(false), m_verbose(false), m_width(width), m_height(height), m_posx(posx), m_posy(posy),
69 m_window_name(window_name), m_viewer(nullptr)
70{ }
71
81
85void vpDisplayPCL::createViewer()
86{
87 m_viewer = pcl::visualization::PCLVisualizer::Ptr(new pcl::visualization::PCLVisualizer(m_window_name));
88 m_viewer->setBackgroundColor(0, 0, 0);
89 m_viewer->initCameraParameters();
90 m_viewer->setPosition(m_posx, m_posy);
91 m_viewer->setCameraPosition(0, 0, -0.25, 0, -1, 0);
92 m_viewer->setSize(m_width, m_height);
93}
94
100void vpDisplayPCL::insertLegend(const size_t &id)
101{
102 std::string text = this->mv_xyz_pcl[id].second.m_name;
103 unsigned int posU = 10;
104 unsigned int size = 16;
105 unsigned int posV = 10 + id * size;
106 double rRatio = this->mv_xyz_pcl[id].second.m_color.R / 255.0;
107 double gRatio = this->mv_xyz_pcl[id].second.m_color.G / 255.0;
108 double bRatio = this->mv_xyz_pcl[id].second.m_color.B / 255.0;
109
110 this->m_viewer->addText(text, posU, posV, rRatio, gRatio, bRatio);
111};
112
124void vpDisplayPCL::display(const bool &blocking)
125{
126 if (m_thread_running) {
127 stop();
128
129 // Reset the fact that the point-clouds must be first inserted before being updated
130 // as the viewer will be an entirely new one
131 size_t nb_pcls = mv_xyz_pcl.size();
132 for (size_t id = 0; id < nb_pcls; ++id) {
133 mv_xyz_pcl[id].second.m_do_init = true;
134 }
135
136 nb_pcls = mv_colored_pcl.size();
137 for (size_t id = 0; id < nb_pcls; ++id) {
138 mv_colored_pcl[id].second.m_do_init = true;
139 }
140 }
141
142 if (!m_viewer) {
143 createViewer();
144 }
145
146 size_t nb_pcls = mv_xyz_pcl.size();
147 for (size_t id = 0; id < nb_pcls; ++id) {
148 if (mv_xyz_pcl[id].second.m_do_init) {
149 m_viewer->addPointCloud<pcl::PointXYZ>(mv_xyz_pcl[id].second.m_pcl, mv_xyz_handlers[id], mv_xyz_pcl[id].second.m_name);
150 m_viewer->setPointCloudRenderingProperties(pcl::visualization::PCL_VISUALIZER_POINT_SIZE, 1, mv_xyz_pcl[id].second.m_name);
151 insertLegend(id);
152 mv_xyz_pcl[id].second.m_do_init = false;
153 }
154 else {
155 m_viewer->updatePointCloud<pcl::PointXYZ>(mv_xyz_pcl[id].second.m_pcl, mv_xyz_handlers[id], mv_xyz_pcl[id].second.m_name);
156 }
157 }
158
159 nb_pcls = mv_colored_pcl.size();
160 for (size_t id = 0; id < nb_pcls; ++id) {
161 if (mv_colored_pcl[id].second.m_do_init) {
162 m_viewer->addPointCloud<pcl::PointXYZRGB>(mv_colored_pcl[id].second.m_pcl, mv_color_handlers[id], mv_colored_pcl[id].second.m_name);
163 m_viewer->setPointCloudRenderingProperties(pcl::visualization::PCL_VISUALIZER_POINT_SIZE, 1, mv_colored_pcl[id].second.m_name);
164
165 mv_colored_pcl[id].second.m_do_init = false;
166 }
167 else {
168 m_viewer->updatePointCloud<pcl::PointXYZRGB>(mv_colored_pcl[id].second.m_pcl, mv_color_handlers[id], mv_colored_pcl[id].second.m_name);
169 }
170 }
171
172 if (blocking) {
173 m_viewer->spin();
174 }
175 else {
176 m_viewer->spinOnce(10);
177 }
178}
179
183void vpDisplayPCL::run()
184{
185 // Creating the window in this method for 2 reasons:
186 // 1) It is the same code whichever startThread method is called
187 // 2) According to the pcl documentation, a PCLVisualizer `can NOT be used across multiple threads. Only call functions of objects of this class from the same thread that they were created in!`
188 if (m_viewer) {
189 // m_viewer->close();
190 m_viewer.reset();
191
192 // Reset the fact that the point-clouds must be first inserted before being updated
193 // as the viewer will be an entirely new one
194 size_t nb_pcls;
195 {
196 std::lock_guard<std::mutex> lock(m_mutex_vector);
197 nb_pcls = mv_xyz_pcl.size();
198 }
199 for (size_t id = 0; id < nb_pcls; ++id) {
200 std::lock_guard<std::mutex> lock(mv_xyz_pcl[id].first);
201 mv_xyz_pcl[id].second.m_do_init = true;
202 }
203
204 {
205 std::lock_guard<std::mutex> lock(m_mutex_vector);
206 nb_pcls = mv_colored_pcl.size();
207 }
208
209 for (size_t id = 0; id < nb_pcls; ++id) {
210 std::lock_guard<std::mutex> lock(mv_colored_pcl[id].first);
211 mv_colored_pcl[id].second.m_do_init = true;
212 }
213 }
214
215 createViewer();
216 pcl::PointCloud<pcl::PointXYZ>::Ptr local_pointcloud(new pcl::PointCloud<pcl::PointXYZ>());
217
218 size_t nb_pcls;
219 while (!m_stop) {
220 {
221 std::lock_guard<std::mutex> lock(m_mutex_vector);
222 nb_pcls = mv_xyz_pcl.size();
223 }
224 for (size_t id = 0; id < nb_pcls; ++id) {
225 {
226 std::lock_guard<std::mutex> lock(mv_xyz_pcl[id].first);
227 local_pointcloud = mv_xyz_pcl[id].second.m_pcl->makeShared();
228 }
229
230 if (mv_xyz_pcl[id].second.m_do_init) {
231 m_viewer->addPointCloud<pcl::PointXYZ>(local_pointcloud, mv_xyz_handlers[id], mv_xyz_pcl[id].second.m_name);
232 m_viewer->setPointCloudRenderingProperties(pcl::visualization::PCL_VISUALIZER_POINT_SIZE, 1, mv_xyz_pcl[id].second.m_name);
233 insertLegend(id);
234 mv_xyz_pcl[id].second.m_do_init = false;
235 }
236 else {
237 m_viewer->updatePointCloud<pcl::PointXYZ>(local_pointcloud, mv_xyz_handlers[id], mv_xyz_pcl[id].second.m_name);
238 }
239 }
240
241 m_viewer->spinOnce(10);
242 }
243
244 if (m_verbose) {
245 std::cout << "End of point cloud display thread" << std::endl;
246 }
247
248 // m_viewer->close();
249 m_viewer.reset(); // Mandatory because a viewer can only be used in the thread it was created in
250}
251
255void vpDisplayPCL::runColor()
256{
257 // Creating the window in this method for 2 reasons:
258 // 1) It is the same code for the two "run" methods
259 // 2) To avoid MacOS error "NSInternalInconsistencyException', reason: 'NSWindow should only be instantiated on the main thread!'"
260 if (m_viewer) {
261 // m_viewer->close();
262 m_viewer.reset();
263
264 // Reset the fact that the point-clouds must be first inserted before being updated
265 // as the viewer will be an entirely new one
266 size_t nb_pcls;
267 {
268 std::lock_guard<std::mutex> lock(m_mutex_vector);
269 nb_pcls = mv_xyz_pcl.size();
270 }
271 for (size_t id = 0; id < nb_pcls; ++id) {
272 std::lock_guard<std::mutex> lock(mv_xyz_pcl[id].first);
273 mv_xyz_pcl[id].second.m_do_init = true;
274 }
275
276 {
277 std::lock_guard<std::mutex> lock(m_mutex_vector);
278 nb_pcls = mv_colored_pcl.size();
279 }
280
281 for (size_t id = 0; id < nb_pcls; ++id) {
282 std::lock_guard<std::mutex> lock(mv_colored_pcl[id].first);
283 mv_colored_pcl[id].second.m_do_init = true;
284 }
285 }
286 createViewer();
287
288 pcl::PointCloud<pcl::PointXYZRGB>::Ptr local_pointcloud(new pcl::PointCloud<pcl::PointXYZRGB>());
289 size_t nb_pcls;
290 while (!m_stop) {
291 {
292 std::lock_guard<std::mutex> lock(m_mutex_vector);
293 nb_pcls = mv_colored_pcl.size();
294 }
295 for (size_t id = 0; id < nb_pcls; ++id) {
296 {
297 std::lock_guard<std::mutex> lock(mv_colored_pcl[id].first);
298 local_pointcloud = mv_colored_pcl[id].second.m_pcl->makeShared();
299 }
300
301 if (mv_colored_pcl[id].second.m_do_init) {
302 std::lock_guard<std::mutex> lock(mv_colored_pcl[id].first);
303 m_viewer->addPointCloud<pcl::PointXYZRGB>(local_pointcloud, mv_color_handlers[id], mv_colored_pcl[id].second.m_name);
304 m_viewer->setPointCloudRenderingProperties(pcl::visualization::PCL_VISUALIZER_POINT_SIZE, 1, mv_colored_pcl[id].second.m_name);
305
306 mv_colored_pcl[id].second.m_do_init = false;
307 }
308 else {
309 std::lock_guard<std::mutex> lock(mv_colored_pcl[id].first);
310 m_viewer->updatePointCloud<pcl::PointXYZRGB>(local_pointcloud, mv_color_handlers[id], mv_colored_pcl[id].second.m_name);
311 }
312 }
313
314 m_viewer->spinOnce(10);
315 }
316
317 if (m_verbose) {
318 std::cout << "End of point cloud display thread" << std::endl;
319 }
320
321 // m_viewer->close();
322 m_viewer.reset(); // Mandatory because a viewer can only be used in the thread it was created in
323}
324
332void vpDisplayPCL::startThread(const bool &colorThread)
333{
334 if (!m_thread_running) {
335 m_stop = false;
336 if (colorThread) {
337 m_thread = std::thread(&vpDisplayPCL::runColor, this);
338 }
339 else {
340 m_thread = std::thread(&vpDisplayPCL::run, this);
341 }
342 m_thread_running = true;
343 }
344 else {
345 throw(vpException(vpException::fatalError, "A viewer thread is already running."));
346 }
347}
348
357void vpDisplayPCL::startThread(std::mutex &mutex, pcl::PointCloud<pcl::PointXYZ>::Ptr pointcloud, const std::string &name, const vpColor &color)
358{
359 if (!m_thread_running) {
360 m_stop = false;
361 mv_xyz_pcl.emplace_back(std::pair<std::mutex &, XYZPointCloudHandling>(std::ref(mutex), XYZPointCloudHandling(pointcloud, name, color)));
362 mv_xyz_handlers.push_back(pcl::visualization::PointCloudColorHandlerCustom<pcl::PointXYZ>(pointcloud, color.R, color.G, color.B));
363 m_thread = std::thread(&vpDisplayPCL::run, this);
364 m_thread_running = true;
365 }
366 else {
367 throw(vpException(vpException::fatalError, "A viewer thread is already running."));
368 }
369}
370
378void vpDisplayPCL::startThread(std::mutex &mutex, pcl::PointCloud<pcl::PointXYZRGB>::Ptr pointcloud_color)
379{
380 if (!m_thread_running) {
381 m_stop = false;
382 mv_colored_pcl.emplace_back(std::pair<std::mutex &, ColoredPointCloudHandling>(std::ref(mutex), ColoredPointCloudHandling(pointcloud_color)));
383 mv_color_handlers.push_back(pcl::visualization::PointCloudColorHandlerRGBField<pcl::PointXYZRGB>(pointcloud_color));
384 m_thread = std::thread(&vpDisplayPCL::runColor, this);
385 m_thread_running = true;
386 }
387 else {
388 throw(vpException(vpException::fatalError, "A viewer thread is already running."));
389 }
390}
391
400void vpDisplayPCL::addPointCloud(std::mutex &mutex, pcl::PointCloud<pcl::PointXYZ>::Ptr pointcloud, const std::string &name, const vpColor &color)
401{
402 std::lock_guard lg(m_mutex_vector);
403 mv_xyz_pcl.emplace_back(std::pair<std::mutex &, XYZPointCloudHandling>(std::ref(mutex), XYZPointCloudHandling(pointcloud, name, color)));
404 mv_xyz_handlers.push_back(pcl::visualization::PointCloudColorHandlerCustom<pcl::PointXYZ>(pointcloud, color.R, color.G, color.B));
405}
406
413void vpDisplayPCL::addPointCloud(std::mutex &mutex, pcl::PointCloud<pcl::PointXYZRGB>::Ptr pointcloud)
414{
415 std::lock_guard lg(m_mutex_vector);
416 mv_colored_pcl.emplace_back(std::pair<std::mutex &, ColoredPointCloudHandling>(std::ref(mutex), ColoredPointCloudHandling(pointcloud)));
417 mv_color_handlers.push_back(pcl::visualization::PointCloudColorHandlerRGBField<pcl::PointXYZRGB>(pointcloud));
418}
419
429void vpDisplayPCL::setPosition(int posx, int posy)
430{
431 m_posx = posx;
432 m_posy = posy;
433}
434
443void vpDisplayPCL::setWindowName(const std::string &window_name)
444{
445 m_window_name = window_name;
446}
447
452{
453 if (m_thread_running) {
454 if (!m_stop) {
455 m_stop = true;
456
457 if (m_thread.joinable()) {
458 m_thread.join();
459 }
460 }
461 m_thread_running = false;
462 }
463}
464
469void vpDisplayPCL::setVerbose(bool verbose)
470{
471 m_verbose = verbose;
472}
473END_VISP_NAMESPACE
474#elif !defined(VISP_BUILD_SHARED_LIBS)
475// Work around to avoid warning: libvisp_gui.a(vpDisplayPCL.cpp.o) has no symbols
476void dummy_vpDisplayPCL() { }
477
478#endif
Class to define RGB colors available for display functionalities.
Definition vpColor.h:157
void setPosition(int posx, int posy)
void display(const bool &blocking=false)
Monothread display method.
void setWindowName(const std::string &window_name)
vpDisplayPCL(int posx=0, int posy=0, const std::string &window_name="")
void startThread(const bool &colorThread=false)
void setVerbose(bool verbose)
void addPointCloud(std::mutex &mutex, pcl::PointCloud< pcl::PointXYZ >::Ptr pointcloud, const std::string &name="", const vpColor &color=vpColor::red)
Insert a point cloud to display.
error that can be emitted by ViSP classes.
Definition vpException.h:60
@ fatalError
Fatal error.
Definition vpException.h:72