Reputation: 3124
is there a way of creating a cv::Mat
from external data pointer, but making that object responsible for deleting the data?
i.e., 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?
Upvotes: 2
Views: 7366
Reputation: 1039
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
}
Upvotes: 1
Reputation: 1458
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, i.e. in the same class.
Upvotes: 5
Reputation: 41765
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;
Upvotes: 1
Reputation: 20264
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
Upvotes: 0