Slow shared memory access via pointer - c++

So, here is a setup:
My piece of software processes an image, captured from a device with a camera.
The device runs Linux on top of RTOS and has ARM cpu. A separate process manages image capturing. It uses shared memory and cyclic buffer. My piece of software gets (unsigned char *), I wrap it using openCV cv::Mat using .data attribute. Image sizes are predefined and known.
Turns out processing this cv::Mat is very very slow. Simple cv::resize takes much more time than resize on image loaded from the disk. I mean much more, like x10 more time.
What is the best way to wrap data pointer with cv::Mat? What could be an issue of slow memory access?
I'm sorry for such a scarce description of my problem. Also we have some other piece of software that works fast with this pointer. (Can't look into source code).
Code example:
cv::Mat Frame(480, 640, CV_8UC1);
cv::Mat transformFrame(unsigned char *frame) {
Frame.data = frame;
cv::Mat res;
cv::resize(Frame, res, cv::Size(240, 320), cv::INTER_LINEAR);
return res;
}
cv::resize works very slow if src is cv::Mat wrapped around pointer to shared memory and relatively fast with loaded image from disk.

Related

How to increase memory available to OpenCV

I have a problem with OpenCV.
I need to read from memory, rotate, resize and save some images.
It happens in a thread in 32-bit app (sadly, there is limitation, due to 3rd party library, that doesn't support 64-bit).
My code looks like this:
void processImage(std::vector<char> data)
{
cv::Mat image = cv::imdecode(data, cv::IMREAD_UNCHANGED);
cv::rotate(image, image, image_rotation_);
cv::imwrite(path_.toStdString(), image);
QString preview_path = path_.replace(path_.section("/",-1),"Previews/"+path_.section("/",-1));
cv::resize(image, image, cv::Size(preview_size_.width(), preview_size_.height()));
cv::imwrite(preview_path.toStdString(), image);
image.release();
}
But it crashes on 1st to 3rd pass of imread with message
OpenCV: terminate handler is called! The last OpenCV error is:
OpenCV(3.4.7) Error: Insufficient memory (Failed to allocate 150962688 bytes) in OutOfMemoryError, file C:\opencv-3.4.7\modules\core\src\alloc.cpp, line 72
My app uses around 500Mb of memory and there is about 40% of physical memory free.
Is there any way to resolve such problem or maybe I'm doing something wrong?

Change data of OpenCV matrix from pointer

I am trying to capture images from several cameras using the cameras driver,OpenCV and C++. My goal is to get as many FPS as possible, and to this end I have found saving the images in the hard drive to be the slowest operation. In order to speed up the process, I am doing each saving in separate threads. Problem is, I still have to wait for the saving to be complete to avoid the captured image being overwritten. Doing this provides good results, but for unknown reasons every 30-40 frames the speed is 10x higher.
I am addressing this by creating a ring buffer where I store the images, as these sudden drops in write speed are very short. I have obtained very good results using this approach, but unfortunately for more than 3 cameras the camera driver can't handle the stress and my program halts, waiting for the first image of the 4th camera to be saved. I checked and it's not the CPU, as 3 cameras + a thread writing random data in the disk works fine.
Now, seeing how using opencv reduced the stress on the camera driver, I would like to create a OpenCV mat buffer to hold the images while they are saved without my camera overwritting them (well, not until the buffer has done a whole lap, which I will make sure won't happen).
I know I can do
cv::Mat colorFrame(cv::Size(width, height),CV_8UC3,pointerToMemoryOfCamera);
to initialize a frame from the memory written by the camera. This does not solve my problem, as it will only point to the data, and the moment the camera overwrites it, it will corrupt the image saved.
How do I create a matrix with a given size and type, and then copy the contents of the memory to this matrix?
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.

Capture image frames from Kinect and save to Hard drive

My aim is to capture all the frames (RGB) from Kinect at 30 fps and save them to my hard drive. For doing this I took the following approach.
Get the frames from Kinect and store them in an array buffer. Since writing to disk (using imwrite()) takes a bit of time and I may miss some frames while doing so, so instead of directly saving them to the disk, I store them in an array. Now, I have another parallel thread that accesses this array and writes the individual frames to the disk as images.
Now I have used a static array of size 3000 and type Mat. This will suffice since I need to store frames for 1.5 minute videos (1.5 minutes = 2700 frames). I have declared the array as follows :
#define NUM_FRAMES 3000
Mat rgb[NUM_FRAMES];
I have already tested this limit by reading images and saving them to the array using the following code :
for(int i=0; i<NUM_FRAMES; i++)
{
Mat img = imread("image.jpg", CV_LOAD_IMAGE_COLOR);
rgb[i] = img;
imshow("Image", img);
cvWaitKey(10);
}
The above code executed flawlessly.
But one problem is that the code I am using for capturing image using Kinect, captures the image in an IplImage. Thus I need to convert the image to cv::Mat format before using it. I convert it using the following command:
IplImage* color = cvCreateImageHeader(cvSize(COLOR_WIDTH, COLOR_HEIGHT), IPL_DEPTH_8U, 4);
cvSetData(color, colorBuffer, colorLockedRect.Pitch); // colorBuffer and colorLockedRect.Pitch is something that Kinect uses. Not related to OpenCv
rgb[rgb_read++] = Mat(color, FLAG);
Now here lies my problem. Whenever I am setting #define FLAG true, it causes memory leaks and gives me OpenCv Error: Insufficient memory (failed to allocate 1228804 bytes) error.
But if I use #define FLAG false it works correctly, but the frames that I am getting is erroneous as shown below. They are three consecutive frames.
I was moving around my arm and the image got cut in between as can be seen from above.
Can someone please point out the reason for this weird behavior or any other alternate way of obtaining the desired result. I have been struggling with this since a few days now. Please ask for if any further clarifications are required.
I am using OpenCV 2.4.8, Kinect SDK for Windows version-1.8.0 and Microsoft Visual Studio 2010.
Also can someone please explan to me the role of the CopyData parameter in Mat::Mat. I have already gone through this link, but still it's not completely clear. Maybe that's why I could not solve the above error in the first place since it's working is not very clear.
Thanks in advance.
first, do not use IplImages, stick with cv::Mat, please.
the equivalent code for that would be:
Mat img_borrowed = Mat( height, width, CV_8U4C, colorBuffer, colorLockedRect.Pitch );
note, that this does not do any allocation on its own, it's still the kinect's pixels, so you will have to clone() it:
rgb[rgb_read++] = img_borrowed.clone();
this is the same as setting the flag in your code above to 'true'. (deep-copy the data)
[edit] maybe it's a good idea to skip the useless 4th channel (also less mem required), so , instead of the above you could do:
cvtColor( img_borrowed, rgb[rgb_read++], CV_BGRA2BGR); // will make a 'deep copy', too.
so, - here's the bummer: if you don't save a deep-copy in your array, you'll end up with garbled (and all the same!) images, probably even with undefined behaviour due to the locking/unlocking of the kinect buffer, if you do copy it (and you must), you will need a lot of memory.
unlikely, that you can keep 3000 *1024*786*4 = 9658368000 bytes in memory, you'll have to cut it down one way or another.

OpenCV: Memory leak when writing video from webcam using cvWriteFrame

I want to record video from webcam using opencv.
I put the following code inside a timer event handler function, which is called each 50 ms
IplImage *image = cvQueryFrame(camera);
IplImage *resizeImage = cvCreateImage( size, 8, 3);
cvResize(image, resizeImage);
cvWriteFrame(writer, resizeImage );
With writer is created using cvCreateVideoWriter, and the video is created when I call cvReleaseVideoWriter(&writer)
The problem is I can not release memory allocated by cvWriteFrame until cvReleaseVideoWriter is called. That makes a big issue when I need record a long time video.
How can I handle this situation?
I suppose that the best solution in your case (if you don't want to modify OpenCV code), is to write several video files.
As I can suppose, each frame is grabbed to RAM as is without any compressing. So, you can calculate a number of frames before amount of allocated memory exceed a particular size. Then you finish writing to file and start a new one.

An efficient way to buffer HD video real-time without maxing out memory

I am writing a program that involves real-time processing of video from a network camera using OpenCV. I want to be able to capture (at any time during processing) previous images (e.g. say ten seconds worth) and save to a video file.
I am currently doing this using a queue as a buffer (to push 'cv::Mat' data) but this is obviously not efficient as a few seconds worth of images soon uses up all the PC memory. I tried compressing images using 'cv::imencode' but that doesn't make much difference using PNG, I need a solution that uses hard-drive memory and efficient for real-time operation.
Can anyone suggest a very simple and efficient solution?
EDIT:
Just so that everyone understands what I'm doing at the moment; here's the code for a 10 second buffer:
void run()
{
cv::VideoCapture cap(0);
double fps = cap.get(CV_CAP_PROP_FPS);
int buffer_lenght = 10; // in seconds
int wait = 1000.0/fps;
QTime time;
forever{
time.restart();
cv::mat image;
bool read = cap.read(image);
if(!read)
break;
bool locked = _mutex.tryLock(10);
if(locked){
if(image.data){
_buffer.push(image);
if((int)_buffer.size() > (fps*buffer_lenght))
_buffer.pop();
}
_mutex.unlock();
}
int time_taken = time.elapsed();
if(time_taken<wait)
msleep(wait-time_taken);
}
cap.release();
}
queue<cv::Mat> _buffer and QMutex _mutex are global variables. If you're familiar with QT, signals and slots etc, I've got a slot that grabs the buffer and saves it as a video using cv::VideoWriter.
EDIT:
I think the ideal solution will be for my queue<cv::Mat> _buffer to use hard-drive memory rather than pc memory. Not sure on which planet this is possible? :/
I suggest looking into real-time compression with x264 or similar. x264 is regularly used for real-time encoding of video streams and, with the right settings, can encode multiple streams or a 1080p video stream in a moderately powered processor.
I suggest asking in doom9's forum or similar forums.
x264 is a free h.264 encoder which can achieve 100:1 or better (vs raw) compression. The output of x264 can be stored in your memory queue with much greater efficiency than uncompressed (or losslessly compressed) video.
UPDATED
One thing you can do is store images to the hard disk using imwrite and update their filenames to the queue. When the queue is full, delete images as you pop filenames.
In your video writing slot, load the images as they are popped from the queue and write them to your VideoWriter instance
You mentioned you needed to use Hard Drive Memory
In that case, consider using the OpenCV HighGUI VideoWriter. You can create an instance of VideoWriter as below:
VideoWriter record("RobotVideo.avi", CV_FOURCC('D','I','V','X'),
30, frame.size(), true);
And write image captures to in as below:
record.write(image);
Find the documentation and the sample program on the website.