使用OpenCV模拟matlab的mldivide

Goz*_*Goz 3 c++ matlab opencv linear-algebra

昨天我问了这个问题:用2平方矩阵模拟matlab的mrdivide

这就是mrdivide工作.但是现在我遇到了mldivide的问题,目前实现如下:

cv::Mat mldivide(const cv::Mat& A, const cv::Mat& B ) 
{
    //return  b * A.inv();
    cv::Mat a;
    cv::Mat b;
    A.convertTo( a, CV_64FC1 );
    B.convertTo( b, CV_64FC1 );

    cv::Mat ret;
    cv::solve( a, b, ret, cv::DECOMP_NORMAL );

    cv::Mat ret2;
    ret.convertTo( ret2, A.type() );
    return ret2;
}
Run Code Online (Sandbox Code Playgroud)

根据我的理解,mrdivide工作的事实应该意味着mldivide正在工作,但我不能让它给我与matlab相同的结果.结果再次没有相似之处.

值得注意的是,我试图做[19x19]\[19x200]所以这次不是方形矩阵.

Amr*_*mro 5

就像我之前在你的另一个问题中提到的那样,我正在使用MATLAB和mexopencv,这样我就可以轻松地比较MATLAB和OpenCV的输出.

也就是说,我无法重现你的问题:我生成了随机矩阵,并重复了比较N=100时间.我正在使用针对OpenCV 3.0.0编译的mexopencv运行MATLAB R2015a:

N = 100;
r = zeros(N,2);
d = zeros(N,1);
for i=1:N
    % double precision, i.e CV_64F
    A = randn(19,19);
    B = randn(19,200);

    x1 = A\B;
    x2 = cv.solve(A,B);   % this a MEX function that calls cv::solve

    r(i,:) = [norm(A*x1-B), norm(A*x2-B)];
    d(i) = norm(x1-x2);
end
Run Code Online (Sandbox Code Playgroud)

所有结果都达成一致,误差非常小,大约为1e-11:

>> mean(r)
ans =
   1.0e-12 *
    0.2282    0.2698

>> mean(d)
ans =
   6.5457e-12
Run Code Online (Sandbox Code Playgroud)

(顺便说一句,我也试过x2 = cv.solve(A,B, 'IsNormal',true);设置cv::DECOMP_NORMAL标志,结果也没有那么不同).

这让我相信你的矩阵碰巧在OpenCV求解器中突出了一些边缘情况,它无法提供正确的解决方案,或者更有可能在代码中的其他地方出现错误.

我首先仔细检查你如何加载数据,特别注意矩阵是如何布局的(显然MATLAB是列专业,而OpenCV是行专业)......

你也从未告诉过我们关于你的矩阵的任何信息; 他们是否具有某种特征,是否有任何对称性,它们大多是零,它们的等级等等.

在OpenCV中,默认的求解器方法是LU分解,如果合适,您必须自己明确地更改它.手上的MATLAB将自动选择最适合矩阵的方法,ALU只是可能的分解之一.


编辑(回复评论)

当使用MATLAB SVD decompositition,左,右特征向量的符号UV任意的(这其实就是从DGESVDLAPACK例程).为了获得一致的结果,一种惯例是要求每个特征向量的第一个元素是某个符号,并将每个向量乘以+1或-1以适当地翻转符号.我还建议检查一下eigenshuffle.

再一次,这是我做的测试,以确认我在MATLAB和OpenCV中得到类似的SVD结果:

N = 100;
r = zeros(N,2);
d = zeros(N,3);
for i=1:N
    % double precision, i.e CV_64F
    A = rand(19);

    % compute SVD in MATLAB, and apply sign convention
    [U1,S1,V1] = svd(A);
    sn = sign(U1(1,:));
    U1 = bsxfun(@times, sn, U1);
    V1 = bsxfun(@times, sn, V1);
    r(i,1) = norm(U1*S1*V1' - A);

    % compute SVD in OpenCV, and apply sign convention
    [S2,U2,V2] = cv.SVD.Compute(A);
    S2 = diag(S2);
    sn = sign(U2(1,:));
    U2 = bsxfun(@times, sn, U2);
    V2 = bsxfun(@times, sn', V2)';  % Note: V2 was transposed w.r.t V1
    r(i,2) = norm(U2*S2*V2' - A);

    % compare
    d(i,:) = [norm(V1-V2), norm(U1-U2), norm(S1-S2)];
end
Run Code Online (Sandbox Code Playgroud)

同样,所有结果都非常相似,错误接近机器epsilon并且可以忽略不计:

>> mean(r)
ans =
   1.0e-13 *
    0.3381    0.1215

>> mean(d)
ans =
   1.0e-13 *
    0.3113    0.3009    0.0578
Run Code Online (Sandbox Code Playgroud)

我不确定OpenCV中的一件事,但MATLAB的svd函数返回按递减顺序排序的奇异值(与eig函数不同),其中特征向量的列按相应的顺序排列.

现在,如果OpenCV中的奇异值由于某种原因无法保证排序,那么如果要将结果与MATLAB进行比较,则必须手动完成,如下所示:

% not needed in MATLAB
[U,S,V] = svd(A);
[S, ord] = sort(diag(S), 'descend');
S = diag(S);
U = U(:,ord)
V = V(:,ord);
Run Code Online (Sandbox Code Playgroud)