我昨天问了这个问题:用2个方阵模拟matlab's 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;
}
根据我的理解,mrdivide正在工作的事实应该意味着mldivide正在工作,但我无法让它给我与matlab相同的结果。结果又不一样了。
值得注意的是,我尝试做一个[19x19] [19x200]所以这次不是方阵
就像我之前在你的另一个问题中提到的那样,我正在使用MATLAB和mexopencv,这样我就可以很容易地比较MATLAB和OpenCV的输出。
也就是说,我不能重现你的问题:我生成了随机矩阵,并重复比较N=100
次。我正在运行MATLAB R2015a,使用针对OpenCV 3.0.0编译的mexopencv:
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 = AB;
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
(顺便说一句,我也尝试了x2 = cv.solve(A,B, 'IsNormal',true);
设置cv::DECOMP_NORMAL
标志,结果也没有那么不同)。
这让我相信你的矩阵碰巧在OpenCV求解器中强调了一些边缘情况,在那里它没有给出适当的解决方案,或者更有可能你在代码的其他地方有一个bug。
我首先要仔细检查你如何加载你的数据,特别要注意矩阵是如何布局的(显然MATLAB是列为主,而OpenCV是行为主)…
你也没有告诉我们任何关于你的矩阵的事;它们是否表现出某种特征,是否有任何对称性,它们是否大部分为零,它们的秩等等。
在OpenCV中,默认的解算器方法是LU分解,如果合适的话,您必须自己显式地更改它。MATLAB会自动选择最适合矩阵A
的方法,LU只是可能的分解方法之一。
编辑(回复评论)
在MATLAB中使用SVD分解时,左右特征向量U
和V
的符号是任意的(这实际上来自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);