GSoC2011SfM
0.1
Google Summer of Code 2011: Structure from motion
|
00001 00002 #include "macro.h" //SFM_EXPORTS 00003 00004 #include <opencv2/imgproc/imgproc.hpp> 00005 #include <opencv2/video/tracking.hpp> 00006 #include <algorithm> 00007 00008 #include "PointsMatcher.h" 00009 #include "PointsToTrack.h" 00010 00011 namespace OpencvSfM{ 00012 using cv::Mat; 00013 using cv::Ptr; 00014 using std::vector; 00015 using cv::KeyPoint; 00016 using cv::DMatch; 00017 using cv::Size; 00018 using cv::Rect; 00019 00020 PointsMatcher::PointsMatcher( const cv::Ptr<cv::DescriptorMatcher>& matcher ) 00021 { 00022 CV_DbgAssert( !matcher.empty( ) ); 00023 matcher_ = matcher; 00024 INIT_MUTEX( thread_concurr ); 00025 } 00026 00027 PointsMatcher::PointsMatcher( ) 00028 { 00029 matcher_ = NULL; 00030 INIT_MUTEX( thread_concurr ); 00031 } 00032 00033 PointsMatcher::PointsMatcher( const PointsMatcher& copy ) 00034 { 00035 INIT_MUTEX( thread_concurr ); 00036 pointCollection_=copy.pointCollection_; 00037 matcher_ = copy.matcher_->clone( true ); 00038 } 00039 00040 PointsMatcher::~PointsMatcher( void ) 00041 { 00042 //matcher_ is freed thanks to cv::Ptr 00043 //idem for pointCollection_ 00044 } 00045 const cv::KeyPoint &PointsMatcher::getKeypoint( int numKey ) const 00046 { 00047 CV_DbgAssert( pointCollection_.size( )>0 ); 00048 return pointCollection_[ 0 ]->getKeypoint( numKey ); 00049 } 00050 00051 void PointsMatcher::add( Ptr<PointsToTrack> pointCollection ) 00052 { 00053 P_MUTEX( thread_concurr ); 00054 pointCollection_.push_back( pointCollection ); 00055 V_MUTEX( thread_concurr ); 00056 } 00057 00058 void PointsMatcher::clear( ) 00059 { 00060 P_MUTEX( thread_concurr ); 00061 matcher_->clear( ); 00062 /* 00063 for(size_t i = 0; i<pointCollection_.size(); ++i) 00064 pointCollection_[i]->free_descriptors(); 00065 */ 00066 V_MUTEX( thread_concurr ); 00067 } 00068 00069 void PointsMatcher::train( ) 00070 { 00071 P_MUTEX( thread_concurr ); 00072 matcher_->clear( ); 00073 00074 vector<Mat> pointsDesc; 00075 Ptr<PointsToTrack> pointCollection; 00076 vector< Ptr< PointsToTrack > >::iterator it = 00077 pointCollection_.begin( ), 00078 it_end = pointCollection_.end( ); 00079 while( it != it_end ) 00080 { 00081 pointCollection = *it; 00082 pointCollection->computeKeypointsAndDesc( false ); 00083 00084 pointsDesc.push_back( pointCollection->getDescriptors( ) ); 00085 00086 pointCollection->free_descriptors();//save memory... 00087 00088 it++; 00089 } 00090 00091 00092 matcher_->add( pointsDesc ); 00093 matcher_->train( ); 00094 V_MUTEX( thread_concurr ); 00095 } 00096 00097 bool PointsMatcher::isMaskSupported( ) 00098 { 00099 return matcher_->isMaskSupported( ); 00100 } 00101 00102 void PointsMatcher::match( cv::Ptr<PointsToTrack> queryPoints, 00103 std::vector<cv::DMatch>& matches, 00104 const std::vector<cv::Mat>& masks ) 00105 { 00106 train( ); 00107 00108 P_MUTEX( thread_concurr ); 00109 queryPoints->computeKeypointsAndDesc( false ); 00110 00111 vector<KeyPoint> keyPoints=queryPoints->getKeypoints( ); 00112 Mat descMat=queryPoints->getDescriptors( ); 00113 00114 CV_DbgAssert( !keyPoints.empty( ) ); 00115 CV_DbgAssert( !descMat.empty( ) ); 00116 00117 matcher_->match( descMat, matches, masks ); 00118 V_MUTEX( thread_concurr ); 00119 00120 queryPoints->free_descriptors(); 00121 } 00122 00123 void PointsMatcher::knnMatch( Ptr<PointsToTrack> queryPoints, 00124 vector<vector<DMatch> >& matches, int knn, 00125 const vector<Mat>& masks, bool compactResult ) 00126 { 00127 train( ); 00128 00129 P_MUTEX( thread_concurr ); 00130 queryPoints->computeKeypointsAndDesc( false ); 00131 00132 vector<KeyPoint> keyPoints=queryPoints->getKeypoints( ); 00133 Mat descMat=queryPoints->getDescriptors( ); 00134 00135 CV_DbgAssert( !keyPoints.empty( ) ); 00136 CV_DbgAssert( !descMat.empty( ) ); 00137 00138 matcher_->knnMatch( descMat, matches, knn, masks, compactResult ); 00139 V_MUTEX( thread_concurr ); 00140 00141 queryPoints->free_descriptors(); 00142 } 00143 00144 void PointsMatcher::radiusMatch( cv::Ptr<PointsToTrack> queryPoints, 00145 vector<vector<DMatch> >& matches, float maxDistance, 00146 const vector<Mat>& masks, bool compactResult ) 00147 { 00148 train( ); 00149 00150 P_MUTEX( thread_concurr ); 00151 queryPoints->computeKeypointsAndDesc( false ); 00152 00153 vector<KeyPoint> keyPoints=queryPoints->getKeypoints( ); 00154 Mat descMat=queryPoints->getDescriptors( ); 00155 00156 CV_DbgAssert( !keyPoints.empty( ) ); 00157 CV_DbgAssert( !descMat.empty( ) ); 00158 00159 matcher_->radiusMatch( descMat, matches, maxDistance, masks, compactResult ); 00160 V_MUTEX( thread_concurr ); 00161 00162 queryPoints->free_descriptors(); 00163 } 00164 00165 bool PointsMatcher::empty( ) const 00166 { 00167 return pointCollection_.empty( ) || matcher_->empty( ); 00168 } 00169 00170 Ptr<PointsMatcher> PointsMatcher::clone( bool emptyTrainData ) 00171 { 00172 P_MUTEX( thread_concurr ); 00173 Ptr<PointsMatcher> outPointMatcher=new PointsMatcher( matcher_->clone( emptyTrainData ) ); 00174 if( !emptyTrainData ) 00175 outPointMatcher->pointCollection_=pointCollection_; 00176 V_MUTEX( thread_concurr ); 00177 00178 return outPointMatcher; 00179 } 00180 00181 void PointsMatcher::crossMatch( Ptr<PointsMatcher> otherMatcher, 00182 vector<DMatch>& matches, 00183 const std::vector<cv::Mat>& masks ) 00184 { 00185 train( ); 00186 00187 //TODO: for now this function only work with matcher having only one picture 00188 CV_DbgAssert( this->pointCollection_.size( )==1 ); 00189 CV_DbgAssert( otherMatcher->pointCollection_.size( )==1 ); 00190 00191 if( matches.empty( ) ) 00192 { 00193 //as we don't have matches for img1 -> img2, compute them: 00194 this->match( otherMatcher->pointCollection_[ 0 ], matches, masks ); 00195 } 00196 00197 00198 //now construct the vector of DMatch, but in the other way ( 2 -> 1 ): 00199 vector<DMatch> matchesOtherWay; 00200 P_MUTEX( thread_concurr ); 00201 otherMatcher->match( pointCollection_[ 0 ], matchesOtherWay, masks ); 00202 V_MUTEX( thread_concurr ); 00203 00204 //now check for reciprocity: 00205 size_t nbPoints = matches.size( ), 00206 nbPoints1 = matchesOtherWay.size( ); 00207 for( size_t i=0; i<nbPoints; i++ ) 00208 { 00209 DMatch d1=matches[ i ]; 00210 bool found = false; 00211 for( size_t j=0; j<nbPoints1 && !found ; ++j ) 00212 { 00213 DMatch d2=matchesOtherWay[ j ]; 00214 found = (d1.trainIdx == d2.queryIdx) && (d1.queryIdx == d2.trainIdx); 00215 } 00216 if( !found ) 00217 { 00218 //remove the current match! 00219 nbPoints--;//because we have a match less! 00220 matches[ i ]=matches[ nbPoints ]; 00221 matches.pop_back( ); 00222 i--;//because we have to retest ith point! 00223 } 00224 } 00225 matchesOtherWay.clear( ); 00226 } 00227 void PointsMatcher::drawMatches( const Mat& img1, 00228 const vector<cv::KeyPoint>& keypoints1, 00229 const vector<cv::KeyPoint>& keypoints2, 00230 const vector<cv::DMatch>& matches1to2, Mat& outImg, 00231 const cv::Scalar& matchColor, const cv::Scalar& singlePointColor, 00232 const std::vector<char>& matchesMask, int flags ) 00233 { 00234 Size size( img1.cols, img1.rows ); 00235 if( flags & cv::DrawMatchesFlags::DRAW_OVER_OUTIMG ) 00236 { 00237 if( size.width > outImg.cols || size.height > outImg.rows ) 00238 CV_Error( CV_StsBadSize, "outImg has size less than needed to draw img1" ); 00239 } 00240 else 00241 { 00242 outImg.create( size, CV_MAKETYPE( img1.depth( ), 3 ) ); 00243 00244 if( img1.type( ) == CV_8U ) 00245 cv::cvtColor( img1, outImg, CV_GRAY2BGR ); 00246 else 00247 img1.copyTo( outImg ); 00248 } 00249 00250 // draw keypoints 00251 if( !( flags & cv::DrawMatchesFlags::NOT_DRAW_SINGLE_POINTS ) ) 00252 { 00253 drawKeypoints( outImg, keypoints1, outImg, singlePointColor, 00254 flags + cv::DrawMatchesFlags::DRAW_OVER_OUTIMG ); 00255 } 00256 00257 cv::RNG& rng = cv::theRNG( ); 00258 bool isRandMatchColor = matchColor == cv::Scalar::all( -1 ); 00259 vector<DMatch>::size_type total_size=matches1to2.size( ); 00260 // draw matches 00261 for( vector<DMatch>::size_type m = 0; m < total_size; m++ ) 00262 { 00263 int i1 = matches1to2[ m ].trainIdx; 00264 int i2 = matches1to2[ m ].queryIdx; 00265 if( matchesMask.empty( ) || matchesMask[ m ] ) 00266 { 00267 const KeyPoint &kp1 = keypoints1[ i1 ], &kp2 = keypoints2[ i2 ]; 00268 00269 cv::Scalar color = isRandMatchColor ? 00270 cv::Scalar( rng( 256 ), rng( 256 ), rng( 256 ) ) : matchColor; 00271 00272 cv::Point center1( cvRound( kp1.pt.x ), cvRound( kp1.pt.y ) ); 00273 cv::Point center2( cvRound( kp2.pt.x ), cvRound( kp2.pt.y ) ); 00274 int radius = 1; 00275 cv::circle( outImg, center1, radius, color, 1, CV_AA ); 00276 //cv::circle( outImg, center2, radius, color, 1, CV_AA ); 00277 cv::line( outImg, center1, center2, color, 1, CV_AA ); 00278 } 00279 } 00280 00281 } 00282 00283 00284 void PointsMatcher::read( const cv::FileNode& node, 00285 PointsMatcher& points ) 00286 { 00287 std::string myName=node.name( ); 00288 if( myName != "PointsMatcher" ) 00289 return;//this node is not for us... 00290 00291 //This matcher need only point coordinates as the matcher is already loaded: 00292 00293 if( node.empty( ) || !node.isSeq( ) ) 00294 CV_Error( CV_StsError, "PointsMatcher FileNode is not correct!" ); 00295 00296 int numImg=0; 00297 cv::FileNodeIterator it = node.begin( ), it_end = node.end( ); 00298 while( it != it_end ) 00299 { 00300 Ptr<PointsToTrack> ptt_tmp = Ptr<PointsToTrack>( new PointsToTrack( numImg )); 00301 cv::FileNode node_tmp = ( *it )[ "PointsToTrack" ]; 00302 PointsToTrack::read( node_tmp, *ptt_tmp ); 00303 points.pointCollection_.push_back( ptt_tmp ); 00304 it++; 00305 numImg++; 00306 } 00307 } 00308 00309 void PointsMatcher::write ( cv::FileStorage& fs, 00310 const PointsMatcher& points ) 00311 { 00312 vector<PointsToTrack>::size_type key_size = points.pointCollection_.size( ); 00313 00314 fs << "PointsMatcher" << "["; 00315 00316 for ( vector<PointsToTrack>::size_type i=0; i < key_size; i++ ) 00317 { 00318 PointsToTrack::write( fs, *points.pointCollection_[ i ] ); 00319 } 00320 fs << "]"; 00321 } 00322 00323 00324 PointsMatcherOpticalFlow::PointsMatcherOpticalFlow( std::string name_of_algo, 00325 double dist_allowed ) 00326 { 00327 transform(name_of_algo.begin(), name_of_algo.end(), 00328 name_of_algo.begin(),tolower); 00329 id_algo = 0; 00330 max_distance = dist_allowed; 00331 00332 if( name_of_algo == "opticalflowpyrlk" ) 00333 id_algo = 1; 00334 if( name_of_algo == "opticalflowfarneback" ) 00335 id_algo = 2; 00336 if( name_of_algo == "opticalflowbm" ) 00337 id_algo = 3; 00338 if( name_of_algo == "opticalflowhs" ) 00339 id_algo = 4; 00340 if( name_of_algo == "opticalflowlk" ) 00341 id_algo = 5; 00342 } 00343 00344 void PointsMatcherOpticalFlow::clear( ) 00345 { 00346 //nothing to do... 00347 } 00348 00349 void PointsMatcherOpticalFlow::train( ) 00350 { 00351 //nothing to do... 00352 } 00353 00354 bool PointsMatcherOpticalFlow::isMaskSupported( ) 00355 { 00356 return false; 00357 } 00358 00359 void PointsMatcherOpticalFlow::match( cv::Ptr<PointsToTrack> queryPoints, 00360 std::vector<cv::DMatch>& matches, 00361 const std::vector<cv::Mat>& masks ) 00362 { 00363 P_MUTEX( thread_concurr ); 00364 00365 if(pointCollection_[0]->getKeypoints( ).empty()) 00366 pointCollection_[0]->computeKeypoints(); 00367 const vector<KeyPoint>& keyPoints=pointCollection_[0]->getKeypoints( ); 00368 vector<cv::Point2f> keyPointsIn; 00369 vector<cv::Point2f> keyPointsOut; 00370 vector<uchar> status; 00371 vector<float> error; 00372 00373 cv::Mat img1 = pointCollection_[0]->getImage(); 00374 cv::Mat img2 = queryPoints->getImage(); 00375 CV_Assert( !img1.empty() && !img2.empty() ); 00376 00377 for( size_t cpt = 0; cpt<keyPoints.size(); cpt++) 00378 { 00379 const KeyPoint &kp = keyPoints[cpt]; 00380 keyPointsIn.push_back( cv::Point2f(kp.pt.x, kp.pt.y) ); 00381 } 00382 00383 switch( id_algo ) 00384 { 00385 case 1://1: OpticalFlowPyrLK 00386 cv::calcOpticalFlowPyrLK(img1, img2, keyPointsIn, keyPointsOut, 00387 status, error ); 00388 break;/* 00389 case 2://2: OpticalFlowFarneback 00390 cv::OpticalFlowFarneback(img1, img2, keyPointsIn, keyPointsOut, 00391 status, error ); 00392 break; 00393 case 3://3: OpticalFlowBM 00394 cv::OpticalFlowBM(img1, img2, keyPointsIn, keyPointsOut, 00395 status, error ); 00396 break; 00397 case 4://4: OpticalFlowHS 00398 cv::OpticalFlowHS(img1, img2, keyPointsIn, keyPointsOut, 00399 status, error ); 00400 break; 00401 case 5://5: OpticalFlowLK 00402 cv::OpticalFlowLK(img1, img2, keyPointsIn, keyPointsOut, 00403 status, error ); 00404 break; 00405 default:;*/ 00406 } 00407 00408 //construct the DMatch vector (find the closest point in queryPoints) 00409 if(queryPoints->getKeypoints( ).empty()) 00410 queryPoints->computeKeypoints(); 00411 const vector< KeyPoint >& points = queryPoints->getKeypoints(); 00412 for( size_t cpt = 0; cpt<keyPointsOut.size(); cpt++) 00413 { 00414 if( status[cpt]!=0 ) 00415 { 00416 float dist_min = 1e10; 00417 int idx_min = 0; 00418 for(size_t i = 0; i<points.size() ; ++i) 00419 { 00420 const cv::KeyPoint& kp = points[i]; 00421 float dist = sqrt( (keyPointsOut[cpt].x - kp.pt.x)*(keyPointsOut[cpt].x - kp.pt.x) 00422 + (keyPointsOut[cpt].y - kp.pt.y)*(keyPointsOut[cpt].y - kp.pt.y) ); 00423 if(dist<dist_min) 00424 { 00425 dist_min = dist; 00426 idx_min = i; 00427 } 00428 } 00429 if( dist_min<max_distance ) 00430 matches.push_back( cv::DMatch(idx_min, cpt, dist_min) );//not really the correct dist... 00431 } 00432 } 00433 V_MUTEX( thread_concurr ); 00434 } 00435 00436 void PointsMatcherOpticalFlow::knnMatch( Ptr<PointsToTrack> queryPoints, 00437 vector<vector<DMatch> >& matches, int knn, 00438 const vector<Mat>& masks, bool compactResult ) 00439 { 00440 if( knn!=1 ) 00441 CV_Error( CV_StsError, "not yet implemented..." ); 00442 00443 vector<DMatch> matchesTmp; 00444 match( queryPoints, matchesTmp, masks ); 00445 matches.push_back(matchesTmp); 00446 } 00447 00448 void PointsMatcherOpticalFlow::radiusMatch( cv::Ptr<PointsToTrack> queryPoints, 00449 vector<vector<DMatch> >& matches, float maxDistance, 00450 const vector<Mat>& masks, bool compactResult ) 00451 { 00452 double tmp = max_distance; 00453 vector<DMatch> matchesTmp; 00454 match( queryPoints, matchesTmp, masks ); 00455 matches.push_back(matchesTmp); 00456 max_distance = tmp;//restore the old value of max_distance 00457 } 00458 00459 bool PointsMatcherOpticalFlow::empty( ) const 00460 { 00461 return pointCollection_.empty( ); 00462 } 00463 00464 Ptr<PointsMatcher> PointsMatcherOpticalFlow::clone( bool emptyTrainData ) 00465 { 00466 P_MUTEX( thread_concurr ); 00467 PointsMatcherOpticalFlow* outPointMatcher = 00468 new PointsMatcherOpticalFlow( "void" ); 00469 outPointMatcher->id_algo = this->id_algo; 00470 if( !emptyTrainData ) 00471 outPointMatcher->pointCollection_ = pointCollection_; 00472 V_MUTEX( thread_concurr ); 00473 00474 return outPointMatcher; 00475 } 00476 00477 }