备案 网站,wordpress 界面英文版,网站开启速度慢,常州西站建设规划关于算法原理请参考《基于SURF特征的图像与视频拼接技术的研究》。一、问题提出RANSAC的算法原理并不复杂#xff0c;比较复杂的地方在于“建立模型”和“评价模型”。我们经常看到的是采用“直线”或者“圆”作为基本模型进行“建立”#xff0c;而采用所有点到该“直线”或… 关于算法原理请参考《基于SURF特征的图像与视频拼接技术的研究》。一、问题提出 RANSAC的算法原理并不复杂比较复杂的地方在于“建立模型”和“评价模型”。我们经常看到的是采用“直线”或者“圆”作为基本模型进行“建立”而采用所有点到该“直线”或“圆”的欧拉距离作为标准来“评价”当然是越小越好。在经典的图像拼接算法中需要对特征点进行配对。采用的模型简单来说为“根据4对特征点计算出单应矩阵而后计算图1上所有对应的特征点经过这个‘单应矩阵’变化后得到的图片和图2上的距离之和“当然也是越小越好。 为了提高识别的效率前辈对算法进行了不懈的研究和提升目前看来用于图像拼接的RANSAC算法应该如下 及其改进算法二、算法实现 a)数据准备某大学图片很明显的有视场变化 b)算法分析参考《基于SURF特征的图像与视频拼接技术的研究和实现一 和 二》 现在思考RANSAC算法其实是”基于统计的配对算法“在进入RANSAC算法流程之前已经计算出来图1和图2上的特征点值了。我们不仅需要根据这些点值去预测模型而且需要去检测模型。这个模型也不是凭空随便找的而是以”透视变换“作为基础的关于透视变化请参考我前面的博文。 寻找的方法是首先找到符合某一模型的”内点集“而后根据这一”内点集“创建变换模型。 寻找”内点集“的方法就是直接从现有的数据中找出一部分数据计算出一个模型而后根据这个模型计算所有点的误差迭代多次得到最小误差的情况和对应的点集这个时候的模型就是接近正确的模型。 这个误差的计算方法也是设计出来的很可能还是统计值。 所以RANSAC很像是基于统计的一种计算可行解的模式。很多时候你不是需要从很多的数据中找出一个模型来吗比如马尔萨斯模型这个模型可能有函数还有参数。你猜测的是函数但是参数就可以通过这种模式来进行计算。 如果有比较好的评价函数最后你还可以比较几种函数的选择。所以RANSAC就是一种单模型下基于离散数据计算模型的方法。其实也是直观的、基础的、简洁的、有效的 这样我想起之前研究过的一种叫做”交叉检验“(cross check /cross validation)的东西。 定义在给定的建模样本中拿出大部分样本进行模型建立留小部分对建立的模型进行预报并将这小部分进行误差预报计算平方加和。然后当然是选取误差最小的模型和 相比较RANSAC和CROSS VALIDATION有两点不同。一个是模型的建立RANSAC是选择很少量的数据建立模型比如圆、线、透视变换而后大量数据做验证而CROSS需要较多的数据建立模型比如MLP神网较少的数据进行验证它也只有较少的数据了 c)解析OPENCV中的实现 为了实现图像的特征点的匹配并且最后实现图像拼接在OPENCV中实现了RANSAC算法及其改进算法 c.1 调用方法 //-- Step 3: 匹配 FlannBasedMatcher matcher;//BFMatcher为强制匹配 std::vector DMatch matches; matcher.match( descriptors_1, descriptors_2, matches ); //取最大最小距离 double max_dist 0; double min_dist 100; for( int i 0; i descriptors_1.rows; i ) { double dist matches[i].distance; if( dist min_dist ) min_dist dist; if( dist max_dist ) max_dist dist; } std::vector DMatch good_matches; for( int i 0; i descriptors_1.rows; i ) { if( matches[i].distance 3*min_dist )//这里的阈值选择了3倍的min_dist { good_matches.push_back( matches[i]); } } //画出good match Mat img_matches; drawMatches( img_1, keypoints_1, img_2, keypoints_2, good_matches, img_matches, Scalar::all(-1), Scalar::all(-1), vectorchar(), DrawMatchesFlags::NOT_DRAW_SINGLE_POINTS ); //-- Localize the object from img_1 in img_2 std::vectorPoint2f obj; std::vectorPoint2f scene; for( int i 0; i (int)good_matches.size(); i ) { obj.push_back( keypoints_1[ good_matches[i].queryIdx ].pt ); scene.push_back( keypoints_2[ good_matches[i].trainIdx ].pt ); } //直接调用ransac,计算单应矩阵 Mat H findHomography( obj, scene, CV_RANSAC ); c.2 原理分析 opencv中已经封装的很好了注意的是在实际使用中还可以对识别出来的结果做进一步的处理。 findHomography的定义参数分为points1和points2,参数3是method,然后是threshold(一般为3),最后为maskcv::Mat cv::findHomography( InputArray _points1, InputArray _points2, int method, double ransacReprojThreshold, OutputArray _mask ){ const double confidence 0.995; const int maxIters 2000; const double defaultRANSACReprojThreshold 3; bool result false; Mat points1 _points1.getMat(), points2 _points2.getMat(); Mat src, dst, H, tempMask; int npoints -1; for( int i 1; i 2; i ) { Mat p i 1 ? points1 : points2; Mat m i 1 ? src : dst; npoints p.checkVector(2, -1, false); if( npoints 0 ) { npoints p.checkVector(3, -1, false); if( npoints 0 ) CV_Error(Error::StsBadArg, The input arrays should be 2D or 3D point sets); if( npoints 0 ) return Mat(); convertPointsFromHomogeneous(p, p); } p.reshape(2, npoints).convertTo(m, CV_32F); } CV_Assert( src.checkVector(2) dst.checkVector(2) ); if( ransacReprojThreshold 0 ) ransacReprojThreshold defaultRANSACReprojThreshold; PtrPointSetRegistrator::Callback cb makePtrHomographyEstimatorCallback(); if( method 0 || npoints 4 ) { tempMask Mat::ones(npoints, 1, CV_8U); result cb-runKernel(src, dst, H) 0; } else if( method RANSAC ) result createRANSACPointSetRegistrator(cb, 4, ransacReprojThreshold, confidence, maxIters)-run(src, dst, H, tempMask); else if( method LMEDS ) result createLMeDSPointSetRegistrator(cb, 4, confidence, maxIters)-run(src, dst, H, tempMask); else CV_Error(Error::StsBadArg, Unknown estimation method); if( result npoints 4 ) { compressPoints( src.ptrPoint2f(), tempMask.ptruchar(), 1, npoints ); npoints compressPoints( dst.ptrPoint2f(), tempMask.ptruchar(), 1, npoints ); if( npoints 0 ) { Mat src1 src.rowRange(0, npoints); Mat dst1 dst.rowRange(0, npoints); src src1; dst dst1; if( method RANSAC || method LMEDS ) cb-runKernel( src, dst, H ); Mat H8(8, 1, CV_64F, H.ptrdouble()); createLMSolver(makePtrHomographyRefineCallback(src, dst), 10)-run(H8); } } if( result ) { if( _mask.needed() ) tempMask.copyTo(_mask); } else H.release(); return H;} 和RANSAC相关的是PtrPointSetRegistrator createRANSACPointSetRegistrator(const PtrPointSetRegistrator::Callback _cb, int _modelPoints, double _threshold, double _confidence, int _maxIters){ CV_Assert( !RANSACPointSetRegistrator_info_auto.name().empty() ); return PtrPointSetRegistrator( new RANSACPointSetRegistrator(_cb, _modelPoints, _threshold, _confidence, _maxIters));} class RANSACPointSetRegistrator : public PointSetRegistrator{public: RANSACPointSetRegistrator(const PtrPointSetRegistrator::Callback _cbPtrPointSetRegistrator::Callback(), int _modelPoints0, double _threshold0, double _confidence0.99, int _maxIters1000) : cb(_cb), modelPoints(_modelPoints), threshold(_threshold), confidence(_confidence), maxIters(_maxIters) { checkPartialSubsets true; } int findInliers( const Mat m1, const Mat m2, const Mat model, Mat err, Mat mask, double thresh ) const { cb-computeError( m1, m2, model, err ); mask.create(err.size(), CV_8U); CV_Assert( err.isContinuous() err.type() CV_32F mask.isContinuous() mask.type() CV_8U); const float* errptr err.ptrfloat(); uchar* maskptr mask.ptruchar(); float t (float)(thresh*thresh); int i, n (int)err.total(), nz 0; for( i 0; i n; i ) { int f errptr[i] t; maskptr[i] (uchar)f; nz f; } return nz; } bool getSubset( const Mat m1, const Mat m2, Mat ms1, Mat ms2, RNG rng, int maxAttempts1000 ) const { cv::AutoBufferint _idx(modelPoints); int* idx _idx; int i 0, j, k, iters 0; int esz1 (int)m1.elemSize(), esz2 (int)m2.elemSize(); int d1 m1.channels() 1 ? m1.channels() : m1.cols; int d2 m2.channels() 1 ? m2.channels() : m2.cols; int count m1.checkVector(d1), count2 m2.checkVector(d2); const int *m1ptr m1.ptrint(), *m2ptr m2.ptrint(); ms1.create(modelPoints, 1, CV_MAKETYPE(m1.depth(), d1)); ms2.create(modelPoints, 1, CV_MAKETYPE(m2.depth(), d2)); int *ms1ptr ms1.ptrint(), *ms2ptr ms2.ptrint(); CV_Assert( count modelPoints count count2 ); CV_Assert( (esz1 % sizeof(int)) 0 (esz2 % sizeof(int)) 0 ); esz1 / sizeof(int); esz2 / sizeof(int); for(; iters maxAttempts; iters) { for( i 0; i modelPoints iters maxAttempts; ) { int idx_i 0; for(;;) { idx_i idx[i] rng.uniform(0, count); for( j 0; j i; j ) if( idx_i idx[j] ) break; if( j i ) break; } for( k 0; k esz1; k ) ms1ptr[i*esz1 k] m1ptr[idx_i*esz1 k]; for( k 0; k esz2; k ) ms2ptr[i*esz2 k] m2ptr[idx_i*esz2 k]; if( checkPartialSubsets !cb-checkSubset( ms1, ms2, i1 )) { iters; continue; } i; } if( !checkPartialSubsets i modelPoints !cb-checkSubset(ms1, ms2, i)) continue; break; } return i modelPoints iters maxAttempts; } bool run(InputArray _m1, InputArray _m2, OutputArray _model, OutputArray _mask) const { bool result false; Mat m1 _m1.getMat(), m2 _m2.getMat(); Mat err, mask, model, bestModel, ms1, ms2; int iter, niters MAX(maxIters, 1); int d1 m1.channels() 1 ? m1.channels() : m1.cols; int d2 m2.channels() 1 ? m2.channels() : m2.cols; int count m1.checkVector(d1), count2 m2.checkVector(d2), maxGoodCount 0; RNG rng((uint64)-1); CV_Assert( cb ); CV_Assert( confidence 0 confidence 1 ); CV_Assert( count 0 count2 count ); if( count modelPoints ) return false; Mat bestMask0, bestMask; if( _mask.needed() ) { _mask.create(count, 1, CV_8U, -1, true); bestMask0 bestMask _mask.getMat(); CV_Assert( (bestMask.cols 1 || bestMask.rows 1) (int)bestMask.total() count ); } else { bestMask.create(count, 1, CV_8U); bestMask0 bestMask; } if( count modelPoints ) { if( cb-runKernel(m1, m2, bestModel) 0 ) return false; bestModel.copyTo(_model); bestMask.setTo(Scalar::all(1)); return true; } for( iter 0; iter niters; iter ) { int i, goodCount, nmodels; if( count modelPoints ) { bool found getSubset( m1, m2, ms1, ms2, rng ); if( !found ) { if( iter 0 ) return false; break; } } nmodels cb-runKernel( ms1, ms2, model ); if( nmodels 0 ) continue; CV_Assert( model.rows % nmodels 0 ); Size modelSize(model.cols, model.rows/nmodels); for( i 0; i nmodels; i ) { Mat model_i model.rowRange( i*modelSize.height, (i1)*modelSize.height ); goodCount findInliers( m1, m2, model_i, err, mask, threshold ); if( goodCount MAX(maxGoodCount, modelPoints-1) ) { std::swap(mask, bestMask); model_i.copyTo(bestModel); maxGoodCount goodCount; niters RANSACUpdateNumIters( confidence, (double)(count - goodCount)/count, modelPoints, niters ); } } } if( maxGoodCount 0 ) { if( bestMask.data ! bestMask0.data ) { if( bestMask.size() bestMask0.size() ) bestMask.copyTo(bestMask0); else transpose(bestMask, bestMask0); } bestModel.copyTo(_model); result true; } else _model.release(); return result; } void setCallback(const PtrPointSetRegistrator::Callback _cb) { cb _cb; } AlgorithmInfo* info() const; PtrPointSetRegistrator::Callback cb; int modelPoints; bool checkPartialSubsets; double threshold; double confidence; int maxIters;}; d)如何复用OPENCV中的实现于数据的统计研究 opencv中的封装是专门用于特征点的如果需要使用在其它地方还需要修改。但是我更关心的是RANSAC的算法应该用在哪里四、反思小结 即使是这样一个原理来说比较清楚的算法如果想从零开始进行实现还是很不容易的。所以积累算法资源、提高算法实现能力可能都是很重要的事情。 和cross validation算法比较和lmeds 最小平方中值估计法来自为知笔记(Wiz)转载于:https://www.cnblogs.com/jsxyhelu/p/6873309.html