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})
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.
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.
Related
I am working with openCV arUco markers to detect some markers.
So I generated pre-defined dictionary and saved it in a file.
However the aruco::detectMarkers can only get Ptr<aruco::Dictionary>. So I create a Ptr<aruco::Dictionary> object and sent to the constructor the address of the object itself.
This is causing to exception at the end of the application.
How can I solve it?
Here is my (simplified) code:
#include <opencv2/highgui.hpp>
#include <opencv2/aruco/charuco.hpp>
#include <opencv2/imgproc.hpp>
#include <iostream>
using namespace std;
using namespace cv;
aruco::Dictionary ReadDictionaryFromFile(std::string fileName)
{
cv::FileStorage fileStorage(fileName, cv::FileStorage::READ);
Mat bytesList;
int markerSize;
int maxCorrectionBits;
fileStorage\["markerSize"\] >> markerSize;
fileStorage\["maxCorrectionBits"\] >> maxCorrectionBits;
fileStorage\["bytesList"\] >> bytesList;
fileStorage.release();
aruco::Dictionary dictionary = cv::aruco::Dictionary(bytesList, markerSize, maxCorrectionBits);
return dictionary;
}
int main(int argc, char *argv\[\])
{
//Detector parameters:
Ptr<aruco::DetectorParameters> detectorParams = aruco::DetectorParameters::create();
//This works but I am afraid will generate another dictionary on another machine
//Ptr<aruco::Dictionary> dictionary = aruco::generateCustomDictionary(1, 4);
//This works but generate exception when app is terminated
aruco::Dictionary dictionaryTemp = ReadDictionaryFromFile("Dictionary.yml");
Ptr<aruco::Dictionary> dictionary = cv::Ptr<aruco::Dictionary>(&dictionaryTemp);
while (true)
{
if (camera.grab(image) != SUCCESS)
{
cout << "error on grab()" << std::endl;
return 0;
}
vector< int > ids;
vector< vector< Point2f > > corners, rejected;
vector< Vec3d > rvecs, tvecs;
// detect markers and estimate pose
aruco::detectMarkers(image, dictionary, corners, ids, detectorParams, rejected);
aruco::drawDetectedMarkers(image, corners, ids);
imshow("out", image);
char key = (char)waitKey(1);
if (key == 'q')
{
break;
}
}
return 0;
}
Here is the exception:
According to cv::Ptr documentation:
Template class for smart pointers with shared ownership.
A Ptr pretends to be a pointer to an object of type T. Unlike an ordinary pointer, however, the object will be automatically cleaned up once all Ptr instances pointing to it are destroyed.
You are creating an object on the stack, and then you are using it to construct the smart pointer. The smart pointer is not owning the object at all.
aruco::Dictionary dictionaryTemp = ReadDictionaryFromFile("Dictionary.yml");
Ptr<aruco::Dictionary> dictionary = cv::Ptr<aruco::Dictionary>(&dictionaryTemp);
At the end of the scope, which in your case it is the end of the main, dictionaryTemp will go out of scope and destroy the object.
After that, the smart pointer dictionary will go out of scope as well, but now the pointer is dangling and almost anything can happen.
Modify your code to give true ownership to the smart pointer:
Ptr<aruco::Dictionary> ReadDictionaryFromFile(std::string fileName)
{
...
return cv::Ptr<aruco::Dictionary>(
new cv::aruco::Dictionary(bytesList, markerSize, maxCorrectionBits));
}
For consideration related with exception safety, in modern, c++ writing the keyword new is rare and the best practise dictates to use a wrapper function called std::make_shared (which is working with the std::shared_ptr, there are similar function for others smart pointer). I am not familiar with cv::Ptr and I wasn't able to find an equivalent for it. But it worth to be aware of it.
Get familiar with RAII https://en.cppreference.com/w/cpp/language/raii and exception safety https://www.stroustrup.com/except.pdf
Also, you may want to familiarize with different types of smart pointers Smart pointers: who owns the object?
I have written a custom class that stores images and eventually computes a calibration based on those images, but I am encountering an issue in the way that the images are stored. I have two overloaded functions that do this, with one reading the images from file using cv::imread, and the other using an intermediate Snapshot data structure for holding the data. The function using cv::imread works fine, but the one using the custom data structure does not. I am trying to store three images right now, and the issue is that as I push the images into a vector, the data for the second images is copied into the first one.
This is the working function:
bool CalibClass::AddImage(const std::string& snapshotPath) {
cv::Mat img = cv::imread(snapshotPath);
// _snapshots is a private member declared as a std::vector<cv::Mat>
_snapshots.push_back(img);
return true;
}
This is the function that is not working:
bool CalibClass::AddImage(const ImageSet& snapshot) {
RGBImage *rgb_image_ptr = snapshot.GetRGBImage();
std::vector<unsigned char> img_data(rgb_image_ptr->GetData());
cv::Mat img(rgb_image_ptr->GetHeight(), rgb_image_ptr->GetWidth(), CV_8UC3, img_data.data());
_snapshots.push_back(img);
return true;
}
The ImageSet class stores images as an std::unique_ptr<RGBImage>. The RGBImage class stores the image data as a std::vector<unsigned char>.
This is how the images are loaded into the class from the main:
cv::Mat img1 = cv::imread("img1.png");
cv::Mat img2 = cv::imread("img2.png");
cv::Mat img3 = cv::imread("img3.png");
int length = img1.total() * img1.elemSize();
std::vector<unsigned char> data1;
std::vector<unsigned char> data2;
std::vector<unsigned char> data3;
for (int i = 0; i < length; i++) {
data1.push_back(img1.data[i]);
}
for (int i = 0; i < length; i++) {
data2.push_back(img2.data[i]);
}
for (int i = 0; i < length; i++) {
data3.push_back(img3.data[i]);
}
CalibClass calib_test;
std::unique_ptr<RGBImage> rgb_image_ptr1(new RGBImage(img1.rows, img1.cols, data1));
ImageSet new_snap1(rgb_image_ptr1, nullptr, 0);
calib_test.AddImage(new_snap1);
std::unique_ptr<RGBImage> rgb_image_ptr2(new RGBImage(img2.rows, img2.cols, data2));
ImageSet new_snap2(rgb_image_ptr2, nullptr, 0);
calib_test.AddImage(new_snap2);
std::unique_ptr<RGBImage> rgb_image_ptr3(new RGBImage(img3.rows, img3.cols, data3));
ImageSet new_snap3(rgb_image_ptr3, nullptr, 0);
calib_test.AddImage(new_snap3);
When I put a break point inside the function and check the content of the _snapshots, the first element is the second image, and the second and third elements are the third image. When I set a break point after all the AddImage() calls, the content of _snapshots has the second image as the first element, the third image as the second element, and the third element has a cv::Mat with invalid data.
What is the reason why the two methods are storing the images differently? What would be the way to fix this issue?
Those symptoms sound a lot like there is a shallow copy going on, which would mean undefined behavior in the second approach since the cv::Mat in the vector outlives img_data. Let me see if I can find documentation for the constructor you used.
Found it here. Yes, it does a shallow copy (emphasis added):
Matrix constructors that take data and step parameters do not allocate matrix data. Instead, they just initialize the matrix header that points to the specified data, which means that no data is copied.
So when the second approach pushes an image onto _snapshots, that image's data lives in the local variable img_data. Then the function ends, making that data invalid. Thus you get undefined behavior when you look at the data.
To solve this, you would need to make sure the data gets copied. You would also want to make sure the data gets freed at some point to avoid a memory leak. One approach is to define a class consisting of a cv::Mat and something to store the data, perhaps a std::vector<unsigned char>. (Use the latter member instead of the local variable img_data.) A starting point might be the following:
class MatWrapper {
public:
explicit MatWrapper(const RGBImage & rgb_image) :
data(rgb_image.GetData()),
image(rgb_image.GetHeight(), rgb_image.GetWidth(), CV_8UC3, data.data())
{}
private:
std::vector<unsigned char> data; // Declaration order matters!
cv::Mat image;
};
I am writing a function in which I need to access to a element of a Mat, but this function can receive Mat of differents types. So, if I have:
filtered.at<TypeofMat>(i) = (typeofmat) somevalue;
It only occurs to me to do something like this filtered.at<myMat.type()>(i), but obviously this does not work because type returns a int
I am stuck, Could someone give some light?
You can convert the source matrix to a matrix of double (type CV_64F). In this way you won't lose any data due to casting. Then you can work on this matrix as usual, since you know its type. Last step is to convert back the destination image to the source type.
You need to know the number of channels of your matrices, though. A CV_assert will make sure that you're working on the correct type.
#include <opencv2/opencv.hpp>
using namespace cv;
void foo(const Mat& src, Mat& dst)
{
// Assert number of channels
CV_Assert(src.channels() == 3);
// Convert to CV64F
Mat3d _src, _dst;
src.convertTo(_src, CV_64F);
_dst.create(_src.size());
// Work on _src and _dst (you know the type)
_dst(0,0) = _src(0,0) + Vec3d(1,2,3);
// Convert _dst to src type
_dst.convertTo(dst, src.type());
}
int main()
{
Mat3b img(10,10,Vec3b(0,0,0));
Mat res;
foo(img, res);
// res will be CV_8UC3
return 0;
}
There are also alternatives to this approach:
create a template function an call the appropriate specialization. See here
work on raw pointers. See here
use only OpenCV functions, that handle correctly every type. See here. This is usually the best option, if available.
If it is a possibility, make the function receiving the openCV Mat a template function:
void f<typename T>(const Mat& m)
{
(void) m.at<T>(0, 0);
}
use it like that:
Mat m1/* ... */;
m1.at<int>(0, 0) = 0;
f<int>(m);
I'm new to flatbuffer and I want to know if it's possible to get full (not const*) access to data in flatbuffers::Vector. Looking at the example below, I want to steal the ownership of img2::mem::data to store it in an Img-struct and process it in any way I want. Is this somehow possible, without memcopy-ing?
struct Img
{
int iXCount;
int iYCount;
int iXOffset;
unsigned char *mem;
};
int _tmain(int argc, _TCHAR* argv[])
{
Img img;
//init img;
flatbuffers::FlatBufferBuilder fbb;
auto mem = fbb.CreateVector(img.mem, img.iXOffset * img.iYCount);
auto mloc = CreateImage(fbb, img.iXCount, img.iYCount, img.iXOffset, mem);
fbb.Finish(mloc);
//unpack
auto img2 = flatbuffers::GetRoot<Image>(fbb.GetBufferPointer());
const std::uint8_t*pMem = img2->mem()->data(); //unfortunately only const*
return 0;
}
Your pMem points to data that sits somewhere in the middle of the FlatBuffer you're using. So this means you can access it, but only as long as you can keep the parent buffer around.
Since these are bytes, you could const-cast them, and modify them in place without copying. Note that if you ever try this with things that are not bytes, you have to be aware that data in a FlatBuffer is always little-endian.
Alternatively to const-cast, you can compile your schema with --gen-mutable, which will give you additional accessors to modify data in-place starting from GetMutableRoot, and data() will also be non-const from that.
How do you use a file to initialize a global const object that is too large to be created in the stack? This is my attempt so far:
// test.h
#pragma once
#include <boost/array.hpp>
typedef boost::array<int,100000> bigLut_t;
extern const bigLut_t constLut;
// test.cpp
#include <fstream>
#include <boost/filesystem.hpp>
#include "test.h"
bigLut_t& initializeConstLut()
{
if( boost::filesystem::exists("my_binary_file") == false ) {
std::ofstream outStream( "my_binary_file", ios::out | ios::binary );
bigLut_t* tempLut = new bigLut_t;
for(int i = 0; i < 100000; ++i) {
// Imagine this taking a long time,
// which is why we're using a file in the first place
tempLut->at(i) = i;
}
outStream.write( reinterpret_cast<char*>(tempLut), sizeof(bigLut_t) );
outStream.close();
delete tempLut;
}
// We can't write "bigLut_t lut;" because that would cause a stack overflow
bigLut_t* lut = new bigLut_t; // lut gets never deallocated
std::ifstream inStream( "my_binary_file", ios::in | ios::binary );
inStream.read( reinterpret_cast<char*>(lut), sizeof(bigLut_t) );
inStream.close();
return *lut;
}
const bigLut_t constLut = initializeConstLut();
AFAIK this works in a sense that constLut gets corretly initialized, but there's a memory leak since bigLut_t* lut gets never deallocated. I tried using a smart pointer for it, but that resulted in the values in constLut being quite random. I'm baffled by the lack of information I found by trying to google the solution.
How did you use shared_ptr ? Try the following :
// test.h
#pragma once
#include <boost/array.hpp>
typedef boost::array<int,100000> bigLut_t;
extern const bigLut_t constLut;
// test.cpp
#include <fstream>
#include <boost/filesystem.hpp>
#include "test.h"
boost::shared_ptr<bigLut_t> initializeConstLut()
{
if( boost::filesystem::exists("my_binary_file") == false ) {
std::ofstream outStream( "my_binary_file", ios::out | ios::binary );
bigLut_t* tempLut = new bigLut_t;
for(int i = 0; i < 100000; ++i) {
// Imagine this taking a long time,
// which is why we're using a file in the first place
tempLut->at(i) = i;
}
outStream.write( reinterpret_cast<char*>(tempLut), sizeof(bigLut_t) );
outStream.close();
delete tempLut;
}
// We can't write "bigLut_t lut;" because that would cause a stack overflow
boost::shared_ptr<bigLut_t> lut(new bigLut_t); // lut gets never deallocated
std::ifstream inStream( "my_binary_file", ios::in | ios::binary );
inStream.read( reinterpret_cast<char*>(lut), sizeof(bigLut_t) );
inStream.close();
return lut;
}
const bigLut_t constLut = *(initializeConstLut().get());
There are several good solutions to your problem. The solutions offered as answers so far are not among the good solutions (in particular, dynamic allocation and copying and relying on lifetime of temporary objects is just Very Badâ„¢). I'll just give you one common solution.
A simple way to provide a huge constant is to use a Meyers' singleton, which means defining the constant as a static local variable in a function that returns a reference to it:
inline BigThing const& theBigThing()
{
static BigThing const theInstance; // Default constructor does the init job.
return theInstance;
}
This is not yet a complete solution, but let's first see how you can get rid of having to invoke a function, and instead dealing directly with what-looks-like-a-constant:
namespace detail {
inline BigThing const& theBigThing()
{
static BigThing const theInstance; // Default constructor does the init job.
return theInstance;
}
}
BigThing const& theBigThing = detail::theBigThing(); // No copying, just ref.
In your case where BigThing is an array that should be initialized from data in a file, you can't directly rely on the default constructor. You could if you defined a wrapper class, and that's one way. OK, let's do that (it's what I'd choose, I think):
namespace detail {
struct BigThingWrapper
{
BigThing thingy_;
BigThingWrapper()
{
// Initialize the thingy_ member here.
}
};
inline BigThing const& theBigThing()
{
static BigThingWrapper const theInstance;
return theInstance.thingy_;
}
}
BigThing const& theBigThing = detail::theBigThing(); // No copying, just ref.
Note 1: I've used inline so that the code conceivably could be placed in a header file. Just remove for implementation placed in an implementation file.
Note 2: This code is untouched by compiler's hands and so may contain ERORS, TYPPOS and whatnot. :-) But it's the idea that is your answer.
Note 3: There are, as mentioned, also other good ways to do this, so this is not "the" answer, and there is no "the" answer, but it's "an" answer.
Cheers & hth.,
Just doing it the old fashioned way is fine - create an auto pointer whose lifecycle is the whole application life-cycle, and an extern reference to the pointer:
// test.h
#pragma once
#include <boost/array.hpp>
typedef boost::array<int,100000> bigLut_t;
extern const bigLut_t& constLut; // make it a reference
// test.cpp
#include <fstream>
#include <boost/filesystem.hpp>
#include "test.h"
namespace {
std::auto_ptr<bigLut_t> initializeConstLut()
{
std::auto_ptr<bigLut_t> lut(new bigLut_t);
if( boost::filesystem::exists("my_binary_file") == false ) {
std::ofstream outStream( "my_binary_file", ios::out | ios::binary );
for(int i = 0; i < 100000; ++i) {
// Imagine this taking a long time,
// which is why we're using a file in the first place
lut->at(i) = i;
}
outStream.write( reinterpret_cast<char*>(lut), sizeof(bigLut_t) );
outStream.close();
// no point writing then reading the same data
} else {
std::ifstream inStream( "my_binary_file", ios::in | ios::binary );
inStream.read( reinterpret_cast<char*>(lut.get()), sizeof(bigLut_t) );
inStream.close();
}
return lut;
}
// local to this compilation unit, deletes object on exit
std::auto_ptr<bigLut_t> constLutPtr ( initializeConstLut() );
}
// the extern reference refers to the object held by the auto_ptr
const bigLut_t& constLut ( *constLutPtr.get() );
No extra copying, client code sees the extern variable as before, though the linker may have to have an extra indirection rather than the extern varibale being in a fixed address (&constLutPtr is on the heap rather than in static data area).
If having a fixed address for constLut is important, go back to having an extern value rather than an extern reference, read the data in using a reinterpret cast and a const_cast of &constLutPtr. pass reinterpret_cast<char*>(const_cast<bigLut_t*>(&constLut)) to the stream read.
Wouldn't it be easier to just make your global object a pointer, new a temporary object from initializeConstLut and set the global pointer to that object before exiting the function ?
If the object is so big, you should avoid any solution which involves copying and/or assignment. So you're left with several choices :
Make the global object a pointer (free copy)
Make the global object non-const and read directly in it
I can't believe I'm saying this, but use a Singleton like pattern and wrap the object. This will allow you to isolate initialization and provide a public method for access which will return a const reference to the array.
I'm not adding much details here because it really depends on your requirements (I'm thinking thread-safety for instance).
There is a line of thought that says known objects do not leak, if the only leak is at the end of application life. If all you do with the object is remove it from memory (and do not release other resources, nor update some external stuff like DB's and files) then you can just leave it there. After all, by explicitly deleting them from memory you are just blocking the application from finishing for no real benefit: memory will be released one way or the other.
Add an explicit destruction of the lut. You are responsible for destroying anything you create. Best is to do it explicitly:
void destroyLut(bigLut_t& lut)
{
delete &lut;
}
Then simply call this function before exiting main. You should never rely on static initialization and destruction in C++. Better is to always do explicit initialization and destruction.