00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032
00033
00034
00035
00036 #include <frame_common/frame.h>
00037
00038 using namespace Eigen;
00039 using namespace std;
00040 using namespace pcl;
00041
00042
00043 #include <sys/time.h>
00044 #include <opencv2/nonfree/nonfree.hpp>
00045 static double mstime()
00046 {
00047 timeval tv;
00048 gettimeofday(&tv,NULL);
00049 long long ts = tv.tv_sec;
00050 ts *= 1000000;
00051 ts += tv.tv_usec;
00052 return (double)ts*.001;
00053 }
00054
00055 namespace frame_common
00056 {
00057
00058 void Frame::setCamParams(const CamParams &c)
00059 {
00060
00061 cam = c;
00062 iproj.setZero();
00063 iproj(0,0) = c.fx;
00064 iproj(1,1) = c.fy;
00065 iproj(0,2)=c.cx;
00066 iproj(1,2)=c.cy;
00067 iproj(2,2)=1.0;
00068
00069
00070 disp_to_cart(0,0) = 1/c.fx;
00071 disp_to_cart(1,1) = 1/c.fy;
00072 disp_to_cart(0,3) = -c.cx/c.fx;
00073 disp_to_cart(1,3) = -c.cy/c.fy;
00074 disp_to_cart(2,3) = 1.0;
00075 disp_to_cart(3,2) = 1/(c.tx*c.fx);
00076
00077 cart_to_disp.setZero();
00078 cart_to_disp(0,0) = c.fx;
00079 cart_to_disp(1,1) = c.fy;
00080 cart_to_disp(0,2) = c.cx;
00081 cart_to_disp(1,2) = c.cy;
00082 cart_to_disp(2,3) = c.fx*c.tx;
00083 cart_to_disp(3,2) = 1.0;
00084
00085 if (c.tx > 0.0)
00086 isStereo = true;
00087 else
00088 isStereo = false;
00089 }
00090
00091
00092
00093 Eigen::Vector3d Frame::cam2pix(const Eigen::Vector3d &cam_coord) const
00094 {
00095 double xl = cam.fx * cam_coord[0] + cam.cx * cam_coord[2];
00096 double y = cam.fy * cam_coord[1] + cam.cy * cam_coord[2];
00097 double w = cam_coord[2];
00098 double xd = cam.fx * cam.tx;
00099 double rw = 1.0/w;
00100 double y_rw = y * rw;
00101 return Eigen::Vector3d(xl*rw, y_rw, xd*rw);
00102 }
00103
00104
00105 Eigen::Vector3d Frame::pix2cam(const Eigen::Vector3d &pix_coord) const
00106 {
00107 double x = pix_coord[0] - cam.cx;
00108 double y = pix_coord[1] - cam.cy;
00109 double z = cam.fx;
00110 double w = pix_coord[2]/cam.tx;
00111 return Eigen::Vector3d(x/w, y/w, z/w);
00112 }
00113
00114
00115
00116 void Frame::setTKpts(Eigen::Vector4d trans, Eigen::Quaterniond rot)
00117 {
00118 Vector3d tr;
00119 tr = trans.head<3>();
00120
00121 Matrix4d Q;
00122 Q << 1.0, 0.0, 0.0, -cam.cx,
00123 0.0, 1.0, 0.0, -cam.cy,
00124 0.0, 0.0, 0.0, cam.fx,
00125 0.0, 0.0, 1.0/cam.tx, 0;
00126 Matrix<double,3,4> P;
00127 P << cam.fx, 0.0, cam.cx, 0.0,
00128 0.0, cam.fx, cam.cy, 0.0,
00129 0.0, 0.0, 1.0, 0.0;
00130
00131 Matrix4d T;
00132 T.setZero();
00133 Matrix3d R = rot.toRotationMatrix();
00134 T.block<3,3>(0,0) = R.transpose();
00135 T.block<3,1>(0,3) = -R*tr;
00136 T(3,3) = 1.0;
00137
00138 P = P*T*Q;
00139
00140
00141 tkpts.resize(kpts.size());
00142 Vector4d v(0.0,0.0,0.0,1.0);
00143 for (int i=0; i<(int)kpts.size(); i++)
00144 {
00145 Vector3d vk;
00146 v(0) = kpts[i].pt.x;
00147 v(1) = kpts[i].pt.y;
00148 v(2) = disps[i];
00149 vk = P*v;
00150 tkpts[i].pt.x = vk(0)/vk(2);
00151 tkpts[i].pt.y = vk(1)/vk(2);
00152 }
00153 }
00154
00155
00156
00157
00158
00159
00160 FrameProc::FrameProc(int v)
00161 {
00162
00163 detector = new cv::GridAdaptedFeatureDetector(new cv::FastFeatureDetector(v), 1000);
00164
00165
00166
00167 extractor = new cv::SurfDescriptorExtractor;
00168
00169
00170 ndisp = 64;
00171 doSparse = false;
00172 }
00173
00174 void FrameProc::setFrameDetector(const cv::Ptr<cv::FeatureDetector>& new_detector)
00175 {
00176 detector = new_detector;
00177 }
00178
00179 void FrameProc::setFrameDescriptor(const cv::Ptr<cv::DescriptorExtractor>& new_extractor)
00180 {
00181 extractor = new_extractor;
00182 }
00183
00184
00185 void FrameProc::setMonoFrame(Frame& frame, const cv::Mat &img, const cv::Mat& mask )
00186 {
00187 frame.img = img;
00188
00189
00190 frame.kpts.clear();
00191
00192 detector->detect(img, frame.kpts, mask);
00193
00194 extractor->compute(img, frame.kpts, frame.dtors);
00195
00196
00197 frame.pts.resize(frame.kpts.size());
00198 frame.goodPts.assign(frame.kpts.size(), false);
00199 frame.disps.assign(frame.kpts.size(), 10);
00200 }
00201
00202
00203
00204
00205 void FrameProc::setStereoFrame(Frame &frame, const cv::Mat &img, const cv::Mat &imgr, const cv::Mat &left_mask, int nfrac,
00206 bool setPointCloud)
00207 {
00208 setMonoFrame( frame, img, left_mask );
00209 frame.imgRight = imgr;
00210
00211
00212 setStereoPoints( frame, nfrac, setPointCloud );
00213 }
00214
00215
00216
00217
00218 void FrameProc::setStereoFrame(Frame &frame, const cv::Mat &img, const cv::Mat &imgr, int nfrac,
00219 bool setPointCloud)
00220 {
00221 setStereoFrame( frame, img, imgr, cv::Mat(), nfrac, setPointCloud );
00222 }
00223
00224
00225 void FrameProc::setStereoPoints(Frame &frame, int nfrac, bool setPointCloud)
00226 {
00227
00228
00229 frame.disps.clear();
00230
00231
00232 FrameStereo *st;
00233 if (doSparse)
00234 st = new SparseStereo(frame.img,frame.imgRight,true,ndisp);
00235 else if (nfrac > 0)
00236 {
00237 DenseStereo::ndisp = ndisp;
00238 st = new DenseStereo(frame.img,frame.imgRight,ndisp,1.0/(double)nfrac);
00239 }
00240 else
00241 {
00242 DenseStereo::ndisp = ndisp;
00243 st = new DenseStereo(frame.img,frame.imgRight,ndisp);
00244 }
00245
00246 int nkpts = frame.kpts.size();
00247 frame.goodPts.resize(nkpts);
00248 frame.pts.resize(nkpts);
00249 frame.disps.resize(nkpts);
00250
00251 #pragma omp parallel for shared( st )
00252 for (int i=0; i<nkpts; i++)
00253 {
00254 double disp = st->lookup_disparity(frame.kpts[i].pt.x,frame.kpts[i].pt.y);
00255 frame.disps[i] = disp;
00256 if (disp > 0.0)
00257 {
00258 frame.goodPts[i] = true;
00259 Vector3d pt(frame.kpts[i].pt.x,frame.kpts[i].pt.y,disp);
00260 frame.pts[i].head(3) = frame.pix2cam(pt);
00261 frame.pts[i](3) = 1.0;
00262
00263 }
00264 else
00265 frame.goodPts[i] = false;
00266 }
00267
00268 if (!setPointCloud) return;
00269
00270
00271 double cx = frame.cam.cx;
00272 double cy = frame.cam.cy;
00273 double fx = frame.cam.fx;
00274 double tx = frame.cam.tx;
00275
00276
00277 frame.dense_pointcloud.points.resize(0);
00278
00279 for (int v=0; v<(int)frame.img.rows; v++)
00280 {
00281 const uchar *imrow = frame.img.ptr<uchar>(v);
00282 for (int u=0; u<(int)frame.img.cols; u++, imrow++)
00283 {
00284 double disp = st->lookup_disparity(u,v);
00285 if (disp > 0)
00286 {
00287 pcl::PointXYZRGB pt;
00288
00289 double w = tx / disp;
00290 pt.x = ((double)u - cx)*w;
00291 pt.y = ((double)v - cy)*w;
00292 pt.z = fx*w;
00293
00294
00295 uint8_t g = *imrow;
00296 int32_t rgb = (g << 16) | (g << 8) | g;
00297 pt.rgb = *(float*)(&rgb);
00298 frame.dense_pointcloud.points.push_back(pt);
00299 }
00300 }
00301 }
00302
00303 delete st;
00304 }
00305
00306
00307 void PointcloudProc::setPointcloud(Frame &frame, const pcl::PointCloud<pcl::PointXYZRGB>& input_cloud) const
00308 {
00309 reduceCloud(input_cloud, frame.pointcloud);
00310
00311
00312
00313
00314 frame.pl_pts.clear();
00315 frame.pl_kpts.clear();
00316 frame.pl_normals.clear();
00317 frame.pl_ipts.clear();
00318
00319 unsigned int ptcloudsize = frame.pointcloud.points.size();
00320 frame.pl_pts.resize(ptcloudsize);
00321 frame.pl_kpts.resize(ptcloudsize);
00322 frame.pl_normals.resize(ptcloudsize);
00323 frame.pl_ipts.resize(ptcloudsize);
00324
00325 #pragma omp parallel for
00326 for (unsigned int i=0; i < frame.pointcloud.points.size(); i++)
00327 {
00328 PointXYZRGBNormal &pt = frame.pointcloud.points[i];
00329
00330 frame.pl_pts[i] = Eigen::Vector4d(pt.x, pt.y, pt.z, 1.0);
00331 frame.pl_normals[i] = Eigen::Vector4d(pt.normal[0], pt.normal[1], pt.normal[2], 1.0);
00332 frame.pl_kpts[i] = projectPoint(frame.pl_pts[i], frame.cam);
00333 frame.pl_ipts[i] = -1;
00334 }
00335 }
00336
00337 void PointcloudProc::match(const Frame& frame0, const Frame& frame1,
00338 const Eigen::Vector3d& trans, const Eigen::Quaterniond& rot,
00339 std::vector<cv::DMatch>& matches) const
00340 {
00341 PointCloud<PointXYZRGBNormal> transformed_cloud;
00342
00343
00344
00345
00346
00347 transformPointCloudWithNormals<PointXYZRGBNormal>(frame0.pointcloud, transformed_cloud, Vector3f(0,0,0), rot.cast<float>().conjugate());
00348 transformPointCloudWithNormals<PointXYZRGBNormal>(transformed_cloud, transformed_cloud, -trans.cast<float>(), Quaternionf(1, 0, 0, 0));
00349
00350
00351
00352
00353
00354
00355
00356
00357
00358
00359
00360
00361
00362
00363
00364 std::vector<int> f0_indices, f1_indices;
00365 getMatchingIndices(transformed_cloud, frame1.pointcloud, f0_indices, f1_indices);
00366
00367
00368
00369
00370
00371 matches.clear();
00372
00373
00374 #pragma omp parallel for shared( transformed_cloud, f0_indices, f1_indices )
00375 for (unsigned int i=1; i < f0_indices.size(); i++)
00376 {
00377 const PointXYZRGBNormal &pt0 = transformed_cloud.points[f0_indices[i]];
00378 const PointXYZRGBNormal &pt1 = frame1.pointcloud.points[f1_indices[i]];
00379
00380
00381 Quaterniond normalquat;
00382 Vector3d norm0(pt0.normal[0], pt0.normal[1], pt0.normal[2]), norm1(pt1.normal[0], pt1.normal[1], pt1.normal[2]);
00383 normalquat.setFromTwoVectors(norm0, norm1);
00384
00385 double dist = (Vector3d(pt0.x, pt0.y, pt0.z)-Vector3d(pt1.x, pt1.y, pt1.z)).norm();
00386
00387
00388
00389
00390
00391
00392
00393
00394
00395 if ((norm0 - norm1).norm() < 0.5 && dist < 0.2)
00396 #pragma omp critical
00397 matches.push_back(cv::DMatch(f0_indices[i], f1_indices[i], dist));
00398 }
00399
00400 printf("[Frame] Found %d matches, then converted %d matches.\n", (int)f0_indices.size(), (int)matches.size());
00401 }
00402
00403 void PointcloudProc::getMatchingIndices(const PointCloud<PointXYZRGBNormal>& input,
00404 const PointCloud<PointXYZRGBNormal>& output,
00405 std::vector<int>& input_indices, std::vector<int>& output_indices) const
00406 {
00407
00408 KdTreeFLANN<PointXYZRGBNormal> input_tree, output_tree;
00409
00410 input_tree.setInputCloud(boost::make_shared<const PointCloud<PointXYZRGBNormal> >(input));
00411 output_tree.setInputCloud(boost::make_shared<const PointCloud<PointXYZRGBNormal> >(output));
00412
00413
00414
00415 #pragma omp parallel for shared( input_tree, output_tree )
00416 for (unsigned int i = 0; i < input.points.size(); i++)
00417 {
00418 PointXYZRGBNormal input_pt = input.points[i];
00419 std::vector<int> input_indexvect(1), output_indexvect(1);
00420 std::vector<float> input_distvect(1), output_distvect(1);
00421
00422
00423 output_tree.nearestKSearch(input_pt, 1, input_indexvect, input_distvect);
00424
00425 PointXYZRGBNormal output_pt = output.points[input_indexvect[0]];
00426
00427
00428 input_tree.nearestKSearch(output_pt, 1, output_indexvect, output_distvect);
00429
00430
00431 #pragma omp critical
00432 if (output_indexvect[0] == (int)i)
00433 {
00434 input_indices.push_back(i);
00435 output_indices.push_back(input_indexvect[0]);
00436 }
00437 }
00438 }
00439
00440
00441 void PointcloudProc::reduceCloud(const PointCloud<PointXYZRGB>& input, PointCloud<PointXYZRGBNormal>& output) const
00442 {
00443 PointCloud<PointXYZRGB> cloud_nan_filtered, cloud_box_filtered, cloud_voxel_reduced;
00444 PointCloud<Normal> normals;
00445 PointCloud<PointXYZRGBNormal> cloud_normals;
00446
00447 std::vector<int> indices;
00448
00449
00450 removeNaNFromPointCloud(input, cloud_nan_filtered, indices);
00451 indices.clear();
00452
00453
00454 Eigen::Vector4f min_pt(-100, -100, -100, -100);
00455 Eigen::Vector4f max_pt(100, 100, 100, 100);
00456 getPointsInBox(cloud_nan_filtered, min_pt, max_pt, indices);
00457
00458 ExtractIndices<PointXYZRGB> boxfilter;
00459 boxfilter.setInputCloud(boost::make_shared<const PointCloud<PointXYZRGB> >(cloud_nan_filtered));
00460 boxfilter.setIndices (boost::make_shared<vector<int> > (indices));
00461 boxfilter.filter(cloud_box_filtered);
00462
00463
00464 VoxelGrid<PointXYZRGB> voxelfilter;
00465 voxelfilter.setInputCloud (boost::make_shared<const PointCloud<PointXYZRGB> > (cloud_box_filtered));
00466 voxelfilter.setLeafSize (0.05, 0.05, 0.05);
00467
00468 voxelfilter.filter (cloud_voxel_reduced);
00469
00470
00471 NormalEstimation<PointXYZRGB, Normal> normalest;
00472 normalest.setViewPoint(0, 0, 0);
00473 normalest.setSearchMethod (boost::make_shared<search::KdTree<PointXYZRGB> > ());
00474
00475 normalest.setRadiusSearch (0.25);
00476
00477 normalest.setInputCloud(boost::make_shared<const PointCloud<PointXYZRGB> >(cloud_voxel_reduced));
00478 normalest.compute(normals);
00479
00480 pcl::concatenateFields (cloud_voxel_reduced, normals, cloud_normals);
00481
00482
00483 PassThrough<PointXYZRGBNormal> normalfilter;
00484 normalfilter.setFilterFieldName("curvature");
00485
00486 normalfilter.setFilterLimits(0.0, 0.2);
00487 normalfilter.setInputCloud(boost::make_shared<const PointCloud<PointXYZRGBNormal> >(cloud_normals));
00488 normalfilter.filter(output);
00489 }
00490
00491 Eigen::Vector3d PointcloudProc::projectPoint(Eigen::Vector4d& point, CamParams cam) const
00492 {
00493 Eigen::Vector3d keypoint;
00494
00495 keypoint(0) = (cam.fx*point.x()) / point.z() + cam.cx;
00496 keypoint(1) = (cam.fy*point.y()) / point.z() + cam.cy;
00497 keypoint(2) = (cam.fx*(point.x()-cam.tx)/point.z() + cam.cx);
00498
00499 return keypoint;
00500 }
00501
00502 }
00503
00504
00505
00506
00507
00508
00509
00510
00511
00512
00513
00514