我正在使用 OpenCV 版本 3.4.5。我试图了解 OpenCV 中的行为cv::Mat。我乘以一些 8 位矩阵并将它们相加。结果似乎不一致。
OpenCV 是否在每次乘法之后或每次加法之后立即将浮点结果转换回 8 位,或者这里可能发生了一些“融合乘加”?
#include <iostream>
#include <opencv2/opencv.hpp>
int main() {
for (int value = 1; value < 10; value++) {
cv::Mat im(1, 1, CV_8U, value);
cv::Mat result[10];
result[0] = im*0.1f;
result[1] = im*0.1f + im*0.1f;
result[2] = im*0.1f + im*0.1f + im*0.1f;
result[3] = im*0.1f + im*0.1f + im*0.1f + im*0.1f;
result[4] = im*0.1f + im*0.1f + im*0.1f + im*0.1f + im*0.1f;
result[5] = im*0.1f + im*0.1f + im*0.1f + im*0.1f + im*0.1f + im*0.1f;
result[6] = im*0.1f + im*0.1f + im*0.1f + im*0.1f + im*0.1f + im*0.1f + im*0.1f;
result[7] = im*0.1f + im*0.1f + im*0.1f + im*0.1f + im*0.1f + im*0.1f + im*0.1f + im*0.1f;
result[8] = im*0.1f + im*0.1f + im*0.1f + im*0.1f + im*0.1f + im*0.1f + im*0.1f + im*0.1f + im*0.1f;
result[9] = im*0.1f + im*0.1f + im*0.1f + im*0.1f + im*0.1f + im*0.1f + im*0.1f + im*0.1f + im*0.1f + im*0.1f;
std::cout << "base value: " << value << std::endl;
for (int i = 0; i < 10; i++) {
std::cout << i + 1 << ": " << int(result[i].at<uint8_t>(0, 0)) << "\t";
}
std::cout << std::endl;
}
return 0;
}
Run Code Online (Sandbox Code Playgroud)
输出是:
base value: 1
1: 0 2: 0 3: 0 4: 0 5: 0 6: 0 7: 0 8: 0 9: 0 10: 0
base value: 2
1: 0 2: 0 3: 0 4: 0 5: 0 6: 0 7: 0 8: 0 9: 0 10: 0
base value: 3
1: 0 2: 1 3: 1 4: 1 5: 1 6: 1 7: 1 8: 1 9: 1 10: 1
base value: 4
1: 0 2: 1 3: 1 4: 1 5: 1 6: 1 7: 1 8: 1 9: 1 10: 1
base value: 5
1: 0 2: 1 3: 2 4: 2 5: 2 6: 2 7: 2 8: 2 9: 2 10: 2
base value: 6
1: 1 2: 1 3: 2 4: 3 5: 4 6: 5 7: 6 8: 7 9: 8 10: 9
base value: 7
1: 1 2: 1 3: 2 4: 3 5: 4 6: 5 7: 6 8: 7 9: 8 10: 9
base value: 8
1: 1 2: 2 3: 3 4: 4 5: 5 6: 6 7: 7 8: 8 9: 9 10: 10
base value: 9
1: 1 2: 2 3: 3 4: 4 5: 5 6: 6 7: 7 8: 8 9: 9 10: 10
Run Code Online (Sandbox Code Playgroud)
cv::Mat在 OpenCV 中,出于性能原因,不会立即评估之间的操作。\n它使用延迟评估。如果执行A = B*0.1 + C*0.1( A, B, Cis cv::Mat),则创建cv::MatExpr包含B、C及其比例值 的a 。并且仅当操作数超过 3 或分配给实际的.0.10.1cv::Mat
如果您不分配B*0.1 + C*0.1给实际的cv::Mat,则评估将不会发生。
如果这样做,则创建保存和 的A = B*0.1 + C*0.1 + D*0.1a ,然后创建保存最后的评估 和 的另一个。这就是为什么超过 3 次的运算是荒谬的。在评估期间,由于类型不是浮点,因此 0.x 会丢失。cv::MatExprB*0.1C*0.1cv::MatExprcv::MatExprD*0.1
为了避免这个问题,只需将 type 设置为CV_32F或CV_64F。
你im*0.1f + im*0.1f + im*0.1f + ...实际上是这样工作的。
result = im*0.1f + im*0.1f + im*0.1f + im*0.1f + ...\n \xe2\x88\xa7 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82\n \xe2\x94\x82 MatExpr MatExpr MatExpr MatExpr ...\n \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82\n \xe2\x94\x82 \xe2\x94\x94\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\xac\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x98 \xe2\x94\x82 \xe2\x94\x82\n \xe2\x94\x82 MatExpr \xe2\x94\x82 \xe2\x94\x82\n \xe2\x94\x82eval \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82\n \xe2\x94\x82 \xe2\x94\x94>\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\xac\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80<\xe2\x94\x98 \xe2\x94\x82\n \xe2\x94\x82 eval MatExpr \xe2\x94\x82\n \xe2\x94\x82 \xe2\x94\x82 \xe2\x94\x82\n \xe2\x94\x82 \xe2\x94\x94>\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\xac\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80<\xe2\x94\x98\n \xe2\x94\x82 eval MatExpr\n \xe2\x94\x82 ... ...\n \xe2\x94\x82 \n \xe2\x94\x82 \xe2\x94\x94>\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\xac\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80<\xe2\x94\x98\n \xe2\x94\x82 eval MatExpr\n \xe2\x94\x82 \xe2\x94\x82\n \xe2\x94\x94<\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80<\xe2\x94\x98\nRun Code Online (Sandbox Code Playgroud)\n\ncv::Mat*doubleclass MatOp_AddEx : public MatOp\n{\npublic:\n\n void assign(const MatExpr& expr, Mat& m, int type=-1) const;\n void add(const MatExpr& e1, const Scalar& s, MatExpr& res) const;\n static void makeExpr(MatExpr& res, const Mat& a, const Mat& b, double alpha, double beta, const Scalar& s=Scalar());\n\n // erased others for clarity\n};\n\nstatic MatOp_AddEx g_MatOp_AddEx; // only visible in matop.cpp\nRun Code Online (Sandbox Code Playgroud)\n\nMatExpr operator * (const Mat& a, double s)\n{\n MatExpr e;\n MatOp_AddEx::makeExpr(e, a, Mat(), s, 0); // meaning a*s + NULL-mat*0\n return e;\n}\nRun Code Online (Sandbox Code Playgroud)\n\ninline void MatOp_AddEx::makeExpr(MatExpr& res, const Mat& a, const Mat& b, double alpha, double beta, const Scalar& s)\n{\n res = MatExpr(&g_MatOp_AddEx, 0, a, b, Mat(), alpha, beta, s); // MatExpr constructor\n}\nRun Code Online (Sandbox Code Playgroud)\n\ncv::MatExpr+cv::MatExprMatExpr operator + (const MatExpr& e1, const MatExpr& e2)\n{\n MatExpr en;\n e1.op->add(e1, e2, en); // MatOp_AddEx inherits MatOp\n return en;\n}\nRun Code Online (Sandbox Code Playgroud)\n\nvoid MatOp::add(const MatExpr& e1, const MatExpr& e2, MatExpr& res) const\n{\n CV_INSTRUMENT_REGION()\n\n if( this == e2.op )\n {\n double alpha = 1, beta = 1;\n Scalar s;\n Mat m1, m2;\n if( isAddEx(e1) && (!e1.b.data || e1.beta == 0) )\n {\n m1 = e1.a;\n alpha = e1.alpha;\n s = e1.s;\n }\n else\n e1.op->assign(e1, m1); // <- Evaluation. Remember that type is set to auto(-1) by default\n\n if( isAddEx(e2) && (!e2.b.data || e2.beta == 0) )\n {\n m2 = e2.a;\n beta = e2.alpha;\n s += e2.s;\n }\n else\n e2.op->assign(e2, m2); // <- Evaluation\n MatOp_AddEx::makeExpr(res, m1, m2, alpha, beta, s);\n }\n else\n e2.op->add(e1, e2, res); // <- Evaluation\n\n}\nRun Code Online (Sandbox Code Playgroud)\n\nvoid MatOp_AddEx::assign(const MatExpr& e, Mat& m, int _type) const\n{\n Mat temp, &dst = _type == -1 || e.a.type() == _type ? m : temp;\n if( e.b.data )\n {\n if( e.s == Scalar() || !e.s.isReal() )\n {\n if( e.alpha == 1 )\n {\n if( e.beta == 1 )\n cv::add(e.a, e.b, dst);\n else if( e.beta == -1 )\n cv::subtract(e.a, e.b, dst);\n else\n cv::scaleAdd(e.b, e.beta, e.a, dst);\n }\n else if( e.beta == 1 )\n {\n if( e.alpha == -1 )\n cv::subtract(e.b, e.a, dst);\n else\n cv::scaleAdd(e.a, e.alpha, e.b, dst);\n }\n else\n cv::addWeighted(e.a, e.alpha, e.b, e.beta, 0, dst);\n\n if( !e.s.isReal() )\n cv::add(dst, e.s, dst);\n }\n else\n cv::addWeighted(e.a, e.alpha, e.b, e.beta, e.s[0], dst);\n }\n else if( e.s.isReal() && (dst.data != m.data || fabs(e.alpha) != 1))\n {\n e.a.convertTo(m, _type, e.alpha, e.s[0]);\n return;\n }\n else if( e.alpha == 1 )\n cv::add(e.a, e.s, dst);\n else if( e.alpha == -1 )\n cv::subtract(e.s, e.a, dst);\n else\n {\n e.a.convertTo(dst, e.a.type(), e.alpha);\n cv::add(dst, e.s, dst);\n }\n\n if( dst.data != m.data )\n dst.convertTo(m, m.type());\n}\nRun Code Online (Sandbox Code Playgroud)\n