cv ::来自外部原始指针数据的mat,释放自身

时间:2016-02-25 11:15:15

标签: c++ opencv

有没有办法从外部数据指针创建cv::Mat,但是让该对象负责删除数据?

即,我有一个函数从cv::Mat指针创建void *

cv::Mat createMat() {

  void *data = (...);

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

我希望我的返回cv::Mat负责发布数据。我怎么能这样做?

4 个答案:

答案 0 :(得分:5)

所有OpenCV函数都希望cv::Mat::data为void *,因此您无法更改它。如果从cv::Mat继承并编写自己的析构函数,则会与OpenCV不兼容,因为它们不会返回派生类型。 我建议将cv::Mat包含为类的属性,并保留std::shared_ptr或其他智能指针来管理基础void*的生命周期。 只需使用std::shared_ptr::get()共享指针中的原始指针初始化OpenCV矩阵 http://www.cplusplus.com/reference/memory/shared_ptr/get/并确保它们具有相同的生命周期,即在同一类中。

答案 1 :(得分:1)

我提出了类似于@SpamBot的解决方案。但是,您无法使用std::shared_ptr::get()初始化Mat,因为它需要void*,而不是const void*

实际上,您从cv::Mat继承,并保留shared_ptr<void>数据。一旦超出范围,这将释放内存。

#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;
}

请注意,如果您将SelfDeallocMat复制到另一个Mat,但SelfDeallocMat超出范围,则Mat将包含垃圾值:

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

答案 2 :(得分:1)

我看到两种解决方案可以做你想做的事情,而不必派生到cv :: Mat。

最简单的就是致电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*/);

像这样你可以使用cv::Mat对象做你想做的事情,而std::vector析构函数不被称为你的内存是可用的。 然后它被std :: vector析构函数破坏。

第二种解决方案更具技术性,但更接近您想要做的事情。

如果你检查每个Mat对象都有一个成员allocator,那么这个成员是cv::MatAllocator类型的指针,什么是抽象类。

如果您阅读了这两个创建者的实现,您会看到它使用分配器来管理内存分配。 所以做你需要做的事情: 1)创建自己的分配器。 2)创建一个像创建用途的方法一样的对象。

所以或多或少看起来像这样:

    #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
}

答案 3 :(得分:0)

您正在使用的智能指针实际上拥有数据。您希望将此所有权转移到另一个智能指针cv::Matcv::Mat是一种智能指针)。这是不可能的,因为C ++智能指针和cv::Mat

之间没有关系