41 #include <pcl/filters/filter.h>
42 #include <pcl/io/ply_io.h>
43 #include <pcl/io/obj_io.h>
44 #include <pcl/common/common.h>
45 #include <pcl/surface/poisson.h>
61 "rtabmap-export [options] database.db\n"
62 " New version: we now have to explicitly set what we want to export to give a more fine-grained control.\n"
63 " At least one of these options is required:\n"
64 " --cloud (old version used this by default)\n"
75 " --output \"\" Output name (default: name of the database is used).\n"
76 " --output_dir \"\" Output directory (default: same directory than the database).\n"
77 " --ascii Export PLY in ascii format.\n"
78 " --las Export cloud in LAS instead of PLY (PDAL or libLAS dependency required).\n"
79 " --cloud Export assembled cloud.\n"
80 " --mesh Export assembled mesh.\n"
81 " --texture Texture the mesh. Used with --mesh option.\n"
82 " --texture_size # Texture size 1024, 2048, 4096, 8192, 16384 (default 8192).\n"
83 " --texture_count # Maximum textures generated (default 1). Ignored by --multiband option (adjust --multiband_contrib instead).\n"
84 " --texture_range # Maximum camera range for texturing a polygon (default 0 meters: no limit).\n"
85 " --texture_angle # Maximum camera angle for texturing a polygon (default 0 deg: no limit).\n"
86 " --texture_depth_error # Maximum depth error between reprojected mesh and depth image to texture a face\n"
87 " (-1=disabled, 0=edge length is used, default=0).\n"
88 " --texture_color_policy # How vertices are colored when texturing: \n"
89 " 0=colorless (standard OBJ), \n"
90 " 1=keep color only on textureless polygons, \n"
91 " 2=keep all color.\n"
92 " --texture_roi_ratios \"# # # #\" Region of interest from images to texture or to color scans. Format\n"
93 " is \"left right top bottom\" (e.g. \"0 0 0 0.1\" means 10%%\n"
94 " of the image bottom not used).\n"
95 " --texture_d2c Distance to camera policy.\n"
96 " --texture_blur # Motion blur threshold (default 0: disabled). Below this threshold, the image is\n"
97 " considered blurred. 0 means disabled. 50 can be good default.\n"
98 " --cam_projection Camera projection on assembled cloud and export node ID on each point (in PointSourceId field).\n"
99 " --cam_projection_keep_all Keep not colored points from cameras (node ID will be 0 and color will be red).\n"
100 " --cam_projection_decimation Decimate images before projecting the points.\n"
101 " --cam_projection_mask \"\" File path for a mask. Format should be 8-bits grayscale. The mask should\n"
102 " cover all cameras in case multi-camera is used and have the same resolution.\n"
103 " --opt # Optimization approach:\n"
104 " 0=Full Global Optimization (default)\n"
105 " 1=Iterative Global Optimization\n"
106 " 2=Use optimized poses already computed in the database instead\n"
107 " of re-computing them (fallback to default if optimized poses don't exist).\n"
108 " 3=No optimization, use odometry poses directly.\n"
109 " --poses Export optimized poses of the robot frame (e.g., base_link).\n"
110 " --poses_camera Export optimized poses of the camera frame (e.g., optical frame).\n"
111 " --poses_scan Export optimized poses of the scan frame.\n"
112 " --poses_gt Export ground truth poses of the robot frame (e.g., base_link).\n"
113 " --poses_gps Export GPS poses of the GPS frame in local coordinates.\n"
114 " --poses_format # Format used for exported poses (default is 11):\n"
115 " 0=Raw 3x4 transformation matrix (r11 r12 r13 tx r21 r22 r23 ty r31 r32 r33 tz)\n"
116 " 1=RGBD-SLAM (in motion capture coordinate frame)\n"
117 " 2=KITTI (same as raw but in optical frame)\n"
120 " 10=RGBD-SLAM in ROS coordinate frame (stamp x y z qx qy qz qw)\n"
121 " 11=RGBD-SLAM in ROS coordinate frame + ID (stamp x y z qx qy qz qw id)\n"
122 " --gps # Export GPS values of the GPS frame in world coordinates. Formats:\n"
123 " 0=Raw (stamp longitude latitude altitude error bearing)\n"
124 " 1=KML (Google Earth)\n"
125 " --images Export images with stamp as file name.\n"
126 " --images_id Export images with node id as file name.\n"
127 " --ba Do global bundle adjustment before assembling the clouds.\n"
128 " --gain # Gain compensation value (default 1, set 0 to disable).\n"
129 " --gain_gray Do gain estimation compensation on gray channel only (default RGB channels).\n"
130 " --no_blending Disable blending when texturing.\n"
131 " --no_clean Disable cleaning colorless polygons.\n"
132 " --min_cluster # When meshing, filter clusters of polygons with size less than this\n"
133 " threshold (default 200, -1 means keep only biggest contiguous surface).\n"
134 " --low_gain # Low brightness gain 0-100 (default 0).\n"
135 " --high_gain # High brightness gain 0-100 (default 10).\n"
136 " --multiband Enable multiband texturing (AliceVision dependency required). Used with --texture option.\n"
137 " --multiband_downscale # Downscaling reduce the texture quality but speed up the computation time (default 2).\n"
138 " --multiband_contrib \"# # # # \" Number of contributions per frequency band for the\n"
139 " multi-band blending, should be 4 values! (default \"1 5 10 0\").\n"
140 " --multiband_unwrap # Method to unwrap input mesh:\n"
141 " 0=basic (default, >600k faces, fast)\n"
142 " 1=ABF (<=300k faces, generate 1 atlas)\n"
143 " 2=LSCM (<=600k faces, optimize space).\n"
144 " --multiband_fillholes Fill Texture holes with plausible values.\n"
145 " --multiband_padding # Texture edge padding size in pixel (0-100) (default 5).\n"
146 " --multiband_scorethr # 0 to disable filtering based on threshold to relative best score (0.0-1.0). (default 0.1).\n"
147 " --multiband_anglethr # 0 to disable angle hard threshold filtering (0.0, 180.0) (default 90.0).\n"
148 " --multiband_forcevisible Triangle visibility is based on the union of vertices visibility.\n"
149 " --poisson_depth # Set Poisson depth for mesh reconstruction.\n"
150 " --poisson_size # Set target polygon size when computing Poisson's depth for mesh reconstruction (default 0.03 m).\n"
151 " --max_polygons # Maximum polygons when creating a mesh (default 300000, set 0 for no limit).\n"
152 " --min_range # Minimum range of the created clouds (default 0 m).\n"
153 " --max_range # Maximum range of the created clouds (default 4 m, 0 m with --scan).\n"
154 " --decimation # Depth image decimation before creating the clouds (default 4, 1 with --scan).\n"
155 " --voxel # Voxel size of the created clouds (default 0.01 m, 0 m with --scan).\n"
156 " --ground_normals_up # Flip ground normals up if close to -z axis (default 0, 0=disabled, value should be >0 and <1, typical 0.9).\n"
157 " --noise_radius # Noise filtering search radius (default 0, 0=disabled).\n"
158 " --noise_k # Noise filtering minimum neighbors in search radius (default 5, 0=disabled).\n"
159 " --prop_radius_factor # Proportional radius filter factor (default 0, 0=disabled). Start tuning from 0.01.\n"
160 " --prop_radius_scale # Proportional radius filter neighbor scale (default 2).\n"
161 " --random_samples # Number of output samples using a random filter (default 0, 0=disabled).\n"
162 " --color_radius # Radius used to colorize polygons (default 0.05 m, 0 m with --scan). Set 0 for nearest color.\n"
163 " --scan Use laser scan for the point cloud.\n"
164 " --save_in_db Save resulting assembled point cloud or mesh in the database.\n"
165 " --xmin # Minimum range on X axis to keep nodes to export.\n"
166 " --xmax # Maximum range on X axis to keep nodes to export.\n"
167 " --ymin # Minimum range on Y axis to keep nodes to export.\n"
168 " --ymax # Maximum range on Y axis to keep nodes to export.\n"
169 " --zmin # Minimum range on Z axis to keep nodes to export.\n"
170 " --zmax # Maximum range on Z axis to keep nodes to export.\n"
171 " --density_radius # Filter poses in a fixed radius (m) to keep only one to be exported in the assembled cloud.\n"
172 " --density_angle # Filter poses up to angle (deg) in the --density_radius.\n"
173 " --filter_ceiling # Filter points over a custom height (default 0 m, 0=disabled).\n"
174 " --filter_floor # Filter points below a custom height (default 0 m, 0=disabled).\n"
183 virtual bool callback(
const std::string & msg)
const
186 printf(
"%s\n",
msg.c_str());
203 bool exportCloud =
false;
204 bool exportMesh =
false;
205 bool texture =
false;
207 bool doGainCompensationRGB =
true;
209 bool doBlending =
true;
211 int minCluster = 200;
212 int poissonDepth = 0;
213 float poissonSize = 0.03;
214 int maxPolygons = 300000;
216 float minRange = 0.0f;
217 float maxRange = -1.0f;
218 float voxelSize = -1.0f;
219 float groundNormalsUp = 0.0f;
220 float noiseRadius = 0.0f;
221 int noiseMinNeighbors = 5;
222 float proportionalRadiusFactor = 0.0f;
223 float proportionalRadiusScale = 2.0f;
224 int randomSamples = 0;
225 int textureSize = 8192;
226 int textureCount = 1;
227 float textureRange = 0;
228 float textureAngle = 0;
229 float textureDepthError = 0;
230 std::vector<float> textureRoiRatios;
231 bool distanceToCamPolicy =
false;
232 int laplacianThr = 0;
233 bool multiband =
false;
234 int multibandDownScale = 2;
235 std::string multibandNbContrib =
"1 5 10 0";
236 int multibandUnwrap = 0;
237 bool multibandFillHoles =
false;
238 int multibandPadding = 5;
239 double multibandBestScoreThr = 0.1;
240 double multibandAngleHardthr = 90;
241 bool multibandForceVisible =
false;
242 float colorRadius = -1.0f;
243 int textureVertexColorPolicy = 0;
244 bool cloudFromScan =
false;
245 bool saveInDb =
false;
246 int lowBrightnessGain = 0;
247 int highBrightnessGain = 10;
248 bool camProjection =
false;
249 bool camProjectionKeepAll =
false;
250 int cameraProjDecimation = 1;
251 std::string cameraProjMask;
253 bool exportPosesCamera =
false;
254 bool exportPosesScan =
false;
255 bool exportPosesGt =
false;
256 bool exportPosesGps =
false;
257 int exportPosesFormat = 11;
259 bool exportImages =
false;
260 bool exportImagesId =
false;
261 int optimizationApproach = 0;
262 std::string outputName;
263 std::string outputDir;
265 float densityRadius = 0.0f;
266 float densityAngle = 0.0f;
267 float filter_ceiling = 0.0f;
268 float filter_floor = 0.0f;
269 for(
int i=1;
i<argc; ++
i)
271 if(std::strcmp(
argv[
i],
"--help") == 0)
275 else if(std::strcmp(
argv[
i],
"--output") == 0)
280 outputName =
argv[
i];
287 else if(std::strcmp(
argv[
i],
"--output_dir") == 0)
299 else if(std::strcmp(
argv[
i],
"--bin") == 0)
301 printf(
"No need to set --bin anymore, ply are now automatically exported in binary by default. Set --ascii to export as text.\n");
303 else if(std::strcmp(
argv[
i],
"--ascii") == 0)
307 else if(std::strcmp(
argv[
i],
"--las") == 0)
311 #elif defined(RTABMAP_LIBLAS)
312 printf(
"\"--las\" option cannot be used with libLAS because the cloud has normals, build RTAB-Map with PDAL support to export in las with normals. Will export in PLY...\n");
314 printf(
"\"--las\" option cannot be used because RTAB-Map is not built with PDAL support. Will export in PLY...\n");
318 else if(std::strcmp(
argv[
i],
"--cloud") == 0)
322 else if(std::strcmp(
argv[
i],
"--mesh") == 0)
326 else if(std::strcmp(
argv[
i],
"--texture") == 0)
330 else if(std::strcmp(
argv[
i],
"--texture_size") == 0)
343 else if(std::strcmp(
argv[
i],
"--texture_count") == 0)
355 else if(std::strcmp(
argv[
i],
"--texture_range") == 0)
367 else if(std::strcmp(
argv[
i],
"--texture_angle") == 0)
379 else if(std::strcmp(
argv[
i],
"--texture_depth_error") == 0)
391 else if(std::strcmp(
argv[
i],
"--texture_roi_ratios") == 0)
396 std::list<std::string> strValues =
uSplit(
argv[
i],
' ');
397 if(strValues.size() != 4)
399 printf(
"The number of values must be 4 (roi=\"%s\")\n",
argv[
i]);
404 std::vector<float> tmpValues(4);
406 for(std::list<std::string>::iterator jter = strValues.begin(); jter!=strValues.end(); ++jter)
412 if(tmpValues[0] >= 0 && tmpValues[0] < 1 && tmpValues[0] < 1.0
f-tmpValues[1] &&
413 tmpValues[1] >= 0 && tmpValues[1] < 1 && tmpValues[1] < 1.0
f-tmpValues[0] &&
414 tmpValues[2] >= 0 && tmpValues[2] < 1 && tmpValues[2] < 1.0
f-tmpValues[3] &&
415 tmpValues[3] >= 0 && tmpValues[3] < 1 && tmpValues[3] < 1.0
f-tmpValues[2])
417 textureRoiRatios = tmpValues;
421 printf(
"The roi ratios are not valid (roi=\"%s\")\n",
argv[
i]);
431 else if(std::strcmp(
argv[
i],
"--texture_d2c") == 0)
433 distanceToCamPolicy =
true;
435 else if(std::strcmp(
argv[
i],
"--texture_blur") == 0)
447 else if(std::strcmp(
argv[
i],
"--cam_projection") == 0)
449 camProjection =
true;
451 else if(std::strcmp(
argv[
i],
"--cam_projection_keep_all") == 0)
453 camProjectionKeepAll =
true;
455 else if(std::strcmp(
argv[
i],
"--cam_projection_decimation") == 0)
461 if(cameraProjDecimation<1)
463 printf(
"--cam_projection_decimation cannot be <1! value=\"%s\"\n",
argv[
i]);
472 else if(std::strcmp(
argv[
i],
"--cam_projection_mask") == 0)
477 cameraProjMask =
argv[
i];
480 printf(
"--cam_projection_mask is set with a file not existing or don't have permissions to open it. Path=\"%s\"\n",
argv[
i]);
489 else if(std::strcmp(
argv[
i],
"--poses") == 0)
493 else if(std::strcmp(
argv[
i],
"--poses_camera") == 0)
495 exportPosesCamera =
true;
497 else if(std::strcmp(
argv[
i],
"--poses_scan") == 0)
499 exportPosesScan =
true;
501 else if(std::strcmp(
argv[
i],
"--poses_gt") == 0)
503 exportPosesGt =
true;
505 else if(std::strcmp(
argv[
i],
"--poses_gps") == 0)
507 exportPosesGps =
true;
509 else if(std::strcmp(
argv[
i],
"--poses_format") == 0)
521 else if(std::strcmp(
argv[
i],
"--gps") == 0)
527 if(exportGps<0 || exportGps>1)
529 printf(
"Wrong GPS format (%d), should be 0 or 1\n", exportGps);
538 else if(std::strcmp(
argv[
i],
"--opt") == 0)
544 if(optimizationApproach<0 || optimizationApproach >3)
546 printf(
"Invalid --opt (%d)\n", optimizationApproach);
555 else if(std::strcmp(
argv[
i],
"--images") == 0)
559 else if(std::strcmp(
argv[
i],
"--images_id") == 0)
562 exportImagesId =
true;
564 else if(std::strcmp(
argv[
i],
"--ba") == 0)
568 else if(std::strcmp(
argv[
i],
"--gain_gray") == 0)
570 doGainCompensationRGB =
false;
572 else if(std::strcmp(
argv[
i],
"--gain") == 0)
585 else if(std::strcmp(
argv[
i],
"--no_blending") == 0)
589 else if(std::strcmp(
argv[
i],
"--no_clean") == 0)
593 else if(std::strcmp(
argv[
i],
"--min_cluster") == 0)
605 else if(std::strcmp(
argv[
i],
"--multiband") == 0)
607 #ifdef RTABMAP_ALICE_VISION
610 printf(
"\"--multiband\" option cannot be used because RTAB-Map is not built with AliceVision support. Ignoring multiband...\n");
613 else if(std::strcmp(
argv[
i],
"--multiband_fillholes") == 0)
615 multibandFillHoles =
true;
617 else if(std::strcmp(
argv[
i],
"--multiband_downscale") == 0)
629 else if(std::strcmp(
argv[
i],
"--multiband_contrib") == 0)
636 printf(
"--multiband_contrib has wrong format! value=\"%s\"\n",
argv[
i]);
639 multibandNbContrib =
argv[
i];
646 else if(std::strcmp(
argv[
i],
"--multiband_unwrap") == 0)
658 else if(std::strcmp(
argv[
i],
"--multiband_padding") == 0)
670 else if(std::strcmp(
argv[
i],
"--multiband_forcevisible") == 0)
672 multibandForceVisible =
true;
674 else if(std::strcmp(
argv[
i],
"--multiband_scorethr") == 0)
686 else if(std::strcmp(
argv[
i],
"--multiband_anglethr") == 0)
698 else if(std::strcmp(
argv[
i],
"--poisson_depth") == 0)
710 else if(std::strcmp(
argv[
i],
"--poisson_size") == 0)
722 else if(std::strcmp(
argv[
i],
"--max_polygons") == 0)
734 else if(std::strcmp(
argv[
i],
"--min_range") == 0)
746 else if(std::strcmp(
argv[
i],
"--max_range") == 0)
758 else if(std::strcmp(
argv[
i],
"--decimation") == 0)
770 else if(std::strcmp(
argv[
i],
"--voxel") == 0)
782 else if(std::strcmp(
argv[
i],
"--ground_normals_up") == 0)
794 else if(std::strcmp(
argv[
i],
"--noise_radius") == 0)
806 else if(std::strcmp(
argv[
i],
"--noise_k") == 0)
818 else if(std::strcmp(
argv[
i],
"--prop_radius_factor") == 0)
830 else if(std::strcmp(
argv[
i],
"--prop_radius_scale") == 0)
836 UASSERT_MSG(proportionalRadiusScale>=1.0
f,
"--prop_radius_scale should be >= 1.0");
843 else if(std::strcmp(
argv[
i],
"--random_samples") == 0)
855 else if(std::strcmp(
argv[
i],
"--color_radius") == 0)
867 else if(std::strcmp(
argv[
i],
"--texture_color_policy") == 0)
879 else if(std::strcmp(
argv[
i],
"--scan") == 0)
881 cloudFromScan =
true;
883 else if(std::strcmp(
argv[
i],
"--save_in_db") == 0)
887 else if(std::strcmp(
argv[
i],
"--low_gain") == 0)
899 else if(std::strcmp(
argv[
i],
"--high_gain") == 0)
911 else if(std::strcmp(
argv[
i],
"--xmin") == 0)
923 else if(std::strcmp(
argv[
i],
"--xmax") == 0)
935 else if(std::strcmp(
argv[
i],
"--ymin") == 0)
947 else if(std::strcmp(
argv[
i],
"--ymax") == 0)
959 else if(std::strcmp(
argv[
i],
"--zmin") == 0)
971 else if(std::strcmp(
argv[
i],
"--zmax") == 0)
983 else if(std::strcmp(
argv[
i],
"--density_radius") == 0)
996 else if(std::strcmp(
argv[
i],
"--density_angle") == 0)
1009 else if(std::strcmp(
argv[
i],
"--filter_ceiling") == 0)
1015 if(filter_floor!=0.0
f && filter_ceiling != 0.0
f && filter_ceiling<filter_floor)
1017 printf(
"Option --filter_ceiling (%f) should be higher than --filter_floor option (%f)!\n", filter_ceiling, filter_floor);
1026 else if(std::strcmp(
argv[
i],
"--filter_floor") == 0)
1032 if(filter_floor!=0.0
f && filter_ceiling != 0.0
f && filter_ceiling<filter_floor)
1034 printf(
"Option --filter_ceiling (%f) should be higher than --filter_floor option (%f)!\n", filter_ceiling, filter_floor);
1047 decimation = cloudFromScan?1:4;
1051 maxRange = cloudFromScan?0:4;
1053 if(voxelSize < 0.0
f)
1055 voxelSize = cloudFromScan?0:0.01f;
1057 if(colorRadius < 0.0
f)
1059 colorRadius = cloudFromScan?0:0.05f;
1066 printf(
"Option --multiband is not supported with --save_in_db option, disabling multiband...\n");
1071 printf(
"Option --texture_count > 1 is not supported with --save_in_db option, setting texture_count to 1...\n");
1081 exportPosesCamera ||
1087 printf(
"Launching the tool without any required option(s) is deprecated. We will add --cloud to keep compatibilty with old behavior.\n");
1091 if(texture && !exportMesh)
1093 printf(
"To use --texture option, --mesh should be also enabled. Enabling --mesh.\n");
1099 std::string dbPath =
argv[argc-1];
1103 UERROR(
"File \"%s\" doesn't exist!", dbPath.c_str());
1117 UERROR(
"Cannot open database %s!", dbPath.c_str());
1125 printf(
"Added custom parameter %s=%s\n",
iter->first.c_str(),
iter->second.c_str());
1130 printf(
"Opening database \"%s\"...\n", dbPath.c_str());
1133 if(!dbDriver->openConnection(dbPath))
1135 printf(
"Failed to open database \"%s\"!\n", dbPath.c_str());
1138 printf(
"Opening database \"%s\"... done (%fs).\n", dbPath.c_str(),
timer.ticks());
1140 std::map<int, Transform> optimizedPoses;
1141 std::map<int, Transform> odomPoses;
1142 std::multimap<int, Link> links;
1143 dbDriver->getAllOdomPoses(odomPoses,
true);
1144 dbDriver->getAllLinks(links,
true,
true);
1145 if(optimizationApproach == 3 || !(exportCloud || exportMesh ||
exportPoses || exportPosesCamera || exportPosesScan))
1148 optimizedPoses = odomPoses;
1149 if(optimizationApproach == 3)
1151 printf(
"Loaded %d odometry poses from database.\n", (
int)odomPoses.size());
1156 if(optimizationApproach == 2)
1158 printf(
"Loading optimized poses from database...\n");
1159 optimizedPoses = dbDriver->loadOptimizedPoses();
1160 if(optimizedPoses.empty())
1162 printf(
"The are no saved optimized poses in the database, we will do full global optimization instead.\n");
1163 optimizationApproach = 0;
1167 printf(
"Loading optimized poses from database... done (%d optimized poses loaded).\n", (
int)optimizedPoses.size());
1170 if(optimizationApproach <= 1)
1172 std::string optimizationApproachStr = optimizationApproach==1?
"Iterative global optimization":
"Full global optimization";
1173 printf(
"Optimizing the map (%s)...\n", optimizationApproachStr.c_str());
1174 if(odomPoses.empty())
1176 printf(
"The are no odometry poses!? Aborting...\n");
1180 std::map<int, Transform> posesOut;
1181 std::multimap<int, Link> linksOut;
1182 UASSERT(odomPoses.lower_bound(1) != odomPoses.end());
1186 double markerPriorsLinearVariance = Parameters::defaultMarkerPriorsVarianceLinear();
1187 double markerPriorsAngularVariance = Parameters::defaultMarkerPriorsVarianceAngular();
1188 std::map<int, Transform> markerPriors;
1189 Parameters::parse(parameters, Parameters::kMarkerPriorsVarianceLinear(), markerPriorsLinearVariance);
1190 UASSERT(markerPriorsLinearVariance>0.0
f);
1191 Parameters::parse(parameters, Parameters::kMarkerPriorsVarianceAngular(), markerPriorsAngularVariance);
1192 UASSERT(markerPriorsAngularVariance>0.0
f);
1193 std::string markerPriorsStr;
1196 std::list<std::string> strList =
uSplit(markerPriorsStr,
'|');
1197 for(std::list<std::string>::iterator
iter=strList.begin();
iter!=strList.end(); ++
iter)
1199 std::string markerStr = *
iter;
1200 while(!markerStr.empty() && !
uIsDigit(markerStr[0]))
1202 markerStr.erase(markerStr.begin());
1204 if(!markerStr.empty())
1209 if(!
prior.isNull() &&
id>0)
1211 markerPriors.insert(std::make_pair(-
id,
prior));
1215 UERROR(
"Failed to parse element \"%s\" in parameter %s", markerStr.c_str(), Parameters::kMarkerPriors().c_str());
1218 else if(!
iter->empty())
1220 UERROR(
"Failed to parse parameter %s, value=\"%s\"", Parameters::kMarkerPriors().
c_str(),
iter->c_str());
1224 for(std::multimap<int, rtabmap::Link>::iterator
iter=links.begin();
iter!=links.end(); ++
iter)
1229 int markerId =
iter->second.to();
1230 if(odomPoses.find(
iter->second.from()) != odomPoses.end() && odomPoses.find(markerId) == odomPoses.end())
1232 odomPoses.insert(std::make_pair(markerId, odomPoses.at(
iter->second.from())*
iter->second.transform()));
1234 if(markerPriors.find(markerId) != markerPriors.end())
1236 cv::Mat infMatrix = cv::Mat::eye(6, 6, CV_64FC1);
1237 infMatrix(cv::Range(0,3), cv::Range(0,3)) /= markerPriorsLinearVariance;
1238 infMatrix(cv::Range(3,6), cv::Range(3,6)) /= markerPriorsAngularVariance;
1239 links.insert(std::make_pair(markerId,
Link(markerId, markerId,
Link::kPosePrior, markerPriors.at(markerId), infMatrix)));
1240 printf(
"Added prior %d : %s (variance: lin=%f ang=%f)\n", markerId, markerPriors.at(markerId).prettyPrint().c_str(),
1241 markerPriorsLinearVariance, markerPriorsAngularVariance);
1248 optimizer->getConnectedGraph(odomPoses.lower_bound(1)->first, odomPoses, links, posesOut, linksOut);
1249 if(optimizationApproach == 1)
1251 std::list<std::map<int, Transform> > intermediateGraphes;
1252 optimizedPoses = optimizer->optimize(odomPoses.lower_bound(1)->first, posesOut, linksOut, &intermediateGraphes);
1256 optimizedPoses = optimizer->optimize(odomPoses.lower_bound(1)->first, posesOut, linksOut);
1258 printf(
"Optimizing the map (%s)... done (%fs, poses=%d, links=%d).\n", optimizationApproachStr.c_str(),
timer.ticks(), (
int)optimizedPoses.size(), (
int)linksOut.size());
1261 if(optimizedPoses.empty())
1263 printf(
"The optimized graph is empty!? Aborting...\n");
1269 cv::Vec3f minP,maxP;
1271 printf(
"Filtering poses (range: x=%.1f<->%.1f, y=%.1f<->%.1f, z=%.1f<->%.1f, map size=%.1f x %.1f x %.1f, map min/max: [%.1f, %.1f, %.1f] [%.1f, %.1f, %.1f])...\n",
1273 maxP[0]-minP[0],maxP[1]-minP[1],maxP[2]-minP[2],
1274 minP[0],minP[1],minP[2],maxP[0],maxP[1],maxP[2]);
1275 std::map<int, Transform> posesFiltered;
1276 for(std::map<int, Transform>::const_iterator
iter=optimizedPoses.begin();
iter!=optimizedPoses.end(); ++
iter)
1278 bool ignore =
false;
1293 posesFiltered.insert(*
iter);
1297 printf(
"Filtering poses... done! %d/%d remaining.\n", (
int)posesFiltered.size(), (
int)optimizedPoses.size());
1298 optimizedPoses = posesFiltered;
1299 if(optimizedPoses.empty())
1301 printf(
"All poses filtered! Exiting.\n");
1308 printf(
"Global bundle adjustment...\n");
1310 UASSERT(optimizedPoses.lower_bound(1) != optimizedPoses.end());
1313 for(std::map<int, Transform>::iterator
iter=optimizedPoses.lower_bound(1);
iter!=optimizedPoses.end(); ++
iter)
1315 ids.push_back(
iter->first);
1317 std::list<Signature *> signatures;
1318 dbDriver->loadSignatures(ids, signatures);
1319 std::map<int, Signature>
nodes;
1320 for(std::list<Signature *>::iterator
iter=signatures.begin();
iter!=signatures.end(); ++
iter)
1322 nodes.insert(std::make_pair((*iter)->id(), *(*
iter)));
1324 optimizedPoses = ((
Optimizer*)&
g2o)->optimizeBA(optimizedPoses.lower_bound(1)->first, optimizedPoses, links,
nodes,
true);
1325 printf(
"Global bundle adjustment... done (%fs).\n",
timer.ticks());
1329 std::string outputDirectory = outputDir.empty()?
UDirectory::getDir(dbPath):outputDir;
1334 std::string baseName = outputName.empty()?
uSplit(
UFile::getName(dbPath),
'.').front():outputName;
1337 if(exportCloud || exportMesh)
1339 printf(
"Create and assemble the clouds...\n");
1341 else if(exportImages || exportImagesId)
1343 printf(
"Export images...\n");
1345 pcl::PointCloud<pcl::PointXYZRGB>::Ptr assembledCloud(
new pcl::PointCloud<pcl::PointXYZRGB>);
1346 pcl::PointCloud<pcl::PointXYZI>::Ptr assembledCloudI(
new pcl::PointCloud<pcl::PointXYZI>);
1347 std::map<int, rtabmap::Transform> robotPoses;
1348 std::vector<std::map<int, rtabmap::Transform> >
cameraPoses;
1349 std::map<int, rtabmap::Transform> scanPoses;
1350 std::map<int, rtabmap::Transform> gtPoses;
1351 std::map<int, rtabmap::Transform> gpsPoses;
1352 std::map<int, double> gpsStamps;
1354 std::map<int, rtabmap::GPS> gpsValues;
1355 std::map<int, double> cameraStamps;
1356 std::map<int, std::vector<rtabmap::CameraModel> > cameraModels;
1357 std::map<int, cv::Mat> cameraDepths;
1358 int imagesExported = 0;
1359 std::vector<int> rawViewpointIndices;
1360 std::map<int, Transform> rawViewpoints;
1361 std::map<int, Transform> densityPoses;
1362 if(densityRadius && (exportCloud || exportMesh))
1365 printf(
"Keeping %d/%d poses after density filtering (--density_radius = %f --density_angle = %f).\n",
1366 (
int)densityPoses.size(),
1367 (
int)optimizedPoses.size(),
1371 int processedNodes = 0;
1372 int lastPercent = 0;
1373 for(std::map<int, Transform>::iterator
iter=optimizedPoses.begin();
iter!=optimizedPoses.end(); ++
iter)
1378 robotPoses.insert(*
iter);
1379 cameraStamps.insert(std::make_pair(
iter->first, 0));
1387 std::vector<float>
v;
1391 dbDriver->getNodeInfo(
iter->first,
p,
m, weight, l, stamp, gt,
v, gps,
s);
1394 bool loadImages = ((exportCloud || exportMesh) && (!cloudFromScan || texture || camProjection)) || exportImages;
1395 bool loadScan = ((exportCloud || exportMesh) && cloudFromScan) || exportPosesScan;
1398 dbDriver->getNodeData(
1408 std::vector<CameraModel> models;
1409 std::vector<StereoCameraModel> stereoModels;
1410 if(loadImages || exportPosesCamera)
1412 dbDriver->getCalibration(
iter->first, models, stereoModels);
1415 if(exportCloud || exportMesh || exportImages)
1417 bool densityFiltered = !densityPoses.
empty() && densityPoses.find(
iter->first) == densityPoses.end();
1420 pcl::IndicesPtr
indices(
new std::vector<int>);
1421 pcl::PointCloud<pcl::PointXYZRGB>::Ptr cloud;
1422 pcl::PointCloud<pcl::PointXYZI>::Ptr cloudI;
1425 if(!densityFiltered && cloudFromScan && (exportCloud || exportMesh))
1428 data.uncompressData(exportImages?&rgb:0, (texture||exportImages)&&!
data.depthOrRightCompressed().empty()?&depth:0, &scan);
1431 printf(
"Node %d doesn't have scan data, empty cloud is created.\n",
iter->first);
1433 if(decimation>1 || minRange>0.0
f || maxRange)
1440 if(noiseRadius>0.0
f && noiseMinNeighbors>0)
1448 if(noiseRadius>0.0
f && noiseMinNeighbors>0)
1456 data.uncompressData(&rgb, &depth);
1457 if(!densityFiltered && (exportCloud || exportMesh))
1461 printf(
"Node %d doesn't have depth or stereo data, empty cloud is "
1462 "created (if you want to create point cloud from scan, use --scan option).\n",
iter->first);
1470 if(noiseRadius>0.0
f && noiseMinNeighbors>0)
1478 if(exportImages && !rgb.empty())
1480 std::string dirSuffix = (depth.type() != CV_16UC1 && depth.type() != CV_32FC1 && !depth.empty())?
"left":
"rgb";
1481 std::string dir = outputDirectory+
"/"+baseName+
"_"+dirSuffix;
1486 cv::imwrite(outputPath, rgb);
1491 cv::Mat depthExported = depth;
1492 if(depth.type() != CV_16UC1 && depth.type() != CV_32FC1)
1495 dir = outputDirectory+
"/"+baseName+
"_right";
1500 dir = outputDirectory+
"/"+baseName+
"_depth";
1501 if(depth.type() == CV_32FC1)
1511 cv::imwrite(outputPath, depthExported);
1515 for(
size_t i=0;
i<models.size(); ++
i)
1519 if(models.size() > 1) {
1522 model.setName(modelName);
1523 std::string dir = outputDirectory+
"/"+baseName+
"_calib";
1529 for(
size_t i=0;
i<stereoModels.size(); ++
i)
1533 if(stereoModels.size() > 1) {
1536 model.setName(modelName,
"left",
"right");
1537 std::string dir = outputDirectory+
"/"+baseName+
"_calib";
1545 if(exportCloud || exportMesh)
1549 if(cloud.get() && !cloud->empty())
1551 else if(cloudI.get() && !cloudI->empty())
1554 if(cloud.get() && !cloud->empty())
1556 else if(cloudI.get() && !cloudI->empty())
1559 if(filter_ceiling != 0.0 || filter_floor != 0.0
f)
1561 if(cloud.get() && !cloud->empty())
1565 if(cloudI.get() && !cloudI->empty())
1574 rawViewpoints.insert(std::make_pair(
iter->first, lidarViewpoint));
1576 else if(!models.empty() && !models[0].localTransform().isNull())
1578 Transform cameraViewpoint =
iter->second * models[0].localTransform();
1579 rawViewpoints.insert(std::make_pair(
iter->first, cameraViewpoint));
1581 else if(!stereoModels.empty() && !stereoModels[0].localTransform().isNull())
1583 Transform cameraViewpoint =
iter->second * stereoModels[0].localTransform();
1584 rawViewpoints.insert(std::make_pair(
iter->first, cameraViewpoint));
1588 rawViewpoints.insert(*
iter);
1591 if(cloud.get() && !cloud->empty())
1593 if(assembledCloud->empty())
1595 *assembledCloud = *cloud;
1599 *assembledCloud += *cloud;
1601 rawViewpointIndices.resize(assembledCloud->size(),
iter->first);
1603 else if(cloudI.get() && !cloudI->empty())
1605 if(assembledCloudI->empty())
1607 *assembledCloudI = *cloudI;
1611 *assembledCloudI += *cloudI;
1613 rawViewpointIndices.resize(assembledCloudI->size(),
iter->first);
1615 if(texture && !depth.empty() && (depth.type() == CV_16UC1 || depth.type() == CV_32FC1))
1617 cameraDepths.insert(std::make_pair(
iter->first, depth));
1624 for(
size_t i=0;
i<stereoModels.size(); ++
i)
1626 models.push_back(stereoModels[
i].left());
1630 robotPoses.insert(std::make_pair(
iter->first,
iter->second));
1631 cameraStamps.insert(std::make_pair(
iter->first, stamp));
1632 if(models.empty() && weight == -1 && !cameraModels.empty())
1635 models = cameraModels.rbegin()->second;
1639 if(!
data.imageCompressed().empty())
1641 cameraModels.insert(std::make_pair(
iter->first, models));
1643 if(exportPosesCamera)
1649 UASSERT_MSG(models.size() ==
cameraPoses.size(),
"Not all nodes have same number of cameras to export camera poses.");
1650 for(
size_t i=0;
i<models.size(); ++
i)
1656 if(exportPosesScan && !
data.laserScanCompressed().empty())
1658 scanPoses.insert(std::make_pair(
iter->first,
iter->second*
data.laserScanCompressed().localTransform()));
1661 if(exportPosesGps || exportGps>=0)
1663 if(gps.
stamp() > 0.0)
1667 cv::Point3f
p(0.0
f,0.0
f,0.0
f);
1668 if(!gpsPoses.empty())
1678 gpsPoses.insert(std::make_pair(
iter->first, pose));
1682 gpsValues.insert(std::make_pair(
iter->first, gps));
1684 gpsStamps.insert(std::make_pair(
iter->first, gps.
stamp()));
1688 if(exportPosesGt && !gt.
isNull())
1690 gtPoses.insert(std::make_pair(
iter->first, gt));
1693 if(optimizedPoses.size() >= 500)
1696 int percent = processedNodes*100/(
int)optimizedPoses.size();
1697 if(percent != lastPercent)
1699 printf(
"Processed %d/%d (%d%%) nodes...\n",
1701 (
int)optimizedPoses.size(),
1703 lastPercent = percent;
1707 if(exportCloud || exportMesh)
1709 printf(
"Create and assemble the clouds... done (%fs, %d points).\n",
timer.ticks(), !assembledCloud->empty()?(
int)assembledCloud->size():(
int)assembledCloudI->size());
1712 if(exportImages || exportImagesId)
1714 printf(
"%d images exported!\n", imagesExported);
1715 if(!(exportCloud || exportMesh ||
exportPoses || exportPosesCamera || exportPosesScan)) {
1732 cv::Vec3f vmin, vmax;
1734 printf(
"Saved %d poses to database! (min=[%f,%f,%f] max=[%f,%f,%f])\n",
1735 (
int)robotPoses.size(),
1736 vmin[0], vmin[1], vmin[2],
1737 vmax[0], vmax[1], vmax[2]);
1741 std::string posesExt = (exportPosesFormat==3?
"toro":exportPosesFormat==4?
"g2o":
"txt");
1744 std::string outputPath=outputDirectory+
"/"+baseName+
"_poses." + posesExt;
1745 if(robotPoses.begin() != robotPoses.lower_bound(1) && !(exportPosesFormat == 4 || exportPosesFormat == 11))
1747 printf(
"Note that landmarks won't be exported because --poses_format is not 4 or 11 (currently using %d).\n", exportPosesFormat);
1751 std::map<int, Transform>(robotPoses.lower_bound(1), robotPoses.end()),
1753 std::map<int, double>(cameraStamps.lower_bound(1), cameraStamps.end()));
1759 cv::Vec3f vmin, vmax;
1761 printf(
"%d poses exported to \"%s\". (min=[%f,%f,%f] max=[%f,%f,%f])\n",
1762 (
int)robotPoses.size(),
1764 vmin[0], vmin[1], vmin[2],
1765 vmax[0], vmax[1], vmax[2]);
1767 if(exportPosesCamera)
1771 std::string outputPath;
1773 outputPath = outputDirectory+
"/"+baseName+
"_camera_poses." + posesExt;
1775 outputPath = outputDirectory+
"/"+baseName+
"_camera_poses_"+
uNumber2Str((
int)
i)+
"." + posesExt;
1777 cv::Vec3f vmin, vmax;
1779 printf(
"%d camera poses exported to \"%s\". (min=[%f,%f,%f] max=[%f,%f,%f])\n",
1782 vmin[0], vmin[1], vmin[2],
1783 vmax[0], vmax[1], vmax[2]);
1788 std::string outputPath=outputDirectory+
"/"+baseName+
"_scan_poses." + posesExt;
1792 printf(
"%d scan poses exported to \"%s\". (min=[%f,%f,%f] max=[%f,%f,%f])\n",
1793 (
int)scanPoses.size(),
1800 std::string outputPath=outputDirectory+
"/"+baseName+
"_gps_poses." + posesExt;
1802 printf(
"%d GPS poses exported to \"%s\".\n",
1803 (
int)gpsPoses.size(),
1804 outputPath.c_str());
1808 std::string outputPath=outputDirectory+
"/"+baseName+
"_gt_poses." + posesExt;
1810 printf(
"%d scan poses exported to \"%s\".\n",
1811 (
int)gtPoses.size(),
1812 outputPath.c_str());
1816 std::string outputPath=outputDirectory+
"/"+baseName+
"_gps." + (exportGps==0?
"txt":
"kml");
1818 printf(
"%d GPS values exported to \"%s\".\n",
1819 (
int)gpsValues.size(),
1820 outputPath.c_str());
1824 if(!(exportCloud || exportMesh))
1830 if(!assembledCloud->empty() || !assembledCloudI->empty())
1832 if(proportionalRadiusFactor>0.0
f && proportionalRadiusScale>=1.0
f)
1834 printf(
"Proportional radius filtering of the assembled cloud... (factor=%f scale=%f, %d points)\n", proportionalRadiusFactor, proportionalRadiusScale, !assembledCloud->empty()?(
int)assembledCloud->size():(
int)assembledCloudI->size());
1836 if(!assembledCloud->empty())
1839 pcl::PointCloud<pcl::PointXYZRGB> tmp;
1840 pcl::copyPointCloud(*assembledCloud, *
indices, tmp);
1841 *assembledCloud = tmp;
1843 else if(!assembledCloudI->empty())
1846 pcl::PointCloud<pcl::PointXYZI> tmp;
1847 pcl::copyPointCloud(*assembledCloudI, *
indices, tmp);
1848 *assembledCloudI = tmp;
1852 std::vector<int> rawCameraIndicesTmp(
indices->size());
1853 for (std::size_t
i = 0;
i <
indices->size(); ++
i)
1854 rawCameraIndicesTmp[
i] = rawViewpointIndices[
indices->at(
i)];
1855 rawViewpointIndices = rawCameraIndicesTmp;
1857 printf(
"Proportional radius filtering of the assembled cloud.... done! (%fs, %d points)\n",
timer.ticks(), !assembledCloud->empty()?(
int)assembledCloud->size():(
int)assembledCloudI->size());
1860 pcl::PointCloud<pcl::PointXYZ>::Ptr rawAssembledCloud(
new pcl::PointCloud<pcl::PointXYZ>);
1863 printf(
"Voxel grid filtering of the assembled cloud... (voxel=%f, %d points)\n", voxelSize, !assembledCloud->empty()?(
int)assembledCloud->size():(
int)assembledCloudI->size());
1864 if(!assembledCloud->empty()) {
1865 pcl::copyPointCloud(*assembledCloud, *rawAssembledCloud);
1868 else if(!assembledCloudI->empty()) {
1869 pcl::copyPointCloud(*assembledCloudI, *rawAssembledCloud);
1872 printf(
"Voxel grid filtering of the assembled cloud.... done! (%fs, %d points)\n",
timer.ticks(), !assembledCloud->empty()?(
int)assembledCloud->size():(
int)assembledCloudI->size());
1875 printf(
"Computing normals of the assembled cloud... (k=20, %d points)\n", (
int)assembledCloud->size()?(
int)assembledCloud->size():(
int)assembledCloudI->size());
1876 pcl::PointCloud<pcl::PointXYZRGBNormal>::Ptr cloudToExport(
new pcl::PointCloud<pcl::PointXYZRGBNormal>);
1877 pcl::PointCloud<pcl::PointXYZINormal>::Ptr cloudIToExport(
new pcl::PointCloud<pcl::PointXYZINormal>);
1878 if(!assembledCloud->empty()) {
1880 UASSERT(assembledCloud->size() == normals->size());
1881 pcl::concatenateFields(*assembledCloud, *normals, *cloudToExport);
1883 else if(!assembledCloudI->empty()) {
1885 UASSERT(assembledCloudI->size() == normals->size());
1886 pcl::concatenateFields(*assembledCloudI, *normals, *cloudIToExport);
1888 printf(
"Computing normals of the assembled cloud... done! (%fs, %d points)\n",
timer.ticks(), !cloudToExport->empty()?(
int)cloudToExport->size():(
int)cloudIToExport->size());
1889 assembledCloud->clear();
1890 assembledCloudI->clear();
1893 printf(
"Adjust normals to viewpoints of the assembled cloud... (%d points)\n", !cloudToExport->empty()?(
int)cloudToExport->size():(
int)cloudIToExport->size());
1894 if(!rawAssembledCloud->empty()) {
1895 if(!cloudToExport->empty()) {
1899 rawViewpointIndices,
1903 else if(!cloudIToExport->empty()) {
1907 rawViewpointIndices,
1912 else if(!cloudToExport->empty()) {
1915 rawViewpointIndices,
1919 else if(!cloudIToExport->empty()) {
1922 rawViewpointIndices,
1926 printf(
"Adjust normals to viewpoints of the assembled cloud... (%fs, %d points)\n",
timer.ticks(), !cloudToExport->empty()?(
int)cloudToExport->size():(
int)cloudIToExport->size());
1930 printf(
"Random samples filtering of the assembled cloud... (samples=%d, %d points)\n", randomSamples, !cloudToExport->empty()?(
int)cloudToExport->size():(
int)cloudIToExport->size());
1931 if(!cloudToExport->empty())
1935 else if(!cloudIToExport->empty())
1939 printf(
"Random samples filtering of the assembled cloud.... done! (%fs, %d points)\n",
timer.ticks(), !cloudToExport->empty()?(
int)cloudToExport->size():(
int)cloudIToExport->size());
1942 std::vector<int> pointToCamId;
1943 std::vector<float> pointToCamIntensity;
1944 if(camProjection && !robotPoses.empty())
1946 printf(
"Camera projection... (cameras=%d)\n", (
int)cameraModels.size());
1947 std::map<int, std::vector<rtabmap::CameraModel> > cameraModelsProj;
1948 if(cameraProjDecimation>1)
1950 for(std::map<
int, std::vector<rtabmap::CameraModel> >::
iterator iter=cameraModels.begin();
1951 iter!=cameraModels.end();
1954 std::vector<rtabmap::CameraModel> models;
1955 for(
size_t i=0;
i<
iter->second.size(); ++
i)
1957 models.push_back(
iter->second[
i].scaled(1.0/
double(cameraProjDecimation)));
1959 cameraModelsProj.insert(std::make_pair(
iter->first, models));
1964 cameraModelsProj = cameraModels;
1969 printf(
"Camera projection... projecting cloud to individual cameras (--images option)\n");
1971 pcl::PCLPointCloud2::Ptr cloud2(
new pcl::PCLPointCloud2);
1972 if(!cloudToExport->empty())
1974 pcl::toPCLPointCloud2(*cloudToExport, *cloud2);
1976 else if(!cloudIToExport->empty())
1978 pcl::toPCLPointCloud2(*cloudIToExport, *cloud2);
1981 std::string dir = outputDirectory+
"/"+baseName+
"_depth_from_scan";
1986 for(std::map<
int, std::vector<rtabmap::CameraModel> >::
iterator iter=cameraModelsProj.begin();
1987 iter!=cameraModelsProj.end();
1990 cv::Mat depth(
iter->second.front().imageHeight(),
iter->second.front().imageWidth()*
iter->second.size(), CV_32FC1);
1991 for(
size_t i=0;
i<
iter->second.size(); ++
i)
1994 iter->second.at(
i).imageSize(),
1995 iter->second.at(
i).K(),
1997 robotPoses.at(
iter->first) *
iter->second.at(
i).localTransform());
1998 subDepth.copyTo(depth(
cv::Range::all(), cv::Range(
i*
iter->second.front().imageWidth(), (
i+1)*
iter->second.front().imageWidth())));
2003 cv::imwrite(outputPath, depth);
2008 if(!cameraProjMask.empty())
2010 projMask = cv::imread(cameraProjMask, cv::IMREAD_GRAYSCALE);
2011 if(cameraProjDecimation>1)
2013 cv::Mat
out = projMask;
2014 cv::resize(projMask,
out, cv::Size(), 1.0
f/
float(cameraProjDecimation), 1.0
f/
float(cameraProjDecimation), cv::INTER_NEAREST);
2019 printf(
"Camera projection... projecting cloud to all cameras\n");
2020 pointToCamId.resize(!cloudToExport->empty()?cloudToExport->size():cloudIToExport->size());
2021 std::vector<std::pair< std::pair<int, int>, pcl::PointXY> > pointToPixel;
2022 if(!cloudToExport->empty())
2032 distanceToCamPolicy,
2035 else if(!cloudIToExport->empty())
2045 distanceToCamPolicy,
2047 pointToCamIntensity.resize(pointToPixel.size());
2050 printf(
"Camera projection... coloring the cloud\n");
2052 UASSERT(pointToPixel.empty() || pointToPixel.size() == pointToCamId.size());
2053 pcl::PointCloud<pcl::PointXYZRGBNormal>::Ptr assembledCloudValidPoints(
new pcl::PointCloud<pcl::PointXYZRGBNormal>());
2054 assembledCloudValidPoints->resize(pointToCamId.size());
2057 int coloredPoints = 0;
2058 for(std::map<int, rtabmap::Transform>::iterator
iter=robotPoses.lower_bound(1);
iter!=robotPoses.end(); ++
iter)
2060 int nodeID =
iter->first;
2063 dbDriver->getNodeData(nodeID,
data,
true,
false,
false,
false);
2064 if(!
data.imageCompressed().empty())
2066 data.uncompressDataConst(&image, 0);
2070 if(cameraProjDecimation>1)
2075 UASSERT(cameraModelsProj.find(nodeID) != cameraModelsProj.end());
2076 int modelsSize = cameraModelsProj.at(nodeID).size();
2077 for(
size_t i=0;
i<pointToPixel.size(); ++
i)
2079 int cameraIndex = pointToPixel[
i].first.second;
2080 if(nodeID == pointToPixel[
i].
first.first && cameraIndex>=0)
2082 pcl::PointXYZRGBNormal pt;
2083 float intensity = 0;
2084 if(!cloudToExport->empty())
2086 pt = cloudToExport->at(
i);
2088 else if(!cloudIToExport->empty())
2090 pt.x = cloudIToExport->at(
i).x;
2091 pt.y = cloudIToExport->at(
i).y;
2092 pt.z = cloudIToExport->at(
i).z;
2093 pt.normal_x = cloudIToExport->at(
i).normal_x;
2094 pt.normal_y = cloudIToExport->at(
i).normal_y;
2095 pt.normal_z = cloudIToExport->at(
i).normal_z;
2096 intensity = cloudIToExport->at(
i).intensity;
2101 int subImageWidth = image.cols / modelsSize;
2102 cv::Mat subImage = image(
cv::Range::all(), cv::Range(cameraIndex*subImageWidth, (cameraIndex+1)*subImageWidth));
2104 int x = pointToPixel[
i].second.x * (
float)subImage.cols;
2105 int y = pointToPixel[
i].second.y * (
float)subImage.rows;
2109 if(subImage.type()==CV_8UC3)
2111 cv::Vec3b bgr = subImage.at<cv::Vec3b>(
y,
x);
2118 UASSERT(subImage.type()==CV_8UC1);
2119 pt.r = pt.g = pt.b = subImage.at<
unsigned char>(pointToPixel[
i].second.y * subImage.rows, pointToPixel[
i].second.x * subImage.cols);
2124 int exportedId = nodeID;
2125 pointToCamId[
i] = exportedId;
2126 if(!pointToCamIntensity.empty())
2128 pointToCamIntensity[
i] = intensity;
2130 assembledCloudValidPoints->at(
i) = pt;
2135 printf(
"Processed %d/%d images\n", imagesDone++, (
int)robotPoses.size());
2139 printf(
"Node %d doesn't have image! (%d/%d)\n",
iter->first, imagesDone++, (
int)robotPoses.size());
2142 printf(
"Colored %d/%d points.\n", coloredPoints, (
int)assembledCloudValidPoints->size());
2144 pcl::IndicesPtr validIndices(
new std::vector<int>(pointToPixel.size()));
2146 for(
size_t i=0;
i<pointToPixel.size(); ++
i)
2148 if(pointToPixel[
i].
first.first <=0)
2150 if(camProjectionKeepAll)
2152 pcl::PointXYZRGBNormal pt;
2153 float intensity = 0;
2154 if(!cloudToExport->empty())
2156 pt = cloudToExport->at(
i);
2158 else if(!cloudIToExport->empty())
2160 pt.x = cloudIToExport->at(
i).x;
2161 pt.y = cloudIToExport->at(
i).y;
2162 pt.z = cloudIToExport->at(
i).z;
2163 pt.normal_x = cloudIToExport->at(
i).normal_x;
2164 pt.normal_y = cloudIToExport->at(
i).normal_y;
2165 pt.normal_z = cloudIToExport->at(
i).normal_z;
2166 intensity = cloudIToExport->at(
i).intensity;
2169 pointToCamId[
i] = 0;
2173 if(!pointToCamIntensity.empty())
2175 pointToCamIntensity[
i] = intensity;
2177 assembledCloudValidPoints->at(
i) = pt;
2178 validIndices->at(oi++) =
i;
2183 validIndices->at(oi++) =
i;
2186 if(oi != validIndices->size())
2188 validIndices->resize(oi);
2189 assembledCloudValidPoints =
util3d::extractIndices(assembledCloudValidPoints, validIndices,
false,
false);
2190 std::vector<int> pointToCamIdTmp(validIndices->size());
2191 std::vector<float> pointToCamIntensityTmp(pointToCamIntensity.empty()?0:validIndices->size());
2192 for(
size_t i=0;
i<validIndices->size(); ++
i)
2194 size_t index = validIndices->at(
i);
2195 UASSERT(index < pointToCamId.size());
2196 pointToCamIdTmp[
i] = pointToCamId[index];
2197 if(!pointToCamIntensity.empty())
2199 UASSERT(index < pointToCamIntensity.size());
2200 pointToCamIntensityTmp[
i] = pointToCamIntensity[index];
2203 pointToCamId = pointToCamIdTmp;
2204 pointToCamIntensity = pointToCamIntensityTmp;
2205 pointToCamIdTmp.clear();
2206 pointToCamIntensityTmp.clear();
2208 cloudToExport = assembledCloudValidPoints;
2209 cloudIToExport->clear();
2211 printf(
"Camera projection... done! (%fs)\n",
timer.ticks());
2220 printf(
"Option --save_in_db is set and both --cloud and --mesh are set, we will only save the mesh in the database.\n");
2224 printf(
"Saving cloud in db... (%d points)\n", !cloudToExport->empty()?(
int)cloudToExport->size():(
int)cloudIToExport->size());
2225 if(!cloudToExport->empty())
2227 else if(!cloudIToExport->empty())
2229 printf(
"Saving cloud in db... done!\n");
2234 std::string ext = las?
"las":
"ply";
2235 std::string outputPath=outputDirectory+
"/"+baseName+
"_cloud."+ext;
2236 printf(
"Saving %s... (%d points)\n", outputPath.c_str(), !cloudToExport->empty()?(
int)cloudToExport->size():(
int)cloudIToExport->size());
2238 if(las || !pointToCamId.empty() || !pointToCamIntensity.empty())
2240 if(!cloudToExport->empty())
2242 if(!pointToCamIntensity.empty())
2251 else if(!cloudIToExport->empty())
2257 if(!pointToCamId.empty())
2259 if(!pointToCamIntensity.empty())
2261 printf(
"Option --cam_projection is enabled but rtabmap is not built "
2262 "with PDAL support, so camera IDs and lidar intensities won't be exported in the output cloud.\n");
2266 printf(
"Option --cam_projection is enabled but rtabmap is not built "
2267 "with PDAL support, so camera IDs won't be exported in the output cloud.\n");
2270 if(!cloudToExport->empty())
2271 pcl::io::savePLYFile(outputPath, *cloudToExport,
binary);
2272 else if(!cloudIToExport->empty())
2273 pcl::io::savePLYFile(outputPath, *cloudIToExport,
binary);
2275 printf(
"Saving %s... done!\n", outputPath.c_str());
2282 if(!cloudIToExport->empty())
2284 pcl::copyPointCloud(*cloudIToExport, *cloudToExport);
2285 cloudIToExport->clear();
2291 int optimizedDepth = 12;
2292 for(
int i=6;
i<12; ++
i)
2294 if(mapLength/
float(1<<
i) < poissonSize)
2302 optimizedDepth = poissonDepth;
2306 printf(
"Mesh reconstruction... depth=%d\n", optimizedDepth);
2307 pcl::PolygonMesh::Ptr mesh(
new pcl::PolygonMesh);
2308 pcl::Poisson<pcl::PointXYZRGBNormal> poisson;
2309 poisson.setDepth(optimizedDepth);
2310 poisson.setInputCloud(cloudToExport);
2311 poisson.reconstruct(*mesh);
2312 printf(
"Mesh reconstruction... done (%fs, %d polygons).\n",
timer.ticks(), (
int)mesh->polygons.size());
2314 if(mesh->polygons.size())
2316 printf(
"Mesh color transfer (max polygons=%d, color radius=%f, clean=%s)...\n",
2319 doClean?
"true":
"false");
2320 rtabmap::util3d::denseMeshPostProcessing<pcl::PointXYZRGBNormal>(
2326 !(texture && textureVertexColorPolicy == 0),
2329 printf(
"Mesh color transfer... done (%fs).\n",
timer.ticks());
2335 printf(
"Saving mesh in db...\n");
2336 std::vector<std::vector<std::vector<RTABMAP_PCL_INDEX> > > polygons;
2341 printf(
"Saving mesh in db... done!\n");
2345 std::string outputPath=outputDirectory+
"/"+baseName+
"_mesh.ply";
2346 printf(
"Saving %s...\n", outputPath.c_str());
2348 pcl::io::savePLYFileBinary(outputPath, *mesh);
2350 pcl::io::savePLYFile(outputPath, *mesh);
2351 printf(
"Saving %s... done!\n", outputPath.c_str());
2357 std::map<int, rtabmap::Transform> robotPosesFiltered;
2360 printf(
"Filtering %ld images from texturing...\n", robotPoses.size());
2361 for(std::map<int, rtabmap::Transform>::iterator
iter=robotPoses.lower_bound(1);
iter!=robotPoses.end(); ++
iter)
2364 dbDriver->getNodeData(
iter->first,
data,
true,
false,
false,
false);
2366 data.uncompressDataConst(&
img, 0);
2369 cv::Mat imgLaplacian;
2370 cv::Laplacian(
img, imgLaplacian, CV_16S);
2372 cv::meanStdDev(imgLaplacian,
m,
s);
2373 double stddev_pxl =
s.at<
double>(0);
2374 double var = stddev_pxl*stddev_pxl;
2375 if(var < (
double)laplacianThr)
2377 printf(
"Camera's image %d is detected as blurry (var=%f < thr=%d), camera is ignored for texturing.\n",
iter->first, var, laplacianThr);
2381 robotPosesFiltered.insert(*
iter);
2385 printf(
"Filtered %ld/%ld images from texturing", robotPosesFiltered.size(), robotPoses.size());
2389 robotPosesFiltered = robotPoses;
2392 printf(
"Texturing %d polygons... robotPoses=%d, cameraModels=%d, cameraDepths=%d\n", (
int)mesh->polygons.size(), (
int)robotPosesFiltered.size(), (
int)cameraModels.size(), (
int)cameraDepths.size());
2393 std::vector<std::map<int, pcl::PointXY> > vertexToPixels;
2406 distanceToCamPolicy);
2407 printf(
"Texturing... done (%fs).\n",
timer.ticks());
2410 if(doClean && textureMesh->tex_coordinates.size())
2412 printf(
"Cleanup mesh...\n");
2414 printf(
"Cleanup mesh... done (%fs).\n",
timer.ticks());
2417 if(textureMesh->tex_materials.size())
2421 printf(
"Merging %d texture(s) to single one (multiband enabled)...\n", (
int)textureMesh->tex_materials.size());
2425 printf(
"Merging %d texture(s)... (%d max textures)\n", (
int)textureMesh->tex_materials.size(), textureCount);
2427 std::map<int, std::map<int, cv::Vec4d> > gains;
2428 std::map<int, std::map<int, cv::Mat> > blendingGains;
2429 std::pair<float, float> contrastValues(0,0);
2432 std::map<int, cv::Mat>(),
2433 std::map<
int, std::vector<rtabmap::CameraModel> >(),
2437 multiband?1:textureCount,
2439 gainValue>0.0f, gainValue, doGainCompensationRGB,
2441 lowBrightnessGain, highBrightnessGain,
2445 textureVertexColorPolicy == 1,
2449 printf(
"Merging to %d texture(s)... done (%fs).\n", (
int)textureMesh->tex_materials.size(),
timer.ticks());
2453 printf(
"Saving texture mesh in db...\n");
2457 textureMesh->tex_coordinates,
2459 printf(
"Saving texture mesh in db... done!\n");
2467 std::string outputPath=outputDirectory+
"/"+baseName+
"_mesh_multiband.obj";
2468 printf(
"MultiBand texturing (size=%d, downscale=%d, unwrap method=%s, fill holes=%s, padding=%d, best score thr=%f, angle thr=%f, force visible=%s)... \"%s\"\n",
2471 multibandUnwrap==1?
"ABF":multibandUnwrap==2?
"LSCM":
"Basic",
2472 multibandFillHoles?
"true":
"false",
2474 multibandBestScoreThr,
2475 multibandAngleHardthr,
2476 multibandForceVisible?
"false":
"true",
2477 outputPath.c_str());
2480 textureMesh->tex_polygons[0],
2483 std::map<int, cv::Mat >(),
2484 std::map<
int, std::vector<CameraModel> >(),
2494 doGainCompensationRGB,
2498 multibandBestScoreThr,
2499 multibandAngleHardthr,
2500 multibandForceVisible))
2502 printf(
"MultiBand texturing...done (%fs).\n",
timer.ticks());
2506 printf(
"MultiBand texturing...failed! (%fs)\n",
timer.ticks());
2512 bool success =
false;
2514 for(
size_t i=0;
i<textureMesh->tex_materials.size(); ++
i)
2518 textureMesh->tex_materials[
i].tex_file.clear();
2521 if(
v.compare(
"texture") == 0){
2522 textureMesh->tex_materials[
i].tex_file.append(baseName+
"_mesh");
2525 textureMesh->tex_materials[
i].tex_file.append(
v);
2528 textureMesh->tex_materials[
i].tex_file +=
".jpg";
2529 printf(
"Saving texture to %s.\n", textureMesh->tex_materials[
i].tex_file.c_str());
2530 UASSERT(textures.cols % textures.rows == 0);
2531 success = cv::imwrite(outputDirectory+
"/"+textureMesh->tex_materials[
i].tex_file, cv::Mat(textures,
cv::Range::all(), cv::Range(textures.rows*
i, textures.rows*(
i+1))));
2534 UERROR(
"Failed saving %s!", textureMesh->tex_materials[
i].tex_file.c_str());
2538 printf(
"Saved %s.\n", textureMesh->tex_materials[
i].tex_file.c_str());
2543 std::string outputPath=outputDirectory+
"/"+baseName+
"_mesh.obj";
2544 printf(
"Saving obj (%d vertices) to %s.\n", (
int)textureMesh->cloud.data.size()/textureMesh->cloud.point_step, outputPath.c_str());
2545 #if PCL_VERSION_COMPARE(>=, 1, 13, 0)
2546 textureMesh->tex_coord_indices = std::vector<std::vector<pcl::Vertices>>();
2547 auto nr_meshes =
static_cast<unsigned>(textureMesh->tex_polygons.size());
2549 for (
unsigned m = 0;
m < nr_meshes;
m++) {
2550 std::vector<pcl::Vertices> ci = textureMesh->tex_polygons[
m];
2551 for(std::size_t
i = 0;
i < ci.size();
i++) {
2552 for (std::size_t
j = 0;
j < ci[
i].vertices.size();
j++) {
2553 ci[
i].vertices[
j] = ci[
i].vertices.size() * (
i + f_idx) +
j;
2556 textureMesh->tex_coord_indices.push_back(ci);
2557 f_idx +=
static_cast<unsigned>(textureMesh->tex_polygons[
m].size());
2564 printf(
"Saved obj to %s!\n", outputPath.c_str());
2568 UERROR(
"Failed saving obj to %s!", outputPath.c_str());
2580 printf(
"Export failed! The cloud is empty.\n");