Is cv::Mat class flawed by design?

前端 未结 4 948
感情败类
感情败类 2020-12-29 07:06

I work a lot with the OpenCV C++ interface and designed a number of classes which use Mat\'s as private resources.

Recently, I got concerned about the Mat class, as

相关标签:
4条回答
  • 2020-12-29 07:52

    Adding and expanding on Vadim's answer, here are some thoughts on the topic.

    I have also used cv::Mat extensively, in many ways, and enjoyed its benefits.

    A general truth in programming is that you have to balance different opposing needs of a projects. One of them is performance versus maintainability. And this was resolved once and for all with "Premature optimization is evil.". This approach is great, but many programmers just follow it blindly.

    For image processing, performance is of paramount importance. Without it, many projects simply aren't feasible. So it is never premature to optimize when processing images. It is one of those very few fields where milliseconds count, where all you do is measured by both quality and speed. And, it may be hard to digest it if you come from C#, Java, or user interface design, but for this speed improvement, it's worth sacrificing some of the established practices of object oriented design.

    If you go through the OpenCV's source code, you will see an incredible emphasis on optimization: SSE-based functions, NEON functions, pointer tricks, all kinds of algorithm curiosities, graphics processor implementations, OpenCL implementations, lookup tables, and many, many others which would be considered overkill, hard-to-mantain, or "premature optimization" in other types of projects.

    A small change in your app architecture (like cv::Mat allocation strategy) can make a really huge difference when it comes to performance. Sharing an image on an embedded device, instead of cloning may make the difference between a great gadget and a dead-end proof-of-concept.

    So, when Vadim said they do not see an efficient way to implement your suggested changes, he suggested that the performance penalties of those changes would not cover the benefits.

    Such a project is harder to write and to maintain, but it is for good. And usually, the difficult part of an imaging project is writing the right algorithm. Encapsulating it is just the 1% work at the end.

    0 讨论(0)
  • 2020-12-29 07:53

    [shameless ad] we now have answers.opencv.org, which is sort of StackOverflow for OpenCV-specific questions.

    Now to the questions:

    1. No. We do not see an efficient way to implement this. If you do, let's discuss it.

    2. Yes. Do not use Mat::clone() unless you plan to modify the data. Reference counting takes care of proper deallocation of the data when it's no longer used.

    0 讨论(0)
  • 2020-12-29 07:58

    To answer the OPs question:

    Yes, most definitely! As a couple of people have pointed out: OpenCV gives you no possibility to describe a const reference to an image. This is indeed a flaw. "const cv::Mat&" is not what a c++ programmer would expect it to be and I have often found myself sprinkling clone() calls all over my code to the point where I lose the benefit of the data sharing to begin with.

    To answer Vadims question of how to do it efficiently:

    It is absolutely possible to do this efficiently, though not without an API change. Look how Qt abandoned the explicit sharing model it had before Qt 4(analogue to OpenCV's current model) to it's current implicit sharing (copy on write) with great success. Basically all function calls which mutate an object, or returns a reference which could later mutate the object have to "deref" it. That is make a copy if there are more than one references.

    The cost of this is miniscule compared to the average cost of an image operation. It only becomes prohibitive if it has to be performed per pixel. That's why the class need to be separated into two. Much like cv::Mat and cv::Mat_. One that is responsible for the implicit sharing and copying, and one which is just a templated wrapper of a IplImage. Here is an example of what the API could look like(I have chosen overly explicit names for clarity):

    // 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);
    

    I realize there is too much OpenCV code floating around to make any radical changes and the window to make an API change with OpenCV 3 is closed, but I don't see why it shouldn't be possible to add a new improved interface.

    0 讨论(0)
  • 2020-12-29 08:03

    Yes, this is a poor design. Because Mat implements shared ownership internally, it’s not compatible with the standard way of choosing ownership policy, namely smart pointers. The basic issue is that data and ownership are orthogonal, and ought to be separated.

    Because it is mutable, even a const Mat is more like a const shared_ptr<Mat>, with no way of describing that the contained Mat ought to be mutable, i.e., shared_ptr<const Mat>. This bears great similarity to the problems with final in Java, if you’re familiar.

    I believe you can skirt these issues by wrapping Mat in a class that exposes the same interface as Mat, but which implements copy-on-write behaviour atop the default shared implementation.

    0 讨论(0)
提交回复
热议问题