Efficiently load a large Mat into memory in OpenCV
Are you ok with a 100x speedup?
You should save and load your images in binary format. You can do that with the matwrite
and matread
function in the code below.
I tested both loading from a FileStorage
and the binary file, and for a smaller image with 250K rows, 192 columns, type CV_8UC1
I got these results (time in ms):
// Mat: 250K rows, 192 cols, type CV_8UC1
Using FileStorage: 5523.45
Using Raw: 50.0879
On a image with 1M rows and 192 cols using the binary mode I got (time in ms):
// Mat: 1M rows, 192 cols, type CV_8UC1
Using FileStorage: (can't load, out of memory)
Using Raw: 197.381
NOTE
- Never measure performance in debug.
- 3 minutes to load a matrix seems way too much, even for
FileStorage
s. However, you'll gain a lot switching to binary mode.
Here the code with the functions matwrite
and matread
, and the test:
#include <opencv2\opencv.hpp>
#include <iostream>
#include <fstream>
using namespace std;
using namespace cv;
void matwrite(const string& filename, const Mat& mat)
{
ofstream fs(filename, fstream::binary);
// Header
int type = mat.type();
int channels = mat.channels();
fs.write((char*)&mat.rows, sizeof(int)); // rows
fs.write((char*)&mat.cols, sizeof(int)); // cols
fs.write((char*)&type, sizeof(int)); // type
fs.write((char*)&channels, sizeof(int)); // channels
// Data
if (mat.isContinuous())
{
fs.write(mat.ptr<char>(0), (mat.dataend - mat.datastart));
}
else
{
int rowsz = CV_ELEM_SIZE(type) * mat.cols;
for (int r = 0; r < mat.rows; ++r)
{
fs.write(mat.ptr<char>(r), rowsz);
}
}
}
Mat matread(const string& filename)
{
ifstream fs(filename, fstream::binary);
// Header
int rows, cols, type, channels;
fs.read((char*)&rows, sizeof(int)); // rows
fs.read((char*)&cols, sizeof(int)); // cols
fs.read((char*)&type, sizeof(int)); // type
fs.read((char*)&channels, sizeof(int)); // channels
// Data
Mat mat(rows, cols, type);
fs.read((char*)mat.data, CV_ELEM_SIZE(type) * rows * cols);
return mat;
}
int main()
{
// Save the random generated data
{
Mat m(1024*256, 192, CV_8UC1);
randu(m, 0, 1000);
FileStorage fs("fs.yml", FileStorage::WRITE);
fs << "m" << m;
matwrite("raw.bin", m);
}
// Load the saved matrix
{
// Method 1: using FileStorage
double tic = double(getTickCount());
FileStorage fs("fs.yml", FileStorage::READ);
Mat m1;
fs["m"] >> m1;
double toc = (double(getTickCount()) - tic) * 1000. / getTickFrequency();
cout << "Using FileStorage: " << toc << endl;
}
{
// Method 2: usign raw binary data
double tic = double(getTickCount());
Mat m2 = matread("raw.bin");
double toc = (double(getTickCount()) - tic) * 1000. / getTickFrequency();
cout << "Using Raw: " << toc << endl;
}
int dummy;
cin >> dummy;
return 0;
}
Large data in a cv::Mat or a CvMat*
As far as I can tell, you can do this sort of. You can use the Mat_ template class. Below is a short example I wrote:
#include <opencv2/core/core.hpp>
#include <iostream>
using namespace std;
using namespace cv;
int main(int argc, char* argv[])
{
Mat_<long double> testing(Size(5, 5));
// initialize matrix to ones
for(int i = 0; i < testing.rows; i++)
{
for(int j = 0; j < testing.cols; j++)
{
testing.at<long double>(i, j) = 1;
}
}
cout << "Element size in bytes is " << testing.elemSize() << "." << endl;
return 0;
}
Now for the caveat... If you try to use many of the helper methods, ones
, zeros
, operator<<
, and others), you will likely see this error:
OpenCV Error: Unsupported format or combination of formats () in scalarToRawData
Hopefully that will be enough that you can use it for some things, but it won't be as clean as usual.
Why there is always 4.5 times difference between RAM and hard disk size of a cv::Mat?
Take a look at the contents of your XML or YML or .bin file with Notepad++. (By the way, if you specify a path ending in .bin, OpenCV will write it in a YAML format...)
You will see that each float
from your CV_32F Mat
has been written in a format like this 6.49999976e-001
. This represents 15 bytes instead of the 4 bytes expected for a float. This is a ratio of 15 / 4 = 3.75. If you add to that all the characters for formatting like ',' '\n' or ' '
, you may reach a size that is more than 4 times bigger than what you had on RAM.
If you try to save a Mat
with only zeros inside, you will see that the size is quite similar to what you had in RAM because the zeros are written 0.
. It is actually smaller if you save it in XML format.
Change data of OpenCV matrix from pointer
You need to create a deep copy. You can use clone
:
cv::Mat colorFrame = cv::Mat(height, width, CV_8UC3, pointerToMemoryOfCamera).clone();
You can also speed up the process of saving the images using matwrite
and matread
functions.
Related Topics
How Can a Windows Service Execute a Gui Application
How to Include Libraries in Visual Studio 2012
Error Lnk2019: Unresolved External Symbol _Winmain@16 Referenced in Function _Tmaincrtstartup
Copying a Polymorphic Object in C++
How to Initialize Base Class Member Variables in Derived Class Constructor
Calling Pthread_Cond_Signal Without Locking Mutex
How to Capture a Unique_Ptr into a Lambda Expression
How to Build Qt For Visual Studio 2010
Create Random Number Sequence With No Repeats
What Is the Partial Ordering Procedure in Template Deduction
How to Convert a Lambda to an Std::Function Using Templates
How to Hide a String in Binary Code
Why Is Std::Map Implemented as a Red-Black Tree
C++ Openmp Parallel For Loop - Alternatives to Std::Vector
C++ Function Template Partial Specialization
Is There Any Overhead to Declaring a Variable Within a Loop - C++