OpenCV 矩阵加法、乘法与浮点和 8 位值的奇怪行为

flm*_*620 2 c++ opencv

我正在使用 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)

김선달*_*김선달 5

cv::Mat在 OpenCV 中,出于性能原因,不会立即评估之间的操作。\n它使用延迟评估。如果执行A = B*0.1 + C*0.1( A, B, Cis cv::Mat),则创建cv::MatExpr包含BC及其比例值 的a 。并且仅当操作数超过 3 或分配给实际的.0.10.1cv::Mat

\n\n

如果您不分配B*0.1 + C*0.1给实际的cv::Mat,则评估将不会发生。

\n\n

如果这样做,则创建保存和 的A = B*0.1 + C*0.1 + D*0.1a ,然后创建保存最后的评估 和 的另一个。这就是为什么超过 3 次的运算是荒谬的。在评估期间,由于类型不是浮点,因此 0.x 会丢失。cv::MatExprB*0.1C*0.1cv::MatExprcv::MatExprD*0.1

\n\n

为了避免这个问题,只需将 type 设置为CV_32FCV_64F

\n\n

im*0.1f + im*0.1f + im*0.1f + ...实际上是这样工作的。

\n\n
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\n
Run Code Online (Sandbox Code Playgroud)\n\n

马托普.cpp

\n\n

cv::Mat*double

\n\n
class 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\n
Run Code Online (Sandbox Code Playgroud)\n\n
MatExpr 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}\n
Run Code Online (Sandbox Code Playgroud)\n\n
inline 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}\n
Run Code Online (Sandbox Code Playgroud)\n\n

cv::MatExpr+cv::MatExpr

\n\n
MatExpr 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}\n
Run Code Online (Sandbox Code Playgroud)\n\n
void 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}\n
Run Code Online (Sandbox Code Playgroud)\n\n
void 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}\n
Run Code Online (Sandbox Code Playgroud)\n