A library API takes an image raw pixel data as std::shared_ptr<uint8_t>
.
The image files I have can be in multiple formats (e.g. .png, .jpg, .bmp), so I use cv::imread
for practicality.
However, this returns the image as a cv::Mat
object, which I still need to convert to std::shared_ptr<uint8_t>
to comply with the library API signature.
I have tried similar solutions on the Internet, such as How to save image data in shared pointer and Creating shared_ptr from raw pointer, as well as std::make_shared
, either directly from the cv::Mat
std::make_shared<uint8_t>(*mat.data);
or from a std::vector
data.assign(mat.data, mat.data mat.total() * mat.channels());
std::make_shared<uint8_t>(*data.data());
But all attempts resulted in either double deletion exception, corrupted data or compilation error.
The only way I got it to work was to write the raw pixels data to a file then read it to a shared pointer with
std::shared_ptr<uint8_t> data;
std::ofstream output("temp", std::ios::out | std::ios::binary);
output.write(reinterpret_cast<char*>(mat.data), size);
std::ifstream input("temp", std::ios::in | std::ios::binary);
data.reset(new uint8_t[size], std::default_delete<uint8_t[]>());
input.read((char*)data.get(), size);
Although this works, it bothers me to have such terrible solution for this.
What is the proper way to convert cv::Mat
to a shared pointer so that the data complies with the library API signature?
The full example that works, but looks terrible is
#include <opencv2/core.hpp>
#include <opencv2/imgcodecs.hpp>
#include <opencv2/highgui.hpp>
#include <fstream>
int main(int argc,char* argv[])
{
std::string image_path = cv::samples::findFile("starry_night.jpg");
cv::Mat mat = cv::imread(image_path, cv::IMREAD_COLOR);
size_t size = mat.cols * mat.rows * mat.channels();
std::shared_ptr<uint8_t> data;
std::ofstream output("temp", std::ios::out | std::ios::binary);
output.write(reinterpret_cast<char*>(mat.data), size);
std::ifstream input("temp", std::ios::in | std::ios::binary);
data.reset(new uint8_t[size], std::default_delete<uint8_t[]>());
input.read((char*)data.get(), size);
// function(std::shared_ptr<uint8_t> data);
return 0;
}
I use the following CMakeLists.txt
to compile it (required OpenCV installed)
project(help)
find_package(OpenCV REQUIRED)
include_directories(${OpenCV_INCLUDE_DIRS})
add_executable(help help.cpp)
target_link_libraries(help ${OpenCV_LIBS})
CodePudding user response:
Its impossible. Copy the matrix or read the image using another library than cv::imread
to reverse the ownership semantics if it is a performance concern. OpenCV provides non-owning matrices (constructor from pointer, e.g. feed it with the output of stbi_load()
) which you can use as simple view like std::span
. However, you can't change that flag manually after the construction of the matrix.
Looking at the source code at modules/core/src/matrix.cpp
, it may be possible to add manually the flag cv::UMatData::USER_ALLOCATED
to prevent deallocation of memory.
Like that:
#include <opencv2/core.hpp>
int main()
{
cv::Mat img(10, 10, CV_8UC1);
auto ptr = std::shared_ptr<unsigned char>(img.data);
if(img.u)
{
img.u->flags |= cv::UMatData::USER_ALLOCATED;
}
// Single memory deallocation at the end of main()
}
~Mat()
calls release()
which calls deallocate()
which calls u->unmap()
and after a few function calls:
if( !(u->flags & UMatData::USER_ALLOCATED) )
{
fastFree(u->origdata);
u->origdata = 0;
}
However, it doesn't seem to work, maybe because OpenCV uses a custom allocator (fastFree()
) which may not be compatible with delete
called by the std::shared_ptr
. This may be the reason why OpenCV don't let release the matrix.
CodePudding user response:
As suggested in the comments, I will copy the data so that I can keep it after deleting cv::Mat
cv::Mat mat = cv::imread(image_path, cv::IMREAD_COLOR);
size_t size = mat.cols * mat.rows * mat.channels();
std::shared_ptr<uint8_t> data(new uint8_t[size]);
std::copy(mat.data, mat.data size, data.get());
This already looks way better than what I had before, but a better solution would be to cv::Mat
release its ownership of the data.