map_util.cpp
Go to the documentation of this file.
00001 #include "rgbdtools/map_util.h"
00002 #include <iostream>
00003 #include <fstream>
00004 #include <ros/ros.h>
00005 #include <ros/time.h>
00006 #include <geometry_msgs/PoseStamped.h>
00007 
00008 
00009 namespace rgbdtools {
00010  
00011 
00012 void concatenate_clouds(PointCloudT& map_cloud,KeyframeVector keyframes_)
00013 {
00014 //    PointCloudT::Ptr aggregate_cloud(new PointCloudT());
00015 //    aggregate_cloud->header.frame_id = keyframes_[0].header.frame_id;
00016 //    aggregate_cloud->sensor_origin_[3] = 1;
00017 //    // aggregate all frames into single cloud
00018 //    for (unsigned int kf_idx = 0; kf_idx < keyframes_.size(); ++kf_idx)
00019 //    {
00020 //        const rgbdtools::RGBDKeyframe& keyframe = keyframes_[kf_idx];
00021 //        PointCloudT cloud;
00022 //        keyframe.constructDensePointCloud(cloud, 10, 1);
00023 
00024 //        PointCloudT cloud_tf;
00025 //        pcl::transformPointCloud(cloud, cloud_tf, keyframe.pose);
00026 //        cloud_tf.header.frame_id = keyframes_[0].header.frame_id;
00028 //        *aggregate_cloud += cloud_tf;
00029 
00030 
00031 //    }
00032 //    PointT min,max;
00033 //    pcl::getMinMax3D(*aggregate_cloud,min,max);
00034 
00035 
00036 }
00037 
00038 void floatMatrixToUintMatrix(
00039   const cv::Mat& mat_in, 
00040   cv::Mat& mat_out, 
00041   float scale)
00042 {
00043   if (scale == 0)
00044   {
00045     // normalize the matrix
00046     float max_val = 0;
00047     
00048     for (int u = 0; u < mat_in.cols; ++u)
00049     for (int v = 0; v < mat_in.rows; ++v)   
00050     {
00051       float val_in = mat_in.at<float>(v, u);
00052       if (val_in > max_val) max_val = val_in;
00053     }
00054   
00055     scale = 255.0 / max_val;
00056   }
00057    
00058   mat_out = cv::Mat::zeros(mat_in.size(), CV_8UC1);
00059   for (int u = 0; u < mat_in.cols; ++u)
00060   for (int v = 0; v < mat_in.rows; ++v)   
00061   {
00062     float val_in = mat_in.at<float>(v, u) ;
00063     uint8_t& val_out = mat_out.at<uint8_t>(v, u); 
00064 
00065     val_out = val_in * scale;
00066   }
00067 }
00068 
00069 void thresholdMatrix(
00070   const cv::Mat& mat_in, 
00071   cv::Mat& mat_out,
00072   int threshold)
00073 {
00074   mat_out = cv::Mat::zeros(mat_in.size(), CV_8UC1);
00075   
00076   for (int u = 0; u < mat_in.cols; ++u)
00077   for (int v = 0; v < mat_in.rows; ++v)   
00078   {
00079     uint16_t val_in = mat_in.at<uint16_t>(v, u) ;
00080     uint8_t& val_out = mat_out.at<uint8_t>(v, u); 
00081     
00082     if (val_in >= threshold) val_out = 1;
00083   }
00084 }
00085 
00086 void buildExpectedPhiHistorgtam(
00087   cv::Mat& histogram,
00088   double degrees_per_bin,
00089   double stdev)
00090 {
00091   int n_bins = (int)(360.0 / degrees_per_bin);
00092   histogram = cv::Mat::zeros(1, n_bins, CV_32FC1);
00093 
00094   double s = stdev  / degrees_per_bin;
00095 
00096   double mean[4];
00097   mean[0] =   0.0 / degrees_per_bin;
00098   mean[1] =  90.0 / degrees_per_bin;
00099   mean[2] = 180.0 / degrees_per_bin;
00100   mean[3] = 270.0 / degrees_per_bin;
00101 
00102   double a = 1.0 / (s * sqrt(2.0 * M_PI));
00103   double b = 2.0 * s * s; 
00104 
00105   for (int u = 0; u < n_bins; ++u)
00106   {
00107     float& bin = histogram.at<float>(0, u);
00108 
00109     // accumulate 4 gaussians
00110     for (int g = 0; g < 4; g++)
00111     {
00112       int x = u - mean[g];
00113   
00114       // wrap around to closer distance
00115       if (x < -n_bins/2) x += n_bins;
00116       if (x >= n_bins/2) x -= n_bins;
00117 
00118       float r = a * exp(-x*x / b);
00119 
00120       bin += r;
00121     }
00122   }
00123 
00124   normalizeHistogram(histogram);
00125 }  
00126 
00127 void buildPhiHistogram(
00128   const pcl::PointCloud<pcl::PointXYZRGBNormal>& cloud,
00129   cv::Mat& histogram,
00130   double degrees_per_bin)
00131 {
00132   int phi_bins = (int)(360.0 / degrees_per_bin);
00133   histogram = cv::Mat::zeros(1, phi_bins, CV_32FC1);
00134   
00135   for (unsigned int i = 0; i < cloud.points.size(); ++i)
00136   {
00137     const pcl::PointXYZRGBNormal& p = cloud.points[i]; 
00138 
00139     double nx = p.normal_x;
00140     double ny = p.normal_y;
00141     double nz = p.normal_z;
00142 
00143     if (isnan(nx) || isnan(ny) || isnan(nz)) continue;
00144 
00145     double r = sqrt(nx*nx + ny*ny + nz*nz);
00146     double theta = acos(nz/r);
00147     double phi   = atan2(ny, nx);
00148 
00149     double phi_deg   = phi   * 180.0 / M_PI;
00150     double theta_deg = theta * 180.0 / M_PI; 
00151 
00152     // normalize phi to [0, 360)
00153     if (phi_deg < 0.0) phi_deg += 360.0;
00154 
00155     // only consider points which are close to vertical
00156     if (std::abs(90-theta_deg) > 3.0) continue; 
00157 
00158     int idx_phi = (int)(phi_deg / degrees_per_bin);
00159 
00160     float& bin = histogram.at<float>(0, idx_phi);
00161     bin = bin + 1.0; 
00162   }
00163 
00164   normalizeHistogram(histogram);
00165 }
00166 
00167 void shiftHistogram(
00168   const cv::Mat& hist_in,
00169   cv::Mat& hist_out,
00170   int bins)
00171 {
00172   hist_out = cv::Mat(hist_in.size(), CV_32FC1);
00173   int w = hist_in.cols;
00174   for (int u = 0; u < w; ++u)
00175   {
00176     int u_shifted = (u + bins) % w;
00177 
00178     hist_out.at<float>(0, u_shifted) = hist_in.at<float>(0, u);
00179   } 
00180 }
00181 
00182 void normalizeHistogram(cv::Mat& histogram)
00183 {
00184   float sum = 0;
00185 
00186   for (int u = 0; u < histogram.cols; ++u)
00187     sum += histogram.at<float>(0,u);
00188 
00189   histogram = histogram / sum;
00190 }
00191 
00192 bool alignHistogram(
00193   const cv::Mat& hist,
00194   const cv::Mat& hist_exp,
00195   double hist_resolution,
00196   double& best_angle)
00197 {
00198   // check diff
00199   int best_i = 0;
00200   double best_diff = 9999999999;
00201 
00202   for (int i = 0; i < 90.0 / hist_resolution; ++i)
00203   {
00204     cv::Mat hist_shifted;
00205     shiftHistogram(hist, hist_shifted, i);
00206 
00207     double diff = cv::compareHist(hist_shifted, hist_exp, CV_COMP_BHATTACHARYYA);
00208     if (std::abs(diff) < best_diff)
00209     {
00210       best_diff = std::abs(diff);
00211       best_i = i;
00212     }
00213   }
00214 
00215   best_angle = best_i * hist_resolution;
00216 
00217   cv::Mat hist_best;
00218   shiftHistogram(hist, hist_best, best_i);
00219   cv::Mat hist_best_img;
00220   createImageFromHistogram(hist_best, hist_best_img);
00221   cv::imshow("hist_best_img", hist_best_img);
00222 
00223   return true;
00224 }
00225 
00226 void create8bitHistogram(
00227   const cv::Mat& histogram,
00228   cv::Mat& histogram_norm)
00229 {
00230   // find max value
00231   double min_val, max_val;
00232   cv::Point minLoc, maxLoc;
00233   cv::minMaxLoc(histogram, &min_val, &max_val, &minLoc, &maxLoc);
00234 
00235   // rescale so that max = 255, for visualization purposes
00236   cv::Mat temp = histogram.clone();
00237   temp = temp * 255.0 / max_val;
00238 
00239   // convert to uint
00240   histogram_norm = cv::Mat::zeros(histogram.size(), CV_8UC1); 
00241   temp.convertTo(histogram_norm, CV_8UC1); 
00242 }
00243 
00244 void createImageFromHistogram(
00245   const cv::Mat& histogram,
00246   cv::Mat& image)
00247 {
00248   // normalize the histogram in range 0 - 255
00249   cv::Mat hist_norm;
00250   create8bitHistogram(histogram, hist_norm);
00251 
00252   image = cv::Mat::zeros(256, histogram.cols, CV_8UC1);
00253   for (int u = 0; u < histogram.cols; ++u)
00254   {
00255     uint8_t val = hist_norm.at<uint8_t>(0, u);
00256     for (int v = 0; v < val; ++v)
00257       image.at<uint8_t>(255-v, u) = 255;
00258   }
00259 }
00260 
00261 void create2DProjectionImage(
00262   const PointCloudT& cloud, 
00263   cv::Mat& img,
00264   double min_z,
00265   double max_z)
00266 {
00267   // TODO: thses shouldnt be hard-coded
00268   double resolution = 0.02; // 2cm
00269   double w = 20.0;
00270   double h = 20.0;
00271   double cx = w/2;
00272   double cy = h/2;
00273   
00274   img = cv::Mat::zeros(h/resolution, w/resolution, CV_8UC1);
00275 
00276   for (unsigned int i = 0; i < cloud.points.size(); ++i)
00277   {
00278     const PointT& p = cloud.points[i];
00279 
00280     // filter z
00281     if (isnan(p.z) || p.z >= max_z || p.z < min_z) continue;
00282 
00283     int u = (h - p.x + cx)/resolution;
00284     int v = (p.y + cy)/resolution;
00285 
00286     uint8_t& bin = img.at<uint8_t>(v, u);
00287     if(bin < 255) bin++;
00288   }
00289 }
00290 
00291 void filterCloudByHeight(
00292   const pcl::PointCloud<pcl::PointXYZRGBNormal>& cloud_in,
00293   pcl::PointCloud<pcl::PointXYZRGBNormal>& cloud_out,
00294   double min_z,
00295   double max_z)
00296 {
00297   for (unsigned int i = 0; i < cloud_in.points.size(); ++i)
00298   {
00299     const pcl::PointXYZRGBNormal& p = cloud_in.points[i]; 
00300     
00301     if (p.z >= min_z && p.z < max_z)
00302       cloud_out.push_back(p); 
00303   }
00304 }
00305 
00306 /*
00307 void prepareFeaturesForRANSAC(KeyframeVector& keyframes)
00308 {
00309   double init_surf_threshold = 400.0;
00310   double min_surf_threshold = 25;
00311   int n_keypoints = 400;
00312 
00313   printf("preparing SURF features for RANSAC associations...\n");  
00314 
00315   cv::SurfDescriptorExtractor extractor;
00316  
00317   for (unsigned int kf_idx = 0; kf_idx < keyframes.size(); kf_idx++)
00318   { 
00319     RGBDKeyframe& keyframe = keyframes[kf_idx];
00320 
00321     double surf_threshold = init_surf_threshold;
00322 
00323     while (surf_threshold >= min_surf_threshold)
00324     {
00325       cv::SurfFeatureDetector detector(surf_threshold);
00326       keyframe.keypoints.clear();
00327       detector.detect(keyframe.rgb_img, keyframe.keypoints);
00328 
00329       printf("[KF %d of %d] %d SURF keypoints detected (threshold: %.1f)\n", 
00330         (int)kf_idx, (int)keyframes.size(), 
00331         (int)keyframe.keypoints.size(), surf_threshold); 
00332 
00333       if ((int)keyframe.keypoints.size() < n_keypoints)
00334         surf_threshold /= 2.0;
00335       else break;
00336     }
00337 
00338     extractor.compute(keyframe.rgb_img, keyframe.keypoints, keyframe.descriptors);
00339     keyframe.computeDistributions();
00340   }
00341 }
00342 */
00343 
00344 void pairwiseMatchingRANSAC(
00345   const RGBDFrame& frame_a, const RGBDFrame& frame_b,
00346   double max_eucl_dist_sq, 
00347   double max_desc_dist,
00348   double sufficient_inlier_ratio,
00349   int max_ransac_iterations,
00350   std::vector<cv::DMatch>& all_matches,
00351   std::vector<cv::DMatch>& best_inlier_matches,
00352   Eigen::Matrix4f& best_transformation)
00353 {
00354   // params
00355   bool use_ratio_test = true;
00356   float max_desc_ratio = 0.75;
00357 
00358   // constants
00359   int min_sample_size = 3;
00360 
00361   cv::FlannBasedMatcher matcher;          // for SURF
00362   TransformationEstimationSVD svd;
00363 
00364   std::vector<cv::DMatch> candidate_matches;
00365 
00366   // **** build candidate matches ***********************************
00367   // assumes detectors and distributions are computed
00368   // establish all matches from b to a
00369 
00370   if (use_ratio_test)
00371   {
00372     std::vector<std::vector<cv::DMatch> > all_matches2;
00373     
00374     matcher.knnMatch(
00375       frame_b.descriptors, frame_a.descriptors, all_matches2, 2);
00376 
00377     for (unsigned int m_idx = 0; m_idx < all_matches2.size(); ++m_idx)
00378     {
00379       const cv::DMatch& match1 = all_matches2[m_idx][0];
00380       const cv::DMatch& match2 = all_matches2[m_idx][1];
00381       
00382       double ratio =  match1.distance / match2.distance;
00383       
00384       // remove bad matches - ratio test, valid keypoints
00385       if (ratio < max_desc_ratio)
00386       {
00387         int idx_b = match1.queryIdx;
00388         int idx_a = match1.trainIdx; 
00389 
00390         if (frame_a.kp_valid[idx_a] && frame_b.kp_valid[idx_b])
00391           candidate_matches.push_back(match1);
00392       }
00393     }
00394   }
00395   else
00396   {
00397     matcher.match(
00398       frame_b.descriptors, frame_a.descriptors, all_matches);
00399 
00400     for (unsigned int m_idx = 0; m_idx < all_matches.size(); ++m_idx)
00401     {
00402       const cv::DMatch& match = all_matches[m_idx];
00403 
00404       // remove bad matches - descriptor distance, valid keypoints
00405       if (match.distance < max_desc_dist)
00406       {      
00407         int idx_b = match.queryIdx;
00408         int idx_a = match.trainIdx; 
00409         
00410         if (frame_a.kp_valid[idx_a] && frame_b.kp_valid[idx_b])
00411           candidate_matches.push_back(match);
00412       }
00413     }
00414   }
00415 
00416   int size = candidate_matches.size();
00417   //printf("size: %d\n", size);
00418   
00419   if (size < min_sample_size) return;
00420   
00421   // **** build 3D features for SVD ********************************
00422 
00423   PointCloudFeature features_a, features_b;
00424 
00425   features_a.resize(size);
00426   features_b.resize(size);
00427 
00428   for (int m_idx = 0; m_idx < size; ++m_idx)
00429   {
00430     const cv::DMatch& match = candidate_matches[m_idx];
00431     int idx_b = match.queryIdx;
00432     int idx_a = match.trainIdx; 
00433 
00434     PointFeature& p_a = features_a[m_idx];
00435     p_a.x = frame_a.kp_means[idx_a](0,0);
00436     p_a.y = frame_a.kp_means[idx_a](1,0);
00437     p_a.z = frame_a.kp_means[idx_a](2,0);
00438 
00439     PointFeature& p_b = features_b[m_idx];
00440     p_b.x = frame_b.kp_means[idx_b](0,0);
00441     p_b.y = frame_b.kp_means[idx_b](1,0);
00442     p_b.z = frame_b.kp_means[idx_b](2,0);
00443   }
00444 
00445   // **** main RANSAC loop ****************************************
00446   
00447   int best_n_inliers = 0;
00448   Eigen::Matrix4f transformation; // transformation used inside loop
00449   
00450   for (int iteration = 0; iteration < max_ransac_iterations; ++iteration)
00451   {   
00452     // generate random indices
00453     IntVector sample_idx;
00454     getRandomIndices(min_sample_size, size, sample_idx);
00455     
00456     // build initial inliers from random indices
00457     IntVector inlier_idx;
00458     std::vector<cv::DMatch> inlier_matches;
00459 
00460     for (unsigned int s_idx = 0; s_idx < sample_idx.size(); ++s_idx)
00461     {
00462       int m_idx = sample_idx[s_idx];
00463       inlier_idx.push_back(m_idx);
00464       inlier_matches.push_back(candidate_matches[m_idx]);
00465     } 
00466     
00467     // estimate transformation from minimum set of random samples
00468     svd.estimateRigidTransformation(
00469       features_b, inlier_idx,
00470       features_a, inlier_idx,
00471       transformation);
00472 
00473     // evaluate transformation fitness by checking distance to all points
00474     PointCloudFeature features_b_tf;
00475     pcl::transformPointCloud(features_b, features_b_tf, transformation);
00476 
00477     for (int m_idx = 0; m_idx < size; ++m_idx)
00478     {
00479       const PointFeature& p_a = features_a[m_idx];
00480       const PointFeature& p_b = features_b_tf[m_idx];
00481 
00482       float dist_sq = distEuclideanSq(p_a, p_b);
00483       
00484       if (dist_sq < max_eucl_dist_sq)
00485       {
00486         inlier_idx.push_back(m_idx);
00487         inlier_matches.push_back(candidate_matches[m_idx]);
00488 
00489         // reestimate transformation from all inliers
00490         svd.estimateRigidTransformation(
00491           features_b, inlier_idx,
00492           features_a, inlier_idx,
00493           transformation);
00494         pcl::transformPointCloud(features_b, features_b_tf, transformation);
00495       }
00496     }
00497     
00498     // check if inliers are better than the best model so far
00499     int n_inliers = inlier_idx.size();
00500 
00501     if (n_inliers > best_n_inliers)
00502     {
00503       svd.estimateRigidTransformation(
00504         features_b, inlier_idx,
00505         features_a, inlier_idx,
00506         transformation);
00507 
00508       best_n_inliers = n_inliers;
00509       best_transformation = transformation;
00510       best_inlier_matches = inlier_matches;
00511     }
00512 
00513     // check if we reached ratio termination criteria
00514     double inlier_ratio = (double) n_inliers / (double) size;
00515 
00516     if (inlier_ratio > sufficient_inlier_ratio)
00517       break;
00518   }
00519 }
00520 
00521 void getRandomIndices(
00522   int k, int n, IntVector& output)
00523 {
00524   while ((int)output.size() < k)
00525   {
00526     int random_number = rand() % n;
00527     bool duplicate = false;    
00528 
00529     for (unsigned int i = 0; i < output.size(); ++i)
00530     {
00531       if (output[i] == random_number)
00532       {
00533         duplicate = true;
00534         break;
00535       }
00536     }
00537 
00538     if (!duplicate)
00539       output.push_back(random_number);
00540   }
00541 }
00542 
00543 void get3RandomIndices(
00544   int n, std::set<int>& mask, IntVector& output)
00545 {
00546 //  int key;
00547   
00548 //  for(u_int i=0;i<999999;i++)
00549 //  {
00550 //    output.clear();
00551 //    getRandomIndices(3, n, output);
00552     
00553 //    // calculate a key based on the 3 random indices
00554 //    key = output[0] * n * n + output[1] * n + output[2];
00555            
00556 //    //printf("%d %d %d\n", output[0], output[1], output[2]);
00557     
00558 //    // value not present in mask
00559 //    if (mask.find(key) == mask.end())
00560 //      break;
00561 //  }
00562   getRandomIndices(3, n, output);
00563 
00564 //  mask.insert(key);
00565 }
00566 
00567 double distEuclideanSq(const PointFeature& a, const PointFeature& b)
00568 {
00569   float dx = a.x - b.x;
00570   float dy = a.y - b.y;
00571   float dz = a.z - b.z;
00572   return dx*dx + dy*dy + dz*dz;
00573 }
00574 
00575 // a = ground truth, b=meas
00576 void compareAssociationMatrix(
00577   const cv::Mat& a,
00578   const cv::Mat& b,
00579   int& false_pos,
00580   int& false_neg,
00581   int& total)
00582 {
00583   false_pos = 0;
00584   false_neg = 0;
00585   total = 0;
00586   
00587   // assert both matrices are square, and same size
00588   assert(a.rows == a.cols);
00589   assert(b.rows == b.cols);
00590   assert(a.rows == b.rows);
00591   
00592   int size = a.rows;
00593   
00594   for (int u = 0; u < size; ++u)
00595   for (int v = 0; v < size; ++v)
00596   {
00597     if (u !=v)
00598     {
00599       int val_a = a.at<uint8_t>(v,u);
00600       int val_b = b.at<uint8_t>(v,u);
00601     
00602       if (val_a != 0 && val_b == 0) false_neg++;
00603       if (val_a == 0 && val_b != 0) false_pos++;
00604     
00605       if (val_a != 0) total++;
00606     }
00607   }
00608 }
00609 
00610 void makeSymmetricOR(cv::Mat mat)
00611 {
00612   assert(mat.rows == mat.cols);
00613   int size = mat.rows;
00614   
00615   for (int u = 0; u < size; ++u)
00616   for (int v = 0; v < size; ++v)
00617   {
00618     if (mat.at<uint8_t>(v, u) != 0 ||
00619         mat.at<uint8_t>(u, v) != 0)
00620     {
00621       mat.at<uint8_t>(v, u) = 1;
00622       mat.at<uint8_t>(u, v) = 1;
00623     }
00624   }
00625 }
00626 
00627 void trainSURFMatcher(
00628   const KeyframeVector& keyframes,
00629   cv::FlannBasedMatcher& matcher)
00630 {  
00631   std::vector<cv::Mat> descriptors_vector;
00632   for (unsigned int kf_idx = 0; kf_idx < keyframes.size(); ++kf_idx)
00633   {
00634     const RGBDKeyframe& keyframe = keyframes[kf_idx];
00635     descriptors_vector.push_back(keyframe.descriptors);
00636   }
00637   matcher.add(descriptors_vector);
00638 
00639   matcher.train();
00640 }
00641 
00642 void trainSURFMatcher_Iterative(
00643   const KeyframeVector& keyframes,u_int min, u_int max,
00644   cv::FlannBasedMatcher& matcher)
00645 {
00646   std::vector<cv::Mat> descriptors_vector;
00647 
00648   for (unsigned int kf_idx = min; kf_idx < max; ++kf_idx)
00649   {
00650     const RGBDKeyframe& keyframe = keyframes[kf_idx];
00651     descriptors_vector.push_back(keyframe.descriptors);
00652   }
00653   matcher.add(descriptors_vector);
00654 
00655   matcher.train();
00656 }
00657 
00658 /*
00659 // a = array
00660 // s = array size
00661 // n = number of items
00662 void shuffle(int * a, int s, n)
00663 {
00664   int i = s - 1;
00665   int j, temp;
00666   
00667   while (i > 0)
00668   {
00669     j = rand() % (i + 1);
00670     temp = a[i];
00671     a[i] = a[j];
00672     a[j] = temp;
00673     i = i - 1;
00674   }
00675 }*/
00676 
00677 } // namespace ccny_rgbd
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends


lib_rgbdtools
Author(s): Ivan Dryanovski
autogenerated on Tue Aug 27 2013 10:33:54