diff --git a/cvnp/cvnp.cpp b/cvnp/cvnp.cpp index 7002113..4ea484d 100644 --- a/cvnp/cvnp.cpp +++ b/cvnp/cvnp.cpp @@ -113,130 +113,134 @@ namespace cvnp return true; } - // - // This class is a MatAllocator that uses a numpy array as the data pointer - // -------------------------------------------------------------------------------------------- - // The implementation is quite tricky: - // - A collection of all instances is kept in a static vector (see register_instance and unregister_instance) - // - The constructor: - // * keeps a reference to the numpy array - // * registers itself via register_instance() - // - allocate: - // * creates a new UMatData object - // "steals" the data pointer from the numpy array - // - deallocate will: - // * decrements the reference count and deletes the UMatData object if no more references - // * unregisters itself via unregister_instance() which will *destroy* this instance! - // *The destructor is called via deallocate()* - // - // As a consequence, MatAllocator_LinkArray is created via a *naked* new() and will be destroyed via deallocate() - // + namespace // anonymous namespace to hide MatAllocator_LinkArray from other translation units + { + // + // This class is a MatAllocator that uses a numpy array as the data pointer + // -------------------------------------------------------------------------------------------- + // The implementation is quite tricky: + // - A collection of all instances is kept in a static vector (see register_instance and unregister_instance) + // - The constructor: + // * keeps a reference to the numpy array + // * registers itself via register_instance() + // - allocate: + // * creates a new UMatData object + // "steals" the data pointer from the numpy array + // - deallocate will: + // * decrements the reference count and deletes the UMatData object if no more references + // * unregisters itself via unregister_instance() which will *destroy* this instance! + // *The destructor is called via deallocate()* + // + // As a consequence, MatAllocator_LinkArray is created via a *naked* new() and will be destroyed via deallocate() + // //#define DEBUG_MATALLOCATOR 0 #ifdef DEBUG_MATALLOCATOR - static int nbInstances = 0; + static int nbInstances = 0; #endif - class MatAllocator_LinkArray: public cv::MatAllocator - { - public: - MatAllocator_LinkArray(pybind11::array& a) : m_linked_array(a) + + class MatAllocator_LinkArray: public cv::MatAllocator { - register_instance(this); - #ifdef DEBUG_MATALLOCATOR - ++nbInstances; + public: + MatAllocator_LinkArray(pybind11::array& a) : m_linked_array(a) + { + register_instance(this); +#ifdef DEBUG_MATALLOCATOR + ++nbInstances; printf("MatAllocator_LinkArray constructor %p / nbInstances=%d\n", this, nbInstances); - #endif - } +#endif + } - ~MatAllocator_LinkArray() - { - #ifdef DEBUG_MATALLOCATOR - --nbInstances; + ~MatAllocator_LinkArray() + { +#ifdef DEBUG_MATALLOCATOR + --nbInstances; printf("MatAllocator_LinkArray destructor %p / nbInstances=%d\n", this, nbInstances); - #endif - } +#endif + } - cv::UMatData* allocate( - int dims, const int* sizes, int type, - void* data, size_t* step, cv::AccessFlag flags, cv::UMatUsageFlags usageFlags) const override - { - // This is the allocation that will be called by cv::Mat::create - #ifdef DEBUG_MATALLOCATOR - printf("Allocate 1\n"); - #endif - - // Create a new UMatData object - cv::UMatData *u = new cv::UMatData(this); - // "Steal" the data pointer from the numpy array - u->data = (uchar*)m_linked_array.mutable_data(0); - - // Set the reference counts to 0 - // (since UMatData is not documented, this is based on a reverse engineering of the OpenCV code) - // (deallocate won't be called if refcount or urefcount is set to 1 here) - u->refcount = u->urefcount = 0; // What is the difference between refcount and urefcount? this is undocumented - - return u; - } + cv::UMatData* allocate( + int dims, const int* sizes, int type, + void* data, size_t* step, cv::AccessFlag flags, cv::UMatUsageFlags usageFlags) const override + { + // This is the allocation that will be called by cv::Mat::create +#ifdef DEBUG_MATALLOCATOR + printf("Allocate 1\n"); +#endif - bool allocate(cv::UMatData* data, cv::AccessFlag accessflags, cv::UMatUsageFlags usageFlags) const override - { - // We never reach here (I guess) - #ifdef DEBUG_MATALLOCATOR - printf("Allocate 2\n"); - #endif - data->urefcount++; - return true; - } + // Create a new UMatData object + cv::UMatData *u = new cv::UMatData(this); + // "Steal" the data pointer from the numpy array + u->data = (uchar*)m_linked_array.mutable_data(0); - void deallocate(cv::UMatData* data) const override - { - #ifdef DEBUG_MATALLOCATOR - printf("Deallocate\n"); - #endif - // Decrement the reference count - data->urefcount--; - - // If no more references, delete the UMatData object - if (data->urefcount <= 0) + // Set the reference counts to 0 + // (since UMatData is not documented, this is based on a reverse engineering of the OpenCV code) + // (deallocate won't be called if refcount or urefcount is set to 1 here) + u->refcount = u->urefcount = 0; // What is the difference between refcount and urefcount? this is undocumented + + return u; + } + + bool allocate(cv::UMatData* data, cv::AccessFlag accessflags, cv::UMatUsageFlags usageFlags) const override { - delete data; - unregister_instance(const_cast(this)); + // We never reach here (I guess) +#ifdef DEBUG_MATALLOCATOR + printf("Allocate 2\n"); +#endif + data->urefcount++; + return true; } - } + void deallocate(cv::UMatData* data) const override + { +#ifdef DEBUG_MATALLOCATOR + printf("Deallocate\n"); +#endif + // Decrement the reference count + data->urefcount--; + + // If no more references, delete the UMatData object + if (data->urefcount <= 0) + { + delete data; + unregister_instance(const_cast(this)); + } + } - private: - mutable pybind11::array m_linked_array; - private: - static std::vector m_all_instances; - static std::mutex m_all_instances_mutex; + private: + mutable pybind11::array m_linked_array; + private: + static std::vector m_all_instances; + static std::mutex m_all_instances_mutex; - static void register_instance(MatAllocator_LinkArray* instance) - { - #ifdef DEBUG_MATALLOCATOR - printf("register_instance %p\n", instance); - #endif - std::lock_guard lock(m_all_instances_mutex); - m_all_instances.push_back(instance); - } - static void unregister_instance(MatAllocator_LinkArray* instance) - { - #ifdef DEBUG_MATALLOCATOR - printf("unregister_instance %p\n", instance); - #endif - - std::lock_guard lock(m_all_instances_mutex); - delete instance; - auto it = std::find(m_all_instances.begin(), m_all_instances.end(), instance); - if (it != m_all_instances.end()) - m_all_instances.erase(it); - else - throw std::runtime_error("MatAllocator_LinkArray::unregister_instance / instance not found"); - } - }; - std::vector MatAllocator_LinkArray::m_all_instances; // C++ at its best syntactic terseness - std::mutex MatAllocator_LinkArray::m_all_instances_mutex; // with static members + + static void register_instance(MatAllocator_LinkArray* instance) + { +#ifdef DEBUG_MATALLOCATOR + printf("register_instance %p\n", instance); +#endif + std::lock_guard lock(m_all_instances_mutex); + m_all_instances.push_back(instance); + } + static void unregister_instance(MatAllocator_LinkArray* instance) + { +#ifdef DEBUG_MATALLOCATOR + printf("unregister_instance %p\n", instance); +#endif + + std::lock_guard lock(m_all_instances_mutex); + delete instance; + auto it = std::find(m_all_instances.begin(), m_all_instances.end(), instance); + if (it != m_all_instances.end()) + m_all_instances.erase(it); + else + throw std::runtime_error("MatAllocator_LinkArray::unregister_instance / instance not found"); + } + }; + std::vector MatAllocator_LinkArray::m_all_instances; // C++ at its best syntactic terseness + std::mutex MatAllocator_LinkArray::m_all_instances_mutex; // with static members + } // anonymous namespace cv::Mat nparray_to_mat(pybind11::array& a) @@ -263,7 +267,7 @@ namespace cvnp // which will // - keep a reference to the numpy array // - create a cv::UMatData object that uses the numpy array data pointer - // - auto-destruct when the cv::Mat is destroyed (we hope...) + // - auto-destruct when the cv::Mat is destroyed auto allocator = new MatAllocator_LinkArray(a); cv::Mat m; m.allocator = allocator; diff --git a/tests/test_cvnp_cpp.cpp b/tests/test_cvnp_cpp.cpp index ba7fb97..b181369 100644 --- a/tests/test_cvnp_cpp.cpp +++ b/tests/test_cvnp_cpp.cpp @@ -151,7 +151,8 @@ void test_nparray_to_mat() } -//Python seems to fail with the following C++ function: +// See https://github.com/pthom/cvnp/issues/13 +// Python seems to fail with the following C++ function: //cpp: // m.def("test", [](cv::Mat mat) { // return mat;