简体   繁体   中英

cv::Mat from external raw pointer data, releasing itself

is there a way of creating a cv::Mat from external data pointer, but making that object responsible for deleting the data?

ie, I have a function creating a cv::Mat from a void * pointer,

cv::Mat createMat() {

  void *data = (...);

  cv::Mat data_m(rows, cols, CV_8UC1, data);
  return data_m;
}

and I want that my returned cv::Mat to be responsible for releasing the data. How can I do this?

All OpenCV functions expect cv::Mat::data to be a void*, so you cannot change that. If you inherit from cv::Mat and write your own destructor, you become incompatible from OpenCV because they do not return your derived type. I would suggest to contain your cv::Mat as an attribute of a class, and keep a std::shared_ptr or other smart pointer to manage the lifetime of the underlying void* . Just initialize the OpenCV matrix with the raw pointer from the shared pointer with std::shared_ptr::get() http://www.cplusplus.com/reference/memory/shared_ptr/get/ and make sure they have the same lifetime, ie in the same class.

I come up with a solution similar to @SpamBot 's one. However, you can't use std::shared_ptr::get() to initialize a Mat , since it expects a void* , and not a const void* .

In practice, you inherit from a cv::Mat , and keep a shared_ptr<void> of the data. This will free the memory once it goes out of scope.

#include <opencv2\opencv.hpp>
#include <memory>
using namespace cv;
using namespace std;


struct SelfDeallocMat : cv::Mat
{
    shared_ptr<void> pdata;
    SelfDeallocMat(int rows, int cols, int type, void* data, size_t step = 0U) 
        : Mat(rows, cols, type, data, step), pdata(data) {};
};

int main()
{
    uchar* pdata = new uchar[3];

    pdata[0] = 1;
    pdata[1] = 2;
    pdata[2] = 3;

    for (int i = 0; i < 3; ++i) {cout << int(pdata[i]) << " "; }
    cout << endl;

    {
        SelfDeallocMat m(1, 3, CV_8UC1, (void*)pdata);
        cout << m << endl;

    }

    // Some garbage values
    for (int i = 0; i < 3; ++i) { cout << int(pdata[i]) << " "; }
    cout << endl;

    return 0;
}

Note that if you copy the SelfDeallocMat to another Mat , but the SelfDeallocMat goes out of scope, the Mat will contain garbage values:

Mat n;
{
    SelfDeallocMat m(1, 3, CV_8UC1, (void*)pdata);
    n = m;
    cout << m << endl;
}
// Garbage!
cout << n << endl;

I see two solutions for doing what you want without having to derivate to cv::Mat.

The easiest one is call std::vector .

std::vector<what_type_you_want> data = {a,b,c,d,thank_you_cpp_eleven_for_the_initializer_list};

// If you are a memory size maniac.
data.shrink_to_fit();

    cv::Mat data_m(rows,cols,type_with_or_without_channels,data.data()/*you don't need to do a reinterpret_cast<void*> it work as is*/,data.size()/*not needed*/);

Like this you can do what ever you want with the cv::Mat object while the std::vector destructor is not called your memory is availlable. Then it's destroy by the std::vector destructor.

Second solution more technical but more closer about what you want to do.

If you check every Mat object have a member allocator this member is a pointer of type cv::MatAllocator what is an abstract class.

If you read the implementation of both creator you'll see it use an allocator for manage the memory allocation. So doing what you waht you need to : 1) create your own allocator. 2) create an the object like the method create use to do it.

So more or less it look like this :

    #include <opencv2/core.hpp>
    #include <iostream>
    #include <cstdlib>

namespace
{    

        class MyMatAllocator : public cv::MatAllocator


   {
    public:

    cv::UMatData* allocate(int dims, const int* sizes, int type,
                       void* data0, size_t* step, int /*flags*/, cv::UMatUsageFlags /*usageFlags*/) const
    {
        std::cout<<"I am call"<<std::endl;

        size_t total = CV_ELEM_SIZE(type);
        for( int i = dims-1; i >= 0; i-- )
        {
            if( step )
            {
                if( data0 && step[i] != CV_AUTOSTEP )
                {
                    CV_Assert(total <= step[i]);
                    total = step[i];
                }
                else
                    step[i] = total;
            }
            total *= sizes[i];
        }
        uchar* data = data0 ? (uchar*)data0 : (uchar*)cv::fastMalloc(total);
        cv::UMatData* u = new cv::UMatData(this);
        u->data = u->origdata = data;
        u->size = total;
//        if(data0)
//            u->flags |= cv::UMatData::USER_ALLOCATED;


        return u;
    }

    bool allocate(cv::UMatData* u, int /*accessFlags*/, cv::UMatUsageFlags /*usageFlags*/) const
    {
        if(!u) return false;
        return true;
    }

    void deallocate(cv::UMatData* u) const
    {

        if(!u)
            return;

        CV_Assert(u->urefcount == 0);
        CV_Assert(u->refcount == 0);
//        if( !(u->flags & cv::UMatData::USER_ALLOCATED) )
//        {
            std::cout<<"deallocation"<<std::endl;
            cv::fastFree(u->origdata);
            u->origdata = 0;
//        }
        delete u;
    }
};

void create(cv::Mat& obj,int d, const int* _sizes, int _type,void* p)
{
    int i;
    CV_Assert(0 <= d && d <= CV_MAX_DIM && _sizes);
    _type = CV_MAT_TYPE(_type);

    if( obj.data && (d == obj.dims || (d == 1 && obj.dims <= 2)) && _type == obj.type() )
    {
        if( d == 2 && obj.rows == _sizes[0] && obj.cols == _sizes[1] )
            return;
        for( i = 0; i < d; i++ )
            if( obj.size[i] != _sizes[i] )
                break;
        if( i == d && (d > 1 || obj.size[1] == 1))
            return;
    }

    obj.release();
    if( d == 0 )
        return;
    obj.flags = (_type & CV_MAT_TYPE_MASK) | cv::Mat::MAGIC_VAL;
//    setSize(*this, d, _sizes, 0, true);

    CV_Assert( 0 <= d && d <= CV_MAX_DIM );
     if( obj.dims != d )
     {
         if( obj.step.p != obj.step.buf )
         {
             cv::fastFree(obj.step.p);
             obj.step.p = obj.step.buf;
             obj.size.p = &obj.rows;
         }
         if( d > 2 )
         {
             obj.step.p = (size_t*)cv::fastMalloc(d*sizeof(obj.step.p[0]) + (d+1)*sizeof(obj.size.p[0]));
             obj.size.p = (int*)(obj.step.p + d) + 1;
             obj.size.p[-1] = d;
             obj.rows = obj.cols = -1;
         }
     }

     obj.dims = d;
     if( !_sizes )
         return;

     size_t esz = CV_ELEM_SIZE(obj.flags), esz1 = CV_ELEM_SIZE1(obj.flags), total = esz;

     for( i = d-1; i >= 0; i-- )
     {
         int s = _sizes[i];
         CV_Assert( s >= 0 );
         obj.size.p[i] = s;

         obj.step.p[i] = total;
         int64 total1 = (int64)total*s;
         if( (uint64)total1 != (size_t)total1 )
             CV_Error( CV_StsOutOfRange, "The total matrix size does not fit to \"size_t\" type" );
         total = (size_t)total1;
     }

     if( d == 1 )
     {
         obj.dims = 2;
         obj.cols = 1;
         obj.step[1] = esz;
     }

    if( obj.total() > 0 )
    {
        cv::MatAllocator *a = new MyMatAllocator();

            obj.u = a->allocate(obj.dims, obj.size.p, _type, p, obj.step.p, 0, cv::USAGE_DEFAULT);
            CV_Assert(obj.u != 0);

        CV_Assert( obj.step[obj.dims-1] == (size_t)CV_ELEM_SIZE(obj.flags) );
    }

    obj.addref();


    int j;
    for( i = 0; i < obj.dims; i++ )
    {
        if( obj.size[i] > 1 )
            break;
    }

    for( j = obj.dims-1; j > i; j-- )
    {
        if( obj.step[j]*obj.size[j] < obj.step[j-1] )
            break;
    }

    uint64 t = (uint64)obj.step[0]*obj.size[0];
    if( j <= i && t == (size_t)t )
        obj.flags |= cv::Mat::CONTINUOUS_FLAG;
    else
        obj.flags &= ~cv::Mat::CONTINUOUS_FLAG;

    d = obj.dims;
    if( d > 2 )
        obj.rows = obj.cols = -1;
    if(obj.u)
        obj.datastart = obj.data = obj.u->data;
    if( obj.data )
    {
        obj.datalimit = obj.datastart + obj.size[0]*obj.step[0];
        if( obj.size[0] > 0 )
        {
            obj.dataend = obj.ptr() + obj.size[d-1]*obj.step[d-1];
            for( int i = 0; i < d-1; i++ )
                obj.dataend += (obj.size[i] - 1)*obj.step[i];
        }
        else
            obj.dataend = obj.datalimit;
    }
    else
        obj.dataend = obj.datalimit = 0;
}

void create(cv::Mat& obj, const int& rows,const int& cols, int _type,void* p)
{
    const int sizes[2] = {rows,cols};

        create(obj,2,sizes,_type,p);
    }

}

    int main(int argc,char* argv[])
    {
        uchar* toto = new uchar[10];

    std::iota(toto,toto+10,1);

    cv::Mat a;

    create(a,1,10,CV_8UC1,toto);

    return EXIT_SUCCESS
}

The smart pointer you are using actually owns the data. You want to transfer this ownership to another smart pointer which is cv::Mat ( cv::Mat is a kind of smart pointer). That is not possible since there is no relation between C++ smart pointers and cv::Mat

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM