00001 /********************************************************************* 00002 * 00003 * Software License Agreement (BSD License) 00004 * 00005 * Copyright (c) 2000-2008, Intel Corporation, all rights reserved. 00006 * Copyright (c) 2009, Willow Garage Inc., all rights reserved. 00007 * Copyright (c) 2015-2016, Jiri Horner. 00008 * All rights reserved. 00009 * 00010 * Redistribution and use in source and binary forms, with or without 00011 * modification, are permitted provided that the following conditions 00012 * are met: 00013 * 00014 * * Redistributions of source code must retain the above copyright 00015 * notice, this list of conditions and the following disclaimer. 00016 * * Redistributions in binary form must reproduce the above 00017 * copyright notice, this list of conditions and the following 00018 * disclaimer in the documentation and/or other materials provided 00019 * with the distribution. 00020 * * Neither the name of the author nor the names of its 00021 * contributors may be used to endorse or promote products derived 00022 * from this software without specific prior written permission. 00023 * 00024 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 00025 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 00026 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 00027 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 00028 * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 00029 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 00030 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 00031 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 00032 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 00033 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 00034 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 00035 * POSSIBILITY OF SUCH DAMAGE. 00036 * 00037 *********************************************************************/ 00038 00039 #include <combine_grids/features_matcher.h> 00040 00041 #include <combine_grids/estimate_rigid_transform.h> 00042 00043 #include <opencv2/core/utility.hpp> 00044 #include <opencv2/video/tracking.hpp> 00045 00046 #include <ros/console.h> 00047 #include <ros/assert.h> 00048 00049 namespace combine_grids 00050 { 00051 namespace internal 00052 { 00053 /* modified implementation of match from BestOf2NearestMatcher */ 00054 void AffineBestOf2NearestMatcher::match( 00055 const cv::detail::ImageFeatures &features1, 00056 const cv::detail::ImageFeatures &features2, 00057 cv::detail::MatchesInfo &matches_info) 00058 { 00059 (*impl_)(features1, features2, matches_info); 00060 00061 ROS_DEBUG("AffineMatcher: have %lu matches", matches_info.matches.size()); 00062 00063 // Check if it makes sense to find homography 00064 if (matches_info.matches.size() < static_cast<size_t>(num_matches_thresh1_)) 00065 return; 00066 00067 // Construct point-point correspondences for homography estimation 00068 // Points are centered s.t. image center is (0,0). This is similar to other 00069 // matchers a shows better results in practice (numerical stability?). 00070 cv::Mat src_points(1, static_cast<int>(matches_info.matches.size()), 00071 CV_32FC2); 00072 cv::Mat dst_points(1, static_cast<int>(matches_info.matches.size()), 00073 CV_32FC2); 00074 for (size_t i = 0; i < matches_info.matches.size(); ++i) { 00075 const cv::DMatch &m = matches_info.matches[i]; 00076 00077 cv::Point2f p = features1.keypoints[static_cast<size_t>(m.queryIdx)].pt; 00078 p.x -= features1.img_size.width * 0.5f; 00079 p.y -= features1.img_size.height * 0.5f; 00080 src_points.at<cv::Point2f>(0, static_cast<int>(i)) = p; 00081 00082 p = features2.keypoints[static_cast<size_t>(m.trainIdx)].pt; 00083 p.x -= features2.img_size.width * 0.5f; 00084 p.y -= features2.img_size.height * 0.5f; 00085 dst_points.at<cv::Point2f>(0, static_cast<int>(i)) = p; 00086 } 00087 00088 // Find pair-wise motion 00089 // this is my special modified version of estimateRigidTransform 00090 matches_info.H = estimateRigidTransform(src_points, dst_points, 00091 matches_info.inliers_mask, false); 00092 ROS_DEBUG_STREAM("estimate:\n" << matches_info.H); 00093 00094 if (matches_info.H.empty()) { 00095 // could not find trasformation 00096 matches_info.confidence = 0; 00097 matches_info.num_inliers = 0; 00098 return; 00099 } 00100 00101 // extend H to represent linear tranformation in homogeneous coordinates 00102 matches_info.H.push_back(cv::Mat::zeros(1, 3, CV_64F)); 00103 matches_info.H.at<double>(2, 2) = 1; 00104 00105 /* TODO: should we handle determinant ~ 0 (can it happen due to agressive 00106 * scaling?) */ 00107 00108 // Find number of inliers 00109 matches_info.num_inliers = 0; 00110 for (size_t i = 0; i < matches_info.inliers_mask.size(); ++i) 00111 if (matches_info.inliers_mask[i]) 00112 matches_info.num_inliers++; 00113 00114 ROS_DEBUG("num_inliers %d", matches_info.num_inliers); 00115 00116 // These coeffs are from paper M. Brown and D. Lowe. "Automatic Panoramic 00117 // Image Stitching using Invariant Features" 00118 matches_info.confidence = 00119 matches_info.num_inliers / (8 + 0.3 * matches_info.matches.size()); 00120 00121 /* we don't want any cuttof for merging maps. TODO: allow to set this in 00122 * constructor. */ 00123 // Set zero confidence to remove matches between too close images, as they 00124 // don't provide additional information anyway. The threshold was set 00125 // experimentally. 00126 // matches_info.confidence = 00127 // matches_info.confidence > 3. ? 0. : matches_info.confidence; 00128 00129 /* Unlike other matchers it makes no sense to rerun estimation on liers only. 00130 * estimateRigidTransform already did this for us. So we are done here. */ 00131 } 00132 00133 } // namespace internal 00134 } // namespace combine_grids