标量的OpenCV双矩阵除法产生不正确的结果

时间:2015-01-05 21:03:31

标签: c++ matlab opencv division

我很好奇是否有人在使用标量(双倍)划分矩阵(带有双值)时能够得到正确的结果。我注意到当我试图追踪MATLAB中的算法与用C ++重现的算法之间的某些不一致的来源时,OpenCV没有给出正确的(好的,#34;确切的")结果。以下是我看到的问题的最小例子:

cv::Mat some_matrix(1, 1, CV_64FC1, cv::Scalar::all(95));
cv::Mat some_matrix_div = some_matrix / 235.0;
printf(
        "Expected: %.53g\n"
        "OpenCV  : %.53g\n",
        some_matrix.at<double>(0,0) / 235.0,
        some_matrix_div.at<double>(0,0) );

跑完后我看到了

Expected: 0.40425531914893614304773450385255273431539535522460938
OpenCV  : 0.404255319148936198558885735110379755496978759765625

第一个是值应该是什么(以及如果在C ++或MATLAB中执行95/235的双精度除法,你会得到什么)但第二个是OpenCV在使用除法运算符时产生的。我尝试在OpenCV源代码中追踪问题,但矩阵操作有点复杂,我现在没有很多时间来搜索它,所以我想知道是否还有其他人遇到过这个问题并知道修复?

修改

我会补充一些说明。

首先,我知道双打不是精确的数字表示。我的意思是&#34;确切的&#34; (为什么它在引号中)是直接执行的双重除法(例如打印95.0 / 235.0的结果)与使用标量划分矩阵时OpenCV所做的不完全相同,尽管矩阵中的值确实是存储为双精度,标量确实被视为双精度。人们可以预期这两个结果应该相同;也就是说,如果我将double除以另一个double,结果应该与OpenCV双矩阵除以双标量相同。

我还尝试过将所有数字常量显式地转换为代码中的双精度数,但没有成功。

虽然在这种情况下确实如此,但差异相对较小(e ^ -16),我不确定这可能会随着时间推移而产生越来越大的错误。这是一个问题。另一个更多的是一个小麻烦,一个误解为什么OpenCV没有做到人们直觉所期望的。最后它可能不会引起任何问题,但如果可以避免奇怪的行为,我显然更喜欢这一点,特别是因为它不清楚计算何时与MATLAB结果的预期结果不匹配,因为计算奇怪的或因为实际的算法实现问题(这是我所认为的)。

希望这更清楚。

2 个答案:

答案 0 :(得分:0)

浮点数学本质上并不精确。在x86平台上,可以使用FPU(80位扩展精度)或SSE / AVX向量单位(64位双精度)计算双精度数。完成此计算取决于编译器的选择和传递给编译器的各种选项。更糟糕的是,如果编译器耗尽了80位寄存器,它会将结果“溢出”到64位结果。事实证明,对于大多数浮点运算,即使对于标量,向量单元也更快,因此在允许的情况下,编译器通常会首选它。

如果明确编写软件以使用SSE或AVX以获得最大速度,那么肯定会使用64位版本。这可能是OpenCV的情况。 OpenCV甚至可能通过先计算倒数(1.0 / 235.0)来近似计算,然后将结果乘以每个像素,因为这会更快。

有些事情要尝试:

some_matrix.at<double>(0,0) * (1.0 / 235.0)

还尝试更改编译器标志以包含-mfpmath=sse -msse2,以确保您的编译器知道您有一个SSE单元,并将其用于双精度。

请阅读此处以获取有关这些效果的更长说明:https://gcc.gnu.org/wiki/x87note

答案 1 :(得分:0)

如@Borgleader在评论中所提到的,问题是在计算库中使用-fast-math编译器选项进行库编译,但不是我的应用程序。在这种情况下,它不是OpenCV,而是我的发行版中的另一个库,但正是这种差异导致了差异。通过重建没有标记的库来确定问题是为了获得一致的结果。