cv :: Mat类是否有缺陷?

X''*_*X'' 16 c++ oop resources opencv

我使用OpenCV C++接口工作了很多,并设计了许多使用Mat作为私有资源的类.

最近,我关注Mat类,因为它总是使用图像数据作为共享资源,除非我明确地调用clone.即使我写了,const Mat我也不能确定imagedata以后不会从外面改变.

所以我需要克隆以确保封装.但是需要明确克隆Mat的问题在于它通常是不必要且昂贵的.另一方面,我理解共享图像数据的需求源于roi选择器,并且能够编写如下内容: Mat m_small = m_big(my_roi).

我的问题是:

1.)cv :: Mat类不应该被懒得克隆吗?因此,用户不会将Mat视为来自外部的共享资源处理程序.用户是否应该显式实例化一个类似于SharedMat需要真正的共享图像数据的类?
2.)如果cv :: Mat作为一个类的私有资源,你有没有比克隆更好的策略


更新:" Mat::clone() 除非您计划修改数据,否则不要使用." (作者:Vadim Pisarevsky)
这个想法存在问题. 考虑一下这个类的情况:

class Res_handler{
public:
  const Mat emit_mat(){ return m_treasure; } // I argue you are compelled to clone here.
private:
  Mat m_treasure;
};
Run Code Online (Sandbox Code Playgroud)

如果你不在clone这种情况下你可以写

Mat m_pirate = res_handler.emit_mat(); m_pirate = Scalar(0,0,0);
Run Code Online (Sandbox Code Playgroud)

通过和之间的共享图像数据导致m_treasure内部完全黑屏.:)所以为了避免意外修改内部,你需要.res_handlerm_piratem_treasurem_treasureclone

另一方面,这个解决方案也存在缺陷:

const Mat m_pirate = res_handler.emit_mat(); 
Run Code Online (Sandbox Code Playgroud)

因为m_treasure也可以修改,所以内容m_pirate在后台变化,给海盗的程序员带来了极大的麻烦.:)

Jon*_*rdy 12

是的,这是一个糟糕的设计.因为在Mat内部实现共享所有权,所以它与选择所有权策略的标准方式不兼容,即智能指针.基本问题是数据和所有权是正交的,应该是分开的.

因为它是可变的,即使a const Mat更像是a const shared_ptr<Mat>,也无法描述所包含的Mat应该是可变的,即shared_ptr<const Mat>.final如果您熟悉的话,这与Java中的问题非常相似.

我相信你可以通过包装Mat暴露相同接口的类来解决这些问题Mat,但是它在默认的共享实现上实现了写时复制行为.


小智 11

[无耻的广告]我们现在有answers.opencv.org,这是针对OpenCV特定问题的StackOverflow.

现在问题:

  1. 不,我们没有看到实现这一目标的有效方法.如果你这样做,我们来讨论吧.

  2. 是.Mat::clone()除非您计划修改数据,否则请勿使用.当不再使用数据时,引用计数负责正确地重新分配数据.

  • 坚持,我认为根据stackoverflow规则,Vadim应该提到他是一个OpenCV开发人员. (2认同)

bgp*_*000 6

回答OP问题:

是的,绝对是!正如一些人所指出的那样:OpenCV使您无法描述对图像的const引用.这确实是一个缺陷."const cv :: Mat&"并不是c ++程序员所期望的那样,而且我经常发现自己在我的代码中遍布clone()调用,以至于我开始失去数据共享的好处.

要回答Vadims有关如何有效地做到这一点的问题:

尽管不是没有API更改,但绝对有可能有效地执行此操作.看看Qt如何放弃它在Qt 4(类似于OpenCV的当前模型)之前的显式共享模型,以及它当前的隐式共享(写入时复制)并取得了巨大的成功.基本上所有改变对象的函数调用,或者返回一个稍后可以改变对象的引用都必须"deref"它.如果有多个引用,那就是复制.

与图像操作的平均成本相比,这是微不足道的.如果必须按像素执行,则变得禁止.这就是为什么这个班需要分成两个.很像cv :: Mat和cv :: Mat_.一个负责隐式共享和复制,一个只是IplImage的模板包装器.这里的API是什么一个例子可能看起来像(我已经选择了清晰明确的过度名):

// The following makes no unnecessary copies. Only a 
// couple of atomic increments and decrements.
const cv::Image img = cv::Image("lenna.bmp").toGray().brighter(0.3).inverted();

cv::Image copy(img);// Still no deep copy.

cv::ConstImageRef<char> src = img.constRef<char>();// Still no deep copy.

// This is where the copy(detach) happens.
// "img" is left untouched
cv::MutableImageRef<char> dst = copy.ref<char>();

// The following is as efficient as it has ever been.
for(int y = 0; y<dst.height(); y++)
    for(int x = 0; x<dst.width(); x++)
        dst.at(x, y) += src.at(x, y);
Run Code Online (Sandbox Code Playgroud)

我意识到有太多的OpenCV代码浮动进行任何根本性的改变,并且用OpenCV 3进行API更改的窗口已关闭,但我不明白为什么不应该添加新的改进接口.