target.cpp
Go to the documentation of this file.
00001 /*
00002  *  This file is part of ucl_drone 2016.
00003  *  For more information, refer
00004  *  to the corresponding header file.
00005  *
00006  *  \author Arnaud Jacques & Alexandre Leclere
00007  *  \date 2016
00008  *
00009  *  tuto:
00010  * http://docs.opencv.org/2.4/doc/tutorials/features2d/feature_homography/feature_homography.html
00011  */
00012 
00013 #include <ucl_drone/computer_vision/computer_vision.h>
00014 
00016 static const std::string PKG_DIR = ros::package::getPath("ucl_drone");
00017 
00018 #if EXTRACTOR_TYPE == TYPE_ORB
00019 Target::Target() : matcher(new cv::flann::LshIndexParams(20, 10, 2))
00020 #else
00021 Target::Target()
00022 #endif /* EXTRACTOR_TYPE == TYPE_ORB */
00023 {
00024 }
00025 
00026 Target::~Target()
00027 {
00028 }
00029 
00030 bool Target::init(const std::string relative_path)
00031 {
00032   // Read and import the picture file
00033   std::string str = PKG_DIR + relative_path;
00034   ROS_INFO("%s", str.c_str());
00035   cv::Mat target_img = cv::imread(str.c_str(), CV_LOAD_IMAGE_COLOR);
00036 
00037   if (!target_img.data)  // Check for invalid input
00038   {
00039     ROS_INFO("Could not open or find the image");
00040     return false;
00041   };
00042 
00043   image = target_img;
00044 
00045   // Determine position of corners and center on the image picture
00046   // This is used to draw green rectangle in the viewer and to estimate target world coordinates
00047   centerAndCorners.resize(5);
00048   centerAndCorners[0] = cv::Point(0, 0);
00049   centerAndCorners[1] = cv::Point(image.cols, 0);
00050   centerAndCorners[2] = cv::Point(image.cols, image.rows);
00051   centerAndCorners[3] = cv::Point(0, image.rows);
00052   centerAndCorners[4] = cv::Point(image.cols / 2.0, image.rows / 2.0);
00053 
00054   ROS_DEBUG("TARGET IMAGE DIMENSIONS: %d,%d", image.cols, image.rows);
00055 
00056   detector.detect(image, keypoints);
00057   extractor.compute(image, keypoints, descriptors);
00058 
00059   /*
00060   // We didn't know the exact effect of these lines, maybe it will improve matching performances
00061   // Read opencv API for more informations
00062   std::vector<cv::Mat> descriptor_vector;
00063   descriptor_vector.push_back(descriptors);
00064   matcher.add(descriptor_vector);
00065   matcher.train();
00066   */
00067 
00068   ROS_DEBUG("DESCRIPTORS DIMENSIONS: %d,%d", descriptors.cols, descriptors.rows);
00069   ROS_DEBUG("end Target::init");
00070   ROS_DEBUG("Target: %d keypoints", keypoints.size());
00071   return true;
00072 }
00073 
00074 // This function is called when target detection has to be performed on a picture
00075 bool Target::detect(cv::Mat cam_descriptors, std::vector< cv::KeyPoint >& cam_keypoints,
00076                     std::vector< cv::DMatch >& good_matches, std::vector< int >& idxs_to_remove,
00077                     std::vector< cv::Point2f >& target_coord
00078 #ifdef DEBUG_PROJECTION
00079                     ,
00080                     ucl_drone::Pose3D pose
00081 #endif
00082 #ifdef DEBUG_TARGET
00083                     ,
00084                     cv::Mat& image_cam
00085 #endif
00086                     )
00087 {
00088   ROS_DEBUG("begin Target::detect");
00089   // step 3: matching descriptors
00090 
00091   if (cam_descriptors.rows == 0)
00092   {
00093     return false;
00094   }
00095 
00096   bool target_detected = false;
00097 
00098   std::vector< cv::DMatch > matches;
00099   matcher.match(descriptors, cam_descriptors, matches);
00100 
00101   // step 5: Draw only "good" matches
00102   for (int i = 0; i < descriptors.rows; i++)
00103   {
00104     // ROS_DEBUG("Target::detect DISTANCE %d: %f", i, matches[i].distance);
00105     if (matches[i].distance < DIST_THRESHOLD * 0.75)  // 1.2)
00106     {
00107       good_matches.push_back(matches[i]);
00108     }
00109   }
00110   // std::vector< std::vector< cv::DMatch > > matches2;
00111   // matcher.radiusMatch(descriptors, cam_descriptors, matches2, DIST_THRESHOLD * 1.1);
00112   // for (int i = 0; i < matches2.size(); i++)
00113   // {
00114   //   for (int k = 0; k < matches2[i].size(); k++)
00115   //   {
00116   //     idxs_to_remove.push_back(matches2[i][k].trainIdx);
00117   //   }
00118   // }
00119 
00120   ROS_DEBUG("Target::detect matches:%d", good_matches.size());
00121   if (good_matches.size() > this->descriptors.rows / 6.0 && good_matches.size() > 8)
00122   {
00123     target_detected = true;
00124     this->position(cam_keypoints, good_matches, target_coord);
00125 
00126     std::vector< cv::Point2f >::const_iterator first = target_coord.begin();
00127     std::vector< cv::Point2f >::const_iterator last = target_coord.begin() + 4;
00128     std::vector< cv::Point2f > contour(first, last);
00129 
00130     for (unsigned i = 0; i < cam_keypoints.size(); i++)
00131     {
00132       double result = cv::pointPolygonTest(contour, cam_keypoints[i].pt, false);
00133       if (result >= 0)
00134       {
00135         idxs_to_remove.push_back(i);
00136       }
00137     }
00138   }
00139 
00140   std::sort(idxs_to_remove.begin(), idxs_to_remove.end());  //, customLess);
00141 
00142   // remove duplicates
00143   int k = 1;
00144   for (int i = 1; i < idxs_to_remove.size(); i++)
00145   {
00146     if (idxs_to_remove[i] != idxs_to_remove[i - 1])
00147     {
00148       idxs_to_remove[k] = idxs_to_remove[i];
00149       k++;
00150     }
00151   }
00152   idxs_to_remove.resize(k);
00153 
00154 #ifdef DEBUG_TARGET
00155   cv::Mat img_matches;
00156   cv::drawMatches(image, keypoints, image_cam, cam_keypoints, good_matches, img_matches,
00157                   cv::Scalar::all(-1), cv::Scalar::all(-1), std::vector< char >(),
00158                   cv::DrawMatchesFlags::NOT_DRAW_SINGLE_POINTS);
00159   cv::imshow(OPENCV_WINDOW, img_matches);
00160   cv::waitKey(3);
00161 #endif /*DEBUG_TARGET*/
00162 
00163   if (!target_detected)
00164     return false;
00165 
00166 #ifdef DEBUG_PROJECTION
00167 
00168   ROS_INFO("DEBUG_PROJECTION");
00169   std::vector< cv::Point2f > obj;
00170   std::vector< cv::Point2f > scene;
00171 
00172   for (int i = 0; i < good_matches.size(); i++)
00173   {
00174     // Get the keypoints from the good matches
00175     obj.push_back(keypoints[good_matches[i].queryIdx].pt);
00176     scene.push_back(cam_keypoints[good_matches[i].trainIdx].pt);
00177   }
00178 
00179   std::vector< uchar > mask;
00180   cv::Mat H =
00181       cv::findHomography(obj, scene, CV_RANSAC, 2, mask);  // arg#4: ransacReprojThreshold [pixel]
00182 
00183   std::string rel_error = "\nrelative_error = [";
00184   std::string abs_error = "\nabsolute_error = [";
00185   for (int i = 0; i < good_matches.size(); i++)
00186   {
00187     for (int j = i + 1; j < good_matches.size(); j++)
00188     {
00189       if (mask[i] == 1 && mask[j] == 1)
00190       {
00191         cv::Point2f target_point1 = this->keypoints[good_matches[i].queryIdx].pt;
00192         cv::Point2f target_point2 = this->keypoints[good_matches[j].queryIdx].pt;
00193         double dist1 = cv::norm(target_point1 - target_point2) * 0.274 / 240.0;  // 0.205 / 135.0;
00194 
00195         if (dist1 > 0.1)
00196         {
00197           std::vector< cv::Point2f > camera_points(2);
00198           camera_points[0] = cam_keypoints[good_matches[i].trainIdx].pt;
00199           camera_points[1] = cam_keypoints[good_matches[j].trainIdx].pt;
00200           std::vector< cv::Point3f > map_points(2);
00201           projection_2D(camera_points, pose, map_points, true);
00202 
00203           double dist2 = cv::norm(map_points[0] - map_points[1]);
00204 
00205           //   if (dist2 > 0.001)  // if two super-imposed identical descriptors in map
00206           //   {
00207           double relative_error = (dist2 - dist1) / dist1;
00208 
00209           double absolute_error = (dist2 - dist1);
00210 
00211           rel_error += to_string(relative_error) + ", ";
00212           abs_error += to_string(absolute_error) + ", ";
00213           //   }
00214         }
00215       }
00216     }
00217   }
00218   rel_error += "];\n\n";
00219   abs_error += "];\n\n";
00220 
00221   printf("%s", rel_error.c_str());
00222   printf("%s", abs_error.c_str());
00223 
00224 #endif /*DEBUG_PROJECTION*/
00225 
00226   ROS_DEBUG("end Target::detect");
00227   return true;
00228 }
00229 
00230 void Target::draw(cv::Mat cam_img, std::vector< cv::KeyPoint > cam_keypoints,
00231                   std::vector< cv::DMatch > good_matches, cv::Mat& img_matches)
00232 {
00233   cv::drawMatches(image, keypoints, cam_img, cam_keypoints, good_matches, img_matches,
00234                   cv::Scalar::all(-1), cv::Scalar::all(-1), std::vector< char >(),
00235                   cv::DrawMatchesFlags::NOT_DRAW_SINGLE_POINTS);
00236 
00237   // step 6: Localize the object
00238   std::vector< cv::Point2f > obj;
00239   std::vector< cv::Point2f > scene;
00240 
00241   for (int i = 0; i < good_matches.size(); i++)
00242   {
00243     // Get the keypoints from the good matches
00244     obj.push_back(keypoints[good_matches[i].queryIdx].pt);
00245     scene.push_back(cam_keypoints[good_matches[i].trainIdx].pt);
00246   }
00247 
00248   cv::Mat H = cv::findHomography(obj, scene, CV_RANSAC, 2);
00249 
00250   // step 7: Get the corners from the target
00251   std::vector< cv::Point2f > scene_corners(4);
00252 
00253   cv::perspectiveTransform(centerAndCorners, scene_corners, H);
00254 
00255   // step 8: Draw lines between the corners (the mapped object in the scene - image_2 )
00256   cv::line(img_matches, scene_corners[0] + centerAndCorners[1],
00257            scene_corners[1] + centerAndCorners[1], cv::Scalar(0, 255, 0), 4);
00258   cv::line(img_matches, scene_corners[1] + centerAndCorners[1],
00259            scene_corners[2] + centerAndCorners[1], cv::Scalar(0, 255, 0), 4);
00260   cv::line(img_matches, scene_corners[2] + centerAndCorners[1],
00261            scene_corners[3] + centerAndCorners[1], cv::Scalar(0, 255, 0), 4);
00262   cv::line(img_matches, scene_corners[3] + centerAndCorners[1],
00263            scene_corners[0] + centerAndCorners[1], cv::Scalar(0, 255, 0), 4);
00264 
00265   ROS_DEBUG("corner[0] %f %f", scene_corners[0].x, scene_corners[0].y);
00266   ROS_DEBUG("corner[1] %f %f", scene_corners[1].x, scene_corners[1].y);
00267   ROS_DEBUG("corner[2] %f %f", scene_corners[2].x, scene_corners[2].y);
00268   ROS_DEBUG("corner[3] %f %f", scene_corners[3].x, scene_corners[3].y);
00269 
00270   ROS_DEBUG("end Target::draw");
00271 }
00272 
00273 void Target::position(std::vector< cv::KeyPoint > cam_keypoints,
00274                       std::vector< cv::DMatch > good_matches, std::vector< cv::Point2f >& coord)
00275 {
00276   // step 6: Localize the object
00277   std::vector< cv::Point2f > obj;
00278   std::vector< cv::Point2f > scene;
00279 
00280   for (int i = 0; i < good_matches.size(); i++)
00281   {
00282     // Get the keypoints from the good matches
00283     obj.push_back(keypoints[good_matches[i].queryIdx].pt);
00284     scene.push_back(cam_keypoints[good_matches[i].trainIdx].pt);
00285   }
00286 
00287   cv::Mat H = cv::findHomography(obj, scene, CV_RANSAC, 2);
00288 
00289   // step 7: Get the corners from the target
00290   coord.resize(5);
00291 
00292   cv::perspectiveTransform(centerAndCorners, coord, H);
00293 
00294   ROS_DEBUG("end Target::position");
00295 }
00296 
00297 bool customLess(cv::DMatch a, cv::DMatch b)
00298 {
00299   return a.trainIdx < b.trainIdx;
00300 }


ucl_drone
Author(s): dronesinma
autogenerated on Sat Jun 8 2019 20:51:53