I am trying to use a custom allocator with opencv. I tried to test it but the beahvior doesn’t make sense to me. I created a CV mat inside some scope. I can see the print inside the allocate() but don’t see the print inside the deallocate().
Any explanation?
#include "gtest/gtest.h"
#include "opencv2/opencv.hpp"
class CvMatAllocatorTest : public ::testing::Test
{
protected:
};
class UserMatDebugAllocator : public cv::MatAllocator
{
public:
UserMatDebugAllocator()
{
m_defaultAllocator = cv::Mat::getDefaultAllocator();
m_stdAllocator = cv::Mat::getStdAllocator();
std::cout << "UserMatDebugAllocator: constructor" << std::endl;
}
~UserMatDebugAllocator() override
{
std::cout << "UserMatDebugAllocator: destructor" << std::endl;
}
cv::UMatData* allocate(int dims, const int* sizes, int type,
void* data, size_t* step, cv::AccessFlag flags, cv::UMatUsageFlags usageFlags) const override
{
std::cout << "UserMatDebugAllocator: allocate" << std::endl;
return m_defaultAllocator->allocate(dims, sizes, type, data, step, flags, usageFlags);
}
bool allocate(cv::UMatData* data, cv::AccessFlag accessflags, cv::UMatUsageFlags usageFlags) const override
{
std::cout << "UserMatDebugAllocator: allocate2" << std::endl;
return m_defaultAllocator->allocate(data, accessflags, usageFlags);
}
void deallocate(cv::UMatData* data) const override
{
std::cout << "UserMatDebugAllocator: deallocate" << std::endl;
return m_defaultAllocator->deallocate(data);
}
void map(cv::UMatData* data, cv::AccessFlag accessflags) const override
{
std::cout << "UserMatDebugAllocator: map" << std::endl;
return m_defaultAllocator->map(data, accessflags);
}
void unmap(cv::UMatData* data) const override
{
std::cout << "UserMatDebugAllocator: unmap" << std::endl;
return m_defaultAllocator->unmap(data);
}
void download(cv::UMatData* data, void* dst, int dims, const size_t sz[],
const size_t srcofs[], const size_t srcstep[],
const size_t dststep[]) const override
{
std::cout << "UserMatDebugAllocator: download" << std::endl;
return m_defaultAllocator->download(data, dst, dims, sz, srcofs, srcstep, dststep);
}
void upload(cv::UMatData* data, const void* src, int dims, const size_t sz[],
const size_t dstofs[], const size_t dststep[],
const size_t srcstep[]) const override
{
std::cout << "UserMatDebugAllocator: upload" << std::endl;
return m_defaultAllocator->upload(data, src, dims, sz, dstofs, dststep, srcstep);
}
void copy(cv::UMatData* srcdata, cv::UMatData* dstdata, int dims, const size_t sz[],
const size_t srcofs[], const size_t srcstep[],
const size_t dstofs[], const size_t dststep[], bool sync) const override
{
std::cout << "UserMatDebugAllocator: copy" << std::endl;
return m_defaultAllocator->copy(srcdata, dstdata, dims, sz, srcofs, srcstep, dstofs, dststep, sync);
}
cv::BufferPoolController* getBufferPoolController(const char* id = NULL) const override
{
std::cout << "UserMatDebugAllocator: getBufferPoolController" << std::endl;
return m_defaultAllocator->getBufferPoolController(id);
}
cv::MatAllocator* m_defaultAllocator;
cv::MatAllocator* m_stdAllocator;
};
TEST_F(CvMatAllocatorTest, cvMatAllocatorTest)
{
std::cout << "test start" << std::endl;
UserMatDebugAllocator userAllocator;
cv::Mat::setDefaultAllocator(&userAllocator);
{
cv::Mat mat(1000, 1000, CV_8UC3);
mat.at<cv::Vec3b>(500, 500) = cv::Vec3b(255, 0, 0);
}
sleep(3);
std::cout << "test end" << std::endl;
}
Output:
test start
UserMatDebugAllocator: constructor
UserMatDebugAllocator: allocate
test end
UserMatDebugAllocator: destructor
[ OK ] CvMatAllocatorTest.cvMatAllocatorTest (3000 ms)
I finally managed to find the problem. allocate() is the one that tells the CV Mat who is its allocator, and later this is used when the unmap() is called. In the sample code posted in the question, the CV Mat wasn't aware of UserMatDebugAllocator being its allocator, and hence no other function from this class was called during deallocation.
Changing the code to be as follows makes the deallocate() prints being shown:
cv::UMatData* allocate(int dims, const int* sizes, int type,
void* data, size_t* step, cv::AccessFlag flags, cv::UMatUsageFlags usageFlags) const override
{
std::cout << "UserMatDebugAllocator: allocate" << std::endl;
cv::UMatData* ret = m_defaultAllocator->allocate(dims, sizes, type, data, step, flags, usageFlags);
if (nullptr != ret)
{
// the following line is what causes the unmap() to reach this class as well
ret->currAllocator = this;
}
return ret;
}
void unmap(cv::UMatData* data) const override
{
std::cout << "UserMatDebugAllocator: unmap" << std::endl;
if ((data->urefcount == 0) && (data->refcount == 0))
{
deallocate(data);
}
}