有没有办法从外部数据指针创建cv::Mat
,但是让该对象负责删除数据?
即,我有一个函数从cv::Mat
指针创建void *
,
cv::Mat createMat() {
void *data = (...);
cv::Mat data_m(rows, cols, CV_8UC1, data);
return data_m;
}
我希望我的返回cv::Mat
负责发布数据。我怎么能这样做?
答案 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::Mat
(cv::Mat
是一种智能指针)。这是不可能的,因为C ++智能指针和cv::Mat