[꼭대기] openCV 의 find Homography 함수 분석 및 RANSAC 알고리즘 에 대한 상세 한 설명

본 고 는 openCV 의 RANSAC 코드 를 모두 선택 하여 RANSAC 알고리즘 을 잘 이해 하도록 분석 하고 설명 한다.코드 는 내 가 모두 시험 해 보 았 으 니 직접 실행 할 수 있다.
컴퓨터 시각 과 이미지 처리 등 여러 분야 에서 RANSAC 알고리즘 을 사용 해 야 한다.openCV 에 도 사람들 이 사용 할 수 있 도록 봉 인 된 RANSAC 알고리즘 이 있다.RANSAC 알고리즘 에 대한 응용 프로그램 은 제 다른 블 로 그 를 볼 수 있 습 니 다.
SIFT 와 RANSAC 알고리즘 (openCV 프레임 워 크) 을 이용 하여 물체 의 검 측 과 포 지 셔 닝 을 실현 하고 행렬 (find FundamentalMat 와 find Homography 의 비교) 을 구 합 니 다.
그러나 며칠 전 사제 가 openCV 가 자체 적 으로 가지 고 있 는 RANSAC 알고리즘 을 사용 할 때 실험의 운행 시간 이 입력 데이터 가 증가 함 에 따라 증가 하지 않 고 이론 적 으로 맞지 않 는 다 는 것 을 발견 했다.그래서 저 는 시간 이 좀 걸 려 서 openCV 에서 RANSAC 에 관 한 소스 코드 를 모두 복사 해서 연 구 했 습 니 다.RANSAC 알고리즘 의 실제 운행 과정 을 더욱 명확 하 게 이해 할 수 있 도록 합 니 다.
일단 두 가지 보 겠 습 니 다.
4. 567913. 위의 두 가지 유형 중에서 Cv 모델 Estimator 2 는 하나의 기본 적 인 유형 으로 이름 에서 알 수 있 듯 이 이 유형 은 모델 을 평가 하 는 데 사용 된다.안에서 많은 가상 함 수 를 제공 하 는 것 을 볼 수 있다. 이런 함 수 는 매우 많다. 예 를 들 어 runRANSAC 는 RANSAC 방법 으로 단 응 행렬 을 계산 하 는 것 이 고 runLMeDS 는 LMeDS 방법 으로 단 응 행렬 을 계산 하 는 것 이다. 우 리 는 여기 서 RANSAC 방법 만 설명 하기 때문에 다른 필요 하지 않 은 내용 은 내 가 직접 주석 을 달 았 다.
Cv Homography Estimator 는 Cv Model Estimator 2 에서 계승 되 었 습 니 다. 마찬가지 로 이름 에서 도 알 수 있 듯 이 이런 사용 은 단일 행렬 을 평가 할 수 있 습 니 다.
다음은 두 가지 유형의 구조 함수 와 분석 함수 입 니 다. 이것 은 더 이상 할 말 이 없습니다. 기본적으로 모두 기본 적 인 것 입 니 다.
//       ,             
//     0。99         2000
class CvModelEstimator2
{
public:
    CvModelEstimator2(int _modelPoints, CvSize _modelSize, int _maxBasicSolutions);
    virtual ~CvModelEstimator2();

    virtual int runKernel( const CvMat* m1, const CvMat* m2, CvMat* model )=0;
    //virtual bool runLMeDS( const CvMat* m1, const CvMat* m2, CvMat* model,
                          // CvMat* mask, double confidence=0.99, int maxIters=2000 );
    virtual bool runRANSAC( const CvMat* m1, const CvMat* m2, CvMat* model,
                            CvMat* mask, double threshold,
                            double confidence=0.99, int maxIters=2000 );
    virtual bool refine( const CvMat*, const CvMat*, CvMat*, int ) { return true; }
    //virtual void setSeed( int64 seed );

protected:
    virtual void computeReprojError( const CvMat* m1, const CvMat* m2,
                                     const CvMat* model, CvMat* error ) = 0;
    virtual int findInliers( const CvMat* m1, const CvMat* m2,
                             const CvMat* model, CvMat* error,
                             CvMat* mask, double threshold );
    virtual bool getSubset( const CvMat* m1, const CvMat* m2,
                            CvMat* ms1, CvMat* ms2, int maxAttempts=1000 );
    virtual bool checkSubset( const CvMat* ms1, int count );

    CvRNG rng;
    int modelPoints;
    CvSize modelSize;
    int maxBasicSolutions;
    bool checkPartialSubsets;
};
//         
class CvHomographyEstimator : public CvModelEstimator2
{
public:
    CvHomographyEstimator( int modelPoints );

    virtual int runKernel( const CvMat* m1, const CvMat* m2, CvMat* model );
    virtual bool refine( const CvMat* m1, const CvMat* m2,
                         CvMat* model, int maxIters );
	

protected:
    virtual void computeReprojError( const CvMat* m1, const CvMat* m2,
                                     const CvMat* model, CvMat* error );
};
<pre name="code" class="cpp">//    
CvModelEstimator2::CvModelEstimator2(int _modelPoints, CvSize _modelSize, int _maxBasicSolutions)
{
    modelPoints = _modelPoints;
    modelSize = _modelSize;
    maxBasicSolutions = _maxBasicSolutions;
    checkPartialSubsets = true;
    rng = cvRNG(-1);
}
//    
CvModelEstimator2::~CvModelEstimator2()
{
}

CvHomographyEstimator::CvHomographyEstimator(int _modelPoints)
    : CvModelEstimator2(_modelPoints, cvSize(3,3), 1)
{
    assert( _modelPoints == 4 || _modelPoints == 5 );
    checkPartialSubsets = false;

}
 接下来到重点了。runRANSAC方法就是通过RANSAC来计算矩阵 
  
  
 
<pre name="code" class="cpp">bool CvModelEstimator2::runRANSAC( const CvMat* m1, const CvMat* m2, CvMat* model,
                                    CvMat* mask0, double reprojThreshold,
                                    double confidence, int maxIters )
{
    bool result = false;
    cv::Ptr<CvMat> mask = cvCloneMat(mask0);   //    ,       
    cv::Ptr<CvMat> models, err, tmask;
    cv::Ptr<CvMat> ms1, ms2;

    int iter, niters = maxIters;   //      ,          2000 
    int count = m1->rows*m1->cols, maxGoodCount = 0;
    CV_Assert( CV_ARE_SIZES_EQ(m1, m2) && CV_ARE_SIZES_EQ(m1, mask) );

    if( count < modelPoints )  //  RANSAC   ,modelPoints 4
        return false;

    models = cvCreateMat( modelSize.height*maxBasicSolutions, modelSize.width, CV_64FC1 );
    err = cvCreateMat( 1, count, CV_32FC1 );
    tmask = cvCreateMat( 1, count, CV_8UC1 );
    
    if( count > modelPoints )
    {
        ms1 = cvCreateMat( 1, modelPoints, m1->type );
        ms2 = cvCreateMat( 1, modelPoints, m2->type );
    }
    else
    {
        niters = 1;
        ms1 = cvCloneMat(m1);
        ms2 = cvCloneMat(m2);
    }

    for( iter = 0; iter < niters; iter++ )
    {
        int i, goodCount, nmodels;
		
        if( count > modelPoints )
        {
            bool found = getSubset( m1, m2, ms1, ms2, 300 );//    ,300     ,    
            if( !found )                                    //            4 ,  
            {                                               //           
                if( iter == 0 )
                    return false;
                break;
            }
        }
		printf("------");
		 
        nmodels = runKernel( ms1, ms2, models );//          4        
		
        if( nmodels <= 0 )
            continue;
        for( i = 0; i < nmodels; i++ )
        {
            CvMat model_i;
            cvGetRows( models, &model_i, i*modelSize.height, (i+1)*modelSize.height );
            goodCount = findInliers( m1, m2, &model_i, err, tmask, reprojThreshold );
			//            
			printf("%5d %5d %5d %5d
",iter,niters,goodCount,maxGoodCount); if( goodCount > MAX(maxGoodCount, modelPoints-1) ) { std::swap(tmask, mask); cvCopy( &model_i, model ); maxGoodCount = goodCount; // , niters = cvRANSACUpdateNumIters( confidence, (double)(count - goodCount)/count, modelPoints, niters ); } } } //printf("RANSAC %d
",niters); if( maxGoodCount > 0 ) { if( mask != mask0 ) cvCopy( mask, mask0 ); result = true; } return result; }

niters 최초의 값 은 2000 입 니 다. 이것 이 바로 초기 RANSAC 알고리즘 의 순환 횟수 입 니 다. getSubset () 함 수 는 한 그룹의 대응 하 는 서열 에서 무 작위 로 4 그룹 을 선택 하 는 것 입 니 다. (3X3 의 행렬 을 계산 하려 면 적어도 4 개의 대응 하 는 좌표 가 필요 하기 때 문 입 니 다) m1 과 m2 는 우리 의 입력 서열 이 고 ms1 과 ms2 는 무 작위 로 선택 한 대응 하 는 4 그룹 이 일치 합 니 다.
무 작위 로 4 조 의 매 칭 을 선택 한 후 이 4 개의 매 칭 에 따라 해당 하 는 행렬 을 계산 해 야 하기 때문에 함수 runKernel () 은 4 조 의 매 칭 에 따라 행렬 을 계산 하 는 것 이 고 매개 변수 에 있 는 models 는 얻 은 행렬 입 니 다.이 행렬 은 하나의 가설 일 뿐 이 가설 을 검증 하기 위해 다른 점 으로 계산 하고 다른 점 이 내 점 인지 외 점 인지 봐 야 한다.
findInliers () 함 수 는 내 점 을 계산 하 는 데 쓰 인 다.앞에서 얻 은 행렬 을 이용 하여 모든 서열 을 가 져 와 서 어떤 것 이 내 점 이 고 어떤 것 이 외 점 인지 계산 할 수 있 습 니 다. 함수 의 반환 값 은 goodCount 입 니 다. 바로 이번에 계산 한 내 점 의 개수 입 니 다.함수 에 또 하나의 값 은 maxGoodCount 입 니 다. 매번 순환 하 는 내 점 개수 의 최대 값 은 이 값 에 저 장 됩 니 다. 추정 되 는 행렬 이 내 점 이 많 을 수록 이 행렬 은 정확 할 수 있 습 니 다.그래서 내 점 개 수 를 계산 한 다음 에 goodCount 와 maxGoodCount 의 크기 관 계 를 판단 하고 goodCount > maxGoodCount 가 있 으 면 goodCount 를 maxGoodCount 에 할당 합 니 다.값 을 부여 한 후의 코드 는 매우 중요 합 니 다. 우 리 는 단독으로 꺼 내 서 말 합 니 다. 코드 는 다음 과 같 습 니 다.
4. 567913. niters 는 원래 교체 의 횟수, 즉 순환 의 횟수 이다.그러나 이 코드 를 통 해 우 리 는 순환 할 때마다 niters 라 는 값 을 업데이트 하 는 것 을 발견 했다. 즉, 순환 할 때마다 순환 의 총 횟수 를 바 꾸 는 것 이다.cvRANSACUpdateNumIters () 함 수 는 confidence (신뢰 도) count (총 일치 개수) goodCount (현재 내 점 개수) niters (현재 총 교체 횟수) 라 는 매개 변 수 를 이용 하여 총 교체 횟수 의 크기 를 동적 으로 변경 합 니 다.이 함수 의 중심 사상 은 내 점 이 차지 하 는 비율 이 비교적 많 을 때 정확 한 평 가 를 찾 았 을 가능성 이 높 기 때문에 교체 횟수 를 적당 하 게 줄 여 시간 을 절약 하 는 것 이다.이 교체 횟수 의 감 소 는 지수 로 줄 어 들 기 때문에 절약 하 는 시간 비용 도 상당 하 다.따라서 최초 로 디자인 된 2000 의 교체 횟수 는 최종 교체 횟수 가 몇 십 에 불과 할 것 이다.마찬가지 로, 만약 당신 이 처음에 교체 횟수 를 10000 또는 더 크게 설정 했다 면, 몇 번 의 교체 에 들 어간 후에 niters 는 또 매우 작 아 질 것 입 니 다.그래서 초기 niters 설정 이 아무리 커 도 최종 운행 시간 에 영향 을 주지 않 습 니 다.저 는 제 프로그램 으로 간단하게 해 봤 습 니 다. 초기 값 이 2000, 10000, 20000 이 든 최종 교체 횟수 는 58 가 되 었 습 니 다!!
그래서 왜 입력 데이터 가 증가 하고 알고리즘 운행 시간 이 증가 하지 않 는 지 알 아야 한다.openCV 의 RANSAC 알고리즘 은 먼저 교체 횟수 를 2000 으로 설정 한 다음 에 교체 하 는 과정 에서 동태 적 으로 전체 교체 횟수 를 바 꿉 니 다. 입력 데이터 가 얼마 가 되 든 전체적인 교체 횟수 는 증가 하지 않 고 4 개의 매 칭 을 통 해 평가 하 는 행렬 의 시간 은 변 하지 않 습 니 다. 행렬 을 통 해 내 점 을 계산 합 니 다.이 방면 의 증가 한 시간 지출 은 기본적으로 소홀히 할 수 있다.그래서 최종 결 과 는 입력 점 이 얼마 가 되 든 연산 시간 은 크게 달라 지지 않 는 다 는 것 이다.
이상 은 RANSAC 알고리즘 의 핵심 코드 입 니 다. 그 중에서 사용 하 는 함수 들 은 다음 과 같 습 니 다.
1. 일괄 왼쪽 으로 전환 하면 길 어 보이 지만 완 성 된 기능 은 일반적인 좌 표를 일괄 좌표 로 전환 하여 이후 의 계산 을 편리 하 게 하 는 것 이다.
 在这个函数参数中,输入的m1和m2是两个对应的序列,这两组序列的每一对数据一一匹配,其中既有正确的匹配,也有错误的匹配,正确的可以称为内点,错误的称为外点,RANSAC方法就是从这些包含错误匹配的数据中,分离出正确的匹配,并且求得单应矩阵。model就是我们需要求解的单应矩阵,mask我们可以称为标记矩阵,他和m1,m2的长度一样,当一个m1和m2中的点为内点时,mask相应的标记为1,反之为0,说白了,通过mask我们最终可以知道序列中哪些是内点,哪些是外点。reprojThreshold为阈值,当某一个匹配与估计的假设小于阈值时,则被认为是一个内点,这个阈值,openCV默认给的是3,后期使用的时候自己也可以修改。confidence为置信度,其实也就是人为的规定了一个数值,这个数值可以大致表示RANSAC结果的准确性,这个具体有啥用后面咱们再说。这个值初始时被设置为0.995. maxIters为初始迭代次数,RANSAC算法核心就是不断的迭代,这个值就是迭代的次数,默认设为了2000 
  
 

这个函数的前期,主要是设置了一些变量然后赋初值,然后转换相应的格式等等。最关键的部分,是那个for循环。我们把这个for循环单独拿出来分析一下。代码如下。

    for( iter = 0; iter < niters; iter++ )
    {
        int i, goodCount, nmodels;
		
        if( count > modelPoints )
        {
            bool found = getSubset( m1, m2, ms1, ms2, 300 );//    ,300     ,    
            if( !found )                                    //            4 ,  
            {                                               //           
                if( iter == 0 )
                    return false;
                break;
            }
        } 
        nmodels = runKernel( ms1, ms2, models );//          4        
		
        if( nmodels <= 0 )
            continue;
        for( i = 0; i < nmodels; i++ )
        {
            CvMat model_i;
            cvGetRows( models, &model_i, i*modelSize.height, (i+1)*modelSize.height );
            goodCount = findInliers( m1, m2, &model_i, err, tmask, reprojThreshold );
			//            
			printf("%5d %5d %5d %5d
",iter,niters,goodCount,maxGoodCount); if( goodCount > MAX(maxGoodCount, modelPoints-1) ) { std::swap(tmask, mask); cvCopy( &model_i, model ); maxGoodCount = goodCount; // , niters = cvRANSACUpdateNumIters( confidence, (double)(count - goodCount)/count, modelPoints, niters ); } } }

2. 교체 값 을 업데이트 하 는 함수.이 함 수 는 전체 교체 횟수 를 업데이트 하 는 것 으로 그 중에서 볼 수 있 듯 이 교체 값 은 지수 형식 으로 줄어든다.최초의 2000 의 교체 횟수 는 어떤 때 는 끊 임 없 는 업 데 이 트 를 거 쳐 최종 결 과 는 수 십 이 되 었 다.
 niters = cvRANSACUpdateNumIters( confidence,
                    (double)(count - goodCount)/count, modelPoints, niters );

3. 4 개의 매 칭 을 통 해 계산 단 응 행렬 은 4 개의 매 칭 을 주 는 것 입 니 다. 이 네 개의 매 칭 에 맞 는 행렬 을 계산 해 보 세 요.
CV_IMPL void cvConvertPointsHomogeneous( const CvMat* src, CvMat* dst )
{
    Ptr<CvMat> temp, denom;

    int i, s_count, s_dims, d_count, d_dims;
    CvMat _src, _dst, _ones;
    CvMat* ones = 0;

    if( !CV_IS_MAT(src) )
        CV_Error( !src ? CV_StsNullPtr : CV_StsBadArg,
        "The input parameter is not a valid matrix" );

    if( !CV_IS_MAT(dst) )
        CV_Error( !dst ? CV_StsNullPtr : CV_StsBadArg,
        "The output parameter is not a valid matrix" );

    if( src == dst || src->data.ptr == dst->data.ptr )
    {
        if( src != dst && (!CV_ARE_TYPES_EQ(src, dst) || !CV_ARE_SIZES_EQ(src,dst)) )
            CV_Error( CV_StsBadArg, "Invalid inplace operation" );
        return;
    }

    if( src->rows > src->cols )
    {
        if( !((src->cols > 1) ^ (CV_MAT_CN(src->type) > 1)) )
            CV_Error( CV_StsBadSize, "Either the number of channels or columns or rows must be =1" );

        s_dims = CV_MAT_CN(src->type)*src->cols;
        s_count = src->rows;
    }
    else
    {
        if( !((src->rows > 1) ^ (CV_MAT_CN(src->type) > 1)) )
            CV_Error( CV_StsBadSize, "Either the number of channels or columns or rows must be =1" );

        s_dims = CV_MAT_CN(src->type)*src->rows;
        s_count = src->cols;
    }

    if( src->rows == 1 || src->cols == 1 )
        src = cvReshape( src, &_src, 1, s_count );

    if( dst->rows > dst->cols )
    {
        if( !((dst->cols > 1) ^ (CV_MAT_CN(dst->type) > 1)) )
            CV_Error( CV_StsBadSize,
            "Either the number of channels or columns or rows in the input matrix must be =1" );

        d_dims = CV_MAT_CN(dst->type)*dst->cols;
        d_count = dst->rows;
    }
    else
    {
        if( !((dst->rows > 1) ^ (CV_MAT_CN(dst->type) > 1)) )
            CV_Error( CV_StsBadSize,
            "Either the number of channels or columns or rows in the output matrix must be =1" );

        d_dims = CV_MAT_CN(dst->type)*dst->rows;
        d_count = dst->cols;
    }

    if( dst->rows == 1 || dst->cols == 1 )
        dst = cvReshape( dst, &_dst, 1, d_count );

    if( s_count != d_count )
        CV_Error( CV_StsUnmatchedSizes, "Both matrices must have the same number of points" );

    if( CV_MAT_DEPTH(src->type) < CV_32F || CV_MAT_DEPTH(dst->type) < CV_32F )
        CV_Error( CV_StsUnsupportedFormat,
        "Both matrices must be floating-point (single or double precision)" );

    if( s_dims < 2 || s_dims > 4 || d_dims < 2 || d_dims > 4 )
        CV_Error( CV_StsOutOfRange,
        "Both input and output point dimensionality must be 2, 3 or 4" );

    if( s_dims < d_dims - 1 || s_dims > d_dims + 1 )
        CV_Error( CV_StsUnmatchedSizes,
        "The dimensionalities of input and output point sets differ too much" );

    if( s_dims == d_dims - 1 )
    {
        if( d_count == dst->rows )
        {
            ones = cvGetSubRect( dst, &_ones, cvRect( s_dims, 0, 1, d_count ));
            dst = cvGetSubRect( dst, &_dst, cvRect( 0, 0, s_dims, d_count ));
        }
        else
        {
            ones = cvGetSubRect( dst, &_ones, cvRect( 0, s_dims, d_count, 1 ));
            dst = cvGetSubRect( dst, &_dst, cvRect( 0, 0, d_count, s_dims ));
        }
    }

    if( s_dims <= d_dims )
    {
        if( src->rows == dst->rows && src->cols == dst->cols )
        {
            if( CV_ARE_TYPES_EQ( src, dst ) )
                cvCopy( src, dst );
            else
                cvConvert( src, dst );
        }
        else
        {
            if( !CV_ARE_TYPES_EQ( src, dst ))
            {
                temp = cvCreateMat( src->rows, src->cols, dst->type );
                cvConvert( src, temp );
                src = temp;
            }
            cvTranspose( src, dst );
        }

        if( ones )
            cvSet( ones, cvRealScalar(1.) );
    }
    else
    {
        int s_plane_stride, s_stride, d_plane_stride, d_stride, elem_size;

        if( !CV_ARE_TYPES_EQ( src, dst ))
        {
            temp = cvCreateMat( src->rows, src->cols, dst->type );
            cvConvert( src, temp );
            src = temp;
        }

        elem_size = CV_ELEM_SIZE(src->type);

        if( s_count == src->cols )
            s_plane_stride = src->step / elem_size, s_stride = 1;
        else
            s_stride = src->step / elem_size, s_plane_stride = 1;

        if( d_count == dst->cols )
            d_plane_stride = dst->step / elem_size, d_stride = 1;
        else
            d_stride = dst->step / elem_size, d_plane_stride = 1;

        denom = cvCreateMat( 1, d_count, dst->type );

        if( CV_MAT_DEPTH(dst->type) == CV_32F )
        {
            const float* xs = src->data.fl;
            const float* ys = xs + s_plane_stride;
            const float* zs = 0;
            const float* ws = xs + (s_dims - 1)*s_plane_stride;

            float* iw = denom->data.fl;

            float* xd = dst->data.fl;
            float* yd = xd + d_plane_stride;
            float* zd = 0;

            if( d_dims == 3 )
            {
                zs = ys + s_plane_stride;
                zd = yd + d_plane_stride;
            }

            for( i = 0; i < d_count; i++, ws += s_stride )
            {
                float t = *ws;
                iw[i] = fabs((double)t) > FLT_EPSILON ? t : 1.f;
            }

            cvDiv( 0, denom, denom );

            if( d_dims == 3 )
                for( i = 0; i < d_count; i++ )
                {
                    float w = iw[i];
                    float x = *xs * w, y = *ys * w, z = *zs * w;
                    xs += s_stride; ys += s_stride; zs += s_stride;
                    *xd = x; *yd = y; *zd = z;
                    xd += d_stride; yd += d_stride; zd += d_stride;
                }
            else
                for( i = 0; i < d_count; i++ )
                {
                    float w = iw[i];
                    float x = *xs * w, y = *ys * w;
                    xs += s_stride; ys += s_stride;
                    *xd = x; *yd = y;
                    xd += d_stride; yd += d_stride;
                }
        }
        else
        {
            const double* xs = src->data.db;
            const double* ys = xs + s_plane_stride;
            const double* zs = 0;
            const double* ws = xs + (s_dims - 1)*s_plane_stride;

            double* iw = denom->data.db;

            double* xd = dst->data.db;
            double* yd = xd + d_plane_stride;
            double* zd = 0;

            if( d_dims == 3 )
            {
                zs = ys + s_plane_stride;
                zd = yd + d_plane_stride;
            }

            for( i = 0; i < d_count; i++, ws += s_stride )
            {
                double t = *ws;
                iw[i] = fabs(t) > DBL_EPSILON ? t : 1.;
            }

            cvDiv( 0, denom, denom );

            if( d_dims == 3 )
                for( i = 0; i < d_count; i++ )
                {
                    double w = iw[i];
                    double x = *xs * w, y = *ys * w, z = *zs * w;
                    xs += s_stride; ys += s_stride; zs += s_stride;
                    *xd = x; *yd = y; *zd = z;
                    xd += d_stride; yd += d_stride; zd += d_stride;
                }
            else
                for( i = 0; i < d_count; i++ )
                {
                    double w = iw[i];
                    double x = *xs * w, y = *ys * w;
                    xs += s_stride; ys += s_stride;
                    *xd = x; *yd = y;
                    xd += d_stride; yd += d_stride;
                }
        }
    }
}

4. 입력 시퀀스 를 지정 한 후 4 쌍 을 무 작위 로 선택 합 니 다.
CV_IMPL int
cvRANSACUpdateNumIters( double p, double ep,
                        int model_points, int max_iters )
{
    if( model_points <= 0 )
        CV_Error( CV_StsOutOfRange, "the number of model points should be positive" );

    p = MAX(p, 0.);
    p = MIN(p, 1.);
    ep = MAX(ep, 0.);
    ep = MIN(ep, 1.);

    // avoid inf's & nan's
    double num = MAX(1. - p, DBL_MIN);
    double denom = 1. - pow(1. - ep,model_points);
    if( denom < DBL_MIN )
        return 0;

    num = log(num);
    denom = log(denom);
    
    return denom >= 0 || -num >= max_iters*(-denom) ?
        max_iters : cvRound(num/denom);
}


5. 생 성 된 4 조 의 일치 에 대해 검 사 를 하고 요구 에 부합 되 는 지 관찰한다.
4. 567913. 6. 내 점 의 개 수 를 계산 하고 표기 서열 에서 어떤 점 이 내 점 인지 계산한다.
//      ,           
int CvHomographyEstimator::runKernel( const CvMat* m1, const CvMat* m2, CvMat* H )
{
    int i, count = m1->rows*m1->cols;
    const CvPoint2D64f* M = (const CvPoint2D64f*)m1->data.ptr;
    const CvPoint2D64f* m = (const CvPoint2D64f*)m2->data.ptr;

    double LtL[9][9], W[9][1], V[9][9];
    CvMat _LtL = cvMat( 9, 9, CV_64F, LtL );
    CvMat matW = cvMat( 9, 1, CV_64F, W );
    CvMat matV = cvMat( 9, 9, CV_64F, V );
    CvMat _H0 = cvMat( 3, 3, CV_64F, V[8] );
    CvMat _Htemp = cvMat( 3, 3, CV_64F, V[7] );
    CvPoint2D64f cM={0,0}, cm={0,0}, sM={0,0}, sm={0,0};

    for( i = 0; i < count; i++ )
    {
        cm.x += m[i].x; cm.y += m[i].y;
        cM.x += M[i].x; cM.y += M[i].y;
    }

    cm.x /= count; cm.y /= count;
    cM.x /= count; cM.y /= count;

    for( i = 0; i < count; i++ )
    {
        sm.x += fabs(m[i].x - cm.x);
        sm.y += fabs(m[i].y - cm.y);
        sM.x += fabs(M[i].x - cM.x);
        sM.y += fabs(M[i].y - cM.y);
    }

    if( fabs(sm.x) < DBL_EPSILON || fabs(sm.y) < DBL_EPSILON ||
        fabs(sM.x) < DBL_EPSILON || fabs(sM.y) < DBL_EPSILON )
        return 0;
    sm.x = count/sm.x; sm.y = count/sm.y;
    sM.x = count/sM.x; sM.y = count/sM.y;

    double invHnorm[9] = { 1./sm.x, 0, cm.x, 0, 1./sm.y, cm.y, 0, 0, 1 };
    double Hnorm2[9] = { sM.x, 0, -cM.x*sM.x, 0, sM.y, -cM.y*sM.y, 0, 0, 1 };
    CvMat _invHnorm = cvMat( 3, 3, CV_64FC1, invHnorm );
    CvMat _Hnorm2 = cvMat( 3, 3, CV_64FC1, Hnorm2 );

    cvZero( &_LtL );
    for( i = 0; i < count; i++ )
    {
        double x = (m[i].x - cm.x)*sm.x, y = (m[i].y - cm.y)*sm.y;
        double X = (M[i].x - cM.x)*sM.x, Y = (M[i].y - cM.y)*sM.y;
        double Lx[] = { X, Y, 1, 0, 0, 0, -x*X, -x*Y, -x };
        double Ly[] = { 0, 0, 0, X, Y, 1, -y*X, -y*Y, -y };
        int j, k;
        for( j = 0; j < 9; j++ )
            for( k = j; k < 9; k++ )
                LtL[j][k] += Lx[j]*Lx[k] + Ly[j]*Ly[k];
    }
    cvCompleteSymm( &_LtL );

    //cvSVD( &_LtL, &matW, 0, &matV, CV_SVD_MODIFY_A + CV_SVD_V_T );
    cvEigenVV( &_LtL, &matV, &matW );
    cvMatMul( &_invHnorm, &_H0, &_Htemp );
    cvMatMul( &_Htemp, &_Hnorm2, &_H0 );
    cvConvertScale( &_H0, H, 1./_H0.data.db[8] );

    return 1;
}

7. 위의 함수 가 호출 하 는 일부 함수 들 은 어렵 지 않 기 때문에 아래 에 해당 하 는 것 을 열거 합 니 다.
bool CvModelEstimator2::getSubset( const CvMat* m1, const CvMat* m2,
                                   CvMat* ms1, CvMat* ms2, int maxAttempts )  //maxAttempts   300
{
    cv::AutoBuffer<int> _idx(modelPoints);
    int* idx = _idx;
    int i = 0, j, k, idx_i, iters = 0;
    int type = CV_MAT_TYPE(m1->type), elemSize = CV_ELEM_SIZE(type);
    const int *m1ptr = m1->data.i, *m2ptr = m2->data.i;
    int *ms1ptr = ms1->data.i, *ms2ptr = ms2->data.i;
    int count = m1->cols*m1->rows;

    assert( CV_IS_MAT_CONT(m1->type & m2->type) && (elemSize % sizeof(int) == 0) );
    elemSize /= sizeof(int);

    for(; iters < maxAttempts; iters++)
    {
        for( i = 0; i < modelPoints && iters < maxAttempts; )
        {
            idx[i] = idx_i = cvRandInt(&rng) % count;    //  count      ,count     
            for( j = 0; j < i; j++ )                    //             
                if( idx_i == idx[j] )
                    break;
            if( j < i )
                continue;
            for( k = 0; k < elemSize; k++ )
            {  
                ms1ptr[i*elemSize + k] = m1ptr[idx_i*elemSize + k];   //         ms1 ms2
                ms2ptr[i*elemSize + k] = m2ptr[idx_i*elemSize + k];
            }
            if( checkPartialSubsets && (!checkSubset( ms1, i+1 ) || !checkSubset( ms2, i+1 )))   //    checkSubset
            {
                iters++;
                continue;
            }
            i++;
        }
        if( !checkPartialSubsets && i == modelPoints &&
            (!checkSubset( ms1, i ) || !checkSubset( ms2, i )))
            continue;
        break;
    }

    return i == modelPoints && iters < maxAttempts;
}

8. 마지막 부분 이 관건 입 니 다.바로 FindHomography 함수 자체 입 니 다.이 함 수 는 cvFindHomography 함 수 를 다시 호출 했 습 니 다. openCV 의 서로 다른 버 전의 함수 일 것 입 니 다. 그 실현 기능 과 사상 은 모두 같 습 니 다.이 함수 내 부 는 기본적으로 넘 치 는 것 을 방지 하고 오 류 를 검사 하 며 변 수 를 검사 하고 형식 을 바 꾸 는 등 보조 적 인 내용 을 판단 하 는 것 입 니 다. 진정한 방법 적 인 코드 는 위 에서 언급 한 Cv Homography Estimator 류 입 니 다.
bool CvModelEstimator2::checkSubset( const CvMat* m, int count )
{
    int j, k, i, i0, i1;
    CvPoint2D64f* ptr = (CvPoint2D64f*)m->data.ptr;

    assert( CV_MAT_TYPE(m->type) == CV_64FC2 );
    
    if( checkPartialSubsets )
        i0 = i1 = count - 1;
    else
        i0 = 0, i1 = count - 1;
    
    for( i = i0; i <= i1; i++ )
    {
        // check that the i-th selected point does not belong
        // to a line connecting some previously selected points
        for( j = 0; j < i; j++ )
        {
            double dx1 = ptr[j].x - ptr[i].x;
            double dy1 = ptr[j].y - ptr[i].y;
            for( k = 0; k < j; k++ )
            {
                double dx2 = ptr[k].x - ptr[i].x;
                double dy2 = ptr[k].y - ptr[i].y;
                if( fabs(dx2*dy1 - dy2*dx1) <= FLT_EPSILON*(fabs(dx1) + fabs(dy1) + fabs(dx2) + fabs(dy2)))
                    break;
            }
            if( k < j )
                break;
        }
        if( j < i )
            break;
    }

    return i >= i1;
}

좋은 웹페이지 즐겨찾기