【问题标题】:Simulating matlab's mldivide with OpenCV用 OpenCV 模拟 matlab 的 mldivide
【发布时间】:2015-09-23 13:42:49
【问题描述】:

我昨天问了这个问题:Simulating matlab's mrdivide with 2 square matrices

这就是 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;
}

据我了解,mrdivide 正在工作的事实应该意味着 mldivide 正在工作,但我无法让它给我与 matlab 相同的结果。结果再次完全不同。

值得注意的是,我这次尝试做 [19x19] \ [19x200] 所以不是方阵。

【问题讨论】:

  • @beaker 如果你看你会看到我已经“实现”了该链接中解释的方法。
  • 那么您必须向我们展示问题所在。我将删除重复的投票。
  • @beaker .. 我不知道怎么给你看。上面给我的答案和matlab用相同数据给我的答案是完全不同的
  • 嗯,你有没有用他们每个人的结果计算A*x = B,发现一个不正确?

标签: c++ matlab opencv linear-algebra


【解决方案1】:

就像我之前在您的other question 中提到的那样,我将 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

所有结果一致,误差非常小,1e-11级:

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

>> mean(d)
ans =
   6.5457e-12

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

这让我相信,要么你的矩阵碰巧突出了 OpenCV 求解器中的一些边缘情况,它未能给出正确的解决方案,要么你的代码中的其他地方更有可能存在错误。

首先我会仔细检查您如何加载数据,尤其要注意矩阵的布局方式(显然 MATLAB 是列优先的,而 OpenCV 是行优先的)...

此外,您从未告诉我们有关您的矩阵的任何信息;它们是否表现出某种特征,是否有任何对称性,它们是否大多为零,它们的等级等。

在 OpenCV 中,默认的求解器方法是 LU 分解,如果合适,您必须自己显式更改它。手上的MATLAB会automatically选择最适合矩阵A的方法,而LU只是可能的分解之一。


编辑(响应 cmets)

在 MATLAB 中使用 SVD 分解时,左右特征向量 UV 的符号为 arbitrary(这确实来自 DGESVD LAPACK 例程)。为了得到一致的结果,一个约定是要求每个特征向量的第一个元素是某个符号,并将每个向量乘以 +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

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

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

>> mean(d)
ans =
   1.0e-13 *
    0.3113    0.3009    0.0578

在 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);

【讨论】:

  • 感谢您的回复。想一想……它可能工作正常。我忘记了 OpenCV 的 SVD 返回与 Matlab 相似的结果(但符号不同)。由于在前面的步骤中使用了 SVD,因此这些迹象很可能让我感到困惑......
  • @Goz 查看我对 SVD 的编辑。还有一点值得注意的是,从 OpenCV 2.3 开始,他们不再使用 LAPACK 进行 SVD,转而使用自己的实现(他们声称对于小矩阵更快):code.opencv.org/issues/1498code.opencv.org/projects/opencv/wiki/ChangeLog#23-beta
猜你喜欢
  • 1970-01-01
  • 2016-02-07
  • 2013-11-11
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多