我遇到了一个奇怪的行为从矩阵类在OpenCV关于转换float到uint8_t。似乎OpenCV与矩阵类转换float为uint8_t通过做一个上限,而不是只是截断小数。
#include <iostream>
#include <opencv2/core/core.hpp>
#include <opencv2/imgcodecs.hpp>
int main() {
cv::Mat m1(1, 1, CV_8UC1);
cv::Mat m2(1, 1, CV_8UC1);
cv::Mat m3(1, 1, CV_8UC1);
m1.at<uint8_t>(0, 0) = 121;
m2.at<uint8_t>(0, 0) = 105;
m3.at<uint8_t>(0, 0) = 82;
cv::Mat x = m1 * 0.5 + m2 * 0.25 + m3 * 0.25;
printf("%d n", x.at<uint8_t>(0, 0));
uint8_t r = 121 * 0.5 + 105 * 0.25 + 82 * 0.25;
printf("%d nn", r);
return 0;
}
输出:
108
107
你知道为什么这个附加和如何纠正这个行为吗?
谢谢你,
这种奇怪的行为是cv::MatExpr和Lasy计算使用的结果。
实际结果等于:
round(round(121*0.5 + 105*0.25) + 82*0.25) = 108
- 使用舍入是因为元素类型是UINT8(整数类型)。
- 计算顺序是"最后求值"的结果。策略。
使用调试器跟踪计算过程是具有挑战性的,因为OpenCV实现包括操作符重载,模板,宏和指向函数的指针…
实际计算在
中的static void scalar_loop
函数中进行。dst[x] = op::r(src1[x], src2[x], scalar);
例如:src1[x] = 121
,src2[x] = 105
,scalar = 0.5
。
执行内联函数:
inline uchar c_add<uchar, float>(uchar a, uchar b, float alpha, float beta, float gamma)
{ return saturate_cast<uchar>(CV_8TO32F(a) * alpha + CV_8TO32F(b) * beta + gamma); }
实际舍入在saturate_cast
:
template<> inline uchar saturate_cast<uchar>(float v) { int iv = cvRound(v); return saturate_cast<uchar>(iv); }
cvRound
使用SIMD固有的return _mm_cvtss_si32(t)
它相当于:return (int)(value + (value >= 0 ? 0.5f : -0.5f));
最后的评估阶段用alpha
和beta
标量构建MatExpr
。
cv::Mat x = m1 * 0.5 + m2 * 0.25 + m3 * 0.25; //m1 = 121, m2 = 105, m3 = 82
表达式是递归构建的(很难理解)。
在"操作符+"之后函数(使用调试器):
MatExpr operator + (const MatExpr& e1, const MatExpr& e2)
{
MatExpr en;
e1.op->add(e1, e2, en);
return en;
}
State 1:
e1.a data = 121 (UINT8)
e1.b (NULL)
e1.alpha = 0.5
e1.beta = 0
e2.a data = 105 (UINT8)
e1.b (NULL)
e1.alpha = 0.25
e1.beta = 0
Result:
en.a data = 121 (UINT8)
en.b data = 105 (UINT8)
en.alpha = 0.5
en.beta = 0.25
State 2:
e1.a data = 121 (UINT8)
e1.b data = 105 (UINT8)
e1.alpha = 0.5
e1.beta = 0.25
e2.a data = 82 (UINT8)
e1.b (NULL)
e1.alpha = 0.25
e1.beta = 0
en.a data = 87 (UINT8) <--- 121*0.5 + 105*0.25 = 86.7500 rounded to 87
en.b data = 82 (UINT8)
en.alpha = 1
en.beta = 0.25
Stage 3: (in MatExpr::operator Mat() const):
m data = 108 (UINT8) <--- 87*1 + 82*0.25 = 87 + 20.5 = 107.5 rounded to 108
您可以尝试使用调试器跟踪计算过程。
它需要从源代码中构建OpenCV,在调试配置中,以及大量的耐心…