我在 OpenCV 中的 Matrix 類中遇到了關于將浮點數轉換為 uint8_t 的奇怪行為。似乎帶有 Matrix 類的 OpenCV 通過執行 ceil 而不是截斷小數將 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 \n\n", r);
return 0;
}
輸出:
108
107
你知道為什么要追加這個行為以及如何糾正這種行為嗎?
謝謝,
uj5u.com熱心網友回復:
奇怪的行為是這里描述的cv::MatExpr和Lasy 評估用法的結果。
實際結果等于:
round(round(121*0.5 105*0.25) 82*0.25) = 108
- 使用舍入是因為元素型別是 UINT8(整數型別)。
- 計算順序是“Lasy Evaluation”策略的結果。
使用除錯器進行計算程序具有挑戰性,因為 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));
Lasy 評估階段MatExpr使用alpha和beta標量構建。
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,在 Debug 配置中,以及很多耐心......
轉載請註明出處,本文鏈接:https://www.uj5u.com/net/339337.html
