奇怪的uint8_t转换与OpenCV



我遇到了一个奇怪的行为从矩阵类在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));


最后的评估阶段用alphabeta标量构建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,在调试配置中,以及大量的耐心…

相关内容

  • 没有找到相关文章

最新更新