OpenCV两个Mat相减的隐藏秘密

起因

今天在看同事写的代码时,发现一个“错误”:
他的原意是实现以下功能:

cv::Mat absDiff;
cv::absdiff(mat1, mat2, absDiff);

其中mat1mat2均为CV_8UC1类型。

但是可能是一时没想起这个函数,于是他写成了这个样子:

cv::Mat absDiff = cv::abs(mat1 - mat2);

问题

于是我认真地告诉他,这样做是错的。假设mat1[0]mat2[255],那么mat1 - mat2将会得到[0],因为cv::saturate_cast<uchar>(0 - 255) == 0。则cv::abs([0])自然就是[0],而他的期望是得到[255]

并且我写出如下代码证明他是错的:

cv::Mat diff = mat1 - mat;

结果diff确实是[0]。那么cv::Mat absDiff = cv::abs(diff);肯定就是[0]了。

但是他坚持让我用cv::Mat absDiff = cv::abs(mat1 - mat2);测试。结果。。。。absDiff竟然真的是[255]!我当时就震惊了,同时隐隐有一种感觉:这其中一定隐藏着一个天大的秘密。

秘密

于是我进入调试模式,认真观察每一步,终于明白了玄机所在。

为什么我得到了[0]

先来分析我的测试代码:

cv::Mat diff = mat1 - mat;

在我以前的观念里,mat1 - mat是两个cv::Mat互相作用,实际上调用的是cv::subtract()。但是事实上,mat1 - mat2调用的是以下函数:

CV_EXPORTS MatExpr operator - (const Mat& a, const Mat& b);

MatExpr operator - (const Mat& a, const Mat& b)
{
    MatExpr e;
    MatOp_AddEx::makeExpr(e, a, b, 1, -1);
    return e;
}

可以看到,mat1 - mat2实际上是生成了一个MatExpr对象。在生成过程中,并没有进行实际的相减操作,而只是保存了ab,并记录了它们之间期望进行的操作:相减。实际的相减操作是在类型转换时进行的:

//cv::Mat diff = mat1 - mat; // 在这里调用了operator Mat()

MatExpr::operator Mat() const
{
    Mat m;
    op->assign(*this, m);
    return m;
}

assign中最终调用了cv::subtract()。所以mat1 - mat2得到了[0]

为什么同事得到了[255]

再来分析我同事的测试代码:

cv::Mat absDiff = cv::abs(mat1 - mat2);

从上面我们知道,mat1 - mat2生成了一个MatExpr对象。而cv::abs()调用的是

MatExpr abs(const MatExpr& e)
{
    CV_INSTRUMENT_REGION()

    MatExpr en;
    e.op->abs(e, en);
    return en;
}

这个函数再次生成了一个MatExpr对象,其中记录了操作对象e和期望进行的操作:取绝对值,而并没有进行实际的运算。然后同上面一样,它是在赋值给absDiff时调用operator Mat()进行运算的。神奇的地方来了,它把前面的相减操作与这里的取绝对值操作组合到了一起(而不是依次运算),最终调用的正是cv::absdiff()

于是谜底揭开了,一切的原因都是因为MatExpr这个中间对象实现了延迟运算和操作组合。

感叹

我还是太年轻!

  • 0
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值