您当前的位置: 首页 >  矩阵
  • 2浏览

    0关注

    483博文

    0收益

  • 0浏览

    0点赞

    0打赏

    0留言

私信
关注
热门博文

OpenCV源码解析:Jacobi法计算矩阵的特征值和特征向量

高精度计算机视觉 发布时间:2018-08-13 21:39:20 ,浏览量:2

(注:CSDN不适合写公式,只好上传图片格式)

 

其中Pkk=Pll=cosθ, Plk=Pkl=sinθ,形式上就是这样,

A*PT

   Aik = Aik×Pkk+Ail×Pkl

   Ail = Aik×Plk+Ail×Pll

P*A

   Aki = Pkk×Aki+ Pkl×Ali

   Ali = Plk×Aki+ Pll×Ali

实际计算时,只计算那些必须计算的量,已经归0的量和不变的量都不用计算,比如,当k=1,l=3时,只计算了下面这6个量A01,A03, A12, A23, A14, A34,其中A13已经归0化(A03=0),

为了达到上述目的,OpenCV充分利用了矩阵的对称性(如Ali=Ail), 连续采用了三个for循环计算右上角的元素(对右上角的元素,lk 一定成立),这样既可以避免左下角不必要的运算,同时也免除了向左下角拷贝元素值的必要。

第1个for循环,处理0到k-1行的第k,l个元素,这些元素在对角线的上面;

第2个for循环,k+1到l-1之间的元素,为了确保只用右上角的元素,后面写成了A[astep*i+l];

第3个for循环,l+1到n-1之间的元素,在对角线的右边;

        for( i = 0; i < k; i++ )
            rotate(A[astep*i+k], A[astep*i+l]); 
        for( i = k+1; i < l; i++ )
            rotate(A[astep*k+i], A[astep*i+l]);
        for( i = l+1; i < n; i++ )
            rotate(A[astep*k+i], A[astep*l+i]);

那些对角线的上元素(最后得到的就是特征值),则由下面的代码处理,

        if( V )
            for( i = 0; i < n; i++ )
                rotate(V[vstep*k+i], V[vstep*l+i]);

看到这里,不难明白,为什么OpenCV的eigen/Jacobi函数只适合实对称矩阵的处理,而不适合那些非对称的矩阵。

另外要说明一下源码中的最大值查找,它是冗余的,函数先实现了按行查找最大值,然后又实现了按列查找最大值,对于非对称矩阵,这种算法确保能找到最大值所在的位置;对于对称矩阵,这两种算法就完全重复了,两次查找得到的结果是一样的。

下面的我们看一下OpenCV中Jacob算法的具体实现,源码在lapack.cpp中

Hypot计算的是(a^2 + b^2)^0.5
template static inline _Tp hypot(_Tp a, _Tp b)
{
    a = std::abs(a);
    b = std::abs(b);
    if( a > b )
    {
        b /= a;
        return a*std::sqrt(1 + b*b);
    }
    if( b > 0 )
    {
        a /= b;
        return b*std::sqrt(1 + a*a);
    }
    return 0;
}

template bool
JacobiImpl_( _Tp* A, size_t astep, _Tp* W, _Tp* V, size_t vstep, int n, uchar* buf ) 
{
    const _Tp eps = std::numeric_limits::epsilon();  // 设置计算精度 
    int i, j, k, m; 

    astep /= sizeof(A[0]); 
    if( V ) 
    { 
        vstep /= sizeof(V[0]); 
        for( i = 0; i < n; i++ ) 
        {
            for( j = 0; j < n; j++ ) 
                V[i*vstep + j] = (_Tp)0; 	// 非对角线元素置0   
            V[i*vstep + i] = (_Tp)1;  		// 所有对角线元素置1 
        } 
    } 

    int iters, maxIters = n*n*30; // 最大允许叠代次数

    int* indR = (int*)alignPtr(buf, sizeof(int));
int* indC = indR + n;

    _Tp mv = (_Tp)0;

    for( k = 0; k < n; k++ )
    {
        W[k] = A[(astep + 1)*k];  // 取第k行对角线上的元素(第k行:astep*k,第k列:k)

        // 下面这个if,是对k行右边所有的元素逐个查找,找到绝对值最大的那个,并记录下其所在的位置
        if( k < n - 1 )
        {
		   //第k行中,从第k+1到第n-1个元素,逐个找
            for( m = k+1, mv = std::abs(A[astep*k + m]), i = k+2; i < n; i++ )  
            {
                _Tp val = std::abs(A[astep*k+i]);
                if( mv < val )
                    mv = val, m = i;
            }
            indR[k] = m;  // k行中最大值元素所在的位置
        }
		
	    // 下面这个if,是对k列上边所有的元素逐个查找,找到绝对值最大的那个,并记录下其所在的位置
        if( k > 0 ) 
        {
		   //第k列行中,从第0到第k-1个元素,逐个找
            for( m = 0, mv = std::abs(A[k]), i = 1; i < k; i++ )
            {
                _Tp val = std::abs(A[astep*i+k]);
                if( mv < val )
                    mv = val, m = i;
            }
            indC[k] = m;  // 第k列中最大值元素所在的位置(行数)
        }
    }

    if( n > 1 ) for( iters = 0; iters < maxIters; iters++ )
    {
        // find index (k,l) of pivot p
        // 从第0行到第n-2行,每行最大的那个值相互比对,逐行找最大值, 
        // 注意,最后一行是第n-1行,右边已经没有元素了,故而不参与计算
        for( k = 0, mv = std::abs(A[indR[0]]), i = 1; i < n-1; i++ )
        {
            _Tp val = std::abs(A[astep*i + indR[i]]); // 第i行的最大值
            if( mv < val )
                mv = val, k = i;
        }
        int l = indR[k];  // 右上角元素最大值所在的列		
	    
        // 从第1列到第n-1列,逐列找 
        for( i = 1; i < n; i++ )
        {
            _Tp val = std::abs(A[astep*indC[i] + i]);
            if( mv < val )
                mv = val, k = indC[i], l = i;
        }

        _Tp p = A[astep*k + l];
        if( std::abs(p)  0 )
            {
                for( m = 0, mv = std::abs(A[idx]), i = 1; i < idx; i++ )
                {
                    _Tp val = std::abs(A[astep*i+idx]);
                    if( mv < val )
                        mv = val, m = i;
                }
                indC[idx] = m;
            }
        }
    }

    // sort eigenvalues & eigenvectors
    for( k = 0; k < n-1; k++ )
    {
        m = k;
        for( i = k+1; i < n; i++ )
        {
            if( W[m] < W[i] )
                m = i;
        }
        if( k != m )
        {
            std::swap(W[m], W[k]);
            if( V )
                for( i = 0; i < n; i++ )
                    std::swap(V[vstep*m + i], V[vstep*k + i]);
        }
    }

    return true;
}

static bool Jacobi( float* S, size_t sstep, float* e, float* E, size_t estep, int n, uchar* buf )
{
    return JacobiImpl_(S, sstep, e, E, estep, n, buf);
}

static bool Jacobi( double* S, size_t sstep, double* e, double* E, size_t estep, int n, uchar* buf )
{
    return JacobiImpl_(S, sstep, e, E, estep, n, buf);
}

解析完毕。

 

关注
打赏
1661664439
查看更多评论
立即登录/注册

微信扫码登录

0.0535s