mirror of
https://github.com/introlab/rtabmap.git
synced 2026-09-01 17:10:26 +08:00
Tango: added mesh smoothing, show file size on Open, default timestamp filename on save/export
This commit is contained in:
@@ -29,8 +29,8 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
#include "util.h"
|
||||
#include "rtabmap/utilite/ULogger.h"
|
||||
#include "rtabmap/core/util3d_transforms.h"
|
||||
#include "rtabmap/core/util2d.h"
|
||||
#include "rtabmap/core/OdometryEvent.h"
|
||||
#include "rtabmap/core/util2d.h"
|
||||
#include <tango_client_api.h>
|
||||
|
||||
namespace rtabmap {
|
||||
@@ -613,6 +613,7 @@ SensorData CameraTango::captureImage(CameraInfo * info)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(oi)
|
||||
{
|
||||
scan = cv::Mat(1, oi, CV_32FC3, scanData.data()).clone();
|
||||
|
||||
@@ -30,6 +30,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
#include "RTABMapApp.h"
|
||||
|
||||
#include <rtabmap/core/Rtabmap.h>
|
||||
#include <rtabmap/core/util2d.h>
|
||||
#include <rtabmap/core/util3d.h>
|
||||
#include <rtabmap/core/util3d_transforms.h>
|
||||
#include <rtabmap/core/util3d_filtering.h>
|
||||
@@ -137,6 +138,7 @@ RTABMapApp::RTABMapApp() :
|
||||
clearSceneOnNextRender_(false),
|
||||
filterPolygonsOnNextRender_(false),
|
||||
gainCompensationOnNextRender_(0),
|
||||
bilateralFilteringOnNextRender_(false),
|
||||
cameraJustInitialized_(false),
|
||||
totalPoints_(0),
|
||||
totalPolygons_(0),
|
||||
@@ -324,6 +326,49 @@ private:
|
||||
rtabmap::Statistics stats_;
|
||||
};
|
||||
|
||||
// OpenGL thread
|
||||
bool RTABMapApp::smoothMesh(int id, Mesh & mesh)
|
||||
{
|
||||
UTimer t;
|
||||
// reconstruct depth image
|
||||
cv::Mat depth = cv::Mat::zeros(mesh.height, mesh.width, CV_32FC1);
|
||||
rtabmap::Transform localTransformInv = mesh.cameraModel.localTransform().inverse();
|
||||
for(unsigned int i=0; i<mesh.denseToOrganizedIndices.size(); ++i)
|
||||
{
|
||||
// FastBilateralFilter works in camera frame
|
||||
pcl::PointXYZRGB pt = rtabmap::util3d::transformPoint(mesh.cloud->at(i), localTransformInv);
|
||||
depth.at<float>(mesh.denseToOrganizedIndices[i]) = pt.z;
|
||||
}
|
||||
|
||||
depth = rtabmap::util2d::fastBilateralFiltering(depth, 2.0f, 0.075f);
|
||||
LOGI("smoothMesh() Bilateral filtering of %d, time=%fs", id, t.ticks());
|
||||
|
||||
pcl::PointCloud<pcl::PointXYZRGB>::Ptr cloudOrganized = rtabmap::util3d::cloudFromDepthRGB(mesh.texture.rows>1?mesh.texture:rtabmap::uncompressImage(mesh.texture), depth, mesh.cameraModel, 1, maxCloudDepth_);
|
||||
cloudOrganized = rtabmap::util3d::transformPointCloud(cloudOrganized, mesh.cameraModel.localTransform());
|
||||
|
||||
//reconstruct the mesh with smoothed surfaces
|
||||
std::vector<pcl::Vertices> polygons = rtabmap::util3d::organizedFastMesh(cloudOrganized, meshAngleToleranceDeg_*M_PI/180.0, false, meshTrianglePix_);
|
||||
|
||||
// filter NaN points
|
||||
pcl::PointCloud<pcl::PointXYZRGB>::Ptr outputCloud(new pcl::PointCloud<pcl::PointXYZRGB>);
|
||||
std::vector<pcl::Vertices> outputPolygons;
|
||||
std::vector<int> denseToOrganizedIndices = rtabmap::util3d::filterNaNPointsFromMesh(*cloudOrganized, polygons, *outputCloud, outputPolygons);
|
||||
|
||||
LOGI("smoothMesh() Reconstructing the mesh of %d, time=%fs", id, t.ticks());
|
||||
if(outputPolygons.size())
|
||||
{
|
||||
mesh.cloud = outputCloud;
|
||||
mesh.polygons = outputPolygons;
|
||||
mesh.denseToOrganizedIndices = denseToOrganizedIndices;
|
||||
}
|
||||
else
|
||||
{
|
||||
LOGE("smoothMesh() Failed to smooth surface %d", id);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// OpenGL thread
|
||||
int RTABMapApp::Render()
|
||||
{
|
||||
@@ -368,7 +413,7 @@ int RTABMapApp::Render()
|
||||
{
|
||||
cv::Mat compressed = iter->second.texture;
|
||||
iter->second.texture = rtabmap::uncompressImage(iter->second.texture);
|
||||
main_scene_.addMesh(iter->first, iter->second, iter->second.pose);
|
||||
main_scene_.addMesh(iter->first, iter->second, opengl_world_T_rtabmap_world*iter->second.pose);
|
||||
main_scene_.setCloudVisible(iter->first, iter->second.visible);
|
||||
iter->second.texture = compressed;
|
||||
}
|
||||
@@ -484,7 +529,7 @@ int RTABMapApp::Render()
|
||||
main_scene_.setCloudVisible(id, true);
|
||||
std::map<int, Mesh>::iterator meshIter = createdMeshes_.find(id);
|
||||
UASSERT(meshIter!=createdMeshes_.end());
|
||||
meshIter->second.pose = iter->second;
|
||||
meshIter->second.pose = opengl_world_T_rtabmap_world.inverse()*iter->second;
|
||||
meshIter->second.visible = true;
|
||||
}
|
||||
else if(uContains(bufferedSensorData, id))
|
||||
@@ -514,7 +559,7 @@ int RTABMapApp::Render()
|
||||
pcl::PointCloud<pcl::PointXYZRGB>::Ptr outputCloud(new pcl::PointCloud<pcl::PointXYZRGB>);
|
||||
std::vector<pcl::Vertices> outputPolygons;
|
||||
|
||||
std::vector<int> denseToOrganizedIndices = rtabmap::util3d::filterNotUsedVerticesFromMesh(*output, polygons, *outputCloud, outputPolygons);
|
||||
std::vector<int> denseToOrganizedIndices = rtabmap::util3d::filterNaNPointsFromMesh(*output, polygons, *outputCloud, outputPolygons);
|
||||
|
||||
LOGI("Creating mesh, %d polygons (%fs)", (int)outputPolygons.size(), time.ticks());
|
||||
|
||||
@@ -529,9 +574,10 @@ int RTABMapApp::Render()
|
||||
inserted.first->second.width = cloud->width;
|
||||
inserted.first->second.height = cloud->height;
|
||||
inserted.first->second.polygons = outputPolygons;
|
||||
inserted.first->second.pose = iter->second;
|
||||
inserted.first->second.pose = opengl_world_T_rtabmap_world.inverse()*iter->second;
|
||||
inserted.first->second.visible = true;
|
||||
inserted.first->second.texture = data.imageRaw();
|
||||
inserted.first->second.cameraModel = data.cameraModels()[0];
|
||||
|
||||
main_scene_.addMesh(id, inserted.first->second, iter->second);
|
||||
|
||||
@@ -582,7 +628,7 @@ int RTABMapApp::Render()
|
||||
}
|
||||
else
|
||||
{
|
||||
main_scene_.setCloudVisible(-1, odomCloudShown_ && !trajectoryMode_);
|
||||
main_scene_.setCloudVisible(-1, odomCloudShown_ && !trajectoryMode_ && !paused_);
|
||||
|
||||
//just process the last one
|
||||
if(!odomEvent.pose().isNull())
|
||||
@@ -660,14 +706,44 @@ int RTABMapApp::Render()
|
||||
compensator.apply(iter->first, iter->second.texture);
|
||||
}
|
||||
}
|
||||
main_scene_.updateMesh(iter->first, iter->second);
|
||||
|
||||
// If we do bilateral filtering, do it right now as the texture is uncompressed
|
||||
if((notifyDataLoaded || bilateralFilteringOnNextRender_) &&
|
||||
iter->second.cloud->size() && smoothMesh(iter->first, iter->second))
|
||||
{
|
||||
main_scene_.addMesh(iter->first, iter->second, opengl_world_T_rtabmap_world*iter->second.pose);
|
||||
}
|
||||
else
|
||||
{
|
||||
main_scene_.updateMesh(iter->first, iter->second);
|
||||
}
|
||||
|
||||
iter->second.texture = rtabmap::compressImage2(iter->second.texture, ".jpg");
|
||||
}
|
||||
bilateralFilteringOnNextRender_ = false;
|
||||
}
|
||||
gainCompensationOnNextRender_ = 0;
|
||||
notifyDataLoaded = true;
|
||||
}
|
||||
|
||||
if(bilateralFilteringOnNextRender_)
|
||||
{
|
||||
LOGI("Bilateral filtering...");
|
||||
bilateralFilteringOnNextRender_ = false;
|
||||
boost::mutex::scoped_lock lock(meshesMutex_);
|
||||
for(std::map<int, Mesh>::iterator iter = createdMeshes_.begin(); iter!=createdMeshes_.end(); ++iter)
|
||||
{
|
||||
if(iter->second.cloud->size())
|
||||
{
|
||||
if(smoothMesh(iter->first, iter->second))
|
||||
{
|
||||
main_scene_.addMesh(iter->first, iter->second, opengl_world_T_rtabmap_world*iter->second.pose);
|
||||
}
|
||||
}
|
||||
}
|
||||
notifyDataLoaded = true;
|
||||
}
|
||||
|
||||
if(filterPolygonsOnNextRender_)
|
||||
{
|
||||
LOGI("Polygon filtering...");
|
||||
@@ -1122,7 +1198,7 @@ int RTABMapApp::postProcessing(int approach)
|
||||
LOGE("g2o not available!");
|
||||
}
|
||||
}
|
||||
else if(approach!=4 && approach!=5)
|
||||
else if(approach!=4 && approach!=5 && approach != 7)
|
||||
{
|
||||
// simple graph optmimization
|
||||
rtabmap_->getGraph(poses, links, true, true);
|
||||
@@ -1139,26 +1215,31 @@ int RTABMapApp::postProcessing(int approach)
|
||||
|
||||
rtabmap_->setOptimizedPoses(poses);
|
||||
}
|
||||
else if(approach!=4 && approach!=5)
|
||||
else if(approach!=4 && approach!=5 && approach != 7)
|
||||
{
|
||||
returnedValue = -1;
|
||||
}
|
||||
|
||||
if(returnedValue >=0)
|
||||
{
|
||||
boost::mutex::scoped_lock lock(renderingMutex_);
|
||||
// filter polygons
|
||||
if(approach == 4)
|
||||
if(approach == -1 && approach == 4)
|
||||
{
|
||||
boost::mutex::scoped_lock lock(renderingMutex_);
|
||||
filterPolygonsOnNextRender_ = true;
|
||||
}
|
||||
|
||||
// gain compensation
|
||||
if(approach == -1 || approach == 5 || approach == 6)
|
||||
{
|
||||
boost::mutex::scoped_lock lock(renderingMutex_);
|
||||
gainCompensationOnNextRender_ = approach == 6 ? 2 : 1; // 2 = full, 1 = fast
|
||||
}
|
||||
|
||||
// bilateral filtering
|
||||
if(approach == -1 && approach == 7)
|
||||
{
|
||||
bilateralFilteringOnNextRender_ = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -138,6 +138,7 @@ class RTABMapApp : public UEventsHandler {
|
||||
|
||||
private:
|
||||
rtabmap::ParametersMap getRtabmapParameters();
|
||||
bool smoothMesh(int id, Mesh & mesh);
|
||||
|
||||
private:
|
||||
rtabmap::CameraTango * camera_;
|
||||
@@ -164,6 +165,7 @@ class RTABMapApp : public UEventsHandler {
|
||||
bool clearSceneOnNextRender_;
|
||||
bool filterPolygonsOnNextRender_;
|
||||
int gainCompensationOnNextRender_;
|
||||
bool bilateralFilteringOnNextRender_;
|
||||
bool cameraJustInitialized_;
|
||||
int totalPoints_;
|
||||
int totalPolygons_;
|
||||
|
||||
@@ -32,6 +32,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
#include <android/log.h>
|
||||
#include <rtabmap/utilite/UEventsHandler.h>
|
||||
#include <rtabmap/utilite/ULogger.h>
|
||||
#include <rtabmap/core/CameraModel.h>
|
||||
#include <tango-gl/util.h>
|
||||
#include <pcl/point_cloud.h>
|
||||
#include <pcl/point_types.h>
|
||||
@@ -42,6 +43,7 @@ class LogHandler : public UEventsHandler
|
||||
public:
|
||||
LogHandler()
|
||||
{
|
||||
ULogger::setLevel(ULogger::kWarning);
|
||||
ULogger::setEventLevel(ULogger::kWarning);
|
||||
ULogger::setPrintThreadId(true);
|
||||
|
||||
@@ -142,9 +144,10 @@ struct Mesh
|
||||
std::vector<int> denseToOrganizedIndices; // should be set if cloud is dense, used for texturing
|
||||
unsigned int width; // width of the organized cloud
|
||||
unsigned int height; // height of the organized cloud
|
||||
rtabmap::Transform pose;
|
||||
rtabmap::Transform pose; // in rtabmap coordinates
|
||||
bool visible;
|
||||
cv::Mat texture;
|
||||
rtabmap::CameraModel cameraModel;
|
||||
};
|
||||
|
||||
#endif /* UTIL_H_ */
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
<item android:id="@+id/icp_refining" android:title="ICP Refining" />
|
||||
<item android:id="@+id/gain_compensation_fast" android:title="Adjust Colors (Fast)" />
|
||||
<item android:id="@+id/gain_compensation_full" android:title="Adjust Colors (Full)" />
|
||||
<item android:id="@+id/bilateral_filtering" android:title="Mesh Smoothing" />
|
||||
<item android:id="@+id/sba" android:title="Bundle Adjustement" />
|
||||
<item android:id="@+id/polygons_filtering" android:title="Noise Filtering" />
|
||||
</menu>
|
||||
|
||||
@@ -2,6 +2,8 @@ package com.introlab.rtabmap;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FilenameFilter;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Date;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.app.AlertDialog;
|
||||
@@ -106,6 +108,7 @@ public class RTABMapActivity extends Activity implements OnClickListener {
|
||||
private LinearLayout mLayoutDebug;
|
||||
|
||||
private int mTotalLoopClosures = 0;
|
||||
private boolean mMapIsEmpty = false;
|
||||
|
||||
private Toast mToast = null;
|
||||
|
||||
@@ -676,17 +679,18 @@ public class RTABMapActivity extends Activity implements OnClickListener {
|
||||
mPauseFirstTime = false;
|
||||
mToast.makeText(getActivity(), String.format("Tip: Try \"Post-Processing...\" to optimize even more the map!"), mToast.LENGTH_LONG).show();
|
||||
}
|
||||
mMapIsEmpty = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
RTABMapLib.setPausedMapping(false);
|
||||
((TextView)findViewById(R.id.status)).setText(mItemLocalizationMode.isChecked()?"Localization":mItemDataRecorderMode.isChecked()?"Recording":"Mapping");
|
||||
if(mItemDataRecorderMode.isChecked())
|
||||
{
|
||||
mToast.makeText(getActivity(), String.format("Data Recorder Mode: no map is created, only raw data is recorded."), mToast.LENGTH_LONG).show();
|
||||
}
|
||||
else
|
||||
else if(!mMapIsEmpty)
|
||||
{
|
||||
((TextView)findViewById(R.id.status)).setText(mItemLocalizationMode.isChecked()?"Localization":"Mapping");
|
||||
mToast.makeText(getActivity(), String.format("On resume, a new map is created. Tip: Try relocalizing in the previous area."), mToast.LENGTH_LONG).show();
|
||||
}
|
||||
}
|
||||
@@ -705,7 +709,7 @@ public class RTABMapActivity extends Activity implements OnClickListener {
|
||||
if(loopDetected >= 0)
|
||||
{
|
||||
mTotalLoopClosures+=loopDetected;
|
||||
mProgressDialog.setMessage(String.format("Optimization done! Adjusting colors..."));
|
||||
mProgressDialog.setMessage(String.format("Optimization done! Increasing visual appeal..."));
|
||||
}
|
||||
else if(loopDetected < 0)
|
||||
{
|
||||
@@ -817,6 +821,13 @@ public class RTABMapActivity extends Activity implements OnClickListener {
|
||||
mProgressDialog.show();
|
||||
RTABMapLib.postProcessing(6);
|
||||
}
|
||||
else if (itemId == R.id.bilateral_filtering)
|
||||
{
|
||||
mProgressDialog.setTitle("Post-Processing");
|
||||
mProgressDialog.setMessage(String.format("Mesh smoothing..."));
|
||||
mProgressDialog.show();
|
||||
RTABMapLib.postProcessing(7);
|
||||
}
|
||||
else if (itemId == R.id.sba)
|
||||
{
|
||||
mProgressDialog.setTitle("Post-Processing");
|
||||
@@ -1094,7 +1105,11 @@ public class RTABMapActivity extends Activity implements OnClickListener {
|
||||
AlertDialog.Builder builder = new AlertDialog.Builder(this);
|
||||
builder.setTitle("RTAB-Map Database Name (*.db):");
|
||||
final EditText input = new EditText(this);
|
||||
input.setInputType(InputType.TYPE_CLASS_TEXT);
|
||||
input.setInputType(InputType.TYPE_CLASS_TEXT);
|
||||
String timeStamp = new SimpleDateFormat("yyMMdd-hhmmss").format(new Date());
|
||||
input.setText(timeStamp);
|
||||
input.setSelectAllOnFocus(true);
|
||||
input.selectAll();
|
||||
builder.setView(input);
|
||||
builder.setPositiveButton("OK", new DialogInterface.OnClickListener() {
|
||||
@Override
|
||||
@@ -1195,6 +1210,7 @@ public class RTABMapActivity extends Activity implements OnClickListener {
|
||||
mOpenedDatabasePath = "";
|
||||
RTABMapLib.openDatabase(mTempDatabasePath);
|
||||
}
|
||||
mMapIsEmpty = true;
|
||||
}
|
||||
else if(itemId == R.id.data_recorder)
|
||||
{
|
||||
@@ -1258,6 +1274,10 @@ public class RTABMapActivity extends Activity implements OnClickListener {
|
||||
final EditText input = new EditText(this);
|
||||
input.setInputType(InputType.TYPE_CLASS_TEXT);
|
||||
builder.setView(input);
|
||||
String timeStamp = new SimpleDateFormat("yyMMdd-hhmmss").format(new Date());
|
||||
input.setText(timeStamp);
|
||||
input.setSelectAllOnFocus(true);
|
||||
input.selectAll();
|
||||
builder.setPositiveButton("OK", new DialogInterface.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog, int which)
|
||||
@@ -1377,9 +1397,16 @@ public class RTABMapActivity extends Activity implements OnClickListener {
|
||||
final String[] files = loadFileList(mWorkingDirectory);
|
||||
if(files.length > 0)
|
||||
{
|
||||
String[] filesWithSize = files;
|
||||
for(int i = 0; i<filesWithSize.length; ++i)
|
||||
{
|
||||
File filePath = new File(mWorkingDirectory+files[i]);
|
||||
long mb = filePath.length()/(1024*1024);
|
||||
filesWithSize[i] += " ("+mb+" MB)";
|
||||
}
|
||||
AlertDialog.Builder builder = new AlertDialog.Builder(this);
|
||||
builder.setTitle("Choose your file");
|
||||
builder.setItems(files, new DialogInterface.OnClickListener() {
|
||||
builder.setItems(filesWithSize, new DialogInterface.OnClickListener() {
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
mOpenedDatabasePath = mWorkingDirectory + files[which];
|
||||
|
||||
|
||||
@@ -100,6 +100,11 @@ std::vector<int> RTABMAP_EXP filterNotUsedVerticesFromMesh(
|
||||
const std::vector<pcl::Vertices> & polygons,
|
||||
pcl::PointCloud<pcl::PointXYZRGB> & outputCloud,
|
||||
std::vector<pcl::Vertices> & outputPolygons);
|
||||
std::vector<int> RTABMAP_EXP filterNaNPointsFromMesh(
|
||||
const pcl::PointCloud<pcl::PointXYZRGB> & cloud,
|
||||
const std::vector<pcl::Vertices> & polygons,
|
||||
pcl::PointCloud<pcl::PointXYZRGB> & outputCloud,
|
||||
std::vector<pcl::Vertices> & outputPolygons);
|
||||
|
||||
std::vector<pcl::Vertices> RTABMAP_EXP filterCloseVerticesFromMesh(
|
||||
const pcl::PointCloud<pcl::PointXYZRGBNormal>::Ptr cloud,
|
||||
|
||||
@@ -1845,7 +1845,9 @@ cv::Mat fastBilateralFiltering(const cv::Mat & depth, float sigmaS, float sigmaR
|
||||
v = 0.0f;
|
||||
}
|
||||
if(depth.type()==CV_32FC1)
|
||||
{
|
||||
output.at<float>(y,x) = v;
|
||||
}
|
||||
else
|
||||
{
|
||||
v*=1000.0f;
|
||||
|
||||
@@ -346,6 +346,49 @@ std::vector<int> filterNotUsedVerticesFromMesh(
|
||||
return output;
|
||||
}
|
||||
|
||||
std::vector<int> filterNaNPointsFromMesh(
|
||||
const pcl::PointCloud<pcl::PointXYZRGB> & cloud,
|
||||
const std::vector<pcl::Vertices> & polygons,
|
||||
pcl::PointCloud<pcl::PointXYZRGB> & outputCloud,
|
||||
std::vector<pcl::Vertices> & outputPolygons)
|
||||
{
|
||||
UDEBUG("size=%d polygons=%d", (int)cloud.size(), (int)polygons.size());
|
||||
std::map<int, int> addedVertices; //<oldIndex, newIndex>
|
||||
std::vector<int> output; //<oldIndex>
|
||||
output.resize(cloud.size());
|
||||
outputCloud.resize(cloud.size());
|
||||
outputCloud.is_dense = true;
|
||||
std::vector<int> organizedToDense(cloud.size(), -1);
|
||||
|
||||
int oi = 0;
|
||||
for(unsigned int i=0; i<cloud.size(); ++i)
|
||||
{
|
||||
if(pcl::isFinite(cloud.at(i)))
|
||||
{
|
||||
outputCloud.at(oi) = cloud.at(i);
|
||||
output[oi] = i;
|
||||
organizedToDense[i] = oi;
|
||||
++oi;
|
||||
}
|
||||
}
|
||||
outputCloud.resize(oi);
|
||||
output.resize(oi);
|
||||
|
||||
// remap polygons to dense cloud
|
||||
outputPolygons = polygons;
|
||||
for(unsigned int i=0; i<outputPolygons.size(); ++i)
|
||||
{
|
||||
pcl::Vertices & v = outputPolygons[i];
|
||||
for(unsigned int j=0; j<v.vertices.size(); ++j)
|
||||
{
|
||||
UASSERT(organizedToDense[v.vertices[j]] >= 0);
|
||||
v.vertices[j] = organizedToDense[v.vertices[j]];
|
||||
}
|
||||
}
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
std::vector<pcl::Vertices> filterCloseVerticesFromMesh(
|
||||
const pcl::PointCloud<pcl::PointXYZRGBNormal>::Ptr cloud,
|
||||
const std::vector<pcl::Vertices> & polygons,
|
||||
|
||||
Reference in New Issue
Block a user